SF.net SVN: geany:[4723] trunk

eht16 at users.sourceforge.net eht16 at xxxxx
Sun Mar 7 10:31:52 UTC 2010


Revision: 4723
          http://geany.svn.sourceforge.net/geany/?rev=4723&view=rev
Author:   eht16
Date:     2010-03-07 10:31:51 +0000 (Sun, 07 Mar 2010)

Log Message:
-----------
Update Scintilla to version 2.03.

Modified Paths:
--------------
    trunk/scintilla/AutoComplete.cxx
    trunk/scintilla/CallTip.cxx
    trunk/scintilla/CallTip.h
    trunk/scintilla/CellBuffer.cxx
    trunk/scintilla/CharClassify.cxx
    trunk/scintilla/CharClassify.h
    trunk/scintilla/Document.cxx
    trunk/scintilla/Document.h
    trunk/scintilla/DocumentAccessor.h
    trunk/scintilla/Editor.cxx
    trunk/scintilla/Editor.h
    trunk/scintilla/ExternalLexer.cxx
    trunk/scintilla/ExternalLexer.h
    trunk/scintilla/KeyWords.cxx
    trunk/scintilla/LexAda.cxx
    trunk/scintilla/LexCaml.cxx
    trunk/scintilla/LexCmake.cxx
    trunk/scintilla/LexHTML.cxx
    trunk/scintilla/LexHaskell.cxx
    trunk/scintilla/LexNsis.cxx
    trunk/scintilla/LexOthers.cxx
    trunk/scintilla/LexPython.cxx
    trunk/scintilla/LexSQL.cxx
    trunk/scintilla/Makefile.am
    trunk/scintilla/PerLine.cxx
    trunk/scintilla/PlatGTK.cxx
    trunk/scintilla/PositionCache.cxx
    trunk/scintilla/PositionCache.h
    trunk/scintilla/PropSet.cxx
    trunk/scintilla/RESearch.cxx
    trunk/scintilla/SVector.h
    trunk/scintilla/ScintillaBase.cxx
    trunk/scintilla/ScintillaBase.h
    trunk/scintilla/ScintillaGTK.cxx
    trunk/scintilla/Style.cxx
    trunk/scintilla/Style.h
    trunk/scintilla/StyleContext.h
    trunk/scintilla/ViewStyle.cxx
    trunk/scintilla/ViewStyle.h
    trunk/scintilla/include/KeyWords.h
    trunk/scintilla/include/Platform.h
    trunk/scintilla/include/PropSet.h
    trunk/scintilla/include/SciLexer.h
    trunk/scintilla/include/Scintilla.h
    trunk/scintilla/include/Scintilla.iface
    trunk/scintilla/include/WindowAccessor.h
    trunk/scintilla/makefile.win32
    trunk/scintilla/scintilla_changes.patch
    trunk/wscript

Added Paths:
-----------
    trunk/scintilla/PropSetSimple.h
    trunk/scintilla/Selection.cxx
    trunk/scintilla/Selection.h

Modified: trunk/scintilla/AutoComplete.cxx
===================================================================
--- trunk/scintilla/AutoComplete.cxx	2010-03-05 18:07:41 UTC (rev 4722)
+++ trunk/scintilla/AutoComplete.cxx	2010-03-07 10:31:51 UTC (rev 4723)
@@ -11,7 +11,7 @@
 
 #include "Platform.h"
 
-#include "PropSet.h"
+#include "CharClassify.h"
 #include "AutoComplete.h"
 
 #ifdef SCI_NAMESPACE

Modified: trunk/scintilla/CallTip.cxx
===================================================================
--- trunk/scintilla/CallTip.cxx	2010-03-05 18:07:41 UTC (rev 4722)
+++ trunk/scintilla/CallTip.cxx	2010-03-07 10:31:51 UTC (rev 4723)
@@ -254,11 +254,9 @@
                                  const char *faceName, int size,
                                  int codePage_, int characterSet, Window &wParent) {
 	clickPlace = 0;
-	if (val)
-		delete []val;
+	delete []val;
+	val = 0;
 	val = new char[strlen(defn) + 1];
-	if (!val)
-		return PRectangle();
 	strcpy(val, defn);
 	codePage = codePage_;
 	Surface *surfaceMeasure = Surface::Allocate();

Modified: trunk/scintilla/CallTip.h
===================================================================
--- trunk/scintilla/CallTip.h	2010-03-05 18:07:41 UTC (rev 4722)
+++ trunk/scintilla/CallTip.h	2010-03-07 10:31:51 UTC (rev 4723)
@@ -27,8 +27,8 @@
 	bool useStyleCallTip;   // if true, STYLE_CALLTIP should be used
 
 	// Private so CallTip objects can not be copied
-	CallTip(const CallTip &) {}
-	CallTip &operator=(const CallTip &) { return *this; }
+	CallTip(const CallTip &);
+	CallTip &operator=(const CallTip &);
 	void DrawChunk(Surface *surface, int &x, const char *s,
 		int posStart, int posEnd, int ytext, PRectangle rcClient,
 		bool highlight, bool draw);

Modified: trunk/scintilla/CellBuffer.cxx
===================================================================
--- trunk/scintilla/CellBuffer.cxx	2010-03-05 18:07:41 UTC (rev 4722)
+++ trunk/scintilla/CellBuffer.cxx	2010-03-07 10:31:51 UTC (rev 4723)
@@ -71,6 +71,7 @@
 	position = 0;
 	data = 0;
 	lenData = 0;
+	mayCoalesce = false;
 }
 
 Action::~Action() {
@@ -150,8 +151,6 @@
 		// Run out of undo nodes so extend the array
 		int lenActionsNew = lenActions * 2;
 		Action *actionsNew = new Action[lenActionsNew];
-		if (!actionsNew)
-			return;
 		for (int act = 0; act <= currentAction; act++)
 			actionsNew[act].Grab(&actions[act]);
 		delete []actions;

Modified: trunk/scintilla/CharClassify.cxx
===================================================================
--- trunk/scintilla/CharClassify.cxx	2010-03-05 18:07:41 UTC (rev 4722)
+++ trunk/scintilla/CharClassify.cxx	2010-03-07 10:31:51 UTC (rev 4723)
@@ -5,6 +5,7 @@
 // Copyright 2006 by Neil Hodgson <neilh at scintilla.org>
 // The License.txt file describes the conditions under which this software may be distributed.
 
+#include <stdlib.h>
 #include <ctype.h>
 
 #include "CharClassify.h"
@@ -41,3 +42,37 @@
 		}
 	}
 }
+
+int CompareCaseInsensitive(const char *a, const char *b) {
+	while (*a && *b) {
+		if (*a != *b) {
+			char upperA = MakeUpperCase(*a);
+			char upperB = MakeUpperCase(*b);
+			if (upperA != upperB)
+				return upperA - upperB;
+		}
+		a++;
+		b++;
+	}
+	// Either *a or *b is nul
+	return *a - *b;
+}
+
+int CompareNCaseInsensitive(const char *a, const char *b, size_t len) {
+	while (*a && *b && len) {
+		if (*a != *b) {
+			char upperA = MakeUpperCase(*a);
+			char upperB = MakeUpperCase(*b);
+			if (upperA != upperB)
+				return upperA - upperB;
+		}
+		a++;
+		b++;
+		len--;
+	}
+	if (len == 0)
+		return 0;
+	else
+		// Either *a or *b is nul
+		return *a - *b;
+}

Modified: trunk/scintilla/CharClassify.h
===================================================================
--- trunk/scintilla/CharClassify.h	2010-03-05 18:07:41 UTC (rev 4722)
+++ trunk/scintilla/CharClassify.h	2010-03-07 10:31:51 UTC (rev 4723)
@@ -2,7 +2,7 @@
 /** @file CharClassify.h
  ** Character classifications used by Document and RESearch.
  **/
-// Copyright 2006 by Neil Hodgson <neilh at scintilla.org>
+// Copyright 2006-2009 by Neil Hodgson <neilh at scintilla.org>
 // The License.txt file describes the conditions under which this software may be distributed.
 
 #ifndef CHARCLASSIFY_H
@@ -22,4 +22,16 @@
 	enum { maxChar=256 };
 	unsigned char charClass[maxChar];    // not type cc to save space
 };
+
+// These functions are implemented because each platform calls them something different.
+int CompareCaseInsensitive(const char *a, const char *b);
+int CompareNCaseInsensitive(const char *a, const char *b, size_t len);
+
+inline char MakeUpperCase(char ch) {
+	if (ch < 'a' || ch > 'z')
+		return ch;
+	else
+		return static_cast<char>(ch - 'a' + 'A');
+}
+
 #endif

Modified: trunk/scintilla/Document.cxx
===================================================================
--- trunk/scintilla/Document.cxx	2010-03-05 18:07:41 UTC (rev 4722)
+++ trunk/scintilla/Document.cxx	2010-03-07 10:31:51 UTC (rev 4723)
@@ -217,6 +217,10 @@
 	return LineEnd(LineFromPosition(position));
 }
 
+bool Document::IsLineEndPosition(int position) const {
+	return LineEnd(LineFromPosition(position)) == position;
+}
+
 int Document::VCHomePosition(int position) const {
 	int line = LineFromPosition(position);
 	int startPosition = LineStart(line);
@@ -757,10 +761,9 @@
 		CreateIndentation(linebuf, sizeof(linebuf), indent, tabInChars, !useTabs);
 		int thisLineStart = LineStart(line);
 		int indentPos = GetLineIndentPosition(line);
-		BeginUndoAction();
+		UndoGroup ug(this);
 		DeleteChars(thisLineStart, indentPos - thisLineStart);
 		InsertCString(thisLineStart, linebuf);
-		EndUndoAction();
 	}
 }
 
@@ -801,8 +804,8 @@
 
 int Document::FindColumn(int line, int column) {
 	int position = LineStart(line);
-	int columnCurrent = 0;
 	if ((line >= 0) && (line < LinesTotal())) {
+		int columnCurrent = 0;
 		while ((columnCurrent < column) && (position < Length())) {
 			char ch = cb.CharAt(position);
 			if (ch == '\t') {
@@ -867,7 +870,7 @@
 }
 
 void Document::ConvertLineEnds(int eolModeSet) {
-	BeginUndoAction();
+	UndoGroup ug(this);
 
 	for (int pos = 0; pos < Length(); pos++) {
 		if (cb.CharAt(pos) == '\r') {
@@ -902,7 +905,6 @@
 		}
 	}
 
-	EndUndoAction();
 }
 
 bool Document::IsWhiteLine(int line) const {
@@ -1065,16 +1067,6 @@
 	return IsWordStartAt(start) && IsWordEndAt(end);
 }
 
-// The comparison and case changing functions here assume ASCII
-// or extended ASCII such as the normal Windows code page.
-
-static inline char MakeUpperCase(char ch) {
-	if (ch < 'a' || ch > 'z')
-		return ch;
-	else
-		return static_cast<char>(ch - 'a' + 'A');
-}
-
 static inline char MakeLowerCase(char ch) {
 	if (ch < 'A' || ch > 'Z')
 		return ch;
@@ -1374,8 +1366,6 @@
 			return false;
 	}
 	WatcherWithUserData *pwNew = new WatcherWithUserData[lenWatchers + 1];
-	if (!pwNew)
-		return false;
 	for (int j = 0; j < lenWatchers; j++)
 		pwNew[j] = watchers[j];
 	pwNew[lenWatchers].watcher = watcher;
@@ -1396,8 +1386,6 @@
 				lenWatchers = 0;
 			} else {
 				WatcherWithUserData *pwNew = new WatcherWithUserData[lenWatchers];
-				if (!pwNew)
-					return false;
 				for (int j = 0; j < lenWatchers - 1; j++) {
 					pwNew[j] = (j < i) ? watchers[j] : watchers[j + 1];
 				}
@@ -1758,8 +1746,6 @@
 		}
 	}
 	substituted = new char[lenResult + 1];
-	if (!substituted)
-		return 0;
 	char *o = substituted;
 	for (int j = 0; j < *length; j++) {
 		if (text[j] == '\\') {

Modified: trunk/scintilla/Document.h
===================================================================
--- trunk/scintilla/Document.h	2010-03-05 18:07:41 UTC (rev 4722)
+++ trunk/scintilla/Document.h	2010-03-07 10:31:51 UTC (rev 4723)
@@ -239,6 +239,7 @@
 	int LineStart(int line) const;
 	int LineEnd(int line) const;
 	int LineEndPosition(int position) const;
+	bool IsLineEndPosition(int position) const;
 	int VCHomePosition(int position) const;
 
 	int SetLevel(int line, int level);
@@ -319,6 +320,27 @@
 	void NotifyModified(DocModification mh);
 };
 
+class UndoGroup {
+	Document *pdoc;
+	bool groupNeeded;
+public:
+	UndoGroup(Document *pdoc_, bool groupNeeded_=true) : 
+		pdoc(pdoc_), groupNeeded(groupNeeded_) {
+		if (groupNeeded) {
+			pdoc->BeginUndoAction();
+		}
+	}
+	~UndoGroup() {
+		if (groupNeeded) {
+			pdoc->EndUndoAction();
+		}
+	}
+	bool Needed() const {
+		return groupNeeded;
+	}
+};
+
+
 /**
  * To optimise processing of document modifications by DocWatchers, a hint is passed indicating the
  * scope of the change.

Modified: trunk/scintilla/DocumentAccessor.h
===================================================================
--- trunk/scintilla/DocumentAccessor.h	2010-03-05 18:07:41 UTC (rev 4722)
+++ trunk/scintilla/DocumentAccessor.h	2010-03-07 10:31:51 UTC (rev 4723)
@@ -14,14 +14,15 @@
 
 /**
  */
+
 class DocumentAccessor : public Accessor {
 	// Private so DocumentAccessor objects can not be copied
-	DocumentAccessor(const DocumentAccessor &source) : Accessor(), props(source.props) {}
-	DocumentAccessor &operator=(const DocumentAccessor &) { return *this; }
+	DocumentAccessor(const DocumentAccessor &source);
+	DocumentAccessor &operator=(const DocumentAccessor &);
 
 protected:
 	Document *pdoc;
-	PropSet &props;
+	PropertyGet &props;
 	WindowID id;
 	int lenDoc;
 
@@ -37,7 +38,7 @@
 	void Fill(int position);
 
 public:
-	DocumentAccessor(Document *pdoc_, PropSet &props_, WindowID id_=0) : 
+	DocumentAccessor(Document *pdoc_, PropertyGet &props_, WindowID id_=0) : 
 		Accessor(), pdoc(pdoc_), props(props_), id(id_),
 		lenDoc(-1), validLen(0), chFlags(0), chWhile(0), 
 		startSeg(0), startPosStyling(0),

Modified: trunk/scintilla/Editor.cxx
===================================================================
--- trunk/scintilla/Editor.cxx	2010-03-05 18:07:41 UTC (rev 4722)
+++ trunk/scintilla/Editor.cxx	2010-03-07 10:31:51 UTC (rev 4723)
@@ -10,6 +10,18 @@
 #include <stdio.h>
 #include <ctype.h>
 
+#include <string>
+#include <vector>
+#include <algorithm>
+
+// With Borland C++ 5.5, including <string> includes Windows.h leading to defining
+// FindText to FindTextA which makes calls here to Document::FindText fail.
+#ifdef __BORLANDC__
+#ifdef FindText
+#undef FindText
+#endif
+#endif
+
 #include "Platform.h"
 
 #include "Scintilla.h"
@@ -28,6 +40,7 @@
 #include "CharClassify.h"
 #include "Decoration.h"
 #include "Document.h"
+#include "Selection.h"
 #include "PositionCache.h"
 #include "Editor.h"
 
@@ -108,18 +121,14 @@
 	ptMouseLast.y = 0;
 	inDragDrop = ddNone;
 	dropWentOutside = false;
-	posDrag = invalidPosition;
-	posDrop = invalidPosition;
+	posDrag = SelectionPosition(invalidPosition);
+	posDrop = SelectionPosition(invalidPosition);
 	selectionType = selChar;
 
 	lastXChosen = 0;
 	lineAnchor = 0;
 	originalAnchorPos = 0;
 
-	selType = selStream;
-	moveExtendsSelection = false;
-	xStartSelect = 0;
-	xEndSelect = 0;
 	primarySelection = true;
 
 	caretXPolicy = CARET_SLOP | CARET_EVEN;
@@ -139,6 +148,11 @@
 	verticalScrollBarVisible = true;
 	endAtLastLine = true;
 	caretSticky = false;
+	multipleSelection = false;
+	additionalSelectionTyping = false;
+	additionalCaretsBlink = true;
+	additionalCaretsVisible = true;
+	virtualSpaceOptions = SCVS_NONE;
 
 	pixmapLine = Surface::Allocate();
 	pixmapSelMargin = Surface::Allocate();
@@ -146,9 +160,6 @@
 	pixmapIndentGuide = Surface::Allocate();
 	pixmapIndentGuideHighlight = Surface::Allocate();
 
-	currentPos = 0;
-	anchor = 0;
-
 	targetStart = 0;
 	targetEnd = 0;
 	searchFlags = 0;
@@ -227,10 +238,6 @@
 	palette.Release();
 	llc.Invalidate(LineLayout::llInvalid);
 	posCache.Clear();
-	if (selType == selRectangle) {
-		xStartSelect = XFromPosition(anchor);
-		xEndSelect = XFromPosition(currentPos);
-	}
 }
 
 void Editor::InvalidateStyleRedraw() {
@@ -263,6 +270,7 @@
 				wrapAddIndent = vs.aveCharWidth; // must indent to show start visual
 		}
 		SetScrollBars();
+		SetRectangularRange();
 	}
 }
 
