From af3a87ef16e2f4df9fa01a89fd709125c91188d6 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Bruyère Date: Sat, 24 Feb 2018 11:51:12 +0100 Subject: change namespace of CrowIDE to Crow.Coding, move editor source inside, crowIde wip --- CrowIDE/src/SourceEditor/SourceEditor.cs | 1107 ++++++++++++++++++++++++++++++ 1 file changed, 1107 insertions(+) create mode 100644 CrowIDE/src/SourceEditor/SourceEditor.cs (limited to 'CrowIDE/src/SourceEditor/SourceEditor.cs') diff --git a/CrowIDE/src/SourceEditor/SourceEditor.cs b/CrowIDE/src/SourceEditor/SourceEditor.cs new file mode 100644 index 00000000..677ba8b7 --- /dev/null +++ b/CrowIDE/src/SourceEditor/SourceEditor.cs @@ -0,0 +1,1107 @@ +// +// ScrollingTextBox.cs +// +// Author: +// Jean-Philippe Bruyère +// +// Copyright (c) 2013-2017 Jean-Philippe Bruyère +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Xml.Serialization; +using System.ComponentModel; +using System.Collections; +using Cairo; +using System.Text; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using System.Linq; +using System.Diagnostics; +using System.IO; +using System.Threading; + +namespace Crow.Coding +{ + /// + /// Scrolling text box optimized for monospace fonts, for coding + /// + public class SourceEditor : ScrollingObject + { + #region CTOR + public SourceEditor (): base() + { + formatting.Add ((int)XMLParser.TokenType.AttributeName, new TextFormatting (Color.UnitedNationsBlue, Color.Transparent)); + formatting.Add ((int)XMLParser.TokenType.ElementName, new TextFormatting (Color.DarkBlue, Color.Transparent)); + formatting.Add ((int)XMLParser.TokenType.ElementStart, new TextFormatting (Color.Black, Color.Transparent)); + formatting.Add ((int)XMLParser.TokenType.ElementEnd, new TextFormatting (Color.Black, Color.Transparent)); + formatting.Add ((int)XMLParser.TokenType.ElementClosing, new TextFormatting (Color.Black, Color.Transparent)); + formatting.Add ((int)XMLParser.TokenType.Affectation, new TextFormatting (Color.Black, Color.Transparent)); + + formatting.Add ((int)XMLParser.TokenType.AttributeValueOpening, new TextFormatting (Color.Carmine, Color.Transparent)); + formatting.Add ((int)XMLParser.TokenType.AttributeValueClosing, new TextFormatting (Color.Carmine, Color.Transparent)); + formatting.Add ((int)XMLParser.TokenType.AttributeValue, new TextFormatting (Color.OrangeRed, Color.Transparent, false, true)); + formatting.Add ((int)XMLParser.TokenType.XMLDecl, new TextFormatting (Color.GreenCrayola, Color.Transparent)); + formatting.Add ((int)XMLParser.TokenType.BlockComment, new TextFormatting (Color.Gray, Color.Transparent, false, true)); + + parsing.Add (".crow", "Crow.Coding.XMLParser"); + + buffer = new CodeBuffer (); + buffer.LineUpadateEvent += Buffer_LineUpadateEvent; + buffer.LineAdditionEvent += Buffer_LineAdditionEvent;; + buffer.LineRemoveEvent += Buffer_LineRemoveEvent; + buffer.BufferCleared += Buffer_BufferCleared; + buffer.SelectionChanged += Buffer_SelectionChanged; + buffer.PositionChanged += Buffer_PositionChanged; + buffer.FoldingEvent += Buffer_FoldingEvent; + buffer.Add (new CodeLine("")); + + Thread updateSource = new Thread (updateSourceThreadFunc); + updateSource.IsBackground = true; + updateSource.Start (); + } + #endregion + string oldSource = ""; + void updateSourceThreadFunc (){ + while (true) { + if (projNode != null && buffer != null) { + string newSrc = buffer.FullText; + if (projNode.Source != newSrc) + projNode.Source = newSrc; + } + Thread.Sleep (100); + } + } + const int leftMarginGap = 3;//gap between items in margin and text + const int foldSize = 9;//folding rectangles size + + #region private and protected fields + bool foldingEnabled = true; + ProjectFile projNode = null; + int leftMargin = 0; //margin used to display line numbers, folding errors,etc... + int visibleLines = 1; + int visibleColumns = 1; + CodeBuffer buffer; + Parser parser; + Color selBackground; + Color selForeground; +// int _currentCol; //0 based cursor position in string +// int _currentLine; + + + Dictionary formatting = new Dictionary(); + Dictionary parsing = new Dictionary(); + + protected Rectangle rText; + protected FontExtents fe; + protected TextExtents te; + #endregion + + void measureLeftMargin () { + leftMargin = 0; + if (PrintLineNumbers) + leftMargin += (int)Math.Ceiling((double)buffer.LineCount.ToString().Length * fe.MaxXAdvance) +6; + if (foldingEnabled) + leftMargin += foldSize; + if (leftMargin > 0) + leftMargin += leftMarginGap; + updateVisibleColumns (); + } + void findLongestLineAndUpdateMaxScrollX() { + buffer.FindLongestVisualLine (); + MaxScrollX = Math.Max (0, buffer.longestLineCharCount - visibleColumns); + Debug.WriteLine ("SourceEditor: Find Longest line and update maxscrollx: {0} visible cols:{1}", MaxScrollX, visibleColumns); + } + /// + /// Updates visible line in widget, adapt max scroll y and updatePrintedLines + /// + void updateVisibleLines(){ + visibleLines = (int)Math.Floor ((double)ClientRectangle.Height / fe.Height); + NotifyValueChanged ("VisibleLines", visibleLines); + updateMaxScrollY (); + updatePrintedLines (); + System.Diagnostics.Debug.WriteLine ("update visible lines: " + visibleLines); + System.Diagnostics.Debug.WriteLine ("update MaxScrollY: " + MaxScrollY); + } + void updateVisibleColumns(){ + visibleColumns = (int)Math.Floor ((double)(ClientRectangle.Width - leftMargin)/ fe.MaxXAdvance); + MaxScrollX = Math.Max (0, buffer.longestLineCharCount - visibleColumns); + + System.Diagnostics.Debug.WriteLine ("update visible columns: {0} leftMargin:{1}",visibleColumns, leftMargin); + System.Diagnostics.Debug.WriteLine ("update MaxScrollX: " + MaxScrollX); + } + void updateMaxScrollY () { + if (parser == null || !foldingEnabled) { + MaxScrollY = Math.Max (0, buffer.LineCount - visibleLines); + if (buffer.UnfoldedLines > 0) + NotifyValueChanged ("ChildHeightRatio", Slot.Height * visibleLines / buffer.UnfoldedLines); + } else { + MaxScrollY = Math.Max (0, buffer.UnfoldedLines - visibleLines); + if (buffer.UnfoldedLines > 0) + NotifyValueChanged ("ChildHeightRatio", Slot.Height * visibleLines / buffer.UnfoldedLines); + } + } + + int firstPrintedLine = -1; + /// + /// list of lines visible in the Editor depending on scrolling and folding + /// + List PrintedLines; + + void updatePrintedLines () { + lock (buffer.EditMutex) { + PrintedLines = new List (); + int curL = 0; + int i = 0; + + while (curL < buffer.LineCount && i < ScrollY) { + if (buffer [curL].IsFolded) + curL = buffer.GetEndNodeIndex (curL); + curL++; + i++; + } + + firstPrintedLine = curL; + i = 0; + while (i < visibleLines && curL < buffer.LineCount) { + PrintedLines.Add (buffer [curL]); + + if (buffer [curL].IsFolded) + curL = buffer.GetEndNodeIndex (curL); + + curL++; + i++; + } + } + RegisterForGraphicUpdate (); + } + void toogleFolding (int line) { + if (parser == null || !foldingEnabled) + return; + buffer.ToogleFolding (line); + } + + #region Buffer events handlers + void Buffer_BufferCleared (object sender, EventArgs e) + { + buffer.longestLineCharCount = 0; + buffer.longestLineIdx = 0; + measureLeftMargin (); + MaxScrollX = MaxScrollY = 0; + PrintedLines = null; + RegisterForGraphicUpdate (); + } + void Buffer_LineAdditionEvent (object sender, CodeBufferEventArgs e) + { + for (int i = 0; i < e.LineCount; i++) { + int lptr = e.LineStart + i; + int charCount = buffer[lptr].PrintableLength; + if (charCount > buffer.longestLineCharCount) { + buffer.longestLineIdx = lptr; + buffer.longestLineCharCount = charCount; + }else if (lptr <= buffer.longestLineIdx) + buffer.longestLineIdx++; + if (parser == null) + continue; + parser.tryParseBufferLine (e.LineStart + i); + } + measureLeftMargin (); + + if (parser != null) + parser.reparseSource (); + + updatePrintedLines (); + updateMaxScrollY (); + RegisterForGraphicUpdate (); + } + void Buffer_LineRemoveEvent (object sender, CodeBufferEventArgs e) + { + bool trigFindLongestLine = false; + for (int i = 0; i < e.LineCount; i++) { + int lptr = e.LineStart + i; + if (lptr <= buffer.longestLineIdx) + trigFindLongestLine = true; + } + if (trigFindLongestLine) + findLongestLineAndUpdateMaxScrollX (); + + measureLeftMargin (); + updatePrintedLines (); + updateMaxScrollY (); + RegisterForGraphicUpdate (); + } + void Buffer_LineUpadateEvent (object sender, CodeBufferEventArgs e) + { + bool trigFindLongestLine = false; + for (int i = 0; i < e.LineCount; i++) { + + int lptr = e.LineStart + i; + if (lptr == buffer.longestLineIdx) + trigFindLongestLine = true; + else if (buffer[lptr].PrintableLength > buffer.longestLineCharCount) { + buffer.longestLineCharCount = buffer[lptr].PrintableLength; + buffer.longestLineIdx = lptr; + } + } + if (trigFindLongestLine) + findLongestLineAndUpdateMaxScrollX (); + + RegisterForGraphicUpdate (); + } + void Buffer_PositionChanged (object sender, EventArgs e) + { + updateOnScreenCurLineFromBuffCurLine (); + } + + void Buffer_SelectionChanged (object sender, EventArgs e) + { + RegisterForGraphicUpdate (); + } + void Buffer_FoldingEvent (object sender, CodeBufferEventArgs e) + { + updatePrintedLines (); + updateMaxScrollY (); + RegisterForGraphicUpdate (); + } + #endregion + + Parser getParserFromExt (string extension) { + if (string.IsNullOrEmpty(extension)) + return null; + if (!parsing.ContainsKey(extension)) + return null; + Type parserType = Type.GetType (parsing [extension]); + if (parserType == null) + return null; + return (Parser)Activator.CreateInstance (parserType, buffer ); + } + + #region Public Crow Properties + [XmlAttributeAttribute] + public bool PrintLineNumbers + { + get { return Configuration.Global.Get ("PrintLineNumbers"); + } + set + { + if (PrintLineNumbers == value) + return; + Configuration.Global.Set ("PrintLineNumbers", value); + NotifyValueChanged ("PrintLineNumbers", PrintLineNumbers); + measureLeftMargin (); + RegisterForGraphicUpdate (); + } + } + [XmlAttributeAttribute] + public ProjectFile ProjectNode + { + get { + return projNode; + } + set + { + if (projNode == value) + return; + + projNode = value; + + NotifyValueChanged ("ProjectNode", projNode); + + if (projNode == null) + return; + + if (!File.Exists (projNode.AbsolutePath)) + return; + + parser = getParserFromExt (System.IO.Path.GetExtension (projNode.Extension)); + + try { + buffer.Load (projNode.Source); + oldSource = projNode.Source; + } catch (Exception ex) { + Debug.WriteLine (ex.ToString ()); + } + + updateMaxScrollY (); + MaxScrollX = Math.Max (0, buffer.longestLineCharCount - visibleColumns); + updatePrintedLines (); + + RegisterForGraphicUpdate (); + } + } + [XmlAttributeAttribute][DefaultValue("BlueGray")] + public virtual Color SelectionBackground { + get { return selBackground; } + set { + if (value == selBackground) + return; + selBackground = value; + NotifyValueChanged ("SelectionBackground", selBackground); + RegisterForRedraw (); + } + } + [XmlAttributeAttribute][DefaultValue("White")] + public virtual Color SelectionForeground { + get { return selForeground; } + set { + if (value == selForeground) + return; + selForeground = value; + NotifyValueChanged ("SelectionForeground", selForeground); + RegisterForRedraw (); + } + } + + // [XmlAttributeAttribute][DefaultValue(0)] +// public int CurrentColumn{ +// get { return _currentCol; } +//// set { +//// if (value == _currentCol) +//// return; +//// if (value < 0) +//// _currentCol = 0; +//// else if (value > buffer[_currentLine].PrintableLength) +//// _currentCol = buffer[_currentLine].PrintableLength; +//// else +//// _currentCol = value; +//// +//// buffer.SetBufferPos (CurrentPosition); +//// +//// if (_currentCol < ScrollX) +//// ScrollX = _currentCol; +//// else if (_currentCol >= ScrollX + visibleColumns) +//// ScrollX = _currentCol - visibleColumns + 1; +//// +//// NotifyValueChanged ("CurrentColumn", _currentCol); +//// } +// } +// [XmlAttributeAttribute][DefaultValue(0)] +// public int CurrentLine{ +// get { return _currentLine; } +//// set { +//// if (value == _currentLine) +//// return; +//// if (value >= buffer.LineCount) +//// _currentLine = buffer.LineCount-1; +//// else if (value < 0) +//// _currentLine = 0; +//// else +//// _currentLine = value; +//// +//// if (_currentCol > buffer[_currentLine].PrintableLength) +//// CurrentColumn = buffer[_currentLine].PrintableLength;//buffer.setBufferPos is called inside +//// else +//// buffer.SetBufferPos (CurrentPosition); +//// +////// if (_currentLine < ScrollY) +////// ScrollY = _currentLine; +////// else if (_currentLine >= ScrollY + visibleLines) +////// ScrollY = _currentLine - visibleLines + 1; +//// +//// NotifyValueChanged ("CurrentLine", _currentLine); +//// } +// } +// /// +// /// Current position is in the printed coord system, tabulation chars are replaced with 4 spaces, +// /// while in the buffer, the position holds tabulations as single chars +// /// +// /// The current position. +// [XmlIgnore]public Point CurrentPosition { +// get { return new Point(CurrentColumn, CurrentLine); } +// set { +// _currentCol = value.X; +// _currentLine = value.Y; +// +// if (_currentCol < ScrollX) +// ScrollX = _currentCol; +// else if (_currentCol >= ScrollX + visibleColumns) +// ScrollX = _currentCol - visibleColumns + 1; +//// +//// if (_currentLine < ScrollY) +//// ScrollY = _currentLine; +//// else if (_currentLine >= ScrollY + visibleLines) +//// ScrollY = _currentLine - visibleLines + 1; +// +// NotifyValueChanged ("CurrentColumn", _currentCol); +// NotifyValueChanged ("CurrentLine", _currentLine); +// } +// } + +// [XmlIgnore]public string SelectedText +// { +// get { +// if (!selectionIsEmpty) +// buffer.SetSelection (selectionStart, selectionEnd); +// return buffer.SelectedText; +// } +// } + + #endregion + + + void updateOnScreenCurLineFromBuffCurLine(){ + printedCurrentLine = PrintedLines.IndexOf (buffer.CurrentCodeLine); + } + +// void setCurrentLineFromBuffer () { +// _currentLine = buffer.CurrentLine; +// NotifyValueChanged ("CurrentLine", _currentLine); +// } + + public override int ScrollY { + get { + return base.ScrollY; + } + set { + if (value == base.ScrollY) + return; + base.ScrollY = value; + updatePrintedLines (); + } + } + + + /// + /// Index of the currentline in the PrintedLines array + /// + int printedCurrentLine = 0; + + /// + /// Current editor line, when set, update buffer.CurrentLine + /// + int PrintedCurrentLine { + get { return printedCurrentLine;} + set { + if (value < 0) { + ScrollY += value; + printedCurrentLine = 0; + } else if (PrintedLines.Count < visibleLines && value >= PrintedLines.Count) { + printedCurrentLine = PrintedLines.Count - 1; + }else if (value >= visibleLines) { + ScrollY += value - visibleLines + 1; + printedCurrentLine = visibleLines - 1; + }else + printedCurrentLine = value; + Debug.WriteLine ("printed current line:" + printedCurrentLine.ToString ()); + //update position in buffer + buffer.CurrentLine = buffer.IndexOf (PrintedLines[printedCurrentLine]); + } + } + int getTabulatedColumn (int col, int line) { + return buffer [line].Content.Substring (0, col).Replace ("\t", new String (' ', Interface.TabSize)).Length; + } + int getTabulatedColumn (Point pos) { + return getTabulatedColumn (pos.X,pos.Y); + } + /// + /// Moves cursor one char to the left, move up if cursor reaches start of line + /// + /// true if move succeed + public bool MoveLeft(){ + if (buffer.CurrentColumn == 0) { + if (printedCurrentLine == 0) + return false; + PrintedCurrentLine--; + buffer.CurrentColumn = int.MaxValue; + } else + buffer.CurrentColumn--; + return true; + } + /// + /// Moves cursor one char to the right, move down if cursor reaches end of line + /// + /// true if move succeed + public bool MoveRight(){ + if (buffer.CurrentColumn >= buffer.CurrentCodeLine.Length) { + if (PrintedCurrentLine == buffer.UnfoldedLines - 1) + return false; + buffer.CurrentColumn = 0; + PrintedCurrentLine++; + } else + buffer.CurrentColumn++; + return true; + } + #region Drawing + void drawLine(Context gr, Rectangle cb, int i) { + CodeLine cl = PrintedLines[i]; + int lineIndex = buffer.IndexOf(cl); + + double y = cb.Y + fe.Height * i, x = cb.X; + + //Draw line numbering + Color mgFg = Color.Gray; + Color mgBg = Color.White; + if (PrintLineNumbers){ + Rectangle mgR = new Rectangle ((int)x, (int)y, leftMargin - leftMarginGap, (int)Math.Ceiling(fe.Height)); + if (cl.exception != null) { + mgBg = Color.Red; + if (buffer.CurrentLine == lineIndex) + mgFg = Color.White; + else + mgFg = Color.LightGray; + }else if (buffer.CurrentLine == lineIndex) { + mgFg = Color.Black; + } + string strLN = (lineIndex+1).ToString (); + gr.SetSourceColor (mgBg); + gr.Rectangle (mgR); + gr.Fill(); + gr.SetSourceColor (mgFg); + + gr.MoveTo (cb.X + (int)(gr.TextExtents (buffer.LineCount.ToString()).Width - gr.TextExtents (strLN).Width), y + fe.Ascent); + gr.ShowText (strLN); + gr.Fill (); + } + + + //draw folding + if (foldingEnabled){ + if (cl.IsFoldable) { + if (cl.SyntacticNode.StartLine != cl.SyntacticNode.EndLine) { + gr.SetSourceColor (Color.Black); + Rectangle rFld = new Rectangle (cb.X + leftMargin - leftMarginGap - foldSize, (int)(y + fe.Height / 2.0 - foldSize / 2.0), foldSize, foldSize); + gr.Rectangle (rFld, 1.0); + if (cl.IsFolded) { + gr.MoveTo (rFld.Center.X + 0.5, rFld.Y + 2); + gr.LineTo (rFld.Center.X + 0.5, rFld.Bottom - 2); + } + gr.MoveTo (rFld.Left + 2, rFld.Center.Y + 0.5); + gr.LineTo (rFld.Right - 2, rFld.Center.Y + 0.5); + gr.Stroke (); + } + } + } + + gr.SetSourceColor (Foreground); + x += leftMargin; + + if (cl.Tokens == null) + drawRawCodeLine (gr, x, y, i, lineIndex); + else + drawParsedCodeLine (gr, x, y, i, lineIndex); + } +// void drawParsed(Context gr){ +// if (PrintedLines == null) +// return; +// +// gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight); +// gr.SetFontSize (Font.Size); +// gr.FontOptions = Interface.FontRenderingOptions; +// gr.Antialias = Interface.Antialias; +// +// Rectangle cb = ClientRectangle; +// gr.Save (); +// CairoHelpers.CairoRectangle (gr, cb, CornerRadius); +// gr.Clip (); +// +// bool selectionInProgress = false; +// +// Foreground.SetAsSource (gr); +// +// #region draw text cursor +// if (SelBegin != SelRelease) +// selectionInProgress = true; +// else if (HasFocus){ +// gr.LineWidth = 1.0; +// double cursorX = + leftMargin + cb.X + (CurrentColumn - ScrollX) * fe.MaxXAdvance; +// gr.MoveTo (0.5 + cursorX, cb.Y + printedCurrentLine * fe.Height); +// gr.LineTo (0.5 + cursorX, cb.Y + (printedCurrentLine + 1) * fe.Height); +// gr.Stroke(); +// } +// #endregion +// +// for (int i = 0; i < PrintedLines.Count; i++) +// drawTokenLine (gr, i, selectionInProgress, cb); +// +// gr.Restore (); +// } + void drawRawCodeLine(Context gr, double x, double y, int i, int lineIndex) { + string lstr = buffer[lineIndex].PrintableContent; + if (ScrollX < lstr.Length) + lstr = lstr.Substring (ScrollX); + else + lstr = ""; + + gr.MoveTo (x, y + fe.Ascent); + gr.ShowText (lstr); + gr.Fill (); + + if (!buffer.SelectionIsEmpty && lineIndex >= buffer.SelectionStart.Y && lineIndex <= buffer.SelectionEnd.Y) { + double rLineX = x, + rLineY = y, + rLineW = lstr.Length * fe.MaxXAdvance; + + //System.Diagnostics.Debug.WriteLine ("sel start: " + buffer.SelectionStart + " sel end: " + buffer.SelectionEnd); + if (lineIndex == buffer.SelectionStart.Y) { + rLineX += (selStartCol - ScrollX) * fe.MaxXAdvance; + rLineW -= selStartCol * fe.MaxXAdvance; + } + if (lineIndex == buffer.SelectionEnd.Y) + rLineW -= (lstr.Length - selEndCol) * fe.MaxXAdvance; + + gr.Save (); + gr.Operator = Operator.Source; + gr.Rectangle (rLineX, rLineY, rLineW, fe.Height); + gr.SetSourceColor (SelectionBackground); + gr.FillPreserve (); + gr.Clip (); + gr.Operator = Operator.Over; + gr.SetSourceColor (SelectionForeground); + gr.MoveTo (x, y + fe.Ascent); + gr.ShowText (lstr); + gr.Fill (); + gr.Restore (); + } + } + void drawParsedCodeLine (Context gr, double x, double y, int i, int lineIndex) { + int lPtr = 0; + CodeLine cl = PrintedLines[i]; + + for (int t = 0; t < cl.Tokens.Count; t++) { + string lstr = cl.Tokens [t].PrintableContent; + if (lPtr < ScrollX) { + if (lPtr - ScrollX + lstr.Length <= 0) { + lPtr += lstr.Length; + continue; + } + lstr = lstr.Substring (ScrollX - lPtr); + lPtr += ScrollX - lPtr; + } + Color bg = this.Background; + Color fg = this.Foreground; + Color selbg = this.SelectionBackground; + Color selfg = this.SelectionForeground; + FontSlant fts = FontSlant.Normal; + FontWeight ftw = FontWeight.Normal; + + if (formatting.ContainsKey ((int)cl.Tokens [t].Type)) { + TextFormatting tf = formatting [(int)cl.Tokens [t].Type]; + bg = tf.Background; + fg = tf.Foreground; + if (tf.Bold) + ftw = FontWeight.Bold; + if (tf.Italic) + fts = FontSlant.Italic; + } + + gr.SelectFontFace (Font.Name, fts, ftw); + gr.SetSourceColor (fg); + + gr.MoveTo (x, y + fe.Ascent); + gr.ShowText (lstr); + gr.Fill (); + + if (buffer.SelectionInProgress && lineIndex >= buffer.SelectionStart.Y && lineIndex <= buffer.SelectionEnd.Y && + !(lineIndex == buffer.SelectionStart.Y && lPtr + lstr.Length <= selStartCol) && + !(lineIndex == buffer.SelectionEnd.Y && selEndCol <= lPtr)) { + + double rLineX = x, + rLineY = y, + rLineW = lstr.Length * fe.MaxXAdvance; + double startAdjust = 0.0; + + if ((lineIndex == buffer.SelectionStart.Y) && (selStartCol < lPtr + lstr.Length) && (selStartCol > lPtr)) + startAdjust = (selStartCol - lPtr) * fe.MaxXAdvance; + rLineX += startAdjust; + if ((lineIndex == buffer.SelectionEnd.Y) && (selEndCol < lPtr + lstr.Length)) + rLineW = (selEndCol - lPtr) * fe.MaxXAdvance; + rLineW -= startAdjust; + + gr.Save (); + gr.Operator = Operator.Source; + gr.Rectangle (rLineX, rLineY, rLineW, fe.Height); + gr.SetSourceColor (selbg); + gr.FillPreserve (); + gr.Clip (); + gr.Operator = Operator.Over; + gr.SetSourceColor (selfg); + gr.MoveTo (x, y + fe.Ascent); + gr.ShowText (lstr); + gr.Fill (); + gr.Restore (); + } + x += (int)lstr.Length * fe.MaxXAdvance; + lPtr += lstr.Length; + } + } + + #endregion + + #region GraphicObject overrides + public override Font Font { + get { return base.Font; } + set { + base.Font = value; + + using (ImageSurface img = new ImageSurface (Format.Argb32, 1, 1)) { + using (Context gr = new Context (img)) { + gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight); + gr.SetFontSize (Font.Size); + + fe = gr.FontExtents; + } + } + MaxScrollY = 0; + RegisterForGraphicUpdate (); + } + } + protected override int measureRawSize(LayoutingType lt) + { + if (lt == LayoutingType.Height) + return (int)Math.Ceiling(fe.Height * buffer.LineCount) + Margin * 2; + + return (int)(fe.MaxXAdvance * buffer.longestLineCharCount) + Margin * 2 + leftMargin; + } + public override void OnLayoutChanges (LayoutingType layoutType) + { + base.OnLayoutChanges (layoutType); + + if (layoutType == LayoutingType.Height) + updateVisibleLines (); + else if (layoutType == LayoutingType.Width) + updateVisibleColumns (); + } + + int selStartCol; + int selEndCol; + + protected override void onDraw (Context gr) + { +// if (!System.Threading.Monitor.TryEnter (buffer.EditMutex)) { +// RegisterForGraphicUpdate (); +// return; +// } + + base.onDraw (gr); + + gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight); + gr.SetFontSize (Font.Size); + gr.FontOptions = Interface.FontRenderingOptions; + gr.Antialias = Interface.Antialias; + + Rectangle cb = ClientRectangle; + + Foreground.SetAsSource (gr); + + lock (buffer.EditMutex) { + #region draw text cursor + if (buffer.SelectionInProgress){ + selStartCol = getTabulatedColumn (buffer.SelectionStart); + selEndCol = getTabulatedColumn (buffer.SelectionEnd); + }else if (HasFocus){ + gr.LineWidth = 1.0; + double cursorX = cb.X + (getTabulatedColumn(buffer.CurrentPosition) - ScrollX) * fe.MaxXAdvance + leftMargin; + gr.MoveTo (0.5 + cursorX, cb.Y + (printedCurrentLine) * fe.Height); + gr.LineTo (0.5 + cursorX, cb.Y + (printedCurrentLine + 1) * fe.Height); + gr.Stroke(); + } + #endregion + + if (PrintedLines != null) { + for (int i = 0; i < visibleLines; i++) { + if (i + ScrollY >= buffer.UnfoldedLines)//TODO:need optimize + break; + drawLine (gr, cb, i); + } + } + } + //System.Threading.Monitor.Exit (buffer.EditMutex); + } + #endregion + + #region Mouse handling + Point mouseLocalPos; + bool doubleClicked = false; + + void updateCurrentPos(){ +// if (mouseLocalPos.X < 0) +// CurrentColumn--; +// else + PrintedCurrentLine = (int)Math.Max (0, Math.Floor (mouseLocalPos.Y / fe.Height)); + int curVisualCol = ScrollX + (int)Math.Round ((mouseLocalPos.X - leftMargin) / fe.MaxXAdvance); + + int i = 0; + int buffCol = 0; + while (i < curVisualCol && buffCol < buffer.CurrentCodeLine.Length) { + if (buffer.CurrentCodeLine[buffCol] == '\t') + i += Interface.TabSize; + else + i++; + buffCol++; + } + buffer.CurrentColumn = buffCol; + +// if (mouseLocalPos.Y < 0) +// ScrollY--; + } + public override void onMouseEnter (object sender, MouseMoveEventArgs e) + { + base.onMouseEnter (sender, e); + if (e.X - ScreenCoordinates(Slot).X < leftMargin + ClientRectangle.X) + IFace.MouseCursor = XCursor.Default; + else + IFace.MouseCursor = XCursor.Text; + } + public override void onMouseLeave (object sender, MouseMoveEventArgs e) + { + base.onMouseLeave (sender, e); + IFace.MouseCursor = XCursor.Default; + } + public override void onMouseMove (object sender, MouseMoveEventArgs e) + { + base.onMouseMove (sender, e); + + mouseLocalPos = e.Position - ScreenCoordinates(Slot).TopLeft - ClientRectangle.TopLeft; + + if (!e.Mouse.IsButtonDown (MouseButton.Left)) { + if (mouseLocalPos.X < leftMargin) + IFace.MouseCursor = XCursor.Default; + else + IFace.MouseCursor = XCursor.Text; + return; + } + + if (!HasFocus || !buffer.SelectionInProgress) + return; + + //mouse is down + updateCurrentPos(); + buffer.SetSelEndPos (); + } + public override void onMouseDown (object sender, MouseButtonEventArgs e) + { + if (!this.Focusable) + return; + + if (mouseLocalPos.X >= leftMargin) + base.onMouseDown (sender, e); + + if (doubleClicked) { + doubleClicked = false; + return; + } + + if (mouseLocalPos.X < leftMargin) { + toogleFolding (buffer.IndexOf (PrintedLines [(int)Math.Max (0, Math.Floor (mouseLocalPos.Y / fe.Height))])); + return; + } + + updateCurrentPos (); + buffer.SetSelStartPos (); + } + public override void onMouseUp (object sender, MouseButtonEventArgs e) + { + base.onMouseUp (sender, e); + + if (buffer.SelectionIsEmpty) + buffer.ResetSelection (); + } + + public override void onMouseDoubleClick (object sender, MouseButtonEventArgs e) + { + doubleClicked = true; + base.onMouseDoubleClick (sender, e); + + buffer.GotoWordStart (); + buffer.SetSelStartPos (); + buffer.GotoWordEnd (); + buffer.SetSelEndPos (); + } + + public override void onMouseWheel (object sender, MouseWheelEventArgs e) + { + base.onMouseWheel (sender, e); + } + #endregion + + #region Keyboard handling + public override void onKeyDown (object sender, KeyboardKeyEventArgs e) + { + //base.onKeyDown (sender, e); + + Key key = e.Key; + + switch (key) + { + case Key.Back: + buffer.DeleteChar (); + break; + case Key.Clear: + break; + case Key.Delete: + if (buffer.SelectionIsEmpty) + MoveRight (); + else if (e.Shift) + IFace.Clipboard = buffer.SelectedText; + buffer.DeleteChar (); + break; + case Key.Enter: + case Key.KeypadEnter: + if (!buffer.SelectionIsEmpty) + buffer.DeleteChar (); + buffer.InsertLineBreak (); + break; + case Key.Escape: + buffer.ResetSelection (); + break; + case Key.Home: + if (e.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + if (e.Control) + buffer.CurrentLine = 0; + buffer.CurrentColumn = 0; + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + if (e.Control) + buffer.CurrentLine = 0; + buffer.CurrentColumn = 0; + break; + case Key.End: + if (e.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + if (e.Control) + buffer.CurrentLine = int.MaxValue; + buffer.CurrentColumn = int.MaxValue; + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + if (e.Control) + buffer.CurrentLine = int.MaxValue; + buffer.CurrentColumn = int.MaxValue; + break; + case Key.Insert: + if (e.Shift) + buffer.Insert (IFace.Clipboard); + else if (e.Control && !buffer.SelectionIsEmpty) + IFace.Clipboard = buffer.SelectedText; + break; + case Key.Left: + if (e.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + if (e.Control) + buffer.GotoWordStart (); + else + MoveLeft (); + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + if (e.Control) + buffer.GotoWordStart (); + else + MoveLeft(); + break; + case Key.Right: + if (e.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + if (e.Control) + buffer.GotoWordEnd (); + else + MoveRight (); + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + if (e.Control) + buffer.GotoWordEnd (); + else + MoveRight (); + break; + case Key.Up: + if (e.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + PrintedCurrentLine--; + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + PrintedCurrentLine--; + break; + case Key.Down: + if (e.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + PrintedCurrentLine++; + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + PrintedCurrentLine++; + break; + case Key.Menu: + break; + case Key.NumLock: + break; + case Key.PageDown: + if (e.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + PrintedCurrentLine += visibleLines; + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + PrintedCurrentLine += visibleLines; + break; + case Key.PageUp: + if (e.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + PrintedCurrentLine -= visibleLines; + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + PrintedCurrentLine -= visibleLines; + break; + case Key.RWin: + break; + case Key.Tab: + buffer.Insert ("\t"); + break; + case Key.F8: + toogleFolding (buffer.CurrentLine); +// if (parser != null) +// reparseSource (); + break; + default: + break; + } + RegisterForGraphicUpdate(); + } + public override void onKeyPress (object sender, KeyPressEventArgs e) + { + base.onKeyPress (sender, e); + + buffer.Insert (e.KeyChar.ToString()); + buffer.ResetSelection (); + + //RegisterForGraphicUpdate(); + } + #endregion + } +} \ No newline at end of file -- cgit v1.3.1-4-g156e