]> O.S.I.I.S - jp/crow.git/commitdiff
debug log view
authorJean-Philippe Bruyère <jp_bruyere@hotmail.com>
Thu, 29 Nov 2018 15:30:15 +0000 (16:30 +0100)
committerJean-Philippe Bruyère <jp_bruyere@hotmail.com>
Thu, 29 Nov 2018 15:30:15 +0000 (16:30 +0100)
Crow.csproj
Tests/ui/dbgLog.crow
src/GraphicObjects/GraphicObject.cs
src/GraphicObjects/ScrollingObject.cs
src/debug/DbgLogViewer.cs
src/debug/DebugLogViewer.cs [new file with mode: 0644]
src/debug/DebugLogger.cs

index fdb6d7c1ec4d96a80c26c553981111804f6beb1e..6ad0c309f7a20d0b65aedc365dc7a78aaccbce6d 100644 (file)
     </None>
     <None Include="Icons\compiler_warning.svg" />
     <None Include="Icons\compiler_warning_orange.svg" />
+    <None Include="src\debug\DebugLogViewer.cs" />
   </ItemGroup>
 </Project>
index d0735082b892a7782632502bd87d8e1c23a5776c..86ed0edc32fffd9f31d8647ba5442a51d27fa7b3 100755 (executable)
@@ -5,6 +5,16 @@
                <Label Text="{../../view.XScale}"/>
                <Label Text="Scroll X:"/>
                <Label Text="{../../view.ScrollX}"/>
+               <Label Text="Max Scroll X:"/>
+               <Label Text="{../../view.MaxScrollX}"/>
+               <Label Text="Scroll Y:"/>
+               <Label Text="{../../view.ScrollY}"/>
+               <Label Text="Visible Lines:"/>
+               <Label Text="{../../view.VisibleLines}"/>
+               <Label Text="Visible Ticks:"/>
+               <Label Text="{../../view.VisibleTicks}"/>
+               <Label Text="CurrentLine:"/>
+               <Label Text="{../../view.CurrentLine}"/>
        </HorizontalStack>
        <HorizontalStack>
                <DbgLogViewer Margin="0" Name="view" LogFile="debug.log" MouseWheelSpeed="10"/>
index 686dcb7f744b971ade0f5023f02fa94456d2f4a8..e67b3523002101386f1c7a9e8f4a67ca38a3e93d 100644 (file)
@@ -51,6 +51,9 @@ namespace Crow
        {
                internal ReaderWriterLockSlim parentRWLock = new ReaderWriterLockSlim();
                #if DEBUG_LOG
+               //0 is the main graphic tree, for other obj tree not added to main tree, it range from 1->n
+               //useful to track events for obj shown later, not on start, or never added to main tree
+               public int treeIndex;
                public int yIndex;//absolute index in the graphic tree for debug draw
                public int xLevel;//x increment for debug draw
                #endif
@@ -1652,7 +1655,7 @@ namespace Crow
                }
                protected virtual void UpdateCache(Context ctx){
                        #if DEBUG_LOG
-                       DebugLog.AddEvent(DbgEvtType.GOUpdateCacheAndPaintOnCTX, this);
+                       DbgEvent dbgEvt = DebugLog.AddEvent(DbgEvtType.GOUpdateCacheAndPaintOnCTX, this);
                        #endif
                        Rectangle rb = Slot + Parent.ClientRectangle.Position;
                        if (clearBackground) {
@@ -1666,6 +1669,9 @@ namespace Crow
                        ctx.Paint ();
                        Clipping.Dispose ();
                        Clipping = new Region ();
+                       #if DEBUG_LOG
+                       dbgEvt.end = DebugLog.chrono.ElapsedTicks;
+                       #endif
                }
                /// <summary> Chained painting routine on the parent context of the actual cached version
                /// of the widget </summary>
index ea7fd291adb0cbc74435ed7993078cc99f429c0f..855ac0e32097b58bd1b9cef19cbf425cf0639791 100644 (file)
@@ -68,7 +68,7 @@ namespace Crow
                                if (newS == scrollX)
                                        return;
 
-                               scrollX = value;
+                               scrollX = newS;
 
                                NotifyValueChanged ("ScrollX", scrollX);
                                RegisterForGraphicUpdate ();
index eca502fb41dbaf17e3a7063ba1b4b7415e507804..7c8a1bdf13895f9894cc4352c7cb11d0046c00ba 100644 (file)
@@ -27,21 +27,94 @@ using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
+using Cairo;
 
 #if DEBUG_LOG
 namespace Crow
 {
        public class DbgLogViewer : ScrollingObject
        {
+               #region debug viewer private classes
+               class DbgData {
+                       public int objInstanceNum;
+                       public LayoutingType layout;
+                       public LayoutingQueueItem.Result result;
+
+                       public DbgData (int _obj) {
+                               objInstanceNum = _obj;
+                       }
+               }
+               class DbgEvent {
+                       public long begin, end;
+                       public DbgEvtType type;
+                       public DbgData data = null;
+
+                       public DbgEvent() {}
+
+                       public static DbgEvent Parse (string str) {
+                               if (str == null)
+                                       return null;
+                               string[] tmp = str.Trim().Split(';');
+
+                               DbgEvent evt = new DbgEvent ();
+                               evt.begin = long.Parse (tmp [0]);
+                               evt.end = long.Parse (tmp [1]);
+                               evt.type = (DbgEvtType)Enum.Parse (typeof(DbgEvtType), tmp [2]);
+
+                               if (evt.type.HasFlag (DbgEvtType.GraphicObject)) {
+                                       evt.data = new DbgData (int.Parse (tmp [3]));
+                                       if (evt.type.HasFlag (DbgEvtType.GOLayouting)) {
+                                               evt.data.layout = (LayoutingType)Enum.Parse (typeof(LayoutingType), tmp [4]);
+                                               if (evt.type == DbgEvtType.GOProcessLayouting)
+                                                       evt.data.result = (LayoutingQueueItem.Result)Enum.Parse (typeof(LayoutingQueueItem.Result), tmp [5]);                                                                           
+                                       }
+                               }
+                               return evt;
+                       }
+               }
+               class DbgGo {
+                       public int listIndex;//prevent doing an IndexOf on list for each event to know y pos on screen
+                       public int instanceNum;//class instantiation order, used to bind events to objs
+                       public string name;
+                       //0 is the main graphic tree, for other obj tree not added to main tree, it range from 1->n
+                       //useful to track events for obj shown later, not on start
+                       public int treeIndex;
+                       public int yIndex;//index in parenting, the whole main graphic tree is one continuous suite
+                       public int xLevel;//depth
+
+                       public List<DbgEvent> events = new List<DbgEvent>();
+
+                       public static DbgGo Parse (string str) {
+                               DbgGo g = new DbgGo ();
+                               if (str == null)
+                                       return null;
+                               string[] tmp = str.Trim().Split(';');
+                               g.instanceNum = int.Parse (tmp [0]);
+                               g.name = tmp [1];
+                               g.yIndex = int.Parse (tmp [2]);
+                               g.xLevel = int.Parse (tmp [3]);
+                               return g;
+                       }
+
+               }
+               #endregion
+
                #region CTOR
                protected DbgLogViewer () : base(){}
                public DbgLogViewer (Interface iface) : base(iface){}
                #endregion
 
-               bool loaded = false;
-               double xScale = 0.125, yScale = 1.0, lineHeight = 0.0, leftMargin;
+               FontExtents fe;
+
+               double xScale = 1.0/512.0, yScale = 1.0, leftMargin, topMargin = 0.0;
                string logFile;
-               long currentTick = 0, selStart = -1, selEnd = -1, minTicks = 0, maxTicks = 0;
+
+               List<DbgEvent> events;//global events
+               List<DbgGo> objs;
+
+               long currentTick = 0, selStart = -1, selEnd = -1, minTicks = 0, maxTicks = 0, visibleTicks = 0;
+               int currentLine = -1;
+               int visibleLines;
 
                public string LogFile {
                        get { return logFile; }
@@ -49,8 +122,11 @@ namespace Crow
                                if (logFile == value)
                                        return;
                                logFile = value;
+
+                               loadDebugFile ();
+
+
                                NotifyValueChanged ("LogFile", logFile);
-                               loaded = false;
                                RegisterForGraphicUpdate ();
                        }
                }
@@ -61,6 +137,7 @@ namespace Crow
                                        return;
                                xScale = value;
                                NotifyValueChanged ("XScale", xScale);
+                               updateVisibleTicks ();
                                RegisterForGraphicUpdate ();
                        }
                }
