#develop (short for SharpDevelop) is a free IDE for .NET programming languages.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

245 lines
7.3 KiB

// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <author name="Daniel Grunwald"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.Windows.Controls;
using System.Windows.Media;
using ICSharpCode.NRefactory;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Dom;
namespace ICSharpCode.AvalonEdit.AddIn
{
/// <summary>
/// Panel with two combo boxes. Used to quickly navigate to entities in the current file.
/// </summary>
public partial class QuickClassBrowser : UserControl
{
const int TYPE_CLASS = 0;
const int TYPE_CONSTRUCTOR = 1;
const int TYPE_METHOD = 2;
const int TYPE_PROPERTY = 3;
const int TYPE_FIELD = 4;
const int TYPE_EVENT = 5;
/// <summary>
/// ViewModel used for combobox items.
/// </summary>
class EntityItem : IComparable<EntityItem>
{
IEntity entity;
IImage image;
string text;
int typeCode; // type code is used for sorting entities by type
public IEntity Entity {
get { return entity; }
}
public EntityItem(IEntity entity, int typeCode)
{
this.IsInSamePart = true;
this.entity = entity;
this.typeCode = typeCode;
IAmbience ambience = entity.ProjectContent.Language.GetAmbience();
if (entity is IClass)
ambience.ConversionFlags = ConversionFlags.ShowTypeParameterList | ConversionFlags.UseFullyQualifiedMemberNames;
else
ambience.ConversionFlags = ConversionFlags.ShowTypeParameterList | ConversionFlags.ShowParameterList | ConversionFlags.ShowParameterNames;
text = ambience.Convert(entity);
image = ClassBrowserIconService.GetIcon(entity);
}
/// <summary>
/// Text to display in combo box.
/// </summary>
public string Text {
get { return text; }
}
/// <summary>
/// Image to use in combox box
/// </summary>
public ImageSource Image {
get {
return image.ImageSource;
}
}
/// <summary>
/// Gets/Sets whether the item is in the current file.
/// </summary>
/// <returns>
/// <c>true</c>: item is in current file;
/// <c>false</c>: item is in another part of the partial class
/// </returns>
public bool IsInSamePart { get; set; }
public int CompareTo(EntityItem other)
{
int r = this.typeCode.CompareTo(other.typeCode);
if (r != 0)
return r;
r = string.Compare(text, other.text, StringComparison.OrdinalIgnoreCase);
if (r != 0)
return r;
return string.Compare(text, other.text, StringComparison.Ordinal);
}
}
public QuickClassBrowser()
{
InitializeComponent();
}
// The lists of items currently visible in the combo boxes.
// These should never be null.
List<EntityItem> classItems = new List<EntityItem>();
List<EntityItem> memberItems = new List<EntityItem>();
/// <summary>
/// Updates the list of available classes.
/// This causes the classes combo box to loose its current selected,
/// so the members combo box will be cleared.
/// </summary>
public void Update(ICompilationUnit compilationUnit)
{
classItems = new List<EntityItem>();
if (compilationUnit != null) {
AddClasses(compilationUnit.Classes);
}
classItems.Sort();
classComboBox.ItemsSource = classItems;
}
void AddClasses(IEnumerable<IClass> classes)
{
foreach (IClass c in classes) {
classItems.Add(new EntityItem(c, TYPE_CLASS));
AddClasses(c.InnerClasses);
}
}
/// <summary>
/// Selects the class and member closest to the specified location.
/// </summary>
public void SelectItemAtCaretPosition(Location location)
{
EntityItem matchInside = null;
EntityItem nearestMatch = null;
int nearestMatchDistance = int.MaxValue;
foreach (EntityItem item in classItems) {
if (item.IsInSamePart) {
IClass c = (IClass)item.Entity;
if (c.Region.IsInside(location.Line, location.Column)) {
matchInside = item;
// when there are multiple matches inside (nested classes), use the last one
} else {
// Not a perfect match?
// Try to first the nearest match. We want the classes combo box to always
// have a class selected if possible.
int matchDistance = Math.Min(Math.Abs(location.Line - c.Region.BeginLine),
Math.Abs(location.Line - c.Region.EndLine));
if (matchDistance < nearestMatchDistance) {
nearestMatchDistance = matchDistance;
nearestMatch = item;
}
}
}
}
jumpOnSelectionChange = false;
try {
classComboBox.SelectedItem = matchInside ?? nearestMatch;
// the SelectedItem setter will update the list of member items
} finally {
jumpOnSelectionChange = true;
}
matchInside = null;
foreach (EntityItem item in memberItems) {
if (item.IsInSamePart) {
IMember member = (IMember)item.Entity;
if (member.Region.IsInside(location.Line, location.Column) || member.BodyRegion.IsInside(location.Line, location.Column)) {
matchInside = item;
}
}
}
jumpOnSelectionChange = false;
try {
membersComboBox.SelectedItem = matchInside;
} finally {
jumpOnSelectionChange = true;
}
}
bool jumpOnSelectionChange = true;
void classComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
{
// The selected class was changed.
// Update the list of member items to be the list of members of the current class.
EntityItem item = classComboBox.SelectedItem as EntityItem;
IClass selectedClass = item != null ? item.Entity as IClass : null;
memberItems = new List<EntityItem>();
if (selectedClass != null) {
IClass compoundClass = selectedClass.GetCompoundClass();
foreach (var m in compoundClass.Methods) {
AddMember(selectedClass, m, m.IsConstructor ? TYPE_CONSTRUCTOR : TYPE_METHOD);
}
foreach (var m in compoundClass.Properties) {
AddMember(selectedClass, m, TYPE_PROPERTY);
}
foreach (var m in compoundClass.Fields) {
AddMember(selectedClass, m, TYPE_FIELD);
}
foreach (var m in compoundClass.Events) {
AddMember(selectedClass, m, TYPE_EVENT);
}
memberItems.Sort();
if (jumpOnSelectionChange) {
JumpTo(item, selectedClass.Region);
}
}
membersComboBox.ItemsSource = memberItems;
}
void AddMember(IClass selectedClass, IMember member, int typeCode)
{
bool isInSamePart = (member.DeclaringType == selectedClass);
memberItems.Add(new EntityItem(member, typeCode) { IsInSamePart = isInSamePart });
}
void membersComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
{
EntityItem item = membersComboBox.SelectedItem as EntityItem;
if (item != null) {
IMember member = item.Entity as IMember;
if (member != null && jumpOnSelectionChange) {
JumpTo(item, member.Region);
}
}
}
void JumpTo(EntityItem item, DomRegion region)
{
if (region.IsEmpty)
return;
Action<DomRegion> jumpAction = this.JumpAction;
if (item.IsInSamePart && jumpAction != null) {
jumpAction(region);
} else {
FileService.JumpToFilePosition(item.Entity.CompilationUnit.FileName, region.BeginLine, region.BeginColumn);
}
}
/// <summary>
/// Action used for jumping to a position inside the current file.
/// </summary>
public Action<DomRegion> JumpAction { get; set; }
}
}