Work on documents, separated UI from Main

This commit is contained in:
Tiberiu Chibici 2014-07-29 23:35:59 +03:00
parent 09224d9af7
commit 473f23378f
21 changed files with 316 additions and 226 deletions

View File

@ -2,13 +2,18 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using RainmeterStudio.Documents;
using RainmeterStudio.Model;
using RainmeterStudio.Model.Events;
using RainmeterStudio.Utils;
namespace RainmeterStudio.Business
{
/// <summary>
/// Document manager
/// </summary>
public class DocumentManager
{
#region Events
@ -62,6 +67,61 @@ namespace RainmeterStudio.Business
{
}
/// <summary>
/// Registers all classes with the auto register flag
/// </summary>
/// <remarks>We love linq</remarks>
public void PerformAutoRegister()
{
// Get all assemblies
AppDomain.CurrentDomain.GetAssemblies()
// Get all types
.SelectMany(assembly => assembly.GetTypes())
// Select only the classes
.Where(type => type.IsClass)
// That have the AutoRegister attribute
.Where(type => type.GetCustomAttributes(typeof(AutoRegisterAttribute), false).Length > 0)
// That implement any of the types that can be registered
.Where((type) =>
{
bool res = false;
res |= typeof(IDocumentEditorFactory).IsAssignableFrom(type);
res |= typeof(IDocumentStorage).IsAssignableFrom(type);
res |= typeof(DocumentTemplate).IsAssignableFrom(type);
return res;
})
// Obtain their default constructor
.Select(type => type.GetConstructor(new Type[0]))
// Invoke the default constructor
.Select(constructor => constructor.Invoke(new object[0]))
// Register
.ForEach(obj =>
{
// Try to register factory
var factory = obj as IDocumentEditorFactory;
if (factory != null)
RegisterEditorFactory(factory);
// Try to register as storage
var storage = obj as IDocumentStorage;
if (storage != null)
RegisterStorage(storage);
// Try to register as document template
var doctemplate = obj as DocumentTemplate;
if (doctemplate != null)
RegisterTemplate(doctemplate);
});
}
/// <summary>
/// Registers a document editor factory
/// </summary>
@ -117,6 +177,7 @@ namespace RainmeterStudio.Business
/// <summary>
/// Opens the specified document
/// </summary>
/// <param name="path">The path to the file to open</param>
public IDocumentEditor Open(string path)
{
// Try to open
@ -132,6 +193,10 @@ namespace RainmeterStudio.Business
return editor;
}
/// <summary>
/// Saves a document
/// </summary>
/// <param name="document">The document</param>
public void Save(IDocument document)
{
// Find a storage
@ -147,6 +212,11 @@ namespace RainmeterStudio.Business
document.IsDirty = false;
}
/// <summary>
/// Saves the document as
/// </summary>
/// <param name="path">Path</param>
/// <param name="document">Document</param>
public void SaveAs(string path, IDocument document)
{
// Find a storage
@ -163,6 +233,11 @@ namespace RainmeterStudio.Business
document.IsDirty = false;
}
/// <summary>
/// Saves a copy of the document
/// </summary>
/// <param name="path">Path</param>
/// <param name="document">Document</param>
public void SaveACopy(string path, IDocument document)
{
// Find a storage
@ -172,6 +247,20 @@ namespace RainmeterStudio.Business
storage.Write(path, document);
}
/// <summary>
/// Closes a document editor
/// </summary>
/// <param name="editor"></param>
public void Close(IDocumentEditor editor)
{
// Remove from list of opened editors
_editors.Remove(editor);
// Close event
if (DocumentClosed != null)
DocumentClosed(this, new DocumentClosedEventArgs(editor));
}
#endregion
#region Private functions

View File

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace RainmeterStudio.Documents
{
public class AutoRegisterAttribute : Attribute
{
}
}

View File

@ -1,132 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media;
using RainmeterStudio.Model;
using RainmeterStudio.UI.Controller;
namespace RainmeterStudio.Documents
{
/// <summary>
/// Represents a document template
/// </summary>
public class DocumentTemplate<T> where T : IDocument
{
#region Private fields
private Func<T> _createFunction;
#endregion
/// <summary>
/// Gets the document template name
/// </summary>
public string Name { get; private set; }
#region Icon property
private ImageSource _icon = null;
/// <summary>
/// Gets or sets the template's icon
/// </summary>
public virtual ImageSource Icon
{
get
{
if (_icon == null)
return IconProvider.GetIcon("Template_" + Name);
return _icon;
}
set
{
_icon = value;
}
}
#endregion
#region Display text property
private string _displayText = null;
/// <summary>
/// Gets or sets the display text
/// </summary>
public virtual string DisplayText
{
get
{
if (_displayText == null)
return Resources.Strings.ResourceManager.GetString("Template_" + Name + "_DisplayText");
return _displayText;
}
set
{
_displayText = value;
}
}
#endregion
#region Description property
private string _description = null;
/// <summary>
/// Gets or sets the description
/// </summary>
public virtual string Description
{
get
{
if (_description == null)
return Resources.Strings.ResourceManager.GetString("Template_" + Name + "_Description");
return _description;
}
set
{
_description = value;
}
}
#endregion
/// <summary>
/// Gets or sets the default extension of this template
/// </summary>
public string DefaultExtension { get; set; }
/// <summary>
/// Gets or sets the category in which this template belongs
/// </summary>
public virtual string Category { get; set; }
/// <summary>
/// Initializes the document template
/// </summary>
/// <param name="name">Name of document template</param>
public DocumentTemplate(string name, string defaultExtension = null, string category = null, Func<T> createDocument = null)
{
Name = name;
DefaultExtension = defaultExtension;
_createFunction = createDocument;
}
/// <summary>
/// Creates a document of type T
/// </summary>
/// <returns></returns>
public virtual T CreateDocument()
{
if (_createFunction != null)
return _createFunction();
return default(T);
}
}
}

View File

@ -1,4 +1,5 @@
namespace RainmeterStudio.Model
using RainmeterStudio.Model;
namespace RainmeterStudio.Documents
{
/// <summary>
/// Represents a document template

View File

@ -16,9 +16,9 @@ namespace RainmeterStudio.Documents.Text
/// <summary>
/// Gets or sets the text associated with this document
/// </summary>
public string Text
public List<string> Lines
{
get; set;
get; private set;
}
/// <summary>
@ -67,7 +67,7 @@ namespace RainmeterStudio.Documents.Text
/// </summary>
public TextDocument()
{
Text = String.Empty;
Lines = new List<string>();
}
}
}

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RainmeterStudio.Model;
namespace RainmeterStudio.Documents.Text
{
/// <summary>
/// A blank text document template
/// </summary>
[AutoRegister]
public class TextDocumentTemplate : DocumentTemplate
{
public TextDocumentTemplate()
: base("TextDocument", "txt")
{
}
public override IDocument CreateDocument()
{
return new TextDocument();
}
}
}

View File

@ -7,7 +7,7 @@ using RainmeterStudio.Model;
namespace RainmeterStudio.Documents.Text
{
/*public class TextEditor : IDocumentEditor
public class TextEditor : IDocumentEditor
{
private TextDocument _document;
private TextEditorControl _control;
@ -18,10 +18,14 @@ namespace RainmeterStudio.Documents.Text
_control = new TextEditorControl(document);
}
public override IDocument Document { get { return _document; } }
public override string Title { get { return _document.Name; } }
public override System.Windows.UIElement EditorUI { get { return _control; } }
}*/
public IDocument AttachedDocument
{
get { return _document; }
}
public System.Windows.UIElement EditorUI
{
get { return _control; }
}
}
}

