diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Editor.cxx | 3705 | ||||
| -rw-r--r-- | src/Editor.h | 281 | ||||
| -rw-r--r-- | src/Indicator.cxx | 45 | ||||
| -rw-r--r-- | src/Indicator.h | 18 | ||||
| -rw-r--r-- | src/KeyMap.cxx | 111 | ||||
| -rw-r--r-- | src/KeyMap.h | 35 | ||||
| -rw-r--r-- | src/KeyWords.cxx | 2648 | ||||
| -rw-r--r-- | src/LineMarker.cxx | 125 | ||||
| -rw-r--r-- | src/LineMarker.h | 22 | ||||
| -rw-r--r-- | src/PropSet.cxx | 399 | ||||
| -rw-r--r-- | src/SVector.h | 110 | ||||
| -rw-r--r-- | src/ScintillaBase.cxx | 397 | ||||
| -rw-r--r-- | src/ScintillaBase.h | 68 | ||||
| -rw-r--r-- | src/Style.cxx | 89 | ||||
| -rw-r--r-- | src/Style.h | 39 | ||||
| -rw-r--r-- | src/ViewStyle.cxx | 226 | ||||
| -rw-r--r-- | src/ViewStyle.h | 72 | 
17 files changed, 8390 insertions, 0 deletions
| diff --git a/src/Editor.cxx b/src/Editor.cxx new file mode 100644 index 000000000..1ead18d34 --- /dev/null +++ b/src/Editor.cxx @@ -0,0 +1,3705 @@ +// Scintilla source code edit control +// Editor.cxx - main code for the edit control +// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <ctype.h> + +#include "Platform.h" + +#include "Scintilla.h" +#include "ContractionState.h" +#include "SVector.h" +#include "CellBuffer.h" +#include "KeyMap.h" +#include "Indicator.h" +#include "LineMarker.h" +#include "Style.h" +#include "ViewStyle.h" +#include "Document.h" +#include "Editor.h" + +Caret::Caret() : +active(true), on(true), period(500) {} + +Timer::Timer() : +ticking(false), ticksToWait(0), tickerID(0) {} + +Editor::Editor() { +	ctrlID = 0; + +	stylesValid = false; + +	hideSelection = false; +	inOverstrike = false; + +	bufferedDraw = true; + +	lastClickTime = 0; +	ptMouseLast.x = 0; +	ptMouseLast.y = 0; +	firstExpose = true; +	inDragDrop = false; +	dropWentOutside = false; +	posDrag = invalidPosition; +	posDrop = invalidPosition; +	selectionType = selChar; + +	lastXChosen = 0; +	lineAnchor = 0; +	originalAnchorPos = 0; + +	dragChars = 0; +	lenDrag = 0; +	dragIsRectangle = false; +	selType = selStream; +	xStartSelect = 0; +	xEndSelect = 0; + +	caretPolicy = CARET_SLOP; +	caretSlop = 0; + +	searchAnchor = 0; +	 +	ucWheelScrollLines = 0; +	cWheelDelta = 0;   //wheel delta from roll + +	xOffset = 0; +	xCaretMargin = 50; +	 +	currentPos = 0; +	anchor = 0; + +	topLine = 0; +	posTopLine = 0; +	 +	needUpdateUI = true; +	braces[0]=invalidPosition; +	braces[1]=invalidPosition; +	bracesMatchStyle = STYLE_BRACEBAD; +	 +	edgeState = EDGE_NONE; +	theEdge = 0; +	 +	paintState = notPainting; +	 +	modEventMask = SC_MODEVENTMASKALL; + +	pdoc = new Document(); +	pdoc ->AddRef(); +	pdoc->AddWatcher(this, 0); + +#ifdef MACRO_SUPPORT +	recordingMacro = 0; +#endif +	foldFlags = 0; +} + +Editor::~Editor() { +	pdoc->RemoveWatcher(this, 0); +	pdoc->Release(); +	pdoc = 0; +	DropGraphics(); + +	delete []dragChars; +	dragChars = 0; +	lenDrag = 0; +} + +void Editor::Finalise() { +} + +void Editor::DropGraphics() { +	pixmapLine.Release(); +	pixmapSelMargin.Release(); +	pixmapSelPattern.Release(); +} + +void Editor::InvalidateStyleData() { +	stylesValid = false; +	palette.Release(); +	DropGraphics(); +} + +void Editor::InvalidateStyleRedraw() { +	InvalidateStyleData(); +	Redraw(); +} + +void Editor::RefreshColourPalette(Palette &pal, bool want) { +	vs.RefreshColourPalette(pal, want); +} + +void Editor::RefreshStyleData() { +	if (!stylesValid) { +		stylesValid = true; +		Surface surface; +		surface.Init(); +		vs.Refresh(surface); +		RefreshColourPalette(palette, true); +		palette.Allocate(wMain); +		RefreshColourPalette(palette, false); +		SetScrollBars(); +	} +} + +PRectangle Editor::GetClientRectangle() { +	return wDraw.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() - LinesOnScreen(); +	if (retVal < 0) +		return 0; +	else +		return retVal; +} + +bool IsControlCharacter(char ch) { +	// iscntrl returns true for lots of chars > 127 which are displayable +	return ch >= 0 && ch < ' '; +} + +const char *ControlCharacterString(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"; +	} +} + +Point Editor::LocationFromPosition(unsigned int pos) { +	RefreshStyleData(); +	int line = pdoc->LineFromPosition(pos); +	int lineVisible = cs.DisplayFromDoc(line); +	//Platform::DebugPrintf("line=%d\n", line); +	Surface surface; +	surface.Init(); +	Point pt; +	pt.y = (lineVisible - topLine) * vs.lineHeight;  	// + half a lineheight? +	unsigned int posLineStart = pdoc->LineStart(line); +	if ((pos - posLineStart) > LineLayout::maxLineLength) { +		// very long line so put x at arbitrary large position +		pt.x = 30000 + vs.fixedColumnWidth - xOffset; +	} else { +		LineLayout ll; +		LayoutLine(line, &surface, vs, ll); +		pt.x = ll.positions[pos - posLineStart] + vs.fixedColumnWidth - xOffset; +	} +	return pt; +} + +int Editor::XFromPosition(unsigned 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(topLine); +} + +int Editor::PositionFromLocation(Point pt) { +	RefreshStyleData(); +	pt.x = pt.x - vs.fixedColumnWidth + xOffset; +	int line = cs.DocFromDisplay(pt.y / vs.lineHeight + topLine); +	if (pt.y < 0) {	// Division rounds towards 0 +		line = cs.DocFromDisplay((pt.y - (vs.lineHeight - 1)) / vs.lineHeight + topLine); +	} +	if (line < 0) +		return 0; +	if (line >= pdoc->LinesTotal()) +		return pdoc->Length(); +//Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine); +	Surface surface; +	surface.Init(); +	unsigned int posLineStart = pdoc->LineStart(line); + +	LineLayout ll; +	LayoutLine(line, &surface, vs, ll); +	for (int i = 0; i < ll.numCharsInLine; i++) { +		if (pt.x < ((ll.positions[i] + ll.positions[i + 1]) / 2) ||  +			ll.chars[i] == '\r' || ll.chars[i] == '\n') { +			return i + posLineStart; +		} +	} + +	return ll.numCharsInLine + posLineStart; +} + +int Editor::PositionFromLineX(int line, int x) { +	RefreshStyleData(); +	if (line >= pdoc->LinesTotal()) +		return pdoc->Length(); +	//Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine); +	Surface surface; +	surface.Init(); +	unsigned int posLineStart = pdoc->LineStart(line); + +	LineLayout ll; +	LayoutLine(line, &surface, vs, ll); +	for (int i = 0; i < ll.numCharsInLine; i++) { +		if (x < ((ll.positions[i] + ll.positions[i + 1]) / 2) ||  +			ll.chars[i] == '\r' || ll.chars[i] == '\n') { +			return i + posLineStart; +		} +	} + +	return ll.numCharsInLine + posLineStart; +} + +void Editor::RedrawRect(PRectangle rc) { +	//Platform::DebugPrintf("Redraw %d %d - %d %d\n", rc.left, rc.top, rc.right, rc.bottom); +	wDraw.InvalidateRectangle(rc); +} + +void Editor::Redraw() { +	//Platform::DebugPrintf("Redraw all\n"); +	wDraw.InvalidateAll(); +} + +void Editor::RedrawSelMargin() { +	if (vs.maskInLine) { +		Redraw(); +	} else { +		PRectangle rcSelMargin = GetClientRectangle(); +		rcSelMargin.right = vs.fixedColumnWidth; +		wDraw.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 maxLine = cs.DisplayFromDoc(pdoc->LineFromPosition(maxPos)); +	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(int line) { +	if ((line == -1) || (selType == selStream)) { +		return Platform::Minimum(currentPos, anchor); +	} else { // selType == selRectangle +		int selStart = SelectionStart(); +		int selEnd = SelectionEnd(); +		int lineStart = pdoc->LineFromPosition(selStart); +		int lineEnd = pdoc->LineFromPosition(selEnd); +		if (line < lineStart || line > lineEnd) { +			return -1; +		} else { +			int minX = Platform::Minimum(xStartSelect, xEndSelect); +			//return PositionFromLineX(line, minX + vs.fixedColumnWidth - xOffset); +			return PositionFromLineX(line, minX); +		} +	}  +} + +int Editor::SelectionEnd(int line) { +	if ((line == -1) || (selType == selStream)) { +		return Platform::Maximum(currentPos, anchor); +	} else { // selType == selRectangle +		int selStart = SelectionStart(); +		int selEnd = SelectionEnd(); +		int lineStart = pdoc->LineFromPosition(selStart); +		int lineEnd = pdoc->LineFromPosition(selEnd); +		if (line < lineStart || line > lineEnd) { +			return -1; +		} else { +			int maxX = Platform::Maximum(xStartSelect, xEndSelect); +			// measure line and return character closest to minx +			return PositionFromLineX(line, maxX); +		} +	} +} + +void Editor::SetSelection(int currentPos_, int anchor_) { +	currentPos_ = pdoc->ClampPositionIntoDocument(currentPos_); +	anchor_ = pdoc->ClampPositionIntoDocument(anchor_); +	if ((currentPos != currentPos_) || (anchor != 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); +		currentPos = currentPos_; +		anchor = anchor_; +		needUpdateUI = true; +		InvalidateRange(firstAffected, lastAffected); +	} +	ClaimSelection(); +} + +void Editor::SetSelection(int currentPos_) { +	currentPos_ = pdoc->ClampPositionIntoDocument(currentPos_); +	if (currentPos != currentPos_) { +		int firstAffected = anchor; +		if (firstAffected > currentPos) +			firstAffected = currentPos; +		if (firstAffected > currentPos_) +			firstAffected = currentPos_; +		int lastAffected = anchor; +		if (lastAffected < currentPos) +			lastAffected = currentPos; +		if (lastAffected < (currentPos_ + 1))	// +1 ensures caret repainted +			lastAffected = (currentPos_ + 1); +		currentPos = currentPos_; +		needUpdateUI = true; +		InvalidateRange(firstAffected, lastAffected); +	} +	ClaimSelection(); +} + +void Editor::SetEmptySelection(int currentPos_) { +	SetSelection(currentPos_, currentPos_); +} + +int Editor::MovePositionTo(int newPos, bool extend) { +	int delta = newPos - currentPos; +	newPos = pdoc->ClampPositionIntoDocument(newPos); +	newPos = pdoc->MovePositionOutsideChar(newPos, delta); +	if (extend) { +		SetSelection(newPos); +	} else { +		SetEmptySelection(newPos); +	} +	EnsureCaretVisible(); +	ShowCaretAtCurrentPosition(); +	return 0; +} + +int Editor::MovePositionSoVisible(int pos, int moveDir) { +	pos = pdoc->ClampPositionIntoDocument(pos); +	pos = pdoc->MovePositionOutsideChar(pos, moveDir); +	int lineDoc = pdoc->LineFromPosition(pos); +	if (cs.GetVisible(lineDoc)) { +		return pos; +	} else { +		int lineDisplay = cs.DisplayFromDoc(lineDoc); +		if (moveDir > 0) { +			lineDisplay = Platform::Clamp(lineDisplay + 1, 0, cs.LinesDisplayed()); +			return pdoc->LineStart(cs.DocFromDisplay(lineDisplay)); +		} else { +			// lineDisplay is already line before fold as lines in fold use display line of line before fold +			lineDisplay = Platform::Clamp(lineDisplay, 0, cs.LinesDisplayed()); +			return pdoc->LineEndPosition(pdoc->LineStart(cs.DocFromDisplay(lineDisplay))); +		} +	} +} + +// Choose the x position that the caret will try to stick to as it is moves up and down +void Editor::SetLastXChosen() { +	Point pt = LocationFromPosition(currentPos); +	lastXChosen = pt.x; +} + +void Editor::ScrollTo(int line) { +	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. +		if (abs(linesToMove) <= 10) { +			ScrollText(linesToMove); +		} else { +			Redraw(); +		} +		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); +	xOffset = xPos; +	if (xOffset < 0) +		xOffset = 0; +	SetHorizontalScrollPos(); +	Redraw(); +} + +void Editor::EnsureCaretVisible(bool useMargin) { +	//Platform::DebugPrintf("EnsureCaretVisible %d\n", xOffset); +	PRectangle rcClient = GetTextRectangle(); +	int posCaret = currentPos; +	if (posDrag >= 0) +		posCaret = posDrag; +	Point pt = LocationFromPosition(posCaret); +	Point ptEOL = LocationFromPosition(pdoc->LineEndPosition(posCaret)); +	Point ptBottomCaret = pt; +	int lineCaret = cs.DisplayFromDoc(pdoc->LineFromPosition(posCaret)); +	ptBottomCaret.y += vs.lineHeight - 1; + +	// Ensure the caret is reasonably visible in context. +	int xMargin = Platform::Clamp(xCaretMargin, 2, Platform::Maximum(rcClient.Width() - 10, 4) / 2); +	if (!useMargin) +		xMargin = 2; +	 +	// Ensure certain amount of text visible on both sides of caretSo move if caret just on edge +	rcClient.left = rcClient.left + xMargin; +	rcClient.right = rcClient.right - xMargin; +	 +	if (!rcClient.Contains(pt) || !rcClient.Contains(ptBottomCaret) || (caretPolicy & CARET_STRICT)) { +		//Platform::DebugPrintf("EnsureCaretVisible move, (%d,%d) (%d,%d)\n", pt.x, pt.y, rcClient.left, rcClient.right); +		// It should be possible to scroll the window to show the caret, +		// but this fails to remove the caret on GTK+ +		if (caretPolicy & CARET_SLOP) { +			if ((topLine > lineCaret) || ((caretPolicy & CARET_STRICT) && (topLine + caretSlop > lineCaret))) { +				SetTopLine(Platform::Clamp(lineCaret - caretSlop, 0, MaxScrollPos())); +				SetVerticalScrollPos(); +				Redraw(); +			} else if ((lineCaret > topLine + LinesOnScreen() - 1) ||  +				((caretPolicy & CARET_STRICT) && (lineCaret > topLine + LinesOnScreen() - 1 - caretSlop))) { +				SetTopLine(Platform::Clamp(lineCaret - LinesOnScreen() + 1 + caretSlop, 0, MaxScrollPos())); +				SetVerticalScrollPos(); +				Redraw(); +			} +		} else { +			if ((topLine > lineCaret) || (lineCaret > topLine + LinesOnScreen() - 1) || (caretPolicy & CARET_STRICT)) { +				SetTopLine(Platform::Clamp(lineCaret - LinesOnScreen() / 2 + 1, 0, MaxScrollPos())); +				SetVerticalScrollPos(); +				Redraw(); +			} +		} +		int xOffsetNew = xOffset; +		if (pt.x < rcClient.left) { +			xOffsetNew = xOffset - (rcClient.left - pt.x); +		} else if (pt.x >= rcClient.right) { +			xOffsetNew = xOffset + (pt.x - rcClient.right); +			int xOffsetEOL = xOffset + (ptEOL.x - rcClient.right) - xMargin + 2; +			//Platform::DebugPrintf("Margin %d %d\n", xOffsetNew, xOffsetEOL); +			// Ensure don't scroll out into empty space +			if (xOffsetNew > xOffsetEOL) +				xOffsetNew = xOffsetEOL; +		} +		if (xOffsetNew < 0) +			xOffsetNew = 0; +		if (xOffset != xOffsetNew) { +			xOffset = xOffsetNew; +			SetHorizontalScrollPos(); +			Redraw(); +		} +	} +} + +void Editor::ShowCaretAtCurrentPosition() { +	if (!wMain.HasFocus()) { +		caret.active = false; +		caret.on = false; +		return; +	} +	caret.active = true; +	caret.on = true; +	SetTicking(true); +} + +void Editor::DropCaret() { +	caret.active = false; +	InvalidateCaret(); +} + +void Editor::InvalidateCaret() { +	if (posDrag >= 0) +		InvalidateRange(posDrag, posDrag + 1); +	else +		InvalidateRange(currentPos, currentPos + 1); +} + +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].symbol) { +				/* 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 +					surface->FillRectangle(rcSelMargin, vs.styles[STYLE_LINENUMBER].back.allocated);  +			} else { +				surface->FillRectangle(rcSelMargin, vs.styles[STYLE_LINENUMBER].back.allocated); +			} +			 +			int visibleLine = topLine; +			int line = cs.DocFromDisplay(visibleLine); +			int yposScreen = 0; + +			while (line < pdoc->LinesTotal() && yposScreen < rcMargin.bottom) { +				int marks = pdoc->GetMark(line); +				if (pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) { +					if (cs.GetExpanded(line)) { +						marks |= 1 << SC_MARKNUM_FOLDEROPEN; +					} else { +						marks |= 1 << SC_MARKNUM_FOLDER; +					} +				} +				marks &= vs.ms[margin].mask; +				PRectangle rcMarker = rcSelMargin; +				rcMarker.top = yposScreen; +				rcMarker.bottom = yposScreen + vs.lineHeight; +				if (!vs.ms[margin].symbol) { +					char number[100]; +					number[0] = '\0'; +					sprintf(number, "%d", line + 1); +					if (foldFlags & 64) +						sprintf(number, "%X", pdoc->GetLevel(line)); +					int xpos = 0; +					PRectangle rcNumber=rcMarker; +					// Right justify +					int width = surface->WidthText(vs.styles[STYLE_LINENUMBER].font, number, strlen(number)); +					xpos = rcNumber.right - width - 3; +					rcNumber.left = xpos; +					if ((visibleLine < cs.LinesDisplayed()) && cs.GetVisible(line)) { +						surface->DrawText(rcNumber, vs.styles[STYLE_LINENUMBER].font, +						                  rcNumber.top + vs.maxAscent, number, strlen(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) { +							rcMarker.top++; +							rcMarker.bottom--; +							vs.markers[markBit].Draw(surface, rcMarker); +						} +						marks >>= 1; +					} +				} +				 +				visibleLine++; +				line = cs.DocFromDisplay(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 ((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); +} + +void Editor::LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayout &ll) { +	int numCharsInLine = 0; +	int posLineStart = pdoc->LineStart(line); +	int posLineEnd = pdoc->LineStart(line + 1); +	Font &ctrlCharsFont = vstyle.styles[STYLE_CONTROLCHAR].font; +	char styleByte = 0; +	int styleMask = pdoc->stylingBitsMask; +	for (int charInDoc = posLineStart;  +		charInDoc < posLineEnd && numCharsInLine < LineLayout::maxLineLength - 1;  +		charInDoc++) { +		char chDoc = pdoc->CharAt(charInDoc); +		styleByte = pdoc->StyleAt(charInDoc); +		if (vstyle.viewEOL || ((chDoc != '\r') && (chDoc != '\n'))) { +			ll.chars[numCharsInLine] = chDoc; +			ll.styles[numCharsInLine] = styleByte & styleMask; +			ll.indicators[numCharsInLine] = styleByte & ~styleMask; +			numCharsInLine++; +		} +	} +	ll.chars[numCharsInLine] = 0; +	ll.styles[numCharsInLine] = styleByte;	// For eolFilled +	ll.indicators[numCharsInLine] = 0; + +	// Layout the line, determining the position of each character +	int startseg = 0; +	int startsegx = 0; +	ll.positions[0] = 0; +	unsigned int tabWidth = vstyle.spaceWidth * pdoc->tabInChars; +	 +	for (int charInLine = 0; charInLine < numCharsInLine; charInLine++) { +		if ((ll.styles[charInLine] != ll.styles[charInLine + 1]) || +		        IsControlCharacter(ll.chars[charInLine]) || IsControlCharacter(ll.chars[charInLine + 1])) { +			ll.positions[startseg] = 0; +			if (IsControlCharacter(ll.chars[charInLine])) { +				if (ll.chars[charInLine] == '\t') { +					ll.positions[charInLine + 1] = ((((startsegx + 2) / +					                                   tabWidth) + 1) * tabWidth) - startsegx; +				} else { +					const char *ctrlChar = ControlCharacterString(ll.chars[charInLine]); +					// +3 For a blank on front and rounded edge each side: +					ll.positions[charInLine + 1] = surface->WidthText(ctrlCharsFont, ctrlChar, strlen(ctrlChar)) + 3; +				} +			} else { +				surface->MeasureWidths(vstyle.styles[ll.styles[charInLine]].font, ll.chars + startseg,  +					charInLine - startseg + 1, ll.positions + startseg + 1); +			} +			for (int posToIncrease = startseg; posToIncrease <= (charInLine + 1); posToIncrease++) { +				ll.positions[posToIncrease] += startsegx; +			} +			startsegx = ll.positions[charInLine + 1]; +			startseg = charInLine + 1; +		} +	} +	ll.numCharsInLine = numCharsInLine; +} + +void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int xStart, PRectangle rcLine, LineLayout &ll) { +	 +	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; + +	int marks = 0; +	Colour markBack = Colour(0, 0, 0); +	if (vsDraw.maskInLine) { +		marks = pdoc->GetMark(line) & vsDraw.maskInLine; +		if (marks) { +			for (int markBit = 0; (markBit < 32) && marks; markBit++) { +				if (marks & 1) { +					markBack = vsDraw.markers[markBit].back.allocated; +				} +				marks >>= 1; +			} +		} +		marks = pdoc->GetMark(line) & vsDraw.maskInLine; +	} + +	int posLineStart = pdoc->LineStart(line); +	int posLineEnd = pdoc->LineStart(line + 1); + +	int selStart = SelectionStart(line); +	int selEnd = SelectionEnd(line); + +	int styleMask = pdoc->stylingBitsMask; +	int startseg = 0; +	for (int i = 0; i < ll.numCharsInLine; 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]) || +		        IsControlCharacter(ll.chars[i]) || IsControlCharacter(ll.chars[i + 1]) || +		        ((selStart != selEnd) && ((iDoc + 1 == selStart) || (iDoc + 1 == selEnd))) || +				(i == (theEdge-1))) { +			int styleMain = ll.styles[i]; +			Colour textBack = vsDraw.styles[styleMain].back.allocated; +			Colour textFore = vsDraw.styles[styleMain].fore.allocated; +			Font &textFont = vsDraw.styles[styleMain].font; +			bool inSelection = (iDoc >= selStart) && (iDoc < selEnd) && (selStart != selEnd); +			if (inSelection && !hideSelection) { +				if (vsDraw.selbackset) +					textBack = vsDraw.selbackground.allocated; +				if (vsDraw.selforeset) +					textFore = vsDraw.selforeground.allocated; +			} else { +				if (marks) +					textBack = markBack; +				if ((edgeState == EDGE_BACKGROUND) && (i >= theEdge) && (ll.chars[i] != '\n') && (ll.chars[i] != '\r')) +					textBack = vs.edgecolour.allocated; +			} +			// Manage tab display +			if (ll.chars[i] == '\t') { +				rcSegment.left = ll.positions[i] + xStart; +				rcSegment.right = ll.positions[i + 1] + xStart; +				surface->FillRectangle(rcSegment, textBack); +				if (vsDraw.viewWhitespace) { +					surface->PenColour(textFore); +					PRectangle rcTab(rcSegment.left + 1, rcSegment.top + 4, +					                 rcSegment.right - 1, rcSegment.bottom - vsDraw.maxDescent); +					DrawTabArrow(surface, rcTab, rcSegment.top + vsDraw.lineHeight / 2); +				} +			// Manage control character display +			} else if (IsControlCharacter(ll.chars[i])) { +				const char *ctrlChar = ControlCharacterString(ll.chars[i]); +				rcSegment.left = ll.positions[i] + xStart; +				rcSegment.right = ll.positions[i + 1] + xStart; +				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, strlen(ctrlChar),  +							textBack, textFore); +			// Manage normal display +			} else { +				rcSegment.left = ll.positions[startseg] + xStart; +				rcSegment.right = ll.positions[i + 1] + xStart; +				// Only try do 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) { +					surface->DrawText(rcSegment, textFont, +					                  rcSegment.top + vsDraw.maxAscent, ll.chars + startseg, +					                  i - startseg + 1, textFore, textBack); +					if (vsDraw.viewWhitespace) { +						for (int cpos = 0; cpos <= i - startseg; cpos++) { +							if (ll.chars[cpos + startseg] == ' ') { +								int xmid = (ll.positions[cpos + startseg] + ll.positions[cpos + startseg + 1]) / 2; +								PRectangle rcDot(xmid + xStart, rcSegment.top + vsDraw.lineHeight / 2, 0, 0); +								rcDot.right = rcDot.left + 1; +								rcDot.bottom = rcDot.top + 1; +								surface->FillRectangle(rcDot, textFore); +							} +						} +					} +				} +			} +			startseg = i + 1; +		} +	} + +	// Draw indicators +	int indStart[INDIC_MAX + 1] = {0}; +	for (int indica = 0; indica <= INDIC_MAX; indica++) +		indStart[indica] = 0; + +	for (int indicPos = 0; indicPos <= ll.numCharsInLine; indicPos++) { +		if (ll.indicators[indicPos] != ll.indicators[indicPos + 1]) { +			int mask = 1 << pdoc->stylingBits; +			for (int indicnum = 0; mask <= 0x100; indicnum++) { +				if ((ll.indicators[indicPos + 1] & mask) && !(ll.indicators[indicPos] & mask)) { +					indStart[indicnum] = ll.positions[indicPos + 1]; +				} +				if (!(ll.indicators[indicPos + 1] & mask) && (ll.indicators[indicPos] & mask)) { +					PRectangle rcIndic( +					    indStart[indicnum] + xStart, +					    rcLine.top + vsDraw.maxAscent, +					    ll.positions[indicPos + 1] + xStart, +					    rcLine.top + vsDraw.maxAscent + 3); +					vsDraw.indicators[indicnum].Draw(surface, rcIndic); +				} +				mask = mask << 1; +			} +		} +	} +	// End of the drawing of the current line + +	// Fill in a PRectangle representing the end of line characters +	int xEol = ll.positions[ll.numCharsInLine]; +	rcSegment.left = xEol + xStart; +	rcSegment.right = xEol + vsDraw.aveCharWidth + xStart; +	bool eolInSelection = (posLineEnd > selStart) && (posLineEnd <= selEnd) && (selStart != selEnd); +	if (eolInSelection && !hideSelection && vsDraw.selbackset && (line < pdoc->LinesTotal()-1)) { +		surface->FillRectangle(rcSegment, vsDraw.selbackground.allocated); +	} else if (marks) { +		surface->FillRectangle(rcSegment, markBack); +	} else { +		surface->FillRectangle(rcSegment, vsDraw.styles[ll.styles[ll.numCharsInLine] & styleMask].back.allocated); +	} + +	rcSegment.left = xEol + vsDraw.aveCharWidth + xStart; +	rcSegment.right = rcLine.right; +	if (marks) { +		surface->FillRectangle(rcSegment, markBack); +	} 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 (edgeState == EDGE_LINE) { +		int edgeX = theEdge * vsDraw.spaceWidth; +		rcSegment.left = edgeX + xStart; +		rcSegment.right = rcSegment.left + 1; +		surface->FillRectangle(rcSegment, vs.edgecolour.allocated); +	} +} + +void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) { +	//Platform::DebugPrintf("Paint %d %d - %d %d\n", rcArea.left, rcArea.top, rcArea.right, rcArea.bottom); +	RefreshStyleData(); + +	PRectangle rcClient = GetClientRectangle(); +	//Platform::DebugPrintf("Client: (%3d,%3d) ... (%3d,%3d)   %d\n", +	//	rcClient.left, rcClient.top, rcClient.right, rcClient.bottom); + +	if (!pixmapSelPattern.Initialised()) { +		pixmapSelPattern.InitPixMap(8, 8, surfaceWindow); +		// This complex procedure is to reproduce the checker board 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, 8, 8); +		if (vs.selbarlight.desired == Colour(0xff, 0xff, 0xff)) { +			pixmapSelPattern.FillRectangle(rcPattern, vs.selbar.allocated); +			pixmapSelPattern.PenColour(vs.selbarlight.allocated); +			for (int stripe = 0; stripe < 8; stripe++) { +				pixmapSelPattern.MoveTo(0, stripe * 2); +				pixmapSelPattern.LineTo(8, stripe * 2 - 8); +			} +		} else { +			// User has chosen an unusual chrome colour scheme so just use the highlight edge colour. +			pixmapSelPattern.FillRectangle(rcPattern, vs.selbarlight.allocated); +		} +	} + +	if (bufferedDraw) { +		if (!pixmapLine.Initialised()) { +			pixmapLine.InitPixMap(rcClient.Width(), rcClient.Height(), +			                      surfaceWindow); +			pixmapSelMargin.InitPixMap(vs.fixedColumnWidth, +			                           rcClient.Height(), surfaceWindow); +		} +	} + +	surfaceWindow->SetPalette(&palette, true); +	pixmapLine.SetPalette(&palette, !wMain.HasFocus()); + +	//Platform::DebugPrintf("Paint: (%3d,%3d) ... (%3d,%3d)\n", +	//	rcArea.left, rcArea.top, rcArea.right, rcArea.bottom); + +	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 statrting 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; + +	if (endPosPaint > pdoc->GetEndStyled()) { +		// Notify container to do some more styling +		NotifyStyleNeeded(endPosPaint); +	} +	if (needUpdateUI) { +		NotifyUpdateUI(); +		needUpdateUI = false; +	} +	 +	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 NotifyStyleNeeded or NotifyUpdateUI noticed that painting is needed +		// outside the current painting rectangle +		//Platform::DebugPrintf("Abandoning paint\n"); +		return; +	} +	//Platform::DebugPrintf("start display %d, offset = %d\n", pdoc->Length(), xOffset); + +	Surface *surface = 0; +	if (rcArea.right > vs.fixedColumnWidth) { + +		if (bufferedDraw) { +			surface = &pixmapLine; +		} else { +			surface = surfaceWindow; +		} + +		int visibleLine = topLine + screenLinePaintFirst; +		int line = cs.DocFromDisplay(visibleLine); + +		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); +		//GTimer *tim=g_timer_new(); +		while (visibleLine <= cs.LinesDisplayed() && yposScreen < rcArea.bottom) { +			//g_timer_start(tim); +			//Platform::DebugPrintf("Painting line %d\n", line); + +			int posLineStart = pdoc->LineStart(line); +			int posLineEnd = pdoc->LineStart(line + 1); +			//Platform::DebugPrintf("line %d %d - %d\n", line, posLineStart, posLineEnd); + +			PRectangle rcLine = rcClient; +			rcLine.top = ypos; +			rcLine.bottom = ypos + vs.lineHeight; + +			// Copy this line and its styles from the document into local arrays +			// and determine the x position at which each character starts. +			LineLayout ll; +			LayoutLine(line, surface, vs, ll); +			                                 +			// Highlight the current braces if any +			if ((braces[0] >= posLineStart) && (braces[0] < posLineEnd)) +				ll.styles[braces[0] - posLineStart] = bracesMatchStyle; +			if ((braces[1] >= posLineStart) && (braces[1] < posLineEnd)) +				ll.styles[braces[1] - posLineStart] = bracesMatchStyle; +				 +			// Draw the line +			if (cs.GetVisible(line)) +				DrawLine(surface, vs, line, xStart, rcLine, ll); + +			bool expanded = cs.GetExpanded(line); +			if ( (expanded && (foldFlags & 2)) || (!expanded && (foldFlags & 4)) ) { +				if (pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) { +					PRectangle rcFoldLine = rcLine; +					rcFoldLine.bottom = rcFoldLine.top + 1; +					surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated); +				} +			} +			if ( (expanded && (foldFlags & 8)) || (!expanded && (foldFlags & 16)) ) { +				if (pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) { +					PRectangle rcFoldLine = rcLine; +					rcFoldLine.top = rcFoldLine.bottom - 1; +					surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated); +				} +			} +			 +			// Draw the Caret +			if (line == lineCaret) { +				int xposCaret = ll.positions[posCaret - posLineStart] + xStart; +				int widthOverstrikeCaret = +				    ll.positions[posCaret - posLineStart + 1] - ll.positions[posCaret - posLineStart]; +				if (posCaret == pdoc->Length())	// At end of document +					widthOverstrikeCaret = vs.aveCharWidth; +				if ((posCaret - posLineStart) >= ll.numCharsInLine)	// At end of line +					widthOverstrikeCaret = vs.aveCharWidth; +				if (widthOverstrikeCaret < 3)	// Make sure its visible +					widthOverstrikeCaret = 3; +				if (((caret.active && caret.on) || (posDrag >= 0)) && xposCaret >= 0) { +					PRectangle rcCaret = rcLine; +					if (posDrag >= 0) { +						rcCaret.left = xposCaret; +						rcCaret.right = xposCaret + 1; +					} else { +						if (inOverstrike) { +							rcCaret.top = rcCaret.bottom - 2; +							rcCaret.left = xposCaret + 1; +							rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1; +						} else { +							rcCaret.left = xposCaret; +							rcCaret.right = xposCaret + 1; +						} +					} +					surface->FillRectangle(rcCaret, vs.caretcolour.allocated); +				} +			} +			 +			if (cs.GetVisible(line)) { +				if (bufferedDraw) { +					Point from(vs.fixedColumnWidth, 0); +					PRectangle rcCopyArea(vs.fixedColumnWidth, yposScreen, +					                  rcClient.right, yposScreen + vs.lineHeight); +					surfaceWindow->Copy(rcCopyArea, from, pixmapLine); +				} +			} + +			if (!bufferedDraw) { +				ypos += vs.lineHeight; +			} + +			yposScreen += vs.lineHeight; +			visibleLine++; +			line = cs.DocFromDisplay(visibleLine); +			//gdk_flush(); +			//g_timer_stop(tim); +			//Platform::DebugPrintf("Paint [%0d] took %g\n", line, g_timer_elapsed(tim, 0)); +		} +		//g_timer_destroy(tim); +		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 (edgeState == EDGE_LINE) { +				int edgeX = theEdge * vs.spaceWidth; +				rcBeyondEOF.left = edgeX + xStart; +				rcBeyondEOF.right = rcBeyondEOF.left + 1; +				surfaceWindow->FillRectangle(rcBeyondEOF, vs.edgecolour.allocated); +			} +		} +	} +} + +// Space (3 space characters) between line numbers and text when printing. +#define lineNumberPrintSpace "   " + +// 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, FORMATRANGE *pfr) { +	if (!pfr) +		return 0; + +	Surface *surface = new Surface(); +	surface->Init(pfr->hdc); +	Surface *surfaceMeasure = new Surface(); +	surfaceMeasure->Init(pfr->hdcTarget); +	 +	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].symbol) && (vsPrint.ms[margin].width > 0)) { +			lineNumberIndex = margin; +		} else { +			vsPrint.ms[margin].width = 0; +		} +	} +	vsPrint.showMarkedLines = false; +	vsPrint.fixedColumnWidth = 0; +	vsPrint.zoomLevel = 0; +	// Don't show the selection when printing +	vsPrint.selbackset = false; +	vsPrint.selforeset = false; +	// White background for the line numbers +	vsPrint.styles[STYLE_LINENUMBER].back.desired = Colour(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 = surface->WidthText(vsPrint.styles[STYLE_LINENUMBER].font,  +			"9999" lineNumberPrintSpace, 4 + strlen(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 - 1); +	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); + +	if (endPosPrint > pdoc->GetEndStyled()) { +		// Notify container to do some more styling +		NotifyStyleNeeded(endPosPrint); +	} +	int xStart = vsPrint.fixedColumnWidth + pfr->rc.left + lineNumberWidth; +	int ypos = pfr->rc.top; +	int line = linePrintStart; + +	if (draw) {	// Otherwise just measuring + +		while (line <= linePrintLast && ypos < pfr->rc.bottom) { + +			PRectangle rcLine; +			rcLine.left = pfr->rc.left + lineNumberWidth; +			rcLine.top = ypos; +			rcLine.right = pfr->rc.right; +			rcLine.bottom = ypos + vsPrint.lineHeight; +			 +			if (lineNumberWidth) { +				char number[100]; +				sprintf(number, "%d" lineNumberPrintSpace, line + 1); +				PRectangle rcNumber = rcLine; +				rcNumber.right = rcNumber.left + lineNumberWidth; +				// Right justify +				rcNumber.left += lineNumberWidth -  +					surface->WidthText(vsPrint.styles[STYLE_LINENUMBER].font, number, strlen(number)); +				surface->DrawText(rcNumber, vsPrint.styles[STYLE_LINENUMBER].font, +				                  ypos + vsPrint.maxAscent, number, strlen(number), +				                  vsPrint.styles[STYLE_LINENUMBER].fore.allocated,  +						  vsPrint.styles[STYLE_LINENUMBER].back.allocated); +			} +			 +			// Copy this line and its styles from the document into local arrays +			// and determine the x position at which each character starts. +			LineLayout ll; +			LayoutLine(line, surfaceMeasure, vsPrint, ll); +			                                 +			// Draw the line +			DrawLine(surface, vsPrint, line, xStart, rcLine, ll); + +			ypos += vsPrint.lineHeight; +			line++; +		} +	} + +	delete surface; +	delete surfaceMeasure; + +	return endPosPrint; +} + +void Editor::SetScrollBarsTo(PRectangle) { +	RefreshStyleData(); + +	int nMax = cs.LinesDisplayed(); +	int nPage = cs.LinesDisplayed() - MaxScrollPos() + 1; +	bool modified = ModifyScrollBars(nMax, nPage); + +	// 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) +		Redraw(); +	//Platform::DebugPrintf("end max = %d page = %d\n", nMax, nPage); +} + +void Editor::SetScrollBars() { +	PRectangle rsClient = GetClientRectangle(); +	SetScrollBarsTo(rsClient); +} + +void Editor::AddChar(char ch) { +	bool wasSelection = currentPos != anchor; +	ClearSelection(); +	if (inOverstrike && !wasSelection) { +		if (currentPos < (pdoc->Length() - 1)) { +			if ((pdoc->CharAt(currentPos) != '\r') && (pdoc->CharAt(currentPos) != '\n')) { +				pdoc->DelChar(currentPos); +			} +		} +	} +	pdoc->InsertChar(currentPos, ch); +	SetEmptySelection(currentPos + 1); +	EnsureCaretVisible(); +	SetLastXChosen(); +	NotifyChar(ch); +} + +void Editor::ClearSelection() { +	if (selType == selRectangle) { +		pdoc->BeginUndoAction(); +		int lineStart = pdoc->LineFromPosition(SelectionStart()); +		int lineEnd = pdoc->LineFromPosition(SelectionEnd()); +		int startPos = SelectionStart(); +		int line; +		for (line=lineStart; line <= lineEnd; line++) { +			startPos = SelectionStart(line); +			unsigned int chars = SelectionEnd(line) - startPos; +			if (0 != chars) { +				pdoc->DeleteChars(startPos, chars); +			} +		} +		SetEmptySelection(startPos); +		selType = selStream; +		pdoc->EndUndoAction(); +	} else { +		int startPos = SelectionStart(); +		unsigned int chars = SelectionEnd() - startPos; +		SetEmptySelection(startPos); +		if (0 != chars) { +			pdoc->DeleteChars(startPos, chars); +		} +	} +} + +void Editor::ClearAll() { +	if (0 != pdoc->Length()) { +		pdoc->DeleteChars(0, pdoc->Length()); +	} +	cs.Clear(); +	anchor = 0; +	currentPos = 0; +	SetTopLine(0); +	SetVerticalScrollPos(); +} + +void Editor::Cut() { +	Copy(); +	ClearSelection(); +} + +void Editor::PasteRectangular(int pos, const char *ptr, int len) { +	currentPos = pos; +	int insertPos = currentPos; +	int xInsert = XFromPosition(currentPos); +	int line = pdoc->LineFromPosition(currentPos); +	bool prevCr = false; +	for (int i=0; i<len; i++) { +		if ((ptr[i] == '\r') || (ptr[i] == '\n')) { +			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'); +			} +			currentPos = PositionFromLineX(line, xInsert); +			prevCr = ptr[i] == '\r'; +		} else { +			pdoc->InsertString(currentPos, ptr+i, 1); +			currentPos++; +			insertPos = currentPos; +			prevCr = false; +		} +	} +	SetEmptySelection(insertPos); +} + +void Editor::Clear() { +	if (currentPos == anchor) { +		DelChar(); +	} else { +		ClearSelection(); +	} +	SetEmptySelection(currentPos); +} + +void Editor::SelectAll() { +	SetSelection(0, pdoc->Length()); +	Redraw(); +} + +void Editor::Undo() { +	if (pdoc->CanUndo()) { +		int newPos = pdoc->Undo(); +		SetEmptySelection(newPos); +		EnsureCaretVisible(); +	} +} + +void Editor::Redo() { +	if (pdoc->CanRedo()) { +		int newPos = pdoc->Redo(); +		SetEmptySelection(newPos); +		EnsureCaretVisible(); +	} +} + +void Editor::DelChar() { +	pdoc->DelChar(currentPos); +} + +void Editor::DelCharBack() { +	if (currentPos == anchor) { +		int newPos = pdoc->DelCharBack(currentPos); +		SetEmptySelection(newPos); +	} else { +		ClearSelection(); +		SetEmptySelection(currentPos); +	} +} + +void Editor::NotifyFocus(bool) { +} + +void Editor::NotifyStyleNeeded(int endStyleNeeded) { +	SCNotification scn; +	scn.nmhdr.code = SCN_STYLENEEDED; +	scn.position = endStyleNeeded; +	NotifyParent(scn); +} + +void Editor::NotifyChar(char ch) { +	SCNotification scn; +	scn.nmhdr.code = SCN_CHARADDED; +	scn.ch = ch; +	NotifyParent(scn); +#ifdef MACRO_SUPPORT +	if (recordingMacro) { +		char txt[2]; +		txt[0] = ch; +		txt[1] = '\0'; +		NotifyMacroRecord(EM_REPLACESEL, 0, (LPARAM) txt); +	} +#endif +} + +void Editor::NotifySavePoint(bool isSavePoint) { +	SCNotification scn; +	if (isSavePoint) { +		scn.nmhdr.code = SCN_SAVEPOINTREACHED; +	} else { +		scn.nmhdr.code = SCN_SAVEPOINTLEFT; +	} +	NotifyParent(scn); +} + +void Editor::NotifyModifyAttempt() { +	SCNotification scn; +	scn.nmhdr.code = SCN_MODIFYATTEMPTRO; +	NotifyParent(scn); +} + +void Editor::NotifyDoubleClick(Point, bool) { +	SCNotification scn; +	scn.nmhdr.code = SCN_DOUBLECLICK; +	NotifyParent(scn); +} + +void Editor::NotifyUpdateUI() { +	SCNotification scn; +	scn.nmhdr.code = SCN_UPDATEUI; +	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; +		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; +	scn.nmhdr.code = SCN_NEEDSHOWN; +	scn.position = pos; +	scn.length = len; +	NotifyParent(scn); +} + +// Notifications from document +void Editor::NotifyModifyAttempt(Document*, void *) { +	//Platform::DebugPrintf("** Modify Attempt\n"); +	NotifyModifyAttempt(); +} + +void Editor::NotifySavePoint(Document*, void *, bool atSavePoint) { +	//Platform::DebugPrintf("** Save Point %s\n", atSavePoint ? "On" : "Off"); +	NotifySavePoint(atSavePoint); +} + +void Editor::NotifyModified(Document*, DocModification mh, void *) { +	needUpdateUI = true; +	if (paintState == painting) { +		CheckForChangeOutsidePaint(Range(mh.position, mh.position + mh.length)); +	} else if (paintState == notPainting) { +		if (mh.modificationType & SC_MOD_CHANGESTYLE) { +			if (mh.position < pdoc->LineStart(topLine)) { +				// Styling performed before this view +				Redraw(); +			} else { +				InvalidateRange(mh.position, mh.position + mh.length); +			} +		} else { +			// Move selection and brace highlights +			if (mh.modificationType & SC_MOD_INSERTTEXT) { +				if (currentPos > mh.position) { +					currentPos += mh.length; +				} +				if (anchor > mh.position) { +					anchor += mh.length; +				} +				if (braces[0] > mh.position) { +					braces[0] += mh.length; +				} +				if (braces[1] > mh.position) { +					braces[1] += mh.length; +				} +			} else {	// SC_MOD_DELETETEXT +				int endPos = mh.position + mh.length; +				if (currentPos > mh.position) { +					if (currentPos > endPos) { +						currentPos -= mh.length; +					} else { +						currentPos = endPos; +					} +				} +				if (anchor > mh.position) { +					if (anchor > endPos) { +						anchor -= mh.length; +					} else { +						anchor = endPos; +					} +				} +				if (braces[0] > mh.position) { +					if (braces[0] > endPos) { +						braces[0] -= mh.length; +					} else { +						braces[0] = endPos; +					} +				} +				if (braces[1] > mh.position) { +					if (braces[1] > endPos) { +						braces[1] -= mh.length; +					} else { +						braces[1] = endPos; +					} +				} +			} +			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) { +					NotifyNeedShown(mh.position, mh.length); +					cs.InsertLines(lineOfPos, mh.linesAdded); +				} else { +					cs.DeleteLines(lineOfPos, -mh.linesAdded); +				} +				// Avoid scrolling of display if change before current display +				if (mh.position < posTopLine) { +					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); +				Redraw(); +			} else { +				//Platform::DebugPrintf("** %x Line Changed %d .. %d\n", this, +				//	mh.position, mh.position + mh.length); +				InvalidateRange(mh.position, mh.position + mh.length); +			} +		} +	} // else paintState == paintAbandoned so no need to do anything + +	if (mh.linesAdded != 0) { +		SetScrollBars(); +	} + +	// 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; +		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 *document, void *userData) { +	/* Do nothing */ +} + +#ifdef MACRO_SUPPORT +void Editor::NotifyMacroRecord(UINT iMessage, WPARAM wParam, LPARAM lParam) { + +	// Enumerates all macroable messages +	switch (iMessage) { +	case WM_CUT: +	case WM_COPY: +	case WM_PASTE: +	case WM_CLEAR: +	case EM_REPLACESEL: +	case SCI_ADDTEXT: +	case SCI_INSERTTEXT: +	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_LINEUP: +	case SCI_LINEUPEXTEND: +	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_HOME: +	case SCI_HOMEEXTEND: +	case SCI_LINEEND: +	case SCI_LINEENDEXTEND: +	case SCI_DOCUMENTSTART: +	case SCI_DOCUMENTSTARTEXTEND: +	case SCI_DOCUMENTEND: +	case SCI_DOCUMENTENDEXTEND: +	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_DELWORDLEFT: +	case SCI_DELWORDRIGHT: +		break; + +	// Filter out all others (display changes, etc) +	default: +//		printf("Filtered out %ld of macro recording\n", iMessage); +		return; +	} + +	// Send notification +	SCNotification scn; +	scn.nmhdr.code = SCN_MACRORECORD; +	scn.message = iMessage; +	scn.wParam = wParam; +	scn.lParam = lParam; +	NotifyParent(scn); +} +#endif + +// Force scroll and keep position relative to top of window +void Editor::PageMove(int direction, bool extend) { +	Point pt = LocationFromPosition(currentPos); +	int topLineNew = Platform::Clamp( +	                 topLine + direction * LinesToScroll(), 0, MaxScrollPos()); +	int newPos = PositionFromLocation( +	                 Point(lastXChosen, pt.y + direction * (vs.lineHeight * LinesToScroll()))); +	if (topLineNew != topLine) { +		SetTopLine(topLineNew); +		MovePositionTo(newPos, extend); +		Redraw(); +		SetVerticalScrollPos(); +	} else { +		MovePositionTo(newPos, extend); +	} +} + +int Editor::KeyCommand(UINT iMessage) { +	Point pt = LocationFromPosition(currentPos); + +	switch (iMessage) { +	case SCI_LINEDOWN: +		MovePositionTo(PositionFromLocation( +		                   Point(lastXChosen, pt.y + vs.lineHeight))); +		break; +	case SCI_LINEDOWNEXTEND: +		MovePositionTo(PositionFromLocation( +		                   Point(lastXChosen, pt.y + vs.lineHeight)), true); +		break; +	case SCI_LINEUP: +		MovePositionTo(PositionFromLocation( +		                   Point(lastXChosen, pt.y - vs.lineHeight))); +		break; +	case SCI_LINEUPEXTEND: +		MovePositionTo(PositionFromLocation( +		                   Point(lastXChosen, pt.y - vs.lineHeight)), true); +		break; +	case SCI_CHARLEFT: +		if (SelectionEmpty()) { +			MovePositionTo(MovePositionSoVisible(currentPos - 1, -1)); +		} else { +			MovePositionTo(SelectionStart()); +		} +		SetLastXChosen(); +		break; +	case SCI_CHARLEFTEXTEND: +		MovePositionTo(MovePositionSoVisible(currentPos - 1, -1), true); +		SetLastXChosen(); +		break; +	case SCI_CHARRIGHT: +		if (SelectionEmpty()) { +			MovePositionTo(MovePositionSoVisible(currentPos + 1, 1)); +		} else { +			MovePositionTo(SelectionEnd()); +		} +		SetLastXChosen(); +		break; +	case SCI_CHARRIGHTEXTEND: +		MovePositionTo(MovePositionSoVisible(currentPos + 1, 1), true); +		SetLastXChosen(); +		break; +	case SCI_WORDLEFT: +		MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, -1), -1)); +		SetLastXChosen(); +		break; +	case SCI_WORDLEFTEXTEND: +		MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, -1), -1), true); +		SetLastXChosen(); +		break; +	case SCI_WORDRIGHT: +		MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, 1), 1)); +		SetLastXChosen(); +		break; +	case SCI_WORDRIGHTEXTEND: +		MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, 1), 1), true); +		SetLastXChosen(); +		break; +	case SCI_HOME: +		MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(currentPos))); +		SetLastXChosen(); +		break; +	case SCI_HOMEEXTEND: +		MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(currentPos)), true); +		SetLastXChosen(); +		break; +	case SCI_LINEEND: +		MovePositionTo(pdoc->LineEndPosition(currentPos)); +		SetLastXChosen(); +		break; +	case SCI_LINEENDEXTEND: +		MovePositionTo(pdoc->LineEndPosition(currentPos), true); +		SetLastXChosen(); +		break; +	case SCI_DOCUMENTSTART: +		MovePositionTo(0); +		SetLastXChosen(); +		break; +	case SCI_DOCUMENTSTARTEXTEND: +		MovePositionTo(0, true); +		SetLastXChosen(); +		break; +	case SCI_DOCUMENTEND: +		MovePositionTo(pdoc->Length()); +		SetLastXChosen(); +		break; +	case SCI_DOCUMENTENDEXTEND: +		MovePositionTo(pdoc->Length(), true); +		SetLastXChosen(); +		break; +	case SCI_PAGEUP: +		PageMove( -1); +		break; +	case SCI_PAGEUPEXTEND: +		PageMove( -1, true); +		break; +	case SCI_PAGEDOWN: +		PageMove(1); +		break; +	case SCI_PAGEDOWNEXTEND: +		PageMove(1, true); +		break; +	case SCI_EDITTOGGLEOVERTYPE: +		inOverstrike = !inOverstrike; +		DropCaret(); +		ShowCaretAtCurrentPosition(); +		break; +	case SCI_CANCEL:  	// Cancel any modes - handled in subclass +		// Also unselect text +		SetEmptySelection(currentPos); +		break; +	case SCI_DELETEBACK: +		DelCharBack(); +		EnsureCaretVisible(); +		break; +	case SCI_TAB: +		Indent(true); +		break; +	case SCI_BACKTAB: +		Indent(false); +		break; +	case SCI_NEWLINE: +		ClearSelection(); +		if (pdoc->eolMode == SC_EOL_CRLF) { +			pdoc->InsertString(currentPos, "\r\n"); +			SetEmptySelection(currentPos + 2); +			NotifyChar('\r'); +			NotifyChar('\n'); +		} else if (pdoc->eolMode == SC_EOL_CR) { +			pdoc->InsertChar(currentPos, '\r'); +			SetEmptySelection(currentPos + 1); +			NotifyChar('\r'); +		} else if (pdoc->eolMode == SC_EOL_LF) { +			pdoc->InsertChar(currentPos, '\n'); +			SetEmptySelection(currentPos + 1); +			NotifyChar('\n'); +		} +		SetLastXChosen(); +		EnsureCaretVisible(); +		break; +	case SCI_FORMFEED: +		AddChar('\f'); +		break; +	case SCI_VCHOME: +		MovePositionTo(pdoc->VCHomePosition(currentPos)); +		SetLastXChosen(); +		break; +	case SCI_VCHOMEEXTEND: +		MovePositionTo(pdoc->VCHomePosition(currentPos), true); +		SetLastXChosen(); +		break; +	case SCI_ZOOMIN: +		if (vs.zoomLevel < 20) +			vs.zoomLevel++; +		InvalidateStyleRedraw(); +		break; +	case SCI_ZOOMOUT: +		if (vs.zoomLevel > -10) +			vs.zoomLevel--; +		InvalidateStyleRedraw(); +		break; +	case SCI_DELWORDLEFT: { +			int startWord = pdoc->NextWordStart(currentPos, -1); +			pdoc->DeleteChars(startWord, currentPos - startWord); +			MovePositionTo(startWord); +		} +		break; +	case SCI_DELWORDRIGHT: { +			int endWord = pdoc->NextWordStart(currentPos, 1); +			pdoc->DeleteChars(currentPos, endWord - currentPos); +		} +		break; +	} +	return 0; +} + +int Editor::KeyDefault(int, int) { +	return 0; +} + +int Editor::KeyDown(int key, bool shift, bool ctrl, bool alt) { +	int modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) | +	                (alt ? SCI_ALT : 0); +	int msg = kmap.Find(key, modifiers); +	if (msg) +		return WndProc(msg, 0, 0); +	else +		return KeyDefault(key, modifiers); +} + +void Editor::SetWhitespaceVisible(bool view) { +	vs.viewWhitespace = view; +} + +bool 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) { +		ClearSelection(); +		pdoc->InsertChar(currentPos, '\t'); +		//pdoc->InsertChar(currentPos++, '\t'); +		SetEmptySelection(currentPos + 1); +	} 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)); +		} +	} +} + +long Editor::FindText(UINT iMessage, WPARAM wParam, LPARAM lParam) { +	FINDTEXTEX *ft = reinterpret_cast<FINDTEXTEX *>(lParam); +	int pos = pdoc->FindText(ft->chrg.cpMin, ft->chrg.cpMax, ft->lpstrText, +			wParam & FR_MATCHCASE, wParam & FR_WHOLEWORD); +	if (pos != -1) { +		if (iMessage == EM_FINDTEXTEX) { +			ft->chrgText.cpMin = pos; +			ft->chrgText.cpMax = pos + strlen(ft->lpstrText); +		} +	} +	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 SearchAnchor first. +// Accepts both SCI_SEARCHNEXT and SCI_SEARCHPREV. +// wParam contains search modes : ORed FR_MATCHCASE and FR_WHOLEWORD. +// lParam contains the text to search for. +long Editor::SearchText(UINT iMessage, WPARAM wParam, LPARAM lParam) { +	const char *txt = reinterpret_cast<char *>(lParam); +	int pos; + +	if (iMessage == SCI_SEARCHNEXT) { +		pos = pdoc->FindText(searchAnchor, pdoc->Length(), txt,  +		                     wParam & FR_MATCHCASE, +		                     wParam & FR_WHOLEWORD); +	} else { +		pos = pdoc->FindText(searchAnchor, 0, txt,  +		                     wParam & FR_MATCHCASE, +		                     wParam & FR_WHOLEWORD); +	} + +	if (pos != -1) { +		SetSelection(pos, pos + strlen(txt)); +	} + +	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; +} + +int Editor::SelectionRangeLength() { +	if (selType == selRectangle) { +		int lineStart = pdoc->LineFromPosition(SelectionStart()); +		int lineEnd = pdoc->LineFromPosition(SelectionEnd()); +		int totalSize = 0; +		for (int line=lineStart; line <= lineEnd; line++) { +			totalSize += SelectionEnd(line) - SelectionStart(line) + 1; +			if (pdoc->eolMode == SC_EOL_CRLF) +				totalSize++; +		} +		return totalSize; +	} else { +		return SelectionEnd() - SelectionStart(); +	} +} + +char *Editor::CopySelectionRange() { +	if (selType == selRectangle) { +		char *text = 0; +		int lineStart = pdoc->LineFromPosition(SelectionStart()); +		int lineEnd = pdoc->LineFromPosition(SelectionEnd()); +		int totalSize = SelectionRangeLength(); +		if (totalSize > 0) { +			text = new char[totalSize + 1]; +			if (text) { +				int j = 0; +				for (int line=lineStart; line <= lineEnd; line++) { +					for (int i=SelectionStart(line);i<SelectionEnd(line);i++) { +						text[j++] = pdoc->CharAt(i); +					} +					if (pdoc->eolMode != SC_EOL_LF) +						text[j++] = '\r'; +					if (pdoc->eolMode != SC_EOL_CR) +						text[j++] = '\n'; +				} +				text[totalSize] = '\0'; +			} +		} +		return text; +	} else { +		return CopyRange(SelectionStart(), SelectionEnd()); +	} +} + +void Editor::CopySelectionIntoDrag() { +	delete []dragChars; +	dragChars = 0; +	lenDrag = SelectionRangeLength(); +	dragChars = CopySelectionRange(); +	dragIsRectangle = selType == selRectangle; +	if (!dragChars) { +		lenDrag = 0; +	} +} + +void Editor::SetDragPosition(int newPos) { +	if (newPos >= 0) { +		newPos = pdoc->MovePositionOutsideChar(newPos, 1); +		posDrop = newPos; +	} +	if (posDrag != newPos) { +		caret.on = true; +		SetTicking(true); +		InvalidateCaret(); +		posDrag = newPos; +		InvalidateCaret(); +	} +} + +void Editor::StartDrag() { +	// Always handled by subclasses +	//SetMouseCapture(true); +	//wDraw.SetCursor(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); + +	if ((!inDragDrop) || !(0 == positionWasInSelection)) { + +		int selStart = SelectionStart(); +		int selEnd = SelectionEnd(); +		 +		pdoc->BeginUndoAction(); + +		int positionAfterDeletion = position; +		if (inDragDrop && moving) { +			// Remove dragged out text +			if (rectangular) { +				int lineStart = pdoc->LineFromPosition(SelectionStart()); +				int lineEnd = pdoc->LineFromPosition(SelectionEnd()); +				for (int line=lineStart; line <= lineEnd; line++) { +					int startPos = SelectionStart(line); +					int endPos = SelectionEnd(line); +					if (position >= startPos) { +						if (position > endPos) { +							positionAfterDeletion -= endPos - startPos; +						} else { +							positionAfterDeletion -= position - startPos; +						} +					} +				} +			} else { +				if (position > selStart) { +					positionAfterDeletion -= selEnd - selStart; +				} +			} +			ClearSelection(); +		} +		position = positionAfterDeletion; +		 +		if (rectangular) { +			PasteRectangular(position, value, strlen(value)); +			pdoc->EndUndoAction(); +			// Should try to select new rectangle but it may not be a rectangle now so just select the drop position +			SetSelection(position, position);	 +		} else { +			position = pdoc->MovePositionOutsideChar(position, currentPos - position); +			pdoc->InsertString(position, value); +			pdoc->EndUndoAction(); +			SetSelection(position + strlen(value), position); +		} +	} else if (inDragDrop) { +		SetSelection(position, position); +	} +} + +static int BeforeInOrAfter(int val, int minim, int maxim) { +	if (val < minim) +		return -1; +	else if (val > maxim) +		return 1; +	else +		return 0; +} + +int Editor::PositionInSelection(int pos) { +	pos = pdoc->MovePositionOutsideChar(pos, currentPos - pos); +	if (selType == selRectangle) { +		if (pos < SelectionStart()) +			return -1; +		if (pos > SelectionEnd()) +			return 1; +		int linePos = pdoc->LineFromPosition(pos); +		return BeforeInOrAfter(pos, SelectionStart(linePos), SelectionEnd(linePos)); +	} else { +		if (currentPos > anchor) { +			return BeforeInOrAfter(pos, anchor, currentPos); +		} else if (currentPos < anchor) { +			return BeforeInOrAfter(pos, currentPos, anchor); +		} +	} +	return 1; +} + +bool Editor::PointInSelection(Point pt) { +	// TODO: fix up for rectangular selection +	int pos = PositionFromLocation(pt); +	if (0 == PositionInSelection(pos)) { +		if (pos == SelectionStart()) { +			// see if just before selection +			Point locStart = LocationFromPosition(pos); +			if (pt.x < locStart.x) +				return false; +		} +		if (pos == SelectionEnd()) { +			// 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::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 = pdoc->MovePositionOutsideChar(newPos, currentPos - newPos); +	inDragDrop = false; +	 +	bool processed = NotifyMarginClick(pt, shift, ctrl, alt); +	if (processed) +		return; +		 +	if (shift) { +		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 (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); +	} else {	// Single click +		if (PointInSelMargin(pt)) { +			if (ctrl) { +				SelectAll(); +				lastClickTime = curTime; +				return; +			} +			lineAnchor = LineFromLocation(pt); +			// While experimenting with folding turn off line selection +			if (!shift) { +				// Single click in margin: select whole line +				SetSelection(pdoc->LineStart(lineAnchor + 1), pdoc->LineStart(lineAnchor)); +			} else { +				// Single shift+click in margin: select from anchor to beginning of clicked line +				SetSelection(pdoc->LineStart(lineAnchor), anchor); +			} +			SetDragPosition(invalidPosition); +			SetMouseCapture(true); +			selectionType = selLine; +		} else { +			if (!shift) { +				inDragDrop = PointInSelection(pt); +			} +			if (inDragDrop) { +				SetMouseCapture(false); +				SetDragPosition(newPos); +				CopySelectionIntoDrag(); +				StartDrag(); +			} else { +				selType = alt ? selRectangle : selStream; +				xStartSelect = pt.x - vs.fixedColumnWidth + xOffset; +				xEndSelect = pt.x - vs.fixedColumnWidth + xOffset; +				SetDragPosition(invalidPosition); +				SetMouseCapture(true); +				if (!shift) +					SetEmptySelection(newPos); +				selectionType = selChar; +				originalAnchorPos = currentPos; +			} +		} +	} +	lastClickTime = curTime; +	lastXChosen = pt.x; +	ShowCaretAtCurrentPosition(); +} + +void Editor::ButtonMove(Point pt) { +	//Platform::DebugPrintf("Move %d %d\n", pt.x, pt.y); +	if (HaveMouseCapture()) { +		xEndSelect = pt.x - vs.fixedColumnWidth + xOffset; +		ptMouseLast = pt; +		int movePos = PositionFromLocation(pt); +		movePos = pdoc->MovePositionOutsideChar(movePos, currentPos - movePos); +		if (posDrag >= 0) { +			SetDragPosition(movePos); +		} else { +			if (selectionType == selChar) { +				SetSelection(movePos); +			} else if (selectionType == selWord) { +				// Continue selecting by word +				if (currentPos > 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); +				if (lineAnchor < lineMove) { +					SetSelection(pdoc->LineStart(lineMove + 1), +								pdoc->LineStart(lineAnchor)); +				} else { +					SetSelection(pdoc->LineStart(lineMove), +								pdoc->LineStart(lineAnchor + 1)); +				} +			} +		} +		EnsureCaretVisible(false); +	} else { +		if (vs.fixedColumnWidth > 0) {	// There is a margin +			if (PointInSelMargin(pt)) { +				wDraw.SetCursor(Window::cursorReverseArrow); +				return; 	// No need to test for selection +			} +		} +		// Display regular (drag) cursor over selection +		if (PointInSelection(pt)) +			wDraw.SetCursor(Window::cursorArrow); +		else +			wDraw.SetCursor(Window::cursorText); +	} + +} + +void Editor::ButtonUp(Point pt, unsigned int curTime, bool ctrl) { +	//Platform::DebugPrintf("ButtonUp %d\n", HaveMouseCapture()); +	if (HaveMouseCapture()) { +		if (PointInSelMargin(pt)) { +			wDraw.SetCursor(Window::cursorReverseArrow); +		} else { +			wDraw.SetCursor(Window::cursorText); +		} +		xEndSelect = pt.x - vs.fixedColumnWidth + xOffset; +		ptMouseLast = pt; +		SetMouseCapture(false); +		int newPos = PositionFromLocation(pt); +		newPos = pdoc->MovePositionOutsideChar(newPos, currentPos - newPos); +		if (inDragDrop) { +			int selStart = SelectionStart(); +			int selEnd = SelectionEnd(); +			if (selStart < selEnd) { +				if (dragChars && lenDrag) { +					if (ctrl) { +						pdoc->InsertString(newPos, dragChars, lenDrag); +						SetSelection(newPos, newPos + lenDrag); +					} else if (newPos < selStart) { +						pdoc->DeleteChars(selStart, lenDrag); +						pdoc->InsertString(newPos, dragChars, lenDrag); +						SetSelection(newPos, newPos + lenDrag); +					} else if (newPos > selEnd) { +						pdoc->DeleteChars(selStart, lenDrag); +						newPos -= lenDrag; +						pdoc->InsertString(newPos, dragChars, lenDrag); +						SetSelection(newPos, newPos + lenDrag); +					} else { +						SetEmptySelection(newPos); +					} +					delete []dragChars; +					dragChars = 0; +					lenDrag = 0; +				} +				selectionType = selChar; +			} +		} else { +			if (selectionType == selChar) { +				SetSelection(newPos); +			} +		} +		lastClickTime = curTime; +		lastClick = pt; +		lastXChosen = pt.x; +		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; +			InvalidateCaret(); +		} +	} +} + +static bool IsIn(int a, int minimum, int maximum) { +	return (a >= minimum) && (a <= maximum); +} + +static bool IsOverlap(int mina, int maxa, int minb, int maxb) { +	return  +		IsIn(mina, minb, maxb) || +		IsIn(maxa, minb, maxb) || +		IsIn(minb, mina, maxa) || +		IsIn(maxb, mina, maxa); +} + +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 rcText = GetTextRectangle(); +		// Determine number of lines displayed including a possible partially displayed last line +		int linesDisplayed = (rcText.bottom - rcText.top - 1) / vs.lineHeight + 1; +		int bottomLine = topLine + linesDisplayed - 1; +		 +		int lineRangeStart = cs.DisplayFromDoc(pdoc->LineFromPosition(r.start)); +		int lineRangeEnd = cs.DisplayFromDoc(pdoc->LineFromPosition(r.end)); +		if (!IsOverlap(topLine, bottomLine, lineRangeStart, lineRangeEnd)) { +			//Platform::DebugPrintf("No overlap (%d-%d) with window(%d-%d)\n", +			//		lineRangeStart, lineRangeEnd, topLine, bottomLine); +			return; +		} +		 +		// Assert rcPaint contained within or equal to rcText +		if (rcPaint.top > rcText.top) { + 			// does range intersect rcText.top .. rcPaint.top +			int paintTopLine = ((rcPaint.top - rcText.top-1) / vs.lineHeight) + topLine; +			// paintTopLine is the top line of the paint rectangle or the line just above if that line is completely inside the paint rectangle +			if (IsOverlap(topLine, paintTopLine, lineRangeStart, lineRangeEnd)) { +				//Platform::DebugPrintf("Change (%d-%d) in top npv(%d-%d)\n",  +				//	lineRangeStart, lineRangeEnd, topLine, paintTopLine); +				paintState = paintAbandoned; +				return; +			} +		} +		if (rcPaint.bottom < rcText.bottom) { +			// does range intersect rcPaint.bottom .. rcText.bottom +			int paintBottomLine = ((rcPaint.bottom - rcText.top-1) / vs.lineHeight + 1) + topLine; +			// paintTopLine is the bottom line of the paint rectangle or the line just below if that line is completely inside the paint rectangle +			if (IsOverlap(paintBottomLine, bottomLine, lineRangeStart, lineRangeEnd)) { +				//Platform::DebugPrintf("Change (%d-%d) in bottom npv(%d-%d)\n",  +				//	lineRangeStart, lineRangeEnd, paintBottomLine, bottomLine); +				paintState = paintAbandoned; +				return; +			} +		} +	} +} + +char BraceOpposite(char ch) { +	switch (ch) { +                case '(': return ')'; +                case ')': return '('; +                case '[': return ']'; +                case ']': return '['; +                case '{': return '}'; +                case '}': return '{'; +                case '<': return '>'; +                case '>': return '<'; +                default: return '\0'; +	} +} + +// TODO: should be able to extend styled region to find matching brace +// TODO: may need to make DBCS safe +// so should be moved into Document +int Editor::BraceMatch(int position, int maxReStyle) { +	char chBrace = pdoc->CharAt(position); +	char chSeek = BraceOpposite(chBrace); +	if (!chSeek) +		return - 1; +	char styBrace = pdoc->StyleAt(position) & pdoc->stylingBitsMask; +	int direction = -1; +	if (chBrace == '(' || chBrace == '[' || chBrace == '{' || chBrace == '<') +		direction = 1; +	int depth = 1; +	position = position + direction; +	while ((position >= 0) && (position < pdoc->Length())) { +		char chAtPos = pdoc->CharAt(position); +		char styAtPos = pdoc->StyleAt(position) & pdoc->stylingBitsMask; +		if ((position > pdoc->GetEndStyled()) || (styAtPos == styBrace)) { +			if (chAtPos == chBrace) +				depth++; +			if (chAtPos == chSeek) +				depth--; +			if (depth == 0) +				return position; +		} +		position = position + direction; +	} +	return - 1; +} + +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(); +	// Reset the contraction state to fully shown. +	cs.Clear(); +	cs.InsertLines(0, pdoc->LinesTotal()); + +	pdoc->AddWatcher(this, 0); +	Redraw(); +	SetScrollBars(); +} + +// 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 (pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) { +		if (cs.GetExpanded(line)) { +			int lineMaxSubord = pdoc->GetLastChild(line); +			cs.SetExpanded(line, 0); +			if (lineMaxSubord > line) { +				cs.SetVisible(line+1, lineMaxSubord, false); +				SetScrollBars(); +				Redraw(); +			} +		} else { +			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 line) { +	if (!cs.GetVisible(line)) { +		int lineParent = pdoc->GetFoldParent(line); +		if (lineParent >= 0) { +			if (line != lineParent) +				EnsureLineVisible(lineParent); +			if (!cs.GetExpanded(lineParent)) { +				cs.SetExpanded(lineParent, 1); +				Expand(lineParent, true); +			} +		} +		SetScrollBars(); +		Redraw(); +	} +} + +LRESULT Editor::WndProc(UINT iMessage, WPARAM wParam, LPARAM lParam) { +	//Platform::DebugPrintf("S start wnd proc %d %d %d\n",iMessage, wParam, lParam); + +	// Optional macro recording hook +#ifdef MACRO_SUPPORT +	if (recordingMacro) +		NotifyMacroRecord(iMessage, wParam, lParam); +#endif + +	switch (iMessage) { + +	case WM_GETTEXT: +		{ +			if (lParam == 0) +				return 0; +			char *ptr = reinterpret_cast<char *>(lParam); +			unsigned int iChar = 0; +			for (; iChar < wParam-1; iChar++) +				ptr[iChar] = pdoc->CharAt(iChar); +			ptr[iChar] = '\0'; +			return iChar; +		} + +	case WM_SETTEXT: +		{ +			if (lParam == 0) +				return FALSE; +			pdoc->DeleteChars(0, pdoc->Length()); +			SetEmptySelection(0); +			pdoc->InsertString(0, reinterpret_cast<char *>(lParam)); +			return TRUE; +		} + +	case WM_GETTEXTLENGTH: +		return pdoc->Length(); + +	case WM_NOTIFY: +		//Platform::DebugPrintf("S notify %d %d\n", wParam, lParam); +		break; + +	case WM_CUT: +		Cut(); +		SetLastXChosen(); +		break; + +	case WM_COPY: +		Copy(); +		break; + +	case WM_PASTE: +		Paste(); +		SetLastXChosen(); +		break; + +	case WM_CLEAR: +		Clear(); +		SetLastXChosen(); +		break; + +	case WM_UNDO: +		Undo(); +		SetLastXChosen(); +		break; + +		// Edit control mesages + +		// Not supported (no-ops): +		//		EM_GETWORDBREAKPROC +		//		EM_GETWORDBREAKPROCEX +		//		EM_SETWORDBREAKPROC +		//		EM_SETWORDBREAKPROCEX +		//		EM_GETWORDWRAPMODE +		//		EM_SETWORDWRAPMODE +		//		EM_LIMITTEXT +		//		EM_EXLIMITTEXT +		//		EM_SETRECT +		//		EM_SETRECTNP +		//		EM_FMTLINES +		//		EM_GETHANDLE +		//		EM_SETHANDLE +		//		EM_GETPASSWORDCHAR +		//		EM_SETPASSWORDCHAR +		//		EM_SETTABSTOPS +		//		EM_FINDWORDBREAK +		//		EM_GETCHARFORMAT +		//		EM_SETCHARFORMAT +		//		EM_GETOLEINTERFACE +		//		EM_SETOLEINTERFACE +		//		EM_SETOLECALLBACK +		//		EM_GETPARAFORMAT +		//		EM_SETPARAFORMAT +		//		EM_PASTESPECIAL +		//		EM_REQUESTRESIZE +		//		EM_GETBKGNDCOLOR +		//		EM_SETBKGNDCOLOR +		//		EM_STREAMIN +		//		EM_STREAMOUT +		//		EM_GETIMECOLOR +		//		EM_SETIMECOLOR +		//		EM_GETIMEOPTIONS +		//		EM_SETIMEOPTIONS +		//		EM_GETOPTIONS +		//		EM_SETOPTIONS +		//		EM_GETPUNCTUATION +		//		EM_SETPUNCTUATION +		//		EM_GETTHUMB + +		// Not supported but should be: +		//		EM_GETEVENTMASK +		//		EM_SETEVENTMASK +		//		For printing: +		//			EM_DISPLAYBAND +		//			EM_SETTARGETDEVICE + +	case EM_CANUNDO: +		return pdoc->CanUndo() ? TRUE : FALSE; + +	case EM_UNDO: +		Undo(); +		break; + +	case EM_EMPTYUNDOBUFFER: +		pdoc->DeleteUndoHistory(); +		return 0; + +	case EM_GETFIRSTVISIBLELINE: +		return topLine; + +	case EM_GETLINE: { +			if (lParam == 0) +				return 0; +			int lineStart = pdoc->LineStart(wParam); +			int lineEnd = pdoc->LineStart(wParam + 1); +			char *ptr = reinterpret_cast<char *>(lParam); +			WORD *pBufSize = reinterpret_cast<WORD *>(lParam); +			if (*pBufSize < lineEnd - lineStart) { +				ptr[0] = '\0'; 	// If no characters copied have to put a NUL into buffer +				return 0; +			} +			int iPlace = 0; +			for (int iChar = lineStart; iChar < lineEnd; iChar++) +				ptr[iPlace++] = pdoc->CharAt(iChar); +			return iPlace; +		} + +	case EM_GETLINECOUNT: +		if (pdoc->LinesTotal() == 0) +			return 1; +		else +			return pdoc->LinesTotal(); + +	case EM_GETMODIFY: +		return !pdoc->IsSavePoint(); + +	case EM_SETMODIFY: +		// Not really supported now that there is the save point stuff +		//pdoc->isModified = wParam; +		//return pdoc->isModified; +		return false; + +	case EM_GETRECT: +		if (lParam == 0) +			return 0; +		*(reinterpret_cast<PRectangle *>(lParam)) = GetClientRectangle(); +		break; + +	case EM_GETSEL: +		if (wParam) +			*reinterpret_cast<int *>(wParam) = SelectionStart(); +		if (lParam) +			*reinterpret_cast<int *>(lParam) = SelectionEnd(); +		return MAKELONG(SelectionStart(), SelectionEnd()); + +	case EM_EXGETSEL: { +			if (lParam == 0) +				return 0; +			CHARRANGE *pCR = reinterpret_cast<CHARRANGE *>(lParam); +			pCR->cpMin = SelectionStart(); +			pCR->cpMax = SelectionEnd(); +		} +		break; + +	case EM_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 +			SetSelection(nEnd, nStart); +			EnsureCaretVisible(); +		} +		break; + +	case EM_EXSETSEL: { +			if (lParam == 0) +				return 0; +			CHARRANGE *pCR = reinterpret_cast<CHARRANGE *>(lParam); +			if (pCR->cpMax == -1) { +				SetSelection(pCR->cpMin, pdoc->Length()); +			} else { +				SetSelection(pCR->cpMin, pCR->cpMax); +			} +			EnsureCaretVisible(); +			return pdoc->LineFromPosition(SelectionStart()); +		} + +	case EM_GETSELTEXT: { +			if (lParam == 0) +				return 0; +			char *ptr = reinterpret_cast<char *>(lParam); +			int selSize = SelectionRangeLength(); +			char *text = CopySelectionRange(); +			int iChar = 0; +			if (text) { +				for (; iChar < selSize; iChar++) +					ptr[iChar] = text[iChar]; +				ptr[iChar] = '\0'; +				delete []text; +			} +			return iChar; +		} + +	case EM_GETWORDBREAKPROC: +		return 0; + +	case EM_SETWORDBREAKPROC: +		break; + +	case EM_LIMITTEXT: +		// wParam holds the number of characters control should be limited to +		break; + +	case EM_GETLIMITTEXT: +		return 0xffffffff; + +	case EM_GETOLEINTERFACE: +		return 0; + +	case EM_LINEFROMCHAR: +		if (static_cast<int>(wParam) < 0) +			wParam = SelectionStart(); +		return pdoc->LineFromPosition(wParam); + +	case EM_EXLINEFROMCHAR: +		if (static_cast<int>(lParam) < 0) +			lParam = SelectionStart(); 	// Not specified, but probably OK +		return pdoc->LineFromPosition(lParam); + +	case EM_LINEINDEX: +		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); + +	case EM_LINELENGTH: +		{ +			if (static_cast<int>(wParam) < 0)	// Who use this anyway? +				return 0; 	// Should be... Too complex to describe here, see MS specs! +			if (static_cast<int>(wParam) > pdoc->Length())	// Useful test, anyway... +				return 0; +			int line = pdoc->LineFromPosition(wParam); +			int charsOnLine = 0; +			for (int pos = pdoc->LineStart(line); pos < pdoc->LineStart(line + 1); pos++) { +				if ((pdoc->CharAt(pos) != '\r') && (pdoc->CharAt(pos) != '\n')) +					charsOnLine++; +			} +			return charsOnLine; +		} + +		// 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 EM_REPLACESEL: { +			if (lParam == 0) +				return 0; +			pdoc->BeginUndoAction(); +			ClearSelection(); +			char *replacement = reinterpret_cast<char *>(lParam); +			pdoc->InsertString(currentPos, replacement); +			pdoc->EndUndoAction(); +			SetEmptySelection(currentPos + strlen(replacement)); +			EnsureCaretVisible(); +		} +		break; + +	case EM_LINESCROLL: +		ScrollTo(topLine + lParam); +		HorizontalScrollTo(xOffset + wParam * vs.spaceWidth); +		return TRUE; + +	case EM_SCROLLCARET: +		EnsureCaretVisible(); +		break; + +	case EM_SETREADONLY: +		pdoc->SetReadOnly(wParam); +		return TRUE; + +	case EM_SETRECT: +		break; + +	case EM_CANPASTE: +		return 1; + +	case EM_CHARFROMPOS: { +			if (lParam == 0) +				return 0; +			Point *ppt = reinterpret_cast<Point *>(lParam); +			int pos = PositionFromLocation(*ppt); +			int line = pdoc->LineFromPosition(pos); +			return MAKELONG(pos, line); +		} + +	case EM_POSFROMCHAR: { +			// The MS specs for this have changed 3 times: using the RichEdit 3 version +			if (wParam == 0) +				return 0; +			Point *ppt = reinterpret_cast<Point *>(wParam); +			if (lParam < 0) { +				*ppt = Point(0, 0); +			} else { +				*ppt = LocationFromPosition(lParam); +			} +			return 0; +		} + +	case EM_FINDTEXT: +		return FindText(iMessage, wParam, lParam); + +	case EM_FINDTEXTEX: +		return FindText(iMessage, wParam, lParam); + +	case EM_GETTEXTRANGE: { +			if (lParam == 0) +				return 0; +			TEXTRANGE *tr = reinterpret_cast<TEXTRANGE *>(lParam); +			int cpMax = tr->chrg.cpMax; +			if (cpMax == -1) +				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 EM_SELECTIONTYPE: +		if (currentPos == anchor) +			return SEL_EMPTY; +		else +			return SEL_TEXT; + +	case EM_HIDESELECTION: +		hideSelection = wParam; +		Redraw(); +		break; + +	case EM_FORMATRANGE: +		return FormatRange(wParam, reinterpret_cast<FORMATRANGE *>(lParam)); + +	case EM_GETMARGINS: +		return MAKELONG(vs.leftMarginWidth, vs.rightMarginWidth); +		 +	case EM_SETMARGINS: +		if (wParam & EC_LEFTMARGIN) { +			vs.leftMarginWidth = LOWORD(lParam); +		} +		if (wParam & EC_RIGHTMARGIN) { +			vs.rightMarginWidth = HIWORD(lParam); +		} +		if (wParam == EC_USEFONTINFO) { +			vs.leftMarginWidth = vs.aveCharWidth / 2; +			vs.rightMarginWidth = vs.aveCharWidth / 2; +		} +		InvalidateStyleRedraw(); +		break; +	 +		// Control specific mesages + +	case SCI_ADDTEXT: { +			if (lParam == 0) +				return 0; +			pdoc->InsertString(CurrentPosition(), reinterpret_cast<char *>(lParam), wParam); +			SetEmptySelection(currentPos + wParam); +			return 0; +		} + +	case SCI_ADDSTYLEDTEXT: { +			if (lParam == 0) +				return 0; +			pdoc->InsertStyledString(CurrentPosition() * 2, reinterpret_cast<char *>(lParam), wParam); +			SetEmptySelection(currentPos + wParam / 2); +			return 0; +		} + +	case SCI_INSERTTEXT: { +			if (lParam == 0) +				return 0; +			int insertPos = wParam; +			if (static_cast<short>(wParam) == -1) +				insertPos = CurrentPosition(); +			int newCurrent = CurrentPosition(); +			int newAnchor = anchor; +			char *sz = reinterpret_cast<char *>(lParam); +			pdoc->InsertString(insertPos, sz); +			if (newCurrent > insertPos) +				newCurrent += strlen(sz); +			if (newAnchor > insertPos) +				newAnchor += strlen(sz); +			SetEmptySelection(newCurrent); +			return 0; +		} + +	case SCI_CLEARALL: +		ClearAll(); +		return 0; + +	case SCI_SETUNDOCOLLECTION: +		pdoc->SetUndoCollection(static_cast<enum undoCollectionType>(wParam)); +		return 0; + +#ifdef INCLUDE_DEPRECATED_FEATURES +	case SCI_APPENDUNDOSTARTACTION: +		pdoc->AppendUndoStartAction(); +		return 0; +#endif + +	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: { +			if (lParam == 0) +				return 0; +			pdoc->SetWordChars(reinterpret_cast<unsigned char *>(lParam)); +		} +		break; + +	case SCI_GETLENGTH: +		return pdoc->Length(); + +	case SCI_GETCHARAT: +		return pdoc->CharAt(wParam); + +	case SCI_GETCURRENTPOS: +		return currentPos; + +	case SCI_GETANCHOR: +		return anchor; + +	case SCI_GETSTYLEAT: +		if (static_cast<short>(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(); +		NotifySavePoint(true); +		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() ? TRUE : FALSE; + +	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 = wParam; +		Redraw(); +		break; + +	case SCI_GOTOLINE: +		GoToLine(wParam); +		break; + +	case SCI_GOTOPOS: +		SetEmptySelection(wParam); +		EnsureCaretVisible(); +		Redraw(); +		break; + +	case SCI_SETANCHOR: +		SetSelection(currentPos, wParam); +		break; + +	case SCI_GETCURLINE: { +			if (lParam == 0) +				return 0; +			int lineCurrentPos = pdoc->LineFromPosition(currentPos); +			int lineStart = pdoc->LineStart(lineCurrentPos); +			unsigned int lineEnd = pdoc->LineStart(lineCurrentPos + 1); +			char *ptr = reinterpret_cast<char *>(lParam); +			unsigned int iPlace = 0; +			for (unsigned int iChar = lineStart; iChar < lineEnd && iPlace < wParam; 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, lParam); +		break; + +	case SCI_SETSTYLING: +		pdoc->SetStyleFor(wParam, lParam); +		break; + +	case SCI_SETSTYLINGEX:   // Specify a complete styling buffer +		if (lParam == 0) +			return 0; +		pdoc->SetStyles(wParam, reinterpret_cast<char *>(lParam)); +		break; + +#ifdef INCLUDE_DEPRECATED_FEATURES +	case SCI_SETMARGINWIDTH: +		if (wParam < 100) { +			vs.ms[1].width = wParam; +		} +		InvalidateStyleRedraw(); +		break; +#endif + +	case SCI_SETBUFFEREDDRAW: +		bufferedDraw = wParam; +		break; + +	case SCI_SETTABWIDTH: +		if (wParam > 0) +			pdoc->tabInChars = wParam; +		InvalidateStyleRedraw(); +		break; + +	case SCI_SETCODEPAGE: +		pdoc->dbcsCodePage = wParam; +		break; + +#ifdef INCLUDE_DEPRECATED_FEATURES +	case SCI_SETLINENUMBERWIDTH: +		if (wParam < 200) { +			vs.ms[0].width = wParam; +		} +		InvalidateStyleRedraw(); +		break; +#endif + +	case SCI_SETUSEPALETTE: +		palette.allowRealization = wParam; +		InvalidateStyleRedraw(); +		break; + +		// 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 = Colour(lParam); +		InvalidateStyleData(); +		RedrawSelMargin(); +		break; +	case SCI_MARKERSETBACK: +		if (wParam <= MARKER_MAX) +			vs.markers[wParam].back.desired = Colour(lParam); +		InvalidateStyleData(); +		RedrawSelMargin(); +		break; +	case SCI_MARKERADD: { +			int markerID = pdoc->AddMark(wParam, lParam); +			RedrawSelMargin(); +			return markerID; +		} + +	case SCI_MARKERDELETE: +		pdoc->DeleteMark(wParam, lParam); +		RedrawSelMargin(); +		break; + +	case SCI_MARKERDELETEALL: +		pdoc->DeleteAllMarks(static_cast<int>(wParam)); +		RedrawSelMargin(); +		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_SETMARGINTYPEN: +		if (wParam >= 0 && wParam < ViewStyle::margins) { +			vs.ms[wParam].symbol = (lParam == SC_MARGIN_SYMBOL); +			InvalidateStyleRedraw(); +		} +		break; +		 +	case SCI_GETMARGINTYPEN: +		if (wParam >= 0 && wParam < ViewStyle::margins)  +			return vs.ms[wParam].symbol ? SC_MARGIN_SYMBOL : SC_MARGIN_NUMBER; +		else +			return 0; +			 +	case SCI_SETMARGINWIDTHN: +		if (wParam >= 0 && wParam < ViewStyle::margins) { +			vs.ms[wParam].width = lParam; +			InvalidateStyleRedraw(); +		} +		break; +		 +	case SCI_GETMARGINWIDTHN: +		if (wParam >= 0 && wParam < ViewStyle::margins)  +			return vs.ms[wParam].width; +		else +			return 0; +			 +	case SCI_SETMARGINMASKN: +		if (wParam >= 0 && wParam < ViewStyle::margins) { +			vs.ms[wParam].mask = lParam; +			InvalidateStyleRedraw(); +		} +		break; +		 +	case SCI_GETMARGINMASKN: +		if (wParam >= 0 && wParam < ViewStyle::margins)  +			return vs.ms[wParam].mask; +		else +			return 0; +		 +	case SCI_SETMARGINSENSITIVEN: +		if (wParam >= 0 && wParam < ViewStyle::margins) { +			vs.ms[wParam].sensitive = lParam; +			InvalidateStyleRedraw(); +		} +		break; +		 +	case SCI_GETMARGINSENSITIVEN: +		if (wParam >= 0 && wParam < ViewStyle::margins)  +			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 = Colour(lParam); +			InvalidateStyleRedraw(); +		} +		break; +	case SCI_STYLESETBACK: +		if (wParam <= STYLE_MAX) { +			vs.styles[wParam].back.desired = Colour(lParam); +			InvalidateStyleRedraw(); +		} +		break; +	case SCI_STYLESETBOLD: +		if (wParam <= STYLE_MAX) { +			vs.styles[wParam].bold = lParam; +			InvalidateStyleRedraw(); +		} +		break; +	case SCI_STYLESETITALIC: +		if (wParam <= STYLE_MAX) { +			vs.styles[wParam].italic = lParam; +			InvalidateStyleRedraw(); +		} +		break; +	case SCI_STYLESETEOLFILLED: +		if (wParam <= STYLE_MAX) { +			vs.styles[wParam].eolFilled = lParam; +			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, reinterpret_cast<const char *>(lParam)); +			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(); +		 +	// Folding messages +		 +	case SCI_VISIBLEFROMDOCLINE: +		return cs.DisplayFromDoc(wParam); +		 +	case SCI_DOCLINEFROMVISIBLE: +		return cs.DocFromDisplay(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)) { +			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); +		break; +		 +	case SCI_SEARCHANCHOR: +		SearchAnchor(); +		break; + +	case SCI_SEARCHNEXT: +	case SCI_SEARCHPREV: +		return SearchText(iMessage, wParam, lParam); +		break; + +	case SCI_SETCARETPOLICY: +		caretPolicy = wParam; +		caretSlop = lParam; +		break; + +#ifdef INCLUDE_DEPRECATED_FEATURES +	case SCI_SETFORE: +		vs.styles[STYLE_DEFAULT].fore.desired = Colour(wParam); +		InvalidateStyleRedraw(); +		break; + +	case SCI_SETBACK: +		vs.styles[STYLE_DEFAULT].back.desired = Colour(wParam); +		InvalidateStyleRedraw(); +		break; + +	case SCI_SETBOLD: +		vs.styles[STYLE_DEFAULT].bold = wParam; +		InvalidateStyleRedraw(); +		break; + +	case SCI_SETITALIC: +		vs.styles[STYLE_DEFAULT].italic = wParam; +		InvalidateStyleRedraw(); +		break; + +	case SCI_SETSIZE: +		vs.styles[STYLE_DEFAULT].size = wParam; +		InvalidateStyleRedraw(); +		break; + +	case SCI_SETFONT: +		if (wParam == 0) +			return 0; +		strcpy(vs.styles[STYLE_DEFAULT].fontName, reinterpret_cast<char *>(wParam)); +		InvalidateStyleRedraw(); +		break; +#endif + +	case SCI_SETSELFORE: +		vs.selforeset = wParam; +		vs.selforeground.desired = Colour(lParam); +		InvalidateStyleRedraw(); +		break; + +	case SCI_SETSELBACK: +		vs.selbackset = wParam; +		vs.selbackground.desired = Colour(lParam); +		InvalidateStyleRedraw(); +		break; + +	case SCI_SETCARETFORE: +		vs.caretcolour.desired = Colour(wParam); +		InvalidateStyleRedraw(); +		break; + +	case SCI_ASSIGNCMDKEY: +		kmap.AssignCmdKey(LOWORD(wParam), HIWORD(wParam), lParam); +		break; + +	case SCI_CLEARCMDKEY: +		kmap.AssignCmdKey(LOWORD(wParam), HIWORD(wParam), WM_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 = Colour(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_LINEUP: +	case SCI_LINEUPEXTEND: +	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_HOME: +	case SCI_HOMEEXTEND: +	case SCI_LINEEND: +	case SCI_LINEENDEXTEND: +	case SCI_DOCUMENTSTART: +	case SCI_DOCUMENTSTARTEXTEND: +	case SCI_DOCUMENTEND: +	case SCI_DOCUMENTENDEXTEND: +	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_ZOOMIN: +	case SCI_ZOOMOUT: +	case SCI_DELWORDLEFT: +	case SCI_DELWORDRIGHT: +		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 BraceMatch(wParam, lParam); + +	case SCI_GETVIEWEOL: +		return vs.viewEOL; + +	case SCI_SETVIEWEOL: +		vs.viewEOL = wParam; +		Redraw(); +		break; + +	case SCI_GETEDGECOLUMN: +		return theEdge; +		 +	case SCI_SETEDGECOLUMN: +		theEdge = wParam; +		InvalidateStyleRedraw(); +		break; +		 +	case SCI_GETEDGEMODE: +		return edgeState; +		 +	case SCI_SETEDGEMODE: +		edgeState = wParam; +		InvalidateStyleRedraw(); +		break; +		 +	case SCI_GETEDGECOLOUR: +		return vs.edgecolour.desired.AsLong(); +		 +	case SCI_SETEDGECOLOUR: +		vs.edgecolour.desired = Colour(wParam); +		InvalidateStyleRedraw(); +		break; +		 +	case SCI_GETDOCPOINTER: +		return reinterpret_cast<LRESULT>(pdoc); + +	case SCI_SETDOCPOINTER: +		SetDocPointer(reinterpret_cast<Document *>(lParam)); +		return 0; + +	case SCI_SETMODEVENTMASK: +		modEventMask = wParam; +		return 0; +		 +	case SCI_CONVERTEOLS: +		pdoc->ConvertLineEnds(wParam); +		SetSelection(currentPos, anchor);	// Ensure selection inside document +		return 0; + +#ifdef MACRO_SUPPORT +	case SCI_STARTRECORD: +		recordingMacro = 1; +		return 0; + +	case SCI_STOPRECORD: +		recordingMacro = 0; +		return 0; +#endif +		 +	default: +		return DefWndProc(iMessage, wParam, lParam); +	} +	//Platform::DebugPrintf("end wnd proc\n"); +	return 0l; +} diff --git a/src/Editor.h b/src/Editor.h new file mode 100644 index 000000000..4ff334767 --- /dev/null +++ b/src/Editor.h @@ -0,0 +1,281 @@ +// Scintilla source code edit control +// Editor.h - defines the main editor class +// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef EDITOR_H +#define EDITOR_H + +class Caret { +public: +	bool active; +	bool on; +	int period; +		 +	Caret(); +}; + +class Timer { + +public: +	bool ticking; +	int ticksToWait; +	enum {tickSize = 100}; +	int tickerID; +		 +	Timer(); +}; + +class LineLayout { +public: +	// Drawing is only performed for maxLineLength characters on each line. +	enum {maxLineLength = 4000}; +	int numCharsInLine; +	char chars[maxLineLength]; +	char styles[maxLineLength]; +	char indicators[maxLineLength]; +	int positions[maxLineLength]; +}; + +class Editor : public DocWatcher { +protected:	// ScintillaBase subclass needs access to much of Editor + +	// On GTK+, Scintilla is a container widget holding two scroll bars and a drawing area +	// whereas on Windows there is just one window with both scroll bars turned on. +	// Therefore, on GTK+ the following are separate windows but only one window on Windows. +	Window wMain;	// The Scintilla parent window +	Window wDraw;	// The text drawing area + +	// Style resources may be expensive to allocate so are cached between uses. +	// When a style attribute is changed, this cache is flushed. +	bool stylesValid;	 +	ViewStyle vs; +	Palette palette; +	 +	bool hideSelection; +	bool inOverstrike; + +	// In bufferedDraw mode, graphics operations are drawn to a pixmap and then copied to  +	// the screen. This avoids flashing but is about 30% slower. +	bool bufferedDraw; + +	int xOffset;				// Horizontal scrolled amount in pixels +	int xCaretMargin;	// Ensure this many pixels visible on both sides of caret +	 +	Surface pixmapLine; +	Surface pixmapSelMargin; +	Surface pixmapSelPattern; +	// Intellimouse support - currently only implemented for Windows +	unsigned int ucWheelScrollLines; +	short cWheelDelta; //wheel delta from roll + +	KeyMap kmap; + +	Caret caret; +	Timer timer; + +	Point lastClick; +	unsigned int lastClickTime; +	enum { selChar, selWord, selLine } selectionType; +	Point ptMouseLast; +	bool firstExpose; +	bool inDragDrop; +	bool dropWentOutside; +	int posDrag; +	int posDrop; +	int lastXChosen; +	int lineAnchor; +	int originalAnchorPos; +	int currentPos; +	int anchor; +	int topLine; +	int posTopLine; +	 +	bool needUpdateUI; +	Position braces[2]; +	int bracesMatchStyle; +	 +	int edgeState; +	int theEdge; + +	enum { notPainting, painting, paintAbandoned } paintState; +	PRectangle rcPaint; +	bool paintingAllText; +	 +	int modEventMask; +	 +	char *dragChars; +	int lenDrag; +	bool dragIsRectangle; +	enum { selStream, selRectangle, selRectangleFixed } selType; +	int xStartSelect; +	int xEndSelect; +	 +	int caretPolicy; +	int caretSlop; + +	int searchAnchor; + +#ifdef MACRO_SUPPORT +	int recordingMacro; +#endif + +	int foldFlags; +	ContractionState cs; + +	Document *pdoc; + +	Editor(); +	virtual ~Editor(); +	virtual void Initialise() = 0; +	virtual void Finalise(); + +	void InvalidateStyleData(); +	void InvalidateStyleRedraw(); +	virtual void RefreshColourPalette(Palette &pal, bool want); +	void RefreshStyleData(); +	void DropGraphics(); + +	PRectangle GetClientRectangle(); +	PRectangle GetTextRectangle(); +	 +	int LinesOnScreen(); +	int LinesToScroll(); +	int MaxScrollPos(); +	Point LocationFromPosition(unsigned int pos); +	int XFromPosition(unsigned int pos); +	int PositionFromLocation(Point pt); +	int PositionFromLineX(int line, int x); +	int LineFromLocation(Point pt); +	void SetTopLine(int topLineNew); +	 +	void RedrawRect(PRectangle rc); +	void Redraw(); +	void RedrawSelMargin(); +	PRectangle RectangleFromRange(int start, int end); +	void InvalidateRange(int start, int end); +	 +	int CurrentPosition(); +	bool SelectionEmpty(); +	int SelectionStart(int line=-1); +	int SelectionEnd(int line=-1); +	void SetSelection(int currentPos_, int anchor_); +	void SetSelection(int currentPos_); +	void SetEmptySelection(int currentPos_); +	int MovePositionTo(int newPos, bool extend = false); +	int MovePositionSoVisible(int pos, int moveDir); +	void SetLastXChosen(); + +	void ScrollTo(int line); +	virtual void ScrollText(int linesToMove); +	void HorizontalScrollTo(int xPos); +	void EnsureCaretVisible(bool useMargin=true); +	void ShowCaretAtCurrentPosition(); +	void DropCaret(); +	void InvalidateCaret(); + +	void PaintSelMargin(Surface *surface, PRectangle &rc); +        void LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayout &ll); +	void DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int xStart,  +		PRectangle rcLine, LineLayout &ll); +	void Paint(Surface *surfaceWindow, PRectangle rcArea); +	long FormatRange(bool draw, FORMATRANGE *pfr); + +	virtual void SetVerticalScrollPos() = 0; +	virtual void SetHorizontalScrollPos() = 0; +	virtual bool ModifyScrollBars(int nMax, int nPage) = 0; +	void SetScrollBarsTo(PRectangle rsClient); +	void SetScrollBars(); + +	virtual void AddChar(char ch); +	void ClearSelection(); +	void ClearAll(); +	void Cut(); +	void PasteRectangular(int pos, const char *ptr, int len); +	virtual void Copy() = 0; +	virtual void Paste() = 0; +	void Clear(); +	void SelectAll(); +	void Undo(); +	void Redo(); +	void DelChar(); +	void DelCharBack(); +	virtual void ClaimSelection() = 0; + +	virtual void NotifyChange() = 0; +	virtual void NotifyFocus(bool focus); +	virtual void NotifyParent(SCNotification scn) = 0; +	virtual void NotifyStyleNeeded(int endStyleNeeded); +	void NotifyChar(char ch); +	void NotifySavePoint(bool isSavePoint); +	void NotifyModifyAttempt(); +	virtual void NotifyDoubleClick(Point pt, bool shift); +	void NotifyUpdateUI(); +	bool NotifyMarginClick(Point pt, bool shift, bool ctrl, bool alt); +	void NotifyNeedShown(int pos, int len); +	 +	void NotifyModifyAttempt(Document *document, void *userData); +	void NotifySavePoint(Document *document, void *userData, bool atSavePoint); +	void NotifyModified(Document *document, DocModification mh, void *userData); +	void NotifyDeleted(Document *document, void *userData); +	 +#ifdef MACRO_SUPPORT +	void NotifyMacroRecord(UINT iMessage, WPARAM wParam, LPARAM lParam); +#endif + +	void PageMove(int direction, bool extend=false); +	virtual int KeyCommand(UINT iMessage); +	virtual int KeyDefault(int /* key */, int /*modifiers*/); +	int KeyDown(int key, bool shift, bool ctrl, bool alt); + +	bool GetWhitespaceVisible(); +	void SetWhitespaceVisible(bool view); + +	void Indent(bool forwards); + +	long FindText(UINT iMessage,WPARAM wParam,LPARAM lParam); +	void SearchAnchor(); +	long SearchText(UINT iMessage,WPARAM wParam,LPARAM lParam); +	void GoToLine(int lineNo); + +	char *CopyRange(int start, int end); +	int SelectionRangeLength(); +	char *CopySelectionRange(); +	void CopySelectionIntoDrag(); +	void SetDragPosition(int newPos); +	virtual void StartDrag(); +	void DropAt(int position, const char *value, bool moving, bool rectangular); +	// PositionInSelection returns 0 if position in selection, -1 if position before selection, and 1 if after. +	// Before means either before any line of selection or before selection on its line, with a similar meaning to after +	int PositionInSelection(int pos); +	bool PointInSelection(Point pt); +	bool PointInSelMargin(Point pt); +	virtual void ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt); +	void ButtonMove(Point pt); +	void ButtonUp(Point pt, unsigned int curTime, bool ctrl); + +	void Tick(); +	virtual void SetTicking(bool on) = 0; +	virtual void SetMouseCapture(bool on) = 0; +	virtual bool HaveMouseCapture() = 0; + +	void CheckForChangeOutsidePaint(Range r); +	int BraceMatch(int position, int maxReStyle); +	void SetBraceHighlight(Position pos0, Position pos1, int matchStyle); +	 +	void SetDocPointer(Document *document); +	 +	void Expand(int &line, bool doExpand); +	void ToggleContraction(int line); +	void EnsureLineVisible(int line); + +	virtual LRESULT DefWndProc(UINT iMessage, WPARAM wParam, LPARAM lParam) = 0; +	 +public: +	// Public so scintilla_send_message can use it +	virtual LRESULT WndProc(UINT iMessage, WPARAM wParam, LPARAM lParam); +	// Public so scintilla_set_id can use it +	int ctrlID;	 +}; + +#endif diff --git a/src/Indicator.cxx b/src/Indicator.cxx new file mode 100644 index 000000000..fb6ad0915 --- /dev/null +++ b/src/Indicator.cxx @@ -0,0 +1,45 @@ +// Scintilla source code edit control +// Indicator.cxx - defines the style of indicators which are text decorations such as underlining +// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#include "Platform.h" + +#include "Scintilla.h" +#include "Indicator.h" + +void Indicator::Draw(Surface *surface, PRectangle &rc) { +	surface->PenColour(fore.allocated); +	int ymid = (rc.bottom + rc.top) / 2; +	if (style == INDIC_SQUIGGLE) { +		surface->MoveTo(rc.left, rc.top); +		int x = rc.left + 2; +		int y = 2; +		while (x < rc.right) { +			surface->LineTo(x, rc.top + y); +			x += 2; +			y = 2 - y; +		} +		surface->LineTo(rc.right, rc.top + y);	// Finish the line +	} else if (style == INDIC_TT) { +		surface->MoveTo(rc.left, ymid); +		int x = rc.left + 5; +		while (x < rc.right) { +			surface->LineTo(x, ymid); +			surface->MoveTo(x-3, ymid); +			surface->LineTo(x-3, ymid+2); +			x++; +			surface->MoveTo(x, ymid); +			x += 5; +		} +		surface->LineTo(rc.right, ymid);	// Finish the line +		if (x - 3 <= rc.right) { +			surface->MoveTo(x-3, ymid); +			surface->LineTo(x-3, ymid+2); +		} +	} else {	// Either INDIC_PLAIN or unknown +		surface->MoveTo(rc.left, ymid); +		surface->LineTo(rc.right, ymid); +	} +} + diff --git a/src/Indicator.h b/src/Indicator.h new file mode 100644 index 000000000..2472cedc1 --- /dev/null +++ b/src/Indicator.h @@ -0,0 +1,18 @@ +// Scintilla source code edit control +// Indicator.h - defines the style of indicators which are text decorations such as underlining +// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef INDICATOR_H +#define INDICATOR_H + +class Indicator { +public: +	int style; +	ColourPair fore; +	Indicator() : style(INDIC_PLAIN), fore(Colour(0,0,0)) { +	} +	void Draw(Surface *surface, PRectangle &rc); +}; + +#endif diff --git a/src/KeyMap.cxx b/src/KeyMap.cxx new file mode 100644 index 000000000..f339cd275 --- /dev/null +++ b/src/KeyMap.cxx @@ -0,0 +1,111 @@ +// Scintilla source code edit control +// KeyMap.cxx  - defines a mapping between keystrokes and commands +// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#include "Platform.h" + +#include "Scintilla.h" + +#include "KeyMap.h" + +KeyMap::KeyMap() : kmap(0), len(0), alloc(0) { +	for (int i = 0; MapDefault[i].key; i++) { +		AssignCmdKey(MapDefault[i].key,  +			MapDefault[i].modifiers, +             		MapDefault[i].msg); +	} +} + +KeyMap::~KeyMap() { +	Clear(); +} + +void KeyMap::Clear() { +	delete []kmap; +	kmap = 0; +	len = 0; +	alloc = 0; +} + +void KeyMap::AssignCmdKey(int key, int modifiers, UINT msg) { +	if ((len+1) >= alloc) { +		KeyToCommand *ktcNew = new KeyToCommand[alloc + 5]; +		if (!ktcNew) +			return; +		for (int k=0;k<len;k++) +			ktcNew[k] = kmap[k]; +		alloc += 5; +		delete []kmap; +		kmap = ktcNew; +	} +	for (int keyIndex = 0; keyIndex < len; keyIndex++) { +		if ((key == kmap[keyIndex].key) && (modifiers == kmap[keyIndex].modifiers)) { +			kmap[keyIndex].msg = msg; +			return; +		} +	} +	kmap[len].key = key; +	kmap[len].modifiers = modifiers; +	kmap[len].msg = msg; +	len++; +} + +UINT KeyMap::Find(int key, int modifiers) { +	for (int i=0; i < len; i++) { +		if ((key == kmap[i].key) && (modifiers == kmap[i].modifiers)) { +			return kmap[i].msg; +		} +	} +	return 0; +} + +KeyToCommand KeyMap::MapDefault[] = { +    VK_DOWN,	SCI_NORM,	SCI_LINEDOWN, +    VK_DOWN,	SCI_SHIFT,	SCI_LINEDOWNEXTEND, +    VK_UP,		SCI_NORM,	SCI_LINEUP, +    VK_UP,		SCI_SHIFT,	SCI_LINEUPEXTEND, +    VK_LEFT,		SCI_NORM,	SCI_CHARLEFT, +    VK_LEFT,		SCI_SHIFT,	SCI_CHARLEFTEXTEND, +    VK_LEFT,		SCI_CTRL,	SCI_WORDLEFT, +    VK_LEFT,		SCI_CSHIFT,	SCI_WORDLEFTEXTEND, +    VK_RIGHT,	SCI_NORM,	SCI_CHARRIGHT, +    VK_RIGHT,	SCI_SHIFT,	SCI_CHARRIGHTEXTEND, +    VK_RIGHT,	SCI_CTRL,	SCI_WORDRIGHT, +    VK_RIGHT,	SCI_CSHIFT,	SCI_WORDRIGHTEXTEND, +    VK_HOME, 	SCI_NORM, 	SCI_VCHOME, +    VK_HOME, 	SCI_SHIFT, 	SCI_VCHOMEEXTEND, +    VK_HOME, 	SCI_CTRL, 	SCI_DOCUMENTSTART, +    VK_HOME, 	SCI_CSHIFT, 	SCI_DOCUMENTSTARTEXTEND, +    VK_END,	 	SCI_NORM, 	SCI_LINEEND, +    VK_END,	 	SCI_SHIFT, 	SCI_LINEENDEXTEND, +    VK_END, 		SCI_CTRL, 	SCI_DOCUMENTEND, +    VK_END, 		SCI_CSHIFT, 	SCI_DOCUMENTENDEXTEND, +    VK_PRIOR,	SCI_NORM, 	SCI_PAGEUP, +    VK_PRIOR,	SCI_SHIFT, 	SCI_PAGEUPEXTEND, +    VK_NEXT, 	SCI_NORM, 	SCI_PAGEDOWN, +    VK_NEXT, 	SCI_SHIFT, 	SCI_PAGEDOWNEXTEND, +    VK_DELETE, 	SCI_NORM,	WM_CLEAR, +    VK_DELETE, 	SCI_SHIFT,	WM_CUT, +    VK_DELETE, 	SCI_CTRL,	SCI_DELWORDRIGHT, +    VK_INSERT, 	SCI_NORM,	SCI_EDITTOGGLEOVERTYPE, +    VK_INSERT, 	SCI_SHIFT,	WM_PASTE, +    VK_INSERT, 	SCI_CTRL,	WM_COPY, +    VK_ESCAPE,  	SCI_NORM,	SCI_CANCEL, +    VK_BACK,		SCI_NORM, 	SCI_DELETEBACK, +    VK_BACK,		SCI_CTRL, 	SCI_DELWORDLEFT, +    'Z', 			SCI_CTRL,	WM_UNDO, +    'Y', 			SCI_CTRL,	SCI_REDO, +    'X', 			SCI_CTRL,	WM_CUT, +    'C', 			SCI_CTRL,	WM_COPY, +    'V', 			SCI_CTRL,	WM_PASTE, +    'A', 			SCI_CTRL,	SCI_SELECTALL, +    VK_TAB,		SCI_NORM,	SCI_TAB, +    VK_TAB,		SCI_SHIFT,	SCI_BACKTAB, +    VK_RETURN, 	SCI_NORM,	SCI_NEWLINE, +    'L', 			SCI_CTRL,	SCI_FORMFEED, +    VK_ADD, 		SCI_CTRL,	SCI_ZOOMIN, +    VK_SUBTRACT,	SCI_CTRL,	SCI_ZOOMOUT, +    0,0,0, +}; + diff --git a/src/KeyMap.h b/src/KeyMap.h new file mode 100644 index 000000000..814f3aa3b --- /dev/null +++ b/src/KeyMap.h @@ -0,0 +1,35 @@ +// Scintilla source code edit control +// KeyMap.h - defines a mapping between keystrokes and commands +// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef KEYTOCOMMAND_H +#define KEYTOCOMMAND_H + +#define SCI_NORM 0 +#define SCI_SHIFT SHIFT_PRESSED +#define SCI_CTRL LEFT_CTRL_PRESSED +#define SCI_ALT LEFT_ALT_PRESSED +#define SCI_CSHIFT (SCI_CTRL | SCI_SHIFT) + +class KeyToCommand { +public: +	int key; +	int modifiers; +	UINT msg; +}; + +class KeyMap { +	KeyToCommand *kmap; +	int len; +	int alloc; +	static KeyToCommand MapDefault[]; +public: +	KeyMap(); +	~KeyMap(); +	void Clear(); +	void AssignCmdKey(int key, int modifiers, UINT msg); +	UINT Find(int key, int modifiers);	// 0 returned on failure +}; + +#endif diff --git a/src/KeyWords.cxx b/src/KeyWords.cxx new file mode 100644 index 000000000..7a6784f73 --- /dev/null +++ b/src/KeyWords.cxx @@ -0,0 +1,2648 @@ +// SciTE - Scintilla based Text Editor +// KeyWords.cxx - colourise for particular languages +// Copyright 1998-2000 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 <ctype.h>  +#include <stdio.h>  +#include <stdarg.h>  + +#include "Platform.h" + +#include "PropSet.h" +#include "Accessor.h" +#include "KeyWords.h" +#include "Scintilla.h" +#include "SciLexer.h" + +inline bool IsLeadByte(int codePage, char ch) { +#if PLAT_GTK +	// TODO: support DBCS under GTK+ +	return false; +#elif PLAT_WIN  +	return codePage && IsDBCSLeadByteEx(codePage, ch); +#elif PLAT_WX  +	return false; +#endif  +} + +inline bool iswordchar(char ch) { +	return isalnum(ch) || ch == '.' || ch == '_'; +} + +inline bool iswordstart(char ch) { +	return isalnum(ch) || ch == '_'; +} + +enum { wsSpace = 1, wsTab = 2, wsSpaceTab = 4, wsInconsistent=8}; + +typedef bool (*PFNIsCommentLeader)(StylingContext &styler, int pos, int len); + +static int IndentAmount(StylingContext &styler, int line, int *flags, PFNIsCommentLeader pfnIsCommentLeader = 0) { +	int end = styler.Length(); +	int spaceFlags = 0; +	 +	// Determines the indentation level of the current line and also checks for consistent  +	// indentation compared to the previous line. +	// Indentation is judged consistent when the indentation whitespace of each line lines  +	// the same or the indentation of one line is a prefix of the other. +	 +	int pos = styler.LineStart(line); +	char ch = styler[pos]; +	int indent = 0; +	bool inPrevPrefix = line > 0; +	int posPrev = inPrevPrefix ? styler.LineStart(line-1) : 0; +	while ((ch == ' ' || ch == '\t') && (pos < end)) { +		if (inPrevPrefix) { +			char chPrev = styler[posPrev++]; +			if (chPrev == ' ' || chPrev == '\t') { +				if (chPrev != ch) +					spaceFlags |= wsInconsistent; +			} else { +				inPrevPrefix = false; +			} +		} +		if (ch == ' ') { +			spaceFlags |= wsSpace; +			indent++; +		} else {	// Tab +			spaceFlags |= wsTab; +			if (spaceFlags & wsSpace) +				spaceFlags |= wsSpaceTab; +			indent = (indent / 8 + 1) * 8; +		} +		ch = styler[++pos]; +	} +	 +	*flags = spaceFlags; +	indent += SC_FOLDLEVELBASE; +	// if completely empty line or the start of a comment... +	if ( isspace(ch) || (pfnIsCommentLeader && (*pfnIsCommentLeader)(styler, pos, end-pos)) ) +		return indent | SC_FOLDLEVELWHITEFLAG; +	else +		return indent; +} + +inline bool isoperator(char ch) { +	if (isalnum(ch)) +		return false; +	// '.' left out as it is used to make up numbers +	if (ch == '%' || ch == '^' || ch == '&' || ch == '*' || +	        ch == '(' || ch == ')' || ch == '-' || ch == '+' || +	        ch == '=' || ch == '|' || ch == '{' || ch == '}' || +	        ch == '[' || ch == ']' || ch == ':' || ch == ';' || +	        ch == '<' || ch == '>' || ch == ',' || ch == '/' || +	        ch == '?' || ch == '!' || ch == '.' || ch == '~') +		return true; +	return false; +} + +static void classifyWordCpp(unsigned int start, unsigned int end, WordList &keywords, StylingContext &styler) { +	char s[100]; +	bool wordIsNumber = isdigit(styler[start]) || (styler[start] == '.'); +	for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) { +		s[i] = styler[start + i]; +		s[i + 1] = '\0'; +	} +	char chAttr = SCE_C_IDENTIFIER; +	if (wordIsNumber) +		chAttr = SCE_C_NUMBER; +	else { +		if (keywords.InList(s)) +			chAttr = SCE_C_WORD; +	} +	styler.ColourSegment(start, end, chAttr); +} + +static void ColouriseCppDoc(int codePage, int startPos, int length, +                            int initStyle, WordList &keywords, StylingContext &styler) { + +	bool fold = styler.GetPropSet().GetInt("fold"); +	int lineCurrent = styler.GetLine(startPos); +	int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK; +	int levelCurrent = levelPrev; + +	int state = initStyle; +	char chPrev = ' '; +	char chNext = styler[startPos]; +	int startSeg = startPos; +	int lengthDoc = startPos + length; +	int visChars = 0; +	for (unsigned int i = startPos; i <= lengthDoc; i++) { +		char ch = chNext; +		chNext = styler.SafeGetCharAt(i + 1); + +		if ((fold) && ((ch == '\r' && chNext != '\n') || (ch == '\n'))) { +			int lev = levelPrev; +			if (visChars == 0) +				lev |= SC_FOLDLEVELWHITEFLAG; +			if ((levelCurrent > levelPrev) && (visChars > 0)) +				lev |= SC_FOLDLEVELHEADERFLAG; +			styler.SetLevel(lineCurrent, lev); +			lineCurrent++; +			visChars = 0; +			levelPrev = levelCurrent; +		} +		if (!isspace(ch)) +			visChars++; + +		if (IsLeadByte(codePage, ch)) {	// dbcs +			chNext = styler.SafeGetCharAt(i + 2); +			chPrev = ' '; +			i += 1; +			continue; +		} + +		if (state == SCE_C_STRINGEOL) { +			if (ch != '\r' && ch != '\n') { +				styler.ColourSegment(startSeg, i - 1, state); +				state = SCE_C_DEFAULT; +				startSeg = i; +			} +		} +		if (state == SCE_C_DEFAULT) { +			if (iswordstart(ch)) { +				styler.ColourSegment(startSeg, i - 1, state); +				state = SCE_C_WORD; +				startSeg = i; +			} else if (ch == '/' && chNext == '*') { +				styler.ColourSegment(startSeg, i - 1, state); +				if (styler.SafeGetCharAt(i + 2) == '*') +					state = SCE_C_COMMENTDOC; +				else +					state = SCE_C_COMMENT; +				startSeg = i; +			} else if (ch == '/' && chNext == '/') { +				styler.ColourSegment(startSeg, i - 1, state); +				state = SCE_C_COMMENTLINE; +				startSeg = i; +			} else if (ch == '\"') { +				styler.ColourSegment(startSeg, i - 1, state); +				state = SCE_C_STRING; +				startSeg = i; +			} else if (ch == '\'') { +				styler.ColourSegment(startSeg, i - 1, state); +				state = SCE_C_CHARACTER; +				startSeg = i; +			} else if (ch == '#') { +				styler.ColourSegment(startSeg, i - 1, state); +				state = SCE_C_PREPROCESSOR; +				startSeg = i; +			} else if (isoperator(ch)) { +				styler.ColourSegment(startSeg, i - 1, state); +				styler.ColourSegment(i, i, SCE_C_OPERATOR); +				startSeg = i + 1; +				if ((ch == '{') || (ch == '}')) { +					levelCurrent += (ch == '{') ? 1 : -1; +				} +			} +		} else if (state == SCE_C_WORD) { +			if (!iswordchar(ch)) { +				classifyWordCpp(startSeg, i - 1, keywords, styler); +				state = SCE_C_DEFAULT; +				startSeg = i; +				if (ch == '/' && chNext == '*') { +					if (styler.SafeGetCharAt(i + 2) == '*') +						state = SCE_C_COMMENTDOC; +					else +						state = SCE_C_COMMENT; +				} else if (ch == '/' && chNext == '/') { +					state = SCE_C_COMMENTLINE; +				} else if (ch == '\"') { +					state = SCE_C_STRING; +				} else if (ch == '\'') { +					state = SCE_C_CHARACTER; +				} else if (ch == '#') { +					state = SCE_C_PREPROCESSOR; +				} else if (isoperator(ch)) { +					styler.ColourSegment(startSeg, i, SCE_C_OPERATOR); +					startSeg = i + 1; +					if ((ch == '{') || (ch == '}')) { +						levelCurrent += (ch == '{') ? 1 : -1; +					} +				} +			} +		} else { +			if (state == SCE_C_PREPROCESSOR) { +				if ((ch == '\r' || ch == '\n') && (chPrev != '\\')) { +					styler.ColourSegment(startSeg, i - 1, state); +					state = SCE_C_DEFAULT; +					startSeg = i; +				} +			} else if (state == SCE_C_COMMENT) { +				if (ch == '/' && chPrev == '*' && ( +				            (i > startSeg + 2) || ((initStyle == SCE_C_COMMENT) && (startSeg == startPos)))) { +					styler.ColourSegment(startSeg, i, state); +					state = SCE_C_DEFAULT; +					startSeg = i + 1; +				} +			} else if (state == SCE_C_COMMENTDOC) { +				if (ch == '/' && chPrev == '*' && ( +				            (i > startSeg + 3) || ((initStyle == SCE_C_COMMENTDOC) && (startSeg == startPos)))) { +					styler.ColourSegment(startSeg, i, state); +					state = SCE_C_DEFAULT; +					startSeg = i + 1; +				} +			} else if (state == SCE_C_COMMENTLINE) { +				if (ch == '\r' || ch == '\n') { +					styler.ColourSegment(startSeg, i - 1, state); +					state = SCE_C_DEFAULT; +					startSeg = i; +				} +			} else if (state == SCE_C_STRING) { +				if ((ch == '\r' || ch == '\n') && (chPrev != '\\')) { +					styler.ColourSegment(startSeg, i - 1, state); +					state = SCE_C_STRINGEOL; +					startSeg = i; +				} else if (ch == '\\') { +					if (chNext == '\"' || chNext == '\'' || chNext == '\\') { +						i++; +						ch = chNext; +						chNext = styler.SafeGetCharAt(i + 1); +					} +				} else if (ch == '\"') { +					styler.ColourSegment(startSeg, i, state); +					state = SCE_C_DEFAULT; +					i++; +					ch = chNext; +					chNext = styler.SafeGetCharAt(i + 1); +					startSeg = i; +				} +			} else if (state == SCE_C_CHARACTER) { +				if ((ch == '\r' || ch == '\n') && (chPrev != '\\')) { +					styler.ColourSegment(startSeg, i - 1, state); +					state = SCE_C_STRINGEOL; +					startSeg = i; +				} else if (ch == '\\') { +					if (chNext == '\"' || chNext == '\'' || chNext == '\\') { +						i++; +						ch = chNext; +						chNext = styler.SafeGetCharAt(i + 1); +					} +				} else if (ch == '\'') { +					styler.ColourSegment(startSeg, i, state); +					state = SCE_C_DEFAULT; +					i++; +					ch = chNext; +					chNext = styler.SafeGetCharAt(i + 1); +					startSeg = i; +				} +			} +			if (state == SCE_C_DEFAULT) {    // One of the above succeeded +				if (ch == '/' && chNext == '*') { +					if (styler.SafeGetCharAt(i + 2) == '*') +						state = SCE_C_COMMENTDOC; +					else +						state = SCE_C_COMMENT; +				} else if (ch == '/' && chNext == '/') { +					state = SCE_C_COMMENTLINE; +				} else if (ch == '\"') { +					state = SCE_C_STRING; +				} else if (ch == '\'') { +					state = SCE_C_CHARACTER; +				} else if (ch == '#') { +					state = SCE_C_PREPROCESSOR; +				} else if (iswordstart(ch)) { +					state = SCE_C_WORD; +				} else if (isoperator(ch)) { +					styler.ColourSegment(startSeg, i, SCE_C_OPERATOR); +					startSeg = i + 1; +					if ((ch == '{') || (ch == '}')) { +						levelCurrent += (ch == '{') ? 1 : -1; +					} +				} +			} +		} +		chPrev = ch; +	} +	if (startSeg < lengthDoc) +		styler.ColourSegment(startSeg, lengthDoc - 1, state); +	// Fill in the real level of the next line, keeping the current flags as they will be filled in later +	if (fold) { +		int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK; +		//styler.SetLevel(lineCurrent, levelCurrent | flagsNext); +		styler.SetLevel(lineCurrent, levelPrev | flagsNext); +		 +	} +} + +inline bool isPerlOperator(char ch) { +	if (isalnum(ch)) +		return false; +	// '.' left out as it is used to make up numbers +	if (ch == '%' || ch == '^' || ch == '&' || ch == '*' || ch == '\\' || +	        ch == '(' || ch == ')' || ch == '-' || ch == '+' || +	        ch == '=' || ch == '|' || ch == '{' || ch == '}' || +	        ch == '[' || ch == ']' || ch == ':' || ch == ';' || +	        ch == '<' || ch == '>' || ch == ',' || ch == '/' || +	        ch == '?' || ch == '!' || ch == '.' || ch == '~') +		return true; +	return false; +} + +static int classifyWordPerl(unsigned int start, unsigned int end, WordList &keywords, StylingContext &styler) { +	char s[100]; +	bool wordIsNumber = isdigit(styler[start]) || (styler[start] == '.'); +	for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) { +		s[i] = styler[start + i]; +		s[i + 1] = '\0'; +	} +	char chAttr = SCE_PL_IDENTIFIER; +	if (wordIsNumber) +		chAttr = SCE_PL_NUMBER; +	else { +		if (keywords.InList(s)) +			chAttr = SCE_PL_WORD; +	} +	styler.ColourSegment(start, end, chAttr); +	return chAttr; +} + +static bool isEndVar(char ch) { +	return !isalnum(ch) && ch != '#' && ch != '$' && +	       ch != '_' && ch != '\''; +} + +static bool isMatch(StylingContext &styler, int lengthDoc, int pos, const char *val) { +	if ((pos + static_cast<int>(strlen(val))) >= lengthDoc) { +		return false; +	} +	while (*val) { +		if (*val != styler[pos++]) { +			return false; +		} +		val++; +	} +	return true; +} + +static bool isOKQuote(char ch) { +	if (isalnum(ch)) +		return false; +	if (isspace(ch)) +		return false; +	if (iscntrl(ch)) +		return false; +	return true; +} + +static char opposite(char ch) { +	if (ch == '(') +		return ')'; +	if (ch == '[') +		return ']'; +	if (ch == '{') +		return '}'; +	if (ch == '<') +		return '>'; +	return ch; +} + +static void ColourisePerlDoc(int codePage, int startPos, int length, int initStyle, +                             WordList &keywords, StylingContext &styler) { +	char sooked[100]; +	int quotes = 0; +	char quoteDown = 'd'; +	char quoteUp = 'd'; +	int quoteRep = 1; +	int sookedpos = 0; +	bool preferRE = true; +	sooked[sookedpos] = '\0'; +	int state = initStyle; +	int lengthDoc = startPos + length; +	// If in a long distance lexical state, seek to the beginning  to find quote characters +	if (state == SCE_PL_HERE || state == SCE_PL_REGEX ||  +		state == SCE_PL_REGSUBST || state == SCE_PL_LONGQUOTE) { +		while ((startPos > 1) && (styler.StyleAt(startPos - 1) == state)) { +			startPos--; +		} +		state = SCE_PL_DEFAULT; +	} +	styler.StartAt(startPos); +	char chPrev = ' '; +	char chNext = styler[startPos]; +	int startSeg = startPos; +	for (int i = startPos; i <= lengthDoc; i++) { +		char ch = chNext; +		chNext = styler.SafeGetCharAt(i + 1); +		char chNext2 = styler.SafeGetCharAt(i + 2); + +		if (IsLeadByte(codePage, ch)) {	// dbcs +			chNext = styler.SafeGetCharAt(i + 2); +			chPrev = ' '; +			i += 1; +			continue; +		} + +		if (state == SCE_PL_DEFAULT) { +			if (iswordstart(ch)) { +				styler.ColourSegment(startSeg, i - 1, state); +				if (ch == 's' && !isalnum(chNext)) { +					state = SCE_PL_REGSUBST; +					quotes = 0; +					quoteUp = '\0'; +					quoteDown = '\0'; +					quoteRep = 2; +					startSeg = i; +				} else if (ch == 'm' && !isalnum(chNext)) { +					state = SCE_PL_REGEX; +					quotes = 0; +					quoteUp = '\0'; +					quoteDown = '\0'; +					quoteRep = 1; +					startSeg = i; +				} else if (ch == 't' && chNext == 'r' && !isalnum(chNext2)) { +					state = SCE_PL_REGSUBST; +					quotes = 0; +					quoteUp = '\0'; +					quoteDown = '\0'; +					quoteRep = 2; +					startSeg = i; +					i++; +					chNext = chNext2; +				} else if (ch == 'q' && (chNext == 'q' || chNext == 'r' || chNext == 'w' || chNext == 'x') && !isalnum(chNext2)) { +					state = SCE_PL_LONGQUOTE; +					startSeg = i; +					i++; +					chNext = chNext2; +					quotes = 0; +					quoteUp = '\0'; +					quoteDown = '\0'; +					quoteRep = 1; +				} else { +					state = SCE_PL_WORD; +					startSeg = i; +					preferRE = false; +				} +			} else if (ch == '#') { +				styler.ColourSegment(startSeg, i - 1, state); +				state = SCE_PL_COMMENTLINE; +				startSeg = i; +			} else if (ch == '\"') { +				styler.ColourSegment(startSeg, i - 1, state); +				state = SCE_PL_STRING; +				startSeg = i; +			} else if (ch == '\'') { +				if (chPrev == '&') { +					// Archaic call +					styler.ColourSegment(i, i, state); +					startSeg = i + 1; +				} else { +					styler.ColourSegment(startSeg, i - 1, state); +					state = SCE_PL_CHARACTER; +					startSeg = i; +				} +			} else if (ch == '`') { +				styler.ColourSegment(startSeg, i - 1, state); +				state = SCE_PL_BACKTICKS; +				startSeg = i; +			} else if (ch == '$') { +				preferRE = false; +				styler.ColourSegment(startSeg, i - 1, state); +				if (isalnum(chNext) || chNext == '#' || chNext == '$' || chNext == '_') { +					state = SCE_PL_SCALAR; +					startSeg = i; +				} else if (chNext != '{' && chNext != '[') { +					styler.ColourSegment(i - 1, i, SCE_PL_SCALAR); +					i++; +					startSeg = i + 1; +					ch = ' '; +					chNext = ' '; +				} else { +					styler.ColourSegment(i, i, SCE_PL_SCALAR); +					startSeg = i + 1; +				} +			} else if (ch == '@') { +				preferRE = false; +				styler.ColourSegment(startSeg, i - 1, state); +				if (isalpha(chNext) || chNext == '#' || chNext == '$' || chNext == '_') { +					state = SCE_PL_ARRAY; +					startSeg = i; +				} else if (chNext != '{' && chNext != '[') { +					styler.ColourSegment(i - 1, i, SCE_PL_ARRAY); +					i++; +					startSeg = i + 1; +					ch = ' '; +				} else { +					styler.ColourSegment(i, i, SCE_PL_ARRAY); +					startSeg = i + 1; +				} +			} else if (ch == '%') { +				preferRE = false; +				styler.ColourSegment(startSeg, i - 1, state); +				if (isalpha(chNext) || chNext == '#' || chNext == '$' || chNext == '_') { +					state = SCE_PL_HASH; +					startSeg = i; +				} else if (chNext != '{' && chNext != '[') { +					styler.ColourSegment(i - 1, i, SCE_PL_HASH); +					i++; +					startSeg = i + 1; +					ch = ' '; +				} else { +					styler.ColourSegment(i, i, SCE_PL_HASH); +					startSeg = i + 1; +				} +			} else if (ch == '*') { +				styler.ColourSegment(startSeg, i - 1, state); +				state = SCE_PL_SYMBOLTABLE; +				startSeg = i; +			} else if (ch == '/' && preferRE) { +				styler.ColourSegment(startSeg, i - 1, state); +				state = SCE_PL_REGEX; +				quoteUp = '/'; +				quoteDown = '/'; +				quotes = 1; +				quoteRep = 1; +				startSeg = i; +			} else if (ch == '<' && chNext == '<') { +				styler.ColourSegment(startSeg, i - 1, state); +				state = SCE_PL_HERE; +				startSeg = i; +				i++; +				ch = chNext; +				chNext = chNext2; +				quotes = 0; +				sookedpos = 0; +				sooked[sookedpos] = '\0'; +			} else if (ch == '=' && isalpha(chNext)) { +				styler.ColourSegment(startSeg, i - 1, state); +				state = SCE_PL_POD; +				startSeg = i; +				quotes = 0; +				sookedpos = 0; +				sooked[sookedpos] = '\0'; +			} else if (isPerlOperator(ch)) { +				if (ch == ')' || ch == ']') +					preferRE = false; +				else +					preferRE = true; +				styler.ColourSegment(startSeg, i - 1, state); +				styler.ColourSegment(i, i, SCE_PL_OPERATOR); +				startSeg = i + 1; +			} +		} else if (state == SCE_PL_WORD) { +			if (!iswordchar(ch) && ch != '\'') {	// Archaic Perl has quotes inside names +				if (isMatch(styler, lengthDoc, startSeg, "__DATA__")) { +					styler.ColourSegment(startSeg, i, SCE_PL_DATASECTION); +					state = SCE_PL_DATASECTION; +				} else if (isMatch(styler, lengthDoc, startSeg, "__END__")) { +					styler.ColourSegment(startSeg, i, SCE_PL_DATASECTION); +					state = SCE_PL_DATASECTION; +				} else { +					if (classifyWordPerl(startSeg, i - 1, keywords, styler) == SCE_PL_WORD) +						preferRE = true; +					state = SCE_PL_DEFAULT; +					startSeg = i; +					if (ch == '#') { +						state = SCE_PL_COMMENTLINE; +					} else if (ch == '\"') { +						state = SCE_PL_STRING; +					} else if (ch == '\'') { +						state = SCE_PL_CHARACTER; +					} else if (ch == '<' && chNext == '<') { +						state = SCE_PL_HERE; +						quotes = 0; +						startSeg = i; +						sookedpos = 0; +						sooked[sookedpos] = '\0'; +					} else if (isPerlOperator(ch)) { +						if (ch == ')' || ch == ']') +							preferRE = false; +						else +							preferRE = true; +						styler.ColourSegment(startSeg, i, SCE_PL_OPERATOR); +						state = SCE_PL_DEFAULT; +						startSeg = i + 1; +					} +				} +			} +		} else { +			if (state == SCE_PL_COMMENTLINE) { +				if (ch == '\r' || ch == '\n') { +					styler.ColourSegment(startSeg, i - 1, state); +					state = SCE_PL_DEFAULT; +					startSeg = i; +				} +			} else if (state == SCE_PL_HERE) { +				if (isalnum(ch) && quotes < 2) { +					sooked[sookedpos++] = ch; +					sooked[sookedpos] = '\0'; +					if (quotes == 0) +						quotes = 1; +				} else { +					quotes++; +				} + +				if (quotes > 1 && isMatch(styler, lengthDoc, i, sooked)) { +					styler.ColourSegment(startSeg, i + sookedpos - 1, SCE_PL_HERE); +					state = SCE_PL_DEFAULT; +					i += sookedpos; +					startSeg = i; +					chNext = ' '; +				} +			} else if (state == SCE_PL_STRING) { +				if (ch == '\\') { +					if (chNext == '\"' || chNext == '\'' || chNext == '\\') { +						i++; +						ch = chNext; +						chNext = styler.SafeGetCharAt(i + 1); +					} +				} else if (ch == '\"') { +					styler.ColourSegment(startSeg, i, state); +					state = SCE_PL_DEFAULT; +					i++; +					ch = chNext; +					chNext = styler.SafeGetCharAt(i + 1); +					startSeg = i; +				} +			} else if (state == SCE_PL_CHARACTER) { +				if (ch == '\\') { +					if (chNext == '\"' || chNext == '\'' || chNext == '\\') { +						i++; +						ch = chNext; +						chNext = styler.SafeGetCharAt(i + 1); +					} +				} else if (ch == '\'') { +					styler.ColourSegment(startSeg, i, state); +					state = SCE_PL_DEFAULT; +					i++; +					ch = chNext; +					chNext = styler.SafeGetCharAt(i + 1); +					startSeg = i; +				} +			} else if (state == SCE_PL_BACKTICKS) { +				if (ch == '`') { +					styler.ColourSegment(startSeg, i, state); +					state = SCE_PL_DEFAULT; +					i++; +					ch = chNext; +					chNext = styler.SafeGetCharAt(i + 1); +					startSeg = i; +				} +			} else if (state == SCE_PL_POD) { +				if (ch == '=') { +					if (isMatch(styler, lengthDoc, i, "=cut")) { +						styler.ColourSegment(startSeg, i - 1 + 4, state); +						i += 4; +						startSeg = i; +						state = SCE_PL_DEFAULT; +						chNext = ' '; +						ch = ' '; +					} +				} +			} else if (state == SCE_PL_SCALAR) { +				if (isEndVar(ch)) { +					styler.ColourSegment(startSeg, i - 1, state); +					startSeg = i; +					state = SCE_PL_DEFAULT; +				} +			} else if (state == SCE_PL_ARRAY) { +				if (isEndVar(ch)) { +					styler.ColourSegment(startSeg, i - 1, state); +					startSeg = i; +					state = SCE_PL_DEFAULT; +				} +			} else if (state == SCE_PL_HASH) { +				if (isEndVar(ch)) { +					styler.ColourSegment(startSeg, i - 1, state); +					startSeg = i; +					state = SCE_PL_DEFAULT; +				} +			} else if (state == SCE_PL_SYMBOLTABLE) { +				if (isEndVar(ch)) { +					styler.ColourSegment(startSeg, i - 1, state); +					startSeg = i; +					state = SCE_PL_DEFAULT; +				} +			} else if (state == SCE_PL_REF) { +				if (isEndVar(ch)) { +					styler.ColourSegment(startSeg, i - 1, state); +					startSeg = i; +					state = SCE_PL_DEFAULT; +				} +			} else if (state == SCE_PL_REGEX) { +				if (!quoteUp && !isspace(ch)) { +					quoteUp = ch; +					quoteDown = opposite(ch); +					quotes++; +				} else { +					if (ch == quoteDown && chPrev != '\\') { +						quotes--; +						if (quotes == 0) { +							quoteRep--; +							if (quoteUp == quoteDown) { +								quotes++; +							} +						} +						if (!isalpha(chNext)) { +							if (quoteRep <= 0) { +								styler.ColourSegment(startSeg, i, state); +								startSeg = i + 1; +								state = SCE_PL_DEFAULT; +								ch = ' '; +							} +						} +					} else if (ch == quoteUp && chPrev != '\\') { +						quotes++; +					} else if (!isalpha(chNext)) { +						if (quoteRep <= 0) { +							styler.ColourSegment(startSeg, i, state); +							startSeg = i + 1; +							state = SCE_PL_DEFAULT; +							ch = ' '; +						} +					} +				} +			} else if (state == SCE_PL_REGSUBST) { +				if (!quoteUp && !isspace(ch)) { +					quoteUp = ch; +					quoteDown = opposite(ch); +					quotes++; +				} else { +					if (ch == quoteDown && chPrev != '\\') { +						quotes--; +						if (quotes == 0) { +							quoteRep--; +						} +						if (!isalpha(chNext)) { +							if (quoteRep <= 0) { +								styler.ColourSegment(startSeg, i, state); +								startSeg = i + 1; +								state = SCE_PL_DEFAULT; +								ch = ' '; +							} +						} +						if (quoteUp == quoteDown) { +							quotes++; +						} +					} else if (ch == quoteUp && chPrev != '\\') { +						quotes++; +					} else if (!isalpha(chNext)) { +						if (quoteRep <= 0) { +							styler.ColourSegment(startSeg, i, state); +							startSeg = i + 1; +							state = SCE_PL_DEFAULT; +							ch = ' '; +						} +					} +				} +			} else if (state == SCE_PL_LONGQUOTE) { +				if (!quoteDown && !isspace(ch)) { +					quoteUp = ch; +					quoteDown = opposite(quoteUp); +					quotes++; +				} else if (ch == quoteDown) { +					quotes--; +					if (quotes == 0) { +						quoteRep--; +						if (quoteRep <= 0) { +							styler.ColourSegment(startSeg, i, state); +							startSeg = i + 1; +							state = SCE_PL_DEFAULT; +							ch = ' '; +						} +						if (quoteUp == quoteDown) { +							quotes++; +						} +					} +				} else if (ch == quoteUp) { +					quotes++; +				} +			} + +			if (state == SCE_PL_DEFAULT) {    // One of the above succeeded +				if (ch == '#') { +					state = SCE_PL_COMMENTLINE; +				} else if (ch == '\"') { +					state = SCE_PL_STRING; +				} else if (ch == '\'') { +					state = SCE_PL_CHARACTER; +				} else if (iswordstart(ch)) { +					state = SCE_PL_WORD; +					preferRE = false; +				} else if (isoperator(ch)) { +					styler.ColourSegment(startSeg, i, SCE_PL_OPERATOR); +					startSeg = i + 1; +				} +			} +		} +		chPrev = ch; +	} +	if (startSeg < lengthDoc) +		styler.ColourSegment(startSeg, lengthDoc, state); +} + + +static int classifyWordVB(unsigned int start, unsigned int end, WordList &keywords, StylingContext &styler) { +	char s[100]; +	bool wordIsNumber = isdigit(styler[start]) || (styler[start] == '.'); +	for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) { +		s[i] = tolower(styler[start + i]); +		s[i + 1] = '\0'; +	} +	char chAttr = SCE_C_DEFAULT; +	if (wordIsNumber) +		chAttr = SCE_C_NUMBER; +	else { +		if (keywords.InList(s)) { +			chAttr = SCE_C_WORD; +			if (strcmp(s, "rem") == 0) +				chAttr = SCE_C_COMMENTLINE; +		} +	} +	styler.ColourSegment(start, end, chAttr); +	if (chAttr == SCE_C_COMMENTLINE) +		return SCE_C_COMMENTLINE; +	else +		return SCE_C_DEFAULT; +} + +static void ColouriseVBDoc(int codePage, int startPos, int length, int initStyle, +                           WordList &keywords, StylingContext &styler) { +	int state = initStyle; +	char chNext = styler[startPos]; +	int startSeg = startPos; +	int lengthDoc = startPos + length; +	for (int i = startPos; i < lengthDoc; i++) { +		char ch = chNext; +		chNext = styler.SafeGetCharAt(i + 1); + +		if (IsLeadByte(codePage, ch)) {	// dbcs +			chNext = styler.SafeGetCharAt(i + 2); +			i += 1; +			continue; +		} + +		if (state == SCE_C_DEFAULT) { +			if (iswordstart(ch)) { +				styler.ColourSegment(startSeg, i - 1, state); +				state = SCE_C_WORD; +				startSeg = i; +			} else if (ch == '\'') { +				styler.ColourSegment(startSeg, i - 1, state); +				state = SCE_C_COMMENTLINE; +				startSeg = i; +			} else if (ch == '\"') { +				styler.ColourSegment(startSeg, i - 1, state); +				state = SCE_C_STRING; +				startSeg = i; +			} +		} else if (state == SCE_C_WORD) { +			if (!iswordchar(ch)) { +				state = classifyWordVB(startSeg, i - 1, keywords, styler); +				if (state == SCE_C_DEFAULT) { +					startSeg = i; +					if (ch == '\'') { +						state = SCE_C_COMMENTLINE; +					} else if (ch == '\"') { +						state = SCE_C_STRING; +					} +				} +			} +		} else { +			if (state == SCE_C_COMMENTLINE) { +				if (ch == '\r' || ch == '\n') { +					styler.ColourSegment(startSeg, i - 1, state); +					state = SCE_C_DEFAULT; +					startSeg = i; +				} +			} else if (state == SCE_C_STRING) { +				// VB doubles quotes to preserve them +				if (ch == '\"') { +					styler.ColourSegment(startSeg, i, state); +					state = SCE_C_DEFAULT; +					i++; +					ch = chNext; +					chNext = styler.SafeGetCharAt(i + 1); +					startSeg = i; +				} +			} +			if (state == SCE_C_DEFAULT) {    // One of the above succeeded +				if (ch == '\'') { +					state = SCE_C_COMMENTLINE; +				} else if (ch == '\"') { +					state = SCE_C_STRING; +				} else if (iswordstart(ch)) { +					state = SCE_C_WORD; +				} +			} +		} +	} +	if (startSeg < lengthDoc) +		styler.ColourSegment(startSeg, lengthDoc, state); +} + +static void classifyWordPy(unsigned int start, unsigned int end, WordList &keywords, StylingContext &styler, char *prevWord) { +	char s[100]; +	bool wordIsNumber = isdigit(styler[start]); +	for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) { +		s[i] = styler[start + i]; +		s[i + 1] = '\0'; +	} +	char chAttr = SCE_P_IDENTIFIER; +	if (0 == strcmp(prevWord, "class")) +		chAttr = SCE_P_CLASSNAME; +	else if (0 == strcmp(prevWord, "def")) +		chAttr = SCE_P_DEFNAME; +	else if (wordIsNumber) +		chAttr = SCE_P_NUMBER; +	else if (keywords.InList(s)) +		chAttr = SCE_P_WORD; +	styler.ColourSegment(start, end, chAttr); +	strcpy(prevWord, s); +} + +static bool IsPyComment(StylingContext &styler, int pos, int len) { +	return len>0 && styler[pos]=='#'; +} + +static void ColourisePyDoc(int codePage, int startPos, int length, int initStyle, WordList &keywords, StylingContext &styler) { +	//Platform::DebugPrintf("Python coloured\n"); +	bool fold = styler.GetPropSet().GetInt("fold"); +	int whingeLevel = styler.GetPropSet().GetInt("tab.timmy.whinge.level"); +	char prevWord[200]; +	prevWord[0] = '\0'; +	if (length == 0) +		return ; +	int lineCurrent = styler.GetLine(startPos); +	int spaceFlags = 0; +	// TODO: Need to check previous line for indentation for both folding and bad indentation +	int indentCurrent = IndentAmount(styler, lineCurrent, &spaceFlags, IsPyComment); + +	int state = initStyle & 31; +	char chPrev = ' '; +	char chPrev2 = ' '; +	char chNext = styler[startPos]; +	char chNext2 = styler[startPos]; +	int startSeg = startPos; +	int lengthDoc = startPos + length; +	bool atStartLine = true; +	for (int i = startPos; i <= lengthDoc; i++) { +	 +		if (atStartLine) { +			if (whingeLevel == 1) { +				styler.SetFlags((spaceFlags & wsInconsistent) ? 64 : 0, state); +			} else if (whingeLevel == 2) { +				styler.SetFlags((spaceFlags & wsSpaceTab) ? 64 : 0, state); +			} else if (whingeLevel == 3) { +				styler.SetFlags((spaceFlags & wsSpace) ? 64 : 0, state); +			} else if (whingeLevel == 4) { +				styler.SetFlags((spaceFlags & wsTab) ? 64 : 0, state); +			} +			atStartLine = false; +		} +		 +		char ch = chNext; +		chNext = styler.SafeGetCharAt(i + 1); +		chNext2 = styler.SafeGetCharAt(i + 2); +		 +		if ((ch == '\r' && chNext != '\n') || (ch == '\n')) { +			if ((state == SCE_P_DEFAULT) || (state == SCE_P_TRIPLE) || (state == SCE_P_TRIPLEDOUBLE)) { +				// Perform colourisation of white space and triple quoted strings at end of each line to allow +				// tab marking to work inside white space and triple quoted strings +				styler.ColourSegment(startSeg, i, state); +				startSeg = i + 1; +			} + +			int lev = indentCurrent; +			int indentNext = IndentAmount(styler, lineCurrent + 1, &spaceFlags, IsPyComment); +			if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG)) { +				// Only non whitespace lines can be headers +				if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext & SC_FOLDLEVELNUMBERMASK)) { +					lev |= SC_FOLDLEVELHEADERFLAG; +				} +			} +			indentCurrent = indentNext; +			if (fold) { +				styler.SetLevel(lineCurrent, lev); +			} +			lineCurrent++; +			atStartLine = true; +		} + +		if (IsLeadByte(codePage, ch)) {	// dbcs +			chNext = styler.SafeGetCharAt(i + 2); +			chPrev = ' '; +			chPrev2 = ' '; +			i += 1; +			continue; +		} + +		if (state == SCE_P_STRINGEOL) { +			if (ch != '\r' && ch != '\n') { +				styler.ColourSegment(startSeg, i - 1, state); +				state = SCE_P_DEFAULT; +				startSeg = i; +			} +		} +		if (state == SCE_P_DEFAULT) { +			if (iswordstart(ch)) { +				styler.ColourSegment(startSeg, i - 1, state); +				state = SCE_P_WORD; +				startSeg = i; +			} else if (ch == '#') { +				styler.ColourSegment(startSeg, i - 1, state); +				state = chNext == '#' ? SCE_P_COMMENTBLOCK : SCE_P_COMMENTLINE; +				startSeg = i; +			} else if (ch == '\"') { +				styler.ColourSegment(startSeg, i - 1, state); +				startSeg = i; +				if (chNext == '\"' && chNext2 == '\"') { +					i += 2; +					state = SCE_P_TRIPLEDOUBLE; +					ch = ' '; +					chPrev = ' '; +					chNext = styler.SafeGetCharAt(i + 1); +				} else { +					state = SCE_P_STRING; +				} +			} else if (ch == '\'') { +				styler.ColourSegment(startSeg, i - 1, state); +				startSeg = i; +				if (chNext == '\'' && chNext2 == '\'') { +					i += 2; +					state = SCE_P_TRIPLE; +					ch = ' '; +					chPrev = ' '; +					chNext = styler.SafeGetCharAt(i + 1); +				} else { +					state = SCE_P_CHARACTER; +				} +			} else if (isoperator(ch)) { +				styler.ColourSegment(startSeg, i - 1, state); +				styler.ColourSegment(i, i, SCE_P_OPERATOR); +				startSeg = i + 1; +			} +		} else if (state == SCE_P_WORD) { +			if (!iswordchar(ch)) { +				classifyWordPy(startSeg, i - 1, keywords, styler, prevWord); +				state = SCE_P_DEFAULT; +				startSeg = i; +				if (ch == '#') { +					state = chNext == '#' ? SCE_P_COMMENTBLOCK : SCE_P_COMMENTLINE; +				} else if (ch == '\"') { +					if (chNext == '\"' && chNext2 == '\"') { +						i += 2; +						state = SCE_P_TRIPLEDOUBLE; +						ch = ' '; +						chPrev = ' '; +						chNext = styler.SafeGetCharAt(i + 1); +					} else { +						state = SCE_P_STRING; +					} +				} else if (ch == '\'') { +					if (chNext == '\'' && chNext2 == '\'') { +						i += 2; +						state = SCE_P_TRIPLE; +						ch = ' '; +						chPrev = ' '; +						chNext = styler.SafeGetCharAt(i + 1); +					} else { +						state = SCE_P_CHARACTER; +					} +				} else if (isoperator(ch)) { +					styler.ColourSegment(startSeg, i, SCE_P_OPERATOR); +					startSeg = i + 1; +				} +			} +		} else { +			if (state == SCE_P_COMMENTLINE || state == SCE_P_COMMENTBLOCK) { +				if (ch == '\r' || ch == '\n') { +					styler.ColourSegment(startSeg, i - 1, state); +					state = SCE_P_DEFAULT; +					startSeg = i; +				} +			} else if (state == SCE_P_STRING) { +				if ((ch == '\r' || ch == '\n') && (chPrev != '\\')) { +					styler.ColourSegment(startSeg, i - 1, state); +					state = SCE_P_STRINGEOL; +					startSeg = i; +				} else if (ch == '\\') { +					if (chNext == '\"' || chNext == '\'' || chNext == '\\') { +						i++; +						ch = chNext; +						chNext = styler.SafeGetCharAt(i + 1); +					} +				} else if (ch == '\"') { +					styler.ColourSegment(startSeg, i, state); +					state = SCE_P_DEFAULT; +					startSeg = i + 1; +				} +			} else if (state == SCE_P_CHARACTER) { +				if ((ch == '\r' || ch == '\n') && (chPrev != '\\')) { +					styler.ColourSegment(startSeg, i - 1, state); +					state = SCE_P_STRINGEOL; +					startSeg = i; +				} else if (ch == '\\') { +					if (chNext == '\"' || chNext == '\'' || chNext == '\\') { +						i++; +						ch = chNext; +						chNext = styler.SafeGetCharAt(i + 1); +					} +				} else if (ch == '\'') { +					styler.ColourSegment(startSeg, i, state); +					state = SCE_P_DEFAULT; +					startSeg = i + 1; +				} +			} else if (state == SCE_P_TRIPLE) { +				if (ch == '\'' && chPrev == '\'' && chPrev2 == '\'') { +					styler.ColourSegment(startSeg, i, state); +					state = SCE_P_DEFAULT; +					startSeg = i + 1; +				} +			} else if (state == SCE_P_TRIPLEDOUBLE) { +				if (ch == '\"' && chPrev == '\"' && chPrev2 == '\"') { +					styler.ColourSegment(startSeg, i, state); +					state = SCE_P_DEFAULT; +					startSeg = i + 1; +				} +			} +		} +		chPrev2 = chPrev; +		chPrev = ch; +	} +	if (startSeg <= lengthDoc) { +		if (state == SCE_P_DEFAULT) { +			styler.ColourSegment(startSeg, lengthDoc, state); +		} else if (state == SCE_P_WORD) { +			classifyWordPy(startSeg, lengthDoc, keywords, styler, prevWord); +		} else if (state == SCE_P_COMMENTLINE) { +			styler.ColourSegment(startSeg, lengthDoc, state); +		} else if (state == SCE_P_COMMENTBLOCK) { +			styler.ColourSegment(startSeg, lengthDoc, state); +		} else if (state == SCE_P_STRING) { +			styler.ColourSegment(startSeg, lengthDoc, state); +		} else if (state == SCE_P_CHARACTER) { +			styler.ColourSegment(startSeg, lengthDoc, state); +		} else if (state == SCE_P_TRIPLE) { +			styler.ColourSegment(startSeg, lengthDoc, state); +		} else if (state == SCE_P_TRIPLEDOUBLE) { +			styler.ColourSegment(startSeg, lengthDoc, state); +		} +	} +} + +static void ColouriseBatchLine(char *lineBuffer, int lengthLine, StylingContext &styler) { +	if (0 == strncmp(lineBuffer, "REM", 3)) { +		styler.ColourSegment(0, lengthLine - 1, 1); +	} else if (0 == strncmp(lineBuffer, "rem", 3)) { +		styler.ColourSegment(0, lengthLine - 1, 1); +	} else if (0 == strncmp(lineBuffer, "SET", 3)) { +		styler.ColourSegment(0, lengthLine - 1, 2); +	} else if (0 == strncmp(lineBuffer, "set", 3)) { +		styler.ColourSegment(0, lengthLine - 1, 2); +	} else if (lineBuffer[0] == ':') { +		styler.ColourSegment(0, lengthLine - 1, 3); +	} else { +		styler.ColourSegment(0, lengthLine - 1, 0); +	} +} + +static void ColouriseBatchDoc(int startPos, int length, int, StylingContext &styler) { +	char lineBuffer[1024]; +	unsigned int linePos = 0; +	for (int i = startPos; i < startPos + length; i++) { +		lineBuffer[linePos++] = styler[i]; +		if (styler[i] == '\r' || styler[i] == '\n' || (linePos >= sizeof(lineBuffer) - 1)) { +			ColouriseBatchLine(lineBuffer, linePos, styler); +			linePos = 0; +		} +	} +	if (linePos > 0) +		ColouriseBatchLine(lineBuffer, linePos, styler); +} + +enum { eScriptNone, eScriptJS, eScriptVBS, eScriptPython }; +static int segIsScriptingIndicator(StylingContext &styler, unsigned int start, unsigned int end, int prevValue) { +	char s[100]; +	s[0] = '\0'; +	for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) { +		s[i] = tolower(styler[start + i]); +		s[i + 1] = '\0'; +	} +Platform::DebugPrintf("Scripting indicator [%s]\n", s); +	if (strstr(s, "vbs")) +		return eScriptVBS; +	if (strstr(s, "pyth")) +		return eScriptPython; +	if (strstr(s, "javas")) +		return eScriptJS; +	if (strstr(s, "jscr")) +		return eScriptJS; +		 +	return prevValue; +} + +static void classifyAttribHTML(unsigned int start, unsigned int end, WordList &keywords, StylingContext &styler) { +	bool wordIsNumber = isdigit(styler[start]) || (styler[start] == '.') || +	                    (styler[start] == '-') || (styler[start] == '#'); +	char chAttr = SCE_H_ATTRIBUTEUNKNOWN; +	if (wordIsNumber) { +		chAttr = SCE_H_NUMBER; +	} else { +		char s[100]; +		s[0] = '\0'; +		for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) { +			s[i] = tolower(styler[start + i]); +			s[i + 1] = '\0'; +		} +		if (keywords.InList(s)) +			chAttr = SCE_H_ATTRIBUTE; +	} +	styler.ColourTo(end, chAttr); +} + +static int classifyTagHTML(unsigned int start, unsigned int end, +                         WordList &keywords, StylingContext &styler) { +	char s[100]; +	// Copy after the '<' +	unsigned int i = 0; +	for (int cPos=start; cPos <= end && i < 30; cPos++) { +		char ch = styler[cPos]; +		if (ch != '<') +			s[i++] = tolower(ch); +	} +	s[i] = '\0'; +	char chAttr = SCE_H_TAGUNKNOWN; +	if (s[0] == '!' && s[1] == '-' && s[2] == '-') {	//Comment +		chAttr = SCE_H_COMMENT; +	} else if (s[0] == '/') {	// Closing tag +		if (keywords.InList(s + 1)) +			chAttr = SCE_H_TAG; +	} else { +		if (keywords.InList(s)) { +			chAttr = SCE_H_TAG; +			if (0 == strcmp(s, "script")) +				chAttr = SCE_H_SCRIPT; +		} +	} +	styler.ColourTo(end, chAttr); +	return chAttr; +} + +static void classifyWordHTJS(unsigned int start, unsigned int end, +                             WordList &keywords, StylingContext &styler) { +	char s[100]; +	bool wordIsNumber = isdigit(styler[start]) || (styler[start] == '.'); +	for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) { +		s[i] = styler[start + i]; +		s[i + 1] = '\0'; +	} +	char chAttr = SCE_HJ_WORD; +	if (wordIsNumber) +		chAttr = SCE_HJ_NUMBER; +	else { +		if (keywords.InList(s)) +			chAttr = SCE_HJ_KEYWORD; +	} +	styler.ColourTo(end, chAttr); +} + +static void classifyWordHTJSA(unsigned int start, unsigned int end, +                             WordList &keywords, StylingContext &styler) { +	char s[100]; +	bool wordIsNumber = isdigit(styler[start]) || (styler[start] == '.'); +	for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) { +		s[i] = styler[start + i]; +		s[i + 1] = '\0'; +	} +	char chAttr = SCE_HJA_WORD; +	if (wordIsNumber) +		chAttr = SCE_HJA_NUMBER; +	else { +		if (keywords.InList(s)) +			chAttr = SCE_HJA_KEYWORD; +	} +	styler.ColourTo(end, chAttr); +} + +static int classifyWordHTVB(unsigned int start, unsigned int end, WordList &keywords, StylingContext &styler) { +	char s[100]; +	bool wordIsNumber = isdigit(styler[start]) || (styler[start] == '.'); +	for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) { +		s[i] = tolower(styler[start + i]); +		s[i + 1] = '\0'; +	} +	char chAttr = SCE_HB_IDENTIFIER; +	if (wordIsNumber) +		chAttr = SCE_HB_NUMBER; +	else { +		if (keywords.InList(s)) { +			chAttr = SCE_HB_WORD; +			if (strcmp(s, "rem") == 0) +				chAttr = SCE_HB_COMMENTLINE; +		} +	} +	styler.ColourTo(end, chAttr); +	if (chAttr == SCE_HB_COMMENTLINE) +		return SCE_HB_COMMENTLINE; +	else +		return SCE_HB_DEFAULT; +} + +static int classifyWordHTVBA(unsigned int start, unsigned int end, WordList &keywords, StylingContext &styler) { +	char s[100]; +	bool wordIsNumber = isdigit(styler[start]) || (styler[start] == '.'); +	for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) { +		s[i] = tolower(styler[start + i]); +		s[i + 1] = '\0'; +	} +	char chAttr = SCE_HBA_IDENTIFIER; +	if (wordIsNumber) +		chAttr = SCE_HBA_NUMBER; +	else { +		if (keywords.InList(s)) { +			chAttr = SCE_HBA_WORD; +			if (strcmp(s, "rem") == 0) +				chAttr = SCE_HBA_COMMENTLINE; +		} +	} +	styler.ColourTo(end, chAttr); +	if (chAttr == SCE_HBA_COMMENTLINE) +		return SCE_HBA_COMMENTLINE; +	else +		return SCE_HBA_DEFAULT; +} + +static void classifyWordHTPy(unsigned int start, unsigned int end, WordList &keywords, StylingContext &styler, char *prevWord) { +	char s[100]; +	bool wordIsNumber = isdigit(styler[start]); +	for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) { +		s[i] = styler[start + i]; +		s[i + 1] = '\0'; +	} +	char chAttr = SCE_HP_IDENTIFIER; +	if (0 == strcmp(prevWord, "class")) +		chAttr = SCE_HP_CLASSNAME; +	else if (0 == strcmp(prevWord, "def")) +		chAttr = SCE_HP_DEFNAME; +	else if (wordIsNumber) +		chAttr = SCE_HP_NUMBER; +	else if (keywords.InList(s)) +		chAttr = SCE_HP_WORD; +	styler.ColourTo(end, chAttr); +	strcpy(prevWord, s); +} + +static void classifyWordHTPyA(unsigned int start, unsigned int end, WordList &keywords, StylingContext &styler, char *prevWord) { +	char s[100]; +	bool wordIsNumber = isdigit(styler[start]); +	for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) { +		s[i] = styler[start + i]; +		s[i + 1] = '\0'; +	} +	char chAttr = SCE_HPA_IDENTIFIER; +	if (0 == strcmp(prevWord, "class")) +		chAttr = SCE_HPA_CLASSNAME; +	else if (0 == strcmp(prevWord, "def")) +		chAttr = SCE_HPA_DEFNAME; +	else if (wordIsNumber) +		chAttr = SCE_HPA_NUMBER; +	else if (keywords.InList(s)) +		chAttr = SCE_HPA_WORD; +	styler.ColourTo(end, chAttr); +	strcpy(prevWord, s); +} + +inline bool ishtmlwordchar(char ch) { +	return isalnum(ch) || ch == '.' || ch == '-' || ch == '_' || ch == ':' || ch == '!' || ch == '#'; +} + +static bool InTagState(int state) { +	return state == SCE_H_TAG || state == SCE_H_TAGUNKNOWN || +		state == SCE_H_SCRIPT || +		state == SCE_H_ATTRIBUTE || state == SCE_H_ATTRIBUTEUNKNOWN || +		state == SCE_H_NUMBER || state == SCE_H_OTHER || +		state == SCE_H_DOUBLESTRING || state == SCE_H_SINGLESTRING; +} + +static bool isLineEnd(char ch) { +	return ch == '\r' || ch == '\n'; +} + +static void ColouriseHyperTextDoc(int codePage, int startPos, int length, +                                  int initStyle, WordList &keywords, WordList &keywords2, WordList &keywords3, WordList &keywords4,  +				  StylingContext &styler) { +	 +	styler.StartAt(startPos, 127); +	bool lastTagWasScript = false; +	char prevWord[200]; +	prevWord[0] = '\0'; +	int scriptLanguage = eScriptJS; +	int state = initStyle; +	// If inside a tag, it may be a script tage, so reread from the start to ensure any language tas are seen +	if (InTagState(state)) { +		while ((startPos > 1) && (InTagState(styler.StyleAt(startPos - 1)))) { +			startPos--; +		} +		state = SCE_H_DEFAULT; +	} +	styler.StartAt(startPos, 127); + +	int lineState = eScriptVBS; +	int lineCurrent = styler.GetLine(startPos); +	if (lineCurrent > 0) +		lineState = styler.GetLineState(lineCurrent); +	int defaultScript = lineState &0xff; +	int beforeASP = (lineState >> 8) &0xff; +	int inASP = (lineState >> 16) &0xff; +	 +	char chPrev = ' '; +	char chPrev2 = ' '; +	styler.StartSegment(startPos); +	int lengthDoc = startPos + length; +	for (int i = startPos; i <= lengthDoc; i++) { +		char ch = styler[i]; +		char chNext = styler.SafeGetCharAt(i + 1); +		char chNext2 = styler.SafeGetCharAt(i + 2); + +		if (IsLeadByte(codePage, ch)) {	// dbcs +			chPrev2 = ' '; +			chPrev = ' '; +			i += 1; +			continue; +		} + +		if ((ch == '\r' && chNext != '\n') || (ch == '\n')) { +			// New line -> record any line state onto /next/ line +			lineCurrent++; +			styler.SetLineState(lineCurrent,  +				defaultScript | (beforeASP << 8) | (inASP << 16)); +		} +		 +		// Handle ASP even within other constructs as it is a preprocessor +		if ((ch == '<') && (chNext == '%')) { +			beforeASP = state; +			styler.ColourTo(i - 1, state); +			if (chNext2 == '@') { +				styler.ColourTo(i + 2, SCE_H_ASP); +				state = SCE_H_ASPAT; +				i+=2; +			} else { +				if (defaultScript == eScriptVBS) +					state = SCE_HBA_START; +				else if  (defaultScript == eScriptPython) +				 	state = SCE_HPA_START; +				else +					state = SCE_HJA_START; +				if (chNext2 == '=') { +					styler.ColourTo(i + 2, SCE_H_ASP); +					i+=2; +				} else { +					styler.ColourTo(i + 1, SCE_H_ASP); +					i++; +				} +			} +			inASP = 1; +			continue; +		} +		if (inASP && (ch == '%') && (chNext == '>')) { +			if (state == SCE_H_ASPAT) +				defaultScript = segIsScriptingIndicator(styler, styler.GetStartSegment(), i-1, defaultScript); +			// Bounce out of any ASP mode +			if (state == SCE_HJA_WORD) { +				classifyWordHTJSA(styler.GetStartSegment(), i - 1, keywords2, styler); +			} else if (state == SCE_HBA_WORD) { +				classifyWordHTVBA(styler.GetStartSegment(), i - 1, keywords3, styler); +			} else if (state == SCE_HPA_WORD) { +				classifyWordHTPyA(styler.GetStartSegment(), i - 1, keywords4, styler, prevWord); +			} else { +				styler.ColourTo(i - 1, state); +			} +			//if (state == SCE_H_ASPAT) +			//	styler.ColourTo(i+1, SCE_H_ASPAT); +			//else +				styler.ColourTo(i+1, SCE_H_ASP); +			i++; +			state = beforeASP; +			beforeASP = SCE_H_DEFAULT; +			inASP = 0; +			continue; +		} +		 +		if (state == SCE_H_DEFAULT) { +			if (ch == '<') { +				styler.ColourTo(i - 1, state); +				state = SCE_H_TAGUNKNOWN; +				if (chNext == '?') { +					styler.ColourTo(i + 1, SCE_H_XMLSTART); +					i++; +					ch = chNext; +				} +			} else if (ch == '&') { +				styler.ColourTo(i - 1, SCE_H_DEFAULT); +				state = SCE_H_ENTITY; +			} +		} else if (state == SCE_H_COMMENT) { +			if ((ch == '>') && (chPrev == '-')) { +				styler.ColourTo(i, state); +				state = SCE_H_DEFAULT; +			} +		} else if (state == SCE_H_ENTITY) { +			if (ch == ';') { +				styler.ColourTo(i, state); +				state = SCE_H_DEFAULT; +			} +		} else if (state == SCE_H_TAGUNKNOWN) { +			if (!ishtmlwordchar(ch) && ch != '/' && ch != '-') { +				int eClass = classifyTagHTML(styler.GetStartSegment(), i - 1, keywords, styler); +				lastTagWasScript = eClass == SCE_H_SCRIPT; +				if (lastTagWasScript) { +					scriptLanguage = eScriptJS; +					eClass = SCE_H_TAG; +				} +				if (ch == '>') { +					styler.ColourTo(i, SCE_H_TAG); +					if (lastTagWasScript) { +						if (scriptLanguage == eScriptVBS) +							state = SCE_HB_START; +						else if  (scriptLanguage == eScriptPython) +						 	state = SCE_HP_START; +						else +							state = SCE_HJ_START; +					} else { +						state = SCE_H_DEFAULT; +					} +				} else { +					if (eClass == SCE_H_COMMENT) { +						state = SCE_H_COMMENT; +					} else { +						state = SCE_H_OTHER; +					} +				} +			} +		} else if (state == SCE_H_ATTRIBUTE) { +			if (!ishtmlwordchar(ch) && ch != '/' && ch != '-') { +				if (lastTagWasScript) +					scriptLanguage = segIsScriptingIndicator(styler, styler.GetStartSegment(), i-1, scriptLanguage); +				classifyAttribHTML(styler.GetStartSegment(), i - 1, keywords, styler); +				if (ch == '>') { +					styler.ColourTo(i, SCE_H_TAG); +					if (lastTagWasScript) { +						if (scriptLanguage == eScriptVBS) +							state = SCE_HB_START; +						else if  (scriptLanguage == eScriptPython) +						 	state = SCE_HP_START; +						else +							state = SCE_HJ_START; +					} else { +						state = SCE_H_DEFAULT; +					} +				} else { +					state = SCE_H_OTHER; +				} +			} +		} else if (state == SCE_H_ASP) { +			if ((ch == '>') && (chPrev == '%')) { +				styler.ColourTo(i, state); +				state = SCE_H_DEFAULT; +			} +		} else if (state == SCE_H_ASPAT) { +			if ((ch == '>') && (chPrev == '%')) { +				styler.ColourTo(i, state); +				state = SCE_H_DEFAULT; +			} +		} else if (state == SCE_H_OTHER) { +			if (ch == '>') { +				styler.ColourTo(i - 1, state); +				styler.ColourTo(i, SCE_H_TAG); +				if (lastTagWasScript) { +					if (scriptLanguage == eScriptVBS) +						state = SCE_HB_START; +					else if  (scriptLanguage == eScriptPython) +					 	state = SCE_HP_START; +					else +						state = SCE_HJ_START; +				} else { +					state = SCE_H_DEFAULT; +				} +			} else if (ch == '\"') { +				styler.ColourTo(i - 1, state); +				state = SCE_H_DOUBLESTRING; +			} else if (ch == '\'') { +				styler.ColourTo(i - 1, state); +				state = SCE_H_SINGLESTRING; +			} else if (ch == '/' && chNext == '>') { +				styler.ColourTo(i - 1, state); +				styler.ColourTo(i + 1, SCE_H_TAGEND); +				i++; +				ch = chNext; +				state = SCE_H_DEFAULT; +			} else if (ch == '?' && chNext == '>') { +				styler.ColourTo(i - 1, state); +				styler.ColourTo(i + 1, SCE_H_XMLEND); +				i++; +				ch = chNext; +				state = SCE_H_DEFAULT; +			} else if (ishtmlwordchar(ch)) { +				styler.ColourTo(i - 1, state); +				state = SCE_H_ATTRIBUTE; +			} +		} else if (state == SCE_H_DOUBLESTRING) { +			if (ch == '\"') { +				if (lastTagWasScript) +					scriptLanguage = segIsScriptingIndicator(styler, styler.GetStartSegment(), i, scriptLanguage); +				styler.ColourTo(i, SCE_H_DOUBLESTRING); +				state = SCE_H_OTHER; +			} +		} else if (state == SCE_H_SINGLESTRING) { +			if (ch == '\'') { +				if (lastTagWasScript) +					scriptLanguage = segIsScriptingIndicator(styler, styler.GetStartSegment(), i, scriptLanguage); +				styler.ColourTo(i, SCE_H_SINGLESTRING); +				state = SCE_H_OTHER; +			} +		} else if (state == SCE_HJ_DEFAULT || state == SCE_HJ_START) { +			if (iswordstart(ch)) { +				styler.ColourTo(i - 1, state); +				state = SCE_HJ_WORD; +			} else if (ch == '/' && chNext == '*') { +				styler.ColourTo(i - 1, state); +				if (chNext2 == '*') +					state = SCE_HJ_COMMENTDOC; +				else +					state = SCE_HJ_COMMENT; +			} else if (ch == '/' && chNext == '/') { +				styler.ColourTo(i - 1, state); +				state = SCE_HJ_COMMENTLINE; +			} else if (ch == '\"') { +				styler.ColourTo(i - 1, state); +				state = SCE_HJ_DOUBLESTRING; +			} else if (ch == '\'') { +				styler.ColourTo(i - 1, state); +				state = SCE_HJ_SINGLESTRING; +			} else if ((ch == '<') && (chNext == '/')) { +				styler.ColourTo(i - 1, state); +				state = SCE_H_TAGUNKNOWN; +			} else if ((ch == '<') && (chNext == '!') && (chNext2 == '-') && +				styler.SafeGetCharAt(i + 3) == '-') { +				styler.ColourTo(i - 1, state); +				state = SCE_HJ_COMMENTLINE; +			} else if (isoperator(ch)) { +				styler.ColourTo(i - 1, state); +				styler.ColourTo(i, SCE_HJ_SYMBOLS); +				state = SCE_HJ_DEFAULT; +			} else if ((ch == ' ') || (ch == '\t')) { +				if (state == SCE_HJ_START) { +					styler.ColourTo(i - 1, state); +					state = SCE_HJ_DEFAULT; +				} +			} +		} else if (state == SCE_HJ_WORD) { +			if (!iswordchar(ch)) { +				classifyWordHTJS(styler.GetStartSegment(), i - 1, keywords2, styler); +				//styler.ColourTo(i - 1, eHTJSKeyword); +				state = SCE_HJ_DEFAULT; +				if (ch == '/' && chNext == '*') { +					if (chNext2 == '*') +						state = SCE_HJ_COMMENTDOC; +					else +						state = SCE_HJ_COMMENT; +				} else if (ch == '/' && chNext == '/') { +					state = SCE_HJ_COMMENTLINE; +				} else if (ch == '\"') { +					state = SCE_HJ_DOUBLESTRING; +				} else if (ch == '\'') { +					state = SCE_HJ_SINGLESTRING; +				} else if (isoperator(ch)) { +					styler.ColourTo(i, SCE_HJ_SYMBOLS); +					state = SCE_HJ_DEFAULT; +				} +			} +		} else if (state == SCE_HJ_COMMENT) { +			if (ch == '/' && chPrev == '*') { +				state = SCE_HJ_DEFAULT; +				styler.ColourTo(i, SCE_HJ_COMMENT); +			} else if ((ch == '<') && (chNext == '/')) { +				styler.ColourTo(i - 1, state); +				styler.ColourTo(i + 1, SCE_H_TAGEND); +				i++; +				ch = chNext; +				state = SCE_H_DEFAULT; +			} +		} else if (state == SCE_HJ_COMMENTDOC) { +			if (ch == '/' && chPrev == '*') { +				state = SCE_HJ_DEFAULT; +				styler.ColourTo(i, SCE_HJ_COMMENTDOC); +			} else if ((ch == '<') && (chNext == '/')) { +				styler.ColourTo(i - 1, state); +				styler.ColourTo(i + 1, SCE_H_TAGEND); +				i++; +				ch = chNext; +				state = SCE_H_DEFAULT; +			} +		} else if (state == SCE_HJ_COMMENTLINE) { +			if (ch == '\r' || ch == '\n') { +				styler.ColourTo(i - 1, SCE_HJ_COMMENTLINE); +				state = SCE_HJ_DEFAULT; +			} else if ((ch == '<') && (chNext == '/')) { +				// Common to hide end script tag in comment +				styler.ColourTo(i - 1, SCE_HJ_COMMENTLINE); +				state = SCE_H_TAGUNKNOWN; +			} +		} else if (state == SCE_HJ_DOUBLESTRING) { +			if (ch == '\\') { +				if (chNext == '\"' || chNext == '\'' || chNext == '\\') { +					i++; +				} +			} else if (ch == '\"') { +				styler.ColourTo(i, SCE_HJ_DOUBLESTRING); +				state = SCE_HJ_DEFAULT; +				i++; +				ch = chNext; +			} else if (isLineEnd(ch)) { +				styler.ColourTo(i-1, state); +				state = SCE_HJ_STRINGEOL; +			} +		} else if (state == SCE_HJ_SINGLESTRING) { +			if (ch == '\\') { +				if (chNext == '\"' || chNext == '\'' || chNext == '\\') { +					i++; +				} +			} else if (ch == '\'') { +				styler.ColourTo(i, SCE_HJ_SINGLESTRING); +				state = SCE_HJ_DEFAULT; +				i++; +				ch = chNext; +			} else if (isLineEnd(ch)) { +				styler.ColourTo(i-1, state); +				state = SCE_HJ_STRINGEOL; +			} +		} else if (state == SCE_HJ_STRINGEOL) { +			if (!isLineEnd(ch)) { +				styler.ColourTo(i - 1, state); +				state = SCE_HJ_DEFAULT; +			} else if (!isLineEnd(chNext)) { +				styler.ColourTo(i, state); +				state = SCE_HJ_DEFAULT; +			} +		} else if (state == SCE_HJA_DEFAULT || state == SCE_HJA_START) { +			if (iswordstart(ch)) { +				styler.ColourTo(i - 1, state); +				state = SCE_HJA_WORD; +			} else if (ch == '/' && chNext == '*') { +				styler.ColourTo(i - 1, state); +				if (chNext2 == '*') +					state = SCE_HJA_COMMENTDOC; +				else +					state = SCE_HJA_COMMENT; +			} else if (ch == '/' && chNext == '/') { +				styler.ColourTo(i - 1, state); +				state = SCE_HJA_COMMENTLINE; +			} else if (ch == '\"') { +				styler.ColourTo(i - 1, state); +				state = SCE_HJA_DOUBLESTRING; +			} else if (ch == '\'') { +				styler.ColourTo(i - 1, state); +				state = SCE_HJA_SINGLESTRING; +			} else if ((ch == '<') && (chNext == '/')) { +				styler.ColourTo(i - 1, state); +				state = SCE_H_TAGUNKNOWN; +			} else if ((ch == '<') && (chNext == '!') && (chNext2 == '-') && +				styler.SafeGetCharAt(i + 3) == '-') { +				styler.ColourTo(i - 1, state); +				state = SCE_HJA_COMMENTLINE; +			} else if (isoperator(ch)) { +				styler.ColourTo(i - 1, state); +				styler.ColourTo(i, SCE_HJA_SYMBOLS); +				state = SCE_HJA_DEFAULT; +			} else if ((ch == ' ') || (ch == '\t')) { +				if (state == SCE_HJA_START) { +					styler.ColourTo(i - 1, state); +					state = SCE_HJA_DEFAULT; +				} +			} +		} else if (state == SCE_HJA_WORD) { +			if (!iswordchar(ch)) { +				classifyWordHTJSA(styler.GetStartSegment(), i - 1, keywords2, styler); +				//styler.ColourTo(i - 1, eHTJSKeyword); +				state = SCE_HJA_DEFAULT; +				if (ch == '/' && chNext == '*') { +					if (chNext2 == '*') +						state = SCE_HJA_COMMENTDOC; +					else +						state = SCE_HJA_COMMENT; +				} else if (ch == '/' && chNext == '/') { +					state = SCE_HJA_COMMENTLINE; +				} else if (ch == '\"') { +					state = SCE_HJA_DOUBLESTRING; +				} else if (ch == '\'') { +					state = SCE_HJA_SINGLESTRING; +				} else if (isoperator(ch)) { +					styler.ColourTo(i, SCE_HJA_SYMBOLS); +					state = SCE_HJA_DEFAULT; +				} +			} +		} else if (state == SCE_HJA_COMMENT) { +			if (ch == '/' && chPrev == '*') { +				state = SCE_HJA_DEFAULT; +				styler.ColourTo(i, SCE_HJA_COMMENT); +			} else if ((ch == '<') && (chNext == '/')) { +				styler.ColourTo(i - 1, state); +				styler.ColourTo(i + 1, SCE_H_TAGEND); +				i++; +				ch = chNext; +				state = SCE_H_DEFAULT; +			} +		} else if (state == SCE_HJA_COMMENTDOC) { +			if (ch == '/' && chPrev == '*') { +				state = SCE_HJA_DEFAULT; +				styler.ColourTo(i, SCE_HJA_COMMENTDOC); +			} else if ((ch == '<') && (chNext == '/')) { +				styler.ColourTo(i - 1, state); +				styler.ColourTo(i + 1, SCE_H_TAGEND); +				i++; +				ch = chNext; +				state = SCE_H_DEFAULT; +			} +		} else if (state == SCE_HJA_COMMENTLINE) { +			if (ch == '\r' || ch == '\n') { +				styler.ColourTo(i - 1, SCE_HJA_COMMENTLINE); +				state = SCE_HJA_DEFAULT; +			} else if ((ch == '<') && (chNext == '/')) { +				// Common to hide end script tag in comment +				styler.ColourTo(i - 1, SCE_HJA_COMMENTLINE); +				state = SCE_H_TAGUNKNOWN; +			} +		} else if (state == SCE_HJA_DOUBLESTRING) { +			if (ch == '\\') { +				if (chNext == '\"' || chNext == '\'' || chNext == '\\') { +					i++; +				} +			} else if (ch == '\"') { +				styler.ColourTo(i, SCE_HJA_DOUBLESTRING); +				state = SCE_HJA_DEFAULT; +				i++; +				ch = chNext; +			} else if (isLineEnd(ch)) { +				styler.ColourTo(i-1, state); +				state = SCE_HJA_STRINGEOL; +			} +		} else if (state == SCE_HJA_SINGLESTRING) { +			if (ch == '\\') { +				if (chNext == '\"' || chNext == '\'' || chNext == '\\') { +					i++; +				} +			} else if (ch == '\'') { +				styler.ColourTo(i, SCE_HJA_SINGLESTRING); +				state = SCE_HJA_DEFAULT; +				i++; +				ch = chNext; +			} else if (isLineEnd(ch)) { +				styler.ColourTo(i-1, state); +				state = SCE_HJA_STRINGEOL; +			} +		} else if (state == SCE_HJA_STRINGEOL) { +			if (!isLineEnd(ch)) { +				styler.ColourTo(i - 1, state); +				state = SCE_HJA_DEFAULT; +			} else if (!isLineEnd(chNext)) { +				styler.ColourTo(i, state); +				state = SCE_HJA_DEFAULT; +			} +		} else if (state == SCE_HB_DEFAULT || state == SCE_HB_START) { +			if (iswordstart(ch)) { +				styler.ColourTo(i - 1, state); +				state = SCE_HB_WORD; +			} else if (ch == '\'') { +				styler.ColourTo(i - 1, state); +				state = SCE_HB_COMMENTLINE; +			} else if (ch == '\"') { +				styler.ColourTo(i - 1, state); +				state = SCE_HB_STRING; +			} else if ((ch == '<') && (chNext == '/')) { +				styler.ColourTo(i - 1, state); +				state = SCE_H_TAGUNKNOWN; +			} else if ((ch == '<') && (chNext == '!') && (chNext2 == '-') && +				styler.SafeGetCharAt(i + 3) == '-') { +				styler.ColourTo(i - 1, state); +				state = SCE_HB_COMMENTLINE; +			} else if (isoperator(ch)) { +				styler.ColourTo(i - 1, state); +				styler.ColourTo(i, SCE_HB_DEFAULT); +				state = SCE_HB_DEFAULT; +			} else if ((ch == ' ') || (ch == '\t')) { +				if (state == SCE_HB_START) { +					styler.ColourTo(i - 1, state); +					state = SCE_HB_DEFAULT; +				} +			} +		} else if (state == SCE_HB_WORD) { +			if (!iswordchar(ch)) { +				state = classifyWordHTVB(styler.GetStartSegment(), i - 1, keywords3, styler); +				if (state == SCE_HB_DEFAULT) { +					if (ch == '\"') { +						state = SCE_HB_STRING; +					} else if (ch == '\'') { +						state = SCE_HB_COMMENTLINE; +					} else if (isoperator(ch)) { +						styler.ColourTo(i, SCE_HB_DEFAULT); +						state = SCE_HB_DEFAULT; +					} +				} +			} +		} else if (state == SCE_HB_STRING) { +			if (ch == '\"') { +				styler.ColourTo(i, state); +				state = SCE_HB_DEFAULT; +				i++; +				ch = chNext; +			} else if (ch == '\r' || ch == '\n') { +				styler.ColourTo(i-1, state); +				state = SCE_HB_STRINGEOL; +			} +		} else if (state == SCE_HB_COMMENTLINE) { +			if (ch == '\r' || ch == '\n') { +				styler.ColourTo(i - 1, state); +				state = SCE_HB_DEFAULT; +			} else if ((ch == '<') && (chNext == '/')) { +				// Common to hide end script tag in comment +				styler.ColourTo(i - 1, state); +				state = SCE_H_TAGUNKNOWN; +			} +		} else if (state == SCE_HB_STRINGEOL) { +			if (!isLineEnd(ch)) { +				styler.ColourTo(i - 1, state); +				state = SCE_HB_DEFAULT; +			} else if (!isLineEnd(chNext)) { +				styler.ColourTo(i, state); +				state = SCE_HB_DEFAULT; +			} +		} else if (state == SCE_HBA_DEFAULT || state == SCE_HBA_START) { +			if (iswordstart(ch)) { +				styler.ColourTo(i - 1, state); +				state = SCE_HBA_WORD; +			} else if (ch == '\'') { +				styler.ColourTo(i - 1, state); +				state = SCE_HBA_COMMENTLINE; +			} else if (ch == '\"') { +				styler.ColourTo(i - 1, state); +				state = SCE_HBA_STRING; +			} else if ((ch == '<') && (chNext == '/')) { +				styler.ColourTo(i - 1, state); +				state = SCE_H_TAGUNKNOWN; +			} else if ((ch == '<') && (chNext == '!') && (chNext2 == '-') && +				styler.SafeGetCharAt(i + 3) == '-') { +				styler.ColourTo(i - 1, state); +				state = SCE_HBA_COMMENTLINE; +			} else if (isoperator(ch)) { +				styler.ColourTo(i - 1, state); +				styler.ColourTo(i, SCE_HBA_DEFAULT); +				state = SCE_HBA_DEFAULT; +			} else if ((ch == ' ') || (ch == '\t')) { +				if (state == SCE_HBA_START) { +					styler.ColourTo(i - 1, state); +					state = SCE_HBA_DEFAULT; +				} +			} +		} else if (state == SCE_HBA_WORD) { +			if (!iswordchar(ch)) { +				state = classifyWordHTVBA(styler.GetStartSegment(), i - 1, keywords3, styler); +				if (state == SCE_HBA_DEFAULT) { +					if (ch == '\"') { +						state = SCE_HBA_STRING; +					} else if (ch == '\'') { +						state = SCE_HBA_COMMENTLINE; +					} else if (isoperator(ch)) { +						styler.ColourTo(i, SCE_HBA_DEFAULT); +						state = SCE_HBA_DEFAULT; +					} +				} +			} +		} else if (state == SCE_HBA_STRING) { +			if (ch == '\"') { +				styler.ColourTo(i, state); +				state = SCE_HBA_DEFAULT; +				i++; +				ch = chNext; +			} else if (ch == '\r' || ch == '\n') { +				styler.ColourTo(i-1, state); +				state = SCE_HBA_STRINGEOL; +			} +		} else if (state == SCE_HBA_COMMENTLINE) { +			if (ch == '\r' || ch == '\n') { +				styler.ColourTo(i - 1, state); +				state = SCE_HBA_DEFAULT; +			} else if ((ch == '<') && (chNext == '/')) { +				// Common to hide end script tag in comment +				styler.ColourTo(i - 1, state); +				state = SCE_H_TAGUNKNOWN; +			} +		} else if (state == SCE_HBA_STRINGEOL) { +			if (!isLineEnd(ch)) { +				styler.ColourTo(i - 1, state); +				state = SCE_HBA_DEFAULT; +			} else if (!isLineEnd(chNext)) { +				styler.ColourTo(i, state); +				state = SCE_HBA_DEFAULT; +			} +		} else if (state == SCE_HP_DEFAULT || state == SCE_HP_START) { +			if (iswordstart(ch)) { +				styler.ColourTo(i - 1, state); +				state = SCE_HP_WORD; +			} else if ((ch == '<') && (chNext == '/')) { +				styler.ColourTo(i - 1, state); +				state = SCE_H_TAGUNKNOWN; +			} else if ((ch == '<') && (chNext == '!') && (chNext2 == '-') && +				styler.SafeGetCharAt(i + 3) == '-') { +				styler.ColourTo(i - 1, state); +				state = SCE_HP_COMMENTLINE; +			} else if (ch == '#') { +				styler.ColourTo(i - 1, state); +				state = SCE_HP_COMMENTLINE; +			} else if (ch == '\"') { +				styler.ColourTo(i - 1, state); +				if (chNext == '\"' && chNext2 == '\"') { +					i += 2; +					state = SCE_HP_TRIPLEDOUBLE; +					ch = ' '; +					chPrev = ' '; +					chNext = styler.SafeGetCharAt(i + 1); +				} else { +					state = SCE_HP_STRING; +				} +			} else if (ch == '\'') { +				styler.ColourTo(i - 1, state); +				if (chNext == '\'' && chNext2 == '\'') { +					i += 2; +					state = SCE_HP_TRIPLE; +					ch = ' '; +					chPrev = ' '; +					chNext = styler.SafeGetCharAt(i + 1); +				} else { +					state = SCE_HP_CHARACTER; +				} +			} else if (isoperator(ch)) { +				styler.ColourTo(i - 1, state); +				styler.ColourTo(i, SCE_HP_OPERATOR); +			} else if ((ch == ' ') || (ch == '\t')) { +				if (state == SCE_HP_START) { +					styler.ColourTo(i - 1, state); +					state = SCE_HP_DEFAULT; +				} +			} +		} else if (state == SCE_HP_WORD) { +			if (!iswordchar(ch)) { +				classifyWordHTPy(styler.GetStartSegment(), i - 1, keywords4, styler, prevWord); +				state = SCE_HP_DEFAULT; +				if (ch == '#') { +					state = SCE_HP_COMMENTLINE; +				} else if (ch == '\"') { +					if (chNext == '\"' && chNext2 == '\"') { +						i += 2; +						state = SCE_HP_TRIPLEDOUBLE; +						ch = ' '; +						chPrev = ' '; +						chNext = styler.SafeGetCharAt(i + 1); +					} else { +						state = SCE_HP_STRING; +					} +				} else if (ch == '\'') { +					if (chNext == '\'' && chNext2 == '\'') { +						i += 2; +						state = SCE_HP_TRIPLE; +						ch = ' '; +						chPrev = ' '; +						chNext = styler.SafeGetCharAt(i + 1); +					} else { +						state = SCE_HP_CHARACTER; +					} +				} else if (isoperator(ch)) { +					styler.ColourTo(i, SCE_HP_OPERATOR); +				} +			} +		} else if (state == SCE_HP_COMMENTLINE) { +			if (ch == '\r' || ch == '\n') { +				styler.ColourTo(i - 1, state); +				state = SCE_HP_DEFAULT; +			} +		} else if (state == SCE_HP_STRING) { +			if (ch == '\\') { +				if (chNext == '\"' || chNext == '\'' || chNext == '\\') { +					i++; +					ch = chNext; +					chNext = styler.SafeGetCharAt(i + 1); +				} +			} else if (ch == '\"') { +				styler.ColourTo(i, state); +				state = SCE_HP_DEFAULT; +			} +		} else if (state == SCE_HP_CHARACTER) { +			if (ch == '\\') { +				if (chNext == '\"' || chNext == '\'' || chNext == '\\') { +					i++; +					ch = chNext; +					chNext = styler.SafeGetCharAt(i + 1); +				} +			} else if (ch == '\'') { +				styler.ColourTo(i, state); +				state = SCE_HP_DEFAULT; +			} +		} else if (state == SCE_HP_TRIPLE) { +			if (ch == '\'' && chPrev == '\'' && chPrev2 == '\'') { +				styler.ColourTo(i, state); +				state = SCE_HP_DEFAULT; +			} +		} else if (state == SCE_HP_TRIPLEDOUBLE) { +			if (ch == '\"' && chPrev == '\"' && chPrev2 == '\"') { +				styler.ColourTo(i, state); +				state = SCE_HP_DEFAULT; +			} +		} else if (state == SCE_HPA_DEFAULT || state == SCE_HPA_START) { +			if (iswordstart(ch)) { +				styler.ColourTo(i - 1, state); +				state = SCE_HPA_WORD; +			} else if ((ch == '<') && (chNext == '/')) { +				styler.ColourTo(i - 1, state); +				state = SCE_H_TAGUNKNOWN; +			} else if ((ch == '<') && (chNext == '!') && (chNext2 == '-') && +				styler.SafeGetCharAt(i + 3) == '-') { +				styler.ColourTo(i - 1, state); +				state = SCE_HPA_COMMENTLINE; +			} else if (ch == '#') { +				styler.ColourTo(i - 1, state); +				state = SCE_HPA_COMMENTLINE; +			} else if (ch == '\"') { +				styler.ColourTo(i - 1, state); +				if (chNext == '\"' && chNext2 == '\"') { +					i += 2; +					state = SCE_HPA_TRIPLEDOUBLE; +					ch = ' '; +					chPrev = ' '; +					chNext = styler.SafeGetCharAt(i + 1); +				} else { +					state = SCE_HPA_STRING; +				} +			} else if (ch == '\'') { +				styler.ColourTo(i - 1, state); +				if (chNext == '\'' && chNext2 == '\'') { +					i += 2; +					state = SCE_HPA_TRIPLE; +					ch = ' '; +					chPrev = ' '; +					chNext = styler.SafeGetCharAt(i + 1); +				} else { +					state = SCE_HPA_CHARACTER; +				} +			} else if (isoperator(ch)) { +				styler.ColourTo(i - 1, state); +				styler.ColourTo(i, SCE_HPA_OPERATOR); +			} else if ((ch == ' ') || (ch == '\t')) { +				if (state == SCE_HPA_START) { +					styler.ColourTo(i - 1, state); +					state = SCE_HPA_DEFAULT; +				} +			} +		} else if (state == SCE_HPA_WORD) { +			if (!iswordchar(ch)) { +				classifyWordHTPyA(styler.GetStartSegment(), i - 1, keywords4, styler, prevWord); +				state = SCE_HPA_DEFAULT; +				if (ch == '#') { +					state = SCE_HPA_COMMENTLINE; +				} else if (ch == '\"') { +					if (chNext == '\"' && chNext2 == '\"') { +						i += 2; +						state = SCE_HPA_TRIPLEDOUBLE; +						ch = ' '; +						chPrev = ' '; +						chNext = styler.SafeGetCharAt(i + 1); +					} else { +						state = SCE_HPA_STRING; +					} +				} else if (ch == '\'') { +					if (chNext == '\'' && chNext2 == '\'') { +						i += 2; +						state = SCE_HPA_TRIPLE; +						ch = ' '; +						chPrev = ' '; +						chNext = styler.SafeGetCharAt(i + 1); +					} else { +						state = SCE_HPA_CHARACTER; +					} +				} else if (isoperator(ch)) { +					styler.ColourTo(i, SCE_HPA_OPERATOR); +				} +			} +		} else if (state == SCE_HPA_COMMENTLINE) { +			if (ch == '\r' || ch == '\n') { +				styler.ColourTo(i - 1, state); +				state = SCE_HPA_DEFAULT; +			} +		} else if (state == SCE_HPA_STRING) { +			if (ch == '\\') { +				if (chNext == '\"' || chNext == '\'' || chNext == '\\') { +					i++; +					ch = chNext; +					chNext = styler.SafeGetCharAt(i + 1); +				} +			} else if (ch == '\"') { +				styler.ColourTo(i, state); +				state = SCE_HPA_DEFAULT; +			} +		} else if (state == SCE_HPA_CHARACTER) { +			if (ch == '\\') { +				if (chNext == '\"' || chNext == '\'' || chNext == '\\') { +					i++; +					ch = chNext; +					chNext = styler.SafeGetCharAt(i + 1); +				} +			} else if (ch == '\'') { +				styler.ColourTo(i, state); +				state = SCE_HPA_DEFAULT; +			} +		} else if (state == SCE_HPA_TRIPLE) { +			if (ch == '\'' && chPrev == '\'' && chPrev2 == '\'') { +				styler.ColourTo(i, state); +				state = SCE_HPA_DEFAULT; +			} +		} else if (state == SCE_HPA_TRIPLEDOUBLE) { +			if (ch == '\"' && chPrev == '\"' && chPrev2 == '\"') { +				styler.ColourTo(i, state); +				state = SCE_HPA_DEFAULT; +			} +		} +		if (state == SCE_HB_DEFAULT) {    // One of the above succeeded +			if (ch == '\"') { +				state = SCE_HB_STRING; +			} else if (ch == '\'') { +				state = SCE_HB_COMMENTLINE; +			} else if (iswordstart(ch)) { +				state = SCE_HB_WORD; +			} else if (isoperator(ch)) { +				styler.ColourTo(i, SCE_HB_DEFAULT); +			} +		} +		if (state == SCE_HBA_DEFAULT) {    // One of the above succeeded +			if (ch == '\"') { +				state = SCE_HBA_STRING; +			} else if (ch == '\'') { +				state = SCE_HBA_COMMENTLINE; +			} else if (iswordstart(ch)) { +				state = SCE_HBA_WORD; +			} else if (isoperator(ch)) { +				styler.ColourTo(i, SCE_HBA_DEFAULT); +			} +		} +		if (state == SCE_HJ_DEFAULT) {    // One of the above succeeded +			if (ch == '/' && chNext == '*') { +				if (styler.SafeGetCharAt(i + 2) == '*') +					state = SCE_HJ_COMMENTDOC; +				else +					state = SCE_HJ_COMMENT; +			} else if (ch == '/' && chNext == '/') { +				state = SCE_HJ_COMMENTLINE; +			} else if (ch == '\"') { +				state = SCE_HJ_DOUBLESTRING; +			} else if (ch == '\'') { +				state = SCE_HJ_SINGLESTRING; +			} else if (iswordstart(ch)) { +				state = SCE_HJ_WORD; +			} else if (isoperator(ch)) { +				styler.ColourTo(i, SCE_HJ_SYMBOLS); +			} +		} +		if (state == SCE_HJA_DEFAULT) {    // One of the above succeeded +			if (ch == '/' && chNext == '*') { +				if (styler.SafeGetCharAt(i + 2) == '*') +					state = SCE_HJA_COMMENTDOC; +				else +					state = SCE_HJA_COMMENT; +			} else if (ch == '/' && chNext == '/') { +				state = SCE_HJA_COMMENTLINE; +			} else if (ch == '\"') { +				state = SCE_HJA_DOUBLESTRING; +			} else if (ch == '\'') { +				state = SCE_HJA_SINGLESTRING; +			} else if (iswordstart(ch)) { +				state = SCE_HJA_WORD; +			} else if (isoperator(ch)) { +				styler.ColourTo(i, SCE_HJA_SYMBOLS); +			} +		} +		chPrev2 = chPrev; +		chPrev = ch; +	} +	styler.ColourTo(lengthDoc - 1, state); +} + +static void ColourisePropsLine(char *lineBuffer, int lengthLine, StylingContext &styler) { +	int i = 0; +	while (isspace(lineBuffer[i]) && (i < lengthLine))	// Skip initial spaces +		i++; +	if (lineBuffer[i] == '#' || lineBuffer[i] == '!' || lineBuffer[i] == ';') { +		styler.ColourSegment(0, lengthLine - 1, 1); +	} else if (lineBuffer[i] == '[') { +		styler.ColourSegment(0, lengthLine - 1, 2); +	} else if (lineBuffer[i] == '@') { +		styler.ColourSegment(0, i, 4); +		if (lineBuffer[++i] == '=') +			styler.ColourSegment(i, i, 3); +		if (++i < lengthLine) +			styler.ColourSegment(i, lengthLine - 1, 0); +	} else { +		while (lineBuffer[i] != '=' && (i < lengthLine))	// Search the '=' character +			i++; +		if (lineBuffer[i] == '=') { +			styler.ColourSegment(0, i - 1, 0); +			styler.ColourSegment(i, i, 3); +			if (++i < lengthLine) +				styler.ColourSegment(i, lengthLine - 1, 0); +		} else +			styler.ColourSegment(0, lengthLine - 1, 0); +	} +} + +static void ColourisePropsDoc(int startPos, int length, int, StylingContext &styler) { +	char lineBuffer[1024]; +	unsigned int linePos = 0; +	for (int i = startPos; i <= startPos + length; i++) { +		lineBuffer[linePos++] = styler[i]; +		if (styler[i] == '\r' || styler[i] == '\n' || (linePos >= sizeof(lineBuffer) - 1)) { +			lineBuffer[linePos] = '\0'; +			ColourisePropsLine(lineBuffer, linePos, styler); +			linePos = 0; +		} +	} +	if (linePos > 0) +		ColourisePropsLine(lineBuffer, linePos, styler); +} + +static void ColouriseMakeLine(char *lineBuffer, int lengthLine, StylingContext &styler) { +	int i = 0; +	while (isspace(lineBuffer[i]) && (i < lengthLine)) +		i++; +	if (lineBuffer[i] == '#' || lineBuffer[i] == '!') { +		styler.ColourSegment(0, lengthLine - 1, 1); +	} else { +		styler.ColourSegment(0, lengthLine - 1, 0); +	} +} + +static void ColouriseMakeDoc(int startPos, int length, int, StylingContext &styler) { +	char lineBuffer[1024]; +	unsigned int linePos = 0; +	for (int i = startPos; i <= startPos + length; i++) { +		lineBuffer[linePos++] = styler[i]; +		if (styler[i] == '\r' || styler[i] == '\n' || (linePos >= sizeof(lineBuffer) - 1)) { +			ColouriseMakeLine(lineBuffer, linePos, styler); +			linePos = 0; +		} +	} +	if (linePos > 0) +		ColouriseMakeLine(lineBuffer, linePos, styler); +} + +static void ColouriseErrorListLine(char *lineBuffer, int lengthLine, StylingContext &styler) { +	if (lineBuffer[0] == '>') { +		// Command or return status +		styler.ColourSegment(0, lengthLine - 1, 4); +	} else if (strstr(lineBuffer, "File \"") && strstr(lineBuffer, ", line ")) { +		styler.ColourSegment(0, lengthLine - 1, 1); +	} else if (0 == strncmp(lineBuffer, "Error ", strlen("Error "))) { +		// Borland error message +		styler.ColourSegment(0, lengthLine - 1, 5); +	} else if (0 == strncmp(lineBuffer, "Warning ", strlen("Warning "))) { +		// Borland warning message +		styler.ColourSegment(0, lengthLine - 1, 5); +	} else { +		// Look for <filename>:<line>:message +		// Look for <filename>(line)message +		// Look for <filename>(line,pos)message +		int state = 0; +		for (int i = 0; i < lengthLine; i++) { +			if (state == 0 && lineBuffer[i] == ':' && isdigit(lineBuffer[i + 1])) { +				state = 1; +			} else if (state == 0 && lineBuffer[i] == '(') { +				state = 10; +			} else if (state == 1 && isdigit(lineBuffer[i])) { +				state = 2; +			} else if (state == 2 && lineBuffer[i] == ':') { +				state = 3; +				break; +			} else if (state == 2 && !isdigit(lineBuffer[i])) { +				state = 99; +			} else if (state == 10 && isdigit(lineBuffer[i])) { +				state = 11; +			} else if (state == 11 && lineBuffer[i] == ',') { +				state = 14; +			} else if (state == 11 && lineBuffer[i] == ')') { +				state = 12; +				break; +			} else if (state == 12 && lineBuffer[i] == ':') { +				state = 13; +			} else if (state == 14 && lineBuffer[i] == ')') { +				state = 15; +				break; +			} else if (((state == 11) || (state == 14)) && !((lineBuffer[i] == ' ') || isdigit(lineBuffer[i]))) { +				state = 99; +			} +		} +		if (state == 3) { +			styler.ColourSegment(0, lengthLine - 1, 2); +		} else if ((state == 14) || (state == 15)) { +			styler.ColourSegment(0, lengthLine - 1, 3); +		} else { +			styler.ColourSegment(0, lengthLine - 1, 0); +		} +	} +} + +static void ColouriseErrorListDoc(int startPos, int length, int, StylingContext &styler) { +	char lineBuffer[1024]; +	unsigned int linePos = 0; +	for (int i = startPos; i <= startPos + length; i++) { +		lineBuffer[linePos++] = styler[i]; +		if (styler[i] == '\r' || styler[i] == '\n' || (linePos >= sizeof(lineBuffer) - 1)) { +			ColouriseErrorListLine(lineBuffer, linePos, styler); +			linePos = 0; +		} +	} +	if (linePos > 0) +		ColouriseErrorListLine(lineBuffer, linePos, styler); +} + +static void classifyWordSQL(unsigned int start, unsigned int end, WordList &keywords, StylingContext &styler) { +	char s[100]; +	bool wordIsNumber = isdigit(styler[start]) || (styler[start] == '.'); +	for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) { +		s[i] = toupper(styler[start + i]); +		s[i + 1] = '\0'; +	} +	char chAttr = SCE_C_IDENTIFIER; +	if (wordIsNumber) +		chAttr = SCE_C_NUMBER; +	else { +		if (keywords.InList(s)) +			chAttr = SCE_C_WORD; +	} +	styler.ColourSegment(start, end, chAttr); +} + +static void ColouriseSQLDoc(int codePage, int startPos, int length, +                            int initStyle, WordList &keywords, StylingContext &styler) { + +	bool fold = styler.GetPropSet().GetInt("fold"); +	int lineCurrent = styler.GetLine(startPos); +	int spaceFlags = 0; +	int indentCurrent = 0; + +	int state = initStyle; +	char chPrev = ' '; +	char chNext = styler[startPos]; +	int startSeg = startPos; +	int lengthDoc = startPos + length; +	bool prevCr = false; +	for (int i = startPos; i <= lengthDoc; i++) { +		char ch = chNext; +		chNext = styler.SafeGetCharAt(i + 1); + +		if ((ch == '\r' && chNext != '\n') || (ch == '\n')) { +			indentCurrent = IndentAmount(styler, lineCurrent, &spaceFlags); +			int lev = indentCurrent; +			if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG)) { +				// Only non whitespace lines can be headers +				int indentNext = IndentAmount(styler, lineCurrent + 1, &spaceFlags); +				if (indentCurrent < (indentNext & ~SC_FOLDLEVELWHITEFLAG)) { +					lev |= SC_FOLDLEVELHEADERFLAG; +				} +			} +			if (fold) { +				styler.SetLevel(lineCurrent, lev); +			} +		} + +		if (IsLeadByte(codePage, ch)) {	// dbcs +			chNext = styler.SafeGetCharAt(i + 2); +			chPrev = ' '; +			i += 1; +			continue; +		} + +		if (state == SCE_C_DEFAULT) { +			if (iswordstart(ch)) { +				styler.ColourSegment(startSeg, i - 1, state); +				state = SCE_C_WORD; +				startSeg = i; +			} else if (ch == '/' && chNext == '*') { +				styler.ColourSegment(startSeg, i - 1, state); +				state = SCE_C_COMMENT; +				startSeg = i; +			} else if (ch == '-' && chNext == '-') { +				styler.ColourSegment(startSeg, i - 1, state); +				state = SCE_C_COMMENTLINE; +				startSeg = i; +			} else if (ch == '\'') { +				styler.ColourSegment(startSeg, i - 1, state); +				state = SCE_C_STRING; +				startSeg = i; +			} else if (isoperator(ch)) { +				styler.ColourSegment(startSeg, i - 1, state); +				styler.ColourSegment(i, i, SCE_C_OPERATOR); +				startSeg = i + 1; +			} +		} else if (state == SCE_C_WORD) { +			if (!iswordchar(ch)) { +				classifyWordSQL(startSeg, i - 1, keywords, styler); +				state = SCE_C_DEFAULT; +				startSeg = i; +				if (ch == '/' && chNext == '*') { +					state = SCE_C_COMMENT; +				} else if (ch == '-' && chNext == '-') { +					state = SCE_C_COMMENTLINE; +				} else if (ch == '\'') { +					state = SCE_C_STRING; +				} else if (isoperator(ch)) { +					styler.ColourSegment(startSeg, i, SCE_C_OPERATOR); +					startSeg = i + 1; +				} +			} +		} else { +			if (state == SCE_C_COMMENT) { +				if (ch == '/' && chPrev == '*' && ( +				            (i > startSeg + 2) || ((initStyle == SCE_C_COMMENT) && (startSeg == startPos)))) { +					state = SCE_C_DEFAULT; +					styler.ColourSegment(startSeg, i, state); +					startSeg = i + 1; +				} +			} else if (state == SCE_C_COMMENTLINE) { +				if (ch == '\r' || ch == '\n') { +					styler.ColourSegment(startSeg, i - 1, state); +					state = SCE_C_DEFAULT; +					startSeg = i; +				} +			} else if (state == SCE_C_STRING) { +				if (ch == '\'') { +					if ( chNext == '\'' ) { +						i++; +					} else { +						styler.ColourSegment(startSeg, i, state); +						state = SCE_C_DEFAULT; +						i++; +						startSeg = i; +					} +					ch = chNext; +					chNext = styler.SafeGetCharAt(i + 1); +				} +			} +			if (state == SCE_C_DEFAULT) {    // One of the above succeeded +				if (ch == '/' && chNext == '*') { +					state = SCE_C_COMMENT; +				} else if (ch == '-' && chNext == '-') { +					state = SCE_C_COMMENTLINE; +				} else if (ch == '\'') { +					state = SCE_C_STRING; +				} else if (iswordstart(ch)) { +					state = SCE_C_WORD; +				} else if (isoperator(ch)) { +					styler.ColourSegment(startSeg, i, SCE_C_OPERATOR); +					startSeg = i + 1; +				} +			} +		} +		chPrev = ch; +	} +	if (startSeg < lengthDoc) +		styler.ColourSegment(startSeg, lengthDoc - 1, state); +} + +void ColouriseDoc(int codePage, int startPos, int lengthDoc, int initStyle, +                  int language, WordList *keywordlists[], StylingContext &styler) { +	//Platform::DebugPrintf("ColouriseDoc <%s>\n", language); +	if (language == SCLEX_PYTHON) { +		// Python uses a different mask because bad indentation is marked by oring with 32 +		styler.StartAt(startPos, 127); +		ColourisePyDoc(codePage, startPos, lengthDoc, initStyle, *keywordlists[0], styler); +	} else if (language == SCLEX_PERL) { +		// Lexer for perl often has to backtrack to start of current style to determine +		// which characters are being used as quotes, how deeply nested is the +		// start position and what the termination string is for here documents +		ColourisePerlDoc(codePage, startPos, lengthDoc, initStyle, *keywordlists[0], styler); +	} else if ((language == SCLEX_HTML) || (language == SCLEX_XML)) { +		// Lexer for HTML requires more lexical states (6 bits worth) than most lexers +		ColouriseHyperTextDoc(codePage, startPos, lengthDoc, initStyle,  +			*keywordlists[0], *keywordlists[1], *keywordlists[2], *keywordlists[3], styler); +	} else { +		styler.StartAt(startPos); +		if (language == SCLEX_CPP) { +			ColouriseCppDoc(codePage, startPos, lengthDoc, initStyle, *keywordlists[0], styler); +		} else if (language == SCLEX_SQL) { +			ColouriseSQLDoc(codePage, startPos, lengthDoc, initStyle, *keywordlists[0], styler); +		} else if (language == SCLEX_VB) { +			ColouriseVBDoc(codePage, startPos, lengthDoc, initStyle, *keywordlists[0], styler); +		} else if (language == SCLEX_PROPERTIES) { +			ColourisePropsDoc(startPos, lengthDoc, initStyle, styler); +		} else if (language == SCLEX_ERRORLIST) { +			ColouriseErrorListDoc(startPos, lengthDoc, initStyle, styler); +		} else if (language == SCLEX_MAKEFILE) { +			ColouriseMakeDoc(startPos, lengthDoc, initStyle, styler); +		} else if (language == SCLEX_BATCH) { +			ColouriseBatchDoc(startPos, lengthDoc, initStyle, styler); +		} else { +			// Null language means all style bytes are 0 so just mark the end - no need to fill in. +			styler.StartAt(startPos + lengthDoc - 1); +			styler.ColourSegment(0, 0, 0); +		} +	} +} diff --git a/src/LineMarker.cxx b/src/LineMarker.cxx new file mode 100644 index 000000000..9afccb822 --- /dev/null +++ b/src/LineMarker.cxx @@ -0,0 +1,125 @@ +// Scintilla source code edit control +// LineMarker.cxx - defines the look of a line marker in the margin  +// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#include "Platform.h" + +#include "Scintilla.h" +#include "LineMarker.h" + +void LineMarker::Draw(Surface *surface, PRectangle &rc) { +	int minDim = Platform::Minimum(rc.Width(), rc.Height()); +	minDim--;	// Ensure does not go beyond edge +	int centreX = (rc.right + rc.left) / 2; +	int centreY = (rc.bottom + rc.top) / 2; +	int dimOn2 = minDim / 2; +	int dimOn4 = minDim / 4; +	if (rc.Width() > (rc.Height() * 2)) { +		// Wide column is line number so move to left to try to avoid overlapping number +		centreX = rc.left + dimOn2 + 1; +	} +	if (markType == SC_MARK_ROUNDRECT) { +		PRectangle rcRounded = rc; +		rcRounded.left = rc.left + 1; +		rcRounded.right = rc.right - 1; +		surface->RoundedRectangle(rcRounded, fore.allocated, back.allocated); +	} else if (markType == SC_MARK_CIRCLE) { +		PRectangle rcCircle; +		rcCircle.left = centreX - dimOn2; +		rcCircle.top = centreY - dimOn2; +		rcCircle.right = centreX + dimOn2; +		rcCircle.bottom = centreY + dimOn2; +		surface->Ellipse(rcCircle, fore.allocated, back.allocated); +	} else if (markType == SC_MARK_ARROW) { +		Point pts[] = { +    		Point(centreX - dimOn4, centreY - dimOn2), +    		Point(centreX - dimOn4, centreY + dimOn2), +    		Point(centreX + dimOn2 - dimOn4, centreY), +		}; +		surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]), +                 		fore.allocated, back.allocated); + +	} else if (markType == SC_MARK_ARROWDOWN) { +		Point pts[] = { +    		Point(centreX - dimOn2, centreY - dimOn4), +    		Point(centreX + dimOn2, centreY - dimOn4), +    		Point(centreX, centreY + dimOn2 - dimOn4), +		}; +		surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]), +                 		fore.allocated, back.allocated); + +	} else if (markType == SC_MARK_PLUS) { +		int armSize = dimOn2-2; +		Point xpts[] = { +    		Point(centreX - armSize, centreY), +    		Point(centreX, centreY), +    		Point(centreX, centreY - armSize), +    		Point(centreX, centreY - armSize), +    		Point(centreX, centreY), +    		Point(centreX + armSize, centreY), +    		Point(centreX + armSize, centreY), +    		Point(centreX, centreY), +    		Point(centreX, centreY + armSize), +    		Point(centreX, centreY + armSize), +    		Point(centreX, centreY), +    		Point(centreX - armSize, centreY), +		}; +		Point pts[] = { +    		Point(centreX - armSize, centreY - 1), +    		Point(centreX - 1, centreY - 1), +    		Point(centreX - 1, centreY - armSize), +    		Point(centreX + 1, centreY - armSize), +    		Point(centreX + 1, centreY - 1), +    		Point(centreX + armSize, centreY -1), +    		Point(centreX + armSize, centreY +1), +    		Point(centreX + 1, centreY + 1), +    		Point(centreX + 1, centreY + armSize), +    		Point(centreX - 1, centreY + armSize), +    		Point(centreX - 1, centreY + 1), +    		Point(centreX - armSize, centreY + 1), +		}; +		surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]), +                 		fore.allocated, back.allocated); + +	} else if (markType == SC_MARK_MINUS) { +		int armSize = dimOn2-2; +		Point pts[] = { +    		Point(centreX - armSize, centreY - 1), +    		Point(centreX + armSize, centreY -1), +    		Point(centreX + armSize, centreY +1), +    		Point(centreX - armSize, centreY + 1), +		}; +		Point xpts[] = { +    		Point(centreX - armSize, centreY), +    		Point(centreX + armSize, centreY), +    		Point(centreX + armSize, centreY), +    		Point(centreX - armSize, centreY), +		}; +		surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]), +                 		fore.allocated, back.allocated); + +	} else if (markType == SC_MARK_SMALLRECT) { +		PRectangle rcSmall; +		rcSmall.left = rc.left + 1; +		rcSmall.top = rc.top + 2; +		rcSmall.right = rc.right - 1; +		rcSmall.bottom = rc.bottom - 2; +		surface->RectangleDraw(rcSmall, fore.allocated, back.allocated); +	} else if (markType == SC_MARK_EMPTY) { +		// An invisible marker so don't draw anything +	} else { // SC_MARK_SHORTARROW +		Point pts[] = { +			Point(centreX, centreY + dimOn2), +			Point(centreX + dimOn2, centreY), +			Point(centreX, centreY - dimOn2), +			Point(centreX, centreY - dimOn4), +			Point(centreX - dimOn4, centreY - dimOn4), +			Point(centreX - dimOn4, centreY + dimOn4), +			Point(centreX, centreY + dimOn4), +			Point(centreX, centreY + dimOn2), +		}; +		surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]), +				fore.allocated, back.allocated); +	} +} diff --git a/src/LineMarker.h b/src/LineMarker.h new file mode 100644 index 000000000..f22241bb1 --- /dev/null +++ b/src/LineMarker.h @@ -0,0 +1,22 @@ +// Scintilla source code edit control +// LineMarker.h - defines the look of a line marker in the margin  +// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef LINEMARKER_H +#define LINEMARKER_H + +class LineMarker { +public: +	int markType; +	ColourPair fore; +	ColourPair back; +	LineMarker() { +		markType = SC_MARK_CIRCLE; +		fore = Colour(0,0,0); +		back = Colour(0xff,0xff,0xff); +	} +	void Draw(Surface *surface, PRectangle &rc); +}; + +#endif diff --git a/src/PropSet.cxx b/src/PropSet.cxx new file mode 100644 index 000000000..7e2a906a4 --- /dev/null +++ b/src/PropSet.cxx @@ -0,0 +1,399 @@ +// SciTE - Scintilla based Text Editor +// PropSet.cxx - a java style properties file module +// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +// Maintain a dictionary of properties + +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <stdio.h> + +#include "Platform.h" + +#include "PropSet.h" + +bool EqualCaseInsensitive(const char *a, const char *b) { +#if PLAT_GTK +	return 0 == strcasecmp(a, b); +#elif PLAT_WIN +	return 0 == stricmp(a, b); +#elif PLAT_WX +	return 0 == wxStricmp(a, b); +#endif +} + +// Get a line of input. If end of line escaped with '\\' then continue reading. +static bool GetFullLine(const char *&fpc, int &lenData, char *s, int len) { +	bool continuation = true; +	while ((len > 1) && lenData > 0) { +		char ch = *fpc; +		fpc++; +		lenData--; +		if ((ch == '\r') || (ch == '\n')) { +			if (!continuation) { +				if ((lenData > 0) && (ch == '\r') && ((*fpc) == '\n')) { +					// munch the second half of a crlf +					fpc++; +					lenData--; +				} +				*s++ = '\0'; +				return true; +			} +		} else if ((ch == '\\') && (lenData > 0) && ((*fpc == '\r') || (*fpc == '\n'))) { +			continuation = true; +		} else { +			continuation = false; +			*s++ = ch; +			len--; +		} +	} +	return false; +} + +PropSet::PropSet() { +	superPS = 0; +	size = 10; +	used = 0; +	vals = new char * [size]; +} + +PropSet::~PropSet() { +	superPS = 0; +	Clear(); +	delete []vals; +} + +void PropSet::EnsureCanAddEntry() { +	if (used >= size - 2) { +		int newsize = size + 10; +		char **newvals = new char * [newsize]; + +		for (int i = 0; i < used; i++) { +			newvals[i] = vals[i]; +		} +		delete []vals; +		vals = newvals; +		size = newsize; +	} +} + +void PropSet::Set(const char *key, const char *val) { +	EnsureCanAddEntry(); +	for (int i = 0; i < used; i += 2) { +		if (EqualCaseInsensitive(vals[i], key)) { +			// Replace current value +			delete [](vals[i + 1]); +			vals[i + 1] = StringDup(val); +			return; +		} +	} +	// Not found +	vals[used++] = StringDup(key); +	vals[used++] = StringDup(val); +} + +void PropSet::Set(char *keyval) { +	char *eqat = strchr(keyval, '='); +	if (eqat) { +		*eqat = '\0'; +		Set(keyval, eqat + 1); +		*eqat = '='; +	} +} + +SString PropSet::Get(const char *key) { +	for (int i = 0; i < used; i += 2) { +		if (EqualCaseInsensitive(vals[i], key)) { +			return vals[i + 1]; +		} +	} +	if (superPS) { +		// Failed here, so try in base property set +		return superPS->Get(key); +	} else { +		return ""; +	} +} + +int PropSet::GetInt(const char *key, int defaultValue) { +	SString val = Get(key); +	if (val.length()) +		return Get(key).value(); +	else +		return defaultValue; +} + +bool isprefix(const char *target, const char *prefix) { +	while (*target && *prefix) { +		if (toupper(*target) != toupper(*prefix)) +			return false; +		target++; +		prefix++; +	} +	if (*prefix) +		return false; +	else +		return true; +} + +bool issuffix(const char *target, const char *suffix) { +	int lentarget = strlen(target); +	int lensuffix = strlen(suffix); +	if (lensuffix > lentarget) +		return false; +	for (int i = lensuffix - 1; i >= 0; i--) { +		if (toupper(target[i + lentarget - lensuffix]) != toupper(suffix[i])) +			return false; +	} +	return true; +} + +SString PropSet::GetWild(const char *keybase, const char *filename) { +	for (int i = 0; i < used; i += 2) { +		if (isprefix(vals[i], keybase)) { +			char *orgkeyfile = vals[i] + strlen(keybase); +			char *keyfile = NULL; + +			if (strstr(orgkeyfile, "$(") == orgkeyfile) { +				char *cpendvar = strchr(orgkeyfile, ')'); +				if (cpendvar) { +					int lenvar = cpendvar - orgkeyfile - 2; 	// Subtract the $() +					char *var = static_cast<char *>(malloc(lenvar + 1)); +					strncpy(var, orgkeyfile + 2, lenvar); +					var[lenvar] = '\0'; +					SString s = Get(var); +					free(var); +					keyfile = strdup(s.c_str()); +				} +			} +			char *keyptr = keyfile; + +			if (keyfile == NULL) +				keyfile = orgkeyfile; + +			for (; ; ) { +				char *del = strchr(keyfile, ';'); +				if (del == NULL) +					del = keyfile + strlen(keyfile); +				char delchr = *del; +				*del = '\0'; +				if (*keyfile == '*') { +					if (issuffix(filename, keyfile + 1)) { +						*del = delchr; +						free(keyptr); +						return vals[i + 1]; +					} +				} else if (EqualCaseInsensitive(keyfile, filename)) { +					*del = delchr; +					free(keyptr); +					return vals[i + 1]; +				} +				if (delchr == '\0') +					break; +				*del = delchr; +				keyfile = del + 1; +			} +			free(keyptr); + +			if (EqualCaseInsensitive(vals[i], keybase)) { +				return vals[i + 1]; +			} +		} +	} +	if (superPS) { +		// Failed here, so try in base property set +		return superPS->GetWild(keybase, filename); +	} else { +		return ""; +	} +} + +SString PropSet::GetNewExpand(const char *keybase, const char *filename) { +	char *base = StringDup(GetWild(keybase, filename).c_str()); +	char *cpvar = strstr(base, "$("); +	while (cpvar) { +		char *cpendvar = strchr(cpvar, ')'); +		if (cpendvar) { +			int lenvar = cpendvar - cpvar - 2;  	// Subtract the $() +			char *var = new char[lenvar + 1]; +			strncpy(var, cpvar + 2, lenvar); +			var[lenvar] = '\0'; +			SString val = GetWild(var, filename); +			int newlenbase = strlen(base) + val.length() - lenvar; +			char *newbase = new char[newlenbase]; +			strncpy(newbase, base, cpvar - base); +			strcpy(newbase + (cpvar - base), val.c_str()); +			strcpy(newbase + (cpvar - base) + val.length(), cpendvar + 1); +			delete []var; +			delete []base; +			base = newbase; +		} +		cpvar = strstr(base, "$("); +	} +	SString sret = base; +	delete []base; +	return sret; +} + +void PropSet::Clear() { +	for (int i = 0; i < used; i++) { +		delete [](vals[i]); +		vals[i] = 0; +	} +	used = 0; +} + +void PropSet::ReadFromMemory(const char *data, int len) { +	if (len > 0) { +		const char *pd = data; +		char linebuf[60000]; +		while (GetFullLine(pd, len, linebuf, sizeof(linebuf))) { +			if (isalpha(linebuf[0])) +				Set(linebuf); +		} +	} +} + +void PropSet::Read(const char *filename) { +	//printf("Opening properties <%s>\n", filename); +	Clear(); +	char propsData[60000]; +	FILE *rcfile = fopen(filename, "rb"); +	if (rcfile) { +		int lenFile = fread(propsData, 1, sizeof(propsData), rcfile); +		fclose(rcfile); +		ReadFromMemory(propsData, lenFile); +	} else { +		//printf("Could not open <%s>\n", filename); +	} +} + +static bool iswordsep(char ch, bool onlyLineEnds) { +	if (!isspace(ch)) +		return false; +	if (!onlyLineEnds) +		return true; +	return ch == '\r' || ch == '\n'; +} + +// Creates an array that points into each word in the string and puts \0 terminators +// after each word. +static char **ArrayFromWordList(char *wordlist, bool onlyLineEnds = false) { +	char prev = '\n'; +	int words = 0; +	for (int j = 0; wordlist[j]; j++) { +		if (!iswordsep(wordlist[j], onlyLineEnds) && iswordsep(prev, onlyLineEnds)) +			words++; +		prev = wordlist[j]; +	} +	char **keywords = new char * [words + 1]; +	if (keywords) { +		words = 0; +		prev = '\0'; +		int len = strlen(wordlist); +		for (int k = 0; k < len; k++) { +			if (!iswordsep(wordlist[k], onlyLineEnds)) { +				if (!prev) { +					keywords[words] = &wordlist[k]; +					words++; +				} +			} else { +				wordlist[k] = '\0'; +			} +			prev = wordlist[k]; +		} +		keywords[words] = &wordlist[len]; +	} +	return keywords; +} + +void WordList::Clear() { +	if (words) { +		delete []words; +		delete []list; +	} +	words = 0; +	list = 0; +	len = 0; +} + +void WordList::Set(const char *s) { +	len = 0; +	list = StringDup(s); +	words = ArrayFromWordList(list, onlyLineEnds); +} + +char *WordList::Allocate(int size) { +	list = new char[size + 1]; +	list[size] = '\0'; +	return list; +} + +void WordList::SetFromAllocated() { +	len = 0; +	words = ArrayFromWordList(list, onlyLineEnds); +} + +// Shell sort based upon public domain C implementation by Raymond Gardner 1991 +// Used here because of problems with mingw qsort. +static void SortWordList(char **words, unsigned int len) { +	unsigned int gap = len / 2; + +	while (gap > 0) { +		unsigned int i = gap; +		while (i < len) { +			unsigned int j = i; +			char **a = words + j; +			do { +				j -= gap; +				char **b = a; +				a -= gap; +				if (strcmp(*a, *b) > 0) { +					char *tmp = *a; +					*a = *b; +					*b = tmp; +				} else { +					break; +				} +			} while (j >= gap); +			i++; +		} +		gap = gap / 2; +	} +} + +bool WordList::InList(const char *s) { +	if (0 == words) +		return false; +	if (len == 0) { +		for (int i = 0; words[i][0]; i++) +			len++; +		SortWordList(words, len); +		for (int k = 0; k < (sizeof(starts) / sizeof(starts[0])); k++) +			starts[k] = -1; +		for (int l = len - 1; l >= 0; l--) { +			unsigned char indexChar = words[l][0]; +			starts[indexChar] = l; +		} +	} +	unsigned char firstChar = s[0]; +	int j = starts[firstChar]; +	if (j >= 0) { +		while (words[j][0] == firstChar) { +			if (s[1] == words[j][1]) { +				const char *a = words[j] + 1; +				const char *b = s + 1; +				while (*a && *a == *b) { +					a++; +					b++; +				} +				if (!*a && !*b) +					return true; +			} +			j++; +		} +	} +	return false; +} diff --git a/src/SVector.h b/src/SVector.h new file mode 100644 index 000000000..7bc948738 --- /dev/null +++ b/src/SVector.h @@ -0,0 +1,110 @@ +// Scintilla source code edit control +// SVector.h - a simple expandable vector +// Copyright 1998-1999 by Neil Hodgson <neilh@hare.net.au> +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef SVECTOR_H +#define SVECTOR_H + +// A simple expandable vector.  +// T must support assignment. +// Storage not allocated for elements until an element is used.  +// This makes it very lightweight unless used so is a good match for optional features. +template<class T, int sizeIncrement> +class SVector { +	T *v; +	unsigned int size;	// Number of elements allocated +	unsigned int len;	// Number of elements in vector +	bool allocFailure;	// A memory allocation call has failed +	 +	// Internally allocate more elements than the user wants to avoid  +	// thrashng the memory allocator +	void SizeTo(int newSize) { +		if (newSize < sizeIncrement) +			newSize += sizeIncrement; +		else  +			newSize = (newSize * 3) / 2; +		T* newv = new T[newSize]; +		if (!newv) { +			allocFailure = true; +			return; +		} +		size = newSize; +		for (int i=0; i<len; i++) { +			newv[i] = v[i]; +		} +		delete []v; +		v = newv; +	} +	 +public: +	SVector() { +		allocFailure = false; +		v = 0; +		len = 0; +		size = 0; +	} +	~SVector() { +		Free(); +	} +	SVector(const SVector &other) { +		allocFailure = false; +		v = 0; +		len = 0; +		size = 0; +		if (other.Length() > 0) { +			SizeTo(other.Length()); +			if (!allocFailure) { +				for (int i=0;i<other.Length();i++) +					v[i] = other.v[i]; +				len = other.Length(); +			} +		} +	} +	SVector &operator=(const SVector &other) { +		if (this != &other) { +			delete []v; +			allocFailure = false; +			v = 0; +			len = 0; +			size = 0; +			if (other.Length() > 0) { +				SizeTo(other.Length()); +				if (!allocFailure) { +					for (int i=0;i<other.Length();i++) +						v[i] = other.v[i]; +				} +				len = other.Length(); +			} +		} +		return *this; +	} +	T &operator[](unsigned int i) { +		if (i >= len) { +			if (i >= size) { +				SizeTo(i); +			} +			len = i+1; +		} +		return v[i]; +	} +	void Free() { +		delete []v; +		v = 0; +		size = 0; +		len = 0; +	} +	void SetLength(int newLen) { +		if (newLength > len) { +			if (newLength >= size) { +				SizeTo(newLength); +			} +		} +		len = newLen; +	} +	int Length() const { +		return len; +	} +}; + +#endif diff --git a/src/ScintillaBase.cxx b/src/ScintillaBase.cxx new file mode 100644 index 000000000..0ca299bd2 --- /dev/null +++ b/src/ScintillaBase.cxx @@ -0,0 +1,397 @@ +// Scintilla source code edit control +// ScintillaBase.cxx - an enhanced subclass of Editor with calltips, autocomplete and context menu +// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <ctype.h> + +#include "Platform.h" + +#include "Scintilla.h" +#ifdef SCI_LEXER +#include "SciLexer.h" +#include "PropSet.h" +#include "Accessor.h" +#include "KeyWords.h" +#endif +#include "ContractionState.h" +#include "SVector.h" +#include "CellBuffer.h" +#include "CallTip.h" +#include "KeyMap.h" +#include "Indicator.h" +#include "LineMarker.h" +#include "Style.h" +#include "ViewStyle.h" +#include "AutoComplete.h" +#include "Document.h" +#include "Editor.h" +#include "ScintillaBase.h" + +ScintillaBase::ScintillaBase() { +#ifdef SCI_LEXER	 +	lexLanguage = SCLEX_CONTAINER; +	for (int wl=0;wl<numWordLists;wl++) +		keyWordLists[wl] = new WordList; +#endif +} + +ScintillaBase::~ScintillaBase() {} + +void ScintillaBase::Finalise() { +	popup.Destroy(); +} + +void ScintillaBase::RefreshColourPalette(Palette &pal, bool want) { +	Editor::RefreshColourPalette(pal, want); +	ct.RefreshColourPalette(pal, want); +} + +void ScintillaBase::AddChar(char ch) { +	bool acActiveBeforeCharAdded = ac.Active(); +	Editor::AddChar(ch); +	if (acActiveBeforeCharAdded) +		AutoCompleteChanged(ch); +} + +void ScintillaBase::Command(int cmdId) { + +	switch (cmdId) { + +	case idAutoComplete: 	// Nothing to do +		break; + +	case idCallTip: 	// Nothing to do +		break; + +	case idcmdUndo: +		WndProc(WM_UNDO, 0, 0); +		break; + +	case idcmdRedo: +		WndProc(SCI_REDO, 0, 0); +		break; + +	case idcmdCut: +		WndProc(WM_CUT, 0, 0); +		break; + +	case idcmdCopy: +		WndProc(WM_COPY, 0, 0); +		break; + +	case idcmdPaste: +		WndProc(WM_PASTE, 0, 0); +		break; + +	case idcmdDelete: +		WndProc(WM_CLEAR, 0, 0); +		break; + +	case idcmdSelectAll: +		WndProc(SCI_SELECTALL, 0, 0); +		break; +	} +} + +int ScintillaBase::KeyCommand(UINT iMessage) { +	// Most key commands cancel autocompletion mode +	if (ac.Active()) { +		switch (iMessage) { +			// Except for these +		case SCI_LINEDOWN: +			AutoCompleteMove(1); +			return 0; +		case SCI_LINEUP: +			AutoCompleteMove( -1); +			return 0; +		case SCI_PAGEDOWN: +			AutoCompleteMove(5); +			return 0; +		case SCI_PAGEUP: +			AutoCompleteMove( -5); +			return 0; +		case SCI_VCHOME: +			AutoCompleteMove( -5000); +			return 0; +		case SCI_LINEEND: +			AutoCompleteMove(5000); +			return 0; +		case SCI_DELETEBACK: +			DelCharBack(); +			AutoCompleteChanged(); +			EnsureCaretVisible(); +			return 0; +		case SCI_TAB: +			AutoCompleteCompleted(); +			return 0; + +		default: +			ac.Cancel(); +		} +	} + +	if (ct.inCallTipMode) { +		if ( +		    (iMessage != SCI_CHARLEFT) && +		    (iMessage != SCI_CHARLEFTEXTEND) && +		    (iMessage != SCI_CHARRIGHT) && +		    (iMessage != SCI_CHARLEFTEXTEND) && +		    (iMessage != SCI_EDITTOGGLEOVERTYPE) && +		    (iMessage != SCI_DELETEBACK) +		) { +			ct.CallTipCancel(); +		} +		if (iMessage == SCI_DELETEBACK) { +			if (currentPos <= ct.posStartCallTip) { +				ct.CallTipCancel(); +			} +		} +	} +	return Editor::KeyCommand(iMessage); +} + +void ScintillaBase::AutoCompleteStart(int lenEntered, const char *list) { +	//Platform::DebugPrintf("AutoCOmplete %s\n", list); +	ct.CallTipCancel(); + +	ac.Start(wDraw, idAutoComplete, currentPos, lenEntered); + +	PRectangle rcClient = GetClientRectangle(); +	Point pt = LocationFromPosition(currentPos-lenEntered); + +	//Platform::DebugPrintf("Auto complete %x\n", lbAutoComplete); +	int heightLB = 100; +	int widthLB = 100; +	if (pt.x >= rcClient.right - widthLB) { +		HorizontalScrollTo(xOffset + pt.x - rcClient.right + widthLB); +		Redraw(); +		pt = LocationFromPosition(currentPos); +	} +	PRectangle rcac; +	rcac.left = pt.x - 5; +	if (pt.y >= rcClient.bottom - heightLB && // Wont fit below. +	    pt.y >= (rcClient.bottom + rcClient.top) / 2) { // and there is more room above. +		rcac.top = pt.y - heightLB; +		if (rcac.top < 0) { +			heightLB += rcac.top; +			rcac.top = 0; +		} +	} else { +		rcac.top = pt.y + vs.lineHeight; +	} +	rcac.right = rcac.left + widthLB; +	rcac.bottom = Platform::Minimum(rcac.top + heightLB, rcClient.bottom); +	ac.lb.SetPositionRelative(rcac, wMain); +	ac.lb.SetFont(vs.styles[0].font); + +	int maxStrLen = ac.SetList(list); + +	// Fiddle the position of the list so it is right next to the target and wide enough for all its strings +	PRectangle rcList = ac.lb.GetPosition(); +	int heightAlloced = rcList.bottom - rcList.top; +	// Make an allowance for large strings in list +	rcList.left = pt.x - 5; +	rcList.right = rcList.left + Platform::Maximum(widthLB, maxStrLen * 8 + 16); +	if (pt.y >= rcClient.bottom - heightLB && // Wont fit below. +	    pt.y >= (rcClient.bottom + rcClient.top) / 2) { // and there is more room above. +		rcList.top = pt.y - heightAlloced; +	} else { +		rcList.top = pt.y + vs.lineHeight; +	} +	rcList.bottom = rcList.top + heightAlloced; +	ac.lb.SetPositionRelative(rcList, wMain); +	//lbAutoComplete.SetPosition(rcList); +	ac.Show(); +} + +void ScintillaBase::AutoCompleteCancel() { +	ac.Cancel(); +} + +void ScintillaBase::AutoCompleteMove(int delta) { +	ac.Move(delta); +} + +void ScintillaBase::AutoCompleteChanged(char ch) { +	if (currentPos <= ac.posStart) { +		ac.Cancel(); +	} else if (ac.IsStopChar(ch)) { +		ac.Cancel(); +	} else { +		char wordCurrent[1000]; +		int i; +		int startWord = ac.posStart - ac.startLen; +		for (i = startWord; i < currentPos; i++) +			wordCurrent[i - startWord] = pdoc->CharAt(i); +		wordCurrent[i - startWord] = '\0'; +		ac.Select(wordCurrent); +	} +} + +void ScintillaBase::AutoCompleteCompleted() { +	int item = ac.lb.GetSelection(); +	char selected[200]; +	if (item != -1) { +		ac.lb.GetValue(item, selected, sizeof(selected)); +	} +	ac.Cancel(); +	if (currentPos != ac.posStart) { +		pdoc->DeleteChars(ac.posStart, currentPos - ac.posStart); +	} +	SetEmptySelection(ac.posStart); +	if (item != -1) { +		pdoc->InsertString(currentPos, selected + ac.startLen); +		SetEmptySelection(currentPos + strlen(selected + ac.startLen)); +	} +} + +void ScintillaBase::ContextMenu(Point pt) { +	popup.CreatePopUp(); +	AddToPopUp("Undo", idcmdUndo, pdoc->CanUndo()); +	AddToPopUp("Redo", idcmdRedo, pdoc->CanRedo()); +	AddToPopUp(""); +	AddToPopUp("Cut", idcmdCut, currentPos != anchor); +	AddToPopUp("Copy", idcmdCopy, currentPos != anchor); +	AddToPopUp("Paste", idcmdPaste, WndProc(EM_CANPASTE, 0, 0)); +	AddToPopUp("Delete", idcmdDelete, currentPos != anchor); +	AddToPopUp(""); +	AddToPopUp("Select All", idcmdSelectAll); +	popup.Show(pt, wMain); +} + +void ScintillaBase::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt) { +	AutoCompleteCancel(); +	ct.CallTipCancel(); +	Editor::ButtonDown(pt, curTime, shift, ctrl, alt); +} + +#ifdef SCI_LEXER +void ScintillaBase::Colourise(int start, int end) { +	int lengthDoc = Platform::SendScintilla(wMain.GetID(), SCI_GETLENGTH, 0, 0); +	if (end == -1) +		end = lengthDoc; +	int len = end - start; + +	StylingContext styler(wMain.GetID(), props); + +	int styleStart = 0; +	if (start > 0) +		styleStart = styler.StyleAt(start - 1); + +	ColouriseDoc(pdoc->dbcsCodePage, start, len, styleStart, lexLanguage, keyWordLists, styler); +	styler.Flush(); +} +#endif + +void ScintillaBase::NotifyStyleNeeded(int endStyleNeeded) { +#ifdef SCI_LEXER +	if (lexLanguage != SCLEX_CONTAINER) { +		int endStyled = Platform::SendScintilla(wMain.GetID(), SCI_GETENDSTYLED, 0, 0); +		int lineEndStyled = Platform::SendScintilla(wMain.GetID(), EM_LINEFROMCHAR, endStyled, 0); +		endStyled = Platform::SendScintilla(wMain.GetID(), EM_LINEINDEX, lineEndStyled, 0); +		Colourise(endStyled, endStyleNeeded); +		return; +	} +#endif +	Editor::NotifyStyleNeeded(endStyleNeeded); +} + +LRESULT ScintillaBase::WndProc(UINT iMessage, WPARAM wParam, LPARAM lParam) { +	switch (iMessage) { +	case SCI_AUTOCSHOW: +		AutoCompleteStart(wParam, reinterpret_cast<const char *>(lParam)); +		break; + +	case SCI_AUTOCCANCEL: +		AutoCompleteCancel(); +		break; + +	case SCI_AUTOCACTIVE: +		return ac.Active(); + +	case SCI_AUTOCPOSSTART: +		return ac.posStart; + +	case SCI_AUTOCCOMPLETE: +		AutoCompleteCompleted(); +		break; + +	case SCI_AUTOCSTOPS: +		ac.SetStopChars(reinterpret_cast<char *>(lParam)); +		break; + +	case SCI_CALLTIPSHOW: { +			AutoCompleteCancel(); +			if (!ct.wCallTip.Created()) { +				PRectangle rc = ct.CallTipStart(currentPos, LocationFromPosition(wParam), +				                                reinterpret_cast<char *>(lParam), +				                                vs.styles[0].fontName, vs.styles[0].size); +				// If the call-tip window would be out of the client +				// space, adjust so it displays above the text. +				PRectangle rcClient = GetClientRectangle(); +				if (rc.bottom > rcClient.bottom) { +					int offset = vs.lineHeight + rc.Height(); +					rc.top -= offset; +					rc.bottom -= offset; +				} +				// Now display the window. +				CreateCallTipWindow(rc); +				ct.wCallTip.SetPositionRelative(rc, wDraw); +				ct.wCallTip.Show(); +			} +		} +		break; + +	case SCI_CALLTIPCANCEL: +		ct.CallTipCancel(); +		break; + +	case SCI_CALLTIPACTIVE: +		return ct.inCallTipMode; + +	case SCI_CALLTIPPOSSTART: +		return ct.posStartCallTip; + +	case SCI_CALLTIPSETHLT: +		ct.SetHighlight(wParam, lParam); +		break; + +	case SCI_CALLTIPSETBACK: +		ct.colourBG = Colour(wParam); +		InvalidateStyleRedraw(); +		break; +		 +#ifdef SCI_LEXER +	case SCI_SETLEXER: +		lexLanguage = wParam; +		break; +		 +	case SCI_GETLEXER: +		return lexLanguage; +		 +	case SCI_COLOURISE: +		Colourise(wParam, lParam); +		break; +		 +	case SCI_SETPROPERTY: +		props.Set(reinterpret_cast<const char *>(wParam),  +			reinterpret_cast<const char *>(lParam)); +		break; +		 +	case SCI_SETKEYWORDS: +		if ((wParam >= 0) && (wParam < numWordLists)) { +			keyWordLists[wParam]->Clear(); +			keyWordLists[wParam]->Set(reinterpret_cast<const char *>(lParam)); +		} +		break; +#endif + +	default: +		return Editor::WndProc(iMessage, wParam, lParam); +	} +	return 0l; +} diff --git a/src/ScintillaBase.h b/src/ScintillaBase.h new file mode 100644 index 000000000..e9f8f28d0 --- /dev/null +++ b/src/ScintillaBase.h @@ -0,0 +1,68 @@ +// Scintilla source code edit control +// ScintillaBase.h - defines an enhanced subclass of Editor with calltips, autocomplete and context menu +// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef SCINTILLABASE_H +#define SCINTILLABASE_H + +class ScintillaBase : public Editor { +protected: +	// Enumeration of commands and child windows +	enum { +		idCallTip=1, +		idAutoComplete=2, +		 +		idcmdUndo=10, +		idcmdRedo=11, +		idcmdCut=12, +		idcmdCopy=13, +		idcmdPaste=14, +		idcmdDelete=15, +		idcmdSelectAll=16 +	}; + +	Menu popup; +	AutoComplete ac; + +	CallTip ct; + +#ifdef SCI_LEXER +	int lexLanguage; +	PropSet props; +	enum {numWordLists=5}; +	WordList *keyWordLists[numWordLists]; +	void Colourise(int start, int end); +#endif + +	ScintillaBase(); +	virtual ~ScintillaBase(); +	virtual void Initialise() = 0; +	virtual void Finalise() = 0; + +	virtual void RefreshColourPalette(Palette &pal, bool want); +	 +	virtual void AddChar(char ch); +	void Command(int cmdId); +	virtual int KeyCommand(UINT iMessage); +	 +	void AutoCompleteStart(int lenEntered, const char *list); +	void AutoCompleteCancel(); +	void AutoCompleteMove(int delta); +	void AutoCompleteChanged(char ch=0); +	void AutoCompleteCompleted(); + +	virtual void CreateCallTipWindow(PRectangle rc) = 0; +		 +	virtual void AddToPopUp(const char *label, int cmd=0, bool enabled=true) = 0; +	void ContextMenu(Point pt); +	 +	virtual void ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt); + +	virtual void NotifyStyleNeeded(int endStyleNeeded); +public: +	// Public so scintilla_send_message can use it +	virtual LRESULT WndProc(UINT iMessage, WPARAM wParam, LPARAM lParam); +}; + +#endif diff --git a/src/Style.cxx b/src/Style.cxx new file mode 100644 index 000000000..271d98fc2 --- /dev/null +++ b/src/Style.cxx @@ -0,0 +1,89 @@ +// Scintilla source code edit control +// Style.cxx - defines the font and colour style for a class of text +// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#include <string.h> + +#include "Platform.h" + +#include "Style.h" + +Style::Style() { +	Clear(Colour(0,0,0), Colour(0xff,0xff,0xff), +	        Platform::DefaultFontSize(), 0, +		false, false, false); +} +	 +Style::~Style() { +	if (!aliasOfDefaultFont) +		font.Release(); +} + +Style &Style::operator=(const Style &source) { +	if (this == &source) +		return *this; +	Clear(Colour(0,0,0), Colour(0xff,0xff,0xff), +	        Platform::DefaultFontSize(), 0, +		false, false, false); +	fore.desired = source.fore.desired; +	back.desired = source.back.desired; +	bold = source.bold; +	italic = source.italic; +	size = source.size; +	fontName = source.fontName; +	eolFilled = source.eolFilled; +	return *this; +} + +void Style::Clear(Colour fore_, Colour back_, int size_, const char *fontName_,  +	bool bold_, bool italic_, bool eolFilled_) { +	fore.desired = fore_; +	back.desired = back_; +	bold = bold_; +	italic = italic_; +	size = size_; +	fontName = fontName_; +	eolFilled = eolFilled_; +	if (!aliasOfDefaultFont) +		font.Release(); +	aliasOfDefaultFont = false; +} + +bool Style::EquivalentFontTo(const Style *other) const { +	if (bold != other->bold || +		italic != other->italic || +		size != other->size) +		return false; +	if (fontName == other->fontName) +		return true; +	if (!fontName) +		return false; +	if (!other->fontName) +		return false; +	return strcmp(fontName, other->fontName) == 0; +} + +void Style::Realise(Surface &surface, int zoomLevel, Style *defaultStyle) { +	int sizeZoomed = size + zoomLevel; +	if (sizeZoomed <= 2)	// Hangs if sizeZoomed <= 1 +		sizeZoomed = 2; +		 +	int deviceHeight = (sizeZoomed * surface.LogPixelsY()) / 72; +	aliasOfDefaultFont = defaultStyle && EquivalentFontTo(defaultStyle); +	if (aliasOfDefaultFont) { +		font.SetID(defaultStyle->font.GetID()); +	} else if (fontName) { +		font.Create(fontName, deviceHeight, bold, italic); +	} + +	ascent = surface.Ascent(font); +	descent = surface.Descent(font); +	// Probably more typographically correct to include leading +	// but that means more complex drawing as leading must be erased +	//lineHeight = surface.ExternalLeading() + surface.Height(); +	externalLeading = surface.ExternalLeading(font); +	lineHeight = surface.Height(font); +	aveCharWidth = surface.AverageCharWidth(font); +	spaceWidth = surface.WidthChar(font, ' '); +} diff --git a/src/Style.h b/src/Style.h new file mode 100644 index 000000000..a610ff8ba --- /dev/null +++ b/src/Style.h @@ -0,0 +1,39 @@ +// Scintilla source code edit control +// Style.h - defines the font and colour style for a class of text +// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef STYLE_H +#define STYLE_H + +class Style { +public: +	ColourPair fore; +	ColourPair back; +	bool aliasOfDefaultFont; +	bool bold; +	bool italic; +	int size; +	const char *fontName; +	bool eolFilled; + +	Font font; +	unsigned int lineHeight; +	unsigned int ascent; +	unsigned int descent; +	unsigned int externalLeading; +	unsigned int aveCharWidth; +	unsigned int spaceWidth; + +	Style(); +	~Style(); +	Style &operator=(const Style &source); +	void Clear(Colour fore_, Colour back_, +           	int size_,  +		const char *fontName_,  +		bool bold_, bool italic_, bool eolFilled_); +	bool EquivalentFontTo(const Style *other) const; +	void Realise(Surface &surface, int zoomLevel, Style *defaultStyle=0); +}; + +#endif diff --git a/src/ViewStyle.cxx b/src/ViewStyle.cxx new file mode 100644 index 000000000..122db6a00 --- /dev/null +++ b/src/ViewStyle.cxx @@ -0,0 +1,226 @@ +// Scintilla source code edit control +// ViewStyle.cxx - store information on how the document is to be viewed +// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#include <string.h> + +#include "Platform.h" + +#include "Scintilla.h" +#include "Indicator.h" +#include "LineMarker.h" +#include "Style.h" +#include "ViewStyle.h" + +MarginStyle::MarginStyle() :  +	symbol(false), width(16), mask(0xffffffff), sensitive(false) { +} + +// A list of the fontnames - avoids wasting space in each style +FontNames::FontNames() { +	max = 0; +} + +FontNames::~FontNames() { +	Clear(); +} + +void FontNames::Clear() { +	for (int i=0;i<max;i++) { +		delete []names[i]; +	} +	max = 0; +} + +const char *FontNames::Save(const char *name) { +	for (int i=0;i<max;i++) { +		if (strcmp(names[i], name) == 0) { +			return names[i]; +		} +	} +	names[max] = new char[strlen(name) + 1]; +	strcpy(names[max], name); +	max++; +	return names[max-1]; +} + +ViewStyle::ViewStyle() { +	Init(); +} + +ViewStyle::ViewStyle(const ViewStyle &source) { +	Init(); +	for (int sty=0;sty<=STYLE_MAX;sty++) { +		styles[sty] = source.styles[sty]; +	} +	for (int mrk=0;mrk<=MARKER_MAX;mrk++) { +		markers[mrk] = source.markers[mrk]; +	} +	for (int ind=0;ind<=INDIC_MAX;ind++) { +		indicators[ind] = source.indicators[ind]; +	} +	 +	selforeset = source.selforeset; +	selforeground.desired = source.selforeground.desired; +	selbackset = source.selbackset; +	selbackground.desired = source.selbackground.desired; +	selbar.desired = source.selbar.desired; +	selbarlight.desired = source.selbarlight.desired; +	caretcolour.desired = source.caretcolour.desired; +	edgecolour.desired = source.edgecolour.desired; +	leftMarginWidth = source.leftMarginWidth; +	rightMarginWidth = source.rightMarginWidth; +	for (int i=0;i < margins; i++) { +		ms[i] = source.ms[i]; +	} +	symbolMargin = source.symbolMargin; +	maskInLine = source.maskInLine; +	fixedColumnWidth = source.fixedColumnWidth; +	zoomLevel = source.zoomLevel; +	viewWhitespace = source.viewWhitespace; +	viewEOL = source.viewEOL; +	showMarkedLines = source.showMarkedLines;		 +} + +ViewStyle::~ViewStyle() { +} + +void ViewStyle::Init() { +	fontNames.Clear(); +	 +	indicators[0].style = INDIC_SQUIGGLE; +	indicators[0].fore = Colour(0, 0x7f, 0); +	indicators[1].style = INDIC_TT; +	indicators[1].fore = Colour(0, 0, 0xff); +	indicators[2].style = INDIC_PLAIN; +	indicators[2].fore = Colour(0xff, 0, 0); + +	lineHeight = 1; +	maxAscent = 1; +	maxDescent = 1; +	aveCharWidth = 8; +	spaceWidth = 8; + +	selforeset = false; +	selforeground.desired = Colour(0xff, 0, 0); +	selbackset = true; +	selbackground.desired = Colour(0xc0, 0xc0, 0xc0); +	selbar.desired = Platform::Chrome(); +	selbarlight.desired = Platform::ChromeHighlight(); +	styles[STYLE_LINENUMBER].fore.desired = Colour(0, 0, 0); +	styles[STYLE_LINENUMBER].back.desired = Platform::Chrome(); +	//caretcolour.desired = Colour(0xff, 0, 0); +	caretcolour.desired = Colour(0, 0, 0); +	edgecolour.desired = Colour(0xc0, 0xc0, 0xc0); +	 +	leftMarginWidth = 1; +	rightMarginWidth = 1; +	ms[0].symbol = false; +	ms[0].width = 0; +	ms[0].mask = 0; +	ms[1].symbol = true; +	ms[1].width = 16; +	ms[1].mask = ~SC_MASK_FOLDERS; +	ms[2].symbol = true; +	ms[2].width = 14;	// Nice width for arrows +	ms[2].mask = SC_MASK_FOLDERS; +	ms[2].width = 0;	// Nice width for arrows +	ms[2].mask = 0; +	fixedColumnWidth = leftMarginWidth; +	symbolMargin = false; +	maskInLine = 0xffffffff; +	for (int margin=0; margin < margins; margin++) { +		fixedColumnWidth += ms[margin].width; +		symbolMargin = symbolMargin || ms[margin].symbol; +		if (ms[margin].width > 0) +			maskInLine &= ~ms[margin].mask; +	} +	zoomLevel = 0; +	viewWhitespace = false; +	viewEOL = false; +	showMarkedLines = true; +} + +void ViewStyle::RefreshColourPalette(Palette &pal, bool want) { +	unsigned int i; +	for (i=0;i<(sizeof(styles)/sizeof(styles[0]));i++) { +		pal.WantFind(styles[i].fore, want); +		pal.WantFind(styles[i].back, want); +	} +	for (i=0;i<(sizeof(indicators)/sizeof(indicators[0]));i++) { +		pal.WantFind(indicators[i].fore, want); +	} +	for (i=0;i<(sizeof(markers)/sizeof(markers[0]));i++) { +		pal.WantFind(markers[i].fore, want); +		pal.WantFind(markers[i].back, want); +	} +	pal.WantFind(selforeground, want); +	pal.WantFind(selbackground, want); +	pal.WantFind(selbar, want); +	pal.WantFind(selbarlight, want); +	pal.WantFind(caretcolour, want); +	pal.WantFind(edgecolour, want); +} + +#include <mmsystem.h> +void ViewStyle::Refresh(Surface &surface) { +DWORD dwStart = timeGetTime(); +	selbar.desired = Platform::Chrome(); +	selbarlight.desired = Platform::ChromeHighlight(); +	maxAscent = 1; +	maxDescent = 1; +	styles[STYLE_DEFAULT].Realise(surface, zoomLevel); +	for (unsigned int i=0;i<(sizeof(styles)/sizeof(styles[0]));i++) { +		if (i != STYLE_DEFAULT) { +			styles[i].Realise(surface, zoomLevel, &styles[STYLE_DEFAULT]); +			if (maxAscent < styles[i].ascent) +				maxAscent = styles[i].ascent; +			if (maxDescent < styles[i].descent) +				maxDescent = styles[i].descent; +		} +	} +	 +	lineHeight = maxAscent + maxDescent; +	aveCharWidth = styles[STYLE_DEFAULT].aveCharWidth; +	spaceWidth = styles[STYLE_DEFAULT].spaceWidth; + +	fixedColumnWidth = leftMarginWidth; +	symbolMargin = false; +	maskInLine = 0xffffffff; +	for (int margin=0; margin < margins; margin++) { +		fixedColumnWidth += ms[margin].width; +		symbolMargin = symbolMargin || ms[margin].symbol; +		if (ms[margin].width > 0) +			maskInLine &= ~ms[margin].mask; +	} +DWORD dwEnd = timeGetTime(); +Platform::DebugPrintf("Refresh took %d\n", dwEnd - dwStart); +} + +void ViewStyle::ResetDefaultStyle() { +	styles[STYLE_DEFAULT].Clear(Colour(0,0,0), Colour(0xff,0xff,0xff), +	        Platform::DefaultFontSize(), fontNames.Save(Platform::DefaultFont()), +		false, false, false); +} + +void ViewStyle::ClearStyles() { +	// Reset all styles to be like the default style +	for (int i=0; i<=STYLE_MAX; i++) { +		if (i != STYLE_DEFAULT) { +			styles[i].Clear( +				styles[STYLE_DEFAULT].fore.desired,  +				styles[STYLE_DEFAULT].back.desired,  +				styles[STYLE_DEFAULT].size,  +				styles[STYLE_DEFAULT].fontName,  +				styles[STYLE_DEFAULT].bold,  +				styles[STYLE_DEFAULT].italic, +				styles[STYLE_DEFAULT].eolFilled); +		} +	} +	styles[STYLE_LINENUMBER].back.desired = Platform::Chrome(); +} + +void ViewStyle::SetStyleFontName(int styleIndex, const char *name) { +	styles[styleIndex].fontName = fontNames.Save(name); +} diff --git a/src/ViewStyle.h b/src/ViewStyle.h new file mode 100644 index 000000000..4436e83ff --- /dev/null +++ b/src/ViewStyle.h @@ -0,0 +1,72 @@ +// Scintilla source code edit control +// ViewStyle.h - store information on how the document is to be viewed +// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef VIEWSTYLE_H +#define VIEWSTYLE_H + +class MarginStyle { +public: +	bool symbol; +	int width; +	int mask; +	bool sensitive; +	MarginStyle(); +}; + +class FontNames { +private: +	char *names[STYLE_MAX + 1]; +	int max; +public: +	FontNames(); +	~FontNames(); +	void Clear(); +	const char *Save(const char *name); +}; + +class ViewStyle { +public: +	FontNames fontNames; +	Style styles[STYLE_MAX + 1]; +	LineMarker markers[MARKER_MAX + 1]; +	Indicator indicators[INDIC_MAX + 1]; +	int lineHeight; +	unsigned int maxAscent; +	unsigned int maxDescent; +	unsigned int aveCharWidth; +	unsigned int spaceWidth; +	bool selforeset; +	ColourPair selforeground; +	bool selbackset; +	ColourPair selbackground; +	ColourPair selbar; +	ColourPair selbarlight; +	// Margins are ordered: Line Numbers, Selection Margin, Spacing Margin +	int leftMarginWidth;		// Spacing margin on left of text +	int rightMarginWidth;	// Spacing margin on left of text +	enum { margins=3 }; +	bool symbolMargin; +	int maskInLine;	// Mask for markers to be put into text because there is nowhere for them to go in margin +	MarginStyle ms[margins]; +	int fixedColumnWidth; +	int zoomLevel; +	bool viewWhitespace; +	bool viewEOL; +	bool showMarkedLines; +	ColourPair caretcolour; +	ColourPair edgecolour; +	 +	ViewStyle(); +	ViewStyle(const ViewStyle &source); +	~ViewStyle(); +	void Init(); +	void RefreshColourPalette(Palette &pal, bool want); +	void Refresh(Surface &surface); +	void ResetDefaultStyle(); +	void ClearStyles(); +	void SetStyleFontName(int styleIndex, const char *name); +}; + +#endif | 