@@ -328,7 +336,7 @@
 class AutoLineLayout {
 	LineLayoutCache &llc;
 	LineLayout *ll;
-	AutoLineLayout &operator=(const AutoLineLayout &) { return * this; }
+	AutoLineLayout &operator=(const AutoLineLayout &);
 public:
 	AutoLineLayout(LineLayoutCache &llc_, LineLayout *ll_) : llc(llc_), ll(ll_) {}
 	~AutoLineLayout() {
@@ -347,98 +355,25 @@
 	}
 };
 
-#ifdef SCI_NAMESPACE
-namespace Scintilla {
-#endif
-
-/**
- * Allows to iterate through the lines of a selection.
- * Althought it can be called for a stream selection, in most cases
- * it is inefficient and it should be used only for
- * a rectangular or a line selection.
- */
-class SelectionLineIterator {
-private:
-	Editor *ed;
-	int line;	///< Current line within the iteration.
-	bool forward;	///< True if iterating by increasing line number, false otherwise.
-	int selStart, selEnd;	///< Positions of the start and end of the selection relative to the start of the document.
-	int minX, maxX;	///< Left and right of selection rectangle.
-
-public:
-	int lineStart, lineEnd;	///< Line numbers, first and last lines of the selection.
-	int startPos, endPos;	///< Positions of the beginning and end of the selection on the current line.
-
-	void Reset() {
-		if (forward) {
-			line = lineStart;
-		} else {
-			line = lineEnd;
-		}
+SelectionPosition Editor::ClampPositionIntoDocument(SelectionPosition sp) const {
+	if (sp.Position() < 0) {
+		return SelectionPosition(0);
+	} else if (sp.Position() > pdoc->Length()) {
+		return SelectionPosition(pdoc->Length());
+	} else {
+		// If not at end of line then set offset to 0
+		if (!pdoc->IsLineEndPosition(sp.Position()))
+			sp.SetVirtualSpace(0);
+		return sp;
 	}
-
-	SelectionLineIterator(Editor *ed_, bool forward_ = true) : line(0), startPos(0), endPos(0) {
-		ed = ed_;
-		forward = forward_;
-		selStart = ed->SelectionStart();
-		selEnd = ed->SelectionEnd();
-		lineStart = ed->pdoc->LineFromPosition(selStart);
-		lineEnd = ed->pdoc->LineFromPosition(selEnd);
-		// Left of rectangle
-		minX = Platform::Minimum(ed->xStartSelect, ed->xEndSelect);
-		// Right of rectangle
-		maxX = Platform::Maximum(ed->xStartSelect, ed->xEndSelect);
-		Reset();
-	}
-	~SelectionLineIterator() {}
-
-	void SetAt(int line) {
-		if (line < lineStart || line > lineEnd) {
-			startPos = endPos = INVALID_POSITION;
-		} else {
-			if (ed->selType == ed->selRectangle) {
-				// Measure line and return character closest to minX
-				startPos = ed->PositionFromLineX(line, minX);
-				// Measure line and return character closest to maxX
-				endPos = ed->PositionFromLineX(line, maxX);
-			} else if (ed->selType == ed->selLines) {
-				startPos = ed->pdoc->LineStart(line);
-				endPos = ed->pdoc->LineStart(line + 1);
-			} else {	// Stream selection, here only for completion
-				if (line == lineStart) {
-					startPos = selStart;
-				} else {
-					startPos = ed->pdoc->LineStart(line);
-				}
-				if (line == lineEnd) {
-					endPos = selEnd;
-				} else {
-					endPos = ed->pdoc->LineStart(line + 1);
-				}
-			}
-		}
-	}
-	bool Iterate() {
-		SetAt(line);
-		if (forward) {
-			line++;
-		} else {
-			line--;
-		}
-		return startPos != INVALID_POSITION;
-	}
-};
-
-#ifdef SCI_NAMESPACE
 }
-#endif
 
-Point Editor::LocationFromPosition(int pos) {
+Point Editor::LocationFromPosition(SelectionPosition pos) {
 	Point pt;
 	RefreshStyleData();
-	if (pos == INVALID_POSITION)
+	if (pos.Position() == INVALID_POSITION)
 		return pt;
-	int line = pdoc->LineFromPosition(pos);
+	int line = pdoc->LineFromPosition(pos.Position());
 	int lineVisible = cs.DisplayFromDoc(line);
 	//Platform::DebugPrintf("line=%d\n", line);
 	AutoSurface surface(this);
@@ -449,7 +384,7 @@
 		pt.x = 0;
 		unsigned int posLineStart = pdoc->LineStart(line);
 		LayoutLine(line, surface, vs, ll, wrapWidth);
-		int posInLine = pos - posLineStart;
+		int posInLine = pos.Position() - posLineStart;
 		// In case of very long line put x at arbitrary large position
 		if (posInLine > ll->maxLineLength) {
 			pt.x = ll->positions[ll->maxLineLength] - ll->positions[ll->LineStart(ll->lines)];
@@ -470,14 +405,24 @@
 		}
 		pt.x += vs.fixedColumnWidth - xOffset;
 	}
+	pt.x += pos.VirtualSpace() * static_cast<int>(vs.styles[ll->EndLineStyle()].spaceWidth);
 	return pt;
 }
 
+Point Editor::LocationFromPosition(int pos) {
+	return LocationFromPosition(SelectionPosition(pos));
+}
+
 int Editor::XFromPosition(int pos) {
 	Point pt = LocationFromPosition(pos);
 	return pt.x - vs.fixedColumnWidth + xOffset;
 }
 
+int Editor::XFromPosition(SelectionPosition sp) {
+	Point pt = LocationFromPosition(sp);
+	return pt.x - vs.fixedColumnWidth + xOffset;
+}
+
 int Editor::LineFromLocation(Point pt) {
 	return cs.DocFromDisplay(pt.y / vs.lineHeight + topLine);
 }
@@ -487,16 +432,16 @@
 	posTopLine = pdoc->LineStart(cs.DocFromDisplay(topLine));
 }
 
-int Editor::PositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition) {
+SelectionPosition Editor::SPositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition, bool virtualSpace) {
 	RefreshStyleData();
 	if (canReturnInvalid) {
 		PRectangle rcClient = GetTextRectangle();
 		if (!rcClient.Contains(pt))
-			return INVALID_POSITION;
+			return SelectionPosition(INVALID_POSITION);
 		if (pt.x < vs.fixedColumnWidth)
-			return INVALID_POSITION;
+			return SelectionPosition(INVALID_POSITION);
 		if (pt.y < 0)
-			return INVALID_POSITION;
+			return SelectionPosition(INVALID_POSITION);
 	}
 	pt.x = pt.x - vs.fixedColumnWidth + xOffset;
 	int visibleLine = pt.y / vs.lineHeight + topLine;
@@ -507,11 +452,11 @@
 		visibleLine = 0;
 	int lineDoc = cs.DocFromDisplay(visibleLine);
 	if (canReturnInvalid && (lineDoc < 0))
-		return INVALID_POSITION;
+		return SelectionPosition(INVALID_POSITION);
 	if (lineDoc >= pdoc->LinesTotal())
-		return canReturnInvalid ? INVALID_POSITION : pdoc->Length();
+		return SelectionPosition(canReturnInvalid ? INVALID_POSITION : pdoc->Length());
 	unsigned int posLineStart = pdoc->LineStart(lineDoc);
-	int retVal = canReturnInvalid ? INVALID_POSITION : static_cast<int>(posLineStart);
+	SelectionPosition retVal(canReturnInvalid ? INVALID_POSITION : static_cast<int>(posLineStart));
 	AutoSurface surface(this);
 	AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
 	if (surface && ll) {
@@ -531,29 +476,38 @@
 			while (i < lineEnd) {
 				if (charPosition) {
 					if ((pt.x + subLineStart) < (ll->positions[i + 1])) {
-						return pdoc->MovePositionOutsideChar(i + posLineStart, 1);
+						return SelectionPosition(pdoc->MovePositionOutsideChar(i + posLineStart, 1));
 					}
 				} else {
 					if ((pt.x + subLineStart) < ((ll->positions[i] + ll->positions[i + 1]) / 2)) {
-						return pdoc->MovePositionOutsideChar(i + posLineStart, 1);
+						return SelectionPosition(pdoc->MovePositionOutsideChar(i + posLineStart, 1));
 					}
 				}
 				i++;
 			}
-			if (canReturnInvalid) {
+			if (virtualSpace) {
+				const int spaceWidth = static_cast<int>(vs.styles[ll->EndLineStyle()].spaceWidth);
+				int spaceOffset = (pt.x + subLineStart - ll->positions[lineEnd] + spaceWidth / 2) /
+					spaceWidth;
+				return SelectionPosition(lineEnd + posLineStart, spaceOffset);
+			} else if (canReturnInvalid) {
 				if (pt.x < (ll->positions[lineEnd] - subLineStart)) {
-					return pdoc->MovePositionOutsideChar(lineEnd + posLineStart, 1);
+					return SelectionPosition(pdoc->MovePositionOutsideChar(lineEnd + posLineStart, 1));
 				}
 			} else {
-				return lineEnd + posLineStart;
+				return SelectionPosition(lineEnd + posLineStart);
 			}
 		}
 		if (!canReturnInvalid)
-			return ll->numCharsInLine + posLineStart;
+			return SelectionPosition(ll->numCharsInLine + posLineStart);
 	}
 	return retVal;
 }
 
+int Editor::PositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition) {
+	return SPositionFromLocation(pt, canReturnInvalid, charPosition, false).Position();
+}
+
 /**
  * Find the document position corresponding to an x coordinate on a particular document line.
  * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
@@ -569,7 +523,7 @@
 	if (surface && ll) {
 		unsigned int posLineStart = pdoc->LineStart(lineDoc);
 		LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
-		retVal = ll->numCharsInLine + posLineStart;
+		retVal = ll->numCharsBeforeEOL + posLineStart;
 		int subLine = 0;
 		int lineStart = ll->LineStart(subLine);
 		int lineEnd = ll->LineLastVisible(subLine);
@@ -592,6 +546,45 @@
 }
 
 /**
+ * Find the document position corresponding to an x coordinate on a particular document line.
+ * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
+ */
+SelectionPosition Editor::SPositionFromLineX(int lineDoc, int x) {
+	RefreshStyleData();
+	if (lineDoc >= pdoc->LinesTotal())
+		return SelectionPosition(pdoc->Length());
+	//Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine);
+	AutoSurface surface(this);
+	AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
+	int retVal = 0;
+	if (surface && ll) {
+		unsigned int posLineStart = pdoc->LineStart(lineDoc);
+		LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
+		int subLine = 0;
+		int lineStart = ll->LineStart(subLine);
+		int lineEnd = ll->LineLastVisible(subLine);
+		int subLineStart = ll->positions[lineStart];
+
+		if (ll->wrapIndent != 0) {
+			if (lineStart != 0)	// Wrapped
+				x -= ll->wrapIndent;
+		}
+		int i = ll->FindBefore(x + subLineStart, lineStart, lineEnd);
+		while (i < lineEnd) {
+			if ((x + subLineStart) < ((ll->positions[i] + ll->positions[i + 1]) / 2)) {
+				retVal = pdoc->MovePositionOutsideChar(i + posLineStart, 1);
+				return SelectionPosition(retVal);
+			}
+			i++;
+		}
+		const int spaceWidth = static_cast<int>(vs.styles[ll->EndLineStyle()].spaceWidth);
+		int spaceOffset = (x + subLineStart - ll->positions[lineEnd] + spaceWidth / 2) / spaceWidth;
+		return SelectionPosition(lineEnd + posLineStart, spaceOffset);
+	}
+	return SelectionPosition(retVal);
+}
+
+/**
  * If painting then abandon the painting because a wider redraw is needed.
  * @return true if calling code should stop drawing.
  */
@@ -676,80 +669,125 @@
 }
 
 int Editor::CurrentPosition() {
-	return currentPos;
+	return sel.MainCaret();
 }
 
 bool Editor::SelectionEmpty() {
-	return anchor == currentPos;
+	return sel.Empty();
 }
 
