Skip to content

Commit

Permalink
Merge pull request #78 from thoemmi/most-recent-used-folders
Browse files Browse the repository at this point in the history
Most recent used folders
  • Loading branch information
thoemmi committed Dec 5, 2013
2 parents bf30422 + 2fac6eb commit 478249e
Show file tree
Hide file tree
Showing 9 changed files with 195 additions and 9 deletions.
16 changes: 14 additions & 2 deletions Solutionizer.Framework/BootstrapperBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.IO;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
using Autofac;
using NLog;
using NLog.Config;
Expand Down Expand Up @@ -93,9 +94,20 @@ protected virtual void OnExit(object sender, ExitEventArgs e) {
}
}

public abstract class BootstrapperBase<TViewModel> : BootstrapperBase {
public abstract class BootstrapperBase<TViewModel> : BootstrapperBase, IUiExecution {
private Window _window;
protected override void ConfigureContainer(ContainerBuilder builder) {
base.ConfigureContainer(builder);
builder.RegisterInstance(this).As<IUiExecution>();
}

protected override void OnStartup(object sender, StartupEventArgs e) {
Container.Resolve<WindowManager>().ShowWindow<TViewModel>();
_window = Container.Resolve<WindowManager>().ShowWindow<TViewModel>();
}

public void Execute(Action action) {
var dispatcher = _window != null ? _window.Dispatcher : Dispatcher.CurrentDispatcher;
dispatcher.Invoke(action);
}
}
}
7 changes: 7 additions & 0 deletions Solutionizer.Framework/IUiExecution.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
using System;

namespace Solutionizer.Framework {
public interface IUiExecution {
void Execute(Action action);
}
}
1 change: 1 addition & 0 deletions Solutionizer.Framework/Solutionizer.Framework.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
<Compile Include="DialogViewModel.cs" />
<Compile Include="FlyoutManager.cs" />
<Compile Include="IOnLoadedHandler.cs" />
<Compile Include="IUiExecution.cs" />
<Compile Include="LogRequestsModule.cs" />
<Compile Include="PropertyChangedBase.cs" />
<Compile Include="RelayCommand.Generic.cs" />
Expand Down
3 changes: 2 additions & 1 deletion Solutionizer.Framework/WindowManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