@@ -74,8 +151,188 @@ namespace Crow
                                RegisterForGraphicUpdate ();
                        }
                }
-               List<DbgEvent> events;
-               List<DbgGo> objs;
+
+
+               void storeEvent (DbgEvent evt) {
+                       if (evt.data == null)//global events
+                               events.Add (evt);
+                       else
+                               objs.Where (o => o.instanceNum == evt.data.objInstanceNum).FirstOrDefault ().events.Add (evt);                                          
+               }
+
+               void loadDebugFile() {
+                       if (!File.Exists (logFile))
+                               return;
+
+                       events = new List<DbgEvent>();
+                       objs = new List<DbgGo> ();
+                       minTicks = maxTicks = 0;
+                       leftMargin = topMargin = 0.0;
+
+                       using (ImageSurface img = new ImageSurface (Format.Argb32, 1, 1)) {
+                               using (Context gr = new Context (img)) {
+                                       double maxNameWidth = 0.0;
+
+                                       gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight);
+                                       gr.SetFontSize (Font.Size);
+
+                                       using (StreamReader s = new StreamReader (logFile)) {
+                                               if (s.ReadLine () != "[GraphicObjects]")                                        
+                                                       return;                         
+                                               while (!s.EndOfStream) {
+                                                       string l = s.ReadLine ();
+                                                       if (l == "[Events]")
+                                                               break;
+                                                       DbgGo o = DbgGo.Parse (l);
+                                                       objs.Add (o);
+                                                       double nameWidth = gr.TextExtents (o.name).Width + 5.0 * o.xLevel;
+                                                       if (nameWidth > maxNameWidth)
+                                                               maxNameWidth = nameWidth;
+                                               }
+                                               if (!s.EndOfStream) {
+                                                       DbgEvent firstEvt = DbgEvent.Parse (s.ReadLine ());
+                                                       storeEvent (firstEvt);
+                                                       minTicks = firstEvt.begin;
+                                               }
+
+                                               if (!s.EndOfStream) {
+                                                       while (true) {
+                                                               DbgLogViewer.DbgEvent evt = DbgEvent.Parse (s.ReadLine ());
+                                                               storeEvent (evt);
+                                                               if (s.EndOfStream) {
+                                                                       maxTicks = evt.end;
+                                                                       break;
+                                                               }
+                                                       }
+                                               }
+                                       }
+
+                                       leftMargin = 2.5 + maxNameWidth;
+
+                                       fe = gr.FontExtents;
+
+                                       topMargin = 2.0 * fe.Height;
+
+                                       updateVisibleLines ();
+                                       updateVisibleTicks ();
+                               }
+                       }
+
+               }
+               void updateVisibleLines(){
+                       visibleLines = (int)Math.Floor (((double)ClientRectangle.Height - topMargin) / fe.Height);
+                       NotifyValueChanged ("VisibleLines", visibleLines);
+                       updateMaxScrollY ();
+               }
+               void updateVisibleTicks() {
+                       visibleTicks = Math.Max (0, (long)((double)(ClientRectangle.Width - leftMargin) / XScale));
+                       NotifyValueChanged ("VisibleTicks", visibleTicks);
+                       updateMaxScrollX ();
+               }
+               /*
+               void updateVisibleColumns(){
+                       visibleColumns = (int)Math.Floor ((double)(ClientRectangle.Width - leftMargin)/ fe.MaxXAdvance);
+                       NotifyValueChanged ("VisibleColumns", visibleColumns);
+                       updateMaxScrollX ();
+               }
+               void updateMaxScrollX () {
+                       MaxScrollX = Math.Max (0, buffer.longestLineCharCount - visibleColumns);
+                       if (buffer.longestLineCharCount > 0)
+                               NotifyValueChanged ("ChildWidthRatio", Slot.Width * visibleColumns / buffer.longestLineCharCount);                      
+               }*/
+
+               void updateMaxScrollX () {
+                       if (objs == null)
+                               MaxScrollX = 0;
+                       else                            
+                               MaxScrollX =  (int)Math.Max(0L, maxTicks - minTicks - visibleTicks);
+               }
+               void updateMaxScrollY () {
+                       if (objs == null)
+                               MaxScrollY = 0;
+                       else                            
+                               MaxScrollY =  Math.Max(0, objs.Count - visibleLines);
+               }
+
+
+               public override Font Font {
+                       get { return base.Font; }
+                       set {
+                               base.Font = value;
+                               loadDebugFile ();
+                       }
+               }
+
+               public override int ScrollY {
+                       get {
+                               return base.ScrollY;
+                       }
+                       set {
+                               base.ScrollY = value;
+
+                               if (objs == null)
+                                       return;
+                               
+                               Rectangle cb = ClientRectangle;
+                               cb.Left += (int)leftMargin;
+                               cb.Width -= (int)leftMargin;
+                               cb.Y += (int)topMargin;
+                               cb.Height -= (int)topMargin;
+
+                               if (mousePos.Y < cb.Top || mousePos.Y > cb.Bottom)
+                                       currentLine = -1;
+                               else
+                                       currentLine = (int)((double)(mousePos.Y - cb.Top) / fe.Height) + ScrollY;
+
+                               NotifyValueChanged ("CurrentLine", currentLine);
+
+                       }
+               }
+
+               Crow.Color getObjEventColor (DbgEvent evt) {
+                       if (evt.type.HasFlag (DbgEvtType.GOLayouting)) {
+                               if (evt.type == DbgEvtType.GOProcessLayouting) {                                                        
+                                       switch (evt.data.result) {
+                                       case LayoutingQueueItem.Result.Success:
+                                               return Crow.Color.Green;
+                                       case LayoutingQueueItem.Result.Deleted:
+                                               return Crow.Color.Red;
+                                       case LayoutingQueueItem.Result.Discarded:
+                                               return Crow.Color.DarkRed;
+                                       case LayoutingQueueItem.Result.Requeued:
+                                               return Crow.Color.Orange;
+                                       }
+                               } else if (evt.type == DbgEvtType.GOProcessLayoutingWithNoParent)
+                                       return Color.IndianRed;
+                               else
+                                       return Crow.Color.Blue;                         
+                       }
+                       switch (evt.type) {
+                       case DbgEvtType.GOClassCreation:
+                               return Color.GhostWhite;
+                       case DbgEvtType.GOInitialization:
+                               return Color.Cyan;
+                       case DbgEvtType.GOClippingRegistration:
+                               return Color.Cyan;
+                       case DbgEvtType.GORegisterClip:
+                               return Color.Cyan;
+                       case DbgEvtType.GORegisterForGraphicUpdate:
+                               return Color.Cyan;
+                       case DbgEvtType.GOEnqueueForRepaint:
+                               return Color.Cyan;
+                       case DbgEvtType.GODraw:
+                               return Color.Cyan;
+                       case DbgEvtType.GORecreateCache:
+                               return Color.Cyan;
+                       case DbgEvtType.GOUpdateCacheAndPaintOnCTX:
+                               return Color.Cyan;
+                       case DbgEvtType.GOPaint:
+                               return Color.Cyan;
+                       case DbgEvtType.GONewDataSource:
+                               return Color.Cyan;
+                       }
+                       return Crow.Color.RebeccaPurple;                                
+               }
 
                protected override void onDraw (Cairo.Context gr)
                {
@@ -86,167 +343,132 @@ namespace Crow
                        gr.FontOptions = Interface.FontRenderingOptions;
                        gr.Antialias = Interface.Antialias;
 
-                       if (!loaded) {
-                               if (!File.Exists (logFile))
-                                       return;
-                               events = new List<DbgEvent>();
-                               objs = new List<DbgGo> ();
+                       if (objs == null)
+                               return;
 
-                               using (StreamReader s = new StreamReader (logFile)) {
-                                       if (s.ReadLine () != "[GraphicObjects]") {
-                                               loaded = false;
-                                               return;
-                                       }
-                                       while (!s.EndOfStream) {
-                                               string l = s.ReadLine ();
-                                               if (l == "[Events]")
-                                                       break;
-                                               objs.Add (DbgGo.Parse (l));
+                       gr.LineWidth = 1.0;
+
+                       Rectangle cb = ClientRectangle;
+
+                       double penY = topMargin + ClientRectangle.Top;
+
+                       for (int i = 0; i < visibleLines; i++) {
+                               DbgGo g = objs [i + ScrollY];
+
+                               foreach (DbgEvent evt in g.events) {
+                                       if (evt.end - minTicks <= ScrollX)
+                                               continue;
+                                       if (evt.begin - minTicks > ScrollX + visibleTicks)
+                                               break;
+                                       double x = xScale * (evt.begin - minTicks - ScrollX) ;
+                                       double w = Math.Max (Math.Max(2.0, 2.0 * xScale), (double)(evt.end - evt.begin) * xScale);
+                                       if (x < 0.0) {
+                                               w += x;
+                                               x = 0.0;
                                        }
-                                       while (!s.EndOfStream)
-                                               events.Add (DbgEvent.Parse (s.ReadLine ()));                                            
+                                       x += leftMargin + cb.Left;
+                                       double rightDiff = x + w - cb.Right;
+                                       if (rightDiff > 0)
+                                               w -= rightDiff;
+                                       //if (x + w > cb.Right)
+                                       //      continue;
+
+                                       gr.SetSourceColor (getObjEventColor (evt));                                                                                     
+
+                                       gr.Rectangle (x, penY, w, fe.Height);
+                                       gr.Fill ();
+
                                }
-                               loaded = true;
-                               lineHeight = gr.FontExtents.Height;
-                               if (events.Count > 0) {
-                                       minTicks = events [0].begin;
-                                       maxTicks = events [events.Count - 1].begin;
-                               } else
-                                       minTicks = maxTicks = 0;
-                               
-                               MaxScrollY = Math.Max(0, objs.Count - (int)Math.Ceiling((double)ClientRectangle.Height / lineHeight));
-                       }
 
-                       Rectangle r = ClientRectangle;
+                               penY += fe.Height;
 
-                       gr.LineWidth = 1.0;
+                               gr.SetSourceColor (Crow.Color.Jet);
+                               gr.MoveTo (cb.X, penY - 0.5);
+                               gr.LineTo (cb.Right, penY - 0.5);
+                               gr.Stroke ();
 
-                       leftMargin = 0.0;
+                               double penX = 5.0 * g.xLevel + cb.Left;
 
-                       foreach (DbgGo g in objs) {
                                if (g.yIndex == 0)
-                                       continue;
-                               double penX = r.X + g.xLevel * 5.0;
-                               Cairo.TextExtents te = gr.TextExtents (g.name);
-                               if (te.Width + penX > leftMargin)
-                                       leftMargin = te.Width + penX; 
-                               double penY = (g.yIndex - ScrollY) * lineHeight + r.Top;
-                               if (penY < r.Top || penY > r.Bottom + lineHeight)
-                                       continue;
-                       
-                               Foreground.SetAsSource (gr);
+                                       gr.SetSourceColor (Crow.Color.LightSalmon);
+                               else
+                                       Foreground.SetAsSource (gr);
+
                                gr.MoveTo (penX, penY - gr.FontExtents.Descent);
                                gr.ShowText (g.name);
-                               gr.SetSourceColor (Crow.Color.Jet);
-                               gr.MoveTo (r.X, penY - 0.5);
-                               gr.LineTo (r.Right, penY - 0.5);
-                               gr.Stroke ();
+
+                               gr.SetSourceColor (Crow.Color.White);
+                               gr.MoveTo (cb.X, penY - gr.FontExtents.Descent);
+                               gr.ShowText ((i+ ScrollY).ToString());
+
                        }
 
-                       leftMargin += 2.5;
-                       r.Left += (int)leftMargin;
-                       gr.MoveTo (leftMargin, r.Top);
-                       gr.LineTo (leftMargin, r.Bottom);
-                       Foreground.SetAsSource (gr);
-                       gr.Stroke ();
 
-                       MaxScrollX = (int)(maxTicks - minTicks) - r.Width;
+                       gr.MoveTo (cb.Left, topMargin - 0.5 + cb.Top);
+                       gr.LineTo (cb.Right, topMargin - 0.5 + cb.Top);
 
-                       gr.SetFontSize (8);
+                       gr.MoveTo (leftMargin + cb.Left, cb.Top);
+                       gr.LineTo (leftMargin + cb.Left, cb.Bottom);
+                       gr.SetSourceColor (Crow.Color.Grey);
 
-                       gr.Save ();
-                       gr.Rectangle (r);
-                       gr.Clip ();
+                       penY = topMargin + ClientRectangle.Top;
 
-                       foreach (DbgEvent evt in events) {
-                               double x = (int)((double)(evt.begin - minTicks - ScrollX) * xScale) ;
-                               x += (double)r.Left + 0.5;
-                               double w = Math.Max (Math.Max(2.0, 2.0 * xScale), (double)(evt.end - evt.begin) * xScale);
+                       //graduation
+                       int largeGrad = int.Parse ("1" + new string ('0', visibleTicks.ToString ().Length - 1));
+                       int smallGrad = Math.Max (1, largeGrad / 10);
 
-                               if (x + w < r.Left || x > r.Right)
-                                       continue;
+                       long firstVisibleTicks = minTicks + ScrollX;
+                       long curGrad = firstVisibleTicks - firstVisibleTicks % smallGrad + smallGrad;
 
-                               if (evt.type.HasFlag (DbgEvtType.GraphicObject)) {
-                                       gr.SetSourceColor (Crow.Color.DarkSlateBlue);
-                                       DbgData data = (DbgData)evt.data;
-                                       double y = (objs.Where(o=>o.index == data.obj).FirstOrDefault().yIndex - ScrollY - 1) * lineHeight;
-                                       if (evt.type.HasFlag (DbgEvtType.GOLayouting)) {
-                                               if (evt.type == DbgEvtType.GOProcessLayouting) {                                                        
-                                                       switch (data.result) {
-                                                       case LayoutingQueueItem.Result.Success:
-                                                               gr.SetSourceColor (Crow.Color.Green);
-                                                               break;
-                                                       case LayoutingQueueItem.Result.Deleted:
-                                                               gr.SetSourceColor (Crow.Color.Red);
-                                                               break;
-                                                       case LayoutingQueueItem.Result.Discarded:
-                                                               gr.SetSourceColor (Crow.Color.DarkOrange);
-                                                               break;
-                                                       case LayoutingQueueItem.Result.Requeued:
-                                                               gr.SetSourceColor (Crow.Color.GreenYellow);
-                                                               break;
-                                                       case LayoutingQueueItem.Result.Register:
-                                                               gr.SetSourceColor (Crow.Color.Blue);
-                                                               break;
-                                                       }
+                       long gg = curGrad - ScrollX - minTicks;
+                       while (gg < visibleTicks ) {
+                               double x = (double)gg * xScale + leftMargin + cb.Left;
 
-                                               }else
-                                                       gr.SetSourceColor (Crow.Color.Bisque);
-                                               
-                                       }else if (evt.type == DbgEvtType.GOInitialization)
-                                               gr.SetSourceColor (Crow.Color.Cyan);                                    
-                                       else if (evt.type == DbgEvtType.GOClippingRegistration)
-                                               gr.SetSourceColor (Crow.Color.BlueViolet);      
-                                       else if (evt.type == DbgEvtType.GORegisterClip)
-                                               gr.SetSourceColor (Crow.Color.Orange);
-                                       else if (evt.type == DbgEvtType.GODraw)
-                                               gr.SetSourceColor (Crow.Color.Pink);
-                                       else if (evt.type == DbgEvtType.GORecreateCache)
-                                               gr.SetSourceColor (Crow.Color.YellowGreen);
-                                               
-                                       gr.Rectangle (x, y, w, lineHeight);
-                                       gr.Fill ();
+                               gr.MoveTo (x, penY - 0.5);
+                               if (curGrad % largeGrad == 0) { 
+                                       gr.LineTo (x, penY - 8.5);
+                                       string str = curGrad.ToString ();
+                                       TextExtents te = gr.TextExtents (str);
+                                       gr.RelMoveTo (-0.5 * te.Width, -2.0);
+                                       gr.ShowText (str);
+                               }else
+                                       gr.LineTo (x, penY - 2.5);
 
-                                       if (evt.type.HasFlag (DbgEvtType.GOLayouting)) {
-                                               gr.SetSourceColor (Crow.Color.Black);
-                                               switch (data.layout) {
-                                               case LayoutingType.Height:
-                                                       gr.MoveTo (x + w * 0.5, y + 1);
-                                                       gr.LineTo (x + w * 0.5, y + lineHeight - 2);
-                                                       gr.Stroke ();
-                                                       break;
-                                               case LayoutingType.Width:
-                                                       gr.MoveTo (x + 0.5, y + lineHeight * 0.5);
-                                                       gr.LineTo (x + w - 1.0, y + lineHeight * 0.5);
-                                                       gr.Stroke ();
-                                                       break;
-                                               default:
-                                                       break;
-                                               }
-                                       }
-                               } else {
-                                       gr.SetSourceColor (Crow.Color.Yellow);
-                                       gr.MoveTo (x, r.Top);
-                                       gr.LineTo (x, r.Bottom);
-                                       gr.Stroke ();
-                                       string s = evt.type.ToString () [10].ToString ();
-                                       Cairo.TextExtents te = gr.TextExtents (s);
-                                       gr.Rectangle (x, r.Top, te.Width, lineHeight);
-                                       gr.Fill ();
-                                       gr.MoveTo (x, r.Top + gr.FontExtents.Ascent);
-                                       gr.SetSourceColor (Crow.Color.Jet);
-                                       gr.ShowText (s);
-                               }
+                               curGrad += smallGrad;
+                               gg = curGrad - ScrollX - minTicks;
+                       }
+
+                       gr.Stroke ();
+
+                       //global events
+                       foreach (DbgEvent evt in events) {
+                               if (evt.begin - minTicks <= ScrollX)
+                                       continue;
+                               double x = xScale * (evt.begin - minTicks - ScrollX) ;
+                               x += leftMargin + cb.Left;
+
+                               gr.SetSourceColor (Crow.Color.Yellow);
+                               gr.MoveTo (x, penY);
+                               gr.LineTo (x, cb.Bottom);
+                               gr.Stroke ();
+                               string s = evt.type.ToString () [10].ToString ();
+                               Cairo.TextExtents te = gr.TextExtents (s);
+                               gr.Rectangle (x - 0.5 * te.Width , penY - te.Height, te.Width, te.Height);
+                               gr.Fill ();
+                               gr.MoveTo (x- 0.5 * te.Width, penY - gr.FontExtents.Descent);
+                               gr.SetSourceColor (Crow.Color.Jet);
+                               gr.ShowText (s);
                        }
-                       gr.Restore ();
                }
                public override void Paint (ref Cairo.Context ctx)
                {
                        base.Paint (ref ctx);
+
                        Rectangle r = new Rectangle(mousePos.X, 0, 1, Slot.Height);
-                       Rectangle cb = ContextCoordinates (r);
+                       Rectangle ctxR = ContextCoordinates (r);
+                       Rectangle cb = ClientRectangle;
 
-                       ctx.Rectangle (cb);
+                       ctx.Rectangle (ctxR);
                        ctx.SetSourceColor (Color.CornflowerBlue);
                        ctx.Fill();
 
@@ -255,25 +477,38 @@ namespace Crow
                        ctx.FontOptions = Interface.FontRenderingOptions;
                        ctx.Antialias = Interface.Antialias;
 
-                       ctx.MoveTo (cb.X, cb.Y + lineHeight);
+                       ctx.MoveTo (ctxR.X, ctxR.Y + fe.Height);
                        ctx.ShowText (currentTick.ToString ());
 
-                       if (selStart < 0)
+                       ctx.Operator = Cairo.Operator.Add;
+
+                       if (currentLine >= 0) {
+                               r = new Rectangle (cb.Left, (currentLine + 2 - ScrollY) * (int)fe.Height + cb.Top, cb.Width, (int)fe.Height);
+
+                               ctx.Operator = Cairo.Operator.Add;
+                               ctx.SetSourceRGBA (0.1, 0.1, 0.1, 0.4);
+                               ctx.Rectangle (ContextCoordinates (r));
+                               ctx.Fill ();
+                       }
+
+                       if (selStart < 0) {
+                               ctx.Operator = Cairo.Operator.Over;
                                return;
-                       double selStartX = (double)(selStart - ScrollX - minTicks) * xScale + leftMargin;
-                       double selEndX = (selEnd >= 0) ? (double)(selEnd - ScrollX - minTicks) * xScale + leftMargin :
-                               (double)(currentTick - ScrollX - minTicks) * xScale + leftMargin;
+                       }
+                       double selStartX = (double)(selStart - ScrollX - minTicks) * xScale + leftMargin + cb.Left;
+                       double selEndX = (selEnd >= 0) ? (double)(selEnd - ScrollX - minTicks) * xScale + leftMargin + cb.Left :
+                               (double)(currentTick - ScrollX - minTicks) * xScale + leftMargin + cb.Left;
 
                        if (selStartX < selEndX) {
-                               cb.X = (int)selStartX;
-                               cb.Width = (int)(selEndX - selStartX);
+                               ctxR.X = (int)selStartX;
+                               ctxR.Width = (int)(selEndX - selStartX);
                        } else {
-                               cb.X = (int)selEndX;
-                               cb.Width = (int)(selStartX - selEndX);
+                               ctxR.X = (int)selEndX;
+                               ctxR.Width = (int)(selStartX - selEndX);
                        }
-                       ctx.Operator = Cairo.Operator.Add;
-                       cb.Width = Math.Max (1, cb.Width);
-                       ctx.Rectangle (cb);
+
+                       ctxR.Width = Math.Max (1, ctxR.Width);
+                       ctx.Rectangle (ctxR);
                        //ctx.SetSourceColor (Color.LightYellow);
                        ctx.SetSourceColor (Color.Jet);
                        ctx.Fill();
@@ -285,28 +520,46 @@ namespace Crow
                        base.OnLayoutChanges (layoutType);
                        switch (layoutType) {
                        case LayoutingType.Width:
-                               
+                               updateVisibleTicks ();
                                break;
                        case LayoutingType.Height:
-                               if (!loaded)
-                                       break;
-                               MaxScrollY = objs.Count - (int)Math.Ceiling((double)ClientRectangle.Height / lineHeight);
+                               updateVisibleLines ();
                                break;
                        }
                }
+
                Point mousePos;
                void updateMouseLocalPos(Point mPos){
                        Rectangle r = ScreenCoordinates (Slot);
                        Rectangle cb = ClientRectangle;
                        cb.Left += (int)leftMargin;
+                       cb.Width -= (int)leftMargin;
+                       cb.Y += (int)topMargin;
+                       cb.Height -= (int)topMargin;
+
                        mousePos = mPos - r.Position;
 
                        mousePos.X = Math.Max(cb.X, mousePos.X);
                        mousePos.X = Math.Min(cb.Right, mousePos.X);
+
+                       if (mousePos.Y < cb.Top || mousePos.Y > cb.Bottom)
+                               currentLine = -1;
+                       else
+                               currentLine = (int)((double)(mousePos.Y - cb.Top) / fe.Height) + ScrollY;
+
+                       NotifyValueChanged ("CurrentLine", currentLine);
+                       
                        mousePos.Y = Math.Max(cb.Y, mousePos.Y);
                        mousePos.Y = Math.Min(cb.Bottom, mousePos.Y);
 
-                       currentTick = (int)((double)Math.Max(0, mousePos.X - cb.X) / xScale) + minTicks + ScrollX;
+                       currentTick = (int)((double)(mousePos.X - cb.X) / xScale) + minTicks + ScrollX;
+
+               }
+               public override void onMouseLeave (object sender, MouseMoveEventArgs e)
+               {
+                       base.onMouseLeave (sender, e);
+                       currentLine = -1;
+                       currentTick = 0;
                }
                public override void onMouseMove (object sender, MouseMoveEventArgs e)
                {
@@ -349,10 +602,10 @@ namespace Crow
                                if (e.Delta > 0) {
                                        XScale *= 2.0;
                                } else {
-                                       XScale *= 0.5;
+                                       if (MaxScrollX > 0)
+                                               XScale *= 0.5;
                                }
-                               int cbx = ClientRectangle.X + (int)leftMargin;
-                               ScrollX = (int)(currentTick - (int)((double)Math.Max(0, mousePos.X - cbx) / xScale) - minTicks);
+                               ScrollX = (int)(currentTick - (int)((double)Math.Max(0, mousePos.X - (int)leftMargin) / xScale) - minTicks);
                        }else
                                ScrollY -= e.Delta * MouseWheelSpeed;
                }
@@ -372,9 +625,11 @@ namespace Crow
                        }
                }
 
-               void zoom (long start, long end) {                      
+               void zoom (long start, long end) {                                              
+                       //Rectangle cb = ClientRectangle;
+                       //cb.X += (int)leftMargin;
+                       XScale = ((double)ClientRectangle.Width - leftMargin)/(end - start);
                        ScrollX = (int)(start - minTicks);
-                       XScale = (ClientRectangle.Width - leftMargin)/(end - start);
                }
        }
 }