-int Editor::SelectionStart() {
-	return Platform::Minimum(currentPos, anchor);
+SelectionPosition Editor::SelectionStart() {
+	return sel.RangeMain().Start();
 }
 
-int Editor::SelectionEnd() {
-	return Platform::Maximum(currentPos, anchor);
+SelectionPosition Editor::SelectionEnd() {
+	return sel.RangeMain().End();
 }
 
 void Editor::SetRectangularRange() {
-	if (selType == selRectangle) {
-		xStartSelect = XFromPosition(anchor);
-		xEndSelect = XFromPosition(currentPos);
+	if (sel.IsRectangular()) {
+		int xAnchor = XFromPosition(sel.Rectangular().anchor);
+		int xCaret = XFromPosition(sel.Rectangular().caret);
+		if (sel.selType == Selection::selThin) {
+			xCaret = xAnchor;
+		}
+		int lineAnchor = pdoc->LineFromPosition(sel.Rectangular().anchor.Position());
+		int lineCaret = pdoc->LineFromPosition(sel.Rectangular().caret.Position());
+		int increment = (lineCaret > lineAnchor) ? 1 : -1;
+		for (int line=lineAnchor; line != lineCaret+increment; line += increment) {
+			SelectionRange range(SPositionFromLineX(line, xCaret), SPositionFromLineX(line, xAnchor));
+			if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) == 0)
+				range.ClearVirtualSpace();
+			if (line == lineAnchor)
+				sel.SetSelection(range);
+			else
+				sel.AddSelection(range);
+		}
 	}
 }
 
-void Editor::InvalidateSelection(int currentPos_, int anchor_, bool invalidateWholeSelection) {
-	if (anchor != anchor_ || selType == selRectangle) {
+void Editor::ThinRectangularRange() {
+	if (sel.IsRectangular()) {
+		sel.selType = Selection::selThin;
+		if (sel.Rectangular().caret < sel.Rectangular().anchor) {
+			sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).caret, sel.Range(0).anchor);
+		} else {
+			sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).anchor, sel.Range(0).caret);
+		}
+		SetRectangularRange();
+	}
+}
+
+void Editor::InvalidateSelection(SelectionRange newMain, bool invalidateWholeSelection) {
+	if (sel.Count() > 1 || !(sel.RangeMain().anchor == newMain.anchor) || sel.IsRectangular()) {
 		invalidateWholeSelection = true;
 	}
-	int firstAffected = currentPos;
+	int firstAffected = Platform::Minimum(sel.RangeMain().Start().Position(), newMain.Start().Position());
+	// +1 for lastAffected ensures caret repainted
+	int lastAffected = Platform::Maximum(newMain.caret.Position()+1, newMain.anchor.Position());
+	lastAffected = Platform::Maximum(lastAffected, sel.RangeMain().End().Position());
 	if (invalidateWholeSelection) {
-		if (firstAffected > anchor)
-			firstAffected = anchor;
-		if (firstAffected > anchor_)
-			firstAffected = anchor_;
+		for (size_t r=0; r<sel.Count(); r++) {
+			firstAffected = Platform::Minimum(firstAffected, sel.Range(r).caret.Position());
+			firstAffected = Platform::Minimum(firstAffected, sel.Range(r).anchor.Position());
+			lastAffected = Platform::Maximum(lastAffected, sel.Range(r).caret.Position()+1);
+			lastAffected = Platform::Maximum(lastAffected, sel.Range(r).anchor.Position());
+		}
 	}
-	if (firstAffected > currentPos_)
-		firstAffected = currentPos_;
-	int lastAffected = currentPos;
-	if (invalidateWholeSelection) {
-		if (lastAffected < anchor)
-			lastAffected = anchor;
-		if (lastAffected < anchor_)
-			lastAffected = anchor_;
-	}
-	if (lastAffected < (currentPos_ + 1))	// +1 ensures caret repainted
-		lastAffected = (currentPos_ + 1);
 	needUpdateUI = true;
 	InvalidateRange(firstAffected, lastAffected);
 }
 
