Browse Source

Implemented search filter for TreeView.

pull/1/head
Daniel Grunwald 15 years ago
parent
commit
212a08345a
  1. 1
      ILSpy/AssemblyListTreeNode.cs
  2. 9
      ILSpy/AssemblyTreeNode.cs
  3. 198
      ILSpy/CueBannerService.cs
  4. 8
      ILSpy/EventTreeNode.cs
  5. 8
      ILSpy/FieldTreeNode.cs
  6. 12
      ILSpy/FilterSettings.cs
  7. 1
      ILSpy/ILSpy.csproj
  8. 36
      ILSpy/ILSpyTreeNode.cs
  9. 19
      ILSpy/MainWindow.xaml
  10. 9
      ILSpy/MainWindow.xaml.cs
  11. 8
      ILSpy/MethodTreeNode.cs
  12. 10
      ILSpy/NamespaceTreeNode.cs
  13. 8
      ILSpy/PropertyTreeNode.cs
  14. 10
      ILSpy/TypeTreeNode.cs
  15. 8
      SharpTreeView/SharpTreeNodeCollection.cs
  16. 2
      SharpTreeView/SharpTreeNodeView.cs

1
ILSpy/AssemblyListTreeNode.cs

@ -48,7 +48,6 @@ namespace ICSharpCode.ILSpy
if (assemblyList == null)
throw new ArgumentNullException("assemblyList");
this.assemblyList = assemblyList;
this.FilterSettings = new FilterSettings(); // default filter
}
public override bool CanDelete(SharpTreeNode[] nodes)

9
ILSpy/AssemblyTreeNode.cs

@ -197,5 +197,14 @@ namespace ICSharpCode.ILSpy
return null;
}
}
public override FilterResult Filter(FilterSettings settings)
{
// avoid accessing this.AssemblyDefinition (waiting for background thread) if settings.SearchTerm == null
if (settings.SearchTerm == null || settings.SearchTermMatches(this.AssemblyDefinition.Name.Name))
return FilterResult.Match;
else
return FilterResult.Recurse;
}
}
}

198
ILSpy/CueBannerService.cs

