From 1e6d9bbb33f98f85e89cd3321ff9c29ffa4fccb0 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Bruyère Date: Thu, 29 Nov 2018 16:30:15 +0100 Subject: debug log view --- src/debug/DbgLogViewer.cs | 579 +++++++++++++++------ src/debug/DebugLogViewer.cs | 1177 +++++++++++++++++++++++++++++++++++++++++++ src/debug/DebugLogger.cs | 105 +--- 3 files changed, 1608 insertions(+), 253 deletions(-) create mode 100644 src/debug/DebugLogViewer.cs (limited to 'src/debug') diff --git a/src/debug/DbgLogViewer.cs b/src/debug/DbgLogViewer.cs index eca502fb..7c8a1bdf 100644 --- a/src/debug/DbgLogViewer.cs +++ b/src/debug/DbgLogViewer.cs @@ -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 events = new List(); + + 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 events;//global events + List 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 events; - List 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(); + objs = new List (); + 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(); - objs = new List (); + 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 index 00000000..5b6ca360 --- /dev/null +++ b/src/debug/DebugLogViewer.cs @@ -0,0 +1,1177 @@ +// +// ScrollingTextBox.cs +// +// Author: +// Jean-Philippe Bruyère +// +// Copyright (c) 2013-2017 Jean-Philippe Bruyère +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Xml.Serialization; +using System.ComponentModel; +using System.Collections; +using Cairo; +using System.Text; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using System.Linq; +using System.Diagnostics; +using System.IO; +using System.Threading; + +namespace Crow +{ + /// + /// Scrolling text box optimized for monospace fonts, for coding + /// + 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 PrintedLines;//list of lines visible in the Editor depending on scrolling and folding + + Dictionary formatting = new Dictionary(); + Dictionary parsing = new Dictionary(); + + 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 (); + } + + /// + /// Updates visible line in widget, adapt max scroll y and updatePrintedLines + /// + void updateVisibleLines(){ + visibleLines = (int)Math.Floor ((double)ClientRectangle.Height / (fe.Ascent+fe.Descent)); + NotifyValueChanged ("VisibleLines", visibleLines); + updateMaxScrollY (); + 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 (); + 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 ("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 (); + } + + /// + /// Current editor line, when set, update buffer.CurrentLine + /// + int PrintedCurrentLine { + get { return printedCurrentLine;} + set { + if (value < 0) { + ScrollY += value; + printedCurrentLine = 0; + } else if (PrintedLines.Count < visibleLines && value >= PrintedLines.Count) { + printedCurrentLine = PrintedLines.Count - 1; + }else if (value >= visibleLines) { + ScrollY += value - visibleLines + 1; + printedCurrentLine = visibleLines - 1; + }else + printedCurrentLine = value; + //Debug.WriteLine ("printed current line:" + printedCurrentLine.ToString ()); + //update position in buffer + buffer.CurrentLine = buffer.IndexOf (PrintedLines[printedCurrentLine]); + } + } + int getTabulatedColumn (int col, int line) { + return buffer [line].Content.Substring (0, col).Replace ("\t", new String (' ', Interface.TabSize)).Length; + } + int getTabulatedColumn (Point pos) { + return getTabulatedColumn (pos.X,pos.Y); + } + /// + /// Moves cursor one char to the left, move up if cursor reaches start of line + /// + /// true if move succeed + public bool MoveLeft(){ + if (buffer.CurrentColumn == 0) { + if (printedCurrentLine == 0) + return false; + PrintedCurrentLine--; + buffer.CurrentColumn = int.MaxValue; + } else + buffer.CurrentColumn--; + return true; + } + /// + /// Moves cursor one char to the right, move down if cursor reaches end of line + /// + /// true if move succeed + public bool MoveRight(){ + if (buffer.CurrentColumn >= buffer.CurrentCodeLine.Length) { + if (PrintedCurrentLine == buffer.UnfoldedLines - 1) + return false; + buffer.CurrentColumn = 0; + PrintedCurrentLine++; + } else + buffer.CurrentColumn++; + return true; + } + + #region Drawing + void drawLine(Context gr, Rectangle cb, int i) { + CodeLine cl = PrintedLines[i]; + int lineIndex = buffer.IndexOf(cl); + + double y = cb.Y + (fe.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 diff --git a/src/debug/DebugLogger.cs b/src/debug/DebugLogger.cs index 161d742c..18e4ae19 100644 --- a/src/debug/DebugLogger.cs +++ b/src/debug/DebugLogger.cs @@ -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; - } - } + /// + /// debug events as recorded, another class is used in the viewer + /// 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 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 drawn = new List (); - - - 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 lqis = new List(); - - 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"); - */ } } } -- cgit v1.3.1-4-g156e