-void Editor::SetSelection(int currentPos_, int anchor_) {
-	currentPos_ = pdoc->ClampPositionIntoDocument(currentPos_);
-	anchor_ = pdoc->ClampPositionIntoDocument(anchor_);
-	if ((currentPos != currentPos_) || (anchor != anchor_)) {
-		InvalidateSelection(currentPos_, anchor_, true);
-		currentPos = currentPos_;
-		anchor = anchor_;
+void Editor::SetSelection(SelectionPosition currentPos_, SelectionPosition anchor_) {
+	SelectionRange rangeNew(ClampPositionIntoDocument(currentPos_),
+		ClampPositionIntoDocument(anchor_));
+	if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
+		InvalidateSelection(rangeNew);
 	}
+	sel.RangeMain() = rangeNew;
 	SetRectangularRange();
 	ClaimSelection();
 }
 
+void Editor::SetSelection(int currentPos_, int anchor_) {
+	SetSelection(SelectionPosition(currentPos_), SelectionPosition(anchor_));
+}
+
+// Just move the caret on the main selection
+void Editor::SetSelection(SelectionPosition currentPos_) {
+	currentPos_ = ClampPositionIntoDocument(currentPos_);
+	if (sel.Count() > 1 || !(sel.RangeMain().caret == currentPos_)) {
+		InvalidateSelection(SelectionRange(currentPos_));
+	}
+	if (sel.IsRectangular()) {
+		sel.Rectangular() =
+			SelectionRange(SelectionPosition(currentPos_), sel.Rectangular().anchor);
+		SetRectangularRange();
+	} else {
+		sel.RangeMain() =
+			SelectionRange(SelectionPosition(currentPos_), sel.RangeMain().anchor);
+	}
+	ClaimSelection();
+}
+
 void Editor::SetSelection(int currentPos_) {
-	currentPos_ = pdoc->ClampPositionIntoDocument(currentPos_);
-	if (currentPos != currentPos_) {
-		InvalidateSelection(currentPos_, anchor, false);
-		currentPos = currentPos_;
+	SetSelection(SelectionPosition(currentPos_));
+}
+
+void Editor::SetEmptySelection(SelectionPosition currentPos_) {
+	SelectionRange rangeNew(ClampPositionIntoDocument(currentPos_));
+	if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
+		InvalidateSelection(rangeNew);
 	}
+	sel.Clear();
+	sel.RangeMain() = rangeNew;
 	SetRectangularRange();
 	ClaimSelection();
+
 }
 
 void Editor::SetEmptySelection(int currentPos_) {
-	selType = selStream;
-	moveExtendsSelection = false;
-	SetSelection(currentPos_, currentPos_);
+	SetEmptySelection(SelectionPosition(currentPos_));
 }
 
 bool Editor::RangeContainsProtected(int start, int end) const {
@@ -769,54 +807,59 @@
 }
 
 bool Editor::SelectionContainsProtected() {
-	// DONE, but untested...: make support rectangular selection
-	bool scp = false;
-	if (selType == selStream) {
-		scp = RangeContainsProtected(anchor, currentPos);
-	} else {
-		SelectionLineIterator lineIterator(this);
-		while (lineIterator.Iterate()) {
-			if (RangeContainsProtected(lineIterator.startPos, lineIterator.endPos)) {
-				scp = true;
-				break;
-			}
+	for (size_t r=0; r<sel.Count(); r++) {
+		if (RangeContainsProtected(sel.Range(r).Start().Position(),
+			sel.Range(r).End().Position())) {
+			return true;
 		}
 	}
-	return scp;
+	return false;
 }
 
 /**
  * Asks document to find a good position and then moves out of any invisible positions.
  */
 int Editor::MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd) const {
-	pos = pdoc->MovePositionOutsideChar(pos, moveDir, checkLineEnd);
+	return MovePositionOutsideChar(SelectionPosition(pos), moveDir, checkLineEnd).Position();
+}
+
+SelectionPosition Editor::MovePositionOutsideChar(SelectionPosition pos, int moveDir, bool checkLineEnd) const {
+	int posMoved = pdoc->MovePositionOutsideChar(pos.Position(), moveDir, checkLineEnd);
+	if (posMoved != pos.Position())
+		pos.SetPosition(posMoved);
 	if (vs.ProtectionActive()) {
 		int mask = pdoc->stylingBitsMask;
 		if (moveDir > 0) {
-			if ((pos > 0) && vs.styles[pdoc->StyleAt(pos - 1) & mask].IsProtected()) {
-				while ((pos < pdoc->Length()) &&
-				        (vs.styles[pdoc->StyleAt(pos) & mask].IsProtected()))
-					pos++;
+			if ((pos.Position() > 0) && vs.styles[pdoc->StyleAt(pos.Position() - 1) & mask].IsProtected()) {
+				while ((pos.Position() < pdoc->Length()) &&
+				        (vs.styles[pdoc->StyleAt(pos.Position()) & mask].IsProtected()))
+					pos.Add(1);
 			}
 		} else if (moveDir < 0) {
-			if (vs.styles[pdoc->StyleAt(pos) & mask].IsProtected()) {
-				while ((pos > 0) &&
-				        (vs.styles[pdoc->StyleAt(pos - 1) & mask].IsProtected()))
-					pos--;
+			if (vs.styles[pdoc->StyleAt(pos.Position()) & mask].IsProtected()) {
+				while ((pos.Position() > 0) &&
+				        (vs.styles[pdoc->StyleAt(pos.Position() - 1) & mask].IsProtected()))
+					pos.Add(-1);
 			}
 		}
 	}
 	return pos;
 }
 
-int Editor::MovePositionTo(int newPos, selTypes sel, bool ensureVisible) {
-	int delta = newPos - currentPos;
-	newPos = pdoc->ClampPositionIntoDocument(newPos);
+int Editor::MovePositionTo(SelectionPosition newPos, Selection::selTypes selt, bool ensureVisible) {
+	int delta = newPos.Position() - sel.MainCaret();
+	newPos = ClampPositionIntoDocument(newPos);
 	newPos = MovePositionOutsideChar(newPos, delta);
-	if (sel != noSel) {
-		selType = sel;
+	if (!sel.IsRectangular() && (selt == Selection::selRectangle)) {
+		// Switching to rectangular
+		SelectionRange rangeMain = sel.RangeMain();
+		sel.Clear();
+		sel.Rectangular() = rangeMain;
 	}
-	if (sel != noSel || moveExtendsSelection) {
+	if (selt != Selection::noSel) {
+		sel.selType = selt;
+	}
+	if (selt != Selection::noSel || sel.MoveExtends()) {
 		SetSelection(newPos);
 	} else {
 		SetEmptySelection(newPos);
@@ -828,10 +871,14 @@
 	return 0;
 }
 
-int Editor::MovePositionSoVisible(int pos, int moveDir) {
-	pos = pdoc->ClampPositionIntoDocument(pos);
+int Editor::MovePositionTo(int newPos, Selection::selTypes selt, bool ensureVisible) {
+	return MovePositionTo(SelectionPosition(newPos), selt, ensureVisible);
+}
+
+SelectionPosition Editor::MovePositionSoVisible(SelectionPosition pos, int moveDir) {
+	pos = ClampPositionIntoDocument(pos);
 	pos = MovePositionOutsideChar(pos, moveDir);
-	int lineDoc = pdoc->LineFromPosition(pos);
+	int lineDoc = pdoc->LineFromPosition(pos.Position());
 	if (cs.GetVisible(lineDoc)) {
 		return pos;
 	} else {
@@ -839,20 +886,28 @@
 		if (moveDir > 0) {
 			// lineDisplay is already line before fold as lines in fold use display line of line after fold
 			lineDisplay = Platform::Clamp(lineDisplay, 0, cs.LinesDisplayed());
-			return pdoc->LineStart(cs.DocFromDisplay(lineDisplay));
+			return SelectionPosition(pdoc->LineStart(cs.DocFromDisplay(lineDisplay)));
 		} else {
 			lineDisplay = Platform::Clamp(lineDisplay - 1, 0, cs.LinesDisplayed());
-			return pdoc->LineEnd(cs.DocFromDisplay(lineDisplay));
+			return SelectionPosition(pdoc->LineEnd(cs.DocFromDisplay(lineDisplay)));
 		}
 	}
 }
 
+SelectionPosition Editor::MovePositionSoVisible(int pos, int moveDir) {
+	return MovePositionSoVisible(SelectionPosition(pos), moveDir);
+}
+
+Point Editor::PointMainCaret() {
+	return LocationFromPosition(sel.Range(sel.Main()).caret);
+}
+
 /**
  * Choose the x position that the caret will try to stick to
  * as it moves up and down.
  */
 void Editor::SetLastXChosen() {
-	Point pt = LocationFromPosition(currentPos);
+	Point pt = PointMainCaret();
 	lastXChosen = pt.x;
 }
 
@@ -897,16 +952,16 @@
 
 void Editor::MoveCaretInsideView(bool ensureVisible) {
 	PRectangle rcClient = GetTextRectangle();
-	Point pt = LocationFromPosition(currentPos);
+	Point pt = PointMainCaret();
 	if (pt.y < rcClient.top) {
-		MovePositionTo(PositionFromLocation(
+		MovePositionTo(SPositionFromLocation(
 		            Point(lastXChosen, rcClient.top)),
-		        noSel, ensureVisible);
+					Selection::noSel, ensureVisible);
 	} else if ((pt.y + vs.lineHeight - 1) > rcClient.bottom) {
 		int yOfLastLineFullyDisplayed = rcClient.top + (LinesOnScreen() - 1) * vs.lineHeight;
-		MovePositionTo(PositionFromLocation(
+		MovePositionTo(SPositionFromLocation(
 		            Point(lastXChosen, rcClient.top + yOfLastLineFullyDisplayed)),
-		        noSel, ensureVisible);
+		        Selection::noSel, ensureVisible);
 	}
 }
 
@@ -978,14 +1033,14 @@
 	//Platform::DebugPrintf("EnsureCaretVisible %d %s\n", xOffset, useMargin ? " margin" : " ");
 	PRectangle rcClient = GetTextRectangle();
 	//int rcClientFullWidth = rcClient.Width();
-	int posCaret = currentPos;
-	if (posDrag >= 0) {
+	SelectionPosition posCaret = sel.RangeMain().caret;
+	if (posDrag.IsValid()) {
 		posCaret = posDrag;
 	}
 	Point pt = LocationFromPosition(posCaret);
 	Point ptBottomCaret = pt;
 	ptBottomCaret.y += vs.lineHeight - 1;
-	int lineCaret = DisplayFromPosition(posCaret);
+	int lineCaret = DisplayFromPosition(posCaret.Position());
 	bool bSlop, bStrict, bJump, bEven;
 
 	// Vertical positioning
@@ -1222,10 +1277,13 @@
 }
 
 void Editor::InvalidateCaret() {
-	if (posDrag >= 0)
-		InvalidateRange(posDrag, posDrag + 1);
-	else
-		InvalidateRange(currentPos, currentPos + 1);
+	if (posDrag.IsValid()) {
+		InvalidateRange(posDrag.Position(), posDrag.Position() + 1);
+	} else {
+		for (size_t r=0; r<sel.Count(); r++) {
+			InvalidateRange(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1);
+		}
+	}
 	UpdateSystemCaret();
 }
 
@@ -1255,14 +1313,14 @@
 		LayoutLine(lineToWrap, surface, vs, ll, wrapWidth);
 		linesWrapped = ll->lines;
 	}
-	return cs.SetHeight(lineToWrap, linesWrapped + 
+	return cs.SetHeight(lineToWrap, linesWrapped +
 		(vs.annotationVisible ? pdoc->AnnotationLines(lineToWrap) : 0));
 }
 
 // Check if wrapping needed and perform any needed wrapping.
 // fullwrap: if true, all lines which need wrapping will be done,
 //           in this single call.
-// priorityWrapLineStart: If greater than zero, all lines starting from
+// priorityWrapLineStart: If greater than or equal to zero, all lines starting from
 //           here to 1 page + 100 lines past will be wrapped (even if there are
 //           more lines under wrapping process in idle).
 // If it is neither fullwrap, nor priorityWrap, then 1 page + 100 lines will be
@@ -1294,7 +1352,7 @@
 			if (wrapWidth != LineLayout::wrapWidthInfinite) {
 				wrapWidth = LineLayout::wrapWidthInfinite;
 				for (int lineDoc = 0; lineDoc < pdoc->LinesTotal(); lineDoc++) {
-					cs.SetHeight(lineDoc, 1 + 
+					cs.SetHeight(lineDoc, 1 +
 						(vs.annotationVisible ? pdoc->AnnotationLines(lineDoc) : 0));
 				}
 				wrapOccurred = true;
@@ -1368,7 +1426,7 @@
 
 void Editor::LinesJoin() {
 	if (!RangeContainsProtected(targetStart, targetEnd)) {
-		pdoc->BeginUndoAction();
+		UndoGroup ug(pdoc);
 		bool prevNonWS = true;
 		for (int pos = targetStart; pos < targetEnd; pos++) {
 			if (IsEOLChar(pdoc->CharAt(pos))) {
@@ -1383,7 +1441,6 @@
 				prevNonWS = pdoc->CharAt(pos) != ' ';
 			}
 		}
-		pdoc->EndUndoAction();
 	}
 }
 
@@ -1406,7 +1463,7 @@
 		int lineStart = pdoc->LineFromPosition(targetStart);
 		int lineEnd = pdoc->LineFromPosition(targetEnd);
 		const char *eol = StringFromEOLMode(pdoc->eolMode);
-		pdoc->BeginUndoAction();
+		UndoGroup ug(pdoc);
 		for (int line = lineStart; line <= lineEnd; line++) {
 			AutoSurface surface(this);
 			AutoLineLayout ll(llc, RetrieveLineLayout(line));
@@ -1421,7 +1478,6 @@
 			}
 			lineEnd = pdoc->LineFromPosition(targetEnd);
 		}
-		pdoc->EndUndoAction();
 	}
 }
 
@@ -1483,7 +1539,7 @@
 	return widthMax;
 }
 
-void DrawStyledText(Surface *surface, ViewStyle &vs, int styleOffset, PRectangle rcText, int ascent, 
+void DrawStyledText(Surface *surface, ViewStyle &vs, int styleOffset, PRectangle rcText, int ascent,
 	const StyledText &st, size_t start, size_t length) {
 
 	if (st.multipleStyles) {
@@ -1700,13 +1756,13 @@
 					if (firstSubLine) {
 						const StyledText stMargin  = pdoc->MarginStyledText(lineDoc);
 						if (stMargin.text && ValidStyledText(vs, vs.marginStyleOffset, stMargin)) {
-							surface->FillRectangle(rcMarker, 
+							surface->FillRectangle(rcMarker,
 								vs.styles[stMargin.StyleAt(0)+vs.marginStyleOffset].back.allocated);
 							if (vs.ms[margin].style == SC_MARGIN_RTEXT) {
 								int width = WidestLineWidth(surface, vs, vs.marginStyleOffset, stMargin);
 								rcMarker.left = rcMarker.right - width - 3;
 							}
-							DrawStyledText(surface, vs, vs.marginStyleOffset, rcMarker, rcMarker.top + vs.maxAscent, 
+							DrawStyledText(surface, vs, vs.marginStyleOffset, rcMarker, rcMarker.top + vs.maxAscent,
 								stMargin, 0, stMargin.length);
 						}
 					}
@@ -1757,7 +1813,7 @@
 	int posLineStart = pdoc->LineStart(lineNumber);
 	int posLineEnd = pdoc->LineStart(lineNumber + 1);
 	PLATFORM_ASSERT(posLineEnd >= posLineStart);
-	int lineCaret = pdoc->LineFromPosition(currentPos);
+	int lineCaret = pdoc->LineFromPosition(sel.MainCaret());
 	return llc.Retrieve(lineNumber, lineCaret,
 	        posLineEnd - posLineStart, pdoc->GetStyleClock(),
 	        LinesOnScreen() + 1, pdoc->LinesTotal());
@@ -1884,6 +1940,7 @@
 		ll->widthLine = LineLayout::wrapWidthInfinite;
 		ll->lines = 1;
 		int numCharsInLine = 0;
+		int numCharsBeforeEOL = 0;
 		if (vstyle.edgeState == EDGE_BACKGROUND) {
 			ll->edgeColumn = pdoc->FindColumn(line, theEdge);
 			if (ll->edgeColumn >= posLineStart) {
@@ -1910,6 +1967,8 @@
 				else if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseLower)
 					ll->chars[numCharsInLine] = static_cast<char>(tolower(chDoc));
 				numCharsInLine++;
+				if (!IsEOLChar(chDoc))
+					numCharsBeforeEOL++;
 			}
 		}
 		ll->xHighlightGuide = 0;
@@ -1992,6 +2051,7 @@
 			ll->positions[startseg] += 2;
 		}
 		ll->numCharsInLine = numCharsInLine;
+		ll->numCharsBeforeEOL = numCharsBeforeEOL;
 		ll->validity = LineLayout::llPositions;
 	}
 	// Hard to cope when too narrow, so just assume there is space
@@ -2072,16 +2132,22 @@
 	}
 }
 
-ColourAllocated Editor::SelectionBackground(ViewStyle &vsDraw) {
-	return primarySelection ? vsDraw.selbackground.allocated : vsDraw.selbackground2.allocated;
+ColourAllocated Editor::SelectionBackground(ViewStyle &vsDraw, bool main) {
+	return main ?
+		(primarySelection ? vsDraw.selbackground.allocated : vsDraw.selbackground2.allocated) :
+		vsDraw.selAdditionalBackground.allocated;
 }
 
 ColourAllocated Editor::TextBackground(ViewStyle &vsDraw, bool overrideBackground,
-        ColourAllocated background, bool inSelection, bool inHotspot, int styleMain, int i, LineLayout *ll) {
-	if (inSelection) {
+        ColourAllocated background, int inSelection, bool inHotspot, int styleMain, int i, LineLayout *ll) {
+	if (inSelection == 1) {
 		if (vsDraw.selbackset && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) {
-			return SelectionBackground(vsDraw);
+			return SelectionBackground(vsDraw, true);
 		}
+	} else if (inSelection == 2) {
+		if (vsDraw.selbackset && (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA)) {
+			return SelectionBackground(vsDraw, false);
+		}
 	} else {
 		if ((vsDraw.edgeState == EDGE_BACKGROUND) &&
 		        (i >= ll->edgeColumn) &&
@@ -2183,35 +2249,103 @@
         bool overrideBackground, ColourAllocated background,
         bool drawWrapMarkEnd, ColourAllocated wrapColour) {
 
-	int styleMask = pdoc->stylingBitsMask;
+	const int posLineStart = pdoc->LineStart(line);
+	const int styleMask = pdoc->stylingBitsMask;
 	PRectangle rcSegment = rcLine;
 
+	const bool lastSubLine = subLine == (ll->lines - 1);
+	int virtualSpace = 0;
+	if (lastSubLine) {
+		const int spaceWidth = static_cast<int>(vsDraw.styles[ll->EndLineStyle()].spaceWidth);
+		virtualSpace = sel.VirtualSpaceFor(pdoc->LineEnd(line)) * spaceWidth;
+	}
+
 	// Fill in a PRectangle representing the end of line characters
+
 	int xEol = ll->positions[lineEnd] - subLineStart;
-	rcSegment.left = xEol + xStart;
-	rcSegment.right = xEol + vsDraw.aveCharWidth + xStart;
-	int posLineEnd = pdoc->LineStart(line + 1);
-	bool eolInSelection = (subLine == (ll->lines - 1)) &&
-	        (posLineEnd > ll->selStart) && (posLineEnd <= ll->selEnd) && (ll->selStart != ll->selEnd);
 
-	if (eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) {
-		surface->FillRectangle(rcSegment, SelectionBackground(vsDraw));
+	// Fill the virtual space and show selections within it
+	if (virtualSpace) {
+		rcSegment.left = xEol + xStart;
+		rcSegment.right = xEol + xStart + virtualSpace;
+		surface->FillRectangle(rcSegment, overrideBackground ? background : vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back.allocated);
+		if (!hideSelection && ((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA))) {
+			SelectionSegment virtualSpaceRange(SelectionPosition(pdoc->LineEnd(line)), SelectionPosition(pdoc->LineEnd(line), sel.VirtualSpaceFor(pdoc->LineEnd(line))));
+			for (size_t r=0; r<sel.Count(); r++) {
+				int alpha = (r == sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
+				if (alpha == SC_ALPHA_NOALPHA) {
+					SelectionSegment portion = sel.Range(r).Intersect(virtualSpaceRange);
+					if (!portion.Empty()) {
+						const int spaceWidth = static_cast<int>(vsDraw.styles[ll->EndLineStyle()].spaceWidth);
+						rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] - subLineStart + portion.start.VirtualSpace() * spaceWidth;
+						rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] - subLineStart + portion.end.VirtualSpace() * spaceWidth;
+						rcSegment.left = Platform::Maximum(rcSegment.left, rcLine.left);
+						rcSegment.right = Platform::Minimum(rcSegment.right, rcLine.right);
+						surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, r == sel.Main()));
+					}
+				}
+			}
+		}
+	}
+
+	int posAfterLineEnd = pdoc->LineStart(line + 1);
+	int eolInSelection = (subLine == (ll->lines - 1)) ? sel.InSelectionForEOL(posAfterLineEnd) : 0;
+	int alpha = (eolInSelection == 1) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
+
+	// Draw the [CR], [LF], or [CR][LF] blobs if visible line ends are on
+	int blobsWidth = 0;
+	if (lastSubLine) {
+		for (int eolPos=ll->numCharsBeforeEOL; eolPos<ll->numCharsInLine; eolPos++) {
+			rcSegment.left = xStart + ll->positions[eolPos] - subLineStart + virtualSpace;
+			rcSegment.right = xStart + ll->positions[eolPos+1] - subLineStart + virtualSpace;
+			blobsWidth += rcSegment.Width();
+			const char *ctrlChar = ControlCharacterString(ll->chars[eolPos]);
+			int inSelection = 0;
+			bool inHotspot = false;
+			int styleMain = ll->styles[eolPos];
+			ColourAllocated textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, eolPos, ll);
+			ColourAllocated textFore = vsDraw.styles[styleMain].fore.allocated;
+			if (!hideSelection && eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1)) {
+				if (alpha == SC_ALPHA_NOALPHA) {
+					surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
+				} else {
+					surface->FillRectangle(rcSegment, textBack);
+					SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
+				}
+			} else {
+				surface->FillRectangle(rcSegment, textBack);
+			}
+			DrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, textBack, textFore, twoPhaseDraw);
+		}
+	}
+
+	// Draw the eol-is-selected rectangle
+	rcSegment.left = xEol + xStart + virtualSpace + blobsWidth;
+	rcSegment.right = xEol + xStart + virtualSpace + blobsWidth + vsDraw.aveCharWidth;
+
+	if (!hideSelection && eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
+		surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
 	} else {
 		if (overrideBackground) {
 			surface->FillRectangle(rcSegment, background);
+		} else if (line < pdoc->LinesTotal() - 1) {
+			surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back.allocated);
+		} 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[ll->styles[ll->numCharsInLine] & styleMask].back.allocated);
+			surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back.allocated);
 		}
-		if (eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (vsDraw.selAlpha != SC_ALPHA_NOALPHA)) {
-			SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw), vsDraw.selAlpha);
+		if (!hideSelection && eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
+			SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
 		}
 	}
 
-	rcSegment.left = xEol + vsDraw.aveCharWidth + xStart;
+	// Fill the remainder of the line
+	rcSegment.left = xEol + xStart + virtualSpace + blobsWidth + vsDraw.aveCharWidth;
 	rcSegment.right = rcLine.right;
 
-	if (vsDraw.selEOLFilled && eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) {
-		surface->FillRectangle(rcSegment, SelectionBackground(vsDraw));
+	if (!hideSelection && vsDraw.selEOLFilled && eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
+		surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
 	} else {
 		if (overrideBackground) {
 			surface->FillRectangle(rcSegment, background);
@@ -2220,8 +2354,8 @@
 		} else {
 			surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back.allocated);
 		}
-		if (vsDraw.selEOLFilled && eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (vsDraw.selAlpha != SC_ALPHA_NOALPHA)) {
-			SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw), vsDraw.selAlpha);
+		if (!hideSelection && vsDraw.selEOLFilled && eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
+			SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
 		}
 	}
 
@@ -2229,7 +2363,7 @@
 		PRectangle rcPlace = rcSegment;
 
 		if (wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_END_BY_TEXT) {
-			rcPlace.left = xEol + xStart;
+			rcPlace.left = xEol + xStart + virtualSpace;
 			rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
 		} else {
 			// draw left of the right text margin, to avoid clipping by the current clip rect
@@ -2336,11 +2470,11 @@
 		}
 		PRectangle rcText = rcSegment;
 		if (vs.annotationVisible == ANNOTATION_BOXED) {
-			surface->FillRectangle(rcText, 
+			surface->FillRectangle(rcText,
 				vsDraw.styles[stAnnotation.StyleAt(start) + vsDraw.annotationStyleOffset].back.allocated);
 			rcText.left += vsDraw.spaceWidth;
 		}
-		DrawStyledText(surface, vsDraw, vsDraw.annotationStyleOffset, rcText, rcText.top + vsDraw.maxAscent, 
+		DrawStyledText(surface, vsDraw, vsDraw.annotationStyleOffset, rcText, rcText.top + vsDraw.maxAscent,
 			stAnnotation, start, lengthAnnotation);
 		if (vs.annotationVisible == ANNOTATION_BOXED) {
 			surface->MoveTo(rcSegment.left, rcSegment.top);
@@ -2350,7 +2484,7 @@
 			if (subLine == ll->lines){
 				surface->MoveTo(rcSegment.left, rcSegment.top);
 				surface->LineTo(rcSegment.right, rcSegment.top);
-			} 
+			}
 			if (subLine == ll->lines+annotationLines-1) {
 				surface->MoveTo(rcSegment.left, rcSegment.bottom - 1);
 				surface->LineTo(rcSegment.right, rcSegment.bottom - 1);
@@ -2427,6 +2561,9 @@
 	if (subLine < ll->lines) {
 		lineStart = ll->LineStart(subLine);
 		lineEnd = ll->LineStart(subLine + 1);
+		if (subLine == ll->lines - 1) {
+			lineEnd = ll->numCharsBeforeEOL;
+		}
 	}
 
 	ColourAllocated wrapColour = vsDraw.styles[STYLE_DEFAULT].fore.allocated;
@@ -2478,10 +2615,15 @@
 		}
 	}
 
+	bool selBackDrawn = vsDraw.selbackset &&
+		((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA));
+
 	// Does not take margin into account but not significant
 	int xStartVisible = subLineStart - xStart;
 
-	BreakFinder bfBack(ll, lineStart, lineEnd, posLineStart, IsUnicodeMode(), xStartVisible);
+	ll->psel = &sel;
+
+	BreakFinder bfBack(ll, lineStart, lineEnd, posLineStart, IsUnicodeMode(), xStartVisible, selBackDrawn);
 	int next = bfBack.First();
 
 	// Background drawing loop
@@ -2502,7 +2644,7 @@
 			rcSegment.right = Platform::Minimum(rcSegment.right, rcLine.right);
 
 			int styleMain = ll->styles[i];
-			bool inSelection = (iDoc >= ll->selStart) && (iDoc < ll->selEnd) && (ll->selStart != ll->selEnd);
+			const int inSelection = hideSelection ? 0 : sel.CharacterInSelection(iDoc);
 			bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd);
 			ColourAllocated textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, i, ll);
 			if (ll->chars[i] == '\t') {
@@ -2571,7 +2713,8 @@
 
 	inIndentation = subLine == 0;	// Do not handle indentation except on first subline.
 	// Foreground drawing loop
-	BreakFinder bfFore(ll, lineStart, lineEnd, posLineStart, IsUnicodeMode(), xStartVisible);
+	BreakFinder bfFore(ll, lineStart, lineEnd, posLineStart, IsUnicodeMode(), xStartVisible,
+		((!twoPhaseDraw && selBackDrawn) || vsDraw.selforeset));
 	next = bfFore.First();
 
 	while (next < lineEnd) {
@@ -2595,9 +2738,9 @@
 				if (vsDraw.hotspotForegroundSet)
 					textFore = vsDraw.hotspotForeground.allocated;
 			}
-			bool inSelection = (iDoc >= ll->selStart) && (iDoc < ll->selEnd) && (ll->selStart != ll->selEnd);
+			const int inSelection = hideSelection ? 0 : sel.CharacterInSelection(iDoc);
 			if (inSelection && (vsDraw.selforeset)) {
-				textFore = vsDraw.selforeground.allocated;
+				textFore = (inSelection == 1) ? vsDraw.selforeground.allocated : vsDraw.selAdditionalForeground.allocated;
 			}
 			bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd);
 			ColourAllocated textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, i, ll);
