textview.cs

来自「SharpDevelop2.0.0 c#开发免费工具」· CS 代码 · 共 1,093 行 · 第 1/3 页

CS
1,093
字号
						}
						break;
					case TextMarkerType.SolidBlock:
						g.FillRectangle(BrushRegistry.GetBrush(marker.Color), drawingRect);
						break;
				}
			}
			markersToDraw.Clear();
		}
		
		/// <summary>
		/// Get the marker brush (for solid block markers) at a given position.
		/// </summary>
		/// <param name="offset">The offset.</param>
		/// <param name="length">The length.</param>
		/// <param name="markers">All markers that have been found.</param>
		/// <returns>The Brush or null when no marker was found.</returns>
		Brush GetMarkerBrushAt(int offset, int length, ref Color foreColor, out IList<TextMarker> markers)
		{
			markers = Document.MarkerStrategy.GetMarkers(offset, length);
			foreach (TextMarker marker in markers) {
				if (marker.TextMarkerType == TextMarkerType.SolidBlock) {
					if (marker.OverrideForeColor) {
						foreColor = marker.ForeColor;
					}
					return BrushRegistry.GetBrush(marker.Color);
				}
			}
			return null;
		}
		
		float PaintLinePart(Graphics g, int lineNumber, int startColumn, int endColumn, Rectangle lineRectangle, float physicalXPos)
		{
			bool  drawLineMarker  = DrawLineMarkerAtLine(lineNumber);
			Brush backgroundBrush = textArea.Enabled ? GetBgColorBrush(lineNumber) : SystemBrushes.InactiveBorder;
			
			HighlightColor selectionColor = textArea.Document.HighlightingStrategy.GetColorFor("Selection");
			ColumnRange    selectionRange = textArea.SelectionManager.GetSelectionAtLine(lineNumber);
			HighlightColor tabMarkerColor   = textArea.Document.HighlightingStrategy.GetColorFor("TabMarkers");
			HighlightColor spaceMarkerColor = textArea.Document.HighlightingStrategy.GetColorFor("SpaceMarkers");
			
			LineSegment currentLine    = textArea.Document.GetLineSegment(lineNumber);
			
			Brush selectionBackgroundBrush  = BrushRegistry.GetBrush(selectionColor.BackgroundColor);
			
			if (currentLine.Words == null) {
				return physicalXPos;
			}
			
			int currentWordOffset = 0; // we cannot use currentWord.Offset because it is not set on space words
			
			TextWord currentWord;
			TextWord nextCurrentWord = null;
			for (int wordIdx = 0; wordIdx < currentLine.Words.Count; wordIdx++) {
				currentWord = currentLine.Words[wordIdx];
				if (currentWordOffset < startColumn) {
					// TODO: maybe we need to split at startColumn when we support fold markers
					// inside words
					currentWordOffset += currentWord.Length;
					continue;
				}
			repeatDrawCurrentWord:
				//physicalXPos += 10; // leave room between drawn words - useful for debugging the drawing code
				if (currentWordOffset >= endColumn || physicalXPos >= lineRectangle.Right) {
					break;
				}
				int currentWordEndOffset = currentWordOffset + currentWord.Length - 1;
				TextWordType currentWordType = currentWord.Type;
				
				IList<TextMarker> markers;
				Color wordForeColor;
				if (currentWordType == TextWordType.Space)
					wordForeColor = spaceMarkerColor.Color;
				else if (currentWordType == TextWordType.Tab)
					wordForeColor = tabMarkerColor.Color;
				else
					wordForeColor = currentWord.Color;
				Brush wordBackBrush = GetMarkerBrushAt(currentLine.Offset + currentWordOffset, currentWord.Length, ref wordForeColor, out markers);
				
				// It is possible that we have to split the current word because a marker/the selection begins/ends inside it
				if (currentWord.Length > 1) {
					int splitPos = int.MaxValue;
					if (highlight != null) {
						// split both before and after highlight
						if (highlight.OpenBrace.Y == lineNumber) {
							if (highlight.OpenBrace.X >= currentWordOffset && highlight.OpenBrace.X <= currentWordEndOffset) {
								splitPos = Math.Min(splitPos, highlight.OpenBrace.X - currentWordOffset);
							}
						}
						if (highlight.CloseBrace.Y == lineNumber) {
							if (highlight.CloseBrace.X >= currentWordOffset && highlight.CloseBrace.X <= currentWordEndOffset) {
								splitPos = Math.Min(splitPos, highlight.CloseBrace.X - currentWordOffset);
							}
						}
						if (splitPos == 0) {
							splitPos = 1; // split after highlight
						}
					}
					if (endColumn < currentWordEndOffset) { // split when endColumn is reached
						splitPos = Math.Min(splitPos, endColumn - currentWordOffset);
					}
					if (selectionRange.StartColumn > currentWordOffset && selectionRange.StartColumn <= currentWordEndOffset) {
						splitPos = Math.Min(splitPos, selectionRange.StartColumn - currentWordOffset);
					} else if (selectionRange.EndColumn > currentWordOffset && selectionRange.EndColumn <= currentWordEndOffset) {
						splitPos = Math.Min(splitPos, selectionRange.EndColumn - currentWordOffset);
					}
					foreach (TextMarker marker in markers) {
						int markerColumn = marker.Offset - currentLine.Offset;
						int markerEndColumn = marker.EndOffset - currentLine.Offset + 1; // make end offset exclusive
						if (markerColumn > currentWordOffset && markerColumn <= currentWordEndOffset) {
							splitPos = Math.Min(splitPos, markerColumn - currentWordOffset);
						} else if (markerEndColumn > currentWordOffset && markerEndColumn <= currentWordEndOffset) {
							splitPos = Math.Min(splitPos, markerEndColumn - currentWordOffset);
						}
					}
					if (splitPos != int.MaxValue) {
						if (nextCurrentWord != null)
							throw new ApplicationException("split part invalid: first part cannot be splitted further");
						nextCurrentWord = TextWord.Split(ref currentWord, splitPos);
						goto repeatDrawCurrentWord; // get markers for first word part
					}
				}
				
				// get colors from selection status:
				if (ColumnRange.WholeColumn.Equals(selectionRange) || (selectionRange.StartColumn <= currentWordOffset
				                                                       && selectionRange.EndColumn > currentWordEndOffset))
				{
					// word is completely selected
					wordBackBrush = selectionBackgroundBrush;
					if (selectionColor.HasForeground) {
						wordForeColor = selectionColor.Color;
					}
				} else if (drawLineMarker) {
					wordBackBrush = backgroundBrush;
				}
				
				if (wordBackBrush == null) { // use default background if no other background is set
					if (currentWord.SyntaxColor != null && currentWord.SyntaxColor.HasBackground)
						wordBackBrush = BrushRegistry.GetBrush(currentWord.SyntaxColor.BackgroundColor);
					else
						wordBackBrush = backgroundBrush;
				}
				
				RectangleF wordRectangle;
				
				if (currentWord.Type == TextWordType.Space) {
					++physicalColumn;
					
					wordRectangle = new RectangleF(physicalXPos, lineRectangle.Y, (float)Math.Ceiling(SpaceWidth), lineRectangle.Height);
					g.FillRectangle(wordBackBrush, wordRectangle);
					
					if (TextEditorProperties.ShowSpaces) {
						DrawSpaceMarker(g, wordForeColor, physicalXPos, lineRectangle.Y);
					}
					physicalXPos += SpaceWidth;
				} else if (currentWord.Type == TextWordType.Tab) {
					
					physicalColumn += TextEditorProperties.TabIndent;
					physicalColumn = (physicalColumn / TextEditorProperties.TabIndent) * TextEditorProperties.TabIndent;
					// go to next tabstop
					float physicalTabEnd = (int)((physicalXPos + MinTabWidth - lineRectangle.X)
					                             / WideSpaceWidth / TextEditorProperties.TabIndent)
						* WideSpaceWidth * TextEditorProperties.TabIndent + lineRectangle.X;
					physicalTabEnd += WideSpaceWidth * TextEditorProperties.TabIndent;
					
					wordRectangle = new RectangleF(physicalXPos, lineRectangle.Y, (float)Math.Ceiling(physicalTabEnd - physicalXPos), lineRectangle.Height);
					g.FillRectangle(wordBackBrush, wordRectangle);
					
					if (TextEditorProperties.ShowTabs) {
						DrawTabMarker(g, wordForeColor, physicalXPos, lineRectangle.Y);
					}
					physicalXPos = physicalTabEnd;
				} else {
					float wordWidth = DrawDocumentWord(g,
					                                   currentWord.Word,
					                                   new Point((int)physicalXPos, lineRectangle.Y),
					                                   currentWord.Font,
					                                   wordForeColor,
					                                   wordBackBrush);
					wordRectangle = new RectangleF(physicalXPos, lineRectangle.Y, wordWidth, lineRectangle.Height);
					physicalXPos += wordWidth;
				}
				foreach (TextMarker marker in markers) {
					if (marker.TextMarkerType != TextMarkerType.SolidBlock) {
						DrawMarker(g, marker, wordRectangle);
					}
				}
				
				// draw bracket highlight
				if (highlight != null) {
					if (highlight.OpenBrace.Y == lineNumber && highlight.OpenBrace.X == currentWordOffset ||
					    highlight.CloseBrace.Y == lineNumber && highlight.CloseBrace.X == currentWordOffset) {
						DrawBracketHighlight(g, new Rectangle((int)wordRectangle.X, lineRectangle.Y, (int)wordRectangle.Width - 1, lineRectangle.Height - 1));
					}
				}
				
				currentWordOffset += currentWord.Length;
				if (nextCurrentWord != null) {
					currentWord = nextCurrentWord;
					nextCurrentWord = null;
					goto repeatDrawCurrentWord;
				}
			}
			if (physicalXPos < lineRectangle.Right && endColumn >= currentLine.Length) {
				// draw markers at line end
				IList<TextMarker> markers = Document.MarkerStrategy.GetMarkers(currentLine.Offset + currentLine.Length);
				foreach (TextMarker marker in markers) {
					if (marker.TextMarkerType != TextMarkerType.SolidBlock) {
						DrawMarker(g, marker, new RectangleF(physicalXPos, lineRectangle.Y, WideSpaceWidth, lineRectangle.Height));
					}
				}
			}
			return physicalXPos;
		}
		
		float DrawDocumentWord(Graphics g, string word, Point position, Font font, Color foreColor, Brush backBrush)
		{
			if (word == null || word.Length == 0) {
				return 0f;
			}
			
			if (word.Length > MaximumWordLength) {
				float width = 0;
				for (int i = 0; i < word.Length; i += MaximumWordLength) {
					Point pos = position;
					pos.X += (int)width;
					if (i + MaximumWordLength < word.Length)
						width += DrawDocumentWord(g, word.Substring(i, MaximumWordLength), pos, font, foreColor, backBrush);
					else
						width += DrawDocumentWord(g, word.Substring(i, word.Length - i), pos, font, foreColor, backBrush);
				}
				return width;
			}
			
			float wordWidth = MeasureStringWidth(g, word, font);
			
			//num = ++num % 3;
			g.FillRectangle(backBrush, //num == 0 ? Brushes.LightBlue : num == 1 ? Brushes.LightGreen : Brushes.Yellow,
			                new RectangleF(position.X, position.Y, (float)Math.Ceiling(wordWidth + 1), FontHeight));
			
			g.DrawString(word,
			             font,
			             BrushRegistry.GetBrush(foreColor),
			             position.X,
			             position.Y,
			             measureStringFormat);
			return wordWidth;
		}
		
		struct WordFontPair {
			string word;
			Font font;
			public WordFontPair(string word, Font font) {
				this.word = word;
				this.font = font;
			}
			public override bool Equals(object obj) {
				WordFontPair myWordFontPair = (WordFontPair)obj;
				if (!word.Equals(myWordFontPair.word)) return false;
				return font.Equals(myWordFontPair.font);
			}
			
			public override int GetHashCode() {
				return word.GetHashCode() ^ font.GetHashCode();
			}
		}
		
		Dictionary<WordFontPair, float> measureCache = new Dictionary<WordFontPair, float>();
		
		// split words after 1000 characters. Fixes GDI+ crash on very longs words, for example
		// a 100 KB Base64-file without any line breaks.
		const int MaximumWordLength = 1000;
		
		float MeasureStringWidth(Graphics g, string word, Font font)
		{
			float width;
			
			if (word == null || word.Length == 0)
				return 0;
			if (word.Length > MaximumWordLength) {
				width = 0;
				for (int i = 0; i < word.Length; i += MaximumWordLength) {
					if (i + MaximumWordLength < word.Length)
						width += MeasureStringWidth(g, word.Substring(i, MaximumWordLength), font);
					else
						width += MeasureStringWidth(g, word.Substring(i, word.Length - i), font);
				}
				return width;
			}
			if (measureCache.TryGetValue(new WordFontPair(word, font), out width)) {
				return width;
			}
			if (measureCache.Count > 1000) {
				measureCache.Clear();
			}
			
			// This code here provides better results than MeasureString!
			// Example line that is measured wrong:
			// txt.GetPositionFromCharIndex(txt.SelectionStart)
			// (Verdana 10, highlighting makes GetP... bold) -> note the space between 'x' and '('
			// this also fixes "jumping" characters when selecting in non-monospace fonts
			Rectangle rect = new Rectangle(0, 0, 32768, 1000);
			CharacterRange[] ranges = { new CharacterRange(0, word.Length) };
			Region[] regions = new Region[1];
			measureStringFormat.SetMeasurableCharacterRanges (ranges);
			regions = g.MeasureCharacterRanges (word, font, rect, measureStringFormat);
			width = regions[0].GetBounds(g).Right;
			measureCache.Add(new WordFontPair(word, font), width);
			return width;
		}
		#endregion
		
		#region Conversion Functions
		Dictionary<Font, Dictionary<char, float>> fontBoundCharWidth = new Dictionary<Font, Dictionary<char, float>>();
		
		public float GetWidth(char ch, Font font)
		{
			if (!fontBoundCharWidth.ContainsKey(font)) {
				fontBoundCharWidth.Add(font, new Dictionary<char, float>());
			}
			if (!fontBoundCharWidth[font].ContainsKey(ch)) {
				using (Graphics g = textArea.CreateGraphics()) {
					return GetWidth(g, ch, font);
				}
			}
			return (float)fontBoundCharWidth[font][ch];
		}
		
		public float GetWidth(Graphics g, char ch, Font font)
		{
			if (!fontBoundCharWidth.ContainsKey(font)) {
				fontBoundCharWidth.Add(font, new Dictionary<char, float>());
			}
			if (!fontBoundCharWidth[font].ContainsKey(ch)) {
				//Console.WriteLine("Calculate character width: " + ch);
				fontBoundCharWidth[font].Add(ch, MeasureStringWidth(g, ch.ToString(), font));
			}
			return (float)fontBoundCharWidth[font][ch];
		}
		
		public int GetVisualColumn(int logicalLine, int logicalColumn)
		{
			int column = 0;
			using (Graphics g = textArea.CreateGraphics()) {
				CountColumns(ref column, 0, logicalColumn, logicalLine, g);
			}
			return column;
		}
		
		public int GetVisualColumnFast(LineSegment line, int logicalColumn)
		{
			int lineOffset = line.Offset;
			int tabIndent = Document.TextEditorProperties.TabIndent;
			int guessedColumn = 0;
			for (int i = 0; i < logicalColumn; ++i) {
				char ch;
				if (i >= line.Length) {
					ch = ' ';
				} else {
					ch = Document.GetCharAt(lineOffset + i);
				}
				switch (ch) {
					case '\t':
						guessedColumn += tabIndent;
						guessedColumn = (guessedColumn / tabIndent) * tabIndent;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?