From 99d63b94c2e0e09eae8efb67e720920c130f7346 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jean-Philippe=20Bruy=C3=A8re?= Date: Mon, 12 Mar 2018 14:17:43 +0100 Subject: [PATCH] test with simpler text buffer --- CrowIDE/CrowIDE.csproj | 11 +- CrowIDE/icons/binding.svg | 4 +- CrowIDE/icons/curly-brackets.svg | 4 +- CrowIDE/icons/edit.svg | 2 +- CrowIDE/icons/eraser.svg | 2 +- CrowIDE/icons/expand-arrows-1.svg | 8 +- CrowIDE/icons/file.svg | 6 + CrowIDE/icons/folder.svg | 11 + CrowIDE/icons/light-bulb.svg | 4 +- CrowIDE/icons/magic-wand.svg | 16 +- CrowIDE/icons/magnet.svg | 6 +- CrowIDE/icons/move-arrows.svg | 2 +- CrowIDE/icons/open-folder-1.svg | 6 + CrowIDE/icons/paint-brush.svg | 6 +- CrowIDE/icons/palette.svg | 2 +- CrowIDE/icons/pin.svg | 2 +- CrowIDE/icons/redo.svg | 2 +- CrowIDE/icons/search.svg | 2 +- CrowIDE/icons/text-file.svg | 8 +- CrowIDE/icons/tools.svg | 6 +- CrowIDE/icons/trash.svg | 4 +- CrowIDE/icons/undo.svg | 2 +- CrowIDE/icons/zoom-in.svg | 4 +- CrowIDE/icons/zoom-out.svg | 4 +- CrowIDE/src/CrowIDE.cs | 47 +- .../src/Editors/CodeBuffer/SourceEditor.cs | 2 +- CrowIDE/src/Editors/CodeBuffer/TextBuffer.cs | 527 +++++++++++++ CrowIDE/src/Editors/CodeBuffer/TextEditor.cs | 712 ++++++++++++++++++ CrowIDE/src/Editors/Editor.cs | 18 +- CrowIDE/src/Editors/ImlSchematicEditor.cs | 132 ++++ CrowIDE/src/Editors/ImlVisualEditor.cs | 61 +- CrowIDE/ui/CSProjExplorer.crow | 59 -- CrowIDE/ui/CrowIDE.crow | 3 +- CrowIDE/ui/DockWindows/WinSchemaItem.template | 13 + CrowIDE/ui/DockWindows/winSchema.crow | 18 + CrowIDE/ui/IDE.style | 14 + CrowIDE/ui/ProjectProperties.crow | 2 +- CrowIDE/ui/ProjectTree.template | 20 +- CrowIDE/ui/TreeExpandable.template | 6 +- CrowIDE/ui/editors/EditPaneItems.template | 6 + CrowIDE/ui/editors/TextEditor.crow | 31 + 41 files changed, 1622 insertions(+), 173 deletions(-) create mode 100644 CrowIDE/icons/file.svg create mode 100644 CrowIDE/icons/folder.svg create mode 100644 CrowIDE/icons/open-folder-1.svg create mode 100644 CrowIDE/src/Editors/CodeBuffer/TextBuffer.cs create mode 100644 CrowIDE/src/Editors/CodeBuffer/TextEditor.cs create mode 100644 CrowIDE/src/Editors/ImlSchematicEditor.cs delete mode 100644 CrowIDE/ui/CSProjExplorer.crow create mode 100755 CrowIDE/ui/DockWindows/WinSchemaItem.template create mode 100644 CrowIDE/ui/DockWindows/winSchema.crow create mode 100644 CrowIDE/ui/editors/TextEditor.crow diff --git a/CrowIDE/CrowIDE.csproj b/CrowIDE/CrowIDE.csproj index 205e7f19..6f423c09 100644 --- a/CrowIDE/CrowIDE.csproj +++ b/CrowIDE/CrowIDE.csproj @@ -119,6 +119,9 @@ + + + @@ -141,7 +144,6 @@ - Crow.TreeExpandable.template @@ -250,6 +252,8 @@ + + Crow.ContextMenu.template @@ -281,6 +285,11 @@ + + + + Crow.Coding.ui.TextEditor.crow + diff --git a/CrowIDE/icons/binding.svg b/CrowIDE/icons/binding.svg index 3d825c65..04f669ec 100644 --- a/CrowIDE/icons/binding.svg +++ b/CrowIDE/icons/binding.svg @@ -2,6 +2,6 @@ - - + + diff --git a/CrowIDE/icons/curly-brackets.svg b/CrowIDE/icons/curly-brackets.svg index f70fe9fa..89ef798c 100644 --- a/CrowIDE/icons/curly-brackets.svg +++ b/CrowIDE/icons/curly-brackets.svg @@ -2,6 +2,6 @@ - - + + diff --git a/CrowIDE/icons/edit.svg b/CrowIDE/icons/edit.svg index f4b3190c..366862c7 100644 --- a/CrowIDE/icons/edit.svg +++ b/CrowIDE/icons/edit.svg @@ -2,5 +2,5 @@ - + diff --git a/CrowIDE/icons/eraser.svg b/CrowIDE/icons/eraser.svg index ae4c190f..5dd73bac 100644 --- a/CrowIDE/icons/eraser.svg +++ b/CrowIDE/icons/eraser.svg @@ -2,5 +2,5 @@ - + diff --git a/CrowIDE/icons/expand-arrows-1.svg b/CrowIDE/icons/expand-arrows-1.svg index d96d2ec9..5a77b1d1 100644 --- a/CrowIDE/icons/expand-arrows-1.svg +++ b/CrowIDE/icons/expand-arrows-1.svg @@ -2,8 +2,8 @@ - - - - + + + + diff --git a/CrowIDE/icons/file.svg b/CrowIDE/icons/file.svg new file mode 100644 index 00000000..9d06b00b --- /dev/null +++ b/CrowIDE/icons/file.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowIDE/icons/folder.svg b/CrowIDE/icons/folder.svg new file mode 100644 index 00000000..f59e2dac --- /dev/null +++ b/CrowIDE/icons/folder.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/CrowIDE/icons/light-bulb.svg b/CrowIDE/icons/light-bulb.svg index c7df82aa..89ff2364 100644 --- a/CrowIDE/icons/light-bulb.svg +++ b/CrowIDE/icons/light-bulb.svg @@ -2,6 +2,6 @@ - - + + diff --git a/CrowIDE/icons/magic-wand.svg b/CrowIDE/icons/magic-wand.svg index 0196115d..8b406918 100644 --- a/CrowIDE/icons/magic-wand.svg +++ b/CrowIDE/icons/magic-wand.svg @@ -2,12 +2,12 @@ - - - - - - - - + + + + + + + + diff --git a/CrowIDE/icons/magnet.svg b/CrowIDE/icons/magnet.svg index 613fe7bb..10335f69 100644 --- a/CrowIDE/icons/magnet.svg +++ b/CrowIDE/icons/magnet.svg @@ -2,7 +2,7 @@ - - - + + + diff --git a/CrowIDE/icons/move-arrows.svg b/CrowIDE/icons/move-arrows.svg index 2100ea28..ecbb2f91 100644 --- a/CrowIDE/icons/move-arrows.svg +++ b/CrowIDE/icons/move-arrows.svg @@ -2,5 +2,5 @@ - + diff --git a/CrowIDE/icons/open-folder-1.svg b/CrowIDE/icons/open-folder-1.svg new file mode 100644 index 00000000..90294692 --- /dev/null +++ b/CrowIDE/icons/open-folder-1.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowIDE/icons/paint-brush.svg b/CrowIDE/icons/paint-brush.svg index b98eb856..2bdd5bed 100644 --- a/CrowIDE/icons/paint-brush.svg +++ b/CrowIDE/icons/paint-brush.svg @@ -2,7 +2,7 @@ - - - + + + diff --git a/CrowIDE/icons/palette.svg b/CrowIDE/icons/palette.svg index 79137cd4..8e425f70 100644 --- a/CrowIDE/icons/palette.svg +++ b/CrowIDE/icons/palette.svg @@ -2,5 +2,5 @@ - + diff --git a/CrowIDE/icons/pin.svg b/CrowIDE/icons/pin.svg index 0c66eb39..b36340b8 100644 --- a/CrowIDE/icons/pin.svg +++ b/CrowIDE/icons/pin.svg @@ -2,5 +2,5 @@ - + diff --git a/CrowIDE/icons/redo.svg b/CrowIDE/icons/redo.svg index 5c177f00..59fcc904 100644 --- a/CrowIDE/icons/redo.svg +++ b/CrowIDE/icons/redo.svg @@ -2,5 +2,5 @@ - + diff --git a/CrowIDE/icons/search.svg b/CrowIDE/icons/search.svg index 3b13c130..f3eb3682 100644 --- a/CrowIDE/icons/search.svg +++ b/CrowIDE/icons/search.svg @@ -2,5 +2,5 @@ - + diff --git a/CrowIDE/icons/text-file.svg b/CrowIDE/icons/text-file.svg index 88fe9798..826bc326 100644 --- a/CrowIDE/icons/text-file.svg +++ b/CrowIDE/icons/text-file.svg @@ -2,8 +2,8 @@ - - - - + + + + diff --git a/CrowIDE/icons/tools.svg b/CrowIDE/icons/tools.svg index 8fa643b0..5ad8a8d9 100644 --- a/CrowIDE/icons/tools.svg +++ b/CrowIDE/icons/tools.svg @@ -2,7 +2,7 @@ - - - + + + diff --git a/CrowIDE/icons/trash.svg b/CrowIDE/icons/trash.svg index cf7f7284..e73a5e33 100644 --- a/CrowIDE/icons/trash.svg +++ b/CrowIDE/icons/trash.svg @@ -2,6 +2,6 @@ - - + + diff --git a/CrowIDE/icons/undo.svg b/CrowIDE/icons/undo.svg index 955b767a..f78f1349 100644 --- a/CrowIDE/icons/undo.svg +++ b/CrowIDE/icons/undo.svg @@ -2,5 +2,5 @@ - + diff --git a/CrowIDE/icons/zoom-in.svg b/CrowIDE/icons/zoom-in.svg index 1ec25bb6..540a93bb 100644 --- a/CrowIDE/icons/zoom-in.svg +++ b/CrowIDE/icons/zoom-in.svg @@ -2,6 +2,6 @@ - - + + diff --git a/CrowIDE/icons/zoom-out.svg b/CrowIDE/icons/zoom-out.svg index 414cf9df..6bd256f9 100644 --- a/CrowIDE/icons/zoom-out.svg +++ b/CrowIDE/icons/zoom-out.svg @@ -2,6 +2,6 @@ - - + + diff --git a/CrowIDE/src/CrowIDE.cs b/CrowIDE/src/CrowIDE.cs index 1be7d1ad..12664f1b 100644 --- a/CrowIDE/src/CrowIDE.cs +++ b/CrowIDE/src/CrowIDE.cs @@ -42,7 +42,7 @@ namespace Crow.Coding CMDUndo, CMDRedo, CMDCut, CMDCopy, CMDPaste, CMDHelp, CMDAbout, CMDOptions, CMDViewGTExp, CMDViewProps, CMDViewProj, CMDViewProjProps, CMDViewErrors, CMDViewSolution, CMDViewEditor, CMDViewProperties, - CMDViewToolbox, + CMDViewToolbox, CMDViewSchema, CMDCompile; void initCommands () { @@ -59,24 +59,28 @@ namespace Crow.Coding CMDHelp = new Command(new Action(() => System.Diagnostics.Debug.WriteLine("help"))) { Caption = "Help", Icon = new SvgPicture("#Crow.Coding.icons.question.svg")}; CMDOptions = new Command(new Action(() => openOptionsDialog())) { Caption = "Editor Options", Icon = new SvgPicture("#Crow.Coding.icons.tools.svg")}; - cmdCloseSolution = new Command(new Action(() => closeSolution())) { Caption = "Close Solution", Icon = new SvgPicture("#Crow.Coding.ui.icons.paste-on-document.svg"), CanExecute = true}; + cmdCloseSolution = new Command(new Action(() => closeSolution())) + { Caption = "Close Solution", Icon = new SvgPicture("#Crow.Coding.ui.icons.paste-on-document.svg"), CanExecute = false}; CMDViewErrors = new Command(new Action(() => loadDockWindow ("#Crow.Coding.ui.DockWindows.winErrors.crow"))) { Caption = "Errors pane"}; CMDViewSolution = new Command(new Action(() => loadDockWindow ("#Crow.Coding.ui.DockWindows.winSolution.crow"))) - { Caption = "Solution Tree"}; + { Caption = "Solution Tree", CanExecute = false}; CMDViewEditor = new Command(new Action(() => loadDockWindow ("#Crow.Coding.ui.DockWindows.winEditor.crow"))) { Caption = "Editor Pane"}; CMDViewProperties = new Command(new Action(() => loadDockWindow ("#Crow.Coding.ui.DockWindows.winProperties.crow"))) { Caption = "Properties"}; CMDViewToolbox = new Command(new Action(() => loadDockWindow ("#Crow.Coding.ui.DockWindows.winToolbox.crow"))) - { Caption = "Toolbox"}; - - CMDViewGTExp = new Command(new Action(() => loadDockWindow ("#Crow.Coding.ui.DockWindows.winGTExplorer.crow"))) { Caption = "Graphic Tree Explorer"}; - CMDViewProps = new Command(new Action(() => loadWindow ("#Crow.Coding.ui.MemberView.crow"))) { Caption = "Properties View"}; - CMDCompile = new Command(new Action(() => compileSolution())) { Caption = "Compile"}; - CMDViewProj = new Command(new Action(() => loadWindow ("#Crow.Coding.ui.CSProjExplorer.crow"))) { Caption = "Project Explorer"}; - CMDViewProjProps = new Command(new Action(loadProjProps) ){ Caption = "Project Properties"}; + { Caption = "Toolbox", CanExecute = false}; + CMDViewSchema = new Command(new Action(() => loadDockWindow ("#Crow.Coding.ui.DockWindows.winSchema.crow"))) + { Caption = "IML Shematic View", CanExecute = true}; + + CMDViewGTExp = new Command(new Action(() => loadDockWindow ("#Crow.Coding.ui.DockWindows.winGTExplorer.crow"))) + { Caption = "Graphic Tree Explorer", CanExecute = false}; + CMDCompile = new Command(new Action(() => compileSolution())) + { Caption = "Compile", CanExecute = false}; + CMDViewProjProps = new Command(new Action(loadProjProps)) + { Caption = "Project Properties", CanExecute = false}; } void openFileDialog () { @@ -109,6 +113,7 @@ namespace Crow.Coding Instantiator instFileDlg; Solution currentSolution; + Project currentProject; Docker mainDock; public static Interface MainIFace; @@ -152,7 +157,7 @@ namespace Crow.Coding } void loadProjProps () { - //loadWindow ("#Crow.Coding.ui.ProjectProperties.crow", currentProject); + loadWindow ("#Crow.Coding.ui.ProjectProperties.crow"); } void compileSolution () { //ProjectItem pi = CurrentSolution.SelectedItem; @@ -173,12 +178,32 @@ namespace Crow.Coding set { if (currentSolution == value) return; + currentSolution = value; + + CMDCompile.CanExecute = (currentSolution != null); + cmdCloseSolution.CanExecute = (currentSolution != null); + CMDViewSolution.CanExecute = (currentSolution != null); + lock (MainIFace) { NotifyValueChanged ("CurrentSolution", currentSolution); } } } + public Project CurrentProject { + get { return currentProject; } + set { + if (currentProject == value) + return; + currentProject = value; + + CMDViewProjProps.CanExecute = (currentProject != null); + + lock (MainIFace) { + NotifyValueChanged ("CurrentProject", currentProject); + } + } + } public string LastOpenSolution { get { return Crow.Configuration.Global.Get("LastOpenSolution");} diff --git a/CrowIDE/src/Editors/CodeBuffer/SourceEditor.cs b/CrowIDE/src/Editors/CodeBuffer/SourceEditor.cs index 72e916eb..48d634b3 100644 --- a/CrowIDE/src/Editors/CodeBuffer/SourceEditor.cs +++ b/CrowIDE/src/Editors/CodeBuffer/SourceEditor.cs @@ -234,7 +234,7 @@ namespace Crow.Coding set { isDirty = value; } } protected override bool IsReady { - get { return buffer != null; } + get { return projFile != null && buffer != null; } } #endregion diff --git a/CrowIDE/src/Editors/CodeBuffer/TextBuffer.cs b/CrowIDE/src/Editors/CodeBuffer/TextBuffer.cs new file mode 100644 index 00000000..a98aa08c --- /dev/null +++ b/CrowIDE/src/Editors/CodeBuffer/TextBuffer.cs @@ -0,0 +1,527 @@ +// +// CodeTextBuffer.cs +// +// Author: +// Jean-Philippe Bruyère +// +// 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 . +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using System.Diagnostics; +using System.Threading; +using System.Text; + +namespace Crow.Coding +{ + /// + /// Code buffer, lines are arranged in a List, new line chars are removed during string.split on '\n...', + /// + 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 LineUpadateEvent; + public event EventHandler LineRemoveEvent; + public event EventHandler 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 lineLength = new List();//line length table + /// + /// real position in char arrays, tab = 1 char + /// + int _currentLine = 0; + int _currentCol = 0; + + /// + /// Gets the total line count. + /// + public int LineCount { get { return lineLength.Count;}} + + /// + /// get a substring in the buffer + /// + /// a new string + /// absolute index in the buffer + /// length of the substring + public string GetSubString (int idx, int length){ + return buffer.ToString (idx, length); + } + /// + /// Gets length of a line. + /// + /// length of line + /// line nuber + public int GetLineLength (int lineIdx) { + return lineLength [lineIdx]; + } + /// + /// get a single charactere in the buffer + /// + /// a single char + /// absolute index in the buffer + public char GetCharAt (int idx){ + return buffer [idx]; + } + /// + /// return full text with original line breaks + /// + /// a string containing the full text + public string FullText { + get { return buffer.Replace("\n", lineBreak).ToString (); } + set { + Load (value); + } + } + /// + /// Gets the buffer pointer of a line + /// + /// absolute index of the line in the buffer + /// line number + public int GetBufferIndexOfLine (int i) { + int ptr = 0; + editMutex.EnterReadLock (); + for (int j = 0; j < i; j++) { + ptr += lineLength [j]; + } + editMutex.ExitReadLock (); + return ptr; + } + /// + /// Gets the buffer pointer for the current position + /// + /// absolute index in the buffer of current position + 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; + } + } + /// + /// remove line number i + /// + /// index of the line + public void RemoveLine(int i){ + editMutex.EnterWriteLock (); + buffer.Remove (GetBufferIndexOfLine (i), lineLength [i]); + lineLength.RemoveAt (i); + editMutex.ExitWriteLock (); + LineRemoveEvent.Raise (this, new CodeBufferEventArgs (i)); + } + /// + /// insert string without linebreaks at position i in buff + /// + /// absolute index in the buffer + /// linebreak free string + 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 CodeBufferEventArgs (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 CodeBufferEventArgs (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 CodeBufferEventArgs (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 CodeBufferEventArgs (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 CodeBufferEventArgs (i)); + } + /// + /// Insert new string at caret position, should be sure no line break is inside. + /// + /// String. + 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); + } + /// + /// Insert a line break. + /// + 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); + } + /// + /// Set selection in buffer to -1, empty selection + /// + public void ResetSelection () { + selStartPos = selEndPos = -1; + SelectionChanged.Raise (this, null); + } + /// + /// ordered selection start and end positions in char units + /// + 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; } } + /// + /// Current column in buffer coordinate, tabulation = 1 char + /// + 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); + } + } + /// + /// Current row in buffer coordinate, tabulation = 1 char + /// + 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); + } + } + /// + /// Current position in buffer coordinate, tabulation = 1 char + /// + public Point CurrentPosition { + get { return new Point(CurrentColumn, CurrentLine); } + } + /// + /// get char at current position in buffer + /// + 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; + } + /// + /// Moves cursor one char to the left, move up if cursor reaches start of line + /// + public void MoveLeft(){ + if (CurrentColumn == 0) { + CurrentLine--; + CurrentColumn = int.MaxValue; + } else + CurrentColumn--; + } + /// + /// Moves cursor one char to the right, move down if cursor reaches end of line + /// + public void MoveRight(){ + if (_currentLine < LineCount -1){ + if (CurrentColumn >= lineLength [CurrentLine] - 1) { + CurrentColumn = 0; + CurrentLine++; + return; + } + } + CurrentColumn++; + } + + #endregion + } +} + diff --git a/CrowIDE/src/Editors/CodeBuffer/TextEditor.cs b/CrowIDE/src/Editors/CodeBuffer/TextEditor.cs new file mode 100644 index 00000000..69233e18 --- /dev/null +++ b/CrowIDE/src/Editors/CodeBuffer/TextEditor.cs @@ -0,0 +1,712 @@ +// +// ScrollingTextBox.cs +// +// Author: +// Jean-Philippe Bruyère +// +// Copyright (c) 2013-2017 Jean-Philippe Bruyère +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Xml.Serialization; +using System.ComponentModel; +using System.Collections; +using Cairo; +using System.Text; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using System.Linq; +using System.Diagnostics; +using System.IO; +using System.Threading; + +namespace Crow.Coding +{ + /// + /// Scrolling text box optimized for monospace fonts, for coding + /// + public class TextEditor : 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 + + /// + /// Updates visible line in widget, adapt max scroll y and updatePrintedLines + /// + 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, CodeBufferEventArgs e) + { + updateMaxScrollY (); + RegisterForGraphicUpdate (); + isDirty = true; + } + void Buffer_LineRemoveEvent (object sender, CodeBufferEventArgs e) + { + updateMaxScrollY (); + RegisterForGraphicUpdate (); + notifyPositionChanged (); + isDirty = true; + } + void Buffer_LineUpadateEvent (object sender, CodeBufferEventArgs 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("BlueGray")] + 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 (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 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.SelectFontFace (Font.Name, Font.Slant, Font.Wheight); + gr.SetFontSize (Font.Size); + gr.FontOptions = Interface.FontRenderingOptions; + gr.Antialias = Interface.Antialias; + + Rectangle cb = ClientRectangle; + + Foreground.SetAsSource (gr); + + 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 = XCursor.Text; + } + public override void onMouseLeave (object sender, MouseMoveEventArgs e) + { + base.onMouseLeave (sender, e); + IFace.MouseCursor = XCursor.Default; + } + public override void onMouseMove (object sender, MouseMoveEventArgs e) + { + base.onMouseMove (sender, e); + + mouseLocalPos = e.Position - ScreenCoordinates(Slot).TopLeft - ClientRectangle.TopLeft; + + 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 (!this.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, KeyboardKeyEventArgs e) + { + //base.onKeyDown (sender, e); + + Key key = e.Key; + + if (e.Control) { + switch (key) { + case Key.S: + projFile.Save (); + break; + case Key.W: + editorMutex.EnterWriteLock (); + if (e.Shift) + projFile.Redo (null); + else + projFile.Undo (null); + editorMutex.ExitWriteLock (); + break; + default: + Console.WriteLine (""); + break; + } + } + + switch (key) + { + case Key.Back: + buffer.Delete (); + break; + case Key.Clear: + 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 (e.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + if (e.Control) + buffer.CurrentLine = 0; + buffer.CurrentColumn = 0; + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + if (e.Control) + buffer.CurrentLine = 0; + buffer.CurrentColumn = 0; + break; + case Key.End: + if (e.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + if (e.Control) + buffer.CurrentLine = int.MaxValue; + buffer.CurrentColumn = int.MaxValue; + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + if (e.Control) + buffer.CurrentLine = int.MaxValue; + buffer.CurrentColumn = int.MaxValue; + break; + case Key.Insert: + if (e.Shift) + buffer.InsertAt (IFace.Clipboard); + else if (e.Control && !buffer.SelectionIsEmpty) + IFace.Clipboard = buffer.SelectedText; + break; + case Key.Left: + if (e.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + if (e.Control) + buffer.GotoWordStart (); + else + buffer.MoveLeft (); + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + if (e.Control) + buffer.GotoWordStart (); + else + buffer.MoveLeft(); + break; + case Key.Right: + if (e.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + if (e.Control) + buffer.GotoWordEnd (); + else + buffer.MoveRight (); + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + if (e.Control) + buffer.GotoWordEnd (); + else + buffer.MoveRight (); + break; + case Key.Up: + if (e.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + CurrentLine--; + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + CurrentLine--; + break; + case Key.Down: + if (e.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 (e.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + CurrentLine += visibleLines; + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + CurrentLine += visibleLines; + break; + case Key.PageUp: + if (e.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + CurrentLine -= visibleLines; + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + CurrentLine -= visibleLines; + break; + case Key.RWin: + 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/CrowIDE/src/Editors/Editor.cs b/CrowIDE/src/Editors/Editor.cs index 88cab8e9..2cd34412 100644 --- a/CrowIDE/src/Editors/Editor.cs +++ b/CrowIDE/src/Editors/Editor.cs @@ -84,17 +84,15 @@ namespace Crow.Coding protected void backgroundThreadFunc () { while (true) { - if (IsReady) { - if (projFile != null) { - if (!projFile.RegisteredEditors [this]) { - projFile.RegisteredEditors [this] = true; - updateEditorFromProjFile (); - } else if (EditorIsDirty) { - EditorIsDirty = false; - updateProjFileFromEditor (); - } - updateCheckPostProcess (); + if (IsReady) { + if (!projFile.RegisteredEditors [this]) { + projFile.RegisteredEditors [this] = true; + updateEditorFromProjFile (); + } else if (EditorIsDirty) { + EditorIsDirty = false; + updateProjFileFromEditor (); } + updateCheckPostProcess (); } Thread.Sleep (10); } diff --git a/CrowIDE/src/Editors/ImlSchematicEditor.cs b/CrowIDE/src/Editors/ImlSchematicEditor.cs new file mode 100644 index 00000000..f7b1bb65 --- /dev/null +++ b/CrowIDE/src/Editors/ImlSchematicEditor.cs @@ -0,0 +1,132 @@ +// +// ImlVisualEditor.cs +// +// Author: +// Jean-Philippe Bruyère +// +// 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 . +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; + GraphicObject 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 (); + } + } + [XmlAttributeAttribute]public GraphicObject 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/CrowIDE/src/Editors/ImlVisualEditor.cs b/CrowIDE/src/Editors/ImlVisualEditor.cs index 1ba6c559..7a7a888b 100644 --- a/CrowIDE/src/Editors/ImlVisualEditor.cs +++ b/CrowIDE/src/Editors/ImlVisualEditor.cs @@ -47,6 +47,7 @@ namespace Crow.Coding bool drawGrid, snapToGrid; int gridSpacing; + bool updateEnabled; [DefaultValue(true)] public bool DrawGrid { @@ -80,7 +81,7 @@ namespace Crow.Coding RegisterForRedraw (); } } - [XmlAttributeAttribute]public GraphicObject SelectedItem { + public GraphicObject SelectedItem { get { return selectedItem; } set { if (selectedItem == value) @@ -90,6 +91,18 @@ namespace Crow.Coding RegisterForRedraw (); } } + /// + /// use to disable update if tab is not the visible one + /// + public bool UpdateEnabled { + get { return updateEnabled; } + set { + if (value == updateEnabled) + return; + updateEnabled = value; + NotifyValueChanged ("UpdateEnabled", updateEnabled); + } + } /// PoinprojFilever the widget public virtual GraphicObject HoverWidget { @@ -107,6 +120,11 @@ namespace Crow.Coding get { return imlVE.LQIs; } } + public List GraphicTree { + get { return imlVE.GraphicTree; } + } + + #region editor overrides public override ProjectFile ProjectNode { get { return base.ProjectNode; @@ -117,10 +135,18 @@ namespace Crow.Coding imlVE.ProjFile = imlProjFile; } } - - public List GraphicTree { - get { return imlVE.GraphicTree; } + protected override bool EditorIsDirty { + get { return imlProjFile == null ? false : + imlProjFile.Instance == null ? false : imlProjFile.Instance.design_HasChanged; } + set { + if (GraphicTree [0] != null) + GraphicTree [0].design_HasChanged = value; + } + } + protected override bool IsReady { + get { return updateEnabled && imlVE != null && imlProjFile != null; } } + protected override void updateProjFileFromEditor () { try { @@ -175,31 +201,6 @@ namespace Crow.Coding Monitor.Exit (imlVE.UpdateMutex); } } - - protected override bool EditorIsDirty { - get { return imlProjFile == null ? false : - imlProjFile.Instance == null ? false : imlProjFile.Instance.design_HasChanged; } - set { - if (GraphicTree [0] != null) - GraphicTree [0].design_HasChanged = value; - } - } - protected override bool IsReady { - get { return updateEnabled && imlVE != null && imlProjFile != null; } - } - bool updateEnabled; - /// - /// use to disable update if tab is not the visible one - /// - public bool UpdateEnabled { - get { return updateEnabled; } - set { - if (value == updateEnabled) - return; - updateEnabled = value; - NotifyValueChanged ("UpdateEnabled", updateEnabled); - } - } protected override void updateCheckPostProcess () { imlVE.Update (); @@ -213,6 +214,7 @@ namespace Crow.Coding RegisterForRedraw (); } } + #endregion #region GraphicObject overrides public override void OnLayoutChanges (LayoutingType layoutType) @@ -398,7 +400,6 @@ namespace Crow.Coding } #endregion - void WidgetCheckOver (GraphicObject go, MouseMoveEventArgs e){ Type tGo = go.GetType(); if (typeof(TemplatedGroup).IsAssignableFrom (tGo)) { diff --git a/CrowIDE/ui/CSProjExplorer.crow b/CrowIDE/ui/CSProjExplorer.crow deleted file mode 100644 index 5f9f9949..00000000 --- a/CrowIDE/ui/CSProjExplorer.crow +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CrowIDE/ui/CrowIDE.crow b/CrowIDE/ui/CrowIDE.crow index 94a57e71..10b5d93b 100644 --- a/CrowIDE/ui/CrowIDE.crow +++ b/CrowIDE/ui/CrowIDE.crow @@ -25,8 +25,7 @@ - - + diff --git a/CrowIDE/ui/DockWindows/WinSchemaItem.template b/CrowIDE/ui/DockWindows/WinSchemaItem.template new file mode 100755 index 00000000..31d8d9df --- /dev/null +++ b/CrowIDE/ui/DockWindows/WinSchemaItem.template @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/CrowIDE/ui/DockWindows/winSchema.crow b/CrowIDE/ui/DockWindows/winSchema.crow new file mode 100644 index 00000000..463c97a3 --- /dev/null +++ b/CrowIDE/ui/DockWindows/winSchema.crow @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + diff --git a/CrowIDE/ui/IDE.style b/CrowIDE/ui/IDE.style index e7b96c17..9adcfe62 100644 --- a/CrowIDE/ui/IDE.style +++ b/CrowIDE/ui/IDE.style @@ -23,3 +23,17 @@ IcoBut { Height = "14"; Background = "White"; } + +ProjTreeIcon { + Margin="1"; + Width="14"; + Height="14"; +} + +WinSchema { + Template="#Crow.Coding.ui.DockWindows.WinSchemaItem.template"; + Focusable = "true"; + MinimumSize="150,50"; + Width = "Fit"; + Height = "Fit"; +} diff --git a/CrowIDE/ui/ProjectProperties.crow b/CrowIDE/ui/ProjectProperties.crow index e4366b1e..9a6ba39b 100644 --- a/CrowIDE/ui/ProjectProperties.crow +++ b/CrowIDE/ui/ProjectProperties.crow @@ -1,5 +1,5 @@  - + diff --git a/CrowIDE/ui/ProjectTree.template b/CrowIDE/ui/ProjectTree.template index 55e7e828..e449549b 100644 --- a/CrowIDE/ui/ProjectTree.template +++ b/CrowIDE/ui/ProjectTree.template @@ -17,7 +17,7 @@ SvgSub="{./IsExpanded}" MouseEnter="{Background=LightGray}" MouseLeave="{Background=Transparent}"/> -