@@ -2679,8 +2822,8 @@
 										surface->FillRectangle(rcSpace, textBack);
 									}
 									PRectangle rcDot(xmid + xStart - subLineStart, rcSegment.top + vsDraw.lineHeight / 2, 0, 0);
-									rcDot.right = rcDot.left + 1;
-									rcDot.bottom = rcDot.top + 1;
+									rcDot.right = rcDot.left + vs.whitespaceSize;
+									rcDot.bottom = rcDot.top + vs.whitespaceSize;
 									surface->FillRectangle(rcDot, textFore);
 								}
 							}
@@ -2718,6 +2861,8 @@
 	if ((vsDraw.viewIndentationGuides == ivLookForward || vsDraw.viewIndentationGuides == ivLookBoth)
 	        && (subLine == 0)) {
 		int indentSpace = pdoc->GetLineIndentation(line);
+		int xStartText = ll->positions[pdoc->GetLineIndentPosition(line) - posLineStart];
+
 		// Find the most recent line with some text
 
 		int lineLastWithText = line;
@@ -2725,6 +2870,7 @@
 			lineLastWithText--;
 		}
 		if (lineLastWithText < line) {
+			xStartText = 100000;	// Don't limit to visible indentation on empty line
 			// This line is empty, so use indentation of last line with text
 			int indentLastWithText = pdoc->GetLineIndentation(lineLastWithText);
 			int isFoldHeader = pdoc->GetLevel(lineLastWithText) & SC_FOLDLEVELHEADERFLAG;
@@ -2754,8 +2900,10 @@
 
 		for (int indentPos = pdoc->IndentSize(); indentPos < indentSpace; indentPos += pdoc->IndentSize()) {
 			int xIndent = indentPos * vsDraw.spaceWidth;
-			DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
-			        (ll->xHighlightGuide == xIndent));
+			if (xIndent < xStartText) {
+				DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
+					(ll->xHighlightGuide == xIndent));
+			}
 		}
 	}
 
@@ -2767,16 +2915,29 @@
 		        xStart, subLine, subLineStart, overrideBackground, background,
 		        drawWrapMarkEnd, wrapColour);
 	}
-	if ((vsDraw.selAlpha != SC_ALPHA_NOALPHA) && (ll->selStart >= 0) && (ll->selEnd >= 0)) {
-		int startPosSel = (ll->selStart < posLineStart) ? posLineStart : ll->selStart;
-		int endPosSel = (ll->selEnd < (lineEnd + posLineStart)) ? ll->selEnd : (lineEnd + posLineStart);
-		if (startPosSel < endPosSel) {
-			rcSegment.left = xStart + ll->positions[startPosSel - posLineStart] - subLineStart;
-			rcSegment.right = xStart + ll->positions[endPosSel - posLineStart] - subLineStart;
-			rcSegment.left = Platform::Maximum(rcSegment.left, rcLine.left);
-			rcSegment.right = Platform::Minimum(rcSegment.right, rcLine.right);
-			SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw), vsDraw.selAlpha);
+	if (!hideSelection && ((vsDraw.selAlpha != SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha != SC_ALPHA_NOALPHA))) {
+		// For each selection draw
+		int virtualSpaces = 0;
+		if (subLine == (ll->lines - 1)) {
+			virtualSpaces = sel.VirtualSpaceFor(pdoc->LineEnd(line));
 		}
+		SelectionPosition posStart(posLineStart);
+		SelectionPosition posEnd(posLineStart + lineEnd, virtualSpaces);
+		SelectionSegment virtualSpaceRange(posStart, posEnd);
+		for (size_t r=0; r<sel.Count(); r++) {
+			int alpha = (r == sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
+			if (alpha != SC_ALPHA_NOALPHA) {
+				SelectionSegment portion = sel.Range(r).Intersect(virtualSpaceRange);
+				if (!portion.Empty()) {
+					const int spaceWidth = static_cast<int>(vsDraw.styles[ll->EndLineStyle()].spaceWidth);
+					rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] - subLineStart + portion.start.VirtualSpace() * spaceWidth;
+					rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] - subLineStart + portion.end.VirtualSpace() * spaceWidth;
+					rcSegment.left = Platform::Maximum(rcSegment.left, rcLine.left);
+					rcSegment.right = Platform::Minimum(rcSegment.right, rcLine.right);
+					SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, r == sel.Main()), alpha);
+				}
+			}
+		}
 	}
 
 	// Draw any translucent whole line states
@@ -2809,7 +2970,8 @@
 	}
 }
 
-void Editor::DrawBlockCaret(Surface *surface, ViewStyle &vsDraw, LineLayout *ll, int subLine, int xStart, int offset, int posCaret, PRectangle rcCaret) {
+void Editor::DrawBlockCaret(Surface *surface, ViewStyle &vsDraw, LineLayout *ll, int subLine,
+							int xStart, int offset, int posCaret, PRectangle rcCaret, ColourAllocated caretColour) {
 
 	int lineStart = ll->LineStart(subLine);
 	int posBefore = posCaret;
@@ -2868,7 +3030,7 @@
 	surface->DrawTextClipped(rcCaret, vsDraw.styles[styleMain].font,
 	        rcCaret.top + vsDraw.maxAscent, ll->chars + offsetFirstChar,
 	        numCharsToDraw, vsDraw.styles[styleMain].back.allocated,
-	        vsDraw.caretcolour.allocated);
+	        caretColour);
 }
 
 void Editor::RefreshPixMaps(Surface *surfaceWindow) {
@@ -2937,6 +3099,89 @@
 	}
 }
 
+void Editor::DrawCarets(Surface *surface, ViewStyle &vsDraw, int lineDoc, int xStart,
+        PRectangle rcLine, LineLayout *ll, int subLine) {
+	// When drag is active it is the only caret drawn
+	bool drawDrag = posDrag.IsValid();
+	if (hideSelection && !drawDrag)
+		return;
+	const int posLineStart = pdoc->LineStart(lineDoc);
+	// For each selection draw
+	for (size_t r=0; (r<sel.Count()) || drawDrag; r++) {
+		const bool mainCaret = r == sel.Main();
+		const SelectionPosition posCaret = (drawDrag ? posDrag : sel.Range(r).caret);
+		const int offset = posCaret.Position() - posLineStart;
+		const int spaceWidth = static_cast<int>(vsDraw.styles[ll->EndLineStyle()].spaceWidth);
+		const int virtualOffset = posCaret.VirtualSpace() * spaceWidth;
+		if (ll->InLine(offset, subLine) && offset <= ll->numCharsBeforeEOL) {
+			int xposCaret = ll->positions[offset] + virtualOffset - ll->positions[ll->LineStart(subLine)];
+			if (ll->wrapIndent != 0) {
+				int lineStart = ll->LineStart(subLine);
+				if (lineStart != 0)	// Wrapped
+					xposCaret += ll->wrapIndent;
+			}
+			bool caretBlinkState = (caret.active && caret.on) || (!additionalCaretsBlink && !mainCaret);
+			bool caretVisibleState = additionalCaretsVisible || mainCaret;
+			if ((xposCaret >= 0) && (vsDraw.caretWidth > 0) && (vsDraw.caretStyle != CARETSTYLE_INVISIBLE) &&
+			        ((posDrag.IsValid()) || (caretBlinkState && caretVisibleState))) {
+				bool caretAtEOF = false;
+				bool caretAtEOL = false;
+				bool drawBlockCaret = false;
+				int widthOverstrikeCaret;
+				int caretWidthOffset = 0;
+				PRectangle rcCaret = rcLine;
+
+				if (posCaret.Position() == pdoc->Length())	{   // At end of document
+					caretAtEOF = true;
+					widthOverstrikeCaret = vsDraw.aveCharWidth;
+				} else if ((posCaret.Position() - posLineStart) >= ll->numCharsInLine) {	// At end of line
+					caretAtEOL = true;
+					widthOverstrikeCaret = vsDraw.aveCharWidth;
+				} else {
+					widthOverstrikeCaret = ll->positions[offset + 1] - ll->positions[offset];
+				}
+				if (widthOverstrikeCaret < 3)	// Make sure its visible
+					widthOverstrikeCaret = 3;
+
+				if (xposCaret > 0)
+					caretWidthOffset = 1;	// Move back so overlaps both character cells.
+				xposCaret += xStart;
+				if (posDrag.IsValid()) {
+					/* Dragging text, use a line caret */
+					rcCaret.left = xposCaret - caretWidthOffset;
+					rcCaret.right = rcCaret.left + vsDraw.caretWidth;
+				} else if (inOverstrike) {
+					/* Overstrike (insert mode), use a modified bar caret */
+					rcCaret.top = rcCaret.bottom - 2;
+					rcCaret.left = xposCaret + 1;
+					rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1;
+				} else if (vsDraw.caretStyle == CARETSTYLE_BLOCK) {
+					/* Block caret */
+					rcCaret.left = xposCaret;
+					if (!caretAtEOL && !caretAtEOF && (ll->chars[offset] != '\t') && !(IsControlCharacter(ll->chars[offset]))) {
+						drawBlockCaret = true;
+						rcCaret.right = xposCaret + widthOverstrikeCaret;
+					} else {
+						rcCaret.right = xposCaret + vsDraw.aveCharWidth;
+					}
+				} else {
+					/* Line caret */
+					rcCaret.left = xposCaret - caretWidthOffset;
+					rcCaret.right = rcCaret.left + vsDraw.caretWidth;
+				}
+				ColourAllocated caretColour = mainCaret ? vsDraw.caretcolour.allocated : vsDraw.additionalCaretColour.allocated;
+				if (drawBlockCaret) {
+					DrawBlockCaret(surface, vsDraw, ll, subLine, xStart, offset, posCaret.Position(), rcCaret, caretColour);
+				} else {
+					surface->FillRectangle(rcCaret, caretColour);
+				}
+			}
+		}
+		if (drawDrag)
+			break;
+	}
+}
+
 void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
 	//Platform::DebugPrintf("Paint:%1d (%3d,%3d) ... (%3d,%3d)\n",
 	//	paintingAllText, rcArea.left, rcArea.top, rcArea.right, rcArea.bottom);
@@ -2989,7 +3234,7 @@
 	// 	lines first).
 	int startLineToWrap = cs.DocFromDisplay(topLine) - 5;
 	if (startLineToWrap < 0)
-		startLineToWrap = -1;
+		startLineToWrap = 0;
 	if (WrapLines(false, startLineToWrap)) {
 		// The wrapping process has changed the height of some lines so
 		// abandon this paint for a complete repaint.
@@ -3036,10 +3281,10 @@
 
 		int visibleLine = topLine + screenLinePaintFirst;
 
-		int posCaret = currentPos;
-		if (posDrag >= 0)
+		SelectionPosition posCaret = sel.RangeMain().caret;
+		if (posDrag.IsValid())
 			posCaret = posDrag;
-		int lineCaret = pdoc->LineFromPosition(posCaret);
+		int lineCaret = pdoc->LineFromPosition(posCaret.Position());
 
 		// Remove selection margin from drawing area so text will not be drawn
 		// on it in unbuffered mode.
@@ -3055,7 +3300,6 @@
 		//ElapsedTime etWhole;
 		int lineDocPrevious = -1;	// Used to avoid laying out one document line multiple times
 		AutoLineLayout ll(llc, 0);
-		SelectionLineIterator lineIterator(this);
 		while (visibleLine < cs.LinesDisplayed() && yposScreen < rcArea.bottom) {
 
 			int lineDoc = cs.DocFromDisplay(visibleLine);
@@ -3069,8 +3313,6 @@
 			//ElapsedTime et;
 			if (lineDoc != lineDocPrevious) {
 				ll.Set(0);
-				// For rectangular selection this accesses the layout cache so should be after layout returned.
-				lineIterator.SetAt(lineDoc);
 				ll.Set(RetrieveLineLayout(lineDoc));
 				LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
 				lineDocPrevious = lineDoc;
@@ -3078,17 +3320,8 @@
 			//durLayout += et.Duration(true);
 
 			if (ll) {
-				if (selType == selStream) {
-					ll->selStart = SelectionStart();
-					ll->selEnd = SelectionEnd();
-				} else {
-					ll->selStart = lineIterator.startPos;
-					ll->selEnd = lineIterator.endPos;
-				}
 				ll->containsCaret = lineDoc == lineCaret;
 				if (hideSelection) {
-					ll->selStart = -1;
-					ll->selEnd = -1;
 					ll->containsCaret = false;
 				}
 
@@ -3132,78 +3365,17 @@
 					}
 				}
 
-				// Draw the Caret
-				if (lineDoc == lineCaret) {
-					int offset = Platform::Minimum(posCaret - rangeLine.start, ll->maxLineLength);
-					if (ll->InLine(offset, subLine)) {
-						int xposCaret = ll->positions[offset] - ll->positions[ll->LineStart(subLine)] + xStart;
+				DrawCarets(surface, vs, lineDoc, xStart, rcLine, ll, subLine);
 
-						if (ll->wrapIndent != 0) {
-							int lineStart = ll->LineStart(subLine);
-							if (lineStart != 0)	// Wrapped
-								xposCaret += ll->wrapIndent;
-						}
-						if ((xposCaret >= 0) && (vs.caretWidth > 0) && (vs.caretStyle != CARETSTYLE_INVISIBLE) &&
-						        ((posDrag >= 0) || (caret.active && caret.on))) {
-							bool caretAtEOF = false;
-							bool caretAtEOL = false;
-							bool drawBlockCaret = false;
-							int widthOverstrikeCaret;
-							int caretWidthOffset = 0;
-							PRectangle rcCaret = rcLine;
-
-							if (posCaret == pdoc->Length())	{   // At end of document
-								caretAtEOF = true;
-								widthOverstrikeCaret = vs.aveCharWidth;
-							} else if ((posCaret - rangeLine.start) >= ll->numCharsInLine) {	// At end of line
-								caretAtEOL = true;
-								widthOverstrikeCaret = vs.aveCharWidth;
-							} else {
-								widthOverstrikeCaret = ll->positions[offset + 1] - ll->positions[offset];
-							}
-							if (widthOverstrikeCaret < 3)	// Make sure its visible
-								widthOverstrikeCaret = 3;
-
-							if (offset > ll->LineStart(subLine))
-								caretWidthOffset = 1;	// Move back so overlaps both character cells.
-							if (posDrag >= 0) {
-								/* Dragging text, use a line caret */
-								rcCaret.left = xposCaret - caretWidthOffset;
-								rcCaret.right = rcCaret.left + vs.caretWidth;
-							} else if (inOverstrike) {
-								/* Overstrike (insert mode), use a modified bar caret */
-								rcCaret.top = rcCaret.bottom - 2;
-								rcCaret.left = xposCaret + 1;
-								rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1;
-							} else if (vs.caretStyle == CARETSTYLE_BLOCK) {
-								/* Block caret */
-								rcCaret.left = xposCaret;
-								if (!caretAtEOL && !caretAtEOF && (ll->chars[offset] != '\t') && !(IsControlCharacter(ll->chars[offset]))) {
-									drawBlockCaret = true;
-									rcCaret.right = xposCaret + widthOverstrikeCaret;
-								} else {
-									rcCaret.right = xposCaret + vs.aveCharWidth;
-								}
-							} else {
-								/* Line caret */
-								rcCaret.left = xposCaret - caretWidthOffset;
-								rcCaret.right = rcCaret.left + vs.caretWidth;
-							}
-							if (drawBlockCaret) {
-								DrawBlockCaret(surface, vs, ll, subLine, xStart, offset, posCaret, rcCaret);
-							} else {
-								surface->FillRectangle(rcCaret, vs.caretcolour.allocated);
-							}
-						}
-					}
-				}
-
 				if (bufferedDraw) {
 					Point from(vs.fixedColumnWidth, 0);
 					PRectangle rcCopyArea(vs.fixedColumnWidth, yposScreen,
 					        rcClient.right, yposScreen + vs.lineHeight);
 					surfaceWindow->Copy(rcCopyArea, from, *pixmapLine);
 				}
+
+				lineWidthMaxSeen = Platform::Maximum(
+					    lineWidthMaxSeen, ll->positions[ll->numCharsInLine]);
 				//durCopy += et.Duration(true);
 			}
 
@@ -3214,8 +3386,6 @@
 			yposScreen += vs.lineHeight;
 			visibleLine++;
 
-			lineWidthMaxSeen = Platform::Maximum(
-			            lineWidthMaxSeen, ll->positions[ll->numCharsInLine]);
 			//gdk_flush();
 		}
 		ll.Set(0);