View File

@ -26,7 +26,7 @@ namespace RainmeterStudio.Documents.Text
InitializeComponent();
_document = document;
text.Text = document.Text;
text.Text = document.Lines.Aggregate((a, b) => a + "\n" + b);
}
}
}

View File

@ -8,51 +8,17 @@ using RainmeterStudio.Model;
namespace RainmeterStudio.Documents.Text
{
/*public class TextEditorFactory : IDocumentEditorFactory
[AutoRegister]
public class TextEditorFactory : IDocumentEditorFactory
{
private TextStorage _storage = new TextStorage();
/// <inheritdoc />
public string EditorName
{
get { return Resources.Strings.DocumentEditor_Text_Name; }
}
/// <inheritdoc />
public IEnumerable<DocumentTemplate> CreateDocumentFormats
{
get
{
yield return new DocumentTemplate()
{
Name = Resources.Strings.DocumentFormat_TextFile_Name,
Category = Resources.Strings.Category_Utility,
DefaultExtension = ".txt",
Description = Resources.Strings.DocumentFormat_TextFile_Description,
Icon = new System.Windows.Media.Imaging.BitmapImage(new Uri(Resources.Icons.DocumentTemplate_Text, UriKind.RelativeOrAbsolute)),
Factory = this
};
}
}
public IDocumentEditor CreateEditor(IDocument document)
{
TextDocument textDocument = document as TextDocument;
if (textDocument == null)
throw new ArgumentException("Cannot edit provided document.");
return new TextEditor(textDocument);
return new TextEditor((TextDocument)document);
}
public IDocumentStorage Storage { get { return _storage; } }
public IDocument CreateDocument(DocumentTemplate format, string path)
public bool CanEdit(Type type)
{
var document = new TextDocument();
document.FilePath = path;
return document;
return type.Equals(typeof(TextDocument));
}
}
}*/
}

