Skip to content

Commit b1f3dc5

Browse files
committed
Add user project folder support for references.
1 parent ead66d2 commit b1f3dc5

15 files changed

+519
-12
lines changed

Rubberduck.Core/AddRemoveReferences/ReferenceModel.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,23 +21,25 @@ private ReferenceModel()
2121
_info = new Lazy<ReferenceInfo>(GenerateInfo);
2222
}
2323

24-
public ReferenceModel(ReferenceInfo info, bool recent = false, bool pinned = false) : this()
24+
public ReferenceModel(ReferenceInfo info, ReferenceKind type, bool recent = false, bool pinned = false) : this()
2525
{
2626
Guid = info.Guid;
2727
Name = info.Name;
28+
Description = Name;
2829
FullPath = info.FullPath;
2930
Major = info.Major;
3031
Minor = info.Minor;
3132
IsRecent = recent;
3233
IsPinned = pinned;
34+
Type = type;
3335
}
3436

3537
public ReferenceModel(IVBProject project, int priority) : this()
3638
{
3739
Name = project.Name ?? string.Empty;
3840
Priority = priority;
3941
Guid = Guid.Empty;
40-
Description = project.Description ?? string.Empty;
42+
Description = project.Description ?? project.Name;
4143
FullPath = project.FileName ?? string.Empty;
4244
IsBuiltIn = false;
4345
Type = ReferenceKind.Project;

Rubberduck.Core/Settings/ReferenceConfigProvider.cs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
11
using System;
2+
using System.IO;
23
using System.Reflection;
34
using Rubberduck.Resources.Registration;
45
using Rubberduck.SettingsProvider;
6+
using Rubberduck.UI;
57
using Rubberduck.VBEditor;
68

79
namespace Rubberduck.Settings
810
{
911
public class ReferenceConfigProvider : IConfigProvider<ReferenceSettings>
1012
{
1113
private readonly IPersistanceService<ReferenceSettings> _persister;
14+
private readonly IEnvironmentProvider _environment;
1215

13-
public ReferenceConfigProvider(IPersistanceService<ReferenceSettings> persister)
16+
public ReferenceConfigProvider(IPersistanceService<ReferenceSettings> persister, IEnvironmentProvider environment)
1417
{
1518
_persister = persister;
19+
_environment = environment;
1620
}
1721

1822
public ReferenceSettings Create()
@@ -32,6 +36,28 @@ public ReferenceSettings CreateDefaults()
3236
defaults.PinReference(new ReferenceInfo(new Guid(RubberduckGuid.RubberduckTypeLibGuid), string.Empty, string.Empty, version.Major, version.Minor));
3337
defaults.PinReference(new ReferenceInfo(new Guid(RubberduckGuid.RubberduckApiTypeLibGuid), string.Empty, string.Empty, version.Major, version.Minor));
3438

39+
var documents = _environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
40+
if (!string.IsNullOrEmpty(documents))
41+
{
42+
defaults.ProjectPaths.Add(documents);
43+
}
44+
45+
var appdata = _environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
46+
if (!string.IsNullOrEmpty(documents))
47+
{
48+
var addins = Path.Combine(appdata, "Microsoft", "AddIns");
49+
if (Directory.Exists(addins))
50+
{
51+
defaults.ProjectPaths.Add(addins);
52+
}
53+
54+
var templates = Path.Combine(appdata, "Microsoft", "Templates");
55+
if (Directory.Exists(templates))
56+
{
57+
defaults.ProjectPaths.Add(templates);
58+
}
59+
}
60+
3561
return defaults;
3662
}
3763

Rubberduck.Core/Settings/ReferenceSettings.cs

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,26 @@ namespace Rubberduck.Settings
1010
public interface IReferenceSettings
1111
{
1212
int RecentReferencesTracked { get; set; }
13+
bool FixBrokenReferences { get; set; }
14+
bool AddToRecentOnReferenceEvents { get; set; }
15+
List<string> ProjectPaths { get; set; }
1316
List<ReferenceInfo> GetRecentReferencesForHost(string host);
1417
void UpdateRecentReferencesForHost(string host, List<ReferenceInfo> references);
1518
List<ReferenceInfo> GetPinnedReferencesForHost(string host);
1619
void UpdatePinnedReferencesForHost(string host, List<ReferenceInfo> references);
1720
void PinReference(ReferenceInfo reference, string host = null);
1821
void TrackUsage(ReferenceInfo reference, string host = null);
22+
bool IsPinnedProject(string filePath, string host);
23+
bool IsRecentProject(string filePath, string host);
1924
}
2025

2126
[DataContract]
2227
[KnownType(typeof(ReferenceInfo))]
2328
[KnownType(typeof(ReferenceUsage))]
2429
public class ReferenceSettings : IReferenceSettings, IEquatable<ReferenceSettings>
2530
{
31+
public const int RecentTrackingLimit = 50;
32+
2633
[DataMember(IsRequired = true)]
2734
[XmlElement(ElementName = "RecentReferences")]
2835
private List<HostUsages> _recent;
@@ -34,20 +41,43 @@ public class ReferenceSettings : IReferenceSettings, IEquatable<ReferenceSetting
3441
[OnDeserialized]
3542
private void DeserializationLoad(StreamingContext context)
3643
{
37-
RecentProjectReferences = _recent.ToDictionary(usage => usage.Host, usage => usage.Usages);
44+
RecentProjectReferences = _recent.Take(RecentTrackingLimit).ToDictionary(usage => usage.Host, usage => usage.Usages);
3845
PinnedProjectReferences = _pinned.ToDictionary(usage => usage.Host, usage => usage.Pins);
3946
}
4047

4148
[OnSerializing]
4249
private void SerializationPrep(StreamingContext context)
4350
{
44-
_recent = new List<HostUsages>(RecentProjectReferences.Select(recent => new HostUsages(recent.Key, recent.Value)));
51+
_recent = new List<HostUsages>(RecentProjectReferences.Select(recent => new HostUsages(recent.Key, recent.Value)).Take(RecentTrackingLimit));
4552
_pinned = new List<HostPins>(PinnedProjectReferences.Select(recent => new HostPins(recent.Key, recent.Value)));
4653
}
4754

55+
public ReferenceSettings() { }
56+
57+
public ReferenceSettings(ReferenceSettings other)
58+
{
59+
RecentReferencesTracked = other.RecentReferencesTracked;
60+
FixBrokenReferences = other.FixBrokenReferences;
61+
AddToRecentOnReferenceEvents = other.AddToRecentOnReferenceEvents;
62+
ProjectPaths = new List<string>(other.ProjectPaths);
63+
other.SerializationPrep(new StreamingContext(StreamingContextStates.All));
64+
_recent = other._recent;
65+
_pinned = other._pinned;
66+
DeserializationLoad(new StreamingContext(StreamingContextStates.All));
67+
}
68+
4869
[DataMember(IsRequired = true)]
4970
public int RecentReferencesTracked { get; set; }
5071

72+
[DataMember(IsRequired = true)]
73+
public bool FixBrokenReferences { get; set; }
74+
75+
[DataMember(IsRequired = true)]
76+
public bool AddToRecentOnReferenceEvents { get; set; }
77+
78+
[DataMember(IsRequired = true)]
79+
public List<string> ProjectPaths { get; set; } = new List<string>();
80+
5181
[DataMember(IsRequired = true)]
5282
protected List<ReferenceUsage> RecentLibraryReferences { get; private set; } = new List<ReferenceUsage>();
5383

@@ -76,6 +106,24 @@ public void PinReference(ReferenceInfo reference, string host = null)
76106
}
77107
}
78108

109+
public bool IsPinnedProject(string filePath, string host)
110+
{
111+
var key = host.ToUpperInvariant();
112+
return PinnedProjectReferences.ContainsKey(key) &&
113+
PinnedProjectReferences[key]
114+
.Select(pin => pin.FullPath)
115+
.Contains(filePath, StringComparer.OrdinalIgnoreCase);
116+
}
117+
118+
public bool IsRecentProject(string filePath, string host)
119+
{
120+
var key = host.ToUpperInvariant();
121+
return RecentProjectReferences.ContainsKey(key) &&
122+
RecentProjectReferences[key]
123+
.Select(usage => usage.Reference.FullPath)
124+
.Contains(filePath, StringComparer.OrdinalIgnoreCase);
125+
}
126+
79127
public void TrackUsage(ReferenceInfo reference, string host = null)
80128
{
81129
var use = new ReferenceUsage(reference);

Rubberduck.Core/UI/AddRemoveReferences/AddRemoveReferencesPresenterFactory.cs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.IO;
34
using System.Linq;
5+
using System.Windows.Forms;
46
using NLog;
57
using Rubberduck.AddRemoveReferences;
68
using Rubberduck.Parsing.Symbols;
79
using Rubberduck.Parsing.VBA;
810
using Rubberduck.Refactorings;
911
using Rubberduck.Settings;
1012
using Rubberduck.SettingsProvider;
13+
using Rubberduck.VBEditor;
14+
using Rubberduck.VBEditor.SafeComWrappers;
1115
using Rubberduck.VBEditor.SafeComWrappers.Abstract;
1216

1317
namespace Rubberduck.UI.AddRemoveReferences
@@ -95,10 +99,62 @@ public AddRemoveReferencesPresenter Create(ProjectDeclaration project)
9599

96100
var settings = _settings.Create();
97101
var model = new AddRemoveReferencesModel(project, models.Values, settings);
102+
if (AddRemoveReferencesViewModel.HostHasProjects)
103+
{
104+
model.References.AddRange(GetUserProjectFolderModels(model.Settings).Where(proj =>
105+
!model.References.Any(item =>
106+
item.FullPath.Equals(proj.FullPath, StringComparison.OrdinalIgnoreCase))));
107+
}
98108

99109
return new AddRemoveReferencesPresenter(new AddRemoveReferencesDialog(new AddRemoveReferencesViewModel(model, _reconciler)));
100110
}
101111

112+
private IEnumerable<ReferenceModel> GetUserProjectFolderModels(IReferenceSettings settings)
113+
{
114+
var host = Path.GetFileName(Application.ExecutablePath).ToUpperInvariant();
115+
if (!AddRemoveReferencesViewModel.HostFileFilters.ContainsKey(host))
116+
{
117+
return Enumerable.Empty<ReferenceModel>();
118+
}
119+
120+
var filter = AddRemoveReferencesViewModel.HostFileFilters[host].Select(ext => $".{ext}").ToList();
121+
var projects = new List<ReferenceModel>();
122+
123+
foreach (var path in settings.ProjectPaths.Where(Directory.Exists))
124+
{
125+
try
126+
{
127+
foreach (var fullPath in Directory.EnumerateFiles(path))
128+
{
129+
try
130+
{
131+
if (!filter.Contains(Path.GetExtension(fullPath)))
132+
{
133+
continue;
134+
}
135+
var file = Path.GetFileName(fullPath);
136+
137+
projects.Add(new ReferenceModel(
138+
new ReferenceInfo(Guid.Empty, file, fullPath, 0, 0),
139+
ReferenceKind.Project,
140+
settings.IsRecentProject(fullPath, host),
141+
settings.IsPinnedProject(fullPath, host)));
142+
}
143+
catch
144+
{
145+
// 'Ignored
146+
}
147+
}
148+
}
149+
catch
150+
{
151+
_logger.Info("User project directory in reference settings does not exist.");
152+
}
153+
}
154+
155+
return projects;
156+
}
157+
102158
public AddRemoveReferencesPresenter Create()
103159
{
104160
using (var pane = _vbe.ActiveCodePane)

Rubberduck.Core/UI/AddRemoveReferences/AddRemoveReferencesViewModel.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public enum ReferenceFilter
2626

2727
public class AddRemoveReferencesViewModel : ViewModelBase
2828
{
29-
private static readonly Dictionary<string, string[]> HostFileFilters = new Dictionary<string, string[]>
29+
public static readonly Dictionary<string, string[]> HostFileFilters = new Dictionary<string, string[]>
3030
{
3131
{ "EXCEL.EXE", new [] {"xlsm","xlam","xls","xla"} },
3232
{ "WINWORD.EXE", new [] {"docm","dotm","doc","dot"} },
@@ -57,7 +57,7 @@ public class AddRemoveReferencesViewModel : ViewModelBase
5757
RubberduckUI.References_BrowseFilterAllFiles,
5858
};
5959

60-
private static bool HostHasProjects { get; }
60+
public static bool HostHasProjects { get; }
6161

6262
static AddRemoveReferencesViewModel()
6363
{
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
<UserControl x:Class="Rubberduck.UI.Settings.AddRemoveReferencesUserSettings"
2+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
5+
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
6+
xmlns:settings="clr-namespace:Rubberduck.UI.Settings"
7+
xmlns:converters="clr-namespace:Rubberduck.UI.Settings.Converters"
8+
mc:Ignorable="d"
9+
d:DesignWidth="300"
10+
d:DesignHeight="350"
11+
d:DataContext="{d:DesignInstance {x:Type settings:AddRemoveReferencesUserSettingsViewModel}, IsDesignTimeCreatable=False}">
12+
<UserControl.Resources>
13+
<BitmapImage x:Key="AddImage" UriSource="pack://application:,,,/Rubberduck.Resources;component/Icons/Fugue/plus-circle.png" />
14+
<BitmapImage x:Key="DeleteImage" UriSource="pack://application:,,,/Rubberduck.Resources;component/Icons/Fugue/cross-script.png" />
15+
16+
<converters:SelectedItemToBooleanConverter x:Key="HasSelectedItems"/>
17+
18+
<LinearGradientBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" EndPoint="0,1" StartPoint="0,0">
19+
<GradientStop Color="#FFD9F4FF" Offset="0"/>
20+
<GradientStop Color="#FF9BDDFB" Offset="1"/>
21+
</LinearGradientBrush>
22+
<LinearGradientBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" EndPoint="0,1" StartPoint="0,0">
23+
<GradientStop Color="#FFEEEDED" Offset="0"/>
24+
<GradientStop Color="#FFDDDDDD" Offset="1"/>
25+
</LinearGradientBrush>
26+
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="Black" />
27+
<SolidColorBrush x:Key="{x:Static SystemColors.ControlTextBrushKey}" Color="Black" />
28+
<Style TargetType="ScrollViewer">
29+
<Style.Setters>
30+
<Setter Property="settings:ScrollViewerCorrector.FixScrolling" Value="True" />
31+
</Style.Setters>
32+
</Style>
33+
</UserControl.Resources>
34+
<Grid>
35+
<ScrollViewer x:Name="ScrollViewer">
36+
<Grid>
37+
<Grid.RowDefinitions>
38+
<RowDefinition Height="Auto"/>
39+
<RowDefinition Height="Auto"/>
40+
</Grid.RowDefinitions>
41+
<StackPanel Grid.Row="0" Margin="5,5,5,0">
42+
<Label Background="DarkGray"
43+
HorizontalContentAlignment="Stretch"
44+
Margin="0,0,0,3">
45+
<Label.Style>
46+
<Style>
47+
<Style.Resources>
48+
<Style TargetType="{x:Type Border}">
49+
<Setter Property="CornerRadius" Value="5"/>
50+
</Style>
51+
</Style.Resources>
52+
</Style>
53+
</Label.Style>
54+
<DockPanel Background="DarkGray" FlowDirection="LeftToRight">
55+
<StackPanel Orientation="Horizontal" DockPanel.Dock="Left">
56+
<Label Foreground="White"
57+
FontWeight="SemiBold"
58+
Content="{Resx ResxName=Rubberduck.Resources.Settings.SettingsUI, Key=PageHeader_ReferenceSettings}">
59+
</Label>
60+
</StackPanel>
61+
</DockPanel>
62+
</Label>
63+
<CheckBox Margin="5,10,0,5"
64+
HorizontalAlignment="Left"
65+
IsChecked="{Binding AddToRecentOnReferenceEvents,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
66+
<AccessText TextWrapping="WrapWithOverflow" Text="{Resx ResxName=Rubberduck.Resources.RubberduckUI, Key=ReferenceSettings_TrackHostReferences}" />
67+
</CheckBox>
68+
<CheckBox Margin="5,0,0,5"
69+
HorizontalAlignment="Left"
70+
IsChecked="{Binding FixBrokenReferences,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
71+
Visibility="Collapsed">
72+
<AccessText TextWrapping="WrapWithOverflow" Text="{Resx ResxName=Rubberduck.Resources.RubberduckUI, Key=ReferenceSettings_FixBroken}" />
73+
</CheckBox>
74+
<StackPanel Orientation="Horizontal" Margin="5,0,0,5">
75+
<TextBlock VerticalAlignment="Center" Text="{Resx ResxName=Rubberduck.Resources.RubberduckUI, Key=ReferenceSettings_RecentTracked}" />
76+
<StackPanel Orientation="Horizontal" Margin="15,0,15,0">
77+
<TextBox Margin="5,5,0,5" Height="24" Width="50" Text="{Binding RecentReferencesTracked, Mode=TwoWay, StringFormat=\{0:D\},
78+
UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" VerticalAlignment="Top" VerticalContentAlignment="Center" />
79+
<StackPanel Margin="0,5" >
80+
<Button Height="12" Width="20" Command="{Binding IncrementRecentReferencesTrackedCommand}">
81+
<TextBlock Text="" FontSize="10" Margin="0,-4,0,0"/>
82+
</Button>
83+
<Button Height="12" Width="20" Command="{Binding DecrementReferencesTrackedCommand}">
84+
<TextBlock Text="" FontSize="10" Margin="0,-3,0,0"/>
85+
</Button>
86+
</StackPanel>
87+
</StackPanel>
88+
</StackPanel>
89+
</StackPanel>
90+
<StackPanel Grid.Row="1" Margin="5,0,0,5">
91+
<StackPanel Orientation="Horizontal">
92+
<Label FontWeight="SemiBold" Content="{Resx ResxName=Rubberduck.Resources.RubberduckUI, Key=ReferenceSettings_FoldersHeader}" />
93+
<Button Margin="10,0,0,0"
94+
ToolTip="{Resx ResxName=Rubberduck.Resources.RubberduckUI, Key=Add}"
95+
Command="{Binding BrowseForPathCommand}"
96+
BorderThickness="0"
97+
Background="Transparent">
98+
<StackPanel Orientation="Horizontal">
99+
<Image Source="{StaticResource AddImage}" />
100+
<TextBlock Text="{Resx ResxName=Rubberduck.Resources.RubberduckUI, Key=Add}" Margin="2,0" />
101+
</StackPanel>
102+
</Button>
103+
<Button Margin="10,0,0,0"
104+
ToolTip="{Resx ResxName=Rubberduck.Resources.RubberduckUI, Key=Remove}"
105+
Command="{Binding RemoveSelectedPaths}"
106+
CommandParameter="{Binding ElementName=PathList, Path=SelectedItem}"
107+
BorderThickness="0"
108+
Background="Transparent"
109+
IsEnabled="{Binding ElementName=PathList, Path=SelectedItem, Converter={StaticResource HasSelectedItems}}">
110+
<StackPanel Orientation="Horizontal">
111+
<Image Source="{StaticResource DeleteImage}" />
112+
<TextBlock Text="{Resx ResxName=Rubberduck.Resources.RubberduckUI, Key=Remove}" Margin="2,0" />
113+
</StackPanel>
114+
</Button>
115+
</StackPanel>
116+
<ListBox Name="PathList" MinHeight="150" ItemsSource="{Binding ProjectPaths}" />
117+
</StackPanel>
118+
</Grid>
119+
</ScrollViewer>
120+
</Grid>
121+
</UserControl>

0 commit comments

Comments
 (0)