@@ -3298,6 +3468,7 @@
 	vsPrint.selbackset = false;
 	vsPrint.selforeset = false;
 	vsPrint.selAlpha = SC_ALPHA_NOALPHA;
+	vsPrint.selAdditionalAlpha = SC_ALPHA_NOALPHA;
 	vsPrint.whitespaceBackgroundSet = false;
 	vsPrint.whitespaceForegroundSet = false;
 	vsPrint.showCaretLineBackground = false;
@@ -3375,8 +3546,6 @@
 		LineLayout ll(8000);
 		LayoutLine(lineDoc, surfaceMeasure, vsPrint, &ll, widthPrint);
 
-		ll.selStart = -1;
-		ll.selEnd = -1;
 		ll.containsCaret = false;
 
 		PRectangle rcLine;
@@ -3499,6 +3668,15 @@
 	}
 }
 
+int Editor::InsertSpace(int position, unsigned int spaces) {
+	if (spaces > 0) {
+		std::string spaceText(spaces, ' ');
+		pdoc->InsertString(position, spaceText.c_str(), spaces);
+		position += spaces;
+	}
+	return position;
+}
+
 void Editor::AddChar(char ch) {
 	char s[2];
 	s[0] = ch;
@@ -3506,63 +3684,60 @@
 	AddCharUTF(s, 1);
 }
 
