Browse Source

Cleaning up debugger tree model and tooltips

newNRvisualizers
David Srbecký 14 years ago
parent
commit
fb31be5e4c
  1. 7
      src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.addin
  2. 16
      src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj
  3. 2
      src/AddIns/Debugger/Debugger.AddIn/Options/DebuggingOptions.cs
  4. 4
      src/AddIns/Debugger/Debugger.AddIn/Pads/Commands/WatchPadCommands.cs
  5. 2
      src/AddIns/Debugger/Debugger.AddIn/Pads/Controls/TreeNodeWrapper.cs
  6. 8
      src/AddIns/Debugger/Debugger.AddIn/Pads/Controls/WatchList.xaml
  7. 6
      src/AddIns/Debugger/Debugger.AddIn/Pads/Controls/WatchList.xaml.cs
  8. 2
      src/AddIns/Debugger/Debugger.AddIn/Pads/LocalVarPad.cs
  9. 2
      src/AddIns/Debugger/Debugger.AddIn/Pads/WatchPad.cs
  10. 82
      src/AddIns/Debugger/Debugger.AddIn/Service/RemotingConfigurationHelpper.cs
  11. 16
      src/AddIns/Debugger/Debugger.AddIn/Service/WindowsDebugger.cs
  12. 102
      src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerPopup.cs
  13. 334
      src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerTooltipControl.xaml
  14. 427
      src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerTooltipControl.xaml.cs
  15. 113
      src/AddIns/Debugger/Debugger.AddIn/Tooltips/LazyItemsControl.cs
  16. 29
      src/AddIns/Debugger/Debugger.AddIn/Tooltips/PinCloseControl.xaml
  17. 74
      src/AddIns/Debugger/Debugger.AddIn/Tooltips/PinCloseControl.xaml.cs
  18. 363
      src/AddIns/Debugger/Debugger.AddIn/Tooltips/PinControlsDictionary.xaml
  19. 240
      src/AddIns/Debugger/Debugger.AddIn/Tooltips/PinDebuggerControl.xaml
  20. 384
      src/AddIns/Debugger/Debugger.AddIn/Tooltips/PinDebuggerControl.xaml.cs
  21. 184
      src/AddIns/Debugger/Debugger.AddIn/Tooltips/PinLayer.cs
  22. 118
      src/AddIns/Debugger/Debugger.AddIn/Tooltips/PinningBinding.cs
  23. 53
      src/AddIns/Debugger/Debugger.AddIn/Tooltips/VirtualizingIEnumerable.cs
  24. 114
      src/AddIns/Debugger/Debugger.AddIn/TreeModel/TreeNode.cs
  25. 517
      src/AddIns/Debugger/Debugger.AddIn/TreeModel/ValueNode.cs
  26. 0
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Common/IVisualizerCommand.cs
  27. 2
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/TreeModel/ContentPropertyNode.cs
  28. 3
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/GridVisualizer/GridVisualizerWindow.xaml.cs
  29. 13
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Utils/DebuggerHelpers.cs
  30. 2
      src/AddIns/Debugger/Debugger.Core/Eval.cs
  31. 11
      src/AddIns/Debugger/Debugger.Core/MetaData/DebugLocalVariableInfo.cs
  32. 5
      src/AddIns/Debugger/Debugger.Core/MetaData/DebugMethodInfo.cs
  33. 5
      src/AddIns/Debugger/Debugger.Core/MetaData/DebugParameterInfo.cs
  34. 3
      src/AddIns/Debugger/Debugger.Core/StackFrame.cs
  35. 79
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorView.cs
  36. 72
      src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/CodeView.cs
  37. 5
      src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
  38. 42
      src/Main/Base/Project/Src/Bookmarks/BookmarkConverter.cs
  39. 25
      src/Main/Base/Project/Src/Editor/ITooltip.cs
  40. 20
      src/Main/Base/Project/Src/Services/Debugger/Tooltips/IPinDebuggerControl.cs
  41. 37
      src/Main/Base/Project/Src/Services/Debugger/Tooltips/ITreeNode.cs
  42. 102
      src/Main/Base/Project/Src/Services/Debugger/Tooltips/PinBookmark.cs

7
src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.addin

@ -16,13 +16,6 @@ @@ -16,13 +16,6 @@
<Import assembly="Debugger.Core.dll"/>
</Runtime>
<Path name="/SharpDevelop/Workbench/LanguageBindings">
<LanguageBinding
id="Pin"
class="Debugger.AddIn.Tooltips.PinningBinding"
extensions="" />
</Path>
<Path name="/SharpDevelop/Services/DebuggerService/Debugger">
<Debugger id="DefaultDebugger"
supportsStepping = "true"

16
src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj

@ -176,20 +176,9 @@ @@ -176,20 +176,9 @@
<DependentUpon>EditBreakpointScriptWindow.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="Tooltips\DebuggerPopup.cs" />
<Compile Include="Tooltips\DebuggerTooltipControl.xaml.cs">
<DependentUpon>DebuggerTooltipControl.xaml</DependentUpon>
</Compile>
<Compile Include="Tooltips\LazyItemsControl.cs" />
<Compile Include="Tooltips\PinCloseControl.xaml.cs">
<DependentUpon>PinCloseControl.xaml</DependentUpon>
</Compile>
<Compile Include="Tooltips\PinDebuggerControl.xaml.cs">
<DependentUpon>PinDebuggerControl.xaml</DependentUpon>
</Compile>
<Compile Include="Tooltips\PinLayer.cs" />
<Compile Include="Tooltips\PinningBinding.cs" />
<Compile Include="Tooltips\VirtualizingIEnumerable.cs" />
<Compile Include="Tooltips\VisualizerPicker.cs">
<DependentUpon>VisualizerPicker.xaml</DependentUpon>
</Compile>
@ -205,6 +194,7 @@ @@ -205,6 +194,7 @@
<Compile Include="Visualizers\Commands\XmlVisualizerCommand.cs" />
<Compile Include="Visualizers\Common\DebuggerVisualizerException.cs" />
<Compile Include="Visualizers\Common\IEvaluate.cs" />
<Compile Include="Visualizers\Common\IVisualizerCommand.cs" />
<Compile Include="Visualizers\Common\ObjectProperty.cs" />
<Compile Include="Visualizers\Common\ObjectPropertyComparer.cs" />
<Compile Include="Visualizers\Common\VirtualizingCollection.cs" />
@ -267,7 +257,6 @@ @@ -267,7 +257,6 @@
<EmbeddedResource Include="Service\DebuggeeExceptionForm.resx">
<DependentUpon>DebuggeeExceptionForm.cs</DependentUpon>
</EmbeddedResource>
<Compile Include="Service\RemotingConfigurationHelpper.cs" />
<Compile Include="Service\RunToCursorCommand.cs" />
<Compile Include="Service\WindowsDebugger.cs" />
<EmbeddedResource Include="Options\DebuggingSymbolsPanel.resx">
@ -372,9 +361,6 @@ @@ -372,9 +361,6 @@
<Page Include="Pads\WatchInputBox.xaml" />
<Page Include="Service\EditBreakpointScriptWindow.xaml" />
<Page Include="Tooltips\DebuggerTooltipControl.xaml" />
<Page Include="Tooltips\PinCloseControl.xaml" />
<Page Include="Tooltips\PinControlsDictionary.xaml" />
<Page Include="Tooltips\PinDebuggerControl.xaml" />
<Page Include="Tooltips\VisualizerPicker.xaml" />
<Page Include="Visualizers\Graph\Drawing\NodeControlResources.xaml" />
<Page Include="Visualizers\Graph\Drawing\PositionedGraphNodeControl.xaml" />

2
src/AddIns/Debugger/Debugger.AddIn/Options/DebuggingOptions.cs