@ -0,0 +1,198 @@
// Copyright (c) 2008 Jason Kemp
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Media;
namespace ICSharpCode.ILSpy
{
/// <summary>
/// Watermark for text boxes; from http://www.ageektrapped.com/blog/the-missing-net-4-cue-banner-in-wpf-i-mean-watermark-in-wpf/.
/// </summary>
public static class CueBannerService
{
//there is absolutely no way to write this statement out
//to look pretty
public static readonly DependencyProperty CueBannerProperty = DependencyProperty.RegisterAttached(
"CueBanner", typeof (object), typeof (CueBannerService),
new FrameworkPropertyMetadata("", CueBannerPropertyChanged));
public static object GetCueBanner(Control control)
{
return control.GetValue(CueBannerProperty);
}
public static void SetCueBanner(Control control, object value)
{
control.SetValue(CueBannerProperty, value);
}
private static void CueBannerPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Control control = (Control)d;
control.Loaded += control_Loaded;
if (d is ComboBox || d is TextBox)
{
control.GotFocus += control_GotFocus;
control.LostFocus += control_Loaded;
}
if (d is ItemsControl && !(d is ComboBox))
{
ItemsControl i = (ItemsControl) d;
//for Items property
i.ItemContainerGenerator.ItemsChanged += ItemsChanged;
itemsControls.Add(i.ItemContainerGenerator, i);
//for ItemsSource property
DependencyPropertyDescriptor prop =
DependencyPropertyDescriptor.FromProperty(ItemsControl.ItemsSourceProperty, i.GetType());
prop.AddValueChanged(i, ItemsSourceChanged);
}
}
private static readonly Dictionary<object, ItemsControl> itemsControls = new Dictionary<object, ItemsControl>();
private static void ItemsSourceChanged(object sender, EventArgs e)
{
ItemsControl c = (ItemsControl)sender;
if (c.ItemsSource != null)
RemoveCueBanner(c);
else
ShowCueBanner(c);
}
private static void ItemsChanged(object sender, ItemsChangedEventArgs e)
{
ItemsControl control;
if (itemsControls.TryGetValue(sender, out control))
{
if (e.ItemCount > 0)
RemoveCueBanner(control);
else
ShowCueBanner(control);
}
}
private static void control_GotFocus(object sender, RoutedEventArgs e)
{
Control c = (Control)sender;
if (ShouldShowCueBanner(c))
{
RemoveCueBanner(c);
}
}
private static void control_Loaded(object sender, RoutedEventArgs e)
{
Control control = (Control)sender;
if (ShouldShowCueBanner(control))
{
ShowCueBanner(control);
}
}
private static void RemoveCueBanner(UIElement control)
{
AdornerLayer layer = AdornerLayer.GetAdornerLayer(control);
Adorner[] adorners = layer.GetAdorners(control);
if (adorners == null) return;
foreach (Adorner adorner in adorners)
{
if (adorner is CueBannerAdorner)
{
adorner.Visibility = Visibility.Hidden;
layer.Remove(adorner);
}
}
}
private static void ShowCueBanner(Control control)
{
AdornerLayer layer = AdornerLayer.GetAdornerLayer(control);
layer.Add(new CueBannerAdorner(control, GetCueBanner(control)));
}
private static bool ShouldShowCueBanner(Control c)
{
DependencyProperty dp = GetDependencyProperty(c);
if (dp == null) return true;
return c.GetValue(dp).Equals("");
}
private static DependencyProperty GetDependencyProperty (Control control)
{
if (control is ComboBox)
return ComboBox.TextProperty;
if (control is TextBoxBase)
return TextBox.TextProperty;
return null;
}
}
internal class CueBannerAdorner : Adorner
{
private readonly ContentPresenter contentPresenter;
public CueBannerAdorner(UIElement adornedElement, object cueBanner) :
base(adornedElement)
{
this.IsHitTestVisible = false;
contentPresenter = new ContentPresenter();
contentPresenter.Content = cueBanner;
contentPresenter.Opacity = 0.7;
contentPresenter.Margin =
new Thickness(Control.Margin.Left + Control.Padding.Left,
Control.Margin.Top + Control.Padding.Top, 0, 0);
}
private Control Control
{
get { return (Control) this.AdornedElement; }
}
protected override Visual GetVisualChild(int index)
{
return contentPresenter;
}
protected override int VisualChildrenCount
{
get { return 1; }
}
protected override Size MeasureOverride(Size constraint)
{
contentPresenter.Measure(Control.RenderSize);
return Control.RenderSize;
}
protected override Size ArrangeOverride(Size finalSize)
{
contentPresenter.Arrange(new Rect(finalSize));
return finalSize;
}
}
}

8
ILSpy/EventTreeNode.cs

@ -60,5 +60,13 @@ namespace ICSharpCode.ILSpy
this.Children.Add(new MethodTreeNode(m));
}
}
public override FilterResult Filter(FilterSettings settings)
{
if (settings.SearchTermMatches(ev.Name))
return FilterResult.Match;
else
return FilterResult.Hidden;
}
}
}

8
ILSpy/FieldTreeNode.cs

@ -48,5 +48,13 @@ namespace ICSharpCode.ILSpy
return Images.Field;
}
}
public override FilterResult Filter(FilterSettings settings)
{
if (settings.SearchTermMatches(field.Name))
return FilterResult.Match;
else
return FilterResult.Hidden;
}
}
}

12
ILSpy/FilterSettings.cs