View File

@ -7,6 +7,7 @@ namespace RainmeterStudio.Documents.Text
/// <summary>
/// Storage for text files
/// </summary>
[AutoRegister]
public class TextStorage : IDocumentStorage
{
/// <inheritdoc />
@ -15,7 +16,7 @@ namespace RainmeterStudio.Documents.Text
TextDocument document = new TextDocument();
document.Reference.Path = path;
document.Reference.Name = Path.GetFileName(path);
document.Text = File.ReadAllText(path);
document.Lines.AddRange(File.ReadAllLines(path));
return document;
}
@ -28,7 +29,7 @@ namespace RainmeterStudio.Documents.Text
if (textDocument == null)
throw new ArgumentException("Provided document is not supported by this storage.");
File.WriteAllText(path, textDocument.Text);
File.WriteAllLines(path, textDocument.Lines);
}
/// <inheritdoc />

View File

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows;
using RainmeterStudio.Business;
using RainmeterStudio.Documents;
using RainmeterStudio.Storage;
using RainmeterStudio.UI;
using RainmeterStudio.UI.Controller;
namespace RainmeterStudio
{
static class MainClass
{
[STAThread]
public static void Main()
{
// Display splash
SplashScreen splash = new SplashScreen("Resources/splash.png");
splash.Show(true);
// Initialize managers
ProjectStorage projectStorage = new ProjectStorage();
ProjectManager projectManager = new ProjectManager(projectStorage);
DocumentManager documentManager = new DocumentManager();
documentManager.PerformAutoRegister();
// Create & run app
var uiManager = new UIManager(projectManager, documentManager);
uiManager.Run();
}
}
}

View File

