9733 lines
306 KiB
C++
9733 lines
306 KiB
C++
// Scintilla source code edit control
|
|
/** @file Editor.cxx
|
|
** Main code for the edit control.
|
|
**/
|
|
// Copyright 1998-2011 by Neil Hodgson <neilh@scintilla.org>
|
|
// The License.txt file describes the conditions under which this software may be distributed.
|
|
|
|
#include "Platform.h"
|
|
|
|
#include "ILexer.h"
|
|
#include "Scintilla.h"
|
|
|
|
#include "StringCopy.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 "CaseFolder.h"
|
|
#include "Document.h"
|
|
#include "UniConversion.h"
|
|
#include "Selection.h"
|
|
#include "PositionCache.h"
|
|
#include "Editor.h"
|
|
|
|
#ifdef SCI_NAMESPACE
|
|
using 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 < ' ';
|
|
}
|
|
|
|
static inline bool IsAllSpacesOrTabs(char *s, unsigned int len) {
|
|
for (unsigned int i = 0; i < len; i++) {
|
|
// This is safe because IsSpaceOrTab() will return false for null terminators
|
|
if (!IsSpaceOrTab(s[i]))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
PrintParameters::PrintParameters() {
|
|
magnification = 0;
|
|
colourMode = SC_PRINT_NORMAL;
|
|
wrapState = eWrapWord;
|
|
}
|
|
|
|
Editor::Editor() {
|
|
ctrlID = 0;
|
|
|
|
stylesValid = false;
|
|
technology = SC_TECHNOLOGY_DEFAULT;
|
|
scaleRGBAImage = 100.0f;
|
|
|
|
cursorMode = SC_CURSORNORMAL;
|
|
|
|
hasFocus = false;
|
|
hideSelection = false;
|
|
inOverstrike = false;
|
|
drawOverstrikeCaret = true;
|
|
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 = SelectionPosition(invalidPosition);
|
|
posDrop = SelectionPosition(invalidPosition);
|
|
hotSpotClickPos = INVALID_POSITION;
|
|
selectionType = selChar;
|
|
|
|
lastXChosen = 0;
|
|
lineAnchorPos = 0;
|
|
originalAnchorPos = 0;
|
|
wordSelectAnchorStartPos = 0;
|
|
wordSelectAnchorEndPos = 0;
|
|
wordSelectInitialCaretPos = -1;
|
|
|
|
primarySelection = true;
|
|
|
|
caretXPolicy = CARET_SLOP | CARET_EVEN;
|
|
caretXSlop = 50;
|
|
|
|
caretYPolicy = CARET_EVEN;
|
|
caretYSlop = 0;
|
|
|
|
visiblePolicy = 0;
|
|
visibleSlop = 0;
|
|
|
|
searchAnchor = 0;
|
|
|
|
xOffset = 0;
|
|
xCaretMargin = 50;
|
|
horizontalScrollBarVisible = true;
|
|
scrollWidth = 2000;
|
|
trackLineWidth = false;
|
|
lineWidthMaxSeen = 0;
|
|
verticalScrollBarVisible = true;
|
|
endAtLastLine = true;
|
|
caretSticky = SC_CARETSTICKY_OFF;
|
|
marginOptions = SC_MARGINOPTION_NONE;
|
|
mouseSelectionRectangularSwitch = false;
|
|
multipleSelection = false;
|
|
additionalSelectionTyping = false;
|
|
multiPasteMode = SC_MULTIPASTE_ONCE;
|
|
additionalCaretsBlink = true;
|
|
additionalCaretsVisible = true;
|
|
virtualSpaceOptions = SCVS_NONE;
|
|
|
|
pixmapLine = 0;
|
|
pixmapSelMargin = 0;
|
|
pixmapSelPattern = 0;
|
|
pixmapSelPatternOffset1 = 0;
|
|
pixmapIndentGuide = 0;
|
|
pixmapIndentGuideHighlight = 0;
|
|
|
|
targetStart = 0;
|
|
targetEnd = 0;
|
|
searchFlags = 0;
|
|
|
|
topLine = 0;
|
|
posTopLine = 0;
|
|
|
|
lengthForEncode = -1;
|
|
|
|
needUpdateUI = 0;
|
|
ContainerNeedsUpdate(SC_UPDATE_CONTENT);
|
|
braces[0] = invalidPosition;
|
|
braces[1] = invalidPosition;
|
|
bracesMatchStyle = STYLE_BRACEBAD;
|
|
highlightGuideColumn = 0;
|
|
|
|
paintState = notPainting;
|
|
paintAbandonedByStyling = false;
|
|
paintingAllText = false;
|
|
willRedrawAll = false;
|
|
|
|
modEventMask = SC_MODEVENTMASKALL;
|
|
|
|
pdoc = new Document();
|
|
pdoc->AddRef();
|
|
pdoc->AddWatcher(this, 0);
|
|
|
|
recordingMacro = false;
|
|
foldFlags = 0;
|
|
foldAutomatic = 0;
|
|
|
|
wrapWidth = LineLayout::wrapWidthInfinite;
|
|
|
|
convertPastes = true;
|
|
|
|
hsStart = -1;
|
|
hsEnd = -1;
|
|
|
|
llc.SetLevel(LineLayoutCache::llcCaret);
|
|
posCache.SetSize(0x400);
|
|
|
|
SetRepresentations();
|
|
}
|
|
|
|
Editor::~Editor() {
|
|
pdoc->RemoveWatcher(this, 0);
|
|
pdoc->Release();
|
|
pdoc = 0;
|
|
DropGraphics(true);
|
|
}
|
|
|
|
void Editor::Finalise() {
|
|
SetIdle(false);
|
|
CancelModes();
|
|
}
|
|
|
|
void Editor::SetRepresentations() {
|
|
reprs.Clear();
|
|
|
|
// C0 control set
|
|
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"
|
|
};
|
|
for (size_t j=0; j < ELEMENTS(reps); j++) {
|
|
char c[2] = { static_cast<char>(j), 0 };
|
|
reprs.SetRepresentation(c, reps[j]);
|
|
}
|
|
|
|
// C1 control set
|
|
// As well as Unicode mode, ISO-8859-1 should use these
|
|
if (IsUnicodeMode()) {
|
|
const char *repsC1[] = {
|
|
"PAD", "HOP", "BPH", "NBH", "IND", "NEL", "SSA", "ESA",
|
|
"HTS", "HTJ", "VTS", "PLD", "PLU", "RI", "SS2", "SS3",
|
|
"DCS", "PU1", "PU2", "STS", "CCH", "MW", "SPA", "EPA",
|
|
"SOS", "SGCI", "SCI", "CSI", "ST", "OSC", "PM", "APC"
|
|
};
|
|
for (size_t j=0; j < ELEMENTS(repsC1); j++) {
|
|
char c1[3] = { '\xc2', static_cast<char>(0x80+j), 0 };
|
|
reprs.SetRepresentation(c1, repsC1[j]);
|
|
}
|
|
reprs.SetRepresentation("\xe2\x80\xa8", "LS");
|
|
reprs.SetRepresentation("\xe2\x80\xa9", "PS");
|
|
}
|
|
|
|
// UTF-8 invalid bytes
|
|
if (IsUnicodeMode()) {
|
|
for (int k=0x80; k < 0x100; k++) {
|
|
char hiByte[2] = { static_cast<char>(k), 0 };
|
|
char hexits[4];
|
|
sprintf(hexits, "x%2X", k);
|
|
reprs.SetRepresentation(hiByte, hexits);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Editor::DropGraphics(bool freeObjects) {
|
|
if (freeObjects) {
|
|
delete pixmapLine;
|
|
pixmapLine = 0;
|
|
delete pixmapSelMargin;
|
|
pixmapSelMargin = 0;
|
|
delete pixmapSelPattern;
|
|
pixmapSelPattern = 0;
|
|
delete pixmapSelPatternOffset1;
|
|
pixmapSelPatternOffset1 = 0;
|
|
delete pixmapIndentGuide;
|
|
pixmapIndentGuide = 0;
|
|
delete pixmapIndentGuideHighlight;
|
|
pixmapIndentGuideHighlight = 0;
|
|
} else {
|
|
if (pixmapLine)
|
|
pixmapLine->Release();
|
|
if (pixmapSelMargin)
|
|
pixmapSelMargin->Release();
|
|
if (pixmapSelPattern)
|
|
pixmapSelPattern->Release();
|
|
if (pixmapSelPatternOffset1)
|
|
pixmapSelPatternOffset1->Release();
|
|
if (pixmapIndentGuide)
|
|
pixmapIndentGuide->Release();
|
|
if (pixmapIndentGuideHighlight)
|
|
pixmapIndentGuideHighlight->Release();
|
|
}
|
|
}
|
|
|
|
void Editor::AllocateGraphics() {
|
|
if (!pixmapLine)
|
|
pixmapLine = Surface::Allocate(technology);
|
|
if (!pixmapSelMargin)
|
|
pixmapSelMargin = Surface::Allocate(technology);
|
|
if (!pixmapSelPattern)
|
|
pixmapSelPattern = Surface::Allocate(technology);
|
|
if (!pixmapSelPatternOffset1)
|
|
pixmapSelPatternOffset1 = Surface::Allocate(technology);
|
|
if (!pixmapIndentGuide)
|
|
pixmapIndentGuide = Surface::Allocate(technology);
|
|
if (!pixmapIndentGuideHighlight)
|
|
pixmapIndentGuideHighlight = Surface::Allocate(technology);
|
|
}
|
|
|
|
void Editor::InvalidateStyleData() {
|
|
stylesValid = false;
|
|
vs.technology = technology;
|
|
DropGraphics(false);
|
|
AllocateGraphics();
|
|
llc.Invalidate(LineLayout::llInvalid);
|
|
posCache.Clear();
|
|
}
|
|
|
|
void Editor::InvalidateStyleRedraw() {
|
|
NeedWrapping();
|
|
InvalidateStyleData();
|
|
Redraw();
|
|
}
|
|
|
|
void Editor::RefreshStyleData() {
|
|
if (!stylesValid) {
|
|
stylesValid = true;
|
|
AutoSurface surface(this);
|
|
if (surface) {
|
|
vs.Refresh(*surface, pdoc->tabInChars);
|
|
}
|
|
SetScrollBars();
|
|
SetRectangularRange();
|
|
}
|
|
}
|
|
|
|
Point Editor::GetVisibleOriginInMain() {
|
|
return Point(0,0);
|
|
}
|
|
|
|
Point Editor::DocumentPointFromView(Point ptView) {
|
|
Point ptDocument = ptView;
|
|
if (wMargin.GetID()) {
|
|
Point ptOrigin = GetVisibleOriginInMain();
|
|
ptDocument.x += ptOrigin.x;
|
|
ptDocument.y += ptOrigin.y;
|
|
} else {
|
|
ptDocument.x += xOffset;
|
|
ptDocument.y += topLine * vs.lineHeight;
|
|
}
|
|
return ptDocument;
|
|
}
|
|
|
|
int Editor::TopLineOfMain() const {
|
|
if (wMargin.GetID())
|
|
return 0;
|
|
else
|
|
return topLine;
|
|
}
|
|
|
|
PRectangle Editor::GetClientRectangle() {
|
|
return wMain.GetClientPosition();
|
|
}
|
|
|
|
PRectangle Editor::GetClientDrawingRectangle() {
|
|
return GetClientRectangle();
|
|
}
|
|
|
|
PRectangle Editor::GetTextRectangle() {
|
|
PRectangle rc = GetClientRectangle();
|
|
rc.left += vs.textStart;
|
|
rc.right -= vs.rightMarginWidth;
|
|
return rc;
|
|
}
|
|
|
|
int Editor::LinesOnScreen() {
|
|
PRectangle rcClient = GetClientRectangle();
|
|
int htClient = static_cast<int>(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 < ELEMENTS(reps)) {
|
|
return reps[ch];
|
|
} else {
|
|
return "BAD";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convenience class to ensure LineLayout objects are always disposed.
|
|
*/
|
|
class AutoLineLayout {
|
|
LineLayoutCache &llc;
|
|
LineLayout *ll;
|
|
AutoLineLayout(const AutoLineLayout &src);
|
|
AutoLineLayout &operator=(const AutoLineLayout &);
|
|
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_;
|
|
}
|
|
};
|
|
|
|
SelectionPosition Editor::ClampPositionIntoDocument(SelectionPosition sp) const {
|
|
if (sp.Position() < 0) {
|
|
return SelectionPosition(0);
|
|
} else if (sp.Position() > pdoc->Length()) {
|
|
return SelectionPosition(pdoc->Length());
|
|
} else {
|
|
// If not at end of line then set offset to 0
|
|
if (!pdoc->IsLineEndPosition(sp.Position()))
|
|
sp.SetVirtualSpace(0);
|
|
return sp;
|
|
}
|
|
}
|
|
|
|
Point Editor::LocationFromPosition(SelectionPosition pos) {
|
|
Point pt;
|
|
RefreshStyleData();
|
|
if (pos.Position() == INVALID_POSITION)
|
|
return pt;
|
|
const int line = pdoc->LineFromPosition(pos.Position());
|
|
const int lineVisible = cs.DisplayFromDoc(line);
|
|
//Platform::DebugPrintf("line=%d\n", line);
|
|
AutoSurface surface(this);
|
|
AutoLineLayout ll(llc, RetrieveLineLayout(line));
|
|
if (surface && ll) {
|
|
const int posLineStart = pdoc->LineStart(line);
|
|
LayoutLine(line, surface, vs, ll, wrapWidth);
|
|
const int posInLine = pos.Position() - posLineStart;
|
|
pt = ll->PointFromPosition(posInLine, vs.lineHeight);
|
|
pt.y += (lineVisible - topLine) * vs.lineHeight;
|
|
pt.x += vs.textStart - xOffset;
|
|
}
|
|
pt.x += pos.VirtualSpace() * vs.styles[ll->EndLineStyle()].spaceWidth;
|
|
return pt;
|
|
}
|
|
|
|
Point Editor::LocationFromPosition(int pos) {
|
|
return LocationFromPosition(SelectionPosition(pos));
|
|
}
|
|
|
|
int Editor::XFromPosition(int pos) {
|
|
Point pt = LocationFromPosition(pos);
|
|
return static_cast<int>(pt.x) - vs.textStart + xOffset;
|
|
}
|
|
|
|
int Editor::XFromPosition(SelectionPosition sp) {
|
|
Point pt = LocationFromPosition(sp);
|
|
return static_cast<int>(pt.x) - vs.textStart + xOffset;
|
|
}
|
|
|
|
int Editor::LineFromLocation(Point pt) const {
|
|
return cs.DocFromDisplay(static_cast<int>(pt.y) / vs.lineHeight + topLine);
|
|
}
|
|
|
|
void Editor::SetTopLine(int topLineNew) {
|
|
if ((topLine != topLineNew) && (topLineNew >= 0)) {
|
|
topLine = topLineNew;
|
|
ContainerNeedsUpdate(SC_UPDATE_V_SCROLL);
|
|
}
|
|
posTopLine = pdoc->LineStart(cs.DocFromDisplay(topLine));
|
|
}
|
|
|
|
SelectionPosition Editor::SPositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition, bool virtualSpace) {
|
|
RefreshStyleData();
|
|
if (canReturnInvalid) {
|
|
PRectangle rcClient = GetTextRectangle();
|
|
// May be in scroll view coordinates so translate back to main view
|
|
Point ptOrigin = GetVisibleOriginInMain();
|
|
rcClient.Move(-ptOrigin.x, -ptOrigin.y);
|
|
if (!rcClient.Contains(pt))
|
|
return SelectionPosition(INVALID_POSITION);
|
|
if (pt.x < vs.textStart)
|
|
return SelectionPosition(INVALID_POSITION);
|
|
if (pt.y < 0)
|
|
return SelectionPosition(INVALID_POSITION);
|
|
}
|
|
pt = DocumentPointFromView(pt);
|
|
pt.x = pt.x - vs.textStart;
|
|
int visibleLine = static_cast<int>(floor(pt.y / vs.lineHeight));
|
|
if (!canReturnInvalid && (visibleLine < 0))
|
|
visibleLine = 0;
|
|
const int lineDoc = cs.DocFromDisplay(visibleLine);
|
|
if (canReturnInvalid && (lineDoc < 0))
|
|
return SelectionPosition(INVALID_POSITION);
|
|
if (lineDoc >= pdoc->LinesTotal())
|
|
return SelectionPosition(canReturnInvalid ? INVALID_POSITION : pdoc->Length());
|
|
const int posLineStart = pdoc->LineStart(lineDoc);
|
|
AutoSurface surface(this);
|
|
AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
|
|
if (surface && ll) {
|
|
LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
|
|
const int lineStartSet = cs.DisplayFromDoc(lineDoc);
|
|
const int subLine = visibleLine - lineStartSet;
|
|
if (subLine < ll->lines) {
|
|
const Range rangeSubLine = ll->SubLineRange(subLine);
|
|
const XYPOSITION subLineStart = ll->positions[rangeSubLine.start];
|
|
if (subLine > 0) // Wrapped
|
|
pt.x -= ll->wrapIndent;
|
|
const int positionInLine = ll->FindPositionFromX(pt.x + subLineStart, rangeSubLine, charPosition);
|
|
if (positionInLine < rangeSubLine.end) {
|
|
return SelectionPosition(pdoc->MovePositionOutsideChar(positionInLine + posLineStart, 1));
|
|
}
|
|
if (virtualSpace) {
|
|
const XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth;
|
|
const int spaceOffset = static_cast<int>(
|
|
(pt.x + subLineStart - ll->positions[rangeSubLine.end] + spaceWidth / 2) / spaceWidth);
|
|
return SelectionPosition(rangeSubLine.end + posLineStart, spaceOffset);
|
|
} else if (canReturnInvalid) {
|
|
if (pt.x < (ll->positions[rangeSubLine.end] - subLineStart)) {
|
|
return SelectionPosition(pdoc->MovePositionOutsideChar(rangeSubLine.end + posLineStart, 1));
|
|
}
|
|
} else {
|
|
return SelectionPosition(rangeSubLine.end + posLineStart);
|
|
}
|
|
}
|
|
if (!canReturnInvalid)
|
|
return SelectionPosition(ll->numCharsInLine + posLineStart);
|
|
}
|
|
return SelectionPosition(canReturnInvalid ? INVALID_POSITION : posLineStart);
|
|
}
|
|
|
|
int Editor::PositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition) {
|
|
return SPositionFromLocation(pt, canReturnInvalid, charPosition, false).Position();
|
|
}
|
|
|
|
/**
|
|
* Find the document position corresponding to an x coordinate on a particular document line.
|
|
* Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
|
|
* This method is used for rectangular selections and does not work on wrapped lines.
|
|
*/
|
|
SelectionPosition Editor::SPositionFromLineX(int lineDoc, int x) {
|
|
RefreshStyleData();
|
|
if (lineDoc >= pdoc->LinesTotal())
|
|
return SelectionPosition(pdoc->Length());
|
|
//Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine);
|
|
AutoSurface surface(this);
|
|
AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
|
|
if (surface && ll) {
|
|
const int posLineStart = pdoc->LineStart(lineDoc);
|
|
LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
|
|
const Range rangeSubLine = ll->SubLineRange(0);
|
|
const XYPOSITION subLineStart = ll->positions[rangeSubLine.start];
|
|
const int positionInLine = ll->FindPositionFromX(x + subLineStart, rangeSubLine, false);
|
|
if (positionInLine < rangeSubLine.end) {
|
|
return SelectionPosition(pdoc->MovePositionOutsideChar(positionInLine + posLineStart, 1));
|
|
}
|
|
const XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth;
|
|
const int spaceOffset = static_cast<int>(
|
|
(x + subLineStart - ll->positions[rangeSubLine.end] + spaceWidth / 2) / spaceWidth);
|
|
return SelectionPosition(rangeSubLine.end + posLineStart, spaceOffset);
|
|
}
|
|
return SelectionPosition(0);
|
|
}
|
|
|
|
int Editor::PositionFromLineX(int lineDoc, int x) {
|
|
return SPositionFromLineX(lineDoc, x).Position();
|
|
}
|
|
|
|
/**
|
|
* If painting then abandon the painting because a wider redraw is needed.
|
|
* @return true if calling code should stop drawing.
|
|
*/
|
|
bool Editor::AbandonPaint() {
|
|
if ((paintState == painting) && !paintingAllText) {
|
|
paintState = paintAbandoned;
|
|
}
|
|
return paintState == paintAbandoned;
|
|
}
|
|
|
|
void Editor::RedrawRect(PRectangle rc) {
|
|
//Platform::DebugPrintf("Redraw %0d,%0d - %0d,%0d\n", rc.left, rc.top, rc.right, rc.bottom);
|
|
|
|
// Clip the redraw rectangle into the client area
|
|
PRectangle rcClient = GetClientRectangle();
|
|
if (rc.top < rcClient.top)
|
|
rc.top = rcClient.top;
|
|
if (rc.bottom > rcClient.bottom)
|
|
rc.bottom = rcClient.bottom;
|
|
if (rc.left < rcClient.left)
|
|
rc.left = rcClient.left;
|
|
if (rc.right > rcClient.right)
|
|
rc.right = rcClient.right;
|
|
|
|
if ((rc.bottom > rc.top) && (rc.right > rc.left)) {
|
|
wMain.InvalidateRectangle(rc);
|
|
}
|
|
}
|
|
|
|
void Editor::DiscardOverdraw() {
|
|
// Overridden on platforms that may draw outside visible area.
|
|
}
|
|
|
|
void Editor::Redraw() {
|
|
//Platform::DebugPrintf("Redraw all\n");
|
|
PRectangle rcClient = GetClientRectangle();
|
|
wMain.InvalidateRectangle(rcClient);
|
|
if (wMargin.GetID())
|
|
wMargin.InvalidateAll();
|
|
//wMain.InvalidateAll();
|
|
}
|
|
|
|
void Editor::RedrawSelMargin(int line, bool allAfter) {
|
|
bool abandonDraw = false;
|
|
if (!wMargin.GetID()) // Margin in main window so may need to abandon and retry
|
|
abandonDraw = AbandonPaint();
|
|
if (!abandonDraw) {
|
|
if (vs.maskInLine) {
|
|
Redraw();
|
|
} else {
|
|
PRectangle rcSelMargin = GetClientRectangle();
|
|
rcSelMargin.right = rcSelMargin.left + vs.fixedColumnWidth;
|
|
if (line != -1) {
|
|
PRectangle rcLine = RectangleFromRange(Range(pdoc->LineStart(line)));
|
|
|
|
// Inflate line rectangle if there are image markers with height larger than line height
|
|
if (vs.largestMarkerHeight > vs.lineHeight) {
|
|
int delta = (vs.largestMarkerHeight - vs.lineHeight + 1) / 2;
|
|
rcLine.top -= delta;
|
|
rcLine.bottom += delta;
|
|
if (rcLine.top < rcSelMargin.top)
|
|
rcLine.top = rcSelMargin.top;
|
|
if (rcLine.bottom > rcSelMargin.bottom)
|
|
rcLine.bottom = rcSelMargin.bottom;
|
|
}
|
|
|
|
rcSelMargin.top = rcLine.top;
|
|
if (!allAfter)
|
|
rcSelMargin.bottom = rcLine.bottom;
|
|
if (rcSelMargin.Empty())
|
|
return;
|
|
}
|
|
if (wMargin.GetID()) {
|
|
Point ptOrigin = GetVisibleOriginInMain();
|
|
rcSelMargin.Move(-ptOrigin.x, -ptOrigin.y);
|
|
wMargin.InvalidateRectangle(rcSelMargin);
|
|
} else {
|
|
wMain.InvalidateRectangle(rcSelMargin);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
PRectangle Editor::RectangleFromRange(Range r) {
|
|
const int minLine = cs.DisplayFromDoc(pdoc->LineFromPosition(r.First()));
|
|
const int maxLine = cs.DisplayLastFromDoc(pdoc->LineFromPosition(r.Last()));
|
|
const PRectangle rcClientDrawing = GetClientDrawingRectangle();
|
|
PRectangle rc;
|
|
const int leftTextOverlap = ((xOffset == 0) && (vs.leftMarginWidth > 0)) ? 1 : 0;
|
|
rc.left = static_cast<XYPOSITION>(vs.textStart - leftTextOverlap);
|
|
rc.top = static_cast<XYPOSITION>((minLine - TopLineOfMain()) * vs.lineHeight);
|
|
if (rc.top < rcClientDrawing.top)
|
|
rc.top = rcClientDrawing.top;
|
|
// Extend to right of prepared area if any to prevent artifacts from caret line highlight
|
|
rc.right = rcClientDrawing.right;
|
|
rc.bottom = static_cast<XYPOSITION>((maxLine - TopLineOfMain() + 1) * vs.lineHeight);
|
|
|
|
return rc;
|
|
}
|
|
|
|
void Editor::InvalidateRange(int start, int end) {
|
|
RedrawRect(RectangleFromRange(Range(start, end)));
|
|
}
|
|
|
|
int Editor::CurrentPosition() const {
|
|
return sel.MainCaret();
|
|
}
|
|
|
|
bool Editor::SelectionEmpty() const {
|
|
return sel.Empty();
|
|
}
|
|
|
|
SelectionPosition Editor::SelectionStart() {
|
|
return sel.RangeMain().Start();
|
|
}
|
|
|
|
SelectionPosition Editor::SelectionEnd() {
|
|
return sel.RangeMain().End();
|
|
}
|
|
|
|
void Editor::SetRectangularRange() {
|
|
if (sel.IsRectangular()) {
|
|
int xAnchor = XFromPosition(sel.Rectangular().anchor);
|
|
int xCaret = XFromPosition(sel.Rectangular().caret);
|
|
if (sel.selType == Selection::selThin) {
|
|
xCaret = xAnchor;
|
|
}
|
|
int lineAnchorRect = pdoc->LineFromPosition(sel.Rectangular().anchor.Position());
|
|
int lineCaret = pdoc->LineFromPosition(sel.Rectangular().caret.Position());
|
|
int increment = (lineCaret > lineAnchorRect) ? 1 : -1;
|
|
for (int line=lineAnchorRect; line != lineCaret+increment; line += increment) {
|
|
SelectionRange range(SPositionFromLineX(line, xCaret), SPositionFromLineX(line, xAnchor));
|
|
if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) == 0)
|
|
range.ClearVirtualSpace();
|
|
if (line == lineAnchorRect)
|
|
sel.SetSelection(range);
|
|
else
|
|
sel.AddSelectionWithoutTrim(range);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Editor::ThinRectangularRange() {
|
|
if (sel.IsRectangular()) {
|
|
sel.selType = Selection::selThin;
|
|
if (sel.Rectangular().caret < sel.Rectangular().anchor) {
|
|
sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).caret, sel.Range(0).anchor);
|
|
} else {
|
|
sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).anchor, sel.Range(0).caret);
|
|
}
|
|
SetRectangularRange();
|
|
}
|
|
}
|
|
|
|
void Editor::InvalidateSelection(SelectionRange newMain, bool invalidateWholeSelection) {
|
|
if (sel.Count() > 1 || !(sel.RangeMain().anchor == newMain.anchor) || sel.IsRectangular()) {
|
|
invalidateWholeSelection = true;
|
|
}
|
|
int firstAffected = Platform::Minimum(sel.RangeMain().Start().Position(), newMain.Start().Position());
|
|
// +1 for lastAffected ensures caret repainted
|
|
int lastAffected = Platform::Maximum(newMain.caret.Position()+1, newMain.anchor.Position());
|
|
lastAffected = Platform::Maximum(lastAffected, sel.RangeMain().End().Position());
|
|
if (invalidateWholeSelection) {
|
|
for (size_t r=0; r<sel.Count(); r++) {
|
|
firstAffected = Platform::Minimum(firstAffected, sel.Range(r).caret.Position());
|
|
firstAffected = Platform::Minimum(firstAffected, sel.Range(r).anchor.Position());
|
|
lastAffected = Platform::Maximum(lastAffected, sel.Range(r).caret.Position()+1);
|
|
lastAffected = Platform::Maximum(lastAffected, sel.Range(r).anchor.Position());
|
|
}
|
|
}
|
|
ContainerNeedsUpdate(SC_UPDATE_SELECTION);
|
|
InvalidateRange(firstAffected, lastAffected);
|
|
}
|
|
|
|
void Editor::SetSelection(SelectionPosition currentPos_, SelectionPosition anchor_) {
|
|
currentPos_ = ClampPositionIntoDocument(currentPos_);
|
|
anchor_ = ClampPositionIntoDocument(anchor_);
|
|
int currentLine = pdoc->LineFromPosition(currentPos_.Position());
|
|
/* For Line selection - ensure the anchor and caret are always
|
|
at the beginning and end of the region lines. */
|
|
if (sel.selType == Selection::selLines) {
|
|
if (currentPos_ > anchor_) {
|
|
anchor_ = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(anchor_.Position())));
|
|
currentPos_ = SelectionPosition(pdoc->LineEnd(pdoc->LineFromPosition(currentPos_.Position())));
|
|
} else {
|
|
currentPos_ = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(currentPos_.Position())));
|
|
anchor_ = SelectionPosition(pdoc->LineEnd(pdoc->LineFromPosition(anchor_.Position())));
|
|
}
|
|
}
|
|
SelectionRange rangeNew(currentPos_, anchor_);
|
|
if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
|
|
InvalidateSelection(rangeNew);
|
|
}
|
|
sel.RangeMain() = rangeNew;
|
|
SetRectangularRange();
|
|
ClaimSelection();
|
|
|
|
if (highlightDelimiter.NeedsDrawing(currentLine)) {
|
|
RedrawSelMargin();
|
|
}
|
|
QueueIdleWork(WorkNeeded::workUpdateUI);
|
|
}
|
|
|
|
void Editor::SetSelection(int currentPos_, int anchor_) {
|
|
SetSelection(SelectionPosition(currentPos_), SelectionPosition(anchor_));
|
|
}
|
|
|
|
// Just move the caret on the main selection
|
|
void Editor::SetSelection(SelectionPosition currentPos_) {
|
|
currentPos_ = ClampPositionIntoDocument(currentPos_);
|
|
int currentLine = pdoc->LineFromPosition(currentPos_.Position());
|
|
if (sel.Count() > 1 || !(sel.RangeMain().caret == currentPos_)) {
|
|
InvalidateSelection(SelectionRange(currentPos_));
|
|
}
|
|
if (sel.IsRectangular()) {
|
|
sel.Rectangular() =
|
|
SelectionRange(SelectionPosition(currentPos_), sel.Rectangular().anchor);
|
|
SetRectangularRange();
|
|
} else {
|
|
sel.RangeMain() =
|
|
SelectionRange(SelectionPosition(currentPos_), sel.RangeMain().anchor);
|
|
}
|
|
ClaimSelection();
|
|
|
|
if (highlightDelimiter.NeedsDrawing(currentLine)) {
|
|
RedrawSelMargin();
|
|
}
|
|
QueueIdleWork(WorkNeeded::workUpdateUI);
|
|
}
|
|
|
|
void Editor::SetSelection(int currentPos_) {
|
|
SetSelection(SelectionPosition(currentPos_));
|
|
}
|
|
|
|
void Editor::SetEmptySelection(SelectionPosition currentPos_) {
|
|
int currentLine = pdoc->LineFromPosition(currentPos_.Position());
|
|
SelectionRange rangeNew(ClampPositionIntoDocument(currentPos_));
|
|
if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
|
|
InvalidateSelection(rangeNew);
|
|
}
|
|
sel.Clear();
|
|
sel.RangeMain() = rangeNew;
|
|
SetRectangularRange();
|
|
ClaimSelection();
|
|
|
|
if (highlightDelimiter.NeedsDrawing(currentLine)) {
|
|
RedrawSelMargin();
|
|
}
|
|
QueueIdleWork(WorkNeeded::workUpdateUI);
|
|
}
|
|
|
|
void Editor::SetEmptySelection(int currentPos_) {
|
|
SetEmptySelection(SelectionPosition(currentPos_));
|
|
}
|
|
|
|
bool Editor::RangeContainsProtected(int start, int end) const {
|
|
if (vs.ProtectionActive()) {
|
|
if (start > end) {
|
|
int t = start;
|
|
start = end;
|
|
end = t;
|
|
}
|
|
int mask = pdoc->stylingBitsMask;
|
|
for (int pos = start; pos < end; pos++) {
|
|
if (vs.styles[pdoc->StyleAt(pos) & mask].IsProtected())
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Editor::SelectionContainsProtected() {
|
|
for (size_t r=0; r<sel.Count(); r++) {
|
|
if (RangeContainsProtected(sel.Range(r).Start().Position(),
|
|
sel.Range(r).End().Position())) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Asks document to find a good position and then moves out of any invisible positions.
|
|
*/
|
|
int Editor::MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd) const {
|
|
return MovePositionOutsideChar(SelectionPosition(pos), moveDir, checkLineEnd).Position();
|
|
}
|
|
|
|
SelectionPosition Editor::MovePositionOutsideChar(SelectionPosition pos, int moveDir, bool checkLineEnd) const {
|
|
int posMoved = pdoc->MovePositionOutsideChar(pos.Position(), moveDir, checkLineEnd);
|
|
if (posMoved != pos.Position())
|
|
pos.SetPosition(posMoved);
|
|
if (vs.ProtectionActive()) {
|
|
int mask = pdoc->stylingBitsMask;
|
|
if (moveDir > 0) {
|
|
if ((pos.Position() > 0) && vs.styles[pdoc->StyleAt(pos.Position() - 1) & mask].IsProtected()) {
|
|
while ((pos.Position() < pdoc->Length()) &&
|
|
(vs.styles[pdoc->StyleAt(pos.Position()) & mask].IsProtected()))
|
|
pos.Add(1);
|
|
}
|
|
} else if (moveDir < 0) {
|
|
if (vs.styles[pdoc->StyleAt(pos.Position()) & mask].IsProtected()) {
|
|
while ((pos.Position() > 0) &&
|
|
(vs.styles[pdoc->StyleAt(pos.Position() - 1) & mask].IsProtected()))
|
|
pos.Add(-1);
|
|
}
|
|
}
|
|
}
|
|
return pos;
|
|
}
|
|
|
|
int Editor::MovePositionTo(SelectionPosition newPos, Selection::selTypes selt, bool ensureVisible) {
|
|
bool simpleCaret = (sel.Count() == 1) && sel.Empty();
|
|
SelectionPosition spCaret = sel.Last();
|
|
|
|
int delta = newPos.Position() - sel.MainCaret();
|
|
newPos = ClampPositionIntoDocument(newPos);
|
|
newPos = MovePositionOutsideChar(newPos, delta);
|
|
if (!multipleSelection && sel.IsRectangular() && (selt == Selection::selStream)) {
|
|
// Can't turn into multiple selection so clear additional selections
|
|
InvalidateSelection(SelectionRange(newPos), true);
|
|
SelectionRange rangeMain = sel.RangeMain();
|
|
sel.SetSelection(rangeMain);
|
|
}
|
|
if (!sel.IsRectangular() && (selt == Selection::selRectangle)) {
|
|
// Switching to rectangular
|
|
InvalidateSelection(sel.RangeMain(), false);
|
|
SelectionRange rangeMain = sel.RangeMain();
|
|
sel.Clear();
|
|
sel.Rectangular() = rangeMain;
|
|
}
|
|
if (selt != Selection::noSel) {
|
|
sel.selType = selt;
|
|
}
|
|
if (selt != Selection::noSel || sel.MoveExtends()) {
|
|
SetSelection(newPos);
|
|
} else {
|
|
SetEmptySelection(newPos);
|
|
}
|
|
ShowCaretAtCurrentPosition();
|
|
|
|
int currentLine = pdoc->LineFromPosition(newPos.Position());
|
|
if (ensureVisible) {
|
|
// In case in need of wrapping to ensure DisplayFromDoc works.
|
|
if (currentLine >= wrapPending.start)
|
|
WrapLines(wsAll);
|
|
XYScrollPosition newXY = XYScrollToMakeVisible(
|
|
SelectionRange(posDrag.IsValid() ? posDrag : sel.RangeMain().caret), xysDefault);
|
|
if (simpleCaret && (newXY.xOffset == xOffset)) {
|
|
// simple vertical scroll then invalidate
|
|
ScrollTo(newXY.topLine);
|
|
InvalidateSelection(SelectionRange(spCaret), true);
|
|
} else {
|
|
SetXYScroll(newXY);
|
|
}
|
|
}
|
|
|
|
if (highlightDelimiter.NeedsDrawing(currentLine)) {
|
|
RedrawSelMargin();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int Editor::MovePositionTo(int newPos, Selection::selTypes selt, bool ensureVisible) {
|
|
return MovePositionTo(SelectionPosition(newPos), selt, ensureVisible);
|
|
}
|
|
|
|
SelectionPosition Editor::MovePositionSoVisible(SelectionPosition pos, int moveDir) {
|
|
pos = ClampPositionIntoDocument(pos);
|
|
pos = MovePositionOutsideChar(pos, moveDir);
|
|
int lineDoc = pdoc->LineFromPosition(pos.Position());
|
|
if (cs.GetVisible(lineDoc)) {
|
|
return pos;
|
|
} else {
|
|
int lineDisplay = cs.DisplayFromDoc(lineDoc);
|
|
if (moveDir > 0) {
|
|
// lineDisplay is already line before fold as lines in fold use display line of line after fold
|
|
lineDisplay = Platform::Clamp(lineDisplay, 0, cs.LinesDisplayed());
|
|
return SelectionPosition(pdoc->LineStart(cs.DocFromDisplay(lineDisplay)));
|
|
} else {
|
|
lineDisplay = Platform::Clamp(lineDisplay - 1, 0, cs.LinesDisplayed());
|
|
return SelectionPosition(pdoc->LineEnd(cs.DocFromDisplay(lineDisplay)));
|
|
}
|
|
}
|
|
}
|
|
|
|
SelectionPosition Editor::MovePositionSoVisible(int pos, int moveDir) {
|
|
return MovePositionSoVisible(SelectionPosition(pos), moveDir);
|
|
}
|
|
|
|
Point Editor::PointMainCaret() {
|
|
return LocationFromPosition(sel.Range(sel.Main()).caret);
|
|
}
|
|
|
|
/**
|
|
* Choose the x position that the caret will try to stick to
|
|
* as it moves up and down.
|
|
*/
|
|
void Editor::SetLastXChosen() {
|
|
Point pt = PointMainCaret();
|
|
lastXChosen = static_cast<int>(pt.x) + xOffset;
|
|
}
|
|
|
|
void Editor::ScrollTo(int line, bool moveThumb) {
|
|
int topLineNew = Platform::Clamp(line, 0, MaxScrollPos());
|
|
if (topLineNew != topLine) {
|
|
// Try to optimise small scrolls
|
|
#ifndef UNDER_CE
|
|
int linesToMove = topLine - topLineNew;
|
|
bool performBlit = (abs(linesToMove) <= 10) && (paintState == notPainting);
|
|
willRedrawAll = !performBlit;
|
|
#endif
|
|
SetTopLine(topLineNew);
|
|
// Optimize by styling the view as this will invalidate any needed area
|
|
// which could abort the initial paint if discovered later.
|
|
StyleToPositionInView(PositionAfterArea(GetClientRectangle()));
|
|
#ifndef UNDER_CE
|
|
// Perform redraw rather than scroll if many lines would be redrawn anyway.
|
|
if (performBlit) {
|
|
ScrollText(linesToMove);
|
|
} else {
|
|
Redraw();
|
|
}
|
|
willRedrawAll = false;
|
|
#else
|
|
Redraw();
|
|
#endif
|
|
if (moveThumb) {
|
|
SetVerticalScrollPos();
|
|
}
|
|
}
|
|
}
|
|
|
|
void Editor::ScrollText(int /* linesToMove */) {
|
|
//Platform::DebugPrintf("Editor::ScrollText %d\n", linesToMove);
|
|
Redraw();
|
|
}
|
|
|
|
void Editor::HorizontalScrollTo(int xPos) {
|
|
//Platform::DebugPrintf("HorizontalScroll %d\n", xPos);
|
|
if (xPos < 0)
|
|
xPos = 0;
|
|
if (!Wrapping() && (xOffset != xPos)) {
|
|
xOffset = xPos;
|
|
ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
|
|
SetHorizontalScrollPos();
|
|
RedrawRect(GetClientRectangle());
|
|
}
|
|
}
|
|
|
|
void Editor::VerticalCentreCaret() {
|
|
int lineDoc = pdoc->LineFromPosition(sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret());
|
|
int lineDisplay = cs.DisplayFromDoc(lineDoc);
|
|
int newTop = lineDisplay - (LinesOnScreen() / 2);
|
|
if (topLine != newTop) {
|
|
SetTopLine(newTop > 0 ? newTop : 0);
|
|
RedrawRect(GetClientRectangle());
|
|
}
|
|
}
|
|
|
|
// Avoid 64 bit compiler warnings.
|
|
// Scintilla does not support text buffers larger than 2**31
|
|
static int istrlen(const char *s) {
|
|
return static_cast<int>(s ? strlen(s) : 0);
|
|
}
|
|
|
|
void Editor::MoveSelectedLines(int lineDelta) {
|
|
|
|
// if selection doesn't start at the beginning of the line, set the new start
|
|
int selectionStart = SelectionStart().Position();
|
|
int startLine = pdoc->LineFromPosition(selectionStart);
|
|
int beginningOfStartLine = pdoc->LineStart(startLine);
|
|
selectionStart = beginningOfStartLine;
|
|
|
|
// if selection doesn't end at the beginning of a line greater than that of the start,
|
|
// then set it at the beginning of the next one
|
|
int selectionEnd = SelectionEnd().Position();
|
|
int endLine = pdoc->LineFromPosition(selectionEnd);
|
|
int beginningOfEndLine = pdoc->LineStart(endLine);
|
|
bool appendEol = false;
|
|
if (selectionEnd > beginningOfEndLine
|
|
|| selectionStart == selectionEnd) {
|
|
selectionEnd = pdoc->LineStart(endLine + 1);
|
|
appendEol = (selectionEnd == pdoc->Length() && pdoc->LineFromPosition(selectionEnd) == endLine);
|
|
}
|
|
|
|
// if there's nowhere for the selection to move
|
|
// (i.e. at the beginning going up or at the end going down),
|
|
// stop it right there!
|
|
if ((selectionStart == 0 && lineDelta < 0)
|
|
|| (selectionEnd == pdoc->Length() && lineDelta > 0)
|
|
|| selectionStart == selectionEnd) {
|
|
return;
|
|
}
|
|
|
|
UndoGroup ug(pdoc);
|
|
|
|
if (lineDelta > 0 && selectionEnd == pdoc->LineStart(pdoc->LinesTotal() - 1)) {
|
|
SetSelection(pdoc->MovePositionOutsideChar(selectionEnd - 1, -1), selectionEnd);
|
|
ClearSelection();
|
|
selectionEnd = CurrentPosition();
|
|
}
|
|
SetSelection(selectionStart, selectionEnd);
|
|
|
|
SelectionText selectedText;
|
|
CopySelectionRange(&selectedText);
|
|
|
|
int selectionLength = SelectionRange(selectionStart, selectionEnd).Length();
|
|
Point currentLocation = LocationFromPosition(CurrentPosition());
|
|
int currentLine = LineFromLocation(currentLocation);
|
|
|
|
if (appendEol)
|
|
SetSelection(pdoc->MovePositionOutsideChar(selectionStart - 1, -1), selectionEnd);
|
|
ClearSelection();
|
|
|
|
const char *eol = StringFromEOLMode(pdoc->eolMode);
|
|
if (currentLine + lineDelta >= pdoc->LinesTotal())
|
|
pdoc->InsertString(pdoc->Length(), eol, istrlen(eol));
|
|
GoToLine(currentLine + lineDelta);
|
|
|
|
selectionLength = pdoc->InsertString(CurrentPosition(), selectedText.Data(), selectionLength);
|
|
if (appendEol) {
|
|
const int lengthInserted = pdoc->InsertString(CurrentPosition() + selectionLength, eol, istrlen(eol));
|
|
selectionLength += lengthInserted;
|
|
}
|
|
SetSelection(CurrentPosition(), CurrentPosition() + selectionLength);
|
|
}
|
|
|
|
void Editor::MoveSelectedLinesUp() {
|
|
MoveSelectedLines(-1);
|
|
}
|
|
|
|
void Editor::MoveSelectedLinesDown() {
|
|
MoveSelectedLines(1);
|
|
}
|
|
|
|
void Editor::MoveCaretInsideView(bool ensureVisible) {
|
|
PRectangle rcClient = GetTextRectangle();
|
|
Point pt = PointMainCaret();
|
|
if (pt.y < rcClient.top) {
|
|
MovePositionTo(SPositionFromLocation(
|
|
Point::FromInts(lastXChosen - xOffset, static_cast<int>(rcClient.top)),
|
|
false, false, UserVirtualSpace()),
|
|
Selection::noSel, ensureVisible);
|
|
} else if ((pt.y + vs.lineHeight - 1) > rcClient.bottom) {
|
|
int yOfLastLineFullyDisplayed = static_cast<int>(rcClient.top) + (LinesOnScreen() - 1) * vs.lineHeight;
|
|
MovePositionTo(SPositionFromLocation(
|
|
Point::FromInts(lastXChosen - xOffset, static_cast<int>(rcClient.top) + yOfLastLineFullyDisplayed),
|
|
false, false, UserVirtualSpace()),
|
|
Selection::noSel, ensureVisible);
|
|
}
|
|
}
|
|
|
|
int Editor::DisplayFromPosition(int pos) {
|
|
int lineDoc = pdoc->LineFromPosition(pos);
|
|
int lineDisplay = cs.DisplayFromDoc(lineDoc);
|
|
AutoSurface surface(this);
|
|
AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
|
|
if (surface && ll) {
|
|
LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
|
|
unsigned int posLineStart = pdoc->LineStart(lineDoc);
|
|
int posInLine = pos - posLineStart;
|
|
lineDisplay--; // To make up for first increment ahead.
|
|
for (int subLine = 0; subLine < ll->lines; subLine++) {
|
|
if (posInLine >= ll->LineStart(subLine)) {
|
|
lineDisplay++;
|
|
}
|
|
}
|
|
}
|
|
return lineDisplay;
|
|
}
|
|
|
|
/**
|
|
* Ensure the caret is reasonably visible in context.
|
|
*
|
|
Caret policy in SciTE
|
|
|
|
If slop is set, we can define a slop value.
|
|
This value defines an unwanted zone (UZ) where the caret is... unwanted.
|
|
This zone is defined as a number of pixels near the vertical margins,
|
|
and as a number of lines near the horizontal margins.
|
|
By keeping the caret away from the edges, it is seen within its context,
|
|
so it is likely that the identifier that the caret is on can be completely seen,
|
|
and that the current line is seen with some of the lines following it which are
|
|
often dependent on that line.
|
|
|
|
If strict is set, the policy is enforced... strictly.
|
|
The caret is centred on the display if slop is not set,
|
|
and cannot go in the UZ if slop is set.
|
|
|
|
If jumps is set, the display is moved more energetically
|
|
so the caret can move in the same direction longer before the policy is applied again.
|
|
'3UZ' notation is used to indicate three time the size of the UZ as a distance to the margin.
|
|
|
|
If even is not set, instead of having symmetrical UZs,
|
|
the left and bottom UZs are extended up to right and top UZs respectively.
|
|
This way, we favour the displaying of useful information: the beginning of lines,
|
|
where most code reside, and the lines after the caret, eg. the body of a function.
|
|
|
|
| | | | |
|
|
slop | strict | jumps | even | Caret can go to the margin | When reaching limit (caret going out of
|
|
| | | | | visibility or going into the UZ) display is...
|
|
-----+--------+-------+------+--------------------------------------------+--------------------------------------------------------------
|
|
0 | 0 | 0 | 0 | Yes | moved to put caret on top/on right
|
|
0 | 0 | 0 | 1 | Yes | moved by one position
|
|
0 | 0 | 1 | 0 | Yes | moved to put caret on top/on right
|
|
0 | 0 | 1 | 1 | Yes | centred on the caret
|
|
0 | 1 | - | 0 | Caret is always on top/on right of display | -
|
|
0 | 1 | - | 1 | No, caret is always centred | -
|
|
1 | 0 | 0 | 0 | Yes | moved to put caret out of the asymmetrical UZ
|
|
1 | 0 | 0 | 1 | Yes | moved to put caret out of the UZ
|
|
1 | 0 | 1 | 0 | Yes | moved to put caret at 3UZ of the top or right margin
|
|
1 | 0 | 1 | 1 | Yes | moved to put caret at 3UZ of the margin
|
|
1 | 1 | - | 0 | Caret is always at UZ of top/right margin | -
|
|
1 | 1 | 0 | 1 | No, kept out of UZ | moved by one position
|
|
1 | 1 | 1 | 1 | No, kept out of UZ | moved to put caret at 3UZ of the margin
|
|
*/
|
|
|
|
Editor::XYScrollPosition Editor::XYScrollToMakeVisible(const SelectionRange &range, const XYScrollOptions options) {
|
|
PRectangle rcClient = GetTextRectangle();
|
|
Point pt = LocationFromPosition(range.caret);
|
|
Point ptAnchor = LocationFromPosition(range.anchor);
|
|
const Point ptOrigin = GetVisibleOriginInMain();
|
|
pt.x += ptOrigin.x;
|
|
pt.y += ptOrigin.y;
|
|
ptAnchor.x += ptOrigin.x;
|
|
ptAnchor.y += ptOrigin.y;
|
|
const Point ptBottomCaret(pt.x, pt.y + vs.lineHeight - 1);
|
|
|
|
XYScrollPosition newXY(xOffset, topLine);
|
|
if (rcClient.Empty()) {
|
|
return newXY;
|
|
}
|
|
|
|
// Vertical positioning
|
|
if ((options & xysVertical) && (pt.y < rcClient.top || ptBottomCaret.y >= rcClient.bottom || (caretYPolicy & CARET_STRICT) != 0)) {
|
|
const int lineCaret = DisplayFromPosition(range.caret.Position());
|
|
const int linesOnScreen = LinesOnScreen();
|
|
const int halfScreen = Platform::Maximum(linesOnScreen - 1, 2) / 2;
|
|
const bool bSlop = (caretYPolicy & CARET_SLOP) != 0;
|
|
const bool bStrict = (caretYPolicy & CARET_STRICT) != 0;
|
|
const bool bJump = (caretYPolicy & CARET_JUMPS) != 0;
|
|
const bool bEven = (caretYPolicy & CARET_EVEN) != 0;
|
|
|
|
// It should be possible to scroll the window to show the caret,
|
|
// but this fails to remove the caret on GTK+
|
|
if (bSlop) { // A margin is defined
|
|
int yMoveT, yMoveB;
|
|
if (bStrict) {
|
|
int yMarginT, yMarginB;
|
|
if (!(options & xysUseMargin)) {
|
|
// In drag mode, avoid moves
|
|
// otherwise, a double click will select several lines.
|
|
yMarginT = yMarginB = 0;
|
|
} else {
|
|
// yMarginT must equal to caretYSlop, with a minimum of 1 and
|
|
// a maximum of slightly less than half the heigth of the text area.
|
|
yMarginT = Platform::Clamp(caretYSlop, 1, halfScreen);
|
|
if (bEven) {
|
|
yMarginB = yMarginT;
|
|
} else {
|
|
yMarginB = linesOnScreen - yMarginT - 1;
|
|
}
|
|
}
|
|
yMoveT = yMarginT;
|
|
if (bEven) {
|
|
if (bJump) {
|
|
yMoveT = Platform::Clamp(caretYSlop * 3, 1, halfScreen);
|
|
}
|
|
yMoveB = yMoveT;
|
|
} else {
|
|
yMoveB = linesOnScreen - yMoveT - 1;
|
|
}
|
|
if (lineCaret < topLine + yMarginT) {
|
|
// Caret goes too high
|
|
newXY.topLine = lineCaret - yMoveT;
|
|
} else if (lineCaret > topLine + linesOnScreen - 1 - yMarginB) {
|
|
// Caret goes too low
|
|
newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
|
|
}
|
|
} else { // Not strict
|
|
yMoveT = bJump ? caretYSlop * 3 : caretYSlop;
|
|
yMoveT = Platform::Clamp(yMoveT, 1, halfScreen);
|
|
if (bEven) {
|
|
yMoveB = yMoveT;
|
|
} else {
|
|
yMoveB = linesOnScreen - yMoveT - 1;
|
|
}
|
|
if (lineCaret < topLine) {
|
|
// Caret goes too high
|
|
newXY.topLine = lineCaret - yMoveT;
|
|
} else if (lineCaret > topLine + linesOnScreen - 1) {
|
|
// Caret goes too low
|
|
newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
|
|
}
|
|
}
|
|
} else { // No slop
|
|
if (!bStrict && !bJump) {
|
|
// Minimal move
|
|
if (lineCaret < topLine) {
|
|
// Caret goes too high
|
|
newXY.topLine = lineCaret;
|
|
} else if (lineCaret > topLine + linesOnScreen - 1) {
|
|
// Caret goes too low
|
|
if (bEven) {
|
|
newXY.topLine = lineCaret - linesOnScreen + 1;
|
|
} else {
|
|
newXY.topLine = lineCaret;
|
|
}
|
|
}
|
|
} else { // Strict or going out of display
|
|
if (bEven) {
|
|
// Always center caret
|
|
newXY.topLine = lineCaret - halfScreen;
|
|
} else {
|
|
// Always put caret on top of display
|
|
newXY.topLine = lineCaret;
|
|
}
|
|
}
|
|
}
|
|
if (!(range.caret == range.anchor)) {
|
|
const int lineAnchor = DisplayFromPosition(range.anchor.Position());
|
|
if (lineAnchor < lineCaret) {
|
|
// Shift up to show anchor or as much of range as possible
|
|
newXY.topLine = std::min(newXY.topLine, lineAnchor);
|
|
newXY.topLine = std::max(newXY.topLine, lineCaret - LinesOnScreen());
|
|
} else {
|
|
// Shift down to show anchor or as much of range as possible
|
|
newXY.topLine = std::max(newXY.topLine, lineAnchor - LinesOnScreen());
|
|
newXY.topLine = std::min(newXY.topLine, lineCaret);
|
|
}
|
|
}
|
|
newXY.topLine = Platform::Clamp(newXY.topLine, 0, MaxScrollPos());
|
|
}
|
|
|
|
// Horizontal positioning
|
|
if ((options & xysHorizontal) && !Wrapping()) {
|
|
const int halfScreen = Platform::Maximum(static_cast<int>(rcClient.Width()) - 4, 4) / 2;
|
|
const bool bSlop = (caretXPolicy & CARET_SLOP) != 0;
|
|
const bool bStrict = (caretXPolicy & CARET_STRICT) != 0;
|
|
const bool bJump = (caretXPolicy & CARET_JUMPS) != 0;
|
|
const bool bEven = (caretXPolicy & CARET_EVEN) != 0;
|
|
|
|
if (bSlop) { // A margin is defined
|
|
int xMoveL, xMoveR;
|
|
if (bStrict) {
|
|
int xMarginL, xMarginR;
|
|
if (!(options & xysUseMargin)) {
|
|
// In drag mode, avoid moves unless very near of the margin
|
|
// otherwise, a simple click will select text.
|
|
xMarginL = xMarginR = 2;
|
|
} else {
|
|
// xMargin must equal to caretXSlop, with a minimum of 2 and
|
|
// a maximum of slightly less than half the width of the text area.
|
|
xMarginR = Platform::Clamp(caretXSlop, 2, halfScreen);
|
|
if (bEven) {
|
|
xMarginL = xMarginR;
|
|
} else {
|
|
xMarginL = static_cast<int>(rcClient.Width()) - xMarginR - 4;
|
|
}
|
|
}
|
|
if (bJump && bEven) {
|
|
// Jump is used only in even mode
|
|
xMoveL = xMoveR = Platform::Clamp(caretXSlop * 3, 1, halfScreen);
|
|
} else {
|
|
xMoveL = xMoveR = 0; // Not used, avoid a warning
|
|
}
|
|
if (pt.x < rcClient.left + xMarginL) {
|
|
// Caret is on the left of the display
|
|
if (bJump && bEven) {
|
|
newXY.xOffset -= xMoveL;
|
|
} else {
|
|
// Move just enough to allow to display the caret
|
|
newXY.xOffset -= static_cast<int>((rcClient.left + xMarginL) - pt.x);
|
|
}
|
|
} else if (pt.x >= rcClient.right - xMarginR) {
|
|
// Caret is on the right of the display
|
|
if (bJump && bEven) {
|
|
newXY.xOffset += xMoveR;
|
|
} else {
|
|
// Move just enough to allow to display the caret
|
|
newXY.xOffset += static_cast<int>(pt.x - (rcClient.right - xMarginR) + 1);
|
|
}
|
|
}
|
|
} else { // Not strict
|
|
xMoveR = bJump ? caretXSlop * 3 : caretXSlop;
|
|
xMoveR = Platform::Clamp(xMoveR, 1, halfScreen);
|
|
if (bEven) {
|
|
xMoveL = xMoveR;
|
|
} else {
|
|
xMoveL = static_cast<int>(rcClient.Width()) - xMoveR - 4;
|
|
}
|
|
if (pt.x < rcClient.left) {
|
|
// Caret is on the left of the display
|
|
newXY.xOffset -= xMoveL;
|
|
} else if (pt.x >= rcClient.right) {
|
|
// Caret is on the right of the display
|
|
newXY.xOffset += xMoveR;
|
|
}
|
|
}
|
|
} else { // No slop
|
|
if (bStrict ||
|
|
(bJump && (pt.x < rcClient.left || pt.x >= rcClient.right))) {
|
|
// Strict or going out of display
|
|
if (bEven) {
|
|
// Center caret
|
|
newXY.xOffset += static_cast<int>(pt.x - rcClient.left - halfScreen);
|
|
} else {
|
|
// Put caret on right
|
|
newXY.xOffset += static_cast<int>(pt.x - rcClient.right + 1);
|
|
}
|
|
} else {
|
|
// Move just enough to allow to display the caret
|
|
if (pt.x < rcClient.left) {
|
|
// Caret is on the left of the display
|
|
if (bEven) {
|
|
newXY.xOffset -= static_cast<int>(rcClient.left - pt.x);
|
|
} else {
|
|
newXY.xOffset += static_cast<int>(pt.x - rcClient.right) + 1;
|
|
}
|
|
} else if (pt.x >= rcClient.right) {
|
|
// Caret is on the right of the display
|
|
newXY.xOffset += static_cast<int>(pt.x - rcClient.right) + 1;
|
|
}
|
|
}
|
|
}
|
|
// In case of a jump (find result) largely out of display, adjust the offset to display the caret
|
|
if (pt.x + xOffset < rcClient.left + newXY.xOffset) {
|
|
newXY.xOffset = static_cast<int>(pt.x + xOffset - rcClient.left) - 2;
|
|
} else if (pt.x + xOffset >= rcClient.right + newXY.xOffset) {
|
|
newXY.xOffset = static_cast<int>(pt.x + xOffset - rcClient.right) + 2;
|
|
if (vs.caretStyle == CARETSTYLE_BLOCK) {
|
|
// Ensure we can see a good portion of the block caret
|
|
newXY.xOffset += static_cast<int>(vs.aveCharWidth);
|
|
}
|
|
}
|
|
if (!(range.caret == range.anchor)) {
|
|
if (ptAnchor.x < pt.x) {
|
|
// Shift to left to show anchor or as much of range as possible
|
|
int maxOffset = static_cast<int>(ptAnchor.x + xOffset - rcClient.left) - 1;
|
|
int minOffset = static_cast<int>(pt.x + xOffset - rcClient.right) + 1;
|
|
newXY.xOffset = std::min(newXY.xOffset, maxOffset);
|
|
newXY.xOffset = std::max(newXY.xOffset, minOffset);
|
|
} else {
|
|
// Shift to right to show anchor or as much of range as possible
|
|
int minOffset = static_cast<int>(ptAnchor.x + xOffset - rcClient.right) + 1;
|
|
int maxOffset = static_cast<int>(pt.x + xOffset - rcClient.left) - 1;
|
|
newXY.xOffset = std::max(newXY.xOffset, minOffset);
|
|
newXY.xOffset = std::min(newXY.xOffset, maxOffset);
|
|
}
|
|
}
|
|
if (newXY.xOffset < 0) {
|
|
newXY.xOffset = 0;
|
|
}
|
|
}
|
|
|
|
return newXY;
|
|
}
|
|
|
|
void Editor::SetXYScroll(XYScrollPosition newXY) {
|
|
if ((newXY.topLine != topLine) || (newXY.xOffset != xOffset)) {
|
|
if (newXY.topLine != topLine) {
|
|
SetTopLine(newXY.topLine);
|
|
SetVerticalScrollPos();
|
|
}
|
|
if (newXY.xOffset != xOffset) {
|
|
xOffset = newXY.xOffset;
|
|
ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
|
|
if (newXY.xOffset > 0) {
|
|
PRectangle rcText = GetTextRectangle();
|
|
if (horizontalScrollBarVisible &&
|
|
rcText.Width() + xOffset > scrollWidth) {
|
|
scrollWidth = xOffset + static_cast<int>(rcText.Width());
|
|
SetScrollBars();
|
|
}
|
|
}
|
|
SetHorizontalScrollPos();
|
|
}
|
|
Redraw();
|
|
UpdateSystemCaret();
|
|
}
|
|
}
|
|
|
|
void Editor::ScrollRange(SelectionRange range) {
|
|
SetXYScroll(XYScrollToMakeVisible(range, xysDefault));
|
|
}
|
|
|
|
void Editor::EnsureCaretVisible(bool useMargin, bool vert, bool horiz) {
|
|
SetXYScroll(XYScrollToMakeVisible(SelectionRange(posDrag.IsValid() ? posDrag : sel.RangeMain().caret),
|
|
static_cast<XYScrollOptions>((useMargin?xysUseMargin:0)|(vert?xysVertical:0)|(horiz?xysHorizontal:0))));
|
|
}
|
|
|
|
void Editor::ShowCaretAtCurrentPosition() {
|
|
if (hasFocus) {
|
|
caret.active = true;
|
|
caret.on = true;
|
|
SetTicking(true);
|
|
} else {
|
|
caret.active = false;
|
|
caret.on = false;
|
|
}
|
|
InvalidateCaret();
|
|
}
|
|
|
|
void Editor::DropCaret() {
|
|
caret.active = false;
|
|
InvalidateCaret();
|
|
}
|
|
|
|
void Editor::CaretSetPeriod(int period) {
|
|
if (caret.period != period) {
|
|
caret.period = period;
|
|
caret.on = true;
|
|
InvalidateCaret();
|
|
}
|
|
}
|
|
|
|
void Editor::InvalidateCaret() {
|
|
if (posDrag.IsValid()) {
|
|
InvalidateRange(posDrag.Position(), posDrag.Position() + 1);
|
|
} else {
|
|
for (size_t r=0; r<sel.Count(); r++) {
|
|
InvalidateRange(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1);
|
|
}
|
|
}
|
|
UpdateSystemCaret();
|
|
}
|
|
|
|
void Editor::UpdateSystemCaret() {
|
|
}
|
|
|
|
bool Editor::Wrapping() const {
|
|
return vs.wrapState != eWrapNone;
|
|
}
|
|
|
|
void Editor::NeedWrapping(int docLineStart, int docLineEnd) {
|
|
//Platform::DebugPrintf("\nNeedWrapping: %0d..%0d\n", docLineStart, docLineEnd);
|
|
if (wrapPending.AddRange(docLineStart, docLineEnd)) {
|
|
llc.Invalidate(LineLayout::llPositions);
|
|
}
|
|
// Wrap lines during idle.
|
|
if (Wrapping() && wrapPending.NeedsWrap()) {
|
|
SetIdle(true);
|
|
}
|
|
}
|
|
|
|
bool Editor::WrapOneLine(Surface *surface, int lineToWrap) {
|
|
AutoLineLayout ll(llc, RetrieveLineLayout(lineToWrap));
|
|
int linesWrapped = 1;
|
|
if (ll) {
|
|
LayoutLine(lineToWrap, surface, vs, ll, wrapWidth);
|
|
linesWrapped = ll->lines;
|
|
}
|
|
return cs.SetHeight(lineToWrap, linesWrapped +
|
|
(vs.annotationVisible ? pdoc->AnnotationLines(lineToWrap) : 0));
|
|
}
|
|
|
|
// Perform wrapping for a subset of the lines needing wrapping.
|
|
// wsAll: wrap all lines which need wrapping in this single call
|
|
// wsVisible: wrap currently visible lines
|
|
// wsIdle: wrap one page + 100 lines
|
|
// Return true if wrapping occurred.
|
|
bool Editor::WrapLines(enum wrapScope ws) {
|
|
int goodTopLine = topLine;
|
|
bool wrapOccurred = false;
|
|
if (!Wrapping()) {
|
|
if (wrapWidth != LineLayout::wrapWidthInfinite) {
|
|
wrapWidth = LineLayout::wrapWidthInfinite;
|
|
for (int lineDoc = 0; lineDoc < pdoc->LinesTotal(); lineDoc++) {
|
|
cs.SetHeight(lineDoc, 1 +
|
|
(vs.annotationVisible ? pdoc->AnnotationLines(lineDoc) : 0));
|
|
}
|
|
wrapOccurred = true;
|
|
}
|
|
wrapPending.Reset();
|
|
|
|
} else if (wrapPending.NeedsWrap()) {
|
|
wrapPending.start = std::min(wrapPending.start, pdoc->LinesTotal());
|
|
if (!SetIdle(true)) {
|
|
// Idle processing not supported so full wrap required.
|
|
ws = wsAll;
|
|
}
|
|
// Decide where to start wrapping
|
|
int lineToWrap = wrapPending.start;
|
|
int lineToWrapEnd = std::min(wrapPending.end, pdoc->LinesTotal());
|
|
const int lineDocTop = cs.DocFromDisplay(topLine);
|
|
const int subLineTop = topLine - cs.DisplayFromDoc(lineDocTop);
|
|
if (ws == wsVisible) {
|
|
lineToWrap = Platform::Clamp(lineDocTop-5, wrapPending.start, pdoc->LinesTotal());
|
|
// Priority wrap to just after visible area.
|
|
// Since wrapping could reduce display lines, treat each
|
|
// as taking only one display line.
|
|
lineToWrapEnd = lineDocTop;
|
|
int lines = LinesOnScreen() + 1;
|
|
while ((lineToWrapEnd < cs.LinesInDoc()) && (lines>0)) {
|
|
if (cs.GetVisible(lineToWrapEnd))
|
|
lines--;
|
|
lineToWrapEnd++;
|
|
}
|
|
// .. and if the paint window is outside pending wraps
|
|
if ((lineToWrap > wrapPending.end) || (lineToWrapEnd < wrapPending.start)) {
|
|
// Currently visible text does not need wrapping
|
|
return false;
|
|
}
|
|
} else if (ws == wsIdle) {
|
|
lineToWrapEnd = lineToWrap + LinesOnScreen() + 100;
|
|
}
|
|
const int lineEndNeedWrap = std::min(wrapPending.end, pdoc->LinesTotal());
|
|
lineToWrapEnd = std::min(lineToWrapEnd, lineEndNeedWrap);
|
|
|
|
// Ensure all lines being wrapped are styled.
|
|
pdoc->EnsureStyledTo(pdoc->LineStart(lineToWrapEnd));
|
|
|
|
if (lineToWrap < lineToWrapEnd) {
|
|
|
|
PRectangle rcTextArea = GetClientRectangle();
|
|
rcTextArea.left = static_cast<XYPOSITION>(vs.textStart);
|
|
rcTextArea.right -= vs.rightMarginWidth;
|
|
wrapWidth = static_cast<int>(rcTextArea.Width());
|
|
RefreshStyleData();
|
|
AutoSurface surface(this);
|
|
if (surface) {
|
|
//Platform::DebugPrintf("Wraplines: scope=%0d need=%0d..%0d perform=%0d..%0d\n", ws, wrapPending.start, wrapPending.end, lineToWrap, lineToWrapEnd);
|
|
|
|
while (lineToWrap < lineToWrapEnd) {
|
|
if (WrapOneLine(surface, lineToWrap)) {
|
|
wrapOccurred = true;
|
|
}
|
|
wrapPending.Wrapped(lineToWrap);
|
|
lineToWrap++;
|
|
}
|
|
|
|
goodTopLine = cs.DisplayFromDoc(lineDocTop) + std::min(subLineTop, cs.GetHeight(lineDocTop)-1);
|
|
}
|
|
}
|
|
|
|
// If wrapping is done, bring it to resting position
|
|
if (wrapPending.start >= lineEndNeedWrap) {
|
|
wrapPending.Reset();
|
|
}
|
|
}
|
|
|
|
if (wrapOccurred) {
|
|
SetScrollBars();
|
|
SetTopLine(Platform::Clamp(goodTopLine, 0, MaxScrollPos()));
|
|
SetVerticalScrollPos();
|
|
}
|
|
|
|
return wrapOccurred;
|
|
}
|
|
|
|
void Editor::LinesJoin() {
|
|
if (!RangeContainsProtected(targetStart, targetEnd)) {
|
|
UndoGroup ug(pdoc);
|
|
bool prevNonWS = true;
|
|
for (int pos = targetStart; pos < targetEnd; pos++) {
|
|
if (pdoc->IsPositionInLineEnd(pos)) {
|
|
targetEnd -= pdoc->LenChar(pos);
|
|
pdoc->DelChar(pos);
|
|
if (prevNonWS) {
|
|
// Ensure at least one space separating previous lines
|
|
const int lengthInserted = pdoc->InsertString(pos, " ", 1);
|
|
targetEnd += lengthInserted;
|
|
}
|
|
} else {
|
|
prevNonWS = pdoc->CharAt(pos) != ' ';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const char *Editor::StringFromEOLMode(int eolMode) {
|
|
if (eolMode == SC_EOL_CRLF) {
|
|
return "\r\n";
|
|
} else if (eolMode == SC_EOL_CR) {
|
|
return "\r";
|
|
} else {
|
|
return "\n";
|
|
}
|
|
}
|
|
|
|
void Editor::LinesSplit(int pixelWidth) {
|
|
if (!RangeContainsProtected(targetStart, targetEnd)) {
|
|
if (pixelWidth == 0) {
|
|
PRectangle rcText = GetTextRectangle();
|
|
pixelWidth = static_cast<int>(rcText.Width());
|
|
}
|
|
int lineStart = pdoc->LineFromPosition(targetStart);
|
|
int lineEnd = pdoc->LineFromPosition(targetEnd);
|
|
const char *eol = StringFromEOLMode(pdoc->eolMode);
|
|
UndoGroup ug(pdoc);
|
|
for (int line = lineStart; line <= lineEnd; line++) {
|
|
AutoSurface surface(this);
|
|
AutoLineLayout ll(llc, RetrieveLineLayout(line));
|
|
if (surface && ll) {
|
|
unsigned int posLineStart = pdoc->LineStart(line);
|
|
LayoutLine(line, surface, vs, ll, pixelWidth);
|
|
int lengthInsertedTotal = 0;
|
|
for (int subLine = 1; subLine < ll->lines; subLine++) {
|
|
const int lengthInserted = pdoc->InsertString(
|
|
static_cast<int>(posLineStart + lengthInsertedTotal +
|
|
ll->LineStart(subLine)),
|
|
eol, istrlen(eol));
|
|
targetEnd += lengthInserted;
|
|
lengthInsertedTotal += lengthInserted;
|
|
}
|
|
}
|
|
lineEnd = pdoc->LineFromPosition(targetEnd);
|
|
}
|
|
}
|
|
}
|
|
|
|
int Editor::SubstituteMarkerIfEmpty(int markerCheck, int markerDefault) const {
|
|
if (vs.markers[markerCheck].markType == SC_MARK_EMPTY)
|
|
return markerDefault;
|
|
return markerCheck;
|
|
}
|
|
|
|
bool ValidStyledText(ViewStyle &vs, size_t styleOffset, const StyledText &st) {
|
|
if (st.multipleStyles) {
|
|
for (size_t iStyle=0; iStyle<st.length; iStyle++) {
|
|
if (!vs.ValidStyle(styleOffset + st.styles[iStyle]))
|
|
return false;
|
|
}
|
|
} else {
|
|
if (!vs.ValidStyle(styleOffset + st.style))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static int WidthStyledText(Surface *surface, ViewStyle &vs, int styleOffset,
|
|
const char *text, const unsigned char *styles, size_t len) {
|
|
int width = 0;
|
|
size_t start = 0;
|
|
while (start < len) {
|
|
size_t style = styles[start];
|
|
size_t endSegment = start;
|
|
while ((endSegment+1 < len) && (static_cast<size_t>(styles[endSegment+1]) == style))
|
|
endSegment++;
|
|
width += static_cast<int>(surface->WidthText(vs.styles[style + styleOffset].font, text + start,
|
|
static_cast<int>(endSegment - start + 1)));
|
|
start = endSegment + 1;
|
|
}
|
|
return width;
|
|
}
|
|
|
|
static int WidestLineWidth(Surface *surface, ViewStyle &vs, int styleOffset, const StyledText &st) {
|
|
int widthMax = 0;
|
|
size_t start = 0;
|
|
while (start < st.length) {
|
|
size_t lenLine = st.LineLength(start);
|
|
int widthSubLine;
|
|
if (st.multipleStyles) {
|
|
widthSubLine = WidthStyledText(surface, vs, styleOffset, st.text + start, st.styles + start, lenLine);
|
|
} else {
|
|
widthSubLine = static_cast<int>(surface->WidthText(vs.styles[styleOffset + st.style].font,
|
|
st.text + start, static_cast<int>(lenLine)));
|
|
}
|
|
if (widthSubLine > widthMax)
|
|
widthMax = widthSubLine;
|
|
start += lenLine + 1;
|
|
}
|
|
return widthMax;
|
|
}
|
|
|
|
void DrawStyledText(Surface *surface, ViewStyle &vs, int styleOffset, PRectangle rcText, int ascent,
|
|
const StyledText &st, size_t start, size_t length) {
|
|
|
|
if (st.multipleStyles) {
|
|
int x = static_cast<int>(rcText.left);
|
|
size_t i = 0;
|
|
while (i < length) {
|
|
size_t end = i;
|
|
int style = st.styles[i + start];
|
|
while (end < length-1 && st.styles[start+end+1] == style)
|
|
end++;
|
|
style += styleOffset;
|
|
int width = static_cast<int>(surface->WidthText(vs.styles[style].font,
|
|
st.text + start + i, static_cast<int>(end - i + 1)));
|
|
PRectangle rcSegment = rcText;
|
|
rcSegment.left = static_cast<XYPOSITION>(x);
|
|
rcSegment.right = static_cast<XYPOSITION>(x + width + 1);
|
|
surface->DrawTextNoClip(rcSegment, vs.styles[style].font,
|
|
static_cast<XYPOSITION>(ascent), st.text + start + i,
|
|
static_cast<int>(end - i + 1),
|
|
vs.styles[style].fore,
|
|
vs.styles[style].back);
|
|
x += width;
|
|
i = end + 1;
|
|
}
|
|
} else {
|
|
size_t style = st.style + styleOffset;
|
|
surface->DrawTextNoClip(rcText, vs.styles[style].font,
|
|
rcText.top + vs.maxAscent, st.text + start,
|
|
static_cast<int>(length),
|
|
vs.styles[style].fore,
|
|
vs.styles[style].back);
|
|
}
|
|
}
|
|
|
|
void Editor::PaintSelMargin(Surface *surfWindow, PRectangle &rc) {
|
|
if (vs.fixedColumnWidth == 0)
|
|
return;
|
|
|
|
AllocateGraphics();
|
|
RefreshStyleData();
|
|
RefreshPixMaps(surfWindow);
|
|
|
|
PRectangle rcMargin = GetClientRectangle();
|
|
Point ptOrigin = GetVisibleOriginInMain();
|
|
rcMargin.Move(0, -ptOrigin.y);
|
|
rcMargin.left = 0;
|
|
rcMargin.right = static_cast<XYPOSITION>(vs.fixedColumnWidth);
|
|
|
|
if (!rc.Intersects(rcMargin))
|
|
return;
|
|
|
|
Surface *surface;
|
|
if (bufferedDraw) {
|
|
surface = pixmapSelMargin;
|
|
} else {
|
|
surface = surfWindow;
|
|
}
|
|
|
|
// Clip vertically to paint area to avoid drawing line numbers
|
|
if (rcMargin.bottom > rc.bottom)
|
|
rcMargin.bottom = rc.bottom;
|
|
if (rcMargin.top < rc.top)
|
|
rcMargin.top = rc.top;
|
|
|
|
PRectangle rcSelMargin = rcMargin;
|
|
rcSelMargin.right = rcMargin.left;
|
|
if (rcSelMargin.bottom < rc.bottom)
|
|
rcSelMargin.bottom = rc.bottom;
|
|
|
|
for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) {
|
|
if (vs.ms[margin].width > 0) {
|
|
|
|
rcSelMargin.left = rcSelMargin.right;
|
|
rcSelMargin.right = rcSelMargin.left + vs.ms[margin].width;
|
|
|
|
if (vs.ms[margin].style != SC_MARGIN_NUMBER) {
|
|
if (vs.ms[margin].mask & SC_MASK_FOLDERS) {
|
|
// Required because of special way brush is created for selection margin
|
|
// Ensure patterns line up when scrolling with separate margin view
|
|
// by choosing correctly aligned variant.
|
|
bool invertPhase = static_cast<int>(ptOrigin.y) & 1;
|
|
surface->FillRectangle(rcSelMargin,
|
|
invertPhase ? *pixmapSelPattern : *pixmapSelPatternOffset1);
|
|
} else {
|
|
ColourDesired colour;
|
|
switch (vs.ms[margin].style) {
|
|
case SC_MARGIN_BACK:
|
|
colour = vs.styles[STYLE_DEFAULT].back;
|
|
break;
|
|
case SC_MARGIN_FORE:
|
|
colour = vs.styles[STYLE_DEFAULT].fore;
|
|
break;
|
|
default:
|
|
colour = vs.styles[STYLE_LINENUMBER].back;
|
|
break;
|
|
}
|
|
surface->FillRectangle(rcSelMargin, colour);
|
|
}
|
|
} else {
|
|
surface->FillRectangle(rcSelMargin, vs.styles[STYLE_LINENUMBER].back);
|
|
}
|
|
|
|
const int lineStartPaint = static_cast<int>(rcMargin.top + ptOrigin.y) / vs.lineHeight;
|
|
int visibleLine = TopLineOfMain() + lineStartPaint;
|
|
int yposScreen = lineStartPaint * vs.lineHeight - static_cast<int>(ptOrigin.y);
|
|
// Work out whether the top line is whitespace located after a
|
|
// lessening of fold level which implies a 'fold tail' but which should not
|
|
// be displayed until the last of a sequence of whitespace.
|
|
bool needWhiteClosure = false;
|
|
if (vs.ms[margin].mask & SC_MASK_FOLDERS) {
|
|
int level = pdoc->GetLevel(cs.DocFromDisplay(visibleLine));
|
|
if (level & SC_FOLDLEVELWHITEFLAG) {
|
|
int lineBack = cs.DocFromDisplay(visibleLine);
|
|
int levelPrev = level;
|
|
while ((lineBack > 0) && (levelPrev & SC_FOLDLEVELWHITEFLAG)) {
|
|
lineBack--;
|
|
levelPrev = pdoc->GetLevel(lineBack);
|
|
}
|
|
if (!(levelPrev & SC_FOLDLEVELHEADERFLAG)) {
|
|
if ((level & SC_FOLDLEVELNUMBERMASK) < (levelPrev & SC_FOLDLEVELNUMBERMASK))
|
|
needWhiteClosure = true;
|
|
}
|
|
}
|
|
if (highlightDelimiter.isEnabled) {
|
|
int lastLine = cs.DocFromDisplay(topLine + LinesOnScreen()) + 1;
|
|
pdoc->GetHighlightDelimiters(highlightDelimiter, pdoc->LineFromPosition(CurrentPosition()), lastLine);
|
|
}
|
|
}
|
|
|
|
// Old code does not know about new markers needed to distinguish all cases
|
|
const int folderOpenMid = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEROPENMID,
|
|
SC_MARKNUM_FOLDEROPEN);
|
|
const int folderEnd = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEREND,
|
|
SC_MARKNUM_FOLDER);
|
|
|
|
while ((visibleLine < cs.LinesDisplayed()) && yposScreen < rc.bottom) {
|
|
|
|
PLATFORM_ASSERT(visibleLine < cs.LinesDisplayed());
|
|
const int lineDoc = cs.DocFromDisplay(visibleLine);
|
|
PLATFORM_ASSERT(cs.GetVisible(lineDoc));
|
|
const bool firstSubLine = visibleLine == cs.DisplayFromDoc(lineDoc);
|
|
const bool lastSubLine = visibleLine == cs.DisplayLastFromDoc(lineDoc);
|
|
|
|
int marks = pdoc->GetMark(lineDoc);
|
|
if (!firstSubLine)
|
|
marks = 0;
|
|
|
|
bool headWithTail = false;
|
|
|
|
if (vs.ms[margin].mask & SC_MASK_FOLDERS) {
|
|
// Decide which fold indicator should be displayed
|
|
const int level = pdoc->GetLevel(lineDoc);
|
|
const int levelNext = pdoc->GetLevel(lineDoc + 1);
|
|
const int levelNum = level & SC_FOLDLEVELNUMBERMASK;
|
|
const int levelNextNum = levelNext & SC_FOLDLEVELNUMBERMASK;
|
|
if (level & SC_FOLDLEVELHEADERFLAG) {
|
|
if (firstSubLine) {
|
|
if (levelNum < levelNextNum) {
|
|
if (cs.GetExpanded(lineDoc)) {
|
|
if (levelNum == SC_FOLDLEVELBASE)
|
|
marks |= 1 << SC_MARKNUM_FOLDEROPEN;
|
|
else
|
|
marks |= 1 << folderOpenMid;
|
|
} else {
|
|
if (levelNum == SC_FOLDLEVELBASE)
|
|
marks |= 1 << SC_MARKNUM_FOLDER;
|
|
else
|
|
marks |= 1 << folderEnd;
|
|
}
|
|
} else if (levelNum > SC_FOLDLEVELBASE) {
|
|
marks |= 1 << SC_MARKNUM_FOLDERSUB;
|
|
}
|
|
} else {
|
|
if (levelNum < levelNextNum) {
|
|
if (cs.GetExpanded(lineDoc)) {
|
|
marks |= 1 << SC_MARKNUM_FOLDERSUB;
|
|
} else if (levelNum > SC_FOLDLEVELBASE) {
|
|
marks |= 1 << SC_MARKNUM_FOLDERSUB;
|
|
}
|
|
} else if (levelNum > SC_FOLDLEVELBASE) {
|
|
marks |= 1 << SC_MARKNUM_FOLDERSUB;
|
|
}
|
|
}
|
|
needWhiteClosure = false;
|
|
const int firstFollowupLine = cs.DocFromDisplay(cs.DisplayFromDoc(lineDoc + 1));
|
|
const int firstFollowupLineLevel = pdoc->GetLevel(firstFollowupLine);
|
|
const int secondFollowupLineLevelNum = pdoc->GetLevel(firstFollowupLine + 1) & SC_FOLDLEVELNUMBERMASK;
|
|
if (!cs.GetExpanded(lineDoc)) {
|
|
if ((firstFollowupLineLevel & SC_FOLDLEVELWHITEFLAG) &&
|
|
(levelNum > secondFollowupLineLevelNum))
|
|
needWhiteClosure = true;
|
|
|
|
if (highlightDelimiter.IsFoldBlockHighlighted(firstFollowupLine))
|
|
headWithTail = true;
|
|
}
|
|
} else if (level & SC_FOLDLEVELWHITEFLAG) {
|
|
if (needWhiteClosure) {
|
|
if (levelNext & SC_FOLDLEVELWHITEFLAG) {
|
|
marks |= 1 << SC_MARKNUM_FOLDERSUB;
|
|
} else if (levelNextNum > SC_FOLDLEVELBASE) {
|
|
marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
|
|
needWhiteClosure = false;
|
|
} else {
|
|
marks |= 1 << SC_MARKNUM_FOLDERTAIL;
|
|
needWhiteClosure = false;
|
|
}
|
|
} else if (levelNum > SC_FOLDLEVELBASE) {
|
|
if (levelNextNum < levelNum) {
|
|
if (levelNextNum > SC_FOLDLEVELBASE) {
|
|
marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
|
|
} else {
|
|
marks |= 1 << SC_MARKNUM_FOLDERTAIL;
|
|
}
|
|
} else {
|
|
marks |= 1 << SC_MARKNUM_FOLDERSUB;
|
|
}
|
|
}
|
|
} else if (levelNum > SC_FOLDLEVELBASE) {
|
|
if (levelNextNum < levelNum) {
|
|
needWhiteClosure = false;
|
|
if (levelNext & SC_FOLDLEVELWHITEFLAG) {
|
|
marks |= 1 << SC_MARKNUM_FOLDERSUB;
|
|
needWhiteClosure = true;
|
|
} else if (lastSubLine) {
|
|
if (levelNextNum > SC_FOLDLEVELBASE) {
|
|
marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
|
|
} else {
|
|
marks |= 1 << SC_MARKNUM_FOLDERTAIL;
|
|
}
|
|
} else {
|
|
marks |= 1 << SC_MARKNUM_FOLDERSUB;
|
|
}
|
|
} else {
|
|
marks |= 1 << SC_MARKNUM_FOLDERSUB;
|
|
}
|
|
}
|
|
}
|
|
|
|
marks &= vs.ms[margin].mask;
|
|
|
|
PRectangle rcMarker = rcSelMargin;
|
|
rcMarker.top = static_cast<XYPOSITION>(yposScreen);
|
|
rcMarker.bottom = static_cast<XYPOSITION>(yposScreen + vs.lineHeight);
|
|
if (vs.ms[margin].style == SC_MARGIN_NUMBER) {
|
|
if (firstSubLine) {
|
|
char number[100] = "";
|
|
if (lineDoc >= 0)
|
|
sprintf(number, "%d", lineDoc + 1);
|
|
if (foldFlags & (SC_FOLDFLAG_LEVELNUMBERS | SC_FOLDFLAG_LINESTATE)) {
|
|
if (foldFlags & SC_FOLDFLAG_LEVELNUMBERS) {
|
|
int lev = pdoc->GetLevel(lineDoc);
|
|
sprintf(number, "%c%c %03X %03X",
|
|
(lev & SC_FOLDLEVELHEADERFLAG) ? 'H' : '_',
|
|
(lev & SC_FOLDLEVELWHITEFLAG) ? 'W' : '_',
|
|
lev & SC_FOLDLEVELNUMBERMASK,
|
|
lev >> 16
|
|
);
|
|
} else {
|
|
int state = pdoc->GetLineState(lineDoc);
|
|
sprintf(number, "%0X", state);
|
|
}
|
|
}
|
|
PRectangle rcNumber = rcMarker;
|
|
// Right justify
|
|
XYPOSITION width = surface->WidthText(vs.styles[STYLE_LINENUMBER].font, number, istrlen(number));
|
|
XYPOSITION xpos = rcNumber.right - width - vs.marginNumberPadding;
|
|
rcNumber.left = xpos;
|
|
surface->DrawTextNoClip(rcNumber, vs.styles[STYLE_LINENUMBER].font,
|
|
rcNumber.top + vs.maxAscent, number, istrlen(number),
|
|
vs.styles[STYLE_LINENUMBER].fore,
|
|
vs.styles[STYLE_LINENUMBER].back);
|
|
} else if (vs.wrapVisualFlags & SC_WRAPVISUALFLAG_MARGIN) {
|
|
PRectangle rcWrapMarker = rcMarker;
|
|
rcWrapMarker.right -= 3;
|
|
rcWrapMarker.left = rcWrapMarker.right - vs.styles[STYLE_LINENUMBER].aveCharWidth;
|
|
DrawWrapMarker(surface, rcWrapMarker, false, vs.styles[STYLE_LINENUMBER].fore);
|
|
}
|
|
} else if (vs.ms[margin].style == SC_MARGIN_TEXT || vs.ms[margin].style == SC_MARGIN_RTEXT) {
|
|
if (firstSubLine) {
|
|
const StyledText stMargin = pdoc->MarginStyledText(lineDoc);
|
|
if (stMargin.text && ValidStyledText(vs, vs.marginStyleOffset, stMargin)) {
|
|
surface->FillRectangle(rcMarker,
|
|
vs.styles[stMargin.StyleAt(0)+vs.marginStyleOffset].back);
|
|
if (vs.ms[margin].style == SC_MARGIN_RTEXT) {
|
|
int width = WidestLineWidth(surface, vs, vs.marginStyleOffset, stMargin);
|
|
rcMarker.left = rcMarker.right - width - 3;
|
|
}
|
|
DrawStyledText(surface, vs, vs.marginStyleOffset, rcMarker, static_cast<int>(rcMarker.top) + vs.maxAscent,
|
|
stMargin, 0, stMargin.length);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (marks) {
|
|
for (int markBit = 0; (markBit < 32) && marks; markBit++) {
|
|
if (marks & 1) {
|
|
LineMarker::typeOfFold tFold = LineMarker::undefined;
|
|
if ((vs.ms[margin].mask & SC_MASK_FOLDERS) && highlightDelimiter.IsFoldBlockHighlighted(lineDoc)) {
|
|
if (highlightDelimiter.IsBodyOfFoldBlock(lineDoc)) {
|
|
tFold = LineMarker::body;
|
|
} else if (highlightDelimiter.IsHeadOfFoldBlock(lineDoc)) {
|
|
if (firstSubLine) {
|
|
tFold = headWithTail ? LineMarker::headWithTail : LineMarker::head;
|
|
} else {
|
|
if (cs.GetExpanded(lineDoc) || headWithTail) {
|
|
tFold = LineMarker::body;
|
|
} else {
|
|
tFold = LineMarker::undefined;
|
|
}
|
|
}
|
|
} else if (highlightDelimiter.IsTailOfFoldBlock(lineDoc)) {
|
|
tFold = LineMarker::tail;
|
|
}
|
|
}
|
|
vs.markers[markBit].Draw(surface, rcMarker, vs.styles[STYLE_LINENUMBER].font, tFold, vs.ms[margin].style);
|
|
}
|
|
marks >>= 1;
|
|
}
|
|
}
|
|
|
|
visibleLine++;
|
|
yposScreen += vs.lineHeight;
|
|
}
|
|
}
|
|
}
|
|
|
|
PRectangle rcBlankMargin = rcMargin;
|
|
rcBlankMargin.left = rcSelMargin.right;
|
|
surface->FillRectangle(rcBlankMargin, vs.styles[STYLE_DEFAULT].back);
|
|
|
|
if (bufferedDraw) {
|
|
surfWindow->Copy(rcMargin, Point(rcMargin.left, rcMargin.top), *pixmapSelMargin);
|
|
}
|
|
}
|
|
|
|
void DrawTabArrow(Surface *surface, PRectangle rcTab, int ymid) {
|
|
int ydiff = static_cast<int>(rcTab.bottom - rcTab.top) / 2;
|
|
int xhead = static_cast<int>(rcTab.right) - 1 - ydiff;
|
|
if (xhead <= rcTab.left) {
|
|
ydiff -= static_cast<int>(rcTab.left) - xhead - 1;
|
|
xhead = static_cast<int>(rcTab.left) - 1;
|
|
}
|
|
if ((rcTab.left + 2) < (rcTab.right - 1))
|
|
surface->MoveTo(static_cast<int>(rcTab.left) + 2, ymid);
|
|
else
|
|
surface->MoveTo(static_cast<int>(rcTab.right) - 1, ymid);
|
|
surface->LineTo(static_cast<int>(rcTab.right) - 1, ymid);
|
|
surface->LineTo(xhead, ymid - ydiff);
|
|
surface->MoveTo(static_cast<int>(rcTab.right) - 1, ymid);
|
|
surface->LineTo(xhead, ymid + ydiff);
|
|
}
|
|
|
|
LineLayout *Editor::RetrieveLineLayout(int lineNumber) {
|
|
int posLineStart = pdoc->LineStart(lineNumber);
|
|
int posLineEnd = pdoc->LineStart(lineNumber + 1);
|
|
PLATFORM_ASSERT(posLineEnd >= posLineStart);
|
|
int lineCaret = pdoc->LineFromPosition(sel.MainCaret());
|
|
return llc.Retrieve(lineNumber, lineCaret,
|
|
posLineEnd - posLineStart, pdoc->GetStyleClock(),
|
|
LinesOnScreen() + 1, pdoc->LinesTotal());
|
|
}
|
|
|
|
/**
|
|
* Fill in the LineLayout data for the given line.
|
|
* Copy the given @a line and its styles from the document into local arrays.
|
|
* Also determine the x position at which each character starts.
|
|
*/
|
|
void Editor::LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayout *ll, int width) {
|
|
//void LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayout *ll, int width, Document *pdoc, PositionCache &posCache, SpecialRepresentations &reprs) {
|
|
if (!ll)
|
|
return;
|
|
|
|
PLATFORM_ASSERT(line < pdoc->LinesTotal());
|
|
PLATFORM_ASSERT(ll->chars != NULL);
|
|
int posLineStart = pdoc->LineStart(line);
|
|
int posLineEnd = pdoc->LineStart(line + 1);
|
|
// If the line is very long, limit the treatment to a length that should fit in the viewport
|
|
if (posLineEnd > (posLineStart + ll->maxLineLength)) {
|
|
posLineEnd = posLineStart + ll->maxLineLength;
|
|
}
|
|
if (ll->validity == LineLayout::llCheckTextAndStyle) {
|
|
int lineLength = posLineEnd - posLineStart;
|
|
if (!vstyle.viewEOL) {
|
|
lineLength = pdoc->LineEnd(line) - posLineStart;
|
|
}
|
|
if (lineLength == ll->numCharsInLine) {
|
|
// See if chars, styles, indicators, are all the same
|
|
bool allSame = true;
|
|
const int styleMask = pdoc->stylingBitsMask;
|
|
// Check base line layout
|
|
char styleByte = 0;
|
|
int numCharsInLine = 0;
|
|
while (numCharsInLine < lineLength) {
|
|
int charInDoc = numCharsInLine + posLineStart;
|
|
char chDoc = pdoc->CharAt(charInDoc);
|
|
styleByte = pdoc->StyleAt(charInDoc);
|
|
allSame = allSame &&
|
|
(ll->styles[numCharsInLine] == static_cast<unsigned char>(styleByte & styleMask));
|
|
allSame = allSame &&
|
|
(ll->indicators[numCharsInLine] == static_cast<char>(styleByte & ~styleMask));
|
|
if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseMixed)
|
|
allSame = allSame &&
|
|
(ll->chars[numCharsInLine] == chDoc);
|
|
else if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseLower)
|
|
allSame = allSame &&
|
|
(ll->chars[numCharsInLine] == static_cast<char>(tolower(chDoc)));
|
|
else // Style::caseUpper
|
|
allSame = allSame &&
|
|
(ll->chars[numCharsInLine] == static_cast<char>(toupper(chDoc)));
|
|
numCharsInLine++;
|
|
}
|
|
allSame = allSame && (ll->styles[numCharsInLine] == styleByte); // For eolFilled
|
|
if (allSame) {
|
|
ll->validity = LineLayout::llPositions;
|
|
} else {
|
|
ll->validity = LineLayout::llInvalid;
|
|
}
|
|
} else {
|
|
ll->validity = LineLayout::llInvalid;
|
|
}
|
|
}
|
|
if (ll->validity == LineLayout::llInvalid) {
|
|
ll->widthLine = LineLayout::wrapWidthInfinite;
|
|
ll->lines = 1;
|
|
if (vstyle.edgeState == EDGE_BACKGROUND) {
|
|
ll->edgeColumn = pdoc->FindColumn(line, vstyle.theEdge);
|
|
if (ll->edgeColumn >= posLineStart) {
|
|
ll->edgeColumn -= posLineStart;
|
|
}
|
|
} else {
|
|
ll->edgeColumn = -1;
|
|
}
|
|
|
|
const int styleMask = pdoc->stylingBitsMask;
|
|
ll->styleBitsSet = 0;
|
|
// Fill base line layout
|
|
const int lineLength = posLineEnd - posLineStart;
|
|
pdoc->GetCharRange(ll->chars, posLineStart, lineLength);
|
|
pdoc->GetStyleRange(ll->styles, posLineStart, lineLength);
|
|
int numCharsBeforeEOL = pdoc->LineEnd(line) - posLineStart;
|
|
const int numCharsInLine = (vstyle.viewEOL) ? lineLength : numCharsBeforeEOL;
|
|
for (int styleInLine = 0; styleInLine < numCharsInLine; styleInLine++) {
|
|
const unsigned char styleByte = ll->styles[styleInLine];
|
|
ll->styleBitsSet |= styleByte;
|
|
ll->styles[styleInLine] = styleByte & styleMask;
|
|
ll->indicators[styleInLine] = static_cast<char>(styleByte & ~styleMask);
|
|
}
|
|
const unsigned char styleByteLast = ((lineLength > 0) ? ll->styles[lineLength-1] : 0) & styleMask;
|
|
if (vstyle.someStylesForceCase) {
|
|
for (int charInLine = 0; charInLine<lineLength; charInLine++) {
|
|
char chDoc = ll->chars[charInLine];
|
|
if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseUpper)
|
|
ll->chars[charInLine] = static_cast<char>(toupper(chDoc));
|
|
else if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseLower)
|
|
ll->chars[charInLine] = static_cast<char>(tolower(chDoc));
|
|
}
|
|
}
|
|
ll->xHighlightGuide = 0;
|
|
// Extra element at the end of the line to hold end x position and act as
|
|
ll->chars[numCharsInLine] = 0; // Also triggers processing in the loops as this is a control character
|
|
ll->styles[numCharsInLine] = styleByteLast; // For eolFilled
|
|
ll->indicators[numCharsInLine] = 0;
|
|
|
|
// Layout the line, determining the position of each character,
|
|
// with an extra element at the end for the end of the line.
|
|
ll->positions[0] = 0;
|
|
bool lastSegItalics = false;
|
|
|
|
BreakFinder bfLayout(ll, 0, numCharsInLine, posLineStart, 0, false, pdoc, &reprs);
|
|
while (bfLayout.More()) {
|
|
|
|
const TextSegment ts = bfLayout.Next();
|
|
|
|
std::fill(&ll->positions[ts.start+1], &ll->positions[ts.end()+1], 0.0f);
|
|
if (vstyle.styles[ll->styles[ts.start]].visible) {
|
|
if (ts.representation) {
|
|
XYPOSITION representationWidth = vstyle.controlCharWidth;
|
|
if (ll->chars[ts.start] == '\t') {
|
|
// Tab is a special case of representation, taking a variable amount of space
|
|
representationWidth =
|
|
((static_cast<int>((ll->positions[ts.start] + 2) / vstyle.tabWidth) + 1) * vstyle.tabWidth) - ll->positions[ts.start];
|
|
} else {
|
|
if (representationWidth <= 0.0) {
|
|
XYPOSITION positionsRepr[256]; // Should expand when needed
|
|
posCache.MeasureWidths(surface, vstyle, STYLE_CONTROLCHAR, ts.representation->stringRep.c_str(),
|
|
static_cast<unsigned int>(ts.representation->stringRep.length()), positionsRepr, pdoc);
|
|
representationWidth = positionsRepr[ts.representation->stringRep.length()-1] + vstyle.ctrlCharPadding;
|
|
}
|
|
}
|
|
for (int ii=0; ii < ts.length; ii++)
|
|
ll->positions[ts.start + 1 + ii] = representationWidth;
|
|
} else {
|
|
if ((ts.length == 1) && (' ' == ll->chars[ts.start])) {
|
|
// Over half the segments are single characters and of these about half are space characters.
|
|
ll->positions[ts.start + 1] = vstyle.styles[ll->styles[ts.start]].spaceWidth;
|
|
} else {
|
|
posCache.MeasureWidths(surface, vstyle, ll->styles[ts.start], ll->chars + ts.start,
|
|
ts.length, ll->positions + ts.start + 1, pdoc);
|
|
}
|
|
}
|
|
lastSegItalics = (!ts.representation) && ((ll->chars[ts.end()-1] != ' ') && vstyle.styles[ll->styles[ts.start]].italic);
|
|
}
|
|
|
|
for (int posToIncrease = ts.start+1; posToIncrease <= ts.end(); posToIncrease++) {
|
|
ll->positions[posToIncrease] += ll->positions[ts.start];
|
|
}
|
|
}
|
|
|
|
// Small hack to make lines that end with italics not cut off the edge of the last character
|
|
if (lastSegItalics) {
|
|
ll->positions[numCharsInLine] += vstyle.lastSegItalicsOffset;
|
|
}
|
|
ll->numCharsInLine = numCharsInLine;
|
|
ll->numCharsBeforeEOL = numCharsBeforeEOL;
|
|
ll->validity = LineLayout::llPositions;
|
|
}
|
|
// Hard to cope when too narrow, so just assume there is space
|
|
if (width < 20) {
|
|
width = 20;
|
|
}
|
|
if ((ll->validity == LineLayout::llPositions) || (ll->widthLine != width)) {
|
|
ll->widthLine = width;
|
|
if (width == LineLayout::wrapWidthInfinite) {
|
|
ll->lines = 1;
|
|
} else if (width > ll->positions[ll->numCharsInLine]) {
|
|
// Simple common case where line does not need wrapping.
|
|
ll->lines = 1;
|
|
} else {
|
|
if (vstyle.wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
|
|
width -= static_cast<int>(vstyle.aveCharWidth); // take into account the space for end wrap mark
|
|
}
|
|
XYPOSITION wrapAddIndent = 0; // This will be added to initial indent of line
|
|
if (vstyle.wrapIndentMode == SC_WRAPINDENT_INDENT) {
|
|
wrapAddIndent = pdoc->IndentSize() * vstyle.spaceWidth;
|
|
} else if (vstyle.wrapIndentMode == SC_WRAPINDENT_FIXED) {
|
|
wrapAddIndent = vstyle.wrapVisualStartIndent * vstyle.aveCharWidth;
|
|
}
|
|
ll->wrapIndent = wrapAddIndent;
|
|
if (vstyle.wrapIndentMode != SC_WRAPINDENT_FIXED)
|
|
for (int i = 0; i < ll->numCharsInLine; i++) {
|
|
if (!IsSpaceOrTab(ll->chars[i])) {
|
|
ll->wrapIndent += ll->positions[i]; // Add line indent
|
|
break;
|
|
}
|
|
}
|
|
// Check for text width minimum
|
|
if (ll->wrapIndent > width - static_cast<int>(vstyle.aveCharWidth) * 15)
|
|
ll->wrapIndent = wrapAddIndent;
|
|
// Check for wrapIndent minimum
|
|
if ((vstyle.wrapVisualFlags & SC_WRAPVISUALFLAG_START) && (ll->wrapIndent < vstyle.aveCharWidth))
|
|
ll->wrapIndent = vstyle.aveCharWidth; // Indent to show start visual
|
|
ll->lines = 0;
|
|
// Calculate line start positions based upon width.
|
|
int lastGoodBreak = 0;
|
|
int lastLineStart = 0;
|
|
XYACCUMULATOR startOffset = 0;
|
|
int p = 0;
|
|
while (p < ll->numCharsInLine) {
|
|
if ((ll->positions[p + 1] - startOffset) >= width) {
|
|
if (lastGoodBreak == lastLineStart) {
|
|
// Try moving to start of last character
|
|
if (p > 0) {
|
|
lastGoodBreak = pdoc->MovePositionOutsideChar(p + posLineStart, -1)
|
|
- posLineStart;
|
|
}
|
|
if (lastGoodBreak == lastLineStart) {
|
|
// Ensure at least one character on line.
|
|
lastGoodBreak = pdoc->MovePositionOutsideChar(lastGoodBreak + posLineStart + 1, 1)
|
|
- posLineStart;
|
|
}
|
|
}
|
|
lastLineStart = lastGoodBreak;
|
|
ll->lines++;
|
|
ll->SetLineStart(ll->lines, lastGoodBreak);
|
|
startOffset = ll->positions[lastGoodBreak];
|
|
// take into account the space for start wrap mark and indent
|
|
startOffset -= ll->wrapIndent;
|
|
p = lastGoodBreak + 1;
|
|
continue;
|
|
}
|
|
if (p > 0) {
|
|
if (vstyle.wrapState == eWrapChar) {
|
|
lastGoodBreak = pdoc->MovePositionOutsideChar(p + posLineStart, -1)
|
|
- posLineStart;
|
|
p = pdoc->MovePositionOutsideChar(p + 1 + posLineStart, 1) - posLineStart;
|
|
continue;
|
|
} else if ((vstyle.wrapState == eWrapWord) && (ll->styles[p] != ll->styles[p - 1])) {
|
|
lastGoodBreak = p;
|
|
} else if (IsSpaceOrTab(ll->chars[p - 1]) && !IsSpaceOrTab(ll->chars[p])) {
|
|
lastGoodBreak = p;
|
|
}
|
|
}
|
|
p++;
|
|
}
|
|
ll->lines++;
|
|
}
|
|
ll->validity = LineLayout::llLines;
|
|
}
|
|
}
|
|
|
|
ColourDesired Editor::SelectionBackground(ViewStyle &vsDraw, bool main) const {
|
|
return main ?
|
|
(primarySelection ? vsDraw.selColours.back : vsDraw.selBackground2) :
|
|
vsDraw.selAdditionalBackground;
|
|
}
|
|
|
|
ColourDesired Editor::TextBackground(ViewStyle &vsDraw, bool overrideBackground,
|
|
ColourDesired background, int inSelection, bool inHotspot, int styleMain, int i, LineLayout *ll) const {
|
|
if (inSelection == 1) {
|
|
if (vsDraw.selColours.back.isSet && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) {
|
|
return SelectionBackground(vsDraw, true);
|
|
}
|
|
} else if (inSelection == 2) {
|
|
if (vsDraw.selColours.back.isSet && (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA)) {
|
|
return SelectionBackground(vsDraw, false);
|
|
}
|
|
} else {
|
|
if ((vsDraw.edgeState == EDGE_BACKGROUND) &&
|
|
(i >= ll->edgeColumn) &&
|
|
(i < ll->numCharsBeforeEOL))
|
|
return vsDraw.edgecolour;
|
|
if (inHotspot && vsDraw.hotspotColours.back.isSet)
|
|
return vsDraw.hotspotColours.back;
|
|
}
|
|
if (overrideBackground && (styleMain != STYLE_BRACELIGHT) && (styleMain != STYLE_BRACEBAD)) {
|
|
return background;
|
|
} else {
|
|
return vsDraw.styles[styleMain].back;
|
|
}
|
|
}
|
|
|
|
void Editor::DrawIndentGuide(Surface *surface, int lineVisible, int lineHeight, int start, PRectangle rcSegment, bool highlight) {
|
|
Point from = Point::FromInts(0, ((lineVisible & 1) && (lineHeight & 1)) ? 1 : 0);
|
|
PRectangle rcCopyArea = PRectangle::FromInts(start + 1, static_cast<int>(rcSegment.top), start + 2, static_cast<int>(rcSegment.bottom));
|
|
surface->Copy(rcCopyArea, from,
|
|
highlight ? *pixmapIndentGuideHighlight : *pixmapIndentGuide);
|
|
}
|
|
|
|
void Editor::DrawWrapMarker(Surface *surface, PRectangle rcPlace,
|
|
bool isEndMarker, ColourDesired wrapColour) {
|
|
surface->PenColour(wrapColour);
|
|
|
|
enum { xa = 1 }; // gap before start
|
|
int w = static_cast<int>(rcPlace.right - rcPlace.left) - xa - 1;
|
|
|
|
bool xStraight = isEndMarker; // x-mirrored symbol for start marker
|
|
|
|
int x0 = static_cast<int>(xStraight ? rcPlace.left : rcPlace.right - 1);
|
|
int y0 = static_cast<int>(rcPlace.top);
|
|
|
|
int dy = static_cast<int>(rcPlace.bottom - rcPlace.top) / 5;
|
|
int y = static_cast<int>(rcPlace.bottom - rcPlace.top) / 2 + dy;
|
|
|
|
struct Relative {
|
|
Surface *surface;
|
|
int xBase;
|
|
int xDir;
|
|
int yBase;
|
|
int yDir;
|
|
void MoveTo(int xRelative, int yRelative) {
|
|
surface->MoveTo(xBase + xDir * xRelative, yBase + yDir * yRelative);
|
|
}
|
|
void LineTo(int xRelative, int yRelative) {
|
|
surface->LineTo(xBase + xDir * xRelative, yBase + yDir * yRelative);
|
|
}
|
|
};
|
|
Relative rel = {surface, x0, xStraight ? 1 : -1, y0, 1};
|
|
|
|
// arrow head
|
|
rel.MoveTo(xa, y);
|
|
rel.LineTo(xa + 2*w / 3, y - dy);
|
|
rel.MoveTo(xa, y);
|
|
rel.LineTo(xa + 2*w / 3, y + dy);
|
|
|
|
// arrow body
|
|
rel.MoveTo(xa, y);
|
|
rel.LineTo(xa + w, y);
|
|
rel.LineTo(xa + w, y - 2 * dy);
|
|
rel.LineTo(xa - 1, // on windows lineto is exclusive endpoint, perhaps GTK not...
|
|
y - 2 * dy);
|
|
}
|
|
|
|
static void SimpleAlphaRectangle(Surface *surface, PRectangle rc, ColourDesired fill, int alpha) {
|
|
if (alpha != SC_ALPHA_NOALPHA) {
|
|
surface->AlphaRectangle(rc, 0, fill, alpha, fill, alpha, 0);
|
|
}
|
|
}
|
|
|
|
void DrawTextBlob(Surface *surface, ViewStyle &vsDraw, PRectangle rcSegment,
|
|
const char *s, ColourDesired textBack, ColourDesired textFore, bool twoPhaseDraw) {
|
|
if (!twoPhaseDraw) {
|
|
surface->FillRectangle(rcSegment, textBack);
|
|
}
|
|
Font &ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
|
|
int normalCharHeight = static_cast<int>(surface->Ascent(ctrlCharsFont) -
|
|
surface->InternalLeading(ctrlCharsFont));
|
|
PRectangle rcCChar = rcSegment;
|
|
rcCChar.left = rcCChar.left + 1;
|
|
rcCChar.top = rcSegment.top + vsDraw.maxAscent - normalCharHeight;
|
|
rcCChar.bottom = rcSegment.top + vsDraw.maxAscent + 1;
|
|
PRectangle rcCentral = rcCChar;
|
|
rcCentral.top++;
|
|
rcCentral.bottom--;
|
|
surface->FillRectangle(rcCentral, textFore);
|
|
PRectangle rcChar = rcCChar;
|
|
rcChar.left++;
|
|
rcChar.right--;
|
|
surface->DrawTextClipped(rcChar, ctrlCharsFont,
|
|
rcSegment.top + vsDraw.maxAscent, s, istrlen(s),
|
|
textBack, textFore);
|
|
}
|
|
|
|
void Editor::DrawEOL(Surface *surface, ViewStyle &vsDraw, PRectangle rcLine, LineLayout *ll,
|
|
int line, int lineEnd, int xStart, int subLine, XYACCUMULATOR subLineStart,
|
|
bool overrideBackground, ColourDesired background,
|
|
bool drawWrapMarkEnd, ColourDesired wrapColour) {
|
|
|
|
const int posLineStart = pdoc->LineStart(line);
|
|
const int styleMask = pdoc->stylingBitsMask;
|
|
PRectangle rcSegment = rcLine;
|
|
|
|
const bool lastSubLine = subLine == (ll->lines - 1);
|
|
XYPOSITION virtualSpace = 0;
|
|
if (lastSubLine) {
|
|
const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
|
|
virtualSpace = sel.VirtualSpaceFor(pdoc->LineEnd(line)) * spaceWidth;
|
|
}
|
|
XYPOSITION xEol = static_cast<XYPOSITION>(ll->positions[lineEnd] - subLineStart);
|
|
|
|
// Fill the virtual space and show selections within it
|
|
if (virtualSpace) {
|
|
rcSegment.left = xEol + xStart;
|
|
rcSegment.right = xEol + xStart + virtualSpace;
|
|
surface->FillRectangle(rcSegment, overrideBackground ? background : vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back);
|
|
if (!hideSelection && ((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA))) {
|
|
SelectionSegment virtualSpaceRange(SelectionPosition(pdoc->LineEnd(line)), SelectionPosition(pdoc->LineEnd(line), sel.VirtualSpaceFor(pdoc->LineEnd(line))));
|
|
for (size_t r=0; r<sel.Count(); r++) {
|
|
int alpha = (r == sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
|
|
if (alpha == SC_ALPHA_NOALPHA) {
|
|
SelectionSegment portion = sel.Range(r).Intersect(virtualSpaceRange);
|
|
if (!portion.Empty()) {
|
|
const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
|
|
rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] -
|
|
static_cast<XYPOSITION>(subLineStart) + portion.start.VirtualSpace() * spaceWidth;
|
|
rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] -
|
|
static_cast<XYPOSITION>(subLineStart) + portion.end.VirtualSpace() * spaceWidth;
|
|
rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left;
|
|
rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right;
|
|
surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, r == sel.Main()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int eolInSelection = 0;
|
|
int alpha = SC_ALPHA_NOALPHA;
|
|
if (!hideSelection) {
|
|
int posAfterLineEnd = pdoc->LineStart(line + 1);
|
|
eolInSelection = (subLine == (ll->lines - 1)) ? sel.InSelectionForEOL(posAfterLineEnd) : 0;
|
|
alpha = (eolInSelection == 1) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
|
|
}
|
|
|
|
// Draw the [CR], [LF], or [CR][LF] blobs if visible line ends are on
|
|
XYPOSITION blobsWidth = 0;
|
|
if (lastSubLine) {
|
|
for (int eolPos=ll->numCharsBeforeEOL; eolPos<ll->numCharsInLine; eolPos++) {
|
|
rcSegment.left = xStart + ll->positions[eolPos] - static_cast<XYPOSITION>(subLineStart) + virtualSpace;
|
|
rcSegment.right = xStart + ll->positions[eolPos + 1] - static_cast<XYPOSITION>(subLineStart) + virtualSpace;
|
|
blobsWidth += rcSegment.Width();
|
|
char hexits[4];
|
|
const char *ctrlChar;
|
|
unsigned char chEOL = ll->chars[eolPos];
|
|
int styleMain = ll->styles[eolPos];
|
|
ColourDesired textBack = TextBackground(vsDraw, overrideBackground, background, eolInSelection, false, styleMain, eolPos, ll);
|
|
if (UTF8IsAscii(chEOL)) {
|
|
ctrlChar = ControlCharacterString(chEOL);
|
|
} else {
|
|
Representation *repr = reprs.RepresentationFromCharacter(ll->chars + eolPos, ll->numCharsInLine - eolPos);
|
|
if (repr) {
|
|
ctrlChar = repr->stringRep.c_str();
|
|
eolPos = ll->numCharsInLine;
|
|
} else {
|
|
sprintf(hexits, "x%2X", chEOL);
|
|
ctrlChar = hexits;
|
|
}
|
|
}
|
|
ColourDesired textFore = vsDraw.styles[styleMain].fore;
|
|
if (eolInSelection && vsDraw.selColours.fore.isSet) {
|
|
textFore = (eolInSelection == 1) ? vsDraw.selColours.fore : vsDraw.selAdditionalForeground;
|
|
}
|
|
if (eolInSelection && vsDraw.selColours.back.isSet && (line < pdoc->LinesTotal() - 1)) {
|
|
if (alpha == SC_ALPHA_NOALPHA) {
|
|
surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
|
|
} else {
|
|
surface->FillRectangle(rcSegment, textBack);
|
|
}
|
|
} else {
|
|
surface->FillRectangle(rcSegment, textBack);
|
|
}
|
|
DrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, textBack, textFore, twoPhaseDraw);
|
|
if (eolInSelection && vsDraw.selColours.back.isSet && (line < pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
|
|
SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Draw the eol-is-selected rectangle
|
|
rcSegment.left = xEol + xStart + virtualSpace + blobsWidth;
|
|
rcSegment.right = rcSegment.left + vsDraw.aveCharWidth;
|
|
|
|
if (eolInSelection && vsDraw.selColours.back.isSet && (line < pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
|
|
surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
|
|
} else {
|
|
if (overrideBackground) {
|
|
surface->FillRectangle(rcSegment, background);
|
|
} else if (line < pdoc->LinesTotal() - 1) {
|
|
surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back);
|
|
} else if (vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].eolFilled) {
|
|
surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back);
|
|
} else {
|
|
surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back);
|
|
}
|
|
if (eolInSelection && vsDraw.selColours.back.isSet && (line < pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
|
|
SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
|
|
}
|
|
}
|
|
|
|
// Fill the remainder of the line
|
|
rcSegment.left = rcSegment.right;
|
|
if (rcSegment.left < rcLine.left)
|
|
rcSegment.left = rcLine.left;
|
|
rcSegment.right = rcLine.right;
|
|
|
|
if (eolInSelection && vsDraw.selEOLFilled && vsDraw.selColours.back.isSet && (line < pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
|
|
surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
|
|
} else {
|
|
if (overrideBackground) {
|
|
surface->FillRectangle(rcSegment, background);
|
|
} else if (vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].eolFilled) {
|
|
surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back);
|
|
} else {
|
|
surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back);
|
|
}
|
|
if (eolInSelection && vsDraw.selEOLFilled && vsDraw.selColours.back.isSet && (line < pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
|
|
SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
|
|
}
|
|
}
|
|
|
|
if (drawWrapMarkEnd) {
|
|
PRectangle rcPlace = rcSegment;
|
|
|
|
if (vsDraw.wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_END_BY_TEXT) {
|
|
rcPlace.left = xEol + xStart + virtualSpace;
|
|
rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
|
|
} else {
|
|
// rcLine is clipped to text area
|
|
rcPlace.right = rcLine.right;
|
|
rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
|
|
}
|
|
DrawWrapMarker(surface, rcPlace, true, wrapColour);
|
|
}
|
|
}
|
|
|
|
void Editor::DrawIndicator(int indicNum, int startPos, int endPos, Surface *surface, ViewStyle &vsDraw,
|
|
int xStart, PRectangle rcLine, LineLayout *ll, int subLine) {
|
|
const XYPOSITION subLineStart = ll->positions[ll->LineStart(subLine)];
|
|
PRectangle rcIndic(
|
|
ll->positions[startPos] + xStart - subLineStart,
|
|
rcLine.top + vsDraw.maxAscent,
|
|
ll->positions[endPos] + xStart - subLineStart,
|
|
rcLine.top + vsDraw.maxAscent + 3);
|
|
vsDraw.indicators[indicNum].Draw(surface, rcIndic, rcLine);
|
|
}
|
|
|
|
void Editor::DrawIndicators(Surface *surface, ViewStyle &vsDraw, int line, int xStart,
|
|
PRectangle rcLine, LineLayout *ll, int subLine, int lineEnd, bool under) {
|
|
// Draw decorators
|
|
const int posLineStart = pdoc->LineStart(line);
|
|
const int lineStart = ll->LineStart(subLine);
|
|
const int posLineEnd = posLineStart + lineEnd;
|
|
|
|
if (!under) {
|
|
// Draw indicators
|
|
// foreach indicator...
|
|
for (int indicnum = 0, mask = 1 << pdoc->stylingBits; mask < 0x100; indicnum++) {
|
|
if (!(mask & ll->styleBitsSet)) {
|
|
mask <<= 1;
|
|
continue;
|
|
}
|
|
int startPos = -1;
|
|
// foreach style pos in line...
|
|
for (int indicPos = lineStart; indicPos <= lineEnd; indicPos++) {
|
|
// look for starts...
|
|
if (startPos < 0) {
|
|
// NOT in indicator run, looking for START
|
|
if (indicPos < lineEnd && (ll->indicators[indicPos] & mask))
|
|
startPos = indicPos;
|
|
}
|
|
// ... or ends
|
|
if (startPos >= 0) {
|
|
// IN indicator run, looking for END
|
|
if (indicPos >= lineEnd || !(ll->indicators[indicPos] & mask)) {
|
|
// AT end of indicator run, DRAW it!
|
|
DrawIndicator(indicnum, startPos, indicPos, surface, vsDraw, xStart, rcLine, ll, subLine);
|
|
// RESET control var
|
|
startPos = -1;
|
|
}
|
|
}
|
|
}
|
|
mask <<= 1;
|
|
}
|
|
}
|
|
|
|
for (Decoration *deco = pdoc->decorations.root; deco; deco = deco->next) {
|
|
if (under == vsDraw.indicators[deco->indicator].under) {
|
|
int startPos = posLineStart + lineStart;
|
|
if (!deco->rs.ValueAt(startPos)) {
|
|
startPos = deco->rs.EndRun(startPos);
|
|
}
|
|
while ((startPos < posLineEnd) && (deco->rs.ValueAt(startPos))) {
|
|
int endPos = deco->rs.EndRun(startPos);
|
|
if (endPos > posLineEnd)
|
|
endPos = posLineEnd;
|
|
DrawIndicator(deco->indicator, startPos - posLineStart, endPos - posLineStart,
|
|
surface, vsDraw, xStart, rcLine, ll, subLine);
|
|
startPos = endPos;
|
|
if (!deco->rs.ValueAt(startPos)) {
|
|
startPos = deco->rs.EndRun(startPos);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Use indicators to highlight matching braces
|
|
if ((vs.braceHighlightIndicatorSet && (bracesMatchStyle == STYLE_BRACELIGHT)) ||
|
|
(vs.braceBadLightIndicatorSet && (bracesMatchStyle == STYLE_BRACEBAD))) {
|
|
int braceIndicator = (bracesMatchStyle == STYLE_BRACELIGHT) ? vs.braceHighlightIndicator : vs.braceBadLightIndicator;
|
|
if (under == vsDraw.indicators[braceIndicator].under) {
|
|
Range rangeLine(posLineStart + lineStart, posLineEnd);
|
|
if (rangeLine.ContainsCharacter(braces[0])) {
|
|
int braceOffset = braces[0] - posLineStart;
|
|
if (braceOffset < ll->numCharsInLine) {
|
|
DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, xStart, rcLine, ll, subLine);
|
|
}
|
|
}
|
|
if (rangeLine.ContainsCharacter(braces[1])) {
|
|
int braceOffset = braces[1] - posLineStart;
|
|
if (braceOffset < ll->numCharsInLine) {
|
|
DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, xStart, rcLine, ll, subLine);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Editor::DrawAnnotation(Surface *surface, ViewStyle &vsDraw, int line, int xStart,
|
|
PRectangle rcLine, LineLayout *ll, int subLine) {
|
|
int indent = static_cast<int>(pdoc->GetLineIndentation(line) * vsDraw.spaceWidth);
|
|
PRectangle rcSegment = rcLine;
|
|
int annotationLine = subLine - ll->lines;
|
|
const StyledText stAnnotation = pdoc->AnnotationStyledText(line);
|
|
if (stAnnotation.text && ValidStyledText(vsDraw, vsDraw.annotationStyleOffset, stAnnotation)) {
|
|
surface->FillRectangle(rcSegment, vsDraw.styles[0].back);
|
|
rcSegment.left = static_cast<XYPOSITION>(xStart);
|
|
if (trackLineWidth || (vs.annotationVisible == ANNOTATION_BOXED)) {
|
|
// Only care about calculating width if tracking or need to draw box
|
|
int widthAnnotation = WidestLineWidth(surface, vsDraw, vsDraw.annotationStyleOffset, stAnnotation);
|
|
if (vs.annotationVisible == ANNOTATION_BOXED) {
|
|
widthAnnotation += static_cast<int>(vsDraw.spaceWidth * 2); // Margins
|
|
}
|
|
if (widthAnnotation > lineWidthMaxSeen)
|
|
lineWidthMaxSeen = widthAnnotation;
|
|
if (vs.annotationVisible == ANNOTATION_BOXED) {
|
|
rcSegment.left = static_cast<XYPOSITION>(xStart + indent);
|
|
rcSegment.right = rcSegment.left + widthAnnotation;
|
|
}
|
|
}
|
|
const int annotationLines = pdoc->AnnotationLines(line);
|
|
size_t start = 0;
|
|
size_t lengthAnnotation = stAnnotation.LineLength(start);
|
|
int lineInAnnotation = 0;
|
|
while ((lineInAnnotation < annotationLine) && (start < stAnnotation.length)) {
|
|
start += lengthAnnotation + 1;
|
|
lengthAnnotation = stAnnotation.LineLength(start);
|
|
lineInAnnotation++;
|
|
}
|
|
PRectangle rcText = rcSegment;
|
|
if (vs.annotationVisible == ANNOTATION_BOXED) {
|
|
surface->FillRectangle(rcText,
|
|
vsDraw.styles[stAnnotation.StyleAt(start) + vsDraw.annotationStyleOffset].back);
|
|
rcText.left += vsDraw.spaceWidth;
|
|
}
|
|
DrawStyledText(surface, vsDraw, vsDraw.annotationStyleOffset, rcText, static_cast<int>(rcText.top + vsDraw.maxAscent),
|
|
stAnnotation, start, lengthAnnotation);
|
|
if (vs.annotationVisible == ANNOTATION_BOXED) {
|
|
surface->PenColour(vsDraw.styles[vsDraw.annotationStyleOffset].fore);
|
|
surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.top));
|
|
surface->LineTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.bottom));
|
|
surface->MoveTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.top));
|
|
surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.bottom));
|
|
if (subLine == ll->lines) {
|
|
surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.top));
|
|
surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.top));
|
|
}
|
|
if (subLine == ll->lines+annotationLines-1) {
|
|
surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.bottom - 1));
|
|
surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.bottom - 1));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVisible, int xStart,
|
|
PRectangle rcLine, LineLayout *ll, int subLine) {
|
|
|
|
if (subLine >= ll->lines) {
|
|
DrawAnnotation(surface, vsDraw, line, xStart, rcLine, ll, subLine);
|
|
return; // No further drawing
|
|
}
|
|
|
|
PRectangle rcSegment = rcLine;
|
|
|
|
// Using one font for all control characters so it can be controlled independently to ensure
|
|
// the box goes around the characters tightly. Seems to be no way to work out what height
|
|
// is taken by an individual character - internal leading gives varying results.
|
|
Font &ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
|
|
|
|
// See if something overrides the line background color: Either if caret is on the line
|
|
// and background color is set for that, or if a marker is defined that forces its background
|
|
// color onto the line, or if a marker is defined but has no selection margin in which to
|
|
// display itself (as long as it's not an SC_MARK_EMPTY marker). These are checked in order
|
|
// with the earlier taking precedence. When multiple markers cause background override,
|
|
// the color for the highest numbered one is used.
|
|
bool overrideBackground = false;
|
|
ColourDesired background;
|
|
if ((caret.active || vsDraw.alwaysShowCaretLineBackground) && vsDraw.showCaretLineBackground && (vsDraw.caretLineAlpha == SC_ALPHA_NOALPHA) && ll->containsCaret) {
|
|
overrideBackground = true;
|
|
background = vsDraw.caretLineBackground;
|
|
}
|
|
if (!overrideBackground) {
|
|
int marks = pdoc->GetMark(line);
|
|
for (int markBit = 0; (markBit < 32) && marks; markBit++) {
|
|
if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND) &&
|
|
(vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
|
|
background = vsDraw.markers[markBit].back;
|
|
overrideBackground = true;
|
|
}
|
|
marks >>= 1;
|
|
}
|
|
}
|
|
if (!overrideBackground) {
|
|
if (vsDraw.maskInLine) {
|
|
int marksMasked = pdoc->GetMark(line) & vsDraw.maskInLine;
|
|
if (marksMasked) {
|
|
for (int markBit = 0; (markBit < 32) && marksMasked; markBit++) {
|
|
if ((marksMasked & 1) && (vsDraw.markers[markBit].markType != SC_MARK_EMPTY) &&
|
|
(vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
|
|
overrideBackground = true;
|
|
background = vsDraw.markers[markBit].back;
|
|
}
|
|
marksMasked >>= 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const bool drawWhitespaceBackground = (vsDraw.viewWhitespace != wsInvisible) &&
|
|
(!overrideBackground) && (vsDraw.whitespaceColours.back.isSet);
|
|
|
|
bool inIndentation = subLine == 0; // Do not handle indentation except on first subline.
|
|
const XYPOSITION indentWidth = pdoc->IndentSize() * vsDraw.spaceWidth;
|
|
const XYPOSITION epsilon = 0.0001f; // A small nudge to avoid floating point precision issues
|
|
|
|
const int posLineStart = pdoc->LineStart(line);
|
|
|
|
const int startseg = ll->LineStart(subLine);
|
|
const XYACCUMULATOR subLineStart = ll->positions[startseg];
|
|
int lineStart = 0;
|
|
int lineEnd = 0;
|
|
if (subLine < ll->lines) {
|
|
lineStart = ll->LineStart(subLine);
|
|
lineEnd = ll->LineStart(subLine + 1);
|
|
if (subLine == ll->lines - 1) {
|
|
lineEnd = ll->numCharsBeforeEOL;
|
|
}
|
|
}
|
|
|
|
const ColourDesired wrapColour = vsDraw.WrapColour();
|
|
|
|
bool drawWrapMarkEnd = false;
|
|
|
|
if (vsDraw.wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
|
|
if (subLine + 1 < ll->lines) {
|
|
drawWrapMarkEnd = ll->LineStart(subLine + 1) != 0;
|
|
}
|
|
}
|
|
|
|
if (ll->wrapIndent != 0) {
|
|
|
|
bool continuedWrapLine = false;
|
|
if (subLine < ll->lines) {
|
|
continuedWrapLine = ll->LineStart(subLine) != 0;
|
|
}
|
|
|
|
if (continuedWrapLine) {
|
|
// draw continuation rect
|
|
PRectangle rcPlace = rcSegment;
|
|
|
|
rcPlace.left = ll->positions[startseg] + xStart - static_cast<XYPOSITION>(subLineStart);
|
|
rcPlace.right = rcPlace.left + ll->wrapIndent;
|
|
|
|
// default bgnd here..
|
|
surface->FillRectangle(rcSegment, overrideBackground ? background :
|
|
vsDraw.styles[STYLE_DEFAULT].back);
|
|
|
|
// main line style would be below but this would be inconsistent with end markers
|
|
// also would possibly not be the style at wrap point
|
|
//int styleMain = ll->styles[lineStart];
|
|
//surface->FillRectangle(rcPlace, vsDraw.styles[styleMain].back);
|
|
|
|
if (vsDraw.wrapVisualFlags & SC_WRAPVISUALFLAG_START) {
|
|
|
|
if (vsDraw.wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_START_BY_TEXT)
|
|
rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
|
|
else
|
|
rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
|
|
|
|
DrawWrapMarker(surface, rcPlace, false, wrapColour);
|
|
}
|
|
|
|
xStart += static_cast<int>(ll->wrapIndent);
|
|
}
|
|
}
|
|
|
|
const bool selBackDrawn = vsDraw.selColours.back.isSet &&
|
|
((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA));
|
|
|
|
// Does not take margin into account but not significant
|
|
const int xStartVisible = static_cast<int>(subLineStart) - xStart;
|
|
|
|
ll->psel = &sel;
|
|
|
|
if (twoPhaseDraw) {
|
|
BreakFinder bfBack(ll, lineStart, lineEnd, posLineStart, xStartVisible, selBackDrawn, pdoc, &reprs);
|
|
|
|
// Background drawing loop
|
|
while (bfBack.More()) {
|
|
|
|
const TextSegment ts = bfBack.Next();
|
|
const int i = ts.end() - 1;
|
|
const int iDoc = i + posLineStart;
|
|
|
|
rcSegment.left = ll->positions[ts.start] + xStart - static_cast<XYPOSITION>(subLineStart);
|
|
rcSegment.right = ll->positions[ts.end()] + xStart - static_cast<XYPOSITION>(subLineStart);
|
|
// Only try to draw if really visible - enhances performance by not calling environment to
|
|
// draw strings that are completely past the right side of the window.
|
|
if (rcSegment.Intersects(rcLine)) {
|
|
// Clip to line rectangle, since may have a huge position which will not work with some platforms
|
|
if (rcSegment.left < rcLine.left)
|
|
rcSegment.left = rcLine.left;
|
|
if (rcSegment.right > rcLine.right)
|
|
rcSegment.right = rcLine.right;
|
|
|
|
const int inSelection = hideSelection ? 0 : sel.CharacterInSelection(iDoc);
|
|
const bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd);
|
|
ColourDesired textBack = TextBackground(vsDraw, overrideBackground, background, inSelection,
|
|
inHotspot, ll->styles[i], i, ll);
|
|
if (ts.representation) {
|
|
if (ll->chars[i] == '\t') {
|
|
// Tab display
|
|
if (drawWhitespaceBackground &&
|
|
(!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
|
|
textBack = vsDraw.whitespaceColours.back;
|
|
} else {
|
|
// Blob display
|
|
inIndentation = false;
|
|
}
|
|
surface->FillRectangle(rcSegment, textBack);
|
|
} else {
|
|
// Normal text display
|
|
surface->FillRectangle(rcSegment, textBack);
|
|
if (vsDraw.viewWhitespace != wsInvisible ||
|
|
(inIndentation && vsDraw.viewIndentationGuides == ivReal)) {
|
|
for (int cpos = 0; cpos <= i - ts.start; cpos++) {
|
|
if (ll->chars[cpos + ts.start] == ' ') {
|
|
if (drawWhitespaceBackground &&
|
|
(!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
|
|
PRectangle rcSpace(
|
|
ll->positions[cpos + ts.start] + xStart - static_cast<XYPOSITION>(subLineStart),
|
|
rcSegment.top,
|
|
ll->positions[cpos + ts.start + 1] + xStart - static_cast<XYPOSITION>(subLineStart),
|
|
rcSegment.bottom);
|
|
surface->FillRectangle(rcSpace, vsDraw.whitespaceColours.back);
|
|
}
|
|
} else {
|
|
inIndentation = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (rcSegment.left > rcLine.right) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
DrawEOL(surface, vsDraw, rcLine, ll, line, lineEnd,
|
|
xStart, subLine, subLineStart, overrideBackground, background,
|
|
drawWrapMarkEnd, wrapColour);
|
|
}
|
|
|
|
DrawIndicators(surface, vsDraw, line, xStart, rcLine, ll, subLine, lineEnd, true);
|
|
|
|
if (vsDraw.edgeState == EDGE_LINE) {
|
|
int edgeX = static_cast<int>(vsDraw.theEdge * vsDraw.spaceWidth);
|
|
rcSegment.left = static_cast<XYPOSITION>(edgeX + xStart);
|
|
if ((ll->wrapIndent != 0) && (lineStart != 0))
|
|
rcSegment.left -= ll->wrapIndent;
|
|
rcSegment.right = rcSegment.left + 1;
|
|
surface->FillRectangle(rcSegment, vsDraw.edgecolour);
|
|
}
|
|
|
|
// Draw underline mark as part of background if not transparent
|
|
int marks = pdoc->GetMark(line);
|
|
int markBit;
|
|
for (markBit = 0; (markBit < 32) && marks; markBit++) {
|
|
if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE) &&
|
|
(vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
|
|
PRectangle rcUnderline = rcLine;
|
|
rcUnderline.top = rcUnderline.bottom - 2;
|
|
surface->FillRectangle(rcUnderline, vsDraw.markers[markBit].back);
|
|
}
|
|
marks >>= 1;
|
|
}
|
|
|
|
inIndentation = subLine == 0; // Do not handle indentation except on first subline.
|
|
// Foreground drawing loop
|
|
BreakFinder bfFore(ll, lineStart, lineEnd, posLineStart, xStartVisible,
|
|
((!twoPhaseDraw && selBackDrawn) || vsDraw.selColours.fore.isSet), pdoc, &reprs);
|
|
|
|
while (bfFore.More()) {
|
|
|
|
const TextSegment ts = bfFore.Next();
|
|
const int i = ts.end() - 1;
|
|
const int iDoc = i + posLineStart;
|
|
|
|
rcSegment.left = ll->positions[ts.start] + xStart - static_cast<XYPOSITION>(subLineStart);
|
|
rcSegment.right = ll->positions[ts.end()] + xStart - static_cast<XYPOSITION>(subLineStart);
|
|
// Only try to draw if really visible - enhances performance by not calling environment to
|
|
// draw strings that are completely past the right side of the window.
|
|
if (rcSegment.Intersects(rcLine)) {
|
|
int styleMain = ll->styles[i];
|
|
ColourDesired textFore = vsDraw.styles[styleMain].fore;
|
|
Font &textFont = vsDraw.styles[styleMain].font;
|
|
//hotspot foreground
|
|
if (ll->hsStart != -1 && iDoc >= ll->hsStart && iDoc < hsEnd) {
|
|
if (vsDraw.hotspotColours.fore.isSet)
|
|
textFore = vsDraw.hotspotColours.fore;
|
|
}
|
|
const int inSelection = hideSelection ? 0 : sel.CharacterInSelection(iDoc);
|
|
if (inSelection && (vsDraw.selColours.fore.isSet)) {
|
|
textFore = (inSelection == 1) ? vsDraw.selColours.fore : vsDraw.selAdditionalForeground;
|
|
}
|
|
const bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd);
|
|
ColourDesired textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, i, ll);
|
|
if (ts.representation) {
|
|
if (ll->chars[i] == '\t') {
|
|
// Tab display
|
|
if (!twoPhaseDraw) {
|
|
if (drawWhitespaceBackground &&
|
|
(!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
|
|
textBack = vsDraw.whitespaceColours.back;
|
|
surface->FillRectangle(rcSegment, textBack);
|
|
}
|
|
if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
|
|
for (int indentCount = static_cast<int>((ll->positions[i] + epsilon) / indentWidth);
|
|
indentCount <= (ll->positions[i + 1] - epsilon) / indentWidth;
|
|
indentCount++) {
|
|
if (indentCount > 0) {
|
|
int xIndent = static_cast<int>(indentCount * indentWidth);
|
|
DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
|
|
(ll->xHighlightGuide == xIndent));
|
|
}
|
|
}
|
|
}
|
|
if (vsDraw.viewWhitespace != wsInvisible) {
|
|
if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
|
|
if (vsDraw.whitespaceColours.fore.isSet)
|
|
textFore = vsDraw.whitespaceColours.fore;
|
|
surface->PenColour(textFore);
|
|
PRectangle rcTab(rcSegment.left + 1, rcSegment.top + 4,
|
|
rcSegment.right - 1, rcSegment.bottom - vsDraw.maxDescent);
|
|
DrawTabArrow(surface, rcTab, static_cast<int>(rcSegment.top + vsDraw.lineHeight / 2));
|
|
}
|
|
}
|
|
} else {
|
|
inIndentation = false;
|
|
if (vsDraw.controlCharSymbol >= 32) {
|
|
char cc[2] = { static_cast<char>(vsDraw.controlCharSymbol), '\0' };
|
|
surface->DrawTextNoClip(rcSegment, ctrlCharsFont,
|
|
rcSegment.top + vsDraw.maxAscent,
|
|
cc, 1, textBack, textFore);
|
|
} else {
|
|
DrawTextBlob(surface, vsDraw, rcSegment, ts.representation->stringRep.c_str(), textBack, textFore, twoPhaseDraw);
|
|
}
|
|
}
|
|
} else {
|
|
// Normal text display
|
|
if (vsDraw.styles[styleMain].visible) {
|
|
if (twoPhaseDraw) {
|
|
surface->DrawTextTransparent(rcSegment, textFont,
|
|
rcSegment.top + vsDraw.maxAscent, ll->chars + ts.start,
|
|
i - ts.start + 1, textFore);
|
|
} else {
|
|
surface->DrawTextNoClip(rcSegment, textFont,
|
|
rcSegment.top + vsDraw.maxAscent, ll->chars + ts.start,
|
|
i - ts.start + 1, textFore, textBack);
|
|
}
|
|
}
|
|
if (vsDraw.viewWhitespace != wsInvisible ||
|
|
(inIndentation && vsDraw.viewIndentationGuides != ivNone)) {
|
|
for (int cpos = 0; cpos <= i - ts.start; cpos++) {
|
|
if (ll->chars[cpos + ts.start] == ' ') {
|
|
if (vsDraw.viewWhitespace != wsInvisible) {
|
|
if (vsDraw.whitespaceColours.fore.isSet)
|
|
textFore = vsDraw.whitespaceColours.fore;
|
|
if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
|
|
XYPOSITION xmid = (ll->positions[cpos + ts.start] + ll->positions[cpos + ts.start + 1]) / 2;
|
|
if (!twoPhaseDraw && drawWhitespaceBackground &&
|
|
(!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
|
|
textBack = vsDraw.whitespaceColours.back;
|
|
PRectangle rcSpace(
|
|
ll->positions[cpos + ts.start] + xStart - static_cast<XYPOSITION>(subLineStart),
|
|
rcSegment.top,
|
|
ll->positions[cpos + ts.start + 1] + xStart - static_cast<XYPOSITION>(subLineStart),
|
|
rcSegment.bottom);
|
|
surface->FillRectangle(rcSpace, textBack);
|
|
}
|
|
PRectangle rcDot(xmid + xStart - static_cast<XYPOSITION>(subLineStart),
|
|
rcSegment.top + vsDraw.lineHeight / 2, 0.0f, 0.0f);
|
|
rcDot.right = rcDot.left + vs.whitespaceSize;
|
|
rcDot.bottom = rcDot.top + vs.whitespaceSize;
|
|
surface->FillRectangle(rcDot, textFore);
|
|
}
|
|
}
|
|
if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
|
|
for (int indentCount = static_cast<int>((ll->positions[cpos + ts.start] + epsilon) / indentWidth);
|
|
indentCount <= (ll->positions[cpos + ts.start + 1] - epsilon) / indentWidth;
|
|
indentCount++) {
|
|
if (indentCount > 0) {
|
|
int xIndent = static_cast<int>(indentCount * indentWidth);
|
|
DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
|
|
(ll->xHighlightGuide == xIndent));
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
inIndentation = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (ll->hsStart != -1 && vsDraw.hotspotUnderline && iDoc >= ll->hsStart && iDoc < ll->hsEnd) {
|
|
PRectangle rcUL = rcSegment;
|
|
rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
|
|
rcUL.bottom = rcUL.top + 1;
|
|
if (vsDraw.hotspotColours.fore.isSet)
|
|
surface->FillRectangle(rcUL, vsDraw.hotspotColours.fore);
|
|
else
|
|
surface->FillRectangle(rcUL, textFore);
|
|
} else if (vsDraw.styles[styleMain].underline) {
|
|
PRectangle rcUL = rcSegment;
|
|
rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
|
|
rcUL.bottom = rcUL.top + 1;
|
|
surface->FillRectangle(rcUL, textFore);
|
|
}
|
|
} else if (rcSegment.left > rcLine.right) {
|
|
break;
|
|
}
|
|
}
|
|
if ((vsDraw.viewIndentationGuides == ivLookForward || vsDraw.viewIndentationGuides == ivLookBoth)
|
|
&& (subLine == 0)) {
|
|
int indentSpace = pdoc->GetLineIndentation(line);
|
|
int xStartText = static_cast<int>(ll->positions[pdoc->GetLineIndentPosition(line) - posLineStart]);
|
|
|
|
// Find the most recent line with some text
|
|
|
|
int lineLastWithText = line;
|
|
while (lineLastWithText > Platform::Maximum(line-20, 0) && pdoc->IsWhiteLine(lineLastWithText)) {
|
|
lineLastWithText--;
|
|
}
|
|
if (lineLastWithText < line) {
|
|
xStartText = 100000; // Don't limit to visible indentation on empty line
|
|
// This line is empty, so use indentation of last line with text
|
|
int indentLastWithText = pdoc->GetLineIndentation(lineLastWithText);
|
|
int isFoldHeader = pdoc->GetLevel(lineLastWithText) & SC_FOLDLEVELHEADERFLAG;
|
|
if (isFoldHeader) {
|
|
// Level is one more level than parent
|
|
indentLastWithText += pdoc->IndentSize();
|
|
}
|
|
if (vsDraw.viewIndentationGuides == ivLookForward) {
|
|
// In viLookForward mode, previous line only used if it is a fold header
|
|
if (isFoldHeader) {
|
|
indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
|
|
}
|
|
} else { // viLookBoth
|
|
indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
|
|
}
|
|
}
|
|
|
|
int lineNextWithText = line;
|
|
while (lineNextWithText < Platform::Minimum(line+20, pdoc->LinesTotal()) && pdoc->IsWhiteLine(lineNextWithText)) {
|
|
lineNextWithText++;
|
|
}
|
|
if (lineNextWithText > line) {
|
|
xStartText = 100000; // Don't limit to visible indentation on empty line
|
|
// This line is empty, so use indentation of first next line with text
|
|
indentSpace = Platform::Maximum(indentSpace,
|
|
pdoc->GetLineIndentation(lineNextWithText));
|
|
}
|
|
|
|
for (int indentPos = pdoc->IndentSize(); indentPos < indentSpace; indentPos += pdoc->IndentSize()) {
|
|
int xIndent = static_cast<int>(indentPos * vsDraw.spaceWidth);
|
|
if (xIndent < xStartText) {
|
|
DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
|
|
(ll->xHighlightGuide == xIndent));
|
|
}
|
|
}
|
|
}
|
|
|
|
DrawIndicators(surface, vsDraw, line, xStart, rcLine, ll, subLine, lineEnd, false);
|
|
|
|
// End of the drawing of the current line
|
|
if (!twoPhaseDraw) {
|
|
DrawEOL(surface, vsDraw, rcLine, ll, line, lineEnd,
|
|
xStart, subLine, subLineStart, overrideBackground, background,
|
|
drawWrapMarkEnd, wrapColour);
|
|
}
|
|
if (!hideSelection && ((vsDraw.selAlpha != SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha != SC_ALPHA_NOALPHA))) {
|
|
// For each selection draw
|
|
int virtualSpaces = 0;
|
|
if (subLine == (ll->lines - 1)) {
|
|
virtualSpaces = sel.VirtualSpaceFor(pdoc->LineEnd(line));
|
|
}
|
|
SelectionPosition posStart(posLineStart + lineStart);
|
|
SelectionPosition posEnd(posLineStart + lineEnd, virtualSpaces);
|
|
SelectionSegment virtualSpaceRange(posStart, posEnd);
|
|
for (size_t r=0; r<sel.Count(); r++) {
|
|
int alpha = (r == sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
|
|
if (alpha != SC_ALPHA_NOALPHA) {
|
|
SelectionSegment portion = sel.Range(r).Intersect(virtualSpaceRange);
|
|
if (!portion.Empty()) {
|
|
const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
|
|
rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] -
|
|
static_cast<XYPOSITION>(subLineStart) + portion.start.VirtualSpace() * spaceWidth;
|
|
rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] -
|
|
static_cast<XYPOSITION>(subLineStart) + portion.end.VirtualSpace() * spaceWidth;
|
|
if ((ll->wrapIndent != 0) && (lineStart != 0)) {
|
|
if ((portion.start.Position() - posLineStart) == lineStart && sel.Range(r).ContainsCharacter(portion.start.Position() - 1))
|
|
rcSegment.left -= static_cast<int>(ll->wrapIndent); // indentation added to xStart was truncated to int, so we do the same here
|
|
}
|
|
rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left;
|
|
rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right;
|
|
if (rcSegment.right > rcLine.left)
|
|
SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, r == sel.Main()), alpha);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Draw any translucent whole line states
|
|
rcSegment = rcLine;
|
|
if ((caret.active || vsDraw.alwaysShowCaretLineBackground) && vsDraw.showCaretLineBackground && ll->containsCaret) {
|
|
SimpleAlphaRectangle(surface, rcSegment, vsDraw.caretLineBackground, vsDraw.caretLineAlpha);
|
|
}
|
|
marks = pdoc->GetMark(line);
|
|
for (markBit = 0; (markBit < 32) && marks; markBit++) {
|
|
if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND)) {
|
|
SimpleAlphaRectangle(surface, rcSegment, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
|
|
} else if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE)) {
|
|
PRectangle rcUnderline = rcSegment;
|
|
rcUnderline.top = rcUnderline.bottom - 2;
|
|
SimpleAlphaRectangle(surface, rcUnderline, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
|
|
}
|
|
marks >>= 1;
|
|
}
|
|
if (vsDraw.maskInLine) {
|
|
int marksMasked = pdoc->GetMark(line) & vsDraw.maskInLine;
|
|
if (marksMasked) {
|
|
for (markBit = 0; (markBit < 32) && marksMasked; markBit++) {
|
|
if ((marksMasked & 1) && (vsDraw.markers[markBit].markType != SC_MARK_EMPTY)) {
|
|
SimpleAlphaRectangle(surface, rcSegment, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
|
|
}
|
|
marksMasked >>= 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Editor::DrawBlockCaret(Surface *surface, ViewStyle &vsDraw, LineLayout *ll, int subLine,
|
|
int xStart, int offset, int posCaret, PRectangle rcCaret, ColourDesired caretColour) {
|
|
|
|
int lineStart = ll->LineStart(subLine);
|
|
int posBefore = posCaret;
|
|
int posAfter = MovePositionOutsideChar(posCaret + 1, 1);
|
|
int numCharsToDraw = posAfter - posCaret;
|
|
|
|
// Work out where the starting and ending offsets are. We need to
|
|
// see if the previous character shares horizontal space, such as a
|
|
// glyph / combining character. If so we'll need to draw that too.
|
|
int offsetFirstChar = offset;
|
|
int offsetLastChar = offset + (posAfter - posCaret);
|
|
while ((posBefore > 0) && ((offsetLastChar - numCharsToDraw) >= lineStart)) {
|
|
if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - numCharsToDraw]) > 0) {
|
|
// The char does not share horizontal space
|
|
break;
|
|
}
|
|
// Char shares horizontal space, update the numChars to draw
|
|
// Update posBefore to point to the prev char
|
|
posBefore = MovePositionOutsideChar(posBefore - 1, -1);
|
|
numCharsToDraw = posAfter - posBefore;
|
|
offsetFirstChar = offset - (posCaret - posBefore);
|
|
}
|
|
|
|
// See if the next character shares horizontal space, if so we'll
|
|
// need to draw that too.
|
|
if (offsetFirstChar < 0)
|
|
offsetFirstChar = 0;
|
|
numCharsToDraw = offsetLastChar - offsetFirstChar;
|
|
while ((offsetLastChar < ll->LineStart(subLine + 1)) && (offsetLastChar <= ll->numCharsInLine)) {
|
|
// Update posAfter to point to the 2nd next char, this is where
|
|
// the next character ends, and 2nd next begins. We'll need
|
|
// to compare these two
|
|
posBefore = posAfter;
|
|
posAfter = MovePositionOutsideChar(posAfter + 1, 1);
|
|
offsetLastChar = offset + (posAfter - posCaret);
|
|
if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - (posAfter - posBefore)]) > 0) {
|
|
// The char does not share horizontal space
|
|
break;
|
|
}
|
|
// Char shares horizontal space, update the numChars to draw
|
|
numCharsToDraw = offsetLastChar - offsetFirstChar;
|
|
}
|
|
|
|
// We now know what to draw, update the caret drawing rectangle
|
|
rcCaret.left = ll->positions[offsetFirstChar] - ll->positions[lineStart] + xStart;
|
|
rcCaret.right = ll->positions[offsetFirstChar+numCharsToDraw] - ll->positions[lineStart] + xStart;
|
|
|
|
// Adjust caret position to take into account any word wrapping symbols.
|
|
if ((ll->wrapIndent != 0) && (lineStart != 0)) {
|
|
XYPOSITION wordWrapCharWidth = ll->wrapIndent;
|
|
rcCaret.left += wordWrapCharWidth;
|
|
rcCaret.right += wordWrapCharWidth;
|
|
}
|
|
|
|
// This character is where the caret block is, we override the colours
|
|
// (inversed) for drawing the caret here.
|
|
int styleMain = ll->styles[offsetFirstChar];
|
|
surface->DrawTextClipped(rcCaret, vsDraw.styles[styleMain].font,
|
|
rcCaret.top + vsDraw.maxAscent, ll->chars + offsetFirstChar,
|
|
numCharsToDraw, vsDraw.styles[styleMain].back,
|
|
caretColour);
|
|
}
|
|
|
|
void Editor::RefreshPixMaps(Surface *surfaceWindow) {
|
|
if (!pixmapSelPattern->Initialised()) {
|
|
const int patternSize = 8;
|
|
pixmapSelPattern->InitPixMap(patternSize, patternSize, surfaceWindow, wMain.GetID());
|
|
pixmapSelPatternOffset1->InitPixMap(patternSize, patternSize, surfaceWindow, wMain.GetID());
|
|
// This complex procedure is to reproduce the checkerboard dithered pattern used by windows
|
|
// for scroll bars and Visual Studio for its selection margin. The colour of this pattern is half
|
|
// way between the chrome colour and the chrome highlight colour making a nice transition
|
|
// between the window chrome and the content area. And it works in low colour depths.
|
|
PRectangle rcPattern = PRectangle::FromInts(0, 0, patternSize, patternSize);
|
|
|
|
// Initialize default colours based on the chrome colour scheme. Typically the highlight is white.
|
|
ColourDesired colourFMFill = vs.selbar;
|
|
ColourDesired colourFMStripes = vs.selbarlight;
|
|
|
|
if (!(vs.selbarlight == ColourDesired(0xff, 0xff, 0xff))) {
|
|
// User has chosen an unusual chrome colour scheme so just use the highlight edge colour.
|
|
// (Typically, the highlight colour is white.)
|
|
colourFMFill = vs.selbarlight;
|
|
}
|
|
|
|
if (vs.foldmarginColour.isSet) {
|
|
// override default fold margin colour
|
|
colourFMFill = vs.foldmarginColour;
|
|
}
|
|
if (vs.foldmarginHighlightColour.isSet) {
|
|
// override default fold margin highlight colour
|
|
colourFMStripes = vs.foldmarginHighlightColour;
|
|
}
|
|
|
|
pixmapSelPattern->FillRectangle(rcPattern, colourFMFill);
|
|
pixmapSelPatternOffset1->FillRectangle(rcPattern, colourFMStripes);
|
|
for (int y = 0; y < patternSize; y++) {
|
|
for (int x = y % 2; x < patternSize; x+=2) {
|
|
PRectangle rcPixel = PRectangle::FromInts(x, y, x + 1, y + 1);
|
|
pixmapSelPattern->FillRectangle(rcPixel, colourFMStripes);
|
|
pixmapSelPatternOffset1->FillRectangle(rcPixel, colourFMFill);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!pixmapIndentGuide->Initialised()) {
|
|
// 1 extra pixel in height so can handle odd/even positions and so produce a continuous line
|
|
pixmapIndentGuide->InitPixMap(1, vs.lineHeight + 1, surfaceWindow, wMain.GetID());
|
|
pixmapIndentGuideHighlight->InitPixMap(1, vs.lineHeight + 1, surfaceWindow, wMain.GetID());
|
|
PRectangle rcIG = PRectangle::FromInts(0, 0, 1, vs.lineHeight);
|
|
pixmapIndentGuide->FillRectangle(rcIG, vs.styles[STYLE_INDENTGUIDE].back);
|
|
pixmapIndentGuide->PenColour(vs.styles[STYLE_INDENTGUIDE].fore);
|
|
pixmapIndentGuideHighlight->FillRectangle(rcIG, vs.styles[STYLE_BRACELIGHT].back);
|
|
pixmapIndentGuideHighlight->PenColour(vs.styles[STYLE_BRACELIGHT].fore);
|
|
for (int stripe = 1; stripe < vs.lineHeight + 1; stripe += 2) {
|
|
PRectangle rcPixel = PRectangle::FromInts(0, stripe, 1, stripe + 1);
|
|
pixmapIndentGuide->FillRectangle(rcPixel, vs.styles[STYLE_INDENTGUIDE].fore);
|
|
pixmapIndentGuideHighlight->FillRectangle(rcPixel, vs.styles[STYLE_BRACELIGHT].fore);
|
|
}
|
|
}
|
|
|
|
if (bufferedDraw) {
|
|
if (!pixmapLine->Initialised()) {
|
|
PRectangle rcClient = GetClientRectangle();
|
|
pixmapLine->InitPixMap(static_cast<int>(rcClient.Width()), vs.lineHeight,
|
|
surfaceWindow, wMain.GetID());
|
|
pixmapSelMargin->InitPixMap(vs.fixedColumnWidth,
|
|
static_cast<int>(rcClient.Height()), surfaceWindow, wMain.GetID());
|
|
}
|
|
}
|
|
}
|
|
|
|
void Editor::DrawCarets(Surface *surface, ViewStyle &vsDraw, int lineDoc, int xStart,
|
|
PRectangle rcLine, LineLayout *ll, int subLine) {
|
|
// When drag is active it is the only caret drawn
|
|
bool drawDrag = posDrag.IsValid();
|
|
if (hideSelection && !drawDrag)
|
|
return;
|
|
const int posLineStart = pdoc->LineStart(lineDoc);
|
|
// For each selection draw
|
|
for (size_t r=0; (r<sel.Count()) || drawDrag; r++) {
|
|
const bool mainCaret = r == sel.Main();
|
|
const SelectionPosition posCaret = (drawDrag ? posDrag : sel.Range(r).caret);
|
|
const int offset = posCaret.Position() - posLineStart;
|
|
const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
|
|
const XYPOSITION virtualOffset = posCaret.VirtualSpace() * spaceWidth;
|
|
if (ll->InLine(offset, subLine) && offset <= ll->numCharsBeforeEOL) {
|
|
XYPOSITION xposCaret = ll->positions[offset] + virtualOffset - ll->positions[ll->LineStart(subLine)];
|
|
if (ll->wrapIndent != 0) {
|
|
int lineStart = ll->LineStart(subLine);
|
|
if (lineStart != 0) // Wrapped
|
|
xposCaret += ll->wrapIndent;
|
|
}
|
|
bool caretBlinkState = (caret.active && caret.on) || (!additionalCaretsBlink && !mainCaret);
|
|
bool caretVisibleState = additionalCaretsVisible || mainCaret;
|
|
if ((xposCaret >= 0) && (vsDraw.caretWidth > 0) && (vsDraw.caretStyle != CARETSTYLE_INVISIBLE) &&
|
|
((posDrag.IsValid()) || (caretBlinkState && caretVisibleState))) {
|
|
bool caretAtEOF = false;
|
|
bool caretAtEOL = false;
|
|
bool drawBlockCaret = false;
|
|
XYPOSITION widthOverstrikeCaret;
|
|
XYPOSITION caretWidthOffset = 0;
|
|
PRectangle rcCaret = rcLine;
|
|
|
|
if (posCaret.Position() == pdoc->Length()) { // At end of document
|
|
caretAtEOF = true;
|
|
widthOverstrikeCaret = vsDraw.aveCharWidth;
|
|
} else if ((posCaret.Position() - posLineStart) >= ll->numCharsInLine) { // At end of line
|
|
caretAtEOL = true;
|
|
widthOverstrikeCaret = vsDraw.aveCharWidth;
|
|
} else {
|
|
widthOverstrikeCaret = ll->positions[offset + 1] - ll->positions[offset];
|
|
}
|
|
if (widthOverstrikeCaret < 3) // Make sure its visible
|
|
widthOverstrikeCaret = 3;
|
|
|
|
if (xposCaret > 0)
|
|
caretWidthOffset = 0.51f; // Move back so overlaps both character cells.
|
|
xposCaret += xStart;
|
|
if (posDrag.IsValid()) {
|
|
/* Dragging text, use a line caret */
|
|
rcCaret.left = static_cast<XYPOSITION>(RoundXYPosition(xposCaret - caretWidthOffset));
|
|
rcCaret.right = rcCaret.left + vsDraw.caretWidth;
|
|
} else if (inOverstrike && drawOverstrikeCaret) {
|
|
/* Overstrike (insert mode), use a modified bar caret */
|
|
rcCaret.top = rcCaret.bottom - 2;
|
|
rcCaret.left = xposCaret + 1;
|
|
rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1;
|
|
} else if (vsDraw.caretStyle == CARETSTYLE_BLOCK) {
|
|
/* Block caret */
|
|
rcCaret.left = xposCaret;
|
|
if (!caretAtEOL && !caretAtEOF && (ll->chars[offset] != '\t') && !(IsControlCharacter(ll->chars[offset]))) {
|
|
drawBlockCaret = true;
|
|
rcCaret.right = xposCaret + widthOverstrikeCaret;
|
|
} else {
|
|
rcCaret.right = xposCaret + vsDraw.aveCharWidth;
|
|
}
|
|
} else {
|
|
/* Line caret */
|
|
rcCaret.left = static_cast<XYPOSITION>(RoundXYPosition(xposCaret - caretWidthOffset));
|
|
rcCaret.right = rcCaret.left + vsDraw.caretWidth;
|
|
}
|
|
ColourDesired caretColour = mainCaret ? vsDraw.caretcolour : vsDraw.additionalCaretColour;
|
|
if (drawBlockCaret) {
|
|
DrawBlockCaret(surface, vsDraw, ll, subLine, xStart, offset, posCaret.Position(), rcCaret, caretColour);
|
|
} else {
|
|
surface->FillRectangle(rcCaret, caretColour);
|
|
}
|
|
}
|
|
}
|
|
if (drawDrag)
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
|
|
//Platform::DebugPrintf("Paint:%1d (%3d,%3d) ... (%3d,%3d)\n",
|
|
// paintingAllText, rcArea.left, rcArea.top, rcArea.right, rcArea.bottom);
|
|
AllocateGraphics();
|
|
|
|
RefreshStyleData();
|
|
if (paintState == paintAbandoned)
|
|
return; // Scroll bars may have changed so need redraw
|
|
RefreshPixMaps(surfaceWindow);
|
|
|
|
paintAbandonedByStyling = false;
|
|
|
|
StyleToPositionInView(PositionAfterArea(rcArea));
|
|
|
|
PRectangle rcClient = GetClientRectangle();
|
|
Point ptOrigin = GetVisibleOriginInMain();
|
|
//Platform::DebugPrintf("Client: (%3d,%3d) ... (%3d,%3d) %d\n",
|
|
// rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
|
|
|
|
int screenLinePaintFirst = static_cast<int>(rcArea.top) / vs.lineHeight;
|
|
|
|
int xStart = vs.textStart - xOffset + static_cast<int>(ptOrigin.x);
|
|
int ypos = 0;
|
|
if (!bufferedDraw)
|
|
ypos += screenLinePaintFirst * vs.lineHeight;
|
|
int yposScreen = screenLinePaintFirst * vs.lineHeight;
|
|
|
|
if (NotifyUpdateUI()) {
|
|
RefreshStyleData();
|
|
RefreshPixMaps(surfaceWindow);
|
|
}
|
|
|
|
// Wrap the visible lines if needed.
|
|
if (WrapLines(wsVisible)) {
|
|
// The wrapping process has changed the height of some lines so
|
|
// abandon this paint for a complete repaint.
|
|
if (AbandonPaint()) {
|
|
return;
|
|
}
|
|
RefreshPixMaps(surfaceWindow); // In case pixmaps invalidated by scrollbar change
|
|
}
|
|
PLATFORM_ASSERT(pixmapSelPattern->Initialised());
|
|
|
|
if (!bufferedDraw)
|
|
surfaceWindow->SetClip(rcArea);
|
|
|
|
if (paintState != paintAbandoned) {
|
|
if (vs.marginInside) {
|
|
PaintSelMargin(surfaceWindow, rcArea);
|
|
PRectangle rcRightMargin = rcClient;
|
|
rcRightMargin.left = rcRightMargin.right - vs.rightMarginWidth;
|
|
if (rcArea.Intersects(rcRightMargin)) {
|
|
surfaceWindow->FillRectangle(rcRightMargin, vs.styles[STYLE_DEFAULT].back);
|
|
}
|
|
} else { // Else separate view so separate paint event but leftMargin included to allow overlap
|
|
PRectangle rcLeftMargin = rcArea;
|
|
rcLeftMargin.left = 0;
|
|
rcLeftMargin.right = rcLeftMargin.left + vs.leftMarginWidth;
|
|
if (rcArea.Intersects(rcLeftMargin)) {
|
|
surfaceWindow->FillRectangle(rcLeftMargin, vs.styles[STYLE_DEFAULT].back);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (paintState == paintAbandoned) {
|
|
// Either styling or NotifyUpdateUI noticed that painting is needed
|
|
// outside the current painting rectangle
|
|
//Platform::DebugPrintf("Abandoning paint\n");
|
|
if (Wrapping()) {
|
|
if (paintAbandonedByStyling) {
|
|
// Styling has spilled over a line end, such as occurs by starting a multiline
|
|
// comment. The width of subsequent text may have changed, so rewrap.
|
|
NeedWrapping(cs.DocFromDisplay(topLine));
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
//Platform::DebugPrintf("start display %d, offset = %d\n", pdoc->Length(), xOffset);
|
|
|
|
// Allow text at start of line to overlap 1 pixel into the margin as this displays
|
|
// serifs and italic stems for aliased text.
|
|
const int leftTextOverlap = ((xOffset == 0) && (vs.leftMarginWidth > 0)) ? 1 : 0;
|
|
|
|
// Do the painting
|
|
if (rcArea.right > vs.textStart - leftTextOverlap) {
|
|
|
|
Surface *surface = surfaceWindow;
|
|
if (bufferedDraw) {
|
|
surface = pixmapLine;
|
|
PLATFORM_ASSERT(pixmapLine->Initialised());
|
|
}
|
|
surface->SetUnicodeMode(IsUnicodeMode());
|
|
surface->SetDBCSMode(CodePage());
|
|
|
|
int visibleLine = TopLineOfMain() + screenLinePaintFirst;
|
|
|
|
SelectionPosition posCaret = sel.RangeMain().caret;
|
|
if (posDrag.IsValid())
|
|
posCaret = posDrag;
|
|
int lineCaret = pdoc->LineFromPosition(posCaret.Position());
|
|
|
|
PRectangle rcTextArea = rcClient;
|
|
if (vs.marginInside) {
|
|
rcTextArea.left += vs.textStart;
|
|
rcTextArea.right -= vs.rightMarginWidth;
|
|
} else {
|
|
rcTextArea = rcArea;
|
|
}
|
|
|
|
// Remove selection margin from drawing area so text will not be drawn
|
|
// on it in unbuffered mode.
|
|
if (!bufferedDraw && vs.marginInside) {
|
|
PRectangle rcClipText = rcTextArea;
|
|
rcClipText.left -= leftTextOverlap;
|
|
surfaceWindow->SetClip(rcClipText);
|
|
}
|
|
|
|
// Loop on visible lines
|
|
//double durLayout = 0.0;
|
|
//double durPaint = 0.0;
|
|
//double durCopy = 0.0;
|
|
//ElapsedTime etWhole;
|
|
int lineDocPrevious = -1; // Used to avoid laying out one document line multiple times
|
|
AutoLineLayout ll(llc, 0);
|
|
while (visibleLine < cs.LinesDisplayed() && yposScreen < rcArea.bottom) {
|
|
|
|
int lineDoc = cs.DocFromDisplay(visibleLine);
|
|
// Only visible lines should be handled by the code within the loop
|
|
PLATFORM_ASSERT(cs.GetVisible(lineDoc));
|
|
int lineStartSet = cs.DisplayFromDoc(lineDoc);
|
|
int subLine = visibleLine - lineStartSet;
|
|
|
|
// Copy this line and its styles from the document into local arrays
|
|
// and determine the x position at which each character starts.
|
|
//ElapsedTime et;
|
|
if (lineDoc != lineDocPrevious) {
|
|
ll.Set(0);
|
|
ll.Set(RetrieveLineLayout(lineDoc));
|
|
LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
|
|
lineDocPrevious = lineDoc;
|
|
}
|
|
//durLayout += et.Duration(true);
|
|
|
|
if (ll) {
|
|
ll->containsCaret = lineDoc == lineCaret;
|
|
if (hideSelection) {
|
|
ll->containsCaret = false;
|
|
}
|
|
|
|
GetHotSpotRange(ll->hsStart, ll->hsEnd);
|
|
|
|
PRectangle rcLine = rcTextArea;
|
|
rcLine.top = static_cast<XYPOSITION>(ypos);
|
|
rcLine.bottom = static_cast<XYPOSITION>(ypos + vs.lineHeight);
|
|
|
|
bool bracesIgnoreStyle = false;
|
|
if ((vs.braceHighlightIndicatorSet && (bracesMatchStyle == STYLE_BRACELIGHT)) ||
|
|
(vs.braceBadLightIndicatorSet && (bracesMatchStyle == STYLE_BRACEBAD))) {
|
|
bracesIgnoreStyle = true;
|
|
}
|
|
Range rangeLine(pdoc->LineStart(lineDoc), pdoc->LineStart(lineDoc + 1));
|
|
// Highlight the current braces if any
|
|
ll->SetBracesHighlight(rangeLine, braces, static_cast<char>(bracesMatchStyle),
|
|
static_cast<int>(highlightGuideColumn * vs.spaceWidth), bracesIgnoreStyle);
|
|
|
|
if (leftTextOverlap && bufferedDraw) {
|
|
PRectangle rcSpacer = rcLine;
|
|
rcSpacer.right = rcSpacer.left;
|
|
rcSpacer.left -= 1;
|
|
surface->FillRectangle(rcSpacer, vs.styles[STYLE_DEFAULT].back);
|
|
}
|
|
|
|
// Draw the line
|
|
DrawLine(surface, vs, lineDoc, visibleLine, xStart, rcLine, ll, subLine);
|
|
//durPaint += et.Duration(true);
|
|
|
|
// Restore the previous styles for the brace highlights in case layout is in cache.
|
|
ll->RestoreBracesHighlight(rangeLine, braces, bracesIgnoreStyle);
|
|
|
|
bool expanded = cs.GetExpanded(lineDoc);
|
|
const int level = pdoc->GetLevel(lineDoc);
|
|
const int levelNext = pdoc->GetLevel(lineDoc + 1);
|
|
if ((level & SC_FOLDLEVELHEADERFLAG) &&
|
|
((level & SC_FOLDLEVELNUMBERMASK) < (levelNext & SC_FOLDLEVELNUMBERMASK))) {
|
|
// Paint the line above the fold
|
|
if ((expanded && (foldFlags & SC_FOLDFLAG_LINEBEFORE_EXPANDED))
|
|
||
|
|
(!expanded && (foldFlags & SC_FOLDFLAG_LINEBEFORE_CONTRACTED))) {
|
|
PRectangle rcFoldLine = rcLine;
|
|
rcFoldLine.bottom = rcFoldLine.top + 1;
|
|
surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore);
|
|
}
|
|
// Paint the line below the fold
|
|
if ((expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_EXPANDED))
|
|
||
|
|
(!expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_CONTRACTED))) {
|
|
PRectangle rcFoldLine = rcLine;
|
|
rcFoldLine.top = rcFoldLine.bottom - 1;
|
|
surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore);
|
|
}
|
|
}
|
|
|
|
DrawCarets(surface, vs, lineDoc, xStart, rcLine, ll, subLine);
|
|
|
|
if (bufferedDraw) {
|
|
Point from = Point::FromInts(vs.textStart-leftTextOverlap, 0);
|
|
PRectangle rcCopyArea = PRectangle::FromInts(vs.textStart - leftTextOverlap, yposScreen,
|
|
static_cast<int>(rcClient.right - vs.rightMarginWidth),
|
|
yposScreen + vs.lineHeight);
|
|
surfaceWindow->Copy(rcCopyArea, from, *pixmapLine);
|
|
}
|
|
|
|
lineWidthMaxSeen = Platform::Maximum(
|
|
lineWidthMaxSeen, static_cast<int>(ll->positions[ll->numCharsInLine]));
|
|
//durCopy += et.Duration(true);
|
|
}
|
|
|
|
if (!bufferedDraw) {
|
|
ypos += vs.lineHeight;
|
|
}
|
|
|
|
yposScreen += vs.lineHeight;
|
|
visibleLine++;
|
|
|
|
//gdk_flush();
|
|
}
|
|
ll.Set(0);
|
|
//if (durPaint < 0.00000001)
|
|
// durPaint = 0.00000001;
|
|
|
|
// Right column limit indicator
|
|
PRectangle rcBeyondEOF = (vs.marginInside) ? rcClient : rcArea;
|
|
rcBeyondEOF.left = static_cast<XYPOSITION>(vs.textStart);
|
|
rcBeyondEOF.right = rcBeyondEOF.right - ((vs.marginInside) ? vs.rightMarginWidth : 0);
|
|
rcBeyondEOF.top = static_cast<XYPOSITION>((cs.LinesDisplayed() - TopLineOfMain()) * vs.lineHeight);
|
|
if (rcBeyondEOF.top < rcBeyondEOF.bottom) {
|
|
surfaceWindow->FillRectangle(rcBeyondEOF, vs.styles[STYLE_DEFAULT].back);
|
|
if (vs.edgeState == EDGE_LINE) {
|
|
int edgeX = static_cast<int>(vs.theEdge * vs.spaceWidth);
|
|
rcBeyondEOF.left = static_cast<XYPOSITION>(edgeX + xStart);
|
|
rcBeyondEOF.right = rcBeyondEOF.left + 1;
|
|
surfaceWindow->FillRectangle(rcBeyondEOF, vs.edgecolour);
|
|
}
|
|
}
|
|
//Platform::DebugPrintf(
|
|
//"Layout:%9.6g Paint:%9.6g Ratio:%9.6g Copy:%9.6g Total:%9.6g\n",
|
|
//durLayout, durPaint, durLayout / durPaint, durCopy, etWhole.Duration());
|
|
NotifyPainted();
|
|
}
|
|
}
|
|
|
|
// Space (3 space characters) between line numbers and text when printing.
|
|
#define lineNumberPrintSpace " "
|
|
|
|
ColourDesired InvertedLight(ColourDesired orig) {
|
|
unsigned int r = orig.GetRed();
|
|
unsigned int g = orig.GetGreen();
|
|
unsigned int b = orig.GetBlue();
|
|
unsigned int l = (r + g + b) / 3; // There is a better calculation for this that matches human eye
|
|
unsigned int il = 0xff - l;
|
|
if (l == 0)
|
|
return ColourDesired(0xff, 0xff, 0xff);
|
|
r = r * il / l;
|
|
g = g * il / l;
|
|
b = b * il / l;
|
|
return ColourDesired(Platform::Minimum(r, 0xff), Platform::Minimum(g, 0xff), Platform::Minimum(b, 0xff));
|
|
}
|
|
|
|
// This is mostly copied from the Paint method but with some things omitted
|
|
// such as the margin markers, line numbers, selection and caret
|
|
// Should be merged back into a combined Draw method.
|
|
long Editor::FormatRange(bool draw, Sci_RangeToFormat *pfr) {
|
|
if (!pfr)
|
|
return 0;
|
|
|
|
AutoSurface surface(pfr->hdc, this, SC_TECHNOLOGY_DEFAULT);
|
|
if (!surface)
|
|
return 0;
|
|
AutoSurface surfaceMeasure(pfr->hdcTarget, this, SC_TECHNOLOGY_DEFAULT);
|
|
if (!surfaceMeasure) {
|
|
return 0;
|
|
}
|
|
|
|
// Can't use measurements cached for screen
|
|
posCache.Clear();
|
|
|
|
ViewStyle vsPrint(vs);
|
|
vsPrint.technology = SC_TECHNOLOGY_DEFAULT;
|
|
|
|
// Modify the view style for printing as do not normally want any of the transient features to be printed
|
|
// Printing supports only the line number margin.
|
|
int lineNumberIndex = -1;
|
|
for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) {
|
|
if ((vsPrint.ms[margin].style == SC_MARGIN_NUMBER) && (vsPrint.ms[margin].width > 0)) {
|
|
lineNumberIndex = margin;
|
|
} else {
|
|
vsPrint.ms[margin].width = 0;
|
|
}
|
|
}
|
|
vsPrint.fixedColumnWidth = 0;
|
|
vsPrint.zoomLevel = printParameters.magnification;
|
|
// Don't show indentation guides
|
|
// If this ever gets changed, cached pixmap would need to be recreated if technology != SC_TECHNOLOGY_DEFAULT
|
|
vsPrint.viewIndentationGuides = ivNone;
|
|
// Don't show the selection when printing
|
|
vsPrint.selColours.back.isSet = false;
|
|
vsPrint.selColours.fore.isSet = false;
|
|
vsPrint.selAlpha = SC_ALPHA_NOALPHA;
|
|
vsPrint.selAdditionalAlpha = SC_ALPHA_NOALPHA;
|
|
vsPrint.whitespaceColours.back.isSet = false;
|
|
vsPrint.whitespaceColours.fore.isSet = false;
|
|
vsPrint.showCaretLineBackground = false;
|
|
vsPrint.alwaysShowCaretLineBackground = false;
|
|
// Don't highlight matching braces using indicators
|
|
vsPrint.braceHighlightIndicatorSet = false;
|
|
vsPrint.braceBadLightIndicatorSet = false;
|
|
|
|
// Set colours for printing according to users settings
|
|
for (size_t sty = 0; sty < vsPrint.styles.size(); sty++) {
|
|
if (printParameters.colourMode == SC_PRINT_INVERTLIGHT) {
|
|
vsPrint.styles[sty].fore = InvertedLight(vsPrint.styles[sty].fore);
|
|
vsPrint.styles[sty].back = InvertedLight(vsPrint.styles[sty].back);
|
|
} else if (printParameters.colourMode == SC_PRINT_BLACKONWHITE) {
|
|
vsPrint.styles[sty].fore = ColourDesired(0, 0, 0);
|
|
vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
|
|
} else if (printParameters.colourMode == SC_PRINT_COLOURONWHITE) {
|
|
vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
|
|
} else if (printParameters.colourMode == SC_PRINT_COLOURONWHITEDEFAULTBG) {
|
|
if (sty <= STYLE_DEFAULT) {
|
|
vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
|
|
}
|
|
}
|
|
}
|
|
// White background for the line numbers
|
|
vsPrint.styles[STYLE_LINENUMBER].back = ColourDesired(0xff, 0xff, 0xff);
|
|
|
|
// Printing uses different margins, so reset screen margins
|
|
vsPrint.leftMarginWidth = 0;
|
|
vsPrint.rightMarginWidth = 0;
|
|
|
|
vsPrint.Refresh(*surfaceMeasure, pdoc->tabInChars);
|
|
// Determining width must happen after fonts have been realised in Refresh
|
|
int lineNumberWidth = 0;
|
|
if (lineNumberIndex >= 0) {
|
|
lineNumberWidth = static_cast<int>(surfaceMeasure->WidthText(vsPrint.styles[STYLE_LINENUMBER].font,
|
|
"99999" lineNumberPrintSpace, 5 + istrlen(lineNumberPrintSpace)));
|
|
vsPrint.ms[lineNumberIndex].width = lineNumberWidth;
|
|
vsPrint.Refresh(*surfaceMeasure, pdoc->tabInChars); // Recalculate fixedColumnWidth
|
|
}
|
|
|
|
int linePrintStart = pdoc->LineFromPosition(pfr->chrg.cpMin);
|
|
int linePrintLast = linePrintStart + (pfr->rc.bottom - pfr->rc.top) / vsPrint.lineHeight - 1;
|
|
if (linePrintLast < linePrintStart)
|
|
linePrintLast = linePrintStart;
|
|
int linePrintMax = pdoc->LineFromPosition(pfr->chrg.cpMax);
|
|
if (linePrintLast > linePrintMax)
|
|
linePrintLast = linePrintMax;
|
|
//Platform::DebugPrintf("Formatting lines=[%0d,%0d,%0d] top=%0d bottom=%0d line=%0d %0d\n",
|
|
// linePrintStart, linePrintLast, linePrintMax, pfr->rc.top, pfr->rc.bottom, vsPrint.lineHeight,
|
|
// surfaceMeasure->Height(vsPrint.styles[STYLE_LINENUMBER].font));
|
|
int endPosPrint = pdoc->Length();
|
|
if (linePrintLast < pdoc->LinesTotal())
|
|
endPosPrint = pdoc->LineStart(linePrintLast + 1);
|
|
|
|
// Ensure we are styled to where we are formatting.
|
|
pdoc->EnsureStyledTo(endPosPrint);
|
|
|
|
int xStart = vsPrint.fixedColumnWidth + pfr->rc.left;
|
|
int ypos = pfr->rc.top;
|
|
|
|
int lineDoc = linePrintStart;
|
|
|
|
int nPrintPos = pfr->chrg.cpMin;
|
|
int visibleLine = 0;
|
|
int widthPrint = pfr->rc.right - pfr->rc.left - vsPrint.fixedColumnWidth;
|
|
if (printParameters.wrapState == eWrapNone)
|
|
widthPrint = LineLayout::wrapWidthInfinite;
|
|
|
|
while (lineDoc <= linePrintLast && ypos < pfr->rc.bottom) {
|
|
|
|
// When printing, the hdc and hdcTarget may be the same, so
|
|
// changing the state of surfaceMeasure may change the underlying
|
|
// state of surface. Therefore, any cached state is discarded before
|
|
// using each surface.
|
|
surfaceMeasure->FlushCachedState();
|
|
|
|
// Copy this line and its styles from the document into local arrays
|
|
// and determine the x position at which each character starts.
|
|
LineLayout ll(pdoc->LineStart(lineDoc+1)-pdoc->LineStart(lineDoc)+1);
|
|
LayoutLine(lineDoc, surfaceMeasure, vsPrint, &ll, widthPrint);
|
|
|
|
ll.containsCaret = false;
|
|
|
|
PRectangle rcLine = PRectangle::FromInts(
|
|
pfr->rc.left,
|
|
ypos,
|
|
pfr->rc.right - 1,
|
|
ypos + vsPrint.lineHeight);
|
|
|
|
// When document line is wrapped over multiple display lines, find where
|
|
// to start printing from to ensure a particular position is on the first
|
|
// line of the page.
|
|
if (visibleLine == 0) {
|
|
int startWithinLine = nPrintPos - pdoc->LineStart(lineDoc);
|
|
for (int iwl = 0; iwl < ll.lines - 1; iwl++) {
|
|
if (ll.LineStart(iwl) <= startWithinLine && ll.LineStart(iwl + 1) >= startWithinLine) {
|
|
visibleLine = -iwl;
|
|
}
|
|
}
|
|
|
|
if (ll.lines > 1 && startWithinLine >= ll.LineStart(ll.lines - 1)) {
|
|
visibleLine = -(ll.lines - 1);
|
|
}
|
|
}
|
|
|
|
if (draw && lineNumberWidth &&
|
|
(ypos + vsPrint.lineHeight <= pfr->rc.bottom) &&
|
|
(visibleLine >= 0)) {
|
|
char number[100];
|
|
sprintf(number, "%d" lineNumberPrintSpace, lineDoc + 1);
|
|
PRectangle rcNumber = rcLine;
|
|
rcNumber.right = rcNumber.left + lineNumberWidth;
|
|
// Right justify
|
|
rcNumber.left = rcNumber.right - surfaceMeasure->WidthText(
|
|
vsPrint.styles[STYLE_LINENUMBER].font, number, istrlen(number));
|
|
surface->FlushCachedState();
|
|
surface->DrawTextNoClip(rcNumber, vsPrint.styles[STYLE_LINENUMBER].font,
|
|
static_cast<XYPOSITION>(ypos + vsPrint.maxAscent), number, istrlen(number),
|
|
vsPrint.styles[STYLE_LINENUMBER].fore,
|
|
vsPrint.styles[STYLE_LINENUMBER].back);
|
|
}
|
|
|
|
// Draw the line
|
|
surface->FlushCachedState();
|
|
|
|
for (int iwl = 0; iwl < ll.lines; iwl++) {
|
|
if (ypos + vsPrint.lineHeight <= pfr->rc.bottom) {
|
|
if (visibleLine >= 0) {
|
|
if (draw) {
|
|
rcLine.top = static_cast<XYPOSITION>(ypos);
|
|
rcLine.bottom = static_cast<XYPOSITION>(ypos + vsPrint.lineHeight);
|
|
DrawLine(surface, vsPrint, lineDoc, visibleLine, xStart, rcLine, &ll, iwl);
|
|
}
|
|
ypos += vsPrint.lineHeight;
|
|
}
|
|
visibleLine++;
|
|
if (iwl == ll.lines - 1)
|
|
nPrintPos = pdoc->LineStart(lineDoc + 1);
|
|
else
|
|
nPrintPos += ll.LineStart(iwl + 1) - ll.LineStart(iwl);
|
|
}
|
|
}
|
|
|
|
++lineDoc;
|
|
}
|
|
|
|
// Clear cache so measurements are not used for screen
|
|
posCache.Clear();
|
|
|
|
return nPrintPos;
|
|
}
|
|
|
|
int Editor::TextWidth(int style, const char *text) {
|
|
RefreshStyleData();
|
|
AutoSurface surface(this);
|
|
if (surface) {
|
|
return static_cast<int>(surface->WidthText(vs.styles[style].font, text, istrlen(text)));
|
|
} else {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
// Empty method is overridden on GTK+ to show / hide scrollbars
|
|
void Editor::ReconfigureScrollBars() {}
|
|
|
|
void Editor::SetScrollBars() {
|
|
RefreshStyleData();
|
|
|
|
int nMax = MaxScrollPos();
|
|
int nPage = LinesOnScreen();
|
|
bool modified = ModifyScrollBars(nMax + nPage - 1, nPage);
|
|
if (modified) {
|
|
DwellEnd(true);
|
|
}
|
|
|
|
// TODO: ensure always showing as many lines as possible
|
|
// May not be, if, for example, window made larger
|
|
if (topLine > MaxScrollPos()) {
|
|
SetTopLine(Platform::Clamp(topLine, 0, MaxScrollPos()));
|
|
SetVerticalScrollPos();
|
|
Redraw();
|
|
}
|
|
if (modified) {
|
|
if (!AbandonPaint())
|
|
Redraw();
|
|
}
|
|
//Platform::DebugPrintf("end max = %d page = %d\n", nMax, nPage);
|
|
}
|
|
|
|
void Editor::ChangeSize() {
|
|
DropGraphics(false);
|
|
SetScrollBars();
|
|
if (Wrapping()) {
|
|
PRectangle rcTextArea = GetClientRectangle();
|
|
rcTextArea.left = static_cast<XYPOSITION>(vs.textStart);
|
|
rcTextArea.right -= vs.rightMarginWidth;
|
|
if (wrapWidth != rcTextArea.Width()) {
|
|
NeedWrapping();
|
|
Redraw();
|
|
}
|
|
}
|
|
}
|
|
|
|
int Editor::InsertSpace(int position, unsigned int spaces) {
|
|
if (spaces > 0) {
|
|
std::string spaceText(spaces, ' ');
|
|
const int lengthInserted = pdoc->InsertString(position, spaceText.c_str(), spaces);
|
|
position += lengthInserted;
|
|
}
|
|
return position;
|
|
}
|
|
|
|
void Editor::AddChar(char ch) {
|
|
char s[2];
|
|
s[0] = ch;
|
|
s[1] = '\0';
|
|
AddCharUTF(s, 1);
|
|
}
|
|
|
|
void Editor::FilterSelections() {
|
|
if (!additionalSelectionTyping && (sel.Count() > 1)) {
|
|
SelectionRange rangeOnly = sel.RangeMain();
|
|
InvalidateSelection(rangeOnly, true);
|
|
sel.SetSelection(rangeOnly);
|
|
}
|
|
}
|
|
|
|
static bool cmpSelPtrs(const SelectionRange *a, const SelectionRange *b) {
|
|
return *a < *b;
|
|
}
|
|
|
|
// AddCharUTF inserts an array of bytes which may or may not be in UTF-8.
|
|
void Editor::AddCharUTF(char *s, unsigned int len, bool treatAsDBCS) {
|
|
FilterSelections();
|
|
{
|
|
UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty() || inOverstrike);
|
|
|
|
std::vector<SelectionRange *> selPtrs;
|
|
for (size_t r = 0; r < sel.Count(); r++) {
|
|
selPtrs.push_back(&sel.Range(r));
|
|
}
|
|
std::sort(selPtrs.begin(), selPtrs.end(), cmpSelPtrs);
|
|
|
|
for (std::vector<SelectionRange *>::reverse_iterator rit = selPtrs.rbegin();
|
|
rit != selPtrs.rend(); ++rit) {
|
|
SelectionRange *currentSel = *rit;
|
|
if (!RangeContainsProtected(currentSel->Start().Position(),
|
|
currentSel->End().Position())) {
|
|
int positionInsert = currentSel->Start().Position();
|
|
if (!currentSel->Empty()) {
|
|
if (currentSel->Length()) {
|
|
pdoc->DeleteChars(positionInsert, currentSel->Length());
|
|
currentSel->ClearVirtualSpace();
|
|
} else {
|
|
// Range is all virtual so collapse to start of virtual space
|
|
currentSel->MinimizeVirtualSpace();
|
|
}
|
|
} else if (inOverstrike) {
|
|
if (positionInsert < pdoc->Length()) {
|
|
if (!pdoc->IsPositionInLineEnd(positionInsert)) {
|
|
pdoc->DelChar(positionInsert);
|
|
currentSel->ClearVirtualSpace();
|
|
}
|
|
}
|
|
}
|
|
positionInsert = InsertSpace(positionInsert, currentSel->caret.VirtualSpace());
|
|
const int lengthInserted = pdoc->InsertString(positionInsert, s, len);
|
|
if (lengthInserted > 0) {
|
|
currentSel->caret.SetPosition(positionInsert + lengthInserted);
|
|
currentSel->anchor.SetPosition(positionInsert + lengthInserted);
|
|
}
|
|
currentSel->ClearVirtualSpace();
|
|
// If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
|
|
if (Wrapping()) {
|
|
AutoSurface surface(this);
|
|
if (surface) {
|
|
if (WrapOneLine(surface, pdoc->LineFromPosition(positionInsert))) {
|
|
SetScrollBars();
|
|
SetVerticalScrollPos();
|
|
Redraw();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (Wrapping()) {
|
|
SetScrollBars();
|
|
}
|
|
ThinRectangularRange();
|
|
// If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
|
|
EnsureCaretVisible();
|
|
// Avoid blinking during rapid typing:
|
|
ShowCaretAtCurrentPosition();
|
|
if ((caretSticky == SC_CARETSTICKY_OFF) ||
|
|
((caretSticky == SC_CARETSTICKY_WHITESPACE) && !IsAllSpacesOrTabs(s, len))) {
|
|
SetLastXChosen();
|
|
}
|
|
|
|
if (treatAsDBCS) {
|
|
NotifyChar((static_cast<unsigned char>(s[0]) << 8) |
|
|
static_cast<unsigned char>(s[1]));
|
|
} else if (len > 0) {
|
|
int byte = static_cast<unsigned char>(s[0]);
|
|
if ((byte < 0xC0) || (1 == len)) {
|
|
// Handles UTF-8 characters between 0x01 and 0x7F and single byte
|
|
// characters when not in UTF-8 mode.
|
|
// Also treats \0 and naked trail bytes 0x80 to 0xBF as valid
|
|
// characters representing themselves.
|
|
} else {
|
|
// Unroll 1 to 3 byte UTF-8 sequences. See reference data at:
|
|
// http://www.cl.cam.ac.uk/~mgk25/unicode.html
|
|
// http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
|
|
if (byte < 0xE0) {
|
|
int byte2 = static_cast<unsigned char>(s[1]);
|
|
if ((byte2 & 0xC0) == 0x80) {
|
|
// Two-byte-character lead-byte followed by a trail-byte.
|
|
byte = (((byte & 0x1F) << 6) | (byte2 & 0x3F));
|
|
}
|
|
// A two-byte-character lead-byte not followed by trail-byte
|
|
// represents itself.
|
|
} else if (byte < 0xF0) {
|
|
int byte2 = static_cast<unsigned char>(s[1]);
|
|
int byte3 = static_cast<unsigned char>(s[2]);
|
|
if (((byte2 & 0xC0) == 0x80) && ((byte3 & 0xC0) == 0x80)) {
|
|
// Three-byte-character lead byte followed by two trail bytes.
|
|
byte = (((byte & 0x0F) << 12) | ((byte2 & 0x3F) << 6) |
|
|
(byte3 & 0x3F));
|
|
}
|
|
// A three-byte-character lead-byte not followed by two trail-bytes
|
|
// represents itself.
|
|
}
|
|
}
|
|
NotifyChar(byte);
|
|
}
|
|
|
|
if (recordingMacro) {
|
|
NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(s));
|
|
}
|
|
}
|
|
|
|
void Editor::InsertPaste(const char *text, int len) {
|
|
if (multiPasteMode == SC_MULTIPASTE_ONCE) {
|
|
SelectionPosition selStart = sel.Start();
|
|
selStart = SelectionPosition(InsertSpace(selStart.Position(), selStart.VirtualSpace()));
|
|
const int lengthInserted = pdoc->InsertString(selStart.Position(), text, len);
|
|
if (lengthInserted > 0) {
|
|
SetEmptySelection(selStart.Position() + lengthInserted);
|
|
}
|
|
} else {
|
|
// SC_MULTIPASTE_EACH
|
|
for (size_t r=0; r<sel.Count(); r++) {
|
|
if (!RangeContainsProtected(sel.Range(r).Start().Position(),
|
|
sel.Range(r).End().Position())) {
|
|
int positionInsert = sel.Range(r).Start().Position();
|
|
if (!sel.Range(r).Empty()) {
|
|
if (sel.Range(r).Length()) {
|
|
pdoc->DeleteChars(positionInsert, sel.Range(r).Length());
|
|
sel.Range(r).ClearVirtualSpace();
|
|
} else {
|
|
// Range is all virtual so collapse to start of virtual space
|
|
sel.Range(r).MinimizeVirtualSpace();
|
|
}
|
|
}
|
|
positionInsert = InsertSpace(positionInsert, sel.Range(r).caret.VirtualSpace());
|
|
const int lengthInserted = pdoc->InsertString(positionInsert, text, len);
|
|
if (lengthInserted > 0) {
|
|
sel.Range(r).caret.SetPosition(positionInsert + lengthInserted);
|
|
sel.Range(r).anchor.SetPosition(positionInsert + lengthInserted);
|
|
}
|
|
sel.Range(r).ClearVirtualSpace();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Editor::InsertPasteShape(const char *text, int len, PasteShape shape) {
|
|
std::string convertedText;
|
|
if (convertPastes) {
|
|
// Convert line endings of the paste into our local line-endings mode
|
|
convertedText = Document::TransformLineEnds(text, len, pdoc->eolMode);
|
|
len = static_cast<int>(convertedText.length());
|
|
text = convertedText.c_str();
|
|
}
|
|
if (shape == pasteRectangular) {
|
|
PasteRectangular(sel.Start(), text, len);
|
|
} else {
|
|
if (shape == pasteLine) {
|
|
int insertPos = pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret()));
|
|
int lengthInserted = pdoc->InsertString(insertPos, text, len);
|
|
// add the newline if necessary
|
|
if ((len > 0) && (text[len - 1] != '\n' && text[len - 1] != '\r')) {
|
|
const char *endline = StringFromEOLMode(pdoc->eolMode);
|
|
int length = static_cast<int>(strlen(endline));
|
|
lengthInserted += pdoc->InsertString(insertPos + lengthInserted, endline, length);
|
|
}
|
|
if (sel.MainCaret() == insertPos) {
|
|
SetEmptySelection(sel.MainCaret() + lengthInserted);
|
|
}
|
|
} else {
|
|
InsertPaste(text, len);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Editor::ClearSelection(bool retainMultipleSelections) {
|
|
if (!sel.IsRectangular() && !retainMultipleSelections)
|
|
FilterSelections();
|
|
UndoGroup ug(pdoc);
|
|
for (size_t r=0; r<sel.Count(); r++) {
|
|
if (!sel.Range(r).Empty()) {
|
|
if (!RangeContainsProtected(sel.Range(r).Start().Position(),
|
|
sel.Range(r).End().Position())) {
|
|
pdoc->DeleteChars(sel.Range(r).Start().Position(),
|
|
sel.Range(r).Length());
|
|
sel.Range(r) = SelectionRange(sel.Range(r).Start());
|
|
}
|
|
}
|
|
}
|
|
ThinRectangularRange();
|
|
sel.RemoveDuplicates();
|
|
ClaimSelection();
|
|
}
|
|
|
|
void Editor::ClearAll() {
|
|
{
|
|
UndoGroup ug(pdoc);
|
|
if (0 != pdoc->Length()) {
|
|
pdoc->DeleteChars(0, pdoc->Length());
|
|
}
|
|
if (!pdoc->IsReadOnly()) {
|
|
cs.Clear();
|
|
pdoc->AnnotationClearAll();
|
|
pdoc->MarginClearAll();
|
|
}
|
|
}
|
|
sel.Clear();
|
|
SetTopLine(0);
|
|
SetVerticalScrollPos();
|
|
InvalidateStyleRedraw();
|
|
}
|
|
|
|
void Editor::ClearDocumentStyle() {
|
|
Decoration *deco = pdoc->decorations.root;
|
|
while (deco) {
|
|
// Save next in case deco deleted
|
|
Decoration *decoNext = deco->next;
|
|
if (deco->indicator < INDIC_CONTAINER) {
|
|
pdoc->decorations.SetCurrentIndicator(deco->indicator);
|
|
pdoc->DecorationFillRange(0, 0, pdoc->Length());
|
|
}
|
|
deco = decoNext;
|
|
}
|
|
pdoc->StartStyling(0, '\377');
|
|
pdoc->SetStyleFor(pdoc->Length(), 0);
|
|
cs.ShowAll();
|
|
pdoc->ClearLevels();
|
|
}
|
|
|
|
void Editor::CopyAllowLine() {
|
|
SelectionText selectedText;
|
|
CopySelectionRange(&selectedText, true);
|
|
CopyToClipboard(selectedText);
|
|
}
|
|
|
|
void Editor::Cut() {
|
|
pdoc->CheckReadOnly();
|
|
if (!pdoc->IsReadOnly() && !SelectionContainsProtected()) {
|
|
Copy();
|
|
ClearSelection();
|
|
}
|
|
}
|
|
|
|
void Editor::PasteRectangular(SelectionPosition pos, const char *ptr, int len) {
|
|
if (pdoc->IsReadOnly() || SelectionContainsProtected()) {
|
|
return;
|
|
}
|
|
sel.Clear();
|
|
sel.RangeMain() = SelectionRange(pos);
|
|
int line = pdoc->LineFromPosition(sel.MainCaret());
|
|
UndoGroup ug(pdoc);
|
|
sel.RangeMain().caret = SelectionPosition(
|
|
InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
|
|
int xInsert = XFromPosition(sel.RangeMain().caret);
|
|
bool prevCr = false;
|
|
while ((len > 0) && IsEOLChar(ptr[len-1]))
|
|
len--;
|
|
for (int i = 0; i < len; i++) {
|
|
if (IsEOLChar(ptr[i])) {
|
|
if ((ptr[i] == '\r') || (!prevCr))
|
|
line++;
|
|
if (line >= pdoc->LinesTotal()) {
|
|
if (pdoc->eolMode != SC_EOL_LF)
|
|
pdoc->InsertString(pdoc->Length(), "\r", 1);
|
|
if (pdoc->eolMode != SC_EOL_CR)
|
|
pdoc->InsertString(pdoc->Length(), "\n", 1);
|
|
}
|
|
// Pad the end of lines with spaces if required
|
|
sel.RangeMain().caret.SetPosition(PositionFromLineX(line, xInsert));
|
|
if ((XFromPosition(sel.MainCaret()) < xInsert) && (i + 1 < len)) {
|
|
while (XFromPosition(sel.MainCaret()) < xInsert) {
|
|
const int lengthInserted = pdoc->InsertString(sel.MainCaret(), " ", 1);
|
|
sel.RangeMain().caret.Add(lengthInserted);
|
|
}
|
|
}
|
|
prevCr = ptr[i] == '\r';
|
|
} else {
|
|
const int lengthInserted = pdoc->InsertString(sel.MainCaret(), ptr + i, 1);
|
|
sel.RangeMain().caret.Add(lengthInserted);
|
|
prevCr = false;
|
|
}
|
|
}
|
|
SetEmptySelection(pos);
|
|
}
|
|
|
|
bool Editor::CanPaste() {
|
|
return !pdoc->IsReadOnly() && !SelectionContainsProtected();
|
|
}
|
|
|
|
void Editor::Clear() {
|
|
// If multiple selections, don't delete EOLS
|
|
if (sel.Empty()) {
|
|
bool singleVirtual = false;
|
|
if ((sel.Count() == 1) &&
|
|
!RangeContainsProtected(sel.MainCaret(), sel.MainCaret() + 1) &&
|
|
sel.RangeMain().Start().VirtualSpace()) {
|
|
singleVirtual = true;
|
|
}
|
|
UndoGroup ug(pdoc, (sel.Count() > 1) || singleVirtual);
|
|
for (size_t r=0; r<sel.Count(); r++) {
|
|
if (!RangeContainsProtected(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1)) {
|
|
if (sel.Range(r).Start().VirtualSpace()) {
|
|
if (sel.Range(r).anchor < sel.Range(r).caret)
|
|
sel.Range(r) = SelectionRange(InsertSpace(sel.Range(r).anchor.Position(), sel.Range(r).anchor.VirtualSpace()));
|
|
else
|
|
sel.Range(r) = SelectionRange(InsertSpace(sel.Range(r).caret.Position(), sel.Range(r).caret.VirtualSpace()));
|
|
}
|
|
if ((sel.Count() == 1) || !pdoc->IsPositionInLineEnd(sel.Range(r).caret.Position())) {
|
|
pdoc->DelChar(sel.Range(r).caret.Position());
|
|
sel.Range(r).ClearVirtualSpace();
|
|
} // else multiple selection so don't eat line ends
|
|
} else {
|
|
sel.Range(r).ClearVirtualSpace();
|
|
}
|
|
}
|
|
} else {
|
|
ClearSelection();
|
|
}
|
|
sel.RemoveDuplicates();
|
|
}
|
|
|
|
void Editor::SelectAll() {
|
|
sel.Clear();
|
|
SetSelection(0, pdoc->Length());
|
|
Redraw();
|
|
}
|
|
|
|
void Editor::Undo() {
|
|
if (pdoc->CanUndo()) {
|
|
InvalidateCaret();
|
|
int newPos = pdoc->Undo();
|
|
if (newPos >= 0)
|
|
SetEmptySelection(newPos);
|
|
EnsureCaretVisible();
|
|
}
|
|
}
|
|
|
|
void Editor::Redo() {
|
|
if (pdoc->CanRedo()) {
|
|
int newPos = pdoc->Redo();
|
|
if (newPos >= 0)
|
|
SetEmptySelection(newPos);
|
|
EnsureCaretVisible();
|
|
}
|
|
}
|
|
|
|
void Editor::DelChar() {
|
|
if (!RangeContainsProtected(sel.MainCaret(), sel.MainCaret() + 1)) {
|
|
pdoc->DelChar(sel.MainCaret());
|
|
}
|
|
// Avoid blinking during rapid typing:
|
|
ShowCaretAtCurrentPosition();
|
|
}
|
|
|
|
void Editor::DelCharBack(bool allowLineStartDeletion) {
|
|
if (!sel.IsRectangular())
|
|
FilterSelections();
|
|
if (sel.IsRectangular())
|
|
allowLineStartDeletion = false;
|
|
UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty());
|
|
if (sel.Empty()) {
|
|
for (size_t r=0; r<sel.Count(); r++) {
|
|
if (!RangeContainsProtected(sel.Range(r).caret.Position() - 1, sel.Range(r).caret.Position())) {
|
|
if (sel.Range(r).caret.VirtualSpace()) {
|
|
sel.Range(r).caret.SetVirtualSpace(sel.Range(r).caret.VirtualSpace() - 1);
|
|
sel.Range(r).anchor.SetVirtualSpace(sel.Range(r).caret.VirtualSpace());
|
|
} else {
|
|
int lineCurrentPos = pdoc->LineFromPosition(sel.Range(r).caret.Position());
|
|
if (allowLineStartDeletion || (pdoc->LineStart(lineCurrentPos) != sel.Range(r).caret.Position())) {
|
|
if (pdoc->GetColumn(sel.Range(r).caret.Position()) <= pdoc->GetLineIndentation(lineCurrentPos) &&
|
|
pdoc->GetColumn(sel.Range(r).caret.Position()) > 0 && pdoc->backspaceUnindents) {
|
|
UndoGroup ugInner(pdoc, !ug.Needed());
|
|
int indentation = pdoc->GetLineIndentation(lineCurrentPos);
|
|
int indentationStep = pdoc->IndentSize();
|
|
int indentationChange = indentation % indentationStep;
|
|
if (indentationChange == 0)
|
|
indentationChange = indentationStep;
|
|
const int posSelect = pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationChange);
|
|
// SetEmptySelection
|
|
sel.Range(r) = SelectionRange(posSelect);
|
|
} else {
|
|
pdoc->DelCharBack(sel.Range(r).caret.Position());
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
sel.Range(r).ClearVirtualSpace();
|
|
}
|
|
}
|
|
ThinRectangularRange();
|
|
} else {
|
|
ClearSelection();
|
|
}
|
|
sel.RemoveDuplicates();
|
|
ContainerNeedsUpdate(SC_UPDATE_SELECTION);
|
|
// Avoid blinking during rapid typing:
|
|
ShowCaretAtCurrentPosition();
|
|
}
|
|
|
|
int Editor::ModifierFlags(bool shift, bool ctrl, bool alt, bool meta) {
|
|
return
|
|
(shift ? SCI_SHIFT : 0) |
|
|
(ctrl ? SCI_CTRL : 0) |
|
|
(alt ? SCI_ALT : 0) |
|
|
(meta ? SCI_META : 0);
|
|
}
|
|
|
|
void Editor::NotifyFocus(bool focus) {
|
|
SCNotification scn = {};
|
|
scn.nmhdr.code = focus ? SCN_FOCUSIN : SCN_FOCUSOUT;
|
|
NotifyParent(scn);
|
|
}
|
|
|
|
void Editor::SetCtrlID(int identifier) {
|
|
ctrlID = identifier;
|
|
}
|
|
|
|
void Editor::NotifyStyleToNeeded(int endStyleNeeded) {
|
|
SCNotification scn = {};
|
|
scn.nmhdr.code = SCN_STYLENEEDED;
|
|
scn.position = endStyleNeeded;
|
|
NotifyParent(scn);
|
|
}
|
|
|
|
void Editor::NotifyStyleNeeded(Document *, void *, int endStyleNeeded) {
|
|
NotifyStyleToNeeded(endStyleNeeded);
|
|
}
|
|
|
|
void Editor::NotifyLexerChanged(Document *, void *) {
|
|
}
|
|
|
|
void Editor::NotifyErrorOccurred(Document *, void *, int status) {
|
|
errorStatus = status;
|
|
}
|
|
|
|
void Editor::NotifyChar(int ch) {
|
|
SCNotification scn = {};
|
|
scn.nmhdr.code = SCN_CHARADDED;
|
|
scn.ch = ch;
|
|
NotifyParent(scn);
|
|
}
|
|
|
|
void Editor::NotifySavePoint(bool isSavePoint) {
|
|
SCNotification scn = {};
|
|
if (isSavePoint) {
|
|
scn.nmhdr.code = SCN_SAVEPOINTREACHED;
|
|
} else {
|
|
scn.nmhdr.code = SCN_SAVEPOINTLEFT;
|
|
}
|
|
NotifyParent(scn);
|
|
}
|
|
|
|
void Editor::NotifyModifyAttempt() {
|
|
SCNotification scn = {};
|
|
scn.nmhdr.code = SCN_MODIFYATTEMPTRO;
|
|
NotifyParent(scn);
|
|
}
|
|
|
|
void Editor::NotifyDoubleClick(Point pt, int modifiers) {
|
|
SCNotification scn = {};
|
|
scn.nmhdr.code = SCN_DOUBLECLICK;
|
|
scn.line = LineFromLocation(pt);
|
|
scn.position = PositionFromLocation(pt, true);
|
|
scn.modifiers = modifiers;
|
|
NotifyParent(scn);
|
|
}
|
|
|
|
void Editor::NotifyDoubleClick(Point pt, bool shift, bool ctrl, bool alt) {
|
|
NotifyDoubleClick(pt, ModifierFlags(shift, ctrl, alt));
|
|
}
|
|
|
|
void Editor::NotifyHotSpotDoubleClicked(int position, int modifiers) {
|
|
SCNotification scn = {};
|
|
scn.nmhdr.code = SCN_HOTSPOTDOUBLECLICK;
|
|
scn.position = position;
|
|
scn.modifiers = modifiers;
|
|
NotifyParent(scn);
|
|
}
|
|
|
|
void Editor::NotifyHotSpotDoubleClicked(int position, bool shift, bool ctrl, bool alt) {
|
|
NotifyHotSpotDoubleClicked(position, ModifierFlags(shift, ctrl, alt));
|
|
}
|
|
|
|
void Editor::NotifyHotSpotClicked(int position, int modifiers) {
|
|
SCNotification scn = {};
|
|
scn.nmhdr.code = SCN_HOTSPOTCLICK;
|
|
scn.position = position;
|
|
scn.modifiers = modifiers;
|
|
NotifyParent(scn);
|
|
}
|
|
|
|
void Editor::NotifyHotSpotClicked(int position, bool shift, bool ctrl, bool alt) {
|
|
NotifyHotSpotClicked(position, ModifierFlags(shift, ctrl, alt));
|
|
}
|
|
|
|
void Editor::NotifyHotSpotReleaseClick(int position, int modifiers) {
|
|
SCNotification scn = {};
|
|
scn.nmhdr.code = SCN_HOTSPOTRELEASECLICK;
|
|
scn.position = position;
|
|
scn.modifiers = modifiers;
|
|
NotifyParent(scn);
|
|
}
|
|
|
|
void Editor::NotifyHotSpotReleaseClick(int position, bool shift, bool ctrl, bool alt) {
|
|
NotifyHotSpotReleaseClick(position, ModifierFlags(shift, ctrl, alt));
|
|
}
|
|
|
|
bool Editor::NotifyUpdateUI() {
|
|
if (needUpdateUI) {
|
|
SCNotification scn = {};
|
|
scn.nmhdr.code = SCN_UPDATEUI;
|
|
scn.updated = needUpdateUI;
|
|
NotifyParent(scn);
|
|
needUpdateUI = 0;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Editor::NotifyPainted() {
|
|
SCNotification scn = {};
|
|
scn.nmhdr.code = SCN_PAINTED;
|
|
NotifyParent(scn);
|
|
}
|
|
|
|
void Editor::NotifyIndicatorClick(bool click, int position, int modifiers) {
|
|
int mask = pdoc->decorations.AllOnFor(position);
|
|
if ((click && mask) || pdoc->decorations.clickNotified) {
|
|
SCNotification scn = {};
|
|
pdoc->decorations.clickNotified = click;
|
|
scn.nmhdr.code = click ? SCN_INDICATORCLICK : SCN_INDICATORRELEASE;
|
|
scn.modifiers = modifiers;
|
|
scn.position = position;
|
|
NotifyParent(scn);
|
|
}
|
|
}
|
|
|
|
void Editor::NotifyIndicatorClick(bool click, int position, bool shift, bool ctrl, bool alt) {
|
|
NotifyIndicatorClick(click, position, ModifierFlags(shift, ctrl, alt));
|
|
}
|
|
|
|
bool Editor::NotifyMarginClick(Point pt, int modifiers) {
|
|
int marginClicked = -1;
|
|
int x = vs.textStart - vs.fixedColumnWidth;
|
|
for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) {
|
|
if ((pt.x >= x) && (pt.x < x + vs.ms[margin].width))
|
|
marginClicked = margin;
|
|
x += vs.ms[margin].width;
|
|
}
|
|
if ((marginClicked >= 0) && vs.ms[marginClicked].sensitive) {
|
|
int position = pdoc->LineStart(LineFromLocation(pt));
|
|
if ((vs.ms[marginClicked].mask & SC_MASK_FOLDERS) && (foldAutomatic & SC_AUTOMATICFOLD_CLICK)) {
|
|
const bool ctrl = (modifiers & SCI_CTRL) != 0;
|
|
const bool shift = (modifiers & SCI_SHIFT) != 0;
|
|
int lineClick = pdoc->LineFromPosition(position);
|
|
if (shift && ctrl) {
|
|
FoldAll(SC_FOLDACTION_TOGGLE);
|
|
} else {
|
|
int levelClick = pdoc->GetLevel(lineClick);
|
|
if (levelClick & SC_FOLDLEVELHEADERFLAG) {
|
|
if (shift) {
|
|
// Ensure all children visible
|
|
FoldExpand(lineClick, SC_FOLDACTION_EXPAND, levelClick);
|
|
} else if (ctrl) {
|
|
FoldExpand(lineClick, SC_FOLDACTION_TOGGLE, levelClick);
|
|
} else {
|
|
// Toggle this line
|
|
FoldLine(lineClick, SC_FOLDACTION_TOGGLE);
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
SCNotification scn = {};
|
|
scn.nmhdr.code = SCN_MARGINCLICK;
|
|
scn.modifiers = modifiers;
|
|
scn.position = position;
|
|
scn.margin = marginClicked;
|
|
NotifyParent(scn);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool Editor::NotifyMarginClick(Point pt, bool shift, bool ctrl, bool alt) {
|
|
return NotifyMarginClick(pt, ModifierFlags(shift, ctrl, alt));
|
|
}
|
|
|
|
void Editor::NotifyNeedShown(int pos, int len) {
|
|
SCNotification scn = {};
|
|
scn.nmhdr.code = SCN_NEEDSHOWN;
|
|
scn.position = pos;
|
|
scn.length = len;
|
|
NotifyParent(scn);
|
|
}
|
|
|
|
void Editor::NotifyDwelling(Point pt, bool state) {
|
|
SCNotification scn = {};
|
|
scn.nmhdr.code = state ? SCN_DWELLSTART : SCN_DWELLEND;
|
|
scn.position = PositionFromLocation(pt, true);
|
|
scn.x = static_cast<int>(pt.x + vs.ExternalMarginWidth());
|
|
scn.y = static_cast<int>(pt.y);
|
|
NotifyParent(scn);
|
|
}
|
|
|
|
void Editor::NotifyZoom() {
|
|
SCNotification scn = {};
|
|
scn.nmhdr.code = SCN_ZOOM;
|
|
NotifyParent(scn);
|
|
}
|
|
|
|
// Notifications from document
|
|
void Editor::NotifyModifyAttempt(Document *, void *) {
|
|
//Platform::DebugPrintf("** Modify Attempt\n");
|
|
NotifyModifyAttempt();
|
|
}
|
|
|
|
void Editor::NotifySavePoint(Document *, void *, bool atSavePoint) {
|
|
//Platform::DebugPrintf("** Save Point %s\n", atSavePoint ? "On" : "Off");
|
|
NotifySavePoint(atSavePoint);
|
|
}
|
|
|
|
void Editor::CheckModificationForWrap(DocModification mh) {
|
|
if (mh.modificationType & (SC_MOD_INSERTTEXT | SC_MOD_DELETETEXT)) {
|
|
llc.Invalidate(LineLayout::llCheckTextAndStyle);
|
|
int lineDoc = pdoc->LineFromPosition(mh.position);
|
|
int lines = Platform::Maximum(0, mh.linesAdded);
|
|
if (Wrapping()) {
|
|
NeedWrapping(lineDoc, lineDoc + lines + 1);
|
|
}
|
|
RefreshStyleData();
|
|
// Fix up annotation heights
|
|
SetAnnotationHeights(lineDoc, lineDoc + lines + 2);
|
|
}
|
|
}
|
|
|
|
// Move a position so it is still after the same character as before the insertion.
|
|
static inline int MovePositionForInsertion(int position, int startInsertion, int length) {
|
|
if (position > startInsertion) {
|
|
return position + length;
|
|
}
|
|
return position;
|
|
}
|
|
|
|
// Move a position so it is still after the same character as before the deletion if that
|
|
// character is still present else after the previous surviving character.
|
|
static inline int MovePositionForDeletion(int position, int startDeletion, int length) {
|
|
if (position > startDeletion) {
|
|
int endDeletion = startDeletion + length;
|
|
if (position > endDeletion) {
|
|
return position - length;
|
|
} else {
|
|
return startDeletion;
|
|
}
|
|
} else {
|
|
return position;
|
|
}
|
|
}
|
|
|
|
void Editor::NotifyModified(Document *, DocModification mh, void *) {
|
|
ContainerNeedsUpdate(SC_UPDATE_CONTENT);
|
|
if (paintState == painting) {
|
|
CheckForChangeOutsidePaint(Range(mh.position, mh.position + mh.length));
|
|
}
|
|
if (mh.modificationType & SC_MOD_CHANGELINESTATE) {
|
|
if (paintState == painting) {
|
|
CheckForChangeOutsidePaint(
|
|
Range(pdoc->LineStart(mh.line), pdoc->LineStart(mh.line + 1)));
|
|
} else {
|
|
// Could check that change is before last visible line.
|
|
Redraw();
|
|
}
|
|
}
|
|
if (mh.modificationType & SC_MOD_LEXERSTATE) {
|
|
if (paintState == painting) {
|
|
CheckForChangeOutsidePaint(
|
|
Range(mh.position, mh.position + mh.length));
|
|
} else {
|
|
Redraw();
|
|
}
|
|
}
|
|
if (mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) {
|
|
if (mh.modificationType & SC_MOD_CHANGESTYLE) {
|
|
pdoc->IncrementStyleClock();
|
|
}
|
|
if (paintState == notPainting) {
|
|
if (mh.position < pdoc->LineStart(topLine)) {
|
|
// Styling performed before this view
|
|
Redraw();
|
|
} else {
|
|
InvalidateRange(mh.position, mh.position + mh.length);
|
|
}
|
|
}
|
|
if (mh.modificationType & SC_MOD_CHANGESTYLE) {
|
|
llc.Invalidate(LineLayout::llCheckTextAndStyle);
|
|
}
|
|
} else {
|
|
// Move selection and brace highlights
|
|
if (mh.modificationType & SC_MOD_INSERTTEXT) {
|
|
sel.MovePositions(true, mh.position, mh.length);
|
|
braces[0] = MovePositionForInsertion(braces[0], mh.position, mh.length);
|
|
braces[1] = MovePositionForInsertion(braces[1], mh.position, mh.length);
|
|
} else if (mh.modificationType & SC_MOD_DELETETEXT) {
|
|
sel.MovePositions(false, mh.position, mh.length);
|
|
braces[0] = MovePositionForDeletion(braces[0], mh.position, mh.length);
|
|
braces[1] = MovePositionForDeletion(braces[1], mh.position, mh.length);
|
|
}
|
|
if ((mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE)) && cs.HiddenLines()) {
|
|
// Some lines are hidden so may need shown.
|
|
// TODO: check if the modified area is hidden.
|
|
if (mh.modificationType & SC_MOD_BEFOREINSERT) {
|
|
int lineOfPos = pdoc->LineFromPosition(mh.position);
|
|
bool insertingNewLine = false;
|
|
for (int i=0; i < mh.length; i++) {
|
|
if ((mh.text[i] == '\n') || (mh.text[i] == '\r'))
|
|
insertingNewLine = true;
|
|
}
|
|
if (insertingNewLine && (mh.position != pdoc->LineStart(lineOfPos)))
|
|
NeedShown(mh.position, pdoc->LineStart(lineOfPos+1) - mh.position);
|
|
else
|
|
NeedShown(mh.position, 0);
|
|
} else if (mh.modificationType & SC_MOD_BEFOREDELETE) {
|
|
NeedShown(mh.position, mh.length);
|
|
}
|
|
}
|
|
if (mh.linesAdded != 0) {
|
|
// Update contraction state for inserted and removed lines
|
|
// lineOfPos should be calculated in context of state before modification, shouldn't it
|
|
int lineOfPos = pdoc->LineFromPosition(mh.position);
|
|
if (mh.position > pdoc->LineStart(lineOfPos))
|
|
lineOfPos++; // Affecting subsequent lines
|
|
if (mh.linesAdded > 0) {
|
|
cs.InsertLines(lineOfPos, mh.linesAdded);
|
|
} else {
|
|
cs.DeleteLines(lineOfPos, -mh.linesAdded);
|
|
}
|
|
}
|
|
if (mh.modificationType & SC_MOD_CHANGEANNOTATION) {
|
|
int lineDoc = pdoc->LineFromPosition(mh.position);
|
|
if (vs.annotationVisible) {
|
|
cs.SetHeight(lineDoc, cs.GetHeight(lineDoc) + mh.annotationLinesAdded);
|
|
Redraw();
|
|
}
|
|
}
|
|
CheckModificationForWrap(mh);
|
|
if (mh.linesAdded != 0) {
|
|
// Avoid scrolling of display if change before current display
|
|
if (mh.position < posTopLine && !CanDeferToLastStep(mh)) {
|
|
int newTop = Platform::Clamp(topLine + mh.linesAdded, 0, MaxScrollPos());
|
|
if (newTop != topLine) {
|
|
SetTopLine(newTop);
|
|
SetVerticalScrollPos();
|
|
}
|
|
}
|
|
|
|
if (paintState == notPainting && !CanDeferToLastStep(mh)) {
|
|
QueueIdleWork(WorkNeeded::workStyle, pdoc->Length());
|
|
Redraw();
|
|
}
|
|
} else {
|
|
if (paintState == notPainting && mh.length && !CanEliminate(mh)) {
|
|
QueueIdleWork(WorkNeeded::workStyle, mh.position + mh.length);
|
|
InvalidateRange(mh.position, mh.position + mh.length);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mh.linesAdded != 0 && !CanDeferToLastStep(mh)) {
|
|
SetScrollBars();
|
|
}
|
|
|
|
if ((mh.modificationType & SC_MOD_CHANGEMARKER) || (mh.modificationType & SC_MOD_CHANGEMARGIN)) {
|
|
if ((!willRedrawAll) && ((paintState == notPainting) || !PaintContainsMargin())) {
|
|
if (mh.modificationType & SC_MOD_CHANGEFOLD) {
|
|
// Fold changes can affect the drawing of following lines so redraw whole margin
|
|
RedrawSelMargin(highlightDelimiter.isEnabled ? -1 : mh.line-1, true);
|
|
} else {
|
|
RedrawSelMargin(mh.line);
|
|
}
|
|
}
|
|
}
|
|
if ((mh.modificationType & SC_MOD_CHANGEFOLD) && (foldAutomatic & SC_AUTOMATICFOLD_CHANGE)) {
|
|
FoldChanged(mh.line, mh.foldLevelNow, mh.foldLevelPrev);
|
|
}
|
|
|
|
// NOW pay the piper WRT "deferred" visual updates
|
|
if (IsLastStep(mh)) {
|
|
SetScrollBars();
|
|
Redraw();
|
|
}
|
|
|
|
// If client wants to see this modification
|
|
if (mh.modificationType & modEventMask) {
|
|
if ((mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) == 0) {
|
|
// Real modification made to text of document.
|
|
NotifyChange(); // Send EN_CHANGE
|
|
}
|
|
|
|
SCNotification scn = {};
|
|
scn.nmhdr.code = SCN_MODIFIED;
|
|
scn.position = mh.position;
|
|
scn.modificationType = mh.modificationType;
|
|
scn.text = mh.text;
|
|
scn.length = mh.length;
|
|
scn.linesAdded = mh.linesAdded;
|
|
scn.line = mh.line;
|
|
scn.foldLevelNow = mh.foldLevelNow;
|
|
scn.foldLevelPrev = mh.foldLevelPrev;
|
|
scn.token = mh.token;
|
|
scn.annotationLinesAdded = mh.annotationLinesAdded;
|
|
NotifyParent(scn);
|
|
}
|
|
}
|
|
|
|
void Editor::NotifyDeleted(Document *, void *) {
|
|
/* Do nothing */
|
|
}
|
|
|
|
void Editor::NotifyMacroRecord(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
|
|
|
|
// Enumerates all macroable messages
|
|
switch (iMessage) {
|
|
case SCI_CUT:
|
|
case SCI_COPY:
|
|
case SCI_PASTE:
|
|
case SCI_CLEAR:
|
|
case SCI_REPLACESEL:
|
|
case SCI_ADDTEXT:
|
|
case SCI_INSERTTEXT:
|
|
case SCI_APPENDTEXT:
|
|
case SCI_CLEARALL:
|
|
case SCI_SELECTALL:
|
|
case SCI_GOTOLINE:
|
|
case SCI_GOTOPOS:
|
|
case SCI_SEARCHANCHOR:
|
|
case SCI_SEARCHNEXT:
|
|
case SCI_SEARCHPREV:
|
|
case SCI_LINEDOWN:
|
|
case SCI_LINEDOWNEXTEND:
|
|
case SCI_PARADOWN:
|
|
case SCI_PARADOWNEXTEND:
|
|
case SCI_LINEUP:
|
|
case SCI_LINEUPEXTEND:
|
|
case SCI_PARAUP:
|
|
case SCI_PARAUPEXTEND:
|
|
case SCI_CHARLEFT:
|
|
case SCI_CHARLEFTEXTEND:
|
|
case SCI_CHARRIGHT:
|
|
case SCI_CHARRIGHTEXTEND:
|
|
case SCI_WORDLEFT:
|
|
case SCI_WORDLEFTEXTEND:
|
|
case SCI_WORDRIGHT:
|
|
case SCI_WORDRIGHTEXTEND:
|
|
case SCI_WORDPARTLEFT:
|
|
case SCI_WORDPARTLEFTEXTEND:
|
|
case SCI_WORDPARTRIGHT:
|
|
case SCI_WORDPARTRIGHTEXTEND:
|
|
case SCI_WORDLEFTEND:
|
|
case SCI_WORDLEFTENDEXTEND:
|
|
case SCI_WORDRIGHTEND:
|
|
case SCI_WORDRIGHTENDEXTEND:
|
|
case SCI_HOME:
|
|
case SCI_HOMEEXTEND:
|
|
case SCI_LINEEND:
|
|
case SCI_LINEENDEXTEND:
|
|
case SCI_HOMEWRAP:
|
|
case SCI_HOMEWRAPEXTEND:
|
|
case SCI_LINEENDWRAP:
|
|
case SCI_LINEENDWRAPEXTEND:
|
|
case SCI_DOCUMENTSTART:
|
|
case SCI_DOCUMENTSTARTEXTEND:
|
|
case SCI_DOCUMENTEND:
|
|
case SCI_DOCUMENTENDEXTEND:
|
|
case SCI_STUTTEREDPAGEUP:
|
|
case SCI_STUTTEREDPAGEUPEXTEND:
|
|
case SCI_STUTTEREDPAGEDOWN:
|
|
case SCI_STUTTEREDPAGEDOWNEXTEND:
|
|
case SCI_PAGEUP:
|
|
case SCI_PAGEUPEXTEND:
|
|
case SCI_PAGEDOWN:
|
|
case SCI_PAGEDOWNEXTEND:
|
|
case SCI_EDITTOGGLEOVERTYPE:
|
|
case SCI_CANCEL:
|
|
case SCI_DELETEBACK:
|
|
case SCI_TAB:
|
|
case SCI_BACKTAB:
|
|
case SCI_FORMFEED:
|
|
case SCI_VCHOME:
|
|
case SCI_VCHOMEEXTEND:
|
|
case SCI_VCHOMEWRAP:
|
|
case SCI_VCHOMEWRAPEXTEND:
|
|
case SCI_VCHOMEDISPLAY:
|
|
case SCI_VCHOMEDISPLAYEXTEND:
|
|
case SCI_DELWORDLEFT:
|
|
case SCI_DELWORDRIGHT:
|
|
case SCI_DELWORDRIGHTEND:
|
|
case SCI_DELLINELEFT:
|
|
case SCI_DELLINERIGHT:
|
|
case SCI_LINECOPY:
|
|
case SCI_LINECUT:
|
|
case SCI_LINEDELETE:
|
|
case SCI_LINETRANSPOSE:
|
|
case SCI_LINEDUPLICATE:
|
|
case SCI_LOWERCASE:
|
|
case SCI_UPPERCASE:
|
|
case SCI_LINESCROLLDOWN:
|
|
case SCI_LINESCROLLUP:
|
|
case SCI_DELETEBACKNOTLINE:
|
|
case SCI_HOMEDISPLAY:
|
|
case SCI_HOMEDISPLAYEXTEND:
|
|
case SCI_LINEENDDISPLAY:
|
|
case SCI_LINEENDDISPLAYEXTEND:
|
|
case SCI_SETSELECTIONMODE:
|
|
case SCI_LINEDOWNRECTEXTEND:
|
|
case SCI_LINEUPRECTEXTEND:
|
|
case SCI_CHARLEFTRECTEXTEND:
|
|
case SCI_CHARRIGHTRECTEXTEND:
|
|
case SCI_HOMERECTEXTEND:
|
|
case SCI_VCHOMERECTEXTEND:
|
|
case SCI_LINEENDRECTEXTEND:
|
|
case SCI_PAGEUPRECTEXTEND:
|
|
case SCI_PAGEDOWNRECTEXTEND:
|
|
case SCI_SELECTIONDUPLICATE:
|
|
case SCI_COPYALLOWLINE:
|
|
case SCI_VERTICALCENTRECARET:
|
|
case SCI_MOVESELECTEDLINESUP:
|
|
case SCI_MOVESELECTEDLINESDOWN:
|
|
case SCI_SCROLLTOSTART:
|
|
case SCI_SCROLLTOEND:
|
|
break;
|
|
|
|
// Filter out all others like display changes. Also, newlines are redundant
|
|
// with char insert messages.
|
|
case SCI_NEWLINE:
|
|
default:
|
|
// printf("Filtered out %ld of macro recording\n", iMessage);
|
|
return;
|
|
}
|
|
|
|
// Send notification
|
|
SCNotification scn = {};
|
|
scn.nmhdr.code = SCN_MACRORECORD;
|
|
scn.message = iMessage;
|
|
scn.wParam = wParam;
|
|
scn.lParam = lParam;
|
|
NotifyParent(scn);
|
|
}
|
|
|
|
// Something has changed that the container should know about
|
|
void Editor::ContainerNeedsUpdate(int flags) {
|
|
needUpdateUI |= flags;
|
|
}
|
|
|
|
/**
|
|
* Force scroll and keep position relative to top of window.
|
|
*
|
|
* If stuttered = true and not already at first/last row, move to first/last row of window.
|
|
* If stuttered = true and already at first/last row, scroll as normal.
|
|
*/
|
|
void Editor::PageMove(int direction, Selection::selTypes selt, bool stuttered) {
|
|
int topLineNew;
|
|
SelectionPosition newPos;
|
|
|
|
int currentLine = pdoc->LineFromPosition(sel.MainCaret());
|
|
int topStutterLine = topLine + caretYSlop;
|
|
int bottomStutterLine =
|
|
pdoc->LineFromPosition(PositionFromLocation(
|
|
Point::FromInts(lastXChosen - xOffset, direction * vs.lineHeight * LinesToScroll())))
|
|
- caretYSlop - 1;
|
|
|
|
if (stuttered && (direction < 0 && currentLine > topStutterLine)) {
|
|
topLineNew = topLine;
|
|
newPos = SPositionFromLocation(Point::FromInts(lastXChosen - xOffset, vs.lineHeight * caretYSlop),
|
|
false, false, UserVirtualSpace());
|
|
|
|
} else if (stuttered && (direction > 0 && currentLine < bottomStutterLine)) {
|
|
topLineNew = topLine;
|
|
newPos = SPositionFromLocation(Point::FromInts(lastXChosen - xOffset, vs.lineHeight * (LinesToScroll() - caretYSlop)),
|
|
false, false, UserVirtualSpace());
|
|
|
|
} else {
|
|
Point pt = LocationFromPosition(sel.MainCaret());
|
|
|
|
topLineNew = Platform::Clamp(
|
|
topLine + direction * LinesToScroll(), 0, MaxScrollPos());
|
|
newPos = SPositionFromLocation(
|
|
Point::FromInts(lastXChosen - xOffset, static_cast<int>(pt.y) + direction * (vs.lineHeight * LinesToScroll())),
|
|
false, false, UserVirtualSpace());
|
|
}
|
|
|
|
if (topLineNew != topLine) {
|
|
SetTopLine(topLineNew);
|
|
MovePositionTo(newPos, selt);
|
|
Redraw();
|
|
SetVerticalScrollPos();
|
|
} else {
|
|
MovePositionTo(newPos, selt);
|
|
}
|
|
}
|
|
|
|
void Editor::ChangeCaseOfSelection(int caseMapping) {
|
|
UndoGroup ug(pdoc);
|
|
for (size_t r=0; r<sel.Count(); r++) {
|
|
SelectionRange current = sel.Range(r);
|
|
SelectionRange currentNoVS = current;
|
|
currentNoVS.ClearVirtualSpace();
|
|
size_t rangeBytes = currentNoVS.Length();
|
|
if (rangeBytes > 0) {
|
|
std::string sText = RangeText(currentNoVS.Start().Position(), currentNoVS.End().Position());
|
|
|
|
std::string sMapped = CaseMapString(sText, caseMapping);
|
|
|
|
if (sMapped != sText) {
|
|
size_t firstDifference = 0;
|
|
while (sMapped[firstDifference] == sText[firstDifference])
|
|
firstDifference++;
|
|
size_t lastDifferenceText = sText.size() - 1;
|
|
size_t lastDifferenceMapped = sMapped.size() - 1;
|
|
while (sMapped[lastDifferenceMapped] == sText[lastDifferenceText]) {
|
|
lastDifferenceText--;
|
|
lastDifferenceMapped--;
|
|
}
|
|
size_t endDifferenceText = sText.size() - 1 - lastDifferenceText;
|
|
pdoc->DeleteChars(
|
|
static_cast<int>(currentNoVS.Start().Position() + firstDifference),
|
|
static_cast<int>(rangeBytes - firstDifference - endDifferenceText));
|
|
const int lengthChange = static_cast<int>(lastDifferenceMapped - firstDifference + 1);
|
|
const int lengthInserted = pdoc->InsertString(
|
|
static_cast<int>(currentNoVS.Start().Position() + firstDifference),
|
|
sMapped.c_str() + firstDifference,
|
|
lengthChange);
|
|
// Automatic movement changes selection so reset to exactly the same as it was.
|
|
int diffSizes = static_cast<int>(sMapped.size() - sText.size()) + lengthInserted - lengthChange;
|
|
if (diffSizes != 0) {
|
|
if (current.anchor > current.caret)
|
|
current.anchor.Add(diffSizes);
|
|
else
|
|
current.caret.Add(diffSizes);
|
|
}
|
|
sel.Range(r) = current;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Editor::LineTranspose() {
|
|
int line = pdoc->LineFromPosition(sel.MainCaret());
|
|
if (line > 0) {
|
|
UndoGroup ug(pdoc);
|
|
|
|
const int startPrevious = pdoc->LineStart(line - 1);
|
|
const std::string linePrevious = RangeText(startPrevious, pdoc->LineEnd(line - 1));
|
|
|
|
int startCurrent = pdoc->LineStart(line);
|
|
const std::string lineCurrent = RangeText(startCurrent, pdoc->LineEnd(line));
|
|
|
|
pdoc->DeleteChars(startCurrent, static_cast<int>(lineCurrent.length()));
|
|
pdoc->DeleteChars(startPrevious, static_cast<int>(linePrevious.length()));
|
|
startCurrent -= static_cast<int>(linePrevious.length());
|
|
|
|
startCurrent += pdoc->InsertString(startPrevious, lineCurrent.c_str(),
|
|
static_cast<int>(lineCurrent.length()));
|
|
pdoc->InsertString(startCurrent, linePrevious.c_str(),
|
|
static_cast<int>(linePrevious.length()));
|
|
// Move caret to start of current line
|
|
MovePositionTo(SelectionPosition(startCurrent));
|
|
}
|
|
}
|
|
|
|
void Editor::Duplicate(bool forLine) {
|
|
if (sel.Empty()) {
|
|
forLine = true;
|
|
}
|
|
UndoGroup ug(pdoc);
|
|
const char *eol = "";
|
|
int eolLen = 0;
|
|
if (forLine) {
|
|
eol = StringFromEOLMode(pdoc->eolMode);
|
|
eolLen = istrlen(eol);
|
|
}
|
|
for (size_t r=0; r<sel.Count(); r++) {
|
|
SelectionPosition start = sel.Range(r).Start();
|
|
SelectionPosition end = sel.Range(r).End();
|
|
if (forLine) {
|
|
int line = pdoc->LineFromPosition(sel.Range(r).caret.Position());
|
|
start = SelectionPosition(pdoc->LineStart(line));
|
|
end = SelectionPosition(pdoc->LineEnd(line));
|
|
}
|
|
std::string text = RangeText(start.Position(), end.Position());
|
|
int lengthInserted = eolLen;
|
|
if (forLine)
|
|
lengthInserted = pdoc->InsertString(end.Position(), eol, eolLen);
|
|
pdoc->InsertString(end.Position() + lengthInserted, text.c_str(), static_cast<int>(text.length()));
|
|
}
|
|
if (sel.Count() && sel.IsRectangular()) {
|
|
SelectionPosition last = sel.Last();
|
|
if (forLine) {
|
|
int line = pdoc->LineFromPosition(last.Position());
|
|
last = SelectionPosition(last.Position() + pdoc->LineStart(line+1) - pdoc->LineStart(line));
|
|
}
|
|
if (sel.Rectangular().anchor > sel.Rectangular().caret)
|
|
sel.Rectangular().anchor = last;
|
|
else
|
|
sel.Rectangular().caret = last;
|
|
SetRectangularRange();
|
|
}
|
|
}
|
|
|
|
void Editor::CancelModes() {
|
|
sel.SetMoveExtends(false);
|
|
}
|
|
|
|
void Editor::NewLine() {
|
|
// Remove non-main ranges
|
|
InvalidateSelection(sel.RangeMain(), true);
|
|
sel.SetSelection(sel.RangeMain());
|
|
sel.RangeMain().ClearVirtualSpace();
|
|
|
|
// Clear main range and insert line end
|
|
bool needGroupUndo = !sel.Empty();
|
|
if (needGroupUndo)
|
|
pdoc->BeginUndoAction();
|
|
|
|
if (!sel.Empty())
|
|
ClearSelection();
|
|
const char *eol = "\n";
|
|
if (pdoc->eolMode == SC_EOL_CRLF) {
|
|
eol = "\r\n";
|
|
} else if (pdoc->eolMode == SC_EOL_CR) {
|
|
eol = "\r";
|
|
} // else SC_EOL_LF -> "\n" already set
|
|
const int insertLength = pdoc->InsertString(sel.MainCaret(), eol, istrlen(eol));
|
|
// Want to end undo group before NotifyChar as applications often modify text here
|
|
if (needGroupUndo)
|
|
pdoc->EndUndoAction();
|
|
if (insertLength > 0) {
|
|
SetEmptySelection(sel.MainCaret() + insertLength);
|
|
while (*eol) {
|
|
NotifyChar(*eol);
|
|
if (recordingMacro) {
|
|
char txt[2];
|
|
txt[0] = *eol;
|
|
txt[1] = '\0';
|
|
NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(txt));
|
|
}
|
|
eol++;
|
|
}
|
|
}
|
|
SetLastXChosen();
|
|
SetScrollBars();
|
|
EnsureCaretVisible();
|
|
// Avoid blinking during rapid typing:
|
|
ShowCaretAtCurrentPosition();
|
|
}
|
|
|
|
void Editor::CursorUpOrDown(int direction, Selection::selTypes selt) {
|
|
SelectionPosition caretToUse = sel.Range(sel.Main()).caret;
|
|
if (sel.IsRectangular()) {
|
|
if (selt == Selection::noSel) {
|
|
caretToUse = (direction > 0) ? sel.Limits().end : sel.Limits().start;
|
|
} else {
|
|
caretToUse = sel.Rectangular().caret;
|
|
}
|
|
}
|
|
|
|
Point pt = LocationFromPosition(caretToUse);
|
|
int skipLines = 0;
|
|
|
|
if (vs.annotationVisible) {
|
|
int lineDoc = pdoc->LineFromPosition(caretToUse.Position());
|
|
Point ptStartLine = LocationFromPosition(pdoc->LineStart(lineDoc));
|
|
int subLine = static_cast<int>(pt.y - ptStartLine.y) / vs.lineHeight;
|
|
|
|
if (direction < 0 && subLine == 0) {
|
|
int lineDisplay = cs.DisplayFromDoc(lineDoc);
|
|
if (lineDisplay > 0) {
|
|
skipLines = pdoc->AnnotationLines(cs.DocFromDisplay(lineDisplay - 1));
|
|
}
|
|
} else if (direction > 0 && subLine >= (cs.GetHeight(lineDoc) - 1 - pdoc->AnnotationLines(lineDoc))) {
|
|
skipLines = pdoc->AnnotationLines(lineDoc);
|
|
}
|
|
}
|
|
|
|
int newY = static_cast<int>(pt.y) + (1 + skipLines) * direction * vs.lineHeight;
|
|
SelectionPosition posNew = SPositionFromLocation(
|
|
Point::FromInts(lastXChosen - xOffset, newY), false, false, UserVirtualSpace());
|
|
|
|
if (direction < 0) {
|
|
// Line wrapping may lead to a location on the same line, so
|
|
// seek back if that is the case.
|
|
Point ptNew = LocationFromPosition(posNew.Position());
|
|
while ((posNew.Position() > 0) && (pt.y == ptNew.y)) {
|
|
posNew.Add(-1);
|
|
posNew.SetVirtualSpace(0);
|
|
ptNew = LocationFromPosition(posNew.Position());
|
|
}
|
|
} else if (direction > 0 && posNew.Position() != pdoc->Length()) {
|
|
// There is an equivalent case when moving down which skips
|
|
// over a line.
|
|
Point ptNew = LocationFromPosition(posNew.Position());
|
|
while ((posNew.Position() > caretToUse.Position()) && (ptNew.y > newY)) {
|
|
posNew.Add(-1);
|
|
posNew.SetVirtualSpace(0);
|
|
ptNew = LocationFromPosition(posNew.Position());
|
|
}
|
|
}
|
|
|
|
MovePositionTo(MovePositionSoVisible(posNew, direction), selt);
|
|
}
|
|
|
|
void Editor::ParaUpOrDown(int direction, Selection::selTypes selt) {
|
|
int lineDoc, savedPos = sel.MainCaret();
|
|
do {
|
|
MovePositionTo(SelectionPosition(direction > 0 ? pdoc->ParaDown(sel.MainCaret()) : pdoc->ParaUp(sel.MainCaret())), selt);
|
|
lineDoc = pdoc->LineFromPosition(sel.MainCaret());
|
|
if (direction > 0) {
|
|
if (sel.MainCaret() >= pdoc->Length() && !cs.GetVisible(lineDoc)) {
|
|
if (selt == Selection::noSel) {
|
|
MovePositionTo(SelectionPosition(pdoc->LineEndPosition(savedPos)));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
} while (!cs.GetVisible(lineDoc));
|
|
}
|
|
|
|
int Editor::StartEndDisplayLine(int pos, bool start) {
|
|
RefreshStyleData();
|
|
int line = pdoc->LineFromPosition(pos);
|
|
AutoSurface surface(this);
|
|
AutoLineLayout ll(llc, RetrieveLineLayout(line));
|
|
int posRet = INVALID_POSITION;
|
|
if (surface && ll) {
|
|
unsigned int posLineStart = pdoc->LineStart(line);
|
|
LayoutLine(line, surface, vs, ll, wrapWidth);
|
|
int posInLine = pos - posLineStart;
|
|
if (posInLine <= ll->maxLineLength) {
|
|
for (int subLine = 0; subLine < ll->lines; subLine++) {
|
|
if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) {
|
|
if (start) {
|
|
posRet = ll->LineStart(subLine) + posLineStart;
|
|
} else {
|
|
if (subLine == ll->lines - 1)
|
|
posRet = ll->LineStart(subLine + 1) + posLineStart;
|
|
else
|
|
posRet = ll->LineStart(subLine + 1) + posLineStart - 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (posRet == INVALID_POSITION) {
|
|
return pos;
|
|
} else {
|
|
return posRet;
|
|
}
|
|
}
|
|
|
|
int Editor::KeyCommand(unsigned int iMessage) {
|
|
switch (iMessage) {
|
|
case SCI_LINEDOWN:
|
|
CursorUpOrDown(1);
|
|
break;
|
|
case SCI_LINEDOWNEXTEND:
|
|
CursorUpOrDown(1, Selection::selStream);
|
|
break;
|
|
case SCI_LINEDOWNRECTEXTEND:
|
|
CursorUpOrDown(1, Selection::selRectangle);
|
|
break;
|
|
case SCI_PARADOWN:
|
|
ParaUpOrDown(1);
|
|
break;
|
|
case SCI_PARADOWNEXTEND:
|
|
ParaUpOrDown(1, Selection::selStream);
|
|
break;
|
|
case SCI_LINESCROLLDOWN:
|
|
ScrollTo(topLine + 1);
|
|
MoveCaretInsideView(false);
|
|
break;
|
|
case SCI_LINEUP:
|
|
CursorUpOrDown(-1);
|
|
break;
|
|
case SCI_LINEUPEXTEND:
|
|
CursorUpOrDown(-1, Selection::selStream);
|
|
break;
|
|
case SCI_LINEUPRECTEXTEND:
|
|
CursorUpOrDown(-1, Selection::selRectangle);
|
|
break;
|
|
case SCI_PARAUP:
|
|
ParaUpOrDown(-1);
|
|
break;
|
|
case SCI_PARAUPEXTEND:
|
|
ParaUpOrDown(-1, Selection::selStream);
|
|
break;
|
|
case SCI_LINESCROLLUP:
|
|
ScrollTo(topLine - 1);
|
|
MoveCaretInsideView(false);
|
|
break;
|
|
case SCI_CHARLEFT:
|
|
if (SelectionEmpty() || sel.MoveExtends()) {
|
|
if ((sel.Count() == 1) && pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
|
|
SelectionPosition spCaret = sel.RangeMain().caret;
|
|
spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
|
|
MovePositionTo(spCaret);
|
|
} else if (sel.MoveExtends() && sel.selType == Selection::selStream) {
|
|
MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1));
|
|
} else {
|
|
MovePositionTo(MovePositionSoVisible(
|
|
SelectionPosition((sel.LimitsForRectangularElseMain().start).Position() - 1), -1));
|
|
}
|
|
} else {
|
|
MovePositionTo(sel.LimitsForRectangularElseMain().start);
|
|
}
|
|
SetLastXChosen();
|
|
break;
|
|
case SCI_CHARLEFTEXTEND:
|
|
if (pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
|
|
SelectionPosition spCaret = sel.RangeMain().caret;
|
|
spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
|
|
MovePositionTo(spCaret, Selection::selStream);
|
|
} else {
|
|
MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1), Selection::selStream);
|
|
}
|
|
SetLastXChosen();
|
|
break;
|
|
case SCI_CHARLEFTRECTEXTEND:
|
|
if (pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
|
|
SelectionPosition spCaret = sel.RangeMain().caret;
|
|
spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
|
|
MovePositionTo(spCaret, Selection::selRectangle);
|
|
} else {
|
|
MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1), Selection::selRectangle);
|
|
}
|
|
SetLastXChosen();
|
|
break;
|
|
case SCI_CHARRIGHT:
|
|
if (SelectionEmpty() || sel.MoveExtends()) {
|
|
if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(sel.MainCaret())) {
|
|
SelectionPosition spCaret = sel.RangeMain().caret;
|
|
spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
|
|
MovePositionTo(spCaret);
|
|
} else if (sel.MoveExtends() && sel.selType == Selection::selStream) {
|
|
MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1));
|
|
} else {
|
|
MovePositionTo(MovePositionSoVisible(
|
|
SelectionPosition((sel.LimitsForRectangularElseMain().end).Position() + 1), 1));
|
|
}
|
|
} else {
|
|
MovePositionTo(sel.LimitsForRectangularElseMain().end);
|
|
}
|
|
SetLastXChosen();
|
|
break;
|
|
case SCI_CHARRIGHTEXTEND:
|
|
if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(sel.MainCaret())) {
|
|
SelectionPosition spCaret = sel.RangeMain().caret;
|
|
spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
|
|
MovePositionTo(spCaret, Selection::selStream);
|
|
} else {
|
|
MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1), Selection::selStream);
|
|
}
|
|
SetLastXChosen();
|
|
break;
|
|
case SCI_CHARRIGHTRECTEXTEND:
|
|
if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) && pdoc->IsLineEndPosition(sel.MainCaret())) {
|
|
SelectionPosition spCaret = sel.RangeMain().caret;
|
|
spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
|
|
MovePositionTo(spCaret, Selection::selRectangle);
|
|
} else {
|
|
MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1), Selection::selRectangle);
|
|
}
|
|
SetLastXChosen();
|
|
break;
|
|
case SCI_WORDLEFT:
|
|
MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), -1), -1));
|
|
SetLastXChosen();
|
|
break;
|
|
case SCI_WORDLEFTEXTEND:
|
|
MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), -1), -1), Selection::selStream);
|
|
SetLastXChosen();
|
|
break;
|
|
case SCI_WORDRIGHT:
|
|
MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), 1), 1));
|
|
SetLastXChosen();
|
|
break;
|
|
case SCI_WORDRIGHTEXTEND:
|
|
MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), 1), 1), Selection::selStream);
|
|
SetLastXChosen();
|
|
break;
|
|
|
|
case SCI_WORDLEFTEND:
|
|
MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), -1), -1));
|
|
SetLastXChosen();
|
|
break;
|
|
case SCI_WORDLEFTENDEXTEND:
|
|
MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), -1), -1), Selection::selStream);
|
|
SetLastXChosen();
|
|
break;
|
|
case SCI_WORDRIGHTEND:
|
|
MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), 1), 1));
|
|
SetLastXChosen();
|
|
break;
|
|
case SCI_WORDRIGHTENDEXTEND:
|
|
MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), 1), 1), Selection::selStream);
|
|
SetLastXChosen();
|
|
break;
|
|
|
|
case SCI_HOME:
|
|
MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
|
|
SetLastXChosen();
|
|
break;
|
|
case SCI_HOMEEXTEND:
|
|
MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())), Selection::selStream);
|
|
SetLastXChosen();
|
|
break;
|
|
case SCI_HOMERECTEXTEND:
|
|
MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())), Selection::selRectangle);
|
|
SetLastXChosen();
|
|
break;
|
|
case SCI_LINEEND:
|
|
MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()));
|
|
SetLastXChosen();
|
|
break;
|
|
case SCI_LINEENDEXTEND:
|
|
MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()), Selection::selStream);
|
|
SetLastXChosen();
|
|
break;
|
|
case SCI_LINEENDRECTEXTEND:
|
|
MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()), Selection::selRectangle);
|
|
SetLastXChosen();
|
|
break;
|
|
case SCI_HOMEWRAP: {
|
|
SelectionPosition homePos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
|
|
if (sel.RangeMain().caret <= homePos)
|
|
homePos = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
|
|
MovePositionTo(homePos);
|
|
SetLastXChosen();
|
|
}
|
|
break;
|
|
case SCI_HOMEWRAPEXTEND: {
|
|
SelectionPosition homePos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
|
|
if (sel.RangeMain().caret <= homePos)
|
|
homePos = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
|
|
MovePositionTo(homePos, Selection::selStream);
|
|
SetLastXChosen();
|
|
}
|
|
break;
|
|
case SCI_LINEENDWRAP: {
|
|
SelectionPosition endPos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), false), 1);
|
|
SelectionPosition realEndPos = SelectionPosition(pdoc->LineEndPosition(sel.MainCaret()));
|
|
if (endPos > realEndPos // if moved past visible EOLs
|
|
|| sel.RangeMain().caret >= endPos) // if at end of display line already
|
|
endPos = realEndPos;
|
|
MovePositionTo(endPos);
|
|
SetLastXChosen();
|
|
}
|
|
break;
|
|
case SCI_LINEENDWRAPEXTEND: {
|
|
SelectionPosition endPos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), false), 1);
|
|
SelectionPosition realEndPos = SelectionPosition(pdoc->LineEndPosition(sel.MainCaret()));
|
|
if (endPos > realEndPos // if moved past visible EOLs
|
|
|| sel.RangeMain().caret >= endPos) // if at end of display line already
|
|
endPos = realEndPos;
|
|
MovePositionTo(endPos, Selection::selStream);
|
|
SetLastXChosen();
|
|
}
|
|
break;
|
|
case SCI_DOCUMENTSTART:
|
|
MovePositionTo(0);
|
|
SetLastXChosen();
|
|
break;
|
|
case SCI_DOCUMENTSTARTEXTEND:
|
|
MovePositionTo(0, Selection::selStream);
|
|
SetLastXChosen();
|
|
break;
|
|
case SCI_DOCUMENTEND:
|
|
MovePositionTo(pdoc->Length());
|
|
SetLastXChosen();
|
|
break;
|
|
case SCI_DOCUMENTENDEXTEND:
|
|
MovePositionTo(pdoc->Length(), Selection::selStream);
|
|
SetLastXChosen();
|
|
break;
|
|
case SCI_STUTTEREDPAGEUP:
|
|
PageMove(-1, Selection::noSel, true);
|
|
break;
|
|
case SCI_STUTTEREDPAGEUPEXTEND:
|
|
PageMove(-1, Selection::selStream, true);
|
|
break;
|
|
case SCI_STUTTEREDPAGEDOWN:
|
|
PageMove(1, Selection::noSel, true);
|
|
break;
|
|
case SCI_STUTTEREDPAGEDOWNEXTEND:
|
|
PageMove(1, Selection::selStream, true);
|
|
break;
|
|
case SCI_PAGEUP:
|
|
PageMove(-1);
|
|
break;
|
|
case SCI_PAGEUPEXTEND:
|
|
PageMove(-1, Selection::selStream);
|
|
break;
|
|
case SCI_PAGEUPRECTEXTEND:
|
|
PageMove(-1, Selection::selRectangle);
|
|
break;
|
|
case SCI_PAGEDOWN:
|
|
PageMove(1);
|
|
break;
|
|
case SCI_PAGEDOWNEXTEND:
|
|
PageMove(1, Selection::selStream);
|
|
break;
|
|
case SCI_PAGEDOWNRECTEXTEND:
|
|
PageMove(1, Selection::selRectangle);
|
|
break;
|
|
case SCI_EDITTOGGLEOVERTYPE:
|
|
inOverstrike = !inOverstrike;
|
|
DropCaret();
|
|
ShowCaretAtCurrentPosition();
|
|
ContainerNeedsUpdate(SC_UPDATE_CONTENT);
|
|
NotifyUpdateUI();
|
|
break;
|
|
case SCI_CANCEL: // Cancel any modes - handled in subclass
|
|
// Also unselect text
|
|
CancelModes();
|
|
break;
|
|
case SCI_DELETEBACK:
|
|
DelCharBack(true);
|
|
if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
|
|
SetLastXChosen();
|
|
}
|
|
EnsureCaretVisible();
|
|
break;
|
|
case SCI_DELETEBACKNOTLINE:
|
|
DelCharBack(false);
|
|
if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
|
|
SetLastXChosen();
|
|
}
|
|
EnsureCaretVisible();
|
|
break;
|
|
case SCI_TAB:
|
|
Indent(true);
|
|
if (caretSticky == SC_CARETSTICKY_OFF) {
|
|
SetLastXChosen();
|
|
}
|
|
EnsureCaretVisible();
|
|
ShowCaretAtCurrentPosition(); // Avoid blinking
|
|
break;
|
|
case SCI_BACKTAB:
|
|
Indent(false);
|
|
if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
|
|
SetLastXChosen();
|
|
}
|
|
EnsureCaretVisible();
|
|
ShowCaretAtCurrentPosition(); // Avoid blinking
|
|
break;
|
|
case SCI_NEWLINE:
|
|
NewLine();
|
|
break;
|
|
case SCI_FORMFEED:
|
|
AddChar('\f');
|
|
break;
|
|
case SCI_VCHOME:
|
|
MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()));
|
|
SetLastXChosen();
|
|
break;
|
|
case SCI_VCHOMEEXTEND:
|
|
MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()), Selection::selStream);
|
|
SetLastXChosen();
|
|
break;
|
|
case SCI_VCHOMERECTEXTEND:
|
|
MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()), Selection::selRectangle);
|
|
SetLastXChosen();
|
|
break;
|
|
case SCI_VCHOMEWRAP: {
|
|
SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
|
|
SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
|
|
if ((viewLineStart < sel.RangeMain().caret) && (viewLineStart > homePos))
|
|
homePos = viewLineStart;
|
|
|
|
MovePositionTo(homePos);
|
|
SetLastXChosen();
|
|
}
|
|
break;
|
|
case SCI_VCHOMEWRAPEXTEND: {
|
|
SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
|
|
SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
|
|
if ((viewLineStart < sel.RangeMain().caret) && (viewLineStart > homePos))
|
|
homePos = viewLineStart;
|
|
|
|
MovePositionTo(homePos, Selection::selStream);
|
|
SetLastXChosen();
|
|
}
|
|
break;
|
|
case SCI_ZOOMIN:
|
|
if (vs.zoomLevel < 20) {
|
|
vs.zoomLevel++;
|
|
InvalidateStyleRedraw();
|
|
NotifyZoom();
|
|
}
|
|
break;
|
|
case SCI_ZOOMOUT:
|
|
if (vs.zoomLevel > -10) {
|
|
vs.zoomLevel--;
|
|
InvalidateStyleRedraw();
|
|
NotifyZoom();
|
|
}
|
|
break;
|
|
case SCI_DELWORDLEFT: {
|
|
int startWord = pdoc->NextWordStart(sel.MainCaret(), -1);
|
|
pdoc->DeleteChars(startWord, sel.MainCaret() - startWord);
|
|
sel.RangeMain().ClearVirtualSpace();
|
|
SetLastXChosen();
|
|
}
|
|
break;
|
|
case SCI_DELWORDRIGHT: {
|
|
UndoGroup ug(pdoc);
|
|
sel.RangeMain().caret = SelectionPosition(
|
|
InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
|
|
sel.RangeMain().anchor = sel.RangeMain().caret;
|
|
int endWord = pdoc->NextWordStart(sel.MainCaret(), 1);
|
|
pdoc->DeleteChars(sel.MainCaret(), endWord - sel.MainCaret());
|
|
}
|
|
break;
|
|
case SCI_DELWORDRIGHTEND: {
|
|
UndoGroup ug(pdoc);
|
|
sel.RangeMain().caret = SelectionPosition(
|
|
InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
|
|
int endWord = pdoc->NextWordEnd(sel.MainCaret(), 1);
|
|
pdoc->DeleteChars(sel.MainCaret(), endWord - sel.MainCaret());
|
|
}
|
|
break;
|
|
case SCI_DELLINELEFT: {
|
|
int line = pdoc->LineFromPosition(sel.MainCaret());
|
|
int start = pdoc->LineStart(line);
|
|
pdoc->DeleteChars(start, sel.MainCaret() - start);
|
|
sel.RangeMain().ClearVirtualSpace();
|
|
SetLastXChosen();
|
|
}
|
|
break;
|
|
case SCI_DELLINERIGHT: {
|
|
int line = pdoc->LineFromPosition(sel.MainCaret());
|
|
int end = pdoc->LineEnd(line);
|
|
pdoc->DeleteChars(sel.MainCaret(), end - sel.MainCaret());
|
|
}
|
|
break;
|
|
case SCI_LINECOPY: {
|
|
int lineStart = pdoc->LineFromPosition(SelectionStart().Position());
|
|
int lineEnd = pdoc->LineFromPosition(SelectionEnd().Position());
|
|
CopyRangeToClipboard(pdoc->LineStart(lineStart),
|
|
pdoc->LineStart(lineEnd + 1));
|
|
}
|
|
break;
|
|
case SCI_LINECUT: {
|
|
int lineStart = pdoc->LineFromPosition(SelectionStart().Position());
|
|
int lineEnd = pdoc->LineFromPosition(SelectionEnd().Position());
|
|
int start = pdoc->LineStart(lineStart);
|
|
int end = pdoc->LineStart(lineEnd + 1);
|
|
SetSelection(start, end);
|
|
Cut();
|
|
SetLastXChosen();
|
|
}
|
|
break;
|
|
case SCI_LINEDELETE: {
|
|
int line = pdoc->LineFromPosition(sel.MainCaret());
|
|
int start = pdoc->LineStart(line);
|
|
int end = pdoc->LineStart(line + 1);
|
|
pdoc->DeleteChars(start, end - start);
|
|
}
|
|
break;
|
|
case SCI_LINETRANSPOSE:
|
|
LineTranspose();
|
|
break;
|
|
case SCI_LINEDUPLICATE:
|
|
Duplicate(true);
|
|
break;
|
|
case SCI_SELECTIONDUPLICATE:
|
|
Duplicate(false);
|
|
break;
|
|
case SCI_LOWERCASE:
|
|
ChangeCaseOfSelection(cmLower);
|
|
break;
|
|
case SCI_UPPERCASE:
|
|
ChangeCaseOfSelection(cmUpper);
|
|
break;
|
|
case SCI_WORDPARTLEFT:
|
|
MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(sel.MainCaret()), -1));
|
|
SetLastXChosen();
|
|
break;
|
|
case SCI_WORDPARTLEFTEXTEND:
|
|
MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(sel.MainCaret()), -1), Selection::selStream);
|
|
SetLastXChosen();
|
|
break;
|
|
case SCI_WORDPARTRIGHT:
|
|
MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(sel.MainCaret()), 1));
|
|
SetLastXChosen();
|
|
break;
|
|
case SCI_WORDPARTRIGHTEXTEND:
|
|
MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(sel.MainCaret()), 1), Selection::selStream);
|
|
SetLastXChosen();
|
|
break;
|
|
case SCI_HOMEDISPLAY:
|
|
MovePositionTo(MovePositionSoVisible(
|
|
StartEndDisplayLine(sel.MainCaret(), true), -1));
|
|
SetLastXChosen();
|
|
break;
|
|
case SCI_VCHOMEDISPLAY: {
|
|
SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
|
|
SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
|
|
if (viewLineStart > homePos)
|
|
homePos = viewLineStart;
|
|
|
|
MovePositionTo(homePos);
|
|
SetLastXChosen();
|
|
}
|
|
break;
|
|
case SCI_HOMEDISPLAYEXTEND:
|
|
MovePositionTo(MovePositionSoVisible(
|
|
StartEndDisplayLine(sel.MainCaret(), true), -1), Selection::selStream);
|
|
SetLastXChosen();
|
|
break;
|
|
case SCI_VCHOMEDISPLAYEXTEND: {
|
|
SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
|
|
SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
|
|
if (viewLineStart > homePos)
|
|
homePos = viewLineStart;
|
|
|
|
MovePositionTo(homePos, Selection::selStream);
|
|
SetLastXChosen();
|
|
}
|
|
break;
|
|
case SCI_LINEENDDISPLAY:
|
|
MovePositionTo(MovePositionSoVisible(
|
|
StartEndDisplayLine(sel.MainCaret(), false), 1));
|
|
SetLastXChosen();
|
|
break;
|
|
case SCI_LINEENDDISPLAYEXTEND:
|
|
MovePositionTo(MovePositionSoVisible(
|
|
StartEndDisplayLine(sel.MainCaret(), false), 1), Selection::selStream);
|
|
SetLastXChosen();
|
|
break;
|
|
case SCI_SCROLLTOSTART:
|
|
ScrollTo(0);
|
|
break;
|
|
case SCI_SCROLLTOEND:
|
|
ScrollTo(MaxScrollPos());
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int Editor::KeyDefault(int, int) {
|
|
return 0;
|
|
}
|
|
|
|
int Editor::KeyDownWithModifiers(int key, int modifiers, bool *consumed) {
|
|
DwellEnd(false);
|
|
int msg = kmap.Find(key, modifiers);
|
|
if (msg) {
|
|
if (consumed)
|
|
*consumed = true;
|
|
return static_cast<int>(WndProc(msg, 0, 0));
|
|
} else {
|
|
if (consumed)
|
|
*consumed = false;
|
|
return KeyDefault(key, modifiers);
|
|
}
|
|
}
|
|
|
|
int Editor::KeyDown(int key, bool shift, bool ctrl, bool alt, bool *consumed) {
|
|
return KeyDownWithModifiers(key, ModifierFlags(shift, ctrl, alt), consumed);
|
|
}
|
|
|
|
void Editor::Indent(bool forwards) {
|
|
UndoGroup ug(pdoc);
|
|
for (size_t r=0; r<sel.Count(); r++) {
|
|
int lineOfAnchor = pdoc->LineFromPosition(sel.Range(r).anchor.Position());
|
|
int caretPosition = sel.Range(r).caret.Position();
|
|
int lineCurrentPos = pdoc->LineFromPosition(caretPosition);
|
|
if (lineOfAnchor == lineCurrentPos) {
|
|
if (forwards) {
|
|
pdoc->DeleteChars(sel.Range(r).Start().Position(), sel.Range(r).Length());
|
|
caretPosition = sel.Range(r).caret.Position();
|
|
if (pdoc->GetColumn(caretPosition) <= pdoc->GetColumn(pdoc->GetLineIndentPosition(lineCurrentPos)) &&
|
|
pdoc->tabIndents) {
|
|
int indentation = pdoc->GetLineIndentation(lineCurrentPos);
|
|
int indentationStep = pdoc->IndentSize();
|
|
const int posSelect = pdoc->SetLineIndentation(
|
|
lineCurrentPos, indentation + indentationStep - indentation % indentationStep);
|
|
sel.Range(r) = SelectionRange(posSelect);
|
|
} else {
|
|
if (pdoc->useTabs) {
|
|
const int lengthInserted = pdoc->InsertString(caretPosition, "\t", 1);
|
|
sel.Range(r) = SelectionRange(caretPosition + lengthInserted);
|
|
} else {
|
|
int numSpaces = (pdoc->tabInChars) -
|
|
(pdoc->GetColumn(caretPosition) % (pdoc->tabInChars));
|
|
if (numSpaces < 1)
|
|
numSpaces = pdoc->tabInChars;
|
|
const std::string spaceText(numSpaces, ' ');
|
|
const int lengthInserted = pdoc->InsertString(caretPosition, spaceText.c_str(),
|
|
static_cast<int>(spaceText.length()));
|
|
sel.Range(r) = SelectionRange(caretPosition + lengthInserted);
|
|
}
|
|
}
|
|
} else {
|
|
if (pdoc->GetColumn(caretPosition) <= pdoc->GetLineIndentation(lineCurrentPos) &&
|
|
pdoc->tabIndents) {
|
|
int indentation = pdoc->GetLineIndentation(lineCurrentPos);
|
|
int indentationStep = pdoc->IndentSize();
|
|
const int posSelect = pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
|
|
sel.Range(r) = SelectionRange(posSelect);
|
|
} else {
|
|
int newColumn = ((pdoc->GetColumn(caretPosition) - 1) / pdoc->tabInChars) *
|
|
pdoc->tabInChars;
|
|
if (newColumn < 0)
|
|
newColumn = 0;
|
|
int newPos = caretPosition;
|
|
while (pdoc->GetColumn(newPos) > newColumn)
|
|
newPos--;
|
|
sel.Range(r) = SelectionRange(newPos);
|
|
}
|
|
}
|
|
} else { // Multiline
|
|
int anchorPosOnLine = sel.Range(r).anchor.Position() - pdoc->LineStart(lineOfAnchor);
|
|
int currentPosPosOnLine = caretPosition - pdoc->LineStart(lineCurrentPos);
|
|
// Multiple lines selected so indent / dedent
|
|
int lineTopSel = Platform::Minimum(lineOfAnchor, lineCurrentPos);
|
|
int lineBottomSel = Platform::Maximum(lineOfAnchor, lineCurrentPos);
|
|
if (pdoc->LineStart(lineBottomSel) == sel.Range(r).anchor.Position() || pdoc->LineStart(lineBottomSel) == caretPosition)
|
|
lineBottomSel--; // If not selecting any characters on a line, do not indent
|
|
pdoc->Indent(forwards, lineBottomSel, lineTopSel);
|
|
if (lineOfAnchor < lineCurrentPos) {
|
|
if (currentPosPosOnLine == 0)
|
|
sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
|
|
else
|
|
sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos + 1), pdoc->LineStart(lineOfAnchor));
|
|
} else {
|
|
if (anchorPosOnLine == 0)
|
|
sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
|
|
else
|
|
sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor + 1));
|
|
}
|
|
}
|
|
}
|
|
ContainerNeedsUpdate(SC_UPDATE_SELECTION);
|
|
}
|
|
|
|
class CaseFolderASCII : public CaseFolderTable {
|
|
public:
|
|
CaseFolderASCII() {
|
|
StandardASCII();
|
|
}
|
|
~CaseFolderASCII() {
|
|
}
|
|
};
|
|
|
|
|
|
CaseFolder *Editor::CaseFolderForEncoding() {
|
|
// Simple default that only maps ASCII upper case to lower case.
|
|
return new CaseFolderASCII();
|
|
}
|
|
|
|
/**
|
|
* Search of a text in the document, in the given range.
|
|
* @return The position of the found text, -1 if not found.
|
|
*/
|
|
long Editor::FindText(
|
|
uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
|
|
///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
|
|
sptr_t lParam) { ///< @c TextToFind structure: The text to search for in the given range.
|
|
|
|
Sci_TextToFind *ft = reinterpret_cast<Sci_TextToFind *>(lParam);
|
|
int lengthFound = istrlen(ft->lpstrText);
|
|
if (!pdoc->HasCaseFolder())
|
|
pdoc->SetCaseFolder(CaseFolderForEncoding());
|
|
int pos = pdoc->FindText(ft->chrg.cpMin, ft->chrg.cpMax, ft->lpstrText,
|
|
(wParam & SCFIND_MATCHCASE) != 0,
|
|
(wParam & SCFIND_WHOLEWORD) != 0,
|
|
(wParam & SCFIND_WORDSTART) != 0,
|
|
(wParam & SCFIND_REGEXP) != 0,
|
|
static_cast<int>(wParam),
|
|
&lengthFound);
|
|
if (pos != -1) {
|
|
ft->chrgText.cpMin = pos;
|
|
ft->chrgText.cpMax = pos + lengthFound;
|
|
}
|
|
return pos;
|
|
}
|
|
|
|
/**
|
|
* Relocatable search support : Searches relative to current selection
|
|
* point and sets the selection to the found text range with
|
|
* each search.
|
|
*/
|
|
/**
|
|
* Anchor following searches at current selection start: This allows
|
|
* multiple incremental interactive searches to be macro recorded
|
|
* while still setting the selection to found text so the find/select
|
|
* operation is self-contained.
|
|
*/
|
|
void Editor::SearchAnchor() {
|
|
searchAnchor = SelectionStart().Position();
|
|
}
|
|
|
|
/**
|
|
* Find text from current search anchor: Must call @c SearchAnchor first.
|
|
* Used for next text and previous text requests.
|
|
* @return The position of the found text, -1 if not found.
|
|
*/
|
|
long Editor::SearchText(
|
|
unsigned int iMessage, ///< Accepts both @c SCI_SEARCHNEXT and @c SCI_SEARCHPREV.
|
|
uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
|
|
///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
|
|
sptr_t lParam) { ///< The text to search for.
|
|
|
|
const char *txt = reinterpret_cast<char *>(lParam);
|
|
int pos;
|
|
int lengthFound = istrlen(txt);
|
|
if (!pdoc->HasCaseFolder())
|
|
pdoc->SetCaseFolder(CaseFolderForEncoding());
|
|
if (iMessage == SCI_SEARCHNEXT) {
|
|
pos = pdoc->FindText(searchAnchor, pdoc->Length(), txt,
|
|
(wParam & SCFIND_MATCHCASE) != 0,
|
|
(wParam & SCFIND_WHOLEWORD) != 0,
|
|
(wParam & SCFIND_WORDSTART) != 0,
|
|
(wParam & SCFIND_REGEXP) != 0,
|
|
static_cast<int>(wParam),
|
|
&lengthFound);
|
|
} else {
|
|
pos = pdoc->FindText(searchAnchor, 0, txt,
|
|
(wParam & SCFIND_MATCHCASE) != 0,
|
|
(wParam & SCFIND_WHOLEWORD) != 0,
|
|
(wParam & SCFIND_WORDSTART) != 0,
|
|
(wParam & SCFIND_REGEXP) != 0,
|
|
static_cast<int>(wParam),
|
|
&lengthFound);
|
|
}
|
|
if (pos != -1) {
|
|
SetSelection(pos, pos + lengthFound);
|
|
}
|
|
|
|
return pos;
|
|
}
|
|
|
|
std::string Editor::CaseMapString(const std::string &s, int caseMapping) {
|
|
std::string ret(s);
|
|
for (size_t i=0; i<ret.size(); i++) {
|
|
switch (caseMapping) {
|
|
case cmUpper:
|
|
if (ret[i] >= 'a' && ret[i] <= 'z')
|
|
ret[i] = static_cast<char>(ret[i] - 'a' + 'A');
|
|
break;
|
|
case cmLower:
|
|
if (ret[i] >= 'A' && ret[i] <= 'Z')
|
|
ret[i] = static_cast<char>(ret[i] - 'A' + 'a');
|
|
break;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Search for text in the target range of the document.
|
|
* @return The position of the found text, -1 if not found.
|
|
*/
|
|
long Editor::SearchInTarget(const char *text, int length) {
|
|
int lengthFound = length;
|
|
|
|
if (!pdoc->HasCaseFolder())
|
|
pdoc->SetCaseFolder(CaseFolderForEncoding());
|
|
int pos = pdoc->FindText(targetStart, targetEnd, text,
|
|
(searchFlags & SCFIND_MATCHCASE) != 0,
|
|
(searchFlags & SCFIND_WHOLEWORD) != 0,
|
|
(searchFlags & SCFIND_WORDSTART) != 0,
|
|
(searchFlags & SCFIND_REGEXP) != 0,
|
|
searchFlags,
|
|
&lengthFound);
|
|
if (pos != -1) {
|
|
targetStart = pos;
|
|
targetEnd = pos + lengthFound;
|
|
}
|
|
return pos;
|
|
}
|
|
|
|
void Editor::GoToLine(int lineNo) {
|
|
if (lineNo > pdoc->LinesTotal())
|
|
lineNo = pdoc->LinesTotal();
|
|
if (lineNo < 0)
|
|
lineNo = 0;
|
|
SetEmptySelection(pdoc->LineStart(lineNo));
|
|
ShowCaretAtCurrentPosition();
|
|
EnsureCaretVisible();
|
|
}
|
|
|
|
static bool Close(Point pt1, Point pt2) {
|
|
if (abs(pt1.x - pt2.x) > 3)
|
|
return false;
|
|
if (abs(pt1.y - pt2.y) > 3)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
std::string Editor::RangeText(int start, int end) const {
|
|
if (start < end) {
|
|
int len = end - start;
|
|
std::string ret(len, '\0');
|
|
for (int i = 0; i < len; i++) {
|
|
ret[i] = pdoc->CharAt(start + i);
|
|
}
|
|
return ret;
|
|
}
|
|
return std::string();
|
|
}
|
|
|
|
void Editor::CopySelectionRange(SelectionText *ss, bool allowLineCopy) {
|
|
if (sel.Empty()) {
|
|
if (allowLineCopy) {
|
|
int currentLine = pdoc->LineFromPosition(sel.MainCaret());
|
|
int start = pdoc->LineStart(currentLine);
|
|
int end = pdoc->LineEnd(currentLine);
|
|
|
|
std::string text = RangeText(start, end);
|
|
if (pdoc->eolMode != SC_EOL_LF)
|
|
text.push_back('\r');
|
|
if (pdoc->eolMode != SC_EOL_CR)
|
|
text.push_back('\n');
|
|
ss->Copy(text, pdoc->dbcsCodePage,
|
|
vs.styles[STYLE_DEFAULT].characterSet, false, true);
|
|
}
|
|
} else {
|
|
std::string text;
|
|
std::vector<SelectionRange> rangesInOrder = sel.RangesCopy();
|
|
if (sel.selType == Selection::selRectangle)
|
|
std::sort(rangesInOrder.begin(), rangesInOrder.end());
|
|
for (size_t r=0; r<rangesInOrder.size(); r++) {
|
|
SelectionRange current = rangesInOrder[r];
|
|
text.append(RangeText(current.Start().Position(), current.End().Position()));
|
|
if (sel.selType == Selection::selRectangle) {
|
|
if (pdoc->eolMode != SC_EOL_LF)
|
|
text.push_back('\r');
|
|
if (pdoc->eolMode != SC_EOL_CR)
|
|
text.push_back('\n');
|
|
}
|
|
}
|
|
ss->Copy(text, pdoc->dbcsCodePage,
|
|
vs.styles[STYLE_DEFAULT].characterSet, sel.IsRectangular(), sel.selType == Selection::selLines);
|
|
}
|
|
}
|
|
|
|
void Editor::CopyRangeToClipboard(int start, int end) {
|
|
start = pdoc->ClampPositionIntoDocument(start);
|
|
end = pdoc->ClampPositionIntoDocument(end);
|
|
SelectionText selectedText;
|
|
std::string text = RangeText(start, end);
|
|
selectedText.Copy(text,
|
|
pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
|
|
CopyToClipboard(selectedText);
|
|
}
|
|
|
|
void Editor::CopyText(int length, const char *text) {
|
|
SelectionText selectedText;
|
|
selectedText.Copy(std::string(text, length),
|
|
pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
|
|
CopyToClipboard(selectedText);
|
|
}
|
|
|
|
void Editor::SetDragPosition(SelectionPosition newPos) {
|
|
if (newPos.Position() >= 0) {
|
|
newPos = MovePositionOutsideChar(newPos, 1);
|
|
posDrop = newPos;
|
|
}
|
|
if (!(posDrag == newPos)) {
|
|
caret.on = true;
|
|
SetTicking(true);
|
|
InvalidateCaret();
|
|
posDrag = newPos;
|
|
InvalidateCaret();
|
|
}
|
|
}
|
|
|
|
void Editor::DisplayCursor(Window::Cursor c) {
|
|
if (cursorMode == SC_CURSORNORMAL)
|
|
wMain.SetCursor(c);
|
|
else
|
|
wMain.SetCursor(static_cast<Window::Cursor>(cursorMode));
|
|
}
|
|
|
|
bool Editor::DragThreshold(Point ptStart, Point ptNow) {
|
|
int xMove = static_cast<int>(ptStart.x - ptNow.x);
|
|
int yMove = static_cast<int>(ptStart.y - ptNow.y);
|
|
int distanceSquared = xMove * xMove + yMove * yMove;
|
|
return distanceSquared > 16;
|
|
}
|
|
|
|
void Editor::StartDrag() {
|
|
// Always handled by subclasses
|
|
//SetMouseCapture(true);
|
|
//DisplayCursor(Window::cursorArrow);
|
|
}
|
|
|
|
void Editor::DropAt(SelectionPosition position, const char *value, size_t lengthValue, bool moving, bool rectangular) {
|
|
//Platform::DebugPrintf("DropAt %d %d\n", inDragDrop, position);
|
|
if (inDragDrop == ddDragging)
|
|
dropWentOutside = false;
|
|
|
|
bool positionWasInSelection = PositionInSelection(position.Position());
|
|
|
|
bool positionOnEdgeOfSelection =
|
|
(position == SelectionStart()) || (position == SelectionEnd());
|
|
|
|
if ((inDragDrop != ddDragging) || !(positionWasInSelection) ||
|
|
(positionOnEdgeOfSelection && !moving)) {
|
|
|
|
SelectionPosition selStart = SelectionStart();
|
|
SelectionPosition selEnd = SelectionEnd();
|
|
|
|
UndoGroup ug(pdoc);
|
|
|
|
SelectionPosition positionAfterDeletion = position;
|
|
if ((inDragDrop == ddDragging) && moving) {
|
|
// Remove dragged out text
|
|
if (rectangular || sel.selType == Selection::selLines) {
|
|
for (size_t r=0; r<sel.Count(); r++) {
|
|
if (position >= sel.Range(r).Start()) {
|
|
if (position > sel.Range(r).End()) {
|
|
positionAfterDeletion.Add(-sel.Range(r).Length());
|
|
} else {
|
|
positionAfterDeletion.Add(-SelectionRange(position, sel.Range(r).Start()).Length());
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (position > selStart) {
|
|
positionAfterDeletion.Add(-SelectionRange(selEnd, selStart).Length());
|
|
}
|
|
}
|
|
ClearSelection();
|
|
}
|
|
position = positionAfterDeletion;
|
|
|
|
std::string convertedText = Document::TransformLineEnds(value, lengthValue, pdoc->eolMode);
|
|
|
|
if (rectangular) {
|
|
PasteRectangular(position, convertedText.c_str(), static_cast<int>(convertedText.length()));
|
|
// Should try to select new rectangle but it may not be a rectangle now so just select the drop position
|
|
SetEmptySelection(position);
|
|
} else {
|
|
position = MovePositionOutsideChar(position, sel.MainCaret() - position.Position());
|
|
position = SelectionPosition(InsertSpace(position.Position(), position.VirtualSpace()));
|
|
const int lengthInserted = pdoc->InsertString(
|
|
position.Position(), convertedText.c_str(), static_cast<int>(convertedText.length()));
|
|
if (lengthInserted > 0) {
|
|
SelectionPosition posAfterInsertion = position;
|
|
posAfterInsertion.Add(lengthInserted);
|
|
SetSelection(posAfterInsertion, position);
|
|
}
|
|
}
|
|
} else if (inDragDrop == ddDragging) { //-V547
|
|
SetEmptySelection(position);
|
|
}
|
|
}
|
|
|
|
void Editor::DropAt(SelectionPosition position, const char *value, bool moving, bool rectangular) {
|
|
DropAt(position, value, strlen(value), moving, rectangular);
|
|
}
|
|
|
|
/**
|
|
* @return true if given position is inside the selection,
|
|
*/
|
|
bool Editor::PositionInSelection(int pos) {
|
|
pos = MovePositionOutsideChar(pos, sel.MainCaret() - pos);
|
|
for (size_t r=0; r<sel.Count(); r++) {
|
|
if (sel.Range(r).Contains(pos))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Editor::PointInSelection(Point pt) {
|
|
SelectionPosition pos = SPositionFromLocation(pt, false, true);
|
|
Point ptPos = LocationFromPosition(pos);
|
|
for (size_t r=0; r<sel.Count(); r++) {
|
|
SelectionRange range = sel.Range(r);
|
|
if (range.Contains(pos)) {
|
|
bool hit = true;
|
|
if (pos == range.Start()) {
|
|
// see if just before selection
|
|
if (pt.x < ptPos.x) {
|
|
hit = false;
|
|
}
|
|
}
|
|
if (pos == range.End()) {
|
|
// see if just after selection
|
|
if (pt.x > ptPos.x) {
|
|
hit = false;
|
|
}
|
|
}
|
|
if (hit)
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Editor::PointInSelMargin(Point pt) {
|
|
// Really means: "Point in a margin"
|
|
if (vs.fixedColumnWidth > 0) { // There is a margin
|
|
PRectangle rcSelMargin = GetClientRectangle();
|
|
rcSelMargin.right = static_cast<XYPOSITION>(vs.textStart - vs.leftMarginWidth);
|
|
rcSelMargin.left = static_cast<XYPOSITION>(vs.textStart - vs.fixedColumnWidth);
|
|
return rcSelMargin.Contains(pt);
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Window::Cursor Editor::GetMarginCursor(Point pt) const {
|
|
int x = 0;
|
|
for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) {
|
|
if ((pt.x >= x) && (pt.x < x + vs.ms[margin].width))
|
|
return static_cast<Window::Cursor>(vs.ms[margin].cursor);
|
|
x += vs.ms[margin].width;
|
|
}
|
|
return Window::cursorReverseArrow;
|
|
}
|
|
|
|
void Editor::TrimAndSetSelection(int currentPos_, int anchor_) {
|
|
sel.TrimSelection(SelectionRange(currentPos_, anchor_));
|
|
SetSelection(currentPos_, anchor_);
|
|
}
|
|
|
|
void Editor::LineSelection(int lineCurrentPos_, int lineAnchorPos_, bool wholeLine) {
|
|
int selCurrentPos, selAnchorPos;
|
|
if (wholeLine) {
|
|
int lineCurrent_ = pdoc->LineFromPosition(lineCurrentPos_);
|
|
int lineAnchor_ = pdoc->LineFromPosition(lineAnchorPos_);
|
|
if (lineAnchorPos_ < lineCurrentPos_) {
|
|
selCurrentPos = pdoc->LineStart(lineCurrent_ + 1);
|
|
selAnchorPos = pdoc->LineStart(lineAnchor_);
|
|
} else if (lineAnchorPos_ > lineCurrentPos_) {
|
|
selCurrentPos = pdoc->LineStart(lineCurrent_);
|
|
selAnchorPos = pdoc->LineStart(lineAnchor_ + 1);
|
|
} else { // Same line, select it
|
|
selCurrentPos = pdoc->LineStart(lineAnchor_ + 1);
|
|
selAnchorPos = pdoc->LineStart(lineAnchor_);
|
|
}
|
|
} else {
|
|
if (lineAnchorPos_ < lineCurrentPos_) {
|
|
selCurrentPos = StartEndDisplayLine(lineCurrentPos_, false) + 1;
|
|
selCurrentPos = pdoc->MovePositionOutsideChar(selCurrentPos, 1);
|
|
selAnchorPos = StartEndDisplayLine(lineAnchorPos_, true);
|
|
} else if (lineAnchorPos_ > lineCurrentPos_) {
|
|
selCurrentPos = StartEndDisplayLine(lineCurrentPos_, true);
|
|
selAnchorPos = StartEndDisplayLine(lineAnchorPos_, false) + 1;
|
|
selAnchorPos = pdoc->MovePositionOutsideChar(selAnchorPos, 1);
|
|
} else { // Same line, select it
|
|
selCurrentPos = StartEndDisplayLine(lineAnchorPos_, false) + 1;
|
|
selCurrentPos = pdoc->MovePositionOutsideChar(selCurrentPos, 1);
|
|
selAnchorPos = StartEndDisplayLine(lineAnchorPos_, true);
|
|
}
|
|
}
|
|
TrimAndSetSelection(selCurrentPos, selAnchorPos);
|
|
}
|
|
|
|
void Editor::WordSelection(int pos) {
|
|
if (pos < wordSelectAnchorStartPos) {
|
|
// Extend backward to the word containing pos.
|
|
// Skip ExtendWordSelect if the line is empty or if pos is after the last character.
|
|
// This ensures that a series of empty lines isn't counted as a single "word".
|
|
if (!pdoc->IsLineEndPosition(pos))
|
|
pos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos + 1, 1), -1);
|
|
TrimAndSetSelection(pos, wordSelectAnchorEndPos);
|
|
} else if (pos > wordSelectAnchorEndPos) {
|
|
// Extend forward to the word containing the character to the left of pos.
|
|
// Skip ExtendWordSelect if the line is empty or if pos is the first position on the line.
|
|
// This ensures that a series of empty lines isn't counted as a single "word".
|
|
if (pos > pdoc->LineStart(pdoc->LineFromPosition(pos)))
|
|
pos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos - 1, -1), 1);
|
|
TrimAndSetSelection(pos, wordSelectAnchorStartPos);
|
|
} else {
|
|
// Select only the anchored word
|
|
if (pos >= originalAnchorPos)
|
|
TrimAndSetSelection(wordSelectAnchorEndPos, wordSelectAnchorStartPos);
|
|
else
|
|
TrimAndSetSelection(wordSelectAnchorStartPos, wordSelectAnchorEndPos);
|
|
}
|
|
}
|
|
|
|
void Editor::DwellEnd(bool mouseMoved) {
|
|
if (mouseMoved)
|
|
ticksToDwell = dwellDelay;
|
|
else
|
|
ticksToDwell = SC_TIME_FOREVER;
|
|
if (dwelling && (dwellDelay < SC_TIME_FOREVER)) {
|
|
dwelling = false;
|
|
NotifyDwelling(ptMouseLast, dwelling);
|
|
}
|
|
}
|
|
|
|
void Editor::MouseLeave() {
|
|
SetHotSpotRange(NULL);
|
|
if (!HaveMouseCapture()) {
|
|
ptMouseLast = Point(-1,-1);
|
|
DwellEnd(true);
|
|
}
|
|
}
|
|
|
|
static bool AllowVirtualSpace(int virtualSpaceOptions, bool rectangular) {
|
|
return (!rectangular && ((virtualSpaceOptions & SCVS_USERACCESSIBLE) != 0))
|
|
|| (rectangular && ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) != 0));
|
|
}
|
|
|
|
void Editor::ButtonDownWithModifiers(Point pt, unsigned int curTime, int modifiers) {
|
|
//Platform::DebugPrintf("ButtonDown %d %d = %d alt=%d %d\n", curTime, lastClickTime, curTime - lastClickTime, alt, inDragDrop);
|
|
ptMouseLast = pt;
|
|
const bool ctrl = (modifiers & SCI_CTRL) != 0;
|
|
const bool shift = (modifiers & SCI_SHIFT) != 0;
|
|
const bool alt = (modifiers & SCI_ALT) != 0;
|
|
SelectionPosition newPos = SPositionFromLocation(pt, false, false, AllowVirtualSpace(virtualSpaceOptions, alt));
|
|
newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
|
|
SelectionPosition newCharPos = SPositionFromLocation(pt, false, true, false);
|
|
newCharPos = MovePositionOutsideChar(newCharPos, -1);
|
|
inDragDrop = ddNone;
|
|
sel.SetMoveExtends(false);
|
|
|
|
if (NotifyMarginClick(pt, modifiers))
|
|
return;
|
|
|
|
NotifyIndicatorClick(true, newPos.Position(), modifiers);
|
|
|
|
bool inSelMargin = PointInSelMargin(pt);
|
|
// In margin ctrl+(double)click should always select everything
|
|
if (ctrl && inSelMargin) {
|
|
SelectAll();
|
|
lastClickTime = curTime;
|
|
lastClick = pt;
|
|
return;
|
|
}
|
|
if (shift && !inSelMargin) {
|
|
SetSelection(newPos);
|
|
}
|
|
if (((curTime - lastClickTime) < Platform::DoubleClickTime()) && Close(pt, lastClick)) {
|
|
//Platform::DebugPrintf("Double click %d %d = %d\n", curTime, lastClickTime, curTime - lastClickTime);
|
|
SetMouseCapture(true);
|
|
if (!ctrl || !multipleSelection || (selectionType != selChar && selectionType != selWord))
|
|
SetEmptySelection(newPos.Position());
|
|
bool doubleClick = false;
|
|
// Stop mouse button bounce changing selection type
|
|
if (!Platform::MouseButtonBounce() || curTime != lastClickTime) {
|
|
if (inSelMargin) {
|
|
// Inside margin selection type should be either selSubLine or selWholeLine.
|
|
if (selectionType == selSubLine) {
|
|
// If it is selSubLine, we're inside a *double* click and word wrap is enabled,
|
|
// so we switch to selWholeLine in order to select whole line.
|
|
selectionType = selWholeLine;
|
|
} else if (selectionType != selSubLine && selectionType != selWholeLine) {
|
|
// If it is neither, reset selection type to line selection.
|
|
selectionType = (Wrapping() && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
|
|
}
|
|
} else {
|
|
if (selectionType == selChar) {
|
|
selectionType = selWord;
|
|
doubleClick = true;
|
|
} else if (selectionType == selWord) {
|
|
// Since we ended up here, we're inside a *triple* click, which should always select
|
|
// whole line regardless of word wrap being enabled or not.
|
|
selectionType = selWholeLine;
|
|
} else {
|
|
selectionType = selChar;
|
|
originalAnchorPos = sel.MainCaret();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (selectionType == selWord) {
|
|
int charPos = originalAnchorPos;
|
|
if (sel.MainCaret() == originalAnchorPos) {
|
|
charPos = PositionFromLocation(pt, false, true);
|
|
charPos = MovePositionOutsideChar(charPos, -1);
|
|
}
|
|
|
|
int startWord, endWord;
|
|
if ((sel.MainCaret() >= originalAnchorPos) && !pdoc->IsLineEndPosition(charPos)) {
|
|
startWord = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(charPos + 1, 1), -1);
|
|
endWord = pdoc->ExtendWordSelect(charPos, 1);
|
|
} else {
|
|
// Selecting backwards, or anchor beyond last character on line. In these cases,
|
|
// we select the word containing the character to the *left* of the anchor.
|
|
if (charPos > pdoc->LineStart(pdoc->LineFromPosition(charPos))) {
|
|
startWord = pdoc->ExtendWordSelect(charPos, -1);
|
|
endWord = pdoc->ExtendWordSelect(startWord, 1);
|
|
} else {
|
|
// Anchor at start of line; select nothing to begin with.
|
|
startWord = charPos;
|
|
endWord = charPos;
|
|
}
|
|
}
|
|
|
|
wordSelectAnchorStartPos = startWord;
|
|
wordSelectAnchorEndPos = endWord;
|
|
wordSelectInitialCaretPos = sel.MainCaret();
|
|
WordSelection(wordSelectInitialCaretPos);
|
|
} else if (selectionType == selSubLine || selectionType == selWholeLine) {
|
|
lineAnchorPos = newPos.Position();
|
|
LineSelection(lineAnchorPos, lineAnchorPos, selectionType == selWholeLine);
|
|
//Platform::DebugPrintf("Triple click: %d - %d\n", anchor, currentPos);
|
|
} else {
|
|
SetEmptySelection(sel.MainCaret());
|
|
}
|
|
//Platform::DebugPrintf("Double click: %d - %d\n", anchor, currentPos);
|
|
if (doubleClick) {
|
|
NotifyDoubleClick(pt, modifiers);
|
|
if (PositionIsHotspot(newCharPos.Position()))
|
|
NotifyHotSpotDoubleClicked(newCharPos.Position(), modifiers);
|
|
}
|
|
} else { // Single click
|
|
if (inSelMargin) {
|
|
sel.selType = Selection::selStream;
|
|
if (!shift) {
|
|
// Single click in margin: select whole line or only subline if word wrap is enabled
|
|
lineAnchorPos = newPos.Position();
|
|
selectionType = (Wrapping() && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
|
|
LineSelection(lineAnchorPos, lineAnchorPos, selectionType == selWholeLine);
|
|
} else {
|
|
// Single shift+click in margin: select from line anchor to clicked line
|
|
if (sel.MainAnchor() > sel.MainCaret())
|
|
lineAnchorPos = sel.MainAnchor() - 1;
|
|
else
|
|
lineAnchorPos = sel.MainAnchor();
|
|
// Reset selection type if there is an empty selection.
|
|
// This ensures that we don't end up stuck in previous selection mode, which is no longer valid.
|
|
// Otherwise, if there's a non empty selection, reset selection type only if it differs from selSubLine and selWholeLine.
|
|
// This ensures that we continue selecting in the same selection mode.
|
|
if (sel.Empty() || (selectionType != selSubLine && selectionType != selWholeLine))
|
|
selectionType = (Wrapping() && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
|
|
LineSelection(newPos.Position(), lineAnchorPos, selectionType == selWholeLine);
|
|
}
|
|
|
|
SetDragPosition(SelectionPosition(invalidPosition));
|
|
SetMouseCapture(true);
|
|
} else {
|
|
if (PointIsHotspot(pt)) {
|
|
NotifyHotSpotClicked(newCharPos.Position(), modifiers);
|
|
hotSpotClickPos = newCharPos.Position();
|
|
}
|
|
if (!shift) {
|
|
if (PointInSelection(pt) && !SelectionEmpty())
|
|
inDragDrop = ddInitial;
|
|
else
|
|
inDragDrop = ddNone;
|
|
}
|
|
SetMouseCapture(true);
|
|
if (inDragDrop != ddInitial) {
|
|
SetDragPosition(SelectionPosition(invalidPosition));
|
|
if (!shift) {
|
|
if (ctrl && multipleSelection) {
|
|
SelectionRange range(newPos);
|
|
sel.TentativeSelection(range);
|
|
InvalidateSelection(range, true);
|
|
} else {
|
|
InvalidateSelection(SelectionRange(newPos), true);
|
|
if (sel.Count() > 1)
|
|
Redraw();
|
|
if ((sel.Count() > 1) || (sel.selType != Selection::selStream))
|
|
sel.Clear();
|
|
sel.selType = alt ? Selection::selRectangle : Selection::selStream;
|
|
SetSelection(newPos, newPos);
|
|
}
|
|
}
|
|
SelectionPosition anchorCurrent = newPos;
|
|
if (shift)
|
|
anchorCurrent = sel.IsRectangular() ?
|
|
sel.Rectangular().anchor : sel.RangeMain().anchor;
|
|
sel.selType = alt ? Selection::selRectangle : Selection::selStream;
|
|
selectionType = selChar;
|
|
originalAnchorPos = sel.MainCaret();
|
|
sel.Rectangular() = SelectionRange(newPos, anchorCurrent);
|
|
SetRectangularRange();
|
|
}
|
|
}
|
|
}
|
|
lastClickTime = curTime;
|
|
lastClick = pt;
|
|
lastXChosen = static_cast<int>(pt.x) + xOffset;
|
|
ShowCaretAtCurrentPosition();
|
|
}
|
|
|
|
void Editor::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt) {
|
|
return ButtonDownWithModifiers(pt, curTime, ModifierFlags(shift, ctrl, alt));
|
|
}
|
|
|
|
bool Editor::PositionIsHotspot(int position) const {
|
|
return vs.styles[pdoc->StyleAt(position) & pdoc->stylingBitsMask].hotspot;
|
|
}
|
|
|
|
bool Editor::PointIsHotspot(Point pt) {
|
|
int pos = PositionFromLocation(pt, true, true);
|
|
if (pos == INVALID_POSITION)
|
|
return false;
|
|
return PositionIsHotspot(pos);
|
|
}
|
|
|
|
void Editor::SetHotSpotRange(Point *pt) {
|
|
if (pt) {
|
|
int pos = PositionFromLocation(*pt, false, true);
|
|
|
|
// If we don't limit this to word characters then the
|
|
// range can encompass more than the run range and then
|
|
// the underline will not be drawn properly.
|
|
int hsStart_ = pdoc->ExtendStyleRange(pos, -1, vs.hotspotSingleLine);
|
|
int hsEnd_ = pdoc->ExtendStyleRange(pos, 1, vs.hotspotSingleLine);
|
|
|
|
// Only invalidate the range if the hotspot range has changed...
|
|
if (hsStart_ != hsStart || hsEnd_ != hsEnd) {
|
|
if (hsStart != -1) {
|
|
InvalidateRange(hsStart, hsEnd);
|
|
}
|
|
hsStart = hsStart_;
|
|
hsEnd = hsEnd_;
|
|
InvalidateRange(hsStart, hsEnd);
|
|
}
|
|
} else {
|
|
if (hsStart != -1) {
|
|
int hsStart_ = hsStart;
|
|
int hsEnd_ = hsEnd;
|
|
hsStart = -1;
|
|
hsEnd = -1;
|
|
InvalidateRange(hsStart_, hsEnd_);
|
|
} else {
|
|
hsStart = -1;
|
|
hsEnd = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Editor::GetHotSpotRange(int &hsStart_, int &hsEnd_) const {
|
|
hsStart_ = hsStart;
|
|
hsEnd_ = hsEnd;
|
|
}
|
|
|
|
void Editor::ButtonMoveWithModifiers(Point pt, int modifiers) {
|
|
if ((ptMouseLast.x != pt.x) || (ptMouseLast.y != pt.y)) {
|
|
DwellEnd(true);
|
|
}
|
|
|
|
SelectionPosition movePos = SPositionFromLocation(pt, false, false,
|
|
AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
|
|
movePos = MovePositionOutsideChar(movePos, sel.MainCaret() - movePos.Position());
|
|
|
|
if (inDragDrop == ddInitial) {
|
|
if (DragThreshold(ptMouseLast, pt)) {
|
|
SetMouseCapture(false);
|
|
SetDragPosition(movePos);
|
|
CopySelectionRange(&drag);
|
|
StartDrag();
|
|
}
|
|
return;
|
|
}
|
|
|
|
ptMouseLast = pt;
|
|
//Platform::DebugPrintf("Move %d %d\n", pt.x, pt.y);
|
|
if (HaveMouseCapture()) {
|
|
|
|
// Slow down autoscrolling/selection
|
|
autoScrollTimer.ticksToWait -= timer.tickSize;
|
|
if (autoScrollTimer.ticksToWait > 0)
|
|
return;
|
|
autoScrollTimer.ticksToWait = autoScrollDelay;
|
|
|
|
// Adjust selection
|
|
if (posDrag.IsValid()) {
|
|
SetDragPosition(movePos);
|
|
} else {
|
|
if (selectionType == selChar) {
|
|
if (sel.selType == Selection::selStream && (modifiers & SCI_ALT) && mouseSelectionRectangularSwitch) {
|
|
sel.selType = Selection::selRectangle;
|
|
}
|
|
if (sel.IsRectangular()) {
|
|
sel.Rectangular() = SelectionRange(movePos, sel.Rectangular().anchor);
|
|
SetSelection(movePos, sel.RangeMain().anchor);
|
|
} else if (sel.Count() > 1) {
|
|
InvalidateSelection(sel.RangeMain(), false);
|
|
SelectionRange range(movePos, sel.RangeMain().anchor);
|
|
sel.TentativeSelection(range);
|
|
InvalidateSelection(range, true);
|
|
} else {
|
|
SetSelection(movePos, sel.RangeMain().anchor);
|
|
}
|
|
} else if (selectionType == selWord) {
|
|
// Continue selecting by word
|
|
if (movePos.Position() == wordSelectInitialCaretPos) { // Didn't move
|
|
// No need to do anything. Previously this case was lumped
|
|
// in with "Moved forward", but that can be harmful in this
|
|
// case: a handler for the NotifyDoubleClick re-adjusts
|
|
// the selection for a fancier definition of "word" (for
|
|
// example, in Perl it is useful to include the leading
|
|
// '$', '%' or '@' on variables for word selection). In this
|
|
// the ButtonMove() called via Tick() for auto-scrolling
|
|
// could result in the fancier word selection adjustment
|
|
// being unmade.
|
|
} else {
|
|
wordSelectInitialCaretPos = -1;
|
|
WordSelection(movePos.Position());
|
|
}
|
|
} else {
|
|
// Continue selecting by line
|
|
LineSelection(movePos.Position(), lineAnchorPos, selectionType == selWholeLine);
|
|
}
|
|
}
|
|
|
|
// Autoscroll
|
|
PRectangle rcClient = GetClientRectangle();
|
|
Point ptOrigin = GetVisibleOriginInMain();
|
|
rcClient.Move(0, -ptOrigin.y);
|
|
int lineMove = DisplayFromPosition(movePos.Position());
|
|
if (pt.y > rcClient.bottom) {
|
|
ScrollTo(lineMove - LinesOnScreen() + 1);
|
|
Redraw();
|
|
} else if (pt.y < rcClient.top) {
|
|
ScrollTo(lineMove);
|
|
Redraw();
|
|
}
|
|
EnsureCaretVisible(false, false, true);
|
|
|
|
if (hsStart != -1 && !PointIsHotspot(pt))
|
|
SetHotSpotRange(NULL);
|
|
|
|
if (hotSpotClickPos != INVALID_POSITION && PositionFromLocation(pt,true,true) != hotSpotClickPos) {
|
|
if (inDragDrop == ddNone) {
|
|
DisplayCursor(Window::cursorText);
|
|
}
|
|
hotSpotClickPos = INVALID_POSITION;
|
|
}
|
|
|
|
} else {
|
|
if (vs.fixedColumnWidth > 0) { // There is a margin
|
|
if (PointInSelMargin(pt)) {
|
|
DisplayCursor(GetMarginCursor(pt));
|
|
SetHotSpotRange(NULL);
|
|
return; // No need to test for selection
|
|
}
|
|
}
|
|
// Display regular (drag) cursor over selection
|
|
if (PointInSelection(pt) && !SelectionEmpty()) {
|
|
DisplayCursor(Window::cursorArrow);
|
|
} else if (PointIsHotspot(pt)) {
|
|
DisplayCursor(Window::cursorHand);
|
|
SetHotSpotRange(&pt);
|
|
} else {
|
|
DisplayCursor(Window::cursorText);
|
|
SetHotSpotRange(NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Editor::ButtonMove(Point pt) {
|
|
ButtonMoveWithModifiers(pt, 0);
|
|
}
|
|
|
|
void Editor::ButtonUp(Point pt, unsigned int curTime, bool ctrl) {
|
|
//Platform::DebugPrintf("ButtonUp %d %d\n", HaveMouseCapture(), inDragDrop);
|
|
SelectionPosition newPos = SPositionFromLocation(pt, false, false,
|
|
AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
|
|
newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
|
|
if (inDragDrop == ddInitial) {
|
|
inDragDrop = ddNone;
|
|
SetEmptySelection(newPos);
|
|
selectionType = selChar;
|
|
originalAnchorPos = sel.MainCaret();
|
|
}
|
|
if (hotSpotClickPos != INVALID_POSITION && PointIsHotspot(pt)) {
|
|
hotSpotClickPos = INVALID_POSITION;
|
|
SelectionPosition newCharPos = SPositionFromLocation(pt, false, true, false);
|
|
newCharPos = MovePositionOutsideChar(newCharPos, -1);
|
|
NotifyHotSpotReleaseClick(newCharPos.Position(), ctrl ? SCI_CTRL : 0);
|
|
}
|
|
if (HaveMouseCapture()) {
|
|
if (PointInSelMargin(pt)) {
|
|
DisplayCursor(GetMarginCursor(pt));
|
|
} else {
|
|
DisplayCursor(Window::cursorText);
|
|
SetHotSpotRange(NULL);
|
|
}
|
|
ptMouseLast = pt;
|
|
SetMouseCapture(false);
|
|
NotifyIndicatorClick(false, newPos.Position(), 0);
|
|
if (inDragDrop == ddDragging) {
|
|
SelectionPosition selStart = SelectionStart();
|
|
SelectionPosition selEnd = SelectionEnd();
|
|
if (selStart < selEnd) {
|
|
if (drag.Length()) {
|
|
const int length = static_cast<int>(drag.Length());
|
|
if (ctrl) {
|
|
const int lengthInserted = pdoc->InsertString(
|
|
newPos.Position(), drag.Data(), length);
|
|
if (lengthInserted > 0) {
|
|
SetSelection(newPos.Position(), newPos.Position() + lengthInserted);
|
|
}
|
|
} else if (newPos < selStart) {
|
|
pdoc->DeleteChars(selStart.Position(), static_cast<int>(drag.Length()));
|
|
const int lengthInserted = pdoc->InsertString(
|
|
newPos.Position(), drag.Data(), length);
|
|
if (lengthInserted > 0) {
|
|
SetSelection(newPos.Position(), newPos.Position() + lengthInserted);
|
|
}
|
|
} else if (newPos > selEnd) {
|
|
pdoc->DeleteChars(selStart.Position(), static_cast<int>(drag.Length()));
|
|
newPos.Add(-static_cast<int>(drag.Length()));
|
|
const int lengthInserted = pdoc->InsertString(
|
|
newPos.Position(), drag.Data(), length);
|
|
if (lengthInserted > 0) {
|
|
SetSelection(newPos.Position(), newPos.Position() + lengthInserted);
|
|
}
|
|
} else {
|
|
SetEmptySelection(newPos.Position());
|
|
}
|
|
drag.Clear();
|
|
}
|
|
selectionType = selChar;
|
|
}
|
|
} else {
|
|
if (selectionType == selChar) {
|
|
if (sel.Count() > 1) {
|
|
sel.RangeMain() =
|
|
SelectionRange(newPos, sel.Range(sel.Count() - 1).anchor);
|
|
InvalidateSelection(sel.RangeMain(), true);
|
|
} else {
|
|
SetSelection(newPos, sel.RangeMain().anchor);
|
|
}
|
|
}
|
|
sel.CommitTentative();
|
|
}
|
|
SetRectangularRange();
|
|
lastClickTime = curTime;
|
|
lastClick = pt;
|
|
lastXChosen = static_cast<int>(pt.x) + xOffset;
|
|
if (sel.selType == Selection::selStream) {
|
|
SetLastXChosen();
|
|
}
|
|
inDragDrop = ddNone;
|
|
EnsureCaretVisible(false);
|
|
}
|
|
}
|
|
|
|
// Called frequently to perform background UI including
|
|
// caret blinking and automatic scrolling.
|
|
void Editor::Tick() {
|
|
if (HaveMouseCapture()) {
|
|
// Auto scroll
|
|
ButtonMove(ptMouseLast);
|
|
}
|
|
if (caret.period > 0) {
|
|
timer.ticksToWait -= timer.tickSize;
|
|
if (timer.ticksToWait <= 0) {
|
|
caret.on = !caret.on;
|
|
timer.ticksToWait = caret.period;
|
|
if (caret.active) {
|
|
InvalidateCaret();
|
|
}
|
|
}
|
|
}
|
|
if (horizontalScrollBarVisible && trackLineWidth && (lineWidthMaxSeen > scrollWidth)) {
|
|
scrollWidth = lineWidthMaxSeen;
|
|
SetScrollBars();
|
|
}
|
|
if ((dwellDelay < SC_TIME_FOREVER) &&
|
|
(ticksToDwell > 0) &&
|
|
(!HaveMouseCapture()) &&
|
|
(ptMouseLast.y >= 0)) {
|
|
ticksToDwell -= timer.tickSize;
|
|
if (ticksToDwell <= 0) {
|
|
dwelling = true;
|
|
NotifyDwelling(ptMouseLast, dwelling);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Editor::Idle() {
|
|
|
|
bool idleDone;
|
|
|
|
bool wrappingDone = !Wrapping();
|
|
|
|
if (!wrappingDone) {
|
|
// Wrap lines during idle.
|
|
WrapLines(wsIdle);
|
|
// No more wrapping
|
|
if (!wrapPending.NeedsWrap())
|
|
wrappingDone = true;
|
|
}
|
|
|
|
// Add more idle things to do here, but make sure idleDone is
|
|
// set correctly before the function returns. returning
|
|
// false will stop calling this idle function until SetIdle() is
|
|
// called again.
|
|
|
|
idleDone = wrappingDone; // && thatDone && theOtherThingDone...
|
|
|
|
return !idleDone;
|
|
}
|
|
|
|
void Editor::SetFocusState(bool focusState) {
|
|
hasFocus = focusState;
|
|
NotifyFocus(hasFocus);
|
|
if (hasFocus) {
|
|
ShowCaretAtCurrentPosition();
|
|
} else {
|
|
CancelModes();
|
|
DropCaret();
|
|
}
|
|
}
|
|
|
|
int Editor::PositionAfterArea(PRectangle rcArea) const {
|
|
// The start of the document line after the display line after the area
|
|
// This often means that the line after a modification is restyled which helps
|
|
// detect multiline comment additions and heals single line comments
|
|
int lineAfter = TopLineOfMain() + static_cast<int>(rcArea.bottom - 1) / vs.lineHeight + 1;
|
|
if (lineAfter < cs.LinesDisplayed())
|
|
return pdoc->LineStart(cs.DocFromDisplay(lineAfter) + 1);
|
|
else
|
|
return pdoc->Length();
|
|
}
|
|
|
|
// Style to a position within the view. If this causes a change at end of last line then
|
|
// affects later lines so style all the viewed text.
|
|
void Editor::StyleToPositionInView(Position pos) {
|
|
int endWindow = PositionAfterArea(GetClientDrawingRectangle());
|
|
if (pos > endWindow)
|
|
pos = endWindow;
|
|
int styleAtEnd = pdoc->StyleAt(pos-1);
|
|
pdoc->EnsureStyledTo(pos);
|
|
if ((endWindow > pos) && (styleAtEnd != pdoc->StyleAt(pos-1))) {
|
|
// Style at end of line changed so is multi-line change like starting a comment
|
|
// so require rest of window to be styled.
|
|
DiscardOverdraw(); // Prepared bitmaps may be invalid
|
|
// DiscardOverdraw may have truncated client drawing area so recalculate endWindow
|
|
endWindow = PositionAfterArea(GetClientDrawingRectangle());
|
|
pdoc->EnsureStyledTo(endWindow);
|
|
}
|
|
}
|
|
|
|
void Editor::IdleWork() {
|
|
// Style the line after the modification as this allows modifications that change just the
|
|
// line of the modification to heal instead of propagating to the rest of the window.
|
|
if (workNeeded.items & WorkNeeded::workStyle)
|
|
StyleToPositionInView(pdoc->LineStart(pdoc->LineFromPosition(workNeeded.upTo) + 2));
|
|
|
|
NotifyUpdateUI();
|
|
workNeeded.Reset();
|
|
}
|
|
|
|
void Editor::QueueIdleWork(WorkNeeded::workItems items, int upTo) {
|
|
workNeeded.Need(items, upTo);
|
|
}
|
|
|
|
bool Editor::PaintContains(PRectangle rc) {
|
|
if (rc.Empty()) {
|
|
return true;
|
|
} else {
|
|
return rcPaint.Contains(rc);
|
|
}
|
|
}
|
|
|
|
bool Editor::PaintContainsMargin() {
|
|
if (wMargin.GetID()) {
|
|
// With separate margin view, paint of text view
|
|
// never contains margin.
|
|
return false;
|
|
}
|
|
PRectangle rcSelMargin = GetClientRectangle();
|
|
rcSelMargin.right = static_cast<XYPOSITION>(vs.textStart);
|
|
return PaintContains(rcSelMargin);
|
|
}
|
|
|
|
void Editor::CheckForChangeOutsidePaint(Range r) {
|
|
if (paintState == painting && !paintingAllText) {
|
|
//Platform::DebugPrintf("Checking range in paint %d-%d\n", r.start, r.end);
|
|
if (!r.Valid())
|
|
return;
|
|
|
|
PRectangle rcRange = RectangleFromRange(r);
|
|
PRectangle rcText = GetTextRectangle();
|
|
if (rcRange.top < rcText.top) {
|
|
rcRange.top = rcText.top;
|
|
}
|
|
if (rcRange.bottom > rcText.bottom) {
|
|
rcRange.bottom = rcText.bottom;
|
|
}
|
|
|
|
if (!PaintContains(rcRange)) {
|
|
AbandonPaint();
|
|
paintAbandonedByStyling = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Editor::SetBraceHighlight(Position pos0, Position pos1, int matchStyle) {
|
|
if ((pos0 != braces[0]) || (pos1 != braces[1]) || (matchStyle != bracesMatchStyle)) {
|
|
if ((braces[0] != pos0) || (matchStyle != bracesMatchStyle)) {
|
|
CheckForChangeOutsidePaint(Range(braces[0]));
|
|
CheckForChangeOutsidePaint(Range(pos0));
|
|
braces[0] = pos0;
|
|
}
|
|
if ((braces[1] != pos1) || (matchStyle != bracesMatchStyle)) {
|
|
CheckForChangeOutsidePaint(Range(braces[1]));
|
|
CheckForChangeOutsidePaint(Range(pos1));
|
|
braces[1] = pos1;
|
|
}
|
|
bracesMatchStyle = matchStyle;
|
|
if (paintState == notPainting) {
|
|
Redraw();
|
|
}
|
|
}
|
|
}
|
|
|
|
void Editor::SetAnnotationHeights(int start, int end) {
|
|
if (vs.annotationVisible) {
|
|
bool changedHeight = false;
|
|
for (int line=start; line<end && line<pdoc->LinesTotal(); line++) {
|
|
int linesWrapped = 1;
|
|
if (Wrapping()) {
|
|
AutoSurface surface(this);
|
|
AutoLineLayout ll(llc, RetrieveLineLayout(line));
|
|
if (surface && ll) {
|
|
LayoutLine(line, surface, vs, ll, wrapWidth);
|
|
linesWrapped = ll->lines;
|
|
}
|
|
}
|
|
if (cs.SetHeight(line, pdoc->AnnotationLines(line) + linesWrapped))
|
|
changedHeight = true;
|
|
}
|
|
if (changedHeight) {
|
|
Redraw();
|
|
}
|
|
}
|
|
}
|
|
|
|
void Editor::SetDocPointer(Document *document) {
|
|
//Platform::DebugPrintf("** %x setdoc to %x\n", pdoc, document);
|
|
pdoc->RemoveWatcher(this, 0);
|
|
pdoc->Release();
|
|
if (document == NULL) {
|
|
pdoc = new Document();
|
|
} else {
|
|
pdoc = document;
|
|
}
|
|
pdoc->AddRef();
|
|
|
|
// Ensure all positions within document
|
|
sel.Clear();
|
|
targetStart = 0;
|
|
targetEnd = 0;
|
|
|
|
braces[0] = invalidPosition;
|
|
braces[1] = invalidPosition;
|
|
|
|
vs.ReleaseAllExtendedStyles();
|
|
|
|
SetRepresentations();
|
|
|
|
// Reset the contraction state to fully shown.
|
|
cs.Clear();
|
|
cs.InsertLines(0, pdoc->LinesTotal() - 1);
|
|
SetAnnotationHeights(0, pdoc->LinesTotal());
|
|
llc.Deallocate();
|
|
NeedWrapping();
|
|
|
|
pdoc->AddWatcher(this, 0);
|
|
SetScrollBars();
|
|
Redraw();
|
|
}
|
|
|
|
void Editor::SetAnnotationVisible(int visible) {
|
|
if (vs.annotationVisible != visible) {
|
|
bool changedFromOrToHidden = ((vs.annotationVisible != 0) != (visible != 0));
|
|
vs.annotationVisible = visible;
|
|
if (changedFromOrToHidden) {
|
|
int dir = vs.annotationVisible ? 1 : -1;
|
|
for (int line=0; line<pdoc->LinesTotal(); line++) {
|
|
int annotationLines = pdoc->AnnotationLines(line);
|
|
if (annotationLines > 0) {
|
|
cs.SetHeight(line, cs.GetHeight(line) + annotationLines * dir);
|
|
}
|
|
}
|
|
}
|
|
Redraw();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Recursively expand a fold, making lines visible except where they have an unexpanded parent.
|
|
*/
|
|
int Editor::ExpandLine(int line) {
|
|
int lineMaxSubord = pdoc->GetLastChild(line);
|
|
line++;
|
|
while (line <= lineMaxSubord) {
|
|
cs.SetVisible(line, line, true);
|
|
int level = pdoc->GetLevel(line);
|
|
if (level & SC_FOLDLEVELHEADERFLAG) {
|
|
if (cs.GetExpanded(line)) {
|
|
line = ExpandLine(line);
|
|
} else {
|
|
line = pdoc->GetLastChild(line);
|
|
}
|
|
}
|
|
line++;
|
|
}
|
|
return lineMaxSubord;
|
|
}
|
|
|
|
void Editor::SetFoldExpanded(int lineDoc, bool expanded) {
|
|
if (cs.SetExpanded(lineDoc, expanded)) {
|
|
RedrawSelMargin();
|
|
}
|
|
}
|
|
|
|
void Editor::FoldLine(int line, int action) {
|
|
if (line >= 0) {
|
|
if (action == SC_FOLDACTION_TOGGLE) {
|
|
if ((pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) == 0) {
|
|
line = pdoc->GetFoldParent(line);
|
|
if (line < 0)
|
|
return;
|
|
}
|
|
action = (cs.GetExpanded(line)) ? SC_FOLDACTION_CONTRACT : SC_FOLDACTION_EXPAND;
|
|
}
|
|
|
|
if (action == SC_FOLDACTION_CONTRACT) {
|
|
int lineMaxSubord = pdoc->GetLastChild(line);
|
|
if (lineMaxSubord > line) {
|
|
cs.SetExpanded(line, 0);
|
|
cs.SetVisible(line + 1, lineMaxSubord, false);
|
|
|
|
int lineCurrent = pdoc->LineFromPosition(sel.MainCaret());
|
|
if (lineCurrent > line && lineCurrent <= lineMaxSubord) {
|
|
// This does not re-expand the fold
|
|
EnsureCaretVisible();
|
|
}
|
|
}
|
|
|
|
} else {
|
|
if (!(cs.GetVisible(line))) {
|
|
EnsureLineVisible(line, false);
|
|
GoToLine(line);
|
|
}
|
|
cs.SetExpanded(line, 1);
|
|
ExpandLine(line);
|
|
}
|
|
|
|
SetScrollBars();
|
|
Redraw();
|
|
}
|
|
}
|
|
|
|
void Editor::FoldExpand(int line, int action, int level) {
|
|
bool expanding = action == SC_FOLDACTION_EXPAND;
|
|
if (action == SC_FOLDACTION_TOGGLE) {
|
|
expanding = !cs.GetExpanded(line);
|
|
}
|
|
SetFoldExpanded(line, expanding);
|
|
if (expanding && (cs.HiddenLines() == 0))
|
|
// Nothing to do
|
|
return;
|
|
int lineMaxSubord = pdoc->GetLastChild(line, level & SC_FOLDLEVELNUMBERMASK);
|
|
line++;
|
|
cs.SetVisible(line, lineMaxSubord, expanding);
|
|
while (line <= lineMaxSubord) {
|
|
int levelLine = pdoc->GetLevel(line);
|
|
if (levelLine & SC_FOLDLEVELHEADERFLAG) {
|
|
SetFoldExpanded(line, expanding);
|
|
}
|
|
line++;
|
|
}
|
|
SetScrollBars();
|
|
Redraw();
|
|
}
|
|
|
|
int Editor::ContractedFoldNext(int lineStart) const {
|
|
for (int line = lineStart; line<pdoc->LinesTotal();) {
|
|
if (!cs.GetExpanded(line) && (pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG))
|
|
return line;
|
|
line = cs.ContractedNext(line+1);
|
|
if (line < 0)
|
|
return -1;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* Recurse up from this line to find any folds that prevent this line from being visible
|
|
* and unfold them all.
|
|
*/
|
|
void Editor::EnsureLineVisible(int lineDoc, bool enforcePolicy) {
|
|
|
|
// In case in need of wrapping to ensure DisplayFromDoc works.
|
|
if (lineDoc >= wrapPending.start)
|
|
WrapLines(wsAll);
|
|
|
|
if (!cs.GetVisible(lineDoc)) {
|
|
// Back up to find a non-blank line
|
|
int lookLine = lineDoc;
|
|
int lookLineLevel = pdoc->GetLevel(lookLine);
|
|
while ((lookLine > 0) && (lookLineLevel & SC_FOLDLEVELWHITEFLAG)) {
|
|
lookLineLevel = pdoc->GetLevel(--lookLine);
|
|
}
|
|
int lineParent = pdoc->GetFoldParent(lookLine);
|
|
if (lineParent < 0) {
|
|
// Backed up to a top level line, so try to find parent of initial line
|
|
lineParent = pdoc->GetFoldParent(lineDoc);
|
|
}
|
|
if (lineParent >= 0) {
|
|
if (lineDoc != lineParent)
|
|
EnsureLineVisible(lineParent, enforcePolicy);
|
|
if (!cs.GetExpanded(lineParent)) {
|
|
cs.SetExpanded(lineParent, 1);
|
|
ExpandLine(lineParent);
|
|
}
|
|
}
|
|
SetScrollBars();
|
|
Redraw();
|
|
}
|
|
if (enforcePolicy) {
|
|
int lineDisplay = cs.DisplayFromDoc(lineDoc);
|
|
if (visiblePolicy & VISIBLE_SLOP) {
|
|
if ((topLine > lineDisplay) || ((visiblePolicy & VISIBLE_STRICT) && (topLine + visibleSlop > lineDisplay))) {
|
|
SetTopLine(Platform::Clamp(lineDisplay - visibleSlop, 0, MaxScrollPos()));
|
|
SetVerticalScrollPos();
|
|
Redraw();
|
|
} else if ((lineDisplay > topLine + LinesOnScreen() - 1) ||
|
|
((visiblePolicy & VISIBLE_STRICT) && (lineDisplay > topLine + LinesOnScreen() - 1 - visibleSlop))) {
|
|
SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() + 1 + visibleSlop, 0, MaxScrollPos()));
|
|
SetVerticalScrollPos();
|
|
Redraw();
|
|
}
|
|
} else {
|
|
if ((topLine > lineDisplay) || (lineDisplay > topLine + LinesOnScreen() - 1) || (visiblePolicy & VISIBLE_STRICT)) {
|
|
SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() / 2 + 1, 0, MaxScrollPos()));
|
|
SetVerticalScrollPos();
|
|
Redraw();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Editor::FoldAll(int action) {
|
|
pdoc->EnsureStyledTo(pdoc->Length());
|
|
int maxLine = pdoc->LinesTotal();
|
|
bool expanding = action == SC_FOLDACTION_EXPAND;
|
|
if (action == SC_FOLDACTION_TOGGLE) {
|
|
// Discover current state
|
|
for (int lineSeek = 0; lineSeek < maxLine; lineSeek++) {
|
|
if (pdoc->GetLevel(lineSeek) & SC_FOLDLEVELHEADERFLAG) {
|
|
expanding = !cs.GetExpanded(lineSeek);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (expanding) {
|
|
cs.SetVisible(0, maxLine-1, true);
|
|
for (int line = 0; line < maxLine; line++) {
|
|
int levelLine = pdoc->GetLevel(line);
|
|
if (levelLine & SC_FOLDLEVELHEADERFLAG) {
|
|
SetFoldExpanded(line, true);
|
|
}
|
|
}
|
|
} else {
|
|
for (int line = 0; line < maxLine; line++) {
|
|
int level = pdoc->GetLevel(line);
|
|
if ((level & SC_FOLDLEVELHEADERFLAG) &&
|
|
(SC_FOLDLEVELBASE == (level & SC_FOLDLEVELNUMBERMASK))) {
|
|
SetFoldExpanded(line, false);
|
|
int lineMaxSubord = pdoc->GetLastChild(line, -1);
|
|
if (lineMaxSubord > line) {
|
|
cs.SetVisible(line + 1, lineMaxSubord, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
SetScrollBars();
|
|
Redraw();
|
|
}
|
|
|
|
void Editor::FoldChanged(int line, int levelNow, int levelPrev) {
|
|
if (levelNow & SC_FOLDLEVELHEADERFLAG) {
|
|
if (!(levelPrev & SC_FOLDLEVELHEADERFLAG)) {
|
|
// Adding a fold point.
|
|
if (cs.SetExpanded(line, true)) {
|
|
RedrawSelMargin();
|
|
}
|
|
FoldExpand(line, SC_FOLDACTION_EXPAND, levelPrev);
|
|
}
|
|
} else if (levelPrev & SC_FOLDLEVELHEADERFLAG) {
|
|
if (!cs.GetExpanded(line)) {
|
|
// Removing the fold from one that has been contracted so should expand
|
|
// otherwise lines are left invisible with no way to make them visible
|
|
if (cs.SetExpanded(line, true)) {
|
|
RedrawSelMargin();
|
|
}
|
|
FoldExpand(line, SC_FOLDACTION_EXPAND, levelPrev);
|
|
}
|
|
}
|
|
if (!(levelNow & SC_FOLDLEVELWHITEFLAG) &&
|
|
((levelPrev & SC_FOLDLEVELNUMBERMASK) > (levelNow & SC_FOLDLEVELNUMBERMASK))) {
|
|
if (cs.HiddenLines()) {
|
|
// See if should still be hidden
|
|
int parentLine = pdoc->GetFoldParent(line);
|
|
if ((parentLine < 0) || (cs.GetExpanded(parentLine) && cs.GetVisible(parentLine))) {
|
|
cs.SetVisible(line, line, true);
|
|
SetScrollBars();
|
|
Redraw();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Editor::NeedShown(int pos, int len) {
|
|
if (foldAutomatic & SC_AUTOMATICFOLD_SHOW) {
|
|
int lineStart = pdoc->LineFromPosition(pos);
|
|
int lineEnd = pdoc->LineFromPosition(pos+len);
|
|
for (int line = lineStart; line <= lineEnd; line++) {
|
|
EnsureLineVisible(line, false);
|
|
}
|
|
} else {
|
|
NotifyNeedShown(pos, len);
|
|
}
|
|
}
|
|
|
|
int Editor::GetTag(char *tagValue, int tagNumber) {
|
|
const char *text = 0;
|
|
int length = 0;
|
|
if ((tagNumber >= 1) && (tagNumber <= 9)) {
|
|
char name[3] = "\\?";
|
|
name[1] = static_cast<char>(tagNumber + '0');
|
|
length = 2;
|
|
text = pdoc->SubstituteByPosition(name, &length);
|
|
}
|
|
if (tagValue) {
|
|
if (text)
|
|
memcpy(tagValue, text, length + 1);
|
|
else
|
|
*tagValue = '\0';
|
|
}
|
|
return length;
|
|
}
|
|
|
|
int Editor::ReplaceTarget(bool replacePatterns, const char *text, int length) {
|
|
UndoGroup ug(pdoc);
|
|
if (length == -1)
|
|
length = istrlen(text);
|
|
if (replacePatterns) {
|
|
text = pdoc->SubstituteByPosition(text, &length);
|
|
if (!text) {
|
|
return 0;
|
|
}
|
|
}
|
|
if (targetStart != targetEnd)
|
|
pdoc->DeleteChars(targetStart, targetEnd - targetStart);
|
|
targetEnd = targetStart;
|
|
const int lengthInserted = pdoc->InsertString(targetStart, text, length);
|
|
targetEnd = targetStart + lengthInserted;
|
|
return length;
|
|
}
|
|
|
|
bool Editor::IsUnicodeMode() const {
|
|
return pdoc && (SC_CP_UTF8 == pdoc->dbcsCodePage);
|
|
}
|
|
|
|
int Editor::CodePage() const {
|
|
if (pdoc)
|
|
return pdoc->dbcsCodePage;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
int Editor::WrapCount(int line) {
|
|
AutoSurface surface(this);
|
|
AutoLineLayout ll(llc, RetrieveLineLayout(line));
|
|
|
|
if (surface && ll) {
|
|
LayoutLine(line, surface, vs, ll, wrapWidth);
|
|
return ll->lines;
|
|
} else {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
void Editor::AddStyledText(char *buffer, int appendLength) {
|
|
// The buffer consists of alternating character bytes and style bytes
|
|
int textLength = appendLength / 2;
|
|
std::string text(textLength, '\0');
|
|
int i;
|
|
for (i = 0; i < textLength; i++) {
|
|
text[i] = buffer[i*2];
|
|
}
|
|
const int lengthInserted = pdoc->InsertString(CurrentPosition(), text.c_str(), textLength);
|
|
for (i = 0; i < textLength; i++) {
|
|
text[i] = buffer[i*2+1];
|
|
}
|
|
pdoc->StartStyling(CurrentPosition(), static_cast<unsigned char>(0xff));
|
|
pdoc->SetStyles(textLength, text.c_str());
|
|
SetEmptySelection(sel.MainCaret() + lengthInserted);
|
|
}
|
|
|
|
static bool ValidMargin(uptr_t wParam) {
|
|
return wParam <= SC_MAX_MARGIN;
|
|
}
|
|
|
|
static char *CharPtrFromSPtr(sptr_t lParam) {
|
|
return reinterpret_cast<char *>(lParam);
|
|
}
|
|
|
|
void Editor::StyleSetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
|
|
vs.EnsureStyle(wParam);
|
|
switch (iMessage) {
|
|
case SCI_STYLESETFORE:
|
|
vs.styles[wParam].fore = ColourDesired(static_cast<long>(lParam));
|
|
break;
|
|
case SCI_STYLESETBACK:
|
|
vs.styles[wParam].back = ColourDesired(static_cast<long>(lParam));
|
|
break;
|
|
case SCI_STYLESETBOLD:
|
|
vs.styles[wParam].weight = lParam != 0 ? SC_WEIGHT_BOLD : SC_WEIGHT_NORMAL;
|
|
break;
|
|
case SCI_STYLESETWEIGHT:
|
|
vs.styles[wParam].weight = static_cast<int>(lParam);
|
|
break;
|
|
case SCI_STYLESETITALIC:
|
|
vs.styles[wParam].italic = lParam != 0;
|
|
break;
|
|
case SCI_STYLESETEOLFILLED:
|
|
vs.styles[wParam].eolFilled = lParam != 0;
|
|
break;
|
|
case SCI_STYLESETSIZE:
|
|
vs.styles[wParam].size = static_cast<int>(lParam * SC_FONT_SIZE_MULTIPLIER);
|
|
break;
|
|
case SCI_STYLESETSIZEFRACTIONAL:
|
|
vs.styles[wParam].size = static_cast<int>(lParam);
|
|
break;
|
|
case SCI_STYLESETFONT:
|
|
if (lParam != 0) {
|
|
vs.SetStyleFontName(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
|
|
}
|
|
break;
|
|
case SCI_STYLESETUNDERLINE:
|
|
vs.styles[wParam].underline = lParam != 0;
|
|
break;
|
|
case SCI_STYLESETCASE:
|
|
vs.styles[wParam].caseForce = static_cast<Style::ecaseForced>(lParam);
|
|
break;
|
|
case SCI_STYLESETCHARACTERSET:
|
|
vs.styles[wParam].characterSet = static_cast<int>(lParam);
|
|
pdoc->SetCaseFolder(NULL);
|
|
break;
|
|
case SCI_STYLESETVISIBLE:
|
|
vs.styles[wParam].visible = lParam != 0;
|
|
break;
|
|
case SCI_STYLESETCHANGEABLE:
|
|
vs.styles[wParam].changeable = lParam != 0;
|
|
break;
|
|
case SCI_STYLESETHOTSPOT:
|
|
vs.styles[wParam].hotspot = lParam != 0;
|
|
break;
|
|
}
|
|
InvalidateStyleRedraw();
|
|
}
|
|
|
|
sptr_t Editor::StyleGetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
|
|
vs.EnsureStyle(wParam);
|
|
switch (iMessage) {
|
|
case SCI_STYLEGETFORE:
|
|
return vs.styles[wParam].fore.AsLong();
|
|
case SCI_STYLEGETBACK:
|
|
return vs.styles[wParam].back.AsLong();
|
|
case SCI_STYLEGETBOLD:
|
|
return vs.styles[wParam].weight > SC_WEIGHT_NORMAL;
|
|
case SCI_STYLEGETWEIGHT:
|
|
return vs.styles[wParam].weight;
|
|
case SCI_STYLEGETITALIC:
|
|
return vs.styles[wParam].italic ? 1 : 0;
|
|
case SCI_STYLEGETEOLFILLED:
|
|
return vs.styles[wParam].eolFilled ? 1 : 0;
|
|
case SCI_STYLEGETSIZE:
|
|
return vs.styles[wParam].size / SC_FONT_SIZE_MULTIPLIER;
|
|
case SCI_STYLEGETSIZEFRACTIONAL:
|
|
return vs.styles[wParam].size;
|
|
case SCI_STYLEGETFONT:
|
|
return StringResult(lParam, vs.styles[wParam].fontName);
|
|
case SCI_STYLEGETUNDERLINE:
|
|
return vs.styles[wParam].underline ? 1 : 0;
|
|
case SCI_STYLEGETCASE:
|
|
return static_cast<int>(vs.styles[wParam].caseForce);
|
|
case SCI_STYLEGETCHARACTERSET:
|
|
return vs.styles[wParam].characterSet;
|
|
case SCI_STYLEGETVISIBLE:
|
|
return vs.styles[wParam].visible ? 1 : 0;
|
|
case SCI_STYLEGETCHANGEABLE:
|
|
return vs.styles[wParam].changeable ? 1 : 0;
|
|
case SCI_STYLEGETHOTSPOT:
|
|
return vs.styles[wParam].hotspot ? 1 : 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
sptr_t Editor::StringResult(sptr_t lParam, const char *val) {
|
|
const size_t len = val ? strlen(val) : 0;
|
|
if (lParam) {
|
|
char *ptr = CharPtrFromSPtr(lParam);
|
|
if (val)
|
|
memcpy(ptr, val, len+1);
|
|
else
|
|
*ptr = 0;
|
|
}
|
|
return len; // Not including NUL
|
|
}
|
|
|
|
sptr_t Editor::BytesResult(sptr_t lParam, const unsigned char *val, size_t len) {
|
|
// No NUL termination: len is number of valid/displayed bytes
|
|
if (lParam) {
|
|
char *ptr = CharPtrFromSPtr(lParam);
|
|
if (val)
|
|
memcpy(ptr, val, len);
|
|
else
|
|
*ptr = 0;
|
|
}
|
|
return val ? len : 0;
|
|
}
|
|
|
|
sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
|
|
//Platform::DebugPrintf("S start wnd proc %d %d %d\n",iMessage, wParam, lParam);
|
|
|
|
// Optional macro recording hook
|
|
if (recordingMacro)
|
|
NotifyMacroRecord(iMessage, wParam, lParam);
|
|
|
|
switch (iMessage) {
|
|
|
|
case SCI_GETTEXT: {
|
|
if (lParam == 0)
|
|
return pdoc->Length() + 1;
|
|
if (wParam == 0)
|
|
return 0;
|
|
char *ptr = CharPtrFromSPtr(lParam);
|
|
unsigned int iChar = 0;
|
|
for (; iChar < wParam - 1; iChar++)
|
|
ptr[iChar] = pdoc->CharAt(iChar);
|
|
ptr[iChar] = '\0';
|
|
return iChar;
|
|
}
|
|
|
|
case SCI_SETTEXT: {
|
|
if (lParam == 0)
|
|
return 0;
|
|
UndoGroup ug(pdoc);
|
|
pdoc->DeleteChars(0, pdoc->Length());
|
|
SetEmptySelection(0);
|
|
const char *text = CharPtrFromSPtr(lParam);
|
|
pdoc->InsertString(0, text, istrlen(text));
|
|
return 1;
|
|
}
|
|
|
|
case SCI_GETTEXTLENGTH:
|
|
return pdoc->Length();
|
|
|
|
case SCI_CUT:
|
|
Cut();
|
|
SetLastXChosen();
|
|
break;
|
|
|
|
case SCI_COPY:
|
|
Copy();
|
|
break;
|
|
|
|
case SCI_COPYALLOWLINE:
|
|
CopyAllowLine();
|
|
break;
|
|
|
|
case SCI_VERTICALCENTRECARET:
|
|
VerticalCentreCaret();
|
|
break;
|
|
|
|
case SCI_MOVESELECTEDLINESUP:
|
|
MoveSelectedLinesUp();
|
|
break;
|
|
|
|
case SCI_MOVESELECTEDLINESDOWN:
|
|
MoveSelectedLinesDown();
|
|
break;
|
|
|
|
case SCI_COPYRANGE:
|
|
CopyRangeToClipboard(static_cast<int>(wParam), static_cast<int>(lParam));
|
|
break;
|
|
|
|
case SCI_COPYTEXT:
|
|
CopyText(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
|
|
break;
|
|
|
|
case SCI_PASTE:
|
|
Paste();
|
|
if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
|
|
SetLastXChosen();
|
|
}
|
|
EnsureCaretVisible();
|
|
break;
|
|
|
|
case SCI_CLEAR:
|
|
Clear();
|
|
SetLastXChosen();
|
|
EnsureCaretVisible();
|
|
break;
|
|
|
|
case SCI_UNDO:
|
|
Undo();
|
|
SetLastXChosen();
|
|
break;
|
|
|
|
case SCI_CANUNDO:
|
|
return (pdoc->CanUndo() && !pdoc->IsReadOnly()) ? 1 : 0;
|
|
|
|
case SCI_EMPTYUNDOBUFFER:
|
|
pdoc->DeleteUndoHistory();
|
|
return 0;
|
|
|
|
case SCI_GETFIRSTVISIBLELINE:
|
|
return topLine;
|
|
|
|
case SCI_SETFIRSTVISIBLELINE:
|
|
ScrollTo(static_cast<int>(wParam));
|
|
break;
|
|
|
|
case SCI_GETLINE: { // Risk of overwriting the end of the buffer
|
|
int lineStart = pdoc->LineStart(static_cast<int>(wParam));
|
|
int lineEnd = pdoc->LineStart(static_cast<int>(wParam + 1));
|
|
if (lParam == 0) {
|
|
return lineEnd - lineStart;
|
|
}
|
|
char *ptr = CharPtrFromSPtr(lParam);
|
|
int iPlace = 0;
|
|
for (int iChar = lineStart; iChar < lineEnd; iChar++) {
|
|
ptr[iPlace++] = pdoc->CharAt(iChar);
|
|
}
|
|
return iPlace;
|
|
}
|
|
|
|
case SCI_GETLINECOUNT:
|
|
if (pdoc->LinesTotal() == 0)
|
|
return 1;
|
|
else
|
|
return pdoc->LinesTotal();
|
|
|
|
case SCI_GETMODIFY:
|
|
return !pdoc->IsSavePoint();
|
|
|
|
case SCI_SETSEL: {
|
|
int nStart = static_cast<int>(wParam);
|
|
int nEnd = static_cast<int>(lParam);
|
|
if (nEnd < 0)
|
|
nEnd = pdoc->Length();
|
|
if (nStart < 0)
|
|
nStart = nEnd; // Remove selection
|
|
InvalidateSelection(SelectionRange(nStart, nEnd));
|
|
sel.Clear();
|
|
sel.selType = Selection::selStream;
|
|
SetSelection(nEnd, nStart);
|
|
EnsureCaretVisible();
|
|
}
|
|
break;
|
|
|
|
case SCI_GETSELTEXT: {
|
|
SelectionText selectedText;
|
|
CopySelectionRange(&selectedText);
|
|
if (lParam == 0) {
|
|
return selectedText.LengthWithTerminator();
|
|
} else {
|
|
char *ptr = CharPtrFromSPtr(lParam);
|
|
unsigned int iChar = 0;
|
|
if (selectedText.Length()) {
|
|
for (; iChar < selectedText.LengthWithTerminator(); iChar++)
|
|
ptr[iChar] = selectedText.Data()[iChar];
|
|
} else {
|
|
ptr[0] = '\0';
|
|
}
|
|
return iChar;
|
|
}
|
|
}
|
|
|
|
case SCI_LINEFROMPOSITION:
|
|
if (static_cast<int>(wParam) < 0)
|
|
return 0;
|
|
return pdoc->LineFromPosition(static_cast<int>(wParam));
|
|
|
|
case SCI_POSITIONFROMLINE:
|
|
if (static_cast<int>(wParam) < 0)
|
|
wParam = pdoc->LineFromPosition(SelectionStart().Position());
|
|
if (wParam == 0)
|
|
return 0; // Even if there is no text, there is a first line that starts at 0
|
|
if (static_cast<int>(wParam) > pdoc->LinesTotal())
|
|
return -1;
|
|
//if (wParam > pdoc->LineFromPosition(pdoc->Length())) // Useful test, anyway...
|
|
// return -1;
|
|
return pdoc->LineStart(static_cast<int>(wParam));
|
|
|
|
// Replacement of the old Scintilla interpretation of EM_LINELENGTH
|
|
case SCI_LINELENGTH:
|
|
if ((static_cast<int>(wParam) < 0) ||
|
|
(static_cast<int>(wParam) > pdoc->LineFromPosition(pdoc->Length())))
|
|
return 0;
|
|
return pdoc->LineStart(static_cast<int>(wParam) + 1) - pdoc->LineStart(static_cast<int>(wParam));
|
|
|
|
case SCI_REPLACESEL: {
|
|
if (lParam == 0)
|
|
return 0;
|
|
UndoGroup ug(pdoc);
|
|
ClearSelection();
|
|
char *replacement = CharPtrFromSPtr(lParam);
|
|
const int lengthInserted = pdoc->InsertString(
|
|
sel.MainCaret(), replacement, istrlen(replacement));
|
|
SetEmptySelection(sel.MainCaret() + lengthInserted);
|
|
EnsureCaretVisible();
|
|
}
|
|
break;
|
|
|
|
case SCI_SETTARGETSTART:
|
|
targetStart = static_cast<int>(wParam);
|
|
break;
|
|
|
|
case SCI_GETTARGETSTART:
|
|
return targetStart;
|
|
|
|
case SCI_SETTARGETEND:
|
|
targetEnd = static_cast<int>(wParam);
|
|
break;
|
|
|
|
case SCI_GETTARGETEND:
|
|
return targetEnd;
|
|
|
|
case SCI_TARGETFROMSELECTION:
|
|
if (sel.MainCaret() < sel.MainAnchor()) {
|
|
targetStart = sel.MainCaret();
|
|
targetEnd = sel.MainAnchor();
|
|
} else {
|
|
targetStart = sel.MainAnchor();
|
|
targetEnd = sel.MainCaret();
|
|
}
|
|
break;
|
|
|
|
case SCI_REPLACETARGET:
|
|
PLATFORM_ASSERT(lParam);
|
|
return ReplaceTarget(false, CharPtrFromSPtr(lParam), static_cast<int>(wParam));
|
|
|
|
case SCI_REPLACETARGETRE:
|
|
PLATFORM_ASSERT(lParam);
|
|
return ReplaceTarget(true, CharPtrFromSPtr(lParam), static_cast<int>(wParam));
|
|
|
|
case SCI_SEARCHINTARGET:
|
|
PLATFORM_ASSERT(lParam);
|
|
return SearchInTarget(CharPtrFromSPtr(lParam), static_cast<int>(wParam));
|
|
|
|
case SCI_SETSEARCHFLAGS:
|
|
searchFlags = static_cast<int>(wParam);
|
|
break;
|
|
|
|
case SCI_GETSEARCHFLAGS:
|
|
return searchFlags;
|
|
|
|
case SCI_GETTAG:
|
|
return GetTag(CharPtrFromSPtr(lParam), static_cast<int>(wParam));
|
|
|
|
case SCI_POSITIONBEFORE:
|
|
return pdoc->MovePositionOutsideChar(static_cast<int>(wParam) - 1, -1, true);
|
|
|
|
case SCI_POSITIONAFTER:
|
|
return pdoc->MovePositionOutsideChar(static_cast<int>(wParam) + 1, 1, true);
|
|
|
|
case SCI_POSITIONRELATIVE:
|
|
return Platform::Clamp(pdoc->GetRelativePosition(static_cast<int>(wParam), static_cast<int>(lParam)), 0, pdoc->Length());
|
|
|
|
case SCI_LINESCROLL:
|
|
ScrollTo(topLine + static_cast<int>(lParam));
|
|
HorizontalScrollTo(xOffset + static_cast<int>(wParam)* static_cast<int>(vs.spaceWidth));
|
|
return 1;
|
|
|
|
case SCI_SETXOFFSET:
|
|
xOffset = static_cast<int>(wParam);
|
|
ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
|
|
SetHorizontalScrollPos();
|
|
Redraw();
|
|
break;
|
|
|
|
case SCI_GETXOFFSET:
|
|
return xOffset;
|
|
|
|
case SCI_CHOOSECARETX:
|
|
SetLastXChosen();
|
|
break;
|
|
|
|
case SCI_SCROLLCARET:
|
|
EnsureCaretVisible();
|
|
break;
|
|
|
|
case SCI_SETREADONLY:
|
|
pdoc->SetReadOnly(wParam != 0);
|
|
return 1;
|
|
|
|
case SCI_GETREADONLY:
|
|
return pdoc->IsReadOnly();
|
|
|
|
case SCI_CANPASTE:
|
|
return CanPaste();
|
|
|
|
case SCI_POINTXFROMPOSITION:
|
|
if (lParam < 0) {
|
|
return 0;
|
|
} else {
|
|
Point pt = LocationFromPosition(static_cast<int>(lParam));
|
|
// Convert to view-relative
|
|
return static_cast<int>(pt.x) - vs.textStart + vs.fixedColumnWidth;
|
|
}
|
|
|
|
case SCI_POINTYFROMPOSITION:
|
|
if (lParam < 0) {
|
|
return 0;
|
|
} else {
|
|
Point pt = LocationFromPosition(static_cast<int>(lParam));
|
|
return static_cast<int>(pt.y);
|
|
}
|
|
|
|
case SCI_FINDTEXT:
|
|
return FindText(wParam, lParam);
|
|
|
|
case SCI_GETTEXTRANGE: {
|
|
if (lParam == 0)
|
|
return 0;
|
|
Sci_TextRange *tr = reinterpret_cast<Sci_TextRange *>(lParam);
|
|
int cpMax = tr->chrg.cpMax;
|
|
if (cpMax == -1)
|
|
cpMax = pdoc->Length();
|
|
PLATFORM_ASSERT(cpMax <= pdoc->Length());
|
|
int len = cpMax - tr->chrg.cpMin; // No -1 as cpMin and cpMax are referring to inter character positions
|
|
pdoc->GetCharRange(tr->lpstrText, tr->chrg.cpMin, len);
|
|
// Spec says copied text is terminated with a NUL
|
|
tr->lpstrText[len] = '\0';
|
|
return len; // Not including NUL
|
|
}
|
|
|
|
case SCI_HIDESELECTION:
|
|
hideSelection = wParam != 0;
|
|
Redraw();
|
|
break;
|
|
|
|
case SCI_FORMATRANGE:
|
|
return FormatRange(wParam != 0, reinterpret_cast<Sci_RangeToFormat *>(lParam));
|
|
|
|
case SCI_GETMARGINLEFT:
|
|
return vs.leftMarginWidth;
|
|
|
|
case SCI_GETMARGINRIGHT:
|
|
return vs.rightMarginWidth;
|
|
|
|
case SCI_SETMARGINLEFT:
|
|
lastXChosen += static_cast<int>(lParam) - vs.leftMarginWidth;
|
|
vs.leftMarginWidth = static_cast<int>(lParam);
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
case SCI_SETMARGINRIGHT:
|
|
vs.rightMarginWidth = static_cast<int>(lParam);
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
// Control specific mesages
|
|
|
|
case SCI_ADDTEXT: {
|
|
if (lParam == 0)
|
|
return 0;
|
|
const int lengthInserted = pdoc->InsertString(
|
|
CurrentPosition(), CharPtrFromSPtr(lParam), static_cast<int>(wParam));
|
|
SetEmptySelection(sel.MainCaret() + lengthInserted);
|
|
return 0;
|
|
}
|
|
|
|
case SCI_ADDSTYLEDTEXT:
|
|
if (lParam)
|
|
AddStyledText(CharPtrFromSPtr(lParam), static_cast<int>(wParam));
|
|
return 0;
|
|
|
|
case SCI_INSERTTEXT: {
|
|
if (lParam == 0)
|
|
return 0;
|
|
int insertPos = static_cast<int>(wParam);
|
|
if (static_cast<int>(wParam) == -1)
|
|
insertPos = CurrentPosition();
|
|
int newCurrent = CurrentPosition();
|
|
char *sz = CharPtrFromSPtr(lParam);
|
|
const int lengthInserted = pdoc->InsertString(insertPos, sz, istrlen(sz));
|
|
if (newCurrent > insertPos)
|
|
newCurrent += lengthInserted;
|
|
SetEmptySelection(newCurrent);
|
|
return 0;
|
|
}
|
|
|
|
case SCI_CHANGEINSERTION:
|
|
PLATFORM_ASSERT(lParam);
|
|
pdoc->ChangeInsertion(CharPtrFromSPtr(lParam), static_cast<int>(wParam));
|
|
return 0;
|
|
|
|
case SCI_APPENDTEXT:
|
|
pdoc->InsertString(pdoc->Length(), CharPtrFromSPtr(lParam), static_cast<int>(wParam));
|
|
return 0;
|
|
|
|
case SCI_CLEARALL:
|
|
ClearAll();
|
|
return 0;
|
|
|
|
case SCI_DELETERANGE:
|
|
pdoc->DeleteChars(static_cast<int>(wParam), static_cast<int>(lParam));
|
|
return 0;
|
|
|
|
case SCI_CLEARDOCUMENTSTYLE:
|
|
ClearDocumentStyle();
|
|
return 0;
|
|
|
|
case SCI_SETUNDOCOLLECTION:
|
|
pdoc->SetUndoCollection(wParam != 0);
|
|
return 0;
|
|
|
|
case SCI_GETUNDOCOLLECTION:
|
|
return pdoc->IsCollectingUndo();
|
|
|
|
case SCI_BEGINUNDOACTION:
|
|
pdoc->BeginUndoAction();
|
|
return 0;
|
|
|
|
case SCI_ENDUNDOACTION:
|
|
pdoc->EndUndoAction();
|
|
return 0;
|
|
|
|
case SCI_GETCARETPERIOD:
|
|
return caret.period;
|
|
|
|
case SCI_SETCARETPERIOD:
|
|
CaretSetPeriod(static_cast<int>(wParam));
|
|
break;
|
|
|
|
case SCI_GETWORDCHARS:
|
|
return pdoc->GetCharsOfClass(CharClassify::ccWord, reinterpret_cast<unsigned char *>(lParam));
|
|
|
|
case SCI_SETWORDCHARS: {
|
|
pdoc->SetDefaultCharClasses(false);
|
|
if (lParam == 0)
|
|
return 0;
|
|
pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccWord);
|
|
}
|
|
break;
|
|
|
|
case SCI_GETWHITESPACECHARS:
|
|
return pdoc->GetCharsOfClass(CharClassify::ccSpace, reinterpret_cast<unsigned char *>(lParam));
|
|
|
|
case SCI_SETWHITESPACECHARS: {
|
|
if (lParam == 0)
|
|
return 0;
|
|
pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccSpace);
|
|
}
|
|
break;
|
|
|
|
case SCI_GETPUNCTUATIONCHARS:
|
|
return pdoc->GetCharsOfClass(CharClassify::ccPunctuation, reinterpret_cast<unsigned char *>(lParam));
|
|
|
|
case SCI_SETPUNCTUATIONCHARS: {
|
|
if (lParam == 0)
|
|
return 0;
|
|
pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccPunctuation);
|
|
}
|
|
break;
|
|
|
|
case SCI_SETCHARSDEFAULT:
|
|
pdoc->SetDefaultCharClasses(true);
|
|
break;
|
|
|
|
case SCI_GETLENGTH:
|
|
return pdoc->Length();
|
|
|
|
case SCI_ALLOCATE:
|
|
pdoc->Allocate(static_cast<int>(wParam));
|
|
break;
|
|
|
|
case SCI_GETCHARAT:
|
|
return pdoc->CharAt(static_cast<int>(wParam));
|
|
|
|
case SCI_SETCURRENTPOS:
|
|
if (sel.IsRectangular()) {
|
|
sel.Rectangular().caret.SetPosition(static_cast<int>(wParam));
|
|
SetRectangularRange();
|
|
Redraw();
|
|
} else {
|
|
SetSelection(static_cast<int>(wParam), sel.MainAnchor());
|
|
}
|
|
break;
|
|
|
|
case SCI_GETCURRENTPOS:
|
|
return sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret();
|
|
|
|
case SCI_SETANCHOR:
|
|
if (sel.IsRectangular()) {
|
|
sel.Rectangular().anchor.SetPosition(static_cast<int>(wParam));
|
|
SetRectangularRange();
|
|
Redraw();
|
|
} else {
|
|
SetSelection(sel.MainCaret(), static_cast<int>(wParam));
|
|
}
|
|
break;
|
|
|
|
case SCI_GETANCHOR:
|
|
return sel.IsRectangular() ? sel.Rectangular().anchor.Position() : sel.MainAnchor();
|
|
|
|
case SCI_SETSELECTIONSTART:
|
|
SetSelection(Platform::Maximum(sel.MainCaret(), static_cast<int>(wParam)), static_cast<int>(wParam));
|
|
break;
|
|
|
|
case SCI_GETSELECTIONSTART:
|
|
return sel.LimitsForRectangularElseMain().start.Position();
|
|
|
|
case SCI_SETSELECTIONEND:
|
|
SetSelection(static_cast<int>(wParam), Platform::Minimum(sel.MainAnchor(), static_cast<int>(wParam)));
|
|
break;
|
|
|
|
case SCI_GETSELECTIONEND:
|
|
return sel.LimitsForRectangularElseMain().end.Position();
|
|
|
|
case SCI_SETEMPTYSELECTION:
|
|
SetEmptySelection(static_cast<int>(wParam));
|
|
break;
|
|
|
|
case SCI_SETPRINTMAGNIFICATION:
|
|
printParameters.magnification = static_cast<int>(wParam);
|
|
break;
|
|
|
|
case SCI_GETPRINTMAGNIFICATION:
|
|
return printParameters.magnification;
|
|
|
|
case SCI_SETPRINTCOLOURMODE:
|
|
printParameters.colourMode = static_cast<int>(wParam);
|
|
break;
|
|
|
|
case SCI_GETPRINTCOLOURMODE:
|
|
return printParameters.colourMode;
|
|
|
|
case SCI_SETPRINTWRAPMODE:
|
|
printParameters.wrapState = (wParam == SC_WRAP_WORD) ? eWrapWord : eWrapNone;
|
|
break;
|
|
|
|
case SCI_GETPRINTWRAPMODE:
|
|
return printParameters.wrapState;
|
|
|
|
case SCI_GETSTYLEAT:
|
|
if (static_cast<int>(wParam) >= pdoc->Length())
|
|
return 0;
|
|
else
|
|
return pdoc->StyleAt(static_cast<int>(wParam));
|
|
|
|
case SCI_REDO:
|
|
Redo();
|
|
break;
|
|
|
|
case SCI_SELECTALL:
|
|
SelectAll();
|
|
break;
|
|
|
|
case SCI_SETSAVEPOINT:
|
|
pdoc->SetSavePoint();
|
|
break;
|
|
|
|
case SCI_GETSTYLEDTEXT: {
|
|
if (lParam == 0)
|
|
return 0;
|
|
Sci_TextRange *tr = reinterpret_cast<Sci_TextRange *>(lParam);
|
|
int iPlace = 0;
|
|
for (int iChar = tr->chrg.cpMin; iChar < tr->chrg.cpMax; iChar++) {
|
|
tr->lpstrText[iPlace++] = pdoc->CharAt(iChar);
|
|
tr->lpstrText[iPlace++] = pdoc->StyleAt(iChar);
|
|
}
|
|
tr->lpstrText[iPlace] = '\0';
|
|
tr->lpstrText[iPlace + 1] = '\0';
|
|
return iPlace;
|
|
}
|
|
|
|
case SCI_CANREDO:
|
|
return (pdoc->CanRedo() && !pdoc->IsReadOnly()) ? 1 : 0;
|
|
|
|
case SCI_MARKERLINEFROMHANDLE:
|
|
return pdoc->LineFromHandle(static_cast<int>(wParam));
|
|
|
|
case SCI_MARKERDELETEHANDLE:
|
|
pdoc->DeleteMarkFromHandle(static_cast<int>(wParam));
|
|
break;
|
|
|
|
case SCI_GETVIEWWS:
|
|
return vs.viewWhitespace;
|
|
|
|
case SCI_SETVIEWWS:
|
|
vs.viewWhitespace = static_cast<WhiteSpaceVisibility>(wParam);
|
|
Redraw();
|
|
break;
|
|
|
|
case SCI_GETWHITESPACESIZE:
|
|
return vs.whitespaceSize;
|
|
|
|
case SCI_SETWHITESPACESIZE:
|
|
vs.whitespaceSize = static_cast<int>(wParam);
|
|
Redraw();
|
|
break;
|
|
|
|
case SCI_POSITIONFROMPOINT:
|
|
return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
|
|
false, false);
|
|
|
|
case SCI_POSITIONFROMPOINTCLOSE:
|
|
return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
|
|
true, false);
|
|
|
|
case SCI_CHARPOSITIONFROMPOINT:
|
|
return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
|
|
false, true);
|
|
|
|
case SCI_CHARPOSITIONFROMPOINTCLOSE:
|
|
return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
|
|
true, true);
|
|
|
|
case SCI_GOTOLINE:
|
|
GoToLine(static_cast<int>(wParam));
|
|
break;
|
|
|
|
case SCI_GOTOPOS:
|
|
SetEmptySelection(static_cast<int>(wParam));
|
|
EnsureCaretVisible();
|
|
break;
|
|
|
|
case SCI_GETCURLINE: {
|
|
int lineCurrentPos = pdoc->LineFromPosition(sel.MainCaret());
|
|
int lineStart = pdoc->LineStart(lineCurrentPos);
|
|
unsigned int lineEnd = pdoc->LineStart(lineCurrentPos + 1);
|
|
if (lParam == 0) {
|
|
return 1 + lineEnd - lineStart;
|
|
}
|
|
PLATFORM_ASSERT(wParam > 0);
|
|
char *ptr = CharPtrFromSPtr(lParam);
|
|
unsigned int iPlace = 0;
|
|
for (unsigned int iChar = lineStart; iChar < lineEnd && iPlace < wParam - 1; iChar++) {
|
|
ptr[iPlace++] = pdoc->CharAt(iChar);
|
|
}
|
|
ptr[iPlace] = '\0';
|
|
return sel.MainCaret() - lineStart;
|
|
}
|
|
|
|
case SCI_GETENDSTYLED:
|
|
return pdoc->GetEndStyled();
|
|
|
|
case SCI_GETEOLMODE:
|
|
return pdoc->eolMode;
|
|
|
|
case SCI_SETEOLMODE:
|
|
pdoc->eolMode = static_cast<int>(wParam);
|
|
break;
|
|
|
|
case SCI_SETLINEENDTYPESALLOWED:
|
|
if (pdoc->SetLineEndTypesAllowed(static_cast<int>(wParam))) {
|
|
cs.Clear();
|
|
cs.InsertLines(0, pdoc->LinesTotal() - 1);
|
|
SetAnnotationHeights(0, pdoc->LinesTotal());
|
|
InvalidateStyleRedraw();
|
|
}
|
|
break;
|
|
|
|
case SCI_GETLINEENDTYPESALLOWED:
|
|
return pdoc->GetLineEndTypesAllowed();
|
|
|
|
case SCI_GETLINEENDTYPESACTIVE:
|
|
return pdoc->GetLineEndTypesActive();
|
|
|
|
case SCI_STARTSTYLING:
|
|
pdoc->StartStyling(static_cast<int>(wParam), static_cast<char>(lParam));
|
|
break;
|
|
|
|
case SCI_SETSTYLING:
|
|
pdoc->SetStyleFor(static_cast<int>(wParam), static_cast<char>(lParam));
|
|
break;
|
|
|
|
case SCI_SETSTYLINGEX: // Specify a complete styling buffer
|
|
if (lParam == 0)
|
|
return 0;
|
|
pdoc->SetStyles(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
|
|
break;
|
|
|
|
case SCI_SETBUFFEREDDRAW:
|
|
bufferedDraw = wParam != 0;
|
|
break;
|
|
|
|
case SCI_GETBUFFEREDDRAW:
|
|
return bufferedDraw;
|
|
|
|
case SCI_GETTWOPHASEDRAW:
|
|
return twoPhaseDraw;
|
|
|
|
case SCI_SETTWOPHASEDRAW:
|
|
twoPhaseDraw = wParam != 0;
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
case SCI_SETFONTQUALITY:
|
|
vs.extraFontFlag &= ~SC_EFF_QUALITY_MASK;
|
|
vs.extraFontFlag |= (wParam & SC_EFF_QUALITY_MASK);
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
case SCI_GETFONTQUALITY:
|
|
return (vs.extraFontFlag & SC_EFF_QUALITY_MASK);
|
|
|
|
case SCI_SETTABWIDTH:
|
|
if (wParam > 0) {
|
|
pdoc->tabInChars = static_cast<int>(wParam);
|
|
if (pdoc->indentInChars == 0)
|
|
pdoc->actualIndentInChars = pdoc->tabInChars;
|
|
}
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
case SCI_GETTABWIDTH:
|
|
return pdoc->tabInChars;
|
|
|
|
case SCI_SETINDENT:
|
|
pdoc->indentInChars = static_cast<int>(wParam);
|
|
if (pdoc->indentInChars != 0)
|
|
pdoc->actualIndentInChars = pdoc->indentInChars;
|
|
else
|
|
pdoc->actualIndentInChars = pdoc->tabInChars;
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
case SCI_GETINDENT:
|
|
return pdoc->indentInChars;
|
|
|
|
case SCI_SETUSETABS:
|
|
pdoc->useTabs = wParam != 0;
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
case SCI_GETUSETABS:
|
|
return pdoc->useTabs;
|
|
|
|
case SCI_SETLINEINDENTATION:
|
|
pdoc->SetLineIndentation(static_cast<int>(wParam), static_cast<int>(lParam));
|
|
break;
|
|
|
|
case SCI_GETLINEINDENTATION:
|
|
return pdoc->GetLineIndentation(static_cast<int>(wParam));
|
|
|
|
case SCI_GETLINEINDENTPOSITION:
|
|
return pdoc->GetLineIndentPosition(static_cast<int>(wParam));
|
|
|
|
case SCI_SETTABINDENTS:
|
|
pdoc->tabIndents = wParam != 0;
|
|
break;
|
|
|
|
case SCI_GETTABINDENTS:
|
|
return pdoc->tabIndents;
|
|
|
|
case SCI_SETBACKSPACEUNINDENTS:
|
|
pdoc->backspaceUnindents = wParam != 0;
|
|
break;
|
|
|
|
case SCI_GETBACKSPACEUNINDENTS:
|
|
return pdoc->backspaceUnindents;
|
|
|
|
case SCI_SETMOUSEDWELLTIME:
|
|
dwellDelay = static_cast<int>(wParam);
|
|
ticksToDwell = dwellDelay;
|
|
break;
|
|
|
|
case SCI_GETMOUSEDWELLTIME:
|
|
return dwellDelay;
|
|
|
|
case SCI_WORDSTARTPOSITION:
|
|
return pdoc->ExtendWordSelect(static_cast<int>(wParam), -1, lParam != 0);
|
|
|
|
case SCI_WORDENDPOSITION:
|
|
return pdoc->ExtendWordSelect(static_cast<int>(wParam), 1, lParam != 0);
|
|
|
|
case SCI_SETWRAPMODE:
|
|
if (vs.SetWrapState(static_cast<int>(wParam))) {
|
|
xOffset = 0;
|
|
ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
|
|
InvalidateStyleRedraw();
|
|
ReconfigureScrollBars();
|
|
}
|
|
break;
|
|
|
|
case SCI_GETWRAPMODE:
|
|
return vs.wrapState;
|
|
|
|
case SCI_SETWRAPVISUALFLAGS:
|
|
if (vs.SetWrapVisualFlags(static_cast<int>(wParam))) {
|
|
InvalidateStyleRedraw();
|
|
ReconfigureScrollBars();
|
|
}
|
|
break;
|
|
|
|
case SCI_GETWRAPVISUALFLAGS:
|
|
return vs.wrapVisualFlags;
|
|
|
|
case SCI_SETWRAPVISUALFLAGSLOCATION:
|
|
if (vs.SetWrapVisualFlagsLocation(static_cast<int>(wParam))) {
|
|
InvalidateStyleRedraw();
|
|
}
|
|
break;
|
|
|
|
case SCI_GETWRAPVISUALFLAGSLOCATION:
|
|
return vs.wrapVisualFlagsLocation;
|
|
|
|
case SCI_SETWRAPSTARTINDENT:
|
|
if (vs.SetWrapVisualStartIndent(static_cast<int>(wParam))) {
|
|
InvalidateStyleRedraw();
|
|
ReconfigureScrollBars();
|
|
}
|
|
break;
|
|
|
|
case SCI_GETWRAPSTARTINDENT:
|
|
return vs.wrapVisualStartIndent;
|
|
|
|
case SCI_SETWRAPINDENTMODE:
|
|
if (vs.SetWrapIndentMode(static_cast<int>(wParam))) {
|
|
InvalidateStyleRedraw();
|
|
ReconfigureScrollBars();
|
|
}
|
|
break;
|
|
|
|
case SCI_GETWRAPINDENTMODE:
|
|
return vs.wrapIndentMode;
|
|
|
|
case SCI_SETLAYOUTCACHE:
|
|
llc.SetLevel(static_cast<int>(wParam));
|
|
break;
|
|
|
|
case SCI_GETLAYOUTCACHE:
|
|
return llc.GetLevel();
|
|
|
|
case SCI_SETPOSITIONCACHE:
|
|
posCache.SetSize(wParam);
|
|
break;
|
|
|
|
case SCI_GETPOSITIONCACHE:
|
|
return posCache.GetSize();
|
|
|
|
case SCI_SETSCROLLWIDTH:
|
|
PLATFORM_ASSERT(wParam > 0);
|
|
if ((wParam > 0) && (wParam != static_cast<unsigned int >(scrollWidth))) {
|
|
lineWidthMaxSeen = 0;
|
|
scrollWidth = static_cast<int>(wParam);
|
|
SetScrollBars();
|
|
}
|
|
break;
|
|
|
|
case SCI_GETSCROLLWIDTH:
|
|
return scrollWidth;
|
|
|
|
case SCI_SETSCROLLWIDTHTRACKING:
|
|
trackLineWidth = wParam != 0;
|
|
break;
|
|
|
|
case SCI_GETSCROLLWIDTHTRACKING:
|
|
return trackLineWidth;
|
|
|
|
case SCI_LINESJOIN:
|
|
LinesJoin();
|
|
break;
|
|
|
|
case SCI_LINESSPLIT:
|
|
LinesSplit(static_cast<int>(wParam));
|
|
break;
|
|
|
|
case SCI_TEXTWIDTH:
|
|
PLATFORM_ASSERT(wParam < vs.styles.size());
|
|
PLATFORM_ASSERT(lParam);
|
|
return TextWidth(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
|
|
|
|
case SCI_TEXTHEIGHT:
|
|
return vs.lineHeight;
|
|
|
|
case SCI_SETENDATLASTLINE:
|
|
PLATFORM_ASSERT((wParam == 0) || (wParam == 1));
|
|
if (endAtLastLine != (wParam != 0)) {
|
|
endAtLastLine = wParam != 0;
|
|
SetScrollBars();
|
|
}
|
|
break;
|
|
|
|
case SCI_GETENDATLASTLINE:
|
|
return endAtLastLine;
|
|
|
|
case SCI_SETCARETSTICKY:
|
|
PLATFORM_ASSERT(wParam <= SC_CARETSTICKY_WHITESPACE);
|
|
if (wParam <= SC_CARETSTICKY_WHITESPACE) {
|
|
caretSticky = static_cast<int>(wParam);
|
|
}
|
|
break;
|
|
|
|
case SCI_GETCARETSTICKY:
|
|
return caretSticky;
|
|
|
|
case SCI_TOGGLECARETSTICKY:
|
|
caretSticky = !caretSticky;
|
|
break;
|
|
|
|
case SCI_GETCOLUMN:
|
|
return pdoc->GetColumn(static_cast<int>(wParam));
|
|
|
|
case SCI_FINDCOLUMN:
|
|
return pdoc->FindColumn(static_cast<int>(wParam), static_cast<int>(lParam));
|
|
|
|
case SCI_SETHSCROLLBAR :
|
|
if (horizontalScrollBarVisible != (wParam != 0)) {
|
|
horizontalScrollBarVisible = wParam != 0;
|
|
SetScrollBars();
|
|
ReconfigureScrollBars();
|
|
}
|
|
break;
|
|
|
|
case SCI_GETHSCROLLBAR:
|
|
return horizontalScrollBarVisible;
|
|
|
|
case SCI_SETVSCROLLBAR:
|
|
if (verticalScrollBarVisible != (wParam != 0)) {
|
|
verticalScrollBarVisible = wParam != 0;
|
|
SetScrollBars();
|
|
ReconfigureScrollBars();
|
|
if (verticalScrollBarVisible)
|
|
SetVerticalScrollPos();
|
|
}
|
|
break;
|
|
|
|
case SCI_GETVSCROLLBAR:
|
|
return verticalScrollBarVisible;
|
|
|
|
case SCI_SETINDENTATIONGUIDES:
|
|
vs.viewIndentationGuides = IndentView(wParam);
|
|
Redraw();
|
|
break;
|
|
|
|
case SCI_GETINDENTATIONGUIDES:
|
|
return vs.viewIndentationGuides;
|
|
|
|
case SCI_SETHIGHLIGHTGUIDE:
|
|
if ((highlightGuideColumn != static_cast<int>(wParam)) || (wParam > 0)) {
|
|
highlightGuideColumn = static_cast<int>(wParam);
|
|
Redraw();
|
|
}
|
|
break;
|
|
|
|
case SCI_GETHIGHLIGHTGUIDE:
|
|
return highlightGuideColumn;
|
|
|
|
case SCI_GETLINEENDPOSITION:
|
|
return pdoc->LineEnd(static_cast<int>(wParam));
|
|
|
|
case SCI_SETCODEPAGE:
|
|
if (ValidCodePage(static_cast<int>(wParam))) {
|
|
if (pdoc->SetDBCSCodePage(static_cast<int>(wParam))) {
|
|
cs.Clear();
|
|
cs.InsertLines(0, pdoc->LinesTotal() - 1);
|
|
SetAnnotationHeights(0, pdoc->LinesTotal());
|
|
InvalidateStyleRedraw();
|
|
SetRepresentations();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SCI_GETCODEPAGE:
|
|
return pdoc->dbcsCodePage;
|
|
|
|
#ifdef INCLUDE_DEPRECATED_FEATURES
|
|
case SCI_SETUSEPALETTE:
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
case SCI_GETUSEPALETTE:
|
|
return 0;
|
|
#endif
|
|
|
|
// Marker definition and setting
|
|
case SCI_MARKERDEFINE:
|
|
if (wParam <= MARKER_MAX) {
|
|
vs.markers[wParam].markType = static_cast<int>(lParam);
|
|
vs.CalcLargestMarkerHeight();
|
|
}
|
|
InvalidateStyleData();
|
|
RedrawSelMargin();
|
|
break;
|
|
|
|
case SCI_MARKERSYMBOLDEFINED:
|
|
if (wParam <= MARKER_MAX)
|
|
return vs.markers[wParam].markType;
|
|
else
|
|
return 0;
|
|
|
|
case SCI_MARKERSETFORE:
|
|
if (wParam <= MARKER_MAX)
|
|
vs.markers[wParam].fore = ColourDesired(static_cast<long>(lParam));
|
|
InvalidateStyleData();
|
|
RedrawSelMargin();
|
|
break;
|
|
case SCI_MARKERSETBACKSELECTED:
|
|
if (wParam <= MARKER_MAX)
|
|
vs.markers[wParam].backSelected = ColourDesired(static_cast<long>(lParam));
|
|
InvalidateStyleData();
|
|
RedrawSelMargin();
|
|
break;
|
|
case SCI_MARKERENABLEHIGHLIGHT:
|
|
highlightDelimiter.isEnabled = wParam == 1;
|
|
RedrawSelMargin();
|
|
break;
|
|
case SCI_MARKERSETBACK:
|
|
if (wParam <= MARKER_MAX)
|
|
vs.markers[wParam].back = ColourDesired(static_cast<long>(lParam));
|
|
InvalidateStyleData();
|
|
RedrawSelMargin();
|
|
break;
|
|
case SCI_MARKERSETALPHA:
|
|
if (wParam <= MARKER_MAX)
|
|
vs.markers[wParam].alpha = static_cast<int>(lParam);
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
case SCI_MARKERADD: {
|
|
int markerID = pdoc->AddMark(static_cast<int>(wParam), static_cast<int>(lParam));
|
|
return markerID;
|
|
}
|
|
case SCI_MARKERADDSET:
|
|
if (lParam != 0)
|
|
pdoc->AddMarkSet(static_cast<int>(wParam), static_cast<int>(lParam));
|
|
break;
|
|
|
|
case SCI_MARKERDELETE:
|
|
pdoc->DeleteMark(static_cast<int>(wParam), static_cast<int>(lParam));
|
|
break;
|
|
|
|
case SCI_MARKERDELETEALL:
|
|
pdoc->DeleteAllMarks(static_cast<int>(wParam));
|
|
break;
|
|
|
|
case SCI_MARKERGET:
|
|
return pdoc->GetMark(static_cast<int>(wParam));
|
|
|
|
case SCI_MARKERNEXT:
|
|
return pdoc->MarkerNext(static_cast<int>(wParam), static_cast<int>(lParam));
|
|
|
|
case SCI_MARKERPREVIOUS: {
|
|
for (int iLine = static_cast<int>(wParam); iLine >= 0; iLine--) {
|
|
if ((pdoc->GetMark(iLine) & lParam) != 0)
|
|
return iLine;
|
|
}
|
|
}
|
|
return -1;
|
|
|
|
case SCI_MARKERDEFINEPIXMAP:
|
|
if (wParam <= MARKER_MAX) {
|
|
vs.markers[wParam].SetXPM(CharPtrFromSPtr(lParam));
|
|
vs.CalcLargestMarkerHeight();
|
|
}
|
|
InvalidateStyleData();
|
|
RedrawSelMargin();
|
|
break;
|
|
|
|
case SCI_RGBAIMAGESETWIDTH:
|
|
sizeRGBAImage.x = static_cast<XYPOSITION>(wParam);
|
|
break;
|
|
|
|
case SCI_RGBAIMAGESETHEIGHT:
|
|
sizeRGBAImage.y = static_cast<XYPOSITION>(wParam);
|
|
break;
|
|
|
|
case SCI_RGBAIMAGESETSCALE:
|
|
scaleRGBAImage = static_cast<float>(wParam);
|
|
break;
|
|
|
|
case SCI_MARKERDEFINERGBAIMAGE:
|
|
if (wParam <= MARKER_MAX) {
|
|
vs.markers[wParam].SetRGBAImage(sizeRGBAImage, scaleRGBAImage / 100.0f, reinterpret_cast<unsigned char *>(lParam));
|
|
vs.CalcLargestMarkerHeight();
|
|
}
|
|
InvalidateStyleData();
|
|
RedrawSelMargin();
|
|
break;
|
|
|
|
case SCI_SETMARGINTYPEN:
|
|
if (ValidMargin(wParam)) {
|
|
vs.ms[wParam].style = static_cast<int>(lParam);
|
|
InvalidateStyleRedraw();
|
|
}
|
|
break;
|
|
|
|
case SCI_GETMARGINTYPEN:
|
|
if (ValidMargin(wParam))
|
|
return vs.ms[wParam].style;
|
|
else
|
|
return 0;
|
|
|
|
case SCI_SETMARGINWIDTHN:
|
|
if (ValidMargin(wParam)) {
|
|
// Short-circuit if the width is unchanged, to avoid unnecessary redraw.
|
|
if (vs.ms[wParam].width != lParam) {
|
|
lastXChosen += static_cast<int>(lParam) - vs.ms[wParam].width;
|
|
vs.ms[wParam].width = static_cast<int>(lParam);
|
|
InvalidateStyleRedraw();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SCI_GETMARGINWIDTHN:
|
|
if (ValidMargin(wParam))
|
|
return vs.ms[wParam].width;
|
|
else
|
|
return 0;
|
|
|
|
case SCI_SETMARGINMASKN:
|
|
if (ValidMargin(wParam)) {
|
|
vs.ms[wParam].mask = static_cast<int>(lParam);
|
|
InvalidateStyleRedraw();
|
|
}
|
|
break;
|
|
|
|
case SCI_GETMARGINMASKN:
|
|
if (ValidMargin(wParam))
|
|
return vs.ms[wParam].mask;
|
|
else
|
|
return 0;
|
|
|
|
case SCI_SETMARGINSENSITIVEN:
|
|
if (ValidMargin(wParam)) {
|
|
vs.ms[wParam].sensitive = lParam != 0;
|
|
InvalidateStyleRedraw();
|
|
}
|
|
break;
|
|
|
|
case SCI_GETMARGINSENSITIVEN:
|
|
if (ValidMargin(wParam))
|
|
return vs.ms[wParam].sensitive ? 1 : 0;
|
|
else
|
|
return 0;
|
|
|
|
case SCI_SETMARGINCURSORN:
|
|
if (ValidMargin(wParam))
|
|
vs.ms[wParam].cursor = static_cast<int>(lParam);
|
|
break;
|
|
|
|
case SCI_GETMARGINCURSORN:
|
|
if (ValidMargin(wParam))
|
|
return vs.ms[wParam].cursor;
|
|
else
|
|
return 0;
|
|
|
|
case SCI_STYLECLEARALL:
|
|
vs.ClearStyles();
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
case SCI_STYLESETFORE:
|
|
case SCI_STYLESETBACK:
|
|
case SCI_STYLESETBOLD:
|
|
case SCI_STYLESETWEIGHT:
|
|
case SCI_STYLESETITALIC:
|
|
case SCI_STYLESETEOLFILLED:
|
|
case SCI_STYLESETSIZE:
|
|
case SCI_STYLESETSIZEFRACTIONAL:
|
|
case SCI_STYLESETFONT:
|
|
case SCI_STYLESETUNDERLINE:
|
|
case SCI_STYLESETCASE:
|
|
case SCI_STYLESETCHARACTERSET:
|
|
case SCI_STYLESETVISIBLE:
|
|
case SCI_STYLESETCHANGEABLE:
|
|
case SCI_STYLESETHOTSPOT:
|
|
StyleSetMessage(iMessage, wParam, lParam);
|
|
break;
|
|
|
|
case SCI_STYLEGETFORE:
|
|
case SCI_STYLEGETBACK:
|
|
case SCI_STYLEGETBOLD:
|
|
case SCI_STYLEGETWEIGHT:
|
|
case SCI_STYLEGETITALIC:
|
|
case SCI_STYLEGETEOLFILLED:
|
|
case SCI_STYLEGETSIZE:
|
|
case SCI_STYLEGETSIZEFRACTIONAL:
|
|
case SCI_STYLEGETFONT:
|
|
case SCI_STYLEGETUNDERLINE:
|
|
case SCI_STYLEGETCASE:
|
|
case SCI_STYLEGETCHARACTERSET:
|
|
case SCI_STYLEGETVISIBLE:
|
|
case SCI_STYLEGETCHANGEABLE:
|
|
case SCI_STYLEGETHOTSPOT:
|
|
return StyleGetMessage(iMessage, wParam, lParam);
|
|
|
|
case SCI_STYLERESETDEFAULT:
|
|
vs.ResetDefaultStyle();
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
case SCI_SETSTYLEBITS:
|
|
vs.EnsureStyle((1 << wParam) - 1);
|
|
pdoc->SetStylingBits(static_cast<int>(wParam));
|
|
break;
|
|
|
|
case SCI_GETSTYLEBITS:
|
|
return pdoc->stylingBits;
|
|
|
|
case SCI_SETLINESTATE:
|
|
return pdoc->SetLineState(static_cast<int>(wParam), static_cast<int>(lParam));
|
|
|
|
case SCI_GETLINESTATE:
|
|
return pdoc->GetLineState(static_cast<int>(wParam));
|
|
|
|
case SCI_GETMAXLINESTATE:
|
|
return pdoc->GetMaxLineState();
|
|
|
|
case SCI_GETCARETLINEVISIBLE:
|
|
return vs.showCaretLineBackground;
|
|
case SCI_SETCARETLINEVISIBLE:
|
|
vs.showCaretLineBackground = wParam != 0;
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
case SCI_GETCARETLINEVISIBLEALWAYS:
|
|
return vs.alwaysShowCaretLineBackground;
|
|
case SCI_SETCARETLINEVISIBLEALWAYS:
|
|
vs.alwaysShowCaretLineBackground = wParam != 0;
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
case SCI_GETCARETLINEBACK:
|
|
return vs.caretLineBackground.AsLong();
|
|
case SCI_SETCARETLINEBACK:
|
|
vs.caretLineBackground = static_cast<int>(wParam);
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
case SCI_GETCARETLINEBACKALPHA:
|
|
return vs.caretLineAlpha;
|
|
case SCI_SETCARETLINEBACKALPHA:
|
|
vs.caretLineAlpha = static_cast<int>(wParam);
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
// Folding messages
|
|
|
|
case SCI_VISIBLEFROMDOCLINE:
|
|
return cs.DisplayFromDoc(static_cast<int>(wParam));
|
|
|
|
case SCI_DOCLINEFROMVISIBLE:
|
|
return cs.DocFromDisplay(static_cast<int>(wParam));
|
|
|
|
case SCI_WRAPCOUNT:
|
|
return WrapCount(static_cast<int>(wParam));
|
|
|
|
case SCI_SETFOLDLEVEL: {
|
|
int prev = pdoc->SetLevel(static_cast<int>(wParam), static_cast<int>(lParam));
|
|
if (prev != static_cast<int>(lParam))
|
|
RedrawSelMargin();
|
|
return prev;
|
|
}
|
|
|
|
case SCI_GETFOLDLEVEL:
|
|
return pdoc->GetLevel(static_cast<int>(wParam));
|
|
|
|
case SCI_GETLASTCHILD:
|
|
return pdoc->GetLastChild(static_cast<int>(wParam), static_cast<int>(lParam));
|
|
|
|
case SCI_GETFOLDPARENT:
|
|
return pdoc->GetFoldParent(static_cast<int>(wParam));
|
|
|
|
case SCI_SHOWLINES:
|
|
cs.SetVisible(static_cast<int>(wParam), static_cast<int>(lParam), true);
|
|
SetScrollBars();
|
|
Redraw();
|
|
break;
|
|
|
|
case SCI_HIDELINES:
|
|
if (wParam > 0)
|
|
cs.SetVisible(static_cast<int>(wParam), static_cast<int>(lParam), false);
|
|
SetScrollBars();
|
|
Redraw();
|
|
break;
|
|
|
|
case SCI_GETLINEVISIBLE:
|
|
return cs.GetVisible(static_cast<int>(wParam));
|
|
|
|
case SCI_GETALLLINESVISIBLE:
|
|
return cs.HiddenLines() ? 0 : 1;
|
|
|
|
case SCI_SETFOLDEXPANDED:
|
|
SetFoldExpanded(static_cast<int>(wParam), lParam != 0);
|
|
break;
|
|
|
|
case SCI_GETFOLDEXPANDED:
|
|
return cs.GetExpanded(static_cast<int>(wParam));
|
|
|
|
case SCI_SETAUTOMATICFOLD:
|
|
foldAutomatic = static_cast<int>(wParam);
|
|
break;
|
|
|
|
case SCI_GETAUTOMATICFOLD:
|
|
return foldAutomatic;
|
|
|
|
case SCI_SETFOLDFLAGS:
|
|
foldFlags = static_cast<int>(wParam);
|
|
Redraw();
|
|
break;
|
|
|
|
case SCI_TOGGLEFOLD:
|
|
FoldLine(static_cast<int>(wParam), SC_FOLDACTION_TOGGLE);
|
|
break;
|
|
|
|
case SCI_FOLDLINE:
|
|
FoldLine(static_cast<int>(wParam), static_cast<int>(lParam));
|
|
break;
|
|
|
|
case SCI_FOLDCHILDREN:
|
|
FoldExpand(static_cast<int>(wParam), static_cast<int>(lParam), pdoc->GetLevel(static_cast<int>(wParam)));
|
|
break;
|
|
|
|
case SCI_FOLDALL:
|
|
FoldAll(static_cast<int>(wParam));
|
|
break;
|
|
|
|
case SCI_EXPANDCHILDREN:
|
|
FoldExpand(static_cast<int>(wParam), SC_FOLDACTION_EXPAND, static_cast<int>(lParam));
|
|
break;
|
|
|
|
case SCI_CONTRACTEDFOLDNEXT:
|
|
return ContractedFoldNext(static_cast<int>(wParam));
|
|
|
|
case SCI_ENSUREVISIBLE:
|
|
EnsureLineVisible(static_cast<int>(wParam), false);
|
|
break;
|
|
|
|
case SCI_ENSUREVISIBLEENFORCEPOLICY:
|
|
EnsureLineVisible(static_cast<int>(wParam), true);
|
|
break;
|
|
|
|
case SCI_SCROLLRANGE:
|
|
ScrollRange(SelectionRange(static_cast<int>(wParam), static_cast<int>(lParam)));
|
|
break;
|
|
|
|
case SCI_SEARCHANCHOR:
|
|
SearchAnchor();
|
|
break;
|
|
|
|
case SCI_SEARCHNEXT:
|
|
case SCI_SEARCHPREV:
|
|
return SearchText(iMessage, wParam, lParam);
|
|
|
|
case SCI_SETXCARETPOLICY:
|
|
caretXPolicy = static_cast<int>(wParam);
|
|
caretXSlop = static_cast<int>(lParam);
|
|
break;
|
|
|
|
case SCI_SETYCARETPOLICY:
|
|
caretYPolicy = static_cast<int>(wParam);
|
|
caretYSlop = static_cast<int>(lParam);
|
|
break;
|
|
|
|
case SCI_SETVISIBLEPOLICY:
|
|
visiblePolicy = static_cast<int>(wParam);
|
|
visibleSlop = static_cast<int>(lParam);
|
|
break;
|
|
|
|
case SCI_LINESONSCREEN:
|
|
return LinesOnScreen();
|
|
|
|
case SCI_SETSELFORE:
|
|
vs.selColours.fore = ColourOptional(wParam, lParam);
|
|
vs.selAdditionalForeground = ColourDesired(static_cast<long>(lParam));
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
case SCI_SETSELBACK:
|
|
vs.selColours.back = ColourOptional(wParam, lParam);
|
|
vs.selAdditionalBackground = ColourDesired(static_cast<long>(lParam));
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
case SCI_SETSELALPHA:
|
|
vs.selAlpha = static_cast<int>(wParam);
|
|
vs.selAdditionalAlpha = static_cast<int>(wParam);
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
case SCI_GETSELALPHA:
|
|
return vs.selAlpha;
|
|
|
|
case SCI_GETSELEOLFILLED:
|
|
return vs.selEOLFilled;
|
|
|
|
case SCI_SETSELEOLFILLED:
|
|
vs.selEOLFilled = wParam != 0;
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
case SCI_SETWHITESPACEFORE:
|
|
vs.whitespaceColours.fore = ColourOptional(wParam, lParam);
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
case SCI_SETWHITESPACEBACK:
|
|
vs.whitespaceColours.back = ColourOptional(wParam, lParam);
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
case SCI_SETCARETFORE:
|
|
vs.caretcolour = ColourDesired(static_cast<long>(wParam));
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
case SCI_GETCARETFORE:
|
|
return vs.caretcolour.AsLong();
|
|
|
|
case SCI_SETCARETSTYLE:
|
|
if (wParam <= CARETSTYLE_BLOCK)
|
|
vs.caretStyle = static_cast<int>(wParam);
|
|
else
|
|
/* Default to the line caret */
|
|
vs.caretStyle = CARETSTYLE_LINE;
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
case SCI_GETCARETSTYLE:
|
|
return vs.caretStyle;
|
|
|
|
case SCI_SETCARETWIDTH:
|
|
if (static_cast<int>(wParam) <= 0)
|
|
vs.caretWidth = 0;
|
|
else if (wParam >= 3)
|
|
vs.caretWidth = 3;
|
|
else
|
|
vs.caretWidth = static_cast<int>(wParam);
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
case SCI_GETCARETWIDTH:
|
|
return vs.caretWidth;
|
|
|
|
case SCI_ASSIGNCMDKEY:
|
|
kmap.AssignCmdKey(Platform::LowShortFromLong(static_cast<long>(wParam)),
|
|
Platform::HighShortFromLong(static_cast<long>(wParam)), static_cast<unsigned int>(lParam));
|
|
break;
|
|
|
|
case SCI_CLEARCMDKEY:
|
|
kmap.AssignCmdKey(Platform::LowShortFromLong(static_cast<long>(wParam)),
|
|
Platform::HighShortFromLong(static_cast<long>(wParam)), SCI_NULL);
|
|
break;
|
|
|
|
case SCI_CLEARALLCMDKEYS:
|
|
kmap.Clear();
|
|
break;
|
|
|
|
case SCI_INDICSETSTYLE:
|
|
if (wParam <= INDIC_MAX) {
|
|
vs.indicators[wParam].style = static_cast<int>(lParam);
|
|
InvalidateStyleRedraw();
|
|
}
|
|
break;
|
|
|
|
case SCI_INDICGETSTYLE:
|
|
return (wParam <= INDIC_MAX) ? vs.indicators[wParam].style : 0;
|
|
|
|
case SCI_INDICSETFORE:
|
|
if (wParam <= INDIC_MAX) {
|
|
vs.indicators[wParam].fore = ColourDesired(static_cast<long>(lParam));
|
|
InvalidateStyleRedraw();
|
|
}
|
|
break;
|
|
|
|
case SCI_INDICGETFORE:
|
|
return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fore.AsLong() : 0;
|
|
|
|
case SCI_INDICSETUNDER:
|
|
if (wParam <= INDIC_MAX) {
|
|
vs.indicators[wParam].under = lParam != 0;
|
|
InvalidateStyleRedraw();
|
|
}
|
|
break;
|
|
|
|
case SCI_INDICGETUNDER:
|
|
return (wParam <= INDIC_MAX) ? vs.indicators[wParam].under : 0;
|
|
|
|
case SCI_INDICSETALPHA:
|
|
if (wParam <= INDIC_MAX && lParam >=0 && lParam <= 255) {
|
|
vs.indicators[wParam].fillAlpha = static_cast<int>(lParam);
|
|
InvalidateStyleRedraw();
|
|
}
|
|
break;
|
|
|
|
case SCI_INDICGETALPHA:
|
|
return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fillAlpha : 0;
|
|
|
|
case SCI_INDICSETOUTLINEALPHA:
|
|
if (wParam <= INDIC_MAX && lParam >=0 && lParam <= 255) {
|
|
vs.indicators[wParam].outlineAlpha = static_cast<int>(lParam);
|
|
InvalidateStyleRedraw();
|
|
}
|
|
break;
|
|
|
|
case SCI_INDICGETOUTLINEALPHA:
|
|
return (wParam <= INDIC_MAX) ? vs.indicators[wParam].outlineAlpha : 0;
|
|
|
|
case SCI_SETINDICATORCURRENT:
|
|
pdoc->decorations.SetCurrentIndicator(static_cast<int>(wParam));
|
|
break;
|
|
case SCI_GETINDICATORCURRENT:
|
|
return pdoc->decorations.GetCurrentIndicator();
|
|
case SCI_SETINDICATORVALUE:
|
|
pdoc->decorations.SetCurrentValue(static_cast<int>(wParam));
|
|
break;
|
|
case SCI_GETINDICATORVALUE:
|
|
return pdoc->decorations.GetCurrentValue();
|
|
|
|
case SCI_INDICATORFILLRANGE:
|
|
pdoc->DecorationFillRange(static_cast<int>(wParam), pdoc->decorations.GetCurrentValue(), static_cast<int>(lParam));
|
|
break;
|
|
|
|
case SCI_INDICATORCLEARRANGE:
|
|
pdoc->DecorationFillRange(static_cast<int>(wParam), 0, static_cast<int>(lParam));
|
|
break;
|
|
|
|
case SCI_INDICATORALLONFOR:
|
|
return pdoc->decorations.AllOnFor(static_cast<int>(wParam));
|
|
|
|
case SCI_INDICATORVALUEAT:
|
|
return pdoc->decorations.ValueAt(static_cast<int>(wParam), static_cast<int>(lParam));
|
|
|
|
case SCI_INDICATORSTART:
|
|
return pdoc->decorations.Start(static_cast<int>(wParam), static_cast<int>(lParam));
|
|
|
|
case SCI_INDICATOREND:
|
|
return pdoc->decorations.End(static_cast<int>(wParam), static_cast<int>(lParam));
|
|
|
|
case SCI_LINEDOWN:
|
|
case SCI_LINEDOWNEXTEND:
|
|
case SCI_PARADOWN:
|
|
case SCI_PARADOWNEXTEND:
|
|
case SCI_LINEUP:
|
|
case SCI_LINEUPEXTEND:
|
|
case SCI_PARAUP:
|
|
case SCI_PARAUPEXTEND:
|
|
case SCI_CHARLEFT:
|
|
case SCI_CHARLEFTEXTEND:
|
|
case SCI_CHARRIGHT:
|
|
case SCI_CHARRIGHTEXTEND:
|
|
case SCI_WORDLEFT:
|
|
case SCI_WORDLEFTEXTEND:
|
|
case SCI_WORDRIGHT:
|
|
case SCI_WORDRIGHTEXTEND:
|
|
case SCI_WORDLEFTEND:
|
|
case SCI_WORDLEFTENDEXTEND:
|
|
case SCI_WORDRIGHTEND:
|
|
case SCI_WORDRIGHTENDEXTEND:
|
|
case SCI_HOME:
|
|
case SCI_HOMEEXTEND:
|
|
case SCI_LINEEND:
|
|
case SCI_LINEENDEXTEND:
|
|
case SCI_HOMEWRAP:
|
|
case SCI_HOMEWRAPEXTEND:
|
|
case SCI_LINEENDWRAP:
|
|
case SCI_LINEENDWRAPEXTEND:
|
|
case SCI_DOCUMENTSTART:
|
|
case SCI_DOCUMENTSTARTEXTEND:
|
|
case SCI_DOCUMENTEND:
|
|
case SCI_DOCUMENTENDEXTEND:
|
|
case SCI_SCROLLTOSTART:
|
|
case SCI_SCROLLTOEND:
|
|
|
|
case SCI_STUTTEREDPAGEUP:
|
|
case SCI_STUTTEREDPAGEUPEXTEND:
|
|
case SCI_STUTTEREDPAGEDOWN:
|
|
case SCI_STUTTEREDPAGEDOWNEXTEND:
|
|
|
|
case SCI_PAGEUP:
|
|
case SCI_PAGEUPEXTEND:
|
|
case SCI_PAGEDOWN:
|
|
case SCI_PAGEDOWNEXTEND:
|
|
case SCI_EDITTOGGLEOVERTYPE:
|
|
case SCI_CANCEL:
|
|
case SCI_DELETEBACK:
|
|
case SCI_TAB:
|
|
case SCI_BACKTAB:
|
|
case SCI_NEWLINE:
|
|
case SCI_FORMFEED:
|
|
case SCI_VCHOME:
|
|
case SCI_VCHOMEEXTEND:
|
|
case SCI_VCHOMEWRAP:
|
|
case SCI_VCHOMEWRAPEXTEND:
|
|
case SCI_VCHOMEDISPLAY:
|
|
case SCI_VCHOMEDISPLAYEXTEND:
|
|
case SCI_ZOOMIN:
|
|
case SCI_ZOOMOUT:
|
|
case SCI_DELWORDLEFT:
|
|
case SCI_DELWORDRIGHT:
|
|
case SCI_DELWORDRIGHTEND:
|
|
case SCI_DELLINELEFT:
|
|
case SCI_DELLINERIGHT:
|
|
case SCI_LINECOPY:
|
|
case SCI_LINECUT:
|
|
case SCI_LINEDELETE:
|
|
case SCI_LINETRANSPOSE:
|
|
case SCI_LINEDUPLICATE:
|
|
case SCI_LOWERCASE:
|
|
case SCI_UPPERCASE:
|
|
case SCI_LINESCROLLDOWN:
|
|
case SCI_LINESCROLLUP:
|
|
case SCI_WORDPARTLEFT:
|
|
case SCI_WORDPARTLEFTEXTEND:
|
|
case SCI_WORDPARTRIGHT:
|
|
case SCI_WORDPARTRIGHTEXTEND:
|
|
case SCI_DELETEBACKNOTLINE:
|
|
case SCI_HOMEDISPLAY:
|
|
case SCI_HOMEDISPLAYEXTEND:
|
|
case SCI_LINEENDDISPLAY:
|
|
case SCI_LINEENDDISPLAYEXTEND:
|
|
case SCI_LINEDOWNRECTEXTEND:
|
|
case SCI_LINEUPRECTEXTEND:
|
|
case SCI_CHARLEFTRECTEXTEND:
|
|
case SCI_CHARRIGHTRECTEXTEND:
|
|
case SCI_HOMERECTEXTEND:
|
|
case SCI_VCHOMERECTEXTEND:
|
|
case SCI_LINEENDRECTEXTEND:
|
|
case SCI_PAGEUPRECTEXTEND:
|
|
case SCI_PAGEDOWNRECTEXTEND:
|
|
case SCI_SELECTIONDUPLICATE:
|
|
return KeyCommand(iMessage);
|
|
|
|
case SCI_BRACEHIGHLIGHT:
|
|
SetBraceHighlight(static_cast<int>(wParam), static_cast<int>(lParam), STYLE_BRACELIGHT);
|
|
break;
|
|
|
|
case SCI_BRACEHIGHLIGHTINDICATOR:
|
|
if (lParam >= 0 && lParam <= INDIC_MAX) {
|
|
vs.braceHighlightIndicatorSet = wParam != 0;
|
|
vs.braceHighlightIndicator = static_cast<int>(lParam);
|
|
}
|
|
break;
|
|
|
|
case SCI_BRACEBADLIGHT:
|
|
SetBraceHighlight(static_cast<int>(wParam), -1, STYLE_BRACEBAD);
|
|
break;
|
|
|
|
case SCI_BRACEBADLIGHTINDICATOR:
|
|
if (lParam >= 0 && lParam <= INDIC_MAX) {
|
|
vs.braceBadLightIndicatorSet = wParam != 0;
|
|
vs.braceBadLightIndicator = static_cast<int>(lParam);
|
|
}
|
|
break;
|
|
|
|
case SCI_BRACEMATCH:
|
|
// wParam is position of char to find brace for,
|
|
// lParam is maximum amount of text to restyle to find it
|
|
return pdoc->BraceMatch(static_cast<int>(wParam), static_cast<int>(lParam));
|
|
|
|
case SCI_GETVIEWEOL:
|
|
return vs.viewEOL;
|
|
|
|
case SCI_SETVIEWEOL:
|
|
vs.viewEOL = wParam != 0;
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
case SCI_SETZOOM:
|
|
vs.zoomLevel = static_cast<int>(wParam);
|
|
InvalidateStyleRedraw();
|
|
NotifyZoom();
|
|
break;
|
|
|
|
case SCI_GETZOOM:
|
|
return vs.zoomLevel;
|
|
|
|
case SCI_GETEDGECOLUMN:
|
|
return vs.theEdge;
|
|
|
|
case SCI_SETEDGECOLUMN:
|
|
vs.theEdge = static_cast<int>(wParam);
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
case SCI_GETEDGEMODE:
|
|
return vs.edgeState;
|
|
|
|
case SCI_SETEDGEMODE:
|
|
vs.edgeState = static_cast<int>(wParam);
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
case SCI_GETEDGECOLOUR:
|
|
return vs.edgecolour.AsLong();
|
|
|
|
case SCI_SETEDGECOLOUR:
|
|
vs.edgecolour = ColourDesired(static_cast<long>(wParam));
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
case SCI_GETDOCPOINTER:
|
|
return reinterpret_cast<sptr_t>(pdoc);
|
|
|
|
case SCI_SETDOCPOINTER:
|
|
CancelModes();
|
|
SetDocPointer(reinterpret_cast<Document *>(lParam));
|
|
return 0;
|
|
|
|
case SCI_CREATEDOCUMENT: {
|
|
Document *doc = new Document();
|
|
doc->AddRef();
|
|
return reinterpret_cast<sptr_t>(doc);
|
|
}
|
|
|
|
case SCI_ADDREFDOCUMENT:
|
|
(reinterpret_cast<Document *>(lParam))->AddRef();
|
|
break;
|
|
|
|
case SCI_RELEASEDOCUMENT:
|
|
(reinterpret_cast<Document *>(lParam))->Release();
|
|
break;
|
|
|
|
case SCI_CREATELOADER: {
|
|
Document *doc = new Document();
|
|
doc->AddRef();
|
|
doc->Allocate(static_cast<int>(wParam));
|
|
doc->SetUndoCollection(false);
|
|
return reinterpret_cast<sptr_t>(static_cast<ILoader *>(doc));
|
|
}
|
|
|
|
case SCI_SETMODEVENTMASK:
|
|
modEventMask = static_cast<int>(wParam);
|
|
return 0;
|
|
|
|
case SCI_GETMODEVENTMASK:
|
|
return modEventMask;
|
|
|
|
case SCI_CONVERTEOLS:
|
|
pdoc->ConvertLineEnds(static_cast<int>(wParam));
|
|
SetSelection(sel.MainCaret(), sel.MainAnchor()); // Ensure selection inside document
|
|
return 0;
|
|
|
|
case SCI_SETLENGTHFORENCODE:
|
|
lengthForEncode = static_cast<int>(wParam);
|
|
return 0;
|
|
|
|
case SCI_SELECTIONISRECTANGLE:
|
|
return sel.selType == Selection::selRectangle ? 1 : 0;
|
|
|
|
case SCI_SETSELECTIONMODE: {
|
|
switch (wParam) {
|
|
case SC_SEL_STREAM:
|
|
sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
|
|
sel.selType = Selection::selStream;
|
|
break;
|
|
case SC_SEL_RECTANGLE:
|
|
sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selRectangle));
|
|
sel.selType = Selection::selRectangle;
|
|
break;
|
|
case SC_SEL_LINES:
|
|
sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selLines));
|
|
sel.selType = Selection::selLines;
|
|
break;
|
|
case SC_SEL_THIN:
|
|
sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selThin));
|
|
sel.selType = Selection::selThin;
|
|
break;
|
|
default:
|
|
sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
|
|
sel.selType = Selection::selStream;
|
|
}
|
|
InvalidateSelection(sel.RangeMain(), true);
|
|
break;
|
|
}
|
|
case SCI_GETSELECTIONMODE:
|
|
switch (sel.selType) {
|
|
case Selection::selStream:
|
|
return SC_SEL_STREAM;
|
|
case Selection::selRectangle:
|
|
return SC_SEL_RECTANGLE;
|
|
case Selection::selLines:
|
|
return SC_SEL_LINES;
|
|
case Selection::selThin:
|
|
return SC_SEL_THIN;
|
|
default: // ?!
|
|
return SC_SEL_STREAM;
|
|
}
|
|
case SCI_GETLINESELSTARTPOSITION:
|
|
case SCI_GETLINESELENDPOSITION: {
|
|
SelectionSegment segmentLine(SelectionPosition(pdoc->LineStart(static_cast<int>(wParam))),
|
|
SelectionPosition(pdoc->LineEnd(static_cast<int>(wParam))));
|
|
for (size_t r=0; r<sel.Count(); r++) {
|
|
SelectionSegment portion = sel.Range(r).Intersect(segmentLine);
|
|
if (portion.start.IsValid()) {
|
|
return (iMessage == SCI_GETLINESELSTARTPOSITION) ? portion.start.Position() : portion.end.Position();
|
|
}
|
|
}
|
|
return INVALID_POSITION;
|
|
}
|
|
|
|
case SCI_SETOVERTYPE:
|
|
inOverstrike = wParam != 0;
|
|
break;
|
|
|
|
case SCI_GETOVERTYPE:
|
|
return inOverstrike ? 1 : 0;
|
|
|
|
case SCI_SETFOCUS:
|
|
SetFocusState(wParam != 0);
|
|
break;
|
|
|
|
case SCI_GETFOCUS:
|
|
return hasFocus;
|
|
|
|
case SCI_SETSTATUS:
|
|
errorStatus = static_cast<int>(wParam);
|
|
break;
|
|
|
|
case SCI_GETSTATUS:
|
|
return errorStatus;
|
|
|
|
case SCI_SETMOUSEDOWNCAPTURES:
|
|
mouseDownCaptures = wParam != 0;
|
|
break;
|
|
|
|
case SCI_GETMOUSEDOWNCAPTURES:
|
|
return mouseDownCaptures;
|
|
|
|
case SCI_SETCURSOR:
|
|
cursorMode = static_cast<int>(wParam);
|
|
DisplayCursor(Window::cursorText);
|
|
break;
|
|
|
|
case SCI_GETCURSOR:
|
|
return cursorMode;
|
|
|
|
case SCI_SETCONTROLCHARSYMBOL:
|
|
vs.controlCharSymbol = static_cast<int>(wParam);
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
case SCI_GETCONTROLCHARSYMBOL:
|
|
return vs.controlCharSymbol;
|
|
|
|
case SCI_SETREPRESENTATION:
|
|
reprs.SetRepresentation(reinterpret_cast<const char *>(wParam), CharPtrFromSPtr(lParam));
|
|
break;
|
|
|
|
case SCI_GETREPRESENTATION: {
|
|
Representation *repr = reprs.RepresentationFromCharacter(
|
|
reinterpret_cast<const char *>(wParam), UTF8MaxBytes);
|
|
if (repr) {
|
|
return StringResult(lParam, repr->stringRep.c_str());
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
case SCI_CLEARREPRESENTATION:
|
|
reprs.ClearRepresentation(reinterpret_cast<const char *>(wParam));
|
|
break;
|
|
|
|
case SCI_STARTRECORD:
|
|
recordingMacro = true;
|
|
return 0;
|
|
|
|
case SCI_STOPRECORD:
|
|
recordingMacro = false;
|
|
return 0;
|
|
|
|
case SCI_MOVECARETINSIDEVIEW:
|
|
MoveCaretInsideView();
|
|
break;
|
|
|
|
case SCI_SETFOLDMARGINCOLOUR:
|
|
vs.foldmarginColour = ColourOptional(wParam, lParam);
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
case SCI_SETFOLDMARGINHICOLOUR:
|
|
vs.foldmarginHighlightColour = ColourOptional(wParam, lParam);
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
case SCI_SETHOTSPOTACTIVEFORE:
|
|
vs.hotspotColours.fore = ColourOptional(wParam, lParam);
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
case SCI_GETHOTSPOTACTIVEFORE:
|
|
return vs.hotspotColours.fore.AsLong();
|
|
|
|
case SCI_SETHOTSPOTACTIVEBACK:
|
|
vs.hotspotColours.back = ColourOptional(wParam, lParam);
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
case SCI_GETHOTSPOTACTIVEBACK:
|
|
return vs.hotspotColours.back.AsLong();
|
|
|
|
case SCI_SETHOTSPOTACTIVEUNDERLINE:
|
|
vs.hotspotUnderline = wParam != 0;
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
case SCI_GETHOTSPOTACTIVEUNDERLINE:
|
|
return vs.hotspotUnderline ? 1 : 0;
|
|
|
|
case SCI_SETHOTSPOTSINGLELINE:
|
|
vs.hotspotSingleLine = wParam != 0;
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
case SCI_GETHOTSPOTSINGLELINE:
|
|
return vs.hotspotSingleLine ? 1 : 0;
|
|
|
|
case SCI_SETPASTECONVERTENDINGS:
|
|
convertPastes = wParam != 0;
|
|
break;
|
|
|
|
case SCI_GETPASTECONVERTENDINGS:
|
|
return convertPastes ? 1 : 0;
|
|
|
|
case SCI_GETCHARACTERPOINTER:
|
|
return reinterpret_cast<sptr_t>(pdoc->BufferPointer());
|
|
|
|
case SCI_GETRANGEPOINTER:
|
|
return reinterpret_cast<sptr_t>(pdoc->RangePointer(static_cast<int>(wParam), static_cast<int>(lParam)));
|
|
|
|
case SCI_GETGAPPOSITION:
|
|
return pdoc->GapPosition();
|
|
|
|
case SCI_SETEXTRAASCENT:
|
|
vs.extraAscent = static_cast<int>(wParam);
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
case SCI_GETEXTRAASCENT:
|
|
return vs.extraAscent;
|
|
|
|
case SCI_SETEXTRADESCENT:
|
|
vs.extraDescent = static_cast<int>(wParam);
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
case SCI_GETEXTRADESCENT:
|
|
return vs.extraDescent;
|
|
|
|
case SCI_MARGINSETSTYLEOFFSET:
|
|
vs.marginStyleOffset = static_cast<int>(wParam);
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
case SCI_MARGINGETSTYLEOFFSET:
|
|
return vs.marginStyleOffset;
|
|
|
|
case SCI_SETMARGINOPTIONS:
|
|
marginOptions = static_cast<int>(wParam);
|
|
break;
|
|
|
|
case SCI_GETMARGINOPTIONS:
|
|
return marginOptions;
|
|
|
|
case SCI_MARGINSETTEXT:
|
|
pdoc->MarginSetText(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
|
|
break;
|
|
|
|
case SCI_MARGINGETTEXT: {
|
|
const StyledText st = pdoc->MarginStyledText(static_cast<int>(wParam));
|
|
return BytesResult(lParam, reinterpret_cast<const unsigned char *>(st.text), st.length);
|
|
}
|
|
|
|
case SCI_MARGINSETSTYLE:
|
|
pdoc->MarginSetStyle(static_cast<int>(wParam), static_cast<int>(lParam));
|
|
break;
|
|
|
|
case SCI_MARGINGETSTYLE: {
|
|
const StyledText st = pdoc->MarginStyledText(static_cast<int>(wParam));
|
|
return st.style;
|
|
}
|
|
|
|
case SCI_MARGINSETSTYLES:
|
|
pdoc->MarginSetStyles(static_cast<int>(wParam), reinterpret_cast<const unsigned char *>(lParam));
|
|
break;
|
|
|
|
case SCI_MARGINGETSTYLES: {
|
|
const StyledText st = pdoc->MarginStyledText(static_cast<int>(wParam));
|
|
return BytesResult(lParam, st.styles, st.length);
|
|
}
|
|
|
|
case SCI_MARGINTEXTCLEARALL:
|
|
pdoc->MarginClearAll();
|
|
break;
|
|
|
|
case SCI_ANNOTATIONSETTEXT:
|
|
pdoc->AnnotationSetText(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
|
|
break;
|
|
|
|
case SCI_ANNOTATIONGETTEXT: {
|
|
const StyledText st = pdoc->AnnotationStyledText(static_cast<int>(wParam));
|
|
return BytesResult(lParam, reinterpret_cast<const unsigned char *>(st.text), st.length);
|
|
}
|
|
|
|
case SCI_ANNOTATIONGETSTYLE: {
|
|
const StyledText st = pdoc->AnnotationStyledText(static_cast<int>(wParam));
|
|
return st.style;
|
|
}
|
|
|
|
case SCI_ANNOTATIONSETSTYLE:
|
|
pdoc->AnnotationSetStyle(static_cast<int>(wParam), static_cast<int>(lParam));
|
|
break;
|
|
|
|
case SCI_ANNOTATIONSETSTYLES:
|
|
pdoc->AnnotationSetStyles(static_cast<int>(wParam), reinterpret_cast<const unsigned char *>(lParam));
|
|
break;
|
|
|
|
case SCI_ANNOTATIONGETSTYLES: {
|
|
const StyledText st = pdoc->AnnotationStyledText(static_cast<int>(wParam));
|
|
return BytesResult(lParam, st.styles, st.length);
|
|
}
|
|
|
|
case SCI_ANNOTATIONGETLINES:
|
|
return pdoc->AnnotationLines(static_cast<int>(wParam));
|
|
|
|
case SCI_ANNOTATIONCLEARALL:
|
|
pdoc->AnnotationClearAll();
|
|
break;
|
|
|
|
case SCI_ANNOTATIONSETVISIBLE:
|
|
SetAnnotationVisible(static_cast<int>(wParam));
|
|
break;
|
|
|
|
case SCI_ANNOTATIONGETVISIBLE:
|
|
return vs.annotationVisible;
|
|
|
|
case SCI_ANNOTATIONSETSTYLEOFFSET:
|
|
vs.annotationStyleOffset = static_cast<int>(wParam);
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
case SCI_ANNOTATIONGETSTYLEOFFSET:
|
|
return vs.annotationStyleOffset;
|
|
|
|
case SCI_RELEASEALLEXTENDEDSTYLES:
|
|
vs.ReleaseAllExtendedStyles();
|
|
break;
|
|
|
|
case SCI_ALLOCATEEXTENDEDSTYLES:
|
|
return vs.AllocateExtendedStyles(static_cast<int>(wParam));
|
|
|
|
case SCI_ADDUNDOACTION:
|
|
pdoc->AddUndoAction(static_cast<int>(wParam), lParam & UNDO_MAY_COALESCE);
|
|
break;
|
|
|
|
case SCI_SETMOUSESELECTIONRECTANGULARSWITCH:
|
|
mouseSelectionRectangularSwitch = wParam != 0;
|
|
break;
|
|
|
|
case SCI_GETMOUSESELECTIONRECTANGULARSWITCH:
|
|
return mouseSelectionRectangularSwitch;
|
|
|
|
case SCI_SETMULTIPLESELECTION:
|
|
multipleSelection = wParam != 0;
|
|
InvalidateCaret();
|
|
break;
|
|
|
|
case SCI_GETMULTIPLESELECTION:
|
|
return multipleSelection;
|
|
|
|
case SCI_SETADDITIONALSELECTIONTYPING:
|
|
additionalSelectionTyping = wParam != 0;
|
|
InvalidateCaret();
|
|
break;
|
|
|
|
case SCI_GETADDITIONALSELECTIONTYPING:
|
|
return additionalSelectionTyping;
|
|
|
|
case SCI_SETMULTIPASTE:
|
|
multiPasteMode = static_cast<int>(wParam);
|
|
break;
|
|
|
|
case SCI_GETMULTIPASTE:
|
|
return multiPasteMode;
|
|
|
|
case SCI_SETADDITIONALCARETSBLINK:
|
|
additionalCaretsBlink = wParam != 0;
|
|
InvalidateCaret();
|
|
break;
|
|
|
|
case SCI_GETADDITIONALCARETSBLINK:
|
|
return additionalCaretsBlink;
|
|
|
|
case SCI_SETADDITIONALCARETSVISIBLE:
|
|
additionalCaretsVisible = wParam != 0;
|
|
InvalidateCaret();
|
|
break;
|
|
|
|
case SCI_GETADDITIONALCARETSVISIBLE:
|
|
return additionalCaretsVisible;
|
|
|
|
case SCI_GETSELECTIONS:
|
|
return sel.Count();
|
|
|
|
case SCI_GETSELECTIONEMPTY:
|
|
return sel.Empty();
|
|
|
|
case SCI_CLEARSELECTIONS:
|
|
sel.Clear();
|
|
Redraw();
|
|
break;
|
|
|
|
case SCI_SETSELECTION:
|
|
sel.SetSelection(SelectionRange(static_cast<int>(wParam), static_cast<int>(lParam)));
|
|
Redraw();
|
|
break;
|
|
|
|
case SCI_ADDSELECTION:
|
|
sel.AddSelection(SelectionRange(static_cast<int>(wParam), static_cast<int>(lParam)));
|
|
Redraw();
|
|
break;
|
|
|
|
case SCI_DROPSELECTIONN:
|
|
sel.DropSelection(static_cast<size_t>(wParam));
|
|
Redraw();
|
|
break;
|
|
|
|
case SCI_SETMAINSELECTION:
|
|
sel.SetMain(static_cast<size_t>(wParam));
|
|
Redraw();
|
|
break;
|
|
|
|
case SCI_GETMAINSELECTION:
|
|
return sel.Main();
|
|
|
|
case SCI_SETSELECTIONNCARET:
|
|
sel.Range(wParam).caret.SetPosition(static_cast<int>(lParam));
|
|
Redraw();
|
|
break;
|
|
|
|
case SCI_GETSELECTIONNCARET:
|
|
return sel.Range(wParam).caret.Position();
|
|
|
|
case SCI_SETSELECTIONNANCHOR:
|
|
sel.Range(wParam).anchor.SetPosition(static_cast<int>(lParam));
|
|
Redraw();
|
|
break;
|
|
case SCI_GETSELECTIONNANCHOR:
|
|
return sel.Range(wParam).anchor.Position();
|
|
|
|
case SCI_SETSELECTIONNCARETVIRTUALSPACE:
|
|
sel.Range(wParam).caret.SetVirtualSpace(static_cast<int>(lParam));
|
|
Redraw();
|
|
break;
|
|
|
|
case SCI_GETSELECTIONNCARETVIRTUALSPACE:
|
|
return sel.Range(wParam).caret.VirtualSpace();
|
|
|
|
case SCI_SETSELECTIONNANCHORVIRTUALSPACE:
|
|
sel.Range(wParam).anchor.SetVirtualSpace(static_cast<int>(lParam));
|
|
Redraw();
|
|
break;
|
|
|
|
case SCI_GETSELECTIONNANCHORVIRTUALSPACE:
|
|
return sel.Range(wParam).anchor.VirtualSpace();
|
|
|
|
case SCI_SETSELECTIONNSTART:
|
|
sel.Range(wParam).anchor.SetPosition(static_cast<int>(lParam));
|
|
Redraw();
|
|
break;
|
|
|
|
case SCI_GETSELECTIONNSTART:
|
|
return sel.Range(wParam).Start().Position();
|
|
|
|
case SCI_SETSELECTIONNEND:
|
|
sel.Range(wParam).caret.SetPosition(static_cast<int>(lParam));
|
|
Redraw();
|
|
break;
|
|
|
|
case SCI_GETSELECTIONNEND:
|
|
return sel.Range(wParam).End().Position();
|
|
|
|
case SCI_SETRECTANGULARSELECTIONCARET:
|
|
if (!sel.IsRectangular())
|
|
sel.Clear();
|
|
sel.selType = Selection::selRectangle;
|
|
sel.Rectangular().caret.SetPosition(static_cast<int>(wParam));
|
|
SetRectangularRange();
|
|
Redraw();
|
|
break;
|
|
|
|
case SCI_GETRECTANGULARSELECTIONCARET:
|
|
return sel.Rectangular().caret.Position();
|
|
|
|
case SCI_SETRECTANGULARSELECTIONANCHOR:
|
|
if (!sel.IsRectangular())
|
|
sel.Clear();
|
|
sel.selType = Selection::selRectangle;
|
|
sel.Rectangular().anchor.SetPosition(static_cast<int>(wParam));
|
|
SetRectangularRange();
|
|
Redraw();
|
|
break;
|
|
|
|
case SCI_GETRECTANGULARSELECTIONANCHOR:
|
|
return sel.Rectangular().anchor.Position();
|
|
|
|
case SCI_SETRECTANGULARSELECTIONCARETVIRTUALSPACE:
|
|
if (!sel.IsRectangular())
|
|
sel.Clear();
|
|
sel.selType = Selection::selRectangle;
|
|
sel.Rectangular().caret.SetVirtualSpace(static_cast<int>(wParam));
|
|
SetRectangularRange();
|
|
Redraw();
|
|
break;
|
|
|
|
case SCI_GETRECTANGULARSELECTIONCARETVIRTUALSPACE:
|
|
return sel.Rectangular().caret.VirtualSpace();
|
|
|
|
case SCI_SETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
|
|
if (!sel.IsRectangular())
|
|
sel.Clear();
|
|
sel.selType = Selection::selRectangle;
|
|
sel.Rectangular().anchor.SetVirtualSpace(static_cast<int>(wParam));
|
|
SetRectangularRange();
|
|
Redraw();
|
|
break;
|
|
|
|
case SCI_GETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
|
|
return sel.Rectangular().anchor.VirtualSpace();
|
|
|
|
case SCI_SETVIRTUALSPACEOPTIONS:
|
|
virtualSpaceOptions = static_cast<int>(wParam);
|
|
break;
|
|
|
|
case SCI_GETVIRTUALSPACEOPTIONS:
|
|
return virtualSpaceOptions;
|
|
|
|
case SCI_SETADDITIONALSELFORE:
|
|
vs.selAdditionalForeground = ColourDesired(static_cast<long>(wParam));
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
case SCI_SETADDITIONALSELBACK:
|
|
vs.selAdditionalBackground = ColourDesired(static_cast<long>(wParam));
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
case SCI_SETADDITIONALSELALPHA:
|
|
vs.selAdditionalAlpha = static_cast<int>(wParam);
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
case SCI_GETADDITIONALSELALPHA:
|
|
return vs.selAdditionalAlpha;
|
|
|
|
case SCI_SETADDITIONALCARETFORE:
|
|
vs.additionalCaretColour = ColourDesired(static_cast<long>(wParam));
|
|
InvalidateStyleRedraw();
|
|
break;
|
|
|
|
case SCI_GETADDITIONALCARETFORE:
|
|
return vs.additionalCaretColour.AsLong();
|
|
|
|
case SCI_ROTATESELECTION:
|
|
sel.RotateMain();
|
|
InvalidateSelection(sel.RangeMain(), true);
|
|
break;
|
|
|
|
case SCI_SWAPMAINANCHORCARET:
|
|
InvalidateSelection(sel.RangeMain());
|
|
sel.RangeMain() = SelectionRange(sel.RangeMain().anchor, sel.RangeMain().caret);
|
|
break;
|
|
|
|
case SCI_CHANGELEXERSTATE:
|
|
pdoc->ChangeLexerState(static_cast<int>(wParam), static_cast<int>(lParam));
|
|
break;
|
|
|
|
case SCI_SETIDENTIFIER:
|
|
SetCtrlID(static_cast<int>(wParam));
|
|
break;
|
|
|
|
case SCI_GETIDENTIFIER:
|
|
return GetCtrlID();
|
|
|
|
case SCI_SETTECHNOLOGY:
|
|
// No action by default
|
|
break;
|
|
|
|
case SCI_GETTECHNOLOGY:
|
|
return technology;
|
|
|
|
case SCI_COUNTCHARACTERS:
|
|
return pdoc->CountCharacters(static_cast<int>(wParam), static_cast<int>(lParam));
|
|
|
|
default:
|
|
return DefWndProc(iMessage, wParam, lParam);
|
|
}
|
|
//Platform::DebugPrintf("end wnd proc\n");
|
|
return 0l;
|
|
}
|