diff options
Diffstat (limited to 'src/Editor.cxx')
| -rw-r--r-- | src/Editor.cxx | 354 | 
1 files changed, 277 insertions, 77 deletions
diff --git a/src/Editor.cxx b/src/Editor.cxx index cc7b1b40d..11f7a161b 100644 --- a/src/Editor.cxx +++ b/src/Editor.cxx @@ -93,7 +93,7 @@ void LineLayout::SetLineStart(int line, int start) {  	if ((line >= lenLineStarts) && (line != 0)) {  		int newMaxLines = line + 20;  		int *newLineStarts = new int[newMaxLines]; -		if (!newLineStarts)  +		if (!newLineStarts)  			return;  		for (int i=0; i<newMaxLines; i++) {  			if (i < lenLineStarts) @@ -108,7 +108,7 @@ void LineLayout::SetLineStart(int line, int start) {  	lineStarts[line] = start;  } -void LineLayout::SetBracesHighlight(Range rangeLine, Position braces[],  +void LineLayout::SetBracesHighlight(Range rangeLine, Position braces[],  	char bracesMatchStyle, int xHighlight) {  	if (rangeLine.ContainsCharacter(braces[0])) {  		int braceOffset = braces[0] - rangeLine.start; @@ -146,8 +146,8 @@ void LineLayout::RestoreBracesHighlight(Range rangeLine, Position braces[]) {  	xHighlightGuide = 0;  } -LineLayoutCache::LineLayoutCache() :  -	level(0), length(0), size(0), cache(0),  +LineLayoutCache::LineLayoutCache() : +	level(0), length(0), size(0), cache(0),  	allInvalidated(false), styleClock(-1) {  	Allocate(0);  } @@ -224,7 +224,7 @@ void LineLayoutCache::SetLevel(int level_) {  	}  } -LineLayout *LineLayoutCache::Retrieve(int lineNumber, int lineCaret, int maxChars, int styleClock_,  +LineLayout *LineLayoutCache::Retrieve(int lineNumber, int lineCaret, int maxChars, int styleClock_,  	int linesOnScreen, int linesInDoc) {  	AllocateForLevel(linesOnScreen, linesInDoc);  	if (styleClock != styleClock_) { @@ -317,11 +317,11 @@ Editor::Editor() {  	xEndSelect = 0;  	primarySelection = true; -	caretPolicy = CARET_SLOP; -	caretSlop = 0; +	caretXPolicy = CARET_SLOP | CARET_EVEN; +	caretXSlop = 50; -	visiblePolicy = VISIBLE_SLOP; -	visibleSlop = 0; +	caretYPolicy = CARET_EVEN; +	caretYSlop = 0;  	searchAnchor = 0; @@ -621,8 +621,10 @@ int Editor::PositionFromLocationClose(Point pt) {  	return INVALID_POSITION;  } -// Find the document position corresponding to an x coordinate on a particular document line. -// Ensure is between whole characters when document is in multi-byte or UTF-8 mode. +/** + * Find the document position corresponding to an x coordinate on a particular document line. + * Ensure is between whole characters when document is in multi-byte or UTF-8 mode. + */  int Editor::PositionFromLineX(int lineDoc, int x) {  	RefreshStyleData();  	if (lineDoc >= pdoc->LinesTotal()) @@ -930,76 +932,263 @@ int Editor::DisplayFromPosition(int pos) {  	return lineDisplay;  } +/** + * Ensure the caret is reasonably visible in context. + * +Caret policy in SciTE + +If slop is set, we can define a slop value. +This value defines an unwanted zone (UZ) where the caret is... unwanted. +This zone is defined as a number of pixels near the vertical margins, +and as a number of lines near the horizontal margins. +By keeping the caret away from the edges, it is seen within its context, +so it is likely that the identifier that the caret is on can be completely seen, +and that the current line is seen with some of the lines following it which are +often dependent on that line. + +If strict is set, the policy is enforced... strictly. +The caret is centred on the display if slop is not set, +and cannot go in the UZ if slop is set. + +If jumps is set, the display is moved more energetically +so the caret can move in the same direction longer before the policy is applied again. +'3UZ' notation is used to indicate three time the size of the UZ as a distance to the margin. + +If even is not set, instead of having symmetrical UZs, +the left and bottom UZs are extended up to right and top UZs respectively. +This way, we favour the displaying of useful information: the begining of lines, +where most code reside, and the lines after the caret, eg. the body of a function. + +     |        |       |      |                                            | +slop | strict | jumps | even | Caret can go to the margin                 | When reaching limit (caret going out of +     |        |       |      |                                            | visibility or going into the UZ) display is... +-----+--------+-------+------+--------------------------------------------+-------------------------------------------------------------- +  0  |   0    |   0   |   0  | Yes                                        | moved to put caret on top/on right +  0  |   0    |   0   |   1  | Yes                                        | moved by one position +  0  |   0    |   1   |   0  | Yes                                        | moved to put caret on top/on right +  0  |   0    |   1   |   1  | Yes                                        | centred on the caret +  0  |   1    |   -   |   0  | Caret is always on top/on right of display | - +  0  |   1    |   -   |   1  | No, caret is always centred                | - +  1  |   0    |   0   |   0  | Yes                                        | moved to put caret out of the asymmetrical UZ +  1  |   0    |   0   |   1  | Yes                                        | moved to put caret out of the UZ +  1  |   0    |   1   |   0  | Yes                                        | moved to put caret at 3UZ of the top or right margin +  1  |   0    |   1   |   1  | Yes                                        | moved to put caret at 3UZ of the margin +  1  |   1    |   -   |   0  | Caret is always at UZ of top/right margin  | - +  1  |   1    |   0   |   1  | No, kept out of UZ                         | moved by one position +  1  |   1    |   1   |   1  | No, kept out of UZ                         | moved to put caret at 3UZ of the margin +*/  void Editor::EnsureCaretVisible(bool useMargin, bool vert, bool horiz) {  	//Platform::DebugPrintf("EnsureCaretVisible %d %s\n", xOffset, useMargin ? " margin" : " ");  	PRectangle rcClient = GetTextRectangle();  	//int rcClientFullWidth = rcClient.Width();  	int posCaret = currentPos; -	if (posDrag >= 0) +	if (posDrag >= 0) {  		posCaret = posDrag; +	}  	Point pt = LocationFromPosition(posCaret); -	Point ptEOL = LocationFromPosition(pdoc->LineEndPosition(posCaret));  	Point ptBottomCaret = pt; -	int lineCaret = DisplayFromPosition(posCaret);  	ptBottomCaret.y += vs.lineHeight - 1; - -	// Ensure the caret is reasonably visible in context: -	// xMargin must equal to xCaretMargin, with a minimum of 2 and a maximum of -	// slightly less than half the width of the text area. -	int xMargin = Platform::Clamp(xCaretMargin, 2, Platform::Maximum(rcClient.Width() - 10, 4) / 2); -	if (!useMargin) -		xMargin = 2; - -	// If we scroll the display, we use a minimum amount of xMargin. -	int offsetLeft = rcClient.left + xMargin; -	int offsetRight = rcClient.right - xMargin; -	// If we are in XJUMPS mode, then when the margin is reached, the -	// offset jumps so that it won't need to move agin for a while. -	if (!(caretPolicy & CARET_XJUMPS)) { -		rcClient.left = offsetLeft; -		rcClient.right = offsetRight; -	} +	int lineCaret = DisplayFromPosition(posCaret); +	bool bSlop, bStrict, bJump, bEven;  	// Vertical positioning -	if (vert && (!rcClient.Contains(pt) || !rcClient.Contains(ptBottomCaret) || (caretPolicy & CARET_STRICT))) { -		//Platform::DebugPrintf("EnsureCaretVisible move, (%d,%d)(%d,%d)\n", pt.x, pt.y, rcClient.left, rcClient.right); +	if (vert && (pt.y < rcClient.top || ptBottomCaret.y > rcClient.bottom || (caretYPolicy & CARET_STRICT) != 0)) { +		int linesOnScreen = LinesOnScreen(); +		int halfScreen = Platform::Maximum(linesOnScreen - 1, 2) / 2; +		int newTopLine = topLine; +		bSlop = (caretYPolicy & CARET_SLOP) != 0; +		bStrict = (caretYPolicy & CARET_STRICT) != 0; +		bJump = (caretYPolicy & CARET_JUMPS) != 0; +		bEven = (caretYPolicy & CARET_EVEN) != 0; +  		// It should be possible to scroll the window to show the caret,  		// but this fails to remove the caret on GTK+ -		if (caretPolicy & CARET_SLOP) { -			if ((topLine > lineCaret) || ((caretPolicy & CARET_STRICT) && (topLine + caretSlop > lineCaret))) { -				SetTopLine(Platform::Clamp(lineCaret - caretSlop, 0, MaxScrollPos())); -				SetVerticalScrollPos(); -				Redraw(); -			} else if ((lineCaret > topLine + LinesOnScreen() - 1) || -			           ((caretPolicy & CARET_STRICT) && (lineCaret > topLine + LinesOnScreen() - 1 - caretSlop))) { -				SetTopLine(Platform::Clamp(lineCaret - LinesOnScreen() + 1 + caretSlop, 0, MaxScrollPos())); -				SetVerticalScrollPos(); -				Redraw(); +		if (bSlop) {	// A margin is defined +			int yMoveT, yMoveB; +			if (bStrict) { +				int yMarginT, yMarginB; +				if (!useMargin) { +					// In drag mode, avoid moves +					// otherwise, a double click will select several lines. +					yMarginT = yMarginB = 0; +				} else { +					// yMarginT must equal to caretYSlop, with a minimum of 1 and +					// a maximum of slightly less than half the heigth of the text area. +					yMarginT = Platform::Clamp(caretYSlop, 1, halfScreen); +					if (bEven) { +						yMarginB = yMarginT; +					} else { +						yMarginB = linesOnScreen - yMarginT - 1; +					} +				} +				if (bJump) { +					yMoveT = Platform::Clamp(caretYSlop * 3, 1, halfScreen); +				} +				yMoveT = yMarginT; +				if (bEven) { +					if (bJump) { +						yMoveT = Platform::Clamp(caretYSlop * 3, 1, halfScreen); +					} +					yMoveB = yMoveT; +				} else { +					yMoveB = linesOnScreen - yMoveT - 1; +				} +				if (lineCaret < topLine + yMarginT) { +					// Caret goes too high +					newTopLine = lineCaret - yMoveT; +				} else if (lineCaret > topLine + linesOnScreen - 1 - yMarginB) { +					// Caret goes too low +					newTopLine = lineCaret - linesOnScreen + 1 + yMoveB; +				} +			} else {	// Not strict +				yMoveT = bJump ? caretYSlop * 3 : caretYSlop; +				yMoveT = Platform::Clamp(yMoveT, 1, halfScreen); +				if (bEven) { +					yMoveB = yMoveT; +				} else { +					yMoveB = linesOnScreen - yMoveT - 1; +				} +				if (lineCaret < topLine) { +					// Caret goes too high +					newTopLine = lineCaret - yMoveT; +				} else if (lineCaret > topLine + linesOnScreen - 1) { +					// Caret goes too low +					newTopLine = lineCaret - linesOnScreen + 1 + yMoveB; +				}  			} -		} else { -			if ((topLine > lineCaret) || (lineCaret > topLine + LinesOnScreen() - 1) || (caretPolicy & CARET_STRICT)) { -				SetTopLine(Platform::Clamp(lineCaret - LinesOnScreen() / 2 + 1, 0, MaxScrollPos())); -				SetVerticalScrollPos(); -				Redraw(); +		} else {	// No slop +			if (!bStrict && !bJump) { +				// Minimal move +				if (lineCaret < topLine) { +					// Caret goes too high +					newTopLine = lineCaret; +				} else if (lineCaret > topLine + linesOnScreen - 1) { +					// Caret goes too low +					if (bEven) { +						newTopLine = lineCaret - linesOnScreen + 1; +					} else { +						newTopLine = lineCaret; +					} +				} +			} else {	// Strict or going out of display +				if (bEven) { +					// Always center caret +					newTopLine = lineCaret - halfScreen; +				} else { +					// Always put caret on top of display +					newTopLine = lineCaret; +				}  			}  		} +		newTopLine = Platform::Clamp(newTopLine, 0, MaxScrollPos()); +		if (newTopLine != topLine) { +			SetTopLine(newTopLine); +			SetVerticalScrollPos(); +			Redraw(); +		}  	}  	// Horizontal positioning  	if (horiz && (wrapState == eWrapNone)) { +		int halfScreen = Platform::Maximum(rcClient.Width() - 4, 4) / 2;  		int xOffsetNew = xOffset; -		if (pt.x < rcClient.left) { -			xOffsetNew = xOffset - (offsetLeft - pt.x); -		} else if ((!(caretPolicy & CARET_XEVEN) && ((xOffset > 0) && useMargin)) || pt.x >= rcClient.right) { -			xOffsetNew = xOffset + (pt.x - offsetRight); -			int xOffsetEOL = xOffset + (ptEOL.x - offsetRight) - xMargin + 2; -			//Platform::DebugPrintf("Margin %d %d\n", xOffsetNew, xOffsetEOL); -			// Ensure don't scroll out into empty space -			if (xOffsetNew > xOffsetEOL) -				xOffsetNew = xOffsetEOL; -		} -		if (xOffsetNew < 0) +		bSlop = (caretXPolicy & CARET_SLOP) != 0; +		bStrict = (caretXPolicy & CARET_STRICT) != 0; +		bJump = (caretXPolicy & CARET_JUMPS) != 0; +		bEven = (caretXPolicy & CARET_EVEN) != 0; + +		if (bSlop) {	// A margin is defined +			int xMoveL, xMoveR; +			if (bStrict) { +				int xMarginL, xMarginR; +				if (!useMargin) { +					// In drag mode, avoid moves unless very near of the margin +					// otherwise, a simple click will select text. +					xMarginL = xMarginR = 2; +				} else { +					// xMargin must equal to caretXSlop, with a minimum of 2 and +					// a maximum of slightly less than half the width of the text area. +					xMarginR = Platform::Clamp(caretXSlop, 2, halfScreen); +					if (bEven) { +						xMarginL = xMarginR; +					} else { +						xMarginL = rcClient.Width() - xMarginR - 4; +					} +				} +				if (bJump && bEven) { +					// Jump is used only in even mode +					xMoveL = xMoveR = Platform::Clamp(caretXSlop * 3, 1, halfScreen); +				} else { +					xMoveL = xMoveR = 0;	// Not used, avoid a warning +				} +				if (pt.x < rcClient.left + xMarginL) { +					// Caret is on the left of the display +					if (bJump && bEven) { +						xOffsetNew -= xMoveL; +					} else { +						// Move just enough to allow to display the caret +						xOffsetNew -= (rcClient.left + xMarginL) - pt.x; +					} +				} else if (pt.x >= rcClient.right - xMarginR) { +					// Caret is on the right of the display +					if (bJump && bEven) { +						xOffsetNew += xMoveR; +					} else { +						// Move just enough to allow to display the caret +						xOffsetNew += pt.x - (rcClient.right - xMarginR) + 1; +					} +				} +			} else {	// Not strict +				xMoveR = bJump ? caretXSlop * 3 : caretXSlop; +				xMoveR = Platform::Clamp(xMoveR, 1, halfScreen); +				if (bEven) { +					xMoveL = xMoveR; +				} else { +					xMoveL = rcClient.Width() - xMoveR - 4; +				} +				if (pt.x < rcClient.left) { +					// Caret is on the left of the display +					xOffsetNew -= xMoveL; +				} else if (pt.x >= rcClient.right) { +					// Caret is on the right of the display +					xOffsetNew += xMoveR; +				} +			} +		} else {	// No slop +			if (bStrict || +				(bJump && (pt.x < rcClient.left || pt.x >= rcClient.right))) { +				// Strict or going out of display +				if (bEven) { +					// Center caret +					xOffsetNew += pt.x - rcClient.left - halfScreen; +				} else { +					// Put caret on right +					xOffsetNew += pt.x - rcClient.right + 1; +				} +			} else { +				// Move just enough to allow to display the caret +				if (pt.x < rcClient.left) { +					// Caret is on the left of the display +					if (bEven) { +						xOffsetNew -= rcClient.left - pt.x; +					} else { +						xOffsetNew += pt.x - rcClient.right + 1; +					} +				} else if (pt.x >= rcClient.right) { +					// Caret is on the right of the display +					xOffsetNew += pt.x - rcClient.right + 1; +				} +			} +		} +		// In case of a jump (find result) largely out of display, adjust the offset to display the caret +		if (pt.x + xOffset < rcClient.left + xOffsetNew) { +			xOffsetNew = pt.x + xOffset - rcClient.left; +		} else if (pt.x + xOffset >= rcClient.right + xOffsetNew) { +			xOffsetNew = pt.x + xOffset - rcClient.right + 1; +		} +		if (xOffsetNew < 0) {  			xOffsetNew = 0; +		}  		if (xOffset != xOffsetNew) {  			xOffset = xOffsetNew;  			SetHorizontalScrollPos(); @@ -1316,7 +1505,7 @@ LineLayout *Editor::RetrieveLineLayout(int lineNumber) {  	int posLineStart = pdoc->LineStart(lineNumber);  	int posLineEnd = pdoc->LineStart(lineNumber + 1);  	int lineCaret = pdoc->LineFromPosition(currentPos); -	return llc.Retrieve(lineNumber, lineCaret,  +	return llc.Retrieve(lineNumber, lineCaret,  		posLineEnd - posLineStart, pdoc->GetStyleClock(),  		LinesOnScreen() + 1, pdoc->LinesTotal());  } @@ -1342,7 +1531,7 @@ void Editor::LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayou  		} else {  			ll->edgeColumn = -1;  		} -		 +  		int posLineEnd = pdoc->LineStart(line + 1);  		Font &ctrlCharsFont = vstyle.styles[STYLE_CONTROLCHAR].font;  		char styleByte = 0; @@ -1371,7 +1560,7 @@ void Editor::LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayou  		ll->chars[numCharsInLine] = 0;   // Also triggers processing in the loops as this is a control character  		ll->styles[numCharsInLine] = styleByte;	// For eolFilled  		ll->indicators[numCharsInLine] = 0; -	 +  		// Layout the line, determining the position of each character,  		// with an extra element at the end for the end of the line.  		int startseg = 0;	// Start of the current segment, in char. number @@ -1379,7 +1568,7 @@ void Editor::LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayou  		ll->positions[0] = 0;  		unsigned int tabWidth = vstyle.spaceWidth * pdoc->tabInChars;  		bool lastSegItalics = false; -	 +  		for (int charInLine = 0; charInLine < numCharsInLine; charInLine++) {  			if ((ll->styles[charInLine] != ll->styles[charInLine + 1]) ||  				IsControlCharacter(ll->chars[charInLine]) || IsControlCharacter(ll->chars[charInLine + 1])) { @@ -1840,7 +2029,7 @@ void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {  		//Platform::DebugPrintf("Abandoning paint\n");  		if (wrapState != eWrapNone) {  			if (paintAbandonedByStyling) { -				// Styling has spilled over a line end, such as occurs by starting a multiline  +				// Styling has spilled over a line end, such as occurs by starting a multiline  				// comment. The width of subsequent text may have changed, so rewrap.  				NeedWrapping(cs.DocFromDisplay(topLine));  			} @@ -1908,7 +2097,7 @@ void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {  					ll->selEnd = -1;  					ll->containsCaret = false;  				} -	 +  				PRectangle rcLine = rcClient;  				rcLine.top = ypos;  				rcLine.bottom = ypos + vs.lineHeight; @@ -1917,11 +2106,11 @@ void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {  				// Highlight the current braces if any  				ll->SetBracesHighlight(rangeLine, braces, static_cast<char>(bracesMatchStyle),  					highlightGuideColumn * vs.spaceWidth); -	 +  				// Draw the line  				DrawLine(surface, vs, lineDoc, visibleLine, xStart, rcLine, ll, subLine);  				//durPaint += et.Duration(true); -	 +  				// Restore the precvious styles for the brace highlights in case layout is in cache.  				ll->RestoreBracesHighlight(rangeLine, braces); @@ -1940,7 +2129,7 @@ void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {  						surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated);  					}  				} -	 +  				// Draw the Caret  				if (lineDoc == lineCaret) {  					int offset = Platform::Minimum(posCaret - rangeLine.start, ll->maxLineLength); @@ -1979,7 +2168,7 @@ void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {  						}  					}  				} -	 +  				if (bufferedDraw) {  					Point from(vs.fixedColumnWidth, 0);  					PRectangle rcCopyArea(vs.fixedColumnWidth, yposScreen, @@ -1988,7 +2177,7 @@ void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {  				}  				//durCopy += et.Duration(true);  			} -		 +  			if (!bufferedDraw) {  				ypos += vs.lineHeight;  			} @@ -2016,7 +2205,7 @@ void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {  			}  		}  		//Platform::DebugPrintf( -		//"Layout:%9.6g    Paint:%9.6g    Ratio:%9.6g   Copy:%9.6g   Total:%9.6g\n",  +		//"Layout:%9.6g    Paint:%9.6g    Ratio:%9.6g   Copy:%9.6g   Total:%9.6g\n",  		//durLayout, durPaint, durLayout / durPaint, durCopy, etWhole.Duration());  		NotifyPainted();  	} @@ -2249,7 +2438,7 @@ void Editor::AddCharUTF(char *s, unsigned int len, bool treatAsDBCS) {  	SetLastXChosen();  	if (treatAsDBCS) { -		NotifyChar((static_cast<unsigned char>(s[0]) << 8) |  +		NotifyChar((static_cast<unsigned char>(s[0]) << 8) |  			static_cast<unsigned char>(s[1]));  	} else {  		int byte = static_cast<unsigned char>(s[0]); @@ -2905,7 +3094,7 @@ void Editor::CursorUpOrDown(int direction, bool extend) {  	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  +		// 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)) { @@ -3789,7 +3978,7 @@ void Editor::ButtonUp(Point pt, unsigned int curTime, bool ctrl) {  				if (drag.len) {  					if (ctrl) {  						if (pdoc->InsertString(newPos, drag.s, drag.len)) { -							SetSelection(newPos, newPos + drag.len); +						SetSelection(newPos, newPos + drag.len);  						}  					} else if (newPos < selStart) {  						pdoc->DeleteChars(selStart, drag.len); @@ -4178,6 +4367,7 @@ sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {  	case SCI_CLEAR:  		Clear();  		SetLastXChosen(); +		EnsureCaretVisible();  		break;  	case SCI_UNDO: @@ -5086,9 +5276,19 @@ sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {  	case SCI_SEARCHPREV:  		return SearchText(iMessage, wParam, lParam); -	case SCI_SETCARETPOLICY: -		caretPolicy = wParam; -		caretSlop = lParam; +	case SCI_SETCARETPOLICY:	// Deprecated +		caretXPolicy = caretYPolicy = wParam; +		caretXSlop = caretYSlop = lParam; +		break; + +	case SCI_SETXCARETPOLICY: +		caretXPolicy = wParam; +		caretXSlop = lParam; +		break; + +	case SCI_SETYCARETPOLICY: +		caretYPolicy = wParam; +		caretYSlop = lParam;  		break;  	case SCI_SETVISIBLEPOLICY:  | 