+void Editor::FilterSelections() {
+	if (!additionalSelectionTyping && (sel.Count() > 1)) {
+		SelectionRange rangeOnly = sel.RangeMain();
+		InvalidateSelection(rangeOnly, true);
+		sel.SetSelection(rangeOnly);
+	}
+}
+
 // AddCharUTF inserts an array of bytes which may or may not be in UTF-8.
 void Editor::AddCharUTF(char *s, unsigned int len, bool treatAsDBCS) {
-	bool wasSelection = currentPos != anchor;
-    if(wasSelection && selType == selRectangle ) {
-        int startPos;
-        int endPos;
-
-        int c1 = pdoc->GetColumn(currentPos);
-        int c2 = pdoc->GetColumn(anchor);
-        int offset = c1 < c2 ? c1 : c2;
-
-        pdoc->BeginUndoAction();
-        SelectionLineIterator lineIterator(this, false);
-        while (lineIterator.Iterate()) {
-            startPos = lineIterator.startPos;
-            endPos   = lineIterator.endPos;
-
-            if(pdoc->GetColumn(endPos) >= offset){
-                unsigned int chars = endPos - startPos;
-                if (0 != chars) {
-                    pdoc->DeleteChars(startPos, chars);
-                }
-                pdoc->InsertString(startPos, s, len);
-            }
-        }
-        anchor += len;
-        currentPos += len;
-        SetRectangularRange();
-        pdoc->EndUndoAction();
-
-    } else {
-		ClearSelection();
-		bool charReplaceAction = false;
-		if (inOverstrike && !wasSelection && !RangeContainsProtected(currentPos, currentPos + 1)) {
-			if (currentPos < (pdoc->Length())) {
-				if (!IsEOLChar(pdoc->CharAt(currentPos))) {
-					charReplaceAction = true;
-					pdoc->BeginUndoAction();
-					pdoc->DelChar(currentPos);
+	FilterSelections();
+	{
+		UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty() || inOverstrike);
+		for (size_t r=0; r<sel.Count(); r++) {
+			if (!RangeContainsProtected(sel.Range(r).Start().Position(),
+				sel.Range(r).End().Position())) {
+				int positionInsert = sel.Range(r).Start().Position();
+				if (!sel.Range(r).Empty()) {
+					if (sel.Range(r).Length()) {
+						pdoc->DeleteChars(positionInsert, sel.Range(r).Length());
+						sel.Range(r).ClearVirtualSpace();
+					} else {
+						// Range is all virtual so collapse to start of virtual space
+						sel.Range(r).MinimizeVirtualSpace();
+					}
+				} else if (inOverstrike) {
+					if (positionInsert < pdoc->Length()) {
+						if (!IsEOLChar(pdoc->CharAt(positionInsert))) {
+							pdoc->DelChar(positionInsert);
+							sel.Range(r).ClearVirtualSpace();
+						}
+					}
 				}
+				positionInsert = InsertSpace(positionInsert, sel.Range(r).caret.VirtualSpace());
+				if (pdoc->InsertString(positionInsert, s, len)) {
+					sel.Range(r).caret.SetPosition(positionInsert + len);
+					sel.Range(r).anchor.SetPosition(positionInsert + len);
+				}
+				sel.Range(r).ClearVirtualSpace();
+				// If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
+				if (wrapState != eWrapNone) {
+					AutoSurface surface(this);
+					if (surface) {
+						WrapOneLine(surface, pdoc->LineFromPosition(positionInsert));
+					}
+				}
 			}
 		}
-		if (pdoc->InsertString(currentPos, s, len)) {
-			SetEmptySelection(currentPos + len);
-		}
-		if (charReplaceAction) {
-			pdoc->EndUndoAction();
-		}
 	}
-	// If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
 	if (wrapState != eWrapNone) {
-		AutoSurface surface(this);
-		if (surface) {
-			WrapOneLine(surface, pdoc->LineFromPosition(currentPos));
-		}
 		SetScrollBars();
 	}
+	ThinRectangularRange();
+	// If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
 	EnsureCaretVisible();
 	// Avoid blinking during rapid typing:
 	ShowCaretAtCurrentPosition();
@@ -3606,48 +3781,44 @@
 		}
 		NotifyChar(byte);
 	}
+
+	if (recordingMacro) {
+		NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(s));
+	}
 }
 
 void Editor::ClearSelection() {
-	if (!SelectionContainsProtected()) {
-		int startPos = SelectionStart();
-		if (selType == selStream) {
-			unsigned int chars = SelectionEnd() - startPos;
-			if (0 != chars) {
-				pdoc->BeginUndoAction();
-				pdoc->DeleteChars(startPos, chars);
-				pdoc->EndUndoAction();
+	if (!sel.IsRectangular())
+		FilterSelections();
+	UndoGroup ug(pdoc);
+	for (size_t r=0; r<sel.Count(); r++) {
+		if (!sel.Range(r).Empty()) {
+			if (!RangeContainsProtected(sel.Range(r).Start().Position(),
+				sel.Range(r).End().Position())) {
+				pdoc->DeleteChars(sel.Range(r).Start().Position(),
+					sel.Range(r).Length());
+				sel.Range(r) = sel.Range(r).Start();
 			}
-		} else {
-			pdoc->BeginUndoAction();
-			SelectionLineIterator lineIterator(this, false);
-			while (lineIterator.Iterate()) {
-				startPos = lineIterator.startPos;
-				unsigned int chars = lineIterator.endPos - startPos;
-				if (0 != chars) {
-					pdoc->DeleteChars(startPos, chars);
-				}
-			}
-			pdoc->EndUndoAction();
-			selType = selStream;
 		}
-		SetEmptySelection(startPos);
 	}
+	ThinRectangularRange();
+	sel.RemoveDuplicates();
+	ClaimSelection();
 }
 
 void Editor::ClearAll() {
-	pdoc->BeginUndoAction();
-	if (0 != pdoc->Length()) {
-		pdoc->DeleteChars(0, pdoc->Length());
+	{
+		UndoGroup ug(pdoc);
+		if (0 != pdoc->Length()) {
+			pdoc->DeleteChars(0, pdoc->Length());
+		}
+		if (!pdoc->IsReadOnly()) {
+			cs.Clear();
+			pdoc->AnnotationClearAll();
+			pdoc->MarginClearAll();
+		}
 	}
-	if (!pdoc->IsReadOnly()) {
-		cs.Clear();
-		pdoc->AnnotationClearAll();
-		pdoc->MarginClearAll();
-	}
-	pdoc->EndUndoAction();
-	anchor = 0;
-	currentPos = 0;
+	sel.Clear();
 	SetTopLine(0);
 	SetVerticalScrollPos();
 	InvalidateStyleRedraw();
@@ -3684,15 +3855,20 @@
 	}
 }
 
-void Editor::PasteRectangular(int pos, const char *ptr, int len) {
+void Editor::PasteRectangular(SelectionPosition pos, const char *ptr, int len) {
 	if (pdoc->IsReadOnly() || SelectionContainsProtected()) {
 		return;
 	}
-	currentPos = pos;
-	int xInsert = XFromPosition(currentPos);
-	int line = pdoc->LineFromPosition(currentPos);
+	sel.Clear();
+	sel.RangeMain() = SelectionRange(pos);
+	int line = pdoc->LineFromPosition(sel.MainCaret());
+	UndoGroup ug(pdoc);
+	sel.RangeMain().caret = SelectionPosition(
+		InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
+	int xInsert = XFromPosition(sel.RangeMain().caret);
 	bool prevCr = false;
-	pdoc->BeginUndoAction();
+	while ((len > 0) && IsEOLChar(ptr[len-1]))
+		len--;
 	for (int i = 0; i < len; i++) {
 		if (IsEOLChar(ptr[i])) {
 			if ((ptr[i] == '\r') || (!prevCr))
@@ -3704,21 +3880,20 @@
 					pdoc->InsertChar(pdoc->Length(), '\n');
 			}
 			// Pad the end of lines with spaces if required
-			currentPos = PositionFromLineX(line, xInsert);
-			if ((XFromPosition(currentPos) < xInsert) && (i + 1 < len)) {
-				while (XFromPosition(currentPos) < xInsert) {
-					pdoc->InsertChar(currentPos, ' ');
-					currentPos++;
+			sel.RangeMain().caret.SetPosition(PositionFromLineX(line, xInsert));
+			if ((XFromPosition(sel.MainCaret()) < xInsert) && (i + 1 < len)) {
+				while (XFromPosition(sel.MainCaret()) < xInsert) {
+					pdoc->InsertChar(sel.MainCaret(), ' ');
+					sel.RangeMain().caret.Add(1);
 				}
 			}
 			prevCr = ptr[i] == '\r';
 		} else {
-			pdoc->InsertString(currentPos, ptr + i, 1);
-			currentPos++;
+			pdoc->InsertString(sel.MainCaret(), ptr + i, 1);
+			sel.RangeMain().caret.Add(1);
 			prevCr = false;
 		}
 	}
-	pdoc->EndUndoAction();
 	SetEmptySelection(pos);
 }
 
@@ -3727,44 +3902,33 @@
 }
 
 void Editor::Clear() {
-    bool wasSelection = currentPos != anchor;
-    if(wasSelection && selType == selRectangle ) {
-        int startPos;
-        int endPos;
-
-        int c1 = pdoc->GetColumn(currentPos);
-        int c2 = pdoc->GetColumn(anchor);
-        int offset = c1 < c2 ? c1 : c2;
-
-        pdoc->BeginUndoAction();
-        SelectionLineIterator lineIterator(this, false);
-        while (lineIterator.Iterate()) {
-            startPos = lineIterator.startPos;
-            endPos   = lineIterator.endPos;
-
-            if(pdoc->GetColumn(endPos) >= offset){
-                unsigned int chars = endPos - startPos;
-                if (0 != chars) {
-                    pdoc->DeleteChars(startPos, chars);
-                } else
-		    pdoc->DelChar(startPos);
-            }
-        }
-        SetRectangularRange();
-        pdoc->EndUndoAction();
-
-    } else if (currentPos == anchor) {
-		if (!RangeContainsProtected(currentPos, currentPos + 1)) {
-			DelChar();
+	UndoGroup ug(pdoc);
+	// If multiple selections, don't delete EOLS
+	if (sel.Empty()) {
+		for (size_t r=0; r<sel.Count(); r++) {
+			if (!RangeContainsProtected(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1)) {
+				if (sel.Range(r).Start().VirtualSpace()) {
+					if (sel.Range(r).anchor < sel.Range(r).caret)
+						sel.Range(r) = SelectionPosition(InsertSpace(sel.Range(r).anchor.Position(), sel.Range(r).anchor.VirtualSpace()));
+					else
+						sel.Range(r) = SelectionPosition(InsertSpace(sel.Range(r).caret.Position(), sel.Range(r).caret.VirtualSpace()));
+				}
+				if ((sel.Count() == 1) || !IsEOLChar(pdoc->CharAt(sel.Range(r).caret.Position()))) {
+					pdoc->DelChar(sel.Range(r).caret.Position());
+					sel.Range(r).ClearVirtualSpace();
+				}  // else multiple selection so don't eat line ends
+			} else {
+				sel.Range(r).ClearVirtualSpace();
+			}
 		}
 	} else {
 		ClearSelection();
 	}
-	if( !wasSelection )
-	    SetEmptySelection(currentPos);
+	sel.RemoveDuplicates();
 }
 
 void Editor::SelectAll() {
+	sel.Clear();
 	SetSelection(0, pdoc->Length());
 	Redraw();
 }
@@ -3789,65 +3953,54 @@
 }
 
 void Editor::DelChar() {
-	if (!RangeContainsProtected(currentPos, currentPos + 1)) {
-		pdoc->DelChar(currentPos);
+	if (!RangeContainsProtected(sel.MainCaret(), sel.MainCaret() + 1)) {
+		pdoc->DelChar(sel.MainCaret());
 	}
 	// Avoid blinking during rapid typing:
 	ShowCaretAtCurrentPosition();
 }
 
 void Editor::DelCharBack(bool allowLineStartDeletion) {
-	bool wasSelection = currentPos != anchor;
-    if(wasSelection && selType == selRectangle ) {
-        int startPos;
-        int endPos;
-
-        int c1 = pdoc->GetColumn(currentPos);
-        int c2 = pdoc->GetColumn(anchor);
-        int offset = c1 < c2 ? c1 : c2;
-
-        pdoc->BeginUndoAction();
-        SelectionLineIterator lineIterator(this, false);
-        while (lineIterator.Iterate()) {
-            startPos = lineIterator.startPos;
-            endPos   = lineIterator.endPos;
-
-            if(pdoc->GetColumn(endPos) >= offset){
-                unsigned int chars = endPos - startPos;
-                if (0 != chars) {
-                    pdoc->DeleteChars(startPos, chars);
-                } else
-		    pdoc->DelCharBack(startPos);
-            }
-        }
-        SetRectangularRange();
-        pdoc->EndUndoAction();
-
-    } else 	if (currentPos == anchor) {
-		if (!RangeContainsProtected(currentPos - 1, currentPos)) {
-			int lineCurrentPos = pdoc->LineFromPosition(currentPos);
-			if (allowLineStartDeletion || (pdoc->LineStart(lineCurrentPos) != currentPos)) {
-				if (pdoc->GetColumn(currentPos) <= pdoc->GetLineIndentation(lineCurrentPos) &&
-				        pdoc->GetColumn(currentPos) > 0 && pdoc->backspaceUnindents) {
-					pdoc->BeginUndoAction();
-					int indentation = pdoc->GetLineIndentation(lineCurrentPos);
-					int indentationStep = pdoc->IndentSize();
-					if (indentation % indentationStep == 0) {
-						pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
-					} else {
-						pdoc->SetLineIndentation(lineCurrentPos, indentation - (indentation % indentationStep));
+	if (!sel.IsRectangular())
+		FilterSelections();
+	if (sel.IsRectangular())
+		allowLineStartDeletion = false;
+	UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty());
+	if (sel.Empty()) {
+		for (size_t r=0; r<sel.Count(); r++) {
+			if (!RangeContainsProtected(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1)) {
+				if (sel.Range(r).caret.VirtualSpace()) {
+					sel.Range(r).caret.SetVirtualSpace(sel.Range(r).caret.VirtualSpace() - 1);
+					sel.Range(r).anchor.SetVirtualSpace(sel.Range(r).caret.VirtualSpace());
+				} else {
+					int lineCurrentPos = pdoc->LineFromPosition(sel.Range(r).caret.Position());
+					if (allowLineStartDeletion || (pdoc->LineStart(lineCurrentPos) != sel.Range(r).caret.Position())) {
+						if (pdoc->GetColumn(sel.Range(r).caret.Position()) <= pdoc->GetLineIndentation(lineCurrentPos) &&
+								pdoc->GetColumn(sel.Range(r).caret.Position()) > 0 && pdoc->backspaceUnindents) {
+							UndoGroup ugInner(pdoc, !ug.Needed());
+							int indentation = pdoc->GetLineIndentation(lineCurrentPos);
+							int indentationStep = pdoc->IndentSize();
+							if (indentation % indentationStep == 0) {
+								pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
+							} else {
+								pdoc->SetLineIndentation(lineCurrentPos, indentation - (indentation % indentationStep));
+							}
+							// SetEmptySelection
+							sel.Range(r) = SelectionRange(pdoc->GetLineIndentPosition(lineCurrentPos),
+								pdoc->GetLineIndentPosition(lineCurrentPos));
+						} else {
+							pdoc->DelCharBack(sel.Range(r).caret.Position());
+						}
 					}
-					SetEmptySelection(pdoc->GetLineIndentPosition(lineCurrentPos));
-					pdoc->EndUndoAction();
-				} else {
-					pdoc->DelCharBack(currentPos);
 				}
+			} else {
+				sel.Range(r).ClearVirtualSpace();
 			}
 		}
 	} else {
 		ClearSelection();
-		SetEmptySelection(currentPos);
 	}
+	sel.RemoveDuplicates();
 	// Avoid blinking during rapid typing:
 	ShowCaretAtCurrentPosition();
 }
@@ -3870,12 +4023,6 @@
 	scn.nmhdr.code = SCN_CHARADDED;
 	scn.ch = ch;
 	NotifyParent(scn);
-	if (recordingMacro) {
-		char txt[2];
-		txt[0] = static_cast<char>(ch);
-		txt[1] = '\0';
-		NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(txt));
-	}
 }
 
 void Editor::NotifySavePoint(bool isSavePoint) {
@@ -4072,13 +4219,11 @@
 	} else {
 		// Move selection and brace highlights
 		if (mh.modificationType & SC_MOD_INSERTTEXT) {
-			currentPos = MovePositionForInsertion(currentPos, mh.position, mh.length);
-			anchor = MovePositionForInsertion(anchor, mh.position, mh.length);
+			sel.MovePositions(true, mh.position, mh.length);
 			braces[0] = MovePositionForInsertion(braces[0], mh.position, mh.length);
 			braces[1] = MovePositionForInsertion(braces[1], mh.position, mh.length);
 		} else if (mh.modificationType & SC_MOD_DELETETEXT) {
-			currentPos = MovePositionForDeletion(currentPos, mh.position, mh.length);
-			anchor = MovePositionForDeletion(anchor, mh.position, mh.length);
+			sel.MovePositions(false, mh.position, mh.length);
 			braces[0] = MovePositionForDeletion(braces[0], mh.position, mh.length);
 			braces[1] = MovePositionForDeletion(braces[1], mh.position, mh.length);
 		}
@@ -4310,11 +4455,11 @@
  * If stuttered = true and not already at first/last row, move to first/last row of window.
  * If stuttered = true and already at first/last row, scroll as normal.
  */
-void Editor::PageMove(int direction, selTypes sel, bool stuttered) {
+void Editor::PageMove(int direction, Selection::selTypes selt, bool stuttered) {
 	int topLineNew, newPos;
 
 	// I consider only the caretYSlop, and ignore the caretYPolicy-- is that a problem?
-	int currentLine = pdoc->LineFromPosition(currentPos);
+	int currentLine = pdoc->LineFromPosition(sel.MainCaret());
 	int topStutterLine = topLine + caretYSlop;
 	int bottomStutterLine =
 	    pdoc->LineFromPosition(PositionFromLocation(
@@ -4330,7 +4475,7 @@
 		newPos = PositionFromLocation(Point(lastXChosen, vs.lineHeight * (LinesToScroll() - caretYSlop)));
 
 	} else {
-		Point pt = LocationFromPosition(currentPos);
+		Point pt = LocationFromPosition(sel.MainCaret());
 
 		topLineNew = Platform::Clamp(
 		            topLine + direction * LinesToScroll(), 0, MaxScrollPos());
@@ -4340,39 +4485,29 @@
 
 	if (topLineNew != topLine) {
 		SetTopLine(topLineNew);
-		MovePositionTo(newPos, sel);
+		MovePositionTo(SelectionPosition(newPos), selt);
 		Redraw();
 		SetVerticalScrollPos();
 	} else {
-		MovePositionTo(newPos, sel);
+		MovePositionTo(SelectionPosition(newPos), selt);
 	}
 }
 
 void Editor::ChangeCaseOfSelection(bool makeUpperCase) {
-	pdoc->BeginUndoAction();
-	int startCurrent = currentPos;
-	int startAnchor = anchor;
-	if (selType == selStream) {
-		pdoc->ChangeCase(Range(SelectionStart(), SelectionEnd()),
-		        makeUpperCase);
-		SetSelection(startCurrent, startAnchor);
-	} else {
-		SelectionLineIterator lineIterator(this, false);
-		while (lineIterator.Iterate()) {
-			pdoc->ChangeCase(
-			    Range(lineIterator.startPos, lineIterator.endPos),
-			    makeUpperCase);
-		}
-		// Would be nicer to keep the rectangular selection but this is complex
-		SetEmptySelection(startCurrent);
+	UndoGroup ug(pdoc);
+	for (size_t r=0; r<sel.Count(); r++) {
+		SelectionRange current = sel.Range(r);
+		pdoc->ChangeCase(Range(current.Start().Position(), current.End().Position()),
+	        makeUpperCase);
+		// Automatic movement cuts off last character so reset to exactly the same as it was.
+		sel.Range(r) = current;
 	}
-	pdoc->EndUndoAction();
 }
 
 void Editor::LineTranspose() {
-	int line = pdoc->LineFromPosition(currentPos);
+	int line = pdoc->LineFromPosition(sel.MainCaret());
 	if (line > 0) {
-		pdoc->BeginUndoAction();
+		UndoGroup ug(pdoc);
 		int startPrev = pdoc->LineStart(line - 1);
 		int endPrev = pdoc->LineEnd(line - 1);
 		int start = pdoc->LineStart(line);
@@ -4385,37 +4520,54 @@
 		pdoc->DeleteChars(startPrev, len1);
 		pdoc->InsertString(startPrev, line2, len2);
 		pdoc->InsertString(start - len1 + len2, line1, len1);
-		MovePositionTo(start - len1 + len2);
+		MovePositionTo(SelectionPosition(start - len1 + len2));
 		delete []line1;
 		delete []line2;
-		pdoc->EndUndoAction();
 	}
 }
 
 void Editor::Duplicate(bool forLine) {
-	int start = SelectionStart();
-	int end = SelectionEnd();
-	if (start == end) {
+	if (sel.Empty()) {
 		forLine = true;
 	}
+	UndoGroup ug(pdoc, sel.Count() > 1);
+	SelectionPosition last;
+	const char *eol = "";
+	int eolLen = 0;
 	if (forLine) {
-		int line = pdoc->LineFromPosition(currentPos);
-		start = pdoc->LineStart(line);
-		end = pdoc->LineEnd(line);
+		eol = StringFromEOLMode(pdoc->eolMode);
+		eolLen = istrlen(eol);
 	}
-	char *text = CopyRange(start, end);
-	if (forLine) {
-		const char *eol = StringFromEOLMode(pdoc->eolMode);
-		pdoc->InsertCString(end, eol);
-		pdoc->InsertString(end + istrlen(eol), text, end - start);
-	} else {
-		pdoc->InsertString(end, text, end - start);
+	for (size_t r=0; r<sel.Count(); r++) {
+		SelectionPosition start = sel.Range(r).Start();
+		SelectionPosition end = sel.Range(r).End();
+		if (forLine) {
+			int line = pdoc->LineFromPosition(sel.Range(r).caret.Position());
+			start = SelectionPosition(pdoc->LineStart(line));
+			end = SelectionPosition(pdoc->LineEnd(line));
+		}
+		char *text = CopyRange(start.Position(), end.Position());
+		if (forLine)
+			pdoc->InsertString(end.Position(), eol, eolLen);
+		pdoc->InsertString(end.Position() + eolLen, text, SelectionRange(end, start).Length());
+		delete []text;
 	}
-	delete []text;
+	if (sel.Count() && sel.IsRectangular()) {
+		SelectionPosition last = sel.Last();
+		if (forLine) {
+			int line = pdoc->LineFromPosition(last.Position());
+			last = SelectionPosition(last.Position() + pdoc->LineStart(line+1) - pdoc->LineStart(line));
+		}
+		if (sel.Rectangular().anchor > sel.Rectangular().caret)
+			sel.Rectangular().anchor = last;
+		else
+			sel.Rectangular().caret = last;
+		SetRectangularRange();
+	}
 }
 
 void Editor::CancelModes() {
-	moveExtendsSelection = false;
+	sel.SetMoveExtends(false);
 }
 
 void Editor::NewLine() {
@@ -4426,10 +4578,16 @@
 	} else if (pdoc->eolMode == SC_EOL_CR) {
 		eol = "\r";
 	} // else SC_EOL_LF -> "\n" already set
-	if (pdoc->InsertCString(currentPos, eol)) {
-		SetEmptySelection(currentPos + istrlen(eol));
+	if (pdoc->InsertCString(sel.MainCaret(), eol)) {
+		SetEmptySelection(sel.MainCaret() + istrlen(eol));
 		while (*eol) {
 			NotifyChar(*eol);
+			if (recordingMacro) {
+				char txt[2];
+				txt[0] = *eol;
+				txt[1] = '\0';
+				NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(txt));
+			}
 			eol++;
 		}
 	}
@@ -4440,41 +4598,50 @@
 	ShowCaretAtCurrentPosition();
 }
 
-void Editor::CursorUpOrDown(int direction, selTypes sel) {
-	Point pt = LocationFromPosition(currentPos);
-	int lineDoc = pdoc->LineFromPosition(currentPos);
+void Editor::CursorUpOrDown(int direction, Selection::selTypes selt) {
+	SelectionPosition caretToUse = sel.Range(sel.Main()).caret;
+	if (sel.IsRectangular()) {
+		if (selt ==  Selection::noSel) {
+			caretToUse = (direction > 0) ? sel.Limits().end : sel.Limits().start;
+		} else {
+			caretToUse = sel.Rectangular().caret;
+		}
+	}
+	Point pt = LocationFromPosition(caretToUse);
+	int lineDoc = pdoc->LineFromPosition(caretToUse.Position());
 	Point ptStartLine = LocationFromPosition(pdoc->LineStart(lineDoc));
 	int subLine = (pt.y - ptStartLine.y) / vs.lineHeight;
 	int commentLines = vs.annotationVisible ? pdoc->AnnotationLines(lineDoc) : 0;
-	int posNew = PositionFromLocation(
-	            Point(lastXChosen, pt.y + direction * vs.lineHeight));
+	SelectionPosition posNew = SPositionFromLocation(
+	            Point(lastXChosen, pt.y + direction * vs.lineHeight), false, false, UserVirtualSpace());
 	if ((direction > 0) && (subLine >= (cs.GetHeight(lineDoc) - 1 - commentLines))) {
-		posNew = PositionFromLocation(
-	            Point(lastXChosen, pt.y + (commentLines + 1) * vs.lineHeight));
+		posNew = SPositionFromLocation(
+	            Point(lastXChosen, pt.y + (commentLines + 1) * vs.lineHeight), false, false, UserVirtualSpace());
 	}
 	if (direction < 0) {
 		// Line wrapping may lead to a location on the same line, so
 		// seek back if that is the case.
 		// There is an equivalent case when moving down which skips
 		// over a line but as that does not trap the user it is fine.
-		Point ptNew = LocationFromPosition(posNew);
-		while ((posNew > 0) && (pt.y == ptNew.y)) {
-			posNew--;
-			ptNew = LocationFromPosition(posNew);
+		Point ptNew = LocationFromPosition(posNew.Position());
+		while ((posNew.Position() > 0) && (pt.y == ptNew.y)) {
+			posNew.Add(- 1);
+			posNew.SetVirtualSpace(0);
+			ptNew = LocationFromPosition(posNew.Position());
 		}
 	}
-	MovePositionTo(posNew, sel);
+	MovePositionTo(posNew, selt);
 }
 
-void Editor::ParaUpOrDown(int direction, selTypes sel) {
-	int lineDoc, savedPos = currentPos;
+void Editor::ParaUpOrDown(int direction, Selection::selTypes selt) {
+	int lineDoc, savedPos = sel.MainCaret();
 	do {
-		MovePositionTo(direction > 0 ? pdoc->ParaDown(currentPos) : pdoc->ParaUp(currentPos), sel);
-		lineDoc = pdoc->LineFromPosition(currentPos);
+		MovePositionTo(SelectionPosition(direction > 0 ? pdoc->ParaDown(sel.MainCaret()) : pdoc->ParaUp(sel.MainCaret())), selt);
+		lineDoc = pdoc->LineFromPosition(sel.MainCaret());
 		if (direction > 0) {
-			if (currentPos >= pdoc->Length() && !cs.GetVisible(lineDoc)) {
-				if (sel == noSel) {
-					MovePositionTo(pdoc->LineEndPosition(savedPos));
+			if (sel.MainCaret() >= pdoc->Length() && !cs.GetVisible(lineDoc)) {
+				if (selt == Selection::noSel) {
+					MovePositionTo(SelectionPosition(pdoc->LineEndPosition(savedPos)));
 				}
 				break;
 			}
@@ -4520,16 +4687,16 @@
 		CursorUpOrDown(1);
 		break;
 	case SCI_LINEDOWNEXTEND:
-		CursorUpOrDown(1, selStream);
+		CursorUpOrDown(1, Selection::selStream);
 		break;
 	case SCI_LINEDOWNRECTEXTEND:
-		CursorUpOrDown(1, selRectangle);
+		CursorUpOrDown(1, Selection::selRectangle);
 		break;
 	case SCI_PARADOWN:
 		ParaUpOrDown(1);
 		break;
 	case SCI_PARADOWNEXTEND:
-		ParaUpOrDown(1, selStream);
+		ParaUpOrDown(1, Selection::selStream);
 		break;
 	case SCI_LINESCROLLDOWN:
 		ScrollTo(topLine + 1);
@@ -4539,144 +4706,182 @@
 		CursorUpOrDown(-1);
 		break;
 	case SCI_LINEUPEXTEND:
-		CursorUpOrDown(-1, selStream);
+		CursorUpOrDown(-1, Selection::selStream);
 		break;
 	case SCI_LINEUPRECTEXTEND:
-		CursorUpOrDown(-1, selRectangle);
+		CursorUpOrDown(-1, Selection::selRectangle);
 		break;
 	case SCI_PARAUP:
 		ParaUpOrDown(-1);
 		break;
 	case SCI_PARAUPEXTEND:
-		ParaUpOrDown(-1, selStream);
+		ParaUpOrDown(-1, Selection::selStream);
 		break;
 	case SCI_LINESCROLLUP:
 		ScrollTo(topLine - 1);
 		MoveCaretInsideView(false);
 		break;
 	case SCI_CHARLEFT:
-		if (SelectionEmpty() || moveExtendsSelection) {
-			MovePositionTo(MovePositionSoVisible(currentPos - 1, -1));
+		if (SelectionEmpty() || sel.MoveExtends()) {
+			if ((sel.Count() == 1) && pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
+				SelectionPosition spCaret = sel.RangeMain().caret;
+				spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
+				MovePositionTo(spCaret);
+			} else {
+				MovePositionTo(MovePositionSoVisible(
+					SelectionPosition((sel.LimitsForRectangularElseMain().start).Position() - 1), -1));
+			}
 		} else {
-			MovePositionTo(SelectionStart());
+			MovePositionTo(sel.LimitsForRectangularElseMain().start);
 		}
 		SetLastXChosen();
 		break;
 	case SCI_CHARLEFTEXTEND:
-		MovePositionTo(MovePositionSoVisible(currentPos - 1, -1), selStream);
+		if (pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
+			SelectionPosition spCaret = sel.RangeMain().caret;
+			spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
+			MovePositionTo(spCaret, Selection::selStream);
+		} else {
+			MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1), Selection::selStream);
+		}
 		SetLastXChosen();
 		break;
 	case SCI_CHARLEFTRECTEXTEND:
-		MovePositionTo(MovePositionSoVisible(currentPos - 1, -1), selRectangle);
+		if (pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
+			SelectionPosition spCaret = sel.RangeMain().caret;
+			spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
+			MovePositionTo(spCaret, Selection::selRectangle);
+		} else {
+			MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1), Selection::selRectangle);
+		}
 		SetLastXChosen();
 		break;
 	case SCI_CHARRIGHT:
-		if (SelectionEmpty() || moveExtendsSelection) {
-			MovePositionTo(MovePositionSoVisible(currentPos + 1, 1));
+		if (SelectionEmpty() || sel.MoveExtends()) {
+			if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(sel.MainCaret())) {
+				SelectionPosition spCaret = sel.RangeMain().caret;
+				spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
+				MovePositionTo(spCaret);
+			} else {
+				MovePositionTo(MovePositionSoVisible(
+					SelectionPosition((sel.LimitsForRectangularElseMain().end).Position() + 1), 1));
+			}
 		} else {
-			MovePositionTo(SelectionEnd());
+			MovePositionTo(sel.LimitsForRectangularElseMain().end);
 		}
 		SetLastXChosen();
 		break;
 	case SCI_CHARRIGHTEXTEND:
-		MovePositionTo(MovePositionSoVisible(currentPos + 1, 1), selStream);
+		if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(sel.MainCaret())) {
+			SelectionPosition spCaret = sel.RangeMain().caret;
+			spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
+			MovePositionTo(spCaret, Selection::selStream);
+		} else {
+			MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1), Selection::selStream);
+		}
 		SetLastXChosen();
 		break;
 	case SCI_CHARRIGHTRECTEXTEND:
-		MovePositionTo(MovePositionSoVisible(currentPos + 1, 1), selRectangle);
+		if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) && pdoc->IsLineEndPosition(sel.MainCaret())) {
+			SelectionPosition spCaret = sel.RangeMain().caret;
+			spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
+			MovePositionTo(spCaret, Selection::selRectangle);
+		} else {
+			MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1), Selection::selRectangle);
+		}
 		SetLastXChosen();
 		break;
 	case SCI_WORDLEFT:
-		MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, -1), -1));
+		MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), -1), -1));
 		SetLastXChosen();
 		break;
 	case SCI_WORDLEFTEXTEND:
-		MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, -1), -1), selStream);
+		MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), -1), -1), Selection::selStream);
 		SetLastXChosen();
 		break;
 	case SCI_WORDRIGHT:
-		MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, 1), 1));
+		MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), 1), 1));
 		SetLastXChosen();
 		break;
 	case SCI_WORDRIGHTEXTEND:
-		MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, 1), 1), selStream);
+		MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), 1), 1), Selection::selStream);
 		SetLastXChosen();
 		break;
 
 	case SCI_WORDLEFTEND:
-		MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(currentPos, -1), -1));
+		MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), -1), -1));
 		SetLastXChosen();
 		break;
 	case SCI_WORDLEFTENDEXTEND:
-		MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(currentPos, -1), -1), selStream);
+		MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), -1), -1), Selection::selStream);
 		SetLastXChosen();
 		break;
 	case SCI_WORDRIGHTEND:
-		MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(currentPos, 1), 1));
+		MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), 1), 1));
 		SetLastXChosen();
 		break;
 	case SCI_WORDRIGHTENDEXTEND:
-		MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(currentPos, 1), 1), selStream);
+		MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), 1), 1), Selection::selStream);
 		SetLastXChosen();
 		break;
 
 	case SCI_HOME:
-		MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(currentPos)));
+		MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
 		SetLastXChosen();
 		break;
 	case SCI_HOMEEXTEND:
-		MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(currentPos)), selStream);
+		MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())), Selection::selStream);
 		SetLastXChosen();
 		break;
 	case SCI_HOMERECTEXTEND:
-		MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(currentPos)), selRectangle);
+		MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())), Selection::selRectangle);
 		SetLastXChosen();
 		break;
 	case SCI_LINEEND:
-		MovePositionTo(pdoc->LineEndPosition(currentPos));
+		MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()));
 		SetLastXChosen();
 		break;
 	case SCI_LINEENDEXTEND:
-		MovePositionTo(pdoc->LineEndPosition(currentPos), selStream);
+		MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()), Selection::selStream);
 		SetLastXChosen();
 		break;
 	case SCI_LINEENDRECTEXTEND:
-		MovePositionTo(pdoc->LineEndPosition(currentPos), selRectangle);
+		MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()), Selection::selRectangle);
 		SetLastXChosen();
 		break;
 	case SCI_HOMEWRAP: {
-			int homePos = MovePositionSoVisible(StartEndDisplayLine(currentPos, true), -1);
-			if (currentPos <= homePos)
-				homePos = pdoc->LineStart(pdoc->LineFromPosition(currentPos));
+			SelectionPosition homePos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
+			if (sel.RangeMain().caret <= homePos)
+				homePos = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
 			MovePositionTo(homePos);
 			SetLastXChosen();
 		}
 		break;
 	case SCI_HOMEWRAPEXTEND: {
-			int homePos = MovePositionSoVisible(StartEndDisplayLine(currentPos, true), -1);
-			if (currentPos <= homePos)
-				homePos = pdoc->LineStart(pdoc->LineFromPosition(currentPos));
-			MovePositionTo(homePos, selStream);
+			SelectionPosition homePos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
+			if (sel.RangeMain().caret <= homePos)
+				homePos = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
+			MovePositionTo(homePos, Selection::selStream);
 			SetLastXChosen();
 		}
 		break;
 	case SCI_LINEENDWRAP: {
-			int endPos = MovePositionSoVisible(StartEndDisplayLine(currentPos, false), 1);
-			int realEndPos = pdoc->LineEndPosition(currentPos);
+			SelectionPosition endPos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), false), 1);
+			SelectionPosition realEndPos = SelectionPosition(pdoc->LineEndPosition(sel.MainCaret()));
 			if (endPos > realEndPos      // if moved past visible EOLs
-			        || currentPos >= endPos) // if at end of display line already
+			        || sel.RangeMain().caret >= endPos) // if at end of display line already
 				endPos = realEndPos;
 			MovePositionTo(endPos);
 			SetLastXChosen();
 		}
 		break;
 	case SCI_LINEENDWRAPEXTEND: {
-			int endPos = MovePositionSoVisible(StartEndDisplayLine(currentPos, false), 1);
-			int realEndPos = pdoc->LineEndPosition(currentPos);
+			SelectionPosition endPos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), false), 1);
+			SelectionPosition realEndPos = SelectionPosition(pdoc->LineEndPosition(sel.MainCaret()));
 			if (endPos > realEndPos      // if moved past visible EOLs
-			        || currentPos >= endPos) // if at end of display line already
+			        || sel.RangeMain().caret >= endPos) // if at end of display line already
 				endPos = realEndPos;
-			MovePositionTo(endPos, selStream);
+			MovePositionTo(endPos, Selection::selStream);
 			SetLastXChosen();
 		}
 		break;
@@ -4685,7 +4890,7 @@
 		SetLastXChosen();
 		break;
 	case SCI_DOCUMENTSTARTEXTEND:
-		MovePositionTo(0, selStream);
+		MovePositionTo(0, Selection::selStream);
 		SetLastXChosen();
 		break;
 	case SCI_DOCUMENTEND:
@@ -4693,38 +4898,38 @@
 		SetLastXChosen();
 		break;
 	case SCI_DOCUMENTENDEXTEND:
-		MovePositionTo(pdoc->Length(), selStream);
+		MovePositionTo(pdoc->Length(), Selection::selStream);
 		SetLastXChosen();
 		break;
 	case SCI_STUTTEREDPAGEUP:
-		PageMove(-1, noSel, true);
+		PageMove(-1, Selection::noSel, true);
 		break;
 	case SCI_STUTTEREDPAGEUPEXTEND:
-		PageMove(-1, selStream, true);
+		PageMove(-1, Selection::selStream, true);
 		break;
 	case SCI_STUTTEREDPAGEDOWN:
-		PageMove(1, noSel, true);
+		PageMove(1, Selection::noSel, true);
 		break;
 	case SCI_STUTTEREDPAGEDOWNEXTEND:
-		PageMove(1, selStream, true);
+		PageMove(1, Selection::selStream, true);
 		break;
 	case SCI_PAGEUP:
 		PageMove(-1);
 		break;
 	case SCI_PAGEUPEXTEND:
-		PageMove(-1, selStream);
+		PageMove(-1, Selection::selStream);
 		break;
 	case SCI_PAGEUPRECTEXTEND:
-		PageMove(-1, selRectangle);
+		PageMove(-1, Selection::selRectangle);
 		break;
 	case SCI_PAGEDOWN:
 		PageMove(1);
 		break;
 	case SCI_PAGEDOWNEXTEND:
-		PageMove(1, selStream);
+		PageMove(1, Selection::selStream);
 		break;
 	case SCI_PAGEDOWNRECTEXTEND:
-		PageMove(1, selRectangle);
+		PageMove(1, Selection::selRectangle);
 		break;
 	case SCI_EDITTOGGLEOVERTYPE:
 		inOverstrike = !inOverstrike;
@@ -4773,21 +4978,21 @@
 		AddChar('\f');
 		break;
 	case SCI_VCHOME:
-		MovePositionTo(pdoc->VCHomePosition(currentPos));
+		MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()));
 		SetLastXChosen();
 		break;
 	case SCI_VCHOMEEXTEND:
-		MovePositionTo(pdoc->VCHomePosition(currentPos), selStream);
+		MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()), Selection::selStream);
 		SetLastXChosen();
 		break;
 	case SCI_VCHOMERECTEXTEND:
-		MovePositionTo(pdoc->VCHomePosition(currentPos), selRectangle);
+		MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()), Selection::selRectangle);
 		SetLastXChosen();
 		break;
 	case SCI_VCHOMEWRAP: {
-			int homePos = pdoc->VCHomePosition(currentPos);
-			int viewLineStart = MovePositionSoVisible(StartEndDisplayLine(currentPos, true), -1);
-			if ((viewLineStart < currentPos) && (viewLineStart > homePos))
+			SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
+			SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
+			if ((viewLineStart < sel.RangeMain().caret) && (viewLineStart > homePos))

@@ Diff output truncated at 100000 characters. @@

This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.



More information about the Commits mailing list