Work on project panel, trees, added settings

This commit is contained in:
Tiberiu Chibici 2014-07-28 20:18:18 +03:00
parent 5e526fa48c
commit 1c4c7ccfb0
23 changed files with 932 additions and 133 deletions

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using RainmeterStudio.Storage;
namespace RainmeterStudio.Model
{
@ -170,9 +171,26 @@ namespace RainmeterStudio.Model
/// <summary>
/// Gets or sets the root node
/// </summary>
[XmlElement("root")]
[XmlIgnore]
public Tree<Reference> Root { get; set; }
/// <summary>
/// Gets or sets the serializable root node
/// </summary>
/// <remarks>Warning: not efficient</remarks>
[XmlElement("root")]
public SerializableTree<Reference> SerializableRoot
{
get
{
return Root.AsSerializableTree();
}
set
{
Root = value.AsTree();
}
}
#endregion
#region Constructor

View File

@ -7,13 +7,11 @@ using System.Xml.Serialization;
namespace RainmeterStudio.Model
{
public class Tree<T>
public class Tree<T> : IList<Tree<T>>
{
[XmlElement("data")]
public T Data { get; set; }
[XmlArray("children"), XmlArrayItem("child")]
public ObservableCollection<Tree<T>> Children { get; set; }
public ObservableCollection<Tree<T>> Children { get; private set; }
public Tree()
{
@ -150,5 +148,10 @@ namespace RainmeterStudio.Model
return hash;
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return Children.GetEnumerator();
}
}
}

View File

@ -25,12 +25,66 @@ namespace RainmeterStudio.Properties {
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public global::System.Windows.Input.KeyGestureConverter asdf {
[global::System.Configuration.DefaultSettingValueAttribute("Ctrl+Shift+N")]
[global::System.Configuration.SettingsManageabilityAttribute(global::System.Configuration.SettingsManageability.Roaming)]
public string ProjectCreateCommand_Shortcut {
get {
return ((global::System.Windows.Input.KeyGestureConverter)(this["asdf"]));
return ((string)(this["ProjectCreateCommand_Shortcut"]));
}
set {
this["asdf"] = value;
this["ProjectCreateCommand_Shortcut"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("F5")]
[global::System.Configuration.SettingsManageabilityAttribute(global::System.Configuration.SettingsManageability.Roaming)]
public string ProjectPanel_RefreshCommand_Shortcut {
get {
return ((string)(this["ProjectPanel_RefreshCommand_Shortcut"]));
}
set {
this["ProjectPanel_RefreshCommand_Shortcut"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("Ctrl+N")]
[global::System.Configuration.SettingsManageabilityAttribute(global::System.Configuration.SettingsManageability.Roaming)]
public string DocumentCreateCommand_Shortcut {
get {
return ((string)(this["DocumentCreateCommand_Shortcut"]));
}
set {
this["DocumentCreateCommand_Shortcut"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("Ctrl+Shift+O")]
[global::System.Configuration.SettingsManageabilityAttribute(global::System.Configuration.SettingsManageability.Roaming)]
public string ProjectOpenCommand_Shortcut {
get {
return ((string)(this["ProjectOpenCommand_Shortcut"]));
}
set {
this["ProjectOpenCommand_Shortcut"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("Ctrl+W")]
[global::System.Configuration.SettingsManageabilityAttribute(global::System.Configuration.SettingsManageability.Roaming)]
public string DocumentCloseCommand_Shortcut {
get {
return ((string)(this["DocumentCloseCommand_Shortcut"]));
}
set {
this["DocumentCloseCommand_Shortcut"] = value;
}
}
}

View File

@ -1,7 +1,21 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="RainmeterStudio.Properties" GeneratedClassName="Settings">
<Profiles />
<Settings>
<Setting Name="ProjectCreateCommand_Shortcut" Roaming="true" Type="System.String" Scope="User">
<Value Profile="(Default)">Ctrl+Shift+N</Value>
</Setting>
<Setting Name="ProjectPanel_RefreshCommand_Shortcut" Roaming="true" Type="System.String" Scope="User">
<Value Profile="(Default)">F5</Value>
</Setting>
<Setting Name="DocumentCreateCommand_Shortcut" Roaming="true" Type="System.String" Scope="User">
<Value Profile="(Default)">Ctrl+N</Value>
</Setting>
<Setting Name="ProjectOpenCommand_Shortcut" Roaming="true" Type="System.String" Scope="User">
<Value Profile="(Default)">Ctrl+Shift+O</Value>
</Setting>
<Setting Name="DocumentCloseCommand_Shortcut" Roaming="true" Type="System.String" Scope="User">
<Value Profile="(Default)">Ctrl+W</Value>
</Setting>
</Settings>
</SettingsFile>

View File

@ -109,9 +109,11 @@
<Compile Include="Model\IDocumentStorage.cs" />
<Compile Include="Storage\ProjectStorage.cs" />
<Compile Include="Storage\SkinDirectory.cs" />
<Compile Include="Storage\SerializableTree.cs" />
<Compile Include="UI\Command.cs" />
<Compile Include="UI\Controller\IconProvider.cs" />
<Compile Include="UI\Controller\ProjectController.cs" />
<Compile Include="UI\Controller\SettingsProvider.cs" />
<Compile Include="UI\Dialogs\CreateDocumentDialog.xaml.cs">
<DependentUpon>CreateDocumentDialog.xaml</DependentUpon>
</Compile>
@ -123,7 +125,9 @@
<Compile Include="UI\ProjectPanel.xaml.cs">
<DependentUpon>ProjectPanel.xaml</DependentUpon>
</Compile>
<Compile Include="UI\ViewModel\ReferenceViewModel.cs" />
<Compile Include="Utils\DirectoryHelper.cs" />
<Compile Include="Utils\TreeExtensions.cs" />
<Page Include="Documents\Ini\IniSkinDesignerControl.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
@ -248,6 +252,9 @@
<ItemGroup>
<Resource Include="Resources\Icons\16\view-refresh.png" />
</ItemGroup>
<ItemGroup>
<Resource Include="Resources\Icons\16\folder_project.png" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

View File

@ -108,9 +108,9 @@ namespace RainmeterStudio.Resources {
/// <summary>
/// Looks up a localized string similar to /Resources/Icons/16/folder.png.
/// </summary>
internal static string ProjectItemFolder {
internal static string ProjectItemDirectory {
get {
return ResourceManager.GetString("ProjectItemFolder", resourceCulture);
return ResourceManager.GetString("ProjectItemDirectory", resourceCulture);
}
}
@ -132,6 +132,15 @@ namespace RainmeterStudio.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to /Resources/Icons/16/folder_project.png.
/// </summary>
internal static string ProjectOpenCommand {
get {
return ResourceManager.GetString("ProjectOpenCommand", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to /Resources/Icons/16/minus.png.
/// </summary>

View File

@ -126,7 +126,7 @@
<data name="ProjectCreateCommand" xml:space="preserve">
<value>/Resources/Icons/16/project_star.png</value>
</data>
<data name="ProjectItemFolder" xml:space="preserve">
<data name="ProjectItemDirectory" xml:space="preserve">
<value>/Resources/Icons/16/folder.png</value>
</data>
<data name="ProjectItemNone" xml:space="preserve">
@ -141,6 +141,9 @@
<data name="ProjectItem_txt" xml:space="preserve">
<value>/Resources/Icons/16/text_generic.png</value>
</data>
<data name="ProjectOpenCommand" xml:space="preserve">
<value>/Resources/Icons/16/folder_project.png</value>
</data>
<data name="ProjectPanel_CollapseAllCommand" xml:space="preserve">
<value>/Resources/Icons/16/minus.png</value>
</data>

Binary file not shown.

After

Width:  |  Height:  |  Size: 621 B

View File

@ -96,6 +96,24 @@ namespace RainmeterStudio.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to All files.
/// </summary>
public static string Dialog_FileType_AllFiles {
get {
return ResourceManager.GetString("Dialog_FileType_AllFiles", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to RainmeterStudio project.
/// </summary>
public static string Dialog_FileType_Project {
get {
return ResourceManager.GetString("Dialog_FileType_Project", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to OK.
/// </summary>
@ -105,6 +123,33 @@ namespace RainmeterStudio.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Open project....
/// </summary>
public static string Dialog_OpenProject_Title {
get {
return ResourceManager.GetString("Dialog_OpenProject_Title", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to _Close.
/// </summary>
public static string DocumentCloseCommand_DisplayText {
get {
return ResourceManager.GetString("DocumentCloseCommand_DisplayText", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Close active document.
/// </summary>
public static string DocumentCloseCommand_ToolTip {
get {
return ResourceManager.GetString("DocumentCloseCommand_ToolTip", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to _File....
/// </summary>
@ -115,7 +160,7 @@ namespace RainmeterStudio.Resources {
}
/// <summary>
/// Looks up a localized string similar to Creates a new file.
/// Looks up a localized string similar to Create a new file.
/// </summary>
public static string DocumentCreateCommand_ToolTip {
get {
@ -150,6 +195,33 @@ namespace RainmeterStudio.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to _File.
/// </summary>
public static string MainWindow_File {
get {
return ResourceManager.GetString("MainWindow_File", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to _New.
/// </summary>
public static string MainWindow_File_New {
get {
return ResourceManager.GetString("MainWindow_File_New", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to _Open.
/// </summary>
public static string MainWindow_File_Open {
get {
return ResourceManager.GetString("MainWindow_File_Open", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to _Project....
/// </summary>
@ -160,7 +232,7 @@ namespace RainmeterStudio.Resources {
}
/// <summary>
/// Looks up a localized string similar to Creates a new project.
/// Looks up a localized string similar to Create a new project.
/// </summary>
public static string ProjectCreateCommand_ToolTip {
get {
@ -222,6 +294,24 @@ namespace RainmeterStudio.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to _Project....
/// </summary>
public static string ProjectOpenCommand_DisplayText {
get {
return ResourceManager.GetString("ProjectOpenCommand_DisplayText", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Open existing project.
/// </summary>
public static string ProjectOpenCommand_ToolTip {
get {
return ResourceManager.GetString("ProjectOpenCommand_ToolTip", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Collapse all.
/// </summary>

View File

@ -129,14 +129,29 @@
<data name="Dialog_Create" xml:space="preserve">
<value>Create</value>
</data>
<data name="Dialog_FileType_AllFiles" xml:space="preserve">
<value>All files</value>
</data>
<data name="Dialog_FileType_Project" xml:space="preserve">
<value>RainmeterStudio project</value>
</data>
<data name="Dialog_OK" xml:space="preserve">
<value>OK</value>
</data>
<data name="Dialog_OpenProject_Title" xml:space="preserve">
<value>Open project...</value>
</data>
<data name="DocumentCloseCommand_DisplayText" xml:space="preserve">
<value>_Close</value>
</data>
<data name="DocumentCloseCommand_ToolTip" xml:space="preserve">
<value>Close active document</value>
</data>
<data name="DocumentCreateCommand_DisplayText" xml:space="preserve">
<value>_File...</value>
</data>
<data name="DocumentCreateCommand_ToolTip" xml:space="preserve">
<value>Creates a new file</value>
<value>Create a new file</value>
</data>
<data name="DocumentEditor_Text_Name" xml:space="preserve">
<value>Text Editor</value>
@ -147,11 +162,20 @@
<data name="DocumentFormat_TextFile_Name" xml:space="preserve">
<value>Text file</value>
</data>
<data name="MainWindow_File" xml:space="preserve">
<value>_File</value>
</data>
<data name="MainWindow_File_New" xml:space="preserve">
<value>_New</value>
</data>
<data name="MainWindow_File_Open" xml:space="preserve">
<value>_Open</value>
</data>
<data name="ProjectCreateCommand_DisplayText" xml:space="preserve">
<value>_Project...</value>
</data>
<data name="ProjectCreateCommand_ToolTip" xml:space="preserve">
<value>Creates a new project</value>
<value>Create a new project</value>
</data>
<data name="ProjectCreateDialog_Location" xml:space="preserve">
<value>Location:</value>
@ -171,6 +195,12 @@
<data name="ProjectCreateDialog_Title" xml:space="preserve">
<value>Create project</value>
</data>
<data name="ProjectOpenCommand_DisplayText" xml:space="preserve">
<value>_Project...</value>
</data>
<data name="ProjectOpenCommand_ToolTip" xml:space="preserve">
<value>Open existing project</value>
</data>
<data name="ProjectPanel_CollapseAllCommand_DisplayText" xml:space="preserve">
<value>Collapse all</value>
</data>

View File

@ -0,0 +1,92 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using RainmeterStudio.Model;
namespace RainmeterStudio.Storage
{
/// <summary>
/// A special type of tree that implements a very small subset of tree operations, and can be serialized
/// </summary>
/// <typeparam name="T"></typeparam>
public class SerializableTree<T>
{
/// <summary>
/// Gets or sets the attached data
/// </summary>
[XmlElement("data")]
public T Data { get; set; }
/// <summary>
/// Gets or sets the list of children
/// </summary>
[XmlArray("children"), XmlArrayItem("child")]
public List<SerializableTree<T>> Children { get; set; }
/// <summary>
/// Initializes the serializable tree
/// </summary>
public SerializableTree()
{
Children = new List<SerializableTree<T>>();
Data = default(T);
}
/// <summary>
/// Initializes the serializable tree with specified data
/// </summary>
/// <param name="data">Data</param>
public SerializableTree(T data)
{
Children = new List<SerializableTree<T>>();
Data = data;
}
}
/// <summary>
/// Extension methods for converting to and from serializable trees
/// </summary>
public static class SerializableTreeExtensions
{
/// <summary>
/// Converts tree into a serializable tree
/// </summary>
/// <typeparam name="T">Data type of tree</typeparam>
/// <param name="root">Root node</param>
/// <returns>Serializable tree</returns>
public static SerializableTree<T> AsSerializableTree<T>(this Tree<T> root)
{
// Convert current node
SerializableTree<T> sRoot = new SerializableTree<T>(root.Data);
// Add children
foreach (var child in root.Children)
sRoot.Children.Add(AsSerializableTree(child));
// Return root
return sRoot;
}
/// <summary>
/// Converts serializable tree into a tree
/// </summary>
/// <typeparam name="T">Data type of tree</typeparam>
/// <param name="root">Root node</param>
/// <returns>Tree</returns>
public static Tree<T> AsTree<T>(this SerializableTree<T> root)
{
// Convert current node
Tree<T> sRoot = new Tree<T>(root.Data);
// Add children
foreach (var child in root.Children)
sRoot.Add(AsTree(child));
// Return root
return sRoot;
}
}
}

View File

@ -94,11 +94,29 @@ namespace RainmeterStudio.UI
#endregion
#region Keyboard shortcut property
private KeyGesture _shortcut;
/// <summary>
/// Gets or sets the keyboard shortcut of this command
/// </summary>
public KeyGesture Shortcut { get; set; }
public KeyGesture Shortcut
{
get
{
if (_shortcut == null)
{
string str = SettingsProvider.GetSetting<string>(Name + "_Shortcut");
return GetKeyGestureFromString(str);
}
return _shortcut;
}
set
{
_shortcut = value;
}
}
/// <summary>
/// Gets the text representation of the keyboard shortcut
@ -107,10 +125,12 @@ namespace RainmeterStudio.UI
{
get
{
string text = String.Empty;
// Safety check
if (Shortcut == null)
return text;
return null;
// Build string
string text = String.Empty;
if ((Shortcut.Modifiers & ModifierKeys.Windows) != 0)
text += "Win+";
@ -127,17 +147,56 @@ namespace RainmeterStudio.UI
text += Enum.GetName(typeof(Key), Shortcut.Key);
return text;
}
set
{
Shortcut = GetKeyGestureFromString(value);
}
}
private KeyGesture GetKeyGestureFromString(string k)
{
// Safety check
if (k == null)
return null;
// Variables
ModifierKeys mods = ModifierKeys.None;
Key key = Key.None;
// Parse each field
foreach (var field in k.Split('+'))
{
// Trim surrounding white space
string trimmed = field.Trim();
// Parse
if (trimmed.Equals("Win", StringComparison.InvariantCultureIgnoreCase))
mods |= ModifierKeys.Windows;
if (trimmed.Equals("Ctrl", StringComparison.InvariantCultureIgnoreCase))
mods |= ModifierKeys.Control;
if (trimmed.Equals("Alt", StringComparison.InvariantCultureIgnoreCase))
mods |= ModifierKeys.Alt;
if (trimmed.Equals("Shift", StringComparison.InvariantCultureIgnoreCase))
mods |= ModifierKeys.Shift;
else Enum.TryParse<Key>(field, out key);
}
return new KeyGesture(key, mods);
}
#endregion
#endregion
#pragma warning disable 67
public event EventHandler CanExecuteChanged;
#pragma warning restore 67
public void NotifyCanExecuteChanged()
{
if (CanExecuteChanged != null)
CanExecuteChanged(this, new EventArgs());
}
/// <summary>
/// Initializes this command

View File

@ -23,10 +23,7 @@ namespace RainmeterStudio.UI.Controller
{
if (_documentCreateCommand == null)
{
_documentCreateCommand = new Command("DocumentCreateCommand", () => CreateWindow())
{
Shortcut = new KeyGesture(Key.N, ModifierKeys.Control)
};
_documentCreateCommand = new Command("DocumentCreateCommand", () => CreateWindow());
}
return _documentCreateCommand;

View File

@ -55,10 +55,10 @@ namespace RainmeterStudio.UI.Controller
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var tree = value as Tree<Reference>;
if (tree != null)
var reference = value as Reference;
if (reference != null)
{
return IconProvider.GetProjectItemIcon(tree.Data);
return IconProvider.GetProjectItemIcon(reference);
}
return null;

View File

@ -6,6 +6,7 @@ using System.Text;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media.Imaging;
using Microsoft.Win32;
using RainmeterStudio.Business;
using RainmeterStudio.Model;
using RainmeterStudio.UI.Dialogs;
@ -71,22 +72,9 @@ namespace RainmeterStudio.UI.Controller
#region Commands
private Command _projectCreateCommand;
public Command ProjectCreateCommand
{
get
{
if (_projectCreateCommand == null)
{
_projectCreateCommand = new Command("ProjectCreateCommand", () => CreateProject())
{
Shortcut = new KeyGesture(Key.N, ModifierKeys.Control | ModifierKeys.Shift)
};
}
public Command ProjectCreateCommand { get; private set; }
return _projectCreateCommand;
}
}
public Command ProjectOpenCommand { get; private set; }
#endregion
@ -97,6 +85,9 @@ namespace RainmeterStudio.UI.Controller
public ProjectController(ProjectManager manager)
{
Manager = manager;
ProjectCreateCommand = new Command("ProjectCreateCommand", () => CreateProject());
ProjectOpenCommand = new Command("ProjectOpenCommand", () => OpenProject());
}
/// <summary>
@ -133,7 +124,21 @@ namespace RainmeterStudio.UI.Controller
/// <param name="path"></param>
public void OpenProject(string path = null)
{
// Open dialog
OpenFileDialog dialog = new OpenFileDialog();
dialog.Filter = Resources.Strings.Dialog_FileType_Project + "|*.rsproj|"
+ Resources.Strings.Dialog_FileType_AllFiles + "|*.*";
dialog.Title = Resources.Strings.Dialog_OpenProject_Title;
dialog.Multiselect = false;
// Show dialog
bool? res = dialog.ShowDialog(OwnerWindow);
if (!res.HasValue || !res.Value)
return;
// Call manager
string filename = dialog.FileName;
Manager.OpenProject(filename);
}
/// <summary>

View File

@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
namespace RainmeterStudio.UI.Controller
{
public static class SettingsProvider
{
/// <summary>
/// Attempts to retrieve the setting of type T, where T is class
/// </summary>
/// <typeparam name="T">Any class type</typeparam>
/// <param name="name">Name of setting</param>
/// <returns>Retrieved setting, or null if not found</returns>
public static T GetSetting<T> (string name) where T : class
{
var property = Properties.Settings.Default.Properties
.OfType<SettingsProperty>()
.FirstOrDefault(x => String.Equals(x.Name, name));
return (property == null) ? null : (property.DefaultValue as T);
}
/// <summary>
/// Attempts to retrieve the setting of type T
/// </summary>
/// <typeparam name="T">Any type</typeparam>
/// <param name="name">Name of setting</param>
/// <param name="value">Output value</param>
/// <returns>True if attempt was successful</returns>
public static bool TryGetSetting<T>(string name, out T value)
{
var property = Properties.Settings.Default.Properties.OfType<SettingsProperty>().FirstOrDefault(x => x.Name.Equals(name));
if (property != null)
{
value = (T)property.DefaultValue;
return true;
}
else
{
value = default(T);
return false;
}
}
}
}

View File

@ -4,6 +4,7 @@
xmlns:ui="clr-namespace:RainmeterStudio.UI"
xmlns:ad="clr-namespace:Xceed.Wpf.AvalonDock;assembly=Xceed.Wpf.AvalonDock"
xmlns:adlayout="clr-namespace:Xceed.Wpf.AvalonDock.Layout;assembly=Xceed.Wpf.AvalonDock"
xmlns:r="clr-namespace:RainmeterStudio.Resources"
Title="Rainmeter Studio" Height="600" Width="800"
ResizeMode="CanResizeWithGrip" >
@ -21,8 +22,8 @@
<!-- Menu bar -->
<Menu Grid.Row="0" Grid.ColumnSpan="10">
<MenuItem Header="_File">
<MenuItem Header="_New">
<MenuItem Header="{x:Static r:Strings.MainWindow_File}">
<MenuItem Header="{x:Static r:Strings.MainWindow_File_New}">
<MenuItem DataContext="{Binding DocumentController.DocumentCreateCommand}"
Style="{StaticResource CommandMenuItemStyle}" >
<MenuItem.Icon>
@ -37,7 +38,15 @@
</MenuItem>
</MenuItem>
<Separator />
<MenuItem Header="_Open..." />
<MenuItem Header="{x:Static r:Strings.MainWindow_File_Open}">
<MenuItem DataContext="{Binding ProjectController.ProjectOpenCommand}"
Style="{StaticResource CommandMenuItemStyle}">
<MenuItem.Icon>
<Image Source="{Binding Icon}" />
</MenuItem.Icon>
</MenuItem>
<Separator />
</MenuItem>
<Separator />
<MenuItem Header="_Close" />
<MenuItem Header="E_xit" />

View File

@ -4,6 +4,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:ctrl="clr-namespace:RainmeterStudio.UI.Controller"
xmlns:ui="clr-namespace:RainmeterStudio.UI"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
@ -45,13 +46,16 @@
<TreeView Grid.Row="2" Name="treeProjectItems">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="True" />
<Setter Property="IsExpanded" Value="{Binding Data.IsExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding Data.IsSelected, Mode=TwoWay}" />
<EventSetter Event="Expanded" Handler="TreeViewItem_ExpandedOrCollapsed" />
<EventSetter Event="Collapsed" Handler="TreeViewItem_ExpandedOrCollapsed" />
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<DockPanel LastChildFill="True">
<Image DockPanel.Dock="Left" Width="16" Height="16" Source="{Binding Converter={StaticResource IconConverter}}" />
<Image DockPanel.Dock="Left" Width="16" Height="16" Source="{Binding Data.Reference, Converter={StaticResource IconConverter}}" />
<TextBlock Text="{Binding Data.Name}" />
</DockPanel>
</HierarchicalDataTemplate>

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
@ -17,6 +18,7 @@ using RainmeterStudio.Interop;
using RainmeterStudio.Model;
using RainmeterStudio.Storage;
using RainmeterStudio.UI.Controller;
using RainmeterStudio.UI.ViewModel;
using RainmeterStudio.Utils;
namespace RainmeterStudio.UI
@ -48,78 +50,50 @@ namespace RainmeterStudio.UI
}
}
private Command _syncWithActiveViewCommand;
public Command SyncWithActiveViewCommand
#region Commands
public Command SyncWithActiveViewCommand { get; private set; }
public Command RefreshCommand { get; private set; }
public Command ExpandAllCommand { get; private set; }
public Command CollapseAllCommand { get; private set; }
public Command ShowAllFilesCommand { get; private set; }
#endregion
private bool _canExpand = false;
private bool CanExpand
{
get
{
if (_syncWithActiveViewCommand == null)
{
_syncWithActiveViewCommand = new Command("ProjectPanel_SyncWithActiveViewCommand", SyncWithActiveView);
}
return _syncWithActiveViewCommand;
return _canExpand;
}
set
{
_canExpand = value;
if (ExpandAllCommand != null)
ExpandAllCommand.NotifyCanExecuteChanged();
if (CollapseAllCommand != null)
CollapseAllCommand.NotifyCanExecuteChanged();
}
}
private Command _refreshCommand;
public Command RefreshCommand
{
get
{
if (_refreshCommand == null)
{
_refreshCommand = new Command("ProjectPanel_RefreshCommand", SyncWithActiveView)
{
Shortcut = new KeyGesture(Key.F5)
};
}
return _refreshCommand;
}
}
private Command _expandAllCommand;
public Command ExpandAllCommand
{
get
{
if (_expandAllCommand == null)
{
_expandAllCommand = new Command("ProjectPanel_ExpandAllCommand", SyncWithActiveView);
}
return _expandAllCommand;
}
}
private Command _collapseAllCommand;
public Command CollapseAllCommand
{
get
{
if (_collapseAllCommand == null)
{
_collapseAllCommand = new Command("ProjectPanel_CollapseAllCommand", SyncWithActiveView);
}
return _collapseAllCommand;
}
}
private Command _showAllFilesCommand;
public Command ShowAllFilesCommand
{
get
{
if (_showAllFilesCommand == null)
{
_showAllFilesCommand = new Command("ProjectPanel_ShowAllFilesCommand", SyncWithActiveView);
}
return _showAllFilesCommand;
}
}
public ProjectPanel()
{
InitializeComponent();
SyncWithActiveViewCommand = new Command("ProjectPanel_SyncWithActiveViewCommand", SyncWithActiveView);
RefreshCommand = new Command("ProjectPanel_RefreshCommand", Refresh);
ExpandAllCommand = new Command("ProjectPanel_ExpandAllCommand", ExpandAll, () => _canExpand);
CollapseAllCommand = new Command("ProjectPanel_CollapseAllCommand", CollapseAll, () => !_canExpand);
ShowAllFilesCommand = new Command("ProjectPanel_ShowAllFilesCommand", Refresh);
this.DataContext = this;
Refresh();
}
@ -144,34 +118,86 @@ namespace RainmeterStudio.UI
{
this.IsEnabled = true;
// Display all files in the project directory
// Get tree
Tree<ReferenceViewModel> tree;
if (toggleShowAllFiles.IsChecked.HasValue && toggleShowAllFiles.IsChecked.Value)
{
string projectFolder = System.IO.Path.GetDirectoryName(Controller.ActiveProjectPath);
var tree = DirectoryHelper.GetFolderTree(projectFolder);
tree.Data = Controller.ActiveProject.Root.Data;
treeProjectItems.Items.Clear();
treeProjectItems.Items.Add(tree);
tree = GetAllFiles();
}
// Display only the project items
else
{
treeProjectItems.Items.Clear();
treeProjectItems.Items.Add(Controller.ActiveProject.Root);
tree = GetProjectItems();
}
// Add tree to tree view
treeProjectItems.Items.Clear();
treeProjectItems.Items.Add(tree);
}
}
private void toggleShowAllFiles_Checked(object sender, RoutedEventArgs e)
private Tree<ReferenceViewModel> GetAllFiles()
{
Refresh();
// Get directory name
string projectFolder = System.IO.Path.GetDirectoryName(Controller.ActiveProjectPath);
// Get folder tree
Tree<Reference> refTree = DirectoryHelper.GetFolderTree(projectFolder);
refTree.Data = Controller.ActiveProject.Root.Data;
// Remove the project file from the list
Tree<Reference> project = refTree.First(x => DirectoryHelper.PathsEqual(x.Data.Path, Controller.ActiveProjectPath));
refTree.Remove(project);
// Transform to reference view model and return
return refTree.TransformData<Reference, ReferenceViewModel>((data) => new ReferenceViewModel(data));
}
private void toggleShowAllFiles_Unchecked(object sender, RoutedEventArgs e)
private Tree<ReferenceViewModel> GetProjectItems()
{
Refresh();
// Get project items
Tree<Reference> refTree = Controller.ActiveProject.Root;
// Transform to reference view model and return
return refTree.TransformData<Reference, ReferenceViewModel>((data) => new ReferenceViewModel(data));
}
private void ExpandAll()
{
// Get tree
var tree = treeProjectItems.Items[0] as Tree<ReferenceViewModel>;
if (tree == null)
return;
// Expand all
tree.Apply((node) => node.Data.IsExpanded = true);
// Set can expand property
CanExpand = false;
}
private void CollapseAll()
{
// Get tree
var tree = treeProjectItems.Items[0] as Tree<ReferenceViewModel>;
if (tree == null)
return;
// Expand all
tree.Apply((node) => node.Data.IsExpanded = false);
// Set can expand property
CanExpand = true;
}
void TreeViewItem_ExpandedOrCollapsed(object sender, RoutedEventArgs e)
{
// Get tree
var tree = treeProjectItems.Items[0] as Tree<ReferenceViewModel>;
if (tree == null)
return;
// We can expand if the root is not expanded
CanExpand = (!tree.Data.IsExpanded);
}
}
}

View File

@ -0,0 +1,125 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using RainmeterStudio.Model;
namespace RainmeterStudio.UI.ViewModel
{
/// <summary>
/// Contains the view model of a reference
/// </summary>
public class ReferenceViewModel : INotifyPropertyChanged
{
#region Properties
/// <summary>
/// Gets the linked reference
/// </summary>
public Reference Reference { get; private set; }
/// <summary>
/// Gets or sets the name
/// </summary>
public string Name
{
get
{
return Reference.Name;
}
set
{
Reference.Name = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Name"));
}
}
/// <summary>
/// Gets or sets the path
/// </summary>
public string Path
{
get
{
return Reference.Path;
}
set
{
Reference.Path = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Path"));
}
}
private bool _isExpanded = true;
/// <summary>
/// Gets or sets a property indicating if the tree view item is expanded
/// </summary>
public bool IsExpanded
{
get
{
return _isExpanded;
}
set
{
_isExpanded = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("IsExpanded"));
}
}
private bool _isSelected;
/// <summary>
/// Gets or sets a property indicating if the tree view item is selected
/// </summary>
public bool IsSelected
{
get
{
return _isSelected;
}
set
{
_isSelected = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("IsSelected"));
}
}
#endregion
#region Events
/// <summary>
/// Event triggered when a property is changed
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region Constructor
/// <summary>
/// Creates a new instance of reference view model
/// </summary>
/// <param name="reference">Reference</param>
public ReferenceViewModel(Reference reference)
{
Reference = reference;
}
#endregion
}
}

View File

@ -9,6 +9,11 @@ namespace RainmeterStudio.Utils
{
public static class DirectoryHelper
{
/// <summary>
/// Gets a tree of the folder structure
/// </summary>
/// <param name="folder">Folder</param>
/// <returns>A tree</returns>
public static Tree<Reference> GetFolderTree(string folder)
{
// Build tree object
@ -28,5 +33,19 @@ namespace RainmeterStudio.Utils
// Return tree
return tree;
}
/// <summary>
/// Returns true if two paths are equal
/// </summary>
/// <param name="path1">First path</param>
/// <param name="path2">Second path</param>
/// <returns>True if the paths are equal</returns>
public static bool PathsEqual(string path1, string path2)
{
path1 = System.IO.Path.GetFullPath(path1);
path2 = System.IO.Path.GetFullPath(path2);
return String.Equals(path1, path2, StringComparison.InvariantCultureIgnoreCase);
}
}
}

View File

@ -0,0 +1,162 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RainmeterStudio.Model;
namespace RainmeterStudio.Utils
{
/// <summary>
/// Extension methods for trees
/// </summary>
public static class TreeExtensions
{
/// <summary>
/// Tree traversal orders
/// </summary>
public enum TreeTraversalOrder
{
BreadthFirst,
DepthFirst,
DepthFirstPreOrder = DepthFirst,
DepthFirstPostOrder
}
/// <summary>
/// Traverses a tree
/// </summary>
/// <typeparam name="T">Tree data type</typeparam>
/// <param name="root">Root node of tree</param>
/// <param name="order">Traversal order</param>
/// <returns>An enumeration of the nodes in the specified traverse order</returns>
public static IEnumerable<Tree<T>> Traverse<T>(this Tree<T> root, TreeTraversalOrder order = TreeTraversalOrder.BreadthFirst)
{
if (order == TreeTraversalOrder.BreadthFirst)
return TraverseBF(root);
else return TraverseDF(root, order);
}
private static IEnumerable<Tree<T>> TraverseDF<T>(this Tree<T> root, TreeTraversalOrder order)
{
// Preorder - return root first
if (order == TreeTraversalOrder.DepthFirstPreOrder)
yield return root;
// Return children
foreach (var child in root.Children)
foreach (var node in TraverseDF(child, order))
yield return node;
// Postorder - return root last
if (order == TreeTraversalOrder.DepthFirstPostOrder)
yield return root;
}
private static IEnumerable<Tree<T>> TraverseBF<T>(this Tree<T> root)
{
// Create a queue containing the root
Queue<Tree<T>> queue = new Queue<Tree<T>>();
queue.Enqueue(root);
// While there are elements in the queue
while (queue.Count > 0)
{
// Return next node in tree
var node = queue.Dequeue();
yield return node;
// Enqueue node's children
foreach (var child in node.Children)
queue.Enqueue(child);
}
}
/// <summary>
/// Applies an action to every node of the tree
/// </summary>
/// <typeparam name="T">Tree data type</typeparam>
/// <param name="root">Root node of tree</param>
/// <param name="action">Action to apply</param>
/// <param name="order">Traversal order</param>
public static void Apply<T>(this Tree<T> root, Action<Tree<T>> action, TreeTraversalOrder order = TreeTraversalOrder.BreadthFirst)
{
// Safety check
if (action == null)
return;
// Apply action
foreach (var node in Traverse(root, order))
action(node);
}
/// <summary>
/// Applies an action to every node of the tree
/// </summary>
/// <typeparam name="T">Tree data type</typeparam>
/// <param name="root">Root node of tree</param>
/// <param name="action">Action to apply</param>
/// <param name="order">Traversal order</param>
public static void ApplyToData<T>(this Tree<T> root, Action<T> action, TreeTraversalOrder order = TreeTraversalOrder.BreadthFirst)
where T : class
{
// Safety check
if (action == null)
return;
// Apply action
foreach (var node in Traverse(root, order))
action(node.Data);
}
/// <summary>
/// Rebuilds the tree by applying the specified transform function
/// </summary>
/// <typeparam name="T">Data type of tree</typeparam>
/// <typeparam name="TResult">Data type of rebuilt tree</typeparam>
/// <param name="root">The root node</param>
/// <param name="transformFunction">The transform function</param>
/// <returns>The transformed tree</returns>
public static Tree<TResult> Transform<T,TResult>(this Tree<T> root, Func<Tree<T>, Tree<TResult>> transformFunction)
{
// Safety check
if (transformFunction == null)
throw new ArgumentNullException("Transform function cannot be null.");
// Build root
Tree<TResult> resRoot = transformFunction(root);
// Add children
foreach (var node in root.Children)
resRoot.Children.Add(Transform(node, transformFunction));
// Return
return resRoot;
}
/// <summary>
/// Rebuilds the tree by applying the specified transform function
/// </summary>
/// <typeparam name="T">Data type of tree</typeparam>
/// <typeparam name="TResult">Data type of rebuilt tree</typeparam>
/// <param name="root">The root node</param>
/// <param name="transformFunction">The transform function</param>
/// <returns>The transformed tree</returns>
public static Tree<TResult> TransformData<T, TResult>(this Tree<T> root, Func<T, TResult> transformFunction)
{
// Safety check
if (transformFunction == null)
throw new ArgumentNullException("Transform function cannot be null.");
// Build root
Tree<TResult> resRoot = new Tree<TResult>(transformFunction(root.Data));
// Add children
foreach (var node in root.Children)
resRoot.Children.Add(TransformData(node, transformFunction));
// Return
return resRoot;
}
}
}

View File

@ -1,3 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration>
<configSections>
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
<section name="RainmeterStudio.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
</sectionGroup>
</configSections>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup><userSettings>
<RainmeterStudio.Properties.Settings>
<setting name="ProjectCreateCommand_Shortcut" serializeAs="String">
<value>Ctrl+Shift+N</value>
</setting>
<setting name="ProjectPanel_RefreshCommand_Shortcut" serializeAs="String">
<value>F5</value>
</setting>
<setting name="DocumentCreateCommand_Shortcut" serializeAs="String">
<value>Ctrl+N</value>
</setting>
<setting name="ProjectOpenCommand_Shortcut" serializeAs="String">
<value>Ctrl+Shift+O</value>
</setting>
<setting name="DocumentCloseCommand_Shortcut" serializeAs="String">
<value>Ctrl+W</value>
</setting>
</RainmeterStudio.Properties.Settings>
</userSettings>
</configuration>