aboutsummaryrefslogtreecommitdiffstatsci
path: root/Samples/CrowIDE/src/Editors
diff options
context:
space:
mode:
authorJean-Philippe Bruyère <jp_bruyere@hotmail.com>2019-08-03 19:49:15 +0200
committerJean-Philippe Bruyère <jp_bruyere@hotmail.com>2019-08-03 19:49:15 +0200
commite6030fa04a8de3fdae1dc19d081bf5feb3d3d73c (patch)
tree89f392cd974d1109a203ade305da467bd378ee48 /Samples/CrowIDE/src/Editors
parentcad504c62d45952b4651603579f34df0d230841e (diff)
sample directory
Diffstat (limited to 'Samples/CrowIDE/src/Editors')
-rw-r--r--Samples/CrowIDE/src/Editors/CodeBuffer/CodeBuffer.cs530
-rw-r--r--Samples/CrowIDE/src/Editors/CodeBuffer/CodeBufferEventArgs.cs20
-rw-r--r--Samples/CrowIDE/src/Editors/CodeBuffer/CodeLine.cs82
-rw-r--r--Samples/CrowIDE/src/Editors/CodeBuffer/Node.cs36
-rw-r--r--Samples/CrowIDE/src/Editors/CodeBuffer/TextBuffer.cs527
-rw-r--r--Samples/CrowIDE/src/Editors/CodeBuffer/TextBufferEventArgs.cs20
-rw-r--r--Samples/CrowIDE/src/Editors/CodeBuffer/TextEditor.cs699
-rw-r--r--Samples/CrowIDE/src/Editors/CodeBuffer/TextFormatting.cs19
-rw-r--r--Samples/CrowIDE/src/Editors/CodeBuffer/Token.cs71
-rw-r--r--Samples/CrowIDE/src/Editors/EditPane.cs57
-rw-r--r--Samples/CrowIDE/src/Editors/Editor.cs105
-rw-r--r--Samples/CrowIDE/src/Editors/ImlSchematicEditor.cs132
-rw-r--r--Samples/CrowIDE/src/Editors/ImlVisualEditor.cs908
-rw-r--r--Samples/CrowIDE/src/Editors/Parsers/BufferParser.cs380
-rw-r--r--Samples/CrowIDE/src/Editors/Parsers/CSharpParser.cs386
-rw-r--r--Samples/CrowIDE/src/Editors/Parsers/StyleParser.cs179
-rw-r--r--Samples/CrowIDE/src/Editors/Parsers/XMLParser.cs307
-rw-r--r--Samples/CrowIDE/src/Editors/Parsers2/Tokenizer.cs67
-rw-r--r--Samples/CrowIDE/src/Editors/SourceEditor.cs1209
-rw-r--r--Samples/CrowIDE/src/Editors/SvgEditor.cs140
20 files changed, 5874 insertions, 0 deletions
diff --git a/Samples/CrowIDE/src/Editors/CodeBuffer/CodeBuffer.cs b/Samples/CrowIDE/src/Editors/CodeBuffer/CodeBuffer.cs
new file mode 100644
index 00000000..7d5efec9
--- /dev/null
+++ b/Samples/CrowIDE/src/Editors/CodeBuffer/CodeBuffer.cs
@@ -0,0 +1,530 @@
+//
+// CodeTextBuffer.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// Copyright (c) 2017 jp
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text.RegularExpressions;
+using System.Diagnostics;
+using System.Threading;
+
+namespace Crow.Coding
+{
+ /// <summary>
+ /// Code buffer, lines are arranged in a List<string>, new line chars are removed during string.split on '\n...',
+ /// </summary>
+ public class CodeBuffer
+ {
+ public ReaderWriterLockSlim editMutex = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
+
+ //those events are handled in SourceEditor to help keeping sync between textbuffer,parser and editor.
+ //modified lines are marked for reparse
+ #region Events
+ public event EventHandler<CodeBufferEventArgs> LineUpadateEvent;
+ public event EventHandler<CodeBufferEventArgs> LineRemoveEvent;
+ public event EventHandler<CodeBufferEventArgs> LineAdditionEvent;
+ public event EventHandler<CodeBufferEventArgs> FoldingEvent;
+ public event EventHandler BufferCleared;
+ public event EventHandler SelectionChanged;
+ public event EventHandler PositionChanged;
+ #endregion
+
+ string lineBreak = Interface.LineBreak;
+ List<CodeLine> lines = new List<CodeLine>();
+ public int longestLineIdx = 0;
+ public int longestLineCharCount = 0;
+ /// <summary>
+ /// real position in char arrays, tab = 1 char
+ /// </summary>
+ int _currentLine = 0;
+ int _currentCol = 0;
+
+ public int LineCount { get { return lines.Count;}}
+ public int IndexOf (CodeLine cl) {
+ return lines.IndexOf (cl);
+ }
+
+ public CodeLine this[int i]
+ {
+ get { return i < LineCount ? lines[i] : null; }
+ set {
+ if (lines [i] == value)
+ return;
+ editMutex.EnterWriteLock ();
+ lines [i] = value;
+ editMutex.ExitWriteLock ();
+ LineUpadateEvent.Raise (this, new CodeBufferEventArgs (i));
+ }
+ }
+
+ public void RemoveAt(int i){
+ editMutex.EnterWriteLock ();
+ lines.RemoveAt (i);
+ editMutex.ExitWriteLock ();
+ LineRemoveEvent.Raise (this, new CodeBufferEventArgs (i));
+ }
+ public void Insert(int i, string item){
+ editMutex.EnterWriteLock ();
+ lines.Insert (i, item);
+ editMutex.ExitWriteLock ();
+ LineAdditionEvent.Raise (this, new CodeBufferEventArgs (i));
+ }
+ public void Add(CodeLine item){
+ editMutex.EnterWriteLock ();
+ lines.Add (item);
+ editMutex.ExitWriteLock ();
+ LineAdditionEvent.Raise (this, new CodeBufferEventArgs (lines.Count - 1));
+ }
+ public void AddRange (string[] items){
+ int start = lines.Count;
+ editMutex.EnterWriteLock ();
+ for (int i = 0; i < items.Length; i++)
+ lines.Add (items [i]);
+ editMutex.ExitWriteLock ();
+ LineAdditionEvent.Raise (this, new CodeBufferEventArgs (start, items.Length));
+ }
+ public void AddRange (CodeLine[] items){
+ int start = lines.Count;
+ editMutex.EnterWriteLock ();
+ lines.AddRange (items);
+ editMutex.ExitWriteLock ();
+ LineAdditionEvent.Raise (this, new CodeBufferEventArgs (start, items.Length));
+ }
+ public void Clear () {
+ editMutex.EnterWriteLock ();
+ longestLineCharCount = 0;
+ lines.Clear ();
+ editMutex.ExitWriteLock ();
+ BufferCleared.Raise (this, null);
+ }
+ public void UpdateLine(int i, string newContent){
+ editMutex.EnterWriteLock ();
+ this [i].Content = newContent;
+ editMutex.ExitWriteLock ();
+ LineUpadateEvent.Raise (this, new CodeBufferEventArgs (i));
+ }
+ public void AppenedLine(int i, string newContent){
+ editMutex.EnterWriteLock ();
+ this [i].Content += newContent;
+ editMutex.ExitWriteLock ();
+ LineUpadateEvent.Raise (this, new CodeBufferEventArgs (i));
+ }
+ public void RemoveLeadingTab (int l) {
+ if (this [l] [0] == '\t') {
+ UpdateLine (l, this [l].Content.Substring (1));
+ return;
+ }
+ int i = 0;
+ while (i < Interface.TabSize) {
+ if (this [l] [i] != ' ')
+ break;
+ i++;
+ }
+ if (i > 0)
+ UpdateLine (l, this [l].Content.Substring (i));
+ }
+ public void ToogleFolding (int line) {
+ if (!this [line].IsFoldable)
+ return;
+ editMutex.EnterWriteLock ();
+ this [line].IsFolded = !this [line].IsFolded;
+ editMutex.ExitWriteLock ();
+ FoldingEvent.Raise (this, new CodeBufferEventArgs (line));
+ }
+ public void Load(string rawSource, string lineBrkRegex = @"\r\n|\r|\n|\\\n") {
+ this.Clear();
+
+ if (string.IsNullOrEmpty (rawSource))
+ return;
+
+ AddRange (Regex.Split (rawSource, lineBrkRegex));
+
+ lineBreak = detectLineBreakKind (rawSource);
+ }
+
+ /// <summary>
+ /// Finds the longest visual line as printed on screen with tabulation replaced with n spaces
+ /// </summary>
+ public void FindLongestVisualLine(){
+ longestLineCharCount = 0;
+ editMutex.EnterReadLock ();
+ for (int i = 0; i < this.LineCount; i++) {
+ if (lines[i].PrintableLength > longestLineCharCount) {
+ longestLineCharCount = lines[i].PrintableLength;
+ longestLineIdx = i;
+ }
+ }
+ editMutex.ExitReadLock ();
+ //Debug.WriteLine ("Longest line: {0}->{1}", longestLineIdx, longestLineCharCount);
+ }
+ /// <summary> line break could be '\r' or '\n' or '\r\n' </summary>
+ static string detectLineBreakKind(string buffer){
+ string strLB = "";
+
+ if (string.IsNullOrEmpty(buffer))
+ return Interface.LineBreak;
+ int i = 0;
+ while ( i < buffer.Length) {
+ if (buffer [i] == '\r') {
+ strLB += '\r';
+ i++;
+ }
+ if (i < buffer.Length) {
+ if (buffer [i] == '\r')
+ return "\r";
+ if (buffer[i] == '\n')
+ strLB += '\n';
+ }
+ if (!string.IsNullOrEmpty (strLB))
+ return strLB;
+ i++;
+ }
+ return Interface.LineBreak;
+ }
+ /// <summary>
+ /// return all lines with linebreaks
+ /// </summary>
+ public string FullText{
+ get {
+ if (lines.Count == 0)
+ return "";
+ string tmp = "";
+ editMutex.EnterReadLock ();
+ for (int i = 0; i < lines.Count -1; i++)
+ tmp += lines [i].Content + this.lineBreak;
+ tmp += lines [lines.Count - 1].Content;
+ editMutex.ExitReadLock ();
+ return tmp;
+ }
+ }
+
+ /// <summary>
+ /// unfolded and not in folds line count
+ /// </summary>
+ public int UnfoldedLines {
+ get {
+ int i = 0, vl = 0;
+ editMutex.EnterReadLock ();
+ while (i < LineCount) {
+ if (this [i].IsFolded) {
+ i = GetEndNodeIndex (i);
+ if (i < 0) {
+ Console.WriteLine ("error folding");
+ break;
+ }
+ }
+ i++;
+ vl++;
+ }
+ editMutex.ExitReadLock ();
+ //Debug.WriteLine ("unfolded lines: " + vl);
+ return vl;
+ }
+ }
+
+ /// <summary>
+ /// convert visual position to buffer position
+ /// </summary>
+ Point getBuffPos (Point visualPos) {
+ int i = 0;
+ int buffCol = 0;
+ while (i < visualPos.X) {
+ if (this [visualPos.Y] [buffCol] == '\t')
+ i += Interface.TabSize;
+ else
+ i++;
+ buffCol++;
+ }
+ return new Point (buffCol, visualPos.Y);
+ }
+
+ public int GetEndNodeIndex (int line) {
+ return IndexOf (this [line].SyntacticNode.EndLine);
+ }
+
+ int ConverteTabulatedPosOfCurLine (int column) {
+ int tmp = 0;
+ int i = 0;
+ while (i < lines [_currentLine].Content.Length){
+ if (lines [_currentLine].Content [i] == '\t')
+ tmp += 4;
+ else
+ tmp++;
+ if (tmp > column)
+ break;
+ i++;
+ }
+ return i;
+ }
+
+ public int CurrentTabulatedColumn {
+ get {
+ return lines [_currentLine].Content.Substring (0, _currentCol).
+ Replace ("\t", new String (' ', Interface.TabSize)).Length;
+ }
+ }
+ /// <summary>
+ /// Gets visual position computed from actual buffer position
+ /// </summary>
+// public Point TabulatedPosition {
+// get { return new Point (TabulatedColumn, _currentLine); }
+// }
+ /// <summary>
+ /// set buffer current position from visual position
+ /// </summary>
+// public void SetBufferPos(Point tabulatedPosition) {
+// CurrentPosition = getBuffPos(tabulatedPosition);
+// }
+
+ #region Editing and moving cursor
+ Point selStartPos = -1; //selection start (row,column)
+ Point selEndPos = -1; //selection end (row,column)
+
+ public bool SelectionInProgress { get { return selStartPos >= 0; }}
+ public void SetSelStartPos () {
+ selStartPos = selEndPos = CurrentPosition;
+ SelectionChanged.Raise (this, null);
+ }
+ public void SetSelEndPos () {
+ selEndPos = CurrentPosition;
+ SelectionChanged.Raise (this, null);
+ }
+ public void SetSelectionOnFullLines () {
+ if (!SelectionInProgress)
+ return;
+ Point s = new Point (0, SelectionStart.Y);
+ Point e = new Point (this [SelectionEnd.Y].Length, SelectionEnd.Y);
+ selStartPos = s;
+ selEndPos = e;
+ SelectionChanged.Raise (this, null);
+ }
+ /// <summary>
+ /// Set selection in buffer to -1, empty selection
+ /// </summary>
+ public void ResetSelection () {
+ selStartPos = selEndPos = -1;
+ SelectionChanged.Raise (this, null);
+ }
+
+ public string SelectedText {
+ get {
+ if (SelectionIsEmpty)
+ return "";
+ Point selStart = SelectionStart;
+ Point selEnd = SelectionEnd;
+ if (selStart.Y == selEnd.Y)
+ return this [selStart.Y].Content.Substring (selStart.X, selEnd.X - selStart.X);
+ string tmp = "";
+ tmp = this [selStart.Y].Content.Substring (selStart.X);
+ for (int l = selStart.Y + 1; l < selEnd.Y; l++) {
+ tmp += Interface.LineBreak + this [l].Content;
+ }
+ tmp += Interface.LineBreak + this [selEnd.Y].Content.Substring (0, selEnd.X);
+ return tmp;
+ }
+ }
+ /// <summary>
+ /// ordered selection start and end positions in char units
+ /// </summary>
+ public Point SelectionStart {
+ get { return selEndPos < 0 || selStartPos.Y < selEndPos.Y ? selStartPos :
+ selStartPos.Y > selEndPos.Y ? selEndPos :
+ selStartPos.X < selEndPos.X ? selStartPos : selEndPos; }
+ }
+ public Point SelectionEnd {
+ get { return selEndPos < 0 || selStartPos.Y > selEndPos.Y ? selStartPos :
+ selStartPos.Y < selEndPos.Y ? selEndPos :
+ selStartPos.X > selEndPos.X ? selStartPos : selEndPos; }
+ }
+ public bool SelectionIsEmpty
+ { get { return selEndPos == selStartPos; } }
+ int requestedColumn = -1;
+ /// <summary>
+ /// Current column in buffer coordinate, tabulation = 1 char
+ /// </summary>
+ public int CurrentColumn{
+ get { return _currentCol; }
+ set {
+ if (value == _currentCol)
+ return;
+
+ editMutex.EnterReadLock ();
+
+ if (value < 0)
+ _currentCol = 0;
+ else if (value > lines [_currentLine].Length)
+ _currentCol = lines [_currentLine].Length;
+ else
+ _currentCol = value;
+
+ requestedColumn = CurrentTabulatedColumn;
+
+ editMutex.ExitReadLock ();
+
+ PositionChanged.Raise (this, null);
+ }
+ }
+ /// <summary>
+ /// Current row in buffer coordinate, tabulation = 1 char
+ /// </summary>
+ public int CurrentLine{
+ get { return _currentLine; }
+ set {
+ if (value == _currentLine)
+ return;
+
+ editMutex.EnterReadLock ();
+
+ if (LineCount == 0)
+ _currentLine = 0;
+ else if (value >= lines.Count)
+ _currentLine = lines.Count-1;
+ else if (value < 0)
+ _currentLine = 0;
+ else
+ _currentLine = value;
+// if (_currentCol < 0)
+// requestedColumn = tabu _currentCol;
+ int tabulatedRequestedCol = ConverteTabulatedPosOfCurLine(requestedColumn);
+ if (requestedColumn > lines [_currentLine].PrintableLength)
+ _currentCol = lines [_currentLine].Length;
+ else
+ //_currentCol = requestedColumn;
+ _currentCol = tabulatedRequestedCol;
+ //Debug.WriteLine ("buff cur line: " + _currentLine);
+
+ editMutex.ExitReadLock();
+
+ PositionChanged.Raise (this, null);
+ }
+ }
+ public CodeLine CurrentCodeLine {
+ get { return this [_currentLine]; }
+ }
+ /// <summary>
+ /// Current position in buffer coordinate, tabulation = 1 char
+ /// </summary>
+ public Point CurrentPosition {
+ get { return new Point(CurrentColumn, CurrentLine); }
+// set {
+// _currentCol = value.X;
+// _currentLine = value.Y;
+// }
+ }
+ /// <summary>
+ /// get char at current position in buffer
+ /// </summary>
+ protected Char CurrentChar { get { return lines [CurrentLine] [CurrentColumn]; } }
+
+ public void GotoWordStart(){
+ if (this[CurrentLine].Length == 0)
+ return;
+ CurrentColumn--;
+ //skip white spaces
+ while (!char.IsLetterOrDigit (this.CurrentChar) && CurrentColumn > 0)
+ CurrentColumn--;
+ while (char.IsLetterOrDigit (this.CurrentChar) && CurrentColumn > 0)
+ CurrentColumn--;
+ if (!char.IsLetterOrDigit (this.CurrentChar))
+ CurrentColumn++;
+ }
+ public void GotoWordEnd(){
+ //skip white spaces
+ if (CurrentColumn >= this [CurrentLine].Length - 1)
+ return;
+ while (!char.IsLetterOrDigit (this.CurrentChar) && CurrentColumn < this [CurrentLine].Length-1)
+ CurrentColumn++;
+ while (char.IsLetterOrDigit (this.CurrentChar) && CurrentColumn < this [CurrentLine].Length-1)
+ CurrentColumn++;
+ if (char.IsLetterOrDigit (this.CurrentChar))
+ CurrentColumn++;
+ }
+ public void DeleteChar()
+ {
+ editMutex.EnterWriteLock ();
+ if (SelectionIsEmpty) {
+ if (CurrentColumn == 0) {
+ if (CurrentLine == 0) {
+ editMutex.ExitWriteLock ();
+ return;
+ }
+ CurrentLine--;
+ CurrentColumn = this [CurrentLine].Length;
+ AppenedLine (CurrentLine, this [CurrentLine + 1].Content);
+ RemoveAt (CurrentLine + 1);
+ editMutex.ExitWriteLock ();
+ return;
+ }
+ CurrentColumn--;
+ UpdateLine (CurrentLine, this [CurrentLine].Content.Remove (CurrentColumn, 1));
+ } else {
+ int linesToRemove = SelectionEnd.Y - SelectionStart.Y + 1;
+ int l = SelectionStart.Y;
+
+ if (linesToRemove > 0) {
+ UpdateLine (l, this [l].Content.Remove (SelectionStart.X, this [l].Length - SelectionStart.X) +
+ this [SelectionEnd.Y].Content.Substring (SelectionEnd.X, this [SelectionEnd.Y].Length - SelectionEnd.X));
+ l++;
+ for (int c = 0; c < linesToRemove - 1; c++)
+ RemoveAt (l);
+ CurrentLine = SelectionStart.Y;
+ CurrentColumn = SelectionStart.X;
+ } else
+ UpdateLine (l, this [l].Content.Remove (SelectionStart.X, SelectionEnd.X - SelectionStart.X));
+ CurrentColumn = SelectionStart.X;
+ ResetSelection ();
+ }
+ editMutex.ExitWriteLock ();
+ }
+ /// <summary>
+ /// Insert new string at caret position, should be sure no line break is inside.
+ /// </summary>
+ /// <param name="str">String.</param>
+ public void Insert(string str)
+ {
+ if (!SelectionIsEmpty)
+ this.DeleteChar ();
+ string[] strLines = Regex.Split (str, "\r\n|\r|\n|" + @"\\n").ToArray();
+ UpdateLine (CurrentLine, this [CurrentLine].Content.Insert (CurrentColumn, strLines[0]));
+ CurrentColumn += strLines[0].Length;
+ for (int i = 1; i < strLines.Length; i++) {
+ InsertLineBreak ();
+ UpdateLine (CurrentLine, this [CurrentLine].Content.Insert (CurrentColumn, strLines[i]));
+ CurrentColumn += strLines[i].Length;
+ }
+ }
+ /// <summary>
+ /// Insert a line break.
+ /// </summary>
+ public void InsertLineBreak()
+ {
+ if (CurrentColumn > 0) {
+ Insert (CurrentLine + 1, this [CurrentLine].Content.Substring (CurrentColumn));
+ UpdateLine (CurrentLine, this [CurrentLine].Content.Substring (0, CurrentColumn));
+ } else
+ Insert(CurrentLine, "");
+
+ CurrentColumn = 0;
+ CurrentLine++;
+ }
+ #endregion
+ }
+}
+
diff --git a/Samples/CrowIDE/src/Editors/CodeBuffer/CodeBufferEventArgs.cs b/Samples/CrowIDE/src/Editors/CodeBuffer/CodeBufferEventArgs.cs
new file mode 100644
index 00000000..07dd25b6
--- /dev/null
+++ b/Samples/CrowIDE/src/Editors/CodeBuffer/CodeBufferEventArgs.cs
@@ -0,0 +1,20 @@
+using System;
+
+namespace Crow.Coding
+{
+ public class CodeBufferEventArgs : EventArgs {
+ public int LineStart;
+ public int LineCount;
+
+ public CodeBufferEventArgs(int lineNumber) {
+ LineStart = lineNumber;
+ LineCount = 1;
+ }
+ public CodeBufferEventArgs(int lineStart, int lineCount) {
+ LineStart = lineStart;
+ LineCount = lineCount;
+ }
+ }
+
+}
+
diff --git a/Samples/CrowIDE/src/Editors/CodeBuffer/CodeLine.cs b/Samples/CrowIDE/src/Editors/CodeBuffer/CodeLine.cs
new file mode 100644
index 00000000..23f43fba
--- /dev/null
+++ b/Samples/CrowIDE/src/Editors/CodeBuffer/CodeLine.cs
@@ -0,0 +1,82 @@
+using System;
+using System.Text;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Crow.Coding
+{
+ public class CodeLine
+ {
+ public string Content;
+ public List<Token> Tokens;
+ public int EndingState = 0;
+ public Node SyntacticNode;
+ public ParserException exception;
+
+ public CodeLine (string _content){
+ Content = _content;
+ Tokens = null;
+ exception = null;
+ }
+
+ public char this[int i]
+ {
+ get { return Content[i]; }
+ set {
+ if (Content [i] == value)
+ return;
+ StringBuilder sb = new StringBuilder(Content);
+ sb[i] = value;
+ Content = sb.ToString();
+ Tokens = null;
+ //LineUpadateEvent.Raise (this, new CodeBufferEventArgs (i));
+ }
+ }
+ public bool IsFoldable { get { return SyntacticNode == null ? false :
+ SyntacticNode.EndLine != SyntacticNode.StartLine && SyntacticNode.EndLine != null; } }
+ public int FoldingLevel { get { return IsFoldable ? SyntacticNode.Level : 0; } }
+ public bool IsFolded = false;
+ public bool IsParsed {
+ get { return Tokens != null; }
+ }
+ public string PrintableContent {
+ get {
+ return string.IsNullOrEmpty (Content) ? "" : Content.Replace ("\t", new String (' ', Interface.TabSize));
+ }
+ }
+ public int PrintableLength {
+ get {
+ return PrintableContent.Length;
+ }
+ }
+ public int Length {
+ get {
+ return string.IsNullOrEmpty (Content) ? 0 : Content.Length;
+ }
+ }
+ public int FirstNonBlankTokIndex {
+ get { return Tokens == null ? -1 : Tokens.FindIndex (tk=>tk.Type != BufferParser.TokenType.WhiteSpace); }
+ }
+
+ public void SetLineInError (ParserException ex) {
+ Tokens = null;
+ exception = ex;
+ }
+
+// public static implicit operator string(CodeLine sl) {
+// return sl == null ? "" : sl.Content;
+// }
+ public static implicit operator CodeLine(string s) {
+ return new CodeLine(s);
+ }
+ public static bool operator ==(string s1, CodeLine s2)
+ {
+ return string.Equals (s1, s2.Content);
+ }
+ public static bool operator !=(string s1, CodeLine s2)
+ {
+ return !string.Equals (s1, s2.Content);
+ }
+ }
+}
+
diff --git a/Samples/CrowIDE/src/Editors/CodeBuffer/Node.cs b/Samples/CrowIDE/src/Editors/CodeBuffer/Node.cs
new file mode 100644
index 00000000..9db55428
--- /dev/null
+++ b/Samples/CrowIDE/src/Editors/CodeBuffer/Node.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+
+namespace Crow.Coding
+{
+ public class Node
+ {
+ public Node Parent;
+ public string Name;
+ public string Type;
+ public CodeLine StartLine;
+ public CodeLine EndLine;
+ public Dictionary<string,string> Attributes = new Dictionary<string, string> ();
+
+ public List<Node> Children = new List<Node>();
+
+ public Node ()
+ {
+ }
+
+ public void AddChild (Node child) {
+ child.Parent = this;
+ Children.Add (child);
+ }
+
+ public int Level {
+ get { return Parent == null ? 1 : Parent.Level + 1; }
+ }
+
+ public override string ToString ()
+ {
+ return string.Format ("Name:{0}, Type:{1}\n\tparent:{2}", Name, Type, Parent);
+ }
+ }
+}
+
diff --git a/Samples/CrowIDE/src/Editors/CodeBuffer/TextBuffer.cs b/Samples/CrowIDE/src/Editors/CodeBuffer/TextBuffer.cs
new file mode 100644
index 00000000..e8c87182
--- /dev/null
+++ b/Samples/CrowIDE/src/Editors/CodeBuffer/TextBuffer.cs
@@ -0,0 +1,527 @@
+//
+// CodeTextBuffer.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// Copyright (c) 2017 jp
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text.RegularExpressions;
+using System.Diagnostics;
+using System.Threading;
+using System.Text;
+
+namespace Crow.Text
+{
+ /// <summary>
+ /// Code buffer, lines are arranged in a List<string>, new line chars are removed during string.split on '\n...',
+ /// </summary>
+ public class TextBuffer
+ {
+ public ReaderWriterLockSlim editMutex = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
+ static Regex slb = new Regex ("\\n");//single char line break used internaly
+ Regex reghexLineBrk = new Regex(@"\r\n|\r|\n|\\\n");//original text line break regex
+
+ #region Events
+ public event EventHandler<TextBufferEventArgs> LineUpadateEvent;
+ public event EventHandler<TextBufferEventArgs> LineRemoveEvent;
+ public event EventHandler<TextBufferEventArgs> LineAdditionEvent;
+ public event EventHandler BufferCleared;
+ public event EventHandler SelectionChanged;
+ public event EventHandler PositionChanged;
+ #endregion
+
+ StringBuilder buffer = new StringBuilder();
+ string lineBreak = Interface.LineBreak;//detected linebreak kind in original source
+ List<int> lineLength = new List<int>();//line length table
+ /// <summary>
+ /// real position in char arrays, tab = 1 char
+ /// </summary>
+ int _currentLine = 0;
+ int _currentCol = 0;
+
+ /// <summary>
+ /// Gets the total line count.
+ /// </summary>
+ public int LineCount { get { return lineLength.Count;}}
+
+ /// <summary>
+ /// get a substring in the buffer
+ /// </summary>
+ /// <returns>a new string</returns>
+ /// <param name="idx">absolute index in the buffer</param>
+ /// <param name="length">length of the substring</param>
+ public string GetSubString (int idx, int length){
+ return buffer.ToString (idx, length);
+ }
+ /// <summary>
+ /// Gets length of a line.
+ /// </summary>
+ /// <returns>length of line</returns>
+ /// <param name="lineIdx">line nuber</param>
+ public int GetLineLength (int lineIdx) {
+ return lineLength [lineIdx];
+ }
+ /// <summary>
+ /// get a single charactere in the buffer
+ /// </summary>
+ /// <returns>a single char</returns>
+ /// <param name="idx">absolute index in the buffer</param>
+ public char GetCharAt (int idx){
+ return buffer [idx];
+ }
+ /// <summary>
+ /// return full text with original line breaks
+ /// </summary>
+ /// <value>a string containing the full text</value>
+ public string FullText {
+ get { return buffer.Replace("\n", lineBreak).ToString (); }
+ set {
+ Load (value);
+ }
+ }
+ /// <summary>
+ /// Gets the buffer pointer of a line
+ /// </summary>
+ /// <returns>absolute index of the line in the buffer</returns>
+ /// <param name="i">line number</param>
+ public int GetBufferIndexOfLine (int i) {
+ int ptr = 0;
+ editMutex.EnterReadLock ();
+ for (int j = 0; j < i; j++) {
+ ptr += lineLength [j];
+ }
+ editMutex.ExitReadLock ();
+ return ptr;
+ }
+ /// <summary>
+ /// Gets the buffer pointer for the current position
+ /// </summary>
+ /// <value>absolute index in the buffer of current position</value>
+ public int BufferIndexOfCurrentPosition {
+ get {return GetBufferIndexOfLine (_currentLine) + _currentCol;}
+ }
+ public int this[int i]
+ {
+ get {
+ int ptr = 0;
+ editMutex.EnterReadLock ();
+ for (int j = 0; j < i; j++) {
+ ptr += lineLength [j];
+ }
+ editMutex.ExitReadLock ();
+ return ptr;
+ }
+ }
+ /// <summary>
+ /// remove line number i
+ /// </summary>
+ /// <param name="i">index of the line</param>
+ public void RemoveLine(int i){
+ editMutex.EnterWriteLock ();
+ buffer.Remove (GetBufferIndexOfLine (i), lineLength [i]);
+ lineLength.RemoveAt (i);
+ editMutex.ExitWriteLock ();
+ LineRemoveEvent.Raise (this, new TextBufferEventArgs (i));
+ }
+ /// <summary>
+ /// insert string without linebreaks at position i in buff
+ /// </summary>
+ /// <param name="i">absolute index in the buffer</param>
+ /// <param name="str">linebreak free string</param>
+ public void InsertAt(int i, string str){
+ editMutex.EnterWriteLock ();
+ buffer.Insert (this [i], str);
+ lineLength.Insert (i, str.Length);
+ editMutex.ExitWriteLock ();
+ LineAdditionEvent.Raise (this, new TextBufferEventArgs (i));
+ }
+ public void AddLine(string str){
+ editMutex.EnterWriteLock ();
+ if (lineLength.LastOrDefault() == 0) {
+ buffer.Append (str);
+ lineLength [lineLength.Count - 1] = str.Length;
+ lineLength.Add (0);
+ }
+ editMutex.ExitWriteLock ();
+ LineAdditionEvent.Raise (this, new TextBufferEventArgs (lineLength.Count - 1));
+ }
+ public void AddRange (string[] items){
+ int start = lineLength.Count;
+ editMutex.EnterWriteLock ();
+ for (int i = 0; i < items.Length; i++)
+ AddLine (items [i]);
+ editMutex.ExitWriteLock ();
+ LineAdditionEvent.Raise (this, new TextBufferEventArgs (start, items.Length));
+ }
+ public void Clear () {
+ editMutex.EnterWriteLock ();
+ lineLength.Clear ();
+ buffer.Clear ();
+ editMutex.ExitWriteLock ();
+ BufferCleared.Raise (this, null);
+ }
+ public void UpdateLine(int i, string newContent){
+ editMutex.EnterWriteLock ();
+ int ptrL = this [i];
+ buffer.Remove (ptrL, lineLength [i]);
+ buffer.Insert (ptrL, newContent);
+ lineLength [i] = newContent.Length;
+ editMutex.ExitWriteLock ();
+ LineUpadateEvent.Raise (this, new TextBufferEventArgs (i));
+ }
+ public void AppenedLine(int i, string newContent){
+ editMutex.EnterWriteLock ();
+ int ptr = this [i] + lineLength [i];
+ if (i < LineCount - 1)
+ ptr--;
+ buffer.Insert(ptr, newContent);
+ lineLength [i] += newContent.Length;
+ editMutex.ExitWriteLock ();
+ LineUpadateEvent.Raise (this, new TextBufferEventArgs (i));
+ }
+ /// <summary>
+ /// Insert new string at caret position, should be sure no line break is inside.
+ /// </summary>
+ /// <param name="str">String.</param>
+ public void InsertAt(string str)
+ {
+ if (!SelectionIsEmpty)
+ this.Delete ();
+
+ editMutex.EnterWriteLock ();
+
+ string tmp = reghexLineBrk.Replace (str, "\n");//use single char line break in buffer
+ int buffPtr = this [CurrentLine] + CurrentColumn;
+ buffer.Insert (buffPtr, tmp);
+
+ int lPtr = CurrentLine, strPtr = 0;
+ int remainingLength = lineLength [lPtr] - CurrentColumn;
+ lineLength [lPtr] = CurrentColumn;
+ foreach (Match match in slb.Matches(tmp))
+ {
+ lineLength [lPtr] += match.Index + 1 - strPtr;
+ lPtr++;
+ lineLength.Insert (lPtr, 0);
+ strPtr = match.Index + 1;
+ //CurrentLine++;
+ }
+ remainingLength += tmp.Length - strPtr;
+ lineLength [lPtr] += remainingLength;
+
+ CurrentLine = lPtr;
+ if (strPtr == 0)
+ CurrentColumn += tmp.Length;
+ else
+ CurrentColumn = tmp.Length - strPtr;
+
+ editMutex.ExitWriteLock ();
+ if (strPtr>0)
+ LineAdditionEvent.Raise (this, null);
+ else
+ LineUpadateEvent.Raise (this, null);
+ }
+ /// <summary>
+ /// Insert a line break.
+ /// </summary>
+ public void InsertLineBreak()
+ {
+ editMutex.EnterWriteLock ();
+ buffer.Insert (this [CurrentLine] + CurrentColumn, '\n');
+ int lgdiff = lineLength [CurrentLine] - CurrentColumn;
+ lineLength.Insert (CurrentLine + 1, lineLength [CurrentLine] - CurrentColumn);
+ lineLength [CurrentLine] = CurrentColumn + 1;
+ editMutex.ExitWriteLock ();
+ LineAdditionEvent.Raise (this, null);
+ CurrentColumn = 0;
+ CurrentLine++;
+ }
+ public void Delete()
+ {
+ editMutex.EnterWriteLock ();
+ if (SelectionIsEmpty) {
+ if (CurrentColumn == 0) {
+ if (CurrentLine == 0) {
+ editMutex.ExitWriteLock ();
+ return;
+ }
+
+ buffer.Remove (this [CurrentLine] - 1, 1);
+
+ int col = lineLength [CurrentLine - 1] - 1;
+ lineLength [CurrentLine - 1] += lineLength [CurrentLine] - 1;
+ lineLength.RemoveAt (CurrentLine);
+
+ CurrentLine--;
+ CurrentColumn = col;
+
+ editMutex.ExitWriteLock ();
+ LineRemoveEvent.Raise (this, null);
+ return;
+ }
+ CurrentColumn--;
+ buffer.Remove (this [CurrentLine] + CurrentColumn, 1);
+ lineLength [CurrentLine]--;
+ } else {
+ int linesToRemove = SelectionEnd.Y - SelectionStart.Y;
+ int ptr = this [SelectionStart.Y] + SelectionStart.X;
+ int length = lineLength [SelectionStart.Y] - SelectionStart.X;
+ int l = 1;
+ while (l <= linesToRemove ) {
+ length += lineLength [SelectionStart.Y + l];
+ l++;
+ }
+ length -= lineLength [SelectionEnd.Y] - SelectionEnd.X;
+ buffer.Remove (ptr, length);
+ lineLength [SelectionStart.Y] = SelectionStart.X + lineLength [SelectionEnd.Y] - SelectionEnd.X;
+ lineLength.RemoveRange (SelectionStart.Y + 1, linesToRemove);
+
+ CurrentLine = SelectionStart.Y;
+ CurrentColumn = SelectionStart.X;
+ ResetSelection ();
+
+ if (linesToRemove > 0)
+ LineRemoveEvent.Raise (this, null);
+ else
+ LineUpadateEvent.Raise (this, null);
+ }
+ editMutex.ExitWriteLock ();
+ }
+ public void Load(string rawSource) {
+ this.Clear();
+
+ if (string.IsNullOrEmpty (rawSource))
+ return;
+
+ lineBreak = reghexLineBrk.Match (rawSource).Value;//store original line break
+ string tmp = reghexLineBrk.Replace (rawSource, "\n");//use single char line break in buffer
+ int ptr = 0;
+ foreach (Match match in slb.Matches(tmp))
+ {
+ int l = match.Index + 1;
+ int lg = l - ptr;
+ lineLength.Add (lg);
+ ptr = l;
+ }
+ lineLength.Add (0);
+
+ buffer = new StringBuilder (tmp);
+ }
+
+// public int CurrentTabulatedColumn {
+// get {
+//// return lines [_currentLine].Content.Substring (0, _currentCol).
+//// Replace ("\t", new String (' ', Interface.TabSize)).Length;
+// }
+// }
+
+ #region moving cursor an selection
+ Point selStartPos = -1; //selection start (row,column)
+ Point selEndPos = -1; //selection end (row,column)
+
+ public bool SelectionInProgress { get { return selStartPos >= 0; }}
+ public void SetSelStartPos () {
+ selStartPos = selEndPos = CurrentPosition;
+ SelectionChanged.Raise (this, null);
+ }
+ public void SetSelEndPos () {
+ selEndPos = CurrentPosition;
+ SelectionChanged.Raise (this, null);
+ }
+ /// <summary>
+ /// Set selection in buffer to -1, empty selection
+ /// </summary>
+ public void ResetSelection () {
+ selStartPos = selEndPos = -1;
+ SelectionChanged.Raise (this, null);
+ }
+ /// <summary>
+ /// ordered selection start and end positions in char units
+ /// </summary>
+ public Point SelectionStart {
+ get { return selEndPos < 0 || selStartPos.Y < selEndPos.Y ? selStartPos :
+ selStartPos.Y > selEndPos.Y ? selEndPos :
+ selStartPos.X < selEndPos.X ? selStartPos : selEndPos; }
+ }
+ public Point SelectionEnd {
+ get { return selEndPos < 0 || selStartPos.Y > selEndPos.Y ? selStartPos :
+ selStartPos.Y < selEndPos.Y ? selEndPos :
+ selStartPos.X > selEndPos.X ? selStartPos : selEndPos; }
+ }
+ public bool SelectionIsEmpty
+ { get { return selEndPos == selStartPos; } }
+ /// <summary>
+ /// Current column in buffer coordinate, tabulation = 1 char
+ /// </summary>
+ public int CurrentColumn{
+ get { return _currentCol; }
+ set {
+ if (value == _currentCol)
+ return;
+
+ editMutex.EnterWriteLock ();
+
+ if (value < 0)
+ _currentCol = 0;
+ else if (value >= lineLength [_currentLine]) {
+ if (_currentLine < LineCount -1 && value > lineLength [_currentLine] - 1)
+ _currentCol = lineLength [_currentLine] - 1;
+ else
+ _currentCol = lineLength [_currentLine];
+ }else
+ _currentCol = value;
+
+ editMutex.ExitWriteLock ();
+
+ PositionChanged.Raise (this, null);
+ }
+ }
+ /// <summary>
+ /// Current row in buffer coordinate, tabulation = 1 char
+ /// </summary>
+ public int CurrentLine{
+ get { return _currentLine; }
+ set {
+ if (value == _currentLine)
+ return;
+
+ editMutex.EnterWriteLock ();
+
+ if (value >= lineLength.Count)
+ _currentLine = lineLength.Count-1;
+ else if (value < 0)
+ _currentLine = 0;
+ else
+ _currentLine = value;
+
+ int c = _currentCol;
+ _currentCol = 0;
+ CurrentColumn = c;
+
+ editMutex.ExitWriteLock();
+
+ PositionChanged.Raise (this, null);
+ }
+ }
+ /// <summary>
+ /// Current position in buffer coordinate, tabulation = 1 char
+ /// </summary>
+ public Point CurrentPosition {
+ get { return new Point(CurrentColumn, CurrentLine); }
+ }
+ /// <summary>
+ /// get char at current position in buffer
+ /// </summary>
+ protected Char CurrentChar { get { return buffer[this [CurrentLine]]; } }
+ public string SelectedText {
+ get {
+ if (SelectionIsEmpty)
+ return "";
+ Point selStart = SelectionStart;
+ Point selEnd = SelectionEnd;
+
+ int ptr = this [selStart.Y] + selStart.X;
+ int length = lineLength[selStart.Y] - selStart.X;
+ for (int i = selStart.Y+1; i <= selEnd.Y; i++)
+ length += lineLength [i];
+ length -= lineLength[selEnd.Y] - selEnd.X;
+
+ return buffer.ToString (ptr, length);
+ }
+ }
+
+ public void GotoWordStart(){
+ if (_currentCol == 0)
+ MoveLeft ();
+ if (_currentCol == 0)
+ return;
+ int ptrStart = BufferIndexOfCurrentPosition;
+ int ptr = ptrStart;
+ char c;
+ //skip white spaces
+ do {
+ ptr--;
+ c = this.GetCharAt (ptr);
+ } while (!char.IsLetterOrDigit (c) && c != '\n' && ptr > 1);
+
+ do {
+ ptr--;
+ c = this.GetCharAt (ptr);
+ } while (char.IsLetterOrDigit (c) && c != '\n' && ptr > 1);
+
+ if (ptr == 0)
+ CurrentColumn = 0;
+ else
+ CurrentColumn -= ptrStart - ptr - 1;
+ }
+ public void GotoWordEnd(){
+ int limx = GetLineLength (_currentLine);
+ if (_currentLine < lineLength.Count - 1)
+ limx--;
+
+ if (_currentCol == limx) {
+ MoveRight ();
+ limx = GetLineLength (_currentLine);
+ if (_currentLine < lineLength.Count - 1)
+ limx--;
+ }
+
+ int ptrLine = GetBufferIndexOfLine(_currentLine);
+ int ptrCol = _currentCol;
+
+ char c;
+ //skip white spaces
+ do {
+ c = GetCharAt (ptrLine+ptrCol);
+ ptrCol++;
+ } while (!char.IsLetterOrDigit (c) && ptrCol < limx);
+
+ do {
+ c = GetCharAt (ptrLine + ptrCol);
+ ptrCol++;
+ } while (char.IsLetterOrDigit (c) && ptrCol < limx);
+ CurrentColumn = ptrCol - 1;
+ }
+ /// <summary>
+ /// Moves cursor one char to the left, move up if cursor reaches start of line
+ /// </summary>
+ public void MoveLeft(){
+ if (CurrentColumn == 0) {
+ CurrentLine--;
+ CurrentColumn = int.MaxValue;
+ } else
+ CurrentColumn--;
+ }
+ /// <summary>
+ /// Moves cursor one char to the right, move down if cursor reaches end of line
+ /// </summary>
+ public void MoveRight(){
+ if (_currentLine < LineCount -1){
+ if (CurrentColumn >= lineLength [CurrentLine] - 1) {
+ CurrentColumn = 0;
+ CurrentLine++;
+ return;
+ }
+ }
+ CurrentColumn++;
+ }
+
+ #endregion
+ }
+}
+
diff --git a/Samples/CrowIDE/src/Editors/CodeBuffer/TextBufferEventArgs.cs b/Samples/CrowIDE/src/Editors/CodeBuffer/TextBufferEventArgs.cs
new file mode 100644
index 00000000..3b67bb1c
--- /dev/null
+++ b/Samples/CrowIDE/src/Editors/CodeBuffer/TextBufferEventArgs.cs
@@ -0,0 +1,20 @@
+using System;
+
+namespace Crow.Text
+{
+ public class TextBufferEventArgs : EventArgs {
+ public int LineStart;
+ public int LineCount;
+
+ public TextBufferEventArgs(int lineNumber) {
+ LineStart = lineNumber;
+ LineCount = 1;
+ }
+ public TextBufferEventArgs(int lineStart, int lineCount) {
+ LineStart = lineStart;
+ LineCount = lineCount;
+ }
+ }
+
+}
+
diff --git a/Samples/CrowIDE/src/Editors/CodeBuffer/TextEditor.cs b/Samples/CrowIDE/src/Editors/CodeBuffer/TextEditor.cs
new file mode 100644
index 00000000..8b16b9a8
--- /dev/null
+++ b/Samples/CrowIDE/src/Editors/CodeBuffer/TextEditor.cs
@@ -0,0 +1,699 @@
+//
+// ScrollingTextBox.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// 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.ComponentModel;
+using vkvg;
+
+namespace Crow.Text
+{
+ /// <summary>
+ /// Scrolling text box optimized for monospace fonts, for coding
+ /// </summary>
+ public class TextEditor : Crow.Coding.Editor
+ {
+ #region CTOR
+ public TextEditor (): base()
+ {
+ buffer = new TextBuffer ();
+ 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.Add ("");
+ }
+ #endregion
+
+ string oldSource = "";
+ volatile bool isDirty = false;
+
+ #region private and protected fields
+ int visibleLines = 1;
+ int visibleColumns = 1;
+
+ TextBuffer buffer;
+
+ Color selBackground;
+ Color selForeground;
+ int selStartCol;
+ int selEndCol;
+
+ protected Rectangle rText;
+ protected FontExtents fe;
+ protected TextExtents te;
+
+ Point mouseLocalPos;
+ bool doubleClicked = false;
+ #endregion
+
+ /// <summary>
+ /// Updates visible line in widget, adapt max scroll y and updatePrintedLines
+ /// </summary>
+ void updateVisibleLines(){
+ visibleLines = (int)Math.Floor ((double)ClientRectangle.Height / (fe.Ascent+fe.Descent));
+ NotifyValueChanged ("VisibleLines", visibleLines);
+ updateMaxScrollY ();
+ RegisterForGraphicUpdate ();
+// System.Diagnostics.Debug.WriteLine ("update visible lines: " + visibleLines);
+// System.Diagnostics.Debug.WriteLine ("update MaxScrollY: " + MaxScrollY);
+ }
+ void updateVisibleColumns(){
+ visibleColumns = (int)Math.Floor ((double)(ClientRectangle.Width)/ fe.MaxXAdvance);
+ NotifyValueChanged ("VisibleColumns", visibleColumns);
+ RegisterForGraphicUpdate ();
+// System.Diagnostics.Debug.WriteLine ("update visible columns: {0} leftMargin:{1}",visibleColumns, leftMargin);
+// System.Diagnostics.Debug.WriteLine ("update MaxScrollX: " + MaxScrollX);
+ }
+ void updateMaxScrollX (int longestTabulatedLineLength) {
+ MaxScrollX = Math.Max (0, longestTabulatedLineLength - visibleColumns);
+ if (longestTabulatedLineLength > 0)
+ NotifyValueChanged ("ChildWidthRatio", Slot.Width * visibleColumns / longestTabulatedLineLength);
+ }
+ void updateMaxScrollY () {
+ int lc = buffer.LineCount;
+ MaxScrollY = Math.Max (0, lc - visibleLines);
+ if (lc > 0)
+ NotifyValueChanged ("ChildHeightRatio", Slot.Height * visibleLines / lc);
+
+ }
+
+
+
+ #region Editor overrides
+ protected override void updateEditorFromProjFile ()
+ {
+ buffer.editMutex.EnterWriteLock ();
+ loadSource ();
+ buffer.editMutex.ExitWriteLock ();
+
+ isDirty = false;
+ oldSource = projFile.Source;
+ projFile.RegisteredEditors [this] = true;
+ }
+ protected override void updateProjFileFromEditor ()
+ {
+ buffer.editMutex.EnterWriteLock ();
+ string newsrc = buffer.FullText;
+ buffer.editMutex.ExitWriteLock ();
+ projFile.UpdateSource (this, newsrc);
+ }
+ protected override bool EditorIsDirty {
+ get { return isDirty; }
+ set { isDirty = value; }
+ }
+ protected override bool IsReady {
+ get { return projFile != null && buffer != null; }
+ }
+ #endregion
+
+ #region Buffer events handlers
+ void Buffer_BufferCleared (object sender, EventArgs e)
+ {
+ editorMutex.EnterWriteLock ();
+
+ MaxScrollX = MaxScrollY = 0;
+ RegisterForGraphicUpdate ();
+ notifyPositionChanged ();
+ isDirty = true;
+
+ editorMutex.ExitWriteLock ();
+ }
+ void Buffer_LineAdditionEvent (object sender, TextBufferEventArgs e)
+ {
+ updateMaxScrollY ();
+ RegisterForGraphicUpdate ();
+ isDirty = true;
+ }
+ void Buffer_LineRemoveEvent (object sender, TextBufferEventArgs e)
+ {
+ updateMaxScrollY ();
+ RegisterForGraphicUpdate ();
+ notifyPositionChanged ();
+ isDirty = true;
+ }
+ void Buffer_LineUpadateEvent (object sender, TextBufferEventArgs e)
+ {
+ RegisterForGraphicUpdate ();
+ notifyPositionChanged ();
+ isDirty = true;
+ }
+ void Buffer_PositionChanged (object sender, EventArgs e)
+ {
+ int cc = getTabulatedColumn (buffer.CurrentPosition);
+
+ if (cc > visibleColumns + ScrollX) {
+ ScrollX = cc - visibleColumns;
+ } else if (cc < ScrollX)
+ ScrollX = cc;
+
+ if (buffer.CurrentLine >= visibleLines + ScrollY - 1)
+ ScrollY = buffer.CurrentLine - visibleLines + 1;
+ else if (buffer.CurrentLine < ScrollY)
+ ScrollY = buffer.CurrentLine;
+
+ RegisterForGraphicUpdate ();
+ notifyPositionChanged ();
+ }
+
+ void Buffer_SelectionChanged (object sender, EventArgs e)
+ {
+ RegisterForGraphicUpdate ();
+ }
+ #endregion
+
+ void notifyPositionChanged (){
+ try {
+ NotifyValueChanged ("CurrentLine", buffer.CurrentLine+1);
+ NotifyValueChanged ("CurrentColumn", buffer.CurrentColumn+1);
+ } catch (Exception ex) {
+ Console.WriteLine (ex.ToString ());
+ }
+ }
+
+ #region Public Crow Properties
+ public int CurrentLine{
+ get { return buffer == null ? 0 : buffer.CurrentLine+1; }
+ set {
+ try {
+ int l = value - 1;
+ if (l == buffer.CurrentLine)
+ return;
+ buffer.CurrentLine = l;
+ } catch (Exception ex) {
+ Console.WriteLine ("Error cur column: " + ex.ToString ());
+ }
+ }
+ }
+ public int CurrentColumn{
+ get { return buffer == null ? 0 : buffer.CurrentColumn+1; }
+ set {
+ try {
+ if (value - 1 == buffer.CurrentColumn)
+ return;
+ buffer.CurrentColumn = value - 1;
+ } catch (Exception ex) {
+ Console.WriteLine ("Error cur column: " + ex.ToString ());
+ }
+ }
+ }
+ [DefaultValue("Blue")]
+ public virtual Color SelectionBackground {
+ get { return selBackground; }
+ set {
+ if (value == selBackground)
+ return;
+ selBackground = value;
+ NotifyValueChanged ("SelectionBackground", selBackground);
+ RegisterForRedraw ();
+ }
+ }
+ [DefaultValue("White")]
+ public virtual Color SelectionForeground {
+ get { return selForeground; }
+ set {
+ if (value == selForeground)
+ return;
+ selForeground = value;
+ NotifyValueChanged ("SelectionForeground", selForeground);
+ RegisterForRedraw ();
+ }
+ }
+ public override int ScrollY {
+ get {
+ return base.ScrollY;
+ }
+ set {
+ if (value == base.ScrollY)
+ return;
+ editorMutex.EnterWriteLock ();
+ base.ScrollY = value;
+ editorMutex.ExitWriteLock ();
+ RegisterForGraphicUpdate ();
+ }
+ }
+ #endregion
+
+
+ void loadSource () {
+ buffer.Load (projFile.Source);
+ projFile.RegisteredEditors [this] = true;
+ updateMaxScrollY ();
+ RegisterForGraphicUpdate ();
+ }
+
+ int getTabulatedColumn (int col, int line) {
+ return buffer.GetSubString (buffer [line],
+ buffer.GetLineLength (line)).Substring(0,col).Replace ("\t", new String (' ', Interface.TabSize)).Length;
+ }
+ int getTabulatedColumn (Point pos) {
+ return getTabulatedColumn (pos.X,pos.Y);
+ }
+
+ #region Drawing
+ void drawLines(Context gr, Rectangle cb) {
+ int longestTabulatedLine = 0;
+ for (int i = 0; i < visibleLines; i++) {
+ int lineIndex = i + ScrollY;
+ if (lineIndex >= buffer.LineCount)//TODO:need optimize
+ break;
+
+ double y = cb.Y + (fe.Ascent+fe.Descent) * i, x = cb.X;
+
+ int lineLength = buffer.GetLineLength (lineIndex);
+ if (lineIndex < buffer.LineCount - 1)//dont print line break
+ lineLength--;
+ string lstr = buffer.GetSubString (buffer [lineIndex],
+ lineLength).Replace ("\t", new String (' ', Interface.TabSize));
+
+ int lstrLength = lstr.Length;
+ if (lstrLength > longestTabulatedLine)
+ longestTabulatedLine = lstrLength;
+
+ if (ScrollX < lstrLength)
+ 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;
+
+ if (lineIndex == buffer.SelectionStart.Y) {
+ rLineX += (selStartCol - ScrollX) * fe.MaxXAdvance;
+ rLineW -= (selStartCol - ScrollX) * fe.MaxXAdvance;
+ }
+ if (lineIndex == buffer.SelectionEnd.Y)
+ rLineW -= (lstr.Length - selEndCol + ScrollX) * fe.MaxXAdvance;
+
+ gr.Save ();
+ gr.Operator = Operator.Source;
+ gr.Rectangle (rLineX, rLineY, rLineW, (fe.Ascent+fe.Descent));
+ 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 ();
+ }
+
+
+ }
+
+ updateMaxScrollX(longestTabulatedLine);
+ }
+ #endregion
+
+ #region GraphicObject overrides
+ public override Font Font {
+ get { return base.Font; }
+ set {
+ base.Font = value;
+
+ using (Surface img = new Surface (IFace.dev, 1, 1)) {
+ using (Context gr = new Context (img)) {
+ gr.FontFace = Font.Name;
+ gr.FontSize = (uint)Font.Size;
+
+ fe = gr.FontExtents;
+ }
+ }
+ MaxScrollY = 0;
+ RegisterForGraphicUpdate ();
+ }
+ }
+ protected override int measureRawSize(LayoutingType lt)
+ {
+ if (lt == LayoutingType.Height)
+ return (int)Math.Ceiling((fe.Ascent+fe.Descent) * buffer.LineCount) + Margin * 2;
+
+ return 0;// (int)(fe.MaxXAdvance * buffer.GetLineLength(buffer.longestLineIdx)) + Margin * 2;
+ }
+ public override void OnLayoutChanges (LayoutingType layoutType)
+ {
+ base.OnLayoutChanges (layoutType);
+
+ if (layoutType == LayoutingType.Height)
+ updateVisibleLines ();
+ else if (layoutType == LayoutingType.Width)
+ updateVisibleColumns ();
+ }
+
+ protected override void onDraw (Context gr)
+ {
+ base.onDraw (gr);
+
+ gr.FontFace = Font.Name;
+ gr.FontSize = (uint)Font.Size;
+ //gr.FontOptions = Interface.FontRenderingOptions;
+ //gr.Antialias = Interface.Antialias;
+
+ Rectangle cb = ClientRectangle;
+
+ Foreground.SetAsSource (gr);
+
+ buffer.editMutex.EnterReadLock ();
+ editorMutex.EnterReadLock ();
+
+ #region draw text cursor
+ if (buffer.SelectionInProgress){
+ selStartCol = getTabulatedColumn (buffer.SelectionStart);
+ selEndCol = getTabulatedColumn (buffer.SelectionEnd);
+ }else if (HasFocus && CurrentLine >= 0){
+ gr.LineWidth = 1.0;
+ double cursorX = cb.X + (getTabulatedColumn(buffer.CurrentPosition) - ScrollX) * fe.MaxXAdvance ;
+ double cursorY = cb.Y + (buffer.CurrentLine - ScrollY) * (fe.Ascent+fe.Descent);
+ gr.MoveTo (0.5 + cursorX, cursorY);
+ gr.LineTo (0.5 + cursorX, cursorY + fe.Ascent+fe.Descent);
+ gr.Stroke();
+ }
+ #endregion
+
+ drawLines (gr, cb);
+
+ editorMutex.ExitReadLock ();
+
+ buffer.editMutex.ExitReadLock ();
+
+ }
+ #endregion
+
+ int getBufferColFromVisualCol (int line, int column) {
+ int i = 0;
+ int buffCol = 0;
+ int buffPtr = buffer [line];
+ while (i < column && buffCol < buffer.GetLineLength(line)) {
+ if (buffer.GetCharAt(buffPtr + buffCol) == '\t')
+ i += Interface.TabSize;
+ else
+ i++;
+ buffCol++;
+ }
+ return buffCol;
+ }
+
+
+ #region Mouse handling
+
+ int hoverLine = -1;
+ public int HoverLine {
+ get { return hoverLine; }
+ set {
+ if (hoverLine == value)
+ return;
+ hoverLine = value;
+ NotifyValueChanged ("HoverLine", hoverLine);
+ }
+ }
+ void updateHoverLine () {
+// int hvl = (int)Math.Max (0, Math.Floor (mouseLocalPos.Y / (fe.Ascent+fe.Descent)));
+// hvl = Math.Min (PrintedLines.Count, hvl);
+// HoverLine = buffer.IndexOf (PrintedLines[hvl]);
+ }
+ void updateCurrentPosFromMouseLocalPos(){
+
+ buffer.CurrentLine = ScrollY + (int)Math.Max (0, Math.Floor (mouseLocalPos.Y / (fe.Ascent+fe.Descent)));
+ int curVisualCol = ScrollX + (int)Math.Round ((mouseLocalPos.X) / fe.MaxXAdvance);
+ buffer.CurrentColumn = getBufferColFromVisualCol (buffer.CurrentLine, curVisualCol);
+ }
+
+ public override void onMouseEnter (object sender, MouseMoveEventArgs e)
+ {
+ base.onMouseEnter (sender, e);
+ IFace.MouseCursor = MouseCursor.IBeam;
+ }
+ public override void onMouseLeave (object sender, MouseMoveEventArgs e)
+ {
+ base.onMouseLeave (sender, e);
+ IFace.MouseCursor = MouseCursor.Arrow;
+ }
+ public override void onMouseMove (object sender, MouseMoveEventArgs e)
+ {
+ base.onMouseMove (sender, e);
+
+ mouseLocalPos = e.Position - ScreenCoordinates(Slot).TopLeft - ClientRectangle.TopLeft;
+
+ updateHoverLine ();
+
+ if (e.Mouse.LeftButton == ButtonState.Released || !buffer.SelectionInProgress)
+ return;
+
+ //mouse is down
+ updateCurrentPosFromMouseLocalPos();
+ buffer.SetSelEndPos ();
+ }
+ public override void onMouseDown (object sender, MouseButtonEventArgs e)
+ {
+ if (!Focusable)
+ return;
+
+ base.onMouseDown (sender, e);
+
+ if (doubleClicked) {
+ doubleClicked = false;
+ return;
+ }
+
+ updateCurrentPosFromMouseLocalPos ();
+ 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, KeyEventArgs e)
+ {
+ //base.onKeyDown (sender, e);
+
+ Key key = e.Key;
+
+ if (IFace.Ctrl) {
+ switch (key) {
+ case Key.S:
+ projFile.Save ();
+ break;
+ case Key.W:
+ editorMutex.EnterWriteLock ();
+ if (IFace.Shift)
+ projFile.Redo (null);
+ else
+ projFile.Undo (null);
+ editorMutex.ExitWriteLock ();
+ break;
+ default:
+ Console.WriteLine ("");
+ break;
+ }
+ }
+
+ switch (key)
+ {
+ case Key.Backspace:
+ buffer.Delete ();
+ break;
+ case Key.Delete:
+ if (buffer.SelectionIsEmpty)
+ buffer.MoveRight ();
+// else if (e.Shift)
+// IFace.Clipboard = buffer.SelectedText;
+ buffer.Delete ();
+ break;
+ case Key.Enter:
+ case Key.KeypadEnter:
+ if (!buffer.SelectionIsEmpty)
+ buffer.Delete ();
+ buffer.InsertLineBreak ();
+ break;
+ case Key.Escape:
+ buffer.ResetSelection ();
+ break;
+ case Key.Home:
+ if (IFace.Shift) {
+ if (buffer.SelectionIsEmpty)
+ buffer.SetSelStartPos ();
+ if (IFace.Ctrl)
+ buffer.CurrentLine = 0;
+ buffer.CurrentColumn = 0;
+ buffer.SetSelEndPos ();
+ break;
+ }
+ buffer.ResetSelection ();
+ if (IFace.Ctrl)
+ buffer.CurrentLine = 0;
+ buffer.CurrentColumn = 0;
+ break;
+ case Key.End:
+ if (IFace.Shift) {
+ if (buffer.SelectionIsEmpty)
+ buffer.SetSelStartPos ();
+ if (IFace.Ctrl)
+ buffer.CurrentLine = int.MaxValue;
+ buffer.CurrentColumn = int.MaxValue;
+ buffer.SetSelEndPos ();
+ break;
+ }
+ buffer.ResetSelection ();
+ if (IFace.Ctrl)
+ buffer.CurrentLine = int.MaxValue;
+ buffer.CurrentColumn = int.MaxValue;
+ break;
+ case Key.Insert:
+ if (IFace.Shift)
+ buffer.InsertAt (IFace.Clipboard);
+ else if (IFace.Ctrl && !buffer.SelectionIsEmpty)
+ IFace.Clipboard = buffer.SelectedText;
+ break;
+ case Key.Left:
+ if (IFace.Shift) {
+ if (buffer.SelectionIsEmpty)
+ buffer.SetSelStartPos ();
+ if (IFace.Ctrl)
+ buffer.GotoWordStart ();
+ else
+ buffer.MoveLeft ();
+ buffer.SetSelEndPos ();
+ break;
+ }
+ buffer.ResetSelection ();
+ if (IFace.Ctrl)
+ buffer.GotoWordStart ();
+ else
+ buffer.MoveLeft();
+ break;
+ case Key.Right:
+ if (IFace.Shift) {
+ if (buffer.SelectionIsEmpty)
+ buffer.SetSelStartPos ();
+ if (IFace.Ctrl)
+ buffer.GotoWordEnd ();
+ else
+ buffer.MoveRight ();
+ buffer.SetSelEndPos ();
+ break;
+ }
+ buffer.ResetSelection ();
+ if (IFace.Ctrl)
+ buffer.GotoWordEnd ();
+ else
+ buffer.MoveRight ();
+ break;
+ case Key.Up:
+ if (IFace.Shift) {
+ if (buffer.SelectionIsEmpty)
+ buffer.SetSelStartPos ();
+ CurrentLine--;
+ buffer.SetSelEndPos ();
+ break;
+ }
+ buffer.ResetSelection ();
+ CurrentLine--;
+ break;
+ case Key.Down:
+ if (IFace.Shift) {
+ if (buffer.SelectionIsEmpty)
+ buffer.SetSelStartPos ();
+ CurrentLine++;
+ buffer.SetSelEndPos ();
+ break;
+ }
+ buffer.ResetSelection ();
+ CurrentLine++;
+ break;
+ case Key.Menu:
+ break;
+ case Key.NumLock:
+ break;
+ case Key.PageDown:
+ if (IFace.Shift) {
+ if (buffer.SelectionIsEmpty)
+ buffer.SetSelStartPos ();
+ CurrentLine += visibleLines;
+ buffer.SetSelEndPos ();
+ break;
+ }
+ buffer.ResetSelection ();
+ CurrentLine += visibleLines;
+ break;
+ case Key.PageUp:
+ if (IFace.Shift) {
+ if (buffer.SelectionIsEmpty)
+ buffer.SetSelStartPos ();
+ CurrentLine -= visibleLines;
+ buffer.SetSelEndPos ();
+ break;
+ }
+ buffer.ResetSelection ();
+ CurrentLine -= visibleLines;
+ break;
+ case Key.Tab:
+ buffer.InsertAt ("\t");
+ break;
+ default:
+ break;
+ }
+ RegisterForGraphicUpdate();
+ }
+ public override void onKeyPress (object sender, KeyPressEventArgs e)
+ {
+ base.onKeyPress (sender, e);
+
+ buffer.InsertAt (e.KeyChar.ToString());
+ buffer.ResetSelection ();
+ }
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/Samples/CrowIDE/src/Editors/CodeBuffer/TextFormatting.cs b/Samples/CrowIDE/src/Editors/CodeBuffer/TextFormatting.cs
new file mode 100644
index 00000000..f7b2e518
--- /dev/null
+++ b/Samples/CrowIDE/src/Editors/CodeBuffer/TextFormatting.cs
@@ -0,0 +1,19 @@
+using System;
+
+namespace Crow.Coding
+{
+ public struct TextFormatting {
+ public Color Foreground;
+ public Color Background;
+ public bool Bold;
+ public bool Italic;
+
+ public TextFormatting(Color fg, Color bg, bool bold = false, bool italic = false){
+ Foreground = fg;
+ Background = bg;
+ Bold = bold;
+ Italic = italic;
+ }
+ }
+}
+
diff --git a/Samples/CrowIDE/src/Editors/CodeBuffer/Token.cs b/Samples/CrowIDE/src/Editors/CodeBuffer/Token.cs
new file mode 100644
index 00000000..8b4df5e5
--- /dev/null
+++ b/Samples/CrowIDE/src/Editors/CodeBuffer/Token.cs
@@ -0,0 +1,71 @@
+//
+// Token.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// Copyright (c) 2017 jp
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+using System;
+
+namespace Crow.Coding
+{
+ public struct Token
+ {
+ public BufferParser.TokenType Type;
+ public string Content;
+ public Point Start;
+ public Point End;
+
+ public string PrintableContent {
+ get { return string.IsNullOrEmpty(Content) ? "" : Content.Replace("\t", new String(' ', Interface.TabSize)); }
+ }
+
+// public Token (TokenType tokType, string content = ""){
+// Type = tokType;
+// Content = content;
+// }
+
+ public bool IsNull { get { return IsEmpty && Type == BufferParser.TokenType.Unknown; }}
+ public bool IsEmpty { get { return string.IsNullOrEmpty(Content); }}
+
+ public static bool operator == (Token t, System.Enum tt){
+ return Convert.ToInt32(t.Type) == Convert.ToInt32(tt);
+ }
+ public static bool operator != (Token t, System.Enum tt){
+ return Convert.ToInt32(t.Type) != Convert.ToInt32(tt);
+ }
+ public static bool operator == (System.Enum tt, Token t){
+ return Convert.ToInt32(t.Type) == Convert.ToInt32(tt);
+ }
+ public static bool operator != (System.Enum tt, Token t){
+ return Convert.ToInt32(t.Type) != Convert.ToInt32(tt);
+ }
+
+ public static Token operator +(Token t, char c){
+ t.Content += c;
+ return t;
+ }
+ public static Token operator +(Token t, string s){
+ t.Content += s;
+ return t;
+ }
+ public override string ToString ()
+ {
+ return string.Format ("[Tok{2}->{3}:{0}: {1}]", Type,Content,Start,End);
+ }
+ }
+}
+
diff --git a/Samples/CrowIDE/src/Editors/EditPane.cs b/Samples/CrowIDE/src/Editors/EditPane.cs
new file mode 100644
index 00000000..a75522f0
--- /dev/null
+++ b/Samples/CrowIDE/src/Editors/EditPane.cs
@@ -0,0 +1,57 @@
+//
+// EditPane.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// 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 Crow;
+using System.Linq;
+
+namespace Crow.Coding
+{
+ public class EditPane : TemplatedGroup
+ {
+ public EditPane () {}
+
+ object selectedItemElement = null;
+
+ public override int SelectedIndex {
+ get {
+ return base.SelectedIndex;
+ }
+ set {
+ base.SelectedIndex = value;
+ }
+ }
+ public object SelectedItemElement {
+ get { return selectedItemElement; }
+ set {
+ if (selectedItemElement == value)
+ return;
+ selectedItemElement = value;
+ NotifyValueChanged ("SelectedItemElement", selectedItemElement);
+ }
+ }
+ }
+}
+
diff --git a/Samples/CrowIDE/src/Editors/Editor.cs b/Samples/CrowIDE/src/Editors/Editor.cs
new file mode 100644
index 00000000..102debb5
--- /dev/null
+++ b/Samples/CrowIDE/src/Editors/Editor.cs
@@ -0,0 +1,105 @@
+//
+// Editor.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// 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.Threading;
+using System.Xml.Serialization;
+
+namespace Crow.Coding
+{
+ public abstract class Editor : ScrollingObject
+ {
+ #region CTOR
+ protected Editor ():base(){
+ Thread t = new Thread (backgroundThreadFunc);
+ t.IsBackground = true;
+ t.Start ();
+ }
+ #endregion
+
+ protected ReaderWriterLockSlim editorMutex = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
+ protected ProjectFile projFile = null;
+ Exception error = null;
+
+ public virtual ProjectFile ProjectNode
+ {
+ get { return projFile; }
+ set
+ {
+ if (projFile == value)
+ return;
+
+ if (projFile != null)
+ projFile.UnregisterEditor (this);
+
+ projFile = value;
+
+ if (projFile != null)
+ projFile.RegisterEditor (this);
+
+ NotifyValueChanged ("ProjectNode", projFile);
+ }
+ }
+ [XmlIgnore]public Exception Error {
+ get { return error; }
+ set {
+ if (error == value)
+ return;
+ error = value;
+ NotifyValueChanged ("Error", error);
+ NotifyValueChanged ("HasError", HasError);
+ }
+ }
+ [XmlIgnore]public bool HasError {
+ get { return error != null; }
+ }
+
+ protected abstract void updateEditorFromProjFile ();
+ protected abstract void updateProjFileFromEditor ();
+ protected abstract bool EditorIsDirty { get; set; }
+ protected virtual bool IsReady { get { return true; }}
+ protected virtual void updateCheckPostProcess () {}
+
+ protected void backgroundThreadFunc () {
+ while (true) {
+ if (IsReady) {
+ if (Monitor.TryEnter (IFace.UpdateMutex)) {
+ if (!projFile.RegisteredEditors [this]) {
+ projFile.RegisteredEditors [this] = true;
+ updateEditorFromProjFile ();
+ } else if (EditorIsDirty) {
+ EditorIsDirty = false;
+ updateProjFileFromEditor ();
+ }
+ updateCheckPostProcess ();
+ Monitor.Exit (IFace.UpdateMutex);
+ }
+ }
+ Thread.Sleep (100);
+ }
+ }
+ }
+}
+
diff --git a/Samples/CrowIDE/src/Editors/ImlSchematicEditor.cs b/Samples/CrowIDE/src/Editors/ImlSchematicEditor.cs
new file mode 100644
index 00000000..64541c90
--- /dev/null
+++ b/Samples/CrowIDE/src/Editors/ImlSchematicEditor.cs
@@ -0,0 +1,132 @@
+//
+// ImlVisualEditor.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// Copyright (c) 2016 jp
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+using System;
+using Crow;
+using System.Threading;
+using System.Xml.Serialization;
+using System.ComponentModel;
+using System.IO;
+using System.Collections.Generic;
+using Crow.IML;
+using System.Text;
+using System.Xml;
+
+namespace Crow.Coding
+{
+ public class ImlSchematicEditor : TemplatedGroup
+ {
+ #region CTOR
+ public ImlSchematicEditor ()
+ {
+ }
+ #endregion
+
+ ProjectFile projNode;
+ Widget selectedItem;
+ ImlProjectItem imlProjFile;
+ Exception imlError = null;
+
+ bool drawGrid, snapToGrid;
+ int gridSpacing;
+
+ [DefaultValue(true)]
+ public bool DrawGrid {
+ get { return drawGrid; }
+ set {
+ if (drawGrid == value)
+ return;
+ drawGrid = value;
+ NotifyValueChanged ("DrawGrid", drawGrid);
+ RegisterForRedraw ();
+ }
+ }
+ [DefaultValue(true)]
+ public bool SnapToGrid {
+ get { return snapToGrid; }
+ set {
+ if (snapToGrid == value)
+ return;
+ snapToGrid = value;
+ NotifyValueChanged ("SnapToGrid", snapToGrid);
+ }
+ }
+ [DefaultValue(10)]
+ public int GridSpacing {
+ get { return gridSpacing; }
+ set {
+ if (gridSpacing == value)
+ return;
+ gridSpacing = value;
+ NotifyValueChanged ("GridSpacing", gridSpacing);
+ RegisterForRedraw ();
+ }
+ }
+ public Widget SelectedItem {
+ get { return selectedItem; }
+ set {
+ if (selectedItem == value)
+ return;
+ selectedItem = value;
+ NotifyValueChanged ("SelectedItem", selectedItem);
+ RegisterForRedraw ();
+ }
+ }
+// public override ProjectFile ProjectNode {
+// get {
+// return projNode;
+// }
+// set {
+// if (projNode == value)
+// return;
+// projNode = value;
+// NotifyValueChanged ("ProjectNode", projNode);
+//
+// if (projNode is ImlProjectItem)
+// imlProjFile = projNode as ImlProjectItem;
+// else
+// imlProjFile = null;
+// }
+// }
+
+
+// protected override bool EditorIsDirty {
+// get {
+// throw new NotImplementedException ();
+// }
+// set {
+// throw new NotImplementedException ();
+// }
+// }
+// protected override void updateProjFileFromEditor ()
+// {
+//
+// }
+// protected override void updateEditorFromProjFile () {
+//
+// }
+// protected override void updateCheckPostProcess ()
+// {
+//
+// }
+
+
+ }
+}
diff --git a/Samples/CrowIDE/src/Editors/ImlVisualEditor.cs b/Samples/CrowIDE/src/Editors/ImlVisualEditor.cs
new file mode 100644
index 00000000..75d8206b
--- /dev/null
+++ b/Samples/CrowIDE/src/Editors/ImlVisualEditor.cs
@@ -0,0 +1,908 @@
+//
+// ImlVisualEditor.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// Copyright (c) 2016 jp
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+using System;
+using Crow;
+using System.Threading;
+using System.Xml.Serialization;
+using System.ComponentModel;
+using System.IO;
+using System.Collections.Generic;
+using Crow.IML;
+using System.Text;
+using System.Xml;
+using System.Diagnostics;
+using vkvg;
+
+namespace Crow.Coding
+{
+ public class ImlVisualEditor : Editor
+ {
+ #region CTOR
+ public ImlVisualEditor () : base()
+ {
+ //imlVE = new DesignInterface (Interface.de);
+ initCommands ();
+ }
+ #endregion
+
+ protected override void onInitialized (object sender, EventArgs e)
+ {
+ initIcons ();
+ base.onInitialized (sender, e);
+ }
+ DesignInterface imlVE;
+ Widget selectedItem;
+ ImlProjectItem imlProjFile;
+
+ bool editorIsDirty = false;//needed when tree is empty
+ bool drawGrid, snapToGrid;
+ int gridSpacing, zoom = 100;
+ Measure designWidth, designHeight;
+ bool updateEnabled;
+
+ Picture icoMove, icoStyle;
+ Rectangle rIcons = default(Rectangle);
+ Size iconSize = new Size(11,11);
+
+ public List<Crow.Command> Commands;
+ Crow.Command cmdDelete;
+
+ void initCommands () {
+ cmdDelete = new Crow.Command (new Action (() => deleteObject (SelectedItem)))
+ { Caption = "Delete", Icon = new SvgPicture ("#CrowIDE.icons.save.svg"), CanExecute = true };
+ Commands = new List<Crow.Command> (new Crow.Command[] { cmdDelete });
+ }
+
+ void initIcons () {
+ icoMove = new SvgPicture ("#CrowIDE.icons.move-arrows.svg");
+
+ // icoStyle = new SvgPicture ();
+ // icoStyle.Load (IFace, "#CrowIDE.icons.palette.svg");
+ }
+
+ [DefaultValue(true)]
+ public bool DrawGrid {
+ get { return drawGrid; }
+ set {
+ if (drawGrid == value)
+ return;
+ drawGrid = value;
+ NotifyValueChanged ("DrawGrid", drawGrid);
+ RegisterForRedraw ();
+ }
+ }
+ [DefaultValue(true)]
+ public bool SnapToGrid {
+ get { return snapToGrid; }
+ set {
+ if (snapToGrid == value)
+ return;
+ snapToGrid = value;
+ NotifyValueChanged ("SnapToGrid", snapToGrid);
+ }
+ }
+ [DefaultValue(10)]
+ public int GridSpacing {
+ get { return gridSpacing; }
+ set {
+ if (gridSpacing == value)
+ return;
+ gridSpacing = value;
+ NotifyValueChanged ("GridSpacing", gridSpacing);
+ RegisterForRedraw ();
+ }
+ }
+ [DefaultValue(100)]
+ public int Zoom {
+ get { return zoom; }
+ set {
+ if (zoom == value)
+ return;
+
+ zoom = value;
+ NotifyValueChanged ("Zoom", zoom);
+ Width = (int)(designWidth * zoom / 100.0);
+ Height = (int)(designHeight * zoom / 100.0);
+ }
+ }
+ [DefaultValue("512")]
+ public Measure DesignWidth {
+ get { return designWidth; }
+ set {
+ if (designWidth == value)
+ return;
+ designWidth = value;
+ NotifyValueChanged ("DesignWidth", designWidth);
+ Width = (int)(designWidth * zoom / 100.0);
+ }
+ }
+ [DefaultValue("400")]
+ public Measure DesignHeight {
+ get { return designHeight; }
+ set {
+ if (designHeight == value)
+ return;
+ designHeight = value;
+ NotifyValueChanged ("DesignHeight", designHeight);
+ Height = (int)(designHeight * zoom / 100.0);
+ }
+ }
+
+ public Widget SelectedItem {
+ get { return selectedItem; }
+ set {
+ if (selectedItem == value)
+ return;
+ selectedItem = value;
+
+ if (selectedItem == null)
+ cmdDelete.CanExecute = false;
+ else
+ cmdDelete.CanExecute = true;
+
+ NotifyValueChanged ("SelectedItem", selectedItem);
+ RegisterForGraphicUpdate ();
+ }
+ }
+ /// <summary>PoinprojFilever the widget</summary>
+ public virtual Widget HoverWidget
+ {
+ get { return imlVE.HoverWidget; }
+ set {
+ if (HoverWidget == value)
+ return;
+
+ imlVE.HoverWidget = value;
+
+ NotifyValueChanged ("HoverWidget", HoverWidget);
+ }
+ }
+ /// <summary>
+ /// use to disable update if tab is not the visible one
+ /// </summary>
+ public bool UpdateEnabled {
+ get { return updateEnabled; }
+ set {
+ if (value == updateEnabled)
+ return;
+ updateEnabled = value;
+ NotifyValueChanged ("UpdateEnabled", updateEnabled);
+ }
+ }
+
+ public List<Widget> GraphicTree {
+ get { return imlVE.GraphicTree; }
+ }
+
+ [XmlIgnore]public List<LQIList> LQIs {
+ get { return imlVE.LQIs; }
+ }
+
+ #region editor overrides
+ public override ProjectFile ProjectNode {
+ get {
+ return base.ProjectNode;
+ }
+ set {
+ base.ProjectNode = value;
+ imlProjFile = projFile as ImlProjectItem;
+ imlVE.ProjFile = imlProjFile;
+ }
+ }
+
+ protected override bool EditorIsDirty {
+ get { return imlProjFile == null ? false :
+ imlProjFile.Instance == null ? editorIsDirty :
+ imlProjFile.Instance.design_HasChanged | editorIsDirty; }
+ set {
+ editorIsDirty = value;
+ if (GraphicTree.Count == 0)
+ return;
+ if (GraphicTree [0] != null)
+ GraphicTree [0].design_HasChanged = value;
+ }
+ }
+ protected override bool IsReady {
+ get { return updateEnabled && imlVE != null && imlProjFile != null; }
+ }
+
+ protected override void updateProjFileFromEditor ()
+ {
+ Debug.WriteLine("\t\tImlEditor updateProjFileFromEditor");
+ try {
+ if (imlProjFile.Instance == null)
+ projFile.UpdateSource(this, @"<?xml version=""1.0""?>");
+ else
+ projFile.UpdateSource(this, imlProjFile.Instance.GetIML());
+ } catch (Exception ex) {
+ Error = ex.InnerException;
+ if (Monitor.IsEntered(imlVE.UpdateMutex))
+ Monitor.Exit (imlVE.UpdateMutex);
+ }
+ }
+ protected override void updateEditorFromProjFile () {
+ Debug.WriteLine("\t\tImlEditor updateEditorFromProjFile");
+ try {
+ string selItemDesignID = null;
+ if (SelectedItem!=null)
+ selItemDesignID = SelectedItem.design_id;
+ imlVE.ClearInterface();
+ Instantiator.NextInstantiatorID = 0;
+ imlVE.Styling = projFile.Project.solution.Styling;
+ imlVE.DefaultValuesLoader.Clear();
+ //imlVE.DefaultTemplates = projFile.Project.solution.DefaultTemplates;
+ imlVE.Instantiators = new Dictionary<string, Instantiator>();
+
+ //prevent error on empty file
+ bool emptyFile = true;
+ string src = projFile.Source;
+ using (Stream s = new MemoryStream (Encoding.UTF8.GetBytes (src))) {
+ using (XmlReader itr = XmlReader.Create (s)) {
+ while(itr.Read()){
+ if (itr.NodeType == XmlNodeType.Element){
+ emptyFile = false;
+ break;
+ }
+ }
+ }
+ }
+ Widget go = null;
+ Error = null;
+
+ if (emptyFile){
+ imlProjFile.Instance = null;
+ }else{
+ imlVE.LoadIMLFragment(src);
+ imlProjFile.Instance = imlVE.GraphicTree[0];
+ if (selItemDesignID!=null)
+ imlProjFile.Instance.FindByDesignID(selItemDesignID,out go);
+
+ }
+ SelectedItem = go;
+ } catch (Exception ex) {
+ Error = ex;
+ }
+ }
+ protected override void updateCheckPostProcess ()
+ {
+ if (Error != null) {
+ RegisterForRedraw ();
+ return;
+ }
+ imlVE.Update ();
+ bool isDirty = false;
+
+ lock (imlVE.RenderMutex)
+ isDirty = imlVE.IsDirty;
+
+ if (isDirty) {
+ lock (IFace.UpdateMutex)
+ RegisterForRedraw ();
+ }
+ }
+ #endregion
+
+ #region GraphicObject overrides
+ public override void OnLayoutChanges (LayoutingType layoutType)
+ {
+ base.OnLayoutChanges (layoutType);
+ switch (layoutType) {
+ case LayoutingType.Width:
+ DesignWidth = Slot.Width * 100 / zoom;
+ imlVE.ProcessResize (new Size(designWidth,designHeight));
+ break;
+ case LayoutingType.Height:
+ DesignHeight = Slot.Height * 100 / zoom;
+ imlVE.ProcessResize (new Size(designWidth,designHeight));
+ break;
+ }
+ }
+
+ public override void onMouseMove (object sender, MouseMoveEventArgs e)
+ {
+ base.onMouseMove (sender, e);
+
+ Widget oldHW = HoverWidget;
+ Rectangle scr = this.ScreenCoordinates (this.getSlot ());
+ ProcessMouseMove (e.X - scr.X, e.Y - scr.Y);
+
+ Widget newHW = HoverWidget;
+ if (newHW == null)
+ return;
+
+ if (draggedObj != null) {
+ if (draggedObj.Parent == null) {
+ if (tryAddObjectTo (newHW, draggedObj)) {
+ RegisterForRedraw ();
+ return;
+ }
+ } else if (newHW != draggedObj) {
+ //lock (imlVE.UpdateMutex) {
+ ILayoutable possibleParent = getPossibleParent (newHW, draggedObj);
+ if (possibleParent == null) {
+ Group g = newHW.Parent as Group;
+ if (g != null && g != draggedObj) {
+ removeObject (draggedObj);
+ g.InsertChild (g.Children.IndexOf (newHW), draggedObj);
+ RegisterForRedraw ();
+ return;
+ }
+ } else if (possibleParent != draggedObj.Parent) {
+ removeObject (draggedObj);
+ if (tryAddObjectTo (possibleParent, draggedObj)) {
+ RegisterForRedraw ();
+ return;
+ }
+ }
+ //}
+ }
+ }
+
+ if (oldHW == newHW)
+ return;
+ //RegisterForRedraw ();
+
+ }
+ public override void onMouseEnter (object sender, MouseMoveEventArgs e)
+ {
+ base.onMouseEnter (sender, e);
+ if (IFace.DragAndDropOperation != null && draggedObj == null)
+ return;
+ IFace.FocusedWidget = this;
+ }
+ public override void onMouseLeave (object sender, MouseMoveEventArgs e)
+ {
+ base.onMouseLeave (sender, e);
+// IFace.FocusedWidget = null;
+ }
+ public override void onMouseDown (object sender, MouseButtonEventArgs e)
+ {
+ if (e.Mouse.RightButton == ButtonState.Pressed) {
+ base.onMouseDown (sender, e);
+ return;
+ }
+ SelectedItem = HoverWidget;
+
+// if (SelectedItem != null && projFile != null) {
+// projFile.CurrentLine = SelectedItem.design_line;
+// projFile.CurrentColumn = SelectedItem.design_column;
+// }
+
+ }
+
+ protected override void onDraw (Context gr)
+ {
+ base.onDraw (gr);
+
+ Rectangle cb = new Rectangle (0, 0, designWidth, designHeight);
+
+ gr.Save ();
+
+ double z = zoom / 100.0;
+
+ gr.Scale (z, z);
+
+ if (drawGrid) {
+ double gridLineWidth = 0.2 / z;
+ double glhw = gridLineWidth / 2.0;
+ int nbLines = cb.Width / gridSpacing;
+ double d = cb.Left + gridSpacing;
+ for (int i = 0; i < nbLines; i++) {
+ gr.MoveTo (d - glhw, cb.Y);
+ gr.LineTo (d - glhw, cb.Bottom);
+ d += gridSpacing;
+ }
+ nbLines = cb.Height / gridSpacing;
+ d = cb.Top + gridSpacing;
+ for (int i = 0; i < nbLines; i++) {
+ gr.MoveTo (cb.X, d - glhw);
+ gr.LineTo (cb.Right, d - glhw);
+ d += gridSpacing;
+ }
+ gr.LineWidth = gridLineWidth;
+ Foreground.SetAsSource (gr, cb);
+ gr.Stroke ();
+ }
+
+ lock (imlVE.RenderMutex) {
+ gr.SetSourceSurface (imlVE.surf, cb.Left, cb.Top);
+ gr.Paint ();
+ imlVE.IsDirty = false;
+ }
+ /*if (Error == null) {
+ gr.SetSourceColor (Color.Black);
+ gr.Rectangle (cb, 1.0 / z);
+ } else {
+ gr.SetSourceColor (Color.LavenderBlush);
+ gr.Rectangle (cb, 2.0 / z);
+ string[] lerrs = Error.ToString ().Split ('\n');
+ Point p = cb.Center;
+ p.Y -= lerrs.Length * 20;
+ foreach (string le in lerrs) {
+ drawCenteredTextLine(gr, p, le);
+ p.Y += 20;
+
+ }
+ }
+ gr.Stroke ();*/
+
+ Rectangle hr;
+
+ /*
+ if (SelectedItem?.Parent != null) {
+
+ gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight);
+ gr.SetFontSize (Font.Size);
+ gr.FontOptions = Interface.FontRenderingOptions;
+ gr.Antialias = Interface.Antialias;
+
+ Widget g = SelectedItem;
+ hr = g.ScreenCoordinates (g.getSlot ());
+
+// Rectangle rIcons = new Rectangle (iconSize);
+// rIcons.Width *= 4;
+// rIcons.Top = hr.Bottom;
+// rIcons.Left = hr.Right - rIcons.Width + iconSize.Width;
+ Rectangle rIcoMove = new Rectangle (hr.BottomRight, iconSize);
+// Rectangle rIcoStyle = rIcoMove;
+// rIcoStyle.Left += iconSize.Width + 4;
+
+ using (Surface mask = new ImageSurface (Format.Argb32, cb.Width, cb.Height)) {
+ using (Context ctx = new Context (mask)) {
+ ctx.Save();
+ ctx.SetSourceRGBA(1.0,1.0,1.0,0.4);
+ ctx.Paint ();
+ ctx.Rectangle (hr);
+ ctx.Operator = Operator.Clear;
+ ctx.Fill ();
+ }
+
+ gr.SetSourceSurface (mask, 0, 0);
+ gr.Paint ();
+
+ using (Surface ol = new ImageSurface (Format.Argb32, cb.Width, cb.Height)) {
+ using (Context ctx = new Context (ol)) {
+ ctx.SetSourceColor (Color.Black);
+ drawDesignOverlay (ctx, g, cb, hr, 0.4 / z, 6.5);
+ }
+
+ gr.SetSourceSurface (ol, 0, 0);
+ gr.Paint ();
+ }
+
+ drawIcon (gr, icoMove, rIcoMove);
+ //drawIcon (gr, icoStyle, rIcoStyle);
+
+ }
+ }*/
+ if (HoverWidget != null) {
+ hr = HoverWidget.ScreenCoordinates (HoverWidget.getSlot ());
+ gr.SetSourceColor (Color.SkyBlue);
+ //gr.SetDash (new double[]{ 5.0, 3.0 }, 0.0);
+ gr.Rectangle (hr, 0.4 / z);
+ }
+ gr.Restore ();
+ }
+
+ void drawIcon (Context gr, Picture pic, Rectangle r) {/*
+// gr.SetSourceColor (Color.Black);
+// CairoHelpers.CairoRectangle (gr, r.Inflated (1), 2, 1.0);
+ gr.SetSourceColor (Color.White);
+ CairoHelpers.CairoRectangle (gr, r.Inflated (1), 2);
+ gr.Fill ();
+ gr.Operator = Operator.Clear;
+ pic.Paint (gr, r);
+ gr.Operator = Operator.Over;*/
+ }
+ void drawDesignOverlay (Context gr, Widget g, Rectangle cb, Rectangle hr, double coteStroke, double space = 6.5){
+ /*
+ double z = zoom / 100.0;
+ double coteW = 3, coteL = 5;
+ bool fill = true;
+ Crow.Cairo.PointD p1 = new Crow.Cairo.PointD (hr.X + 0.5, hr.Y - space);
+ Crow.Cairo.PointD p2 = new Crow.Cairo.PointD (hr.Right - 0.5, hr.Y - space);
+
+ if (p1.Y < cb.Top) {
+ if (hr.Bottom > cb.Bottom - space)
+ p1.Y = p2.Y = hr.Bottom - space;
+ else
+ p1.Y = p2.Y = hr.Bottom + space;
+ }
+
+ if (g.Width.IsFit)
+ gr.DrawCoteInverse (p1, p2, coteStroke, fill, coteW, coteL);
+ else if (g.Width.IsRelativeToParent) {
+ gr.DrawCote (p1, p2, coteStroke, fill, coteW, coteL);
+ if (g.Width.Value < 100)
+ drawCenteredTextLine (gr, p1.Add(p2.Substract(p1).Divide (2)), g.Width.ToString());
+ }else if (g.Width.IsFixed) {
+ gr.DrawCoteFixed (p1, p2, coteStroke * 2.0, coteW);
+ drawCenteredTextLine (gr, p1.Add(p2.Substract(p1).Divide (2)), g.Width.Value.ToString());
+ }
+
+ p1 = new Crow.Cairo.PointD (hr.X - space, hr.Top + 0.5);
+ p2 = new Crow.Cairo.PointD (hr.X - space, hr.Bottom - 0.5);
+
+ if (p1.X < cb.Left) {
+ if (hr.Right > cb.Right - space)
+ p1.X = p2.X = hr.Right - space;
+ else
+ p1.X = p2.X = hr.Right + space;
+ }
+ if (g.Height.IsFit)
+ gr.DrawCoteInverse (p1, p2, coteStroke, fill, coteW, coteL);
+ else if (g.Height.IsRelativeToParent){
+ gr.DrawCote (p1, p2, coteStroke, fill, coteW, coteL);
+ if (g.Height.Value < 100)
+ drawCenteredTextLine (gr, p1.Add(p2.Substract(p1).Divide (2)), g.Height.ToString());
+ }else if (g.Width.IsFixed) {
+ gr.DrawCoteFixed (p1, p2, coteStroke * 2.0, coteW);
+ drawCenteredTextLine (gr, p1.Add(p2.Substract(p1).Divide (2)), g.Height.Value.ToString());
+ }
+
+ // hr.Inflate (2);
+ //gr.SetDash (new double[]{ 1.0, 4.0 }, 0.0);
+ //gr.SetSourceColor (Color.Grey);
+// gr.Rectangle (hr,coteStroke);
+// gr.Stroke ();
+ gr.Operator = Operator.Over;
+ */
+ }
+
+ void drawCenteredTextLine (Context gr, Point center, string txt){
+ drawCenteredTextLine (gr, new PointD(center.X,center.Y), txt);
+ }
+ void drawCenteredTextLine (Context gr, PointD center, string txt){
+ if (string.IsNullOrEmpty(txt))
+ return;
+ FontExtents fe = gr.FontExtents;
+ TextExtents te = gr.TextExtents (txt);
+
+ Rectangle rText = new Rectangle(
+ (int)(center.X - te.Width / 2), (int)(center.Y - (fe.Ascent + fe.Descent) / 2),
+ (int)te.Width, (int)(fe.Ascent + fe.Descent));
+
+ gr.Operator = Operator.Clear;
+ Rectangle r = rText;
+ r.Inflate (2);
+ gr.Rectangle (r);
+ gr.Fill ();
+ gr.Operator = Operator.Over;
+
+ gr.MoveTo (rText.X, rText.Y + fe.Ascent);
+ gr.ShowText (txt);
+ gr.Fill ();
+
+ }
+ protected override void onDragEnter (object sender, DragDropEventArgs e)
+ {
+ base.onDragEnter (sender, e);
+ GraphicObjectDesignContainer godc = e.DragSource.DataSource as GraphicObjectDesignContainer;
+ if (godc == null)
+ return;
+ createDraggedObj (godc.CrowType);
+ }
+ protected override void onDragLeave (object sender, DragDropEventArgs e)
+ {
+ base.onDragLeave (sender, e);
+ GraphicObjectDesignContainer godc = e.DragSource.DataSource as GraphicObjectDesignContainer;
+ if (godc == null)
+ return;
+ ClearDraggedObj ();
+ }
+
+ protected override void onStartDrag (object sender, DragDropEventArgs e)
+ {
+ base.onStartDrag (sender, e);
+ if (SelectedItem == null)
+ return;
+
+ Widget dumy = new Widget (IFace);
+ dumy.EndDrag += dumyOnEndDrag;
+ dumy.Drop += dumyOnDrop;
+ dumy.IsDragged = true;
+ IFace.ActiveWidget = dumy;
+ e.DragSource.IsDragged = false;
+ IFace.DragAndDropOperation.DragSource = dumy;
+ draggedObj = SelectedItem;
+ int dragIconSize = 48;
+ lock (IFace.UpdateMutex) {
+ IFace.DragImageHeight = dragIconSize;
+ IFace.DragImageWidth = dragIconSize;
+ IFace.DragImage = draggedObj.CreateIcon(dragIconSize);
+ }
+ removeObject (draggedObj);
+ SelectedItem = null;
+ HoverWidget = null;
+ }
+ void dumyOnEndDrag (object sender, DragDropEventArgs e)
+ {
+ IFace.ClearDragImage ();
+ }
+ void dumyOnDrop (object sender, DragDropEventArgs e)
+ {
+ ClearDraggedObj (false);
+ IFace.ClearDragImage ();
+ }
+ #endregion
+
+
+ #region draggedObj handling
+
+ public Widget draggedObj = null;
+
+ void createDraggedObj (Type crowType) {
+ lock (imlVE.UpdateMutex) {
+ draggedObj = imlVE.CreateITorFromIMLFragment ("<" + crowType.Name + "/>").CreateInstance ();
+ }
+ }
+
+ public void ClearDraggedObj (bool removeFromTree = true) {
+ if (removeFromTree)
+ deleteObject (draggedObj);
+ draggedObj = null;
+ }
+ #endregion
+
+ void removeObject (Widget go) {
+ if (go == null)
+ return;
+ if (go.Parent == null)
+ return;
+// lock (imlVE.UpdateMutex) {
+ Interface i = go.Parent as Interface;
+ if (i != null) {
+ i.RemoveWidget (go);
+ imlProjFile.Instance = null;
+ } else {
+ Container c = go.Parent as Container;
+ if (c != null)
+ c.SetChild (null);
+ else {
+ TemplatedContainer tc = go.Parent as TemplatedContainer;
+ if (tc != null)
+ tc.Content = null;
+ else {
+ Group g = go.Parent as Group;
+ if (g != null)
+ g.RemoveChild (go);
+ }
+ }
+ }
+ EditorIsDirty = true;
+ //}
+ }
+ void deleteObject (Widget go) {
+ if (go == null)
+ return;
+ //lock (imlVE.UpdateMutex) {
+ removeObject (go);
+ go.Dispose ();
+ //}
+ }
+
+ ILayoutable getPossibleParent (ILayoutable parent, Widget go) {
+ if (go == null)
+ return null;
+// lock (imlVE.UpdateMutex) {
+
+ Interface i = null;
+ if (parent == null)
+ i = imlVE;
+ else
+ i = parent as Interface;
+ if (i != null)
+ return i.GraphicTree.Count > 0 ? null : i;
+
+ Container c = parent as Container;
+ if (c != null)
+ return c.Child == null || c.Child == go ? c : null;
+
+ TemplatedContainer tc = parent as TemplatedContainer;
+ if (tc != null)
+ return tc.Content == null || tc.Content == go? tc : null;
+
+ return parent as Group;
+// }
+ }
+ bool tryAddObjectTo (ILayoutable parent, Widget go) {
+ if (go == null)
+ return false;
+// lock (imlVE.UpdateMutex) {
+ Interface i = null;
+ if (parent == null)
+ i = imlVE;
+ else
+ i = parent as Interface;
+ if (i != null) {
+ if (i.GraphicTree.Count > 0)
+ return false;
+ i.AddWidget (go);
+ imlProjFile.Instance = go;
+ EditorIsDirty = true;
+ return true;
+ }
+ Container c = parent as Container;
+ if (c != null) {
+ if (c.Child != null)
+ return false;
+ //return tryAddObjectTo (c.Parent, go);
+ c.SetChild (go);
+ EditorIsDirty = true;
+ return true;
+ }
+ TemplatedContainer tc = parent as TemplatedContainer;
+ if (tc != null) {
+ if (tc.Content != null)
+ return false;
+ //return tryAddObjectTo (c.Parent, go);
+ tc.Content = (go);
+ EditorIsDirty = true;
+ return true;
+ }
+ Group g = parent as Group;
+ if (g != null) {
+ g.AddChild (go);
+ EditorIsDirty = true;
+ return true;
+ }
+ return false;//tryAddObjectTo (parent.Parent, go);
+// }
+ }
+
+
+
+ void WidgetCheckOver (Widget go, MouseMoveEventArgs e){
+ Type tGo = go.GetType();
+ if (typeof(TemplatedGroup).IsAssignableFrom (tGo)) {
+
+ } else if (typeof(TemplatedContainer).IsAssignableFrom (tGo)) {
+ TemplatedContainer c = go as TemplatedContainer;
+ if (c.Content?.MouseIsIn (e.Position) == true) {
+ WidgetCheckOver (c.Content, e);
+ return;
+ }
+ } else if (typeof(TemplatedControl).IsAssignableFrom (tGo)) {
+ } else if (typeof(Group).IsAssignableFrom (tGo)) {
+ Group c = go as Group;
+ for (int i = c.Children.Count -1; i >= 0; i--) {
+ if (c.Children[i].MouseIsIn (e.Position)) {
+ WidgetCheckOver (c.Children[i], e);
+ return;
+ }
+ }
+ } else if (typeof(Crow.Container).IsAssignableFrom (tGo)) {
+ Crow.Container c = go as Crow.Container;
+ if (c.Child?.MouseIsIn (e.Position)==true) {
+ WidgetCheckOver (c.Child, e);
+ return;
+ }
+ }
+ HoverWidget = go;
+ WidgetMouseEnter (go, e);
+ }
+ void WidgetMouseLeave (Widget go, MouseMoveEventArgs e){
+
+ }
+ void WidgetMouseEnter (Widget go, MouseMoveEventArgs e){
+
+ }
+ void WidgetMouseMove (Widget go, MouseMoveEventArgs e){}
+
+ public bool ProcessMouseMove(int x, int y)
+ {
+ int deltaX = x - imlVE.Mouse.X;
+ int deltaY = y - imlVE.Mouse.Y;
+ imlVE.Mouse.X = x;
+ imlVE.Mouse.Y = y;
+ MouseMoveEventArgs e = new MouseMoveEventArgs (x, y, deltaX, deltaY);
+ e.Mouse = imlVE.Mouse;
+
+ if (imlVE.ActiveWidget != null) {
+ //TODO, ensure object is still in the graphic tree
+ //send move evt even if mouse move outside bounds
+ WidgetMouseMove (imlVE.ActiveWidget, e);
+ return true;
+ }
+
+ if (HoverWidget != null) {
+ //TODO, ensure object is still in the graphic tree
+ //check topmost graphicobject first
+ Widget tmp = HoverWidget;
+ Widget topc = null;
+ while (tmp is Widget) {
+ topc = tmp;
+ tmp = tmp.LogicalParent as Widget;
+ }
+ int idxhw = imlVE.GraphicTree.IndexOf (topc);
+ if (idxhw != 0) {
+ int i = 0;
+ while (i < idxhw) {
+ if (imlVE.GraphicTree [i].LogicalParent == imlVE.GraphicTree [i].Parent) {
+ if (imlVE.GraphicTree [i].MouseIsIn (e.Position)) {
+ while (imlVE.HoverWidget != null) {
+ WidgetMouseLeave (imlVE.HoverWidget, e);
+ imlVE.HoverWidget = imlVE.HoverWidget.LogicalParent as Widget;
+ }
+
+ WidgetCheckOver (GraphicTree [i], e);
+ return true;
+ }
+ }
+ i++;
+ }
+ }
+
+
+ if (imlVE.HoverWidget.MouseIsIn (e.Position)) {
+ WidgetCheckOver (imlVE.HoverWidget, (e));
+ return true;
+ } else {
+ WidgetMouseLeave (imlVE.HoverWidget, e);
+ //seek upward from last focused graph obj's
+ while (imlVE.HoverWidget.LogicalParent as Widget != null) {
+ imlVE.HoverWidget = imlVE.HoverWidget.LogicalParent as Widget;
+ if (imlVE.HoverWidget.MouseIsIn (e.Position)) {
+ WidgetCheckOver (imlVE.HoverWidget, e);
+ return true;
+ } else
+ WidgetMouseLeave (imlVE.HoverWidget, e);
+ }
+ }
+ }
+
+ //top level graphic obj's parsing
+ lock (imlVE.GraphicTree) {
+ for (int i = 0; i < imlVE.GraphicTree.Count; i++) {
+ Widget g = imlVE.GraphicTree [i];
+ if (g.MouseIsIn (e.Position)) {
+ WidgetCheckOver (g, e);
+ return true;
+ }
+ }
+ }
+ imlVE.HoverWidget = null;
+ return false;
+
+ }
+
+ void GTView_SelectedItemChanged (object sender, SelectionChangeEventArgs e)
+ {
+ SelectedItem = e.NewValue as Widget;
+ }
+
+
+ public override void onKeyDown (object sender, KeyEventArgs e)
+ {
+
+ switch (e.Key) {
+ case Key.Delete:
+ if (selectedItem == null)
+ return;
+ deleteObject (selectedItem);
+ break;
+ case Key.Escape:
+ SelectedItem = null;
+ break;
+ }
+ }
+ }
+}
diff --git a/Samples/CrowIDE/src/Editors/Parsers/BufferParser.cs b/Samples/CrowIDE/src/Editors/Parsers/BufferParser.cs
new file mode 100644
index 00000000..c193c7db
--- /dev/null
+++ b/Samples/CrowIDE/src/Editors/Parsers/BufferParser.cs
@@ -0,0 +1,380 @@
+using System;
+using System.IO;
+using Crow;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Text;
+
+namespace Crow.Coding
+{
+ /// <summary>
+ /// base class for tokenizing sources
+ /// </summary>
+ public abstract class BufferParser
+ {
+ /// <summary>
+ /// Default tokens, this enum may be overriden in derived parser with the new keyword,
+ /// see XMLParser for example.
+ /// </summary>
+ public enum TokenType {
+ Unknown = 0,
+ WhiteSpace = 1,
+ NewLine = 2,
+ LineComment = 3,
+ BlockCommentStart = 4,
+ BlockComment = 5,
+ BlockCommentEnd = 6,
+ Preprocessor = 7,
+ Identifier = 8,
+ Keyword = 9,
+ OpenBlock = 10,
+ CloseBlock = 11,
+ StatementEnding = 12,
+ OperatorOrPunctuation = 13,
+ IntegerLitteral = 14,
+ RealLitteral = 15,
+ StringLitteralOpening = 16,
+ StringLitteralClosing = 17,
+ StringLitteral = 18,
+ CharLitteralOpening = 19,
+ CharLitteralClosing = 20,
+ CharLitteral = 21,
+ BoolLitteral = 22,
+ NullLitteral = 23,
+ Type = 24,
+ }
+
+ #region CTOR
+ public BufferParser (CodeBuffer _buffer)
+ {
+ buffer = _buffer;
+
+ buffer.LineUpadateEvent += Buffer_LineUpadateEvent;
+ //buffer.LineAdditionEvent += Buffer_LineAdditionEvent;;
+ buffer.LineRemoveEvent += Buffer_LineRemoveEvent;
+ buffer.BufferCleared += Buffer_BufferCleared;
+ }
+
+ #endregion
+
+ #region Buffer events handlers
+ void Buffer_BufferCleared (object sender, EventArgs e)
+ {
+
+ }
+ void Buffer_LineAdditionEvent (object sender, CodeBufferEventArgs e)
+ {
+
+ }
+ void Buffer_LineRemoveEvent (object sender, CodeBufferEventArgs e)
+ {
+ reparseSource ();
+ }
+ void Buffer_LineUpadateEvent (object sender, CodeBufferEventArgs e)
+ {
+ for (int i = 0; i < e.LineCount; i++)
+ TryParseBufferLine (e.LineStart + i);
+ reparseSource ();
+ }
+ #endregion
+
+ internal int currentLine = 0;
+ internal int currentColumn = 0;
+
+ int syntTreeDepth = 0;
+ public int SyntacticTreeDepth {
+ get { return syntTreeDepth;}
+ set {
+ syntTreeDepth = value;
+ if (syntTreeDepth > SyntacticTreeMaxDepth)
+ SyntacticTreeMaxDepth = syntTreeDepth;
+ }
+ }
+ public int SyntacticTreeMaxDepth = 0;
+
+ protected CodeBuffer buffer;
+ protected Token currentTok;
+ protected bool eol = true;
+ protected Point CurrentPosition {
+ get { return new Point (currentLine, currentColumn); }
+ set {
+ currentLine = value.Y;
+ currentColumn = value.X;
+ }
+ }
+
+ public Node RootNode;
+
+ public abstract void ParseCurrentLine();
+ public abstract void SyntaxAnalysis ();
+ public void reparseSource () {
+ for (int i = 0; i < buffer.LineCount; i++) {
+ if (!buffer[i].IsParsed)
+ TryParseBufferLine (i);
+ }
+ try {
+ SyntaxAnalysis ();
+ } catch (Exception ex) {
+ Debug.WriteLine ("Syntax Error: " + ex.ToString ());
+ if (ex is ParserException)
+ SetLineInError (ex as ParserException);
+ }
+ }
+ public void TryParseBufferLine(int lPtr) {
+ buffer [lPtr].exception = null;
+ currentLine = lPtr;
+ currentColumn = 0;
+ eol = false;
+
+ try {
+ ParseCurrentLine ();
+ } catch (Exception ex) {
+ Debug.WriteLine (ex.ToString ());
+ if (ex is ParserException)
+ SetLineInError (ex as ParserException);
+ }
+
+ }
+
+ public virtual void SetLineInError(ParserException ex) {
+ currentTok = default(Token);
+ if (ex.Line >= buffer.LineCount)
+ ex.Line = buffer.LineCount - 1;
+ if (buffer [ex.Line].IsFolded)
+ buffer.ToogleFolding (ex.Line);
+ buffer [ex.Line].SetLineInError (ex);
+ }
+ public virtual string LineBrkRegex {
+ get { return @"\r\n|\r|\n|\\\\n"; }
+ }
+ void updateFolding () {
+ // Stack<TokenList> foldings = new Stack<TokenList>();
+ // bool inStartTag = false;
+ //
+ // for (int i = 0; i < parser.Tokens.Count; i++) {
+ // TokenList tl = parser.Tokens [i];
+ // tl.foldingTo = null;
+ // int fstTK = tl.FirstNonBlankTokenIndex;
+ // if (fstTK > 0 && fstTK < tl.Count - 1) {
+ // if (tl [fstTK + 1] != XMLParser.TokenType.ElementName)
+ // continue;
+ // if (tl [fstTK] == XMLParser.TokenType.ElementStart) {
+ // //search closing tag
+ // int tkPtr = fstTK+2;
+ // while (tkPtr < tl.Count) {
+ // if (tl [tkPtr] == XMLParser.TokenType.ElementClosing)
+ //
+ // tkPtr++;
+ // }
+ // if (tl.EndingState == (int)XMLParser.States.Content)
+ // foldings.Push (tl);
+ // else if (tl.EndingState == (int)XMLParser.States.StartTag)
+ // inStartTag = true;
+ // continue;
+ // }
+ // if (tl [fstTK] == XMLParser.TokenType.ElementEnd) {
+ // TokenList tls = foldings.Pop ();
+ // int fstTKs = tls.FirstNonBlankTokenIndex;
+ // if (tls [fstTK + 1].Content == tl [fstTK + 1].Content) {
+ // tl.foldingTo = tls;
+ // continue;
+ // }
+ // parser.CurrentPosition = tls [fstTK + 1].Start;
+ // parser.SetLineInError(new ParserException(parser, "closing tag not corresponding"));
+ // }
+ //
+ // }
+ // }
+ }
+
+ #region low level parsing
+ protected void addCharToCurTok(char c, Point position){
+ currentTok.Start = position;
+ currentTok += c;
+ }
+ /// <summary>
+ /// Read one char from current position in buffer and store it into the current token
+ /// </summary>
+ /// <param name="startOfTok">if true, set the Start position of the current token to the current position</param>
+ protected void readToCurrTok(bool startOfTok = false){
+ if (startOfTok)
+ currentTok.Start = CurrentPosition;
+ currentTok += Read();
+ }
+ /// <summary>
+ /// read n char from the buffer and store it into the current token
+ /// </summary>
+ protected void readToCurrTok(int length) {
+ for (int i = 0; i < length; i++)
+ currentTok += Read ();
+ }
+ /// <summary>
+ /// Save current token into current TokensLine and raz current token
+ /// </summary>
+ protected void saveAndResetCurrentTok() {
+ currentTok.End = CurrentPosition;
+ buffer[currentLine].Tokens.Add (currentTok);
+ currentTok = default(Token);
+ }
+ /// <summary>
+ /// read one char and add current token to current TokensLine, current token is reset
+ /// </summary>
+ /// <param name="type">Type of the token</param>
+ /// <param name="startToc">set start of token to current position</param>
+ protected void readAndResetCurrentTok(System.Enum type, bool startToc = false) {
+ readToCurrTok ();
+ saveAndResetCurrentTok (type);
+ }
+ /// <summary>
+ /// Save current tok
+ /// </summary>
+ /// <param name="type">set the type of the tok</param>
+ protected void saveAndResetCurrentTok(System.Enum type) {
+ currentTok.Type = (TokenType)type;
+ saveAndResetCurrentTok ();
+ }
+ protected void setPreviousTokOfTypeTo (TokenType inType, TokenType newType) {
+ for (int i = currentLine; i >= 0; i--) {
+ int j = buffer [i].Tokens.Count - 1;
+ while (j >= 0) {
+ if (buffer [i].Tokens [j].Type == inType) {
+ Token t = buffer [i].Tokens [j];
+ t.Type = newType;
+ buffer [i].Tokens [j] = t;
+ return;
+ }
+ j--;
+ }
+ }
+ }
+ /// <summary>
+ /// Peek next char, emit '\n' if current column > buffer's line length
+ /// Throw error if eof is true
+ /// </summary>
+ protected virtual char Peek() {
+ if (eol)
+ throw new ParserException (currentLine, currentColumn, "Unexpected End of line");
+ return currentColumn < buffer [currentLine].Length ?
+ buffer [currentLine] [currentColumn] : '\n';
+ }
+ /// <summary>
+ /// Peek n char from buffer or less if remaining char in buffer's line is less than requested
+ /// if end of line is reached, no '\n' will be emitted, instead, empty string is returned. '\n' should be checked only
+ /// with single char Peek().
+ /// Throw error is eof is true
+ /// </summary>
+ /// <param name="length">Length.</param>
+ protected virtual string Peek(int length) {
+ if (eol)
+ return "";//throw new ParserException (currentLine, currentColumn, "Unexpected End of Line");
+ int lg = Math.Min(length, Math.Max (buffer [currentLine].Length - currentColumn, buffer [currentLine].Length - currentColumn - length));
+ if (lg == 0)
+ return "";
+ return buffer [currentLine].Content.Substring (currentColumn, lg);
+ }
+ /// <summary>
+ /// read one char from buffer at current position, if '\n' is read, current line is incremented
+ /// and column is reset to 0
+ /// </summary>
+ protected virtual char Read() {
+ char c = Peek ();
+ if (c == '\n')
+ eol = true;
+ currentColumn++;
+ return c;
+ }
+ protected virtual string Read(int charCount){
+ string tmp = "";
+ for (int i = 0; i < charCount; i++) {
+ if (eol)
+ break;
+ tmp += Read ();
+ }
+ return tmp;
+ }
+ /// <summary>
+ /// read until end of line is reached
+ /// </summary>
+ /// <returns>string read</returns>
+ protected virtual string ReadLine () {
+ StringBuilder tmp = new StringBuilder();
+ char c = Read ();
+ while (!eol) {
+ tmp.Append (c);
+ c = Read ();
+ }
+ return tmp.ToString();
+ }
+ /// <summary>
+ /// read until end expression is reached or end of line.
+ /// </summary>
+ /// <returns>string read minus the ending expression that has to be read after</returns>
+ /// <param name="endExp">Expression to search for</param>
+ protected virtual string ReadLineUntil (string endExp){
+ string tmp = "";
+
+ while (!eol) {
+ if (buffer [currentLine].Length - currentColumn - endExp.Length < 0) {
+ tmp += ReadLine();
+ break;
+ }
+ if (string.Equals (Peek (endExp.Length), endExp))
+ return tmp;
+ tmp += Read();
+ }
+ return tmp;
+ }
+ /// <summary>
+ /// skip white spaces, but not line break. Save spaces in a WhiteSpace token.
+ /// </summary>
+ protected void SkipWhiteSpaces () {
+ if (currentTok.Type != TokenType.Unknown)
+ throw new ParserException (currentLine, currentColumn, "current token should be reset to unknown (0) before skiping white spaces");
+ while (!eol) {
+ if (!char.IsWhiteSpace (Peek ())||Peek()=='\n')
+ break;
+ readToCurrTok (currentTok.Type == TokenType.Unknown);
+ currentTok.Type = TokenType.WhiteSpace;
+ }
+ if (currentTok.Type != TokenType.Unknown)
+ saveAndResetCurrentTok ();
+ }
+ #endregion
+
+ protected Node addChildNode (Node curNode, CodeLine cl, int tokPtr, string type = "") {
+ Node n = new Node () { Name = cl.Tokens [tokPtr].Content, StartLine = cl, Type = type };
+ curNode.AddChild (n);
+ if (cl.SyntacticNode == null)
+ cl.SyntacticNode = n;
+ SyntacticTreeDepth++;
+ return n;
+ }
+ protected void closeNodeAndGoUp (ref Node n, CodeLine cl, string type = ""){
+ while (n != null) {
+ if (n.Type == type) {
+ n.EndLine = cl;
+ n = n.Parent;
+ SyntacticTreeDepth--;
+ break;
+ }
+ n = n.Parent;
+ SyntacticTreeDepth--;
+ }
+ }
+ protected void closeNodeAndGoUp (ref Node n, CodeLine cl){
+ SyntacticTreeDepth--;
+ n.EndLine = cl;
+ n = n.Parent;
+ }
+
+ protected void initSyntaxAnalysis () {
+ RootNode = new Node () { Name = "RootNode", Type="Root" };
+ SyntacticTreeDepth = SyntacticTreeMaxDepth = 0;
+ }
+
+
+ protected void throwParserException(string msg){
+ throw new ParserException (currentLine, currentColumn, msg);
+ }
+ }
+} \ No newline at end of file
diff --git a/Samples/CrowIDE/src/Editors/Parsers/CSharpParser.cs b/Samples/CrowIDE/src/Editors/Parsers/CSharpParser.cs
new file mode 100644
index 00000000..23655aa6
--- /dev/null
+++ b/Samples/CrowIDE/src/Editors/Parsers/CSharpParser.cs
@@ -0,0 +1,386 @@
+using System;
+using Crow;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Text.RegularExpressions;
+using System.Linq;
+
+namespace Crow.Coding
+{
+ public class CSharpParser : BufferParser
+ {
+ #region keywords
+ string[] keywords = new string[] {
+ "abstract",
+ "as",
+ "ascending",
+ "async",
+ "await",
+ "base",
+ "bool",
+ "break",
+ "byte",
+ "case",
+ "catch",
+ "char",
+ "checked",
+ "class",
+ "const",
+ "continue",
+ "decimal",
+ "default",
+ "delegate",
+ "descending",
+ "do",
+ "double",
+ "dynamic",
+ "else",
+ "enum",
+ "equals",
+ "event",
+ "explicit",
+ "extern",
+ "false",
+ "finally",
+ "fixed",
+ "float",
+ "for",
+ "foreach",
+ "from",
+ "get",
+ "goto",
+ "group",
+ "if",
+ "implicit",
+ "in",
+ "int",
+ "interface",
+ "internal",
+ "is",
+ "join",
+ "let",
+ "lock",
+ "long",
+ "nameof",
+ "namespace",
+ "new",
+ "null",
+ "object",
+ "operator",
+ "orderby",
+ "out",
+ "override",
+ "params",
+ "partial",
+ "private",
+ "protected",
+ "public",
+ "readonly",
+ "ref",
+ "return",
+ "sbyte",
+ "sealed",
+ "select",
+ "set",
+ "short",
+ "sizeof",
+ "stackalloc",
+ "static",
+ "string",
+ "struct",
+ "switch",
+ "this",
+ "throw",
+ "true",
+ "try",
+ "typeof",
+ "uint",
+ "ulong",
+ "unchecked",
+ "unsafe",
+ "ushort",
+ "using",
+ "value",
+ "var",
+ "virtual",
+ "void",
+ "volatile",
+ "when",
+ "where",
+ "while",
+ "yield "
+ };
+ #endregion
+
+ public enum States
+ {
+ init,
+ BlockComment,
+ InNameSpace,
+ InClass,
+ InMember,
+ Unknown,
+ }
+
+ public CSharpParser (CodeBuffer _buffer) : base(_buffer)
+ {
+ }
+
+ #region Regular Expression for validity checks
+ static Regex rxValidChar = new Regex(@"\p{Lu}|\p{Ll}|\p{Lt}|\p{Lm}|\p{Lo}|\p{Nl}|\p{Mn}|\p{Mc}|\p{Nd}|\p{Pc}|\p{Cf}");
+ static Regex rxNameStartChar = new Regex(@"_|\p{Lu}|\p{Ll}|\p{Lt}|\p{Lm}|\p{Lo}|\p{Nl}");
+ static Regex rxNameChar = new Regex(@"\p{Lu}|\p{Ll}|\p{Lt}|\p{Lm}|\p{Lo}|\p{Nl}|\p{Mn}|\p{Mc}|\p{Nd}|\p{Pc}|\p{Cf}");
+ static Regex rxNewLineChar = new Regex(@"\u000D|\u000A|\u0085|\u2028|\u2029");
+ static Regex rxWhiteSpaceChar = new Regex(@"\p{Zs}|\u0009|\u000B|\u000C");
+ static Regex rxDecimal = new Regex(@"[0-9]+");
+ static Regex rxHexadecimal = new Regex(@"[0-9a-fA-F]+");
+
+ public static bool CharIsValidCharStartName (char c) {
+ return rxNameStartChar.IsMatch(new string(new char[]{c}));
+ }
+ public static bool CharIsValidCharName (char c) {
+ return rxNameChar.IsMatch(new string(new char[]{c}));
+ }
+
+ public bool nextCharIsValidCharStartName
+ {
+ get { return CharIsValidCharStartName(Peek()); }
+ }
+ public bool nextCharIsValidCharName
+ {
+ get { return CharIsValidCharName(Peek()); }
+ }
+ #endregion
+
+ States curState = States.init;
+ States savedState = States.init;
+
+ public override void ParseCurrentLine ()
+ {
+ //Debug.WriteLine (string.Format("parsing line:{0}", currentLine));
+ CodeLine cl = buffer [currentLine];
+ cl.Tokens = new List<Token> ();
+
+
+ //retrieve current parser state from previous line
+ if (currentLine > 0)
+ curState = (States)buffer[currentLine - 1].EndingState;
+ else
+ curState = States.init;
+
+ States previousEndingState = (States)cl.EndingState;
+
+ while (! eol) {
+ if (currentTok.IsNull)
+ SkipWhiteSpaces ();
+
+ if (curState == States.BlockComment) {
+ currentTok.Start = CurrentPosition;
+ currentTok.Type = (BufferParser.TokenType)TokenType.BlockComment;
+ currentTok += ReadLineUntil ("*/");
+ if (Peek (2) == "*/") {
+ readToCurrTok (2);
+ curState = savedState;
+ }
+ saveAndResetCurrentTok ();
+ continue;
+ }
+
+ switch (Peek()) {
+ case '\n':
+ eol = true;
+ if (!currentTok.IsNull)
+ saveAndResetCurrentTok ();
+ break;
+ case '#':
+ readToCurrTok (true);
+ currentTok += ReadLine ();
+ saveAndResetCurrentTok (TokenType.Preprocessor);
+ break;
+ case '/':
+ readToCurrTok (true);
+ switch (Peek ()) {
+ case '*':
+ readToCurrTok ();
+ currentTok += ReadLine ();
+ //currentTok.Type = (Parser.TokenType)TokenType.BlockComment;
+ savedState = curState;
+ curState = States.BlockComment;
+ saveAndResetCurrentTok (TokenType.BlockComment);
+ break;
+ case '/':
+ //readToCurrTok ();
+ currentTok += ReadLine ();
+ saveAndResetCurrentTok (TokenType.LineComment);
+ //currentTok.Type = (Parser.TokenType)TokenType.LineComment;
+ break;
+ default:
+ currentTok += ReadLine ();
+ saveAndResetCurrentTok (TokenType.Unknown);
+ break;
+ }
+ break;
+ case '{':
+ if (currentTok.IsNull)
+ readAndResetCurrentTok (TokenType.OpenBlock, true);
+ else
+ readToCurrTok ();
+ break;
+ case '}':
+ if (currentTok.IsNull)
+ readAndResetCurrentTok (TokenType.CloseBlock, true);
+ else
+ readToCurrTok ();
+ break;
+ case '\\'://unicode escape sequence
+ if (!(currentTok.Type == TokenType.Identifier ||
+ currentTok.IsEmpty || currentTok.Type == TokenType.StringLitteral || currentTok.Type == TokenType.CharLitteral)) {
+ saveAndResetCurrentTok ();
+ }
+ Point pos = CurrentPosition;
+ Read ();
+ char escChar = Read ();
+
+ if (escChar == 'u') {
+ char c = char.ConvertFromUtf32 (int.Parse (Read (4), System.Globalization.NumberStyles.HexNumber))[0];
+ if (currentTok.IsEmpty) {
+ if (!CharIsValidCharStartName (c))
+ throwParserException ("expecting identifier start");
+ currentTok.Start = pos;
+ currentTok.Type = TokenType.Identifier;
+ } else if (currentTok.Type == TokenType.Identifier) {
+ if (!CharIsValidCharName (c))
+ throwParserException ("expecting identifier valid char");
+ }
+ currentTok += c;
+ break;
+ }
+ currentTok += new String (new char[] { '\\', escChar });
+ break;
+ case '\'':
+ if (currentTok.IsNull) {
+ readAndResetCurrentTok (TokenType.CharLitteralOpening, true);
+ currentTok.Type = TokenType.CharLitteral;
+ } else if (currentTok.Type == TokenType.CharLitteral) {
+ saveAndResetCurrentTok ();
+ readAndResetCurrentTok (TokenType.CharLitteralClosing, true);
+ } else if (currentTok.Type == TokenType.StringLitteral){
+ readToCurrTok ();
+ } else
+ throwParserException ("unexpected character: (\')");
+ break;
+ case '"':
+ if (currentTok.IsNull) {
+ readAndResetCurrentTok (TokenType.StringLitteralOpening, true);
+ currentTok.Type = TokenType.StringLitteral;
+ } else if (currentTok.Type == TokenType.StringLitteral) {
+ saveAndResetCurrentTok ();
+ readAndResetCurrentTok (TokenType.StringLitteralClosing, true);
+ } else
+ throwParserException ("unexpected character: (\")");
+ break;
+ default:
+ if (currentTok.Type == TokenType.StringLitteral || currentTok.Type == TokenType.CharLitteral) {
+ readToCurrTok (currentTok.IsEmpty);
+ } else if (currentTok.IsNull) {
+ if (nextCharIsValidCharStartName) {
+ readToCurrTok (true);
+ while (nextCharIsValidCharName)
+ readToCurrTok ();
+
+ if (keywords.Contains (currentTok.Content))
+ saveAndResetCurrentTok (TokenType.Keyword);
+ else
+ saveAndResetCurrentTok (TokenType.Identifier);
+ continue;
+ } else
+ readAndResetCurrentTok(TokenType.Unknown, true);
+ } else
+ readAndResetCurrentTok(TokenType.Unknown, true);
+ break;
+ }
+ }
+
+ if (cl.EndingState != (int)curState && currentLine < buffer.LineCount - 1)
+ buffer [currentLine + 1].Tokens = null;
+
+ cl.EndingState = (int)curState;
+ }
+
+ public override void SyntaxAnalysis ()
+ {
+ initSyntaxAnalysis ();
+ Node currentNode = RootNode;
+
+ int ptrLine = 0;
+ while (ptrLine < buffer.LineCount) {
+ CodeLine cl = buffer [ptrLine];
+ if (cl.Tokens == null){
+ ptrLine++;
+ continue;
+ }
+ cl.SyntacticNode = null;
+
+ int tokPtr = 0;
+ bool onlyWhiteSpace = true;
+ while (tokPtr < cl.Tokens.Count) {
+ if (cl.Tokens [tokPtr].Type == TokenType.WhiteSpace) {
+ tokPtr++;
+ continue;
+ }
+
+ if (cl.Tokens [tokPtr].Type == TokenType.LineComment && onlyWhiteSpace) {
+ int startLine = ptrLine;
+ ptrLine++;
+ while (ptrLine < buffer.LineCount) {
+ int idx = buffer [ptrLine].FirstNonBlankTokIndex;
+ if (idx < 0)
+ break;
+ if (buffer [ptrLine].Tokens [idx].Type != TokenType.LineComment)
+ break;
+ ptrLine++;
+ }
+ ptrLine--;
+ if (ptrLine - startLine > 0) {
+ currentNode = addChildNode (currentNode, cl, tokPtr, "comment");
+ closeNodeAndGoUp (ref currentNode, buffer [ptrLine], "comment");
+ }
+ break;
+ }
+
+ switch (cl.Tokens [tokPtr].Type) {
+ case TokenType.BlockCommentStart:
+ case TokenType.OpenBlock:
+ currentNode = addChildNode (currentNode, cl, tokPtr);
+ break;
+ case TokenType.CloseBlock:
+ case TokenType.BlockCommentEnd:
+ closeNodeAndGoUp (ref currentNode, cl);
+ break;
+ case TokenType.Preprocessor:
+ if (cl.Tokens [tokPtr].Content.StartsWith ("#region", StringComparison.Ordinal)) {
+ currentNode = addChildNode (currentNode, cl, tokPtr, "region");
+ } else if (cl.Tokens [tokPtr].Content.StartsWith ("#endregion", StringComparison.Ordinal)) {
+
+ closeNodeAndGoUp (ref currentNode, cl,"region");
+ }
+ break;
+ }
+ onlyWhiteSpace = false;
+ tokPtr++;
+ }
+ ptrLine++;
+ }
+ /*ptrLine = 0;
+ while (ptrLine < buffer.LineCount) {
+ CodeLine cl = buffer [ptrLine];
+ if (cl.IsFoldable) {
+ if (cl.SyntacticNode.Type == "comment" || cl.SyntacticNode.Type == "region")
+ cl.IsFolded = true;
+ }
+ ptrLine++;
+ }*/
+ }
+ }
+}
+
diff --git a/Samples/CrowIDE/src/Editors/Parsers/StyleParser.cs b/Samples/CrowIDE/src/Editors/Parsers/StyleParser.cs
new file mode 100644
index 00000000..9a1e74cd
--- /dev/null
+++ b/Samples/CrowIDE/src/Editors/Parsers/StyleParser.cs
@@ -0,0 +1,179 @@
+using System;
+using Crow;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Text.RegularExpressions;
+using System.Linq;
+
+namespace Crow.Coding
+{
+ public class StyleParser : BufferParser
+ {
+ enum States { init, classNames, members, value, endOfStatement }
+
+ public StyleParser (CodeBuffer _buffer) : base(_buffer)
+ {
+ }
+
+ #region Character ValidityCheck
+ static Regex rxValidChar = new Regex(@"\p{Lu}|\p{Ll}|\p{Lt}|\p{Lm}|\p{Lo}|\p{Nl}|\p{Mn}|\p{Mc}|\p{Nd}|\p{Pc}|\p{Cf}");
+ static Regex rxNameStartChar = new Regex(@"_|\p{Lu}|\p{Ll}|\p{Lt}|\p{Lm}|\p{Lo}");
+ static Regex rxNameChar = new Regex(@"\p{Lu}|\p{Ll}|\p{Lt}|\p{Lm}|\p{Lo}|\p{Nl}|\p{Mn}|\p{Mc}|\p{Nd}|\p{Pc}|\p{Cf}");
+ static Regex rxDecimal = new Regex(@"[0-9]+");
+ static Regex rxHexadecimal = new Regex(@"[0-9a-fA-F]+");
+
+ public bool nextCharIsValidCharStartName
+ {
+ get { return rxNameStartChar.IsMatch(new string(new char[]{Peek()})); }
+ }
+ public bool nextCharIsValidCharName
+ {
+ get { return rxNameChar.IsMatch(new string(new char[]{Peek()})); }
+ }
+ #endregion
+
+ States curState = States.classNames;
+
+ public override void ParseCurrentLine ()
+ {
+ //Debug.WriteLine (string.Format("parsing line:{0}", currentLine));
+ CodeLine cl = buffer [currentLine];
+ cl.Tokens = new List<Token> ();
+
+ //retrieve current parser state from previous line
+ if (currentLine > 0)
+ curState = (States)buffer[currentLine - 1].EndingState;
+ else
+ curState = States.init;
+
+ States previousEndingState = (States)cl.EndingState;
+
+ while (! eol) {
+ SkipWhiteSpaces ();
+
+ if (eol)
+ break;
+
+ if (Peek () == '\n') {
+ if (currentTok != TokenType.Unknown)
+ throw new ParserException (currentLine, currentColumn, "Unexpected end of line");
+ Read ();
+ eol = true;
+ continue;
+ }
+
+ switch (Peek()) {
+ case '/':
+ readToCurrTok (true);
+ switch (Peek ()) {
+ case '/':
+ currentTok += ReadLine ();
+ saveAndResetCurrentTok (TokenType.LineComment);
+ break;
+ default:
+ currentTok += ReadLine ();
+ saveAndResetCurrentTok (TokenType.Unknown);
+ break;
+ }
+ break;
+ case ',':
+ if (curState != States.init && curState != States.classNames )
+ throw new ParserException (currentLine, currentColumn, "Unexpected char ','");
+ readAndResetCurrentTok (TokenType.OperatorOrPunctuation, true);
+ curState = States.classNames;
+ break;
+ case '{':
+ if (!(curState == States.init || curState == States.classNames))
+ throw new ParserException (currentLine, currentColumn, "Unexpected char '{'");
+ readAndResetCurrentTok (TokenType.OpenBlock, true);
+ curState = States.members;
+ break;
+ case '}':
+ if (curState != States.members)
+ throw new ParserException (currentLine, currentColumn, "Unexpected char '}'");
+ readAndResetCurrentTok (TokenType.CloseBlock, true);
+ curState = States.classNames;
+ break;
+ case '=':
+ if (curState == States.classNames)
+ throw new ParserException (currentLine, currentColumn, "Unexpected char '='");
+ setPreviousTokOfTypeTo (TokenType.Type, TokenType.Identifier);
+ readAndResetCurrentTok (TokenType.OperatorOrPunctuation, true);
+ curState = States.value;
+ break;
+ case '"':
+ if (curState != States.value)
+ throw new ParserException (currentLine, currentColumn, "Unexpected char '\"'");
+ readAndResetCurrentTok (TokenType.StringLitteralOpening, true);
+
+ while (!eol) {
+ currentTok += ReadLineUntil ("\"");
+ if (currentTok.Content [currentTok.Content.Length - 1] == '\\')
+ readToCurrTok ();
+ else
+ break;
+ }
+ if (eol)
+ throw new ParserException (currentLine, currentColumn, "Unexpected end of line");
+ saveAndResetCurrentTok (TokenType.StringLitteral);
+
+ readAndResetCurrentTok (TokenType.StringLitteralClosing, true);
+ curState = States.endOfStatement;
+ break;
+ case ';':
+ if (curState != States.endOfStatement)
+ throw new ParserException (currentLine, currentColumn, "Unexpected end of statement");
+ readAndResetCurrentTok (TokenType.StatementEnding, true);
+ curState = States.members;
+ break;
+ default:
+ if (currentTok.Type != TokenType.Unknown)
+ throw new ParserException (currentLine, currentColumn, "error curtok not null");
+ if (curState == States.value)
+ throw new ParserException (currentLine, currentColumn, "expecting value enclosed in '\"'");
+ if (curState == States.endOfStatement)
+ throw new ParserException (currentLine, currentColumn, "expecting end of statement");
+
+ if (nextCharIsValidCharStartName) {
+ readToCurrTok (true);
+ while (nextCharIsValidCharName)
+ readToCurrTok ();
+ }
+ saveAndResetCurrentTok (TokenType.Type);
+ break;
+ }
+ }
+
+ if (cl.EndingState != (int)curState && currentLine < buffer.LineCount - 1)
+ buffer [currentLine + 1].Tokens = null;
+
+ cl.EndingState = (int)curState;
+ }
+ public override void SyntaxAnalysis ()
+ {
+ initSyntaxAnalysis ();
+ Node currentNode = RootNode;
+
+ for (int i = 0; i < buffer.LineCount; i++) {
+ CodeLine cl = buffer[i];
+ if (cl.Tokens == null)
+ continue;
+ cl.SyntacticNode = null;
+
+ int tokPtr = 0;
+ while (tokPtr < cl.Tokens.Count) {
+ switch (cl.Tokens [tokPtr].Type) {
+ case TokenType.OpenBlock:
+ currentNode = addChildNode (currentNode, cl, tokPtr, "style");
+ break;
+ case TokenType.CloseBlock:
+ closeNodeAndGoUp (ref currentNode, cl, "style");
+ break;
+ }
+ tokPtr++;
+ }
+ }
+ }
+ }
+}
+
diff --git a/Samples/CrowIDE/src/Editors/Parsers/XMLParser.cs b/Samples/CrowIDE/src/Editors/Parsers/XMLParser.cs
new file mode 100644
index 00000000..8e95408e
--- /dev/null
+++ b/Samples/CrowIDE/src/Editors/Parsers/XMLParser.cs
@@ -0,0 +1,307 @@
+using System;
+using Crow;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+using System.Diagnostics;
+using System.Linq;
+
+namespace Crow.Coding
+{
+ public class XMLParser : BufferParser
+ {
+ public new enum TokenType {
+ Unknown = BufferParser.TokenType.Unknown,
+ WhiteSpace = BufferParser.TokenType.WhiteSpace,
+ NewLine = BufferParser.TokenType.NewLine,
+ LineComment = BufferParser.TokenType.LineComment,
+ BlockCommentStart = BufferParser.TokenType.BlockCommentStart,
+ BlockComment = BufferParser.TokenType.BlockComment,
+ BlockCommentEnd = BufferParser.TokenType.BlockCommentEnd,
+ ElementName = BufferParser.TokenType.Type,
+ AttributeName = BufferParser.TokenType.Identifier,
+ ElementClosing = BufferParser.TokenType.StatementEnding,
+ Affectation = BufferParser.TokenType.OperatorOrPunctuation,
+ AttributeValueOpening = BufferParser.TokenType.StringLitteralOpening,
+ AttributeValueClosing = BufferParser.TokenType.StringLitteralClosing,
+ AttributeValue = BufferParser.TokenType.StringLitteral,
+ XMLDecl = BufferParser.TokenType.Preprocessor,
+ Doctype = BufferParser.TokenType.Keyword,
+ ElementStart = 50,
+ ElementEnd = 51,
+ }
+
+ public enum States
+ {
+ init, //first statement of prolog, xmldecl should only apear in this state
+ prolog, //misc before doctypedecl
+ InternalSubset, //doctype declaration subset
+ ExternalSubsetInit,
+ ExternalSubset,
+ BlockComment,
+ DTDEnd,//doctype finished
+ XML,//normal xml
+ StartTag,//inside start tag
+ Content,//after start tag with no closing slash
+ EndTag
+ }
+
+ #region CTOR
+ public XMLParser (CodeBuffer _buffer) : base(_buffer) {}
+ #endregion
+
+ enum Keywords
+ {
+ DOCTYPE,
+ ELEMENT,
+ ATTLIST,
+ ENTITY,
+ NOTATION
+ }
+
+ States curState = States.init;
+
+ #region Regular Expression for validity checks
+ //private static Regex rxValidChar = new Regex("[\u0020-\uD7FF]");
+ private static Regex rxValidChar = new Regex(@"\u0009|\u000A|\u000D|[\u0020-\uD7FF]|[\uE000-\uFFFD]"); //| [\u10000-\u10FFFF] unable to set those plans
+ private static Regex rxNameStartChar = new Regex(@":|[A-Z]|_|[a-z]|[\u00C0-\u00D6]|[\u00D8-\u00F6]|[\u00F8-\u02FF]|[\u0370-\u037D]|[\u037F-\u1FFF]|[\u200C-\u200D]|[\u2070-\u218F]|[\u2C00-\u2FEF]|[\u3001-\uD7FF]|[\uF900-\uFDCF]|[\uFDF0-\uFFFD]"); // | [\u10000-\uEFFFF]
+ private static Regex rxNameChar = new Regex(@":|[A-Z]|_|[a-z]|[\u00C0-\u00D6]|[\u00D8-\u00F6]|[\u00F8-\u02FF]|[\u0370-\u037D]|[\u037F-\u1FFF]|[\u200C-\u200D]|[\u2070-\u218F]|[\u2C00-\u2FEF]|[\u3001-\uD7FF]|[\uF900-\uFDCF]|[\uFDF0-\uFFFD]|-|\.|[0-9]|\u00B7|[\u0300-\u036F]|[\u203F-\u2040]");//[\u10000-\uEFFFF]|
+ private static Regex rxDecimal = new Regex(@"[0-9]+");
+ private static Regex rxHexadecimal = new Regex(@"[0-9a-fA-F]+");
+ private static Regex rxAttributeValue = new Regex(@"[^<]");
+ private static Regex rxEntityValue = new Regex(@"[^<]");
+ private static Regex rxPubidChar = new Regex(@"\u0020|\u000D|\u000A|[a-zA-Z0-9]|[-\(\)\+\,\./:=\?;!\*#@\$_%]");
+ #endregion
+
+ #region Character ValidityCheck
+ public bool nextCharIsValidCharStartName
+ {
+ get { return rxNameStartChar.IsMatch(new string(new char[]{Peek()})); }
+ }
+ public bool nextCharIsValidCharName
+ {
+ get { return rxNameChar.IsMatch(new string(new char[]{Peek()})); }
+ }
+ #endregion
+
+ public override void SetLineInError (ParserException ex)
+ {
+ base.SetLineInError (ex);
+ //buffer[ex.Line].Tokens.EndingState = (int)States.init;
+ }
+
+ public override void ParseCurrentLine ()
+ {
+ //Debug.WriteLine (string.Format("parsing line:{0}", currentLine));
+ CodeLine cl = buffer [currentLine];
+ cl.Tokens = new List<Token> ();
+
+ //retrieve current parser state from previous line
+ if (currentLine > 0)
+ curState = (States)buffer[currentLine - 1].EndingState;
+ else
+ curState = States.init;
+
+ States previousEndingState = (States)cl.EndingState;
+
+
+ while (! eol) {
+ SkipWhiteSpaces ();
+
+ if (eol)
+ break;
+
+ if (Peek () == '\n') {
+ if (currentTok != TokenType.Unknown)
+ throw new ParserException (currentLine, currentColumn, "Unexpected end of line");
+ Read ();
+ eol = true;
+ continue;
+ }
+
+ if (curState == States.BlockComment) {
+ if (currentTok != TokenType.Unknown)
+ Debugger.Break ();
+
+ currentTok.Start = CurrentPosition;
+ currentTok.Type = (BufferParser.TokenType)TokenType.BlockComment;
+ currentTok += ReadLineUntil ("-->");
+ if (Peek (3) == "-->") {
+ readToCurrTok (3);
+ curState = States.XML;
+ }
+ saveAndResetCurrentTok ();
+ continue;
+ }
+
+ switch (Peek()) {
+ case '<':
+ readToCurrTok (true);
+ switch (Peek()) {
+ case '?':
+ if (curState != States.init)
+ throw new ParserException (currentLine, currentColumn, "xml decl may appear only on first line");
+ readToCurrTok ();
+ currentTok += ReadLineUntil ("?>");
+ if (Peek (2) != "?>")
+ throw new ParserException (currentLine, currentColumn, "expecting '?>'");
+ readToCurrTok (2);
+ saveAndResetCurrentTok (TokenType.XMLDecl);
+ curState = States.prolog;
+ break;
+ case '!':
+ readToCurrTok ();
+ switch (Peek()) {
+ case '-':
+ readToCurrTok ();
+ if (Peek () != '-')
+ throw new ParserException (currentLine, currentColumn, "Expecting comment start tag");
+ readToCurrTok ();
+ currentTok += ReadLineUntil ("--");
+ if (Peek (3) == "-->") {
+ readToCurrTok (3);
+ }else
+ curState = States.BlockComment;
+ saveAndResetCurrentTok (TokenType.BlockComment);
+ break;
+ case 'D':
+ case 'd':
+ string tmp = Read (7);
+ currentTok += tmp;
+ if (!string.Equals (tmp, "DOCTYPE", StringComparison.OrdinalIgnoreCase))
+ throw new ParserException (currentLine, currentColumn, "Expecting 'doctype'");
+ saveAndResetCurrentTok (TokenType.Doctype);
+ break;
+ default:
+ throw new ParserException (currentLine, currentColumn, "error");
+ }
+ break;
+ default:
+ if (!(curState == States.Content || curState == States.XML || curState == States.init || curState == States.prolog))
+ throw new ParserException (currentLine, currentColumn, "Unexpected char: '<'");
+ if (Peek () == '/') {
+ curState = States.EndTag;
+ readToCurrTok ();
+ saveAndResetCurrentTok (TokenType.ElementEnd);
+ } else {
+ curState = States.StartTag;
+ saveAndResetCurrentTok (TokenType.ElementStart);
+ }
+
+ if (!nextCharIsValidCharStartName)
+ throw new ParserException (currentLine, currentColumn, "Expected element name");
+
+ readToCurrTok (true);
+ while (nextCharIsValidCharName)
+ readToCurrTok ();
+
+ saveAndResetCurrentTok (TokenType.ElementName);
+ break;
+ }
+ break;
+ case '/':
+ if (curState != States.StartTag)
+ throw new ParserException (currentLine, currentColumn, "Unexpected char: '/'");
+ readToCurrTok (true);
+ if (Peek () != '>')
+ throw new ParserException (currentLine, currentColumn, "Expecting '>'");
+ readAndResetCurrentTok (TokenType.ElementEnd);
+
+ curState = States.XML;
+ break;
+ case '>':
+ readAndResetCurrentTok (TokenType.ElementClosing, true);
+ switch (curState) {
+ case States.EndTag:
+ curState = States.XML;
+ break;
+ case States.StartTag:
+ curState = States.Content;
+ break;
+ default:
+ throw new ParserException (currentLine, currentColumn, "Unexpected char: '>'");
+ }
+ break;
+ default:
+ switch (curState) {
+ case States.StartTag:
+ if (!nextCharIsValidCharStartName)
+ throw new ParserException (currentLine, currentColumn, "Expected attribute name");
+ readToCurrTok (true);
+ while (nextCharIsValidCharName)
+ readToCurrTok ();
+ saveAndResetCurrentTok (TokenType.AttributeName);
+
+ SkipWhiteSpaces ();
+
+ if (Peek () != '=')
+ throw new ParserException (currentLine, currentColumn, "Expecting: '='");
+ readAndResetCurrentTok (TokenType.Affectation, true);
+
+ SkipWhiteSpaces ();
+
+ char openAttVal = Peek ();
+ if (openAttVal != '"' && openAttVal != '\'')
+ throw new ParserException (currentLine, currentColumn, "Expecting attribute value enclosed either in '\"' or in \"'\"");
+ readAndResetCurrentTok (TokenType.AttributeValueOpening, true);
+
+ currentTok.Start = CurrentPosition;
+ currentTok.Content = ReadLineUntil (new string (new char[]{ openAttVal }));
+ saveAndResetCurrentTok (TokenType.AttributeValue);
+
+ if (Peek () != openAttVal)
+ throw new ParserException (currentLine, currentColumn, string.Format ("Expecting {0}", openAttVal));
+ readAndResetCurrentTok (TokenType.AttributeValueClosing, true);
+ break;
+ default:
+ throw new ParserException (currentLine, currentColumn, "unexpected char: " + Peek ());
+ }
+ break;
+ }
+ }
+
+ if (cl.EndingState != (int)curState && currentLine < buffer.LineCount - 1)
+ buffer [currentLine + 1].Tokens = null;
+
+ cl.EndingState = (int)curState;
+ }
+
+ public override void SyntaxAnalysis ()
+ {
+ initSyntaxAnalysis ();
+ Node currentNode = RootNode;
+
+ for (int i = 0; i < buffer.LineCount; i++) {
+ CodeLine cl = buffer[i];
+ if (cl.Tokens == null)
+ continue;
+ cl.SyntacticNode = null;
+
+ int tokPtr = 0;
+ while (tokPtr < cl.Tokens.Count) {
+ switch ((XMLParser.TokenType)cl.Tokens [tokPtr].Type) {
+ case TokenType.ElementStart:
+ tokPtr++;
+ currentNode = addChildNode (currentNode, cl, tokPtr, "Element");
+ break;
+ case TokenType.ElementEnd:
+ tokPtr++;
+ if (tokPtr < cl.Tokens.Count) {
+ if ((XMLParser.TokenType)cl.Tokens [tokPtr].Type == TokenType.ElementName &&
+ cl.Tokens [tokPtr].Content != currentNode.Name)
+ throw new ParserException (currentLine, currentColumn, "Closing tag mismatch");
+ }
+ closeNodeAndGoUp (ref currentNode, cl, "Element");
+ break;
+ case TokenType.ElementClosing:
+ //currentNode = currentNode.Parent;
+ break;
+ default:
+ break;
+ }
+ tokPtr++;
+ }
+ }
+ }
+ }
+}
+
diff --git a/Samples/CrowIDE/src/Editors/Parsers2/Tokenizer.cs b/Samples/CrowIDE/src/Editors/Parsers2/Tokenizer.cs
new file mode 100644
index 00000000..bcda1ec6
--- /dev/null
+++ b/Samples/CrowIDE/src/Editors/Parsers2/Tokenizer.cs
@@ -0,0 +1,67 @@
+//
+// Tokenizer.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// 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 Crow.Text;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+
+namespace Crow.Coding2
+{
+ public static class TokenType {
+ public const int Undefine = 0;
+ public const int WhiteSpace = 0;
+ }
+ public class Token {
+ public int ptr;
+ public int length;
+ }
+
+ public class Tokenizer
+ {
+ #region Regular Expression for validity checks
+ public Regex rxValidChar = new Regex(@"\p{Lu}|\p{Ll}|\p{Lt}|\p{Lm}|\p{Lo}|\p{Nl}|\p{Mn}|\p{Mc}|\p{Nd}|\p{Pc}|\p{Cf}");
+ public Regex rxNameStartChar = new Regex(@"_|\p{Lu}|\p{Ll}|\p{Lt}|\p{Lm}|\p{Lo}|\p{Nl}");
+ public Regex rxNameChar = new Regex(@"\p{Lu}|\p{Ll}|\p{Lt}|\p{Lm}|\p{Lo}|\p{Nl}|\p{Mn}|\p{Mc}|\p{Nd}|\p{Pc}|\p{Cf}");
+ public Regex rxNewLineChar = new Regex(@"\u000D|\u000A|\u0085|\u2028|\u2029");
+ public Regex rxWhiteSpaceChar = new Regex(@"\p{Zs}|\u0009|\u000B|\u000C");
+ public Regex rxDecimal = new Regex(@"[0-9]+");
+ public Regex rxHexadecimal = new Regex(@"[0-9a-fA-F]+");
+ #endregion
+
+ public List<Token> Tokens;
+
+ public Tokenizer (TextBuffer buffer)
+ {
+ }
+
+
+ public void Tokenize () {
+
+ }
+
+ }
+}
+
diff --git a/Samples/CrowIDE/src/Editors/SourceEditor.cs b/Samples/CrowIDE/src/Editors/SourceEditor.cs
new file mode 100644
index 00000000..ca55ee09
--- /dev/null
+++ b/Samples/CrowIDE/src/Editors/SourceEditor.cs
@@ -0,0 +1,1209 @@
+//
+// ScrollingTextBox.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// 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.ComponentModel;
+using System.Collections.Generic;
+using System.Diagnostics;
+using vkvg;
+
+namespace Crow.Coding
+{
+ /// <summary>
+ /// Scrolling text box optimized for monospace fonts, for coding
+ /// </summary>
+ public class SourceEditor : Editor
+ {
+ #region CTOR
+ public SourceEditor (): base()
+ {
+ formatting.Add ((int)XMLParser.TokenType.AttributeName, new TextFormatting (Color.DarkSlateGrey, 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.AttributeValueOpening, new TextFormatting (Color.Crimson, Color.Transparent));
+ formatting.Add ((int)XMLParser.TokenType.AttributeValueClosing, new TextFormatting (Color.Crimson, Color.Transparent));
+ formatting.Add ((int)XMLParser.TokenType.AttributeValue, new TextFormatting (Color.FireBrick, Color.Transparent, false, true));
+ formatting.Add ((int)XMLParser.TokenType.XMLDecl, new TextFormatting (Color.ForestGreen, Color.Transparent));
+
+ formatting.Add ((int)BufferParser.TokenType.BlockComment, new TextFormatting (Color.Grey, Color.Transparent, false, true));
+ formatting.Add ((int)BufferParser.TokenType.LineComment, new TextFormatting (Color.Grey, Color.Transparent, false, true));
+ formatting.Add ((int)BufferParser.TokenType.OperatorOrPunctuation, new TextFormatting (Color.Black, Color.Transparent));
+ formatting.Add ((int)BufferParser.TokenType.Keyword, new TextFormatting (Color.Teal, Color.Transparent));
+ //formatting.Add ((int)BufferParser.TokenType.Keyword, new TextFormatting (Color.DarkCyan, Color.Transparent));
+
+ parsing.Add (".crow", "Crow.Coding.XMLParser");
+ parsing.Add (".svg", "Crow.Coding.XMLParser");
+ parsing.Add (".template", "Crow.Coding.XMLParser");
+ parsing.Add (".cs", "Crow.Coding.CSharpParser");
+ parsing.Add (".style", "Crow.Coding.StyleParser");
+
+ 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(""));
+ }
+ #endregion
+
+ string oldSource = "";
+ //save requested position on error, and try it on next move
+ int requestedLine = 0, requestedCol = 0;
+ volatile bool isDirty = false;
+
+ const int leftMarginGap = 10;//gap between items in margin and text
+ const int foldSize = 9;//folding rectangles size
+ const int foldHSpace = 4;//folding level tabulation x
+ int foldMargin { get { return parser == null ? 0 : parser.SyntacticTreeMaxDepth * foldHSpace; }}//folding margin size
+
+ #region private and protected fields
+ bool foldingEnabled = true;
+ int leftMargin = 0; //margin used to display line numbers, folding errors,etc...
+ int visibleLines = 1;
+ int visibleColumns = 1;
+ int firstPrintedLine = -1;
+ int printedCurrentLine = 0;//Index of the currentline in the PrintedLines array
+
+ CodeBuffer buffer;
+ BufferParser parser;
+ List<CodeLine> PrintedLines;//list of lines visible in the Editor depending on scrolling and folding
+
+ Dictionary<int, TextFormatting> formatting = new Dictionary<int, TextFormatting>();
+ Dictionary<string, string> parsing = new Dictionary<string, string>();
+
+ Color selBackground;
+ Color selForeground;
+ int selStartCol;
+ int selEndCol;
+
+ protected Rectangle rText;
+ protected FontExtents fe;
+ protected TextExtents te;
+
+ Point mouseLocalPos;
+ bool doubleClicked = false;
+ #endregion
+
+ void measureLeftMargin () {
+ leftMargin = 0;
+ if (PrintLineNumbers)
+ leftMargin += (int)Math.Ceiling((double)buffer.LineCount.ToString().Length * fe.MaxXAdvance) +6;
+ if (foldingEnabled)
+ leftMargin += foldMargin;
+ if (leftMargin > 0)
+ leftMargin += leftMarginGap;
+ updateVisibleColumns ();
+ }
+ void findLongestLineAndUpdateMaxScrollX() {
+ buffer.FindLongestVisualLine ();
+ updateMaxScrollX ();
+// Debug.WriteLine ("SourceEditor: Find Longest line and update maxscrollx: {0} visible cols:{1}", MaxScrollX, visibleColumns);
+ }
+ /// <summary>
+ /// Updates visible line in widget, adapt max scroll y and updatePrintedLines
+ /// </summary>
+ void updateVisibleLines(){
+ visibleLines = (int)Math.Floor ((double)ClientRectangle.Height / (fe.Ascent+fe.Descent));
+ NotifyValueChanged ("VisibleLines", visibleLines);
+ updateMaxScrollY ();
+ updatePrintedLines ();
+ RegisterForGraphicUpdate ();
+// 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);
+ NotifyValueChanged ("VisibleColumns", visibleColumns);
+ updateMaxScrollX ();
+// System.Diagnostics.Debug.WriteLine ("update visible columns: {0} leftMargin:{1}",visibleColumns, leftMargin);
+// System.Diagnostics.Debug.WriteLine ("update MaxScrollX: " + MaxScrollX);
+ }
+ void updateMaxScrollX () {
+ MaxScrollX = Math.Max (0, buffer.longestLineCharCount - visibleColumns);
+ if (buffer.longestLineCharCount > 0)
+ NotifyValueChanged ("ChildWidthRatio", Slot.Width * visibleColumns / buffer.longestLineCharCount);
+ }
+ 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);
+ }
+ }
+ void updatePrintedLines () {
+ buffer.editMutex.EnterReadLock ();
+ editorMutex.EnterWriteLock ();
+
+ PrintedLines = new List<CodeLine> ();
+ 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++;
+ }
+
+ buffer.editMutex.ExitReadLock ();
+ editorMutex.ExitWriteLock ();
+ }
+ void updateOnScreenCurLineFromBuffCurLine(){
+ printedCurrentLine = PrintedLines.IndexOf (buffer.CurrentCodeLine);
+ }
+ void toogleFolding (int line) {
+ if (parser == null || !foldingEnabled)
+ return;
+ buffer.ToogleFolding (line);
+ }
+
+ #region Editor overrides
+ protected override void updateEditorFromProjFile ()
+ {
+ Debug.WriteLine("\t\tSourceEditor updateEditorFromProjFile");
+
+ //buffer.editMutex.EnterWriteLock ();
+ loadSource ();
+ //buffer.editMutex.ExitWriteLock ();
+
+ isDirty = false;
+ oldSource = projFile.Source;
+ CurrentLine = requestedLine;
+ CurrentColumn = requestedCol;
+ projFile.RegisteredEditors [this] = true;
+ }
+ protected override void updateProjFileFromEditor ()
+ {
+ Debug.WriteLine("\t\tSourceEditor updateProjFileFromEditor");
+
+ buffer.editMutex.EnterWriteLock ();
+ string newsrc = buffer.FullText;
+ buffer.editMutex.ExitWriteLock ();
+ projFile.UpdateSource (this, newsrc);
+ }
+ protected override bool EditorIsDirty {
+ get { return isDirty; }
+ set { isDirty = value; }
+ }
+ protected override bool IsReady {
+ get { return projFile != null && buffer != null; }
+ }
+ #endregion
+
+ #region Buffer events handlers
+ void Buffer_BufferCleared (object sender, EventArgs e)
+ {
+ editorMutex.EnterWriteLock ();
+
+ buffer.longestLineCharCount = 0;
+ buffer.longestLineIdx = 0;
+ measureLeftMargin ();
+ MaxScrollX = MaxScrollY = 0;
+ PrintedLines = null;
+ RegisterForGraphicUpdate ();
+ notifyPositionChanged ();
+ isDirty = true;
+
+ editorMutex.ExitWriteLock ();
+ }
+ 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);
+ }
+
+ if (parser != null)
+ parser.reparseSource ();
+
+ measureLeftMargin ();
+
+ updatePrintedLines ();
+ updateMaxScrollY ();
+ RegisterForGraphicUpdate ();
+ notifyPositionChanged ();
+ isDirty = true;
+ }
+ 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 ();
+ notifyPositionChanged ();
+ isDirty = true;
+ }
+ 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 ();
+ notifyPositionChanged ();
+ isDirty = true;
+ }
+ void Buffer_PositionChanged (object sender, EventArgs e)
+ {
+ //Console.WriteLine ("Position changes: ({0},{1})", buffer.CurrentLine, buffer.CurrentColumn);
+ int cc = buffer.CurrentTabulatedColumn;
+
+ if (cc > visibleColumns + ScrollX) {
+ ScrollX = cc - visibleColumns;
+ } else if (cc < ScrollX)
+ ScrollX = cc;
+
+ RegisterForGraphicUpdate ();
+ updateOnScreenCurLineFromBuffCurLine ();
+ notifyPositionChanged ();
+ }
+
+ void Buffer_SelectionChanged (object sender, EventArgs e)
+ {
+ RegisterForGraphicUpdate ();
+ }
+ void Buffer_FoldingEvent (object sender, CodeBufferEventArgs e)
+ {
+ updatePrintedLines ();
+ updateOnScreenCurLineFromBuffCurLine ();
+ updateMaxScrollY ();
+ RegisterForGraphicUpdate ();
+ }
+ #endregion
+
+ void notifyPositionChanged (){
+ try {
+ NotifyValueChanged ("CurrentLine", buffer.CurrentLine+1);
+ NotifyValueChanged ("CurrentColumn", buffer.CurrentColumn+1);
+ NotifyValueChanged ("CurrentLineHasError", CurrentLineHasError);
+ NotifyValueChanged ("CurrentLineError", CurrentLineError);
+ } catch (Exception ex) {
+ Console.WriteLine (ex.ToString ());
+ }
+ }
+
+ #region Public Crow Properties
+ public int CurrentLine{
+ get { return buffer == null ? 0 : buffer.CurrentLine+1; }
+ set {
+ try {
+ int l = value - 1;
+ if (l == buffer.CurrentLine)
+ return;
+ buffer.CurrentLine = l;
+ l = buffer.CurrentLine; //reaffect from buffer where bound check is made
+ if ((bool)buffer [l]?.IsFolded)
+ buffer.ToogleFolding (l);
+ } catch (Exception ex) {
+ requestedLine = value - 1;
+ Console.WriteLine ("Error cur column: " + ex);
+ }
+ }
+ }
+ public int CurrentColumn{
+ get { return buffer == null ? 0 : buffer.CurrentColumn+1; }
+ set {
+ try {
+ if (value - 1 == buffer.CurrentColumn)
+ return;
+ buffer.CurrentColumn = value - 1;
+ } catch (Exception ex) {
+ requestedCol = value - 1;
+ Console.WriteLine ("Error cur column: " + ex.ToString ());
+ }
+ }
+ }
+ public bool PrintLineNumbers
+ {
+ get { return Configuration.Global.Get<bool> ("PrintLineNumbers"); }
+ set {
+ if (PrintLineNumbers == value)
+ return;
+ Configuration.Global.Set ("PrintLineNumbers", value);
+ NotifyValueChanged ("PrintLineNumbers", PrintLineNumbers);
+ measureLeftMargin ();
+ RegisterForGraphicUpdate ();
+ }
+ }
+ [DefaultValue("SteelBlue")]
+ public virtual Color SelectionBackground {
+ get { return selBackground; }
+ set {
+ if (value == selBackground)
+ return;
+ selBackground = value;
+ NotifyValueChanged ("SelectionBackground", selBackground);
+ RegisterForRedraw ();
+ }
+ }
+ [DefaultValue("White")]
+ public virtual Color SelectionForeground {
+ get { return selForeground; }
+ set {
+ if (value == selForeground)
+ return;
+ selForeground = value;
+ NotifyValueChanged ("SelectionForeground", selForeground);
+ RegisterForRedraw ();
+ }
+ }
+ public override int ScrollY {
+ get {
+ return base.ScrollY;
+ }
+ set {
+ if (value == base.ScrollY)
+ return;
+ base.ScrollY = value;
+ updatePrintedLines ();
+ updateOnScreenCurLineFromBuffCurLine ();
+ RegisterForGraphicUpdate ();
+ }
+ }
+ public ParserException CurrentLineError {
+ get { return buffer?.CurrentCodeLine?.exception; }
+ }
+ public bool CurrentLineHasError {
+ get { return buffer == null ? false : buffer.CurrentCodeLine == null ? false :
+ buffer.CurrentCodeLine.exception != null; }
+ }
+ public override ProjectFile ProjectNode {
+ get {
+ return base.ProjectNode;
+ }
+ set {
+ base.ProjectNode = value;
+ if (projFile != null)
+ parser = getParserFromExt (System.IO.Path.GetExtension (projFile.Extension));
+ }
+ }
+ #endregion
+
+ BufferParser 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 (BufferParser)Activator.CreateInstance (parserType, buffer );
+ }
+ void loadSource () {
+
+ try {
+
+ if (parser == null)
+ buffer.Load (projFile.Source);
+ else//parser may have special linebrk rules
+ buffer.Load (projFile.Source, parser.LineBrkRegex);
+
+ } catch (Exception ex) {
+ Debug.WriteLine (ex.ToString ());
+ }
+
+ projFile.RegisteredEditors [this] = true;
+
+ updateMaxScrollY ();
+ MaxScrollX = Math.Max (0, buffer.longestLineCharCount - visibleColumns);
+ updatePrintedLines ();
+
+ RegisterForGraphicUpdate ();
+ }
+
+ /// <summary>
+ /// Current editor line, when set, update buffer.CurrentLine
+ /// </summary>
+ 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);
+ }
+ /// <summary>
+ /// Moves cursor one char to the left, move up if cursor reaches start of line
+ /// </summary>
+ /// <returns><c>true</c> if move succeed</returns>
+ public bool MoveLeft(){
+ if (buffer.CurrentColumn == 0) {
+ if (printedCurrentLine == 0)
+ return false;
+ PrintedCurrentLine--;
+ buffer.CurrentColumn = int.MaxValue;
+ } else
+ buffer.CurrentColumn--;
+ return true;
+ }
+ /// <summary>
+ /// Moves cursor one char to the right, move down if cursor reaches end of line
+ /// </summary>
+ /// <returns><c>true</c> if move succeed</returns>
+ 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.Ascent+fe.Descent) * i, x = cb.X;
+
+ //Draw line numbering
+ Color mgFg = Color.Grey;
+ Color mgBg = Color.White;
+ if (PrintLineNumbers){
+ Rectangle mgR = new Rectangle ((int)x, (int)y, leftMargin - leftMarginGap, (int)Math.Ceiling((fe.Ascent+fe.Descent)));
+ if (cl.exception != null) {
+ mgBg = Color.Red;
+ if (buffer.CurrentLine == lineIndex)
+ mgFg = Color.White;
+ else
+ mgFg = Color.LightGrey;
+ }else if (buffer.CurrentLine == lineIndex && HasFocus) {
+ mgFg = Color.Black;
+ mgBg = Color.DarkGrey;
+ }
+ 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){
+
+ Rectangle rFld = new Rectangle (cb.X + leftMargin - leftMarginGap - foldMargin,
+ (int)(y + (fe.Ascent + fe.Descent) / 2.0 - foldSize / 2.0), foldSize, foldSize);
+
+ gr.SetSourceColor (Color.Black);
+ gr.LineWidth = 1.0;
+
+ int level = 0;
+ bool closingNode = false;
+
+ if (currentNode != null) {
+ if (cl == currentNode.EndLine) {
+ currentNode = currentNode.Parent;
+ closingNode = true;
+ }
+ if (currentNode != null)
+ level = currentNode.Level - 1;
+ }
+
+ for (int l = 0; l < level; l++) {
+ gr.MoveTo (rFld.Center.X + 0.5, y);
+ gr.LineTo (rFld.Center.X + 0.5, y + fe.Ascent + fe.Descent);
+ rFld.Left += foldHSpace;
+ }
+ if (closingNode) {
+ gr.MoveTo (rFld.Center.X + 0.5, y);
+ gr.LineTo (rFld.Center.X + 0.5, y + fe.Ascent / 2 + 0.5);
+ gr.LineTo (rFld.Center.X + 0.5 + foldSize / 2, y + fe.Ascent / 2 + 0.5);
+ closingNode = false;
+ }
+ //gr.SetDash (new double[]{ 1.5 },0.0);
+ gr.SetSourceColor (Color.Grey);
+ gr.Stroke ();
+ //gr.SetDash (new double[]{}, 0.0);
+
+ if (cl.IsFoldable) {
+ gr.Rectangle (rFld);
+ gr.SetSourceColor (Color.White);
+ gr.Fill();
+ gr.SetSourceColor (Color.Black);
+ 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);
+ }else
+ currentNode = cl.SyntacticNode;
+
+ 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);
+ }
+ Node currentNode = null;
+// 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.Ascent+fe.Descent));
+// gr.LineTo (0.5 + cursorX, cb.Y + (printedCurrentLine + 1) * (fe.Ascent+fe.Descent));
+// 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.Ascent+fe.Descent));
+ 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.FontFace = Font.Name;
+ 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.Ascent+fe.Descent));
+ 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.Ascent+fe.Descent) * 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 ();
+ }
+
+ protected override void onDraw (Context gr)
+ {
+ base.onDraw (gr);
+
+ gr.FontFace = Font.Name;
+ gr.FontSize = (uint)Font.Size;
+
+ Rectangle cb = ClientRectangle;
+
+ Foreground.SetAsSource (gr);
+
+ buffer.editMutex.EnterReadLock ();
+ editorMutex.EnterReadLock ();
+
+ #region draw text cursor
+ if (buffer.SelectionInProgress){
+ selStartCol = getTabulatedColumn (buffer.SelectionStart);
+ selEndCol = getTabulatedColumn (buffer.SelectionEnd);
+ }else if (HasFocus && printedCurrentLine >= 0){
+ gr.LineWidth = 1.0;
+ double cursorX = cb.X + (getTabulatedColumn(buffer.CurrentPosition) - ScrollX) * fe.MaxXAdvance + leftMargin;
+ gr.MoveTo (0.5 + cursorX, cb.Y + (printedCurrentLine) * (fe.Ascent+fe.Descent));
+ gr.LineTo (0.5 + cursorX, cb.Y + (printedCurrentLine + 1) * (fe.Ascent+fe.Descent));
+ gr.Stroke();
+ }
+ #endregion
+
+ if (PrintedLines?.Count > 0) {
+ int unfoldedLines = buffer.UnfoldedLines;
+ currentNode = null;
+ CodeLine cl = PrintedLines[0];
+ int idx0 = buffer.IndexOf(cl);
+ int li = idx0-1;
+ while (li >= 0) {
+ if (buffer [li].IsFoldable && !buffer [li].IsFolded) {
+ if (buffer.IndexOf(buffer [li].SyntacticNode.EndLine) > idx0){
+ currentNode = buffer [li].SyntacticNode;
+ break;
+ }
+ }
+ li--;
+ }
+
+ for (int i = 0; i < visibleLines; i++) {
+ if (i + ScrollY >= unfoldedLines)//TODO:need optimize
+ break;
+ drawLine (gr, cb, i);
+ }
+ }
+
+ editorMutex.ExitReadLock ();
+
+ buffer.editMutex.ExitReadLock ();
+
+ }
+ #endregion
+
+ #region Mouse handling
+
+ int hoverLine = -1;
+ public int HoverLine {
+ get { return hoverLine; }
+ set {
+ if (hoverLine == value)
+ return;
+ hoverLine = value;
+ NotifyValueChanged ("HoverLine", hoverLine);
+ NotifyValueChanged ("HoverError", buffer [hoverLine].exception);
+ }
+ }
+ void updateHoverLine () {
+ if (PrintedLines.Count > 0) {
+ int hvl = (int)Math.Max (0, Math.Floor (mouseLocalPos.Y / (fe.Ascent + fe.Descent)));
+ hvl = Math.Min (PrintedLines.Count - 1, hvl);
+ HoverLine = buffer.IndexOf (PrintedLines [hvl]);
+ } else
+ HoverLine = 0;
+ }
+ void updateCurrentPosFromMouseLocalPos(){
+ PrintedCurrentLine = (int)Math.Max (0, Math.Floor (mouseLocalPos.Y / (fe.Ascent+fe.Descent)));
+ 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;
+ }
+ public override void onMouseEnter (object sender, MouseMoveEventArgs e)
+ {
+ base.onMouseEnter (sender, e);
+ if (e.X - ScreenCoordinates(Slot).X < leftMargin + ClientRectangle.X)
+ IFace.MouseCursor = MouseCursor.Arrow;
+ else
+ IFace.MouseCursor = MouseCursor.IBeam;
+ }
+ public override void onMouseLeave (object sender, MouseMoveEventArgs e)
+ {
+ base.onMouseLeave (sender, e);
+ IFace.MouseCursor = MouseCursor.Arrow;
+ }
+ public override void onMouseMove (object sender, MouseMoveEventArgs e)
+ {
+ base.onMouseMove (sender, e);
+
+ mouseLocalPos = e.Position - ScreenCoordinates(Slot).TopLeft - ClientRectangle.TopLeft;
+
+ updateHoverLine ();
+
+ if (!e.Mouse.IsButtonDown (MouseButton.Left)) {
+ if (mouseLocalPos.X < leftMargin)
+ IFace.MouseCursor = MouseCursor.Arrow;
+ else
+ IFace.MouseCursor = MouseCursor.IBeam;
+ return;
+ }
+
+ if (!HasFocus || !buffer.SelectionInProgress)
+ return;
+
+ //mouse is down
+ updateCurrentPosFromMouseLocalPos();
+ 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.Ascent+fe.Descent)))]));
+ return;
+ }
+
+ updateCurrentPosFromMouseLocalPos ();
+ 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 void MakeSelection (int lineStart, int colStart, int lineEnd, int colEnd) {
+ buffer.CurrentLine = lineStart;
+ buffer.CurrentColumn = colStart;
+ buffer.SetSelStartPos ();
+ buffer.CurrentLine = lineEnd;
+ buffer.CurrentColumn = colEnd;
+ buffer.SetSelEndPos ();
+ }
+ public override void onMouseWheel (object sender, MouseWheelEventArgs e)
+ {
+ base.onMouseWheel (sender, e);
+ }
+ #endregion
+
+ #region Keyboard handling
+ public override void onKeyDown (object sender, KeyEventArgs e)
+ {
+ //base.onKeyDown (sender, e);
+
+ Key key = e.Key;
+
+ if (IFace.Ctrl) {
+ switch (key) {
+ case Key.S:
+ projFile.Save ();
+ break;
+ case Key.W:
+ editorMutex.EnterWriteLock ();
+ if (IFace.Shift)
+ projFile.Redo (null);
+ else
+ projFile.Undo (null);
+ editorMutex.ExitWriteLock ();
+ break;
+ default:
+ Console.WriteLine ("");
+ break;
+ }
+ }
+
+ switch (key)
+ {
+ case Key.Backspace:
+ buffer.DeleteChar ();
+ break;
+ case Key.Delete:
+ if (buffer.SelectionIsEmpty)
+ MoveRight ();
+ else if (IFace.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 (IFace.Shift) {
+ if (buffer.SelectionIsEmpty)
+ buffer.SetSelStartPos ();
+ if (IFace.Ctrl)
+ buffer.CurrentLine = 0;
+ buffer.CurrentColumn = 0;
+ buffer.SetSelEndPos ();
+ break;
+ }
+ buffer.ResetSelection ();
+ if (IFace.Ctrl)
+ buffer.CurrentLine = 0;
+ buffer.CurrentColumn = 0;
+ break;
+ case Key.End:
+ if (IFace.Shift) {
+ if (buffer.SelectionIsEmpty)
+ buffer.SetSelStartPos ();
+ if (IFace.Ctrl)
+ buffer.CurrentLine = int.MaxValue;
+ buffer.CurrentColumn = int.MaxValue;
+ buffer.SetSelEndPos ();
+ break;
+ }
+ buffer.ResetSelection ();
+ if (IFace.Ctrl)
+ buffer.CurrentLine = int.MaxValue;
+ buffer.CurrentColumn = int.MaxValue;
+ break;
+ case Key.Insert:
+ if (IFace.Shift)
+ buffer.Insert (IFace.Clipboard);
+ else if (IFace.Ctrl && !buffer.SelectionIsEmpty)
+ IFace.Clipboard = buffer.SelectedText;
+ break;
+ case Key.Left:
+ if (IFace.Shift) {
+ if (buffer.SelectionIsEmpty)
+ buffer.SetSelStartPos ();
+ if (IFace.Ctrl)
+ buffer.GotoWordStart ();
+ else
+ MoveLeft ();
+ buffer.SetSelEndPos ();
+ break;
+ }
+ buffer.ResetSelection ();
+ if (IFace.Ctrl)
+ buffer.GotoWordStart ();
+ else
+ MoveLeft();
+ break;
+ case Key.Right:
+ if (IFace.Shift) {
+ if (buffer.SelectionIsEmpty)
+ buffer.SetSelStartPos ();
+ if (IFace.Ctrl)
+ buffer.GotoWordEnd ();
+ else
+ MoveRight ();
+ buffer.SetSelEndPos ();
+ break;
+ }
+ buffer.ResetSelection ();
+ if (IFace.Ctrl)
+ buffer.GotoWordEnd ();
+ else
+ MoveRight ();
+ break;
+ case Key.Up:
+ if (IFace.Shift) {
+ if (buffer.SelectionIsEmpty)
+ buffer.SetSelStartPos ();
+ PrintedCurrentLine--;
+ buffer.SetSelEndPos ();
+ break;
+ }
+ buffer.ResetSelection ();
+ PrintedCurrentLine--;
+ break;
+ case Key.Down:
+ if (IFace.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 (IFace.Shift) {
+ if (buffer.SelectionIsEmpty)
+ buffer.SetSelStartPos ();
+ PrintedCurrentLine += visibleLines;
+ buffer.SetSelEndPos ();
+ break;
+ }
+ buffer.ResetSelection ();
+ PrintedCurrentLine += visibleLines;
+ break;
+ case Key.PageUp:
+ if (IFace.Shift) {
+ if (buffer.SelectionIsEmpty)
+ buffer.SetSelStartPos ();
+ PrintedCurrentLine -= visibleLines;
+ buffer.SetSelEndPos ();
+ break;
+ }
+ buffer.ResetSelection ();
+ PrintedCurrentLine -= visibleLines;
+ break;
+ case Key.Tab:
+ if (IFace.Shift) {
+ if (buffer.SelectionIsEmpty ||
+ (buffer.SelectionStart.Y == buffer.SelectionEnd.Y)) {
+ //TODO
+ break;
+ }
+ for (int i = buffer.SelectionStart.Y; i <= buffer.SelectionEnd.Y; i++)
+ buffer.RemoveLeadingTab (i);
+ buffer.SetSelectionOnFullLines ();
+ } else {
+ if (buffer.SelectionIsEmpty ||
+ (buffer.SelectionStart.Y == buffer.SelectionEnd.Y)) {
+ buffer.Insert ("\t");
+ break;
+ }
+ for (int i = buffer.SelectionStart.Y; i <= buffer.SelectionEnd.Y; i++) {
+ buffer.UpdateLine (i, "\t" + buffer [i].Content);
+ }
+ }
+
+ break;
+ case Key.F8:
+ toogleFolding (buffer.CurrentLine);
+ break;
+ default:
+ break;
+ }
+ RegisterForGraphicUpdate();
+ }
+ public override void onKeyPress (object sender, KeyPressEventArgs e)
+ {
+ base.onKeyPress (sender, e);
+
+ buffer.Insert (e.KeyChar.ToString());
+ buffer.ResetSelection ();
+ }
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/Samples/CrowIDE/src/Editors/SvgEditor.cs b/Samples/CrowIDE/src/Editors/SvgEditor.cs
new file mode 100644
index 00000000..d81b1154
--- /dev/null
+++ b/Samples/CrowIDE/src/Editors/SvgEditor.cs
@@ -0,0 +1,140 @@
+//
+// SvgEditor.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// 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.ComponentModel;
+using vkvg;
+
+namespace Crow.Coding
+{
+ public class SvgEditor : Editor
+ {
+ SvgPicture _pic = new SvgPicture();
+
+ int zoom;
+
+ [DefaultValue(100)]
+ public int Zoom {
+ get { return zoom; }
+ set {
+ if (zoom == value)
+ return;
+ zoom = value;
+ NotifyValueChanged ("Zoom", zoom);
+ updateMaxScrolls ();
+ RegisterForGraphicUpdate ();
+ }
+ }
+
+ void updateMaxScrolls() {
+ MaxScrollX = Math.Max(0, _pic.Dimensions.Width * zoom / 100 - Slot.Width);
+ MaxScrollY = Math.Max(0, _pic.Dimensions.Height * zoom / 100 - Slot.Height);
+
+ if (Slot.Width + MaxScrollX > 0)
+ NotifyValueChanged ("ChildWidthRatio", Slot.Width * Slot.Width / (Slot.Width + MaxScrollX));
+ else
+ NotifyValueChanged ("ChildWidthRatio", 0);
+
+ if (Slot.Height + MaxScrollY > 0)
+ NotifyValueChanged ("ChildHeightRatio", Slot.Height * Slot.Height / (Slot.Height + MaxScrollY));
+ else
+ NotifyValueChanged ("ChildHeightRatio", 0);
+ }
+ #region editor overrides
+ protected override void updateEditorFromProjFile ()
+ {
+ Error = null;
+ try {
+ editorMutex.EnterWriteLock();
+ _pic.LoadSvgFragment (projFile.Source);
+ _pic.Scaled = true;
+ _pic.KeepProportions = true;
+ } catch (Exception ex) {
+ Error = ex;
+ }
+ editorMutex.ExitWriteLock ();
+ updateMaxScrolls ();
+ RegisterForGraphicUpdate ();
+ }
+ protected override void updateProjFileFromEditor ()
+ {
+ throw new NotImplementedException ();
+ }
+ protected override bool EditorIsDirty {
+ get { return false; }
+ set {
+ throw new NotImplementedException ();
+ }
+ }
+ protected override bool IsReady {
+ get { return projFile != null; }
+ }
+ #endregion
+
+ #region GraphicObject overrides
+ protected override int measureRawSize (LayoutingType lt)
+ {
+ if (_pic == null)
+ return 2 * Margin;
+ //_pic = "#Crow.Images.Icons.IconAlerte.svg";
+ //TODO:take scalling in account
+ if (lt == LayoutingType.Width)
+ return _pic.Dimensions.Width + 2 * Margin;
+ else
+ return _pic.Dimensions.Height + 2 * Margin;
+ }
+ protected override void onDraw (Context gr)
+ {
+ base.onDraw (gr);
+
+ Rectangle r = ClientRectangle;
+ Foreground.SetAsSource (gr, r);
+ gr.Rectangle (r, 0.1);
+ gr.Stroke ();
+
+ r.Width = _pic.Dimensions.Width * zoom / 100;
+ r.Height = _pic.Dimensions.Height * zoom / 100;
+
+ gr.Save ();
+
+ editorMutex.EnterReadLock ();
+
+ gr.Translate (-ScrollX, -ScrollY);
+ if (_pic != null)
+ _pic.Paint (gr, r);
+ editorMutex.ExitReadLock ();
+
+ gr.Restore ();
+ }
+ public override void OnLayoutChanges (LayoutingType layoutType)
+ {
+ base.OnLayoutChanges (layoutType);
+ if ((layoutType | LayoutingType.Sizing) > 0)
+ updateMaxScrolls ();
+ }
+ #endregion
+ }
+}
+