@ -66,26 +66,28 @@
</Reference>
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="App.xaml">
<Page Include="UI\App.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
</Page>
<Compile Include="Business\DocumentManager.cs" />
<Compile Include="Business\ProjectManager.cs" />
<Compile Include="Documents\AutoRegisterAttribute.cs" />
<Compile Include="Documents\DocumentEditorFeatures\ICustomDocumentTitleProvider.cs" />
<Compile Include="Documents\DocumentEditorFeatures\ISelectionPropertiesProvider.cs" />
<Compile Include="Documents\DocumentEditorFeatures\IToolboxProvider.cs" />
<Compile Include="Documents\DocumentEditorFeatures\IUndoSupport.cs" />
<Compile Include="Documents\DocumentTemplate.cs" />
<Compile Include="Documents\IDocumentEditor.cs" />
<Compile Include="Documents\IDocumentEditorFactory.cs" />
<Compile Include="Documents\IDocumentStorage.cs" />
<Compile Include="Documents\Text\TextDocument.cs" />
<Compile Include="Documents\Text\TextDocumentTemplate.cs" />
<Compile Include="Documents\Text\TextEditorControl.xaml.cs">
<DependentUpon>TextEditorControl.xaml</DependentUpon>
</Compile>
<Compile Include="Documents\Text\TextStorage.cs" />
<Compile Include="Model\DocumentTemplate.cs" />
<Compile Include="MainClass.cs" />
<Compile Include="Documents\DocumentTemplate_.cs" />
<Compile Include="Model\IDocument.cs" />
<Compile Include="Documents\Ini\IniDocument.cs" />
<Compile Include="Documents\Ini\IniSkinDesigner.cs" />
@ -130,9 +132,11 @@
<Compile Include="UI\ProjectPanel.xaml.cs">
<DependentUpon>ProjectPanel.xaml</DependentUpon>
</Compile>
<Compile Include="UI\UIManager.cs" />
<Compile Include="UI\ViewModel\DocumentTemplateViewModel.cs" />
<Compile Include="UI\ViewModel\ReferenceViewModel.cs" />
<Compile Include="Utils\DirectoryHelper.cs" />
<Compile Include="Utils\LinqExtension.cs" />
<Compile Include="Utils\TreeExtensions.cs" />
<Page Include="Documents\Ini\IniSkinDesignerControl.xaml">
<SubType>Designer</SubType>
@ -154,7 +158,7 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Compile Include="App.xaml.cs">
<Compile Include="UI\App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
@ -214,7 +218,7 @@
<Resource Include="Resources\Icons\32\text_generic.png" />
</ItemGroup>
<ItemGroup>
<SplashScreen Include="Resources\splash.png" />
<Resource Include="Resources\splash.png" />
</ItemGroup>
<ItemGroup>
<Resource Include="Resources\Icons\16\arrow_left.png" />

View File

@ -1,12 +1,10 @@
<Application x:Class="RainmeterStudio.App"
<Application x:Class="RainmeterStudio.UI.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="UI/MainWindow.xaml"
Startup="Application_Startup">
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="UI/Styles/Common.xaml" />
<ResourceDictionary Source="Styles/Common.xaml" />
</ResourceDictionary.MergedDictionaries>
<Style TargetType="ToolBar">

View File

@ -7,16 +7,16 @@ using System.Windows;
using RainmeterStudio.Business;
using RainmeterStudio.Documents.Text;
namespace RainmeterStudio
namespace RainmeterStudio.UI
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
private void Application_Startup(object sender, StartupEventArgs e)
public App()
{
//DocumentManager.Instance.RegisterEditorFactory(new TextEditorFactory());
InitializeComponent();
}
}
}

View File

@ -9,6 +9,7 @@ using RainmeterStudio.Model;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media.Imaging;
using RainmeterStudio.Documents;
namespace RainmeterStudio.UI.Controller
{
@ -34,18 +35,23 @@ namespace RainmeterStudio.UI.Controller
#endregion
/// <summary>
/// Triggered when a document is opened
/// </summary>
public event EventHandler<DocumentOpenedEventArgs> DocumentOpened
{
add
add { DocumentManager.DocumentOpened += value; }
remove { DocumentManager.DocumentOpened -= value; }
}
/// <summary>
/// Triggered when a document is closed
/// </summary>
public event EventHandler<DocumentClosedEventArgs> DocumentClosed
{
DocumentManager.DocumentOpened += value;
add { DocumentManager.DocumentClosed += value; }
remove { DocumentManager.DocumentClosed -= value; }
}
remove
{
DocumentManager.DocumentOpened -= value;
}
}
public event EventHandler DocumentClosed;
public Window OwnerWindow { get; set; }
@ -63,7 +69,7 @@ namespace RainmeterStudio.UI.Controller
var dialog = new CreateDocumentDialog()
{
Owner = OwnerWindow,
SelectedFormat = defaultFormat,
SelectedTemplate = defaultFormat,
SelectedPath = defaultPath
};
bool? res = dialog.ShowDialog();
@ -71,7 +77,7 @@ namespace RainmeterStudio.UI.Controller
if (!res.HasValue || !res.Value)
return;
var format = dialog.SelectedFormat;
var format = dialog.SelectedTemplate;
var path = dialog.SelectedPath;
// Call manager