namespace Solutionizer.Framework {
public class WindowManager {
public void ShowWindow<TViewModel>() {
public Window ShowWindow<TViewModel>() {
var view = (Window)ViewLocator.GetViewForViewModel<TViewModel>();
view.Show();
return view;
}
}
}
1 change: 1 addition & 0 deletions Solutionizer/AppBootstrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ protected override void ConfigureContainer(ContainerBuilder builder) {
builder.Register(c => new SettingsProvider()).SingleInstance();
builder.Register(c => c.Resolve<SettingsProvider>().Settings).As<ISettings>().SingleInstance();
builder.RegisterType<GithubReleaseProvider>().SingleInstance().As<IReleaseProvider>();
builder.RegisterType<MostRecentUsedFoldersRepository>().SingleInstance().As<IMostRecentUsedFoldersRepository>();

builder
.RegisterAssemblyTypes(GetType().Assembly)
Expand Down
132 changes: 132 additions & 0 deletions Solutionizer/Services/MostRecentUsedFoldersRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Shell;
using Newtonsoft.Json;
using NLog;
using Solutionizer.Framework;
using Solutionizer.Infrastructure;

namespace Solutionizer.Services {
public interface IMostRecentUsedFoldersRepository {
void SetCurrentFolder(string folder);
ObservableCollection<string> Folders { get; }
}

public class MostRecentUsedFoldersRepository : IMostRecentUsedFoldersRepository, IDisposable {
private readonly IUiExecution _uiExecution;
private static readonly Logger _log = LogManager.GetCurrentClassLogger();

private const int LENGTH = 10;
private readonly string _mruFile;
private string _currentFolder;
private readonly List<string> _folders = new List<string>();
private readonly ObservableCollection<string> _foldersExceptCurrent = new ObservableCollection<string>();
private readonly FileSystemWatcher _fileSystemWatcher;

public MostRecentUsedFoldersRepository(IUiExecution uiExecution) {
_uiExecution = uiExecution;
_mruFile = Path.Combine(AppEnvironment.DataFolder, "mru.json");
Load();
_fileSystemWatcher = new FileSystemWatcher {
Path = AppEnvironment.DataFolder,
Filter = "mru.json",
NotifyFilter = NotifyFilters.LastWrite,
IncludeSubdirectories = false
};
_fileSystemWatcher.Changed += OnMruFileChanged;
_fileSystemWatcher.EnableRaisingEvents = true;
}

void OnMruFileChanged(object sender, FileSystemEventArgs e) {
_log.Debug("MRU file changed by other instance");
Load();
}

public void Dispose() {
_fileSystemWatcher.EnableRaisingEvents = false;
_fileSystemWatcher.Dispose();
}

private void Load() {
_log.Debug("Loading MRU list");
try {
if (File.Exists(_mruFile)) {
var fileData = File.ReadAllText(_mruFile);
var folders = JsonConvert.DeserializeObject<string[]>(fileData);
_folders.Clear();
foreach (var folder in folders) {
_folders.Add(folder);
}
UpdateMruFolders();
}
}
catch (Exception e) {
_log.ErrorException("Loading most recent used folders from " + _mruFile + " failed", e);
}
}

private void UpdateMruFolders() {
_uiExecution.Execute(() => {
_foldersExceptCurrent.Clear();
foreach (var folder in _folders.Where(f => !String.Equals(f, _currentFolder))) {
_foldersExceptCurrent.Add(folder);
}
});
}

public ObservableCollection<string> Folders {
get { return _foldersExceptCurrent; }
}

public void SetCurrentFolder(string folder) {
_currentFolder = folder;
_folders.Remove(folder);
_folders.Insert(0, folder);
while (_folders.Count > LENGTH) {
_folders.RemoveAt(_folders.Count - 1);
}

_fileSystemWatcher.EnableRaisingEvents = false;
_log.Debug("Saving MRU list");
try {
using (var textWriter = new StreamWriter(_mruFile)) {
textWriter.WriteLine(JsonConvert.SerializeObject(_folders.ToArray(), Formatting.Indented));
}
} catch (Exception e) {
_log.ErrorException("Saving mru folder list failed", e);
} finally {
_fileSystemWatcher.EnableRaisingEvents = true;
}

UpdateJumpList();
UpdateMruFolders();
}

private void UpdateJumpList() {
var jumpList = JumpList.GetJumpList(Application.Current) ?? new JumpList();
jumpList.ShowRecentCategory = true;

// Remove JumpTasks for folders,which are not in the MRU list anymore
//jumpList.JumpItems.RemoveAll(item => item is JumpTask && !_folders.Any(path => String.Equals(path, ((JumpTask)item).Title, StringComparison.OrdinalIgnoreCase)));

// add JumpTasks for folders, which do not exist already
foreach (var folder in _folders.Where(f => !jumpList.JumpItems.OfType<JumpTask>().Any(item => String.Equals(f, item.Title, StringComparison.OrdinalIgnoreCase)))) {
var jumpTask = new JumpTask {
ApplicationPath = Assembly.GetExecutingAssembly().Location,
Arguments = folder,
IconResourcePath = @"C:\Windows\System32\shell32.dll",
IconResourceIndex = 3,
Title = folder,
CustomCategory = "Recent folders"
};
JumpList.AddToRecentCategory(jumpTask);
}

jumpList.Apply(); }
}
}
2 changes: 2 additions & 0 deletions Solutionizer/Solutionizer.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
<UseVSHostingProcess>false</UseVSHostingProcess>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<PlatformTarget>x86</PlatformTarget>
Expand Down Expand Up @@ -113,6 +114,7 @@
</Compile>
<Compile Include="Properties\Annotations.cs" />
<Compile Include="Services\IStatusMessenger.cs" />
<Compile Include="Services\MostRecentUsedFoldersRepository.cs" />
<Compile Include="ViewModels\AboutViewModel.cs" />
<Compile Include="ViewModels\FileScanningViewModel.cs" />
<Compile Include="Infrastructure\BindableSelectedItemBehavior .cs" />
Expand Down
24 changes: 21 additions & 3 deletions Solutionizer/ViewModels/ShellViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Threading;
using System.Windows;
Expand All @@ -15,6 +17,7 @@ public sealed class ShellViewModel : PropertyChangedBase, IShell, IOnLoadedHandl
private readonly IFlyoutManager _flyoutManager;
private readonly IUpdateManager _updateManager;
private readonly IViewModelFactory _viewModelFactory;
private readonly IMostRecentUsedFoldersRepository _mostRecentUsedFoldersRepository;
private readonly ProjectRepositoryViewModel _projectRepository;
private SolutionViewModel _solution;
private string _rootPath;
Expand All @@ -24,15 +27,17 @@ public sealed class ShellViewModel : PropertyChangedBase, IShell, IOnLoadedHandl
private readonly ICommand _showSettingsCommand;
private readonly ICommand _showAboutCommand;
private readonly ICommand _selectRootPathCommand;
private readonly ICommand _setRootPathCommand;
private readonly Timer _updateTimer;
private string _statusMessage;

public ShellViewModel(ISettings settings, IDialogManager dialogManager, IFlyoutManager flyoutManager, IUpdateManager updateManager, IViewModelFactory viewModelFactory) {
public ShellViewModel(ISettings settings, IDialogManager dialogManager, IFlyoutManager flyoutManager, IUpdateManager updateManager, IViewModelFactory viewModelFactory, IMostRecentUsedFoldersRepository mostRecentUsedFoldersRepository) {
_settings = settings;
_dialogManager = dialogManager;
_flyoutManager = flyoutManager;
_updateManager = updateManager;
_viewModelFactory = viewModelFactory;
_mostRecentUsedFoldersRepository = mostRecentUsedFoldersRepository;

_projectRepository = _viewModelFactory.CreateProjectRepositoryViewModel(new RelayCommand<ProjectViewModel>(projectViewModel => _solution.AddProject(projectViewModel.Project)));
_updateManager.UpdatesAvailable +=
Expand All @@ -42,6 +47,7 @@ public ShellViewModel(ISettings settings, IDialogManager dialogManager, IFlyoutM
_showSettingsCommand = new RelayCommand(OnShowSettings);
_showAboutCommand = new RelayCommand(() => _flyoutManager.ShowFlyout(_viewModelFactory.CreateAboutViewModel()));
_selectRootPathCommand = new RelayCommand(SelectRootPath);
_setRootPathCommand = new RelayCommand<string>(LoadProjects, path => !String.Equals(path, RootPath));

_updateTimer = new Timer(_ => _updateManager.CheckForUpdatesAsync(), null, -1, -1);
}
Expand All @@ -66,6 +72,10 @@ public string Title {
}
}

public ObservableCollection<string> MostRecentUsedFolders {
get { return _mostRecentUsedFoldersRepository.Folders; }
}

public ProjectRepositoryViewModel ProjectRepository {
get { return _projectRepository; }
}
Expand Down Expand Up @@ -100,8 +110,15 @@ public ICommand SelectRootPathCommand {
get { return _selectRootPathCommand; }
}

public ICommand SetRootPathCommand {
get { return _setRootPathCommand; }
}

public void OnLoaded() {
if (_settings.ScanOnStartup) {
var args = Environment.GetCommandLineArgs();
if (args.Length > 1 && Directory.Exists(args[1])) {
LoadProjects(args[1]);
} else if (_settings.ScanOnStartup) {
LoadProjects(_settings.RootPath);
}

Expand All @@ -124,7 +141,6 @@ public void SelectRootPath() {
SelectedPath = _settings.RootPath
};
if (dlg.ShowDialog(Application.Current.MainWindow) == true) {
_settings.RootPath = dlg.SelectedPath;
LoadProjects(dlg.SelectedPath);
}
}
Expand Down Expand Up @@ -155,8 +171,10 @@ private async void LoadProjects(string path) {
var result = await _dialogManager.ShowDialog(fileScanningViewModel);

if (result != null) {
_settings.RootPath = path;
_projectRepository.RootPath = path;
_projectRepository.RootFolder = result.ProjectFolder;
_mostRecentUsedFoldersRepository.SetCurrentFolder(path);
Solution = _viewModelFactory.CreateSolutionViewModel(path, result.Projects);
Show(String.Format("{0} projects loaded.", result.Projects.Count));
} else {
Expand Down
18 changes: 15 additions & 3 deletions Solutionizer/Views/ShellView.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ViewModels="clr-namespace:Solutionizer.ViewModels"
xmlns:Framework="clr-namespace:Solutionizer.Framework;assembly=Solutionizer.Framework"
mc:Ignorable="d"
xmlns:Views="clr-namespace:Solutionizer.Views"
mc:Ignorable="d"
Infrastructure:WindowStatePersistence.Settings="{Binding Settings}"
TextOptions.TextFormattingMode="Display"
SnapsToDevicePixels="true"
Title="{Binding Title}" ShowTitleBar="False"
ResizeMode="CanResizeWithGrip"
UseLayoutRounding="True"
UseLayoutRounding="True" x:Name="Window"
Style="{DynamicResource CleanWindowStyleKey}"
Icon="/Solutionizer;component/Logo.ico" d:DataContext="{d:DesignInstance ViewModels:ShellViewModel}">

Expand Down Expand Up @@ -81,10 +82,21 @@
ToolTip="Change the root folder"
FontWeight="Bold" FontSize="20" Margin="0 5 0 0" Padding="5 0 5 0">
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewMouseDown">
<i:EventTrigger EventName="MouseLeftButtonDown" >
<i:InvokeCommandAction Command="{Binding SelectRootPathCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<Label.ContextMenu>
<ContextMenu ItemsSource="{Binding Path=MostRecentUsedFolders}" DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem" BasedOn="{StaticResource MetroMenuItem}">
<Setter Property="Header" Value="{Binding}"/>
<Setter Property="Command" Value="{Binding DataContext.SetRootPathCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Views:ShellView}}"/>
<Setter Property="CommandParameter" Value="{Binding}"/>
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</Label.ContextMenu>
<Label.Style>
<Style TargetType="Label">
<Style.Setters>
Expand Down

0 comments on commit 478249e

Please sign in to comment.