@ -24,6 +24,11 @@ namespace ICSharpCode.ILSpy
/// <summary>
/// Represents the filters applied to the tree view.
/// </summary>
/// <remarks>
/// This class is mutable; but the ILSpyTreeNode filtering assumes that filter settings are immutable.
/// Thus, the main window will use one mutable instance (for data-binding), and assign a new clone to the ILSpyTreeNodes whenever the main
/// mutable instance changes.
/// </remarks>
public class FilterSettings : INotifyPropertyChanged
{
string searchTerm;
@ -38,6 +43,13 @@ namespace ICSharpCode.ILSpy
}
}
public bool SearchTermMatches(string text)
{
if (searchTerm == null)
return true;
return text.IndexOf(searchTerm, StringComparison.OrdinalIgnoreCase) >= 0;
}
bool showInternalApi;
public bool ShowInternalApi {

1
ILSpy/ILSpy.csproj

@ -73,6 +73,7 @@
<Compile Include="AssemblyReferenceTreeNode.cs" />
<Compile Include="AssemblyTreeNode.cs" />
<Compile Include="BaseTypesTreeNode.cs" />
<Compile Include="CueBannerService.cs" />
<Compile Include="Decompiler\CSharpLanguage.cs" />
<Compile Include="EventTreeNode.cs" />
<Compile Include="ExtensionMethods.cs" />

36
ILSpy/ILSpyTreeNode.cs

@ -38,11 +38,18 @@ namespace ICSharpCode.ILSpy
}
}
public SharpTreeNodeCollection VisibleChildren {
get { return base.Children; }
}
protected abstract void OnFilterSettingsChanged();
public virtual FilterResult Filter(FilterSettings settings)
{
return FilterResult.Match;
if (string.IsNullOrEmpty(settings.SearchTerm))
return FilterResult.Match;
else
return FilterResult.Hidden;
}
public virtual void Decompile(Language language, ITextOutput output)
@ -52,8 +59,18 @@ namespace ICSharpCode.ILSpy
public enum FilterResult
{
/// <summary>
/// Hides the node.
/// </summary>
Hidden,
Match
/// <summary>
/// Shows the node.
/// </summary>
Match,
/// <summary>
/// Hides the node only if all children are hidden.
/// </summary>
Recurse
}
/// <summary>
@ -97,10 +114,15 @@ namespace ICSharpCode.ILSpy
FilterChild(child);
}
}
void FilterChild(T child)
{
switch (child.Filter(this.FilterSettings)) {
FilterResult r;
if (this.FilterSettings == null)
r = FilterResult.Match;
else
r = child.Filter(this.FilterSettings);
switch (r) {
case FilterResult.Hidden:
// don't add to base.Children
break;
@ -108,6 +130,12 @@ namespace ICSharpCode.ILSpy
base.Children.Add(child);
child.FilterSettings = StripSearchTerm(this.FilterSettings);
break;
case FilterResult.Recurse:
child.FilterSettings = this.FilterSettings;
child.EnsureLazyChildren();
if (child.VisibleChildren.Count > 0)
base.Children.Add(child);
break;
default:
throw new InvalidEnumArgumentException();
}

19
ILSpy/MainWindow.xaml

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Window
x:Class="ICSharpCode.ILSpy.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:tv="http://icsharpcode.net/sharpdevelop/treeview" xmlns:ae="http://icsharpcode.net/sharpdevelop/avalonedit"
xmlns:local="clr-namespace:ICSharpCode.ILSpy"
Title="ILSpy"
Width="790"
Height="500"
@ -83,12 +84,16 @@
<RowDefinition
Height="*" />
</Grid.RowDefinitions>
<!-- Tree View of assemblies and classes -->
<tv:SharpTreeView
Name="treeView"
ShowRoot="False"
AllowDropOrder="True"
AllowDrop="True" />
<!-- Left pane: Search bar + Tree View -->
<DockPanel>
<TextBox DockPanel.Dock="Top" local:CueBannerService.CueBanner=" Search" Text="{Binding SearchTerm, UpdateSourceTrigger=PropertyChanged}" />
<!-- Tree View of assemblies and classes -->
<tv:SharpTreeView
Name="treeView"
ShowRoot="False"
AllowDropOrder="True"
AllowDrop="True" />
</DockPanel>
<GridSplitter
Grid.ZIndex="1"
Grid.Column="1"
@ -96,7 +101,7 @@
BorderThickness="2,0"
BorderBrush="Transparent"
HorizontalAlignment="Stretch" />
<!-- DockPanel in right pane -->
<!-- Right pane: Text Editor -->
<DockPanel
Grid.Column="2">
<ae:TextEditor

9
ILSpy/MainWindow.xaml.cs

@ -36,6 +36,7 @@ namespace ICSharpCode.ILSpy
public partial class MainWindow : Window
{
AssemblyList assemblyList = new AssemblyList();
FilterSettings filterSettings = new FilterSettings();
static readonly Assembly[] initialAssemblies = {
typeof(object).Assembly,
@ -53,10 +54,18 @@ namespace ICSharpCode.ILSpy
public MainWindow()
{
this.DataContext = filterSettings;
InitializeComponent();
textEditor.Text = "Welcome to ILSpy!";
AssemblyListTreeNode assemblyListTreeNode = new AssemblyListTreeNode(assemblyList);
assemblyListTreeNode.FilterSettings = filterSettings.Clone();
filterSettings.PropertyChanged += delegate {
// filterSettings is mutable; but the ILSpyTreeNode filtering assumes that filter settings are immutable.
// Thus, the main window will use one mutable instance (for data-binding), and assign a new clone to the ILSpyTreeNodes whenever the main
// mutable instance changes.
assemblyListTreeNode.FilterSettings = filterSettings.Clone();
};
treeView.Root = assemblyListTreeNode;
assemblyListTreeNode.Select = delegate(SharpTreeNode obj) {
if (obj != null) {

8
ILSpy/MethodTreeNode.cs

@ -70,5 +70,13 @@ namespace ICSharpCode.ILSpy
{
language.Decompile(method, output);
}
public override FilterResult Filter(FilterSettings settings)
{
if (settings.SearchTermMatches(method.Name))
return FilterResult.Match;
else
return FilterResult.Hidden;
}
}
}

10
ILSpy/NamespaceTreeNode.cs

@ -22,7 +22,7 @@ using ICSharpCode.TreeView;
namespace ICSharpCode.ILSpy
{
sealed class NamespaceTreeNode : ILSpyTreeNode
sealed class NamespaceTreeNode : ILSpyTreeNode<TypeTreeNode>
{
string name;
@ -44,5 +44,13 @@ namespace ICSharpCode.ILSpy
public override object Icon {
get { return Images.Namespace; }
}
public override FilterResult Filter(FilterSettings settings)
{
if (settings.SearchTermMatches(name))
return FilterResult.Match;
else
return FilterResult.Recurse;
}
}
}

8
ILSpy/PropertyTreeNode.cs

@ -60,5 +60,13 @@ namespace ICSharpCode.ILSpy
this.Children.Add(new MethodTreeNode(m));
}
}
public override FilterResult Filter(FilterSettings settings)
{
if (settings.SearchTermMatches(property.Name))
return FilterResult.Match;
else
return FilterResult.Hidden;
}
}
}

10
ILSpy/TypeTreeNode.cs

@ -77,6 +77,16 @@ namespace ICSharpCode.ILSpy
}
}
public override FilterResult Filter(FilterSettings settings)
{
if (settings.ShowInternalApi == false && IsPublicAPI)
return FilterResult.Hidden;
if (settings.SearchTermMatches(type.Name))
return FilterResult.Match;
else
return FilterResult.Recurse;
}
protected override void LoadChildren()
{
if (type.BaseType != null || type.HasInterfaces)

8
SharpTreeView/SharpTreeNodeCollection.cs

@ -34,10 +34,14 @@ namespace ICSharpCode.TreeView
protected override void ClearItems()
{
foreach (var node in this) {
/*foreach (var node in this) {
node.Parent = null;
}
base.ClearItems();
base.ClearItems();*/
// workaround for bug (reproducable when using ILSpy search filter)
while (Count > 0)
RemoveAt(Count - 1);
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)

2
SharpTreeView/SharpTreeNodeView.cs

@ -143,6 +143,8 @@ namespace ICSharpCode.TreeView
else {
result -= 19;
}
if (result < 0)
throw new InvalidOperationException();
return result;
}
}

Loading…
Cancel
Save