View File

@ -12,6 +12,7 @@ using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using RainmeterStudio.Business;
using RainmeterStudio.Documents;
using RainmeterStudio.Model;
namespace RainmeterStudio.UI.Dialogs
@ -24,7 +25,7 @@ namespace RainmeterStudio.UI.Dialogs
/// <summary>
/// Gets or sets the currently selected file format
/// </summary>
public DocumentTemplate SelectedFormat
public DocumentTemplate SelectedTemplate
{
get
{

View File

@ -12,6 +12,7 @@ using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using RainmeterStudio.Business;
using RainmeterStudio.Documents;
using RainmeterStudio.Model;
namespace RainmeterStudio.UI.Dialogs

View File

@ -27,27 +27,22 @@ namespace RainmeterStudio.UI
public DocumentController DocumentController { get; set; }
public ProjectController ProjectController { get; set; }
public MainWindow()
public MainWindow(ProjectController projCtrl, DocumentController docCtrl)
{
InitializeComponent();
this.DataContext = this;
// Set fields
DataContext = this;
DocumentController = docCtrl;
ProjectController = projCtrl;
// Initialize project controller
// TODO: put this in main
ProjectStorage projectStorage = new ProjectStorage();
ProjectManager projectManager = new ProjectManager(projectStorage);
ProjectController = new Controller.ProjectController(projectManager);
ProjectController.OwnerWindow = this;
// Add key bindings
this.AddKeyBinding(ProjectController.ProjectCreateCommand);
// Initialize document controller
DocumentManager documentManager = new DocumentManager();
DocumentController = new DocumentController(documentManager, projectManager);
DocumentController.OwnerWindow = this;
DocumentController.DocumentOpened += documentController_DocumentOpened;
this.AddKeyBinding(DocumentController.DocumentCreateCommand);
// Subscribe to events
DocumentController.DocumentOpened += documentController_DocumentOpened;
// Initialize panels
projectPanel.Controller = ProjectController;
}

View File

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RainmeterStudio.Business;
using RainmeterStudio.UI.Controller;
namespace RainmeterStudio.UI
{
public class UIManager
{
/// <summary>
/// Gets the WPF app
/// </summary>
protected App App {get; private set;}
/// <summary>
/// Gets the project manager
/// </summary>
protected ProjectManager ProjectManager { get; private set; }
/// <summary>
/// Gets the document manager
/// </summary>
protected DocumentManager DocumentManager { get; private set; }
/// <summary>
/// Initializes the UI manager
/// </summary>
/// <param name="projectManager">Project manager</param>
/// <param name="documentManager">Document manager</param>
public UIManager(ProjectManager projectManager, DocumentManager documentManager)
{
App = new UI.App();
ProjectManager = projectManager;
DocumentManager = documentManager;
}
/// <summary>
/// Runs the UI thread
/// </summary>
public void Run()
{
// Create controllers
ProjectController projectController = new ProjectController(ProjectManager);
DocumentController documentController = new DocumentController(DocumentManager, ProjectManager);
// Create and set up main window
MainWindow mainWindow = new MainWindow(projectController, documentController);
projectController.OwnerWindow = mainWindow;
documentController.OwnerWindow = mainWindow;
mainWindow.Show();
// Run app
App.Run();
}
}
}

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media;
using RainmeterStudio.Documents;
using RainmeterStudio.Model;
using RainmeterStudio.UI.Controller;

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace RainmeterStudio.Utils
{
/// <summary>
/// Linq extensions
/// </summary>
public static class LinqExtensions
{
/// <summary>
/// Applies action on every item from the container
/// </summary>
/// <typeparam name="T">Enumerable type</typeparam>
/// <param name="container">Container</param>
/// <param name="action">Action</param>
public static void ForEach<T> (this IEnumerable<T> container, Action<T> action)
{
foreach (var obj in container)
action(obj);
}
}
}