diff --git a/src/debug/DebugLogViewer.cs b/src/debug/DebugLogViewer.cs
new file mode 100644 (file)
index 0000000..5b6ca36
--- /dev/null
@@ -0,0 +1,1177 @@
+//
+// 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.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
+{
+       /// <summary>
+       /// Scrolling text box optimized for monospace fonts, for coding
+       /// </summary>
+       public class DebugLogViewer : ScrollingObject
+       {               
+               #region CTOR
+               public DebugLogViewer (): base()
+               {
+                       
+               }
+               #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 ();
+               }
+
+               /// <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) {
+                                       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.SelectFontFace (Font.Name, fts, ftw);
+                               gr.SetSourceColor (fg);
+
+                               gr.MoveTo (x, y + fe.Ascent);
+                               gr.ShowText (lstr);
+                               gr.Fill ();
+
+                               if (buffer.SelectionInProgress && lineIndex >= buffer.SelectionStart.Y && lineIndex <= buffer.SelectionEnd.Y &&
+                                       !(lineIndex == buffer.SelectionStart.Y && lPtr + lstr.Length <= selStartCol) &&
+                                       !(lineIndex == buffer.SelectionEnd.Y && selEndCol <= lPtr)) {
+
+                                       double rLineX = x,
+                                       rLineY = y,
+                                       rLineW = lstr.Length * fe.MaxXAdvance;
+                                       double startAdjust = 0.0;
+
+                                       if ((lineIndex == buffer.SelectionStart.Y) && (selStartCol < lPtr + lstr.Length) && (selStartCol > lPtr))
+                                               startAdjust = (selStartCol - lPtr) * fe.MaxXAdvance;
+                                       rLineX += startAdjust;
+                                       if ((lineIndex == buffer.SelectionEnd.Y) && (selEndCol < lPtr + lstr.Length))
+                                               rLineW = (selEndCol - lPtr) * fe.MaxXAdvance;
+                                       rLineW -= startAdjust;
+
+                                       gr.Save ();
+                                       gr.Operator = Operator.Source;
+                                       gr.Rectangle (rLineX, rLineY, rLineW, (fe.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.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 && 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 () {
+                       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]);
+               }
+               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 = XCursor.Default;
+                       else
+                               IFace.MouseCursor = XCursor.Text;
+               }
+               public override void onMouseLeave (object sender, MouseMoveEventArgs e)
+               {
+                       base.onMouseLeave (sender, e);
+                       IFace.MouseCursor = XCursor.Default;
+               }
+               public override void onMouseMove (object sender, MouseMoveEventArgs e)
+               {
+                       base.onMouseMove (sender, e);
+
+                       mouseLocalPos = e.Position - ScreenCoordinates(Slot).TopLeft - ClientRectangle.TopLeft;
+
+                       updateHoverLine ();
+
+                       if (!e.Mouse.IsButtonDown (MouseButton.Left)) {
+                               if (mouseLocalPos.X < leftMargin)
+                                       IFace.MouseCursor = XCursor.Default;
+                               else
+                                       IFace.MouseCursor = XCursor.Text;
+                               return;
+                       }
+
+                       if (!HasFocus || !buffer.SelectionInProgress)
+                               return;
+
+                       //mouse is down
+                       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 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 (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.DeleteChar ();
+                               break;
+                       case Key.Clear:
+                               break;
+                       case Key.Delete:
+                               if (buffer.SelectionIsEmpty)
+                                       MoveRight ();
+                               else if (e.Shift)
+                                       IFace.Clipboard = buffer.SelectedText;
+                               buffer.DeleteChar ();
+                               break;
+                       case Key.Enter:
+                       case Key.KeypadEnter:
+                               if (!buffer.SelectionIsEmpty)
+                                       buffer.DeleteChar ();
+                               buffer.InsertLineBreak ();
+                               break;
+                       case Key.Escape:
+                               buffer.ResetSelection ();
+                               break;
+                       case Key.Home:
+                               if (e.Shift) {
+                                       if (buffer.SelectionIsEmpty)
+                                               buffer.SetSelStartPos ();
+                                       if (e.Control)
+                                               buffer.CurrentLine = 0;
+                                       buffer.CurrentColumn = 0;
+                                       buffer.SetSelEndPos ();
+                                       break;
+                               }
+                               buffer.ResetSelection ();
+                               if (e.Control)
+                                       buffer.CurrentLine = 0;
+                               buffer.CurrentColumn = 0;
+                               break;
+                       case Key.End:
+                               if (e.Shift) {
+                                       if (buffer.SelectionIsEmpty)
+                                               buffer.SetSelStartPos ();
+                                       if (e.Control)
+                                               buffer.CurrentLine = int.MaxValue;
+                                       buffer.CurrentColumn = int.MaxValue;
+                                       buffer.SetSelEndPos ();
+                                       break;
+                               }
+                               buffer.ResetSelection ();
+                               if (e.Control)
+                                       buffer.CurrentLine = int.MaxValue;
+                               buffer.CurrentColumn = int.MaxValue;
+                               break;
+                       case Key.Insert:
+                               if (e.Shift)
+                                       buffer.Insert (IFace.Clipboard);
+                               else if (e.Control && !buffer.SelectionIsEmpty)
+                                       IFace.Clipboard = buffer.SelectedText;
+                               break;
+                       case Key.Left:
+                               if (e.Shift) {
+                                       if (buffer.SelectionIsEmpty)
+                                               buffer.SetSelStartPos ();
+                                       if (e.Control)
+                                               buffer.GotoWordStart ();
+                                       else
+                                               MoveLeft ();
+                                       buffer.SetSelEndPos ();
+                                       break;
+                               }
+                               buffer.ResetSelection ();
+                               if (e.Control)
+                                       buffer.GotoWordStart ();
+                               else
+                                       MoveLeft();
+                               break;
+                       case Key.Right:
+                               if (e.Shift) {
+                                       if (buffer.SelectionIsEmpty)
+                                               buffer.SetSelStartPos ();
+                                       if (e.Control)
+                                               buffer.GotoWordEnd ();
+                                       else
+                                               MoveRight ();
+                                       buffer.SetSelEndPos ();
+                                       break;
+                               }
+                               buffer.ResetSelection ();
+                               if (e.Control)
+                                       buffer.GotoWordEnd ();
+                               else
+                                       MoveRight ();
+                               break;
+                       case Key.Up:
+                               if (e.Shift) {
+                                       if (buffer.SelectionIsEmpty)
+                                               buffer.SetSelStartPos ();
+                                       PrintedCurrentLine--;
+                                       buffer.SetSelEndPos ();
+                                       break;
+                               }
+                               buffer.ResetSelection ();
+                               PrintedCurrentLine--;
+                               break;
+                       case Key.Down:
+                               if (e.Shift) {
+                                       if (buffer.SelectionIsEmpty)
+                                               buffer.SetSelStartPos ();
+                                       PrintedCurrentLine++;
+                                       buffer.SetSelEndPos ();
+                                       break;
+                               }
+                               buffer.ResetSelection ();
+                               PrintedCurrentLine++;
+                               break;
+                       case Key.Menu:
+                               break;
+                       case Key.NumLock:
+                               break;
+                       case Key.PageDown:
+                               if (e.Shift) {
+                                       if (buffer.SelectionIsEmpty)
+                                               buffer.SetSelStartPos ();
+                                       PrintedCurrentLine += visibleLines;
+                                       buffer.SetSelEndPos ();
+                                       break;
+                               }
+                               buffer.ResetSelection ();
+                               PrintedCurrentLine += visibleLines;
+                               break;
+                       case Key.PageUp:
+                               if (e.Shift) {
+                                       if (buffer.SelectionIsEmpty)
+                                               buffer.SetSelStartPos ();
+                                       PrintedCurrentLine -= visibleLines;
+                                       buffer.SetSelEndPos ();
+                                       break;
+                               }
+                               buffer.ResetSelection ();
+                               PrintedCurrentLine -= visibleLines;
+                               break;
+                       case Key.RWin:
+                               break;
+                       case Key.Tab:
+                               if (e.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
index 161d742c8a75226b6a9b9d37ee1ff369975039b9..18e4ae194ce687553bbf52ec7b52bfc4ffd09af7 100644 (file)
@@ -28,6 +28,7 @@ using Cairo;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.IO;
+using System.Linq;
 
 #if DEBUG_LOG
 namespace Crow
@@ -50,7 +51,7 @@ namespace Crow
                //10 nth bit set for graphic obj
                GraphicObject                                   = 0x0200,
                GOClassCreation                                 = 0x0201,
-               GOInitialization                                        = 0x0202,
+               GOInitialization                                = 0x0202,
                GOClippingRegistration                  = 0x0203,
                GORegisterClip                                  = 0x0204,
                GORegisterForGraphicUpdate              = 0x0205,
@@ -65,15 +66,10 @@ namespace Crow
                GOPaint                                                 = 0x020d,
                GONewDataSource                                 = 0x020e,
        }
-       public class DbgData {
-               public int obj;
-               public LayoutingType layout;
-               public LayoutingQueueItem.Result result;
 
-               public DbgData (int _obj) {
-                       obj = _obj;
-               }
-       }
+       /// <summary>
+       /// debug events as recorded, another class is used in the viewer
+       /// </summary>
        public class DbgEvent {
                public long begin, end;
                public DbgEvtType type;
@@ -85,6 +81,7 @@ namespace Crow
                        data = _data;
                        type = evt;
                        begin = timeStamp;
+                       end = timeStamp;
                }
 
                public override string ToString ()
@@ -100,49 +97,8 @@ namespace Crow
                        return string.Format ("{0};{1};{2};{3};{4}", begin, end, type, GraphicObject.GraphicObjects.IndexOf(lqi.graphicObject).ToString(), lqi.LayoutType.ToString());
                        
                }
-
-               public static DbgEvent Parse (string str) {
-                       if (str == null)
-                               return null;
-                       string[] tmp = str.Trim().Split(';');
-
-
-                       DbgEvent evt = new DbgEvent ();
-                       evt.begin = long.Parse (tmp [0]);
-                       evt.end = long.Parse (tmp [1]);
-                       evt.type = (DbgEvtType)Enum.Parse (typeof(DbgEvtType), tmp [2]);
-
-                       if (evt.type.HasFlag (DbgEvtType.GraphicObject)) {
-                               DbgData data = new DbgData (int.Parse (tmp [3]));
-                               if (evt.type.HasFlag (DbgEvtType.GOLayouting)) {
-                                       data.layout = (LayoutingType)Enum.Parse (typeof(LayoutingType), tmp [4]);
-                                       if (evt.type == DbgEvtType.GOProcessLayouting)
-                                               data.result = (LayoutingQueueItem.Result)Enum.Parse (typeof(LayoutingQueueItem.Result), tmp [5]);                                                                               
-                               }
-                               evt.data = data;
-                       }
-                       return evt;
-               }
        }
-       public class DbgGo {
-               public int index;
-               public string name;
-               public int yIndex;
-               public int xLevel;
-
-               public static DbgGo Parse (string str) {
-                       DbgGo g = new DbgGo ();
-                       if (str == null)
-                               return null;
-                       string[] tmp = str.Trim().Split(';');
-                       g.index = int.Parse (tmp [0]);
-                       g.name = tmp [1];
-                       g.yIndex = int.Parse (tmp [2]);
-                       g.xLevel = int.Parse (tmp [3]);
-                       return g;
-               }
 
-       }
        public static class DebugLog
        {
                static Surface surf;
@@ -169,7 +125,8 @@ namespace Crow
                static void parseTree (GraphicObject go) {
                        if (go == null)
                                return;
-                               
+
+
                        go.yIndex = y++;
                        go.xLevel = level++;
 
@@ -195,52 +152,18 @@ namespace Crow
 
                        using (StreamWriter s = new StreamWriter("debug.log")){
                                s.WriteLine ("[GraphicObjects]");
-                               for (int i=0; i<GraphicObject.GraphicObjects.Count; i++) {
-                                       GraphicObject g = GraphicObject.GraphicObjects [i];
-                                       s.WriteLine ("{0};{1};{2};{3}", i, g.GetType().Name, g.yIndex, g.xLevel);       
+                               lock (GraphicObject.GraphicObjects) {
+                                       GraphicObject.GraphicObjects = GraphicObject.GraphicObjects.OrderBy (o => o.yIndex).ToList();
+                                       for (int i = 0; i < GraphicObject.GraphicObjects.Count; i++) {
+                                               GraphicObject g = GraphicObject.GraphicObjects [i];
+                                               s.WriteLine ("{0};{1};{2};{3}", i, g.GetType ().Name, g.yIndex, g.xLevel);      
+                                       }
                                }
                                s.WriteLine ("[Events]");
 
                                foreach (DbgEvent e in events)
                                        s.WriteLine (e.ToString ());
                        }
-                       /*
-                       List<GraphicObject> drawn = new List<GraphicObject> ();
-
-
-                       ctx.MoveTo (0.5 + xPenStart, 0.0);
-                       ctx.LineTo (0.5 + xPenStart, bounds.Height);
-                       ctx.LineWidth = 1.0;
-                       ctx.SetSourceColor (Crow.Color.Black);
-                       ctx.Stroke ();
-
-
-                       List<LayoutingQueueItem> lqis = new List<LayoutingQueueItem>();
-
-                       foreach (LayoutingQueueItem lqi in lqis) {
-                               GraphicObject go = lqi.graphicObject;
-                               if (go.yIndex == 0)
-                                       continue;
-                               double penX = 0.0, penY = go.yIndex * ySpacing;
-
-                               if (!drawn.Contains (go)) {
-                                       penX = go.xLevel * 5.0;
-
-                                       ctx.MoveTo (penX, penY);
-                                       ctx.SetSourceColor (Crow.Color.Black);
-
-                                       ctx.ShowText (go.GetType ().ToString ());
-                                       drawn.Add (go);
-                               }
-
-                               //penX = xPenStart + xResolution * lqi.begin;
-                               //ctx.Rectangle (penX, penY, Math.Max(1.0, (lqi.end - lqi.begin) * xResolution), ySpacing);
-
-
-                               ctx.Fill ();
-                       }
-                       surf.WriteToPng ("debug.png");
-                       */
                }
        }
 }