|
|
|
|
// Scintilla source code edit control
|
|
|
|
|
/** @file Editor.cxx
|
|
|
|
|
** Main code for the edit control.
|
|
|
|
|
**/
|
|
|
|
|
// Copyright 1998-2004 by Neil Hodgson <neilh@scintilla.org>
|
|
|
|
|
// The License.txt file describes the conditions under which this software may be distributed.
|
|
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <ctype.h>
|
|
|
|
|
|
|
|
|
|
#include "Platform.h"
|
|
|
|
|
|
|
|
|
|
#ifndef PLAT_QT
|
|
|
|
|
#define INCLUDE_DEPRECATED_FEATURES
|
|
|
|
|
#endif
|
|
|
|
|
#include "Scintilla.h"
|
|
|
|
|
|
|
|
|
|
#include "ContractionState.h"
|
|
|
|
|
#include "SVector.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 "Document.h"
|
|
|
|
|
#include "Editor.h"
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
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) {}
|
|
|
|
|
|
|
|
|
|
LineLayout::LineLayout(int maxLineLength_) :
|
|
|
|
|
lineStarts(0),
|
|
|
|
|
lenLineStarts(0),
|
|
|
|
|
lineNumber(-1),
|
|
|
|
|
inCache(false),
|
|
|
|
|
maxLineLength(-1),
|
|
|
|
|
numCharsInLine(0),
|
|
|
|
|
validity(llInvalid),
|
|
|
|
|
xHighlightGuide(0),
|
|
|
|
|
highlightColumn(0),
|
|
|
|
|
selStart(0),
|
|
|
|
|
selEnd(0),
|
|
|
|
|
containsCaret(false),
|
|
|
|
|
edgeColumn(0),
|
|
|
|
|
chars(0),
|
|
|
|
|
styles(0),
|
|
|
|
|
styleBitsSet(0),
|
|
|
|
|
indicators(0),
|
|
|
|
|
positions(0),
|
|
|
|
|
hsStart(0),
|
|
|
|
|
hsEnd(0),
|
|
|
|
|
widthLine(wrapWidthInfinite),
|
|
|
|
|
lines(1) {
|
|
|
|
|
Resize(maxLineLength_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LineLayout::~LineLayout() {
|
|
|
|
|
Free();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LineLayout::Resize(int maxLineLength_) {
|
|
|
|
|
if (maxLineLength_ > maxLineLength) {
|
|
|
|
|
Free();
|
|
|
|
|
chars = new char[maxLineLength_ + 1];
|
|
|
|
|
styles = new unsigned char[maxLineLength_ + 1];
|
|
|
|
|
indicators = new char[maxLineLength_ + 1];
|
|
|
|
|
// Extra position allocated as sometimes the Windows
|
|
|
|
|
// GetTextExtentExPoint API writes an extra element.
|
|
|
|
|
positions = new int[maxLineLength_ + 1 + 1];
|
|
|
|
|
maxLineLength = maxLineLength_;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LineLayout::Free() {
|
|
|
|
|
delete []chars;
|
|
|
|
|
chars = 0;
|
|
|
|
|
delete []styles;
|
|
|
|
|
styles = 0;
|
|
|
|
|
delete []indicators;
|
|
|
|
|
indicators = 0;
|
|
|
|
|
delete []positions;
|
|
|
|
|
positions = 0;
|
|
|
|
|
delete []lineStarts;
|
|
|
|
|
lineStarts = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LineLayout::Invalidate(validLevel validity_) {
|
|
|
|
|
if (validity > validity_)
|
|
|
|
|
validity = validity_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LineLayout::SetLineStart(int line, int start) {
|
|
|
|
|
if ((line >= lenLineStarts) && (line != 0)) {
|
|
|
|
|
int newMaxLines = line + 20;
|
|
|
|
|
int *newLineStarts = new int[newMaxLines];
|
|
|
|
|
if (!newLineStarts)
|
|
|
|
|
return;
|
|
|
|
|
for (int i = 0; i < newMaxLines; i++) {
|
|
|
|
|
if (i < lenLineStarts)
|
|
|
|
|
newLineStarts[i] = lineStarts[i];
|
|
|
|
|
else
|
|
|
|
|
newLineStarts[i] = 0;
|
|
|
|
|
}
|
|
|
|
|
delete []lineStarts;
|
|
|
|
|
lineStarts = newLineStarts;
|
|
|
|
|
lenLineStarts = newMaxLines;
|
|
|
|
|
}
|
|
|
|
|
lineStarts[line] = start;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LineLayout::SetBracesHighlight(Range rangeLine, Position braces[],
|
|
|
|
|
char bracesMatchStyle, int xHighlight) {
|
|
|
|
|
if (rangeLine.ContainsCharacter(braces[0])) {
|
|
|
|
|
int braceOffset = braces[0] - rangeLine.start;
|
|
|
|
|
if (braceOffset < numCharsInLine) {
|
|
|
|
|
bracePreviousStyles[0] = styles[braceOffset];
|
|
|
|
|
styles[braceOffset] = bracesMatchStyle;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (rangeLine.ContainsCharacter(braces[1])) {
|
|
|
|
|
int braceOffset = braces[1] - rangeLine.start;
|
|
|
|
|
if (braceOffset < numCharsInLine) {
|
|
|
|
|
bracePreviousStyles[1] = styles[braceOffset];
|
|
|
|
|
styles[braceOffset] = bracesMatchStyle;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ((braces[0] >= rangeLine.start && braces[1] <= rangeLine.end) ||
|
|
|
|
|
(braces[1] >= rangeLine.start && braces[0] <= rangeLine.end)) {
|
|
|
|
|
xHighlightGuide = xHighlight;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LineLayout::RestoreBracesHighlight(Range rangeLine, Position braces[]) {
|
|
|
|
|
if (rangeLine.ContainsCharacter(braces[0])) {
|
|
|
|
|
int braceOffset = braces[0] - rangeLine.start;
|
|
|
|
|
if (braceOffset < numCharsInLine) {
|
|
|
|
|
styles[braceOffset] = bracePreviousStyles[0];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (rangeLine.ContainsCharacter(braces[1])) {
|
|
|
|
|
int braceOffset = braces[1] - rangeLine.start;
|
|
|
|
|
if (braceOffset < numCharsInLine) {
|
|
|
|
|
styles[braceOffset] = bracePreviousStyles[1];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
xHighlightGuide = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LineLayoutCache::LineLayoutCache() :
|
|
|
|
|
level(0), length(0), size(0), cache(0),
|
|
|
|
|
allInvalidated(false), styleClock(-1), useCount(0) {
|
|
|
|
|
Allocate(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LineLayoutCache::~LineLayoutCache() {
|
|
|
|
|
Deallocate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LineLayoutCache::Allocate(int length_) {
|
|
|
|
|
PLATFORM_ASSERT(cache == NULL);
|
|
|
|
|
allInvalidated = false;
|
|
|
|
|
length = length_;
|
|
|
|
|
size = length;
|
|
|
|
|
if (size > 1) {
|
|
|
|
|
size = (size / 16 + 1) * 16;
|
|
|
|
|
}
|
|
|
|
|
if (size > 0) {
|
|
|
|
|
cache = new LineLayout * [size];
|
|
|
|
|
}
|
|
|
|
|
for (int i = 0; i < size; i++)
|
|
|
|
|
cache[i] = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LineLayoutCache::AllocateForLevel(int linesOnScreen, int linesInDoc) {
|
|
|
|
|
PLATFORM_ASSERT(useCount == 0);
|
|
|
|
|
int lengthForLevel = 0;
|
|
|
|
|
if (level == llcCaret) {
|
|
|
|
|
lengthForLevel = 1;
|
|
|
|
|
} else if (level == llcPage) {
|
|
|
|
|
lengthForLevel = linesOnScreen + 1;
|
|
|
|
|
} else if (level == llcDocument) {
|
|
|
|
|
lengthForLevel = linesInDoc;
|
|
|
|
|
}
|
|
|
|
|
if (lengthForLevel > size) {
|
|
|
|
|
Deallocate();
|
|
|
|
|
Allocate(lengthForLevel);
|
|
|
|
|
} else {
|
|
|
|
|
if (lengthForLevel < length) {
|
|
|
|
|
for (int i = lengthForLevel; i < length; i++) {
|
|
|
|
|
delete cache[i];
|
|
|
|
|
cache[i] = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
length = lengthForLevel;
|
|
|
|
|
}
|
|
|
|
|
PLATFORM_ASSERT(length == lengthForLevel);
|
|
|
|
|
PLATFORM_ASSERT(cache != NULL || length == 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LineLayoutCache::Deallocate() {
|
|
|
|
|
PLATFORM_ASSERT(useCount == 0);
|
|
|
|
|
for (int i = 0; i < length; i++)
|
|
|
|
|
delete cache[i];
|
|
|
|
|
delete []cache;
|
|
|
|
|
cache = 0;
|
|
|
|
|
length = 0;
|
|
|
|
|
size = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LineLayoutCache::Invalidate(LineLayout::validLevel validity_) {
|
|
|
|
|
if (cache && !allInvalidated) {
|
|
|
|
|
for (int i = 0; i < length; i++) {
|
|
|
|
|
if (cache[i]) {
|
|
|
|
|
cache[i]->Invalidate(validity_);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (validity_ == LineLayout::llInvalid) {
|
|
|
|
|
allInvalidated = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LineLayoutCache::SetLevel(int level_) {
|
|
|
|
|
allInvalidated = false;
|
|
|
|
|
if ((level_ != -1) && (level != level_)) {
|
|
|
|
|
level = level_;
|
|
|
|
|
Deallocate();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LineLayout *LineLayoutCache::Retrieve(int lineNumber, int lineCaret, int maxChars, int styleClock_,
|
|
|
|
|
int linesOnScreen, int linesInDoc) {
|
|
|
|
|
AllocateForLevel(linesOnScreen, linesInDoc);
|
|
|
|
|
if (styleClock != styleClock_) {
|
|
|
|
|
Invalidate(LineLayout::llCheckTextAndStyle);
|
|
|
|
|
styleClock = styleClock_;
|
|
|
|
|
}
|
|
|
|
|
allInvalidated = false;
|
|
|
|
|
int pos = -1;
|
|
|
|
|
LineLayout *ret = 0;
|
|
|
|
|
if (level == llcCaret) {
|
|
|
|
|
pos = 0;
|
|
|
|
|
} else if (level == llcPage) {
|
|
|
|
|
if (lineNumber == lineCaret) {
|
|
|
|
|
pos = 0;
|
|
|
|
|
} else if (length > 1) {
|
|
|
|
|
pos = 1 + (lineNumber % (length - 1));
|
|
|
|
|
}
|
|
|
|
|
} else if (level == llcDocument) {
|
|
|
|
|
pos = lineNumber;
|
|
|
|
|
}
|
|
|
|
|
if (pos >= 0) {
|
|
|
|
|
PLATFORM_ASSERT(useCount == 0);
|
|
|
|
|
if (cache && (pos < length)) {
|
|
|
|
|
if (cache[pos]) {
|
|
|
|
|
if ((cache[pos]->lineNumber != lineNumber) ||
|
|
|
|
|
(cache[pos]->maxLineLength < maxChars)) {
|
|
|
|
|
delete cache[pos];
|
|
|
|
|
cache[pos] = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!cache[pos]) {
|
|
|
|
|
cache[pos] = new LineLayout(maxChars);
|
|
|
|
|
}
|
|
|
|
|
if (cache[pos]) {
|
|
|
|
|
cache[pos]->lineNumber = lineNumber;
|
|
|
|
|
cache[pos]->inCache = true;
|
|
|
|
|
ret = cache[pos];
|
|
|
|
|
useCount++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!ret) {
|
|
|
|
|
ret = new LineLayout(maxChars);
|
|
|
|
|
ret->lineNumber = lineNumber;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LineLayoutCache::Dispose(LineLayout *ll) {
|
|
|
|
|
allInvalidated = false;
|
|
|
|
|
if (ll) {
|
|
|
|
|
if (!ll->inCache) {
|
|
|
|
|
delete ll;
|
|
|
|
|
} else {
|
|
|
|
|
useCount--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Editor::Editor() {
|
|
|
|
|
ctrlID = 0;
|
|
|
|
|
|
|
|
|
|
stylesValid = false;
|
|
|
|
|
|
|
|
|
|
printMagnification = 0;
|
|
|
|
|
printColourMode = SC_PRINT_NORMAL;
|
|
|
|
|
printWrapState = eWrapWord;
|
|
|
|
|
cursorMode = SC_CURSORNORMAL;
|
|
|
|
|
controlCharSymbol = 0; /* Draw the control characters */
|
|
|
|
|
|
|
|
|
|
hasFocus = false;
|
|
|
|
|
hideSelection = false;
|
|
|
|
|
inOverstrike = false;
|
|
|
|
|
errorStatus = 0;
|
|
|
|
|
mouseDownCaptures = true;
|
|
|
|
|
|
|
|
|
|
bufferedDraw = true;
|
|
|
|
|
twoPhaseDraw = true;
|
|
|
|
|
|
|
|
|
|
lastClickTime = 0;
|
|
|
|
|
dwellDelay = SC_TIME_FOREVER;
|
|
|
|
|
ticksToDwell = SC_TIME_FOREVER;
|
|
|
|
|
dwelling = false;
|
|
|
|
|
ptMouseLast.x = 0;
|
|
|
|
|
ptMouseLast.y = 0;
|
|
|
|
|
inDragDrop = false;
|
|
|
|
|
dropWentOutside = false;
|
|
|
|
|
posDrag = invalidPosition;
|
|
|
|
|
posDrop = invalidPosition;
|
|
|
|
|
selectionType = selChar;
|
|
|
|
|
|
|
|
|
|
lastXChosen = 0;
|
|
|
|
|
lineAnchor = 0;
|
|
|
|
|
originalAnchorPos = 0;
|
|
|
|
|
|
|
|
|
|
selType = selStream;
|
|
|
|
|
moveExtendsSelection = false;
|
|
|
|
|
xStartSelect = 0;
|
|
|
|
|
xEndSelect = 0;
|
|
|
|
|
primarySelection = true;
|
|
|
|
|
|
|
|
|
|
caretXPolicy = CARET_SLOP | CARET_EVEN;
|
|
|
|
|
caretXSlop = 50;
|
|
|
|
|
|
|
|
|
|
caretYPolicy = CARET_EVEN;
|
|
|
|
|
caretYSlop = 0;
|
|
|
|
|
|
|
|
|
|
searchAnchor = 0;
|
|
|
|
|
|
|
|
|
|
xOffset = 0;
|
|
|
|
|
xCaretMargin = 50;
|
|
|
|
|
horizontalScrollBarVisible = true;
|
|
|
|
|
scrollWidth = 2000;
|
|
|
|
|
verticalScrollBarVisible = true;
|
|
|
|
|
endAtLastLine = true;
|
|
|
|
|
caretSticky = false;
|
|
|
|
|
|
|
|
|
|
pixmapLine = Surface::Allocate();
|
|
|
|
|
pixmapSelMargin = Surface::Allocate();
|
|
|
|
|
pixmapSelPattern = Surface::Allocate();
|
|
|
|
|
pixmapIndentGuide = Surface::Allocate();
|
|
|
|
|
pixmapIndentGuideHighlight = Surface::Allocate();
|
|
|
|
|
|
|
|
|
|
currentPos = 0;
|
|
|
|
|
anchor = 0;
|
|
|
|
|
|
|
|
|
|
targetStart = 0;
|
|
|
|
|
targetEnd = 0;
|
|
|
|
|
searchFlags = 0;
|
|
|
|
|
|
|
|
|
|
topLine = 0;
|
|
|
|
|
posTopLine = 0;
|
|
|
|
|
|
|
|
|
|
lengthForEncode = -1;
|
|
|
|
|
|
|
|
|
|
needUpdateUI = true;
|
|
|
|
|
braces[0] = invalidPosition;
|
|
|
|
|
braces[1] = invalidPosition;
|
|
|
|
|
bracesMatchStyle = STYLE_BRACEBAD;
|
|
|
|
|
highlightGuideColumn = 0;
|
|
|
|
|
|
|
|
|
|
theEdge = 0;
|
|
|
|
|
|
|
|
|
|
paintState = notPainting;
|
|
|
|
|
|
|
|
|
|
modEventMask = SC_MODEVENTMASKALL;
|
|
|
|
|
|
|
|
|
|
pdoc = new Document();
|
|
|
|
|
pdoc->AddRef();
|
|
|
|
|
pdoc->AddWatcher(this, 0);
|
|
|
|
|
|
|
|
|
|
recordingMacro = false;
|
|
|
|
|
foldFlags = 0;
|
|
|
|
|
|
|
|
|
|
wrapState = eWrapNone;
|
|
|
|
|
wrapWidth = LineLayout::wrapWidthInfinite;
|
|
|
|
|
wrapStart = wrapLineLarge;
|
|
|
|
|
wrapEnd = wrapLineLarge;
|
|
|
|
|
wrapVisualFlags = 0;
|
|
|
|
|
wrapVisualFlagsLocation = 0;
|
|
|
|
|
wrapVisualStartIndent = 0;
|
|
|
|
|
actualWrapVisualStartIndent = 0;
|
|
|
|
|
|
|
|
|
|
convertPastes = true;
|
|
|
|
|
|
|
|
|
|
hsStart = -1;
|
|
|
|
|
hsEnd = -1;
|
|
|
|
|
|
|
|
|
|
llc.SetLevel(LineLayoutCache::llcCaret);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Editor::~Editor() {
|
|
|
|
|
pdoc->RemoveWatcher(this, 0);
|
|
|
|
|
pdoc->Release();
|
|
|
|
|
pdoc = 0;
|
|
|
|
|
DropGraphics();
|
|
|
|
|
delete pixmapLine;
|
|
|
|
|
delete pixmapSelMargin;
|
|
|
|
|
delete pixmapSelPattern;
|
|
|
|
|
delete pixmapIndentGuide;
|
|
|
|
|
delete pixmapIndentGuideHighlight;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::Finalise() {
|
|
|
|
|
SetIdle(false);
|
|
|
|
|
CancelModes();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::DropGraphics() {
|
|
|
|
|
pixmapLine->Release();
|
|
|
|
|
pixmapSelMargin->Release();
|
|
|
|
|
pixmapSelPattern->Release();
|
|
|
|
|
pixmapIndentGuide->Release();
|
|
|
|
|
pixmapIndentGuideHighlight->Release();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::InvalidateStyleData() {
|
|
|
|
|
stylesValid = false;
|
|
|
|
|
palette.Release();
|
|
|
|
|
DropGraphics();
|
|
|
|
|
llc.Invalidate(LineLayout::llInvalid);
|
|
|
|
|
if (selType == selRectangle) {
|
|
|
|
|
xStartSelect = XFromPosition(anchor);
|
|
|
|
|
xEndSelect = XFromPosition(currentPos);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::InvalidateStyleRedraw() {
|
|
|
|
|
NeedWrapping();
|
|
|
|
|
InvalidateStyleData();
|
|
|
|
|
Redraw();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::RefreshColourPalette(Palette &pal, bool want) {
|
|
|
|
|
vs.RefreshColourPalette(pal, want);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::RefreshStyleData() {
|
|
|
|
|
if (!stylesValid) {
|
|
|
|
|
stylesValid = true;
|
|
|
|
|
AutoSurface surface(this);
|
|
|
|
|
if (surface) {
|
|
|
|
|
vs.Refresh(*surface);
|
|
|
|
|
RefreshColourPalette(palette, true);
|
|
|
|
|
palette.Allocate(wMain);
|
|
|
|
|
RefreshColourPalette(palette, false);
|
|
|
|
|
}
|
|
|
|
|
SetScrollBars();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PRectangle Editor::GetClientRectangle() {
|
|
|
|
|
return wMain.GetClientPosition();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PRectangle Editor::GetTextRectangle() {
|
|
|
|
|
PRectangle rc = GetClientRectangle();
|
|
|
|
|
rc.left += vs.fixedColumnWidth;
|
|
|
|
|
rc.right -= vs.rightMarginWidth;
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Editor::LinesOnScreen() {
|
|
|
|
|
PRectangle rcClient = GetClientRectangle();
|
|
|
|
|
int htClient = rcClient.bottom - rcClient.top;
|
|
|
|
|
//Platform::DebugPrintf("lines on screen = %d\n", htClient / lineHeight + 1);
|
|
|
|
|
return htClient / vs.lineHeight;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Editor::LinesToScroll() {
|
|
|
|
|
int retVal = LinesOnScreen() - 1;
|
|
|
|
|
if (retVal < 1)
|
|
|
|
|
return 1;
|
|
|
|
|
else
|
|
|
|
|
return retVal;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Editor::MaxScrollPos() {
|
|
|
|
|
//Platform::DebugPrintf("Lines %d screen = %d maxScroll = %d\n",
|
|
|
|
|
//LinesTotal(), LinesOnScreen(), LinesTotal() - LinesOnScreen() + 1);
|
|
|
|
|
int retVal = cs.LinesDisplayed();
|
|
|
|
|
if (endAtLastLine) {
|
|
|
|
|
retVal -= LinesOnScreen();
|
|
|
|
|
} else {
|
|
|
|
|
retVal--;
|
|
|
|
|
}
|
|
|
|
|
if (retVal < 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
|
|
|
|
return retVal;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline bool IsControlCharacter(int ch) {
|
|
|
|
|
// iscntrl returns true for lots of chars > 127 which are displayable
|
|
|
|
|
return ch >= 0 && ch < ' ';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *ControlCharacterString(unsigned char ch) {
|
|
|
|
|
const char *reps[] = {
|
|
|
|
|
"NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
|
|
|
|
|
"BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
|
|
|
|
|
"DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
|
|
|
|
|
"CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
|
|
|
|
|
};
|
|
|
|
|
if (ch < (sizeof(reps) / sizeof(reps[0]))) {
|
|
|
|
|
return reps[ch];
|
|
|
|
|
} else {
|
|
|
|
|
return "BAD";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Convenience class to ensure LineLayout objects are always disposed.
|
|
|
|
|
*/
|
|
|
|
|
class AutoLineLayout {
|
|
|
|
|
LineLayoutCache &llc;
|
|
|
|
|
LineLayout *ll;
|
|
|
|
|
AutoLineLayout &operator=(const AutoLineLayout &) { return * this; }
|
|
|
|
|
public:
|
|
|
|
|
AutoLineLayout(LineLayoutCache &llc_, LineLayout *ll_) : llc(llc_), ll(ll_) {}
|
|
|
|
|
~AutoLineLayout() {
|
|
|
|
|
llc.Dispose(ll);
|
|
|
|
|
ll = 0;
|
|
|
|
|
}
|
|
|
|
|
LineLayout *operator->() const {
|
|
|
|
|
return ll;
|
|
|
|
|
}
|
|
|
|
|
operator LineLayout *() const {
|
|
|
|
|
return ll;
|
|
|
|
|
}
|
|
|
|
|
void Set(LineLayout *ll_) {
|
|
|
|
|
llc.Dispose(ll);
|
|
|
|
|
ll = ll_;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Allows to iterate through the lines of a selection.
|
|
|
|
|
* Althought it can be called for a stream selection, in most cases
|
|
|
|
|
* it is inefficient and it should be used only for
|
|
|
|
|
* a rectangular or a line selection.
|
|
|
|
|
*/
|
|
|
|
|
class SelectionLineIterator {
|
|
|
|
|
private:
|
|
|
|
|
Editor *ed;
|
|
|
|
|
int line; ///< Current line within the iteration.
|
|
|
|
|
bool forward; ///< True if iterating by increasing line number, false otherwise.
|
|
|
|
|
int selStart, selEnd; ///< Positions of the start and end of the selection relative to the start of the document.
|
|
|
|
|
int minX, maxX; ///< Left and right of selection rectangle.
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
int lineStart, lineEnd; ///< Line numbers, first and last lines of the selection.
|
|
|
|
|
int startPos, endPos; ///< Positions of the beginning and end of the selection on the current line.
|
|
|
|
|
|
|
|
|
|
void Reset() {
|
|
|
|
|
if (forward) {
|
|
|
|
|
line = lineStart;
|
|
|
|
|
} else {
|
|
|
|
|
line = lineEnd;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SelectionLineIterator(Editor *ed_, bool forward_ = true) : line(0), startPos(0), endPos(0) {
|
|
|
|
|
ed = ed_;
|
|
|
|
|
forward = forward_;
|
|
|
|
|
selStart = ed->SelectionStart();
|
|
|
|
|
selEnd = ed->SelectionEnd();
|
|
|
|
|
lineStart = ed->pdoc->LineFromPosition(selStart);
|
|
|
|
|
lineEnd = ed->pdoc->LineFromPosition(selEnd);
|
|
|
|
|
// Left of rectangle
|
|
|
|
|
minX = Platform::Minimum(ed->xStartSelect, ed->xEndSelect);
|
|
|
|
|
// Right of rectangle
|
|
|
|
|
maxX = Platform::Maximum(ed->xStartSelect, ed->xEndSelect);
|
|
|
|
|
Reset();
|
|
|
|
|
}
|
|
|
|
|
~SelectionLineIterator() {}
|
|
|
|
|
|
|
|
|
|
void SetAt(int line) {
|
|
|
|
|
if (line < lineStart || line > lineEnd) {
|
|
|
|
|
startPos = endPos = INVALID_POSITION;
|
|
|
|
|
} else {
|
|
|
|
|
if (ed->selType == ed->selRectangle) {
|
|
|
|
|
// Measure line and return character closest to minX
|
|
|
|
|
startPos = ed->PositionFromLineX(line, minX);
|
|
|
|
|
// Measure line and return character closest to maxX
|
|
|
|
|
endPos = ed->PositionFromLineX(line, maxX);
|
|
|
|
|
} else if (ed->selType == ed->selLines) {
|
|
|
|
|
startPos = ed->pdoc->LineStart(line);
|
|
|
|
|
endPos = ed->pdoc->LineStart(line + 1);
|
|
|
|
|
} else { // Stream selection, here only for completion
|
|
|
|
|
if (line == lineStart) {
|
|
|
|
|
startPos = selStart;
|
|
|
|
|
} else {
|
|
|
|
|
startPos = ed->pdoc->LineStart(line);
|
|
|
|
|
}
|
|
|
|
|
if (line == lineEnd) {
|
|
|
|
|
endPos = selEnd;
|
|
|
|
|
} else {
|
|
|
|
|
endPos = ed->pdoc->LineStart(line + 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
bool Iterate() {
|
|
|
|
|
SetAt(line);
|
|
|
|
|
if (forward) {
|
|
|
|
|
line++;
|
|
|
|
|
} else {
|
|
|
|
|
line--;
|
|
|
|
|
}
|
|
|
|
|
return startPos != INVALID_POSITION;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Point Editor::LocationFromPosition(int pos) {
|
|
|
|
|
Point pt;
|
|
|
|
|
RefreshStyleData();
|
|
|
|
|
if (pos == INVALID_POSITION)
|
|
|
|
|
return pt;
|
|
|
|
|
int line = pdoc->LineFromPosition(pos);
|
|
|
|
|
int lineVisible = cs.DisplayFromDoc(line);
|
|
|
|
|
//Platform::DebugPrintf("line=%d\n", line);
|
|
|
|
|
AutoSurface surface(this);
|
|
|
|
|
AutoLineLayout ll(llc, RetrieveLineLayout(line));
|
|
|
|
|
if (surface && ll) {
|
|
|
|
|
// -1 because of adding in for visible lines in following loop.
|
|
|
|
|
pt.y = (lineVisible - topLine - 1) * vs.lineHeight;
|
|
|
|
|
pt.x = 0;
|
|
|
|
|
unsigned int posLineStart = pdoc->LineStart(line);
|
|
|
|
|
LayoutLine(line, surface, vs, ll, wrapWidth);
|
|
|
|
|
int posInLine = pos - posLineStart;
|
|
|
|
|
// In case of very long line put x at arbitrary large position
|
|
|
|
|
if (posInLine > ll->maxLineLength) {
|
|
|
|
|
pt.x = ll->positions[ll->maxLineLength] - ll->positions[ll->LineStart(ll->lines)];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int subLine = 0; subLine < ll->lines; subLine++) {
|
|
|
|
|
if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) {
|
|
|
|
|
pt.x = ll->positions[posInLine] - ll->positions[ll->LineStart(subLine)];
|
|
|
|
|
if (actualWrapVisualStartIndent != 0) {
|
|
|
|
|
int lineStart = ll->LineStart(subLine);
|
|
|
|
|
if (lineStart != 0) // Wrapped
|
|
|
|
|
pt.x += actualWrapVisualStartIndent * vs.aveCharWidth;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (posInLine >= ll->LineStart(subLine)) {
|
|
|
|
|
pt.y += vs.lineHeight;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
pt.x += vs.fixedColumnWidth - xOffset;
|
|
|
|
|
}
|
|
|
|
|
return pt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Editor::XFromPosition(int pos) {
|
|
|
|
|
Point pt = LocationFromPosition(pos);
|
|
|
|
|
return pt.x - vs.fixedColumnWidth + xOffset;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Editor::LineFromLocation(Point pt) {
|
|
|
|
|
return cs.DocFromDisplay(pt.y / vs.lineHeight + topLine);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::SetTopLine(int topLineNew) {
|
|
|
|
|
topLine = topLineNew;
|
|
|
|
|
posTopLine = pdoc->LineStart(cs.DocFromDisplay(topLine));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline bool IsEOLChar(char ch) {
|
|
|
|
|
return (ch == '\r') || (ch == '\n');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Editor::PositionFromLocation(Point pt) {
|
|
|
|
|
RefreshStyleData();
|
|
|
|
|
pt.x = pt.x - vs.fixedColumnWidth + xOffset;
|
|
|
|
|
int visibleLine = pt.y / vs.lineHeight + topLine;
|
|
|
|
|
if (pt.y < 0) { // Division rounds towards 0
|
|
|
|
|
visibleLine = (pt.y - (vs.lineHeight - 1)) / vs.lineHeight + topLine;
|
|
|
|
|
}
|
|
|
|
|
if (visibleLine < 0)
|
|
|
|
|
visibleLine = 0;
|
|
|
|
|
int lineDoc = cs.DocFromDisplay(visibleLine);
|
|
|
|
|
if (lineDoc >= pdoc->LinesTotal())
|
|
|
|
|
return pdoc->Length();
|
|
|
|
|
unsigned int posLineStart = pdoc->LineStart(lineDoc);
|
|
|
|
|
int retVal = posLineStart;
|
|
|
|
|
AutoSurface surface(this);
|
|
|
|
|
AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
|
|
|
|
|
if (surface && ll) {
|
|
|
|
|
LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
|
|
|
|
|
int lineStartSet = cs.DisplayFromDoc(lineDoc);
|
|
|
|
|
int subLine = visibleLine - lineStartSet;
|
|
|
|
|
if (subLine < ll->lines) {
|
|
|
|
|
int lineStart = ll->LineStart(subLine);
|
|
|
|
|
int lineEnd = ll->LineStart(subLine + 1);
|
|
|
|
|
int subLineStart = ll->positions[lineStart];
|
|
|
|
|
|
|
|
|
|
if (actualWrapVisualStartIndent != 0) {
|
|
|
|
|
if (lineStart != 0) // Wrapped
|
|
|
|
|
pt.x -= actualWrapVisualStartIndent * vs.aveCharWidth;
|
|
|
|
|
}
|
|
|
|
|
for (int i = lineStart; i < lineEnd; i++) {
|
|
|
|
|
if (pt.x < (((ll->positions[i] + ll->positions[i + 1]) / 2) - subLineStart) ||
|
|
|
|
|
IsEOLChar(ll->chars[i])) {
|
|
|
|
|
return pdoc->MovePositionOutsideChar(i + posLineStart, 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return lineEnd + posLineStart;
|
|
|
|
|
}
|
|
|
|
|
retVal = ll->numCharsInLine + posLineStart;
|
|
|
|
|
}
|
|
|
|
|
return retVal;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Like PositionFromLocation but INVALID_POSITION returned when not near any text.
|
|
|
|
|
int Editor::PositionFromLocationClose(Point pt) {
|
|
|
|
|
RefreshStyleData();
|
|
|
|
|
PRectangle rcClient = GetTextRectangle();
|
|
|
|
|
if (!rcClient.Contains(pt))
|
|
|
|
|
return INVALID_POSITION;
|
|
|
|
|
if (pt.x < vs.fixedColumnWidth)
|
|
|
|
|
return INVALID_POSITION;
|
|
|
|
|
if (pt.y < 0)
|
|
|
|
|
return INVALID_POSITION;
|
|
|
|
|
pt.x = pt.x - vs.fixedColumnWidth + xOffset;
|
|
|
|
|
int visibleLine = pt.y / vs.lineHeight + topLine;
|
|
|
|
|
if (pt.y < 0) { // Division rounds towards 0
|
|
|
|
|
visibleLine = (pt.y - (vs.lineHeight - 1)) / vs.lineHeight + topLine;
|
|
|
|
|
}
|
|
|
|
|
int lineDoc = cs.DocFromDisplay(visibleLine);
|
|
|
|
|
if (lineDoc < 0)
|
|
|
|
|
return INVALID_POSITION;
|
|
|
|
|
if (lineDoc >= pdoc->LinesTotal())
|
|
|
|
|
return INVALID_POSITION;
|
|
|
|
|
AutoSurface surface(this);
|
|
|
|
|
AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
|
|
|
|
|
if (surface && ll) {
|
|
|
|
|
LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
|
|
|
|
|
unsigned int posLineStart = pdoc->LineStart(lineDoc);
|
|
|
|
|
int lineStartSet = cs.DisplayFromDoc(lineDoc);
|
|
|
|
|
int subLine = visibleLine - lineStartSet;
|
|
|
|
|
if (subLine < ll->lines) {
|
|
|
|
|
int lineStart = ll->LineStart(subLine);
|
|
|
|
|
int lineEnd = ll->LineStart(subLine + 1);
|
|
|
|
|
int subLineStart = ll->positions[lineStart];
|
|
|
|
|
|
|
|
|
|
if (actualWrapVisualStartIndent != 0) {
|
|
|
|
|
if (lineStart != 0) // Wrapped
|
|
|
|
|
pt.x -= actualWrapVisualStartIndent * vs.aveCharWidth;
|
|
|
|
|
}
|
|
|
|
|
for (int i = lineStart; i < lineEnd; i++) {
|
|
|
|
|
if (pt.x < (((ll->positions[i] + ll->positions[i + 1]) / 2) - subLineStart) ||
|
|
|
|
|
IsEOLChar(ll->chars[i])) {
|
|
|
|
|
return pdoc->MovePositionOutsideChar(i + posLineStart, 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (pt.x < (ll->positions[lineEnd] - subLineStart)) {
|
|
|
|
|
return pdoc->MovePositionOutsideChar(lineEnd + posLineStart, 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return INVALID_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.
|
|
|
|
|
*/
|
|
|
|
|
int Editor::PositionFromLineX(int lineDoc, int x) {
|
|
|
|
|
RefreshStyleData();
|
|
|
|
|
if (lineDoc >= pdoc->LinesTotal())
|
|
|
|
|
return 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));
|
|
|
|
|
int retVal = 0;
|
|
|
|
|
if (surface && ll) {
|
|
|
|
|
unsigned int posLineStart = pdoc->LineStart(lineDoc);
|
|
|
|
|
LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
|
|
|
|
|
retVal = ll->numCharsInLine + posLineStart;
|
|
|
|
|
int subLine = 0;
|
|
|
|
|
int lineStart = ll->LineStart(subLine);
|
|
|
|
|
int lineEnd = ll->LineStart(subLine + 1);
|
|
|
|
|
int subLineStart = ll->positions[lineStart];
|
|
|
|
|
|
|
|
|
|
if (actualWrapVisualStartIndent != 0) {
|
|
|
|
|
if (lineStart != 0) // Wrapped
|
|
|
|
|
x -= actualWrapVisualStartIndent * vs.aveCharWidth;
|
|
|
|
|
}
|
|
|
|
|
for (int i = lineStart; i < lineEnd; i++) {
|
|
|
|
|
if (x < (((ll->positions[i] + ll->positions[i + 1]) / 2) - subLineStart) ||
|
|
|
|
|
IsEOLChar(ll->chars[i])) {
|
|
|
|
|
retVal = pdoc->MovePositionOutsideChar(i + posLineStart, 1);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return retVal;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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::Redraw() {
|
|
|
|
|
//Platform::DebugPrintf("Redraw all\n");
|
|
|
|
|
PRectangle rcClient = GetClientRectangle();
|
|
|
|
|
wMain.InvalidateRectangle(rcClient);
|
|
|
|
|
//wMain.InvalidateAll();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::RedrawSelMargin(int line) {
|
|
|
|
|
if (!AbandonPaint()) {
|
|
|
|
|
if (vs.maskInLine) {
|
|
|
|
|
Redraw();
|
|
|
|
|
} else {
|
|
|
|
|
PRectangle rcSelMargin = GetClientRectangle();
|
|
|
|
|
rcSelMargin.right = vs.fixedColumnWidth;
|
|
|
|
|
if (line != -1) {
|
|
|
|
|
int position = pdoc->LineStart(line);
|
|
|
|
|
PRectangle rcLine = RectangleFromRange(position, position);
|
|
|
|
|
rcSelMargin.top = rcLine.top;
|
|
|
|
|
rcSelMargin.bottom = rcLine.bottom;
|
|
|
|
|
}
|
|
|
|
|
wMain.InvalidateRectangle(rcSelMargin);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PRectangle Editor::RectangleFromRange(int start, int end) {
|
|
|
|
|
int minPos = start;
|
|
|
|
|
if (minPos > end)
|
|
|
|
|
minPos = end;
|
|
|
|
|
int maxPos = start;
|
|
|
|
|
if (maxPos < end)
|
|
|
|
|
maxPos = end;
|
|
|
|
|
int minLine = cs.DisplayFromDoc(pdoc->LineFromPosition(minPos));
|
|
|
|
|
int lineDocMax = pdoc->LineFromPosition(maxPos);
|
|
|
|
|
int maxLine = cs.DisplayFromDoc(lineDocMax) + cs.GetHeight(lineDocMax) - 1;
|
|
|
|
|
PRectangle rcClient = GetTextRectangle();
|
|
|
|
|
PRectangle rc;
|
|
|
|
|
rc.left = vs.fixedColumnWidth;
|
|
|
|
|
rc.top = (minLine - topLine) * vs.lineHeight;
|
|
|
|
|
if (rc.top < 0)
|
|
|
|
|
rc.top = 0;
|
|
|
|
|
rc.right = rcClient.right;
|
|
|
|
|
rc.bottom = (maxLine - topLine + 1) * vs.lineHeight;
|
|
|
|
|
// Ensure PRectangle is within 16 bit space
|
|
|
|
|
rc.top = Platform::Clamp(rc.top, -32000, 32000);
|
|
|
|
|
rc.bottom = Platform::Clamp(rc.bottom, -32000, 32000);
|
|
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::InvalidateRange(int start, int end) {
|
|
|
|
|
RedrawRect(RectangleFromRange(start, end));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Editor::CurrentPosition() {
|
|
|
|
|
return currentPos;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Editor::SelectionEmpty() {
|
|
|
|
|
return anchor == currentPos;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Editor::SelectionStart() {
|
|
|
|
|
return Platform::Minimum(currentPos, anchor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Editor::SelectionEnd() {
|
|
|
|
|
return Platform::Maximum(currentPos, anchor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::SetRectangularRange() {
|
|
|
|
|
if (selType == selRectangle) {
|
|
|
|
|
xStartSelect = XFromPosition(anchor);
|
|
|
|
|
xEndSelect = XFromPosition(currentPos);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::InvalidateSelection(int currentPos_, int anchor_) {
|
|
|
|
|
int firstAffected = anchor;
|
|
|
|
|
if (firstAffected > currentPos)
|
|
|
|
|
firstAffected = currentPos;
|
|
|
|
|
if (firstAffected > anchor_)
|
|
|
|
|
firstAffected = anchor_;
|
|
|
|
|
if (firstAffected > currentPos_)
|
|
|
|
|
firstAffected = currentPos_;
|
|
|
|
|
int lastAffected = anchor;
|
|
|
|
|
if (lastAffected < currentPos)
|
|
|
|
|
lastAffected = currentPos;
|
|
|
|
|
if (lastAffected < anchor_)
|
|
|
|
|
lastAffected = anchor_;
|
|
|
|
|
if (lastAffected < (currentPos_ + 1)) // +1 ensures caret repainted
|
|
|
|
|
lastAffected = (currentPos_ + 1);
|
|
|
|
|
needUpdateUI = true;
|
|
|
|
|
InvalidateRange(firstAffected, lastAffected);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::SetSelection(int currentPos_, int anchor_) {
|
|
|
|
|
currentPos_ = pdoc->ClampPositionIntoDocument(currentPos_);
|
|
|
|
|
anchor_ = pdoc->ClampPositionIntoDocument(anchor_);
|
|
|
|
|
if ((currentPos != currentPos_) || (anchor != anchor_)) {
|
|
|
|
|
InvalidateSelection(currentPos_, anchor_);
|
|
|
|
|
currentPos = currentPos_;
|
|
|
|
|
anchor = anchor_;
|
|
|
|
|
}
|
|
|
|
|
SetRectangularRange();
|
|
|
|
|
ClaimSelection();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::SetSelection(int currentPos_) {
|
|
|
|
|
currentPos_ = pdoc->ClampPositionIntoDocument(currentPos_);
|
|
|
|
|
if (currentPos != currentPos_) {
|
|
|
|
|
InvalidateSelection(currentPos_, currentPos_);
|
|
|
|
|
currentPos = currentPos_;
|
|
|
|
|
}
|
|
|
|
|
SetRectangularRange();
|
|
|
|
|
ClaimSelection();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::SetEmptySelection(int currentPos_) {
|
|
|
|
|
selType = selStream;
|
|
|
|
|
moveExtendsSelection = false;
|
|
|
|
|
SetSelection(currentPos_, 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() {
|
|
|
|
|
// DONE, but untested...: make support rectangular selection
|
|
|
|
|
bool scp = false;
|
|
|
|
|
if (selType == selStream) {
|
|
|
|
|
scp = RangeContainsProtected(anchor, currentPos);
|
|
|
|
|
} else {
|
|
|
|
|
SelectionLineIterator lineIterator(this);
|
|
|
|
|
while (lineIterator.Iterate()) {
|
|
|
|
|
if (RangeContainsProtected(lineIterator.startPos, lineIterator.endPos)) {
|
|
|
|
|
scp = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return scp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Asks document to find a good position and then moves out of any invisible positions.
|
|
|
|
|
*/
|
|
|
|
|
int Editor::MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd) {
|
|
|
|
|
pos = pdoc->MovePositionOutsideChar(pos, moveDir, checkLineEnd);
|
|
|
|
|
if (vs.ProtectionActive()) {
|
|
|
|
|
int mask = pdoc->stylingBitsMask;
|
|
|
|
|
if (moveDir > 0) {
|
|
|
|
|
if ((pos > 0) && vs.styles[pdoc->StyleAt(pos - 1) & mask].IsProtected()) {
|
|
|
|
|
while ((pos < pdoc->Length()) &&
|
|
|
|
|
(vs.styles[pdoc->StyleAt(pos) & mask].IsProtected()))
|
|
|
|
|
pos++;
|
|
|
|
|
}
|
|
|
|
|
} else if (moveDir < 0) {
|
|
|
|
|
if (vs.styles[pdoc->StyleAt(pos) & mask].IsProtected()) {
|
|
|
|
|
while ((pos > 0) &&
|
|
|
|
|
(vs.styles[pdoc->StyleAt(pos - 1) & mask].IsProtected()))
|
|
|
|
|
pos--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return pos;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Editor::MovePositionTo(int newPos, selTypes sel, bool ensureVisible) {
|
|
|
|
|
int delta = newPos - currentPos;
|
|
|
|
|
newPos = pdoc->ClampPositionIntoDocument(newPos);
|
|
|
|
|
newPos = MovePositionOutsideChar(newPos, delta);
|
|
|
|
|
if (sel != noSel) {
|
|
|
|
|
selType = sel;
|
|
|
|
|
}
|
|
|
|
|
if (sel != noSel || moveExtendsSelection) {
|
|
|
|
|
SetSelection(newPos);
|
|
|
|
|
} else {
|
|
|
|
|
SetEmptySelection(newPos);
|
|
|
|
|
}
|
|
|
|
|
ShowCaretAtCurrentPosition();
|
|
|
|
|
if (ensureVisible) {
|
|
|
|
|
EnsureCaretVisible();
|
|
|
|
|
}
|
|
|
|
|
NotifyMove(newPos);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Editor::MovePositionSoVisible(int pos, int moveDir) {
|
|
|
|
|
pos = pdoc->ClampPositionIntoDocument(pos);
|
|
|
|
|
pos = MovePositionOutsideChar(pos, moveDir);
|
|
|
|
|
int lineDoc = pdoc->LineFromPosition(pos);
|
|
|
|
|
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 pdoc->LineStart(cs.DocFromDisplay(lineDisplay));
|
|
|
|
|
} else {
|
|
|
|
|
lineDisplay = Platform::Clamp(lineDisplay - 1, 0, cs.LinesDisplayed());
|
|
|
|
|
return pdoc->LineEnd(cs.DocFromDisplay(lineDisplay));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Choose the x position that the caret will try to stick to
|
|
|
|
|
* as it moves up and down.
|
|
|
|
|
*/
|
|
|
|
|
void Editor::SetLastXChosen() {
|
|
|
|
|
Point pt = LocationFromPosition(currentPos);
|
|
|
|
|
lastXChosen = pt.x;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::ScrollTo(int line, bool moveThumb) {
|
|
|
|
|
int topLineNew = Platform::Clamp(line, 0, MaxScrollPos());
|
|
|
|
|
if (topLineNew != topLine) {
|
|
|
|
|
// Try to optimise small scrolls
|
|
|
|
|
int linesToMove = topLine - topLineNew;
|
|
|
|
|
SetTopLine(topLineNew);
|
|
|
|
|
ShowCaretAtCurrentPosition();
|
|
|
|
|
// Perform redraw rather than scroll if many lines would be redrawn anyway.
|
|
|
|
|
#ifndef UNDER_CE
|
|
|
|
|
if (abs(linesToMove) <= 10) {
|
|
|
|
|
ScrollText(linesToMove);
|
|
|
|
|
} else {
|
|
|
|
|
Redraw();
|
|
|
|
|
}
|
|
|
|
|
#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 ((wrapState == eWrapNone) && (xOffset != xPos)) {
|
|
|
|
|
xOffset = xPos;
|
|
|
|
|
SetHorizontalScrollPos();
|
|
|
|
|
RedrawRect(GetClientRectangle());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::MoveCaretInsideView(bool ensureVisible) {
|
|
|
|
|
PRectangle rcClient = GetTextRectangle();
|
|
|
|
|
Point pt = LocationFromPosition(currentPos);
|
|
|
|
|
if (pt.y < rcClient.top) {
|
|
|
|
|
MovePositionTo(PositionFromLocation(
|
|
|
|
|
Point(lastXChosen, rcClient.top)),
|
|
|
|
|
noSel, ensureVisible);
|
|
|
|
|
} else if ((pt.y + vs.lineHeight - 1) > rcClient.bottom) {
|
|
|
|
|
int yOfLastLineFullyDisplayed = rcClient.top + (LinesOnScreen() - 1) * vs.lineHeight;
|
|
|
|
|
MovePositionTo(PositionFromLocation(
|
|
|
|
|
Point(lastXChosen, rcClient.top + yOfLastLineFullyDisplayed)),
|
|
|
|
|
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 begining 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<EFBFBD>(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
|
|
|
|
|
*/
|
|
|
|
|
void Editor::EnsureCaretVisible(bool useMargin, bool vert, bool horiz) {
|
|
|
|
|
//Platform::DebugPrintf("EnsureCaretVisible %d %s\n", xOffset, useMargin ? " margin" : " ");
|
|
|
|
|
PRectangle rcClient = GetTextRectangle();
|
|
|
|
|
//int rcClientFullWidth = rcClient.Width();
|
|
|
|
|
int posCaret = currentPos;
|
|
|
|
|
if (posDrag >= 0) {
|
|
|
|
|
posCaret = posDrag;
|
|
|
|
|
}
|
|
|
|
|
Point pt = LocationFromPosition(posCaret);
|
|
|
|
|
Point ptBottomCaret = pt;
|
|
|
|
|
ptBottomCaret.y += vs.lineHeight - 1;
|
|
|
|
|
int lineCaret = DisplayFromPosition(posCaret);
|
|
|
|
|
bool bSlop, bStrict, bJump, bEven;
|
|
|
|
|
|
|
|
|
|
//Qt::Vertical positioning
|
|
|
|
|
if (vert && (pt.y < rcClient.top || ptBottomCaret.y > rcClient.bottom || (caretYPolicy & CARET_STRICT) != 0)) {
|
|
|
|
|
int linesOnScreen = LinesOnScreen();
|
|
|
|
|
int halfScreen = Platform::Maximum(linesOnScreen - 1, 2) / 2;
|
|
|
|
|
int newTopLine = topLine;
|
|
|
|
|
bSlop = (caretYPolicy & CARET_SLOP) != 0;
|
|
|
|
|
bStrict = (caretYPolicy & CARET_STRICT) != 0;
|
|
|
|
|
bJump = (caretYPolicy & CARET_JUMPS) != 0;
|
|
|
|
|
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 (!useMargin) {
|
|
|
|
|
// 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 height 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
|
|
|
|
|
newTopLine = lineCaret - yMoveT;
|
|
|
|
|
} else if (lineCaret > topLine + linesOnScreen - 1 - yMarginB) {
|
|
|
|
|
// Caret goes too low
|
|
|
|
|
newTopLine = 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
|
|
|
|
|
newTopLine = lineCaret - yMoveT;
|
|
|
|
|
} else if (lineCaret > topLine + linesOnScreen - 1) {
|
|
|
|
|
// Caret goes too low
|
|
|
|
|
newTopLine = lineCaret - linesOnScreen + 1 + yMoveB;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else { // No slop
|
|
|
|
|
if (!bStrict && !bJump) {
|
|
|
|
|
// Minimal move
|
|
|
|
|
if (lineCaret < topLine) {
|
|
|
|
|
// Caret goes too high
|
|
|
|
|
newTopLine = lineCaret;
|
|
|
|
|
} else if (lineCaret > topLine + linesOnScreen - 1) {
|
|
|
|
|
// Caret goes too low
|
|
|
|
|
if (bEven) {
|
|
|
|
|
newTopLine = lineCaret - linesOnScreen + 1;
|
|
|
|
|
} else {
|
|
|
|
|
newTopLine = lineCaret;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else { // Strict or going out of display
|
|
|
|
|
if (bEven) {
|
|
|
|
|
// Always center caret
|
|
|
|
|
newTopLine = lineCaret - halfScreen;
|
|
|
|
|
} else {
|
|
|
|
|
// Always put caret on top of display
|
|
|
|
|
newTopLine = lineCaret;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
newTopLine = Platform::Clamp(newTopLine, 0, MaxScrollPos());
|
|
|
|
|
if (newTopLine != topLine) {
|
|
|
|
|
Redraw();
|
|
|
|
|
SetTopLine(newTopLine);
|
|
|
|
|
SetVerticalScrollPos();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Qt::Horizontal positioning
|
|
|
|
|
if (horiz && (wrapState == eWrapNone)) {
|
|
|
|
|
int halfScreen = Platform::Maximum(rcClient.Width() - 4, 4) / 2;
|
|
|
|
|
int xOffsetNew = xOffset;
|
|
|
|
|
bSlop = (caretXPolicy & CARET_SLOP) != 0;
|
|
|
|
|
bStrict = (caretXPolicy & CARET_STRICT) != 0;
|
|
|
|
|
bJump = (caretXPolicy & CARET_JUMPS) != 0;
|
|
|
|
|
bEven = (caretXPolicy & CARET_EVEN) != 0;
|
|
|
|
|
|
|
|
|
|
if (bSlop) { // A margin is defined
|
|
|
|
|
int xMoveL, xMoveR;
|
|
|
|
|
if (bStrict) {
|
|
|
|
|
int xMarginL, xMarginR;
|
|
|
|
|
if (!useMargin) {
|
|
|
|
|
// 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 = 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) {
|
|
|
|
|
xOffsetNew -= xMoveL;
|
|
|
|
|
} else {
|
|
|
|
|
// Move just enough to allow to display the caret
|
|
|
|
|
xOffsetNew -= (rcClient.left + xMarginL) - pt.x;
|
|
|
|
|
}
|
|
|
|
|
} else if (pt.x >= rcClient.right - xMarginR) {
|
|
|
|
|
// Caret is on the right of the display
|
|
|
|
|
if (bJump && bEven) {
|
|
|
|
|
xOffsetNew += xMoveR;
|
|
|
|
|
} else {
|
|
|
|
|
// Move just enough to allow to display the caret
|
|
|
|
|
xOffsetNew += 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 = rcClient.Width() - xMoveR - 4;
|
|
|
|
|
}
|
|
|
|
|
if (pt.x < rcClient.left) {
|
|
|
|
|
// Caret is on the left of the display
|
|
|
|
|
xOffsetNew -= xMoveL;
|
|
|
|
|
} else if (pt.x >= rcClient.right) {
|
|
|
|
|
// Caret is on the right of the display
|
|
|
|
|
xOffsetNew += 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
|
|
|
|
|
xOffsetNew += pt.x - rcClient.left - halfScreen;
|
|
|
|
|
} else {
|
|
|
|
|
// Put caret on right
|
|
|
|
|
xOffsetNew += 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) {
|
|
|
|
|
xOffsetNew -= rcClient.left - pt.x;
|
|
|
|
|
} else {
|
|
|
|
|
xOffsetNew += pt.x - rcClient.right + 1;
|
|
|
|
|
}
|
|
|
|
|
} else if (pt.x >= rcClient.right) {
|
|
|
|
|
// Caret is on the right of the display
|
|
|
|
|
xOffsetNew += 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 + xOffsetNew) {
|
|
|
|
|
xOffsetNew = pt.x + xOffset - rcClient.left;
|
|
|
|
|
} else if (pt.x + xOffset >= rcClient.right + xOffsetNew) {
|
|
|
|
|
xOffsetNew = pt.x + xOffset - rcClient.right + 1;
|
|
|
|
|
}
|
|
|
|
|
if (xOffsetNew < 0) {
|
|
|
|
|
xOffsetNew = 0;
|
|
|
|
|
}
|
|
|
|
|
if (xOffset != xOffsetNew) {
|
|
|
|
|
xOffset = xOffsetNew;
|
|
|
|
|
if (xOffsetNew > 0) {
|
|
|
|
|
PRectangle rcText = GetTextRectangle();
|
|
|
|
|
if (horizontalScrollBarVisible == true &&
|
|
|
|
|
rcText.Width() + xOffset > scrollWidth) {
|
|
|
|
|
scrollWidth = xOffset + rcText.Width();
|
|
|
|
|
SetScrollBars();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
SetHorizontalScrollPos();
|
|
|
|
|
Redraw();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
UpdateSystemCaret();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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::InvalidateCaret() {
|
|
|
|
|
if (posDrag >= 0)
|
|
|
|
|
InvalidateRange(posDrag, posDrag + 1);
|
|
|
|
|
else
|
|
|
|
|
InvalidateRange(currentPos, currentPos + 1);
|
|
|
|
|
UpdateSystemCaret();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::UpdateSystemCaret() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::NeedWrapping(int docLineStart, int docLineEnd) {
|
|
|
|
|
docLineStart = Platform::Clamp(docLineStart, 0, pdoc->LinesTotal());
|
|
|
|
|
if (wrapStart > docLineStart) {
|
|
|
|
|
wrapStart = docLineStart;
|
|
|
|
|
llc.Invalidate(LineLayout::llPositions);
|
|
|
|
|
}
|
|
|
|
|
if (wrapEnd < docLineEnd) {
|
|
|
|
|
wrapEnd = docLineEnd;
|
|
|
|
|
}
|
|
|
|
|
wrapEnd = Platform::Clamp(wrapEnd, 0, pdoc->LinesTotal());
|
|
|
|
|
// Wrap lines during idle.
|
|
|
|
|
if ((wrapState != eWrapNone) && (wrapEnd != wrapStart)) {
|
|
|
|
|
SetIdle(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if wrapping needed and perform any needed wrapping.
|
|
|
|
|
// fullwrap: if true, all lines which need wrapping will be done,
|
|
|
|
|
// in this single call.
|
|
|
|
|
// priorityWrapLineStart: If greater than zero, all lines starting from
|
|
|
|
|
// here to 1 page + 100 lines past will be wrapped (even if there are
|
|
|
|
|
// more lines under wrapping process in idle).
|
|
|
|
|
// If it is neither fullwrap, nor priorityWrap, then 1 page + 100 lines will be
|
|
|
|
|
// wrapped, if there are any wrapping going on in idle. (Generally this
|
|
|
|
|
// condition is called only from idler).
|
|
|
|
|
// Return true if wrapping occurred.
|
|
|
|
|
bool Editor::WrapLines(bool fullWrap, int priorityWrapLineStart) {
|
|
|
|
|
// If there are any pending wraps, do them during idle if possible.
|
|
|
|
|
int linesInOneCall = LinesOnScreen() + 100;
|
|
|
|
|
if (wrapState != eWrapNone) {
|
|
|
|
|
if (wrapStart < wrapEnd) {
|
|
|
|
|
if (!SetIdle(true)) {
|
|
|
|
|
// Idle processing not supported so full wrap required.
|
|
|
|
|
fullWrap = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!fullWrap && priorityWrapLineStart >= 0 &&
|
|
|
|
|
// .. and if the paint window is outside pending wraps
|
|
|
|
|
(((priorityWrapLineStart + linesInOneCall) < wrapStart) ||
|
|
|
|
|
(priorityWrapLineStart > wrapEnd))) {
|
|
|
|
|
// No priority wrap pending
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
int goodTopLine = topLine;
|
|
|
|
|
bool wrapOccurred = false;
|
|
|
|
|
if (wrapStart <= pdoc->LinesTotal()) {
|
|
|
|
|
if (wrapState == eWrapNone) {
|
|
|
|
|
if (wrapWidth != LineLayout::wrapWidthInfinite) {
|
|
|
|
|
wrapWidth = LineLayout::wrapWidthInfinite;
|
|
|
|
|
for (int lineDoc = 0; lineDoc < pdoc->LinesTotal(); lineDoc++) {
|
|
|
|
|
cs.SetHeight(lineDoc, 1);
|
|
|
|
|
}
|
|
|
|
|
wrapOccurred = true;
|
|
|
|
|
}
|
|
|
|
|
wrapStart = wrapLineLarge;
|
|
|
|
|
wrapEnd = wrapLineLarge;
|
|
|
|
|
} else {
|
|
|
|
|
if (wrapEnd >= pdoc->LinesTotal())
|
|
|
|
|
wrapEnd = pdoc->LinesTotal();
|
|
|
|
|
//ElapsedTime et;
|
|
|
|
|
int lineDocTop = cs.DocFromDisplay(topLine);
|
|
|
|
|
int subLineTop = topLine - cs.DisplayFromDoc(lineDocTop);
|
|
|
|
|
PRectangle rcTextArea = GetClientRectangle();
|
|
|
|
|
rcTextArea.left = vs.fixedColumnWidth;
|
|
|
|
|
rcTextArea.right -= vs.rightMarginWidth;
|
|
|
|
|
wrapWidth = rcTextArea.Width();
|
|
|
|
|
// Ensure all of the document is styled.
|
|
|
|
|
pdoc->EnsureStyledTo(pdoc->Length());
|
|
|
|
|
RefreshStyleData();
|
|
|
|
|
AutoSurface surface(this);
|
|
|
|
|
if (surface) {
|
|
|
|
|
bool priorityWrap = false;
|
|
|
|
|
int lastLineToWrap = wrapEnd;
|
|
|
|
|
int lineToWrap = wrapStart;
|
|
|
|
|
if (!fullWrap) {
|
|
|
|
|
if (priorityWrapLineStart >= 0) {
|
|
|
|
|
// This is a priority wrap.
|
|
|
|
|
lineToWrap = priorityWrapLineStart;
|
|
|
|
|
lastLineToWrap = priorityWrapLineStart + linesInOneCall;
|
|
|
|
|
priorityWrap = true;
|
|
|
|
|
} else {
|
|
|
|
|
// This is idle wrap.
|
|
|
|
|
lastLineToWrap = wrapStart + linesInOneCall;
|
|
|
|
|
}
|
|
|
|
|
if (lastLineToWrap >= wrapEnd)
|
|
|
|
|
lastLineToWrap = wrapEnd;
|
|
|
|
|
} // else do a fullWrap.
|
|
|
|
|
|
|
|
|
|
// Platform::DebugPrintf("Wraplines: full = %d, priorityStart = %d (wrapping: %d to %d)\n", fullWrap, priorityWrapLineStart, lineToWrap, lastLineToWrap);
|
|
|
|
|
// Platform::DebugPrintf("Pending wraps: %d to %d\n", wrapStart, wrapEnd);
|
|
|
|
|
while (lineToWrap < lastLineToWrap) {
|
|
|
|
|
AutoLineLayout ll(llc, RetrieveLineLayout(lineToWrap));
|
|
|
|
|
int linesWrapped = 1;
|
|
|
|
|
if (ll) {
|
|
|
|
|
LayoutLine(lineToWrap, surface, vs, ll, wrapWidth);
|
|
|
|
|
linesWrapped = ll->lines;
|
|
|
|
|
}
|
|
|
|
|
if (cs.SetHeight(lineToWrap, linesWrapped)) {
|
|
|
|
|
wrapOccurred = true;
|
|
|
|
|
}
|
|
|
|
|
lineToWrap++;
|
|
|
|
|
}
|
|
|
|
|
if (!priorityWrap)
|
|
|
|
|
wrapStart = lineToWrap;
|
|
|
|
|
// If wrapping is done, bring it to resting position
|
|
|
|
|
if (wrapStart >= wrapEnd) {
|
|
|
|
|
wrapStart = wrapLineLarge;
|
|
|
|
|
wrapEnd = wrapLineLarge;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
goodTopLine = cs.DisplayFromDoc(lineDocTop);
|
|
|
|
|
if (subLineTop < cs.GetHeight(lineDocTop))
|
|
|
|
|
goodTopLine += subLineTop;
|
|
|
|
|
else
|
|
|
|
|
goodTopLine += cs.GetHeight(lineDocTop);
|
|
|
|
|
//double durWrap = et.Duration(true);
|
|
|
|
|
//Platform::DebugPrintf("Wrap:%9.6g \n", durWrap);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (wrapOccurred) {
|
|
|
|
|
SetScrollBars();
|
|
|
|
|
SetTopLine(Platform::Clamp(goodTopLine, 0, MaxScrollPos()));
|
|
|
|
|
SetVerticalScrollPos();
|
|
|
|
|
}
|
|
|
|
|
return wrapOccurred;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::LinesJoin() {
|
|
|
|
|
if (!RangeContainsProtected(targetStart, targetEnd)) {
|
|
|
|
|
pdoc->BeginUndoAction();
|
|
|
|
|
bool prevNonWS = true;
|
|
|
|
|
for (int pos = targetStart; pos < targetEnd; pos++) {
|
|
|
|
|
if (IsEOLChar(pdoc->CharAt(pos))) {
|
|
|
|
|
targetEnd -= pdoc->LenChar(pos);
|
|
|
|
|
pdoc->DelChar(pos);
|
|
|
|
|
if (prevNonWS) {
|
|
|
|
|
// Ensure at least one space separating previous lines
|
|
|
|
|
pdoc->InsertChar(pos, ' ');
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
prevNonWS = pdoc->CharAt(pos) != ' ';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
pdoc->EndUndoAction();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *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 = rcText.Width();
|
|
|
|
|
}
|
|
|
|
|
int lineStart = pdoc->LineFromPosition(targetStart);
|
|
|
|
|
int lineEnd = pdoc->LineFromPosition(targetEnd);
|
|
|
|
|
const char *eol = StringFromEOLMode(pdoc->eolMode);
|
|
|
|
|
pdoc->BeginUndoAction();
|
|
|
|
|
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);
|
|
|
|
|
for (int subLine = 1; subLine < ll->lines; subLine++) {
|
|
|
|
|
pdoc->InsertString(posLineStart + (subLine - 1) * strlen(eol) +
|
|
|
|
|
ll->LineStart(subLine), eol);
|
|
|
|
|
targetEnd += static_cast<int>(strlen(eol));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
lineEnd = pdoc->LineFromPosition(targetEnd);
|
|
|
|
|
}
|
|
|
|
|
pdoc->EndUndoAction();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Editor::SubstituteMarkerIfEmpty(int markerCheck, int markerDefault) {
|
|
|
|
|
if (vs.markers[markerCheck].markType == SC_MARK_EMPTY)
|
|
|
|
|
return markerDefault;
|
|
|
|
|
return markerCheck;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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>(strlen(s));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::PaintSelMargin(Surface *surfWindow, PRectangle &rc) {
|
|
|
|
|
if (vs.fixedColumnWidth == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
PRectangle rcMargin = GetClientRectangle();
|
|
|
|
|
rcMargin.right = vs.fixedColumnWidth;
|
|
|
|
|
|
|
|
|
|
if (!rc.Intersects(rcMargin))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
Surface *surface;
|
|
|
|
|
if (bufferedDraw) {
|
|
|
|
|
surface = pixmapSelMargin;
|
|
|
|
|
} else {
|
|
|
|
|
surface = surfWindow;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PRectangle rcSelMargin = rcMargin;
|
|
|
|
|
rcSelMargin.right = rcMargin.left;
|
|
|
|
|
|
|
|
|
|
for (int margin = 0; margin < vs.margins; 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) {
|
|
|
|
|
/* alternate scheme:
|
|
|
|
|
if (vs.ms[margin].mask & SC_MASK_FOLDERS)
|
|
|
|
|
surface->FillRectangle(rcSelMargin, vs.styles[STYLE_DEFAULT].back.allocated);
|
|
|
|
|
else
|
|
|
|
|
// Required because of special way brush is created for selection margin
|
|
|
|
|
surface->FillRectangle(rcSelMargin, pixmapSelPattern);
|
|
|
|
|
*/
|
|
|
|
|
if (vs.ms[margin].mask & SC_MASK_FOLDERS)
|
|
|
|
|
// Required because of special way brush is created for selection margin
|
|
|
|
|
surface->FillRectangle(rcSelMargin, *pixmapSelPattern);
|
|
|
|
|
else {
|
|
|
|
|
ColourAllocated colour;
|
|
|
|
|
switch (vs.ms[margin].style) {
|
|
|
|
|
case SC_MARGIN_BACK:
|
|
|
|
|
colour = vs.styles[STYLE_DEFAULT].back.allocated;
|
|
|
|
|
break;
|
|
|
|
|
case SC_MARGIN_FORE:
|
|
|
|
|
colour = vs.styles[STYLE_DEFAULT].fore.allocated;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
colour = vs.styles[STYLE_LINENUMBER].back.allocated;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
surface->FillRectangle(rcSelMargin, colour);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
surface->FillRectangle(rcSelMargin, vs.styles[STYLE_LINENUMBER].back.allocated);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int visibleLine = topLine;
|
|
|
|
|
int yposScreen = 0;
|
|
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
int level = pdoc->GetLevel(cs.DocFromDisplay(topLine));
|
|
|
|
|
if (level & SC_FOLDLEVELWHITEFLAG) {
|
|
|
|
|
int lineBack = cs.DocFromDisplay(topLine);
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Old code does not know about new markers needed to distinguish all cases
|
|
|
|
|
int folderOpenMid = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEROPENMID,
|
|
|
|
|
SC_MARKNUM_FOLDEROPEN);
|
|
|
|
|
int folderEnd = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEREND,
|
|
|
|
|
SC_MARKNUM_FOLDER);
|
|
|
|
|
|
|
|
|
|
while ((visibleLine < cs.LinesDisplayed()) && yposScreen < rcMargin.bottom) {
|
|
|
|
|
|
|
|
|
|
PLATFORM_ASSERT(visibleLine < cs.LinesDisplayed());
|
|
|
|
|
|
|
|
|
|
int lineDoc = cs.DocFromDisplay(visibleLine);
|
|
|
|
|
PLATFORM_ASSERT(cs.GetVisible(lineDoc));
|
|
|
|
|
bool firstSubLine = visibleLine == cs.DisplayFromDoc(lineDoc);
|
|
|
|
|
|
|
|
|
|
// Decide which fold indicator should be displayed
|
|
|
|
|
level = pdoc->GetLevel(lineDoc);
|
|
|
|
|
int levelNext = pdoc->GetLevel(lineDoc + 1);
|
|
|
|
|
int marks = pdoc->GetMark(lineDoc);
|
|
|
|
|
if (!firstSubLine)
|
|
|
|
|
marks = 0;
|
|
|
|
|
int levelNum = level & SC_FOLDLEVELNUMBERMASK;
|
|
|
|
|
int levelNextNum = levelNext & SC_FOLDLEVELNUMBERMASK;
|
|
|
|
|
if (level & SC_FOLDLEVELHEADERFLAG) {
|
|
|
|
|
if (firstSubLine) {
|
|
|
|
|
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 {
|
|
|
|
|
marks |= 1 << SC_MARKNUM_FOLDERSUB;
|
|
|
|
|
}
|
|
|
|
|
needWhiteClosure = false;
|
|
|
|
|
} else if (level & SC_FOLDLEVELWHITEFLAG) {
|
|
|
|
|
if (needWhiteClosure) {
|
|
|
|
|
if (levelNext & SC_FOLDLEVELWHITEFLAG) {
|
|
|
|
|
marks |= 1 << SC_MARKNUM_FOLDERSUB;
|
|
|
|
|
} else if (levelNum > 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 (levelNextNum > SC_FOLDLEVELBASE) {
|
|
|
|
|
marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
|
|
|
|
|
} else {
|
|
|
|
|
marks |= 1 << SC_MARKNUM_FOLDERTAIL;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
marks |= 1 << SC_MARKNUM_FOLDERSUB;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
marks &= vs.ms[margin].mask;
|
|
|
|
|
PRectangle rcMarker = rcSelMargin;
|
|
|
|
|
rcMarker.top = yposScreen;
|
|
|
|
|
rcMarker.bottom = yposScreen + vs.lineHeight;
|
|
|
|
|
if (vs.ms[margin].style == SC_MARGIN_NUMBER) {
|
|
|
|
|
char number[100];
|
|
|
|
|
number[0] = '\0';
|
|
|
|
|
if (firstSubLine)
|
|
|
|
|
sprintf(number, "%d", lineDoc + 1);
|
|
|
|
|
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
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
PRectangle rcNumber = rcMarker;
|
|
|
|
|
// Right justify
|
|
|
|
|
int width = surface->WidthText(vs.styles[STYLE_LINENUMBER].font, number, istrlen(number));
|
|
|
|
|
int xpos = rcNumber.right - width - 3;
|
|
|
|
|
rcNumber.left = xpos;
|
|
|
|
|
surface->DrawTextNoClip(rcNumber, vs.styles[STYLE_LINENUMBER].font,
|
|
|
|
|
rcNumber.top + vs.maxAscent, number, istrlen(number),
|
|
|
|
|
vs.styles[STYLE_LINENUMBER].fore.allocated,
|
|
|
|
|
vs.styles[STYLE_LINENUMBER].back.allocated);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (marks) {
|
|
|
|
|
for (int markBit = 0; (markBit < 32) && marks; markBit++) {
|
|
|
|
|
if (marks & 1) {
|
|
|
|
|
vs.markers[markBit].Draw(surface, rcMarker, vs.styles[STYLE_LINENUMBER].font);
|
|
|
|
|
}
|
|
|
|
|
marks >>= 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
visibleLine++;
|
|
|
|
|
yposScreen += vs.lineHeight;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PRectangle rcBlankMargin = rcMargin;
|
|
|
|
|
rcBlankMargin.left = rcSelMargin.right;
|
|
|
|
|
surface->FillRectangle(rcBlankMargin, vs.styles[STYLE_DEFAULT].back.allocated);
|
|
|
|
|
|
|
|
|
|
if (bufferedDraw) {
|
|
|
|
|
surfWindow->Copy(rcMargin, Point(), *pixmapSelMargin);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DrawTabArrow(Surface *surface, PRectangle rcTab, int ymid) {
|
|
|
|
|
int ydiff = (rcTab.bottom - rcTab.top) / 2;
|
|
|
|
|
int xhead = rcTab.right - 1 - ydiff;
|
|
|
|
|
if (xhead <= rcTab.left) {
|
|
|
|
|
ydiff -= rcTab.left - xhead - 1;
|
|
|
|
|
xhead = rcTab.left - 1;
|
|
|
|
|
}
|
|
|
|
|
if ((rcTab.left + 2) < (rcTab.right - 1))
|
|
|
|
|
surface->MoveTo(rcTab.left + 2, ymid);
|
|
|
|
|
else
|
|
|
|
|
surface->MoveTo(rcTab.right - 1, ymid);
|
|
|
|
|
surface->LineTo(rcTab.right - 1, ymid);
|
|
|
|
|
surface->LineTo(xhead, ymid - ydiff);
|
|
|
|
|
surface->MoveTo(rcTab.right - 1, ymid);
|
|
|
|
|
surface->LineTo(xhead, ymid + ydiff);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool IsSpaceOrTab(char ch) {
|
|
|
|
|
return ch == ' ' || ch == '\t';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LineLayout *Editor::RetrieveLineLayout(int lineNumber) {
|
|
|
|
|
int posLineStart = pdoc->LineStart(lineNumber);
|
|
|
|
|
int posLineEnd = pdoc->LineStart(lineNumber + 1);
|
|
|
|
|
int lineCaret = pdoc->LineFromPosition(currentPos);
|
|
|
|
|
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) {
|
|
|
|
|
if (!ll)
|
|
|
|
|
return;
|
|
|
|
|
PLATFORM_ASSERT(line < pdoc->LinesTotal());
|
|
|
|
|
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) {
|
|
|
|
|
int cid = posLineEnd - 1;
|
|
|
|
|
while ((cid > posLineStart) && IsEOLChar(pdoc->CharAt(cid))) {
|
|
|
|
|
cid--;
|
|
|
|
|
lineLength--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
int numCharsInLine = 0;
|
|
|
|
|
if (vstyle.edgeState == EDGE_BACKGROUND) {
|
|
|
|
|
ll->edgeColumn = pdoc->FindColumn(line, theEdge);
|
|
|
|
|
if (ll->edgeColumn >= posLineStart) {
|
|
|
|
|
ll->edgeColumn -= posLineStart;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
ll->edgeColumn = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char styleByte = 0;
|
|
|
|
|
int styleMask = pdoc->stylingBitsMask;
|
|
|
|
|
ll->styleBitsSet = 0;
|
|
|
|
|
// Fill base line layout
|
|
|
|
|
for (int charInDoc = posLineStart; charInDoc < posLineEnd; charInDoc++) {
|
|
|
|
|
char chDoc = pdoc->CharAt(charInDoc);
|
|
|
|
|
styleByte = pdoc->StyleAt(charInDoc);
|
|
|
|
|
ll->styleBitsSet |= styleByte;
|
|
|
|
|
if (vstyle.viewEOL || (!IsEOLChar(chDoc))) {
|
|
|
|
|
ll->chars[numCharsInLine] = chDoc;
|
|
|
|
|
ll->styles[numCharsInLine] = static_cast<char>(styleByte & styleMask);
|
|
|
|
|
ll->indicators[numCharsInLine] = static_cast<char>(styleByte & ~styleMask);
|
|
|
|
|
if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseUpper)
|
|
|
|
|
ll->chars[numCharsInLine] = static_cast<char>(toupper(chDoc));
|
|
|
|
|
else if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseLower)
|
|
|
|
|
ll->chars[numCharsInLine] = static_cast<char>(tolower(chDoc));
|
|
|
|
|
numCharsInLine++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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] = styleByte; // 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.
|
|
|
|
|
int startseg = 0; // Start of the current segment, in char. number
|
|
|
|
|
int startsegx = 0; // Start of the current segment, in pixels
|
|
|
|
|
ll->positions[0] = 0;
|
|
|
|
|
unsigned int tabWidth = vstyle.spaceWidth * pdoc->tabInChars;
|
|
|
|
|
bool lastSegItalics = false;
|
|
|
|
|
Font &ctrlCharsFont = vstyle.styles[STYLE_CONTROLCHAR].font;
|
|
|
|
|
|
|
|
|
|
int ctrlCharWidth[32] = {0};
|
|
|
|
|
bool isControlNext = IsControlCharacter(ll->chars[0]);
|
|
|
|
|
for (int charInLine = 0; charInLine < numCharsInLine; charInLine++) {
|
|
|
|
|
bool isControl = isControlNext;
|
|
|
|
|
isControlNext = IsControlCharacter(ll->chars[charInLine + 1]);
|
|
|
|
|
if ((ll->styles[charInLine] != ll->styles[charInLine + 1]) ||
|
|
|
|
|
isControl || isControlNext) {
|
|
|
|
|
ll->positions[startseg] = 0;
|
|
|
|
|
if (vstyle.styles[ll->styles[charInLine]].visible) {
|
|
|
|
|
if (isControl) {
|
|
|
|
|
if (ll->chars[charInLine] == '\t') {
|
|
|
|
|
ll->positions[charInLine + 1] = ((((startsegx + 2) /
|
|
|
|
|
tabWidth) + 1) * tabWidth) - startsegx;
|
|
|
|
|
} else if (controlCharSymbol < 32) {
|
|
|
|
|
if (ctrlCharWidth[ll->chars[charInLine]] == 0) {
|
|
|
|
|
const char *ctrlChar = ControlCharacterString(ll->chars[charInLine]);
|
|
|
|
|
// +3 For a blank on front and rounded edge each side:
|
|
|
|
|
ctrlCharWidth[ll->chars[charInLine]] =
|
|
|
|
|
surface->WidthText(ctrlCharsFont, ctrlChar, istrlen(ctrlChar)) + 3;
|
|
|
|
|
}
|
|
|
|
|
ll->positions[charInLine + 1] = ctrlCharWidth[ll->chars[charInLine]];
|
|
|
|
|
} else {
|
|
|
|
|
char cc[2] = { static_cast<char>(controlCharSymbol), '\0' };
|
|
|
|
|
surface->MeasureWidths(ctrlCharsFont, cc, 1,
|
|
|
|
|
ll->positions + startseg + 1);
|
|
|
|
|
}
|
|
|
|
|
lastSegItalics = false;
|
|
|
|
|
} else { // Regular character
|
|
|
|
|
int lenSeg = charInLine - startseg + 1;
|
|
|
|
|
if ((lenSeg == 1) && (' ' == ll->chars[startseg])) {
|
|
|
|
|
lastSegItalics = false;
|
|
|
|
|
// Over half the segments are single characters and of these about half are space characters.
|
|
|
|
|
ll->positions[charInLine + 1] = vstyle.styles[ll->styles[charInLine]].spaceWidth;
|
|
|
|
|
} else {
|
|
|
|
|
lastSegItalics = vstyle.styles[ll->styles[charInLine]].italic;
|
|
|
|
|
surface->MeasureWidths(vstyle.styles[ll->styles[charInLine]].font, ll->chars + startseg,
|
|
|
|
|
lenSeg, ll->positions + startseg + 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else { // invisible
|
|
|
|
|
for (int posToZero = startseg; posToZero <= (charInLine + 1); posToZero++) {
|
|
|
|
|
ll->positions[posToZero] = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (int posToIncrease = startseg; posToIncrease <= (charInLine + 1); posToIncrease++) {
|
|
|
|
|
ll->positions[posToIncrease] += startsegx;
|
|
|
|
|
}
|
|
|
|
|
startsegx = ll->positions[charInLine + 1];
|
|
|
|
|
startseg = charInLine + 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Small hack to make lines that end with italics not cut off the edge of the last character
|
|
|
|
|
if ((startseg > 0) && lastSegItalics) {
|
|
|
|
|
ll->positions[startseg] += 2;
|
|
|
|
|
}
|
|
|
|
|
ll->numCharsInLine = numCharsInLine;
|
|
|
|
|
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 (wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
|
|
|
|
|
width -= vstyle.aveCharWidth; // take into account the space for end wrap mark
|
|
|
|
|
}
|
|
|
|
|
ll->lines = 0;
|
|
|
|
|
// Calculate line start positions based upon width.
|
|
|
|
|
// For now this is simplistic - wraps on byte rather than character and
|
|
|
|
|
// in the middle of words. Should search for spaces or style changes.
|
|
|
|
|
int lastGoodBreak = 0;
|
|
|
|
|
int lastLineStart = 0;
|
|
|
|
|
int 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 -= actualWrapVisualStartIndent * vstyle.aveCharWidth;
|
|
|
|
|
p = lastGoodBreak + 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (p > 0) {
|
|
|
|
|
if (wrapState == eWrapChar) {
|
|
|
|
|
lastGoodBreak = pdoc->MovePositionOutsideChar(p + posLineStart, -1)
|
|
|
|
|
- posLineStart;
|
|
|
|
|
p = pdoc->MovePositionOutsideChar(p + 1 + posLineStart, 1) - posLineStart;
|
|
|
|
|
continue;
|
|
|
|
|
} else if (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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ColourAllocated Editor::SelectionBackground(ViewStyle &vsDraw) {
|
|
|
|
|
return primarySelection ? vsDraw.selbackground.allocated : vsDraw.selbackground2.allocated;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ColourAllocated Editor::TextBackground(ViewStyle &vsDraw, bool overrideBackground,
|
|
|
|
|
ColourAllocated background, bool inSelection, bool inHotspot, int styleMain, int i, LineLayout *ll) {
|
|
|
|
|
if (inSelection) {
|
|
|
|
|
if (vsDraw.selbackset && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) {
|
|
|
|
|
return SelectionBackground(vsDraw);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if ((vsDraw.edgeState == EDGE_BACKGROUND) &&
|
|
|
|
|
(i >= ll->edgeColumn) &&
|
|
|
|
|
!IsEOLChar(ll->chars[i]))
|
|
|
|
|
return vsDraw.edgecolour.allocated;
|
|
|
|
|
if (inHotspot && vsDraw.hotspotBackgroundSet)
|
|
|
|
|
return vsDraw.hotspotBackground.allocated;
|
|
|
|
|
if (overrideBackground)
|
|
|
|
|
return background;
|
|
|
|
|
}
|
|
|
|
|
return vsDraw.styles[styleMain].back.allocated;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::DrawIndentGuide(Surface *surface, int lineVisible, int lineHeight, int start, PRectangle rcSegment, bool highlight) {
|
|
|
|
|
Point from(0, ((lineVisible & 1) && (lineHeight & 1)) ? 1 : 0);
|
|
|
|
|
PRectangle rcCopyArea(start + 1, rcSegment.top, start + 2, rcSegment.bottom);
|
|
|
|
|
surface->Copy(rcCopyArea, from,
|
|
|
|
|
highlight ? *pixmapIndentGuideHighlight : *pixmapIndentGuide);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::DrawWrapMarker(Surface *surface, PRectangle rcPlace,
|
|
|
|
|
bool isEndMarker, ColourAllocated wrapColour) {
|
|
|
|
|
surface->PenColour(wrapColour);
|
|
|
|
|
|
|
|
|
|
enum { xa = 1 }; // gap before start
|
|
|
|
|
int w = rcPlace.right - rcPlace.left - xa - 1;
|
|
|
|
|
|
|
|
|
|
bool xStraight = isEndMarker; // x-mirrored symbol for start marker
|
|
|
|
|
bool yStraight = true;
|
|
|
|
|
//bool yStraight= isEndMarker; // comment in for start marker y-mirrowed
|
|
|
|
|
|
|
|
|
|
int x0 = xStraight ? rcPlace.left : rcPlace.right - 1;
|
|
|
|
|
int y0 = yStraight ? rcPlace.top : rcPlace.bottom - 1;
|
|
|
|
|
|
|
|
|
|
int dy = (rcPlace.bottom - rcPlace.top) / 5;
|
|
|
|
|
int y = (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, yStraight ? 1 : -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, ColourAllocated fill, int alpha) {
|
|
|
|
|
if (alpha != SC_ALPHA_NOALPHA) {
|
|
|
|
|
surface->AlphaRectangle(rc, 0, fill, alpha, fill, alpha, 0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::DrawEOL(Surface *surface, ViewStyle &vsDraw, PRectangle rcLine, LineLayout *ll,
|
|
|
|
|
int line, int lineEnd, int xStart, int subLine, int subLineStart,
|
|
|
|
|
bool overrideBackground, ColourAllocated background,
|
|
|
|
|
bool drawWrapMarkEnd, ColourAllocated wrapColour) {
|
|
|
|
|
|
|
|
|
|
int styleMask = pdoc->stylingBitsMask;
|
|
|
|
|
PRectangle rcSegment = rcLine;
|
|
|
|
|
|
|
|
|
|
// Fill in a PRectangle representing the end of line characters
|
|
|
|
|
int xEol = ll->positions[lineEnd] - subLineStart;
|
|
|
|
|
rcSegment.left = xEol + xStart;
|
|
|
|
|
rcSegment.right = xEol + vsDraw.aveCharWidth + xStart;
|
|
|
|
|
int posLineEnd = pdoc->LineStart(line + 1);
|
|
|
|
|
bool eolInSelection = (subLine == (ll->lines - 1)) &&
|
|
|
|
|
(posLineEnd > ll->selStart) && (posLineEnd <= ll->selEnd) && (ll->selStart != ll->selEnd);
|
|
|
|
|
|
|
|
|
|
if (eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) {
|
|
|
|
|
surface->FillRectangle(rcSegment, SelectionBackground(vsDraw));
|
|
|
|
|
} else {
|
|
|
|
|
if (overrideBackground) {
|
|
|
|
|
surface->FillRectangle(rcSegment, background);
|
|
|
|
|
} else {
|
|
|
|
|
surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back.allocated);
|
|
|
|
|
}
|
|
|
|
|
if (eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (vsDraw.selAlpha != SC_ALPHA_NOALPHA)) {
|
|
|
|
|
SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw), vsDraw.selAlpha);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rcSegment.left = xEol + vsDraw.aveCharWidth + xStart;
|
|
|
|
|
rcSegment.right = rcLine.right;
|
|
|
|
|
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.allocated);
|
|
|
|
|
} else {
|
|
|
|
|
surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back.allocated);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (drawWrapMarkEnd) {
|
|
|
|
|
PRectangle rcPlace = rcSegment;
|
|
|
|
|
|
|
|
|
|
if (wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_END_BY_TEXT) {
|
|
|
|
|
rcPlace.left = xEol + xStart;
|
|
|
|
|
rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
|
|
|
|
|
} else {
|
|
|
|
|
// draw left of the right text margin, to avoid clipping by the current clip rect
|
|
|
|
|
rcPlace.right = rcLine.right - vs.rightMarginWidth;
|
|
|
|
|
rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
|
|
|
|
|
}
|
|
|
|
|
DrawWrapMarker(surface, rcPlace, true, wrapColour);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVisible, int xStart,
|
|
|
|
|
PRectangle rcLine, LineLayout *ll, int subLine) {
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
ColourAllocated background;
|
|
|
|
|
if (caret.active && vsDraw.showCaretLineBackground && (vsDraw.caretLineAlpha == SC_ALPHA_NOALPHA) && ll->containsCaret) {
|
|
|
|
|
overrideBackground = true;
|
|
|
|
|
background = vsDraw.caretLineBackground.allocated;
|
|
|
|
|
}
|
|
|
|
|
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.allocated;
|
|
|
|
|
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.allocated;
|
|
|
|
|
}
|
|
|
|
|
marksMasked >>= 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool drawWhitespaceBackground = (vsDraw.viewWhitespace != wsInvisible) &&
|
|
|
|
|
(!overrideBackground) && (vsDraw.whitespaceBackgroundSet);
|
|
|
|
|
|
|
|
|
|
bool inIndentation = subLine == 0; // Do not handle indentation except on first subline.
|
|
|
|
|
int indentWidth = pdoc->IndentSize() * vsDraw.spaceWidth;
|
|
|
|
|
|
|
|
|
|
int posLineStart = pdoc->LineStart(line);
|
|
|
|
|
|
|
|
|
|
int startseg = ll->LineStart(subLine);
|
|
|
|
|
int subLineStart = ll->positions[startseg];
|
|
|
|
|
int lineStart = 0;
|
|
|
|
|
int lineEnd = 0;
|
|
|
|
|
if (subLine < ll->lines) {
|
|
|
|
|
lineStart = ll->LineStart(subLine);
|
|
|
|
|
lineEnd = ll->LineStart(subLine + 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool drawWrapMarkEnd = false;
|
|
|
|
|
|
|
|
|
|
if (wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
|
|
|
|
|
if (subLine + 1 < ll->lines) {
|
|
|
|
|
drawWrapMarkEnd = ll->LineStart(subLine + 1) != 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (actualWrapVisualStartIndent != 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 - subLineStart;
|
|
|
|
|
rcPlace.right = rcPlace.left + actualWrapVisualStartIndent * vsDraw.aveCharWidth;
|
|
|
|
|
|
|
|
|
|
// default bgnd here..
|
|
|
|
|
surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back.allocated);
|
|
|
|
|
|
|
|
|
|
// 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.allocated);
|
|
|
|
|
|
|
|
|
|
if (wrapVisualFlags & SC_WRAPVISUALFLAG_START) {
|
|
|
|
|
|
|
|
|
|
if (wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_START_BY_TEXT)
|
|
|
|
|
rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
|
|
|
|
|
else
|
|
|
|
|
rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
|
|
|
|
|
|
|
|
|
|
DrawWrapMarker(surface, rcPlace, false, vsDraw.whitespaceForeground.allocated);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
xStart += actualWrapVisualStartIndent * vsDraw.aveCharWidth;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
// Background drawing loop
|
|
|
|
|
for (i = lineStart; twoPhaseDraw && (i < lineEnd); i++) {
|
|
|
|
|
|
|
|
|
|
int iDoc = i + posLineStart;
|
|
|
|
|
// If there is the end of a style run for any reason
|
|
|
|
|
if ((ll->styles[i] != ll->styles[i + 1]) ||
|
|
|
|
|
i == (lineEnd - 1) ||
|
|
|
|
|
IsControlCharacter(ll->chars[i]) || IsControlCharacter(ll->chars[i + 1]) ||
|
|
|
|
|
((ll->selStart != ll->selEnd) && ((iDoc + 1 == ll->selStart) || (iDoc + 1 == ll->selEnd))) ||
|
|
|
|
|
(i == (ll->edgeColumn - 1))) {
|
|
|
|
|
rcSegment.left = ll->positions[startseg] + xStart - subLineStart;
|
|
|
|
|
rcSegment.right = ll->positions[i + 1] + xStart - 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.left <= rcLine.right) && (rcSegment.right >= rcLine.left)) {
|
|
|
|
|
int styleMain = ll->styles[i];
|
|
|
|
|
bool inSelection = (iDoc >= ll->selStart) && (iDoc < ll->selEnd) && (ll->selStart != ll->selEnd);
|
|
|
|
|
bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd);
|
|
|
|
|
ColourAllocated textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, i, ll);
|
|
|
|
|
if (ll->chars[i] == '\t') {
|
|
|
|
|
// Tab display
|
|
|
|
|
if (drawWhitespaceBackground &&
|
|
|
|
|
(!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
|
|
|
|
|
textBack = vsDraw.whitespaceBackground.allocated;
|
|
|
|
|
surface->FillRectangle(rcSegment, textBack);
|
|
|
|
|
} else if (IsControlCharacter(ll->chars[i])) {
|
|
|
|
|
// Control character display
|
|
|
|
|
inIndentation = false;
|
|
|
|
|
surface->FillRectangle(rcSegment, textBack);
|
|
|
|
|
} else {
|
|
|
|
|
// Normal text display
|
|
|
|
|
surface->FillRectangle(rcSegment, textBack);
|
|
|
|
|
if (vsDraw.viewWhitespace != wsInvisible ||
|
|
|
|
|
(inIndentation && vsDraw.viewIndentationGuides)) {
|
|
|
|
|
for (int cpos = 0; cpos <= i - startseg; cpos++) {
|
|
|
|
|
if (ll->chars[cpos + startseg] == ' ') {
|
|
|
|
|
if (drawWhitespaceBackground &&
|
|
|
|
|
(!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
|
|
|
|
|
PRectangle rcSpace(ll->positions[cpos + startseg] + xStart, rcSegment.top,
|
|
|
|
|
ll->positions[cpos + startseg + 1] + xStart, rcSegment.bottom);
|
|
|
|
|
surface->FillRectangle(rcSpace, vsDraw.whitespaceBackground.allocated);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
inIndentation = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (rcSegment.left > rcLine.right) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
startseg = i + 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (twoPhaseDraw) {
|
|
|
|
|
DrawEOL(surface, vsDraw, rcLine, ll, line, lineEnd,
|
|
|
|
|
xStart, subLine, subLineStart, overrideBackground, background,
|
|
|
|
|
drawWrapMarkEnd, vsDraw.whitespaceForeground.allocated);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inIndentation = subLine == 0; // Do not handle indentation except on first subline.
|
|
|
|
|
startseg = ll->LineStart(subLine);
|
|
|
|
|
// Foreground drawing loop
|
|
|
|
|
for (i = lineStart; i < lineEnd; i++) {
|
|
|
|
|
|
|
|
|
|
int iDoc = i + posLineStart;
|
|
|
|
|
// If there is the end of a style run for any reason
|
|
|
|
|
if ((ll->styles[i] != ll->styles[i + 1]) ||
|
|
|
|
|
i == (lineEnd - 1) ||
|
|
|
|
|
IsControlCharacter(ll->chars[i]) || IsControlCharacter(ll->chars[i + 1]) ||
|
|
|
|
|
((ll->selStart != ll->selEnd) && ((iDoc + 1 == ll->selStart) || (iDoc + 1 == ll->selEnd))) ||
|
|
|
|
|
(i == (ll->edgeColumn - 1))) {
|
|
|
|
|
rcSegment.left = ll->positions[startseg] + xStart - subLineStart;
|
|
|
|
|
rcSegment.right = ll->positions[i + 1] + xStart - 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.left <= rcLine.right) && (rcSegment.right >= rcLine.left)) {
|
|
|
|
|
int styleMain = ll->styles[i];
|
|
|
|
|
ColourAllocated textFore = vsDraw.styles[styleMain].fore.allocated;
|
|
|
|
|
Font &textFont = vsDraw.styles[styleMain].font;
|
|
|
|
|
//hotspot foreground
|
|
|
|
|
if (ll->hsStart != -1 && iDoc >= ll->hsStart && iDoc < hsEnd) {
|
|
|
|
|
if (vsDraw.hotspotForegroundSet)
|
|
|
|
|
textFore = vsDraw.hotspotForeground.allocated;
|
|
|
|
|
}
|
|
|
|
|
bool inSelection = (iDoc >= ll->selStart) && (iDoc < ll->selEnd) && (ll->selStart != ll->selEnd);
|
|
|
|
|
if (inSelection && (vsDraw.selforeset)) {
|
|
|
|
|
textFore = vsDraw.selforeground.allocated;
|
|
|
|
|
}
|
|
|
|
|
bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd);
|
|
|
|
|
ColourAllocated textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, i, ll);
|
|
|
|
|
if (ll->chars[i] == '\t') {
|
|
|
|
|
// Tab display
|
|
|
|
|
if (!twoPhaseDraw) {
|
|
|
|
|
if (drawWhitespaceBackground &&
|
|
|
|
|
(!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
|
|
|
|
|
textBack = vsDraw.whitespaceBackground.allocated;
|
|
|
|
|
surface->FillRectangle(rcSegment, textBack);
|
|
|
|
|
}
|
|
|
|
|
if ((vsDraw.viewWhitespace != wsInvisible) || ((inIndentation && vsDraw.viewIndentationGuides))) {
|
|
|
|
|
if (vsDraw.whitespaceForegroundSet)
|
|
|
|
|
textFore = vsDraw.whitespaceForeground.allocated;
|
|
|
|
|
surface->PenColour(textFore);
|
|
|
|
|
}
|
|
|
|
|
if (inIndentation && vsDraw.viewIndentationGuides) {
|
|
|
|
|
for (int xIG = ll->positions[i] / indentWidth * indentWidth; xIG < ll->positions[i + 1]; xIG += indentWidth) {
|
|
|
|
|
if (xIG >= ll->positions[i] && xIG > 0) {
|
|
|
|
|
DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIG + xStart, rcSegment,
|
|
|
|
|
(ll->xHighlightGuide == xIG));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (vsDraw.viewWhitespace != wsInvisible) {
|
|
|
|
|
if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
|
|
|
|
|
PRectangle rcTab(rcSegment.left + 1, rcSegment.top + 4,
|
|
|
|
|
rcSegment.right - 1, rcSegment.bottom - vsDraw.maxDescent);
|
|
|
|
|
DrawTabArrow(surface, rcTab, rcSegment.top + vsDraw.lineHeight / 2);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (IsControlCharacter(ll->chars[i])) {
|
|
|
|
|
// Control character display
|
|
|
|
|
inIndentation = false;
|
|
|
|
|
if (controlCharSymbol < 32) {
|
|
|
|
|
// Draw the character
|
|
|
|
|
const char *ctrlChar = ControlCharacterString(ll->chars[i]);
|
|
|
|
|
if (!twoPhaseDraw) {
|
|
|
|
|
surface->FillRectangle(rcSegment, textBack);
|
|
|
|
|
}
|
|
|
|
|
int normalCharHeight = 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, ctrlChar, istrlen(ctrlChar),
|
|
|
|
|
textBack, textFore);
|
|
|
|
|
} else {
|
|
|
|
|
char cc[2] = { static_cast<char>(controlCharSymbol), '\0' };
|
|
|
|
|
surface->DrawTextNoClip(rcSegment, ctrlCharsFont,
|
|
|
|
|
rcSegment.top + vsDraw.maxAscent,
|
|
|
|
|
cc, 1, textBack, textFore);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Normal text display
|
|
|
|
|
if (vsDraw.styles[styleMain].visible) {
|
|
|
|
|
if (twoPhaseDraw) {
|
|
|
|
|
surface->DrawTextTransparent(rcSegment, textFont,
|
|
|
|
|
rcSegment.top + vsDraw.maxAscent, ll->chars + startseg,
|
|
|
|
|
i - startseg + 1, textFore);
|
|
|
|
|
} else {
|
|
|
|
|
surface->DrawTextNoClip(rcSegment, textFont,
|
|
|
|
|
rcSegment.top + vsDraw.maxAscent, ll->chars + startseg,
|
|
|
|
|
i - startseg + 1, textFore, textBack);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (vsDraw.viewWhitespace != wsInvisible ||
|
|
|
|
|
(inIndentation && vsDraw.viewIndentationGuides)) {
|
|
|
|
|
for (int cpos = 0; cpos <= i - startseg; cpos++) {
|
|
|
|
|
if (ll->chars[cpos + startseg] == ' ') {
|
|
|
|
|
if (vsDraw.viewWhitespace != wsInvisible) {
|
|
|
|
|
if (vsDraw.whitespaceForegroundSet)
|
|
|
|
|
textFore = vsDraw.whitespaceForeground.allocated;
|
|
|
|
|
if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
|
|
|
|
|
int xmid = (ll->positions[cpos + startseg] + ll->positions[cpos + startseg + 1]) / 2;
|
|
|
|
|
if (!twoPhaseDraw && drawWhitespaceBackground &&
|
|
|
|
|
(!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
|
|
|
|
|
textBack = vsDraw.whitespaceBackground.allocated;
|
|
|
|
|
PRectangle rcSpace(ll->positions[cpos + startseg] + xStart, rcSegment.top, ll->positions[cpos + startseg + 1] + xStart, rcSegment.bottom);
|
|
|
|
|
surface->FillRectangle(rcSpace, textBack);
|
|
|
|
|
}
|
|
|
|
|
PRectangle rcDot(xmid + xStart - subLineStart, rcSegment.top + vsDraw.lineHeight / 2, 0, 0);
|
|
|
|
|
rcDot.right = rcDot.left + 1;
|
|
|
|
|
rcDot.bottom = rcDot.top + 1;
|
|
|
|
|
surface->FillRectangle(rcDot, textFore);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (inIndentation && vsDraw.viewIndentationGuides) {
|
|
|
|
|
int startSpace = ll->positions[cpos + startseg];
|
|
|
|
|
if (startSpace > 0 && (startSpace % indentWidth == 0)) {
|
|
|
|
|
DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, startSpace + xStart, rcSegment,
|
|
|
|
|
(ll->xHighlightGuide == ll->positions[cpos + startseg]));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} 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.hotspotForegroundSet)
|
|
|
|
|
surface->FillRectangle(rcUL, vsDraw.hotspotForeground.allocated);
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
startseg = i + 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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!
|
|
|
|
|
PRectangle rcIndic(
|
|
|
|
|
ll->positions[startPos] + xStart - subLineStart,
|
|
|
|
|
rcLine.top + vsDraw.maxAscent,
|
|
|
|
|
ll->positions[indicPos] + xStart - subLineStart,
|
|
|
|
|
rcLine.top + vsDraw.maxAscent + 3);
|
|
|
|
|
vsDraw.indicators[indicnum].Draw(surface, rcIndic, rcLine);
|
|
|
|
|
// RESET control var
|
|
|
|
|
startPos = -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
mask <<= 1;
|
|
|
|
|
}
|
|
|
|
|
// End of the drawing of the current line
|
|
|
|
|
if (!twoPhaseDraw) {
|
|
|
|
|
DrawEOL(surface, vsDraw, rcLine, ll, line, lineEnd,
|
|
|
|
|
xStart, subLine, subLineStart, overrideBackground, background,
|
|
|
|
|
drawWrapMarkEnd, vsDraw.whitespaceForeground.allocated);
|
|
|
|
|
}
|
|
|
|
|
if ((vsDraw.selAlpha != SC_ALPHA_NOALPHA) && (ll->selStart >= 0) && (ll->selEnd >= 0)) {
|
|
|
|
|
int startPosSel = (ll->selStart < posLineStart) ? posLineStart : ll->selStart;
|
|
|
|
|
int endPosSel = (ll->selEnd < (lineEnd + posLineStart)) ? ll->selEnd : (lineEnd + posLineStart);
|
|
|
|
|
if (startPosSel < endPosSel) {
|
|
|
|
|
rcSegment.left = xStart + ll->positions[startPosSel - posLineStart] - subLineStart;
|
|
|
|
|
rcSegment.right = xStart + ll->positions[endPosSel - posLineStart] - subLineStart;
|
|
|
|
|
SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw), vsDraw.selAlpha);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (vsDraw.edgeState == EDGE_LINE) {
|
|
|
|
|
int edgeX = theEdge * vsDraw.spaceWidth;
|
|
|
|
|
rcSegment.left = edgeX + xStart;
|
|
|
|
|
rcSegment.right = rcSegment.left + 1;
|
|
|
|
|
surface->FillRectangle(rcSegment, vsDraw.edgecolour.allocated);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Draw any translucent whole line states
|
|
|
|
|
rcSegment.left = xStart;
|
|
|
|
|
rcSegment.right = rcLine.right - 1;
|
|
|
|
|
if (caret.active && vsDraw.showCaretLineBackground && ll->containsCaret) {
|
|
|
|
|
SimpleAlphaRectangle(surface, rcSegment, vsDraw.caretLineBackground.allocated, vsDraw.caretLineAlpha);
|
|
|
|
|
}
|
|
|
|
|
int marks = pdoc->GetMark(line);
|
|
|
|
|
for (int markBit = 0; (markBit < 32) && marks; markBit++) {
|
|
|
|
|
if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND)) {
|
|
|
|
|
SimpleAlphaRectangle(surface, rcSegment, vsDraw.markers[markBit].back.allocated, vsDraw.markers[markBit].alpha);
|
|
|
|
|
}
|
|
|
|
|
marks >>= 1;
|
|
|
|
|
}
|
|
|
|
|
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)) {
|
|
|
|
|
SimpleAlphaRectangle(surface, rcSegment, vsDraw.markers[markBit].back.allocated, vsDraw.markers[markBit].alpha);
|
|
|
|
|
}
|
|
|
|
|
marksMasked >>= 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::RefreshPixMaps(Surface *surfaceWindow) {
|
|
|
|
|
if (!pixmapSelPattern->Initialised()) {
|
|
|
|
|
const int patternSize = 8;
|
|
|
|
|
pixmapSelPattern->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(0, 0, patternSize, patternSize);
|
|
|
|
|
|
|
|
|
|
// Initialize default colours based on the chrome colour scheme. Typically the highlight is white.
|
|
|
|
|
ColourAllocated colourFMFill = vs.selbar.allocated;
|
|
|
|
|
ColourAllocated colourFMStripes = vs.selbarlight.allocated;
|
|
|
|
|
|
|
|
|
|
if (!(vs.selbarlight.desired == 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.allocated;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (vs.foldmarginColourSet) {
|
|
|
|
|
// override default fold margin colour
|
|
|
|
|
colourFMFill = vs.foldmarginColour.allocated;
|
|
|
|
|
}
|
|
|
|
|
if (vs.foldmarginHighlightColourSet) {
|
|
|
|
|
// override default fold margin highlight colour
|
|
|
|
|
colourFMStripes = vs.foldmarginHighlightColour.allocated;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pixmapSelPattern->FillRectangle(rcPattern, colourFMFill);
|
|
|
|
|
pixmapSelPattern->PenColour(colourFMStripes);
|
|
|
|
|
for (int stripe = 0; stripe < patternSize; stripe++) {
|
|
|
|
|
// Alternating 1 pixel stripes is same as checkerboard.
|
|
|
|
|
pixmapSelPattern->MoveTo(0, stripe * 2);
|
|
|
|
|
pixmapSelPattern->LineTo(patternSize, stripe * 2 - patternSize);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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(0, 0, 1, vs.lineHeight);
|
|
|
|
|
pixmapIndentGuide->FillRectangle(rcIG, vs.styles[STYLE_INDENTGUIDE].back.allocated);
|
|
|
|
|
pixmapIndentGuide->PenColour(vs.styles[STYLE_INDENTGUIDE].fore.allocated);
|
|
|
|
|
pixmapIndentGuideHighlight->FillRectangle(rcIG, vs.styles[STYLE_BRACELIGHT].back.allocated);
|
|
|
|
|
pixmapIndentGuideHighlight->PenColour(vs.styles[STYLE_BRACELIGHT].fore.allocated);
|
|
|
|
|
for (int stripe = 1; stripe < vs.lineHeight + 1; stripe += 2) {
|
|
|
|
|
pixmapIndentGuide->MoveTo(0, stripe);
|
|
|
|
|
pixmapIndentGuide->LineTo(2, stripe);
|
|
|
|
|
pixmapIndentGuideHighlight->MoveTo(0, stripe);
|
|
|
|
|
pixmapIndentGuideHighlight->LineTo(2, stripe);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bufferedDraw) {
|
|
|
|
|
if (!pixmapLine->Initialised()) {
|
|
|
|
|
PRectangle rcClient = GetClientRectangle();
|
|
|
|
|
pixmapLine->InitPixMap(rcClient.Width(), vs.lineHeight,
|
|
|
|
|
surfaceWindow, wMain.GetID());
|
|
|
|
|
pixmapSelMargin->InitPixMap(vs.fixedColumnWidth,
|
|
|
|
|
rcClient.Height(), surfaceWindow, wMain.GetID());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
RefreshStyleData();
|
|
|
|
|
RefreshPixMaps(surfaceWindow);
|
|
|
|
|
|
|
|
|
|
PRectangle rcClient = GetClientRectangle();
|
|
|
|
|
//Platform::DebugPrintf("Client: (%3d,%3d) ... (%3d,%3d) %d\n",
|
|
|
|
|
// rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
|
|
|
|
|
|
|
|
|
|
surfaceWindow->SetPalette(&palette, true);
|
|
|
|
|
pixmapLine->SetPalette(&palette, !hasFocus);
|
|
|
|
|
|
|
|
|
|
int screenLinePaintFirst = rcArea.top / vs.lineHeight;
|
|
|
|
|
// The area to be painted plus one extra line is styled.
|
|
|
|
|
// The extra line is to determine when a style change, such as starting a comment flows on to other lines.
|
|
|
|
|
int lineStyleLast = topLine + (rcArea.bottom - 1) / vs.lineHeight + 1;
|
|
|
|
|
//Platform::DebugPrintf("Paint lines = %d .. %d\n", topLine + screenLinePaintFirst, lineStyleLast);
|
|
|
|
|
int endPosPaint = pdoc->Length();
|
|
|
|
|
if (lineStyleLast < cs.LinesDisplayed())
|
|
|
|
|
endPosPaint = pdoc->LineStart(cs.DocFromDisplay(lineStyleLast + 1));
|
|
|
|
|
|
|
|
|
|
int xStart = vs.fixedColumnWidth - xOffset;
|
|
|
|
|
int ypos = 0;
|
|
|
|
|
if (!bufferedDraw)
|
|
|
|
|
ypos += screenLinePaintFirst * vs.lineHeight;
|
|
|
|
|
int yposScreen = screenLinePaintFirst * vs.lineHeight;
|
|
|
|
|
|
|
|
|
|
// Ensure we are styled as far as we are painting.
|
|
|
|
|
pdoc->EnsureStyledTo(endPosPaint);
|
|
|
|
|
bool paintAbandonedByStyling = paintState == paintAbandoned;
|
|
|
|
|
if (needUpdateUI) {
|
|
|
|
|
NotifyUpdateUI();
|
|
|
|
|
needUpdateUI = false;
|
|
|
|
|
RefreshStyleData();
|
|
|
|
|
RefreshPixMaps(surfaceWindow);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Call priority lines wrap on a window of lines which are likely
|
|
|
|
|
// to rendered with the following paint (that is wrap the visible
|
|
|
|
|
// lines first).
|
|
|
|
|
int startLineToWrap = cs.DocFromDisplay(topLine) - 5;
|
|
|
|
|
if (startLineToWrap < 0)
|
|
|
|
|
startLineToWrap = -1;
|
|
|
|
|
if (WrapLines(false, startLineToWrap)) {
|
|
|
|
|
// 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());
|
|
|
|
|
|
|
|
|
|
PaintSelMargin(surfaceWindow, rcArea);
|
|
|
|
|
|
|
|
|
|
PRectangle rcRightMargin = rcClient;
|
|
|
|
|
rcRightMargin.left = rcRightMargin.right - vs.rightMarginWidth;
|
|
|
|
|
if (rcArea.Intersects(rcRightMargin)) {
|
|
|
|
|
surfaceWindow->FillRectangle(rcRightMargin, vs.styles[STYLE_DEFAULT].back.allocated);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (paintState == paintAbandoned) {
|
|
|
|
|
// Either styling or NotifyUpdateUI noticed that painting is needed
|
|
|
|
|
// outside the current painting rectangle
|
|
|
|
|
//Platform::DebugPrintf("Abandoning paint\n");
|
|
|
|
|
if (wrapState != eWrapNone) {
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
// Do the painting
|
|
|
|
|
if (rcArea.right > vs.fixedColumnWidth) {
|
|
|
|
|
|
|
|
|
|
Surface *surface = surfaceWindow;
|
|
|
|
|
if (bufferedDraw) {
|
|
|
|
|
surface = pixmapLine;
|
|
|
|
|
PLATFORM_ASSERT(pixmapLine->Initialised());
|
|
|
|
|
}
|
|
|
|
|
surface->SetUnicodeMode(IsUnicodeMode());
|
|
|
|
|
surface->SetDBCSMode(CodePage());
|
|
|
|
|
|
|
|
|
|
int visibleLine = topLine + screenLinePaintFirst;
|
|
|
|
|
|
|
|
|
|
int posCaret = currentPos;
|
|
|
|
|
if (posDrag >= 0)
|
|
|
|
|
posCaret = posDrag;
|
|
|
|
|
int lineCaret = pdoc->LineFromPosition(posCaret);
|
|
|
|
|
|
|
|
|
|
// Remove selection margin from drawing area so text will not be drawn
|
|
|
|
|
// on it in unbuffered mode.
|
|
|
|
|
PRectangle rcTextArea = rcClient;
|
|
|
|
|
rcTextArea.left = vs.fixedColumnWidth;
|
|
|
|
|
rcTextArea.right -= vs.rightMarginWidth;
|
|
|
|
|
surfaceWindow->SetClip(rcTextArea);
|
|
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
SelectionLineIterator lineIterator(this);
|
|
|
|
|
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);
|
|
|
|
|
// For rectangular selection this accesses the layout cache so should be after layout returned.
|
|
|
|
|
lineIterator.SetAt(lineDoc);
|
|
|
|
|
ll.Set(RetrieveLineLayout(lineDoc));
|
|
|
|
|
LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
|
|
|
|
|
lineDocPrevious = lineDoc;
|
|
|
|
|
}
|
|
|
|
|
//durLayout += et.Duration(true);
|
|
|
|
|
|
|
|
|
|
if (ll) {
|
|
|
|
|
if (selType == selStream) {
|
|
|
|
|
ll->selStart = SelectionStart();
|
|
|
|
|
ll->selEnd = SelectionEnd();
|
|
|
|
|
} else {
|
|
|
|
|
ll->selStart = lineIterator.startPos;
|
|
|
|
|
ll->selEnd = lineIterator.endPos;
|
|
|
|
|
}
|
|
|
|
|
ll->containsCaret = lineDoc == lineCaret;
|
|
|
|
|
if (hideSelection) {
|
|
|
|
|
ll->selStart = -1;
|
|
|
|
|
ll->selEnd = -1;
|
|
|
|
|
ll->containsCaret = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GetHotSpotRange(ll->hsStart, ll->hsEnd);
|
|
|
|
|
|
|
|
|
|
PRectangle rcLine = rcClient;
|
|
|
|
|
rcLine.top = ypos;
|
|
|
|
|
rcLine.bottom = ypos + vs.lineHeight;
|
|
|
|
|
|
|
|
|
|
Range rangeLine(pdoc->LineStart(lineDoc), pdoc->LineStart(lineDoc + 1));
|
|
|
|
|
// Highlight the current braces if any
|
|
|
|
|
ll->SetBracesHighlight(rangeLine, braces, static_cast<char>(bracesMatchStyle),
|
|
|
|
|
highlightGuideColumn * vs.spaceWidth);
|
|
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
|
|
|
|
|
bool expanded = cs.GetExpanded(lineDoc);
|
|
|
|
|
if ((foldFlags & SC_FOLDFLAG_BOX) == 0) {
|
|
|
|
|
// Paint the line above the fold
|
|
|
|
|
if ((expanded && (foldFlags & SC_FOLDFLAG_LINEBEFORE_EXPANDED))
|
|
|
|
|
||
|
|
|
|
|
(!expanded && (foldFlags & SC_FOLDFLAG_LINEBEFORE_CONTRACTED))) {
|
|
|
|
|
if (pdoc->GetLevel(lineDoc) & SC_FOLDLEVELHEADERFLAG) {
|
|
|
|
|
PRectangle rcFoldLine = rcLine;
|
|
|
|
|
rcFoldLine.bottom = rcFoldLine.top + 1;
|
|
|
|
|
surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Paint the line below the fold
|
|
|
|
|
if ((expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_EXPANDED))
|
|
|
|
|
||
|
|
|
|
|
(!expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_CONTRACTED))) {
|
|
|
|
|
if (pdoc->GetLevel(lineDoc) & SC_FOLDLEVELHEADERFLAG) {
|
|
|
|
|
PRectangle rcFoldLine = rcLine;
|
|
|
|
|
rcFoldLine.top = rcFoldLine.bottom - 1;
|
|
|
|
|
surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
int FoldLevelCurr = (pdoc->GetLevel(lineDoc) & SC_FOLDLEVELNUMBERMASK) - SC_FOLDLEVELBASE;
|
|
|
|
|
int FoldLevelPrev = (pdoc->GetLevel(lineDoc - 1) & SC_FOLDLEVELNUMBERMASK) - SC_FOLDLEVELBASE;
|
|
|
|
|
int FoldLevelFlags = (pdoc->GetLevel(lineDoc) & ~SC_FOLDLEVELNUMBERMASK) & ~(0xFFF0000);
|
|
|
|
|
int indentationStep = pdoc->IndentSize();
|
|
|
|
|
// Draw line above fold
|
|
|
|
|
if ((FoldLevelPrev < FoldLevelCurr)
|
|
|
|
|
||
|
|
|
|
|
(FoldLevelFlags & SC_FOLDLEVELBOXHEADERFLAG
|
|
|
|
|
&&
|
|
|
|
|
(pdoc->GetLevel(lineDoc - 1) & SC_FOLDLEVELBOXFOOTERFLAG) == 0)) {
|
|
|
|
|
PRectangle rcFoldLine = rcLine;
|
|
|
|
|
rcFoldLine.bottom = rcFoldLine.top + 1;
|
|
|
|
|
rcFoldLine.left += xStart + FoldLevelCurr * vs.spaceWidth * indentationStep - 1;
|
|
|
|
|
surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Line below the fold (or below a contracted fold)
|
|
|
|
|
if (FoldLevelFlags & SC_FOLDLEVELBOXFOOTERFLAG
|
|
|
|
|
||
|
|
|
|
|
(!expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_CONTRACTED))) {
|
|
|
|
|
PRectangle rcFoldLine = rcLine;
|
|
|
|
|
rcFoldLine.top = rcFoldLine.bottom - 1;
|
|
|
|
|
rcFoldLine.left += xStart + (FoldLevelCurr) * vs.spaceWidth * indentationStep - 1;
|
|
|
|
|
surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PRectangle rcBoxLine = rcLine;
|
|
|
|
|
// Draw vertical line for every fold level
|
|
|
|
|
for (int i = 0; i <= FoldLevelCurr; i++) {
|
|
|
|
|
rcBoxLine.left = xStart + i * vs.spaceWidth * indentationStep - 1;
|
|
|
|
|
rcBoxLine.right = rcBoxLine.left + 1;
|
|
|
|
|
surface->FillRectangle(rcBoxLine, vs.styles[STYLE_DEFAULT].fore.allocated);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Draw the Caret
|
|
|
|
|
if (lineDoc == lineCaret) {
|
|
|
|
|
int offset = Platform::Minimum(posCaret - rangeLine.start, ll->maxLineLength);
|
|
|
|
|
if ((offset >= ll->LineStart(subLine)) &&
|
|
|
|
|
((offset < ll->LineStart(subLine + 1)) || offset == ll->numCharsInLine)) {
|
|
|
|
|
int xposCaret = ll->positions[offset] - ll->positions[ll->LineStart(subLine)] + xStart;
|
|
|
|
|
|
|
|
|
|
if (actualWrapVisualStartIndent != 0) {
|
|
|
|
|
int lineStart = ll->LineStart(subLine);
|
|
|
|
|
if (lineStart != 0) // Wrapped
|
|
|
|
|
xposCaret += actualWrapVisualStartIndent * vs.aveCharWidth;
|
|
|
|
|
}
|
|
|
|
|
int widthOverstrikeCaret;
|
|
|
|
|
if (posCaret == pdoc->Length()) { // At end of document
|
|
|
|
|
widthOverstrikeCaret = vs.aveCharWidth;
|
|
|
|
|
} else if ((posCaret - rangeLine.start) >= ll->numCharsInLine) { // At end of line
|
|
|
|
|
widthOverstrikeCaret = vs.aveCharWidth;
|
|
|
|
|
} else {
|
|
|
|
|
widthOverstrikeCaret = ll->positions[offset + 1] - ll->positions[offset];
|
|
|
|
|
}
|
|
|
|
|
if (widthOverstrikeCaret < 3) // Make sure its visible
|
|
|
|
|
widthOverstrikeCaret = 3;
|
|
|
|
|
if (((caret.active && caret.on) || (posDrag >= 0)) && xposCaret >= 0) {
|
|
|
|
|
PRectangle rcCaret = rcLine;
|
|
|
|
|
int caretWidthOffset = 0;
|
|
|
|
|
if ((offset > 0) && (vs.caretWidth > 1))
|
|
|
|
|
caretWidthOffset = 1; // Move back so overlaps both character cells.
|
|
|
|
|
if (posDrag >= 0) {
|
|
|
|
|
rcCaret.left = xposCaret - caretWidthOffset;
|
|
|
|
|
rcCaret.right = rcCaret.left + vs.caretWidth;
|
|
|
|
|
} else {
|
|
|
|
|
if (inOverstrike) {
|
|
|
|
|
rcCaret.top = rcCaret.bottom - 2;
|
|
|
|
|
rcCaret.left = xposCaret + 1;
|
|
|
|
|
rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1;
|
|
|
|
|
} else {
|
|
|
|
|
rcCaret.left = xposCaret - caretWidthOffset;
|
|
|
|
|
rcCaret.right = rcCaret.left + vs.caretWidth;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
surface->FillRectangle(rcCaret, vs.caretcolour.allocated);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bufferedDraw) {
|
|
|
|
|
Point from(vs.fixedColumnWidth, 0);
|
|
|
|
|
PRectangle rcCopyArea(vs.fixedColumnWidth, yposScreen,
|
|
|
|
|
rcClient.right, yposScreen + vs.lineHeight);
|
|
|
|
|
surfaceWindow->Copy(rcCopyArea, from, *pixmapLine);
|
|
|
|
|
}
|
|
|
|
|
//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 = rcClient;
|
|
|
|
|
rcBeyondEOF.left = vs.fixedColumnWidth;
|
|
|
|
|
rcBeyondEOF.right = rcBeyondEOF.right;
|
|
|
|
|
rcBeyondEOF.top = (cs.LinesDisplayed() - topLine) * vs.lineHeight;
|
|
|
|
|
if (rcBeyondEOF.top < rcBeyondEOF.bottom) {
|
|
|
|
|
surfaceWindow->FillRectangle(rcBeyondEOF, vs.styles[STYLE_DEFAULT].back.allocated);
|
|
|
|
|
if (vs.edgeState == EDGE_LINE) {
|
|
|
|
|
int edgeX = theEdge * vs.spaceWidth;
|
|
|
|
|
rcBeyondEOF.left = edgeX + xStart;
|
|
|
|
|
rcBeyondEOF.right = rcBeyondEOF.left + 1;
|
|
|
|
|
surfaceWindow->FillRectangle(rcBeyondEOF, vs.edgecolour.allocated);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//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, RangeToFormat *pfr) {
|
|
|
|
|
if (!pfr)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
AutoSurface surface(pfr->hdc, this);
|
|
|
|
|
if (!surface)
|
|
|
|
|
return 0;
|
|
|
|
|
AutoSurface surfaceMeasure(pfr->hdcTarget, this);
|
|
|
|
|
if (!surfaceMeasure) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ViewStyle vsPrint(vs);
|
|
|
|
|
|
|
|
|
|
// 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 < ViewStyle::margins; margin++) {
|
|
|
|
|
if ((vsPrint.ms[margin].style == SC_MARGIN_NUMBER) && (vsPrint.ms[margin].width > 0)) {
|
|
|
|
|
lineNumberIndex = margin;
|
|
|
|
|
} else {
|
|
|
|
|
vsPrint.ms[margin].width = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
vsPrint.showMarkedLines = false;
|
|
|
|
|
vsPrint.fixedColumnWidth = 0;
|
|
|
|
|
vsPrint.zoomLevel = printMagnification;
|
|
|
|
|
vsPrint.viewIndentationGuides = false;
|
|
|
|
|
// Don't show the selection when printing
|
|
|
|
|
vsPrint.selbackset = false;
|
|
|
|
|
vsPrint.selforeset = false;
|
|
|
|
|
vsPrint.selAlpha = SC_ALPHA_NOALPHA;
|
|
|
|
|
vsPrint.whitespaceBackgroundSet = false;
|
|
|
|
|
vsPrint.whitespaceForegroundSet = false;
|
|
|
|
|
vsPrint.showCaretLineBackground = false;
|
|
|
|
|
|
|
|
|
|
// Set colours for printing according to users settings
|
|
|
|
|
for (int sty = 0;sty <= STYLE_MAX;sty++) {
|
|
|
|
|
if (printColourMode == SC_PRINT_INVERTLIGHT) {
|
|
|
|
|
vsPrint.styles[sty].fore.desired = InvertedLight(vsPrint.styles[sty].fore.desired);
|
|
|
|
|
vsPrint.styles[sty].back.desired = InvertedLight(vsPrint.styles[sty].back.desired);
|
|
|
|
|
} else if (printColourMode == SC_PRINT_BLACKONWHITE) {
|
|
|
|
|
vsPrint.styles[sty].fore.desired = ColourDesired(0, 0, 0);
|
|
|
|
|
vsPrint.styles[sty].back.desired = ColourDesired(0xff, 0xff, 0xff);
|
|
|
|
|
} else if (printColourMode == SC_PRINT_COLOURONWHITE) {
|
|
|
|
|
vsPrint.styles[sty].back.desired = ColourDesired(0xff, 0xff, 0xff);
|
|
|
|
|
} else if (printColourMode == SC_PRINT_COLOURONWHITEDEFAULTBG) {
|
|
|
|
|
if (sty <= STYLE_DEFAULT) {
|
|
|
|
|
vsPrint.styles[sty].back.desired = ColourDesired(0xff, 0xff, 0xff);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// White background for the line numbers
|
|
|
|
|
vsPrint.styles[STYLE_LINENUMBER].back.desired = ColourDesired(0xff, 0xff, 0xff);
|
|
|
|
|
|
|
|
|
|
vsPrint.Refresh(*surfaceMeasure);
|
|
|
|
|
// Ensure colours are set up
|
|
|
|
|
vsPrint.RefreshColourPalette(palette, true);
|
|
|
|
|
vsPrint.RefreshColourPalette(palette, false);
|
|
|
|
|
// Determining width must hapen after fonts have been realised in Refresh
|
|
|
|
|
int lineNumberWidth = 0;
|
|
|
|
|
if (lineNumberIndex >= 0) {
|
|
|
|
|
lineNumberWidth = surfaceMeasure->WidthText(vsPrint.styles[STYLE_LINENUMBER].font,
|
|
|
|
|
"99999" lineNumberPrintSpace, 5 + istrlen(lineNumberPrintSpace));
|
|
|
|
|
vsPrint.ms[lineNumberIndex].width = lineNumberWidth;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 + lineNumberWidth;
|
|
|
|
|
int ypos = pfr->rc.top;
|
|
|
|
|
|
|
|
|
|
int lineDoc = linePrintStart;
|
|
|
|
|
|
|
|
|
|
int nPrintPos = pfr->chrg.cpMin;
|
|
|
|
|
int visibleLine = 0;
|
|
|
|
|
int widthPrint = pfr->rc.Width() - lineNumberWidth;
|
|
|
|
|
if (printWrapState == 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(8000);
|
|
|
|
|
LayoutLine(lineDoc, surfaceMeasure, vsPrint, &ll, widthPrint);
|
|
|
|
|
|
|
|
|
|
ll.selStart = -1;
|
|
|
|
|
ll.selEnd = -1;
|
|
|
|
|
ll.containsCaret = false;
|
|
|
|
|
|
|
|
|
|
PRectangle rcLine;
|
|
|
|
|
rcLine.left = pfr->rc.left + lineNumberWidth;
|
|
|
|
|
rcLine.top = ypos;
|
|
|
|
|
rcLine.right = pfr->rc.right - 1;
|
|
|
|
|
rcLine.bottom = 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 -= surfaceMeasure->WidthText(
|
|
|
|
|
vsPrint.styles[STYLE_LINENUMBER].font, number, istrlen(number));
|
|
|
|
|
surface->FlushCachedState();
|
|
|
|
|
surface->DrawTextNoClip(rcNumber, vsPrint.styles[STYLE_LINENUMBER].font,
|
|
|
|
|
ypos + vsPrint.maxAscent, number, istrlen(number),
|
|
|
|
|
vsPrint.styles[STYLE_LINENUMBER].fore.allocated,
|
|
|
|
|
vsPrint.styles[STYLE_LINENUMBER].back.allocated);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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 = ypos;
|
|
|
|
|
rcLine.bottom = 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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nPrintPos;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Editor::TextWidth(int style, const char *text) {
|
|
|
|
|
RefreshStyleData();
|
|
|
|
|
AutoSurface surface(this);
|
|
|
|
|
if (surface) {
|
|
|
|
|
return 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();
|
|
|
|
|
SetScrollBars();
|
|
|
|
|
if (wrapState != eWrapNone) {
|
|
|
|
|
PRectangle rcTextArea = GetClientRectangle();
|
|
|
|
|
rcTextArea.left = vs.fixedColumnWidth;
|
|
|
|
|
rcTextArea.right -= vs.rightMarginWidth;
|
|
|
|
|
if (wrapWidth != rcTextArea.Width()) {
|
|
|
|
|
NeedWrapping();
|
|
|
|
|
Redraw();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::AddChar(char ch) {
|
|
|
|
|
char s[2];
|
|
|
|
|
s[0] = ch;
|
|
|
|
|
s[1] = '\0';
|
|
|
|
|
AddCharUTF(s, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::AddCharUTF(char *s, unsigned int len, bool treatAsDBCS) {
|
|
|
|
|
bool wasSelection = currentPos != anchor;
|
|
|
|
|
ClearSelection();
|
|
|
|
|
bool charReplaceAction = false;
|
|
|
|
|
if (inOverstrike && !wasSelection && !RangeContainsProtected(currentPos, currentPos + 1)) {
|
|
|
|
|
if (currentPos < (pdoc->Length())) {
|
|
|
|
|
if (!IsEOLChar(pdoc->CharAt(currentPos))) {
|
|
|
|
|
charReplaceAction = true;
|
|
|
|
|
pdoc->BeginUndoAction();
|
|
|
|
|
pdoc->DelChar(currentPos);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (pdoc->InsertString(currentPos, s, len)) {
|
|
|
|
|
SetEmptySelection(currentPos + len);
|
|
|
|
|
}
|
|
|
|
|
if (charReplaceAction) {
|
|
|
|
|
pdoc->EndUndoAction();
|
|
|
|
|
}
|
|
|
|
|
EnsureCaretVisible();
|
|
|
|
|
// Avoid blinking during rapid typing:
|
|
|
|
|
ShowCaretAtCurrentPosition();
|
|
|
|
|
if (!caretSticky) {
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (treatAsDBCS) {
|
|
|
|
|
NotifyChar((static_cast<unsigned char>(s[0]) << 8) |
|
|
|
|
|
static_cast<unsigned char>(s[1]));
|
|
|
|
|
} else {
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::ClearSelection() {
|
|
|
|
|
if (!SelectionContainsProtected()) {
|
|
|
|
|
int startPos = SelectionStart();
|
|
|
|
|
if (selType == selStream) {
|
|
|
|
|
unsigned int chars = SelectionEnd() - startPos;
|
|
|
|
|
if (0 != chars) {
|
|
|
|
|
pdoc->BeginUndoAction();
|
|
|
|
|
pdoc->DeleteChars(startPos, chars);
|
|
|
|
|
pdoc->EndUndoAction();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
pdoc->BeginUndoAction();
|
|
|
|
|
SelectionLineIterator lineIterator(this, false);
|
|
|
|
|
while (lineIterator.Iterate()) {
|
|
|
|
|
startPos = lineIterator.startPos;
|
|
|
|
|
unsigned int chars = lineIterator.endPos - startPos;
|
|
|
|
|
if (0 != chars) {
|
|
|
|
|
pdoc->DeleteChars(startPos, chars);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
pdoc->EndUndoAction();
|
|
|
|
|
selType = selStream;
|
|
|
|
|
}
|
|
|
|
|
SetEmptySelection(startPos);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::ClearAll() {
|
|
|
|
|
pdoc->BeginUndoAction();
|
|
|
|
|
if (0 != pdoc->Length()) {
|
|
|
|
|
pdoc->DeleteChars(0, pdoc->Length());
|
|
|
|
|
}
|
|
|
|
|
if (!pdoc->IsReadOnly()) {
|
|
|
|
|
cs.Clear();
|
|
|
|
|
}
|
|
|
|
|
pdoc->EndUndoAction();
|
|
|
|
|
anchor = 0;
|
|
|
|
|
currentPos = 0;
|
|
|
|
|
SetTopLine(0);
|
|
|
|
|
SetVerticalScrollPos();
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::ClearDocumentStyle() {
|
|
|
|
|
pdoc->StartStyling(0, '\377');
|
|
|
|
|
pdoc->SetStyleFor(pdoc->Length(), 0);
|
|
|
|
|
cs.ShowAll();
|
|
|
|
|
pdoc->ClearLevels();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::Cut() {
|
|
|
|
|
if (!pdoc->IsReadOnly() && !SelectionContainsProtected()) {
|
|
|
|
|
Copy();
|
|
|
|
|
ClearSelection();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::PasteRectangular(int pos, const char *ptr, int len) {
|
|
|
|
|
if (pdoc->IsReadOnly() || SelectionContainsProtected()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
currentPos = pos;
|
|
|
|
|
int xInsert = XFromPosition(currentPos);
|
|
|
|
|
int line = pdoc->LineFromPosition(currentPos);
|
|
|
|
|
bool prevCr = false;
|
|
|
|
|
pdoc->BeginUndoAction();
|
|
|
|
|
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->InsertChar(pdoc->Length(), '\r');
|
|
|
|
|
if (pdoc->eolMode != SC_EOL_CR)
|
|
|
|
|
pdoc->InsertChar(pdoc->Length(), '\n');
|
|
|
|
|
}
|
|
|
|
|
// Pad the end of lines with spaces if required
|
|
|
|
|
currentPos = PositionFromLineX(line, xInsert);
|
|
|
|
|
if ((XFromPosition(currentPos) < xInsert) && (i + 1 < len)) {
|
|
|
|
|
for (int i = 0; i < xInsert - XFromPosition(currentPos); i++) {
|
|
|
|
|
pdoc->InsertChar(currentPos, ' ');
|
|
|
|
|
currentPos++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
prevCr = ptr[i] == '\r';
|
|
|
|
|
} else {
|
|
|
|
|
pdoc->InsertString(currentPos, ptr + i, 1);
|
|
|
|
|
currentPos++;
|
|
|
|
|
prevCr = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
pdoc->EndUndoAction();
|
|
|
|
|
SetEmptySelection(pos);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Editor::CanPaste() {
|
|
|
|
|
return !pdoc->IsReadOnly() && !SelectionContainsProtected();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::Clear() {
|
|
|
|
|
if (currentPos == anchor) {
|
|
|
|
|
if (!RangeContainsProtected(currentPos, currentPos + 1)) {
|
|
|
|
|
DelChar();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
ClearSelection();
|
|
|
|
|
}
|
|
|
|
|
SetEmptySelection(currentPos);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::SelectAll() {
|
|
|
|
|
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(currentPos, currentPos + 1)) {
|
|
|
|
|
pdoc->DelChar(currentPos);
|
|
|
|
|
}
|
|
|
|
|
// Avoid blinking during rapid typing:
|
|
|
|
|
ShowCaretAtCurrentPosition();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::DelCharBack(bool allowLineStartDeletion) {
|
|
|
|
|
if (currentPos == anchor) {
|
|
|
|
|
if (!RangeContainsProtected(currentPos - 1, currentPos)) {
|
|
|
|
|
int lineCurrentPos = pdoc->LineFromPosition(currentPos);
|
|
|
|
|
if (allowLineStartDeletion || (pdoc->LineStart(lineCurrentPos) != currentPos)) {
|
|
|
|
|
if (pdoc->GetColumn(currentPos) <= pdoc->GetLineIndentation(lineCurrentPos) &&
|
|
|
|
|
pdoc->GetColumn(currentPos) > 0 && pdoc->backspaceUnindents) {
|
|
|
|
|
pdoc->BeginUndoAction();
|
|
|
|
|
int indentation = pdoc->GetLineIndentation(lineCurrentPos);
|
|
|
|
|
int indentationStep = pdoc->IndentSize();
|
|
|
|
|
if (indentation % indentationStep == 0) {
|
|
|
|
|
pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
|
|
|
|
|
} else {
|
|
|
|
|
pdoc->SetLineIndentation(lineCurrentPos, indentation - (indentation % indentationStep));
|
|
|
|
|
}
|
|
|
|
|
SetEmptySelection(pdoc->GetLineIndentPosition(lineCurrentPos));
|
|
|
|
|
pdoc->EndUndoAction();
|
|
|
|
|
} else {
|
|
|
|
|
pdoc->DelCharBack(currentPos);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
ClearSelection();
|
|
|
|
|
SetEmptySelection(currentPos);
|
|
|
|
|
}
|
|
|
|
|
// Avoid blinking during rapid typing:
|
|
|
|
|
ShowCaretAtCurrentPosition();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::NotifyFocus(bool) {}
|
|
|
|
|
|
|
|
|
|
void Editor::NotifyStyleToNeeded(int endStyleNeeded) {
|
|
|
|
|
SCNotification scn = {0};
|
|
|
|
|
scn.nmhdr.code = SCN_STYLENEEDED;
|
|
|
|
|
scn.position = endStyleNeeded;
|
|
|
|
|
NotifyParent(scn);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::NotifyStyleNeeded(Document*, void *, int endStyleNeeded) {
|
|
|
|
|
NotifyStyleToNeeded(endStyleNeeded);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::NotifyChar(int ch) {
|
|
|
|
|
SCNotification scn = {0};
|
|
|
|
|
scn.nmhdr.code = SCN_CHARADDED;
|
|
|
|
|
scn.ch = ch;
|
|
|
|
|
NotifyParent(scn);
|
|
|
|
|
if (recordingMacro) {
|
|
|
|
|
char txt[2];
|
|
|
|
|
txt[0] = static_cast<char>(ch);
|
|
|
|
|
txt[1] = '\0';
|
|
|
|
|
NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(txt));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::NotifySavePoint(bool isSavePoint) {
|
|
|
|
|
SCNotification scn = {0};
|
|
|
|
|
if (isSavePoint) {
|
|
|
|
|
scn.nmhdr.code = SCN_SAVEPOINTREACHED;
|
|
|
|
|
} else {
|
|
|
|
|
scn.nmhdr.code = SCN_SAVEPOINTLEFT;
|
|
|
|
|
}
|
|
|
|
|
NotifyParent(scn);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::NotifyModifyAttempt() {
|
|
|
|
|
SCNotification scn = {0};
|
|
|
|
|
scn.nmhdr.code = SCN_MODIFYATTEMPTRO;
|
|
|
|
|
NotifyParent(scn);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::NotifyDoubleClick(Point pt, bool) {
|
|
|
|
|
SCNotification scn = {0};
|
|
|
|
|
scn.nmhdr.code = SCN_DOUBLECLICK;
|
|
|
|
|
scn.line = LineFromLocation(pt);
|
|
|
|
|
scn.position = PositionFromLocationClose(pt);
|
|
|
|
|
NotifyParent(scn);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::NotifyHotSpotDoubleClicked(int position, bool shift, bool ctrl, bool alt) {
|
|
|
|
|
SCNotification scn = {0};
|
|
|
|
|
scn.nmhdr.code = SCN_HOTSPOTDOUBLECLICK;
|
|
|
|
|
scn.position = position;
|
|
|
|
|
scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
|
|
|
|
|
(alt ? SCI_ALT : 0);
|
|
|
|
|
NotifyParent(scn);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::NotifyHotSpotClicked(int position, bool shift, bool ctrl, bool alt) {
|
|
|
|
|
SCNotification scn = {0};
|
|
|
|
|
scn.nmhdr.code = SCN_HOTSPOTCLICK;
|
|
|
|
|
scn.position = position;
|
|
|
|
|
scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
|
|
|
|
|
(alt ? SCI_ALT : 0);
|
|
|
|
|
NotifyParent(scn);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::NotifyUpdateUI() {
|
|
|
|
|
SCNotification scn = {0};
|
|
|
|
|
scn.nmhdr.code = SCN_UPDATEUI;
|
|
|
|
|
NotifyParent(scn);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::NotifyPainted() {
|
|
|
|
|
SCNotification scn = {0};
|
|
|
|
|
scn.nmhdr.code = SCN_PAINTED;
|
|
|
|
|
NotifyParent(scn);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Editor::NotifyMarginClick(Point pt, bool shift, bool ctrl, bool alt) {
|
|
|
|
|
int marginClicked = -1;
|
|
|
|
|
int x = 0;
|
|
|
|
|
for (int margin = 0; margin < ViewStyle::margins; 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) {
|
|
|
|
|
SCNotification scn = {0};
|
|
|
|
|
scn.nmhdr.code = SCN_MARGINCLICK;
|
|
|
|
|
scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
|
|
|
|
|
(alt ? SCI_ALT : 0);
|
|
|
|
|
scn.position = pdoc->LineStart(LineFromLocation(pt));
|
|
|
|
|
scn.margin = marginClicked;
|
|
|
|
|
NotifyParent(scn);
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::NotifyNeedShown(int pos, int len) {
|
|
|
|
|
SCNotification scn = {0};
|
|
|
|
|
scn.nmhdr.code = SCN_NEEDSHOWN;
|
|
|
|
|
scn.position = pos;
|
|
|
|
|
scn.length = len;
|
|
|
|
|
NotifyParent(scn);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::NotifyDwelling(Point pt, bool state) {
|
|
|
|
|
SCNotification scn = {0};
|
|
|
|
|
scn.nmhdr.code = state ? SCN_DWELLSTART : SCN_DWELLEND;
|
|
|
|
|
scn.position = PositionFromLocationClose(pt);
|
|
|
|
|
scn.x = pt.x;
|
|
|
|
|
scn.y = pt.y;
|
|
|
|
|
NotifyParent(scn);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::NotifyZoom() {
|
|
|
|
|
SCNotification scn = {0};
|
|
|
|
|
scn.nmhdr.code = SCN_ZOOM;
|
|
|
|
|
NotifyParent(scn);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Notifications from document
|
|
|
|
|
void Editor::NotifyModifyAttempt(Document*, void *) {
|
|
|
|
|
//Platform::DebugPrintf("** Modify Attempt\n");
|
|
|
|
|
NotifyModifyAttempt();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::NotifyMove(int position) {
|
|
|
|
|
#ifdef INCLUDE_DEPRECATED_FEATURES
|
|
|
|
|
SCNotification scn = {0};
|
|
|
|
|
scn.nmhdr.code = SCN_POSCHANGED;
|
|
|
|
|
scn.position = position;
|
|
|
|
|
NotifyParent(scn);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
if (wrapState != eWrapNone) {
|
|
|
|
|
int lineDoc = pdoc->LineFromPosition(mh.position);
|
|
|
|
|
int lines = Platform::Maximum(0, mh.linesAdded);
|
|
|
|
|
NeedWrapping(lineDoc, lineDoc + lines + 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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 *) {
|
|
|
|
|
needUpdateUI = true;
|
|
|
|
|
if (paintState == painting) {
|
|
|
|
|
CheckForChangeOutsidePaint(Range(mh.position, mh.position + mh.length));
|
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
llc.Invalidate(LineLayout::llCheckTextAndStyle);
|
|
|
|
|
} else {
|
|
|
|
|
// Move selection and brace highlights
|
|
|
|
|
if (mh.modificationType & SC_MOD_INSERTTEXT) {
|
|
|
|
|
currentPos = MovePositionForInsertion(currentPos, mh.position, mh.length);
|
|
|
|
|
anchor = MovePositionForInsertion(anchor, 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) {
|
|
|
|
|
currentPos = MovePositionForDeletion(currentPos, mh.position, mh.length);
|
|
|
|
|
anchor = MovePositionForDeletion(anchor, mh.position, mh.length);
|
|
|
|
|
braces[0] = MovePositionForDeletion(braces[0], mh.position, mh.length);
|
|
|
|
|
braces[1] = MovePositionForDeletion(braces[1], mh.position, mh.length);
|
|
|
|
|
}
|
|
|
|
|
if (cs.LinesDisplayed() < cs.LinesInDoc()) {
|
|
|
|
|
// Some lines are hidden so may need shown.
|
|
|
|
|
// TODO: check if the modified area is hidden.
|
|
|
|
|
if (mh.modificationType & SC_MOD_BEFOREINSERT) {
|
|
|
|
|
NotifyNeedShown(mh.position, 0);
|
|
|
|
|
} else if (mh.modificationType & SC_MOD_BEFOREDELETE) {
|
|
|
|
|
NotifyNeedShown(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.linesAdded > 0) {
|
|
|
|
|
cs.InsertLines(lineOfPos, mh.linesAdded);
|
|
|
|
|
} else {
|
|
|
|
|
cs.DeleteLines(lineOfPos, -mh.linesAdded);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Platform::DebugPrintf("** %x Doc Changed\n", this);
|
|
|
|
|
// TODO: could invalidate from mh.startModification to end of screen
|
|
|
|
|
//InvalidateRange(mh.position, mh.position + mh.length);
|
|
|
|
|
if (paintState == notPainting && !CanDeferToLastStep(mh)) {
|
|
|
|
|
Redraw();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
//Platform::DebugPrintf("** %x Line Changed %d .. %d\n", this,
|
|
|
|
|
// mh.position, mh.position + mh.length);
|
|
|
|
|
if (paintState == notPainting && mh.length && !CanEliminate(mh)) {
|
|
|
|
|
InvalidateRange(mh.position, mh.position + mh.length);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mh.linesAdded != 0 && !CanDeferToLastStep(mh)) {
|
|
|
|
|
SetScrollBars();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mh.modificationType & SC_MOD_CHANGEMARKER) {
|
|
|
|
|
if ((paintState == notPainting) || !PaintContainsMargin()) {
|
|
|
|
|
if (mh.modificationType & SC_MOD_CHANGEFOLD) {
|
|
|
|
|
// Fold changes can affect the drawing of following lines so redraw whole margin
|
|
|
|
|
RedrawSelMargin();
|
|
|
|
|
} else {
|
|
|
|
|
RedrawSelMargin(mh.line);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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) == 0) {
|
|
|
|
|
// Real modification made to text of document.
|
|
|
|
|
NotifyChange(); // Send EN_CHANGE
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SCNotification scn = {0};
|
|
|
|
|
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;
|
|
|
|
|
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_DELWORDLEFT:
|
|
|
|
|
case SCI_DELWORDRIGHT:
|
|
|
|
|
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:
|
|
|
|
|
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 = {0};
|
|
|
|
|
scn.nmhdr.code = SCN_MACRORECORD;
|
|
|
|
|
scn.message = iMessage;
|
|
|
|
|
scn.wParam = wParam;
|
|
|
|
|
scn.lParam = lParam;
|
|
|
|
|
NotifyParent(scn);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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, selTypes sel, bool stuttered) {
|
|
|
|
|
int topLineNew, newPos;
|
|
|
|
|
|
|
|
|
|
// I consider only the caretYSlop, and ignore the caretYPolicy-- is that a problem?
|
|
|
|
|
int currentLine = pdoc->LineFromPosition(currentPos);
|
|
|
|
|
int topStutterLine = topLine + caretYSlop;
|
|
|
|
|
int bottomStutterLine = topLine + LinesToScroll() - caretYSlop;
|
|
|
|
|
|
|
|
|
|
if (stuttered && (direction < 0 && currentLine > topStutterLine)) {
|
|
|
|
|
topLineNew = topLine;
|
|
|
|
|
newPos = PositionFromLocation(Point(lastXChosen, vs.lineHeight * caretYSlop));
|
|
|
|
|
|
|
|
|
|
} else if (stuttered && (direction > 0 && currentLine < bottomStutterLine)) {
|
|
|
|
|
topLineNew = topLine;
|
|
|
|
|
newPos = PositionFromLocation(Point(lastXChosen, vs.lineHeight * (LinesToScroll() - caretYSlop)));
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
Point pt = LocationFromPosition(currentPos);
|
|
|
|
|
|
|
|
|
|
topLineNew = Platform::Clamp(
|
|
|
|
|
topLine + direction * LinesToScroll(), 0, MaxScrollPos());
|
|
|
|
|
newPos = PositionFromLocation(
|
|
|
|
|
Point(lastXChosen, pt.y + direction * (vs.lineHeight * LinesToScroll())));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (topLineNew != topLine) {
|
|
|
|
|
SetTopLine(topLineNew);
|
|
|
|
|
MovePositionTo(newPos, sel);
|
|
|
|
|
Redraw();
|
|
|
|
|
SetVerticalScrollPos();
|
|
|
|
|
} else {
|
|
|
|
|
MovePositionTo(newPos, sel);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::ChangeCaseOfSelection(bool makeUpperCase) {
|
|
|
|
|
pdoc->BeginUndoAction();
|
|
|
|
|
int startCurrent = currentPos;
|
|
|
|
|
int startAnchor = anchor;
|
|
|
|
|
if (selType == selStream) {
|
|
|
|
|
pdoc->ChangeCase(Range(SelectionStart(), SelectionEnd()),
|
|
|
|
|
makeUpperCase);
|
|
|
|
|
SetSelection(startCurrent, startAnchor);
|
|
|
|
|
} else {
|
|
|
|
|
SelectionLineIterator lineIterator(this, false);
|
|
|
|
|
while (lineIterator.Iterate()) {
|
|
|
|
|
pdoc->ChangeCase(
|
|
|
|
|
Range(lineIterator.startPos, lineIterator.endPos),
|
|
|
|
|
makeUpperCase);
|
|
|
|
|
}
|
|
|
|
|
// Would be nicer to keep the rectangular selection but this is complex
|
|
|
|
|
SetEmptySelection(startCurrent);
|
|
|
|
|
}
|
|
|
|
|
pdoc->EndUndoAction();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::LineTranspose() {
|
|
|
|
|
int line = pdoc->LineFromPosition(currentPos);
|
|
|
|
|
if (line > 0) {
|
|
|
|
|
int startPrev = pdoc->LineStart(line - 1);
|
|
|
|
|
int endPrev = pdoc->LineEnd(line - 1);
|
|
|
|
|
int start = pdoc->LineStart(line);
|
|
|
|
|
int end = pdoc->LineEnd(line);
|
|
|
|
|
int startNext = pdoc->LineStart(line + 1);
|
|
|
|
|
if (end < pdoc->Length()) {
|
|
|
|
|
end = startNext;
|
|
|
|
|
char *thisLine = CopyRange(start, end);
|
|
|
|
|
pdoc->DeleteChars(start, end - start);
|
|
|
|
|
if (pdoc->InsertString(startPrev, thisLine, end - start)) {
|
|
|
|
|
MovePositionTo(startPrev + end - start);
|
|
|
|
|
}
|
|
|
|
|
delete []thisLine;
|
|
|
|
|
} else {
|
|
|
|
|
// Last line so line has no line end
|
|
|
|
|
char *thisLine = CopyRange(start, end);
|
|
|
|
|
char *prevEnd = CopyRange(endPrev, start);
|
|
|
|
|
pdoc->DeleteChars(endPrev, end - endPrev);
|
|
|
|
|
pdoc->InsertString(startPrev, thisLine, end - start);
|
|
|
|
|
if (pdoc->InsertString(startPrev + end - start, prevEnd, start - endPrev)) {
|
|
|
|
|
MovePositionTo(startPrev + end - endPrev);
|
|
|
|
|
}
|
|
|
|
|
delete []thisLine;
|
|
|
|
|
delete []prevEnd;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::Duplicate(bool forLine) {
|
|
|
|
|
int start = SelectionStart();
|
|
|
|
|
int end = SelectionEnd();
|
|
|
|
|
if (start == end) {
|
|
|
|
|
forLine = true;
|
|
|
|
|
}
|
|
|
|
|
if (forLine) {
|
|
|
|
|
int line = pdoc->LineFromPosition(currentPos);
|
|
|
|
|
start = pdoc->LineStart(line);
|
|
|
|
|
end = pdoc->LineEnd(line);
|
|
|
|
|
}
|
|
|
|
|
char *text = CopyRange(start, end);
|
|
|
|
|
if (forLine) {
|
|
|
|
|
const char *eol = StringFromEOLMode(pdoc->eolMode);
|
|
|
|
|
pdoc->InsertString(end, eol);
|
|
|
|
|
pdoc->InsertString(end + istrlen(eol), text, end - start);
|
|
|
|
|
} else {
|
|
|
|
|
pdoc->InsertString(end, text, end - start);
|
|
|
|
|
}
|
|
|
|
|
delete []text;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::CancelModes() {
|
|
|
|
|
moveExtendsSelection = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::NewLine() {
|
|
|
|
|
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
|
|
|
|
|
if (pdoc->InsertString(currentPos, eol)) {
|
|
|
|
|
SetEmptySelection(currentPos + istrlen(eol));
|
|
|
|
|
while (*eol) {
|
|
|
|
|
NotifyChar(*eol);
|
|
|
|
|
eol++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
EnsureCaretVisible();
|
|
|
|
|
// Avoid blinking during rapid typing:
|
|
|
|
|
ShowCaretAtCurrentPosition();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::CursorUpOrDown(int direction, selTypes sel) {
|
|
|
|
|
Point pt = LocationFromPosition(currentPos);
|
|
|
|
|
int posNew = PositionFromLocation(
|
|
|
|
|
Point(lastXChosen, pt.y + direction * vs.lineHeight));
|
|
|
|
|
if (direction < 0) {
|
|
|
|
|
// Line wrapping may lead to a location on the same line, so
|
|
|
|
|
// seek back if that is the case.
|
|
|
|
|
// There is an equivalent case when moving down which skips
|
|
|
|
|
// over a line but as that does not trap the user it is fine.
|
|
|
|
|
Point ptNew = LocationFromPosition(posNew);
|
|
|
|
|
while ((posNew > 0) && (pt.y == ptNew.y)) {
|
|
|
|
|
posNew--;
|
|
|
|
|
ptNew = LocationFromPosition(posNew);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
MovePositionTo(posNew, sel);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::ParaUpOrDown(int direction, selTypes sel) {
|
|
|
|
|
int lineDoc, savedPos = currentPos;
|
|
|
|
|
do {
|
|
|
|
|
MovePositionTo(direction > 0 ? pdoc->ParaDown(currentPos) : pdoc->ParaUp(currentPos), sel);
|
|
|
|
|
lineDoc = pdoc->LineFromPosition(currentPos);
|
|
|
|
|
if (direction > 0) {
|
|
|
|
|
if (currentPos >= pdoc->Length() && !cs.GetVisible(lineDoc)) {
|
|
|
|
|
if (sel == noSel) {
|
|
|
|
|
MovePositionTo(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, selStream);
|
|
|
|
|
break;
|
|
|
|
|
case SCI_LINEDOWNRECTEXTEND:
|
|
|
|
|
CursorUpOrDown(1, selRectangle);
|
|
|
|
|
break;
|
|
|
|
|
case SCI_PARADOWN:
|
|
|
|
|
ParaUpOrDown(1);
|
|
|
|
|
break;
|
|
|
|
|
case SCI_PARADOWNEXTEND:
|
|
|
|
|
ParaUpOrDown(1, selStream);
|
|
|
|
|
break;
|
|
|
|
|
case SCI_LINESCROLLDOWN:
|
|
|
|
|
ScrollTo(topLine + 1);
|
|
|
|
|
MoveCaretInsideView(false);
|
|
|
|
|
break;
|
|
|
|
|
case SCI_LINEUP:
|
|
|
|
|
CursorUpOrDown(-1);
|
|
|
|
|
break;
|
|
|
|
|
case SCI_LINEUPEXTEND:
|
|
|
|
|
CursorUpOrDown(-1, selStream);
|
|
|
|
|
break;
|
|
|
|
|
case SCI_LINEUPRECTEXTEND:
|
|
|
|
|
CursorUpOrDown(-1, selRectangle);
|
|
|
|
|
break;
|
|
|
|
|
case SCI_PARAUP:
|
|
|
|
|
ParaUpOrDown(-1);
|
|
|
|
|
break;
|
|
|
|
|
case SCI_PARAUPEXTEND:
|
|
|
|
|
ParaUpOrDown(-1, selStream);
|
|
|
|
|
break;
|
|
|
|
|
case SCI_LINESCROLLUP:
|
|
|
|
|
ScrollTo(topLine - 1);
|
|
|
|
|
MoveCaretInsideView(false);
|
|
|
|
|
break;
|
|
|
|
|
case SCI_CHARLEFT:
|
|
|
|
|
if (SelectionEmpty() || moveExtendsSelection) {
|
|
|
|
|
MovePositionTo(MovePositionSoVisible(currentPos - 1, -1));
|
|
|
|
|
} else {
|
|
|
|
|
MovePositionTo(SelectionStart());
|
|
|
|
|
}
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_CHARLEFTEXTEND:
|
|
|
|
|
MovePositionTo(MovePositionSoVisible(currentPos - 1, -1), selStream);
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_CHARLEFTRECTEXTEND:
|
|
|
|
|
MovePositionTo(MovePositionSoVisible(currentPos - 1, -1), selRectangle);
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_CHARRIGHT:
|
|
|
|
|
if (SelectionEmpty() || moveExtendsSelection) {
|
|
|
|
|
MovePositionTo(MovePositionSoVisible(currentPos + 1, 1));
|
|
|
|
|
} else {
|
|
|
|
|
MovePositionTo(SelectionEnd());
|
|
|
|
|
}
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_CHARRIGHTEXTEND:
|
|
|
|
|
MovePositionTo(MovePositionSoVisible(currentPos + 1, 1), selStream);
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_CHARRIGHTRECTEXTEND:
|
|
|
|
|
MovePositionTo(MovePositionSoVisible(currentPos + 1, 1), selRectangle);
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_WORDLEFT:
|
|
|
|
|
MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, -1), -1));
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_WORDLEFTEXTEND:
|
|
|
|
|
MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, -1), -1), selStream);
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_WORDRIGHT:
|
|
|
|
|
MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, 1), 1));
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_WORDRIGHTEXTEND:
|
|
|
|
|
MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, 1), 1), selStream);
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_WORDLEFTEND:
|
|
|
|
|
MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(currentPos, -1), -1));
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_WORDLEFTENDEXTEND:
|
|
|
|
|
MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(currentPos, -1), -1), selStream);
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_WORDRIGHTEND:
|
|
|
|
|
MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(currentPos, 1), 1));
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_WORDRIGHTENDEXTEND:
|
|
|
|
|
MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(currentPos, 1), 1), selStream);
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_HOME:
|
|
|
|
|
MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(currentPos)));
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_HOMEEXTEND:
|
|
|
|
|
MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(currentPos)), selStream);
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_HOMERECTEXTEND:
|
|
|
|
|
MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(currentPos)), selRectangle);
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_LINEEND:
|
|
|
|
|
MovePositionTo(pdoc->LineEndPosition(currentPos));
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_LINEENDEXTEND:
|
|
|
|
|
MovePositionTo(pdoc->LineEndPosition(currentPos), selStream);
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_LINEENDRECTEXTEND:
|
|
|
|
|
MovePositionTo(pdoc->LineEndPosition(currentPos), selRectangle);
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_HOMEWRAP: {
|
|
|
|
|
int homePos = MovePositionSoVisible(StartEndDisplayLine(currentPos, true), -1);
|
|
|
|
|
if (currentPos <= homePos)
|
|
|
|
|
homePos = pdoc->LineStart(pdoc->LineFromPosition(currentPos));
|
|
|
|
|
MovePositionTo(homePos);
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case SCI_HOMEWRAPEXTEND: {
|
|
|
|
|
int homePos = MovePositionSoVisible(StartEndDisplayLine(currentPos, true), -1);
|
|
|
|
|
if (currentPos <= homePos)
|
|
|
|
|
homePos = pdoc->LineStart(pdoc->LineFromPosition(currentPos));
|
|
|
|
|
MovePositionTo(homePos, selStream);
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case SCI_LINEENDWRAP: {
|
|
|
|
|
int endPos = MovePositionSoVisible(StartEndDisplayLine(currentPos, false), 1);
|
|
|
|
|
int realEndPos = pdoc->LineEndPosition(currentPos);
|
|
|
|
|
if (endPos > realEndPos // if moved past visible EOLs
|
|
|
|
|
|| currentPos >= endPos) // if at end of display line already
|
|
|
|
|
endPos = realEndPos;
|
|
|
|
|
MovePositionTo(endPos);
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case SCI_LINEENDWRAPEXTEND: {
|
|
|
|
|
int endPos = MovePositionSoVisible(StartEndDisplayLine(currentPos, false), 1);
|
|
|
|
|
int realEndPos = pdoc->LineEndPosition(currentPos);
|
|
|
|
|
if (endPos > realEndPos // if moved past visible EOLs
|
|
|
|
|
|| currentPos >= endPos) // if at end of display line already
|
|
|
|
|
endPos = realEndPos;
|
|
|
|
|
MovePositionTo(endPos, selStream);
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case SCI_DOCUMENTSTART:
|
|
|
|
|
MovePositionTo(0);
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_DOCUMENTSTARTEXTEND:
|
|
|
|
|
MovePositionTo(0, selStream);
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_DOCUMENTEND:
|
|
|
|
|
MovePositionTo(pdoc->Length());
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_DOCUMENTENDEXTEND:
|
|
|
|
|
MovePositionTo(pdoc->Length(), selStream);
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_STUTTEREDPAGEUP:
|
|
|
|
|
PageMove(-1, noSel, true);
|
|
|
|
|
break;
|
|
|
|
|
case SCI_STUTTEREDPAGEUPEXTEND:
|
|
|
|
|
PageMove(-1, selStream, true);
|
|
|
|
|
break;
|
|
|
|
|
case SCI_STUTTEREDPAGEDOWN:
|
|
|
|
|
PageMove(1, noSel, true);
|
|
|
|
|
break;
|
|
|
|
|
case SCI_STUTTEREDPAGEDOWNEXTEND:
|
|
|
|
|
PageMove(1, selStream, true);
|
|
|
|
|
break;
|
|
|
|
|
case SCI_PAGEUP:
|
|
|
|
|
PageMove(-1);
|
|
|
|
|
break;
|
|
|
|
|
case SCI_PAGEUPEXTEND:
|
|
|
|
|
PageMove(-1, selStream);
|
|
|
|
|
break;
|
|
|
|
|
case SCI_PAGEUPRECTEXTEND:
|
|
|
|
|
PageMove(-1, selRectangle);
|
|
|
|
|
break;
|
|
|
|
|
case SCI_PAGEDOWN:
|
|
|
|
|
PageMove(1);
|
|
|
|
|
break;
|
|
|
|
|
case SCI_PAGEDOWNEXTEND:
|
|
|
|
|
PageMove(1, selStream);
|
|
|
|
|
break;
|
|
|
|
|
case SCI_PAGEDOWNRECTEXTEND:
|
|
|
|
|
PageMove(1, selRectangle);
|
|
|
|
|
break;
|
|
|
|
|
case SCI_EDITTOGGLEOVERTYPE:
|
|
|
|
|
inOverstrike = !inOverstrike;
|
|
|
|
|
DropCaret();
|
|
|
|
|
ShowCaretAtCurrentPosition();
|
|
|
|
|
NotifyUpdateUI();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_CANCEL: // Cancel any modes - handled in subclass
|
|
|
|
|
// Also unselect text
|
|
|
|
|
CancelModes();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_DELETEBACK:
|
|
|
|
|
DelCharBack(true);
|
|
|
|
|
if (!caretSticky) {
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
}
|
|
|
|
|
EnsureCaretVisible();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_DELETEBACKNOTLINE:
|
|
|
|
|
DelCharBack(false);
|
|
|
|
|
if (!caretSticky) {
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
}
|
|
|
|
|
EnsureCaretVisible();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_TAB:
|
|
|
|
|
Indent(true);
|
|
|
|
|
if (!caretSticky) {
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
}
|
|
|
|
|
EnsureCaretVisible();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_BACKTAB:
|
|
|
|
|
Indent(false);
|
|
|
|
|
if (!caretSticky) {
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
}
|
|
|
|
|
EnsureCaretVisible();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_NEWLINE:
|
|
|
|
|
NewLine();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_FORMFEED:
|
|
|
|
|
AddChar('\f');
|
|
|
|
|
break;
|
|
|
|
|
case SCI_VCHOME:
|
|
|
|
|
MovePositionTo(pdoc->VCHomePosition(currentPos));
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_VCHOMEEXTEND:
|
|
|
|
|
MovePositionTo(pdoc->VCHomePosition(currentPos), selStream);
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_VCHOMERECTEXTEND:
|
|
|
|
|
MovePositionTo(pdoc->VCHomePosition(currentPos), selRectangle);
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_VCHOMEWRAP: {
|
|
|
|
|
int homePos = pdoc->VCHomePosition(currentPos);
|
|
|
|
|
int viewLineStart = MovePositionSoVisible(StartEndDisplayLine(currentPos, true), -1);
|
|
|
|
|
if ((viewLineStart < currentPos) && (viewLineStart > homePos))
|
|
|
|
|
homePos = viewLineStart;
|
|
|
|
|
|
|
|
|
|
MovePositionTo(homePos);
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case SCI_VCHOMEWRAPEXTEND: {
|
|
|
|
|
int homePos = pdoc->VCHomePosition(currentPos);
|
|
|
|
|
int viewLineStart = MovePositionSoVisible(StartEndDisplayLine(currentPos, true), -1);
|
|
|
|
|
if ((viewLineStart < currentPos) && (viewLineStart > homePos))
|
|
|
|
|
homePos = viewLineStart;
|
|
|
|
|
|
|
|
|
|
MovePositionTo(homePos, 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(currentPos, -1);
|
|
|
|
|
pdoc->DeleteChars(startWord, currentPos - startWord);
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case SCI_DELWORDRIGHT: {
|
|
|
|
|
int endWord = pdoc->NextWordStart(currentPos, 1);
|
|
|
|
|
pdoc->DeleteChars(currentPos, endWord - currentPos);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case SCI_DELLINELEFT: {
|
|
|
|
|
int line = pdoc->LineFromPosition(currentPos);
|
|
|
|
|
int start = pdoc->LineStart(line);
|
|
|
|
|
pdoc->DeleteChars(start, currentPos - start);
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case SCI_DELLINERIGHT: {
|
|
|
|
|
int line = pdoc->LineFromPosition(currentPos);
|
|
|
|
|
int end = pdoc->LineEnd(line);
|
|
|
|
|
pdoc->DeleteChars(currentPos, end - currentPos);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case SCI_LINECOPY: {
|
|
|
|
|
int lineStart = pdoc->LineFromPosition(SelectionStart());
|
|
|
|
|
int lineEnd = pdoc->LineFromPosition(SelectionEnd());
|
|
|
|
|
CopyRangeToClipboard(pdoc->LineStart(lineStart),
|
|
|
|
|
pdoc->LineStart(lineEnd + 1));
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case SCI_LINECUT: {
|
|
|
|
|
int lineStart = pdoc->LineFromPosition(SelectionStart());
|
|
|
|
|
int lineEnd = pdoc->LineFromPosition(SelectionEnd());
|
|
|
|
|
int start = pdoc->LineStart(lineStart);
|
|
|
|
|
int end = pdoc->LineStart(lineEnd + 1);
|
|
|
|
|
SetSelection(start, end);
|
|
|
|
|
Cut();
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case SCI_LINEDELETE: {
|
|
|
|
|
int line = pdoc->LineFromPosition(currentPos);
|
|
|
|
|
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(false);
|
|
|
|
|
break;
|
|
|
|
|
case SCI_UPPERCASE:
|
|
|
|
|
ChangeCaseOfSelection(true);
|
|
|
|
|
break;
|
|
|
|
|
case SCI_WORDPARTLEFT:
|
|
|
|
|
MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(currentPos), -1));
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_WORDPARTLEFTEXTEND:
|
|
|
|
|
MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(currentPos), -1), selStream);
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_WORDPARTRIGHT:
|
|
|
|
|
MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(currentPos), 1));
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_WORDPARTRIGHTEXTEND:
|
|
|
|
|
MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(currentPos), 1), selStream);
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_HOMEDISPLAY:
|
|
|
|
|
MovePositionTo(MovePositionSoVisible(
|
|
|
|
|
StartEndDisplayLine(currentPos, true), -1));
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_HOMEDISPLAYEXTEND:
|
|
|
|
|
MovePositionTo(MovePositionSoVisible(
|
|
|
|
|
StartEndDisplayLine(currentPos, true), -1), selStream);
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_LINEENDDISPLAY:
|
|
|
|
|
MovePositionTo(MovePositionSoVisible(
|
|
|
|
|
StartEndDisplayLine(currentPos, false), 1));
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_LINEENDDISPLAYEXTEND:
|
|
|
|
|
MovePositionTo(MovePositionSoVisible(
|
|
|
|
|
StartEndDisplayLine(currentPos, false), 1), selStream);
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Editor::KeyDefault(int, int) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Editor::KeyDown(int key, bool shift, bool ctrl, bool alt, bool *consumed) {
|
|
|
|
|
DwellEnd(false);
|
|
|
|
|
int modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
|
|
|
|
|
(alt ? SCI_ALT : 0);
|
|
|
|
|
int msg = kmap.Find(key, modifiers);
|
|
|
|
|
if (msg) {
|
|
|
|
|
if (consumed)
|
|
|
|
|
*consumed = true;
|
|
|
|
|
return WndProc(msg, 0, 0);
|
|
|
|
|
} else {
|
|
|
|
|
if (consumed)
|
|
|
|
|
*consumed = false;
|
|
|
|
|
return KeyDefault(key, modifiers);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::SetWhitespaceVisible(int view) {
|
|
|
|
|
vs.viewWhitespace = static_cast<WhiteSpaceVisibility>(view);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Editor::GetWhitespaceVisible() {
|
|
|
|
|
return vs.viewWhitespace;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::Indent(bool forwards) {
|
|
|
|
|
//Platform::DebugPrintf("INdent %d\n", forwards);
|
|
|
|
|
int lineOfAnchor = pdoc->LineFromPosition(anchor);
|
|
|
|
|
int lineCurrentPos = pdoc->LineFromPosition(currentPos);
|
|
|
|
|
if (lineOfAnchor == lineCurrentPos) {
|
|
|
|
|
if (forwards) {
|
|
|
|
|
pdoc->BeginUndoAction();
|
|
|
|
|
ClearSelection();
|
|
|
|
|
if (pdoc->GetColumn(currentPos) <= pdoc->GetColumn(pdoc->GetLineIndentPosition(lineCurrentPos)) &&
|
|
|
|
|
pdoc->tabIndents) {
|
|
|
|
|
int indentation = pdoc->GetLineIndentation(lineCurrentPos);
|
|
|
|
|
int indentationStep = pdoc->IndentSize();
|
|
|
|
|
pdoc->SetLineIndentation(lineCurrentPos, indentation + indentationStep - indentation % indentationStep);
|
|
|
|
|
SetEmptySelection(pdoc->GetLineIndentPosition(lineCurrentPos));
|
|
|
|
|
} else {
|
|
|
|
|
if (pdoc->useTabs) {
|
|
|
|
|
pdoc->InsertChar(currentPos, '\t');
|
|
|
|
|
SetEmptySelection(currentPos + 1);
|
|
|
|
|
} else {
|
|
|
|
|
int numSpaces = (pdoc->tabInChars) -
|
|
|
|
|
(pdoc->GetColumn(currentPos) % (pdoc->tabInChars));
|
|
|
|
|
if (numSpaces < 1)
|
|
|
|
|
numSpaces = pdoc->tabInChars;
|
|
|
|
|
for (int i = 0; i < numSpaces; i++) {
|
|
|
|
|
pdoc->InsertChar(currentPos + i, ' ');
|
|
|
|
|
}
|
|
|
|
|
SetEmptySelection(currentPos + numSpaces);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
pdoc->EndUndoAction();
|
|
|
|
|
} else {
|
|
|
|
|
if (pdoc->GetColumn(currentPos) <= pdoc->GetLineIndentation(lineCurrentPos) &&
|
|
|
|
|
pdoc->tabIndents) {
|
|
|
|
|
pdoc->BeginUndoAction();
|
|
|
|
|
int indentation = pdoc->GetLineIndentation(lineCurrentPos);
|
|
|
|
|
int indentationStep = pdoc->IndentSize();
|
|
|
|
|
pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
|
|
|
|
|
SetEmptySelection(pdoc->GetLineIndentPosition(lineCurrentPos));
|
|
|
|
|
pdoc->EndUndoAction();
|
|
|
|
|
} else {
|
|
|
|
|
int newColumn = ((pdoc->GetColumn(currentPos) - 1) / pdoc->tabInChars) *
|
|
|
|
|
pdoc->tabInChars;
|
|
|
|
|
if (newColumn < 0)
|
|
|
|
|
newColumn = 0;
|
|
|
|
|
int newPos = currentPos;
|
|
|
|
|
while (pdoc->GetColumn(newPos) > newColumn)
|
|
|
|
|
newPos--;
|
|
|
|
|
SetEmptySelection(newPos);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
int anchorPosOnLine = anchor - pdoc->LineStart(lineOfAnchor);
|
|
|
|
|
int currentPosPosOnLine = currentPos - 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) == anchor || pdoc->LineStart(lineBottomSel) == currentPos)
|
|
|
|
|
lineBottomSel--; // If not selecting any characters on a line, do not indent
|
|
|
|
|
pdoc->BeginUndoAction();
|
|
|
|
|
pdoc->Indent(forwards, lineBottomSel, lineTopSel);
|
|
|
|
|
pdoc->EndUndoAction();
|
|
|
|
|
if (lineOfAnchor < lineCurrentPos) {
|
|
|
|
|
if (currentPosPosOnLine == 0)
|
|
|
|
|
SetSelection(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
|
|
|
|
|
else
|
|
|
|
|
SetSelection(pdoc->LineStart(lineCurrentPos + 1), pdoc->LineStart(lineOfAnchor));
|
|
|
|
|
} else {
|
|
|
|
|
if (anchorPosOnLine == 0)
|
|
|
|
|
SetSelection(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
|
|
|
|
|
else
|
|
|
|
|
SetSelection(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor + 1));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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.
|
|
|
|
|
|
|
|
|
|
TextToFind *ft = reinterpret_cast<TextToFind *>(lParam);
|
|
|
|
|
int lengthFound = istrlen(ft->lpstrText);
|
|
|
|
|
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,
|
|
|
|
|
(wParam & SCFIND_POSIX) != 0,
|
|
|
|
|
&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();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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 (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,
|
|
|
|
|
(wParam & SCFIND_POSIX) != 0,
|
|
|
|
|
&lengthFound);
|
|
|
|
|
} else {
|
|
|
|
|
pos = pdoc->FindText(searchAnchor, 0, txt,
|
|
|
|
|
(wParam & SCFIND_MATCHCASE) != 0,
|
|
|
|
|
(wParam & SCFIND_WHOLEWORD) != 0,
|
|
|
|
|
(wParam & SCFIND_WORDSTART) != 0,
|
|
|
|
|
(wParam & SCFIND_REGEXP) != 0,
|
|
|
|
|
(wParam & SCFIND_POSIX) != 0,
|
|
|
|
|
&lengthFound);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pos != -1) {
|
|
|
|
|
SetSelection(pos, pos + lengthFound);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return pos;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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;
|
|
|
|
|
int pos = pdoc->FindText(targetStart, targetEnd, text,
|
|
|
|
|
(searchFlags & SCFIND_MATCHCASE) != 0,
|
|
|
|
|
(searchFlags & SCFIND_WHOLEWORD) != 0,
|
|
|
|
|
(searchFlags & SCFIND_WORDSTART) != 0,
|
|
|
|
|
(searchFlags & SCFIND_REGEXP) != 0,
|
|
|
|
|
(searchFlags & SCFIND_POSIX) != 0,
|
|
|
|
|
&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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *Editor::CopyRange(int start, int end) {
|
|
|
|
|
char *text = 0;
|
|
|
|
|
if (start < end) {
|
|
|
|
|
int len = end - start;
|
|
|
|
|
text = new char[len + 1];
|
|
|
|
|
if (text) {
|
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
|
|
|
text[i] = pdoc->CharAt(start + i);
|
|
|
|
|
}
|
|
|
|
|
text[len] = '\0';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return text;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::CopySelectionFromRange(SelectionText *ss, int start, int end) {
|
|
|
|
|
ss->Set(CopyRange(start, end), end - start + 1,
|
|
|
|
|
pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::CopySelectionRange(SelectionText *ss) {
|
|
|
|
|
if (selType == selStream) {
|
|
|
|
|
CopySelectionFromRange(ss, SelectionStart(), SelectionEnd());
|
|
|
|
|
} else {
|
|
|
|
|
char *text = 0;
|
|
|
|
|
int size = 0;
|
|
|
|
|
SelectionLineIterator lineIterator(this);
|
|
|
|
|
while (lineIterator.Iterate()) {
|
|
|
|
|
size += lineIterator.endPos - lineIterator.startPos;
|
|
|
|
|
if (selType != selLines) {
|
|
|
|
|
size++;
|
|
|
|
|
if (pdoc->eolMode == SC_EOL_CRLF) {
|
|
|
|
|
size++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (size > 0) {
|
|
|
|
|
text = new char[size + 1];
|
|
|
|
|
if (text) {
|
|
|
|
|
int j = 0;
|
|
|
|
|
lineIterator.Reset();
|
|
|
|
|
while (lineIterator.Iterate()) {
|
|
|
|
|
for (int i = lineIterator.startPos;
|
|
|
|
|
i < lineIterator.endPos;
|
|
|
|
|
i++) {
|
|
|
|
|
text[j++] = pdoc->CharAt(i);
|
|
|
|
|
}
|
|
|
|
|
if (selType != selLines) {
|
|
|
|
|
if (pdoc->eolMode != SC_EOL_LF) {
|
|
|
|
|
text[j++] = '\r';
|
|
|
|
|
}
|
|
|
|
|
if (pdoc->eolMode != SC_EOL_CR) {
|
|
|
|
|
text[j++] = '\n';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
text[size] = '\0';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ss->Set(text, size + 1, pdoc->dbcsCodePage,
|
|
|
|
|
vs.styles[STYLE_DEFAULT].characterSet, selType == selRectangle);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::CopyRangeToClipboard(int start, int end) {
|
|
|
|
|
start = pdoc->ClampPositionIntoDocument(start);
|
|
|
|
|
end = pdoc->ClampPositionIntoDocument(end);
|
|
|
|
|
SelectionText selectedText;
|
|
|
|
|
selectedText.Set(CopyRange(start, end), end - start + 1,
|
|
|
|
|
pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false);
|
|
|
|
|
CopyToClipboard(selectedText);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::CopyText(int length, const char *text) {
|
|
|
|
|
SelectionText selectedText;
|
|
|
|
|
selectedText.Copy(text, length + 1,
|
|
|
|
|
pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false);
|
|
|
|
|
CopyToClipboard(selectedText);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::SetDragPosition(int newPos) {
|
|
|
|
|
if (newPos >= 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));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::StartDrag() {
|
|
|
|
|
// Always handled by subclasses
|
|
|
|
|
//SetMouseCapture(true);
|
|
|
|
|
//DisplayCursor(Window::cursorArrow);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::DropAt(int position, const char *value, bool moving, bool rectangular) {
|
|
|
|
|
//Platform::DebugPrintf("DropAt %d\n", inDragDrop);
|
|
|
|
|
if (inDragDrop)
|
|
|
|
|
dropWentOutside = false;
|
|
|
|
|
|
|
|
|
|
int positionWasInSelection = PositionInSelection(position);
|
|
|
|
|
|
|
|
|
|
bool positionOnEdgeOfSelection =
|
|
|
|
|
(position == SelectionStart()) || (position == SelectionEnd());
|
|
|
|
|
|
|
|
|
|
if ((!inDragDrop) || !(0 == positionWasInSelection) ||
|
|
|
|
|
(positionOnEdgeOfSelection && !moving)) {
|
|
|
|
|
|
|
|
|
|
int selStart = SelectionStart();
|
|
|
|
|
int selEnd = SelectionEnd();
|
|
|
|
|
|
|
|
|
|
pdoc->BeginUndoAction();
|
|
|
|
|
|
|
|
|
|
int positionAfterDeletion = position;
|
|
|
|
|
if (inDragDrop && moving) {
|
|
|
|
|
// Remove dragged out text
|
|
|
|
|
if (rectangular || selType == selLines) {
|
|
|
|
|
SelectionLineIterator lineIterator(this);
|
|
|
|
|
while (lineIterator.Iterate()) {
|
|
|
|
|
if (position >= lineIterator.startPos) {
|
|
|
|
|
if (position > lineIterator.endPos) {
|
|
|
|
|
positionAfterDeletion -= lineIterator.endPos - lineIterator.startPos;
|
|
|
|
|
} else {
|
|
|
|
|
positionAfterDeletion -= position - lineIterator.startPos;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (position > selStart) {
|
|
|
|
|
positionAfterDeletion -= selEnd - selStart;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ClearSelection();
|
|
|
|
|
}
|
|
|
|
|
position = positionAfterDeletion;
|
|
|
|
|
|
|
|
|
|
if (rectangular) {
|
|
|
|
|
PasteRectangular(position, value, istrlen(value));
|
|
|
|
|
pdoc->EndUndoAction();
|
|
|
|
|
// 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, currentPos - position);
|
|
|
|
|
if (pdoc->InsertString(position, value)) {
|
|
|
|
|
SetSelection(position + istrlen(value), position);
|
|
|
|
|
}
|
|
|
|
|
pdoc->EndUndoAction();
|
|
|
|
|
}
|
|
|
|
|
} else if (inDragDrop) {
|
|
|
|
|
SetEmptySelection(position);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return -1 if given position is before the selection,
|
|
|
|
|
* 1 if position is after the selection,
|
|
|
|
|
* 0 if position is inside the selection,
|
|
|
|
|
*/
|
|
|
|
|
int Editor::PositionInSelection(int pos) {
|
|
|
|
|
pos = MovePositionOutsideChar(pos, currentPos - pos);
|
|
|
|
|
if (pos < SelectionStart()) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if (pos > SelectionEnd()) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
if (selType == selStream) {
|
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
|
|
|
|
SelectionLineIterator lineIterator(this);
|
|
|
|
|
lineIterator.SetAt(pdoc->LineFromPosition(pos));
|
|
|
|
|
if (pos < lineIterator.startPos) {
|
|
|
|
|
return -1;
|
|
|
|
|
} else if (pos > lineIterator.endPos) {
|
|
|
|
|
return 1;
|
|
|
|
|
} else {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Editor::PointInSelection(Point pt) {
|
|
|
|
|
int pos = PositionFromLocation(pt);
|
|
|
|
|
if (0 == PositionInSelection(pos)) {
|
|
|
|
|
// Probably inside, but we must make a finer test
|
|
|
|
|
int selStart, selEnd;
|
|
|
|
|
if (selType == selStream) {
|
|
|
|
|
selStart = SelectionStart();
|
|
|
|
|
selEnd = SelectionEnd();
|
|
|
|
|
} else {
|
|
|
|
|
SelectionLineIterator lineIterator(this);
|
|
|
|
|
lineIterator.SetAt(pdoc->LineFromPosition(pos));
|
|
|
|
|
selStart = lineIterator.startPos;
|
|
|
|
|
selEnd = lineIterator.endPos;
|
|
|
|
|
}
|
|
|
|
|
if (pos == selStart) {
|
|
|
|
|
// see if just before selection
|
|
|
|
|
Point locStart = LocationFromPosition(pos);
|
|
|
|
|
if (pt.x < locStart.x) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (pos == selEnd) {
|
|
|
|
|
// see if just after selection
|
|
|
|
|
Point locEnd = LocationFromPosition(pos);
|
|
|
|
|
if (pt.x > locEnd.x) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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 = vs.fixedColumnWidth - vs.leftMarginWidth;
|
|
|
|
|
return rcSelMargin.Contains(pt);
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::LineSelection(int lineCurrent_, int lineAnchor_) {
|
|
|
|
|
if (lineAnchor_ < lineCurrent_) {
|
|
|
|
|
SetSelection(pdoc->LineStart(lineCurrent_ + 1),
|
|
|
|
|
pdoc->LineStart(lineAnchor_));
|
|
|
|
|
} else if (lineAnchor_ > lineCurrent_) {
|
|
|
|
|
SetSelection(pdoc->LineStart(lineCurrent_),
|
|
|
|
|
pdoc->LineStart(lineAnchor_ + 1));
|
|
|
|
|
} else { // Same line, select it
|
|
|
|
|
SetSelection(pdoc->LineStart(lineAnchor_ + 1),
|
|
|
|
|
pdoc->LineStart(lineAnchor_));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt) {
|
|
|
|
|
//Platform::DebugPrintf("Scintilla:ButtonDown %d %d = %d alt=%d\n", curTime, lastClickTime, curTime - lastClickTime, alt);
|
|
|
|
|
ptMouseLast = pt;
|
|
|
|
|
int newPos = PositionFromLocation(pt);
|
|
|
|
|
newPos = MovePositionOutsideChar(newPos, currentPos - newPos);
|
|
|
|
|
inDragDrop = false;
|
|
|
|
|
moveExtendsSelection = false;
|
|
|
|
|
|
|
|
|
|
bool processed = NotifyMarginClick(pt, shift, ctrl, alt);
|
|
|
|
|
if (processed)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
bool inSelMargin = PointInSelMargin(pt);
|
|
|
|
|
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);
|
|
|
|
|
SetEmptySelection(newPos);
|
|
|
|
|
bool doubleClick = false;
|
|
|
|
|
// Stop mouse button bounce changing selection type
|
|
|
|
|
if (!Platform::MouseButtonBounce() || curTime != lastClickTime) {
|
|
|
|
|
if (selectionType == selChar) {
|
|
|
|
|
selectionType = selWord;
|
|
|
|
|
doubleClick = true;
|
|
|
|
|
} else if (selectionType == selWord) {
|
|
|
|
|
selectionType = selLine;
|
|
|
|
|
} else {
|
|
|
|
|
selectionType = selChar;
|
|
|
|
|
originalAnchorPos = currentPos;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (selectionType == selWord) {
|
|
|
|
|
if (currentPos >= originalAnchorPos) { // Moved forward
|
|
|
|
|
SetSelection(pdoc->ExtendWordSelect(currentPos, 1),
|
|
|
|
|
pdoc->ExtendWordSelect(originalAnchorPos, -1));
|
|
|
|
|
} else { // Moved backward
|
|
|
|
|
SetSelection(pdoc->ExtendWordSelect(currentPos, -1),
|
|
|
|
|
pdoc->ExtendWordSelect(originalAnchorPos, 1));
|
|
|
|
|
}
|
|
|
|
|
} else if (selectionType == selLine) {
|
|
|
|
|
lineAnchor = LineFromLocation(pt);
|
|
|
|
|
SetSelection(pdoc->LineStart(lineAnchor + 1), pdoc->LineStart(lineAnchor));
|
|
|
|
|
//Platform::DebugPrintf("Triple click: %d - %d\n", anchor, currentPos);
|
|
|
|
|
} else {
|
|
|
|
|
SetEmptySelection(currentPos);
|
|
|
|
|
}
|
|
|
|
|
//Platform::DebugPrintf("Double click: %d - %d\n", anchor, currentPos);
|
|
|
|
|
if (doubleClick) {
|
|
|
|
|
NotifyDoubleClick(pt, shift);
|
|
|
|
|
if (PositionIsHotspot(newPos))
|
|
|
|
|
NotifyHotSpotDoubleClicked(newPos, shift, ctrl, alt);
|
|
|
|
|
}
|
|
|
|
|
} else { // Single click
|
|
|
|
|
if (inSelMargin) {
|
|
|
|
|
selType = selStream;
|
|
|
|
|
if (ctrl) {
|
|
|
|
|
SelectAll();
|
|
|
|
|
lastClickTime = curTime;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (!shift) {
|
|
|
|
|
lineAnchor = LineFromLocation(pt);
|
|
|
|
|
// Single click in margin: select whole line
|
|
|
|
|
LineSelection(lineAnchor, lineAnchor);
|
|
|
|
|
SetSelection(pdoc->LineStart(lineAnchor + 1),
|
|
|
|
|
pdoc->LineStart(lineAnchor));
|
|
|
|
|
} else {
|
|
|
|
|
// Single shift+click in margin: select from line anchor to clicked line
|
|
|
|
|
if (anchor > currentPos)
|
|
|
|
|
lineAnchor = pdoc->LineFromPosition(anchor - 1);
|
|
|
|
|
else
|
|
|
|
|
lineAnchor = pdoc->LineFromPosition(anchor);
|
|
|
|
|
int lineStart = LineFromLocation(pt);
|
|
|
|
|
LineSelection(lineStart, lineAnchor);
|
|
|
|
|
//lineAnchor = lineStart; // Keep the same anchor for ButtonMove
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SetDragPosition(invalidPosition);
|
|
|
|
|
SetMouseCapture(true);
|
|
|
|
|
selectionType = selLine;
|
|
|
|
|
} else {
|
|
|
|
|
if (PointIsHotspot(pt)) {
|
|
|
|
|
NotifyHotSpotClicked(newPos, shift, ctrl, alt);
|
|
|
|
|
}
|
|
|
|
|
if (!shift) {
|
|
|
|
|
inDragDrop = PointInSelection(pt) && !SelectionEmpty();
|
|
|
|
|
}
|
|
|
|
|
if (inDragDrop) {
|
|
|
|
|
SetMouseCapture(false);
|
|
|
|
|
SetDragPosition(newPos);
|
|
|
|
|
CopySelectionRange(&drag);
|
|
|
|
|
StartDrag();
|
|
|
|
|
} else {
|
|
|
|
|
SetDragPosition(invalidPosition);
|
|
|
|
|
SetMouseCapture(true);
|
|
|
|
|
if (!shift) {
|
|
|
|
|
SetEmptySelection(newPos);
|
|
|
|
|
}
|
|
|
|
|
selType = alt ? selRectangle : selStream;
|
|
|
|
|
selectionType = selChar;
|
|
|
|
|
originalAnchorPos = currentPos;
|
|
|
|
|
SetRectangularRange();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
lastClickTime = curTime;
|
|
|
|
|
lastXChosen = pt.x;
|
|
|
|
|
ShowCaretAtCurrentPosition();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Editor::PositionIsHotspot(int position) {
|
|
|
|
|
return vs.styles[pdoc->StyleAt(position) & pdoc->stylingBitsMask].hotspot;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Editor::PointIsHotspot(Point pt) {
|
|
|
|
|
int pos = PositionFromLocationClose(pt);
|
|
|
|
|
if (pos == INVALID_POSITION)
|
|
|
|
|
return false;
|
|
|
|
|
return PositionIsHotspot(pos);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::SetHotSpotRange(Point *pt) {
|
|
|
|
|
if (pt) {
|
|
|
|
|
int pos = PositionFromLocation(*pt);
|
|
|
|
|
|
|
|
|
|
// 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_) {
|
|
|
|
|
hsStart_ = hsStart;
|
|
|
|
|
hsEnd_ = hsEnd;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::ButtonMove(Point pt) {
|
|
|
|
|
if ((ptMouseLast.x != pt.x) || (ptMouseLast.y != pt.y)) {
|
|
|
|
|
DwellEnd(true);
|
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
|
int movePos = PositionFromLocation(pt);
|
|
|
|
|
movePos = MovePositionOutsideChar(movePos, currentPos - movePos);
|
|
|
|
|
if (posDrag >= 0) {
|
|
|
|
|
SetDragPosition(movePos);
|
|
|
|
|
} else {
|
|
|
|
|
if (selectionType == selChar) {
|
|
|
|
|
SetSelection(movePos);
|
|
|
|
|
} else if (selectionType == selWord) {
|
|
|
|
|
// Continue selecting by word
|
|
|
|
|
if (movePos == originalAnchorPos) { // 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 if (movePos > originalAnchorPos) { // Moved forward
|
|
|
|
|
SetSelection(pdoc->ExtendWordSelect(movePos, 1),
|
|
|
|
|
pdoc->ExtendWordSelect(originalAnchorPos, -1));
|
|
|
|
|
} else { // Moved backward
|
|
|
|
|
SetSelection(pdoc->ExtendWordSelect(movePos, -1),
|
|
|
|
|
pdoc->ExtendWordSelect(originalAnchorPos, 1));
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Continue selecting by line
|
|
|
|
|
int lineMove = LineFromLocation(pt);
|
|
|
|
|
LineSelection(lineMove, lineAnchor);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// While dragging to make rectangular selection, we don't want the current
|
|
|
|
|
// position to jump to the end of smaller or empty lines.
|
|
|
|
|
//xEndSelect = pt.x - vs.fixedColumnWidth + xOffset;
|
|
|
|
|
xEndSelect = XFromPosition(movePos);
|
|
|
|
|
|
|
|
|
|
// Autoscroll
|
|
|
|
|
PRectangle rcClient = GetClientRectangle();
|
|
|
|
|
if (pt.y > rcClient.bottom) {
|
|
|
|
|
int lineMove = cs.DisplayFromDoc(LineFromLocation(pt));
|
|
|
|
|
if (lineMove < 0) {
|
|
|
|
|
lineMove = cs.DisplayFromDoc(pdoc->LinesTotal() - 1);
|
|
|
|
|
}
|
|
|
|
|
ScrollTo(lineMove - LinesOnScreen() + 5);
|
|
|
|
|
Redraw();
|
|
|
|
|
} else if (pt.y < rcClient.top) {
|
|
|
|
|
int lineMove = cs.DisplayFromDoc(LineFromLocation(pt));
|
|
|
|
|
ScrollTo(lineMove - 5);
|
|
|
|
|
Redraw();
|
|
|
|
|
}
|
|
|
|
|
EnsureCaretVisible(false, false, true);
|
|
|
|
|
|
|
|
|
|
if (hsStart != -1 && !PositionIsHotspot(movePos))
|
|
|
|
|
SetHotSpotRange(NULL);
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
if (vs.fixedColumnWidth > 0) { // There is a margin
|
|
|
|
|
if (PointInSelMargin(pt)) {
|
|
|
|
|
DisplayCursor(Window::cursorReverseArrow);
|
|
|
|
|
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::ButtonUp(Point pt, unsigned int curTime, bool ctrl) {
|
|
|
|
|
//Platform::DebugPrintf("ButtonUp %d\n", HaveMouseCapture());
|
|
|
|
|
if (HaveMouseCapture()) {
|
|
|
|
|
if (PointInSelMargin(pt)) {
|
|
|
|
|
DisplayCursor(Window::cursorReverseArrow);
|
|
|
|
|
} else {
|
|
|
|
|
DisplayCursor(Window::cursorText);
|
|
|
|
|
SetHotSpotRange(NULL);
|
|
|
|
|
}
|
|
|
|
|
ptMouseLast = pt;
|
|
|
|
|
SetMouseCapture(false);
|
|
|
|
|
int newPos = PositionFromLocation(pt);
|
|
|
|
|
newPos = MovePositionOutsideChar(newPos, currentPos - newPos);
|
|
|
|
|
if (inDragDrop) {
|
|
|
|
|
int selStart = SelectionStart();
|
|
|
|
|
int selEnd = SelectionEnd();
|
|
|
|
|
if (selStart < selEnd) {
|
|
|
|
|
if (drag.len) {
|
|
|
|
|
if (ctrl) {
|
|
|
|
|
if (pdoc->InsertString(newPos, drag.s, drag.len)) {
|
|
|
|
|
SetSelection(newPos, newPos + drag.len);
|
|
|
|
|
}
|
|
|
|
|
} else if (newPos < selStart) {
|
|
|
|
|
pdoc->DeleteChars(selStart, drag.len);
|
|
|
|
|
if (pdoc->InsertString(newPos, drag.s, drag.len)) {
|
|
|
|
|
SetSelection(newPos, newPos + drag.len);
|
|
|
|
|
}
|
|
|
|
|
} else if (newPos > selEnd) {
|
|
|
|
|
pdoc->DeleteChars(selStart, drag.len);
|
|
|
|
|
newPos -= drag.len;
|
|
|
|
|
if (pdoc->InsertString(newPos, drag.s, drag.len)) {
|
|
|
|
|
SetSelection(newPos, newPos + drag.len);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
SetEmptySelection(newPos);
|
|
|
|
|
}
|
|
|
|
|
drag.Free();
|
|
|
|
|
}
|
|
|
|
|
selectionType = selChar;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (selectionType == selChar) {
|
|
|
|
|
SetSelection(newPos);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
SetRectangularRange();
|
|
|
|
|
lastClickTime = curTime;
|
|
|
|
|
lastClick = pt;
|
|
|
|
|
lastXChosen = pt.x;
|
|
|
|
|
if (selType == selStream) {
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
}
|
|
|
|
|
inDragDrop = false;
|
|
|
|
|
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 ((dwellDelay < SC_TIME_FOREVER) &&
|
|
|
|
|
(ticksToDwell > 0) &&
|
|
|
|
|
(!HaveMouseCapture())) {
|
|
|
|
|
ticksToDwell -= timer.tickSize;
|
|
|
|
|
if (ticksToDwell <= 0) {
|
|
|
|
|
dwelling = true;
|
|
|
|
|
NotifyDwelling(ptMouseLast, dwelling);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Editor::Idle() {
|
|
|
|
|
|
|
|
|
|
bool idleDone;
|
|
|
|
|
|
|
|
|
|
bool wrappingDone = wrapState == eWrapNone;
|
|
|
|
|
|
|
|
|
|
if (!wrappingDone) {
|
|
|
|
|
// Wrap lines during idle.
|
|
|
|
|
WrapLines(false, -1);
|
|
|
|
|
// No more wrapping
|
|
|
|
|
if (wrapStart == wrapEnd)
|
|
|
|
|
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 funtion 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();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Editor::PaintContains(PRectangle rc) {
|
|
|
|
|
return rcPaint.Contains(rc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Editor::PaintContainsMargin() {
|
|
|
|
|
PRectangle rcSelMargin = GetClientRectangle();
|
|
|
|
|
rcSelMargin.right = vs.fixedColumnWidth;
|
|
|
|
|
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.start, r.end);
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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::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
|
|
|
|
|
selType = selStream;
|
|
|
|
|
currentPos = 0;
|
|
|
|
|
anchor = 0;
|
|
|
|
|
targetStart = 0;
|
|
|
|
|
targetEnd = 0;
|
|
|
|
|
|
|
|
|
|
braces[0] = invalidPosition;
|
|
|
|
|
braces[1] = invalidPosition;
|
|
|
|
|
|
|
|
|
|
// Reset the contraction state to fully shown.
|
|
|
|
|
cs.Clear();
|
|
|
|
|
cs.InsertLines(0, pdoc->LinesTotal() - 1);
|
|
|
|
|
llc.Deallocate();
|
|
|
|
|
NeedWrapping();
|
|
|
|
|
|
|
|
|
|
pdoc->AddWatcher(this, 0);
|
|
|
|
|
SetScrollBars();
|
|
|
|
|
Redraw();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Recursively expand a fold, making lines visible except where they have an unexpanded parent.
|
|
|
|
|
*/
|
|
|
|
|
void Editor::Expand(int &line, bool doExpand) {
|
|
|
|
|
int lineMaxSubord = pdoc->GetLastChild(line);
|
|
|
|
|
line++;
|
|
|
|
|
while (line <= lineMaxSubord) {
|
|
|
|
|
if (doExpand)
|
|
|
|
|
cs.SetVisible(line, line, true);
|
|
|
|
|
int level = pdoc->GetLevel(line);
|
|
|
|
|
if (level & SC_FOLDLEVELHEADERFLAG) {
|
|
|
|
|
if (doExpand && cs.GetExpanded(line)) {
|
|
|
|
|
Expand(line, true);
|
|
|
|
|
} else {
|
|
|
|
|
Expand(line, false);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
line++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Editor::ToggleContraction(int line) {
|
|
|
|
|
if (line >= 0) {
|
|
|
|
|
if ((pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) == 0) {
|
|
|
|
|
line = pdoc->GetFoldParent(line);
|
|
|
|
|
if (line < 0)
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cs.GetExpanded(line)) {
|
|
|
|
|
int lineMaxSubord = pdoc->GetLastChild(line);
|
|
|
|
|
cs.SetExpanded(line, 0);
|
|
|
|
|
if (lineMaxSubord > line) {
|
|
|
|
|
cs.SetVisible(line + 1, lineMaxSubord, false);
|
|
|
|
|
|
|
|
|
|
int lineCurrent = pdoc->LineFromPosition(currentPos);
|
|
|
|
|
if (lineCurrent > line && lineCurrent <= lineMaxSubord) {
|
|
|
|
|
// This does not re-expand the fold
|
|
|
|
|
EnsureCaretVisible();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SetScrollBars();
|
|
|
|
|
Redraw();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
if (!(cs.GetVisible(line))) {
|
|
|
|
|
EnsureLineVisible(line, false);
|
|
|
|
|
GoToLine(line);
|
|
|
|
|
}
|
|
|
|
|
cs.SetExpanded(line, 1);
|
|
|
|
|
Expand(line, true);
|
|
|
|
|
SetScrollBars();
|
|
|
|
|
Redraw();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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.
|
|
|
|
|
WrapLines(true, -1);
|
|
|
|
|
|
|
|
|
|
if (!cs.GetVisible(lineDoc)) {
|
|
|
|
|
int lineParent = pdoc->GetFoldParent(lineDoc);
|
|
|
|
|
if (lineParent >= 0) {
|
|
|
|
|
if (lineDoc != lineParent)
|
|
|
|
|
EnsureLineVisible(lineParent, enforcePolicy);
|
|
|
|
|
if (!cs.GetExpanded(lineParent)) {
|
|
|
|
|
cs.SetExpanded(lineParent, 1);
|
|
|
|
|
Expand(lineParent, true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Editor::ReplaceTarget(bool replacePatterns, const char *text, int length) {
|
|
|
|
|
pdoc->BeginUndoAction();
|
|
|
|
|
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;
|
|
|
|
|
pdoc->InsertString(targetStart, text, length);
|
|
|
|
|
targetEnd = targetStart + length;
|
|
|
|
|
pdoc->EndUndoAction();
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool ValidMargin(unsigned long wParam) {
|
|
|
|
|
return wParam < ViewStyle::margins;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *CharPtrFromSPtr(sptr_t lParam) {
|
|
|
|
|
return reinterpret_cast<char *>(lParam);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
pdoc->BeginUndoAction();
|
|
|
|
|
pdoc->DeleteChars(0, pdoc->Length());
|
|
|
|
|
SetEmptySelection(0);
|
|
|
|
|
pdoc->InsertString(0, CharPtrFromSPtr(lParam));
|
|
|
|
|
pdoc->EndUndoAction();
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case SCI_GETTEXTLENGTH:
|
|
|
|
|
return pdoc->Length();
|
|
|
|
|
|
|
|
|
|
case SCI_CUT:
|
|
|
|
|
Cut();
|
|
|
|
|
SetLastXChosen();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_COPY:
|
|
|
|
|
Copy();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_COPYRANGE:
|
|
|
|
|
CopyRangeToClipboard(wParam, lParam);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_COPYTEXT:
|
|
|
|
|
CopyText(wParam, CharPtrFromSPtr(lParam));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_PASTE:
|
|
|
|
|
Paste();
|
|
|
|
|
if (!caretSticky) {
|
|
|
|
|
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_GETLINE: { // Risk of overwriting the end of the buffer
|
|
|
|
|
int lineStart = pdoc->LineStart(wParam);
|
|
|
|
|
int lineEnd = pdoc->LineStart(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
|
|
|
|
|
selType = selStream;
|
|
|
|
|
SetSelection(nEnd, nStart);
|
|
|
|
|
EnsureCaretVisible();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETSELTEXT: {
|
|
|
|
|
if (lParam == 0) {
|
|
|
|
|
if (selType == selStream) {
|
|
|
|
|
return 1 + SelectionEnd() - SelectionStart();
|
|
|
|
|
} else {
|
|
|
|
|
// TODO: why is selLines handled the slow way?
|
|
|
|
|
int size = 0;
|
|
|
|
|
int extraCharsPerLine = 0;
|
|
|
|
|
if (selType != selLines)
|
|
|
|
|
extraCharsPerLine = (pdoc->eolMode == SC_EOL_CRLF) ? 2 : 1;
|
|
|
|
|
SelectionLineIterator lineIterator(this);
|
|
|
|
|
while (lineIterator.Iterate()) {
|
|
|
|
|
size += lineIterator.endPos + extraCharsPerLine - lineIterator.startPos;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 1 + size;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
SelectionText selectedText;
|
|
|
|
|
CopySelectionRange(&selectedText);
|
|
|
|
|
char *ptr = CharPtrFromSPtr(lParam);
|
|
|
|
|
int iChar = 0;
|
|
|
|
|
if (selectedText.len) {
|
|
|
|
|
for (; iChar < selectedText.len; iChar++)
|
|
|
|
|
ptr[iChar] = selectedText.s[iChar];
|
|
|
|
|
} else {
|
|
|
|
|
ptr[0] = '\0';
|
|
|
|
|
}
|
|
|
|
|
return iChar;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case SCI_LINEFROMPOSITION:
|
|
|
|
|
if (static_cast<int>(wParam) < 0)
|
|
|
|
|
return 0;
|
|
|
|
|
return pdoc->LineFromPosition(wParam);
|
|
|
|
|
|
|
|
|
|
case SCI_POSITIONFROMLINE:
|
|
|
|
|
if (static_cast<int>(wParam) < 0)
|
|
|
|
|
wParam = pdoc->LineFromPosition(SelectionStart());
|
|
|
|
|
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(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(wParam + 1) - pdoc->LineStart(wParam);
|
|
|
|
|
|
|
|
|
|
case SCI_REPLACESEL: {
|
|
|
|
|
if (lParam == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
pdoc->BeginUndoAction();
|
|
|
|
|
ClearSelection();
|
|
|
|
|
char *replacement = CharPtrFromSPtr(lParam);
|
|
|
|
|
pdoc->InsertString(currentPos, replacement);
|
|
|
|
|
pdoc->EndUndoAction();
|
|
|
|
|
SetEmptySelection(currentPos + istrlen(replacement));
|
|
|
|
|
EnsureCaretVisible();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_SETTARGETSTART:
|
|
|
|
|
targetStart = wParam;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETTARGETSTART:
|
|
|
|
|
return targetStart;
|
|
|
|
|
|
|
|
|
|
case SCI_SETTARGETEND:
|
|
|
|
|
targetEnd = wParam;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETTARGETEND:
|
|
|
|
|
return targetEnd;
|
|
|
|
|
|
|
|
|
|
case SCI_TARGETFROMSELECTION:
|
|
|
|
|
if (currentPos < anchor) {
|
|
|
|
|
targetStart = currentPos;
|
|
|
|
|
targetEnd = anchor;
|
|
|
|
|
} else {
|
|
|
|
|
targetStart = anchor;
|
|
|
|
|
targetEnd = currentPos;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_REPLACETARGET:
|
|
|
|
|
PLATFORM_ASSERT(lParam);
|
|
|
|
|
return ReplaceTarget(false, CharPtrFromSPtr(lParam), wParam);
|
|
|
|
|
|
|
|
|
|
case SCI_REPLACETARGETRE:
|
|
|
|
|
PLATFORM_ASSERT(lParam);
|
|
|
|
|
return ReplaceTarget(true, CharPtrFromSPtr(lParam), wParam);
|
|
|
|
|
|
|
|
|
|
case SCI_SEARCHINTARGET:
|
|
|
|
|
PLATFORM_ASSERT(lParam);
|
|
|
|
|
return SearchInTarget(CharPtrFromSPtr(lParam), wParam);
|
|
|
|
|
|
|
|
|
|
case SCI_SETSEARCHFLAGS:
|
|
|
|
|
searchFlags = wParam;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETSEARCHFLAGS:
|
|
|
|
|
return searchFlags;
|
|
|
|
|
|
|
|
|
|
case SCI_POSITIONBEFORE:
|
|
|
|
|
return pdoc->MovePositionOutsideChar(wParam-1, -1, true);
|
|
|
|
|
|
|
|
|
|
case SCI_POSITIONAFTER:
|
|
|
|
|
return pdoc->MovePositionOutsideChar(wParam+1, 1, true);
|
|
|
|
|
|
|
|
|
|
case SCI_LINESCROLL:
|
|
|
|
|
ScrollTo(topLine + lParam);
|
|
|
|
|
HorizontalScrollTo(xOffset + wParam * vs.spaceWidth);
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
case SCI_SETXOFFSET:
|
|
|
|
|
xOffset = wParam;
|
|
|
|
|
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(lParam);
|
|
|
|
|
return pt.x;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case SCI_POINTYFROMPOSITION:
|
|
|
|
|
if (lParam < 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
|
|
|
|
Point pt = LocationFromPosition(lParam);
|
|
|
|
|
return pt.y;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case SCI_FINDTEXT:
|
|
|
|
|
return FindText(wParam, lParam);
|
|
|
|
|
|
|
|
|
|
case SCI_GETTEXTRANGE: {
|
|
|
|
|
if (lParam == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
TextRange *tr = reinterpret_cast<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<RangeToFormat *>(lParam));
|
|
|
|
|
|
|
|
|
|
case SCI_GETMARGINLEFT:
|
|
|
|
|
return vs.leftMarginWidth;
|
|
|
|
|
|
|
|
|
|
case SCI_GETMARGINRIGHT:
|
|
|
|
|
return vs.rightMarginWidth;
|
|
|
|
|
|
|
|
|
|
case SCI_SETMARGINLEFT:
|
|
|
|
|
vs.leftMarginWidth = lParam;
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_SETMARGINRIGHT:
|
|
|
|
|
vs.rightMarginWidth = lParam;
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
// Control specific mesages
|
|
|
|
|
|
|
|
|
|
case SCI_ADDTEXT: {
|
|
|
|
|
if (lParam == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
pdoc->InsertString(CurrentPosition(), CharPtrFromSPtr(lParam), wParam);
|
|
|
|
|
SetEmptySelection(currentPos + wParam);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case SCI_ADDSTYLEDTEXT: {
|
|
|
|
|
if (lParam == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
pdoc->InsertStyledString(CurrentPosition() * 2, CharPtrFromSPtr(lParam), wParam);
|
|
|
|
|
SetEmptySelection(currentPos + wParam / 2);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case SCI_INSERTTEXT: {
|
|
|
|
|
if (lParam == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
int insertPos = wParam;
|
|
|
|
|
if (static_cast<int>(wParam) == -1)
|
|
|
|
|
insertPos = CurrentPosition();
|
|
|
|
|
int newCurrent = CurrentPosition();
|
|
|
|
|
char *sz = CharPtrFromSPtr(lParam);
|
|
|
|
|
pdoc->InsertString(insertPos, sz);
|
|
|
|
|
if (newCurrent > insertPos)
|
|
|
|
|
newCurrent += istrlen(sz);
|
|
|
|
|
SetEmptySelection(newCurrent);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case SCI_APPENDTEXT:
|
|
|
|
|
pdoc->InsertString(pdoc->Length(), CharPtrFromSPtr(lParam), wParam);
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
case SCI_CLEARALL:
|
|
|
|
|
ClearAll();
|
|
|
|
|
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:
|
|
|
|
|
caret.period = wParam;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_SETWORDCHARS: {
|
|
|
|
|
pdoc->SetDefaultCharClasses(false);
|
|
|
|
|
if (lParam == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccWord);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_SETWHITESPACECHARS: {
|
|
|
|
|
if (lParam == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccSpace);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_SETCHARSDEFAULT:
|
|
|
|
|
pdoc->SetDefaultCharClasses(true);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETLENGTH:
|
|
|
|
|
return pdoc->Length();
|
|
|
|
|
|
|
|
|
|
case SCI_ALLOCATE:
|
|
|
|
|
pdoc->Allocate(wParam);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETCHARAT:
|
|
|
|
|
return pdoc->CharAt(wParam);
|
|
|
|
|
|
|
|
|
|
case SCI_SETCURRENTPOS:
|
|
|
|
|
SetSelection(wParam, anchor);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETCURRENTPOS:
|
|
|
|
|
return currentPos;
|
|
|
|
|
|
|
|
|
|
case SCI_SETANCHOR:
|
|
|
|
|
SetSelection(currentPos, wParam);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETANCHOR:
|
|
|
|
|
return anchor;
|
|
|
|
|
|
|
|
|
|
case SCI_SETSELECTIONSTART:
|
|
|
|
|
SetSelection(Platform::Maximum(currentPos, wParam), wParam);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETSELECTIONSTART:
|
|
|
|
|
return Platform::Minimum(anchor, currentPos);
|
|
|
|
|
|
|
|
|
|
case SCI_SETSELECTIONEND:
|
|
|
|
|
SetSelection(wParam, Platform::Minimum(anchor, wParam));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETSELECTIONEND:
|
|
|
|
|
return Platform::Maximum(anchor, currentPos);
|
|
|
|
|
|
|
|
|
|
case SCI_SETPRINTMAGNIFICATION:
|
|
|
|
|
printMagnification = wParam;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETPRINTMAGNIFICATION:
|
|
|
|
|
return printMagnification;
|
|
|
|
|
|
|
|
|
|
case SCI_SETPRINTCOLOURMODE:
|
|
|
|
|
printColourMode = wParam;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETPRINTCOLOURMODE:
|
|
|
|
|
return printColourMode;
|
|
|
|
|
|
|
|
|
|
case SCI_SETPRINTWRAPMODE:
|
|
|
|
|
printWrapState = (wParam == SC_WRAP_WORD) ? eWrapWord : eWrapNone;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETPRINTWRAPMODE:
|
|
|
|
|
return printWrapState;
|
|
|
|
|
|
|
|
|
|
case SCI_GETSTYLEAT:
|
|
|
|
|
if (static_cast<int>(wParam) >= pdoc->Length())
|
|
|
|
|
return 0;
|
|
|
|
|
else
|
|
|
|
|
return pdoc->StyleAt(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;
|
|
|
|
|
TextRange *tr = reinterpret_cast<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(wParam);
|
|
|
|
|
|
|
|
|
|
case SCI_MARKERDELETEHANDLE:
|
|
|
|
|
pdoc->DeleteMarkFromHandle(wParam);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETVIEWWS:
|
|
|
|
|
return vs.viewWhitespace;
|
|
|
|
|
|
|
|
|
|
case SCI_SETVIEWWS:
|
|
|
|
|
vs.viewWhitespace = static_cast<WhiteSpaceVisibility>(wParam);
|
|
|
|
|
Redraw();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_POSITIONFROMPOINT:
|
|
|
|
|
return PositionFromLocation(Point(wParam, lParam));
|
|
|
|
|
|
|
|
|
|
case SCI_POSITIONFROMPOINTCLOSE:
|
|
|
|
|
return PositionFromLocationClose(Point(wParam, lParam));
|
|
|
|
|
|
|
|
|
|
case SCI_GOTOLINE:
|
|
|
|
|
GoToLine(wParam);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GOTOPOS:
|
|
|
|
|
SetEmptySelection(wParam);
|
|
|
|
|
EnsureCaretVisible();
|
|
|
|
|
Redraw();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETCURLINE: {
|
|
|
|
|
int lineCurrentPos = pdoc->LineFromPosition(currentPos);
|
|
|
|
|
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 currentPos - lineStart;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case SCI_GETENDSTYLED:
|
|
|
|
|
return pdoc->GetEndStyled();
|
|
|
|
|
|
|
|
|
|
case SCI_GETEOLMODE:
|
|
|
|
|
return pdoc->eolMode;
|
|
|
|
|
|
|
|
|
|
case SCI_SETEOLMODE:
|
|
|
|
|
pdoc->eolMode = wParam;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_STARTSTYLING:
|
|
|
|
|
pdoc->StartStyling(wParam, static_cast<char>(lParam));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_SETSTYLING:
|
|
|
|
|
pdoc->SetStyleFor(wParam, static_cast<char>(lParam));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_SETSTYLINGEX: // Specify a complete styling buffer
|
|
|
|
|
if (lParam == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
pdoc->SetStyles(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_SETTABWIDTH:
|
|
|
|
|
if (wParam > 0) {
|
|
|
|
|
pdoc->tabInChars = wParam;
|
|
|
|
|
if (pdoc->indentInChars == 0)
|
|
|
|
|
pdoc->actualIndentInChars = pdoc->tabInChars;
|
|
|
|
|
}
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETTABWIDTH:
|
|
|
|
|
return pdoc->tabInChars;
|
|
|
|
|
|
|
|
|
|
case SCI_SETINDENT:
|
|
|
|
|
pdoc->indentInChars = 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(wParam, lParam);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETLINEINDENTATION:
|
|
|
|
|
return pdoc->GetLineIndentation(wParam);
|
|
|
|
|
|
|
|
|
|
case SCI_GETLINEINDENTPOSITION:
|
|
|
|
|
return pdoc->GetLineIndentPosition(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 = wParam;
|
|
|
|
|
ticksToDwell = dwellDelay;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETMOUSEDWELLTIME:
|
|
|
|
|
return dwellDelay;
|
|
|
|
|
|
|
|
|
|
case SCI_WORDSTARTPOSITION:
|
|
|
|
|
return pdoc->ExtendWordSelect(wParam, -1, lParam != 0);
|
|
|
|
|
|
|
|
|
|
case SCI_WORDENDPOSITION:
|
|
|
|
|
return pdoc->ExtendWordSelect(wParam, 1, lParam != 0);
|
|
|
|
|
|
|
|
|
|
case SCI_SETWRAPMODE:
|
|
|
|
|
switch (wParam) {
|
|
|
|
|
case SC_WRAP_WORD:
|
|
|
|
|
wrapState = eWrapWord;
|
|
|
|
|
break;
|
|
|
|
|
case SC_WRAP_CHAR:
|
|
|
|
|
wrapState = eWrapChar;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
wrapState = eWrapNone;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
xOffset = 0;
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
ReconfigureScrollBars();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETWRAPMODE:
|
|
|
|
|
return wrapState;
|
|
|
|
|
|
|
|
|
|
case SCI_SETWRAPVISUALFLAGS:
|
|
|
|
|
wrapVisualFlags = wParam;
|
|
|
|
|
actualWrapVisualStartIndent = wrapVisualStartIndent;
|
|
|
|
|
if ((wrapVisualFlags & SC_WRAPVISUALFLAG_START) && (actualWrapVisualStartIndent == 0))
|
|
|
|
|
actualWrapVisualStartIndent = 1; // must indent to show start visual
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
ReconfigureScrollBars();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETWRAPVISUALFLAGS:
|
|
|
|
|
return wrapVisualFlags;
|
|
|
|
|
|
|
|
|
|
case SCI_SETWRAPVISUALFLAGSLOCATION:
|
|
|
|
|
wrapVisualFlagsLocation = wParam;
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETWRAPVISUALFLAGSLOCATION:
|
|
|
|
|
return wrapVisualFlagsLocation;
|
|
|
|
|
|
|
|
|
|
case SCI_SETWRAPSTARTINDENT:
|
|
|
|
|
wrapVisualStartIndent = wParam;
|
|
|
|
|
actualWrapVisualStartIndent = wrapVisualStartIndent;
|
|
|
|
|
if ((wrapVisualFlags & SC_WRAPVISUALFLAG_START) && (actualWrapVisualStartIndent == 0))
|
|
|
|
|
actualWrapVisualStartIndent = 1; // must indent to show start visual
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
ReconfigureScrollBars();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETWRAPSTARTINDENT:
|
|
|
|
|
return wrapVisualStartIndent;
|
|
|
|
|
|
|
|
|
|
case SCI_SETLAYOUTCACHE:
|
|
|
|
|
llc.SetLevel(wParam);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETLAYOUTCACHE:
|
|
|
|
|
return llc.GetLevel();
|
|
|
|
|
|
|
|
|
|
case SCI_SETSCROLLWIDTH:
|
|
|
|
|
PLATFORM_ASSERT(wParam > 0);
|
|
|
|
|
if ((wParam > 0) && (wParam != static_cast<unsigned int >(scrollWidth))) {
|
|
|
|
|
scrollWidth = wParam;
|
|
|
|
|
SetScrollBars();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETSCROLLWIDTH:
|
|
|
|
|
return scrollWidth;
|
|
|
|
|
|
|
|
|
|
case SCI_LINESJOIN:
|
|
|
|
|
LinesJoin();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_LINESSPLIT:
|
|
|
|
|
LinesSplit(wParam);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_TEXTWIDTH:
|
|
|
|
|
PLATFORM_ASSERT(wParam <= STYLE_MAX);
|
|
|
|
|
PLATFORM_ASSERT(lParam);
|
|
|
|
|
return TextWidth(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 == 0) || (wParam == 1));
|
|
|
|
|
if (caretSticky != (wParam != 0)) {
|
|
|
|
|
caretSticky = wParam != 0;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETCARETSTICKY:
|
|
|
|
|
return caretSticky;
|
|
|
|
|
|
|
|
|
|
case SCI_TOGGLECARETSTICKY:
|
|
|
|
|
caretSticky = !caretSticky;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETCOLUMN:
|
|
|
|
|
return pdoc->GetColumn(wParam);
|
|
|
|
|
|
|
|
|
|
case SCI_FINDCOLUMN:
|
|
|
|
|
return pdoc->FindColumn(wParam, 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();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETVSCROLLBAR:
|
|
|
|
|
return verticalScrollBarVisible;
|
|
|
|
|
|
|
|
|
|
case SCI_SETINDENTATIONGUIDES:
|
|
|
|
|
vs.viewIndentationGuides = wParam != 0;
|
|
|
|
|
Redraw();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETINDENTATIONGUIDES:
|
|
|
|
|
return vs.viewIndentationGuides;
|
|
|
|
|
|
|
|
|
|
case SCI_SETHIGHLIGHTGUIDE:
|
|
|
|
|
if ((highlightGuideColumn != static_cast<int>(wParam)) || (wParam > 0)) {
|
|
|
|
|
highlightGuideColumn = wParam;
|
|
|
|
|
Redraw();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETHIGHLIGHTGUIDE:
|
|
|
|
|
return highlightGuideColumn;
|
|
|
|
|
|
|
|
|
|
case SCI_GETLINEENDPOSITION:
|
|
|
|
|
return pdoc->LineEnd(wParam);
|
|
|
|
|
|
|
|
|
|
case SCI_SETCODEPAGE:
|
|
|
|
|
if (ValidCodePage(wParam)) {
|
|
|
|
|
pdoc->dbcsCodePage = wParam;
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETCODEPAGE:
|
|
|
|
|
return pdoc->dbcsCodePage;
|
|
|
|
|
|
|
|
|
|
case SCI_SETUSEPALETTE:
|
|
|
|
|
palette.allowRealization = wParam != 0;
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETUSEPALETTE:
|
|
|
|
|
return palette.allowRealization;
|
|
|
|
|
|
|
|
|
|
// Marker definition and setting
|
|
|
|
|
case SCI_MARKERDEFINE:
|
|
|
|
|
if (wParam <= MARKER_MAX)
|
|
|
|
|
vs.markers[wParam].markType = lParam;
|
|
|
|
|
InvalidateStyleData();
|
|
|
|
|
RedrawSelMargin();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_MARKERSETFORE:
|
|
|
|
|
if (wParam <= MARKER_MAX)
|
|
|
|
|
vs.markers[wParam].fore.desired = ColourDesired(lParam);
|
|
|
|
|
InvalidateStyleData();
|
|
|
|
|
RedrawSelMargin();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_MARKERSETBACK:
|
|
|
|
|
if (wParam <= MARKER_MAX)
|
|
|
|
|
vs.markers[wParam].back.desired = ColourDesired(lParam);
|
|
|
|
|
InvalidateStyleData();
|
|
|
|
|
RedrawSelMargin();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_MARKERSETALPHA:
|
|
|
|
|
if (wParam <= MARKER_MAX)
|
|
|
|
|
vs.markers[wParam].alpha = lParam;
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_MARKERADD: {
|
|
|
|
|
int markerID = pdoc->AddMark(wParam, lParam);
|
|
|
|
|
return markerID;
|
|
|
|
|
}
|
|
|
|
|
case SCI_MARKERADDSET:
|
|
|
|
|
if (lParam != 0)
|
|
|
|
|
pdoc->AddMarkSet(wParam, lParam);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_MARKERDELETE:
|
|
|
|
|
pdoc->DeleteMark(wParam, lParam);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_MARKERDELETEALL:
|
|
|
|
|
pdoc->DeleteAllMarks(static_cast<int>(wParam));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_MARKERGET:
|
|
|
|
|
return pdoc->GetMark(wParam);
|
|
|
|
|
|
|
|
|
|
case SCI_MARKERNEXT: {
|
|
|
|
|
int lt = pdoc->LinesTotal();
|
|
|
|
|
for (int iLine = wParam; iLine < lt; iLine++) {
|
|
|
|
|
if ((pdoc->GetMark(iLine) & lParam) != 0)
|
|
|
|
|
return iLine;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
case SCI_MARKERPREVIOUS: {
|
|
|
|
|
for (int iLine = 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));
|
|
|
|
|
};
|
|
|
|
|
InvalidateStyleData();
|
|
|
|
|
RedrawSelMargin();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_SETMARGINTYPEN:
|
|
|
|
|
if (ValidMargin(wParam)) {
|
|
|
|
|
vs.ms[wParam].style = 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) {
|
|
|
|
|
vs.ms[wParam].width = 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 = 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_STYLECLEARALL:
|
|
|
|
|
vs.ClearStyles();
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_STYLESETFORE:
|
|
|
|
|
if (wParam <= STYLE_MAX) {
|
|
|
|
|
vs.styles[wParam].fore.desired = ColourDesired(lParam);
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case SCI_STYLESETBACK:
|
|
|
|
|
if (wParam <= STYLE_MAX) {
|
|
|
|
|
vs.styles[wParam].back.desired = ColourDesired(lParam);
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case SCI_STYLESETBOLD:
|
|
|
|
|
if (wParam <= STYLE_MAX) {
|
|
|
|
|
vs.styles[wParam].bold = lParam != 0;
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case SCI_STYLESETITALIC:
|
|
|
|
|
if (wParam <= STYLE_MAX) {
|
|
|
|
|
vs.styles[wParam].italic = lParam != 0;
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case SCI_STYLESETEOLFILLED:
|
|
|
|
|
if (wParam <= STYLE_MAX) {
|
|
|
|
|
vs.styles[wParam].eolFilled = lParam != 0;
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case SCI_STYLESETSIZE:
|
|
|
|
|
if (wParam <= STYLE_MAX) {
|
|
|
|
|
vs.styles[wParam].size = lParam;
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case SCI_STYLESETFONT:
|
|
|
|
|
if (lParam == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
if (wParam <= STYLE_MAX) {
|
|
|
|
|
vs.SetStyleFontName(wParam, CharPtrFromSPtr(lParam));
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case SCI_STYLESETUNDERLINE:
|
|
|
|
|
if (wParam <= STYLE_MAX) {
|
|
|
|
|
vs.styles[wParam].underline = lParam != 0;
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case SCI_STYLESETCASE:
|
|
|
|
|
if (wParam <= STYLE_MAX) {
|
|
|
|
|
vs.styles[wParam].caseForce = static_cast<Style::ecaseForced>(lParam);
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case SCI_STYLESETCHARACTERSET:
|
|
|
|
|
if (wParam <= STYLE_MAX) {
|
|
|
|
|
vs.styles[wParam].characterSet = lParam;
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case SCI_STYLESETVISIBLE:
|
|
|
|
|
if (wParam <= STYLE_MAX) {
|
|
|
|
|
vs.styles[wParam].visible = lParam != 0;
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case SCI_STYLESETCHANGEABLE:
|
|
|
|
|
if (wParam <= STYLE_MAX) {
|
|
|
|
|
vs.styles[wParam].changeable = lParam != 0;
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case SCI_STYLESETHOTSPOT:
|
|
|
|
|
if (wParam <= STYLE_MAX) {
|
|
|
|
|
vs.styles[wParam].hotspot = lParam != 0;
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_STYLERESETDEFAULT:
|
|
|
|
|
vs.ResetDefaultStyle();
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_SETSTYLEBITS:
|
|
|
|
|
pdoc->SetStylingBits(wParam);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETSTYLEBITS:
|
|
|
|
|
return pdoc->stylingBits;
|
|
|
|
|
|
|
|
|
|
case SCI_SETLINESTATE:
|
|
|
|
|
return pdoc->SetLineState(wParam, lParam);
|
|
|
|
|
|
|
|
|
|
case SCI_GETLINESTATE:
|
|
|
|
|
return pdoc->GetLineState(wParam);
|
|
|
|
|
|
|
|
|
|
case SCI_GETMAXLINESTATE:
|
|
|
|
|
return pdoc->GetMaxLineState();
|
|
|
|
|
|
|
|
|
|
case SCI_GETCARETLINEVISIBLE:
|
|
|
|
|
return vs.showCaretLineBackground;
|
|
|
|
|
case SCI_SETCARETLINEVISIBLE:
|
|
|
|
|
vs.showCaretLineBackground = wParam != 0;
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_GETCARETLINEBACK:
|
|
|
|
|
return vs.caretLineBackground.desired.AsLong();
|
|
|
|
|
case SCI_SETCARETLINEBACK:
|
|
|
|
|
vs.caretLineBackground.desired = wParam;
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
break;
|
|
|
|
|
case SCI_GETCARETLINEBACKALPHA:
|
|
|
|
|
return vs.caretLineAlpha;
|
|
|
|
|
case SCI_SETCARETLINEBACKALPHA:
|
|
|
|
|
vs.caretLineAlpha = wParam;
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
// Folding messages
|
|
|
|
|
|
|
|
|
|
case SCI_VISIBLEFROMDOCLINE:
|
|
|
|
|
return cs.DisplayFromDoc(wParam);
|
|
|
|
|
|
|
|
|
|
case SCI_DOCLINEFROMVISIBLE:
|
|
|
|
|
return cs.DocFromDisplay(wParam);
|
|
|
|
|
|
|
|
|
|
case SCI_WRAPCOUNT:
|
|
|
|
|
return WrapCount(wParam);
|
|
|
|
|
|
|
|
|
|
case SCI_SETFOLDLEVEL: {
|
|
|
|
|
int prev = pdoc->SetLevel(wParam, lParam);
|
|
|
|
|
if (prev != lParam)
|
|
|
|
|
RedrawSelMargin();
|
|
|
|
|
return prev;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case SCI_GETFOLDLEVEL:
|
|
|
|
|
return pdoc->GetLevel(wParam);
|
|
|
|
|
|
|
|
|
|
case SCI_GETLASTCHILD:
|
|
|
|
|
return pdoc->GetLastChild(wParam, lParam);
|
|
|
|
|
|
|
|
|
|
case SCI_GETFOLDPARENT:
|
|
|
|
|
return pdoc->GetFoldParent(wParam);
|
|
|
|
|
|
|
|
|
|
case SCI_SHOWLINES:
|
|
|
|
|
cs.SetVisible(wParam, lParam, true);
|
|
|
|
|
SetScrollBars();
|
|
|
|
|
Redraw();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_HIDELINES:
|
|
|
|
|
cs.SetVisible(wParam, lParam, false);
|
|
|
|
|
SetScrollBars();
|
|
|
|
|
Redraw();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETLINEVISIBLE:
|
|
|
|
|
return cs.GetVisible(wParam);
|
|
|
|
|
|
|
|
|
|
case SCI_SETFOLDEXPANDED:
|
|
|
|
|
if (cs.SetExpanded(wParam, lParam != 0)) {
|
|
|
|
|
RedrawSelMargin();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETFOLDEXPANDED:
|
|
|
|
|
return cs.GetExpanded(wParam);
|
|
|
|
|
|
|
|
|
|
case SCI_SETFOLDFLAGS:
|
|
|
|
|
foldFlags = wParam;
|
|
|
|
|
Redraw();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_TOGGLEFOLD:
|
|
|
|
|
ToggleContraction(wParam);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_ENSUREVISIBLE:
|
|
|
|
|
EnsureLineVisible(wParam, false);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_ENSUREVISIBLEENFORCEPOLICY:
|
|
|
|
|
EnsureLineVisible(wParam, true);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_SEARCHANCHOR:
|
|
|
|
|
SearchAnchor();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_SEARCHNEXT:
|
|
|
|
|
case SCI_SEARCHPREV:
|
|
|
|
|
return SearchText(iMessage, wParam, lParam);
|
|
|
|
|
|
|
|
|
|
#ifdef INCLUDE_DEPRECATED_FEATURES
|
|
|
|
|
case SCI_SETCARETPOLICY: // Deprecated
|
|
|
|
|
caretXPolicy = caretYPolicy = wParam;
|
|
|
|
|
caretXSlop = caretYSlop = lParam;
|
|
|
|
|
break;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
case SCI_SETXCARETPOLICY:
|
|
|
|
|
caretXPolicy = wParam;
|
|
|
|
|
caretXSlop = lParam;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_SETYCARETPOLICY:
|
|
|
|
|
caretYPolicy = wParam;
|
|
|
|
|
caretYSlop = lParam;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_SETVISIBLEPOLICY:
|
|
|
|
|
visiblePolicy = wParam;
|
|
|
|
|
visibleSlop = lParam;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_LINESONSCREEN:
|
|
|
|
|
return LinesOnScreen();
|
|
|
|
|
|
|
|
|
|
case SCI_SETSELFORE:
|
|
|
|
|
vs.selforeset = wParam != 0;
|
|
|
|
|
vs.selforeground.desired = ColourDesired(lParam);
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_SETSELBACK:
|
|
|
|
|
vs.selbackset = wParam != 0;
|
|
|
|
|
vs.selbackground.desired = ColourDesired(lParam);
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_SETSELALPHA:
|
|
|
|
|
vs.selAlpha = wParam;
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETSELALPHA:
|
|
|
|
|
return vs.selAlpha;
|
|
|
|
|
|
|
|
|
|
case SCI_SETWHITESPACEFORE:
|
|
|
|
|
vs.whitespaceForegroundSet = wParam != 0;
|
|
|
|
|
vs.whitespaceForeground.desired = ColourDesired(lParam);
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_SETWHITESPACEBACK:
|
|
|
|
|
vs.whitespaceBackgroundSet = wParam != 0;
|
|
|
|
|
vs.whitespaceBackground.desired = ColourDesired(lParam);
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_SETCARETFORE:
|
|
|
|
|
vs.caretcolour.desired = ColourDesired(wParam);
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETCARETFORE:
|
|
|
|
|
return vs.caretcolour.desired.AsLong();
|
|
|
|
|
|
|
|
|
|
case SCI_SETCARETWIDTH:
|
|
|
|
|
if (wParam <= 0)
|
|
|
|
|
vs.caretWidth = 0;
|
|
|
|
|
else if (wParam >= 3)
|
|
|
|
|
vs.caretWidth = 3;
|
|
|
|
|
else
|
|
|
|
|
vs.caretWidth = wParam;
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETCARETWIDTH:
|
|
|
|
|
return vs.caretWidth;
|
|
|
|
|
|
|
|
|
|
case SCI_ASSIGNCMDKEY:
|
|
|
|
|
kmap.AssignCmdKey(Platform::LowShortFromLong(wParam),
|
|
|
|
|
Platform::HighShortFromLong(wParam), lParam);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_CLEARCMDKEY:
|
|
|
|
|
kmap.AssignCmdKey(Platform::LowShortFromLong(wParam),
|
|
|
|
|
Platform::HighShortFromLong(wParam), SCI_NULL);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_CLEARALLCMDKEYS:
|
|
|
|
|
kmap.Clear();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_INDICSETSTYLE:
|
|
|
|
|
if (wParam <= INDIC_MAX) {
|
|
|
|
|
vs.indicators[wParam].style = 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.desired = ColourDesired(lParam);
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_INDICGETFORE:
|
|
|
|
|
return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fore.desired.AsLong() : 0;
|
|
|
|
|
|
|
|
|
|
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_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_ZOOMIN:
|
|
|
|
|
case SCI_ZOOMOUT:
|
|
|
|
|
case SCI_DELWORDLEFT:
|
|
|
|
|
case SCI_DELWORDRIGHT:
|
|
|
|
|
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), lParam, STYLE_BRACELIGHT);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_BRACEBADLIGHT:
|
|
|
|
|
SetBraceHighlight(static_cast<int>(wParam), -1, STYLE_BRACEBAD);
|
|
|
|
|
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(wParam, lParam);
|
|
|
|
|
|
|
|
|
|
case SCI_GETVIEWEOL:
|
|
|
|
|
return vs.viewEOL;
|
|
|
|
|
|
|
|
|
|
case SCI_SETVIEWEOL:
|
|
|
|
|
vs.viewEOL = wParam != 0;
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_SETZOOM:
|
|
|
|
|
vs.zoomLevel = wParam;
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
NotifyZoom();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETZOOM:
|
|
|
|
|
return vs.zoomLevel;
|
|
|
|
|
|
|
|
|
|
case SCI_GETEDGECOLUMN:
|
|
|
|
|
return theEdge;
|
|
|
|
|
|
|
|
|
|
case SCI_SETEDGECOLUMN:
|
|
|
|
|
theEdge = wParam;
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETEDGEMODE:
|
|
|
|
|
return vs.edgeState;
|
|
|
|
|
|
|
|
|
|
case SCI_SETEDGEMODE:
|
|
|
|
|
vs.edgeState = wParam;
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETEDGECOLOUR:
|
|
|
|
|
return vs.edgecolour.desired.AsLong();
|
|
|
|
|
|
|
|
|
|
case SCI_SETEDGECOLOUR:
|
|
|
|
|
vs.edgecolour.desired = ColourDesired(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();
|
|
|
|
|
if (doc) {
|
|
|
|
|
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_SETMODEVENTMASK:
|
|
|
|
|
modEventMask = wParam;
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
case SCI_GETMODEVENTMASK:
|
|
|
|
|
return modEventMask;
|
|
|
|
|
|
|
|
|
|
case SCI_CONVERTEOLS:
|
|
|
|
|
pdoc->ConvertLineEnds(wParam);
|
|
|
|
|
SetSelection(currentPos, anchor); // Ensure selection inside document
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
case SCI_SETLENGTHFORENCODE:
|
|
|
|
|
lengthForEncode = wParam;
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
case SCI_SELECTIONISRECTANGLE:
|
|
|
|
|
return selType == selRectangle ? 1 : 0;
|
|
|
|
|
|
|
|
|
|
case SCI_SETSELECTIONMODE: {
|
|
|
|
|
switch (wParam) {
|
|
|
|
|
case SC_SEL_STREAM:
|
|
|
|
|
moveExtendsSelection = !moveExtendsSelection || (selType != selStream);
|
|
|
|
|
selType = selStream;
|
|
|
|
|
break;
|
|
|
|
|
case SC_SEL_RECTANGLE:
|
|
|
|
|
moveExtendsSelection = !moveExtendsSelection || (selType != selRectangle);
|
|
|
|
|
selType = selRectangle;
|
|
|
|
|
break;
|
|
|
|
|
case SC_SEL_LINES:
|
|
|
|
|
moveExtendsSelection = !moveExtendsSelection || (selType != selLines);
|
|
|
|
|
selType = selLines;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
moveExtendsSelection = !moveExtendsSelection || (selType != selStream);
|
|
|
|
|
selType = selStream;
|
|
|
|
|
}
|
|
|
|
|
InvalidateSelection(currentPos, anchor);
|
|
|
|
|
}
|
|
|
|
|
case SCI_GETSELECTIONMODE:
|
|
|
|
|
switch (selType) {
|
|
|
|
|
case selStream:
|
|
|
|
|
return SC_SEL_STREAM;
|
|
|
|
|
case selRectangle:
|
|
|
|
|
return SC_SEL_RECTANGLE;
|
|
|
|
|
case selLines:
|
|
|
|
|
return SC_SEL_LINES;
|
|
|
|
|
default: // ?!
|
|
|
|
|
return SC_SEL_STREAM;
|
|
|
|
|
}
|
|
|
|
|
case SCI_GETLINESELSTARTPOSITION: {
|
|
|
|
|
SelectionLineIterator lineIterator(this);
|
|
|
|
|
lineIterator.SetAt(wParam);
|
|
|
|
|
return lineIterator.startPos;
|
|
|
|
|
}
|
|
|
|
|
case SCI_GETLINESELENDPOSITION: {
|
|
|
|
|
SelectionLineIterator lineIterator(this);
|
|
|
|
|
lineIterator.SetAt(wParam);
|
|
|
|
|
return lineIterator.endPos;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 = wParam;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETSTATUS:
|
|
|
|
|
return errorStatus;
|
|
|
|
|
|
|
|
|
|
case SCI_SETMOUSEDOWNCAPTURES:
|
|
|
|
|
mouseDownCaptures = wParam != 0;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETMOUSEDOWNCAPTURES:
|
|
|
|
|
return mouseDownCaptures;
|
|
|
|
|
|
|
|
|
|
case SCI_SETCURSOR:
|
|
|
|
|
cursorMode = wParam;
|
|
|
|
|
DisplayCursor(Window::cursorText);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETCURSOR:
|
|
|
|
|
return cursorMode;
|
|
|
|
|
|
|
|
|
|
case SCI_SETCONTROLCHARSYMBOL:
|
|
|
|
|
controlCharSymbol = wParam;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETCONTROLCHARSYMBOL:
|
|
|
|
|
return controlCharSymbol;
|
|
|
|
|
|
|
|
|
|
case SCI_STARTRECORD:
|
|
|
|
|
recordingMacro = true;
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
case SCI_STOPRECORD:
|
|
|
|
|
recordingMacro = false;
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
case SCI_MOVECARETINSIDEVIEW:
|
|
|
|
|
MoveCaretInsideView();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_SETFOLDMARGINCOLOUR:
|
|
|
|
|
vs.foldmarginColourSet = wParam != 0;
|
|
|
|
|
vs.foldmarginColour.desired = ColourDesired(lParam);
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_SETFOLDMARGINHICOLOUR:
|
|
|
|
|
vs.foldmarginHighlightColourSet = wParam != 0;
|
|
|
|
|
vs.foldmarginHighlightColour.desired = ColourDesired(lParam);
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_SETHOTSPOTACTIVEFORE:
|
|
|
|
|
vs.hotspotForegroundSet = wParam != 0;
|
|
|
|
|
vs.hotspotForeground.desired = ColourDesired(lParam);
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_SETHOTSPOTACTIVEBACK:
|
|
|
|
|
vs.hotspotBackgroundSet = wParam != 0;
|
|
|
|
|
vs.hotspotBackground.desired = ColourDesired(lParam);
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_SETHOTSPOTACTIVEUNDERLINE:
|
|
|
|
|
vs.hotspotUnderline = wParam != 0;
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_SETHOTSPOTSINGLELINE:
|
|
|
|
|
vs.hotspotSingleLine = wParam != 0;
|
|
|
|
|
InvalidateStyleRedraw();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_SETPASTECONVERTENDINGS:
|
|
|
|
|
convertPastes = wParam != 0;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCI_GETPASTECONVERTENDINGS:
|
|
|
|
|
return convertPastes ? 1 : 0;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return DefWndProc(iMessage, wParam, lParam);
|
|
|
|
|
}
|
|
|
|
|
//Platform::DebugPrintf("end wnd proc\n");
|
|
|
|
|
return 0l;
|
|
|
|
|
}
|