@ -10,7 +10,7 @@ using ICSharpCode.Core; @@ -10,7 +10,7 @@ using ICSharpCode.Core;
namespace ICSharpCode.SharpDevelop.Services
{
public enum ShowIntegersAs { Auto, Decimal, Hexadecimal };
public enum ShowIntegersAs { Decimal, Hexadecimal, Both, Auto };
[Serializable]
public class DebuggingOptions: Options

4
src/AddIns/Debugger/Debugger.AddIn/Pads/Commands/WatchPadCommands.cs

@ -94,8 +94,8 @@ namespace Debugger.AddIn @@ -94,8 +94,8 @@ namespace Debugger.AddIn
if (this.Owner is WatchPad) {
WatchPad pad = (WatchPad)this.Owner;
var node = pad.WatchList.SelectedNode;
if (node != null && node.Node is ExpressionNode) {
string text = ((ExpressionNode)node.Node).FullText;
if (node != null && node.Node is ValueNode) {
string text = ((ValueNode)node.Node).FullText;
ClipboardWrapper.SetText(text);
}
}

2
src/AddIns/Debugger/Debugger.AddIn/Pads/Controls/TreeNodeWrapper.cs

@ -32,7 +32,7 @@ namespace Debugger.AddIn.Pads.Controls @@ -32,7 +32,7 @@ namespace Debugger.AddIn.Pads.Controls
}
public override object Icon {
get { return this.Node.ImageSource; }
get { return this.Node.Image.ImageSource; }
}
public override bool ShowExpander {

8
src/AddIns/Debugger/Debugger.AddIn/Pads/Controls/WatchList.xaml

@ -21,7 +21,7 @@ @@ -21,7 +21,7 @@
<Setter Property="Visibility">
<Setter.Value>
<MultiBinding Converter="{StaticResource boolToVisibility}" ConverterParameter="True">
<Binding Path="Node.CanSetText" />
<Binding Path="Node.CanSetValue" />
<Binding Path="IsSelected" />
</MultiBinding>
</Setter.Value>
@ -32,7 +32,7 @@ @@ -32,7 +32,7 @@
<Setter Property="Visibility">
<Setter.Value>
<MultiBinding Converter="{StaticResource boolToVisibility}" ConverterParameter="False">
<Binding Path="Node.CanSetText" />
<Binding Path="Node.CanSetValue" />
<Binding Path="IsSelected" />
</MultiBinding>
</Setter.Value>
@ -41,8 +41,8 @@ @@ -41,8 +41,8 @@
<!-- Value column -->
<DataTemplate x:Key="CellTemplate_Value">
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" MinWidth="200" Text="{Binding Node.Text}" Style="{StaticResource TextBlockValueStyle}" />
<TextBox Text="{Binding Node.Text, Mode=OneWay}" Style="{StaticResource TextBoxValueStyle}" KeyUp="OnValueTextBoxKeyUp" />
<TextBlock VerticalAlignment="Center" MinWidth="200" Text="{Binding Node.Value}" Style="{StaticResource TextBlockValueStyle}" />
<TextBox Text="{Binding Node.Value, Mode=OneWay}" Style="{StaticResource TextBoxValueStyle}" KeyUp="OnValueTextBoxKeyUp" />
</StackPanel>
</DataTemplate>
<!-- Type column -->

6
src/AddIns/Debugger/Debugger.AddIn/Pads/Controls/WatchList.xaml.cs

@ -53,9 +53,9 @@ namespace Debugger.AddIn.Pads.Controls @@ -53,9 +53,9 @@ namespace Debugger.AddIn.Pads.Controls
}
if (e.Key == Key.Enter) {
if(SelectedNode.Node is ExpressionNode) {
var node = (ExpressionNode)SelectedNode.Node;
node.SetText(((TextBox)sender).Text);
if(SelectedNode.Node is ValueNode) {
var node = (ValueNode)SelectedNode.Node;
node.Value = ((TextBox)sender).Text;
}
}
if (e.Key == Key.Enter || e.Key == Key.Escape) {

2
src/AddIns/Debugger/Debugger.AddIn/Pads/LocalVarPad.cs

@ -71,7 +71,7 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads @@ -71,7 +71,7 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
debuggedProcess.EnqueueForEach(
Dispatcher.CurrentDispatcher,
Utils.GetLocalVariableNodes(frame).ToList(),
ValueNode.GetLocalVariables().ToList(),
n => localVarList.WatchItems.Add(n.ToSharpTreeNode())
);
} catch (Exception ex) {

2
src/AddIns/Debugger/Debugger.AddIn/Pads/WatchPad.cs

@ -192,7 +192,7 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads @@ -192,7 +192,7 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
LoggingService.Info("Evaluating: " + (string.IsNullOrEmpty(node.Node.Name) ? "is null or empty!" : node.Node.Name));
//Value val = ExpressionEvaluator.Evaluate(nod.Name, nod.Language, debuggedProcess.SelectedStackFrame);
ExpressionNode valNode = new ExpressionNode(null, node.Node.Name, () => debugger.GetExpression(node.Node.Name).Evaluate(debuggedProcess));
ValueNode valNode = new ValueNode(null, node.Node.Name, () => debugger.GetExpression(node.Node.Name).Evaluate(debuggedProcess));
return valNode.ToSharpTreeNode();
} catch (GetValueException) {
string error = String.Format(StringParser.Parse("${res:MainWindow.Windows.Debug.Watch.InvalidExpression}"), node.Node.Name);

82
src/AddIns/Debugger/Debugger.AddIn/Service/RemotingConfigurationHelpper.cs

@ -1,82 +0,0 @@ @@ -1,82 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
using System;
using System.IO;
using System.Reflection;
using System.Runtime.Remoting;
using System.Security.Policy;
namespace ICSharpCode.SharpDevelop.Services
{
[Serializable]
class RemotingConfigurationHelpper
{
public string path;
public RemotingConfigurationHelpper(string path)
{
this.path = path;
}
public static string GetLoadedAssemblyPath(string assemblyName)
{
string path = null;
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) {
try {
string fullFilename = assembly.Location;
if (Path.GetFileName(fullFilename).Equals(assemblyName, StringComparison.OrdinalIgnoreCase)) {
path = Path.GetDirectoryName(fullFilename);
break;
}
} catch (NotSupportedException) {
// assembly.Location throws NotSupportedException for assemblies emitted using
// Reflection.Emit by custom controls used in the forms designer
}
}
if (path == null) {
throw new Exception("Assembly " + assemblyName + " is not loaded");
}
return path;
}
public void Configure()
{
AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolve;
RemotingConfiguration.Configure(Path.Combine(path, "Client.config"), false);
string baseDir = Directory.GetDirectoryRoot(AppDomain.CurrentDomain.BaseDirectory);
string relDirs = AppDomain.CurrentDomain.BaseDirectory + ";" + path;
AppDomain serverAppDomain = AppDomain.CreateDomain("Debugging server",
new Evidence(AppDomain.CurrentDomain.Evidence),
baseDir,
relDirs,
AppDomain.CurrentDomain.ShadowCopyFiles);
serverAppDomain.DoCallBack(new CrossAppDomainDelegate(ConfigureServer));
}
private void ConfigureServer()
{
AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolve;
RemotingConfiguration.Configure(Path.Combine(path, "Server.config"), false);
}
Assembly AssemblyResolve(object sender, ResolveEventArgs args)
{
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) {
try {
string fullFilename = assembly.Location;
if (Path.GetFileNameWithoutExtension(fullFilename).Equals(args.Name, StringComparison.OrdinalIgnoreCase) ||
assembly.FullName == args.Name) {
return assembly;
}
} catch (NotSupportedException) {
// assembly.Location throws NotSupportedException for assemblies emitted using
// Reflection.Emit by custom controls used in the forms designer
}
}
return null;
}
}
}

16
src/AddIns/Debugger/Debugger.AddIn/Service/WindowsDebugger.cs

@ -28,6 +28,7 @@ using ICSharpCode.SharpDevelop.Gui.OptionPanels; @@ -28,6 +28,7 @@ using ICSharpCode.SharpDevelop.Gui.OptionPanels;
using ICSharpCode.SharpDevelop.Project;
using Mono.Cecil;
using Process = Debugger.Process;
using TreeNode = Debugger.AddIn.TreeModel.TreeNode;
namespace ICSharpCode.SharpDevelop.Services
{
@ -39,7 +40,6 @@ namespace ICSharpCode.SharpDevelop.Services @@ -39,7 +40,6 @@ namespace ICSharpCode.SharpDevelop.Services
Cancel = 2
}
bool useRemotingForThreadInterop = false;
bool attached;
NDebugger debugger;
@ -491,18 +491,18 @@ namespace ICSharpCode.SharpDevelop.Services @@ -491,18 +491,18 @@ namespace ICSharpCode.SharpDevelop.Services
{
try {
var tooltipExpression = GetExpression(variableName);
ExpressionNode expressionNode = new ExpressionNode("Icons.16x16.Local", variableName, () => tooltipExpression.Evaluate(this.DebuggedProcess));
return new DebuggerTooltipControl(logicalPosition, expressionNode) { ShowPins = debuggedProcess.GetCurrentExecutingFrame().HasSymbols };
var valueNode = new ValueNode("Icons.16x16.Local", variableName, () => tooltipExpression.Evaluate(this.DebuggedProcess));
return new DebuggerTooltipControl(new TreeNode[] { valueNode });
} catch (System.Exception ex) {
LoggingService.Error("Error on GetTooltipControl: " + ex.Message);
return null;
}
}
public ITreeNode GetNode(string variable, string currentImageName = null)
public Debugger.AddIn.TreeModel.TreeNode GetNode(string variable, string currentImageName = null)
{
try {
return new ExpressionNode(currentImageName ?? "Icons.16x16.Local", variable, () => GetExpression(variable).Evaluate(this.DebuggedProcess));
return new ValueNode(currentImageName ?? "Icons.16x16.Local", variable, () => GetExpression(variable).Evaluate(this.DebuggedProcess));
} catch (GetValueException) {
return null;
}
@ -539,12 +539,6 @@ namespace ICSharpCode.SharpDevelop.Services @@ -539,12 +539,6 @@ namespace ICSharpCode.SharpDevelop.Services
public void InitializeService()
{
if (useRemotingForThreadInterop) {
// This needs to be called before instance of NDebugger is created
string path = RemotingConfigurationHelpper.GetLoadedAssemblyPath("Debugger.Core.dll");
new RemotingConfigurationHelpper(path).Configure();
}
// get decompiler service
var items = AddInTree.BuildItems<IDebuggerDecompilerService>("/SharpDevelop/Services/DebuggerDecompilerService", null, false);
if (items.Count > 0)

102
src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerPopup.cs

@ -1,102 +0,0 @@ @@ -1,102 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using ICSharpCode.Core;
using ICSharpCode.NRefactory;
using ICSharpCode.SharpDevelop.Debugging;
using ICSharpCode.SharpDevelop.Gui;
namespace Debugger.AddIn.Tooltips
{
/// <summary>
/// Popup containing <see cref="DebuggerTooltipControl"></see>.
/// </summary>
public class DebuggerPopup : Popup
{
internal DebuggerTooltipControl innerControl;
public DebuggerPopup(DebuggerTooltipControl parentControl, Location logicalPosition, bool showPins = true)
{
this.innerControl = new DebuggerTooltipControl(parentControl, logicalPosition) { ShowPins = showPins };
this.innerControl.containingPopup = this;
this.Child = this.innerControl;
this.IsLeaf = false;
//this.KeyDown += new KeyEventHandler(DebuggerPopup_KeyDown);
//this.innerControl.Focusable = true;
//Keyboard.Focus(this.innerControl);
//this.AllowsTransparency = true;
//this.PopupAnimation = PopupAnimation.Slide;
}
// attempt to propagate shortcuts to main windows when Popup is focusable (needed for keyboard scrolling + editing)
/*void DebuggerPopup_KeyDown(object sender, KeyEventArgs e)
{
LoggingService.Debug("Unhandled popup key down: " + e.Key);
RaiseEventPair(WorkbenchSingleton.MainWindow, PreviewKeyDownEvent, KeyDownEvent,
new KeyEventArgs(e.KeyboardDevice, e.InputSource, e.Timestamp, e.Key));
}
// copied from CompletionWindowBase
static bool RaiseEventPair(UIElement target, RoutedEvent previewEvent, RoutedEvent @event, RoutedEventArgs args)
{
if (target == null)
throw new ArgumentNullException("target");
if (previewEvent == null)
throw new ArgumentNullException("previewEvent");
if (@event == null)
throw new ArgumentNullException("event");
if (args == null)
throw new ArgumentNullException("args");
args.RoutedEvent = previewEvent;
target.RaiseEvent(args);
args.RoutedEvent = @event;
target.RaiseEvent(args);
return args.Handled;
}*/
public IEnumerable<ITreeNode> ItemsSource
{
get { return this.innerControl.ItemsSource; }
set { this.innerControl.SetItemsSource(value); }
}
private bool isLeaf;
public bool IsLeaf
{
get { return isLeaf; }
set
{
isLeaf = value;
// leaf popup closes on lost focus
this.StaysOpen = !isLeaf;
}
}
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
if (isLeaf) {
this.innerControl.CloseOnLostFocus();
}
}
public void Open()
{
this.IsOpen = true;
}
public void CloseSelfAndChildren()
{
this.innerControl.CloseChildPopups();
this.IsOpen = false;
}
}
}

334
src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerTooltipControl.xaml

@ -1,29 +1,123 @@ @@ -1,29 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<UserControl
<Popup
x:Class="Debugger.AddIn.Tooltips.DebuggerTooltipControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:aero="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
xmlns:debugging="clr-namespace:Debugger.AddIn.Tooltips" xmlns:core="http://icsharpcode.net/sharpdevelop/core" xmlns:localControls="clr-namespace:Debugger.AddIn.Tooltips"
Background="Transparent">
<UserControl.Resources>
xmlns:debugging="clr-namespace:Debugger.AddIn.Tooltips"
AllowsTransparency="True"
>
<Popup.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary
Source="VisualizerPicker.xaml" />
<ResourceDictionary
Source="PinControlsDictionary.xaml" />
<ResourceDictionary Source="VisualizerPicker.xaml" />
</ResourceDictionary.MergedDictionaries>
<LinearGradientBrush x:Key="OrangeBrushKey" EndPoint="0,1" StartPoint="0,0">
<LinearGradientBrush.GradientStops>
<GradientStop Offset="0" Color="White" />
<GradientStop Offset="0.5" Color="Orange" />
<GradientStop Offset="1" Color="Orange" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="OrangePressedBrushKey" EndPoint="0,1" StartPoint="0,0">
<LinearGradientBrush.GradientStops>
<GradientStop Offset="1" Color="White" />
<GradientStop Offset="0.5" Color="Orange" />
<GradientStop Offset="0" Color="Orange" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="SilverBrushKey" EndPoint="0,1" StartPoint="0,0">
<LinearGradientBrush.GradientStops>
<GradientStop Offset="0" Color="White" />
<GradientStop Offset="0.5" Color="LightGray" />
<GradientStop Offset="1" Color="LightGray" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="SilverPressedBrushKey" EndPoint="0,1" StartPoint="0,0">
<LinearGradientBrush.GradientStops>
<GradientStop Offset="1" Color="White" />
<GradientStop Offset="0.5" Color="LightGray" />
<GradientStop Offset="0" Color="LightGray" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
<ControlTemplate x:Key="CloseButtonTemplate" TargetType="Button">
<Border Width="16" Height="16" Name="TheBorder" CornerRadius="2,2,0,0" BorderThickness="1" BorderBrush="Black" Background="{StaticResource SilverPressedBrushKey}">
<Canvas>
<Line X1="3.5" X2="10.5" Y1="3.5" Y2="10.5" Stroke="Black" StrokeThickness="2"/>
<Line X1="3.5" X2="10.5" Y1="10.5" Y2="3.5" Stroke="Black" StrokeThickness="2"/>
</Canvas>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="UIElement.IsMouseOver" Value="true">
<Setter TargetName="TheBorder" Property="Background" Value="{StaticResource OrangeBrushKey}"/>
<Setter TargetName="TheBorder" Property="BorderBrush" Value="Silver"/>
</Trigger>
<Trigger Property="ButtonBase.IsPressed" Value="True">
<Setter TargetName="TheBorder" Property="Background" Value="{StaticResource OrangePressedBrushKey}"/>
<Setter TargetName="TheBorder" Property="BorderBrush" Value="Silver"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<ControlTemplate x:Key="PinButtonTemplate" TargetType="Button">
<Border Width="16" Height="16" Name="TheBorder" CornerRadius="3" BorderThickness="1" BorderBrush="Black" Background="{StaticResource SilverPressedBrushKey}">
<Canvas Name="TheCanvas">
<Line X1="4" X2="10" Y1="2" Y2="2" Stroke="Black" StrokeThickness="1"/>
<Line X1="9" X2="9" Y1="2" Y2="8" Stroke="Black" StrokeThickness="1"/>
<Line X1="2" X2="12" Y1="8" Y2="8" Stroke="Black" StrokeThickness="1"/>
<Rectangle Fill="Black" Width="2" Height="5" Canvas.Left="4" Canvas.Top="3"/>
<Line X1="7" X2="7" Y1="9" Y2="12" Stroke="Black" StrokeThickness="1"/>
</Canvas>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="UIElement.IsMouseOver" Value="true">
<Setter TargetName="TheBorder" Property="Background" Value="{StaticResource OrangeBrushKey}"/>
<Setter TargetName="TheBorder" Property="BorderBrush" Value="Silver"/>
</Trigger>
<Trigger Property="ButtonBase.IsPressed" Value="True">
<Setter TargetName="TheBorder" Property="Background" Value="{StaticResource OrangePressedBrushKey}"/>
<Setter TargetName="TheBorder" Property="BorderBrush" Value="Silver"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<SolidColorBrush x:Key="MouseOverPinBrush" Color="Black" />
<Style TargetType="{x:Type TextBox}" x:Key="TextStyle">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="FontFamily" Value="Khmer UI" />
<Setter Property="FontSize" Value="12" />
<Setter Property="KeyboardNavigation.TabNavigation" Value="None" />
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBoxBase}">
<Border Name="Border" Background="Transparent" BorderBrush="Transparent" BorderThickness="0">
<ScrollViewer Margin="0" Name="PART_ContentHost" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="PinThumbStyle" TargetType="Thumb">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Thumb">
<StackPanel x:Name="Container"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
</UserControl.Resources>
<StackPanel
Orientation="Vertical">
<RepeatButton
Name="btnUp"
Focusable="False"
Style="{StaticResource upButtonStyle}"
Content="^"
Click="BtnUp_Click"></RepeatButton>
</Popup.Resources>
<StackPanel Orientation="Horizontal">
<DataGrid
VerticalScrollBarVisibility="Disabled"
HorizontalScrollBarVisibility="Disabled"
@ -32,7 +126,6 @@ @@ -32,7 +126,6 @@
MaxHeight="202"
SelectionMode="Single"
SelectionUnit="FullRow"
ItemsSource="{Binding}"
Name="dataGrid"
AutoGenerateColumns="False"
CanUserAddRows="False"
@ -40,90 +133,89 @@ @@ -40,90 +133,89 @@
BorderBrush="Gray"
BorderThickness="1">
<DataGrid.Background>
<!-- Control backgound -->
<LinearGradientBrush
StartPoint="0,-0.03"
EndPoint="0,1">
<GradientStop
Color="White" />
<GradientStop
Color="#FFFAFCFE"
Offset="0.983" />
<GradientStop
Color="#FFECF7FC"
Offset="0.07" />
<GradientStop
Color="#FFEEF7FA"
Offset="0.436" />
<LinearGradientBrush StartPoint="0,-0.03" EndPoint="0,1">
<GradientStop Color="White"/>
<GradientStop Color="#FFFAFCFE" Offset="0.983"/>
<GradientStop Color="#FFECF7FC" Offset="0.07"/>
<GradientStop Color="#FFEEF7FA" Offset="0.436"/>
</LinearGradientBrush>
</DataGrid.Background>
<DataGrid.RowStyle>
<Style
TargetType="{x:Type DataGridRow}">
<Setter
Property="Background"
Value="Transparent"></Setter>
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="Background" Value="Transparent"></Setter>
<Style.Triggers>
<Trigger
Property="IsMouseOver"
Value="True">
<Setter
Property="Background"
Value="#FFE2F6FE" />
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#FFE2F6FE" />
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
<DataGrid.CellStyle>
<Style
TargetType="{x:Type DataGridCell}">
<Setter
Property="Focusable"
Value="false" />
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="Focusable" Value="false" />
<!-- Focusable=true blocks shortcuts if cell is focused -->
<Setter
Property="BorderThickness"
Value="0" />
<Setter Property="BorderThickness" Value="0" />
<Style.Triggers>
<Trigger
Property="IsSelected"
Value="True">
<Trigger Property="IsSelected" Value="True">
<!-- disable selection highlight -->
<Setter
Property="Foreground"
Value="Black" />
<Setter
Property="Background"
Value="{x:Null}" />
<Setter Property="Foreground" Value="Black" />
<Setter Property="Background" Value="{x:Null}" />
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.CellStyle>
<DataGrid.Columns>
<DataGridTemplateColumn>
<!-- "Plus" expander -->
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid
Background="White">
<StackPanel
VerticalAlignment="Center">
<Grid Background="White">
<StackPanel VerticalAlignment="Center">
<ToggleButton
x:Name="btnExpander"
Style="{StaticResource ExpandCollapseToggleStyle}"
Click="btnExpander_Click"
Click="Expand_Click"
Padding="0"
Margin="0" />
Margin="0"
Focusable="False"
Width="19"
Height="13"
>
<ToggleButton.Template>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border Width="19" Height="13" Background="Transparent">
<Border Width="9"
Height="9"
BorderThickness="1"
BorderBrush="#FF7898B5"
CornerRadius="1"
SnapsToDevicePixels="true">
<Border.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
<LinearGradientBrush.GradientStops>
<GradientStop Color="White" Offset=".2"/>
<GradientStop Color="#FFC0B7A6" Offset="1"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Border.Background>
<Path x:Name="ExpandPath"
Margin="1,1,1,1"
Fill="Black"
Data="M 0 2 L 0 3 L 2 3 L 2 5 L 3 5 L 3 3 L 5 3 L 5 2 L 3 2 L 3 0 L 2 0 L 2 2 Z"/>
</Border>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Data" TargetName="ExpandPath" Value="M 0 2 L 0 3 L 5 3 L 5 2 Z"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</ToggleButton.Template>
</ToggleButton>
</StackPanel>
</Grid>
<DataTemplate.Triggers>
<DataTrigger
Binding="{Binding Path=HasChildNodes}"
Value="False">
<Setter
TargetName="btnExpander"
Property="Visibility"
Value="Collapsed" />
<DataTrigger Binding="{Binding HasChildren}" Value="False">
<Setter TargetName="btnExpander" Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
@ -133,24 +225,16 @@ @@ -133,24 +225,16 @@
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image
Source="{Binding ImageSource}"></Image>
<Image Source="{Binding Image.ImageSource}"></Image>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn
MinWidth="20"
Header="Name">
<!-- Name -->
<DataGridTemplateColumn MinWidth="20" Header="Name">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Border
BorderBrush="#FFDDDDDD"
BorderThickness="0 0 1 0">
<TextBlock
Style="{StaticResource TextBlockStyle}"
Text="{Binding Path=Name, Mode=OneWay}"
VerticalAlignment="Top"></TextBlock>
<Border BorderBrush="#FFDDDDDD" BorderThickness="0 0 1 0">
<TextBlock Margin="6 0" Text="{Binding Path=Name, Mode=OneWay}" VerticalAlignment="Top"></TextBlock>
</Border>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
@ -159,82 +243,48 @@ @@ -159,82 +243,48 @@
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<debugging:VisualizerPicker
Focusable="False"
x:Name="visPicker"
ItemsSource="{Binding Path=VisualizerCommands, Mode=OneWay}"
Margin="4 0 0 0"></debugging:VisualizerPicker>
<debugging:VisualizerPicker Focusable="False" x:Name="visPicker" ItemsSource="{Binding Path=VisualizerCommands, Mode=OneWay}" Margin="4 0 0 0"></debugging:VisualizerPicker>
<DataTemplate.Triggers>
<DataTrigger
Binding="{Binding Path=HasVisualizerCommands}"
Value="False">
<Setter
TargetName="visPicker"
Property="Visibility"
Value="Collapsed" />
<DataTrigger Binding="{Binding HasVisualizerCommands}" Value="False">
<Setter TargetName="visPicker" Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn
MinWidth="20"
Header="Text">
<!-- Text (value) -->
<DataGridTemplateColumn MinWidth="20" Header="Text">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox
Style="{StaticResource TextStyle}"
IsEnabled="{Binding CanSetText}"
KeyUp="TextBox_KeyUp"
IsEnabled="{Binding CanSetValue}"
ContextMenuService.ShowOnDisabled="True"
Text="{Binding Path=Text}">
Text="{Binding Value}"
>
<TextBox.ContextMenu>
<ContextMenu>
<MenuItem Header="{core:Localize XML.MainMenu.EditMenu.Copy}" DataContext="{Binding}" Click="CopyMenuItemClick">
<!-- <MenuItem Header="{core:Localize XML.MainMenu.EditMenu.Copy}" DataContext="{Binding}" Click="CopyMenuItemClick">
<MenuItem.Icon>
<Image Source="{core:GetBitmap Icons.16x16.CopyIcon}" />
</MenuItem.Icon>
</MenuItem>
</MenuItem>-->
</ContextMenu>
</TextBox.ContextMenu>
</TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn>
<!-- Pin -->
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ToggleButton
IsChecked="{Binding IsPinned}"
DataContext="{Binding}"
Visibility="Collapsed"
Name="PinButton"
VerticalAlignment="Center"
Checked="PinButton_Checked"
Unchecked="PinButton_Unchecked"
Template="{StaticResource PinTooltipButtonTemplate}" />
<DataTemplate.Triggers>
<DataTrigger
Binding="{Binding Path=IsMouseOver, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGridRow}}"
Value="True">
<Setter
TargetName="PinButton"
Property="Visibility"
Value="Visible" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<RepeatButton
Name="btnDown"
Focusable="False"
Style="{StaticResource downButtonStyle}"
Content="v"
Click="BtnDown_Click"></RepeatButton>
<!-- <StackPanel Opacity="0.2" Name="SideButtons" Margin="3">
<Button
Name="CloseButton"
Click="CloseButton_Click"
Template="{StaticResource CloseButtonTemplate}" />
<Button
Name="UnpinButton"
Template="{StaticResource PinButtonTemplate}" />
</StackPanel>-->
</StackPanel>
</UserControl>
</Popup>

427
src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerTooltipControl.xaml.cs

@ -3,417 +3,110 @@ @@ -3,417 +3,110 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Debugger.AddIn.TreeModel;
using ICSharpCode.Core;
using ICSharpCode.NRefactory;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Bookmarks;
using ICSharpCode.SharpDevelop.Debugging;
using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Gui;
namespace Debugger.AddIn.Tooltips
{
/// <summary>
/// Default Control used as content of SharpDevelop debugger tooltips.
/// </summary>
public partial class DebuggerTooltipControl : UserControl, ITooltip
public partial class DebuggerTooltipControl : Popup, ITooltip
{
private const double ChildPopupOpenXOffet = 16;
private const double ChildPopupOpenYOffet = 15;
private const int InitialItemsCount = 12;
private const int VisibleItemsCount = 11;
static Point ChildPopupOffset = new Point(16, 15);
private bool showPins = true;
private LazyItemsControl<ITreeNode> lazyGrid;
private IEnumerable<ITreeNode> itemsSource;
readonly Location logicalPosition;
public DebuggerTooltipControl ChildTooltip { get; private set; }
public IEnumerable<TreeNode> TreeNodes { get; set; }
public DebuggerTooltipControl(Location logicalPosition)
public DebuggerTooltipControl(IEnumerable<TreeNode> treeNodes)
{
this.logicalPosition = logicalPosition;
InitializeComponent();
Loaded += new RoutedEventHandler(OnLoaded);
}
public DebuggerTooltipControl(Location logicalPosition, ITreeNode node)
: this(logicalPosition, new ITreeNode[] { node })
{
}
public DebuggerTooltipControl(Location logicalPosition, IEnumerable<ITreeNode> nodes)
: this(logicalPosition)
{
this.itemsSource = nodes;
}
public DebuggerTooltipControl(DebuggerTooltipControl parentControl, Location logicalPosition)
: this(logicalPosition)
{
this.parentControl = parentControl;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
if (!showPins) {
dataGrid.Columns[5].Visibility = Visibility.Collapsed;
}
SetItemsSource(this.itemsSource);
}
public event RoutedEventHandler Closed;
protected void OnClosed()
{
if (this.Closed != null) {
this.Closed(this, new RoutedEventArgs());
}
}
public bool ShowPins {
get { return showPins; }
set { showPins = value; }
}
public IEnumerable<ITreeNode> ItemsSource {
get { return this.itemsSource; }
}
public void SetItemsSource(IEnumerable<ITreeNode> value) {
this.itemsSource = value;
this.lazyGrid = new LazyItemsControl<ITreeNode>(this.dataGrid, InitialItemsCount);
// HACK for updating the pins in tooltip
var observable = new List<ITreeNode>();
this.itemsSource.ForEach(item => observable.Add(item));
// verify if at the line of the root there's a pin bookmark
ITextEditor editor;
var viewContent = WorkbenchSingleton.Workbench.ActiveViewContent;
ITextEditorProvider provider = viewContent as ITextEditorProvider;
if (provider != null) {
editor = provider.TextEditor;
} else {
editor = viewContent.GetService(typeof(ITextEditor)) as ITextEditor;
}
if (editor != null) {
var pin = BookmarkManager.Bookmarks.Find(
b => b is PinBookmark &&
b.Location.Line == logicalPosition.Line &&
b.FileName == editor.FileName) as PinBookmark;
this.TreeNodes = treeNodes;
this.dataGrid.ItemsSource = treeNodes;
if (pin != null) {
observable.ForEach(item => { // TODO: find a way not to use "observable"
if (pin.ContainsNode(item))
item.IsPinned = true;
});
}
}
var source = new VirtualizingIEnumerable<ITreeNode>(observable);
lazyGrid.ItemsSource = source;
this.dataGrid.AddHandler(ScrollViewer.ScrollChangedEvent, new ScrollChangedEventHandler(handleScroll));
if (this.lazyGrid.ItemsSourceTotalCount != null) {
// hide up/down buttons if too few items
btnUp.Visibility = btnDown.Visibility =
this.lazyGrid.ItemsSourceTotalCount.Value <= VisibleItemsCount ? Visibility.Collapsed : Visibility.Visible;
}
}
//public Location LogicalPosition { get; set; }
/// <inheritdoc/>
public bool ShowAsPopup
{
get
{
return true;
}
}
/// <inheritdoc/>
public bool Close(bool mouseClick)
{
if (mouseClick || (!mouseClick && !isChildExpanded)) {
CloseChildPopups();
return true;
} else {
return false;
}
}
private DebuggerPopup childPopup { get; set; }
private DebuggerTooltipControl parentControl { get; set; }
internal DebuggerPopup containingPopup { get; set; }
bool isChildExpanded
{
get
{
return this.childPopup != null && this.childPopup.IsOpen;
}
}
private ToggleButton expandedButton;
/// <summary>
/// Closes the child popup of this control, if it exists.
/// </summary>
public void CloseChildPopups()
{
if (this.expandedButton != null) {
this.expandedButton.IsChecked = false;
this.expandedButton = null;
// nice simple example of indirect recursion
this.childPopup.CloseSelfAndChildren();
}
}
public void CloseOnLostFocus()
{
// when we close, parent becomes leaf
if (this.containingPopup != null) {
this.containingPopup.IsLeaf = true;
}
if (!this.IsMouseOver) {
if (this.containingPopup != null) {
this.containingPopup.IsOpen = false;
this.containingPopup.IsLeaf = false;
}
if (this.parentControl != null) {
this.parentControl.CloseOnLostFocus();
}
OnClosed();
} else {
// leaf closed because of click inside this control - stop the closing chain
if (this.expandedButton != null && !this.expandedButton.IsMouseOver) {
this.expandedButton.IsChecked = false;
this.expandedButton = null;
}
}
// Only the leaf of the tooltip has this set to false
// Therefore it will automatically close if something else gets focus
this.StaysOpen = false;
this.Placement = PlacementMode.Absolute;
}
private void btnExpander_Click(object sender, RoutedEventArgs e)
private void Expand_Click(object sender, RoutedEventArgs e)
{
var clickedButton = (ToggleButton)e.OriginalSource;
var clickedNode = (ITreeNode)clickedButton.DataContext;
// use device independent units, because child popup Left/Top are in independent units
Point buttonPos = clickedButton.PointToScreen(new Point(0, 0)).TransformFromDevice(clickedButton);
if (clickedButton.IsChecked.GetValueOrDefault(false)) {
CloseChildPopups();
this.expandedButton = clickedButton;
// open child Popup
if (this.childPopup == null) {
this.childPopup = new DebuggerPopup(this, logicalPosition, showPins);
this.childPopup.Placement = PlacementMode.Absolute;
}
if (this.containingPopup != null) {
this.containingPopup.IsLeaf = false;
}
this.childPopup.IsLeaf = true;
this.childPopup.HorizontalOffset = buttonPos.X + ChildPopupOpenXOffet;
this.childPopup.VerticalOffset = buttonPos.Y + ChildPopupOpenYOffet;
if (clickedNode.GetChildren != null) {
this.childPopup.ItemsSource = clickedNode.GetChildren().ToList();
this.childPopup.Open();
}
var clickedNode = (TreeNode)clickedButton.DataContext;
if (clickedButton.IsChecked == true && clickedNode.GetChildren != null) {
Point popupPos = clickedButton.PointToScreen(ChildPopupOffset).TransformFromDevice(clickedButton);
this.ChildTooltip = new DebuggerTooltipControl(clickedNode.GetChildren().ToList()) {
// We can not use placement target otherwise we would get too deep logical tree
Placement = PlacementMode.Absolute,
HorizontalOffset = popupPos.X,
VerticalOffset = popupPos.Y,
};
// The child is now tracking the focus
this.StaysOpen = true;
this.ChildTooltip.StaysOpen = false;
this.ChildTooltip.Closed += delegate {
// The null will have the effect of ignoring the next click
clickedButton.IsChecked = clickedButton.IsMouseOver ? (bool?)null : false;
// Either keep closing or make us the new leaf
if (this.IsMouseOver) {
this.StaysOpen = false;
} else {
CloseChildPopups();
this.IsOpen = false;
}
};
this.ChildTooltip.IsOpen = true;
}
private void handleScroll(object sender, ScrollChangedEventArgs e)
{
btnUp.IsEnabled = !this.lazyGrid.IsScrolledToStart;
btnDown.IsEnabled = !this.lazyGrid.IsScrolledToEnd;
}
void BtnUp_Click(object sender, RoutedEventArgs e)
{
this.lazyGrid.ScrollViewer.ScrollUp(1);
bool ITooltip.CloseOnHoverEnd {
get {
return this.ChildTooltip == null;
}
void BtnDown_Click(object sender, RoutedEventArgs e)
{
this.lazyGrid.ScrollViewer.ScrollDown(1);
}
#region Edit value in tooltip
void TextBox_KeyUp(object sender, KeyEventArgs e)
protected override void OnClosed(EventArgs e)
{
if (e.Key == Key.Escape) {
dataGrid.Focus();
return;
}
if (e.Key == Key.Enter) {
dataGrid.Focus();
// set new value
var textBox = (TextBox)e.OriginalSource;
var newValue = textBox.Text;
var node = ((FrameworkElement)sender).DataContext as ITreeNode;
SaveNewValue(node, textBox.Text);
}
// Closing the popup does not normally cause LostFocus on the textbox so we have to update it manually
TextBox textBox = FocusManager.GetFocusedElement(this) as TextBox;
if (textBox != null) {
BindingExpression be = textBox.GetBindingExpression(TextBox.TextProperty);
be.UpdateSource();
}
void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
var textBox = (TextBox)e.OriginalSource;
var newValue = textBox.Text;
var node = ((FrameworkElement)sender).DataContext as ITreeNode;
SaveNewValue(node, textBox.Text);
}
base.OnClosed(e);
void SaveNewValue(ITreeNode node, string newValue)
{
if(node != null && node.SetText(newValue)) {
// show adorner
var adornerLayer = AdornerLayer.GetAdornerLayer(dataGrid);
var adorners = adornerLayer.GetAdorners(dataGrid);
if (adorners != null && adorners.Length != 0)
adornerLayer.Remove(adorners[0]);
SavedAdorner adorner = new SavedAdorner(dataGrid);
adornerLayer.Add(adorner);
}
}
#endregion
#region Pining checked/unchecked
void PinButton_Checked(object sender, RoutedEventArgs e)
{
ITextEditorProvider provider = WorkbenchSingleton.Workbench.ActiveViewContent as ITextEditorProvider;
var editor = provider.TextEditor;
if (editor == null) return;
var node = (ITreeNode)(((ToggleButton)(e.OriginalSource)).DataContext);
if (!string.IsNullOrEmpty(editor.FileName)) {
// verify if at the line of the root there's a pin bookmark
var pin = BookmarkManager.Bookmarks.Find(
b => b is PinBookmark &&
b.LineNumber == logicalPosition.Line &&
b.FileName == editor.FileName) as PinBookmark;
if (pin == null) {
pin = new PinBookmark(editor.FileName, logicalPosition);
// show pinned DebuggerPopup
if (pin.Popup == null) {
pin.Popup = new PinDebuggerControl();
pin.Popup.Mark = pin;
Rect rect = new Rect(this.DesiredSize);
var point = this.PointToScreen(rect.TopRight);
pin.Popup.Location = new Point { X = 500, Y = point.Y - 150 };
pin.Nodes.Add(node);
pin.Popup.ItemsSource = pin.Nodes;
}
// do actions
pin.Popup.Open();
BookmarkManager.AddMark(pin);
}
else
{
if (!pin.ContainsNode(node)) {
pin.Nodes.Add(node);
pin.Popup.ItemsSource = pin.Nodes;
}
}
}
}
void PinButton_Unchecked(object sender, RoutedEventArgs e)
{
ITextEditorProvider provider = WorkbenchSingleton.Workbench.ActiveViewContent as ITextEditorProvider;
var editor = provider.TextEditor;
if (editor == null) return;
if (!string.IsNullOrEmpty(editor.FileName)) {
// remove from pinned DebuggerPopup
var pin = BookmarkManager.Bookmarks.Find(
b => b is PinBookmark &&
b.LineNumber == logicalPosition.Line &&
b.FileName == editor.FileName) as PinBookmark;
if (pin == null) return;
ToggleButton button = (ToggleButton)e.OriginalSource;
pin.RemoveNode((ITreeNode)button.DataContext);
pin.Popup.ItemsSource = pin.Nodes;
// remove if no more data pins are available
if (pin.Nodes.Count == 0) {
pin.Popup.Close();
BookmarkManager.RemoveMark(pin);
}
if (this.ChildTooltip != null) {
this.ChildTooltip.IsOpen = false;
}
}
void CopyMenuItemClick(object sender, RoutedEventArgs e)
{
ExpressionNode node = ((MenuItem)sender).DataContext as ExpressionNode;
ValueNode node = ((MenuItem)sender).DataContext as ValueNode;
if (node != null) {
Clipboard.SetText(node.FullText);
}
}
#endregion
#region Saved Adorner
class SavedAdorner : Adorner
{
public SavedAdorner(UIElement adornedElement) : base(adornedElement)
{
Loaded += delegate { Show(); };
}
protected override void OnRender(DrawingContext drawingContext)
{
Rect adornedElementRect = new Rect(this.AdornedElement.DesiredSize);
// Some arbitrary drawing implements.
var formatedText = new FormattedText(StringParser.Parse("${res:ICSharpCode.SharpDevelop.Debugging.SavedString}"),
CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
new Typeface(new FontFamily("Arial"),
FontStyles.Normal,
FontWeights.Black,
FontStretches.Expanded),
8d,
Brushes.Black);
drawingContext.DrawText(formatedText,
new Point(adornedElementRect.TopRight.X - formatedText.Width - 2,
adornedElementRect.TopRight.Y));
}
private void Show()
/*
void AnimateCloseControl(bool show)
{
DoubleAnimation animation = new DoubleAnimation();
animation.From = 1;
animation.To = 0;
animation.Duration = new Duration(TimeSpan.FromSeconds(2));
animation.SetValue(Storyboard.TargetProperty, this);
animation.From = show ? 0 : 1;
animation.To = show ? 1 : 0;
animation.BeginTime = new TimeSpan(0, 0, show ? 0 : 1);
animation.Duration = new Duration(TimeSpan.FromMilliseconds(500));
animation.SetValue(Storyboard.TargetProperty, this.PinCloseControl);
animation.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath(Rectangle.OpacityProperty));
Storyboard board = new Storyboard();
@ -421,8 +114,6 @@ namespace Debugger.AddIn.Tooltips @@ -421,8 +114,6 @@ namespace Debugger.AddIn.Tooltips
board.Begin(this);
}
}
#endregion
*/
}
}

113
src/AddIns/Debugger/Debugger.AddIn/Tooltips/LazyItemsControl.cs

@ -1,113 +0,0 @@ @@ -1,113 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Windows.Controls;
using ICSharpCode.SharpDevelop;
namespace Debugger.AddIn.Tooltips
{
/// <summary>
/// ItemsControl wrapper that takes VirtualizingIEnumerable as source,
/// and adds additional items from the source to underlying ItemsControl when scrolled to bottom.
/// </summary>
public class LazyItemsControl<T>
{
private ItemsControl itemsControl;
private int initialItemsCount;
/// <summary>
/// Creates new instance of LazyItemsControl.
/// </summary>
/// <param name="wrappedItemsControl">ItemsControl to wrap and add items to it when scrolled to bottom.</param>
/// <param name="initialItemsCount">Number of items to be initially displayed in wrapped ItemsControl.</param>
public LazyItemsControl(ItemsControl wrappedItemsControl, int initialItemsCount)
{
if (wrappedItemsControl == null)
throw new ArgumentNullException("wrappedItemsControl");
this.initialItemsCount = initialItemsCount;
this.itemsControl = wrappedItemsControl;
this.itemsControl.AddHandler(ScrollViewer.ScrollChangedEvent, new ScrollChangedEventHandler(handleScroll));
}
private ScrollViewer scrollViewerCached;
public ScrollViewer ScrollViewer
{
get
{
if (this.scrollViewerCached == null)
this.scrollViewerCached = this.itemsControl.GetScrollViewer();
return this.scrollViewerCached;
}
}
public bool IsScrolledToStart
{
get
{
if (ScrollViewer == null) // Visual tree not initialized yet
return false;
return ScrollViewer.VerticalOffset == 0;
}
}
public bool IsScrolledToEnd
{
get
{
if (itemsSourceTotalCount == null) {
// not scrolled to end of IEnumerable yet
return false;
}
// already scrolled to end of IEnumerable
int totalItems = itemsSourceTotalCount.Value;
return (ScrollViewer.VerticalOffset >= totalItems - ScrollViewer.ViewportHeight);
}
}
private int? itemsSourceTotalCount = null;
/// <summary> Items count of underlying IEnumerable. Null until scrolled to the end of IEnumerable. </summary>
public int? ItemsSourceTotalCount
{
get
{
return this.itemsSourceTotalCount;
}
}
private VirtualizingIEnumerable<T> itemsSource;
/// <summary> The collection that underlying ItemsControl sees. </summary>
public VirtualizingIEnumerable<T> ItemsSource
{
get { return itemsSource; }
set
{
this.itemsSource = value;
addNextItems(this.itemsSource, initialItemsCount);
this.itemsControl.ItemsSource = value;
}
}
private void addNextItems(VirtualizingIEnumerable<T> sourceToAdd, int nItems)
{
sourceToAdd.AddNextItems(nItems);
if (!sourceToAdd.HasNext) {
// all items from IEnumerable have been added
this.itemsSourceTotalCount = sourceToAdd.Count;
}
}
private void handleScroll(object sender, ScrollChangedEventArgs e)
{
if (e.VerticalChange > 0) {
// scrolled to bottom
if (e.VerticalOffset >= this.itemsSource.Count - e.ViewportHeight) {
addNextItems(this.itemsSource, (int)e.VerticalChange);
}
}
}
}
}

29
src/AddIns/Debugger/Debugger.AddIn/Tooltips/PinCloseControl.xaml

@ -1,29 +0,0 @@ @@ -1,29 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<UserControl
Background="Transparent"
x:Class="Debugger.AddIn.Tooltips.PinCloseControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary
Source="PinControlsDictionary.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<StackPanel>
<Button
Name="CloseButton"
Click="CloseButton_Click"
Template="{StaticResource CloseButtonTemplate}" />
<ToggleButton
Name="UnpinButton"
Checked="UnpinButton_Checked"
Unchecked="UnpinButton_Unchecked"
Template="{StaticResource PinButtonTemplate}" />
<ToggleButton
Name="CommentButton"
Checked="CommentButton_Checked"
Unchecked="CommentButton_Unchecked"
Template="{StaticResource CommentButtonTemplate}" />
</StackPanel>
</UserControl>

74
src/AddIns/Debugger/Debugger.AddIn/Tooltips/PinCloseControl.xaml.cs

@ -1,74 +0,0 @@ @@ -1,74 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Windows;
using System.Windows.Controls;
namespace Debugger.AddIn.Tooltips
{
public class ShowingCommentEventArgs : EventArgs
{
public bool ShowComment { get; private set; }
public ShowingCommentEventArgs(bool showComment)
{
ShowComment = showComment;
}
}
public partial class PinCloseControl : UserControl
{
public event EventHandler Closed;
public event EventHandler PinningChanged;
public event EventHandler<ShowingCommentEventArgs> ShowingComment;
public PinCloseControl()
{
InitializeComponent();
}
public bool IsChecked {
get {
return UnpinButton.IsChecked.GetValueOrDefault(false);
}
}
void CloseButton_Click(object sender, RoutedEventArgs e)
{
var handler = Closed;
if (handler != null)
handler(this, EventArgs.Empty);
}
void CommentButton_Checked(object sender, RoutedEventArgs e)
{
var handler = ShowingComment;
if (handler != null)
handler(this, new ShowingCommentEventArgs(true));
}
void CommentButton_Unchecked(object sender, RoutedEventArgs e)
{
var handler = ShowingComment;
if (handler != null)
handler(this, new ShowingCommentEventArgs(false));
}
void UnpinButton_Checked(object sender, RoutedEventArgs e)
{
var handler = PinningChanged;
if (handler != null)
handler(this, EventArgs.Empty);
}
void UnpinButton_Unchecked(object sender, RoutedEventArgs e)
{
var handler = PinningChanged;
if (handler != null)
handler(this, EventArgs.Empty);
}
}
}

363
src/AddIns/Debugger/Debugger.AddIn/Tooltips/PinControlsDictionary.xaml

@ -1,363 +0,0 @@ @@ -1,363 +0,0 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Debugger.AddIn.Tooltips"
xmlns:core="http://icsharpcode.net/sharpdevelop/core"
>
<LinearGradientBrush x:Key="OrangeBrushKey" EndPoint="0,1" StartPoint="0,0">
<LinearGradientBrush.GradientStops>
<GradientStop Offset="0" Color="White" />
<GradientStop Offset="0.5" Color="Orange" />
<GradientStop Offset="1" Color="Orange" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="OrangePressedBrushKey" EndPoint="0,1" StartPoint="0,0">
<LinearGradientBrush.GradientStops>
<GradientStop Offset="1" Color="White" />
<GradientStop Offset="0.5" Color="Orange" />
<GradientStop Offset="0" Color="Orange" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="SilverBrushKey" EndPoint="0,1" StartPoint="0,0">
<LinearGradientBrush.GradientStops>
<GradientStop Offset="0" Color="White" />
<GradientStop Offset="0.5" Color="LightGray" />
<GradientStop Offset="1" Color="LightGray" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="SilverPressedBrushKey" EndPoint="0,1" StartPoint="0,0">
<LinearGradientBrush.GradientStops>
<GradientStop Offset="1" Color="White" />
<GradientStop Offset="0.5" Color="LightGray" />
<GradientStop Offset="0" Color="LightGray" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
<ControlTemplate x:Key="CloseButtonTemplate" TargetType="Button">
<Border Width="16" Height="16" Name="TheBorder" CornerRadius="2,2,0,0" BorderThickness="1" BorderBrush="Black" Background="{StaticResource SilverPressedBrushKey}">
<Canvas>
<Line X1="3.5" X2="10.5" Y1="3.5" Y2="10.5" Stroke="Black" StrokeThickness="2"/>
<Line X1="3.5" X2="10.5" Y1="10.5" Y2="3.5" Stroke="Black" StrokeThickness="2"/>
</Canvas>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="UIElement.IsMouseOver" Value="true">
<Setter TargetName="TheBorder" Property="Background" Value="{StaticResource OrangeBrushKey}"/>
<Setter TargetName="TheBorder" Property="BorderBrush" Value="Silver"/>
</Trigger>
<Trigger Property="ButtonBase.IsPressed" Value="True">
<Setter TargetName="TheBorder" Property="Background" Value="{StaticResource OrangePressedBrushKey}"/>
<Setter TargetName="TheBorder" Property="BorderBrush" Value="Silver"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<TransformGroup x:Key="Rotate">
<RotateTransform Angle="270" CenterX="7" CenterY="7"/>
</TransformGroup>
<TransformGroup x:Key="RotateUnpin">
<RotateTransform Angle="270" CenterX="7" CenterY="7"/>
<RotateTransform Angle="-90" CenterX="7" CenterY="7"/>
<ScaleTransform ScaleY="-1" CenterX="7" CenterY="7"/>
</TransformGroup>
<TransformGroup x:Key="RotatePin">
<RotateTransform Angle="-90" CenterX="7" CenterY="7"/>
</TransformGroup>
<TransformGroup x:Key="FlipComment">
<ScaleTransform CenterX="7" CenterY="7" ScaleY="-1"/>
</TransformGroup>
<ControlTemplate x:Key="PinButtonTemplate" TargetType="ToggleButton">
<Border Width="16" Height="16" Name="TheBorder" CornerRadius="0" BorderThickness="1" BorderBrush="Black" Background="{StaticResource SilverPressedBrushKey}">
<Canvas Name="TheCanvas">
<Line X1="4" X2="10" Y1="2" Y2="2" Stroke="Black" StrokeThickness="1"/>
<Line X1="9" X2="9" Y1="2" Y2="8" Stroke="Black" StrokeThickness="1"/>
<Line X1="2" X2="12" Y1="8" Y2="8" Stroke="Black" StrokeThickness="1"/>
<Rectangle Fill="Black" Width="2" Height="5" Canvas.Left="4" Canvas.Top="3"/>
<Line X1="7" X2="7" Y1="9" Y2="12" Stroke="Black" StrokeThickness="1"/>
</Canvas>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="UIElement.IsMouseOver" Value="true">
<Setter TargetName="TheBorder" Property="Background" Value="{StaticResource OrangeBrushKey}"/>
<Setter TargetName="TheBorder" Property="BorderBrush" Value="Silver"/>
</Trigger>
<Trigger Property="ButtonBase.IsPressed" Value="True">
<Setter TargetName="TheCanvas" Property="RenderTransform" Value="{StaticResource RotatePin}"/>
<Setter TargetName="TheBorder" Property="Background" Value="{StaticResource OrangePressedBrushKey}"/>
<Setter TargetName="TheBorder" Property="BorderBrush" Value="Silver"/>
</Trigger>
<Trigger Property="IsChecked" Value="true">
<Setter TargetName="TheCanvas" Property="RenderTransform" Value="{StaticResource RotatePin}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<ControlTemplate x:Key="CommentButtonTemplate" TargetType="ToggleButton">
<Border Width="16" Height="16" Name="TheBorder" CornerRadius="0,0,2,2" BorderThickness="1" BorderBrush="Black" Background="{StaticResource SilverPressedBrushKey}">
<Canvas Name="TheCanvas">
<Line X1="3" Y1="3" X2="7" Y2="7.5" Stroke="Black" StrokeThickness="1"/>
<Line X1="7" Y1="7.4" X2="11" Y2="3" Stroke="Black" StrokeThickness="1"/>
<Line X1="3" Y1="7.5" X2="7" Y2="12" Stroke="Black" StrokeThickness="1"/>
<Line X1="7" Y1="12" X2="11" Y2="7.5" Stroke="Black" StrokeThickness="1"/>
</Canvas>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="UIElement.IsMouseOver" Value="true">
<Setter TargetName="TheBorder" Property="Background" Value="{StaticResource OrangeBrushKey}"/>
<Setter TargetName="TheBorder" Property="BorderBrush" Value="Silver"/>
</Trigger>
<Trigger Property="ButtonBase.IsPressed" Value="True">
<Setter TargetName="TheBorder" Property="Background" Value="{StaticResource OrangePressedBrushKey}"/>
<Setter TargetName="TheBorder" Property="BorderBrush" Value="Silver"/>
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="TheCanvas" Property="RenderTransform" Value="{StaticResource FlipComment}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<SolidColorBrush x:Key="MouseOverPinBrush" Color="Black" />
<ControlTemplate x:Key="PinTooltipButtonTemplate" TargetType="ToggleButton">
<Border Width="16" Height="16" Name="TheBorder" CornerRadius="2" BorderBrush="Transparent" BorderThickness="1" Background="Transparent">
<Canvas RenderTransform="{StaticResource Rotate}" Name="TheCanvas">
<Line X1="4" X2="10" Y1="2" Y2="2" Stroke="Silver" StrokeThickness="1" Name="Line1"/>
<Line X1="9" X2="9" Y1="2" Y2="8" Stroke="Silver" StrokeThickness="1" Name="Line2"/>
<Line X1="2" X2="12" Y1="8" Y2="8" Stroke="Silver" StrokeThickness="1" Name="Line3"/>
<Rectangle Fill="Silver" Width="2" Height="7" Canvas.Left="4" Canvas.Top="2" Name="Rectangle"/>
<Line X1="7" X2="7" Y1="9" Y2="12" Stroke="Silver" StrokeThickness="1" Name="Line4"/>
</Canvas>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="ButtonBase.IsPressed" Value="True">
<Setter TargetName="TheCanvas" Property="RenderTransform" Value="{StaticResource RotateUnpin}"/>
</Trigger>
<Trigger Property="ButtonBase.IsMouseOver" Value="True">
<Setter TargetName="Line1" Property="Stroke" Value="{StaticResource MouseOverPinBrush}"/>
<Setter TargetName="Line2" Property="Stroke" Value="{StaticResource MouseOverPinBrush}"/>
<Setter TargetName="Line3" Property="Stroke" Value="{StaticResource MouseOverPinBrush}"/>
<Setter TargetName="Line4" Property="Stroke" Value="{StaticResource MouseOverPinBrush}"/>
<Setter TargetName="Rectangle" Property="Fill" Value="{StaticResource MouseOverPinBrush}"/>
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="TheCanvas" Property="RenderTransform" Value="{StaticResource RotateUnpin}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<Style
TargetType="{x:Type TextBox}"
x:Key="TextStyle">
<Setter
Property="OverridesDefaultStyle"
Value="True" />
<Setter
Property="VerticalAlignment"
Value="Center" />
<Setter
Property="FontFamily" Value="Khmer UI" />
<Setter Property="FontSize" Value="12" />
<Setter
Property="KeyboardNavigation.TabNavigation"
Value="None" />
<Setter
Property="FocusVisualStyle"
Value="{x:Null}" />
<Setter
Property="Template">
<Setter.Value>
<ControlTemplate
TargetType="{x:Type TextBoxBase}">
<Border
Name="Border"
Background="Transparent"
BorderBrush="Transparent"
BorderThickness="0">
<ScrollViewer
Margin="0"
Name="PART_ContentHost" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="TextBlock" x:Key="TextBlockStyle">
<Setter Property="Margin" Value="4 0" />
<Setter
Property="FontFamily" Value="Khmer UI" />
<Setter Property="FontSize" Value="12" />
</Style>
<Style x:Key="PinThumbStyle" TargetType="Thumb">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Thumb">
<StackPanel x:Name="Container"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style
x:Key="ExpandCollapseToggleStyle"
TargetType="{x:Type ToggleButton}">
<Setter
Property="Focusable"
Value="False" />
<Setter
Property="Width"
Value="19" />
<Setter
Property="Height"
Value="13" />
<Setter
Property="Template">
<Setter.Value>
<ControlTemplate
TargetType="{x:Type ToggleButton}">
<Border
Width="19"
Height="13"
Background="Transparent">
<Border
Width="9"
Height="9"
BorderThickness="1"
BorderBrush="#FF7898B5"
CornerRadius="1"
SnapsToDevicePixels="true">
<Border.Background>
<LinearGradientBrush
StartPoint="0,0"
EndPoint="1,1">
<LinearGradientBrush.GradientStops>
<GradientStop
Color="White"
Offset=".2" />
<GradientStop
Color="#FFC0B7A6"
Offset="1" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Border.Background>
<Path
x:Name="ExpandPath"
Margin="1,1,1,1"
Fill="Black"
Data="M 0 2 L 0 3 L 2 3 L 2 5 L 3 5 L 3 3 L 5 3 L 5 2 L 3 2 L 3 0 L 2 0 L 2 2 Z" />
</Border>
</Border>
<ControlTemplate.Triggers>
<Trigger
Property="IsChecked"
Value="True">
<Setter
Property="Data"
TargetName="ExpandPath"
Value="M 0 2 L 0 3 L 5 3 L 5 2 Z" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style
x:Key="upDownBorderStyle"
TargetType="{x:Type Border}">
<Setter
Property="BorderBrush"
Value="Gray" />
<Setter
Property="HorizontalAlignment"
Value="Stretch" />
<Setter
Property="Margin"
Value="0" />
<Setter
Property="Padding"
Value="0" />
<Setter
Property="Background"
Value="#FFECF7FC" />
<Setter
Property="Height"
Value="14" />
<Style.Triggers>
<DataTrigger
Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsEnabled}"
Value="False">
<Setter
Property="Background"
Value="#FFE0E0E0"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
<Style
x:Key="upButtonStyle"
TargetType="{x:Type RepeatButton}">
<Setter
Property="Template">
<Setter.Value>
<ControlTemplate
TargetType="{x:Type RepeatButton}">
<Border
Style="{StaticResource upDownBorderStyle}"
BorderThickness="1 1 1 0">
<ContentPresenter
HorizontalAlignment="Center"></ContentPresenter>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style
x:Key="downButtonStyle"
TargetType="{x:Type RepeatButton}">
<Setter
Property="Template">
<Setter.Value>
<ControlTemplate
TargetType="{x:Type RepeatButton}">
<Border
Style="{StaticResource upDownBorderStyle}"
BorderThickness="1 0 1 1">
<ContentPresenter
HorizontalAlignment="Center"></ContentPresenter>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<ControlTemplate x:Key="RefreshButton" TargetType="Button">
<Border
Name="ImageBorder"
CornerRadius="7"
BorderBrush="Transparent"
BorderThickness="1"
Height="14"
Width="14">
<Image Width="9" Height="9" Margin="2 2"
x:Name="RefreshContentImage"
Tag="{Binding}"
Source="{core:GetBitmap Icons.16x16.Refresh}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="UIElement.IsMouseOver" Value="true">
<Setter TargetName="ImageBorder" Property="Background" Value="{StaticResource SilverBrushKey}"/>
<Setter TargetName="ImageBorder" Property="BorderBrush" Value="Gray"/>
</Trigger>
<Trigger Property="ButtonBase.IsPressed" Value="True">
<Setter TargetName="ImageBorder" Property="Background" Value="{StaticResource SilverPressedBrushKey}"/>
<Setter TargetName="ImageBorder" Property="BorderBrush" Value="Gray"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</ResourceDictionary>

240
src/AddIns/Debugger/Debugger.AddIn/Tooltips/PinDebuggerControl.xaml

@ -1,240 +0,0 @@ @@ -1,240 +0,0 @@
<UserControl x:Class="Debugger.AddIn.Tooltips.PinDebuggerControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:local="clr-namespace:Debugger.AddIn.Tooltips"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary
Source="PinControlsDictionary.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Grid.Resources>
<LinearGradientBrush x:Key="DataGridBackground"
StartPoint="0,-0.03"
EndPoint="0,1">
<GradientStop
Color="White" />
<GradientStop
Color="#FFFAFCFE"
Offset="0.983" />
<GradientStop
Color="#FFECF7FC"
Offset="0.07" />
<GradientStop
Color="#FFEEF7FA"
Offset="0.436" />
</LinearGradientBrush>
<Style x:Key="CellStyle"
TargetType="{x:Type DataGridCell}">
<Setter
Property="Focusable"
Value="false" />
<!-- Focusable=true blocks shortcuts if cell is focused -->
<Setter
Property="BorderThickness"
Value="0" />
<Setter Property="Width" Value="Auto" />
<Style.Triggers>
<Trigger
Property="IsSelected"
Value="True">
<!-- disable selection highlight -->
<Setter
Property="Foreground"
Value="Black" />
<Setter
Property="Background"
Value="{x:Null}" />
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="RowStyle"
TargetType="{x:Type DataGridRow}">
<Setter
Property="Background"
Value="Transparent"></Setter>
</Style>
<Style x:Key="DataGridStyle" TargetType="DataGrid">
<Setter Property="VerticalScrollBarVisibility" Value="Disabled"/>
<Setter Property="HorizontalScrollBarVisibility" Value="Disabled"/>
<Setter Property="GridLinesVisibility" Value="None"/>
<Setter Property="RowHeight" Value="18"/>
<Setter Property="MaxHeight" Value="202"/>
<Setter Property="MinHeight" Value="20" />
<Setter Property="SelectionMode" Value="Single"/>
<Setter Property="SelectionUnit" Value="FullRow"/>
<Setter Property="AutoGenerateColumns" Value="False"/>
<Setter Property="CanUserAddRows" Value="False"/>
<Setter Property="HeadersVisibility" Value="None"/>
<Setter Property="BorderBrush" Value="Gray"/>
<Setter Property="Background" Value="{StaticResource DataGridBackground}"/>
<Setter Property="CellStyle" Value="{StaticResource CellStyle}"/>
<Setter Property="RowStyle" Value="{StaticResource RowStyle}"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Cursor" Value="Arrow"/>
</Trigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel VerticalAlignment="Center">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<DataGrid
Width="21"
BorderThickness="1,1,0,1"
Background="White"
x:Name="ExpandersGrid"
Style="{StaticResource DataGridStyle}"
ItemsSource="{Binding}">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid
Background="White">
<StackPanel
VerticalAlignment="Center">
<ToggleButton
x:Name="btnExpander"
Style="{StaticResource ExpandCollapseToggleStyle}"
Checked="BtnExpander_Checked"
Unchecked="BtnExpander_Unchecked"
Padding="0"
Margin="0" />
</StackPanel>
</Grid>
<DataTemplate.Triggers>
<DataTrigger
Binding="{Binding Path=HasChildNodes}"
Value="False">
<Setter
TargetName="btnExpander"
Property="Visibility"
Value="Collapsed" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<DataGrid
BorderThickness="0,1,0,1"
Grid.Column="1"
IsEnabled="False"
ColumnWidth="SizeToCells"
Style="{StaticResource DataGridStyle}"
ItemsSource="{Binding}"
Foreground="Black"
Name="dataGrid">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image
Source="{Binding ImageSource}"></Image>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn
MinWidth="20"
Header="Name">
<!-- Name -->
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Border
BorderBrush="#FFDDDDDD"
BorderThickness="0 0 1 0">
<TextBlock
Style="{StaticResource TextBlockStyle}"
Text="{Binding Path=FullName, Mode=OneWay}"
VerticalAlignment="Center"></TextBlock>
</Border>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn IsReadOnly="True"
Width="SizeToCells"
Header="Text">
<!-- Text (value) -->
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox
Style="{StaticResource TextStyle}"
IsEnabled="false"
Text="{Binding Path=Text}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<DataGrid
MaxWidth="20"
BorderThickness="1"
Grid.Column="2"
x:Name="ImagesGrid"
Style="{StaticResource DataGridStyle}"
ItemsSource="{Binding}">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Click="Button_Click" Template="{StaticResource RefreshButton}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
<!-- comment textbox -->
<Border
Name="BorderComment"
Background="White"
BorderThickness="1,0,1,1"
BorderBrush="Gray"
Height="0"
MaxHeight="50">
<TextBox
FontFamily="Khmer UI"
BorderBrush="Gray"
BorderThickness="1"
FontSize="12"
Name="CommentTextBox"
TextChanged="CommentTextBox_TextChanged"
Margin="3"/>
</Border>
</StackPanel>
<local:PinCloseControl
VerticalAlignment="Center"
Background="Transparent"
Grid.Column="1"
Margin="5,0,0,0"
x:Name="PinCloseControl">
<local:PinCloseControl.Effect>
<DropShadowEffect
ShadowDepth="5"
Direction="330"
Color="Black"
Opacity="0.5"/>
</local:PinCloseControl.Effect>
</local:PinCloseControl>
</Grid>
</UserControl>

384
src/AddIns/Debugger/Debugger.AddIn/Tooltips/PinDebuggerControl.xaml.cs

@ -1,384 +0,0 @@ @@ -1,384 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Bookmarks;
using ICSharpCode.SharpDevelop.Debugging;
using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Services;
namespace Debugger.AddIn.Tooltips
{
public partial class PinDebuggerControl : UserControl, IPinDebuggerControl
{
private const double ChildPopupOpenXOffet = 16;
private const double ChildPopupOpenYOffet = 15;
private const int InitialItemsCount = 12;
private const double MINIMUM_OPACITY = .3d;
private WindowsDebugger currentDebugger;
private DebuggerPopup childPopup;
private LazyItemsControl<ITreeNode> lazyExpandersGrid;
private LazyItemsControl<ITreeNode> lazyGrid;
private LazyItemsControl<ITreeNode> lazyImagesGrid;
private IEnumerable<ITreeNode> itemsSource;
public PinDebuggerControl()
{
InitializeComponent();
if (!DebuggerService.IsDebuggerStarted)
Opacity = MINIMUM_OPACITY;
this.PinCloseControl.Opacity = 0;
Loaded += OnLoaded;
this.PinCloseControl.Closed += PinCloseControl_Closed;
this.PinCloseControl.ShowingComment += PinCloseControl_ShowingComment;
this.PinCloseControl.PinningChanged += PinCloseControl_PinningChanged;
BookmarkManager.Removed += OnBookmarkRemoved;
currentDebugger = (WindowsDebugger)DebuggerService.CurrentDebugger;
currentDebugger.DebugStopped += OnDebugStopped;
currentDebugger.ProcessSelected += OnProcessSelected;
if (currentDebugger.DebuggedProcess != null)
currentDebugger.DebuggedProcess.Paused += OnDebuggedProcessPaused;
}
#region Properties
public PinBookmark Mark { get; set; }
public IEnumerable<ITreeNode> ItemsSource
{
get { return this.itemsSource; }
set {
itemsSource = value;
var items = new VirtualizingIEnumerable<ITreeNode>(value);
lazyExpandersGrid = new LazyItemsControl<ITreeNode>(this.ExpandersGrid, InitialItemsCount);
lazyExpandersGrid.ItemsSource = items;
lazyGrid = new LazyItemsControl<ITreeNode>(this.dataGrid, InitialItemsCount);
lazyGrid.ItemsSource = items;
lazyImagesGrid = new LazyItemsControl<ITreeNode>(this.ImagesGrid, InitialItemsCount);
lazyImagesGrid.ItemsSource = items;
}
}
/// <summary>
/// Relative position of the pin with respect to the screen.
/// </summary>
public Point Location { get; set; }
#endregion
#region Main operations
public void Open()
{
Pin();
}
public void Close()
{
CloseChildPopups();
Unpin();
BookmarkManager.Removed -= OnBookmarkRemoved;
if (currentDebugger != null) {
currentDebugger.DebugStopped -= OnDebugStopped;
currentDebugger.ProcessSelected -= OnProcessSelected;
currentDebugger = null;
}
}
void Pin()
{
var provider = WorkbenchSingleton.Workbench.ActiveContent as ITextEditorProvider;
if(provider != null) {
var pinLayer = PinningBinding.GetPinlayer(provider.TextEditor);
if (pinLayer != null)
pinLayer.Pin(this);
}
}
void Unpin()
{
var provider = WorkbenchSingleton.Workbench.ActiveContent as ITextEditorProvider;
if(provider != null) {
var pinLayer = PinningBinding.GetPinlayer(provider.TextEditor);
if (pinLayer != null)
pinLayer.Unpin(this);
}
}
#endregion
#region Debugger events
void OnDebugStopped(object sender, EventArgs e)
{
if (currentDebugger.DebuggedProcess != null)
currentDebugger.DebuggedProcess.Paused -= OnDebuggedProcessPaused;
}
void OnProcessSelected(object sender, ProcessEventArgs e)
{
Opacity = 1d;
if (currentDebugger.DebuggedProcess != null)
currentDebugger.DebuggedProcess.Paused += OnDebuggedProcessPaused;
}
void OnDebuggedProcessPaused(object sender, ProcessEventArgs e)
{
//var nodes = new StackFrameNode(e.Process.SelectedStackFrame).ChildNodes;
// if (!lazyGrid.ItemsSource.ContainsNode(node))
// return;
// TODO : find the current expression so we don't update every pin
// var observable = new List<ITreeNode>();
//
// foreach (var node in lazyGrid.ItemsSource) {
// var resultNode = currentDebugger.GetNode(node.FullName);
// // HACK for updating the pins in tooltip
// observable.Add(resultNode);
// }
//
// // update UI
// var newSource = new VirtualizingIEnumerable<ITreeNode>(observable);
// lazyGrid.ItemsSource = newSource;
// lazyExpandersGrid.ItemsSource = newSource;
}
#endregion
#region Expand button
private ToggleButton expandedButton;
/// <summary>
/// Closes the child popup of this control, if it exists.
/// </summary>
void CloseChildPopups()
{
if (this.expandedButton != null) {
this.expandedButton = null;
// nice simple example of indirect recursion
this.childPopup.CloseSelfAndChildren();
}
}
void BtnExpander_Checked(object sender, RoutedEventArgs e)
{
if (!DebuggerService.IsDebuggerStarted)
return;
var clickedButton = (ToggleButton)e.OriginalSource;
var clickedNode = (ITreeNode)clickedButton.DataContext;
// use device independent units, because child popup Left/Top are in independent units
Point buttonPos = clickedButton.PointToScreen(new Point(0, 0)).TransformFromDevice(clickedButton);
if (clickedButton.IsChecked.GetValueOrDefault(false)) {
this.expandedButton = clickedButton;
// open child Popup
if (this.childPopup == null) {
this.childPopup = new DebuggerPopup(null, ICSharpCode.NRefactory.Location.Empty, false);
this.childPopup.PlacementTarget = this;
this.childPopup.Closed += new EventHandler(PinDebuggerControl_Closed);
this.childPopup.Placement = PlacementMode.Absolute;
}
this.childPopup.IsLeaf = true;
this.childPopup.HorizontalOffset = buttonPos.X + ChildPopupOpenXOffet;
this.childPopup.VerticalOffset = buttonPos.Y + ChildPopupOpenYOffet;
if (clickedNode.GetChildren != null) {
this.childPopup.ItemsSource = clickedNode.GetChildren().ToList();
this.childPopup.Open();
}
} else {
}
}
void PinDebuggerControl_Closed(object sender, EventArgs e)
{
if (expandedButton != null && expandedButton.IsChecked.GetValueOrDefault(false))
expandedButton.IsChecked = false;
}
void BtnExpander_Unchecked(object sender, RoutedEventArgs e)
{
CloseChildPopups();
}
#endregion
#region PinCloseControl
void PinCloseControl_Closed(object sender, EventArgs e)
{
BookmarkManager.RemoveMark(Mark);
Close();
}
void PinCloseControl_PinningChanged(object sender, EventArgs e)
{
if (this.PinCloseControl.IsChecked) {
BookmarkManager.RemoveMark(Mark);
}
else {
if(BookmarkManager.Bookmarks.Contains(Mark))
BookmarkManager.RemoveMark(Mark);
BookmarkManager.AddMark(Mark);
}
}
void PinCloseControl_ShowingComment(object sender, ShowingCommentEventArgs e)
{
ShowComment(e.ShowComment);
}
void AnimateCloseControl(bool show)
{
DoubleAnimation animation = new DoubleAnimation();
animation.From = show ? 0 : 1;
animation.To = show ? 1 : 0;
animation.BeginTime = new TimeSpan(0, 0, show ? 0 : 1);
animation.Duration = new Duration(TimeSpan.FromMilliseconds(500));
animation.SetValue(Storyboard.TargetProperty, this.PinCloseControl);
animation.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath(Rectangle.OpacityProperty));
Storyboard board = new Storyboard();
board.Children.Add(animation);
board.Begin(this);
}
#endregion
void OnBookmarkRemoved(object sender, BookmarkEventArgs e)
{
// if the bookmark was removed from pressing the button, return
if (this.PinCloseControl.IsChecked)
return;
if (e.Bookmark is PinBookmark) {
var pin = (PinBookmark)e.Bookmark;
if (pin.Location == Mark.Location && pin.FileName == Mark.FileName) {
Close();
}
}
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
this.CommentTextBox.Text = Mark.Comment;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
if (!DebuggerService.IsDebuggerStarted)
return;
// refresh content
ITreeNode node = ((FrameworkElement)e.OriginalSource).DataContext as ITreeNode;
var resultNode = currentDebugger.GetNode(node.Name, node.ImageName);
if (resultNode == null)
return;
// HACK for updating the pins in tooltip
var observable = new ObservableCollection<ITreeNode>();
var source = lazyGrid.ItemsSource;
source.ForEach(item => {
if (item.Name == node.Name)
observable.Add(resultNode);
else
observable.Add(item);
});
Mark.Nodes = observable;
// update UI
var newSource = new VirtualizingIEnumerable<ITreeNode>(observable);
lazyGrid.ItemsSource = newSource;
lazyExpandersGrid.ItemsSource = newSource;
}
#region Comment
void ShowComment(bool show)
{
if(show && BorderComment.Height != 0)
return;
if(!show && BorderComment.Height != 40)
return;
DoubleAnimation animation = new DoubleAnimation();
animation.From = show ? 0 : 40;
animation.To = show ? 40 : 0;
animation.Duration = new Duration(TimeSpan.FromMilliseconds(300));
animation.SetValue(Storyboard.TargetProperty, BorderComment);
animation.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath(Border.HeightProperty));
Storyboard board = new Storyboard();
board.Children.Add(animation);
board.Begin(this);
}
void CommentTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
Mark.Comment = this.CommentTextBox.Text;
}
#endregion
#region Overrides
protected override void OnMouseEnter(System.Windows.Input.MouseEventArgs e)
{
AnimateCloseControl(true);
Opacity = 1d;
Cursor = Cursors.Arrow;
base.OnMouseEnter(e);
}
protected override void OnMouseMove(MouseEventArgs e)
{
Opacity = 1d;
Cursor = Cursors.Arrow;
base.OnMouseMove(e);
}
protected override void OnMouseLeave(System.Windows.Input.MouseEventArgs e)
{
if (DebuggerService.IsDebuggerStarted)
Opacity = 1;
else
Opacity = MINIMUM_OPACITY;
AnimateCloseControl(false);
Cursor = Cursors.IBeam;
base.OnMouseLeave(e);
}
#endregion
}
}

184
src/AddIns/Debugger/Debugger.AddIn/Tooltips/PinLayer.cs

@ -1,184 +0,0 @@ @@ -1,184 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using ICSharpCode.AvalonEdit.Editing;
using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.Core.Presentation;
using ICSharpCode.SharpDevelop.Refactoring;
namespace Debugger.AddIn.Tooltips
{
/// <summary>
/// Pin layer class. This class handles the pinning and unpinning operations.
/// </summary>
public class PinLayer : Canvas
{
private double verticalOffset = 0;
private double horizontalOffset = 0;
private TextView textView;
/// <summary>
/// PinLayer constructor.
/// </summary>
/// <param name="textArea">Text area for this layer.</param>
public PinLayer(TextArea textArea)
{
textView = textArea.TextView;
textView.VisualLinesChanged += textView_VisualLinesChanged;
}
/// <summary>
/// Pins an element;
/// </summary>
/// <param name="element">Element to pin.</param>
public void Pin(PinDebuggerControl element)
{
if (element == null)
throw new NullReferenceException("Element is null!");
Thumb currentThumb = new Thumb();
// check for saved position
if (!element.Mark.PinPosition.HasValue) {
// this is satisfied when pinning the first time
element.Mark.PinPosition = new Point {
X = element.Location.X + textView.HorizontalOffset,
Y = element.Location.Y + textView.VerticalOffset
};
Canvas.SetTop(currentThumb, element.Location.Y);
Canvas.SetLeft(currentThumb, element.Location.X);
}
else {
// this is satisfied when loading the pins - so we might have hidden pins
element.Location = new Point {
X = element.Mark.PinPosition.Value.X - textView.HorizontalOffset,
Y = element.Mark.PinPosition.Value.Y - textView.VerticalOffset
};
Canvas.SetTop(currentThumb, element.Mark.PinPosition.Value.Y);
Canvas.SetLeft(currentThumb, element.Mark.PinPosition.Value.X);
}
currentThumb.Style = element.TryFindResource("PinThumbStyle") as Style;
currentThumb.ApplyTemplate();
currentThumb.DragDelta += onDragDelta;
currentThumb.DragStarted += currentThumb_DragStarted;
currentThumb.DragCompleted += currentThumb_DragCompleted;
var container = TryFindChild<StackPanel>(currentThumb);
container.Children.Add(element);
this.Children.Add(currentThumb);
}
/// <summary>
/// Unpins an element.
/// </summary>
/// <param name="element">Element to unpin.</param>
public void Unpin(PinDebuggerControl element)
{
if (element == null)
throw new NullReferenceException("Element is null!");
foreach (var thumb in this.Children) {
PinDebuggerControl pinControl = TryFindChild<PinDebuggerControl>((DependencyObject)thumb);
if (pinControl != null && pinControl == element)
{
this.Children.Remove((UIElement)thumb);
element.Close();
break;
}
}
}
void textView_VisualLinesChanged(object sender, EventArgs e)
{
foreach (var ctrl in this.Children) {
var currentThumb = ctrl as Thumb;
PinDebuggerControl pinControl = TryFindChild<PinDebuggerControl>(currentThumb);
if (pinControl != null)
{
// update relative location
Point location = pinControl.Location;
location.X += horizontalOffset - textView.HorizontalOffset;
location.Y += verticalOffset - textView.VerticalOffset;
Canvas.SetLeft(currentThumb, location.X);
Canvas.SetTop(currentThumb, location.Y);
pinControl.Location = location;
pinControl.Mark.PinPosition = new Point {
X = location.X + textView.HorizontalOffset,
Y = location.Y + textView.VerticalOffset,
};
}
}
verticalOffset = textView.VerticalOffset;
horizontalOffset = textView.HorizontalOffset;
}
#region Mouse move
void onDragDelta(object sender, DragDeltaEventArgs e)
{
Thumb currnetThumb = (Thumb)sender;
currnetThumb.Cursor = Cursors.Arrow;
double left = Canvas.GetLeft(currnetThumb) + e.HorizontalChange;
double top = Canvas.GetTop(currnetThumb) + e.VerticalChange;
Canvas.SetLeft(currnetThumb, left);
Canvas.SetTop(currnetThumb, top);
}
void currentThumb_DragCompleted(object sender, DragCompletedEventArgs e)
{
Thumb currnetThumb = (Thumb)sender;
currnetThumb.Cursor = Cursors.Arrow;
var pinControl = TryFindChild<PinDebuggerControl>(currnetThumb);
if (pinControl != null) {
double left = Canvas.GetLeft(currnetThumb);
double top = Canvas.GetTop(currnetThumb);
pinControl.Opacity = 1d;
pinControl.Location = new Point { X = left, Y = top };
// pin's position is with respect to the layer.
pinControl.Mark.PinPosition =
new Point
{
X = textView.HorizontalOffset + left,
Y = textView.VerticalOffset + top
};
}
}
void currentThumb_DragStarted(object sender, DragStartedEventArgs e)
{
Thumb currnetThumb = (Thumb)sender;
currnetThumb.Cursor = Cursors.Arrow;
var pinControl = TryFindChild<PinDebuggerControl>(currnetThumb);
if (pinControl != null)
pinControl.Opacity = 1d;
}
#endregion
#region Static helpers
static T TryFindChild<T>(DependencyObject root) where T : DependencyObject
{
return WpfTreeNavigation.TryFindChild<T>(root);
}
#endregion
}
}

118
src/AddIns/Debugger/Debugger.AddIn/Tooltips/PinningBinding.cs

@ -1,118 +0,0 @@ @@ -1,118 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.ObjectModel;
using ICSharpCode.AvalonEdit;
using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Bookmarks;
using ICSharpCode.SharpDevelop.Debugging;
using ICSharpCode.SharpDevelop.Editor;
namespace Debugger.AddIn.Tooltips
{
public class PinningBinding : DefaultLanguageBinding
{
ITextEditor _editor;
PinLayer pinLayer;
public PinningBinding()
{}
public override void Attach(ITextEditor editor)
{
if (editor == null)
return;
var textEditor = editor.GetService(typeof(TextEditor)) as TextEditor;
if (textEditor != null) {
pinLayer = new PinLayer(textEditor.TextArea);
textEditor.TextArea.TextView.InsertLayer(
pinLayer,
KnownLayer.Caret,
LayerInsertionPosition.Above);
}
_editor = editor;
CreatePins(_editor);
base.Attach(editor);
}
public override void Detach()
{
ClosePins(_editor);
pinLayer = null;
base.Detach();
}
public void CreatePins(ITextEditor editor)
{
// load pins
var pins = BookmarkManager.Bookmarks.FindAll(
b => b is PinBookmark && b.FileName == editor.FileName);
foreach (var bookmark in pins) {
var pin = (PinBookmark)bookmark;
pin.Popup = new PinDebuggerControl();
pin.Popup.Mark = pin;
var nodes = new ObservableCollection<ITreeNode>();
foreach (var tuple in pin.SavedNodes) {
var node = new Debugger.AddIn.TreeModel.TreeNode(
!string.IsNullOrEmpty(tuple.Item1) ? tuple.Item1 : "Icons.16x16.Field",
tuple.Item2,
tuple.Item3,
string.Empty,
null
);
nodes.Add(node);
}
pin.SavedNodes.Clear();
pin.Popup.ItemsSource = nodes;
pin.Nodes = nodes;
pinLayer.Pin((PinDebuggerControl)pin.Popup);
}
}
public void ClosePins(ITextEditor editor)
{
// save pins
var pins = BookmarkManager.Bookmarks.FindAll(
b => b is PinBookmark && b.FileName == editor.FileName);
foreach (var bookmark in pins) {
var pin = (PinBookmark)bookmark;
if (!pin.PinPosition.HasValue)
pin.PinPosition = pin.Popup.Location;
// nodes
if (pin.SavedNodes == null)
pin.SavedNodes = new System.Collections.Generic.List<Tuple<string, string, string>>();
foreach (var node in pin.Nodes) {
pin.SavedNodes.Add(
new Tuple<string, string, string>(
"Icons.16x16.Field",
node.Name,
node.Text));
}
pinLayer.Unpin((PinDebuggerControl)pin.Popup);
pin.Popup = null;
}
}
public static PinLayer GetPinlayer(ITextEditor editor) {
var textEditor = editor.GetService(typeof(TextEditor)) as TextEditor;
if (textEditor != null) {
return textEditor.TextArea.TextView.Layers[3] as PinLayer;
}
return null;
}
}
}

53
src/AddIns/Debugger/Debugger.AddIn/Tooltips/VirtualizingIEnumerable.cs

@ -1,53 +0,0 @@ @@ -1,53 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace Debugger.AddIn.Tooltips
{
/// <summary>
/// A wrapper around IEnumerable&lt;T&gt; with AddNextItems method for pulling additional items
/// from underlying IEnumerable&lt;T&gt;.
/// Can be used as source for <see cref="LazyItemsControl" />.
/// </summary>
public class VirtualizingIEnumerable<T> : ObservableCollection<T>
{
private IEnumerator<T> originalSourceEnumerator;
public VirtualizingIEnumerable(IEnumerable<T> originalSource)
{
if (originalSource == null)
throw new ArgumentNullException("originalSource");
this.originalSourceEnumerator = originalSource.GetEnumerator();
}
private bool hasNext = true;
/// <summary>
/// False if all items from underlying IEnumerable have already been added.
/// </summary>
public bool HasNext
{
get
{
return this.hasNext;
}
}
/// <summary>
/// Requests next <paramref name="count"/> items from underlying IEnumerable source and adds them to the collection.
/// </summary>
public void AddNextItems(int count)
{
for (int i = 0; i < count; i++) {
if (!originalSourceEnumerator.MoveNext()) {
this.hasNext = false;
break;
}
this.Add(originalSourceEnumerator.Current);
}
}
}
}

114
src/AddIns/Debugger/Debugger.AddIn/TreeModel/TreeNode.cs

@ -16,62 +16,81 @@ namespace Debugger.AddIn.TreeModel @@ -16,62 +16,81 @@ namespace Debugger.AddIn.TreeModel
/// <summary>
/// A node in the variable tree.
/// </summary>
public class TreeNode : ITreeNode
public class TreeNode : INotifyPropertyChanged
{
public IImage IconImage { get; protected set; }
public string ImageName { get; set; }
public string Name { get; set; }
public virtual string Text { get; set; }
public virtual string Type { get; protected set; }
public virtual Func<IEnumerable<TreeNode>> GetChildren { get; protected set; }
public event EventHandler<PropertyEventArgs> PropertyRead;
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// System.Windows.Media.ImageSource version of <see cref="IconImage"/>.
/// </summary>
public ImageSource ImageSource {
IImage image;
string name;
string value;
string type;
public IImage Image {
get {
return this.IconImage == null ? null : this.IconImage.ImageSource;
OnPropertyRead("Image");
return this.image;
}
set {
if (this.image != value) {
this.image = value;
OnPropertyChanged("Image");
}
}
}
/// <summary>
/// System.Drawing.Image version of <see cref="IconImage"/>.
/// </summary>
public Image Image {
public string Name {
get {
return this.IconImage == null ? null : this.IconImage.Bitmap;
OnPropertyRead("Name");
return this.name;
}
set {
if (this.name != value) {
this.name = value;
OnPropertyChanged("Name");
}
}
public virtual bool CanSetText {
get { return false; }
}
Func<IEnumerable<ITreeNode>> ITreeNode.GetChildren {
public bool CanSetName { get; protected set; }
public string Value {
get {
if (this.GetChildren == null)
return null;
return () => this.GetChildren();
OnPropertyRead("Value");
return this.value;
}
set {
if (this.value != value) {
this.value = value;
OnPropertyChanged("Value");
}
public virtual bool HasChildNodes {
get { return this.GetChildren != null; }
}
}
public bool CanSetValue { get; protected set; }
public virtual IEnumerable<IVisualizerCommand> VisualizerCommands {
public string Type {
get {
return null;
OnPropertyRead("Type");
return this.type;
}
set {
if (this.type != value) {
this.type = value;
OnPropertyChanged("Type");
}
}
public virtual bool HasVisualizerCommands {
get {
return (VisualizerCommands != null) && (VisualizerCommands.Count() > 0);
}
public Func<IEnumerable<TreeNode>> GetChildren { get; protected set; }
public bool HasChildren {
get { return GetChildren != null; }
}
public bool IsPinned { get; set; }
public IEnumerable<IVisualizerCommand> VisualizerCommands { get; protected set; }
public bool HasVisualizerCommands { get; protected set; }
public TreeNode(string name, Func<IEnumerable<TreeNode>> getChildren)
{
@ -79,19 +98,32 @@ namespace Debugger.AddIn.TreeModel @@ -79,19 +98,32 @@ namespace Debugger.AddIn.TreeModel
this.GetChildren = getChildren;
}
public TreeNode(string imageName, string name, string text, string type, Func<IEnumerable<TreeNode>> getChildren)
public TreeNode(string imageName, string name, string value, string type, Func<IEnumerable<TreeNode>> getChildren)
{
this.ImageName = imageName;
if (imageName != null)
this.IconImage = new ResourceServiceImage(imageName);
this.Image = string.IsNullOrEmpty(imageName) ? null : new ResourceServiceImage(imageName);
this.Name = name;
this.Text = text;
this.Value = value;
this.Type = type;
this.GetChildren = getChildren;
}
public virtual bool SetText(string newValue) {
return false;
protected virtual void OnPropertyRead(string name)
{
if (PropertyRead != null) {
PropertyRead(this, new PropertyEventArgs() { Name = name});
}
}
protected virtual void OnPropertyChanged(string name)
{
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
public class PropertyEventArgs: EventArgs
{
public string Name { get; set; }
}
}

517
src/AddIns/Debugger/Debugger.AddIn/TreeModel/ValueNode.cs

@ -12,346 +12,172 @@ using System.Reflection; @@ -12,346 +12,172 @@ using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using Debugger.AddIn.Visualizers;
using Debugger.AddIn.Visualizers.Utils;
using Debugger.MetaData;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Debugging;
using ICSharpCode.SharpDevelop.Gui.Pads;
using ICSharpCode.SharpDevelop.Services;
namespace Debugger.AddIn.TreeModel
{
/// <summary>
/// Node in the tree which can be defined by a debugger expression.
/// The expression will be lazily evaluated when needed.
/// Tree node which represents debuggee's <see cref="Value"/>.
/// The node stores a lambda which can be used to reobtain the value
/// at any time (possibly even after some stepping).
/// </summary>
public class ExpressionNode: TreeNode, INotifyPropertyChanged
/// <remarks>
/// The general rule is that getting a value or getting children will
/// either succeed or result in <see cref="GetValueException"/>.
/// </remarks>
public class ValueNode: TreeNode
{
bool evaluated;
Func<Value> valueGetter;
Value permanentValue;
bool canSetText;
GetValueException error;
string fullText;
Func<Value> getValue;
Action<Value> setValue;
public bool Evaluated {
get { return evaluated; }
set { evaluated = value; }
}
Value cachedValue;
Debugger.Process cachedValueProcess;
object cachedValueDebuggeeState;
public override bool CanSetText {
get {
if (!evaluated) EvaluateExpression();
return canSetText;
}
}
public GetValueException Error {
get {
if (!evaluated) EvaluateExpression();
return error;
}
}
string fullValue;
GetValueException error;
public string FullText {
get { return fullText; }
}
public override string Text {
get {
if (!evaluated) EvaluateExpression();
return base.Text;
}
set {
if (value != base.Text) {
base.Text = value;
NotifyPropertyChanged("Text");
}
}
}
public override string Type {
get {
if (!evaluated) EvaluateExpression();
return base.Type;
}
}
public override Func<IEnumerable<TreeNode>> GetChildren {
get {
if (!evaluated) EvaluateExpression();
return base.GetChildren;
}
protected set {
base.GetChildren = value;
}
get { return this.Value; }
}
/// <summary> Used to determine available VisualizerCommands </summary>
private DebugType expressionType;
/// <summary> Used to determine available VisualizerCommands </summary>
private bool valueIsNull = true;
private IEnumerable<IVisualizerCommand> visualizerCommands;
public override IEnumerable<IVisualizerCommand> VisualizerCommands {
get {
if (visualizerCommands == null) {
visualizerCommands = getAvailableVisualizerCommands();
}
return visualizerCommands;
}
}
private IEnumerable<IVisualizerCommand> getAvailableVisualizerCommands()
public ValueNode(string imageName, string name, Func<Value> getValue, Action<Value> setValue = null)
: base(imageName, name, string.Empty, string.Empty, null)
{
if (!evaluated) EvaluateExpression();
if (getValue == null)
throw new ArgumentNullException("getValue");
if (this.expressionType == null) {
// no visualizers if EvaluateExpression failed
yield break;
}
if (this.valueIsNull) {
// no visualizers if evaluated value is null
yield break;
}
/*if (this.expressionType.IsPrimitive || this.expressionType.IsSystemDotObject() || this.expressionType.IsEnum()) {
// no visualizers for primitive types
yield break;
}*/
this.getValue = getValue;
this.setValue = setValue;
foreach (var descriptor in VisualizerDescriptors.GetAllDescriptors()) {
if (descriptor.IsVisualizerAvailable(this.expressionType)) {
yield return descriptor.CreateVisualizerCommand(this.Name, () => this.Evaluate());
}
}
}
public ExpressionNode(string imageName, string name, Func<Value> valueGetter)
: base(imageName, name, string.Empty, string.Empty, null)
{
this.valueGetter = valueGetter;
GetValueAndUpdateUI();
}
/// <summary>
/// Get the value of the node and cache it as long-lived reference.
/// We assume that the user will need this value a lot.
/// </summary>
public Value Evaluate()
public Value GetValue()
{
if (permanentValue == null)
// The value still survives across debuggee state, but we want a fresh one for the UI
if (cachedValue == null || cachedValueProcess.DebuggeeState != cachedValueDebuggeeState)
{
Stopwatch watch = new Stopwatch();
watch.Start();
permanentValue = valueGetter().GetPermanentReference();
cachedValue = this.getValue().GetPermanentReference();
cachedValueProcess = cachedValue.Process;
cachedValueDebuggeeState = cachedValue.Process.DebuggeeState;
LoggingService.InfoFormatted("Evaluated node '{0}' in {1} ms (result cached for future use)", this.Name, watch.ElapsedMilliseconds);
}
return permanentValue;
return cachedValue;
}
public void SetValue(Value value)
{
if (setValue == null)
throw new DebuggerException("Setting of value is not supported for this node");
try
{
this.setValue(value);
}
catch(GetValueException e)
{
MessageService.ShowMessage(e.Message, "${res:MainWindow.Windows.Debug.LocalVariables.CannotSetValue.Title}");
}
}
/// <summary>
/// Get the value of the node and update the UI text fields.
/// </summary>
void EvaluateExpression()
void GetValueAndUpdateUI()
{
evaluated = true;
try {
Stopwatch watch = new Stopwatch();
watch.Start();
Value val;
try {
// Do not keep permanent reference
val = valueGetter();
} catch (GetValueException e) {
error = e;
this.Text = e.Message;
return;
}
Value val = this.getValue();
this.canSetText = val.Type.IsPrimitive;
this.expressionType = val.Type;
this.Type = val.Type.Name;
this.valueIsNull = val.IsNull;
// Note that these return enumerators so they are lazy-evaluated
// Note that the child collections are lazy-evaluated
if (val.IsNull) {
this.GetChildren = null;
} else if (val.Type.IsPrimitive || val.Type.FullName == typeof(string).FullName) { // Must be before IsClass
this.GetChildren = null;
} else if (val.Type.IsArray) { // Must be before IsClass
if (val.ArrayLength > 0) {
var dims = val.ArrayDimensions; // Eval now
this.GetChildren = () => Utils.GetChildNodesOfArray(this, dims, dims);
if (dims.TotalElementCount > 0) {
this.GetChildren = () => GetArrayChildren(dims, dims);
}
} else if (val.Type.IsClass || val.Type.IsValueType) {
if (val.Type.FullNameWithoutGenericArguments == typeof(List<>).FullName) {
if ((int)val.GetMemberValue("_size").PrimitiveValue > 0)
this.GetChildren = () => Utils.GetItemsOfIList(() => this.Evaluate());
this.GetChildren = () => GetIListChildren(this.GetValue);
} else {
this.GetChildren = () => Utils.GetChildNodesOfObject(this, val.Type);
this.GetChildren = () => GetObjectChildren(val.Type);
}
} else if (val.Type.IsPointer) {
Value deRef = val.Dereference();
if (deRef != null) {
this.GetChildren = () => new ExpressionNode [] { new ExpressionNode(this.ImageName, "*" + this.Name, () => this.Evaluate().Dereference()) };
if (val.Dereference() != null) {
this.GetChildren = () => new[] { new ValueNode("Icons.16x16.Local", "*" + this.Name, () => GetValue().Dereference()) };
}
}
// Do last since it may expire the object
if (val.Type.IsInteger) {
fullText = FormatInteger(val.PrimitiveValue);
} else if (val.Type.IsPointer) {
fullText = String.Format("0x{0:X}", val.PointerAddress);
} else if ((val.Type.FullName == typeof(string).FullName ||
val.Type.FullName == typeof(char).FullName) && !val.IsNull) {
try {
fullText = '"' + Escape(val.InvokeToString()) + '"';
} catch (GetValueException e) {
error = e;
fullText = e.Message;
return;
}
} else if ((val.Type.IsClass || val.Type.IsValueType) && !val.IsNull) {
try {
fullText = val.InvokeToString();
} catch (GetValueException e) {
error = e;
fullText = e.Message;
return;
}
if (val.IsNull) {
fullValue = "null";
} else if (val.Type.IsInteger) {
var i = val.PrimitiveValue;
if (DebuggingOptions.Instance.ShowIntegersAs == ShowIntegersAs.Decimal) {
fullValue = i.ToString();
} else {
fullText = val.AsString();
}
this.Text = (fullText.Length > 256) ? fullText.Substring(0, 256) + "..." : fullText;
LoggingService.InfoFormatted("Evaluated node '{0}' in {1} ms", this.Name, watch.ElapsedMilliseconds);
}
string Escape(string source)
{
return source.Replace("\n", "\\n")
.Replace("\t", "\\t")
.Replace("\r", "\\r")
.Replace("\0", "\\0")
.Replace("\b", "\\b")
.Replace("\a", "\\a")
.Replace("\f", "\\f")
.Replace("\v", "\\v")
.Replace("\"", "\\\"");
}
string FormatInteger(object i)
{
if (DebuggingOptions.Instance.ShowIntegersAs == ShowIntegersAs.Decimal)
return i.ToString();
string hex = null;
for(int len = 1;; len *= 2) {
hex = string.Format("{0:X" + len + "}", i);
if (hex.Length == len)
break;
}
string hex = string.Format("0x{0:X4}", i);
if (hex.Length > 6 ) hex = string.Format("0x{0:X8}", i);
if (hex.Length > 10) hex = string.Format("0x{0:X16}", i);
if (DebuggingOptions.Instance.ShowIntegersAs == ShowIntegersAs.Hexadecimal) {
return "0x" + hex;
} else {
if (ShowAsHex(i)) {
return String.Format("{0} (0x{1})", i, hex);
fullValue = hex;
} else {
return i.ToString();
}
fullValue = string.Format("{0} ({1})", i, hex);
}
}
bool ShowAsHex(object i)
{
ulong val;
if (i is sbyte || i is short || i is int || i is long) {
unchecked { val = (ulong)Convert.ToInt64(i); }
if (val > (ulong)long.MaxValue)
val = ~val + 1;
} else if (val.Type.IsPointer) {
fullValue = String.Format("0x{0:X}", val.PointerAddress);
} else if (val.Type.FullName == typeof(string).FullName) {
fullValue = '"' + val.InvokeToString().Replace("\n", "\\n").Replace("\t", "\\t").Replace("\r", "\\r").Replace("\0", "\\0").Replace("\b", "\\b").Replace("\a", "\\a").Replace("\f", "\\f").Replace("\v", "\\v").Replace("\"", "\\\"") + '"';
} else if (val.Type.FullName == typeof(char).FullName) {
fullValue = "'" + val.InvokeToString().Replace("\n", "\\n").Replace("\t", "\\t").Replace("\r", "\\r").Replace("\0", "\\0").Replace("\b", "\\b").Replace("\a", "\\a").Replace("\f", "\\f").Replace("\v", "\\v").Replace("\"", "\\\"") + "'";
} else if ((val.Type.IsClass || val.Type.IsValueType)) {
fullValue = val.InvokeToString();
} else {
val = Convert.ToUInt64(i);
fullValue = val.AsString();
}
if (val >= 0x10000)
return true;
int ones = 0; // How many 1s there is
int runs = 0; // How many runs of 1s there is
int size = 0; // Size of the integer in bits
while(val != 0) { // There is at least one 1
while((val & 1) == 0) { // Skip 0s
val = val >> 1;
size++;
}
while((val & 1) == 1) { // Skip 1s
val = val >> 1;
size++;
ones++;
}
runs++;
}
this.error = null;
this.Value = (fullValue.Length > 256) ? fullValue.Substring(0, 256) + "..." : fullValue;
this.Type = val.Type.Name;
return size >= 7 && runs <= (size + 7) / 8;
if (!val.IsNull) {
this.VisualizerCommands = VisualizerDescriptors.GetAllDescriptors()
.Where(descriptor => descriptor.IsVisualizerAvailable(val.Type))
.Select(descriptor => descriptor.CreateVisualizerCommand(this.Name, this.GetValue))
.ToList();
}
public override bool SetText(string newText)
{
Value val = null;
try {
val = this.Evaluate();
if (val.Type.IsInteger && newText.StartsWith("0x")) {
try {
val.PrimitiveValue = long.Parse(newText.Substring(2), NumberStyles.HexNumber);
} catch (FormatException) {
throw new NotSupportedException();
} catch (OverflowException) {
throw new NotSupportedException();
}
} else {
val.PrimitiveValue = newText;
}
this.Text = newText;
return true;
} catch (NotSupportedException) {
string format = ResourceService.GetString("MainWindow.Windows.Debug.LocalVariables.CannotSetValue.BadFormat");
string msg = string.Format(format, newText, val.Type.PrimitiveType);
MessageService.ShowMessage(msg ,"${res:MainWindow.Windows.Debug.LocalVariables.CannotSetValue.Title}");
} catch (COMException) {
// COMException (0x80131330): Cannot perfrom SetValue on non-leaf frames.
// Happens if trying to set value after exception is breaked
MessageService.ShowMessage("${res:MainWindow.Windows.Debug.LocalVariables.CannotSetValue.UnknownError}",
"${res:MainWindow.Windows.Debug.LocalVariables.CannotSetValue.Title}");
}
return false;
}
public static string GetImageForMember(IDebugMemberInfo memberInfo)
{
string name = string.Empty;
if (memberInfo.IsPublic) {
} else if (memberInfo.IsAssembly) {
name += "Internal";
} else if (memberInfo.IsFamily) {
name += "Protected";
} else if (memberInfo.IsPrivate) {
name += "Private";
}
LoggingService.InfoFormatted("Evaluated node '{0}' in {1} ms", this.Name, watch.ElapsedMilliseconds);
if (memberInfo is FieldInfo) {
name += "Field";
} else if (memberInfo is PropertyInfo) {
name += "Property";
} else if (memberInfo is MethodInfo) {
name += "Method";
} else {
throw new DebuggerException("Unknown member type " + memberInfo.GetType().FullName);
} catch (GetValueException e) {
error = e;
this.Value = e.Message;
this.Type = string.Empty;
this.GetChildren = null;
this.VisualizerCommands = null;
return;
}
return "Icons.16x16." + name;
}
// public ContextMenuStrip GetContextMenu()
@ -390,54 +216,72 @@ namespace Debugger.AddIn.TreeModel @@ -390,54 +216,72 @@ namespace Debugger.AddIn.TreeModel
// return menu;
// }
public ContextMenuStrip GetErrorContextMenu()
ContextMenuStrip GetErrorContextMenu()
{
ContextMenuStrip menu = new ContextMenuStrip();
ToolStripMenuItem showError;
showError = new ToolStripMenuItem();
ToolStripMenuItem showError = new ToolStripMenuItem();
showError.Text = StringParser.Parse("${res:MainWindow.Windows.Debug.LocalVariables.ShowFullError}");
showError.Checked = false;
showError.Click += delegate {
MessageService.ShowException(error, null);
};
menu.Items.AddRange(new ToolStripItem[] {
showError
});
showError.Click += delegate { MessageService.ShowException(error, null); };
menu.Items.Add(showError);
return menu;
}
public static WindowsDebugger WindowsDebugger {
get {
return (WindowsDebugger)DebuggerService.CurrentDebugger;
public static string GetImageForMember(IDebugMemberInfo memberInfo)
{
string name = string.Empty;
if (memberInfo.IsPublic) {
} else if (memberInfo.IsAssembly) {
name += "Internal";
} else if (memberInfo.IsFamily) {
name += "Protected";
} else if (memberInfo.IsPrivate) {
name += "Private";
}
if (memberInfo is FieldInfo) {
name += "Field";
} else if (memberInfo is PropertyInfo) {
name += "Property";
} else if (memberInfo is MethodInfo) {
name += "Method";
} else {
throw new DebuggerException("Unknown member type " + memberInfo.GetType().FullName);
}
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
return "Icons.16x16." + name;
}
private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
/// <summary>
/// The root of any node evaluation is valid stack frame.
/// </summary>
static StackFrame GetCurrentStackFrame()
{
PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(info));
}
}
var debugger = DebuggerService.CurrentDebugger as WindowsDebugger;
if (debugger == null || debugger.DebuggedProcess == null)
throw new GetValueException("Debugger is not running");
if (debugger.DebuggedProcess.IsRunning)
throw new GetValueException("Process is not paused");
if (debugger.DebuggedProcess.SelectedStackFrame == null)
throw new GetValueException("No stack frame selected");
return debugger.DebuggedProcess.SelectedStackFrame;
}
public partial class Utils
{
public static IEnumerable<TreeNode> GetLocalVariableNodes(StackFrame stackFrame)
public static IEnumerable<TreeNode> GetLocalVariables()
{
var stackFrame = GetCurrentStackFrame();
foreach(DebugParameterInfo par in stackFrame.MethodInfo.GetParameters()) {
var parCopy = par;
yield return new ExpressionNode("Icons.16x16.Parameter", par.Name, () => parCopy.GetValue(stackFrame));
yield return new ValueNode("Icons.16x16.Parameter", par.Name, () => parCopy.GetValue(GetCurrentStackFrame()));
}
if (stackFrame.HasSymbols) {
foreach(DebugLocalVariableInfo locVar in stackFrame.MethodInfo.GetLocalVariables(stackFrame.IP)) {
var locVarCopy = locVar;
yield return new ExpressionNode("Icons.16x16.Local", locVar.Name, () => locVarCopy.GetValue(stackFrame));
yield return new ValueNode("Icons.16x16.Local", locVar.Name, () => locVarCopy.GetValue(GetCurrentStackFrame()));
}
} else {
WindowsDebugger debugger = (WindowsDebugger)DebuggerService.CurrentDebugger;
@ -446,19 +290,19 @@ namespace Debugger.AddIn.TreeModel @@ -446,19 +290,19 @@ namespace Debugger.AddIn.TreeModel
int methodToken = stackFrame.MethodInfo.MetadataToken;
foreach (var localVar in debugger.debuggerDecompilerService.GetLocalVariables(typeToken, methodToken)) {
int index = ((int[])debugger.debuggerDecompilerService.GetLocalVariableIndex(typeToken, methodToken, localVar))[0];
yield return new ExpressionNode("Icons.16x16.Local", localVar, () => stackFrame.GetLocalVariableValue((uint)index));
}
}
yield return new ValueNode("Icons.16x16.Local", localVar, () => {
var newStackFrame = GetCurrentStackFrame();
if (newStackFrame.MethodInfo != stackFrame.MethodInfo)
throw new GetValueException("Expected stack frame: " + stackFrame.MethodInfo.ToString());
return newStackFrame.GetLocalVariableValue((uint)index);
});
}
if (stackFrame.Thread.CurrentException != null) {
yield return new ExpressionNode(null, "$exception", () => stackFrame.Thread.CurrentException.Value);
}
}
}
public partial class Utils
{
public static IEnumerable<TreeNode> GetChildNodesOfObject(ExpressionNode expr, DebugType shownType)
IEnumerable<TreeNode> GetObjectChildren(DebugType shownType)
{
MemberInfo[] publicStatic = shownType.GetFieldsAndNonIndexedProperties(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly);
MemberInfo[] publicInstance = shownType.GetFieldsAndNonIndexedProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
@ -472,14 +316,14 @@ namespace Debugger.AddIn.TreeModel @@ -472,14 +316,14 @@ namespace Debugger.AddIn.TreeModel
StringParser.Parse("${res:MainWindow.Windows.Debug.LocalVariables.BaseClass}"),
baseType.Name,
baseType.FullName,
baseType.FullName == "System.Object" ? (Func<IEnumerable<TreeNode>>) null : () => Utils.GetChildNodesOfObject(expr, baseType)
baseType.FullName == "System.Object" ? (Func<IEnumerable<TreeNode>>) null : () => GetObjectChildren(baseType)
);
}
if (nonPublicInstance.Length > 0) {
yield return new TreeNode(
StringParser.Parse("${res:MainWindow.Windows.Debug.LocalVariables.NonPublicMembers}"),
() => GetMembersOfObject(expr, nonPublicInstance)
() => GetMembers(nonPublicInstance)
);
}
@ -487,11 +331,11 @@ namespace Debugger.AddIn.TreeModel @@ -487,11 +331,11 @@ namespace Debugger.AddIn.TreeModel
yield return new TreeNode(
StringParser.Parse("${res:MainWindow.Windows.Debug.LocalVariables.StaticMembers}"),
() => {
var children = GetMembersOfObject(expr, publicStatic).ToList();
var children = GetMembers(publicStatic).ToList();
if (nonPublicStatic.Length > 0) {
children.Insert(0, new TreeNode(
StringParser.Parse("${res:MainWindow.Windows.Debug.LocalVariables.NonPublicStaticMembers}"),
() => GetMembersOfObject(expr, nonPublicStatic)
() => GetMembers(nonPublicStatic)
));
}
return children;
@ -502,70 +346,59 @@ namespace Debugger.AddIn.TreeModel @@ -502,70 +346,59 @@ namespace Debugger.AddIn.TreeModel
if (shownType.GetInterface(typeof(IList).FullName) != null) {
yield return new TreeNode(
"IList",
() => GetItemsOfIList(() => expr.Evaluate())
() => GetIListChildren(GetValue)
);
} else {
DebugType listType, iEnumerableType, itemType;
DebugType iEnumerableType, itemType;
if (shownType.ResolveIEnumerableImplementation(out iEnumerableType, out itemType)) {
yield return new TreeNode(
null,
"IEnumerable",
"Expanding will enumerate the IEnumerable",
string.Empty,
() => GetItemsOfIList(() => DebuggerHelpers.CreateListFromIEnumeralbe(expr.Evaluate(), itemType, out listType))
() => GetIListChildren(() => DebuggerHelpers.CreateListFromIEnumeralbe(GetValue()))
);
}
}
foreach(TreeNode node in GetMembersOfObject(expr, publicInstance)) {
foreach(TreeNode node in GetMembers(publicInstance)) {
yield return node;
}
}
public static IEnumerable<TreeNode> GetMembersOfObject(ExpressionNode expr, MemberInfo[] members)
IEnumerable<TreeNode> GetMembers(MemberInfo[] members)
{
foreach(MemberInfo memberInfo in members.OrderBy(m => m.Name)) {
var memberInfoCopy = memberInfo;
string imageName = ExpressionNode.GetImageForMember((IDebugMemberInfo)memberInfo);
yield return new ExpressionNode(imageName, memberInfo.Name, () => expr.Evaluate().GetMemberValue(memberInfoCopy));
string imageName = GetImageForMember((IDebugMemberInfo)memberInfo);
yield return new ValueNode(imageName, memberInfo.Name, () => GetValue().GetMemberValue(memberInfoCopy));
}
}
public static IEnumerable<TreeNode> GetItemsOfIList(Func<Value> getValue)
static IEnumerable<TreeNode> GetIListChildren(Func<Value> getValue)
{
Value list = null;
DebugType iListType = null;
Value list;
PropertyInfo itemProp;
int count = 0;
GetValueException error = null;
try {
// We use lambda for the value just so that we can get it in this try-catch block
// TODO: We want new list on reeval
// We need the list to survive generation of index via Eval
list = getValue().GetPermanentReference();
iListType = (DebugType)list.Type.GetInterface(typeof(IList).FullName);
DebugType iListType = (DebugType)list.Type.GetInterface(typeof(IList).FullName);
itemProp = iListType.GetProperty("Item");
// Do not get string representation since it can be printed in hex
count = (int)list.GetPropertyValue(iListType.GetProperty("Count")).PrimitiveValue;
} catch (GetValueException e) {
// Cannot yield a value in the body of a catch clause (CS1631)
error = e;
return new [] { new TreeNode(null, "(error)", e.Message, string.Empty, null) };
}
if (error != null) {
yield return new TreeNode(null, "(error)", error.Message, string.Empty, null);
} else if (count == 0) {
yield return new TreeNode("(empty)", null);
if (count == 0) {
return new [] { new TreeNode("(empty)", null) };
} else {
PropertyInfo pi = iListType.GetProperty("Item");
for(int i = 0; i < count; i++) {
int iCopy = i;
yield return new ExpressionNode("Icons.16x16.Field", "[" + i + "]", () => list.GetPropertyValue(pi, Eval.CreateValue(list.AppDomain, iCopy)));
}
}
return Enumerable.Range(0, count).Select(i => new ValueNode("Icons.16x16.Field", "[" + i + "]", () => list.GetPropertyValue(itemProp, Eval.CreateValue(list.AppDomain, i))));
}
}
public partial class Utils
{
const int MaxElementCount = 100;
public static TreeNode GetArrayRangeNode(ExpressionNode expr, ArrayDimensions bounds, ArrayDimensions originalBounds)
TreeNode GetArraySubsetNode(ArrayDimensions bounds, ArrayDimensions originalBounds)
{
StringBuilder name = new StringBuilder();
bool isFirst = true;
@ -590,11 +423,13 @@ namespace Debugger.AddIn.TreeModel @@ -590,11 +423,13 @@ namespace Debugger.AddIn.TreeModel
}
name.Append("]");
return new TreeNode(name.ToString(), () => GetChildNodesOfArray(expr, bounds, originalBounds));
return new TreeNode(name.ToString(), () => GetArrayChildren(bounds, originalBounds));
}
public static IEnumerable<TreeNode> GetChildNodesOfArray(ExpressionNode arrayTarget, ArrayDimensions bounds, ArrayDimensions originalBounds)
IEnumerable<TreeNode> GetArrayChildren(ArrayDimensions bounds, ArrayDimensions originalBounds)
{
const int MaxElementCount = 1000;
if (bounds.TotalElementCount == 0)
{
yield return new TreeNode("(empty)", null);
@ -602,7 +437,7 @@ namespace Debugger.AddIn.TreeModel @@ -602,7 +437,7 @@ namespace Debugger.AddIn.TreeModel
}
// The whole array is small - just add all elements as childs
if (bounds.TotalElementCount <= MaxElementCount) {
if (bounds.TotalElementCount <= MaxElementCount * 2) {
foreach(int[] indices in bounds.Indices) {
StringBuilder sb = new StringBuilder(indices.Length * 4);
sb.Append("[");
@ -614,7 +449,7 @@ namespace Debugger.AddIn.TreeModel @@ -614,7 +449,7 @@ namespace Debugger.AddIn.TreeModel
}
sb.Append("]");
int[] indicesCopy = indices;
yield return new ExpressionNode("Icons.16x16.Field", sb.ToString(), () => arrayTarget.Evaluate().GetArrayElement(indicesCopy));
yield return new ValueNode("Icons.16x16.Field", sb.ToString(), () => GetValue().GetArrayElement(indicesCopy));
}
yield break;
}
@ -637,7 +472,7 @@ namespace Debugger.AddIn.TreeModel @@ -637,7 +472,7 @@ namespace Debugger.AddIn.TreeModel
for(int i = splitDim.LowerBound; i <= splitDim.UpperBound; i += elementsPerSegment) {
List<ArrayDimension> newDims = new List<ArrayDimension>(bounds);
newDims[splitDimensionIndex] = new ArrayDimension(i, Math.Min(i + elementsPerSegment - 1, splitDim.UpperBound));
yield return GetArrayRangeNode(arrayTarget, new ArrayDimensions(newDims), originalBounds);
yield return GetArraySubsetNode(new ArrayDimensions(newDims), originalBounds);
}
yield break;
}

0
src/Main/Base/Project/Src/Services/Debugger/Tooltips/IVisualizerCommand.cs → src/AddIns/Debugger/Debugger.AddIn/Visualizers/Common/IVisualizerCommand.cs

2
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/TreeModel/ContentPropertyNode.cs

@ -78,7 +78,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -78,7 +78,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
if ((this.Property != null) && (this.Property.ObjectGraphProperty != null)) {
var memberInfo = (IDebugMemberInfo)this.Property.ObjectGraphProperty.MemberInfo;
if (memberInfo != null) {
var image = new ResourceServiceImage(ExpressionNode.GetImageForMember(memberInfo));
var image = new ResourceServiceImage(ValueNode.GetImageForMember(memberInfo));
this.MemberIcon = image.ImageSource;
}
}

3
src/AddIns/Debugger/Debugger.AddIn/Visualizers/GridVisualizer/GridVisualizerWindow.xaml.cs

@ -47,7 +47,8 @@ namespace Debugger.AddIn.Visualizers.GridVisualizer @@ -47,7 +47,8 @@ namespace Debugger.AddIn.Visualizers.GridVisualizer
} else {
// Value is IEnumerable
if (shownValue.Type.ResolveIEnumerableImplementation(out iEnumerableType, out itemType)) {
shownValue = DebuggerHelpers.CreateListFromIEnumeralbe(shownValue, itemType, out iListType);
shownValue = DebuggerHelpers.CreateListFromIEnumeralbe(shownValue);
iListType = DebugType.CreateFromType(shownValue.AppDomain, typeof(System.Collections.Generic.IList<>), itemType);
} else {
// Not IList or IEnumerable<T> - can't be displayed in GridVisualizer
return;

13
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Utils/DebuggerHelpers.cs

@ -34,14 +34,19 @@ namespace Debugger.AddIn.Visualizers.Utils @@ -34,14 +34,19 @@ namespace Debugger.AddIn.Visualizers.Utils
}
public static Value CreateListFromIEnumeralbe(Value iEnumerableValue, DebugType itemType, out DebugType listType)
public static Value CreateListFromIEnumeralbe(Value iEnumerableValue)
{
listType = DebugType.CreateFromType(iEnumerableValue.AppDomain, typeof(System.Collections.Generic.List<>), itemType);
DebugType iEnumerableType = DebugType.CreateFromType(itemType.AppDomain, typeof(IEnumerable<>), itemType);
DebugType iEnumerableType, itemType;
if (!iEnumerableValue.Type.ResolveIEnumerableImplementation(out iEnumerableType, out itemType))
throw new GetValueException("Value is not IEnumeralbe");
DebugType listType = DebugType.CreateFromType(iEnumerableValue.AppDomain, typeof(System.Collections.Generic.List<>), itemType);
ConstructorInfo ctor = listType.GetConstructor(BindingFlags.Default, null, CallingConventions.Any, new System.Type[] { iEnumerableType }, null);
if (ctor == null)
throw new DebuggerException("List<T> constructor not found");
return (Value)ctor.Invoke(new object[] { iEnumerableValue });
// Keep reference since we do not want to keep reenumerating it
return ((Value)ctor.Invoke(new object[] { iEnumerableValue })).GetPermanentReference();
}
/// <summary>

2
src/AddIns/Debugger/Debugger.Core/Eval.cs

@ -246,7 +246,7 @@ namespace Debugger @@ -246,7 +246,7 @@ namespace Debugger
Value arg = args[i];
DebugType paramType = (DebugType)method.GetParameters()[i].ParameterType;
if (!arg.Type.CanImplicitelyConvertTo(paramType))
throw new GetValueException("Inncorrect parameter type");
throw new GetValueException("Inncorrect parameter type. Expected " + paramType.ToString());
// Implicitely convert to correct primitve type
if (paramType.IsPrimitive && args[i].Type != paramType) {
object oldPrimVal = arg.PrimitiveValue;

11
src/AddIns/Debugger/Debugger.Core/MetaData/DebugLocalVariableInfo.cs

@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Reflection;
namespace Debugger.MetaData
{
@ -11,6 +12,8 @@ namespace Debugger.MetaData @@ -11,6 +12,8 @@ namespace Debugger.MetaData
int localIndex;
DebugType localType;
public DebugMethodInfo Member { get; private set; }
/// <inheritdoc/>
public override int LocalIndex {
get { return localIndex; }
@ -34,8 +37,9 @@ namespace Debugger.MetaData @@ -34,8 +37,9 @@ namespace Debugger.MetaData
public bool IsThis { get; internal set; }
public bool IsCaptured { get; internal set; }
public DebugLocalVariableInfo(string name, int localIndex, int startOffset, int endOffset, DebugType localType, ValueGetter getter)
public DebugLocalVariableInfo(DebugMethodInfo member, string name, int localIndex, int startOffset, int endOffset, DebugType localType, ValueGetter getter)
{
this.Member = member;
this.Name = name;
this.localIndex = localIndex;
this.StartOffset = startOffset;
@ -46,6 +50,11 @@ namespace Debugger.MetaData @@ -46,6 +50,11 @@ namespace Debugger.MetaData
public Value GetValue(StackFrame context)
{
if (context == null)
throw new ArgumentNullException("context");
if (context.MethodInfo != this.Member)
throw new GetValueException("Expected stack frame: " + this.Member.ToString());
return getter(context);
}

5
src/AddIns/Debugger/Debugger.Core/MetaData/DebugMethodInfo.cs

@ -578,6 +578,7 @@ namespace Debugger.MetaData @@ -578,6 +578,7 @@ namespace Debugger.MetaData
// Add this
if (!this.IsStatic) {
DebugLocalVariableInfo thisVar = new DebugLocalVariableInfo(
this,
"this",
-1,
0, int.MaxValue,
@ -593,13 +594,14 @@ namespace Debugger.MetaData @@ -593,13 +594,14 @@ namespace Debugger.MetaData
return localVariables;
}
static void AddCapturedLocalVariables(List<DebugLocalVariableInfo> vars, int scopeStartOffset, int scopeEndOffset, ValueGetter getCaptureClass, DebugType captureClassType)
void AddCapturedLocalVariables(List<DebugLocalVariableInfo> vars, int scopeStartOffset, int scopeEndOffset, ValueGetter getCaptureClass, DebugType captureClassType)
{
if (captureClassType.IsDisplayClass || captureClassType.IsYieldEnumerator || captureClassType.IsAsyncStateMachine) {
foreach(DebugFieldInfo fieldInfo in captureClassType.GetFields()) {
DebugFieldInfo fieldInfoCopy = fieldInfo;
if (fieldInfo.Name.StartsWith("CS$")) continue; // Ignore
DebugLocalVariableInfo locVar = new DebugLocalVariableInfo(
this,
fieldInfo.Name,
-1,
scopeStartOffset,
@ -661,6 +663,7 @@ namespace Debugger.MetaData @@ -661,6 +663,7 @@ namespace Debugger.MetaData
}
} else {
DebugLocalVariableInfo locVar = new DebugLocalVariableInfo(
this,
symVar.GetName(),
(int)symVar.GetAddressField1(),
// symVar also has Get*Offset methods, but the are not implemented

5
src/AddIns/Debugger/Debugger.Core/MetaData/DebugParameterInfo.cs

@ -45,6 +45,11 @@ namespace Debugger.MetaData @@ -45,6 +45,11 @@ namespace Debugger.MetaData
public Value GetValue(StackFrame context)
{
if (context == null)
throw new ArgumentNullException("context");
if (context.MethodInfo != this.Member)
throw new GetValueException("Expected stack frame: " + this.Member.ToString());
return getter(context);
}

3
src/AddIns/Debugger/Debugger.Core/StackFrame.cs

@ -345,6 +345,8 @@ namespace Debugger @@ -345,6 +345,8 @@ namespace Debugger
ICorDebugValue GetArgumentCorValue(int index)
{
this.Process.AssertPaused();
ICorDebugValue corValue;
try {
// Non-static methods include 'this' as first argument
@ -382,6 +384,7 @@ namespace Debugger @@ -382,6 +384,7 @@ namespace Debugger
public Value GetLocalVariableValue(uint address)
{
this.Process.AssertPaused();
try {
return new Value(this.AppDomain, this.CorILFrame.GetLocalVariable(address));
} catch (COMException e) {

79
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorView.cs

@ -231,7 +231,7 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -231,7 +231,7 @@ namespace ICSharpCode.AvalonEdit.AddIn
#region Tooltip
ToolTip toolTip;
Popup popup;
Popup popupToolTip;
void TextEditorMouseHover(object sender, MouseEventArgs e)
{
@ -272,26 +272,19 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -272,26 +272,19 @@ namespace ICSharpCode.AvalonEdit.AddIn
ToolTipRequestService.RequestToolTip(args);
}
if (args.ContentToShow != null) {
var contentToShowITooltip = args.ContentToShow as ITooltip;
if (contentToShowITooltip != null && contentToShowITooltip.ShowAsPopup) {
if (!(args.ContentToShow is UIElement)) {
throw new NotSupportedException("Content to show in Popup must be UIElement: " + args.ContentToShow);
}
if (popup == null) {
popup = CreatePopup();
if (!TryCloseExistingPopup(false)) {
return;
}
// if popup was only first level, hovering somewhere else closes it
if (TryCloseExistingPopup(false)) {
// when popup content decides to close, close the popup
contentToShowITooltip.Closed += (closedSender, closedArgs) => { popup.IsOpen = false; };
popup.Child = (UIElement)args.ContentToShow;
//ICSharpCode.SharpDevelop.Debugging.DebuggerService.CurrentDebugger.IsProcessRunningChanged
SetPopupPosition(popup, e);
popup.IsOpen = true;
}
if (args.ContentToShow != null) {
popupToolTip = args.ContentToShow as Popup;
if (popupToolTip != null) {
var popupPosition = GetPopupPosition(e);
popupToolTip.HorizontalOffset = popupPosition.X;
popupToolTip.VerticalOffset = popupPosition.Y;
popupToolTip.IsOpen = true;
popupToolTip.StaysOpen = true; // We will close it ourselves
e.Handled = true;
} else {
if (toolTip == null) {
@ -313,35 +306,19 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -313,35 +306,19 @@ namespace ICSharpCode.AvalonEdit.AddIn
toolTip.IsOpen = true;
e.Handled = true;
}
} else {
// close popup if mouse hovered over empty area
if (popup != null) {
e.Handled = true;
}
TryCloseExistingPopup(false);
}
}
bool TryCloseExistingPopup(bool hard)
bool TryCloseExistingPopup(bool mouseClick)
{
bool canClose = true;
if (popup != null) {
var popupContentITooltip = popup.Child as ITooltip;
if (popupContentITooltip != null) {
canClose = popupContentITooltip.Close(hard);
}
if (canClose) {
popup.IsOpen = false;
if (popupToolTip != null) {
if (popupToolTip.IsOpen && !mouseClick && popupToolTip is ITooltip && !((ITooltip)popupToolTip).CloseOnHoverEnd) {
return false; // Popup does not want to be closed yet
}
popupToolTip.IsOpen = false;
popupToolTip = null;
}
return canClose;
}
void SetPopupPosition(Popup popup, MouseEventArgs mouseArgs)
{
var popupPosition = GetPopupPosition(mouseArgs);
popup.HorizontalOffset = popupPosition.X;
popup.VerticalOffset = popupPosition.Y;
return true;
}
/// <summary> Returns Popup position based on mouse position, in device independent units </summary>
@ -364,17 +341,6 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -364,17 +341,6 @@ namespace ICSharpCode.AvalonEdit.AddIn
return positionInPixels.TransformFromDevice(this);
}
Popup CreatePopup()
{
popup = new Popup();
popup.Closed += PopupClosed;
popup.AllowsTransparency = true;
popup.PlacementTarget = this; // required for property inheritance
popup.Placement = PlacementMode.Absolute;
popup.StaysOpen = true;
return popup;
}
void TextEditorMouseHoverStopped(object sender, MouseEventArgs e)
{
if (toolTip != null) {
@ -385,7 +351,7 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -385,7 +351,7 @@ namespace ICSharpCode.AvalonEdit.AddIn
void TextEditorMouseLeave(object sender, MouseEventArgs e)
{
if (popup != null && !popup.IsMouseOver) {
if (popupToolTip != null && !popupToolTip.IsMouseOver) {
// do not close popup if mouse moved from editor to popup
TryCloseExistingPopup(false);
}
@ -396,11 +362,6 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -396,11 +362,6 @@ namespace ICSharpCode.AvalonEdit.AddIn
toolTip = null;
}
void PopupClosed(object sender, EventArgs e)
{
popup = null;
}
#region GetTooltipTextForCollapsedSection
string GetTooltipTextForCollapsedSection(FoldingSection foldingSection)
{

72
src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/CodeView.cs

@ -76,7 +76,7 @@ namespace ICSharpCode.ILSpyAddIn.ViewContent @@ -76,7 +76,7 @@ namespace ICSharpCode.ILSpyAddIn.ViewContent
#region Popup
ToolTip toolTip;
Popup popup;
Popup popupToolTip;
void TextEditorMouseHover(object sender, MouseEventArgs e)
{
@ -92,24 +92,19 @@ namespace ICSharpCode.ILSpyAddIn.ViewContent @@ -92,24 +92,19 @@ namespace ICSharpCode.ILSpyAddIn.ViewContent
ToolTipRequestService.RequestToolTip(args);
}
if (args.ContentToShow != null) {
var contentToShowITooltip = args.ContentToShow as ITooltip;
if (contentToShowITooltip != null && contentToShowITooltip.ShowAsPopup) {
if (!(args.ContentToShow is UIElement)) {
throw new NotSupportedException("Content to show in Popup must be UIElement: " + args.ContentToShow);
}
if (popup == null) {
popup = CreatePopup();
}
if (TryCloseExistingPopup(false)) {
// when popup content decides to close, close the popup
contentToShowITooltip.Closed += (closedSender, closedArgs) => { popup.IsOpen = false; };
popup.Child = (UIElement)args.ContentToShow;
//ICSharpCode.SharpDevelop.Debugging.DebuggerService.CurrentDebugger.IsProcessRunningChanged
SetPopupPosition(popup, e);
popup.IsOpen = true;
if (!TryCloseExistingPopup(false)) {
return;
}
if (args.ContentToShow != null) {
popupToolTip = args.ContentToShow as Popup;
if (popupToolTip != null) {
var popupPosition = GetPopupPosition(e);
popupToolTip.HorizontalOffset = popupPosition.X;
popupToolTip.VerticalOffset = popupPosition.Y;
popupToolTip.IsOpen = true;
popupToolTip.StaysOpen = true; // We will close it ourselves
e.Handled = true;
} else {
if (toolTip == null) {
@ -131,35 +126,19 @@ namespace ICSharpCode.ILSpyAddIn.ViewContent @@ -131,35 +126,19 @@ namespace ICSharpCode.ILSpyAddIn.ViewContent
toolTip.IsOpen = true;
e.Handled = true;
}
} else {
// close popup if mouse hovered over empty area
if (popup != null) {
e.Handled = true;
}
TryCloseExistingPopup(false);
}
}
bool TryCloseExistingPopup(bool mouseClick)
{
bool canClose = true;
if (popup != null) {
var popupContentITooltip = popup.Child as ITooltip;
if (popupContentITooltip != null) {
canClose = popupContentITooltip.Close(mouseClick);
}
if (canClose) {
popup.IsOpen = false;
if (popupToolTip != null) {
if (popupToolTip.IsOpen && !mouseClick && popupToolTip is ITooltip && !((ITooltip)popupToolTip).CloseOnHoverEnd) {
return false; // Popup does not want to be closed yet
}
popupToolTip.IsOpen = false;
popupToolTip = null;
}
return canClose;
}
void SetPopupPosition(Popup popup, MouseEventArgs mouseArgs)
{
var popupPosition = GetPopupPosition(mouseArgs);
popup.HorizontalOffset = popupPosition.X;
popup.VerticalOffset = popupPosition.Y;
return true;
}
/// <summary> Returns Popup position based on mouse position, in device independent units </summary>
@ -182,17 +161,6 @@ namespace ICSharpCode.ILSpyAddIn.ViewContent @@ -182,17 +161,6 @@ namespace ICSharpCode.ILSpyAddIn.ViewContent
return positionInPixels.TransformFromDevice(this);
}
Popup CreatePopup()
{
popup = new Popup();
popup.Closed += (s, e) => popup = null;
popup.AllowsTransparency = true;
popup.PlacementTarget = this; // required for property inheritance
popup.Placement = PlacementMode.Absolute;
popup.StaysOpen = true;
return popup;
}
void TextEditorMouseHoverStopped(object sender, MouseEventArgs e)
{
if (toolTip != null) {
@ -205,7 +173,7 @@ namespace ICSharpCode.ILSpyAddIn.ViewContent @@ -205,7 +173,7 @@ namespace ICSharpCode.ILSpyAddIn.ViewContent
void TextEditorMouseLeave(object sender, MouseEventArgs e)
{
if (popup != null && !popup.IsMouseOver) {
if (popupToolTip != null && !popupToolTip.IsMouseOver) {
// do not close popup if mouse moved from editor to popup
TryCloseExistingPopup(false);
}

5
src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj

@ -377,10 +377,6 @@ @@ -377,10 +377,6 @@
<Compile Include="Src\Services\Debugger\BreakpointBookmark.cs" />
<Compile Include="Src\Services\Debugger\BreakpointBookmarkEventArgs.cs" />
<Compile Include="Src\Services\Debugger\DecompiledBreakpointBookmark.cs" />
<Compile Include="Src\Services\Debugger\Tooltips\IPinDebuggerControl.cs" />
<Compile Include="Src\Services\Debugger\Tooltips\ITreeNode.cs" />
<Compile Include="Src\Services\Debugger\Tooltips\IVisualizerCommand.cs" />
<Compile Include="Src\Services\Debugger\Tooltips\PinBookmark.cs" />
<Compile Include="Src\Services\DisplayBinding\AutoDetectDisplayBinding.cs" />
<Compile Include="Src\Services\DisplayBinding\ExternalProcessDisplayBinding.cs" />
<Compile Include="Src\Services\DisplayBinding\ISecondaryDisplayBinding.cs" />
@ -921,7 +917,6 @@ @@ -921,7 +917,6 @@
<Folder Include="Src\Editor\Commands" />
<Folder Include="Src\Editor\Search" />
<Folder Include="Src\Gui\Pads\TaskList" />
<Folder Include="Src\Services\Debugger\Tooltips" />
<Folder Include="Src\Services\RefactoringService\ContextActions" />
<Folder Include="Src\Util" />
<Folder Include="Src\Gui\Pads\ClassBrowser\NodeBuilder" />

42
src/Main/Base/Project/Src/Bookmarks/BookmarkConverter.cs

@ -64,24 +64,6 @@ namespace ICSharpCode.SharpDevelop.Bookmarks @@ -64,24 +64,6 @@ namespace ICSharpCode.SharpDevelop.Bookmarks
bbm.Condition = script;
bookmark = bbm;
break;
case "PinBookmark":
var pin = new PinBookmark(fileName, new Location(columnNumber, lineNumber));
pin.Comment = v[4];
pin.PinPosition =
new Point
{
X = double.Parse(v[5], culture),
Y = double.Parse(v[6], culture)
};
// pop-up nodes
pin.SavedNodes = new System.Collections.Generic.List<Tuple<string, string, string>>();
for (int i = 7; i < v.Length; i+=3) {
pin.SavedNodes.Add(new Tuple<string, string, string>(v[i], v[i+1], v[i+2]));
}
bookmark = pin;
break;
default:
bookmark = new Bookmark(fileName, new Location(columnNumber, lineNumber));
break;
@ -102,9 +84,6 @@ namespace ICSharpCode.SharpDevelop.Bookmarks @@ -102,9 +84,6 @@ namespace ICSharpCode.SharpDevelop.Bookmarks
} else if (bookmark is Debugging.BreakpointBookmark) {
b.Append("Breakpoint");
} else {
if (bookmark is PinBookmark)
b.Append("PinBookmark");
else
b.Append("Bookmark");
}
b.Append('|');
@ -136,27 +115,6 @@ namespace ICSharpCode.SharpDevelop.Bookmarks @@ -136,27 +115,6 @@ namespace ICSharpCode.SharpDevelop.Bookmarks
b.Append(bbm.Condition);
}
if (bookmark is PinBookmark) {
var pin = (PinBookmark)bookmark;
b.Append('|');
b.Append(pin.Comment ?? string.Empty);
// popup position
b.Append('|');
b.Append(pin.PinPosition.Value.X);
b.Append('|');
b.Append(pin.PinPosition.Value.Y);
//popup nodes
foreach(var node in pin.Nodes) {
b.Append('|');
b.Append(node.ImageName);
b.Append('|');
b.Append(node.Name);
b.Append('|');
b.Append(node.Text);
}
}
return b.ToString();
} else {
return base.ConvertTo(context, culture, value, destinationType);

25
src/Main/Base/Project/Src/Editor/ITooltip.cs

@ -6,30 +6,9 @@ using System.Windows; @@ -6,30 +6,9 @@ using System.Windows;
namespace ICSharpCode.SharpDevelop.Editor
{
/// <summary>
/// Content of text editor tooltip (used as <see cref="ToolTipRequestEventArgs.ContentToShow"/>),
/// specifying whether it should be displayed in a WPF Popup.
/// </summary>
public interface ITooltip
{
/// <summary>
/// If true, this ITooltip will be displayed in a WPF Popup.
/// Otherwise it will be displayed in a WPF Tooltip.
/// WPF Popups are (unlike WPF Tooltips) focusable.
/// </summary>
bool ShowAsPopup { get; }
/// <summary>
/// Closes this tooltip.
/// </summary>
/// <param name="mouseClick">True if close request is raised
/// because of mouse click on some SharpDevelop GUI element.</param>
/// <returns>True if Close succeeded (that is, can close). False otherwise.</returns>
bool Close(bool mouseClick);
/// <summary>
/// Occurs when this tooltip decides to close.
/// </summary>
event RoutedEventHandler Closed;
/// <summary> Should the tooltip close when the mouse moves away? </summary>
bool CloseOnHoverEnd { get; }
}
}

20
src/Main/Base/Project/Src/Services/Debugger/Tooltips/IPinDebuggerControl.cs

@ -1,20 +0,0 @@ @@ -1,20 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Windows;
using ICSharpCode.SharpDevelop.Bookmarks;
namespace ICSharpCode.SharpDevelop.Debugging
{
public interface IPinDebuggerControl
{
void Open();
void Close();
PinBookmark Mark { get; set; }
IEnumerable<ITreeNode> ItemsSource { set; }
Point Location { get; set; }
}
}

37
src/Main/Base/Project/Src/Services/Debugger/Tooltips/ITreeNode.cs

@ -1,37 +0,0 @@ @@ -1,37 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Windows.Media;
namespace ICSharpCode.SharpDevelop.Debugging
{
/// <summary>
/// Node that can be bound to <see cref="DebuggerTooltipControl" />.
/// </summary>
public interface ITreeNode
{
string ImageName { get; }
string Name { get; }
string Text { get; }
bool CanSetText { get; }
string Type { get; }
ImageSource ImageSource { get; }
Func<IEnumerable<ITreeNode>> GetChildren { get; }
IEnumerable<IVisualizerCommand> VisualizerCommands { get; }
bool HasVisualizerCommands { get; }
bool IsPinned { get; set; }
bool SetText(string newValue);
}
}

102
src/Main/Base/Project/Src/Services/Debugger/Tooltips/PinBookmark.cs

@ -1,102 +0,0 @@ @@ -1,102 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Windows;
using ICSharpCode.Core;
using ICSharpCode.NRefactory;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Bookmarks;
using ICSharpCode.SharpDevelop.Debugging;
namespace ICSharpCode.SharpDevelop.Bookmarks
{
public class PinBookmark : SDBookmark
{
string tooltip;
public IPinDebuggerControl Popup { get; set; }
public static readonly IImage PinImage = new ResourceServiceImage("Bookmarks.Pin");
public PinBookmark(FileName fileName, Location location) : base(fileName, location)
{
Nodes = new ObservableCollection<ITreeNode>();
IsVisibleInBookmarkPad = false;
}
/// <summary>
/// Pin's position relative to the layer BUT ABSOLUTE TO THE SCREEN.
/// </summary>
public Nullable<Point> PinPosition { get; set; }
/// <summary>
/// Nodes inside the pin control.
/// </summary>
public ObservableCollection<ITreeNode> Nodes { get; set; }
/// <summary>
/// Image, Name, Text
/// </summary>
public List<Tuple<string, string, string>> SavedNodes { get; set; }
public string Comment { get; set; }
public override IImage Image {
get {
return PinImage;
}
}
public string Tooltip {
get { return tooltip; }
set { tooltip = value; }
}
public override bool CanDragDrop {
get { return true; }
}
public override void Drop(int lineNumber)
{
this.Location = new Location(ColumnNumber, lineNumber);
}
}
public static class PinBookmarkExtensions
{
public static bool ContainsNode(this PinBookmark mark, ITreeNode node)
{
if (mark == null)
throw new ArgumentNullException("mark is null");
if (node == null)
throw new ArgumentNullException("Node is null");
foreach (var currentNode in mark.Nodes) {
if (node.Name == currentNode.Name)
return true;
}
return false;
}
public static void RemoveNode(this PinBookmark mark, ITreeNode node)
{
if (mark == null)
throw new ArgumentNullException("mark is null");
if (node == null)
throw new ArgumentNullException("Node is null");
foreach (var currentNode in mark.Nodes) {
if (node.Name == currentNode.Name) {
mark.Nodes.Remove(currentNode);
return;
}
}
}
}
}
Loading…
Cancel
Save