// ReSharper disable once CheckNamespace namespace Fluent { using System; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.IsolatedStorage; using System.Security.Cryptography; using System.Text; using System.Windows; /// /// Handles loading and saving the state of a from/to a , for temporary storage, and from/to , for persistent storage. /// public class RibbonStateStorage : IRibbonStateStorage { private static readonly MD5 md5Hasher = MD5.Create(); private readonly Ribbon ribbon; // Name of the isolated storage file private string isolatedStorageFileName; private readonly Stream memoryStream; /// /// Creates a new instance. /// /// The of which the state should be stored. public RibbonStateStorage(Ribbon ribbon) { this.ribbon = ribbon; this.memoryStream = new MemoryStream(); } /// /// Finalizes an instance of the class. /// ~RibbonStateStorage() { this.Dispose(false); } /// /// Gets whether this object already got disposed. /// protected bool Disposed { get; private set; } /// public bool IsLoading { get; private set; } /// public bool IsLoaded { get; private set; } /// /// Gets name of the isolated storage file /// protected string IsolatedStorageFileName { get { if (this.isolatedStorageFileName != null) { return this.isolatedStorageFileName; } var stringForHash = string.Empty; var window = Window.GetWindow(this.ribbon); if (window != null) { stringForHash += "." + window.GetType().FullName; if (string.IsNullOrEmpty(window.Name) == false && window.Name.Trim().Length > 0) { stringForHash += "." + window.Name; } } if (string.IsNullOrEmpty(this.ribbon.Name) == false && this.ribbon.Name.Trim().Length > 0) { stringForHash += "." + this.ribbon.Name; } this.isolatedStorageFileName = "Fluent.Ribbon.State." + BitConverter.ToInt32(md5Hasher.ComputeHash(Encoding.Default.GetBytes(stringForHash)), 0).ToString("X"); return this.isolatedStorageFileName; } } /// public virtual void SaveTemporary() { this.memoryStream.Position = 0; this.Save(this.memoryStream); } /// public virtual void Save() { // Check whether automatic save is valid now if (this.ribbon.AutomaticStateManagement == false) { Debug.WriteLine("State not saved to isolated storage. Because automatic state management is disabled."); return; } if (this.IsLoaded == false) { Debug.WriteLine("State not saved to isolated storage. Because state was not loaded before."); return; } try { var storage = GetIsolatedStorageFile(); using (var stream = new IsolatedStorageFileStream(this.IsolatedStorageFileName, FileMode.Create, FileAccess.Write, storage)) { this.Save(stream); } } catch (Exception ex) { Trace.WriteLine($"Error while trying to save Ribbon state. Error: {ex}"); } } /// /// Saves state to . /// /// Stream protected virtual void Save(Stream stream) { // Don't save or load state in design mode if (DesignerProperties.GetIsInDesignMode(this.ribbon)) { return; } var builder = this.CreateStateData(); var writer = new StreamWriter(stream); writer.Write(builder.ToString()); writer.Flush(); } /// /// Create the serialized state data which should be saved later. /// /// which contains the serialized state data. protected virtual StringBuilder CreateStateData() { var builder = new StringBuilder(); // Save Ribbon State builder.Append(this.ribbon.IsMinimized.ToString(CultureInfo.InvariantCulture)); builder.Append(','); builder.Append(this.ribbon.ShowQuickAccessToolBarAboveRibbon.ToString(CultureInfo.InvariantCulture)); return builder; } /// public virtual void LoadTemporary() { this.memoryStream.Position = 0; this.Load(this.memoryStream); } /// public virtual void Load() { // Don't save or load state in design mode if (DesignerProperties.GetIsInDesignMode(this.ribbon)) { Debug.WriteLine("State not loaded from isolated storage. Because we are in design mode."); this.IsLoaded = true; return; } if (this.ribbon.AutomaticStateManagement == false) { Debug.WriteLine("State not loaded from isolated storage. Because automatic state management is disabled."); this.IsLoaded = true; return; } try { var storage = GetIsolatedStorageFile(); if (IsolatedStorageFileExists(storage, this.IsolatedStorageFileName)) { using (var stream = new IsolatedStorageFileStream(this.IsolatedStorageFileName, FileMode.Open, FileAccess.Read, storage)) { this.Load(stream); // Copy loaded state to MemoryStream for temporary storage. // Temporary storage is used for style changes etc. so we can apply the current state again. stream.Position = 0; this.memoryStream.Position = 0; stream.CopyTo(this.memoryStream); } } } catch (Exception ex) { Trace.WriteLine($"Error while trying to load Ribbon state. Error: {ex}"); } this.IsLoaded = true; } /// /// Loads state from . /// /// The to load the state from. protected virtual void Load(Stream stream) { this.IsLoading = true; try { this.LoadStateCore(stream); } finally { this.IsLoading = false; } } /// /// Loads state from . /// /// The to load the state from. protected virtual void LoadStateCore(Stream stream) { var reader = new StreamReader(stream); var data = reader.ReadToEnd(); this.LoadState(data); } /// /// Loads state from . /// /// The to load the state from. protected virtual void LoadState(string data) { // Load Ribbon State var ribbonProperties = data.Split(','); this.ribbon.IsMinimized = bool.Parse(ribbonProperties[0]); this.ribbon.ShowQuickAccessToolBarAboveRibbon = bool.Parse(ribbonProperties[1]); } /// /// Determines whether the given file exists in the given storage /// protected static bool IsolatedStorageFileExists(IsolatedStorageFile storage, string fileName) { var files = storage.GetFileNames(fileName); return files.Length != 0; } /// /// Get this which should be used to store the current state. /// /// or if threw an exception. protected static IsolatedStorageFile GetIsolatedStorageFile() { try { return IsolatedStorageFile.GetUserStoreForDomain(); } catch { return IsolatedStorageFile.GetUserStoreForAssembly(); } } /// /// Resets saved state. /// public virtual void Reset() { var storage = GetIsolatedStorageFile(); foreach (var filename in storage.GetFileNames("*Fluent.Ribbon.State*")) { storage.DeleteFile(filename); } } /// public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// /// Defines whether managed resources should also be freed. protected virtual void Dispose(bool disposing) { if (this.Disposed) { return; } if (disposing) { this.memoryStream.Dispose(); } this.Disposed = true; } } }