diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bdc3535 --- /dev/null +++ b/.gitignore @@ -0,0 +1,108 @@ +# Build Folders (you can keep bin if you'd like, to store dlls and pdbs) +[Bb]in/ +[Oo]bj/ + +# mstest test results +TestResults + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results +[Dd]ebug/ +[Rr]elease/ +x64/ +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.log +*.vspscc +*.vssscc +.builds + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper* + +# NCrunch +*.ncrunch* +.*crunch*.local.xml + +# Installshield output folder +[Ee]xpress + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish + +# Publish Web Output +*.Publish.xml + +# NuGet Packages Directory +packages + +# Windows Azure Build Output +csx +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +[Bb]in +[Oo]bj +sql +TestResults +[Tt]est[Rr]esult* +*.Cache +ClientBin +[Ss]tyle[Cc]op.* +~$* +*.dbmdl +Generated_Code #added for RIA/Silverlight projects + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML diff --git a/Odyssey/Demos/AlignTableCellMiddleCenterHS.png b/Odyssey/Demos/AlignTableCellMiddleCenterHS.png new file mode 100644 index 0000000..34014a1 Binary files /dev/null and b/Odyssey/Demos/AlignTableCellMiddleCenterHS.png differ diff --git a/Odyssey/Demos/App.xaml b/Odyssey/Demos/App.xaml new file mode 100644 index 0000000..a335e37 --- /dev/null +++ b/Odyssey/Demos/App.xaml @@ -0,0 +1,8 @@ + + + + + diff --git a/Odyssey/Demos/App.xaml.cs b/Odyssey/Demos/App.xaml.cs new file mode 100644 index 0000000..4d1351a --- /dev/null +++ b/Odyssey/Demos/App.xaml.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Data; +using System.Linq; +using System.Windows; + +namespace Demos +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application + { + } +} diff --git a/Odyssey/Demos/BreadcrumbWithDataSource.xaml b/Odyssey/Demos/BreadcrumbWithDataSource.xaml new file mode 100644 index 0000000..24199bd --- /dev/null +++ b/Odyssey/Demos/BreadcrumbWithDataSource.xaml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Odyssey/Demos/BreadcrumbWithDataSource.xaml.cs b/Odyssey/Demos/BreadcrumbWithDataSource.xaml.cs new file mode 100644 index 0000000..ecdc693 --- /dev/null +++ b/Odyssey/Demos/BreadcrumbWithDataSource.xaml.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Demos +{ + /// + /// Interaction logic for Window1.xaml + /// + public partial class BreadcrumbDS : Window + { + public BreadcrumbDS() + { + + InitializeComponent(); + } + } +} diff --git a/Odyssey/Demos/Demos.csproj b/Odyssey/Demos/Demos.csproj new file mode 100644 index 0000000..eb19a5e --- /dev/null +++ b/Odyssey/Demos/Demos.csproj @@ -0,0 +1,188 @@ + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {4F736E6C-D570-4882-B521-93819C059E82} + WinExe + Properties + Demos + Demos + v3.5 + 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 4 + SAK + SAK + SAK + SAK + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + 3.0 + + + 3.0 + + + 3.0 + + + 3.0 + + + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + App.xaml + Code + + + MainDemo.xaml + Code + + + + + BreadcrumbWithDataSource.xaml + + + + Main.xaml + + + Outlook.xaml + + + Code + + + True + True + Resources.resx + + + True + Settings.settings + True + + + RibbonDemo.xaml + + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Always + + + Always + + + + + {333FDC55-6B47-4A64-A2DF-A4C5823FAC74} + Odyssey + + + + + \ No newline at end of file diff --git a/Odyssey/Demos/FolderItem.cs b/Odyssey/Demos/FolderItem.cs new file mode 100644 index 0000000..e15e633 --- /dev/null +++ b/Odyssey/Demos/FolderItem.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Controls; +using System.Windows.Media.Imaging; +using System.Windows.Media; +using System.IO; + +namespace Demos +{ + public class FolderItem + { + public string Folder { get; set; } + public ImageSource Image { get; set; } + + public FolderItem() + : base() + { + ImageSourceConverter isc = new ImageSourceConverter(); + Image = isc.ConvertFrom("openfolderHS.png") as ImageSource; + } + } +} diff --git a/Odyssey/Demos/Main.xaml b/Odyssey/Demos/Main.xaml new file mode 100644 index 0000000..fa87a52 --- /dev/null +++ b/Odyssey/Demos/Main.xaml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + diff --git a/Odyssey/Demos/Main.xaml.cs b/Odyssey/Demos/Main.xaml.cs new file mode 100644 index 0000000..4cdf858 --- /dev/null +++ b/Odyssey/Demos/Main.xaml.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; +using Demos.Ribbon; + +namespace Demos +{ + /// + /// Interaction logic for Main.xaml + /// + public partial class Main : Window + { + public Main() + { + InitializeComponent(); + } + + private void OutlookClick(object sender, RoutedEventArgs e) + { + Outlook outlook = new Outlook(); + outlook.ShowDialog(); + } + + private void ExplorerBarClick(object sender, RoutedEventArgs e) + { + ExplorerBar explorerBar = new ExplorerBar(); + explorerBar.ShowDialog(); + } + + private void BreadcrumbBarClick(object sender, RoutedEventArgs e) + { + BreadcrumbDS bds = new BreadcrumbDS(); + bds.ShowDialog(); + } + + private void RibbonBarClick(object sender, RoutedEventArgs e) + { + RibbonDemo ribbonDemo = new RibbonDemo(); + ribbonDemo.Show(); + } + } +} diff --git a/Odyssey/Demos/MainDemo.xaml b/Odyssey/Demos/MainDemo.xaml new file mode 100644 index 0000000..5bd41f0 --- /dev/null +++ b/Odyssey/Demos/MainDemo.xaml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Odyssey/Demos/MainDemo.xaml.cs b/Odyssey/Demos/MainDemo.xaml.cs new file mode 100644 index 0000000..33f6a6c --- /dev/null +++ b/Odyssey/Demos/MainDemo.xaml.cs @@ -0,0 +1,208 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using Odyssey.Controls; +using System.Windows.Media.Animation; + +namespace Demos +{ + //BUGFIX: Corrected a glitch when the SubItems of a Breadrumb where reopened. This caused the SelectedItem to be set to null and therefore alls trails after the breadcrumb where removed. + + /// + /// Interaction logic for Window1.xaml + /// + public partial class ExplorerBar : Window + { + public ExplorerBar() + { + InitializeComponent(); + } + + #region ExplorerBar + private void Button_Click(object sender, RoutedEventArgs e) + { + Background = Brushes.LightSteelBlue; + } + + private void Button_Click_1(object sender, RoutedEventArgs e) + { + text3.Visibility = text3.Visibility == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible; + text1.Visibility = text1.Visibility == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible; + } + + + private void Button_Click_3(object sender, RoutedEventArgs e) + { + expander1.IsMinimized ^= true; + } + + private void Button_Click_4(object sender, RoutedEventArgs e) + { + expander2.IsMinimized ^= true; + } + + private void Animate1Click(object sender, RoutedEventArgs e) + { + expander2.IsExpanded ^= true; + expander1.IsMinimized ^= true; + + } + private void Animate2Click(object sender, RoutedEventArgs e) + { + //text2.Visibility = text2.Visibility == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible; + expander1.IsExpanded ^= true; + expander2.IsExpanded ^= true; + } + #endregion + + #region BreadcrumbBar + /// + /// A BreadcrumbItem needs to populate it's Items. This can be due to the fact that a new BreadcrumbItem is selected, and thus + /// it's Items must be populated to determine whether this BreadcrumbItem show a dropdown button, + /// or when the Path property of the BreadcrumbBar is changed and therefore the Breadcrumbs must be populated from the new path. + /// + private void BreadcrumbBar_PopulateItems(object sender, Odyssey.Controls.BreadcrumbItemEventArgs e) + { + BreadcrumbItem item = e.Item; + if (item.Items.Count == 0) + { + PopulateFolders(item); + e.Handled = true; + } + } + + /// + /// Gets a collection of all drives on the computer. + /// + private static IEnumerable GetDrives(string separatorString) + { + int separatorLength = separatorString.Length; + var folders = from drive in System.IO.Directory.GetLogicalDrives() select drive.EndsWith(separatorString) ? drive.Remove(drive.Length - separatorLength) : drive; + return folders.AsEnumerable(); + } + + /// + /// Populate the Items of the specified BreadcrumbItem with the sub folders if necassary. + /// + /// + private static void PopulateFolders(BreadcrumbItem item) + { + List folderItems = GetFolderItemsFromBreadcrumb(item); + item.ItemsSource = folderItems; + } + + /// + /// Gets a list of FolderItems that are the subfolders of the specified BreadcrumbItem. + /// + private static List GetFolderItemsFromBreadcrumb(BreadcrumbItem item) + { + BreadcrumbBar bar = item.BreadcrumbBar; + string path = bar.PathFromBreadcrumbItem(item); + string trace = item.TraceValue; + string[] subFolders; + if (trace.Equals("Computer")) + { + subFolders = GetDrives(bar.SeparatorString).ToArray(); + } + else + { + try + { + subFolders = (from dir in System.IO.Directory.GetDirectories(path + "\\") select System.IO.Path.GetFileName(dir)).ToArray(); + } + catch + { + //maybe we don't have access! + subFolders = new string[] { }; + } + } + List folderItems = (from folder in subFolders orderby folder select new FolderItem { Folder = folder }).ToList(); + return folderItems; + } + + /// + /// Convert the path from visual to logical or vice versa: + /// + private void BreadcrumbBar_PathConversion(object sender, PathConversionEventArgs e) + { + if (e.Mode == PathConversionEventArgs.ConversionMode.DisplayToEdit) + { + if (e.DisplayPath.StartsWith(@"Computer\", StringComparison.OrdinalIgnoreCase)) + { + e.EditPath = e.DisplayPath.Remove(0, 9); + } + else if (e.DisplayPath.StartsWith(@"Network\", StringComparison.OrdinalIgnoreCase)) + { + string editPath = e.DisplayPath.Remove(0, 8); + editPath = @"\\" + editPath.Replace('\\', '/'); + e.EditPath = editPath; + } + } + else + { + if (e.EditPath.StartsWith("c:", StringComparison.OrdinalIgnoreCase)) + { + e.DisplayPath = @"Desktop\Computer\" + e.EditPath; + } + else if (e.EditPath.StartsWith(@"\\")) + { + e.DisplayPath = @"Desktop\Network\" + e.EditPath.Remove(0, 2).Replace('/', '\\'); + } + } + } + + /// + /// Show a progress bar animation for demonstation purpose. + /// + private void RefreshClick(object sender, RoutedEventArgs e) + { + DoubleAnimation da = new DoubleAnimation(100, new Duration(new TimeSpan(0, 0, 2))); + da.FillBehavior = FillBehavior.Stop; + bar.BeginAnimation(BreadcrumbBar.ProgressValueProperty, da); + } + + /// + /// The dropdown menu of a BreadcrumbItem was pressed, so delete the current folders, and repopulate the folders + /// to ensure actual data. + /// + private void bar_BreadcrumbItemDropDownOpened(object sender, BreadcrumbItemEventArgs e) + { + BreadcrumbItem item = e.Item; + + // only repopulate, if the BreadcrumbItem is dynamically generated which means, item.Data is a pointer to itself: + if (!(item.Data is BreadcrumbItem)) + { + UpdateFolderItems(item); + } + } + + /// + /// Update the list of Subfolders from a BreadcrumbItem. + /// + private void UpdateFolderItems(BreadcrumbItem item) + { + List actualFolders = GetFolderItemsFromBreadcrumb(item); + List currentFolders = item.ItemsSource as List; + currentFolders.Clear(); + currentFolders.AddRange(actualFolders); + + } + + public void ShowStaticBreadcrumbBar(object sender, RoutedEventArgs e) + { + BreadcrumbDS ds = new BreadcrumbDS(); + ds.ShowDialog(); + } + #endregion + } +} diff --git a/Odyssey/Demos/Outlook.xaml b/Odyssey/Demos/Outlook.xaml new file mode 100644 index 0000000..0acd681 --- /dev/null +++ b/Odyssey/Demos/Outlook.xaml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Toggle1 + Toggle2 + + + + + diff --git a/Odyssey/Demos/Outlook.xaml.cs b/Odyssey/Demos/Outlook.xaml.cs new file mode 100644 index 0000000..5142c58 --- /dev/null +++ b/Odyssey/Demos/Outlook.xaml.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; +using Odyssey.Controls; +using Odyssey.Controls.Classes; + +namespace Demos +{ + /// + /// Interaction logic for Outlook.xaml + /// + public partial class Outlook : Window + { + public Outlook() + { + InitializeComponent(); + } + + + private void OutlookBarIncClick(object sender, RoutedEventArgs e) + { + bar.MaxNumberOfButtons++; + } + + private void OutlookBarDecClick(object sender, RoutedEventArgs e) + { + bar.MaxNumberOfButtons--; + } + + private void bar_SelectedSectionChanged(object sender, RoutedPropertyChangedEventArgs e) + { + if (IsInitialized) + { + label.Text = e.NewValue.Header.ToString(); + } + } + + + private void BlueSkinClick(object sender, RoutedEventArgs e) + { + SkinManager.SkinId = SkinId.OfficeBlue; + + } + + private void SilverSkinClick(object sender, RoutedEventArgs e) + { + SkinManager.SkinId = SkinId.OfficeSilver; + } + + private void BlackSkinClick(object sender, RoutedEventArgs e) + { + SkinManager.SkinId = SkinId.OfficeBlack; + } + + + } +} diff --git a/Odyssey/Demos/Properties/AssemblyInfo.cs b/Odyssey/Demos/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..4edad3a --- /dev/null +++ b/Odyssey/Demos/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Windows; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Odyssey Demos")] +[assembly: AssemblyDescription("Demonstation of the Odyssey controls.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("tomssoftware.net")] +[assembly: AssemblyProduct("Odyssey Demos")] +[assembly: AssemblyCopyright("Copyright © Thomas Gerber 2008-2009")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +//In order to begin building localizable applications, set +//CultureYouAreCodingWith in your .csproj file +//inside a . For example, if you are using US english +//in your source files, set the to en-US. Then uncomment +//the NeutralResourceLanguage attribute below. Update the "en-US" in +//the line below to match the UICulture setting in the project file. + +//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] + + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] + + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.*")] +[assembly: AssemblyFileVersion("1.0.1.100")] diff --git a/Odyssey/Demos/Properties/Resources.Designer.cs b/Odyssey/Demos/Properties/Resources.Designer.cs new file mode 100644 index 0000000..4181dff --- /dev/null +++ b/Odyssey/Demos/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:2.0.50727.3053 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Demos.Properties +{ + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Demos.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/Odyssey/Demos/Properties/Resources.resx b/Odyssey/Demos/Properties/Resources.resx new file mode 100644 index 0000000..ffecec8 --- /dev/null +++ b/Odyssey/Demos/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Odyssey/Demos/Properties/Settings.Designer.cs b/Odyssey/Demos/Properties/Settings.Designer.cs new file mode 100644 index 0000000..e161cd0 --- /dev/null +++ b/Odyssey/Demos/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:2.0.50727.3053 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Demos.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "9.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/Odyssey/Demos/Properties/Settings.settings b/Odyssey/Demos/Properties/Settings.settings new file mode 100644 index 0000000..8f2fd95 --- /dev/null +++ b/Odyssey/Demos/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/Odyssey/Demos/Ribbon/RibbonDemo.xaml b/Odyssey/Demos/Ribbon/RibbonDemo.xaml new file mode 100644 index 0000000..8880cec --- /dev/null +++ b/Odyssey/Demos/Ribbon/RibbonDemo.xaml @@ -0,0 +1,343 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Close Demo + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Check me + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Radio 1 + Radio 2 + Radio 3 + Radio 4 + Radio 5 + Radio 6 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Odyssey/Demos/Ribbon/RibbonDemo.xaml.cs b/Odyssey/Demos/Ribbon/RibbonDemo.xaml.cs new file mode 100644 index 0000000..0143536 --- /dev/null +++ b/Odyssey/Demos/Ribbon/RibbonDemo.xaml.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; +using Odyssey.Controls.Classes; +using Odyssey.Controls; + +namespace Demos.Ribbon +{ + /// + /// Interaction logic for RibbonDemo.xaml + /// + public partial class RibbonDemo : RibbonWindow + { + public RibbonDemo() + { + InitializeComponent(); + } + + private void Win7Click(object sender, RoutedEventArgs e) + { + SkinManager.SkinId = SkinId.Windows7; + } + + private void VistaClick(object sender, RoutedEventArgs e) + { + SkinManager.SkinId = SkinId.Vista; + } + + private void OfficeBlueClick(object sender, RoutedEventArgs e) + { + SkinManager.SkinId = SkinId.OfficeBlue; + } + + private void OfficeSilverClick(object sender, RoutedEventArgs e) + { + SkinManager.SkinId = SkinId.OfficeSilver; + } + + private void OfficeBlackClick(object sender, RoutedEventArgs e) + { + SkinManager.SkinId = SkinId.OfficeBlack; + } + + private void Context1Click(object sender, RoutedEventArgs e) + { + ribbonBar.ContextualTabSet = ribbonBar.ContextualTabSets[0]; + } + + private void Context2Click(object sender, RoutedEventArgs e) + { + ribbonBar.ContextualTabSet = ribbonBar.ContextualTabSets[1]; + } + + private void ContextOffClick(object sender, RoutedEventArgs e) + { + ribbonBar.ContextualTabSet = null; + } + + + private void ShowBelowClick(object sender, RoutedEventArgs e) + { + ribbonBar.ToolbarPlacement = QAPlacement.Bottom; + } + + private void ShowAboveClick(object sender, RoutedEventArgs e) + { + ribbonBar.ToolbarPlacement = QAPlacement.Top; + } + + private void CloseDemoClick(object sender, RoutedEventArgs e) + { + Close(); + } + + private void RibbonGroup_LaunchDialog(object sender, RoutedEventArgs e) + { + MessageBox.Show("Launcher"); + } + + } +} diff --git a/Odyssey/Demos/Ribbon/ThumbnailConverter.cs b/Odyssey/Demos/Ribbon/ThumbnailConverter.cs new file mode 100644 index 0000000..0f96f72 --- /dev/null +++ b/Odyssey/Demos/Ribbon/ThumbnailConverter.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Data; +using Odyssey.Controls; + +namespace Demos +{ + public class ThumbnailConverter:IValueConverter + { + #region IValueConverter Members + + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + RibbonThumbnail tn = value as RibbonThumbnail; + if (tn == null) return value; + + string s = System.IO.Path.GetFileName(tn.ImageSource.ToString()); + return s; + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + + #endregion + } +} diff --git a/Odyssey/Demos/Web.png b/Odyssey/Demos/Web.png new file mode 100644 index 0000000..4782ae8 Binary files /dev/null and b/Odyssey/Demos/Web.png differ diff --git a/Odyssey/Demos/img/cut16.png b/Odyssey/Demos/img/cut16.png new file mode 100644 index 0000000..018912d Binary files /dev/null and b/Odyssey/Demos/img/cut16.png differ diff --git a/Odyssey/Demos/img/cut32.png b/Odyssey/Demos/img/cut32.png new file mode 100644 index 0000000..171599b Binary files /dev/null and b/Odyssey/Demos/img/cut32.png differ diff --git a/Odyssey/Demos/img/delete16.png b/Odyssey/Demos/img/delete16.png new file mode 100644 index 0000000..9d69207 Binary files /dev/null and b/Odyssey/Demos/img/delete16.png differ diff --git a/Odyssey/Demos/img/delete32.png b/Odyssey/Demos/img/delete32.png new file mode 100644 index 0000000..0046040 Binary files /dev/null and b/Odyssey/Demos/img/delete32.png differ diff --git a/Odyssey/Demos/img/favorites16.png b/Odyssey/Demos/img/favorites16.png new file mode 100644 index 0000000..d50109d Binary files /dev/null and b/Odyssey/Demos/img/favorites16.png differ diff --git a/Odyssey/Demos/img/favorites32.png b/Odyssey/Demos/img/favorites32.png new file mode 100644 index 0000000..838b799 Binary files /dev/null and b/Odyssey/Demos/img/favorites32.png differ diff --git a/Odyssey/Demos/img/folder16.png b/Odyssey/Demos/img/folder16.png new file mode 100644 index 0000000..ca4ff48 Binary files /dev/null and b/Odyssey/Demos/img/folder16.png differ diff --git a/Odyssey/Demos/img/folder32.png b/Odyssey/Demos/img/folder32.png new file mode 100644 index 0000000..5a0b338 Binary files /dev/null and b/Odyssey/Demos/img/folder32.png differ diff --git a/Odyssey/Demos/img/history16.png b/Odyssey/Demos/img/history16.png new file mode 100644 index 0000000..a1a5277 Binary files /dev/null and b/Odyssey/Demos/img/history16.png differ diff --git a/Odyssey/Demos/img/history32.png b/Odyssey/Demos/img/history32.png new file mode 100644 index 0000000..e02f5b9 Binary files /dev/null and b/Odyssey/Demos/img/history32.png differ diff --git a/Odyssey/Demos/img/home16.png b/Odyssey/Demos/img/home16.png new file mode 100644 index 0000000..edee1ae Binary files /dev/null and b/Odyssey/Demos/img/home16.png differ diff --git a/Odyssey/Demos/img/home32.png b/Odyssey/Demos/img/home32.png new file mode 100644 index 0000000..2c1eda1 Binary files /dev/null and b/Odyssey/Demos/img/home32.png differ diff --git a/Odyssey/Demos/img/mail16.png b/Odyssey/Demos/img/mail16.png new file mode 100644 index 0000000..62ce00a Binary files /dev/null and b/Odyssey/Demos/img/mail16.png differ diff --git a/Odyssey/Demos/img/mail32.png b/Odyssey/Demos/img/mail32.png new file mode 100644 index 0000000..95d6106 Binary files /dev/null and b/Odyssey/Demos/img/mail32.png differ diff --git a/Odyssey/Demos/img/paste16.png b/Odyssey/Demos/img/paste16.png new file mode 100644 index 0000000..f618e48 Binary files /dev/null and b/Odyssey/Demos/img/paste16.png differ diff --git a/Odyssey/Demos/img/paste32.png b/Odyssey/Demos/img/paste32.png new file mode 100644 index 0000000..f09085e Binary files /dev/null and b/Odyssey/Demos/img/paste32.png differ diff --git a/Odyssey/Demos/img/props16.png b/Odyssey/Demos/img/props16.png new file mode 100644 index 0000000..8ea4173 Binary files /dev/null and b/Odyssey/Demos/img/props16.png differ diff --git a/Odyssey/Demos/img/props32.png b/Odyssey/Demos/img/props32.png new file mode 100644 index 0000000..262cbd4 Binary files /dev/null and b/Odyssey/Demos/img/props32.png differ diff --git a/Odyssey/Demos/img/save16.png b/Odyssey/Demos/img/save16.png new file mode 100644 index 0000000..613a0d8 Binary files /dev/null and b/Odyssey/Demos/img/save16.png differ diff --git a/Odyssey/Demos/img/save32.png b/Odyssey/Demos/img/save32.png new file mode 100644 index 0000000..4d0e44e Binary files /dev/null and b/Odyssey/Demos/img/save32.png differ diff --git a/Odyssey/Demos/img/search16.png b/Odyssey/Demos/img/search16.png new file mode 100644 index 0000000..50a2390 Binary files /dev/null and b/Odyssey/Demos/img/search16.png differ diff --git a/Odyssey/Demos/img/search32.png b/Odyssey/Demos/img/search32.png new file mode 100644 index 0000000..59b25e8 Binary files /dev/null and b/Odyssey/Demos/img/search32.png differ diff --git a/Odyssey/Demos/img/undo16.png b/Odyssey/Demos/img/undo16.png new file mode 100644 index 0000000..ccf24ca Binary files /dev/null and b/Odyssey/Demos/img/undo16.png differ diff --git a/Odyssey/Demos/img/undo32.png b/Odyssey/Demos/img/undo32.png new file mode 100644 index 0000000..fff1cd9 Binary files /dev/null and b/Odyssey/Demos/img/undo32.png differ diff --git a/Odyssey/Demos/openfolderHS.png b/Odyssey/Demos/openfolderHS.png new file mode 100644 index 0000000..0bc670e Binary files /dev/null and b/Odyssey/Demos/openfolderHS.png differ diff --git a/Odyssey/Odyssey.sln b/Odyssey/Odyssey.sln new file mode 100644 index 0000000..cdf9549 --- /dev/null +++ b/Odyssey/Odyssey.sln @@ -0,0 +1,72 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Odyssey", "Odyssey\Odyssey.csproj", "{333FDC55-6B47-4A64-A2DF-A4C5823FAC74}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Demos", "Demos\Demos.csproj", "{4F736E6C-D570-4882-B521-93819C059E82}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{642DE219-C13C-407C-AD40-51A1D4D03EE5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PasswordSafe", "..\PasswordSafe\PasswordSafe\PasswordSafe.csproj", "{24CBC0A1-F7C4-45A0-8D58-E1BB1B4F0629}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PasswordSafe.Data", "..\PasswordSafe\PasswordSafe.Data\PasswordSafe.Data.csproj", "{E4DA0115-54F3-4DA0-813B-CDEBF4D205E5}" +EndProject +Global + GlobalSection(TeamFoundationVersionControl) = preSolution + SccNumberOfProjects = 5 + SccEnterpriseProvider = {4CA58AB2-18FA-4F8D-95D4-32DDF27D184C} + SccTeamFoundationServer = https://tfs08.codeplex.com/ + SccLocalPath0 = . + SccProjectUniqueName1 = Demos\\Demos.csproj + SccProjectName1 = Demos + SccLocalPath1 = Demos + SccProjectUniqueName2 = Odyssey\\Odyssey.csproj + SccProjectName2 = Odyssey + SccLocalPath2 = Odyssey + SccProjectUniqueName3 = ..\\PasswordSafe\\PasswordSafe\\PasswordSafe.csproj + SccProjectName3 = ../PasswordSafe/PasswordSafe + SccLocalPath3 = ..\\PasswordSafe\\PasswordSafe + SccProjectUniqueName4 = ..\\PasswordSafe\\PasswordSafe.Data\\PasswordSafe.Data.csproj + SccProjectName4 = ../PasswordSafe/PasswordSafe.Data + SccLocalPath4 = ..\\PasswordSafe\\PasswordSafe.Data + EndGlobalSection + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {333FDC55-6B47-4A64-A2DF-A4C5823FAC74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {333FDC55-6B47-4A64-A2DF-A4C5823FAC74}.Debug|Any CPU.Build.0 = Debug|Any CPU + {333FDC55-6B47-4A64-A2DF-A4C5823FAC74}.Debug|x86.ActiveCfg = Debug|x86 + {333FDC55-6B47-4A64-A2DF-A4C5823FAC74}.Debug|x86.Build.0 = Debug|x86 + {333FDC55-6B47-4A64-A2DF-A4C5823FAC74}.Release|Any CPU.ActiveCfg = Release|Any CPU + {333FDC55-6B47-4A64-A2DF-A4C5823FAC74}.Release|Any CPU.Build.0 = Release|Any CPU + {333FDC55-6B47-4A64-A2DF-A4C5823FAC74}.Release|x86.ActiveCfg = Release|x86 + {333FDC55-6B47-4A64-A2DF-A4C5823FAC74}.Release|x86.Build.0 = Release|x86 + {4F736E6C-D570-4882-B521-93819C059E82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4F736E6C-D570-4882-B521-93819C059E82}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4F736E6C-D570-4882-B521-93819C059E82}.Debug|x86.ActiveCfg = Debug|Any CPU + {4F736E6C-D570-4882-B521-93819C059E82}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4F736E6C-D570-4882-B521-93819C059E82}.Release|Any CPU.Build.0 = Release|Any CPU + {4F736E6C-D570-4882-B521-93819C059E82}.Release|x86.ActiveCfg = Release|Any CPU + {24CBC0A1-F7C4-45A0-8D58-E1BB1B4F0629}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {24CBC0A1-F7C4-45A0-8D58-E1BB1B4F0629}.Debug|Any CPU.Build.0 = Debug|Any CPU + {24CBC0A1-F7C4-45A0-8D58-E1BB1B4F0629}.Debug|x86.ActiveCfg = Debug|x86 + {24CBC0A1-F7C4-45A0-8D58-E1BB1B4F0629}.Debug|x86.Build.0 = Debug|x86 + {24CBC0A1-F7C4-45A0-8D58-E1BB1B4F0629}.Release|Any CPU.ActiveCfg = Release|Any CPU + {24CBC0A1-F7C4-45A0-8D58-E1BB1B4F0629}.Release|Any CPU.Build.0 = Release|Any CPU + {24CBC0A1-F7C4-45A0-8D58-E1BB1B4F0629}.Release|x86.ActiveCfg = Release|x86 + {24CBC0A1-F7C4-45A0-8D58-E1BB1B4F0629}.Release|x86.Build.0 = Release|x86 + {E4DA0115-54F3-4DA0-813B-CDEBF4D205E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E4DA0115-54F3-4DA0-813B-CDEBF4D205E5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E4DA0115-54F3-4DA0-813B-CDEBF4D205E5}.Debug|x86.ActiveCfg = Debug|Any CPU + {E4DA0115-54F3-4DA0-813B-CDEBF4D205E5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E4DA0115-54F3-4DA0-813B-CDEBF4D205E5}.Release|Any CPU.Build.0 = Release|Any CPU + {E4DA0115-54F3-4DA0-813B-CDEBF4D205E5}.Release|x86.ActiveCfg = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Odyssey/Odyssey/BreadcrumbBar/ApplyPropertiesEventArgs.cs b/Odyssey/Odyssey/BreadcrumbBar/ApplyPropertiesEventArgs.cs new file mode 100644 index 0000000..8be0a5e --- /dev/null +++ b/Odyssey/Odyssey/BreadcrumbBar/ApplyPropertiesEventArgs.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Media; + +#region Licence +// Copyright (c) 2008 Thomas Gerber +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#endregion +namespace Odyssey.Controls +{ + public class ApplyPropertiesEventArgs:RoutedEventArgs + { + public ApplyPropertiesEventArgs(object item, BreadcrumbItem breadcrumb, RoutedEvent routedEvent) + : base(routedEvent) + { + Item = item; + Breadcrumb = breadcrumb; + + } + + /// + /// The breadcrumb for which to apply the properites. + /// + public BreadcrumbItem Breadcrumb { get; private set; } + + /// + /// The data item of the breadcrumb. + /// + public object Item { get; private set; } + + public ImageSource Image { get; set; } + + /// + /// The trace that is used to show the title of a breadcrumb. + /// + public object Trace { get; set; } + + /// + /// The trace that is used to build the path. + /// This can be used to remove the trace of the root item in the path, if necassary. + /// + public string TraceValue { get; set; } + } + + public delegate void ApplyPropertiesEventHandler(object sender, ApplyPropertiesEventArgs e); +} diff --git a/Odyssey/Odyssey/BreadcrumbBar/BreadcrumbBar.cs b/Odyssey/Odyssey/BreadcrumbBar/BreadcrumbBar.cs new file mode 100644 index 0000000..b91ec65 --- /dev/null +++ b/Odyssey/Odyssey/BreadcrumbBar/BreadcrumbBar.cs @@ -0,0 +1,1399 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using System.ComponentModel; +using System.Collections; +using System.Diagnostics; +using System.Collections.ObjectModel; +using System.Windows.Controls.Primitives; +using System.Windows.Markup; + +#region changelog +// Version 1.3.17: +// - preserved underscores for headers. +// reported by bigbigllama on http://www.codeproject.com/KB/tree/WPFBreadcrumbBar.aspx +// thanx for your help. +#endregion +#region Licence +// Copyright (c) 2008 Thomas Gerber +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#endregion +namespace Odyssey.Controls +{ + + ///TODO: Enable async popuplation on demand. + /// + /// A breadcrumb bar the contains breadcrumb items, a dropdown control, additional buttons and a progress bar. + /// + [ContentProperty("Root")] + [TemplatePart(Name = partComboBox)] + [TemplatePart(Name = partRoot)] + public class BreadcrumbBar : Control, IAddChild + { + private const string partComboBox = "PART_ComboBox"; + private const string partRoot = "PART_Root"; + + /// + /// Gets the number of the first breadcrumb to hide in the path if descending breadcrumbs are selected. + /// + int BreadcrumbsToHide + { + get { return HideRootNode ? 1 : 0; } + } + + + + /// + /// Gets or sets wether the root node is removed from the breadcrumb bar if any child node is selected. + /// This is a dependency property. + /// + public bool HideRootNode + { + get { return (bool)GetValue(HideRootNodeProperty); } + set { SetValue(HideRootNodeProperty, value); } + } + + // Using a DependencyProperty as the backing store for HideRootNode. This enables animation, styling, binding, etc... + public static readonly DependencyProperty HideRootNodeProperty = + DependencyProperty.Register("HideRootNode", typeof(bool), typeof(BreadcrumbBar), new UIPropertyMetadata(true)); + + + + #region Dependency Properties + + public static readonly DependencyProperty HasDropDownItemsProperty = + DependencyProperty.Register("HasDropDownItems", typeof(bool), typeof(BreadcrumbBar), new UIPropertyMetadata(false)); + + + public static readonly DependencyProperty DropDownItemsPanelProperty = + DependencyProperty.Register("DropDownItemsPanel", typeof(ItemsPanelTemplate), typeof(BreadcrumbBar), new UIPropertyMetadata(null)); + + private static readonly DependencyPropertyKey IsRootSelectedPropertyKey = + DependencyProperty.RegisterReadOnly("IsRootSelected", typeof(bool), typeof(BreadcrumbBar), new UIPropertyMetadata(true)); + + public static readonly DependencyProperty IsRootSelectedProperty = IsRootSelectedPropertyKey.DependencyProperty; + + public static readonly DependencyProperty DropDownItemTemplateProperty = + DependencyProperty.Register("DropDownItemTemplate", typeof(DataTemplate), typeof(BreadcrumbBar), new UIPropertyMetadata(null)); + + public static readonly DependencyProperty IsEditableProperty = + DependencyProperty.Register("IsEditable", typeof(bool), typeof(BreadcrumbBar), new UIPropertyMetadata(true)); + + public static readonly DependencyProperty DropDownItemTemplateSelectorProperty = + DependencyProperty.Register("DropDownItemTemplateSelector", typeof(DataTemplateSelector), typeof(BreadcrumbBar), new UIPropertyMetadata(null)); + + public static readonly DependencyProperty OverflowItemTemplateSelectorProperty = + DependencyProperty.Register("OverflowItemTemplateSelector", typeof(DataTemplateSelector), typeof(BreadcrumbBar), new FrameworkPropertyMetadata(null, + FrameworkPropertyMetadataOptions.Inherits)); + + public static readonly DependencyProperty OverflowItemTemplateProperty = + DependencyProperty.Register("OverflowItemTemplate", typeof(DataTemplate), typeof(BreadcrumbBar), new FrameworkPropertyMetadata(null, + FrameworkPropertyMetadataOptions.Inherits)); + + private static readonly DependencyPropertyKey CollapsedTracesPropertyKey = + DependencyProperty.RegisterReadOnly("CollapsedTraces", typeof(IEnumerable), typeof(BreadcrumbBar), new UIPropertyMetadata(null)); + public static readonly DependencyProperty CollapsedTracesProperty = CollapsedTracesPropertyKey.DependencyProperty; + + public static readonly DependencyProperty RootProperty = + DependencyProperty.Register("Root", typeof(object), typeof(BreadcrumbBar), new FrameworkPropertyMetadata(null, + FrameworkPropertyMetadataOptions.AffectsMeasure | + FrameworkPropertyMetadataOptions.AffectsRender | + FrameworkPropertyMetadataOptions.AffectsArrange, + OnRootPropertyChanged)); + + public static readonly DependencyProperty SelectedItemProperty = + DependencyProperty.Register("SelectedItem", typeof(object), typeof(BreadcrumbBar), new FrameworkPropertyMetadata(null, + FrameworkPropertyMetadataOptions.AffectsArrange | + FrameworkPropertyMetadataOptions.AffectsMeasure | + FrameworkPropertyMetadataOptions.AffectsMeasure, + OnSelectedItemPropertyChanged)); + + private static readonly DependencyPropertyKey SelectedBreadcrumbPropertyKey = + DependencyProperty.RegisterReadOnly("SelectedBreadcrumb", typeof(BreadcrumbItem), typeof(BreadcrumbBar), new FrameworkPropertyMetadata(null, + FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsArrange, + OnSelectedBreadcrumbPropertyChanged)); + public static readonly DependencyProperty SelectedBreadcrumbProperty = SelectedBreadcrumbPropertyKey.DependencyProperty; + + public static readonly DependencyProperty IsOverflowPressedProperty = + DependencyProperty.Register("IsOverflowPressed", typeof(bool), typeof(BreadcrumbBar), new UIPropertyMetadata(false, OverflowPressedChanged)); + + private static readonly DependencyPropertyKey RootItemPropertyKey = + DependencyProperty.RegisterReadOnly("RootItem", typeof(BreadcrumbItem), typeof(BreadcrumbBar), new FrameworkPropertyMetadata(null, + FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsMeasure, + OnRootItemPropertyChanged + )); + private static readonly DependencyProperty RootItemProperty = RootItemPropertyKey.DependencyProperty; + + private static void OnRootItemPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((BreadcrumbBar)d).OnRootItemChanged((BreadcrumbItem)e.OldValue, (BreadcrumbItem)e.NewValue); + } + + protected virtual void OnRootItemChanged(BreadcrumbItem oldValue, BreadcrumbItem newValue) + { + } + + public static readonly DependencyProperty BreadcrumbItemTemplateSelectorProperty = + DependencyProperty.Register("BreadcrumbItemTemplateSelector", typeof(DataTemplateSelector), typeof(BreadcrumbBar), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits)); + + public static readonly DependencyProperty BreadcrumbItemTemplateProperty = + DependencyProperty.Register("BreadcrumbItemTemplate", typeof(DataTemplate), typeof(BreadcrumbBar), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits)); + + private static readonly DependencyPropertyKey OverflowModePropertyKey = + DependencyProperty.RegisterReadOnly("OverflowMode", typeof(BreadcrumbButton.ButtonMode), typeof(BreadcrumbBar), + new FrameworkPropertyMetadata(BreadcrumbButton.ButtonMode.Overflow, + FrameworkPropertyMetadataOptions.AffectsRender)); + + public static readonly DependencyProperty OverflowModeProperty = OverflowModePropertyKey.DependencyProperty; + + public static readonly DependencyProperty IsDropDownOpenProperty = + DependencyProperty.Register("IsDropDownOpen", typeof(bool), typeof(BreadcrumbBar), new UIPropertyMetadata(false, IsDropDownOpenChanged)); + + public static readonly DependencyProperty SeparatorStringProperty = + DependencyProperty.Register("SeparatorString", typeof(string), typeof(BreadcrumbBar), new UIPropertyMetadata("\\")); + + public static readonly DependencyProperty PathProperty = + DependencyProperty.Register("Path", typeof(string), typeof(BreadcrumbBar), new FrameworkPropertyMetadata("",FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, PathPropertyChanged)); + + public static readonly DependencyProperty DropDownItemsSourceProperty = + DependencyProperty.Register("DropDownItemsSource", typeof(IEnumerable), typeof(BreadcrumbBar), new UIPropertyMetadata(null, OnDropDownItemsSourcePropertyChanged)); + + + public static readonly DependencyProperty SelectedDropDownIndexProperty = + DependencyProperty.Register("SelectedDropDownIndex", typeof(int), typeof(BreadcrumbBar), new UIPropertyMetadata(-1)); + + public static readonly DependencyProperty ProgressValueProperty = + DependencyProperty.Register("ProgressValue", typeof(double), typeof(BreadcrumbBar), + new UIPropertyMetadata((double)0.0, ProgressValuePropertyChanged, CoerceProgressValue)); + + public static readonly DependencyProperty ProgressMaximumProperty = + DependencyProperty.Register("ProgressMaximum", typeof(double), typeof(BreadcrumbBar), new UIPropertyMetadata(100.0, null, CoerceProgressMaximum)); + + public static readonly DependencyProperty ProgressMinimumProperty = + DependencyProperty.Register("ProgressMinimum", typeof(double), typeof(BreadcrumbBar), new UIPropertyMetadata(0.0, null, CoerceProgressMinimum)); + + #endregion + + #region RoutedEvents + + public static readonly RoutedEvent BreadcrumbItemDropDownOpenedEvent = EventManager.RegisterRoutedEvent("BreadcrumbItemDropDownOpened", + RoutingStrategy.Bubble, typeof(BreadcrumbItemEventHandler), typeof(BreadcrumbBar)); + + public static readonly RoutedEvent BreadcrumbItemDropDownClosedEvent = EventManager.RegisterRoutedEvent("BreadcrumbItemDropDownClosed", + RoutingStrategy.Bubble, typeof(BreadcrumbItemEventHandler), typeof(BreadcrumbBar)); + + public static readonly RoutedEvent ProgressValueChangedEvent = EventManager.RegisterRoutedEvent("ProgressValueChanged", + RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(BreadcrumbBar)); + + public static readonly RoutedEvent ApplyPropertiesEvent = EventManager.RegisterRoutedEvent("ApplyProperties", + RoutingStrategy.Bubble, typeof(ApplyPropertiesEventHandler), typeof(BreadcrumbBar)); + + + public static readonly RoutedEvent SelectedBreadcrumbChangedEvent = EventManager.RegisterRoutedEvent("SelectedBreadcrumbChanged", + RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler), typeof(BreadcrumbBar)); + + public static readonly RoutedEvent PathChangedEvent = EventManager.RegisterRoutedEvent("PathChanged", + RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler), typeof(BreadcrumbBar)); + + /// + /// Occurs before acessing the Items property of a BreadcrumbItem. This event can be used to populate the Items on demand. + /// + public static readonly RoutedEvent PopulateItemsEvent = EventManager.RegisterRoutedEvent("PopulateItems", + RoutingStrategy.Bubble, typeof(BreadcrumbItemEventHandler), typeof(BreadcrumbBar)); + + /// + /// Occurs when a path needs to be converted between display path and edit path. + /// + public static readonly RoutedEvent PathConversionEvent = EventManager.RegisterRoutedEvent("PathConversion", + RoutingStrategy.Bubble, typeof(PathConversionEventHandler), typeof(BreadcrumbBar)); + + #endregion + + /// + /// This command shows the drop down part of the combobox. + /// + public static RoutedUICommand ShowDropDownCommand + { + get { return showDropDownCommand; } + } + + private static RoutedUICommand showDropDownCommand = new RoutedUICommand("Show DropDown", "ShowDropDownCommand", typeof(BreadcrumbBar)); + + /// + /// This command selects the BreadcrumbItem that is specified as Parameter. + /// + public static RoutedUICommand SelectTraceCommand + { + get { return selectTraceCommand; } + } + + /// + /// This command selects the root. + /// + public static RoutedUICommand SelectRootCommand + { + get { return selectRootCommand; } + } + + private static RoutedUICommand selectRootCommand = new RoutedUICommand("Select", "SelectRootCommand", typeof(BreadcrumbBar)); + private static RoutedUICommand selectTraceCommand = new RoutedUICommand("Select", "SelectTraceCommand", typeof(BreadcrumbBar)); + + + static BreadcrumbBar() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(BreadcrumbBar), new FrameworkPropertyMetadata(typeof(BreadcrumbBar))); + BorderThicknessProperty.OverrideMetadata(typeof(BreadcrumbBar), new FrameworkPropertyMetadata(new Thickness(1))); + BorderBrushProperty.OverrideMetadata(typeof(BreadcrumbBar), new FrameworkPropertyMetadata(Brushes.Black)); + Color c = new Color(); + c.R = 245; c.G = 245; c.B = 245; c.A = 255; + BackgroundProperty.OverrideMetadata(typeof(BreadcrumbBar), new FrameworkPropertyMetadata(new SolidColorBrush(c))); + + CommandManager.RegisterClassCommandBinding(typeof(FrameworkElement), new CommandBinding(selectRootCommand, SelectRootCommandExecuted)); + CommandManager.RegisterClassCommandBinding(typeof(FrameworkElement), new CommandBinding(selectTraceCommand, SelectTraceCommandExecuted)); + CommandManager.RegisterClassCommandBinding(typeof(FrameworkElement), new CommandBinding(showDropDownCommand, ShowDropDownExecuted)); + } + + // A helper class to store the DropDownItems since ItemCollection has no public creator: + private ItemsControl comboBoxControlItems; + + /// + /// Creates a new BreadcrumbBar. + /// + public BreadcrumbBar() + : base() + { + comboBoxControlItems = new ItemsControl(); + Binding b = new Binding("HasItems"); + b.Source = comboBoxControlItems; + this.SetBinding(BreadcrumbBar.HasDropDownItemsProperty, b); + + traces = new ObservableCollection(); + CollapsedTraces = traces; + AddHandler(BreadcrumbItem.SelectionChangedEvent, new RoutedEventHandler(breadcrumbItemSelectedItemChanged)); + AddHandler(BreadcrumbItem.TraceChangedEvent, new RoutedEventHandler(breadcrumbItemTraceValueChanged)); + AddHandler(BreadcrumbItem.SelectionChangedEvent, new RoutedEventHandler(breadcrumbItemSelectionChangedEvent)); + AddHandler(BreadcrumbItem.DropDownPressedChangedEvent, new RoutedEventHandler(breadcrumbItemDropDownChangedEvent)); + AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(buttonClickedEvent)); + traces.Add(null); + + InputBindings.Add(new KeyBinding(BreadcrumbBar.ShowDropDownCommand, new KeyGesture(Key.Down, ModifierKeys.Alt))); + } + + static void IsDropDownOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + BreadcrumbBar bar = d as BreadcrumbBar; + + bar.OnDropDownOpenChanged((bool)e.OldValue, (bool)e.NewValue); + } + + static void OnSelectedBreadcrumbPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + BreadcrumbBar bar = d as BreadcrumbBar; + BreadcrumbItem selected = e.NewValue as BreadcrumbItem; + bar.IsRootSelected = selected == bar.RootItem; + if (bar.IsInitialized) + { + RoutedPropertyChangedEventArgs args = new RoutedPropertyChangedEventArgs(e.OldValue as BreadcrumbItem, e.NewValue as BreadcrumbItem, BreadcrumbBar.SelectedBreadcrumbChangedEvent); + bar.RaiseEvent(args); + } + } + + + /// + /// Occurs after a BreadcrumbItem is created for which to apply additional properties. + /// + public event ApplyPropertiesEventHandler ApplyProperties + { + add { AddHandler(BreadcrumbBar.ApplyPropertiesEvent, value); } + remove { RemoveHandler(BreadcrumbBar.ApplyPropertiesEvent, value); } + } + + /// + /// Occurs when the selected BreadcrumbItem is changed. + /// + public event RoutedPropertyChangedEventHandler SelectedBreadcrumbChanged + { + add { AddHandler(BreadcrumbBar.SelectedBreadcrumbChangedEvent, value); } + remove { RemoveHandler(BreadcrumbBar.SelectedBreadcrumbChangedEvent, value); } + } + + protected virtual void OnSelectedBreadcrumbChanged(DependencyPropertyChangedEventArgs e) + { + if (SelectedBreadcrumb != null) SelectedBreadcrumb.SelectedItem = null; + } + + /// + /// On initializing, it is possible that the Path property is set before the RootItem property, thus the declarative xaml Path would be overwritten by settings the + /// RootItem property later. To avoid this affect, setting the Path also sets initPath on initializing and after initializing, the Path is restored by this value: + /// + private string initPath; + + static void PathPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + BreadcrumbBar bar = d as BreadcrumbBar; + string newPath = e.NewValue as string; + // bar.PathBinding = newPath; + + if (!bar.IsInitialized) + { + bar.Path = bar.initPath = newPath; + } + else + { + bar.BuildBreadcrumbsFromPath(newPath); + bar.OnPathChanged(e.OldValue as string, newPath); + } + } + + /// + /// Occurs when the Path property is changed. + /// + public event RoutedPropertyChangedEventHandler PathChanged + { + add { AddHandler(PathChangedEvent, value); } + remove { RemoveHandler(PathChangedEvent, value); } + } + + /// + /// Occurs when the Path property is changed. + /// + protected virtual void OnPathChanged(string oldValue, string newValue) + { + BuildBreadcrumbsFromPath(newValue); + if (IsLoaded) + { + RoutedPropertyChangedEventArgs args = new RoutedPropertyChangedEventArgs(oldValue, newValue, PathChangedEvent); + RaiseEvent(args); + } + } + + + /// + /// Traces the specified path and builds the associated BreadcrumbItems. + /// + /// The traces separated by the SepearatorString property. + private bool BuildBreadcrumbsFromPath(string newPath) + { + PathConversionEventArgs e = new PathConversionEventArgs(PathConversionEventArgs.ConversionMode.EditToDisplay, newPath, Root, PathConversionEvent); + RaiseEvent(e); + newPath = e.DisplayPath; + + BreadcrumbItem item = RootItem; + if (item == null) + { + this.Path = null; + return false; + } + + + newPath = RemoveLastEmptySeparator(newPath); + string[] traces = newPath.Split(new string[] { SeparatorString }, StringSplitOptions.RemoveEmptyEntries); + if (traces.Length == 0) RootItem.SelectedItem = null; + int index = 0; + + List itemIndex = new List(); + + // if the root is specified as first trace, then skip: + int length = traces.Length; + int max = BreadcrumbsToHide; + if (traces.Length>index && max > 0 && traces[index] == (RootItem.TraceValue)) + { + length--; + index++; + max--; + } + + for (int i = index; i < traces.Length; i++) + { + if (item == null) break; + + string trace = traces[i]; + OnPopulateItems(item); + object next = item.GetTraceItem(trace); + if (next == null) break; + itemIndex.Add(item.Items.IndexOf(next)); + BreadcrumbItem container = item.ContainerFromItem(next); + + item = container; + } + if (length != itemIndex.Count) + { + //recover the last path: + Path = GetDisplayPath(); + return false; + } + + // temporarily remove the SelectionChangedEvent handler to minimize processing of events while building the breadcrumb items: + RemoveHandler(BreadcrumbItem.SelectionChangedEvent, new RoutedEventHandler(breadcrumbItemSelectedItemChanged)); + try + { + item = RootItem; + for (int i = 0; i < itemIndex.Count; i++) + { + if (item == null) break; + item.SelectedIndex = itemIndex[i]; + item = item.SelectedBreadcrumb; + } + if (item != null) item.SelectedItem = null; + SelectedBreadcrumb = item; + SelectedItem = item != null ? item.Data : null; + } + finally + { + AddHandler(BreadcrumbItem.SelectionChangedEvent, new RoutedEventHandler(breadcrumbItemSelectedItemChanged)); + } + + return true; + } + + + /// + /// Remove the last separator string from the path if there is no additional trace. + /// + /// The path from which to remove the last separator. + /// The path without an unecassary last separator. + private string RemoveLastEmptySeparator(string path) + { + path = path.Trim(); + int sepLength = SeparatorString.Length; + if (path.EndsWith(SeparatorString)) + { + path = path.Remove(path.Length - sepLength, sepLength); + } + return path; + } + + static void OnDropDownItemsSourcePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + BreadcrumbBar bar = d as BreadcrumbBar; + + bar.comboBoxControlItems.ItemsSource = e.NewValue as IEnumerable; + } + + /// + /// Occurs when the IsDropDownOpen property is changed. + /// + /// + /// + protected virtual void OnDropDownOpenChanged(bool oldValue, bool newValue) + { + if (comboBox != null && newValue) + { + SetInputState(); + if (IsEditable) + { + comboBox.Visibility = Visibility.Visible; + comboBox.IsDropDownOpen = true; + } + } + } + + private static void SelectRootCommandExecuted(object sender, ExecutedRoutedEventArgs e) + { + BreadcrumbItem item = e.Parameter as BreadcrumbItem; + if (item != null) + { + item.SelectedItem = null; + } + } + + private static void SelectTraceCommandExecuted(object sender, ExecutedRoutedEventArgs e) + { + BreadcrumbItem item = e.Parameter as BreadcrumbItem; + if (item != null) + { + item.SelectedItem = null; + } + } + + private static void ShowDropDownExecuted(object sender, ExecutedRoutedEventArgs e) + { + BreadcrumbBar bar = sender as BreadcrumbBar; + if (bar.IsEditable && bar.DropDownItems.Count > 0) bar.IsDropDownOpen = true; + } + + private void breadcrumbItemSelectedItemChanged(object sender, RoutedEventArgs e) + { + BreadcrumbItem breadcrumb = e.OriginalSource as BreadcrumbItem; + if (breadcrumb != null && breadcrumb.SelectedBreadcrumb != null) breadcrumb = breadcrumb.SelectedBreadcrumb; + SelectedBreadcrumb = breadcrumb; + + if (SelectedBreadcrumb != null) + { + SelectedItem = SelectedBreadcrumb.Data; + } + Path = GetEditPath(); + } + + private void breadcrumbItemTraceValueChanged(object sender, RoutedEventArgs e) + { + if (e.OriginalSource == RootItem) + { + //TODO: This causes Path binding not to work (see PasswordSafe): + //Path = GetEditPath(); + } + } + + private void breadcrumbItemSelectionChangedEvent(object sender, RoutedEventArgs e) + { + BreadcrumbItem parent = e.Source as BreadcrumbItem; + if (parent != null && parent.SelectedBreadcrumb != null) + { + OnPopulateItems(parent.SelectedBreadcrumb); + } + } + + + private void breadcrumbItemDropDownChangedEvent(object sender, RoutedEventArgs e) + { + BreadcrumbItem breadcrumb = e.Source as BreadcrumbItem; + if (breadcrumb.IsDropDownPressed) + { + OnBreadcrumbItemDropDownOpened(e); + } + else + { + OnBreadcrumbItemDropDownClosed(e); + } + } + + /// + /// Remove the focus from a button when it was clicked. + /// + /// The sender. + /// The instance containing the event data. + private void buttonClickedEvent(object sender, RoutedEventArgs e) + { + if (!this.IsDropDownOpen) + { + // focus only if it has aleady focus and the button that was pressed is not the drop down button of the combobox: + if (this.IsKeyboardFocusWithin && (e.OriginalSource is BreadcrumbButton)) + { + this.Focus(); + } + } + } + + /// + /// Occurs before acessing the Items property of a BreadcrumbItem. This event can be used to populate the Items on demand. + /// + public event BreadcrumbItemEventHandler PopulateItems + { + add { AddHandler(BreadcrumbBar.PopulateItemsEvent, value); } + remove { RemoveHandler(BreadcrumbBar.PopulateItemsEvent, value); } + } + + /// + /// Occurs when a path needs to be converted between display path and edit path. + /// + public event PathConversionEventHandler PathConversion + { + add { AddHandler(BreadcrumbBar.PathConversionEvent, value); } + remove { RemoveHandler(BreadcrumbBar.PathConversionEvent, value); } + } + + /// + /// Occurs before acessing the Items property of a BreadcrumbItem. This event can be used to populate the Items on demand. + /// + protected virtual void OnPopulateItems(BreadcrumbItem item) + { + BreadcrumbItemEventArgs args = new BreadcrumbItemEventArgs(item, BreadcrumbBar.PopulateItemsEvent); + RaiseEvent(args); + } + + /// + /// Occurs when the dropdown of a BreadcrumbItem is opened. + /// + public event BreadcrumbItemEventHandler BreadcrumbItemDropDownOpened + { + add { AddHandler(BreadcrumbBar.BreadcrumbItemDropDownOpenedEvent, value); } + remove { RemoveHandler(BreadcrumbBar.BreadcrumbItemDropDownOpenedEvent, value); } + } + + /// + /// Occurs when the dropdown of a BreadcrumbItem is closed. + /// + public event BreadcrumbItemEventHandler BreadcrumbItemDropDownClosed + { + add { AddHandler(BreadcrumbBar.BreadcrumbItemDropDownClosedEvent, value); } + remove { RemoveHandler(BreadcrumbBar.BreadcrumbItemDropDownClosedEvent, value); } + } + + /// + /// Occurs when the dropdown of a BreadcrumbItem is opened. + /// + protected virtual void OnBreadcrumbItemDropDownOpened(RoutedEventArgs e) + { + BreadcrumbItemEventArgs args = new BreadcrumbItemEventArgs(e.Source as BreadcrumbItem, BreadcrumbItemDropDownOpenedEvent); + RaiseEvent(args); + } + + /// + /// Occurs when the dropdown of a BreadcrumbItem is closed. + /// + protected virtual void OnBreadcrumbItemDropDownClosed(RoutedEventArgs e) + { + BreadcrumbItemEventArgs args = new BreadcrumbItemEventArgs(e.Source as BreadcrumbItem, BreadcrumbItemDropDownClosedEvent); + RaiseEvent(args); + } + + private ObservableCollection traces; + + protected override Size ArrangeOverride(Size arrangeBounds) + { + Size size = base.ArrangeOverride(arrangeBounds); + CheckOverflowImage(); + + return size; + } + + /// + /// Gets whether the selected breadcrumb is the RootItem. + /// + public bool IsRootSelected + { + get { return (bool)GetValue(IsRootSelectedProperty); } + private set { SetValue(IsRootSelectedPropertyKey, value); } + } + + + /// + /// Check what image to display in the drop down button of the overflow button: + /// + private void CheckOverflowImage() + { + bool isOverflow = (RootItem != null && RootItem.SelectedBreadcrumb != null && RootItem.SelectedBreadcrumb.IsOverflow); + OverflowMode = isOverflow ? BreadcrumbButton.ButtonMode.Overflow : BreadcrumbButton.ButtonMode.Breadcrumb; + } + + /// + /// Build the list of traces for the overflow button. + /// + private void BuildTraces() + { + BreadcrumbItem item = RootItem; + + traces.Clear(); + if (item != null && item.IsOverflow) + { + foreach (object trace in item.Items) + { + MenuItem menuItem = new RibbonMenuItem(); + menuItem.Tag = trace; + BreadcrumbItem bcItem = item.ContainerFromItem(trace); + + menuItem.Click += new RoutedEventHandler(menuItem_Click); + menuItem.Icon = GetImage(bcItem != null ? bcItem.Image : null); + if (trace == RootItem.SelectedItem) menuItem.FontWeight = FontWeights.Bold; + traces.Add(menuItem); + if (bcItem != null) menuItem.Header = bcItem.TraceValue; + } + traces.Insert(0, new Separator()); + MenuItem rootMenuItem = new RibbonMenuItem(); + rootMenuItem.Header = item.TraceValue; + rootMenuItem.Command = BreadcrumbBar.SelectRootCommand; + rootMenuItem.CommandParameter = item; + rootMenuItem.Icon = GetImage(item.Image); + traces.Insert(0, rootMenuItem); + } + + item = item != null ? item.SelectedBreadcrumb : null; + + while (item != null) + { + if (!item.IsOverflow) break; + MenuItem traceMenuItem = new MenuItem(); + traceMenuItem.Header = item.TraceValue; + traceMenuItem.Command = BreadcrumbBar.SelectRootCommand; + traceMenuItem.CommandParameter = item; + traceMenuItem.Icon = GetImage(item.Image); + traces.Insert(0, traceMenuItem); + item = item.SelectedBreadcrumb; + } + } + + private object GetImage(ImageSource imageSource) + { + if (imageSource == null) return null; + Image image = new Image(); + image.Source = imageSource; + image.Stretch = Stretch.Fill; + image.SnapsToDevicePixels = true; + image.Width = image.Height = 16; + + return image; + } + + void menuItem_Click(object sender, RoutedEventArgs e) + { + MenuItem item = e.Source as MenuItem; + if (RootItem != null && item != null) + { + object dataItem = item.Tag; + if (dataItem != null && dataItem.Equals(RootItem.SelectedItem)) RootItem.SelectedItem = null; + RootItem.SelectedItem = dataItem; + } + } + + + /// + /// Gets or sets the DataTemplateSelector for the overflow items. + /// + public DataTemplateSelector OverflowItemTemplateSelector + { + get { return (DataTemplateSelector)GetValue(OverflowItemTemplateSelectorProperty); } + set { SetValue(OverflowItemTemplateSelectorProperty, value); } + } + + /// + /// Gets or set the DataTemplate for the OverflowItem. + /// + public DataTemplate OverflowItemTemplate + { + get { return (DataTemplate)GetValue(OverflowItemTemplateProperty); } + set { SetValue(OverflowItemTemplateProperty, value); } + } + + + /// + /// Gets the collapsed traces. + /// + public IEnumerable CollapsedTraces + { + get { return (IEnumerable)GetValue(CollapsedTracesProperty); } + private set { SetValue(CollapsedTracesPropertyKey, value); } + } + + /// + /// Gets or sets the root of the breadcrumb which can be a hierarchical data source or a BreadcrumbItem. + /// + public object Root + { + get { return (object)GetValue(RootProperty); } + set { SetValue(RootProperty, value); } + } + + private static void OnRootPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + BreadcrumbBar bar = d as BreadcrumbBar; + bar.OnRootChanged(d, e.OldValue, e.NewValue); + } + + private static void OnSelectedItemPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + BreadcrumbBar bar = d as BreadcrumbBar; + bar.OnSelectedItemChanged(d, e.OldValue, e.NewValue); + } + + /// + /// Occurs when the selected item of an embedded BreadcrumbItem is changed. + /// + /// + /// + protected virtual void OnSelectedItemChanged(object sender, object oldvalue, object newValue) + { + } + + /// + /// Occurs when the Root property is changed. + /// + /// + /// + protected virtual void OnRootChanged(object sender, object oldValue, object newValue) + { + newValue = GetFirstItem(newValue); + BreadcrumbItem oldRoot = oldValue as BreadcrumbItem; + if (oldRoot != null) + { + oldRoot.IsRoot = false; + } + + if (newValue == null) + { + RootItem = null; + Path = null; + } + else + { + BreadcrumbItem root = newValue as BreadcrumbItem; + if (root == null) + { + root = BreadcrumbItem.CreateItem(newValue); + } + if (root != null) + { + root.IsRoot = true; + } + this.RemoveLogicalChild(oldValue); + RootItem = root; + if (root != null) + { + if (LogicalTreeHelper.GetParent(root) == null) this.AddLogicalChild(root); + } + SelectedItem = root != null ? root.DataContext : null; + if (IsInitialized) SelectedBreadcrumb = root; else selectedBreadcrumb = root; + } + } + + /// + /// Gets the first item of the specified value if it is a collection, otherwise it returns the value itself. + /// + /// A collection, otherwise an object. + /// The first item of the collection, otherwise the entity. + private object GetFirstItem(object entity) + { + ICollection c = entity as ICollection; + if (c != null) + { + foreach (object item in c) + { + return item; + } + } + return entity; + } + + + /// + /// Gets or sets the selected item. + /// + public object SelectedItem + { + get { return (object)GetValue(SelectedItemProperty); } + private set { SetValue(SelectedItemProperty, value); } + } + + private BreadcrumbItem selectedBreadcrumb; + + /// + /// Gets the selected BreadcrumbItem + /// + public BreadcrumbItem SelectedBreadcrumb + { + get { return (BreadcrumbItem)GetValue(SelectedBreadcrumbProperty); } + private set + { + selectedBreadcrumb = value; + SetValue(SelectedBreadcrumbPropertyKey, value); + } + } + + + /// + /// Gets whether the Overflow button is pressed. + /// + public bool IsOverflowPressed + { + get { return (bool)GetValue(IsOverflowPressedProperty); } + private set { SetValue(IsOverflowPressedProperty, value); } + } + + private static void OverflowPressedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + BreadcrumbBar bar = d as BreadcrumbBar; + bar.OnOverflowPressedChanged(); + } + + /// + /// Occurs when the IsOverflowPressed property is changed. + /// + protected virtual void OnOverflowPressedChanged() + { + // rebuild the list of tracess to show in the popup of the overflow button: + if (IsOverflowPressed) BuildTraces(); + } + + + + /// + /// Gets the Root BreadcrumbItem. + /// + public BreadcrumbItem RootItem + { + get { return (BreadcrumbItem)GetValue(RootItemProperty); } + protected set + { + SetValue(RootItemPropertyKey, value); + } + } + + /// + /// Gets or sets the TemplateSelector for an embedded BreadcrumbItem. + /// + public DataTemplateSelector BreadcrumbItemTemplateSelector + { + get { return (DataTemplateSelector)GetValue(BreadcrumbItemTemplateSelectorProperty); } + set { SetValue(BreadcrumbItemTemplateSelectorProperty, value); } + } + + + /// + /// Gets or sets the Template for an embedded BreadcrumbItem. + /// + public DataTemplate BreadcrumbItemTemplate + { + get { return (DataTemplate)GetValue(BreadcrumbItemTemplateProperty); } + set { SetValue(BreadcrumbItemTemplateProperty, value); } + } + + + /// + /// Gets the overflow mode for the Overflow BreadcrumbButton (PART_Root). + /// + public BreadcrumbButton.ButtonMode OverflowMode + { + get { return (BreadcrumbButton.ButtonMode)GetValue(OverflowModeProperty); } + private set { SetValue(OverflowModePropertyKey, value); } + } + + private ComboBox comboBox; + private BreadcrumbButton rootButton; + + public override void OnApplyTemplate() + { + base.OnApplyTemplate(); + comboBox = GetTemplateChild(partComboBox) as ComboBox; + rootButton = GetTemplateChild(partRoot) as BreadcrumbButton; + if (comboBox != null) + { + comboBox.DropDownClosed += new EventHandler(comboBox_DropDownClosed); + comboBox.IsKeyboardFocusWithinChanged += new DependencyPropertyChangedEventHandler(comboBox_IsKeyboardFocusWithinChanged); + comboBox.KeyDown += new KeyEventHandler(comboBox_KeyDown); + } + if (rootButton != null) + { + rootButton.Click += new RoutedEventHandler(rootButton_Click); + } + } + + + protected override void OnInitialized(EventArgs e) + { + base.OnInitialized(e); + if (initPath != null) + { + initPath = null; + BuildBreadcrumbsFromPath(Path); + } + } + + void comboBox_KeyDown(object sender, KeyEventArgs e) + { + switch (e.Key) + { + case Key.Escape: Exit(false); break; + case Key.Enter: Exit(true); break; + default: return; + } + e.Handled = true; + } + + void comboBox_IsKeyboardFocusWithinChanged(object sender, DependencyPropertyChangedEventArgs e) + { + bool isKeyboardFocusWithin = (bool)e.NewValue; + if (!isKeyboardFocusWithin) Exit(true); + } + + protected override void OnMouseDown(MouseButtonEventArgs e) + { + if (e.Handled) return; + if (e.ChangedButton == MouseButton.Left && e.LeftButton == MouseButtonState.Pressed) + { + e.Handled = true; + SetInputState(); + } + base.OnMouseDown(e); + + } + + void rootButton_Click(object sender, RoutedEventArgs e) + { + SetInputState(); + } + + private void SetInputState() + { + if (comboBox != null && IsEditable) + { + comboBox.Text = Path; + comboBox.Visibility = Visibility.Visible; + comboBox.Focus(); + } + } + + /// + /// Gets the edit path from the tracess of the BreacrumbItems. + /// + /// + public string GetEditPath() + { + string displayPath = GetDisplayPath(); + PathConversionEventArgs e = new PathConversionEventArgs(PathConversionEventArgs.ConversionMode.DisplayToEdit, displayPath, Root, PathConversionEvent); + RaiseEvent(e); + return e.EditPath; + } + + /// + /// Gets the path of the specified BreadcrumbItem. + /// + /// The BreadrumbItem for which to determine the path. + /// The path of the BreadcrumbItem which is the concenation of all Traces from all selected breadcrumbs. + public string PathFromBreadcrumbItem(BreadcrumbItem item) + { + StringBuilder sb = new StringBuilder(); + while (item != null) + { + if (item == RootItem && sb.Length > 0) break; + if (sb.Length > 0) sb.Insert(0, SeparatorString); + sb.Insert(0, item.TraceValue); + item = item.ParentBreadcrumbItem; + } + PathConversionEventArgs e = new PathConversionEventArgs(PathConversionEventArgs.ConversionMode.DisplayToEdit, sb.ToString(), Root, PathConversionEvent); + RaiseEvent(e); + return e.EditPath; + } + + /// + /// Gets the display path from the traces of the BreacrumbItems. + /// + /// + public string GetDisplayPath() + { + string separator = SeparatorString; + StringBuilder sb = new StringBuilder(); + BreadcrumbItem item = RootItem; + int index = 0; + while (item != null) + { + if (sb.Length > 0) sb.Append(separator); + if (index >= BreadcrumbsToHide || item.SelectedItem == null) + { + sb.Append(item.GetTracePathValue()); + } + index++; + item = item.SelectedBreadcrumb; + } + + return sb.ToString(); + } + + + /// + /// Do what's necassary to do when the BreadcrumbBar has lost focus. + /// + private void Exit(bool updatePath) + { + if (comboBox != null) + { + if (updatePath && comboBox.IsVisible) Path = comboBox.Text; + comboBox.Visibility = Visibility.Hidden; + } + } + + void comboBox_DropDownClosed(object sender, EventArgs e) + { + IsDropDownOpen = false; + Path = comboBox.Text; + } + + /// + /// Gets or sets the DataSource for the DropDownItems of the combobox. + /// + public IEnumerable DropDownItemsSource + { + get { return (IEnumerable)GetValue(DropDownItemsSourceProperty); } + set { SetValue(DropDownItemsSourceProperty, value); } + } + + + /// + /// Gets or sets whether the combobox dropdown is opened. + /// + public bool IsDropDownOpen + { + get { return (bool)GetValue(IsDropDownOpenProperty); } + set { SetValue(IsDropDownOpenProperty, value); } + } + + + /// + /// Gets or sets the string that is used to separate between traces. + /// + public string SeparatorString + { + get { return (string)GetValue(SeparatorStringProperty); } + set { SetValue(SeparatorStringProperty, value); } + } + + + + public string PathBinding + { + get { return (string)GetValue(PathBindingProperty); } + set { SetValue(PathBindingProperty, value); } + } + + + + + public static readonly DependencyProperty PathBindingProperty = + DependencyProperty.Register("PathBinding", typeof(string), typeof(BreadcrumbBar), new UIPropertyMetadata("", OnPathBindingPropertyChanged)); + + private static void OnPathBindingPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + d.SetValue(PathProperty, e.NewValue); + } + + + /// + /// Gets or sets the selected path. + /// + public string Path + { + get { return (string)GetValue(PathProperty); } + set { SetValue(PathProperty, value); } + } + + private ObservableCollection buttons = new ObservableCollection(); + + /// + /// Gets the collection of buttons to appear on the right of the breadcrumb bar. + /// + public ObservableCollection Buttons + { + get { return buttons; } + } + + + /// + /// Gets or sets the DropDownItems for the combobox. + /// + public ItemCollection DropDownItems + { + get { return comboBoxControlItems.Items; } + } + + + /// + /// Gets whether the dropdown has items. + /// + public bool HasDropDownItems + { + get { return (bool)GetValue(HasDropDownItemsProperty); } + private set { SetValue(HasDropDownItemsProperty, value); } + } + + + /// + /// Gets or sets the ItemsPanelTemplate for the DropDownItems of the combobox. + /// + public ItemsPanelTemplate DropDownItemsPanel + { + get { return (ItemsPanelTemplate)GetValue(DropDownItemsPanelProperty); } + set { SetValue(DropDownItemsPanelProperty, value); } + } + + + /// + /// Gets or sets the ItemsPanelTemplateSelector for the DropDownItems of the combobox. + /// + public DataTemplateSelector DropDownItemTemplateSelector + { + get { return (DataTemplateSelector)GetValue(DropDownItemTemplateSelectorProperty); } + set { SetValue(DropDownItemTemplateSelectorProperty, value); } + } + + + /// + /// Gets or sets the DataTemplate for the DropDownItems of the combobox. + /// + public DataTemplate DropDownItemTemplate + { + get { return (DataTemplate)GetValue(DropDownItemTemplateProperty); } + set { SetValue(DropDownItemTemplateProperty, value); } + } + + + /// + /// Gets or sets whether the breadcrumb bar can change to edit mode where the path can be edited. + /// + public bool IsEditable + { + get { return (bool)GetValue(IsEditableProperty); } + set { SetValue(IsEditableProperty, value); } + } + + + /// + /// Gets or sets the SelectedIndex of the combobox. + /// + public int SelectedDropDownIndex + { + get { return (int)GetValue(SelectedDropDownIndexProperty); } + set { SetValue(SelectedDropDownIndexProperty, value); } + } + + + /// + /// Gets or sets the current progress indicator value. + /// + public double ProgressValue + { + get { return (double)GetValue(ProgressValueProperty); } + set { SetValue(ProgressValueProperty, value); } + } + + + /// + /// Check the desired value for ProgressValue and asure that it is between Minimum and Maximum: + /// + /// + /// + /// The value between mimimum and maximum. + static object CoerceProgressValue(DependencyObject d, object baseValue) + { + BreadcrumbBar bar = d as BreadcrumbBar; + double value = (double)baseValue; + if (value > bar.ProgressMaximum) value = bar.ProgressMaximum; + if (value < bar.ProgressMimimum) value = bar.ProgressMimimum; + + return value; + } + + /// + /// Occurs when the ProgressValue is changed. + /// + public event RoutedEventHandler ProgressValueChanged + { + add { AddHandler(BreadcrumbBar.ProgressValueChangedEvent, value); } + remove { RemoveHandler(BreadcrumbBar.ProgressValueChangedEvent, value); } + } + + static void ProgressValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + // RoutedPropertyChangedEventArgs args = new RoutedPropertyChangedEventArgs((double)e.OldValue, (double)e.NewValue,BreadcrumbBar.ProgessValueChangedEvent); + RoutedEventArgs args = new RoutedEventArgs(BreadcrumbBar.ProgressValueChangedEvent); + BreadcrumbBar bar = d as BreadcrumbBar; + bar.RaiseEvent(args); + } + + protected override void OnMouseLeave(MouseEventArgs e) + { + // if (this.IsKeyboardFocusWithin) this.Focus(); + base.OnMouseLeave(e); + } + + static object CoerceProgressMaximum(DependencyObject d, object baseValue) + { + BreadcrumbBar bar = d as BreadcrumbBar; + double value = (double)baseValue; + if (value < bar.ProgressMimimum) value = bar.ProgressMimimum; + if (value < bar.ProgressValue) bar.ProgressValue = value; + if (value < 0) value = 0; + + return value; + } + + + static object CoerceProgressMinimum(DependencyObject d, object baseValue) + { + BreadcrumbBar bar = d as BreadcrumbBar; + double value = (double)baseValue; + if (value > bar.ProgressMaximum) value = bar.ProgressMaximum; + if (value > bar.ProgressValue) bar.ProgressValue = value; + + return value; + } + + /// + /// Gets or sets the maximum progress value. + /// + public double ProgressMaximum + { + get { return (double)GetValue(ProgressMaximumProperty); } + set { SetValue(ProgressMaximumProperty, value); } + } + + + /// + /// Gets or sets the minimum progess value. + /// + public double ProgressMimimum + { + get { return (double)GetValue(ProgressMinimumProperty); } + set { SetValue(ProgressMinimumProperty, value); } + } + + + protected override IEnumerator LogicalChildren + { + get + { + object content = this.RootItem; ; + if (content == null) + { + return base.LogicalChildren; + } + if (base.TemplatedParent != null) + { + DependencyObject current = content as DependencyObject; + if (current != null) + { + DependencyObject parent = LogicalTreeHelper.GetParent(current); + if ((parent != null) && (parent != this)) + { + return base.LogicalChildren; + } + } + } + + object[] array = new object[] { RootItem }; + return array.GetEnumerator(); + } + } + + + + #region IAddChild Members + + public void AddChild(object value) + { + this.Root = value; + } + + public void AddText(string text) + { + AddChild(text); + } + + #endregion + + /// + /// Gets or sets the TraceBinding property that will be set to every child BreadcrumbItem. This is not a dependency property! + /// + public BindingBase TraceBinding { get; set; } + + /// + /// Gets or sets the ImageBinding property that will be set to every child BreadcrumbItem. This is not a dependency property! + /// + public BindingBase ImageBinding { get; set; } + + } +} diff --git a/Odyssey/Odyssey/BreadcrumbBar/BreadcrumbButton.cs b/Odyssey/Odyssey/BreadcrumbBar/BreadcrumbButton.cs new file mode 100644 index 0000000..1b3dc57 --- /dev/null +++ b/Odyssey/Odyssey/BreadcrumbBar/BreadcrumbButton.cs @@ -0,0 +1,463 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using System.Windows.Controls.Primitives; +using System.Diagnostics; + + +#region Licence +// Copyright (c) 2008 Thomas Gerber +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#endregion +namespace Odyssey.Controls +{ + /// + /// A breadcrumb button is part of a BreadcrumbItem and contains a header and a dropdown button. + /// + [TemplatePart(Name = partMenu)] + [TemplatePart(Name = partToggle)] + [TemplatePart(Name = partButton)] + [TemplatePart(Name = partDropDown)] + public class BreadcrumbButton : HeaderedItemsControl + { + const string partMenu = "PART_Menu"; + const string partToggle = "PART_Toggle"; + const string partButton = "PART_button"; + const string partDropDown = "PART_DropDown"; + static BreadcrumbButton() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(BreadcrumbButton), new FrameworkPropertyMetadata(typeof(BreadcrumbButton))); + } + + #region Dependency Properties + public static readonly DependencyProperty ImageProperty = + DependencyProperty.Register("Image", typeof(object), typeof(BreadcrumbButton), new UIPropertyMetadata(null)); + + public static readonly DependencyProperty SelectedItemProperty = + DependencyProperty.Register("SelectedItem", typeof(object), typeof(BreadcrumbButton), new UIPropertyMetadata(null, SelectedItemChangedEvent)); + + + #endregion + + #region RoutedEvents + public static readonly RoutedEvent SelectedItemChanged = EventManager.RegisterRoutedEvent("SelectedItemChanged", + RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(BreadcrumbButton)); + + public static readonly RoutedEvent ClickEvent = EventManager.RegisterRoutedEvent("Click", + RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(BreadcrumbButton)); + #endregion + + private ContextMenu contextMenu; + private Control dropDownBtn; + + public BreadcrumbButton() + : base() + { + CommandBindings.Add(new CommandBinding(SelectCommand, SelectCommandExecuted)); + CommandBindings.Add(new CommandBinding(OpenOverflowCommand, OpenOverflowCommandExecuted, OpenOverflowCommandCanExecute)); + + InputBindings.Add(new KeyBinding(BreadcrumbButton.SelectCommand, new KeyGesture(Key.Enter))); + InputBindings.Add(new KeyBinding(BreadcrumbButton.SelectCommand, new KeyGesture(Key.Space))); + InputBindings.Add(new KeyBinding(BreadcrumbButton.OpenOverflowCommand, new KeyGesture(Key.Down))); + InputBindings.Add(new KeyBinding(BreadcrumbButton.OpenOverflowCommand, new KeyGesture(Key.Up))); + } + + + private bool isPressed = false; + + protected override void OnMouseLeave(MouseEventArgs e) + { + IsPressed = false; + } + + + + protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) + { + e.Handled = true; + IsPressed = isPressed = true; + base.OnMouseLeftButtonDown(e); + } + + + + + protected override void OnMouseUp(MouseButtonEventArgs e) + { + e.Handled = true; + if (isPressed) + { + RoutedEventArgs args = new RoutedEventArgs(BreadcrumbButton.ClickEvent); + RaiseEvent(args); + selectCommand.Execute(null, this); + } + IsPressed = isPressed = false; + base.OnMouseUp(e); + } + + private void SelectCommandExecuted(object sender, ExecutedRoutedEventArgs e) + { + SelectedItem = null; + RoutedEventArgs args = new RoutedEventArgs(Button.ClickEvent); + RaiseEvent(args); + } + + private void OpenOverflowCommandExecuted(object sender, ExecutedRoutedEventArgs e) + { + IsDropDownPressed = true; + } + + private void OpenOverflowCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) + { + e.CanExecute = Items.Count > 0; + } + + public static RoutedUICommand OpenOverflowCommand + { + get { return openOverflowCommand; } + } + + public static RoutedUICommand SelectCommand + { + get { return selectCommand; } + } + + private static RoutedUICommand openOverflowCommand = new RoutedUICommand("Open Overflow", "OpenOverflowCommand", typeof(BreadcrumbButton)); + private static RoutedUICommand selectCommand = new RoutedUICommand("Select", "SelectCommand", typeof(BreadcrumbButton)); + + + public override void OnApplyTemplate() + { + dropDownBtn = this.GetTemplateChild(partDropDown) as Control; + contextMenu = this.GetTemplateChild(partMenu) as ContextMenu; + if (contextMenu != null) + { + contextMenu.Opened += new RoutedEventHandler(contextMenu_Opened); + } + if (dropDownBtn != null) + { + dropDownBtn.MouseDown += new MouseButtonEventHandler(dropDownBtn_MouseDown); + } + base.OnApplyTemplate(); + } + + void dropDownBtn_MouseDown(object sender, MouseButtonEventArgs e) + { + e.Handled = true; + IsDropDownPressed ^= true; + } + + + /// + /// Gets or sets the Image of the BreadcrumbButton. + /// + public ImageSource Image + { + get { return (ImageSource)GetValue(ImageProperty); } + set { SetValue(ImageProperty, value); } + } + + + //TODO: Menu needs too long to render if there are too many items (> 20000). + void contextMenu_Opened(object sender, RoutedEventArgs e) + { + contextMenu.Items.Clear(); + contextMenu.ItemTemplate = ItemTemplate; + contextMenu.ItemTemplateSelector = ItemTemplateSelector; + + // List menuItems = new List(); + foreach (object item in Items) + { + if (!(item is MenuItem) && !(item is Separator)) + { + + RibbonMenuItem menuItem = new RibbonMenuItem(); + menuItem.DataContext = item; + BreadcrumbItem bi = item as BreadcrumbItem; + if (bi == null) + { + BreadcrumbItem parent = TemplatedParent as BreadcrumbItem; + if (parent != null) bi = parent.ContainerFromItem(item); + } + if (bi != null) menuItem.Header = bi.TraceValue; + + Image image = new Image(); + image.Source = bi != null ? bi.Image : null; + image.SnapsToDevicePixels = true; + image.Stretch = Stretch.Fill; + image.VerticalAlignment = VerticalAlignment.Center; + image.HorizontalAlignment = HorizontalAlignment.Center; + image.Width = 16; + image.Height = 16; + + menuItem.Icon = image; + + menuItem.Click += new RoutedEventHandler(item_Click); + if (item != null && item.Equals(SelectedItem)) menuItem.FontWeight = FontWeights.Bold; + menuItem.ItemTemplate = ItemTemplate; + menuItem.ItemTemplateSelector = ItemTemplateSelector; + contextMenu.Items.Add(menuItem); + } + else + { + contextMenu.Items.Add(item); + } + } + contextMenu.Placement = PlacementMode.Relative; + contextMenu.PlacementTarget = dropDownBtn; + contextMenu.VerticalOffset = dropDownBtn.ActualHeight; + } + + void item_Click(object sender, RoutedEventArgs e) + { + MenuItem item = e.Source as MenuItem; + object dataItem = item.DataContext; + RemoveSelectedItem(dataItem); + SelectedItem = dataItem; + } + + /// + /// When a BreadcrumbItem is selected from a dropdown menu, the SelectedItem of the new selected item must be set to null. + /// Since no event is raised when a DependencyProperty is assigned to it's current value, this cannot be recognized at this place, + /// therefore the SelectedItem DependencyProperty must previously set to null before setting it to it's new value to raise event + /// when SelectedItem is changed: + /// + /// + private void RemoveSelectedItem(object dataItem) + { + if (dataItem != null && dataItem.Equals(SelectedItem)) SelectedItem = null; + } + + /// + /// Gets or sets the selectedItem. + /// + public object SelectedItem + { + get { return (object)GetValue(SelectedItemProperty); } + set { SetValue(SelectedItemProperty, value); } + } + + private static void SelectedItemChangedEvent(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + BreadcrumbButton button = d as BreadcrumbButton; + if (button.IsInitialized) + { + RoutedEventArgs args = new RoutedEventArgs(SelectedItemChanged); + button.RaiseEvent(args); + } + } + + /// + /// Focus this BreadcrumbButton if the focus is currently within the BreadcrumbBar where this BreadcrumbButton is embedded: + /// + protected override void OnMouseEnter(MouseEventArgs e) + { + isPressed = e.LeftButton == MouseButtonState.Pressed; + FrameworkElement parent = TemplatedParent as FrameworkElement; + while (parent != null && !(parent is BreadcrumbBar)) parent = VisualTreeHelper.GetParent(parent) as FrameworkElement; + BreadcrumbBar bar = parent as BreadcrumbBar; + if (bar != null && bar.IsKeyboardFocusWithin) Focus(); + IsPressed = isPressed; + base.OnMouseEnter(e); + } + + + private static void OverflowPressedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + BreadcrumbButton button = d as BreadcrumbButton; + button.OnOverflowPressedChanged(); + } + + protected virtual void OnOverflowPressedChanged() + { + } + + + /// + /// Specifies how to display the BreadcrumbButton. + /// + public enum ButtonMode + { + /// + /// Display as Breadcrumb. + /// + Breadcrumb, + + /// + /// Display as overflow. + /// + Overflow, + + /// + /// Display as drop down. + /// + DropDown + } + + + /// + /// Gets or sets the ButtonMode for the BreadcrumbButton. + /// + public ButtonMode Mode + { + get { return (ButtonMode)GetValue(ModeProperty); } + set { SetValue(ModeProperty, value); } + } + + // Using a DependencyProperty as the backing store for Mode. This enables animation, styling, binding, etc... + public static readonly DependencyProperty ModeProperty = + DependencyProperty.Register("Mode", typeof(ButtonMode), typeof(BreadcrumbButton), new UIPropertyMetadata(ButtonMode.Breadcrumb)); + + + /// + /// Occurs when the Button is clicked. + /// + public event RoutedEventHandler Click + { + add { AddHandler(BreadcrumbButton.ClickEvent, value); } + remove { RemoveHandler(BreadcrumbButton.ClickEvent, value); } + } + + /// + /// Occurs when the SelectedItem is changed. + /// + public event RoutedEventHandler Select + { + add { AddHandler(BreadcrumbButton.SelectedItemChanged, value); } + remove { RemoveHandler(BreadcrumbButton.SelectedItemChanged, value); } + } + + + /// + /// Gets or sets whether the button is pressed. + /// + public bool IsPressed + { + get { return (bool)GetValue(IsPressedProperty); } + set { SetValue(IsPressedProperty, value); } + } + + /// + /// Gets or sets whether the button is pressed. + /// + public static readonly DependencyProperty IsPressedProperty = + DependencyProperty.Register("IsPressed", typeof(bool), typeof(BreadcrumbButton), new UIPropertyMetadata(false)); + + + /// + /// Gets or sets whether the drop down button is pressed. + /// + public bool IsDropDownPressed + { + get { return (bool)GetValue(IsDropDownPressedProperty); } + set { SetValue(IsDropDownPressedProperty, value); } + } + + /// + /// Gets or sets whether the drop down button is pressed. + /// + public static readonly DependencyProperty IsDropDownPressedProperty = + DependencyProperty.Register("IsDropDownPressed", typeof(bool), typeof(BreadcrumbButton), new UIPropertyMetadata(false, OverflowPressedChanged)); + + + /// + /// Gets or sets the DataTemplate for the drop down items. + /// + public DataTemplate DropDownContentTemplate + { + get { return (DataTemplate)GetValue(DropDownContentTemplateProperty); } + set { SetValue(DropDownContentTemplateProperty, value); } + } + + // Using a DependencyProperty as the backing store for DropDownContentTemplate. This enables animation, styling, binding, etc... + public static readonly DependencyProperty DropDownContentTemplateProperty = + DependencyProperty.Register("DropDownContentTemplate", typeof(DataTemplate), typeof(BreadcrumbButton), new UIPropertyMetadata(null)); + + + + /// + /// Gets or sets whether the drop down button is visible. + /// + public bool IsDropDownVisible + { + get { return (bool)GetValue(IsDropDownVisibleProperty); } + set { SetValue(IsDropDownVisibleProperty, value); } + } + + /// + /// Gets or sets whether the drop down button is visible. + /// + public static readonly DependencyProperty IsDropDownVisibleProperty = + DependencyProperty.Register("IsDropDownVisible", typeof(bool), typeof(BreadcrumbButton), new UIPropertyMetadata(true)); + + + + /// + /// Gets or sets whether the button is visible. + /// + public bool IsButtonVisible + { + get { return (bool)GetValue(IsButtonVisibleProperty); } + set { SetValue(IsButtonVisibleProperty, value); } + } + + /// + /// Gets or sets whether the button is visible. + /// + public static readonly DependencyProperty IsButtonVisibleProperty = + DependencyProperty.Register("IsButtonVisible", typeof(bool), typeof(BreadcrumbButton), new UIPropertyMetadata(true)); + + + /// + /// Gets or sets whether the Image is visible + /// + public bool IsImageVisible + { + get { return (bool)GetValue(IsImageVisibleProperty); } + set { SetValue(IsImageVisibleProperty, value); } + } + + /// + /// Gets or sets whether the Image is visible + /// + public static readonly DependencyProperty IsImageVisibleProperty = + DependencyProperty.Register("IsImageVisible", typeof(bool), typeof(BreadcrumbButton), new UIPropertyMetadata(true)); + + + + + /// + /// Gets or sets whether to use visual background style on MouseOver and/or MouseDown. + /// + public bool EnableVisualButtonStyle + { + get { return (bool)GetValue(EnableVisualButtonStyleProperty); } + set { SetValue(EnableVisualButtonStyleProperty, value); } + } + + /// + /// Gets or sets whether to use visual background style on MouseOver and/or MouseDown. + /// + public static readonly DependencyProperty EnableVisualButtonStyleProperty = + DependencyProperty.Register("EnableVisualButtonStyle", typeof(bool), typeof(BreadcrumbButton), new UIPropertyMetadata(true)); + + + } +} diff --git a/Odyssey/Odyssey/BreadcrumbBar/BreadcrumbItem.cs b/Odyssey/Odyssey/BreadcrumbBar/BreadcrumbItem.cs new file mode 100644 index 0000000..b3eb8d3 --- /dev/null +++ b/Odyssey/Odyssey/BreadcrumbBar/BreadcrumbItem.cs @@ -0,0 +1,724 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using System.Diagnostics; +using System.Windows.Controls.Primitives; +using System.Reflection; +using System.Xml; +using System.Collections; + +#region Licence +// Copyright (c) 2008 Thomas Gerber +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#endregion +namespace Odyssey.Controls +{ + /// + /// A breadcrumb item that is part of a BreadcrumbBar and contains a BreadcrumbButton and nested child BreadcrumbItems. + /// + [TemplatePart(Name = partHeader)] + [TemplatePart(Name = partSelected)] + public class BreadcrumbItem : Selector + { + + #region DependencyProperties + + /// + /// Gets or sets whether the dropdown button is pressed. + /// + public static readonly DependencyProperty IsDropDownPressedProperty = + DependencyProperty.Register("IsDropDownPressed", typeof(bool), typeof(BreadcrumbItem), new UIPropertyMetadata(false, DropDownPressedPropertyChanged)); + + /// + /// Gets or sets whether the BreadcrumbItem is in overflow mode, which means that the header property is not visible. + /// + public static readonly DependencyProperty IsOverflowProperty = + DependencyProperty.Register("IsOverflow", typeof(bool), typeof(BreadcrumbItem), new FrameworkPropertyMetadata(false, + FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsParentArrange | FrameworkPropertyMetadataOptions.AffectsParentMeasure, + OverflowPropertyChanged)); + + /// + /// Gets or sets whether this BreadcrumbItem is the Root of a BreadcrumbBar. + /// + public static readonly DependencyProperty IsRootProperty = + DependencyProperty.Register("IsRoot", typeof(bool), typeof(BreadcrumbItem), new UIPropertyMetadata(false)); + + private static readonly DependencyPropertyKey SelectedBreadcrumbPropertyKey = + DependencyProperty.RegisterReadOnly("SelectedBreadcrumb", typeof(BreadcrumbItem), typeof(BreadcrumbItem), + new UIPropertyMetadata(null, SelectedBreadcrumbPropertyChanged)); + + /// + /// Gets or sets the TemplateSelector of an Item. + /// + public static readonly DependencyProperty OverflowItemTemplateSelectorProperty; + + public static readonly DependencyProperty OverflowItemTemplateProperty; + + /// + /// Gets or sets the ImageSource. + /// + public static readonly DependencyProperty ImageProperty = + DependencyProperty.Register("Image", typeof(ImageSource), typeof(BreadcrumbItem), new UIPropertyMetadata(null)); + + /// + /// Gets or sets the Trace string to build the Path. + /// + public static readonly DependencyProperty TraceProperty = + DependencyProperty.Register("Trace", typeof(object), typeof(BreadcrumbItem), new UIPropertyMetadata(null, TracePropertyChanged)); + + /// + /// Gets or sets the Header. + /// + public static readonly DependencyProperty HeaderProperty = + DependencyProperty.Register("Header", typeof(object), typeof(BreadcrumbItem), new UIPropertyMetadata(null, HeaderPropertyChanged)); + + public static readonly DependencyProperty HeaderTemplateProperty; + + public static readonly DependencyProperty HeaderTemplateSelectorProperty; + + #endregion + + const string partHeader = "PART_Header"; + const string partSelected = "PART_Selected"; + + static BreadcrumbItem() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(BreadcrumbItem), new FrameworkPropertyMetadata(typeof(BreadcrumbItem))); + + OverflowItemTemplateProperty = BreadcrumbBar.OverflowItemTemplateProperty.AddOwner(typeof(BreadcrumbItem), + new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits)); + + OverflowItemTemplateSelectorProperty = BreadcrumbBar.OverflowItemTemplateSelectorProperty.AddOwner(typeof(BreadcrumbItem), + new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits)); + + HeaderTemplateSelectorProperty = BreadcrumbBar.BreadcrumbItemTemplateSelectorProperty.AddOwner(typeof(BreadcrumbItem), + new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits)); + + HeaderTemplateProperty = BreadcrumbBar.BreadcrumbItemTemplateProperty.AddOwner(typeof(BreadcrumbItem), + new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits)); + } + + protected override void OnItemsChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e) + { + base.OnItemsChanged(e); + } + + private static void HeaderPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) + { + BreadcrumbItem item = sender as BreadcrumbItem; + } + + private static void TracePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) + { + BreadcrumbItem item = sender as BreadcrumbItem; + + RoutedPropertyChangedEventArgs args = new RoutedPropertyChangedEventArgs(e.OldValue, e.NewValue, TraceChangedEvent); + item.RaiseEvent(args); + } + + /// + /// Occurs when the IsDropDownPressed property is changed. + /// + public event RoutedPropertyChangedEventHandler DropDownPressedChanged + { + add { AddHandler(DropDownPressedChangedEvent, value); } + remove { RemoveHandler(DropDownPressedChangedEvent, value); } + } + + /// + /// Occurs when the IsDropDownPressed property is changed. + /// + public static readonly RoutedEvent DropDownPressedChangedEvent = EventManager.RegisterRoutedEvent("DropDownPressedChanged", + RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler), typeof(BreadcrumbItem)); + + /// + /// Occurs when the Trace property is changed. + /// + public event RoutedPropertyChangedEventHandler TraceChanged + { + add { AddHandler(TraceChangedEvent, value); } + remove { RemoveHandler(TraceChangedEvent, value); } + } + + /// + /// Occurs when the Trace property is changed. + /// + public static readonly RoutedEvent TraceChangedEvent = EventManager.RegisterRoutedEvent("TraceChanged", + RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler), typeof(BreadcrumbItem)); + + + /// + /// Creates a new instance of BreadcrumbItem. + /// + public BreadcrumbItem() + : base() + { + } + + + /// + /// Creates a new BreadcrumbItem out of the specified data. + /// + /// The DataContext for the BreadcrumbItem + /// DataContext if dataContext is a Breadcrumbitem, otherwhise a new BreadcrumbItem. + public static BreadcrumbItem CreateItem(object dataContext) + { + BreadcrumbItem item = dataContext as BreadcrumbItem; + if (item == null && dataContext != null) + { + item = new BreadcrumbItem(); + item.DataContext = dataContext; + } + return item; + } + + private static void SelectedBreadcrumbPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + BreadcrumbItem item = d as BreadcrumbItem; + item.OnSelectedBreadcrumbChanged(e.OldValue, e.NewValue); + } + + /// + /// Occurs when the selected BreadcrumbItem is changed. + /// + /// + /// + protected virtual void OnSelectedBreadcrumbChanged(object oldItem, object newItem) + { + if (SelectedBreadcrumb != null) + { + SelectedBreadcrumb.SelectedItem = null; + } + } + + protected override bool IsItemItsOwnContainerOverride(object item) + { + return item is BreadcrumbItem; + } + + protected override DependencyObject GetContainerForItemOverride() + { + BreadcrumbItem item = new BreadcrumbItem(); + return item; + } + + private FrameworkElement headerControl; + private FrameworkElement selectedControl; + + + public override void OnApplyTemplate() + { + base.OnApplyTemplate(); + headerControl = GetTemplateChild(partHeader) as FrameworkElement; + selectedControl = GetTemplateChild(partSelected) as FrameworkElement; + + ApplyBinding(); + } + + public object Data + { + get + { + return DataContext != null ? DataContext : this; + } + } + + + + /// + /// Gets or sets wheter the dropdown button is pressed. + /// + public bool IsDropDownPressed + { + get { return (bool)GetValue(IsDropDownPressedProperty); } + set { SetValue(IsDropDownPressedProperty, value); } + } + + + /// + /// Occurs when the IsDropDownPressed property is changed. + /// + /// + /// + public static void DropDownPressedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + BreadcrumbItem item = d as BreadcrumbItem; + item.OnDropDownPressedChanged(); + } + + + /// + /// Occurs when the DropDown button is pressed or released. + /// + protected virtual void OnDropDownPressedChanged() + { + RoutedEventArgs args = new RoutedEventArgs(DropDownPressedChangedEvent); + RaiseEvent(args); + } + + + + /// + /// Gets whether the breadcrumb item is overflowed which means it is not visible in the breadcrumb bar but in the + /// drop down menu of the breadcrumb bar. + /// + public bool IsOverflow + { + get { return (bool)GetValue(IsOverflowProperty); } + private set { SetValue(IsOverflowProperty, value); } + } + + /// + /// Occurs when the Overflow property is changed. + /// + /// + /// + public static void OverflowPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + BreadcrumbItem item = d as BreadcrumbItem; + item.OnOverflowChanged((bool)e.NewValue); + } + + + + /// + /// Set to true, to collapse the item if SelectedItem is not null. otherwise false. + /// + public bool IsRoot + { + get { return (bool)GetValue(IsRootProperty); } + set { SetValue(IsRootProperty, value); } + } + + + /// + /// Occurs when the Overflow property is changed. + /// + public static readonly RoutedEvent OverflowChangedEvent = EventManager.RegisterRoutedEvent("OverflowChanged", + RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(BreadcrumbItem)); + + /// + /// Occurs when the Overflow property is changed. + /// + protected virtual void OnOverflowChanged(bool newValue) + { + RoutedEventArgs args = new RoutedEventArgs(OverflowChangedEvent); + RaiseEvent(args); + } + + + /// + /// Perform a special measurement that checks whether to collapse the header. + /// + /// + /// + protected override Size MeasureOverride(Size constraint) + { + if (SelectedItem != null) + { + headerControl.Visibility = Visibility.Visible; + headerControl.Measure(constraint); + Size size = new Size(constraint.Width - headerControl.DesiredSize.Width, constraint.Height); + selectedControl.Measure(new Size(double.PositiveInfinity, constraint.Height)); + double width = headerControl.DesiredSize.Width + selectedControl.DesiredSize.Width; + if (width > constraint.Width || (IsRoot && SelectedItem != null)) + { + headerControl.Visibility = Visibility.Collapsed; + } + } + else if (headerControl != null) headerControl.Visibility = Visibility.Visible; + IsOverflow = headerControl != null ? headerControl.Visibility != Visibility.Visible : false; + + Size result = base.MeasureOverride(constraint); + return result; + } + + + protected override void OnSelectionChanged(SelectionChangedEventArgs e) + { + if (SelectedItem == null) + { + SelectedBreadcrumb = null; + } + else + { + SelectedBreadcrumb = ContainerFromItem(SelectedItem); + } + base.OnSelectionChanged(e); + + } + + /// + /// Generates a new BreadcrumbItem out of the specified item. + /// + /// The item for which to create a new BreadcrumbItem. + /// Item, if item is a BreadcrumbItem, otherwhise a newly created BreadcrumbItem. + public BreadcrumbItem ContainerFromItem(object item) + { + BreadcrumbItem result = item as BreadcrumbItem; + if (result == null) + { + result = CreateItem(item); + if (result != null) + { + AddLogicalChild(result); + result.ApplyTemplate(); + } + } + return result; + } + + + /// + /// Gets the selected BreadcrumbItem. + /// + public BreadcrumbItem SelectedBreadcrumb + { + get { return (BreadcrumbItem)GetValue(SelectedBreadcrumbProperty); } + private set { SetValue(SelectedBreadcrumbPropertyKey, value); } + } + + + public static readonly DependencyProperty SelectedBreadcrumbProperty = SelectedBreadcrumbPropertyKey.DependencyProperty; + + + public DataTemplateSelector BreadcrumbTemplateSelector { get; set; } + + public DataTemplate BreadcrumbItemTemplate { get; set; } + + + public DataTemplateSelector OverflowItemTemplateSelector + { + get { return (DataTemplateSelector)GetValue(OverflowItemTemplateSelectorProperty); } + set { SetValue(OverflowItemTemplateSelectorProperty, value); } + } + + public DataTemplate OverflowItemTemplate + { + get { return (DataTemplate)GetValue(OverflowItemTemplateProperty); } + set { SetValue(OverflowItemTemplateProperty, value); } + } + + + + + /// + /// Gets or sets the image that is used to display this item. + /// + public ImageSource Image + { + get { return (ImageSource)GetValue(ImageProperty); } + set { SetValue(ImageProperty, value); } + } + + /// + /// Gets or sets the Trace of the breadcrumb + /// + public object Trace + { + get { return (object)GetValue(TraceProperty); } + set { SetValue(TraceProperty, value); } + } + + + /// + /// Gets or sets the Binding to the Trace property. This is not a dependency property. + /// + public BindingBase TraceBinding { get; set; } + + + /// + /// Gets or sets the Binding to the Image property. This is not a dependency property. + /// + public BindingBase ImageBinding { get; set; } + + + /// + /// Gets or sets the header for the breadcrumb item. + /// + public object Header + { + + get { return (object)GetValue(HeaderProperty); } + set { SetValue(HeaderProperty, value); } + } + + /// + /// Gets or sets the header template. + /// + public DataTemplate HeaderTemplate + { + get { return (DataTemplate)GetValue(HeaderTemplateProperty); } + set { SetValue(HeaderTemplateProperty, value); } + } + + /// + /// Gets or sets the header template selector. + /// + public DataTemplateSelector HeaderTemplateSelector + { + get { return (DataTemplateSelector)GetValue(HeaderTemplateSelectorProperty); } + set { SetValue(HeaderTemplateSelectorProperty, value); } + } + + /// + /// Appies the binding to the breadcrumb item. + /// + public void ApplyBinding() + { + object item = DataContext; + if (item == null) return; + + BreadcrumbItem root = this; + DataTemplate template = HeaderTemplate; + DataTemplateSelector templateSelector = HeaderTemplateSelector; + if (templateSelector != null) + { + + template = templateSelector.SelectTemplate(item, root); + } + if (template == null) + { + DataTemplateKey key = GetResourceKey(item); + if (key != null) template = TryFindResource(key) as DataTemplate; + } + + root.SelectedItem = null; + + HierarchicalDataTemplate hdt = template as HierarchicalDataTemplate; + if (template != null) + { + root.Header = template.LoadContent(); + } + else + { + root.Header = item; + } + root.DataContext = item; + + if (hdt != null) + { + // bind the Items to the hierarchical data template: + root.SetBinding(BreadcrumbItem.ItemsSourceProperty, hdt.ItemsSource); + } + + BreadcrumbBar bar = BreadcrumbBar; + + if (bar != null) + { + if (TraceBinding == null) TraceBinding = bar.TraceBinding; + if (ImageBinding == null) ImageBinding = bar.ImageBinding; + } + + if (TraceBinding != null) + { + root.SetBinding(BreadcrumbItem.TraceProperty, TraceBinding); + } + if (ImageBinding != null) + { + root.SetBinding(BreadcrumbItem.ImageProperty, ImageBinding); + } + + + ApplyProperties(item); + } + + + + /// + /// Gets the parent BreadcrumbBar container. + /// + public BreadcrumbBar BreadcrumbBar + { + get + { + DependencyObject current = this; + while (current != null) + { + current = LogicalTreeHelper.GetParent(current); + if (current is BreadcrumbBar) return current as BreadcrumbBar; + } + return null; + } + } + + /// + /// Gets the parent BreadcrumbItem, otherwise null. + /// + public BreadcrumbItem ParentBreadcrumbItem + { + get + { + BreadcrumbItem parent = LogicalTreeHelper.GetParent(this) as BreadcrumbItem; + return parent; + } + } + + private static DataTemplateKey GetResourceKey(object item) + { + XmlDataProvider xml = item as XmlDataProvider; + DataTemplateKey key; + if (xml != null) + { + key = new DataTemplateKey(xml.XPath); + } + else + { + XmlNode node = item as XmlNode; + if (node != null) + { + key = new DataTemplateKey(node.Name); + } + else + { + key = new DataTemplateKey(item.GetType()); + } + } + return key; + } + + + private void ApplyProperties(object item) + { + ApplyPropertiesEventArgs e = new ApplyPropertiesEventArgs(item, this, BreadcrumbBar.ApplyPropertiesEvent); + e.Image = Image; + e.Trace = Trace; + e.TraceValue = TraceValue; + this.RaiseEvent(e); + Image = e.Image; + Trace = e.Trace; + } + + + /// + /// Gets the string trace that is used to build the path. + /// + /// + public string GetTracePathValue() + { + ApplyPropertiesEventArgs e = new ApplyPropertiesEventArgs(DataContext, this, BreadcrumbBar.ApplyPropertiesEvent); + e.Trace = Trace; + e.TraceValue = TraceValue; + this.RaiseEvent(e); + return e.TraceValue; + } + + /// + /// Gets the Trace string from the Trace property. + /// + public string TraceValue + { + get + { + XmlNode xml = Trace as XmlNode; + if (xml != null) + { + return xml.Value; + } + + if (Trace != null) return Trace.ToString(); + if (Header != null) return Header.ToString(); + return string.Empty; + } + } + + /// + /// Gets the item that represents the specified trace otherwise null. + /// + /// The Trace property of the associated BreadcrumbItem. + /// An object included in Items, otherwise null. + public object GetTraceItem(string trace) + { + this.ApplyTemplate(); + foreach (object item in Items) + { + BreadcrumbItem bcItem = ContainerFromItem(item); + if (bcItem != null) + { + ApplyProperties(item); + string value = bcItem.TraceValue; + if (value != null && value.Equals(trace, StringComparison.InvariantCultureIgnoreCase)) return item; + } + else return null; + } + return null; + } + + + protected override IEnumerator LogicalChildren + { + get + { + object content = this.SelectedBreadcrumb; ; + if (content == null) + { + return base.LogicalChildren; + } + if (base.TemplatedParent != null) + { + DependencyObject current = content as DependencyObject; + if (current != null) + { + DependencyObject parent = LogicalTreeHelper.GetParent(current); + if ((parent != null) && (parent != this)) + { + return base.LogicalChildren; + } + } + } + + object[] array = new object[] { SelectedBreadcrumb }; + return array.GetEnumerator(); + } + } + + /// + /// Gets or sets whether the button is visible. + /// + public bool IsButtonVisible + { + get { return (bool)GetValue(IsButtonVisibleProperty); } + set { SetValue(IsButtonVisibleProperty, value); } + } + + // Using a DependencyProperty as the backing store for IsButtonVisible. This enables animation, styling, binding, etc... + public static readonly DependencyProperty IsButtonVisibleProperty = + DependencyProperty.Register("IsButtonVisible", typeof(bool), typeof(BreadcrumbItem), new UIPropertyMetadata(true)); + + + + /// + /// Gets or sets whether the Image is visible. + /// + public bool IsImageVisible + { + get { return (bool)GetValue(IsImageVisibleProperty); } + set { SetValue(IsImageVisibleProperty, value); } + } + + /// + /// Gets or sets whether the Image is visible. + /// + public static readonly DependencyProperty IsImageVisibleProperty = + DependencyProperty.Register("IsImageVisible", typeof(bool), typeof(BreadcrumbItem), new UIPropertyMetadata(false)); + + + } +} diff --git a/Odyssey/Odyssey/BreadcrumbBar/BreadcrumbItemEventArgs.cs b/Odyssey/Odyssey/BreadcrumbBar/BreadcrumbItemEventArgs.cs new file mode 100644 index 0000000..fb183c5 --- /dev/null +++ b/Odyssey/Odyssey/BreadcrumbBar/BreadcrumbItemEventArgs.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; + +#region Licence +// Copyright (c) 2008 Thomas Gerber +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#endregion +namespace Odyssey.Controls +{ + public class BreadcrumbItemEventArgs:RoutedEventArgs + { + public BreadcrumbItemEventArgs(BreadcrumbItem item, RoutedEvent routedEvent) + : base(routedEvent) + { + Item = item; + } + + public BreadcrumbItem Item { get; private set; } + } + + public delegate void BreadcrumbItemEventHandler(object sender, BreadcrumbItemEventArgs e); + +} diff --git a/Odyssey/Odyssey/BreadcrumbBar/ImageButton.cs b/Odyssey/Odyssey/BreadcrumbBar/ImageButton.cs new file mode 100644 index 0000000..0b6e138 --- /dev/null +++ b/Odyssey/Odyssey/BreadcrumbBar/ImageButton.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Odyssey.Controls +{ + public class ImageButton : Button + { + static ImageButton() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(ImageButton), new FrameworkPropertyMetadata(typeof(ImageButton))); + } + + + + public double ImageWidth + { + get { return (double)GetValue(ImageWidthProperty); } + set { SetValue(ImageWidthProperty, value); } + } + + public static readonly DependencyProperty ImageWidthProperty = + DependencyProperty.Register("ImageWidth", typeof(double), typeof(ImageButton), new UIPropertyMetadata(16.0)); + + + + + public double ImageHeight + { + get { return (double)GetValue(ImageHeightProperty); } + set { SetValue(ImageHeightProperty, value); } + } + + public static readonly DependencyProperty ImageHeightProperty = + DependencyProperty.Register("ImageHeight", typeof(double), typeof(ImageButton), new UIPropertyMetadata(16.0)); + + + + + public ImageSource Image + { + get { return (ImageSource)GetValue(ImageProperty); } + set { SetValue(ImageProperty, value); } + } + + public static readonly DependencyProperty ImageProperty = + DependencyProperty.Register("Image", typeof(ImageSource), typeof(ImageButton), new UIPropertyMetadata(null)); + + } +} diff --git a/Odyssey/Odyssey/BreadcrumbBar/PathConversionEventArgs.cs b/Odyssey/Odyssey/BreadcrumbBar/PathConversionEventArgs.cs new file mode 100644 index 0000000..c4559ea --- /dev/null +++ b/Odyssey/Odyssey/BreadcrumbBar/PathConversionEventArgs.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; + +#region Licence +// Copyright (c) 2008 Thomas Gerber +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#endregion +namespace Odyssey.Controls +{ + /// + /// RoutedEventArgs to convert the display path to edit path and vice verca. + /// + public class PathConversionEventArgs:RoutedEventArgs + { + //TODO: Rename PathConverionMode to LogicalToVisual and VisualToLogical + /// + /// Specifies what property to convert. + /// + public enum ConversionMode + { + /// + /// Convert the display path to edit path. + /// + DisplayToEdit, + + /// + /// convert the edit path to display path. + /// + EditToDisplay, + } + + /// + /// Gets or sets the display path. + /// + public string DisplayPath { get; set; } + + /// + /// Gets or sets the edit path. + /// + public string EditPath { get; set; } + + /// + /// Specifies what path property to convert. + /// + public ConversionMode Mode { get; private set; } + + /// + /// Gets the root object of the breadcrumb bar. + /// + public object Root { get; private set; } + + /// + /// Creates a new PathConversionEventArgs class. + /// + /// The conversion mode. + /// The initial values for DisplayPath and EditPath. + /// The root object. + /// + public PathConversionEventArgs(ConversionMode mode, string path, object root, RoutedEvent routedEvent) + : base(routedEvent) + { + Mode = mode; + DisplayPath = EditPath = path; + Root = root; + } + } + + public delegate void PathConversionEventHandler(object sender, PathConversionEventArgs e); + +} diff --git a/Odyssey/Odyssey/ChangeLog.txt b/Odyssey/Odyssey/ChangeLog.txt new file mode 100644 index 0000000..91dbdf4 --- /dev/null +++ b/Odyssey/Odyssey/ChangeLog.txt @@ -0,0 +1,47 @@ +- New attached properties odc:ImageRenderOptions.LargeImageScalingMode and odc:ImageRenderOptions.SmallImageScalingMode: + Specify render options for either LargeImages or SmallImages. These properties are inheritable, so any child inherits the attached value from it's parent. + For instance. you can set to apply this option to all controls inside. + + +- fixed bug in RibbonWindow: + MeasureOverride is required to adjust the correct size, otherwhise controls inside the window do not correctly measure the size. + For instance, adding a to a RibbonWindow caused any row with RowDefinition.Height="*" to appear with a gap. + +- modified OdcExpander to show a vertical scrollbar when it is inside a control which does not allow it's height to be infinite. + +- modified OdcExdpander to correctly measure the available width. ínstead of an infinite width. this has caused controls inside an expander for instance + not to enable word wrap. + +- RibbonBar: switiching Visibility property of a RibbonTabItem now causes the RibbonBar to render again and ensure that only a visible + TabItem is set as SelectedTabItem. + + - added new RoutedEventHandler SelectedTabChanged. + + -added style and theme for TreeViewItem and ListViewItem to appear in the chosen skin. + - TreeViewItems are animated while expanding/collapsing. + + -changed duration for animations in BreadcrumbBar to smaller duration that is more eye friendly. + + - new dependency property BreadcrumbBar.PathBinding: This property is supposed to be used to bind a path to the breadcrumb instead of using + BreadcrumbBar.Path for binding. Since this value can be changed manually, the bindig would get lost. + + - new OdcTextBox control: A Textbox with optional inline buttons on the right and a style equal to the BreadcrumbBar. + - new ImageButton control: A Button control with Image property of type ImageSource to render a 16x16 button, intendet do be used with BreadcrumbBar or OdcTextBox. + + - improved handling of popups: nested popups are no longer accidenty closed. This is archived by deriving RibbonDropDownButton from MenuBase insed of MenuItem + as well as a modified handling for closing a Popup. + + -new Controls: SelectableTreeView and SelectableTreeViewItem. SelectableTreevVewItem adds a Click event and Command to a TreeViewItem as well as a IsPressed property. + - the overlay button of the application menu is now a Rectangle with a VisualBrush that renderes the original (orb) application menu button. It is now inside a Canvas and code changes + the Canvas.Left and Canvas.Top properties to be exactly over the original application menu button. + + - the blur effect of the outer elipse of the orb button caused the clone image not to be exact shape, so I removed the blur effect. + - binding to controls that are on a RibbonTab that was not the initial visible RibbonTab did not execute, so added ApplyTemplate() on EndInit() to make this work. + - RibbonBar is now derived from Selector instead of Control. + - RibbonTab is now derived from ItemsControl instead of Control. + - Optimized Disabled style: Images are now rendered in grayscale by Odyssey.Effects.Grayscale, and the text is also translucent. + - Commands for RibbonDropDownbutton and RibbonSplitButton now recognize Command.CanExecute. + - Improved positioning of window title in ribbon. + - RibbonBar.Title property has been removed, since the xaml now uses Window.Title to present the window title. + - New RibbonToolTip control + \ No newline at end of file diff --git a/Odyssey/Odyssey/Common/AeroChrome.cs b/Odyssey/Odyssey/Common/AeroChrome.cs new file mode 100644 index 0000000..51354f4 --- /dev/null +++ b/Odyssey/Odyssey/Common/AeroChrome.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Odyssey.Controls +{ + + public class AeroChrome : ContentControl + { + static AeroChrome() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(AeroChrome), new FrameworkPropertyMetadata(typeof(AeroChrome))); + } + + + public bool RenderPressed + { + get { return (bool)GetValue(RenderPressedProperty); } + set { SetValue(RenderPressedProperty, value); } + } + + public static readonly DependencyProperty RenderPressedProperty = + DependencyProperty.Register("RenderPressed", typeof(bool), typeof(AeroChrome), new UIPropertyMetadata(false)); + + + public bool RenderMouseOver + { + get { return (bool)GetValue(RenderMouseOverProperty); } + set { SetValue(RenderMouseOverProperty, value); } + } + + // Using a DependencyProperty as the backing store for RenderMouseOver. This enables animation, styling, binding, etc... + public static readonly DependencyProperty RenderMouseOverProperty = + DependencyProperty.Register("RenderMouseOver", typeof(bool), typeof(AeroChrome), new UIPropertyMetadata(false)); + + + + public Brush MouseOverBackground + { + get { return (Brush)GetValue(MouseOverBackgroundProperty); } + set { SetValue(MouseOverBackgroundProperty, value); } + } + + public static readonly DependencyProperty MouseOverBackgroundProperty = + DependencyProperty.Register("MouseOverBackground", typeof(Brush), typeof(AeroChrome), new UIPropertyMetadata(null)); + + + + public Brush MousePressedBackground + { + get { return (Brush)GetValue(MousePressedBackgroundProperty); } + set { SetValue(MousePressedBackgroundProperty, value); } + } + + // Using a DependencyProperty as the backing store for MousePressedBackground. This enables animation, styling, binding, etc... + public static readonly DependencyProperty MousePressedBackgroundProperty = + DependencyProperty.Register("MousePressedBackground", typeof(Brush), typeof(AeroChrome), new UIPropertyMetadata(null)); + + + } +} diff --git a/Odyssey/Odyssey/Common/AnimationDecorator.cs b/Odyssey/Odyssey/Common/AnimationDecorator.cs new file mode 100644 index 0000000..9da0832 --- /dev/null +++ b/Odyssey/Odyssey/Common/AnimationDecorator.cs @@ -0,0 +1,327 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Controls; +using System.Windows; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Diagnostics; + +#region Licence +// Copyright (c) 2008 Thomas Gerber +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#endregion +namespace Odyssey.Controls +{ + public class AnimationDecorator : Decorator + { + private double targetHeight = 0; + + static AnimationDecorator() + { + // AnimationDecorator.ActualHeightProperty.OverrideMetadata(typeof(AnimationDecorator), new UIPropertyMetadata((double)0.0)); + } + + public AnimationDecorator() + : base() + { + ClipToBounds = true; + } + + + /// + /// Specify whether to apply opactiy animation. + /// + public bool OpacityAnimation + { + get { return (bool)GetValue(OpacityAnimationProperty); } + set { SetValue(OpacityAnimationProperty, value); } + } + + public static readonly DependencyProperty OpacityAnimationProperty = + DependencyProperty.Register("OpacityAnimation", + typeof(bool), + typeof(AnimationDecorator), + new UIPropertyMetadata(true)); + + + + /// + /// Gets or sets whether the decorator is expanded or collapsed. + /// + public bool IsExpanded + { + get { return (bool)GetValue(IsExpandedProperty); } + set { SetValue(IsExpandedProperty, value); } + } + + public static readonly DependencyProperty IsExpandedProperty = + DependencyProperty.Register("IsExpanded", + typeof(bool), + typeof(AnimationDecorator), + new PropertyMetadata(true, IsExpandedChanged)); + + + public static void IsExpandedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + AnimationDecorator expander = d as AnimationDecorator; + bool expanded = (bool)e.NewValue; + if (expander.CanAnimate) + { + expander.AnimateExpandedChanged(expanded); + } + else + { + expander.UnanimatedExpandedChanged(expanded); + } + } + + + /// + /// Specify whether to apply animation when IsExpanded is changed. + /// + public DoubleAnimation HeightAnimation + { + get { return (DoubleAnimation)GetValue(HeightAnimationProperty); } + set { SetValue(HeightAnimationProperty, value); } + } + + public static readonly DependencyProperty HeightAnimationProperty = + DependencyProperty.Register("HeightAnimation", + typeof(DoubleAnimation), + typeof(AnimationDecorator), + new UIPropertyMetadata(null)); + + + + /// + /// Gets or sets the duration for the animation. + /// + public Duration Duration + { + get { return (Duration)GetValue(DurationProperty); } + set { SetValue(DurationProperty, value); } + } + + public static readonly DependencyProperty DurationProperty = + DependencyProperty.Register("Duration", typeof(Duration), typeof(AnimationDecorator), new UIPropertyMetadata(new Duration(new TimeSpan(0, 0, 0, 0, 250)))); + + + private bool animating = false; + + + private void UnanimatedExpandedChanged(bool expanded) + { + if (Child != null) + { + YOffset = expanded ? 0 : -Child.DesiredSize.Height; + } + } + + /// + /// Perform the animation when teh IsExpanded has changed. + /// + /// + private void AnimateExpandedChanged(bool expanded) + { + if (Child != null) + { + if (YOffset > 0) YOffset = 0; + if (-YOffset > Child.DesiredSize.Height) YOffset = -Child.DesiredSize.Height; + DoubleAnimation animation = HeightAnimation; + if (animation == null) animation = CreateDoubleAnimation(); + animation.From = null; + animation.To = expanded ? 0 : -Child.DesiredSize.Height; + animation.Completed += new EventHandler(animation_Completed); + animating = true; + this.BeginAnimation(AnimationDecorator.YOffsetProperty, animation); + + if (OpacityAnimation) + { + animation.From = null; + animation.To = expanded ? 1 : 0; + this.BeginAnimation(AnimationDecorator.AnimationOpacityProperty, animation); + } + } + else + { + YOffset = int.MinValue; + } + } + + private DoubleAnimation CreateDoubleAnimation() + { + DoubleAnimation animation = new DoubleAnimation(); + animation.DecelerationRatio = 0.8; + animation.Duration = Duration; + return animation; + } + + void animation_Completed(object sender, EventArgs e) + { + animating = false; + } + + + /// + /// Perform the animation when the child's height has changed. + /// + /// + /// + private Double AnimatedResize(Double h) + { + Double delta = targetHeight - h; + DoubleAnimation animation = HeightAnimation; + if (animation == null) animation = CreateDoubleAnimation(); + targetHeight = h; + animation.From = delta; + animation.To = 0; + this.BeginAnimation(AnimationDecorator.HeightOffsetProperty, animation); + return delta; + } + + + + + + /// + /// Gets or sets the Opacity for animation. This dependency property can be used to modify the opacity of an outer control. + /// + public double AnimationOpacity + { + get { return (double)GetValue(AnimationOpacityProperty); } + set { SetValue(AnimationOpacityProperty, value); } + } + + // Using a DependencyProperty as the backing store for AnimationOpacity. This enables animation, styling, binding, etc... + public static readonly DependencyProperty AnimationOpacityProperty = + DependencyProperty.Register("AnimationOpacity", typeof(double), typeof(AnimationDecorator), new UIPropertyMetadata((double)1.0)); + + + /// + /// A helper value for the current state while in animation. + /// + internal Double HeightOffset + { + get { return (Double)GetValue(HeightOffsetProperty); } + set { SetValue(HeightOffsetProperty, value); } + } + + internal static readonly DependencyProperty HeightOffsetProperty = + DependencyProperty.Register("HeightOffset", typeof(Double), typeof(AnimationDecorator), new FrameworkPropertyMetadata((double)0.0, + FrameworkPropertyMetadataOptions.AffectsMeasure)); + + + /// + /// A helper value for the current state while in animation. + /// + internal Double YOffset + { + get { return (Double)GetValue(YOffsetProperty); } + set { SetValue(YOffsetProperty, value); } + } + + public static readonly DependencyProperty YOffsetProperty = + DependencyProperty.Register("YOffset", typeof(Double), typeof(AnimationDecorator), + new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsMeasure)); + + /// + /// Measures the child element of a to prepare for arranging it during the pass. + /// + /// An upper limit that should not be exceeded. + /// + /// The target of the element. + /// + protected override Size MeasureOverride(Size constraint) + { + if (Child == null) return new Size(0, 0); + Size size; + + if (double.IsInfinity(constraint.Height)) + { + Child.Measure(new Size(constraint.Width, Double.PositiveInfinity)); + Double childHeight = Child.DesiredSize.Height; + Double deltaHeight = 0; + if (this.AnimateOnContentHeightChanged && this.IsLoaded && IsVisible && CanAnimate) + { + if (targetHeight != childHeight) + { + deltaHeight = AnimatedResize(childHeight); + if (animating) + { + AnimateExpandedChanged(IsExpanded); + } + } + } + else targetHeight = childHeight; + + double w = IsExpanded ? Child.DesiredSize.Width : 0; + size = new Size(w, Math.Max(0d, childHeight + YOffset + HeightOffset + deltaHeight)); + + } + else + { + size = base.MeasureOverride(constraint); + } + if (Child != null) + { + Child.IsEnabled = size.Height > 0; + } + if (size.Height == 0) this.AnimationOpacity = 0; + return size; + } + + + /// + /// Arranges the content of a element. + /// + /// The this element uses to arrange its child content. + /// + /// The that represents the arranged size of this element and its child. + /// + protected override Size ArrangeOverride(Size arrangeSize) + { + if (Child == null) return arrangeSize; + + Child.Arrange(new Rect(0d, YOffset, arrangeSize.Width, Child.DesiredSize.Height)); + Double h = Math.Max(0, Child.DesiredSize.Height + YOffset); + return new Size(arrangeSize.Width, h); + } + + + + public bool CanAnimate + { + get { return (bool)GetValue(CanAnimateProperty); } + set { SetValue(CanAnimateProperty, value); } + } + + // Using a DependencyProperty as the backing store for CanAnimate. This enables animation, styling, binding, etc... + public static readonly DependencyProperty CanAnimateProperty = + DependencyProperty.Register("CanAnimate", typeof(bool), typeof(AnimationDecorator), new UIPropertyMetadata(true)); + + + + + public bool AnimateOnContentHeightChanged + { + get { return (bool)GetValue(AnimateOnContentHeightChangedProperty); } + set { SetValue(AnimateOnContentHeightChangedProperty, value); } + } + + // Using a DependencyProperty as the backing store for AnimateOnContentHeightChanged. This enables animation, styling, binding, etc... + public static readonly DependencyProperty AnimateOnContentHeightChangedProperty = + DependencyProperty.Register("AnimateOnContentHeightChanged", typeof(bool), typeof(AnimationDecorator), new UIPropertyMetadata(true)); + + + + } +} diff --git a/Odyssey/Odyssey/Common/EmptyStringVisibilityConverter.cs b/Odyssey/Odyssey/Common/EmptyStringVisibilityConverter.cs new file mode 100644 index 0000000..45ce08e --- /dev/null +++ b/Odyssey/Odyssey/Common/EmptyStringVisibilityConverter.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Data; +using System.Windows; + +namespace Odyssey.Controls +{ + public class EmptyStringVisibilityConverter:IValueConverter + { + #region IValueConverter Members + + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + string s = value as string; + return string.IsNullOrEmpty(s) ? Visibility.Visible : Visibility.Collapsed; + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + + #endregion + } +} diff --git a/Odyssey/Odyssey/Common/IKeyTipControl.cs b/Odyssey/Odyssey/Common/IKeyTipControl.cs new file mode 100644 index 0000000..8ccd666 --- /dev/null +++ b/Odyssey/Odyssey/Common/IKeyTipControl.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Odyssey.Controls.Interfaces +{ + public interface IKeyTipControl + { + void ExecuteKeyTip(); + } +} diff --git a/Odyssey/Odyssey/Common/PopupHelper.cs b/Odyssey/Odyssey/Common/PopupHelper.cs new file mode 100644 index 0000000..0e2150b --- /dev/null +++ b/Odyssey/Odyssey/Common/PopupHelper.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Media; + +namespace Odyssey.Common +{ + public static class PopupHelper + { + public static bool IsLogicalAncestorOf(this UIElement ancestor, UIElement child) + { + if (child != null) + { + FrameworkElement obj = child as FrameworkElement; + while (obj != null) + { + FrameworkElement parent = VisualTreeHelper.GetParent(obj) as FrameworkElement; + obj = parent == null ? obj.Parent as FrameworkElement : parent as FrameworkElement; + if (obj == ancestor) return true; + } + } + return false; + } + } +} diff --git a/Odyssey/Odyssey/Common/SkinId.cs b/Odyssey/Odyssey/Common/SkinId.cs new file mode 100644 index 0000000..3ed3019 --- /dev/null +++ b/Odyssey/Odyssey/Common/SkinId.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls.Classes +{ + public enum SkinId + { + OfficeBlue, + OfficeSilver, + OfficeBlack, + Windows7, + Vista + } +} diff --git a/Odyssey/Odyssey/Common/SkinManager.cs b/Odyssey/Odyssey/Common/SkinManager.cs new file mode 100644 index 0000000..d5d479f --- /dev/null +++ b/Odyssey/Odyssey/Common/SkinManager.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls.Classes +{ + public static class SkinManager + { + private static SkinId skinId = SkinId.OfficeBlue; + + public static event EventHandler SkinChanged; + + public static SkinId SkinId + { + get { return skinId; } + set + { + if (skinId != value) + { + skinId = value; + ApplySkin(value); + } + } + } + + private static void ApplySkin(SkinId skin) + { + var dict = Application.Current.Resources.MergedDictionaries; + dict.Remove(OfficeBlack); + dict.Remove(OfficeSilver); + dict.Remove(WindowsSeven); + dict.Remove(Vista); + switch (skin) + { + case SkinId.OfficeBlack: + dict.Add(OfficeBlack); + break; + + case SkinId.OfficeSilver: + dict.Add(OfficeSilver); + break; + + case SkinId.Windows7: + dict.Add(WindowsSeven); + break; + + case SkinId.Vista: + dict.Add(Vista); + break; + } + OnSkinChanged(); + } + + private static void OnSkinChanged() + { + if (SkinChanged != null) SkinChanged(null, EventArgs.Empty); + } + + + + private static ResourceDictionary officeSilver; + public static ResourceDictionary OfficeSilver + { + get + { + if (officeSilver == null) + { + ResourceDictionary dictionary = new ResourceDictionary(); + dictionary.Source = new Uri("/Odyssey;Component/Skins/SilverSkin.xaml", UriKind.Relative); + officeSilver = dictionary; + } + return officeSilver; + } + } + + private static ResourceDictionary officeBlack; + public static ResourceDictionary OfficeBlack + { + get + { + if (officeBlack == null) + { + ResourceDictionary dictionary = new ResourceDictionary(); + dictionary.Source = new Uri("/Odyssey;Component/Skins/BlackSkin.xaml", UriKind.Relative); + officeBlack = dictionary; + } + return officeBlack; + } + } + + private static ResourceDictionary windowSeven; + public static ResourceDictionary WindowsSeven + { + get + { + if (windowSeven == null) + { + ResourceDictionary dictionary = new ResourceDictionary(); + dictionary.Source = new Uri("/Odyssey;Component/Skins/Win7Skin.xaml", UriKind.Relative); + windowSeven = dictionary; + } + return windowSeven; + } + } + + private static ResourceDictionary vista; + public static ResourceDictionary Vista + { + get + { + if (vista == null) + { + ResourceDictionary dictionary = new ResourceDictionary(); + dictionary.Source = new Uri("/Odyssey;Component/Skins/VistaSkin.xaml", UriKind.Relative); + vista = dictionary; + } + return vista; + } + } + + } +} diff --git a/Odyssey/Odyssey/Common/Skins.cs b/Odyssey/Odyssey/Common/Skins.cs new file mode 100644 index 0000000..0eaf1d0 --- /dev/null +++ b/Odyssey/Odyssey/Common/Skins.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + public static class Skins + { + } +} diff --git a/Odyssey/Odyssey/Controls/ClickableTreeView.cs b/Odyssey/Odyssey/Controls/ClickableTreeView.cs new file mode 100644 index 0000000..ea6f945 --- /dev/null +++ b/Odyssey/Odyssey/Controls/ClickableTreeView.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Controls; + +namespace Odyssey.Controls +{ + public class ClickableTreeView:TreeView + { + + protected override bool IsItemItsOwnContainerOverride(object item) + { + return (item is Separator) || (item is TreeViewItem); + } + + protected override System.Windows.DependencyObject GetContainerForItemOverride() + { + return new ClickableTreeViewItem(); + } + } +} diff --git a/Odyssey/Odyssey/Controls/ClickableTreeViewItem.cs b/Odyssey/Odyssey/Controls/ClickableTreeViewItem.cs new file mode 100644 index 0000000..77155a7 --- /dev/null +++ b/Odyssey/Odyssey/Controls/ClickableTreeViewItem.cs @@ -0,0 +1,240 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Controls; +using System.Windows; +using System.Windows.Input; +using System.Security; + +namespace Odyssey.Controls +{ + /// + /// A TreeViewItem that adds support for Click events and Commands. + /// + public class ClickableTreeViewItem : TreeViewItem, ICommandSource + { + static ClickableTreeViewItem() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(ClickableTreeViewItem), new FrameworkPropertyMetadata(typeof(ClickableTreeViewItem))); + } + + protected override DependencyObject GetContainerForItemOverride() + { + return new ClickableTreeViewItem(); + } + + protected override void OnKeyDown(KeyEventArgs e) + { + switch (e.Key) + { + case Key.Enter: + case Key.Space: + if (ClickMode == ClickMode.Press) + { + OnClick(); + } + else + { + IsPressed = true; + } + e.Handled = true; + break; + } + base.OnKeyDown(e); + } + + protected override void OnKeyUp(KeyEventArgs e) + { + switch (e.Key) + { + case Key.Enter: + case Key.Space: + IsPressed = false; + if (ClickMode == ClickMode.Release) + { + OnClick(); + e.Handled = true; + } + break; + } base.OnKeyUp(e); + } + + + + public ClickMode ClickMode + { + get { return (ClickMode)GetValue(ClickModeProperty); } + set { SetValue(ClickModeProperty, value); } + } + + public static readonly DependencyProperty ClickModeProperty = + DependencyProperty.Register("ClickMode", typeof(ClickMode), typeof(ClickableTreeViewItem), new UIPropertyMetadata(ClickMode.Release)); + + + protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) + { + base.OnMouseLeftButtonDown(e); + base.Focus(); + IsPressed = true; + + if (ClickMode == ClickMode.Press) + { + bool flag = true; + try + { + OnClick(); + flag = false; + } + finally + { + if (flag) + { + IsPressed = false; + base.ReleaseMouseCapture(); + } + } + } + } + + bool IsSpaceKeyDown + { + get + { + return Keyboard.IsKeyDown(Key.Space); + } + } + + bool wasPressed = false; + + protected override void OnMouseLeave(MouseEventArgs e) + { + wasPressed = (e.LeftButton == MouseButtonState.Pressed && IsPressed); + base.OnMouseLeave(e); + } + + protected override void OnMouseEnter(MouseEventArgs e) + { + if (e.LeftButton == MouseButtonState.Pressed && wasPressed) + { + IsPressed = true; + } + else if (wasPressed) + { + IsPressed = false; + wasPressed = false; + } + base.OnMouseEnter(e); + } + + + + protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) + { + base.OnMouseLeftButtonUp(e); + bool flag = (!IsSpaceKeyDown && IsPressed) && (this.ClickMode == ClickMode.Release); + if (base.IsMouseCaptured && !IsSpaceKeyDown) + { + base.ReleaseMouseCapture(); + } + if (flag) + { + OnClick(); + } + wasPressed = false; + IsPressed = false; + } + + + + /// + /// Gets whether the ClickableTreeViewItem is pressed. + /// + public bool IsPressed + { + get { return (bool)GetValue(IsPressedProperty); } + private set { SetValue(IsPressedPropertyKey, value); } + } + + private static readonly DependencyPropertyKey IsPressedPropertyKey = + DependencyProperty.RegisterReadOnly("IsPressed", typeof(bool), typeof(ClickableTreeViewItem), new UIPropertyMetadata(false)); + + public static readonly DependencyProperty IsPressedProperty = IsPressedPropertyKey.DependencyProperty; + + + + [SecurityTreatAsSafe, SecurityCritical] + protected virtual void OnClick() + { + OnClickImpl(false); + } + + public event RoutedEventHandler Click + { + add { AddHandler(ClickEvent, value); } + remove { RemoveHandler(ClickEvent, value); } + } + + [SecurityCritical] + private void OnClickImpl(bool userInitiated) + { + RoutedEventArgs args = new RoutedEventArgs(Button.ClickEvent, this); + if (Command != null) + { + if (Command.CanExecute(CommandParameter)) + { + Command.Execute(CommandParameter); + } + } + RaiseEvent(args); + } + + #region events + + public static readonly RoutedEvent ClickEvent = EventManager.RegisterRoutedEvent("ClickEvent", + RoutingStrategy.Bubble, typeof(RoutedEventArgs), typeof(Button)); + #endregion + + + #region ICommandSource Members + + + + public ICommand Command + { + get { return (ICommand)GetValue(CommandProperty); } + set { SetValue(CommandProperty, value); } + } + + public static readonly DependencyProperty CommandProperty = + DependencyProperty.Register("Command", typeof(ICommand), typeof(ClickableTreeViewItem), new UIPropertyMetadata(null)); + + + + + public object CommandParameter + { + get { return (object)GetValue(CommandParameterProperty); } + set { SetValue(CommandParameterProperty, value); } + } + + public static readonly DependencyProperty CommandParameterProperty = + DependencyProperty.Register("CommandParameter", typeof(object), typeof(ClickableTreeViewItem), new UIPropertyMetadata(null)); + + + + + public IInputElement CommandTarget + { + get { return (IInputElement)GetValue(CommandTargetProperty); } + set { SetValue(CommandTargetProperty, value); } + } + + public static readonly DependencyProperty CommandTargetProperty = + DependencyProperty.Register("CommandTarget", typeof(IInputElement), typeof(ClickableTreeViewItem), new UIPropertyMetadata(null)); + + + + #endregion + } +} diff --git a/Odyssey/Odyssey/Controls/DropDownButton.cs b/Odyssey/Odyssey/Controls/DropDownButton.cs new file mode 100644 index 0000000..0240fcd --- /dev/null +++ b/Odyssey/Odyssey/Controls/DropDownButton.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using System.Windows.Markup; + +namespace Odyssey.Controls +{ + + [TemplatePart(Name = "PART_Popup")] + [ContentProperty("Content")] + public class DropDownButton : RibbonSplitButton + { + static DropDownButton() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(DropDownButton), new FrameworkPropertyMetadata(typeof(DropDownButton))); + } + + public DropDownButton() + : base() + { + AddHandler(ClickableTreeViewItem.ClickEvent, new RoutedEventHandler(OnMenuItemClickedEvent)); + + } + + protected override UIElement PlacementTarget + { + get + { + return DropDownButton; + } + } + + + /// + /// Gets or sets the content in the dropdown button area. + /// + public object DropDownButtonContent + { + get { return (object)GetValue(DropDownButtonContentProperty); } + set { SetValue(DropDownButtonContentProperty, value); } + } + + public static readonly DependencyProperty DropDownButtonContentProperty = + DependencyProperty.Register("DropDownButtonContent", typeof(object), typeof(DropDownButton), new UIPropertyMetadata(null)); + + } +} diff --git a/Odyssey/Odyssey/Controls/OdcButton.cs b/Odyssey/Odyssey/Controls/OdcButton.cs new file mode 100644 index 0000000..4a53c1b --- /dev/null +++ b/Odyssey/Odyssey/Controls/OdcButton.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Controls; +using Odyssey.Controls.Interfaces; +using System.Windows.Controls.Primitives; + +namespace Odyssey.Controls +{ + /// + /// Derived Button that implements IKeyTipControl. + /// + public class OdcButton:Button,IKeyTipControl + { + #region IKeyTipControl Members + + void IKeyTipControl.ExecuteKeyTip() + { + OnClick(); + } + + #endregion + } + + /// + /// Derived ToggleButon that implements IKeyTipControl. + /// + public class OdcToggleButton:ToggleButton,IKeyTipControl + { + #region IKeyTipControl Members + + void IKeyTipControl.ExecuteKeyTip() + { + OnClick(); + } + + #endregion + } +} diff --git a/Odyssey/Odyssey/Controls/OdcTextBox.cs b/Odyssey/Odyssey/Controls/OdcTextBox.cs new file mode 100644 index 0000000..c8902c5 --- /dev/null +++ b/Odyssey/Odyssey/Controls/OdcTextBox.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using System.Collections.ObjectModel; +using System.Windows.Controls.Primitives; +using System.Diagnostics; +using Odyssey.Controls.Interfaces; + +namespace Odyssey.Controls +{ + + /// + /// A TextBox that contains buttons on the right. + /// + [TemplatePart(Name = partTextBox)] + public class OdcTextBox : TextBox,IKeyTipControl + { + private const string partTextBox = "PART_TextBox"; + + static OdcTextBox() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(OdcTextBox), new FrameworkPropertyMetadata(typeof(OdcTextBox))); + UIElement.FocusableProperty.OverrideMetadata(typeof(OdcTextBox),new FrameworkPropertyMetadata(true)); + KeyboardNavigation.TabNavigationProperty.OverrideMetadata(typeof(OdcTextBox), new FrameworkPropertyMetadata(KeyboardNavigationMode.Local)); + KeyboardNavigation.ControlTabNavigationProperty.OverrideMetadata(typeof(OdcTextBox), new FrameworkPropertyMetadata(KeyboardNavigationMode.None)); + KeyboardNavigation.DirectionalNavigationProperty.OverrideMetadata(typeof(OdcTextBox), new FrameworkPropertyMetadata(KeyboardNavigationMode.None)); + } + + + + + private ObservableCollection buttons = new ObservableCollection(); + + /// + /// Gets the collection of buttons to appear on the right of the OdcTextBox. + /// + public ObservableCollection Buttons + { + get { return buttons; } + } + + + /// + /// Gets or sets the text that appears when the textbox is empty. + /// This is a dependency property. + /// + public string Info + { + get { return (string)GetValue(InfoProperty); } + set { SetValue(InfoProperty, value); } + } + + public static readonly DependencyProperty InfoProperty = + DependencyProperty.Register("Info", typeof(string), typeof(OdcTextBox), new UIPropertyMetadata("")); + + + + #region IKeyTipControl Members + + void IKeyTipControl.ExecuteKeyTip() + { + Focus(); + SelectAll(); + } + + #endregion + } +} diff --git a/Odyssey/Odyssey/Effects/GrayscaleEffect.cs b/Odyssey/Odyssey/Effects/GrayscaleEffect.cs new file mode 100644 index 0000000..79741f9 --- /dev/null +++ b/Odyssey/Odyssey/Effects/GrayscaleEffect.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Media.Effects; +using System.Windows; +using System.Windows.Media; + +namespace Odyssey.Effects +{ + /// + /// Source from + /// http://bursjootech.blogspot.com/2008/06/grayscale-effect-pixel-shader-effect-in.html + /// + public class GrayscaleEffect : ShaderEffect + { + private static PixelShader pixelShader = new PixelShader() { UriSource = new Uri(@"pack://application:,,,/Odyssey;Component/Effects/GrayScaleEffect.ps") }; + + public GrayscaleEffect() + { + PixelShader = pixelShader; + UpdateShaderValue(InputProperty); + UpdateShaderValue(DesaturationFactorProperty); + } + + public static readonly DependencyProperty InputProperty = ShaderEffect.RegisterPixelShaderSamplerProperty("Input", typeof(GrayscaleEffect), 0); + + public Brush Input + { + get { return (Brush)GetValue(InputProperty); } + set { SetValue(InputProperty, value); } + } + + public static readonly DependencyProperty DesaturationFactorProperty = DependencyProperty.Register("DesaturationFactor", + typeof(double), typeof(GrayscaleEffect), new UIPropertyMetadata(0.0, PixelShaderConstantCallback(0), CoerceDesaturationFactor)); + + public double DesaturationFactor + { + get { return (double)GetValue(DesaturationFactorProperty); } + set { SetValue(DesaturationFactorProperty, value); } + } + + private static object CoerceDesaturationFactor(DependencyObject d, object value) + { + GrayscaleEffect effect = (GrayscaleEffect)d; + double newFactor = (double)value; + if (newFactor < 0.0 || newFactor > 1.0) + { + return effect.DesaturationFactor; + } + return newFactor; + } + } +} + diff --git a/Odyssey/Odyssey/Effects/GrayscaleEffect.fx b/Odyssey/Odyssey/Effects/GrayscaleEffect.fx new file mode 100644 index 0000000..5be422b --- /dev/null +++ b/Odyssey/Odyssey/Effects/GrayscaleEffect.fx @@ -0,0 +1,16 @@ +sampler2D implicitInput : register(s0); +float factor : register(c0); + +float4 main(float2 uv : TEXCOORD) : COLOR +{ + float4 color = tex2D(implicitInput, uv); + float gray = color.r * 0.3 + color.g * 0.59 + color.b *0.11; + + float4 result; + result.r = (color.r - gray) * factor + gray; + result.g = (color.g - gray) * factor + gray; + result.b = (color.b - gray) * factor + gray; + result.a = color.a; + + return result; +} \ No newline at end of file diff --git a/Odyssey/Odyssey/Effects/GrayscaleEffect.ps b/Odyssey/Odyssey/Effects/GrayscaleEffect.ps new file mode 100644 index 0000000..80910e4 Binary files /dev/null and b/Odyssey/Odyssey/Effects/GrayscaleEffect.ps differ diff --git a/Odyssey/Odyssey/ExplorerBar/ExplorerBar.cs b/Odyssey/Odyssey/ExplorerBar/ExplorerBar.cs new file mode 100644 index 0000000..e8aa326 --- /dev/null +++ b/Odyssey/Odyssey/ExplorerBar/ExplorerBar.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +#region Licence +// Copyright (c) 2008 Thomas Gerber +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#endregion +namespace Odyssey.Controls +{ + public class ExplorerBar : ItemsControl + { + static ExplorerBar() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(ExplorerBar), new FrameworkPropertyMetadata(typeof(ExplorerBar))); + } + } +} diff --git a/Odyssey/Odyssey/ExplorerBar/OdcExpander.cs b/Odyssey/Odyssey/ExplorerBar/OdcExpander.cs new file mode 100644 index 0000000..28c3ce2 --- /dev/null +++ b/Odyssey/Odyssey/ExplorerBar/OdcExpander.cs @@ -0,0 +1,348 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using System.Windows.Markup; +using System.IO; +using System.Xml; +using Odyssey.Controls.Interfaces; + +#region Licence +// Copyright (c) 2008 Thomas Gerber +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#endregion +namespace Odyssey.Controls +{ + /// + /// An Expander with animation. + /// + public class OdcExpander : HeaderedContentControl,IKeyTipControl + { + static OdcExpander() + { + MarginProperty.OverrideMetadata( + typeof(OdcExpander), + new FrameworkPropertyMetadata(new Thickness(10, 10, 10, 2))); + + FocusableProperty.OverrideMetadata(typeof(OdcExpander), + new FrameworkPropertyMetadata(false)); + + DefaultStyleKeyProperty.OverrideMetadata(typeof(OdcExpander), + new FrameworkPropertyMetadata(typeof(OdcExpander))); + } + + /// + /// Gets or sets the custom skin for the control. + /// + public static string Skin { get; set; } + + + protected override void OnInitialized(EventArgs e) + { + base.OnInitialized(e); + ApplySkin(); + } + + public void ApplySkin() + { + if (!string.IsNullOrEmpty(Skin)) + { + Uri uri = new Uri(Skin, UriKind.Absolute); + ResourceDictionary skin = new ResourceDictionary(); + skin.Source = uri; + this.Resources = skin; + } + } + + public Brush HeaderBorderBrush + { + get { return (Brush)GetValue(HeaderBorderBrushProperty); } + set { SetValue(HeaderBorderBrushProperty, value); } + } + + public static readonly DependencyProperty HeaderBorderBrushProperty = + DependencyProperty.Register("HeaderBorderBrush", + typeof(Brush), typeof(OdcExpander), new UIPropertyMetadata(Brushes.Gray)); + + public Brush HeaderBackground + { + get { return (Brush)GetValue(HeaderBackgroundProperty); } + set { SetValue(HeaderBackgroundProperty, value); } + } + + public static readonly DependencyProperty HeaderBackgroundProperty = + DependencyProperty.Register("HeaderBackground", + typeof(Brush), typeof(OdcExpander), new UIPropertyMetadata(Brushes.Silver)); + + + public bool IsMinimized + { + get { return (bool)GetValue(IsMinimizedProperty); } + set { SetValue(IsMinimizedProperty, value); } + } + + public static readonly DependencyProperty IsMinimizedProperty = + DependencyProperty.Register("IsMinimized", + typeof(bool), typeof(OdcExpander), + new UIPropertyMetadata(false, OnMinimizedPropertyChanged)); + + public static void OnMinimizedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + bool minimized = (bool)e.NewValue; + OdcExpander expander = d as OdcExpander; + RoutedEventArgs args = new RoutedEventArgs(minimized ? MinimizedEvent : MaximizedEvent); + expander.IsEnabled = !minimized; + expander.RaiseEvent(args); + } + + + /// + /// Gets or sets the ImageSource for the image in the header. + /// + public ImageSource Image + { + get { return (ImageSource)GetValue(ImageProperty); } + set { SetValue(ImageProperty, value); } + } + + public static readonly DependencyProperty ImageProperty = + DependencyProperty.Register("Image", typeof(ImageSource), typeof(OdcExpander), new UIPropertyMetadata(null)); + + + + public bool IsExpanded + { + get { return (bool)GetValue(IsExpandedProperty); } + set { SetValue(IsExpandedProperty, value); } + } + + public event RoutedEventHandler Expanded + { + add { AddHandler(ExpandedEvent, value); } + remove { RemoveHandler(ExpandedEvent, value); } + } + + public event RoutedEventHandler Collapsed + { + add { AddHandler(CollapsedEvent, value); } + remove { RemoveHandler(CollapsedEvent, value); } + } + + public event RoutedEventHandler Minimized + { + add { AddHandler(MinimizedEvent, value); } + remove { RemoveHandler(MinimizedEvent, value); } + } + + public event RoutedEventHandler Maximized + { + add { AddHandler(MaximizedEvent, value); } + remove { RemoveHandler(MaximizedEvent, value); } + } + + #region dependency properties and routed events definition + + public static readonly DependencyProperty IsExpandedProperty = + DependencyProperty.Register( + "IsExpanded", + typeof(bool), + typeof(OdcExpander), + new UIPropertyMetadata(true, IsExpandedChanged)); + + + public static void IsExpandedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + OdcExpander expander = d as OdcExpander; + RoutedEventArgs args = new RoutedEventArgs((bool)e.NewValue ? ExpandedEvent : CollapsedEvent); + expander.RaiseEvent(args); + } + + public static readonly RoutedEvent ExpandedEvent = EventManager.RegisterRoutedEvent( + "ExpandedEvent", + RoutingStrategy.Bubble, + typeof(RoutedEventHandler), + typeof(OdcExpander)); + + + public static readonly RoutedEvent CollapsedEvent = EventManager.RegisterRoutedEvent( + "CollapsedEvent", + RoutingStrategy.Bubble, + typeof(RoutedEventHandler), + typeof(OdcExpander)); + + public static readonly RoutedEvent MinimizedEvent = EventManager.RegisterRoutedEvent( + "MinimizedEvent", + RoutingStrategy.Bubble, + typeof(RoutedEventHandler), + typeof(OdcExpander)); + + + public static readonly RoutedEvent MaximizedEvent = EventManager.RegisterRoutedEvent( + "MaximizedEvent", + RoutingStrategy.Bubble, + typeof(RoutedEventHandler), + typeof(OdcExpander)); + + #endregion + + + /// + /// Gets or sets the corner radius for the header. + /// + public CornerRadius CornerRadius + { + get { return (CornerRadius)GetValue(CornerRadiusProperty); } + set { SetValue(CornerRadiusProperty, value); } + } + + public static readonly DependencyProperty CornerRadiusProperty = + DependencyProperty.Register("CornerRadius", typeof(CornerRadius), typeof(OdcExpander), new UIPropertyMetadata(null)); + + + + /// + /// Gets or sets the background color of the header on mouse over. + /// + public Brush MouseOverHeaderBackground + { + get { return (Brush)GetValue(MouseOverHeaderBackgroundProperty); } + set { SetValue(MouseOverHeaderBackgroundProperty, value); } + } + + public static readonly DependencyProperty MouseOverHeaderBackgroundProperty = + DependencyProperty.Register("MouseOverHeaderBackground", typeof(Brush), typeof(OdcExpander), new UIPropertyMetadata(null)); + + + /// + /// Gets whether the PressedBackground is not null. + /// + public bool HasPressedBackground + { + get { return (bool)GetValue(HasPressedBackgroundProperty); } + set { SetValue(HasPressedBackgroundProperty, value); } + } + + public static readonly DependencyProperty HasPressedBackgroundProperty = + DependencyProperty.Register("HasPressedBackground", typeof(bool), typeof(OdcExpander), new UIPropertyMetadata(false)); + + + + /// + /// Gets or sets the background color of the header in pressed mode. + /// + public Brush PressedHeaderBackground + { + get { return (Brush)GetValue(PressedHeaderBackgroundProperty); } + set { SetValue(PressedHeaderBackgroundProperty, value); } + } + + public static readonly DependencyProperty PressedHeaderBackgroundProperty = + DependencyProperty.Register("PressedHeaderBackground", typeof(Brush), typeof(OdcExpander), + new UIPropertyMetadata(null, PressedHeaderBackgroundPropertyChangedCallback)); + + + public static void PressedHeaderBackgroundPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + OdcExpander expander = (OdcExpander)d; + expander.HasPressedBackground = e.NewValue != null; + } + + public Thickness HeaderBorderThickness + { + get { return (Thickness)GetValue(HeaderBorderThicknessProperty); } + set { SetValue(HeaderBorderThicknessProperty, value); } + } + + // Using a DependencyProperty as the backing store for HeaderBorderThickness. This enables animation, styling, binding, etc... + public static readonly DependencyProperty HeaderBorderThicknessProperty = + DependencyProperty.Register("HeaderBorderThickness", typeof(Thickness), typeof(OdcExpander), new UIPropertyMetadata(null)); + + + + + /// + /// Gets or sets the foreground color of the header on mouse over. + /// + public Brush MouseOverHeaderForeground + { + get { return (Brush)GetValue(MouseOverHeaderForegroundProperty); } + set { SetValue(MouseOverHeaderForegroundProperty, value); } + } + + public static readonly DependencyProperty MouseOverHeaderForegroundProperty = + DependencyProperty.Register("MouseOverHeaderForeground", typeof(Brush), typeof(OdcExpander), new UIPropertyMetadata(null)); + + + + /// + /// Specifies whether to show a elipse with the expanded/collapsed image. + /// + public bool ShowEllipse + { + get { return (bool)GetValue(ShowEllipseProperty); } + set { SetValue(ShowEllipseProperty, value); } + } + + public static readonly DependencyProperty ShowEllipseProperty = + DependencyProperty.Register("ShowEllipse", typeof(bool), typeof(OdcExpander), new UIPropertyMetadata(false)); + + + + + /// + /// Gets or sets whether animation is possible + /// + public bool CanAnimate + { + get { return (bool)GetValue(CanAnimateProperty); } + set { SetValue(CanAnimateProperty, value); } + } + + public static readonly DependencyProperty CanAnimateProperty = + DependencyProperty.Register("CanAnimate", typeof(bool), typeof(OdcExpander), new UIPropertyMetadata(true)); + + + + + public bool IsHeaderVisible + { + get { return (bool)GetValue(IsHeaderVisibleProperty); } + set { SetValue(IsHeaderVisibleProperty, value); } + } + + public static readonly DependencyProperty IsHeaderVisibleProperty = + DependencyProperty.Register("IsHeaderVisible", typeof(bool), typeof(OdcExpander), new UIPropertyMetadata(true)); + + + + #region IKeyTipControl Members + + void IKeyTipControl.ExecuteKeyTip() + { + this.IsExpanded ^= true; + if (this.IsExpanded) + { + FrameworkElement e = Content as FrameworkElement; + if (e != null && e.Focusable) e.Focus(); + } + } + + #endregion + } +} diff --git a/Odyssey/Odyssey/ExplorerBar/OdcExpanderHeader.cs b/Odyssey/Odyssey/ExplorerBar/OdcExpanderHeader.cs new file mode 100644 index 0000000..44ef645 --- /dev/null +++ b/Odyssey/Odyssey/ExplorerBar/OdcExpanderHeader.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using System.Windows.Controls.Primitives; + +#region Licence +// Copyright (c) 2008 Thomas Gerber +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#endregion +namespace Odyssey.Controls +{ + /// + /// A helper class to specify the header of an OdcExpander. + /// + internal class OdcExpanderHeader : ToggleButton + { + static OdcExpanderHeader() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(OdcExpanderHeader), new FrameworkPropertyMetadata(typeof(OdcExpanderHeader))); + } + + + /// + /// Gets whether the expand geometry is not null. + /// + public bool HasExpandGeometry + { + get { return (bool)GetValue(HasExpandGeometryProperty); } + set { SetValue(HasExpandGeometryProperty, value); } + } + + public static readonly DependencyProperty HasExpandGeometryProperty = + DependencyProperty.Register("HasExpandGeometry", typeof(bool), typeof(OdcExpanderHeader), new UIPropertyMetadata(false)); + + + + /// + /// Gets or sets the geometry for the collapse symbol. + /// + public Geometry CollapseGeometry + { + get { return (Geometry)GetValue(CollapseGeometryProperty); } + set { SetValue(CollapseGeometryProperty, value); } + } + + public static readonly DependencyProperty CollapseGeometryProperty = + DependencyProperty.Register("CollapseGeometry", typeof(Geometry), typeof(OdcExpanderHeader), + new UIPropertyMetadata(null)); + + + public static void CollapseGeometryChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + OdcExpanderHeader eh = d as OdcExpanderHeader; + eh.HasExpandGeometry = e.NewValue != null; + } + + + /// + /// Gets or sets the geometry for the expand symbol. + /// + public Geometry ExpandGeometry + { + get { return (Geometry)GetValue(ExpandGeometryProperty); } + set { SetValue(ExpandGeometryProperty, value); } + } + + public static readonly DependencyProperty ExpandGeometryProperty = + DependencyProperty.Register("ExpandGeometry", typeof(Geometry), typeof(OdcExpanderHeader), new UIPropertyMetadata(null, CollapseGeometryChangedCallback)); + + + + /// + /// Gets or sets the corner radius for the header. + /// + public CornerRadius CornerRadius + { + get { return (CornerRadius)GetValue(CornerRadiusProperty); } + set { SetValue(CornerRadiusProperty, value); } + } + + // Using a DependencyProperty as the backing store for CornerRadius. This enables animation, styling, binding, etc... + public static readonly DependencyProperty CornerRadiusProperty = + DependencyProperty.Register("CornerRadius", typeof(CornerRadius), typeof(OdcExpanderHeader), new UIPropertyMetadata(null)); + + + + /// + /// Gets or sets whether to display the ellipse arround the collapse/expand symbol. + /// + public bool ShowEllipse + { + get { return (bool)GetValue(ShowEllipseProperty); } + set { SetValue(ShowEllipseProperty, value); } + } + + // Using a DependencyProperty as the backing store for ShowEllipse. This enables animation, styling, binding, etc... + public static readonly DependencyProperty ShowEllipseProperty = + DependencyProperty.Register("ShowEllipse", typeof(bool), typeof(OdcExpanderHeader), new UIPropertyMetadata(true)); + + + + /// + /// Gets or sets the Image to display on the header. + /// + public ImageSource Image + { + get { return (ImageSource)GetValue(ImageProperty); } + set { SetValue(ImageProperty, value); } + } + + // Using a DependencyProperty as the backing store for Image. This enables animation, styling, binding, etc... + public static readonly DependencyProperty ImageProperty = + DependencyProperty.Register("Image", typeof(ImageSource), typeof(OdcExpanderHeader), new UIPropertyMetadata(null)); + + } +} diff --git a/Odyssey/Odyssey/Odyssey.csproj b/Odyssey/Odyssey/Odyssey.csproj new file mode 100644 index 0000000..558fea2 --- /dev/null +++ b/Odyssey/Odyssey/Odyssey.csproj @@ -0,0 +1,725 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {333FDC55-6B47-4A64-A2DF-A4C5823FAC74} + Library + Properties + Odyssey + Odyssey + v3.5 + 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 4 + SAK + SAK + SAK + SAK + + + + + + + 4.0 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + AllRules.ruleset + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + AllRules.ruleset + + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + true + GlobalSuppressions.cs + prompt + AllRules.ruleset + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + true + GlobalSuppressions.cs + prompt + AllRules.ruleset + + + + 3.0 + + + + 3.5 + + + + + 3.0 + + + 3.0 + + + 3.0 + + + 3.0 + + + + + Designer + MSBuild:Compile + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + MSBuild:Compile + Designer + + + + + + + + + + + + + + + + + + + + + + + + + Code + + + True + True + Resources.resx + + + True + Settings.settings + True + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + + + + + + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/OutlookBar/ExpandPosition.cs b/Odyssey/Odyssey/OutlookBar/ExpandPosition.cs new file mode 100644 index 0000000..3ce2882 --- /dev/null +++ b/Odyssey/Odyssey/OutlookBar/ExpandPosition.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Odyssey.Controls +{ + public enum ExpandPosition + { + Left, + Right + } +} diff --git a/Odyssey/Odyssey/OutlookBar/OutlookBar.cs b/Odyssey/Odyssey/OutlookBar/OutlookBar.cs new file mode 100644 index 0000000..29204cf --- /dev/null +++ b/Odyssey/Odyssey/OutlookBar/OutlookBar.cs @@ -0,0 +1,973 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using System.Collections.ObjectModel; +using System.Windows.Controls.Primitives; +using System.Diagnostics; +using System.Collections; +using System.ComponentModel; +using Odyssey.Controls.Interfaces; + +#region changelog +// Version 1.3.17 +// +// - do not catch the SizeChanged event in design mode: +// suggested by maze1610 http://www.codeplex.com/odyssey/WorkItem/View.aspx?WorkItemId=1074 +// - renamed Property Dock to DockPosition. +// suggested by maze1610 http://www.codeplex.com/odyssey/WorkItem/View.aspx?WorkItemId=1075 +// thanx for your help. +#endregion +namespace Odyssey.Controls +{ + //UNDONE: Section.Content sometimes not visible when IsMaximized has changed. + [TemplatePart(Name = partMinimizedButtonContainer)] + public class OutlookBar : HeaderedItemsControl,IKeyTipControl + { + const string partMinimizedButtonContainer = "PART_MinimizedContainer"; + const string partPopup = "PART_Popup"; + static OutlookBar() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(OutlookBar), new FrameworkPropertyMetadata(typeof(OutlookBar))); + } + + private Collection maximizedSections; + private Collection minimizedSections; + private FrameworkElement minimizedButtonContainer; + + + public OutlookBar() + : base() + { + overflowMenu = new Collection(); + SetValue(OutlookBar.OverflowMenuItemsPropertyKey, overflowMenu); + SetValue(OutlookBar.OptionButtonsPropertyKey, new Collection()); + + CommandBindings.Add(new CommandBinding(CollapseCommand, CollapseCommandExecuted)); + CommandBindings.Add(new CommandBinding(StartDraggingCommand, StartDraggingCommandExecuted)); + CommandBindings.Add(new CommandBinding(ShowPopupCommand, ShowPopupCommandExecuted)); + CommandBindings.Add(new CommandBinding(ResizeCommand, ResizeCommandExecuted)); + CommandBindings.Add(new CommandBinding(CloseCommand, CloseCommandExecuted)); + + maximizedSections = new Collection(); + minimizedSections = new Collection(); + sections = new ObservableCollection(); + sections.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(SectionsCollectionChanged); + + // do not catch the SizeChanged event in design mode: + // suggested by maze1610 http://www.codeplex.com/odyssey/WorkItem/View.aspx?WorkItemId=1074: + + if (!DesignerProperties.GetIsInDesignMode(this)) + { + this.SizeChanged += new SizeChangedEventHandler(OutlookBar_SizeChanged); + } + } + + void OutlookBar_SizeChanged(object sender, SizeChangedEventArgs e) + { + ApplySections(); + } + + + + private void CollapseCommandExecuted(object sender, ExecutedRoutedEventArgs e) + { + IsMaximized ^= true; + } + + private void ShowPopupCommandExecuted(object sender, ExecutedRoutedEventArgs e) + { + if (!IsMaximized) + { + IsPopupVisible = true; + } + } + + private void ResizeCommandExecuted(object sender, ExecutedRoutedEventArgs e) + { + Control c = e.OriginalSource as Control; + if (c != null) + { + c.PreviewMouseLeftButtonUp += new MouseButtonEventHandler(DragMouseLeftButtonUp); + } + this.PreviewMouseMove += new MouseEventHandler(PreviewMouseMoveResize); + } + + private void CloseCommandExecuted(object sender, ExecutedRoutedEventArgs e) + { + this.Visibility = Visibility.Collapsed; + } + + void PreviewMouseMoveResize(object sender, MouseEventArgs e) + { + Control c = e.OriginalSource as Control; + if (e.LeftButton == MouseButtonState.Pressed) + { + if (DockPosition == HorizontalAlignment.Left) + { + ResizeFromRight(e); + } + else + { + ResizeFromLeft(e); + } + } + else this.PreviewMouseMove -= PreviewMouseMoveResize; + } + + private void ResizeFromLeft(MouseEventArgs e) + { + Point pos = e.GetPosition(this); + double w = this.ActualWidth - pos.X; + + if (w < 80) + { + w = double.NaN; + IsMaximized = false; + } + else + { + IsMaximized = true; + } + if (MaxWidth != double.NaN && w > MaxWidth) w = MaxWidth; + Width = w; + } + private void ResizeFromRight(MouseEventArgs e) + { + Point pos = e.GetPosition(this); + double w = pos.X; + + if (w < 80) + { + w = double.NaN; + IsMaximized = false; + } + else + { + IsMaximized = true; + } + if (MaxWidth != double.NaN && w > MaxWidth) w = MaxWidth; + Width = w; + } + + private void StartDraggingCommandExecuted(object sender, ExecutedRoutedEventArgs e) + { + Control c = e.OriginalSource as Control; + if (c != null) + { + c.PreviewMouseLeftButtonUp += new MouseButtonEventHandler(DragMouseLeftButtonUp); + } + this.PreviewMouseMove += new MouseEventHandler(PreviewMouseMoveButtons); + } + + /// + /// Remove all PreviewMouseMove events from the outlookbar that have been possible set at the beginning of a drag command. + /// + void DragMouseLeftButtonUp(object sender, MouseButtonEventArgs e) + { + Control c = e.OriginalSource as Control; + if (c != null) + { + c.PreviewMouseLeftButtonUp -= DragMouseLeftButtonUp; + } + this.PreviewMouseMove -= PreviewMouseMoveButtons; + this.PreviewMouseMove -= PreviewMouseMoveResize; + } + + void PreviewMouseMoveButtons(object sender, MouseEventArgs e) + { + if (e.LeftButton == MouseButtonState.Pressed) + { + Point pos = e.GetPosition(this); + double h = this.ActualHeight - 1 - ButtonHeight - pos.Y; + MaxNumberOfButtons = (int)(h / ButtonHeight); + } + else this.PreviewMouseMove -= PreviewMouseMoveButtons; + } + + protected override void OnInitialized(EventArgs e) + { + base.OnInitialized(e); + ApplySections(); + } + + + + /// + /// Determine the collection of MinimizedSections and MaximizedSections depending on the MaxVisibleButtons Property. + /// + protected virtual void ApplySections() + { + if (this.IsInitialized) + { + maximizedSections = new Collection(); + minimizedSections = new Collection(); + int max = MaxNumberOfButtons; + int index = 0; + int selectedIndex = SelectedSectionIndex; + OutlookSection selectedContent = null; + + int n = GetNumberOfMinimizedButtons(); + + foreach (OutlookSection e in sections) + { + e.OutlookBar = this; + e.Height = ButtonHeight; + if (max-- > 0) + { + e.IsMaximized = true; + maximizedSections.Add(e); + } + else + { + e.IsMaximized = false; + if (minimizedSections.Count < n) + { + minimizedSections.Add(e); + } + } + bool selected = index++ == selectedIndex; + e.IsSelected = selected; + if (selected) selectedContent = e; + } + SetValue(OutlookBar.MaximizedSectionsPropertyKey, maximizedSections); + SetValue(OutlookBar.MinimizedSectionsPropertyKey, minimizedSections); + SelectedSection = selectedContent; + } + + } + + + private Collection overflowMenu; + + /// + /// Gets or sets the default items for the overflow menu. + /// + [Bindable(true)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] + public Collection OverflowMenuItems + { + get { return overflowMenu; } + // private set { overflowMenu = value; } + } + + public static readonly DependencyProperty OverflowMenuProperty = + DependencyProperty.Register("OverflowMenu", typeof(ItemCollection), typeof(OutlookBar), new UIPropertyMetadata(null)); + + + + + private void ApplyOverflowMenu() + { + Collection overflowItems = new Collection(); + if (OverflowMenuItems.Count > 0) + { + foreach (object item in OverflowMenuItems) + { + overflowItems.Add(item); + } + } + + bool separatorAdded = false; + int visibleButtons = maximizedSections.Count + (IsMaximized ? minimizedSections.Count : 0); + + for (int i = visibleButtons; i < sections.Count; i++) + { + if (!separatorAdded) + { + overflowItems.Add(new Separator()); + separatorAdded = true; + } + OutlookSection section = sections[i]; + MenuItem item = new MenuItem(); + item.Header = section.Header; + Image image = new Image(); + image.Source = section.Image; + item.Icon = image; + item.Tag = section; + item.Click += new RoutedEventHandler(item_Click); + overflowItems.Add(item); + } + + SetValue(OutlookBar.OverflowMenuItemsPropertyKey, overflowItems); + } + + + + + + private int GetNumberOfMinimizedButtons() + { + if (minimizedButtonContainer != null) + { + const double width = 32; + const double overflowWidth = 18; + double fraction = (minimizedButtonContainer.ActualWidth - overflowWidth) / width; + int minimizedButtons = (int)Math.Truncate(fraction); + int visibleButtons = MaxNumberOfButtons + minimizedButtons; + return visibleButtons; + } + return 0; + } + + public event EventHandler OverflowMenuCreated; + + protected virtual void OnOverflowMenuCreated(Collection menuItems) + { + if (OverflowMenuCreated != null) + { + OverflowMenuCreatedEventArgs e = new OverflowMenuCreatedEventArgs(menuItems); + OverflowMenuCreated(this, e); + } + } + + void item_Click(object sender, RoutedEventArgs e) + { + MenuItem item = e.OriginalSource as MenuItem; + OutlookSection section = item.Tag as OutlookSection; + this.SelectedSection = section; + } + + + void SectionsCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) + { + ApplySections(); + } + + private Popup popup; + + public override void OnApplyTemplate() + { + if (popup != null) + { + popup.Closed -= OnPopupClosed; + popup.Opened -= OnPopupOpened; + } + minimizedButtonContainer = this.GetTemplateChild(partMinimizedButtonContainer) as FrameworkElement; + popup = this.GetTemplateChild(partPopup) as Popup; + if (popup != null) + { + popup.Closed += new EventHandler(OnPopupClosed); + popup.Opened += new EventHandler(OnPopupOpened); + } + + ToggleButton btn = GetTemplateChild("PART_ToggleButton") as ToggleButton; + if (btn != null) + { + btn.PreviewMouseLeftButtonUp += new MouseButtonEventHandler(btn_PreviewMouseLeftButtonUp); + } + + base.OnApplyTemplate(); + } + + void btn_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e) + { + Mouse.Capture(null); + popup.StaysOpen = false; + } + + + + protected virtual void OnPopupOpened(object sender, EventArgs e) + { + IsPopupVisible = true; + Mouse.Capture(this, CaptureMode.SubTree); + } + + protected virtual void OnPopupClosed(object sender, EventArgs e) + { + IsPopupVisible = false; + Mouse.Capture(null); + } + + + + private ObservableCollection sections; + + /// + /// Gets the collection of sections. + /// + [Bindable(true)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] + public Collection Sections + { + get { return sections; } + // private set { sections = value as ObservableCollection; } + } + + + private ObservableCollection sideButtons = new ObservableCollection(); + + /// + /// Gets the buttons on the side when IsExpanded is set to false and ShowSideButtons is set to true. + /// + public Collection SideButtons + { + get { return sideButtons; } + } + + + + /// + /// Gets or sets whether to show the SideButtons when IsExpanded is set to false. + /// + public bool ShowSideButtons + { + get { return (bool)GetValue(ShowSideButtonsProperty); } + set { SetValue(ShowSideButtonsProperty, value); } + } + + public static readonly DependencyProperty ShowSideButtonsProperty = + DependencyProperty.Register("ShowSideButtons", typeof(bool), typeof(OutlookBar), new UIPropertyMetadata(true)); + + + /// + /// Gets or sets whether the Outlookbar is Maximized or Minimized. + /// + public bool IsMaximized + { + get { return (bool)GetValue(IsMaximizedProperty); } + set { SetValue(IsMaximizedProperty, value); } + } + + public static readonly DependencyProperty IsMaximizedProperty = + DependencyProperty.Register("IsMaximized", typeof(bool), typeof(OutlookBar), new UIPropertyMetadata(true, MaximizedPropertyChanged)); + + private static void MaximizedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + OutlookBar bar = (OutlookBar)d; + bar.OnMaximizedChanged((bool)e.NewValue); + } + + private double previousMaxWidth = double.PositiveInfinity; + + /// + /// Occurs when the IsMaximized property has changed. + /// + /// + protected virtual void OnMaximizedChanged(bool isExpanded) + { + if (isExpanded) IsPopupVisible = false; + EnsureSectionContentIsVisible(); + + if (isExpanded) + { + MaxWidth = previousMaxWidth; + RaiseEvent(new RoutedEventArgs(ExpandedEvent)); + } + else + { + previousMaxWidth = MaxWidth; + MaxWidth = MinimizedWidth + (CanResize ? 4 : 0); + RaiseEvent(new RoutedEventArgs(CollapsedEvent)); + } + } + + + + /// + /// Occurs after the OutlookBar has collapsed. + /// + public event RoutedEventHandler Collapsed + { + add { AddHandler(OutlookBar.CollapsedEvent, value); } + remove { RemoveHandler(OutlookBar.CollapsedEvent, value); } + } + + /// + /// Occurs after the OutlookBar has expanded. + /// + public event RoutedEventHandler Expanded + { + add { AddHandler(OutlookBar.ExpandedEvent, value); } + remove { RemoveHandler(OutlookBar.ExpandedEvent, value); } + } + + public static readonly RoutedEvent CollapsedEvent = EventManager.RegisterRoutedEvent("CollapsedEvent", + RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(OutlookBar)); + + public static readonly RoutedEvent ExpandedEvent = EventManager.RegisterRoutedEvent("ExpandedEvent", + RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(OutlookBar)); + + /// + /// This code ensures that the section content is visible when the IsExpanded has changed, + /// since the parent of the content could have changed either. + /// + private void EnsureSectionContentIsVisible() + { + object content = SelectedSection != null ? SelectedSection.Content : null; + SectionContent = null; // set temporarily to null, so resetting to the current content will have an effect. + CollapsedSectionContent = IsMaximized ? null : content; + SectionContent = IsMaximized ? content : null; + } + + + /// + /// Gets or sets the width when IsExpanded is set to false. + /// + public double MinimizedWidth + { + get { return (double)GetValue(MinimizedWidthProperty); } + set { SetValue(MinimizedWidthProperty, value); } + } + + public static readonly DependencyProperty MinimizedWidthProperty = + DependencyProperty.Register("MinimizedWidth", typeof(double), typeof(OutlookBar), new UIPropertyMetadata((double)32)); + + + + + /// + /// Gets or sets how to align template of the OutlookBar. + /// Currently, only Left or Right is supported! + /// + /// + /// This property has been renamed from Dock to DockPosition due to a suggestion from maze6210: + /// http://www.codeplex.com/odyssey/WorkItem/View.aspx?WorkItemId=1075 + /// + public HorizontalAlignment DockPosition + { + get { return (HorizontalAlignment)GetValue(DockPositionProperty); } + set { SetValue(DockPositionProperty, value); } + } + + public static readonly DependencyProperty DockPositionProperty = + DependencyProperty.Register("DockPosition", typeof(HorizontalAlignment), typeof(OutlookBar), new UIPropertyMetadata(HorizontalAlignment.Left)); + + private static readonly DependencyPropertyKey MaximizedSectionsPropertyKey = + DependencyProperty.RegisterReadOnly("MaximizedSections", typeof(Collection), typeof(OutlookBar), new UIPropertyMetadata(null)); + public static readonly DependencyProperty MaximizedSectionsProperty = MaximizedSectionsPropertyKey.DependencyProperty; + + private static readonly DependencyPropertyKey MinimizedSectionsPropertyKey = + DependencyProperty.RegisterReadOnly("MinimizedSections", typeof(Collection), typeof(OutlookBar), new UIPropertyMetadata(null)); + public static readonly DependencyProperty MinimizedSectionsProperty = MinimizedSectionsPropertyKey.DependencyProperty; + + private static readonly DependencyPropertyKey OverflowMenuItemsPropertyKey = + DependencyProperty.RegisterReadOnly("OverflowMenuItems", typeof(Collection), typeof(OutlookBar), new UIPropertyMetadata(null)); + public static readonly DependencyProperty OverflowMenuItemsProperty = OverflowMenuItemsPropertyKey.DependencyProperty; + + + /// + /// Gets or sets how many buttons are completely visible. + /// + public int MaxNumberOfButtons + { + get { return (int)GetValue(MaxNumberOfButtonsProperty); } + set { SetValue(MaxNumberOfButtonsProperty, value); } + } + + public static readonly DependencyProperty MaxNumberOfButtonsProperty = + DependencyProperty.Register("MaxNumberOfButtons", typeof(int), typeof(OutlookBar), + new FrameworkPropertyMetadata(int.MaxValue, + FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure, + MaxNumberOfButtonsPropertyChanged)); + + private static void MaxNumberOfButtonsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + OutlookBar bar = (OutlookBar)d; + bar.ApplySections(); + } + + + /// + /// Gets or sets whether the popup panel is visible. + /// + public bool IsPopupVisible + { + get { return (bool)GetValue(IsPopupVisibleProperty); } + set { SetValue(IsPopupVisibleProperty, value); } + } + + public static readonly DependencyProperty IsPopupVisibleProperty = + DependencyProperty.Register("IsPopupVisible", typeof(bool), typeof(OutlookBar), new UIPropertyMetadata(false, PopupVisiblePropertyChanged)); + + private static void PopupVisiblePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + OutlookBar bar = (OutlookBar)d; + bar.OnPopupVisibleChanged((bool)e.NewValue); + } + + /// + /// Occurs when the IsPopupVisible has changed. + /// + /// + protected virtual void OnPopupVisibleChanged(bool isPopupVisible) + { + if (popup != null) + { + popup.StaysOpen = true; + popup.IsOpen = isPopupVisible; + } + if (isPopupVisible) + { + RaiseEvent(new RoutedEventArgs(PopupOpenedEvent)); + } + else + { + RaiseEvent(new RoutedEventArgs(PopupClosedEvent)); + } + } + + /// + /// Occurs after the Popup has opened. + /// + public event RoutedEventHandler PopupOpened + { + add { AddHandler(OutlookBar.PopupOpenedEvent, value); } + remove { RemoveHandler(OutlookBar.PopupOpenedEvent, value); } + } + + /// + /// Occurs after the Popup has closed. + /// + public event RoutedEventHandler PopupClosed + { + add { AddHandler(OutlookBar.PopupClosedEvent, value); } + remove { RemoveHandler(OutlookBar.PopupClosedEvent, value); } + } + + public static readonly RoutedEvent PopupOpenedEvent = EventManager.RegisterRoutedEvent("PopupOpenedEvent", + RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(OutlookBar)); + + public static readonly RoutedEvent PopupClosedEvent = EventManager.RegisterRoutedEvent("PopupClosedEvent", + RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(OutlookBar)); + + /// + /// Gets or sets the index of the selected section. + /// + public int SelectedSectionIndex + { + get { return (int)GetValue(SelectedSectionIndexProperty); } + set { SetValue(SelectedSectionIndexProperty, value); } + } + + public static readonly DependencyProperty SelectedSectionIndexProperty = + DependencyProperty.Register("SelectedSectionIndex", typeof(int), typeof(OutlookBar), new + FrameworkPropertyMetadata( + 0, + FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, + SelectedIndexPropertyChanged)); + + private static void SelectedIndexPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + OutlookBar bar = (OutlookBar)d; + bar.ApplySections(); + } + + + + /// + /// Gets or sets the selected section. + /// + public OutlookSection SelectedSection + { + get { return (OutlookSection)GetValue(SelectedSectionProperty); } + set { SetValue(SelectedSectionProperty, value); } + } + + public static readonly DependencyProperty SelectedSectionProperty = + DependencyProperty.Register("SelectedSection", typeof(OutlookSection), typeof(OutlookBar), + new UIPropertyMetadata(null, SelectedSectionPropertyChanged)); + + + private static void SelectedSectionPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + OutlookBar bar = (OutlookBar)d; + bar.OnSelectedSectionChanged((OutlookSection)e.OldValue, (OutlookSection)e.NewValue); + } + + /// + /// Occurs when the SelectedSection has changed. + /// + protected virtual void OnSelectedSectionChanged(OutlookSection oldSection, OutlookSection newSection) + { + + for (int index = 0; index < sections.Count; index++) + { + OutlookSection section = sections[index]; + bool selected = newSection == section; + section.IsSelected = newSection == section; + if (selected) + { + SelectedSectionIndex = index; + SectionContent = IsMaximized ? section.Content : null; + CollapsedSectionContent = IsMaximized ? null : section.Content; + } + } + RaiseEvent(new RoutedPropertyChangedEventArgs(oldSection, newSection, SelectedSectionChangedEvent)); + } + + + /// + /// Occurs when the SelectedSection has changed. + /// + public event RoutedPropertyChangedEventHandler SelectedSectionChanged + { + add { AddHandler(OutlookBar.SelectedSectionChangedEvent, value); } + remove { RemoveHandler(OutlookBar.SelectedSectionChangedEvent, value); } + } + + public static readonly RoutedEvent SelectedSectionChangedEvent = EventManager.RegisterRoutedEvent("SelectedSectionChangedEvent", + RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler), typeof(OutlookBar)); + + + + + /// + /// Gets the content of the selected section. + /// + internal object SectionContent + { + get { return (object)GetValue(SectionContentProperty); } + set { SetValue(SectionContentPropertyKey, value); } + } + + private static readonly DependencyPropertyKey SectionContentPropertyKey = + DependencyProperty.RegisterReadOnly("SectionContent", typeof(object), typeof(OutlookBar), new UIPropertyMetadata(null)); + public static readonly DependencyProperty SectionContentProperty = SectionContentPropertyKey.DependencyProperty; + + + + + /// + /// Gets or sets the content for the popup. + /// + internal object CollapsedSectionContent + { + get { return (object)GetValue(CollapsedSectionContentProperty); } + set { SetValue(CollapsedSectionContentPropertyKey, value); } + } + + + private static readonly DependencyPropertyKey CollapsedSectionContentPropertyKey = + DependencyProperty.RegisterReadOnly("CollapsedSectionContent", typeof(object), typeof(OutlookBar), new UIPropertyMetadata(null)); + public static readonly DependencyProperty CollapsedSectionContentProperty = SectionContentPropertyKey.DependencyProperty; + + + + /// + /// Gets or sets whether the overflow menu of the available sections is visible. + /// + public bool IsOverflowVisible + { + get { return (bool)GetValue(IsOverflowVisibleProperty); } + set { SetValue(IsOverflowVisibleProperty, value); } + } + + public static readonly DependencyProperty IsOverflowVisibleProperty = + DependencyProperty.Register("IsOverflowVisible", typeof(bool), typeof(OutlookBar), new UIPropertyMetadata(false, OverflowVisiblePropertyChanged)); + + + private static void OverflowVisiblePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + OutlookBar bar = (OutlookBar)d; + bool newValue = (bool)e.NewValue; + bar.OnOverflowVisibleChanged(newValue); + } + + /// + /// Occurs when the IsOverflowVisible has changed. + /// + /// + protected virtual void OnOverflowVisibleChanged(bool newValue) + { + + if (newValue == true) + { + ApplyOverflowMenu(); + } + } + + /// + /// Toggles the IsExpanded property. + /// + public static RoutedUICommand CollapseCommand + { + get { return collapseCommand; } + } + + /// + /// Starts dragging the splitter for the visible section buttons (used for the xaml template). + /// + public static RoutedUICommand StartDraggingCommand + { + get { return startDraggingCommand; } + } + + /// + /// Shows the popup window. + /// + public static RoutedUICommand ShowPopupCommand + { + get { return showPopupCommand; } + } + + /// + /// Start to resize the Width of the OutlookBar (used for the xaml template to initiate resizing). + /// + public static RoutedUICommand ResizeCommand + { + get { return resizeCommand; } + } + private static RoutedUICommand resizeCommand = new RoutedUICommand("Resize", "ResizeCommand", typeof(OutlookBar)); + + /// + /// Close the OutlookBar + /// + public static RoutedUICommand CloseCommand + { + get { return closeCommand; } + } + private static RoutedUICommand collapseCommand = new RoutedUICommand("Collapse", "CollapseCommand", typeof(OutlookBar)); + private static RoutedUICommand startDraggingCommand = new RoutedUICommand("Drag", "StartDraggingCommand", typeof(OutlookBar)); + private static RoutedUICommand showPopupCommand = new RoutedUICommand("ShowPopup", "ShowPopupCommand", typeof(OutlookBar)); + private static RoutedUICommand closeCommand = new RoutedUICommand("Close", "CloseCommand", typeof(OutlookBar)); + + + /// + /// Gets or sets the height of the section buttons. + /// + public double ButtonHeight + { + get { return (double)GetValue(ButtonHeightProperty); } + set { SetValue(ButtonHeightProperty, value); } + } + + public static readonly DependencyProperty ButtonHeightProperty = + DependencyProperty.Register("ButtonHeight", typeof(double), typeof(OutlookBar), new UIPropertyMetadata((double)28.0)); + + + + + + /// + /// Gets or sets the with of the popup window. + /// + public double PopupWidth + { + get { return (double)GetValue(PopupWidthProperty); } + set { SetValue(PopupWidthProperty, value); } + } + + public static readonly DependencyProperty PopupWidthProperty = + DependencyProperty.Register("PopupWidth", typeof(double), typeof(OutlookBar), new UIPropertyMetadata((double)double.NaN)); + + + + + /// + /// Gets or sets whether the splitter for the section buttons is visible + /// + public bool IsButtonSplitterVisible + { + get { return (bool)GetValue(IsButtonSplitterVisibleProperty); } + set { SetValue(IsButtonSplitterVisibleProperty, value); } + } + + public static readonly DependencyProperty IsButtonSplitterVisibleProperty = + DependencyProperty.Register("IsButtonSplitterVisible", typeof(bool), typeof(OutlookBar), new UIPropertyMetadata(true)); + + + /// + /// Gets or sets whether the section buttons are visible. + /// + public bool ShowButtons + { + get { return (bool)GetValue(ShowButtonsProperty); } + set { SetValue(ShowButtonsProperty, value); } + } + + public static readonly DependencyProperty ShowButtonsProperty = + DependencyProperty.Register("ShowButtons", typeof(bool), typeof(OutlookBar), new UIPropertyMetadata(true)); + + + [Bindable(true)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] + public Collection OptionButtons + { + get { return (Collection)GetValue(OptionButtonsProperty); } + // private set { SetValue(OptionButtonsProperty, value); } + } + + private static readonly DependencyPropertyKey OptionButtonsPropertyKey = + DependencyProperty.RegisterReadOnly("OptionButtons", typeof(Collection), typeof(OutlookBar), new UIPropertyMetadata(null)); + public static readonly DependencyProperty OptionButtonsProperty = OptionButtonsPropertyKey.DependencyProperty; + + + + + /// + /// Gets or sets wether the width of the OutlookBar can be manually resized by a gripper at the right (or left). + /// + public bool CanResize + { + get { return (bool)GetValue(CanResizeProperty); } + set { SetValue(CanResizeProperty, value); } + } + + public static readonly DependencyProperty CanResizeProperty = + DependencyProperty.Register("CanResize", typeof(bool), typeof(OutlookBar), new UIPropertyMetadata(true)); + + + + /// + /// Gets or sets wether the close button is visible. + /// + public bool IsCloseButtonVisible + { + get { return (bool)GetValue(IsCloseButtonVisibleProperty); } + set { SetValue(IsCloseButtonVisibleProperty, value); } + } + + public static readonly DependencyProperty IsCloseButtonVisibleProperty = + DependencyProperty.Register("IsCloseButtonVisible", typeof(bool), typeof(OutlookBar), new UIPropertyMetadata(false)); + + + + /// + /// Gets or sets the text or content that is displayed on the minimized OutlookBar at the Button to open up the Navigation Pane. + /// + public object NavigationPaneText + { + get { return (object)GetValue(NavigationPaneTextProperty); } + set { SetValue(NavigationPaneTextProperty, value); } + } + + public static readonly DependencyProperty NavigationPaneTextProperty = + DependencyProperty.Register("NavigationPaneText", typeof(object), typeof(OutlookBar), new UIPropertyMetadata("Navigation Pane")); + + + protected override IEnumerator LogicalChildren + { + get + { + return GetLogicalChildren().GetEnumerator(); + } + } + + protected virtual IEnumerable GetLogicalChildren() + { + foreach (var section in Sections) yield return section; + if (SelectedSection != null) yield return SelectedSection.Content; + } + + + #region IKeyTipControl Members + + void IKeyTipControl.ExecuteKeyTip() + { + this.IsMaximized ^= true; + } + + #endregion + } +} diff --git a/Odyssey/Odyssey/OutlookBar/OutlookSection.cs b/Odyssey/Odyssey/OutlookBar/OutlookSection.cs new file mode 100644 index 0000000..83ab2eb --- /dev/null +++ b/Odyssey/Odyssey/OutlookBar/OutlookSection.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using System.Collections.ObjectModel; +using System.Windows.Controls.Primitives; +using Odyssey.Controls.Interfaces; + +namespace Odyssey.Controls +{ + public class OutlookSection:HeaderedContentControl,IKeyTipControl + { + static OutlookSection() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(OutlookSection), new FrameworkPropertyMetadata(typeof(OutlookSection))); + } + + public OutlookSection():base() + { + AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(buttonClickedEvent)); + } + + private void buttonClickedEvent(object sender, RoutedEventArgs e) + { + OutlookBar bar = OutlookBar; + ToggleButton b = e.OriginalSource as ToggleButton; + if (b != null) b.IsChecked = true; + if (bar != null) + { + bar.SelectedSection = this; + } + OnClick(); + } + + /// + /// Gets or sets the Image for the Section Button. + /// + public ImageSource Image + { + get { return (ImageSource)GetValue(ImageProperty); } + set { SetValue(ImageProperty, value); } + } + + public static readonly DependencyProperty ImageProperty = + DependencyProperty.Register("Image", typeof(ImageSource), typeof(OutlookSection), new UIPropertyMetadata(null)); + + + /// + /// Gets wether the Section is the selected section of the OutlookBar. + /// + public bool IsSelected + { + get { return (bool)GetValue(IsSelectedProperty); } + internal set { SetValue(IsSelectedProperty, value); } + } + + public static readonly DependencyProperty IsSelectedProperty = + DependencyProperty.Register("IsSelected", typeof(bool), typeof(OutlookSection), new UIPropertyMetadata(false, IsSelectedPropertyChanged)); + + private static void IsSelectedPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + ((OutlookSection)o).OnSelectedPropertyChanged((bool)e.OldValue, (bool)e.NewValue); + } + + protected virtual void OnSelectedPropertyChanged(bool oldValue, bool newValue) + { + if (newValue) OutlookBar.SelectedSection = this; + } + + /// + /// Occurs when the section button is clicked,. + /// + protected virtual void OnClick() + { + this.RaiseEvent(new RoutedEventArgs(OutlookSection.ClickEvent)); + } + + /// + /// Occurs when the section button is clicked,. + /// + public event RoutedEventHandler Click + { + add { AddHandler(OutlookSection.ClickEvent, value); } + remove { RemoveHandler(OutlookSection.ClickEvent, value); } + } + + public static readonly RoutedEvent ClickEvent = EventManager.RegisterRoutedEvent("Click", + RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(OutlookSection)); + + + + + /// + /// Gets or sets whether the Section is shown as a complete button with text (true), otherwise as small button with image only. + /// + public bool IsMaximized + { + get { return (bool)GetValue(IsMaximizedProperty); } + set { SetValue(IsMaximizedProperty, value); } + } + + public static readonly DependencyProperty IsMaximizedProperty = + DependencyProperty.Register("IsMaximized", typeof(bool), typeof(OutlookSection), new UIPropertyMetadata(true)); + + /// + /// Gets or sets the OutlookBar to which this Section is assigned. + /// + internal OutlookBar OutlookBar { get; set; } + + #region IKeyTipControl Members + + void IKeyTipControl.ExecuteKeyTip() + { + IsSelected = true; + } + + #endregion + } +} diff --git a/Odyssey/Odyssey/OutlookBar/OverflowMenuCreatedEventArgs.cs b/Odyssey/Odyssey/OutlookBar/OverflowMenuCreatedEventArgs.cs new file mode 100644 index 0000000..c802fc0 --- /dev/null +++ b/Odyssey/Odyssey/OutlookBar/OverflowMenuCreatedEventArgs.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Collections.ObjectModel; + +namespace Odyssey.Controls +{ + public class OverflowMenuCreatedEventArgs:EventArgs + { + public OverflowMenuCreatedEventArgs(Collection menuItems) + : base() + { + this.MenuItems = menuItems; + } + + public Collection MenuItems { get; private set; } + } +} diff --git a/Odyssey/Odyssey/Properties/AssemblyInfo.cs b/Odyssey/Odyssey/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..7bb32aa --- /dev/null +++ b/Odyssey/Odyssey/Properties/AssemblyInfo.cs @@ -0,0 +1,58 @@ +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Windows; +using System.Windows.Markup; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Odyssey")] +[assembly: AssemblyDescription("WCF Control Library")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("tomssoftware.net")] +[assembly: AssemblyProduct("Odyssey")] +[assembly: AssemblyCopyright("Copyright © 2008-2009 Thomas Gerber")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +//In order to begin building localizable applications, set +//CultureYouAreCodingWith in your .csproj file +//inside a . For example, if you are using US english +//in your source files, set the to en-US. Then uncomment +//the NeutralResourceLanguage attribute below. Update the "en-US" in +//the line below to match the UICulture setting in the project file. + +//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] + +[assembly: XmlnsDefinition("http://schemas.odyssey.com/wpf", "Odyssey.Controls")] + +[assembly: ThemeInfo( + ResourceDictionaryLocation.SourceAssembly, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] + + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.4.22.*")] +[assembly: AssemblyFileVersion("1.4.22.35")] +[assembly: NeutralResourcesLanguageAttribute("en-US")] diff --git a/Odyssey/Odyssey/Properties/Resources.Designer.cs b/Odyssey/Odyssey/Properties/Resources.Designer.cs new file mode 100644 index 0000000..76c9fe3 --- /dev/null +++ b/Odyssey/Odyssey/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.20506.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Odyssey.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Odyssey.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/Odyssey/Odyssey/Properties/Resources.resx b/Odyssey/Odyssey/Properties/Resources.resx new file mode 100644 index 0000000..4b8fd17 --- /dev/null +++ b/Odyssey/Odyssey/Properties/Resources.resx @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Properties/Settings.Designer.cs b/Odyssey/Odyssey/Properties/Settings.Designer.cs new file mode 100644 index 0000000..6da2958 --- /dev/null +++ b/Odyssey/Odyssey/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.20506.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Odyssey.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/Odyssey/Odyssey/Properties/Settings.settings b/Odyssey/Odyssey/Properties/Settings.settings new file mode 100644 index 0000000..2bd17f0 --- /dev/null +++ b/Odyssey/Odyssey/Properties/Settings.settings @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Ribbon/Classes/BoolConverter.cs b/Odyssey/Odyssey/Ribbon/Classes/BoolConverter.cs new file mode 100644 index 0000000..c471c4e --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Classes/BoolConverter.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Data; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + [ValueConversion(typeof(bool), typeof(bool))] + public class BoolConverter:IValueConverter + { + #region IValueConverter Members + + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + bool bValue = (bool)value; + return !bValue; + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + + #endregion + } +} diff --git a/Odyssey/Odyssey/Ribbon/Classes/IRibbonSize.cs b/Odyssey/Odyssey/Ribbon/Classes/IRibbonSize.cs new file mode 100644 index 0000000..7aae63b --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Classes/IRibbonSize.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Odyssey.Controls +{ + public interface IRibbonSize + { + RibbonSize Appearance { get; set; } + + RibbonSize PreferedAppearance { get; } + + RibbonSize MinAppearance { get; } + } +} diff --git a/Odyssey/Odyssey/Ribbon/Classes/ImageRenderOptions.cs b/Odyssey/Odyssey/Ribbon/Classes/ImageRenderOptions.cs new file mode 100644 index 0000000..ec2a9f3 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Classes/ImageRenderOptions.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Media; +using System.Windows; +using System.Windows.Controls; + +namespace Odyssey.Controls +{ + /// + /// Specifies how to render Images in Ribbon Controls such as RibbonButton, RibbonSplitButton, etc. + /// + public class ImageRenderOptions + { + static ImageRenderOptions() + { + LargeImageScalingModeProperty.AddOwner(typeof(RibbonButton)); + LargeImageScalingModeProperty.AddOwner(typeof(TextBlock)); + } + + public static BitmapScalingMode GetLargeImageScalingMode(DependencyObject obj) + { + return (BitmapScalingMode)obj.GetValue(LargeImageScalingModeProperty); + } + + public static void SetLargeImageScalingMode(DependencyObject obj, BitmapScalingMode value) + { + obj.SetValue(LargeImageScalingModeProperty, value); + } + + public static readonly DependencyProperty LargeImageScalingModeProperty = + DependencyProperty.RegisterAttached("LargeImageScalingMode", + typeof(BitmapScalingMode), + typeof(ImageRenderOptions), + new FrameworkPropertyMetadata(BitmapScalingMode.NearestNeighbor, FrameworkPropertyMetadataOptions.Inherits)); + + + + public static BitmapScalingMode GetSmallImageScalingMode(DependencyObject obj) + { + return (BitmapScalingMode)obj.GetValue(SmallImageScalingModeProperty); + } + + public static void SetSmallImageScalingMode(DependencyObject obj, BitmapScalingMode value) + { + obj.SetValue(SmallImageScalingModeProperty, value); + } + + public static readonly DependencyProperty SmallImageScalingModeProperty = + DependencyProperty.RegisterAttached("SmallImageScalingMode", + typeof(BitmapScalingMode), + typeof(ImageRenderOptions), + new FrameworkPropertyMetadata(BitmapScalingMode.NearestNeighbor, FrameworkPropertyMetadataOptions.Inherits)); + + + + + + public static EdgeMode GetLargeEdgeMode(DependencyObject obj) + { + return (EdgeMode)obj.GetValue(LargeEdgeModeProperty); + } + + public static void SetLargeEdgeMode(DependencyObject obj, EdgeMode value) + { + obj.SetValue(LargeEdgeModeProperty, value); + } + + public static readonly DependencyProperty LargeEdgeModeProperty = + DependencyProperty.RegisterAttached("LargeEdgeMode", typeof(EdgeMode), typeof(ImageRenderOptions), new UIPropertyMetadata(EdgeMode.Aliased)); + + + + + public static EdgeMode GetSmallEdgeMode(DependencyObject obj) + { + return (EdgeMode)obj.GetValue(SmallEdgeModeProperty); + } + + public static void SetSmallEdgeMode(DependencyObject obj, EdgeMode value) + { + obj.SetValue(SmallEdgeModeProperty, value); + } + + public static readonly DependencyProperty SmallEdgeModeProperty = + DependencyProperty.RegisterAttached("SmallEdgeMode", typeof(EdgeMode), typeof(ImageRenderOptions), new UIPropertyMetadata(EdgeMode.Aliased)); + + + + + + } +} diff --git a/Odyssey/Odyssey/Ribbon/Classes/NativeMethods.cs b/Odyssey/Odyssey/Ribbon/Classes/NativeMethods.cs new file mode 100644 index 0000000..c9994f0 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Classes/NativeMethods.cs @@ -0,0 +1,289 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Runtime.InteropServices; +using System.Windows; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Native +{ + public static class NativeMethods + { + public const int NOREPOSITION = 0x200; + + [DllImport("gdi32.dll", SetLastError = true)] + public static extern IntPtr CreateRectRgn(int left, int top, int right, int bottom); + + [DllImport("user32.dll")] + public static extern int SetWindowRgn(IntPtr hWnd, IntPtr hRgn, [MarshalAs(UnmanagedType.Bool)] bool bRedraw); + + [return: MarshalAs(UnmanagedType.Bool)] + [DllImport("user32.dll")] + public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); + + [return: MarshalAs(UnmanagedType.Bool)] + [DllImport("user32.dll")] + public static extern bool IsWindowVisible(IntPtr hwnd); + + [DllImport("gdi32.dll")] + public static extern IntPtr CreateRoundRectRgn(int x1, int y1, int x2, int y2, int cx, int cy); + + [DllImport("gdi32.dll", SetLastError = true)] + public static extern int CombineRgn(IntPtr hrgnDest, IntPtr hrgnSrc1, IntPtr hrgnSrc2, int fnCombineMode); + + [return: MarshalAs(UnmanagedType.Bool)] + [DllImport("user32.dll", SetLastError = true)] + public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, SWP uFlags); + + [DllImport("user32.dll", EntryPoint = "GetWindowLongPtr")] + private static extern IntPtr GetWindowLongPtr64(IntPtr hWnd, GWL nIndex); + + [DllImport("user32.dll", EntryPoint = "GetWindowLong")] + private static extern IntPtr GetWindowLongPtr32(IntPtr hWnd, GWL nIndex); + + [DllImport("user32.dll", EntryPoint = "DefWindowProcW", CharSet = CharSet.Unicode)] + public static extern IntPtr DefWindowProc(IntPtr hWnd, WM Msg, IntPtr wParam, IntPtr lParam); + + [DllImport("user32.dll", EntryPoint = "GetWindowLongPtr", SetLastError = true)] + private static extern IntPtr SetWindowLongPtr64(IntPtr hWnd, GWL nIndex, IntPtr dwNewLong); + + [DllImport("user32.dll", EntryPoint = "SetWindowLong", SetLastError = true)] + private static extern IntPtr SetWindowLongPtr32(IntPtr hWnd, GWL nIndex, IntPtr dwNewLong); + + [return: MarshalAs(UnmanagedType.Bool)] + [DllImport("dwmapi.dll")] + public static extern bool DwmDefWindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, out IntPtr plResult); + + [DllImport("dwmapi.dll", PreserveSig = false)] + public static extern void DwmExtendFrameIntoClientArea(IntPtr hwnd, ref MARGINS pMarInset); + + [DllImport("dwmapi.dll", PreserveSig = false)] + public static extern bool DwmIsCompositionEnabled(); + + [StructLayout(LayoutKind.Sequential)] + public struct MARGINS + { + public int cxLeftWidth; + public int cxRightWidth; + public int cyTopHeight; + public int cyBottomHeight; + } + + [StructLayout(LayoutKind.Sequential)] + public struct NCCALCSIZE_PARAMS + { + public RECT rect0, rect1, rect2; + public IntPtr lppos; + } + + [Flags] + public enum SWP + { + ASYNCWINDOWPOS = 0x4000, + DEFERERASE = 0x2000, + DRAWFRAME = 0x20, + FRAMECHANGED = 0x20, + HIDEWINDOW = 0x80, + NOACTIVATE = 0x10, + NOCOPYBITS = 0x100, + NOMOVE = 2, + NOOWNERZORDER = 0x200, + NOREDRAW = 8, + NOREPOSITION = 0x200, + NOSENDCHANGING = 0x400, + NOSIZE = 1, + NOZORDER = 4, + SHOWWINDOW = 0x40 + } + + public enum WM + { + ACTIVATE = 6, + APP = 0x8000, + CLOSE = 0x10, + CREATE = 1, + DESTROY = 2, + DWMCOMPOSITIONCHANGED = 0x31e, + ENABLE = 10, + ERASEBKGND = 20, + GETDLGCODE = 0x87, + GETTEXT = 13, + GETTEXTLENGTH = 14, + KILLFOCUS = 8, + MOVE = 3, + NCACTIVATE = 0x86, + NCCALCSIZE = 0x83, + NCCREATE = 0x81, + NCDESTROY = 130, + NCHITTEST = 0x84, + NCLBUTTONDBLCLK = 0xa3, + NCLBUTTONDOWN = 0xa1, + NCLBUTTONUP = 0xa2, + NCMBUTTONDBLCLK = 0xa9, + NCMBUTTONDOWN = 0xa7, + NCMBUTTONUP = 0xa8, + NCMOUSEMOVE = 160, + NCPAINT = 0x85, + NCRBUTTONDBLCLK = 0xa6, + NCRBUTTONDOWN = 0xa4, + NCRBUTTONUP = 0xa5, + NULL = 0, + PAINT = 15, + QUERYENDSESSION = 0x11, + QUERYOPEN = 0x13, + QUIT = 0x12, + SETFOCUS = 7, + SETICON = 0x80, + SETREDRAW = 11, + SETTEXT = 12, + SIZE = 5, + SYNCPAINT = 0x88, + SYSCHAR = 0x106, + SYSCOLORCHANGE = 0x15, + SYSCOMMAND = 0x112, + SYSDEADCHAR = 0x107, + SYSKEYDOWN = 260, + SYSKEYUP = 0x105, + USER = 0x400, + WINDOWPOSCHANGED = 0x47, + WINDOWPOSCHANGING = 70, + MSG794=794 + } + + public enum HT + { + BORDER = 0x12, + BOTTOM = 15, + BOTTOMLEFT = 0x10, + BOTTOMRIGHT = 0x11, + CAPTION = 2, + CLIENT = 1, + CLOSE = 20, + ERROR = -2, + GROWBOX = 4, + HELP = 0x15, + HSCROLL = 6, + LEFT = 10, + MAXBUTTON = 9, + MENU = 5, + MINBUTTON = 8, + NOWHERE = 0, + OBJECT = 0x13, + RIGHT = 11, + SYSMENU = 3, + TOP = 12, + TOPLEFT = 13, + TOPRIGHT = 14, + TRANSPARENT = -1, + VSCROLL = 7 + } + + + public static IntPtr GetWindowLongPtr(IntPtr hwnd, GWL nIndex) + { + if (8 == IntPtr.Size) + { + return GetWindowLongPtr64(hwnd, nIndex); + } + return GetWindowLongPtr32(hwnd, nIndex); + } + + public enum GWL + { + EXSTYLE = -20, + HINSTANCE = -6, + HWNDPARENT = -8, + ID = -12, + STYLE = -16, + USERDATA = -21, + WNDPROC = -4 + } + + + [Flags] + public enum WS : uint + { + BORDER = 0x800000, + CAPTION = 0xc00000, + CHILD = 0x40000000, + CHILDWINDOW = 0x40000000, + CLIPCHILDREN = 0x2000000, + CLIPSIBLINGS = 0x4000000, + DISABLED = 0x8000000, + DLGFRAME = 0x400000, + GROUP = 0x20000, + HSCROLL = 0x100000, + ICONIC = 0x20000000, + MAXIMIZE = 0x1000000, + MAXIMIZEBOX = 0x10000, + MINIMIZE = 0x20000000, + MINIMIZEBOX = 0x20000, + OVERLAPPED = 0, + OVERLAPPEDWINDOW = 0xcf0000, + POPUP = 0x80000000, + POPUPWINDOW = 0x80880000, + SIZEBOX = 0x40000, + SYSMENU = 0x80000, + TABSTOP = 0x10000, + THICKFRAME = 0x40000, + TILED = 0, + TILEDWINDOW = 0xcf0000, + VISIBLE = 0x10000000, + VSCROLL = 0x200000 + } + + + + public static IntPtr SetWindowLongPtr(IntPtr hwnd, GWL nIndex, IntPtr dwNewLong) + { + if (8 == IntPtr.Size) + { + return SetWindowLongPtr64(hwnd, nIndex, dwNewLong); + } + return SetWindowLongPtr32(hwnd, nIndex, dwNewLong); + } + + + public static int SignedLoWord(int i) + { + return (short)(i & 0xffff); + } + + + public static int SignedHiWord(int i) + { + return (short)(i >> 0x10); + } + + + [Obsolete] + internal static bool xModifyHwndStyle(IntPtr hwnd, WS removeStyle, WS addStyle) + { + WS ws = (WS)GetWindowLongPtr(hwnd, GWL.STYLE).ToInt32(); + WS ws2 = (ws & ~removeStyle) | addStyle; + if (ws == ws2) + { + return false; + } + SetWindowLongPtr(hwnd, GWL.STYLE, new IntPtr((int)ws2)); + return true; + } + + + [StructLayout(LayoutKind.Sequential)] + public struct RECT + { + public int Left; + public int Top; + public int Right; + public int Bottom; + public int Width { get { return Right - Left; } } + public int Height { get { return Bottom - Top; } } + } + } +} diff --git a/Odyssey/Odyssey/Ribbon/Classes/QAItemPlacement.cs b/Odyssey/Odyssey/Ribbon/Classes/QAItemPlacement.cs new file mode 100644 index 0000000..9d484c3 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Classes/QAItemPlacement.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + public enum QAItemPlacement + { + ToolBar=1, + Menu=2, + ToolBarAndMenu=3 + } +} diff --git a/Odyssey/Odyssey/Ribbon/Classes/QAPlacement.cs b/Odyssey/Odyssey/Ribbon/Classes/QAPlacement.cs new file mode 100644 index 0000000..faaa014 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Classes/QAPlacement.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + /// + /// Specifies where to place the QuickAccessToolBar. + /// + public enum QAPlacement + { + Top, + Bottom + } +} diff --git a/Odyssey/Odyssey/Ribbon/Classes/RibbonBarAlignment.cs b/Odyssey/Odyssey/Ribbon/Classes/RibbonBarAlignment.cs new file mode 100644 index 0000000..6b93317 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Classes/RibbonBarAlignment.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + public enum RibbonBarAlignment + { + Full, + Left, + Right + } +} diff --git a/Odyssey/Odyssey/Ribbon/Classes/RibbonGalleryColumns.cs b/Odyssey/Odyssey/Ribbon/Classes/RibbonGalleryColumns.cs new file mode 100644 index 0000000..d29483a --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Classes/RibbonGalleryColumns.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.ComponentModel; +using Odyssey.Controls.Classes; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + [TypeConverter(typeof(RibbonGalleryColumnsCollectionConverter))] + public class RibbonGalleryColumns : List + { + } +} diff --git a/Odyssey/Odyssey/Ribbon/Classes/RibbonGalleryColumnsCollectionConverter.cs b/Odyssey/Odyssey/Ribbon/Classes/RibbonGalleryColumnsCollectionConverter.cs new file mode 100644 index 0000000..0718709 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Classes/RibbonGalleryColumnsCollectionConverter.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.ComponentModel; +using System.Collections.Specialized; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls.Classes +{ + public class RibbonGalleryColumnsCollectionConverter : TypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + return sourceType == typeof(string); + } + + public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) + { + string str = value as string; + if (str == null) + { + return base.ConvertFrom(context, culture, value); + } + str = str.Trim(); + if (str.Length == 0) return null; + + string[] names = str.Split(','); + RibbonGalleryColumns collection = new RibbonGalleryColumns(); + foreach (string name in names) + { + collection.Add(int.Parse(name)); + } + return collection; + } + } +} diff --git a/Odyssey/Odyssey/Ribbon/Classes/RibbonGroupReductionOrderConverter.cs b/Odyssey/Odyssey/Ribbon/Classes/RibbonGroupReductionOrderConverter.cs new file mode 100644 index 0000000..f194c2b --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Classes/RibbonGroupReductionOrderConverter.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.ComponentModel; +using System.Collections.Specialized; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls.Classes +{ + public class RibbonGroupReductionOrderConverter : TypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + return sourceType == typeof(string); + } + + public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) + { + string str = value as string; + if (str == null) + { + return base.ConvertFrom(context, culture, value); + } + str = str.Trim(); + if (str.Length == 0) return null; + + string[] names = str.Split(','); + StringCollection collection = new StringCollection(); + foreach (string name in names) + { + collection.Add(name); + } + return collection; + } + } +} diff --git a/Odyssey/Odyssey/Ribbon/Classes/RibbonOption.cs b/Odyssey/Odyssey/Ribbon/Classes/RibbonOption.cs new file mode 100644 index 0000000..44a857d --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Classes/RibbonOption.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; + +namespace Odyssey.Controls +{ + public class RibbonOption + { + + public static bool GetCloseDropDownOnClick(DependencyObject obj) + { + return (bool)obj.GetValue(CloseDropDownOnClickProperty); + } + + public static void SetCloseDropDownOnClick(DependencyObject obj, bool value) + { + obj.SetValue(CloseDropDownOnClickProperty, value); + } + + public static readonly DependencyProperty CloseDropDownOnClickProperty = + DependencyProperty.RegisterAttached("CloseDropDownOnClick", typeof(bool), typeof(RibbonOption), + new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender | FrameworkPropertyMetadataOptions.Inherits)); + + + + /// + /// Gets whether a RibbonButton, RibbonSplitButton, RibbonToggleButton or RibbonDropDownButton should transite it state with animation. + /// This is an attached inheritable dependency property. The default value is false. + /// + public static bool GetAnimateTransition(DependencyObject obj) + { + return (bool)obj.GetValue(AnimateTransitionProperty); + } + + /// + /// Sets whether a RibbonButton, RibbonSplitButton, RibbonToggleButton or RibbonDropDownButton should transite it state with animation. + /// This is an attached inheritable dependency property. The default value is false. + /// + public static void SetAnimateTransition(DependencyObject obj, bool value) + { + obj.SetValue(AnimateTransitionProperty, value); + } + + public static readonly DependencyProperty AnimateTransitionProperty = + DependencyProperty.RegisterAttached("AnimateTransition", typeof(bool), typeof(RibbonOption), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.Inherits)); + + + + } +} diff --git a/Odyssey/Odyssey/Ribbon/Classes/RibbonReductionCollectionConverter.cs b/Odyssey/Odyssey/Ribbon/Classes/RibbonReductionCollectionConverter.cs new file mode 100644 index 0000000..43ba81d --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Classes/RibbonReductionCollectionConverter.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.ComponentModel; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls.Classes +{ + public class RibbonReductionCollectionConverter:TypeConverter + { + + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + return sourceType == typeof(string); + } + + public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) + { + string str = value as string; + if (str == null) + { + return base.ConvertFrom(context, culture, value); + } + str = str.Trim(); + if (str.Length == 0) return null; + + string[] sizes = str.Split(','); + RibbonSizeCollection collection = new RibbonSizeCollection(); + foreach (string size in sizes) + { + RibbonSize rs; + switch (size.ToUpper()) + { + case "L": rs = RibbonSize.Large; break; + case "M": rs = RibbonSize.Medium; break; + case "S": rs = RibbonSize.Small; break; + case "0": rs = RibbonSize.Minimized; break; + + + default: + rs = (RibbonSize)Enum.Parse(typeof(RibbonSize), size, true); + break; + } + collection.Add(rs); + } + return collection; + } + } +} diff --git a/Odyssey/Odyssey/Ribbon/Classes/RibbonSize.cs b/Odyssey/Odyssey/Ribbon/Classes/RibbonSize.cs new file mode 100644 index 0000000..1006987 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Classes/RibbonSize.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + //Specifies the size of a RibbonControl within a RibbonGroup + public enum RibbonSize + { + + /// + /// the control appears with the large image and text. + /// + Large=3, + + /// + /// the control appears with the small image and text. + /// + Medium=2, + + /// + /// the control appears with the small image only. + /// + Small=1, + + /// + /// the control appears collapsed. + /// + Minimized=0 + } +} diff --git a/Odyssey/Odyssey/Ribbon/Classes/RibbonSizeCollection.cs b/Odyssey/Odyssey/Ribbon/Classes/RibbonSizeCollection.cs new file mode 100644 index 0000000..c50d6d6 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Classes/RibbonSizeCollection.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Collections.ObjectModel; +using System.ComponentModel; +using Odyssey.Controls.Classes; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + [TypeConverter(typeof(RibbonReductionCollectionConverter))] + public class RibbonSizeCollection:Collection + { + } +} diff --git a/Odyssey/Odyssey/Ribbon/Classes/RibbonWindowCornerMode.cs b/Odyssey/Odyssey/Ribbon/Classes/RibbonWindowCornerMode.cs new file mode 100644 index 0000000..087db3c --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Classes/RibbonWindowCornerMode.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls.Classes +{ + public enum RibbonWindowCornerMode + { + Top=1, + All=2, + None=0 + } +} diff --git a/Odyssey/Odyssey/Ribbon/Classes/RoundedCornerConverter.cs b/Odyssey/Odyssey/Ribbon/Classes/RoundedCornerConverter.cs new file mode 100644 index 0000000..e80d847 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Classes/RoundedCornerConverter.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.ComponentModel; +using System.Windows.Data; +using System.Windows; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + [ValueConversion(typeof(CornerRadius),typeof(CornerRadius))] + class RoundedCornerConverter:IValueConverter + { + #region IValueConverter Members + + /// + /// Converts a RoundedCorner property to an appropriate value. + /// + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + CornerRadius cr = (CornerRadius)value; + string s = parameter as string; + switch (s) + { + case "left": + return new CornerRadius(cr.TopLeft, 0d, 0d, cr.BottomLeft); + + case "right": + return new CornerRadius(0d, cr.TopRight, cr.BottomRight, 0d); + + case "top": + return new CornerRadius(cr.TopLeft, cr.TopRight, 0d, 0d); + + case "bottom": + return new CornerRadius(0d, 0d, cr.BottomRight, cr.BottomLeft); + + default: + return null; + } + + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + + #endregion + } +} diff --git a/Odyssey/Odyssey/Ribbon/Classes/RoundedCornerResizeConverter.cs b/Odyssey/Odyssey/Ribbon/Classes/RoundedCornerResizeConverter.cs new file mode 100644 index 0000000..40bd770 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Classes/RoundedCornerResizeConverter.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Data; +using System.Windows; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + [ValueConversion(typeof(double), typeof(CornerRadius))] + public class RoundedCornerResizeConverter:IValueConverter + { + #region IValueConverter Members + + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + double d = parameter != null ? double.Parse(parameter.ToString()) : -1; + CornerRadius r = (CornerRadius)value; + CornerRadius result = new CornerRadius( + Math.Max(0, r.TopLeft + d), + Math.Max(0, r.TopRight + d), + Math.Max(0, r.BottomRight + d), + Math.Max(0, r.BottomLeft + d)); + + return result; + + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + + #endregion + } +} diff --git a/Odyssey/Odyssey/Ribbon/Classes/TwoLineConverter.cs b/Odyssey/Odyssey/Ribbon/Classes/TwoLineConverter.cs new file mode 100644 index 0000000..25e8559 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Classes/TwoLineConverter.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Data; +using System.Windows.Controls; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + /// + /// Breaks a string into two lines by adding a LineBreak at the appropriate position. + /// + [ValueConversion(typeof(string), typeof(TextBlock))] + public class TwoLineConverter : IValueConverter + { + #region IValueConverter Members + + /// + /// Converts a string into a TextBlock with one or two lines. + /// + /// Either a string to convert, or any value to return directly. + /// ignored. + /// ignored. + /// ignored. + /// A TextBlock class if value is of string, otherwise value. + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + string s = value as string; + if (s == null) return value; + { + int l = SplitIn2Lines(s); + if (l > 0) + { + s = s.Remove(l,1).Insert(l, "\n"); + } + } + TextBlock tb = new TextBlock(); + tb.TextWrapping = System.Windows.TextWrapping.Wrap; + tb.TextAlignment = System.Windows.TextAlignment.Center; + tb.LineStackingStrategy = System.Windows.LineStackingStrategy.BlockLineHeight; + tb.LineHeight = 12.0; + tb.Text = s; + return tb; + } + + private static int SplitIn2Lines(string s) + { + int n = s.Length; + int l = n / 2; + int r = l + 1; + while (l > 0) + { + char c = s[l]; + if (char.IsSeparator(c)) break; + if (r < n) + { + c = s[r]; + if (char.IsSeparator(c)) + { + l = r; + break; + } + } + r++; + l--; + } + return l; + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + + #endregion + } +} diff --git a/Odyssey/Odyssey/Ribbon/Classes/TwoLineTextConverter.cs b/Odyssey/Odyssey/Ribbon/Classes/TwoLineTextConverter.cs new file mode 100644 index 0000000..d32a753 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Classes/TwoLineTextConverter.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Data; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + [ValueConversion(typeof(string), typeof(string))] + public class TwoLineTextConverter : IValueConverter + { + #region IValueConverter Members + + /// + /// Splits a string into two lines which have almost the same length if possible. + /// + /// The string to split. If this type is not a string, the value is returned directly. + /// Specifies the line number to return. The value must be either (int)1 or (int)2. + /// The first or second line of the string, otherwise value. + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + int line; + try + { + line = int.Parse(parameter as string); + } + catch + { + throw new ArgumentException("parameter must be either 1 or 2."); + } + string s = value as string; + if (s == null) return line == 1 ? value : null; + + int l = SplitIn2Lines(s); + if (l == 0) return line == 1 ? s : null; + + switch (line) + { + case 1: return s.Substring(0, l).Trim(); + case 2: return s.Substring(l + 1).Trim(); + default: throw new ArgumentException("parameter must be either 1 or 2."); + } + } + + private static int SplitIn2Lines(string s) + { + int n = s.Length; + int l = n / 2; + int r = l + 1; + while (l > 0) + { + char c = s[l]; + if (char.IsSeparator(c)) break; + if (r < n) + { + c = s[r]; + if (char.IsSeparator(c)) + { + l = r; + break; + } + } + r++; + l--; + } + return l; + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + + #endregion + } +} diff --git a/Odyssey/Odyssey/Ribbon/Controls/InternalGroupPanel.cs b/Odyssey/Odyssey/Ribbon/Controls/InternalGroupPanel.cs new file mode 100644 index 0000000..460f4a3 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Controls/InternalGroupPanel.cs @@ -0,0 +1,185 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using System.Windows.Markup; +using System.Collections; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + internal class InternalGroupPanel : Panel + { + /// + /// Gets the maximum height to decide whether a control is small enough to be grouped vertically. + /// + public const double MaxSmallHeight = 24; + + + public int RowsToRender + { + get { return (int)GetValue(RowsToRenderProperty); } + set { SetValue(RowsToRenderProperty, value); } + } + + // Using a DependencyProperty as the backing store for RowsToRender. This enables animation, styling, binding, etc... + public static readonly DependencyProperty RowsToRenderProperty = + DependencyProperty.Register("RowsToRender", typeof(int), typeof(InternalGroupPanel), + new FrameworkPropertyMetadata(2, + FrameworkPropertyMetadataOptions.AffectsMeasure | + FrameworkPropertyMetadataOptions.AffectsArrange | + FrameworkPropertyMetadataOptions.AffectsParentMeasure)); + + + + private List dynamicOrder = new List(); + + static Size infiniteSize = new Size(double.PositiveInfinity, double.PositiveInfinity); + + protected override Size MeasureOverride(Size availableSize) + { + dynamicOrder.Clear(); + + foreach (UIElement e in Children) e.Measure(infiniteSize); + if (RowsToRender == 2) + { + Size size = FitsInDynamicRows(availableSize, 2, Children); + if (!size.IsEmpty) return size; + } + else + { + + var ordered = (from UIElement e in Children orderby e.DesiredSize.Width descending select e).ToList(); + + bool swap = false; + IEnumerable elements; + while ((elements = Get3Elements(ordered, swap)) != null) + { + swap ^= true; + foreach (UIElement e in elements) dynamicOrder.Add(e); + } + + Size size = FitsInDynamicRows(availableSize, 3, dynamicOrder); + if (!size.IsEmpty) return size; + } + + return new Size(48, 3 * MaxSmallHeight); + } + + private IEnumerable Get3Elements(List ordered, bool swap) + { + if (ordered.Count == 0) return null; + + List result = ordered.Take(3).ToList(); + if (ordered.Count > 0) ordered.RemoveAt(0); + if (ordered.Count > 0) ordered.RemoveAt(0); + if (ordered.Count > 0) ordered.RemoveAt(0); + while (result.Count < 3) result.Add(null); + + if (swap) result.Reverse(); + return result; + } + + private Size FitsInDynamicRows(Size availableSize, int rows, IEnumerable children) + { + double[] rowWidth = new double[rows]; + int currentRow = 0; + + foreach (UIElement e in children) + { + if (e == null) + { + if (++currentRow >= rows) currentRow = 0; + continue; + } + Size size = e.DesiredSize; + bool isLarge = size.Height > MaxSmallHeight; + + if (isLarge) + { + currentRow = 0; + double maxWidth = rowWidth.Max() + size.Width; + if (maxWidth > availableSize.Width) return Size.Empty; + + for (int i = 0; i < rows; i++) rowWidth[i] = maxWidth; + } + else + { + rowWidth[currentRow] += size.Width; + if (rowWidth[currentRow] > availableSize.Width) return Size.Empty; + + if (++currentRow >= rows) + { + currentRow = 0; + } + } + } + + return new Size(rowWidth.Max(), 3 * MaxSmallHeight); + } + + protected override Size ArrangeOverride(Size finalSize) + { + if (finalSize.Width > 50) + { + return ArrangeDynamicRows(finalSize); + } + else + { + return ArrangeEmpty(finalSize); + } + } + + private Size ArrangeDynamicRows(Size finalSize) + { + int rowsToRender = RowsToRender; + double[] left = new double[rowsToRender]; + double rowHeight = MaxSmallHeight; + double topOffset = rowsToRender == 2 ? MaxSmallHeight / 3 : 0; + + int rowIndex = 0; + + IEnumerable children = dynamicOrder.Count > 0 ? (IEnumerable)dynamicOrder : Children; + foreach (UIElement e in children) + { + if (e != null) + { + double w = e.DesiredSize.Width; + e.Arrange(new Rect(left[rowIndex], topOffset + rowIndex * (rowHeight + topOffset), w, rowHeight)); + left[rowIndex] += w; + } + rowIndex++; + if (rowIndex >= rowsToRender) + { + rowIndex = 0; + } + } + return new Size(left.Max(), 3 * MaxSmallHeight); + } + + private Size ArrangeEmpty(Size finalSize) + { + Rect r = new Rect(0, 0, 0, 0); + foreach (UIElement e in Children) + { + e.Arrange(r); + } + return finalSize; + } + + + } +} diff --git a/Odyssey/Odyssey/Ribbon/Controls/KeyTip.cs b/Odyssey/Odyssey/Ribbon/Controls/KeyTip.cs new file mode 100644 index 0000000..77eab98 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Controls/KeyTip.cs @@ -0,0 +1,406 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using System.Diagnostics; +using Odyssey.Controls.Ribbon.Interfaces; +using System.Windows.Controls.Primitives; +using Odyssey.Controls.Interfaces; +using System.Windows.Automation.Peers; + +namespace Odyssey.Controls +{ + /// + /// Enables Quick Access keys. + /// + public class KeyTip : Control + { + static KeyTip() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(KeyTip), new FrameworkPropertyMetadata(typeof(KeyTip))); + EventManager.RegisterClassHandler(typeof(RibbonWindow), + FrameworkElement.PreviewKeyDownEvent, + new KeyEventHandler(OnPreviewWindowKeyDown), false); + + EventManager.RegisterClassHandler(typeof(RibbonWindow), + FrameworkElement.PreviewMouseDownEvent, + new RoutedEventHandler(OnPreviewMouseDown), false); + + + } + + static void OnPreviewMouseDown(object sender, RoutedEventArgs e) + { + Reset(); + } + + static void OnPreviewWindowKeyDown(object sender, KeyEventArgs e) + { + switch (e.SystemKey) + { + case Key.LeftAlt: + ToggleQuickAccessKeys(sender); + e.Handled = true; + break; + + default: + e.Handled = CheckQuickAccessKey(e); + break; + + } + switch (e.Key) + { + case Key.Escape: + if (current != null) + { + MoveBack(); + e.Handled = true; + //FrameworkElement x = FocusManager.GetFocusedElement(sender as DependencyObject) as FrameworkElement; + //x.MoveFocus(new TraversalRequest(FocusNavigationDirection.Up)); + } + break; + } + if (!e.Handled) + { + Reset(); + } + } + + private static void MoveBack() + { + selectStack.Pop(); + current = selectStack.Count > 0 ? selectStack.Pop() : null; + elements = null; + keySequence = ""; + Keyboard.Focus(null); + RibbonDropDownButton.CloseOpenedPopup(null); + RibbonGroup.CloseOpenedPopup(); + CloseQuickAccessKeys(); + if (current != null) ShowQuickAccessKeys(current); + } + + private static void ToggleQuickAccessKeys(object sender) + { + Window window = (Window)sender; + window.Deactivated -= window_LocationChanged; + window.Deactivated += window_LocationChanged; + window.LocationChanged -= window_LocationChanged; + window.LocationChanged += window_LocationChanged; + if (current == null) + { + ShowQuickAccessKeys(window); + } + else + { + Reset(); + } + } + + + static void window_LocationChanged(object sender, EventArgs e) + { + Reset(); + } + + private static void Reset() + { + if (elements != null) + { + RibbonDropDownButton.CloseOpenedPopup(null); + RibbonGroup.CloseOpenedPopup(); + HideQuickAccessKeys(); + } + } + + private static void HideQuickAccessKeys() + { + CloseQuickAccessKeys(); + elements = null; + keySequence = ""; + current = null; + selectStack.Clear(); + } + + private static bool CheckQuickAccessKey(KeyEventArgs e) + { + if (elements == null || elements.Count == 0) return false; + string c = new KeyConverter().ConvertToInvariantString(e.Key); + keySequence += c; + + FrameworkElement match = null; + List filtered = new List(); + foreach (FrameworkElement child in elements) + { + string key = KeyTip.GetKey(child); + if (keySequence.Equals(key, StringComparison.InvariantCultureIgnoreCase)) + { + match = child; + break; + } + if (key.StartsWith(keySequence, StringComparison.InvariantCultureIgnoreCase)) + { + filtered.Add(child); + } + } + if (match != null) + { + keySequence = null; + //Remove the key tips befor executing, since another window might be opened and thus the key tips would be desturbing: + HideQuickAccessKeys(); + ExecuteElement(match); + if (KeyTip.GetStop(match)) + { + // ensure the matched element to be measured: + match.UpdateLayout(); + + ShowQuickAccessKeys(match); + } + else + { + HideQuickAccessKeys(); + } + return true; + + } + if (filtered.Count > 0) + { + elements = filtered; + ShowKeys(elements); + return true; + } + else return false; + } + + private static void ExecuteElement(FrameworkElement e) + { + IKeyTipControl cmd = e as IKeyTipControl; + if (cmd != null) + { + cmd.ExecuteKeyTip(); + return; + } + CheckBox cb = e as CheckBox; + if (cb != null) + { + cb.IsChecked ^= true; + return; + } + ComboBox box = e as ComboBox; + if (box != null) + { + box.Focus(); + box.IsDropDownOpen = true; + return; + } + if (e.Focusable) e.Focus(); + } + + private static void ShowKeys(List elements) + { + CloseQuickAccessKeys(); + popups = new List(); + foreach (var e in elements) + { + double yOffset = KeyTip.GetYOffset(e); + double xOffset = KeyTip.GetXOffset(e); + if (xOffset < 0.0) xOffset = e.ActualWidth + xOffset; + if (yOffset < 0.0) yOffset = e.ActualHeight + yOffset; + + if (double.IsNaN(yOffset)) yOffset = e.ActualHeight - 16; + if (double.IsNaN(xOffset)) xOffset = 12; + string key = KeyTip.GetKey(e); + Popup popup = new Popup(); + popup.AllowsTransparency = true; + popup.Child = new KeyTip() { Text = key }; + popup.PlacementTarget = e; + popup.Placement = PlacementMode.Relative; + popup.HorizontalOffset = xOffset; + popup.VerticalOffset = yOffset; + popup.StaysOpen = true; + popups.Add(popup); + popup.IsOpen = true; + + } + } + + private static void CloseQuickAccessKeys() + { + if (popups != null) + { + foreach (var popup in popups) + { + popup.IsOpen = false; + } + popups = null; + } + } + + private static Stack selectStack = new Stack(); + private static FrameworkElement current; + private static List popups; + private static List elements; + private static string keySequence = ""; + + private static void ShowQuickAccessKeys(FrameworkElement root) + { + selectStack.Push(root); + current = root; + elements = new List(); + GatherChildElements(elements, root); + + if (elements.Count == 0) + { + HideQuickAccessKeys(); + } + else + { + ShowKeys(elements); + } + } + + private static void GatherChildElements(List elements, FrameworkElement root) + { + foreach (var o in LogicalTreeHelper.GetChildren(root)) + { + FrameworkElement e = o as FrameworkElement; + if (e != null) + { + GatherElements(elements, e); + } + } + } + + /// + /// Don't call this directly, it is only used in GatherChildElements. + /// + private static void GatherElements(List elements, FrameworkElement root) + { + if (root.Visibility != Visibility.Visible || root.IsEnabled == false) return; + string key = KeyTip.GetKey(root); + if (key != null) + { + elements.Add(root); + } + if (key == null || !KeyTip.GetStop(root)) + { + foreach (var o in LogicalTreeHelper.GetChildren(root)) + { + FrameworkElement e = o as FrameworkElement; + if (e != null) + { + GatherElements(elements, e); + } + } + } + } + + + + /// + /// Gets the Quick Access Key combination. + /// + /// + /// + public static string GetKey(DependencyObject obj) + { + return (string)obj.GetValue(KeyProperty); + } + + /// + /// Sets the Quick Access Key. + /// + /// + /// + public static void SetKey(DependencyObject obj, string value) + { + obj.SetValue(KeyProperty, value); + } + + public static readonly DependencyProperty KeyProperty = + DependencyProperty.RegisterAttached("Key", typeof(string), typeof(KeyTip), new UIPropertyMetadata(null)); + + + + + public static double GetXOffset(DependencyObject obj) + { + return (double)obj.GetValue(XOffsetProperty); + } + + public static void SetXOffset(DependencyObject obj, double value) + { + obj.SetValue(XOffsetProperty, value); + } + + public static readonly DependencyProperty XOffsetProperty = + DependencyProperty.RegisterAttached("XOffset", typeof(double), typeof(KeyTip), new UIPropertyMetadata(double.NaN)); + + + + + public static double GetYOffset(DependencyObject obj) + { + return (double)obj.GetValue(YOffsetProperty); + } + + public static void SetYOffset(DependencyObject obj, double value) + { + obj.SetValue(YOffsetProperty, value); + } + + // Using a DependencyProperty as the backing store for YOffset. This enables animation, styling, binding, etc... + public static readonly DependencyProperty YOffsetProperty = + DependencyProperty.RegisterAttached("YOffset", typeof(double), typeof(KeyTip), new UIPropertyMetadata(double.NaN)); + + + + + /// + /// Gets whether to stop gathering for KeyTips of child controls. + /// + public static bool GetStop(DependencyObject obj) + { + return (bool)obj.GetValue(StopProperty); + } + + /// + /// Sets whether to stop gathering for KeyTips of child controls. The default value is true. + /// + public static void SetStop(DependencyObject obj, bool value) + { + obj.SetValue(StopProperty, value); + } + + // Using a DependencyProperty as the backing store for Stop. This enables animation, styling, binding, etc... + public static readonly DependencyProperty StopProperty = + DependencyProperty.RegisterAttached("Stop", typeof(bool), typeof(KeyTip), new UIPropertyMetadata(true)); + + + + + + public string Text + { + get { return (string)GetValue(TextProperty); } + set { SetValue(TextProperty, value); } + } + + // Using a DependencyProperty as the backing store for Text. This enables animation, styling, binding, etc... + public static readonly DependencyProperty TextProperty = + DependencyProperty.Register("Text", typeof(string), typeof(KeyTip), new UIPropertyMetadata(null)); + + + + + + } +} diff --git a/Odyssey/Odyssey/Ribbon/Controls/RibbonApplicationMenu.cs b/Odyssey/Odyssey/Ribbon/Controls/RibbonApplicationMenu.cs new file mode 100644 index 0000000..942647b --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Controls/RibbonApplicationMenu.cs @@ -0,0 +1,387 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Controls; +using System.Windows; +using System.Windows.Markup; +using System.Windows.Media; +using System.Windows.Controls.Primitives; +using System.Windows.Input; +using System.Diagnostics; +using System.Timers; +using System.Windows.Shapes; +using System.Windows.Data; +using Odyssey.Controls.Ribbon.Interfaces; +using Odyssey.Controls.Interfaces; +using System.Collections; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + [ContentProperty("Items")] + [TemplatePart(Name = partRecentItemsList)] + [TemplatePart(Name = partAppButton)] + [TemplatePart(Name = partAppButtonClone)] + public class RibbonApplicationMenu : MenuItem,IKeyTipControl + { + const string partRecentItemsList = "PART_RecentItemsList"; + const string partAppButton = "PART_AppButton"; + const string partAppButtonClone = "PART_AppButtonClone"; + + static RibbonApplicationMenu() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonApplicationMenu), new FrameworkPropertyMetadata(typeof(RibbonApplicationMenu))); + } + + public RibbonApplicationMenu() + : base() + { + AddHandler(MenuItem.ClickEvent, new RoutedEventHandler(OnMenuItemClick)); + } + + #region Obsolete +#if false + protected override void PrepareContainerForItemOverride(DependencyObject element, object item) + { + base.PrepareContainerForItemOverride(element, item); + RibbonMenuItem rItem = item as RibbonMenuItem; + if (rItem != null) + { + rItem.MouseEnter += new MouseEventHandler(rItem_MouseEnter); + rItem.MouseLeave += new MouseEventHandler(rItem_MouseLeave); + } + } + + + private System.Timers.Timer timer; + + private Timer EnsureTimer() + { + if (timer == null) + { + timer = new Timer(500); + timer.Elapsed += new ElapsedEventHandler(timer_Elapsed); + } + return timer; + } + + delegate void MyFunc(); + + void timer_Elapsed(object sender, ElapsedEventArgs e) + { + timer.Stop(); + RibbonMenuItem item = selectedItem; + if (item != null) + { + + MyFunc d = delegate() { item.IsSubmenuOpen = true; }; + this.Dispatcher.BeginInvoke(d); + } + } + + private RibbonMenuItem selectedItem; + + void rItem_MouseLeave(object sender, MouseEventArgs e) + { + if (timer != null) timer.Stop(); + selectedItem = null; + RibbonMenuItem item = sender as RibbonMenuItem; + item.IsSubmenuOpen = false; + } + + void rItem_MouseEnter(object sender, MouseEventArgs e) + { + RibbonMenuItem item = sender as RibbonMenuItem; + if (item.HasItems) + { + selectedItem = item; + EnsureTimer().Start(); + } + } +#endif + #endregion + + + void OnMenuItemClick(object sender, RoutedEventArgs e) + { + IsOpen = false; + } + + + protected override bool IsItemItsOwnContainerOverride(object item) + { + return item is RibbonMenuItem || item is Separator; + } + + protected override System.Windows.DependencyObject GetContainerForItemOverride() + { + return new RibbonMenuItem(); + } + + + private FrameworkElement recentItemsList; + private RibbonDropDownButton appButton; + private FrameworkElement appButtonClone; + + + public override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + + appButton = GetTemplateChild(partAppButton) as RibbonDropDownButton; + appButtonClone = GetTemplateChild(partAppButtonClone) as FrameworkElement; + appButton.PopupOpened += new RoutedEventHandler(appButton_PopupOpened); + appButton.PopupClosed += new RoutedEventHandler(appButton_PopupClosed); + + recentItemsList = GetTemplateChild(partRecentItemsList) as FrameworkElement; + } + + void appButton_PopupClosed(object sender, RoutedEventArgs e) + { + IsOpen = false; + } + + + void appButton_PopupOpened(object sender, RoutedEventArgs e) + { + AdjustApplicationButtons(); + IsOpen = true; + } + + + + /// + /// Ensures that both ApplicationMenu buttons are at the same screen location: + /// + private void AdjustApplicationButtons() + { + if (appButtonClone != null && appButton != null) + { + Point p = appButton.PointToScreen(new Point()); + Point p2 = appButtonClone.PointFromScreen(p); + + double dx = p2.X + Canvas.GetLeft(appButtonClone); + double dy = p2.Y + Canvas.GetTop(appButtonClone); + appButtonClone.Visibility = dy >= -20 ? Visibility.Visible : Visibility.Hidden; + Canvas.SetLeft(appButtonClone, dx); + Canvas.SetTop(appButtonClone, dy); + } + } + + /// + /// Gets or sets the content of the footer for the ApplicationMenu. + /// This is a dependency property. + /// + public object Footer + { + get { return (object)GetValue(FooterProperty); } + set { SetValue(FooterProperty, value); } + } + + + // Using a DependencyProperty as the backing store for Footer. This enables animation, styling, binding, etc... + public static readonly DependencyProperty FooterProperty = + DependencyProperty.Register("Footer", typeof(object), typeof(RibbonApplicationMenu), new UIPropertyMetadata(null)); + + + + public object MenuHeader + { + get { return (object)GetValue(MenuHeaderProperty); } + set { SetValue(MenuHeaderProperty, value); } + } + + // Using a DependencyProperty as the backing store for Header. This enables animation, styling, binding, etc... + public static readonly DependencyProperty MenuHeaderProperty = + DependencyProperty.Register("MenuHeader", typeof(object), typeof(RibbonApplicationMenu), new UIPropertyMetadata(null)); + + + + public DataTemplate MenuHeaderTemplate + { + get { return (DataTemplate)GetValue(MenuHeaderTemplateProperty); } + set { SetValue(MenuHeaderTemplateProperty, value); } + } + + // Using a DependencyProperty as the backing store for HeaderTemplate. This enables animation, styling, binding, etc... + public static readonly DependencyProperty MenuHeaderTemplateProperty = + DependencyProperty.Register("MenuHeaderTemplate", typeof(DataTemplate), typeof(RibbonApplicationMenu), new UIPropertyMetadata(null)); + + + + + + /// + /// Gets or sets the DataTemplate for the footer. + /// This is a dependency property. + /// + public DataTemplate FooterTemplate + { + get { return (DataTemplate)GetValue(FooterTemplateProperty); } + set { SetValue(FooterTemplateProperty, value); } + } + + // Using a DependencyProperty as the backing store for FooterTemplate. This enables animation, styling, binding, etc... + public static readonly DependencyProperty FooterTemplateProperty = + DependencyProperty.Register("FooterTemplate", typeof(DataTemplate), typeof(RibbonApplicationMenu), new UIPropertyMetadata(null)); + + + + + public object RecentItemsSource + { + get { return (object)GetValue(RecentItemsSourceProperty); } + set { SetValue(RecentItemsSourceProperty, value); } + } + + // Using a DependencyProperty as the backing store for RecentItemsSource. This enables animation, styling, binding, etc... + public static readonly DependencyProperty RecentItemsSourceProperty = + DependencyProperty.Register("RecentItemsSource", typeof(object), typeof(RibbonApplicationMenu), new UIPropertyMetadata(null)); + + + + + + public bool IsOpen + { + get { return (bool)GetValue(IsOpenProperty); } + set { SetValue(IsOpenProperty, value); } + } + + // Using a DependencyProperty as the backing store for IsOpen. This enables animation, styling, binding, etc... + public static readonly DependencyProperty IsOpenProperty = + DependencyProperty.Register("IsOpen", typeof(bool), typeof(RibbonApplicationMenu), + new UIPropertyMetadata(false, OpenPropertyChanged)); + + static void OpenPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + RibbonApplicationMenu menu = (RibbonApplicationMenu)o; + bool newValue = (bool)e.NewValue; + + if (menu.appButton != null) + { + menu.appButton.IsDropDownPressed = newValue; + } + if (newValue) menu.OnMenuOpened(); else menu.OnMenuClosed(); + } + + private object toolTip; + + public event EventHandler Opened; + + public event EventHandler OnClosed; + + protected virtual void OnMenuClosed() + { + // restore the tooltip when the menu is closed: + ToolTip = toolTip; + if (OnClosed != null) OnClosed(this, EventArgs.Empty); + } + + protected virtual void OnMenuOpened() + { + // when the menu is open, the tooltip must not be shown: + toolTip = this.ToolTip; + ToolTip = null; + if (Opened != null) Opened(this, EventArgs.Empty); + + } + + + /// Gets or sets the control that represents the recent items list. + /// This is a dependency property. + /// + public object RecentItemsList + { + get { return (object)GetValue(RecentItemsListProperty); } + set { SetValue(RecentItemsListProperty, value); } + } + + // Using a DependencyProperty as the backing store for RectentItemsList. This enables animation, styling, binding, etc... + public static readonly DependencyProperty RecentItemsListProperty = + DependencyProperty.Register("RecentItemsList", typeof(object), typeof(RibbonApplicationMenu), new UIPropertyMetadata(null)); + + + + /// + /// Gets or sets the DataTemplate for the RecentItemsList. + /// This is a dependency property. + /// + public DataTemplate RecentItemsListTemplate + { + get { return (DataTemplate)GetValue(RecentItemsListTemplateProperty); } + set { SetValue(RecentItemsListTemplateProperty, value); } + } + + // Using a DependencyProperty as the backing store for RectentItemsListTemplate. This enables animation, styling, binding, etc... + public static readonly DependencyProperty RecentItemsListTemplateProperty = + DependencyProperty.Register("RecentItemsListTemplate", typeof(DataTemplate), typeof(RibbonApplicationMenu), new UIPropertyMetadata(null)); + + + + + public ImageSource MenuButtonImage + { + get { return (ImageSource)GetValue(MenuButtonImageProperty); } + set { SetValue(MenuButtonImageProperty, value); } + } + + // Using a DependencyProperty as the backing store for MenuButtonImage. This enables animation, styling, binding, etc... + public static readonly DependencyProperty MenuButtonImageProperty = + DependencyProperty.Register("MenuButtonImage", typeof(ImageSource), typeof(RibbonApplicationMenu), new UIPropertyMetadata(null)); + + + + + /// + /// Gets the rectangle where to place the sub menus. + /// + /// The ApplicationMenu class + /// A rectangle. + internal Rect GetSubMenuRect(Visual visual) + { + if (recentItemsList != null) + { + Rect rect = new Rect(0.0, 0.0, recentItemsList.ActualWidth, recentItemsList.ActualHeight); + rect = recentItemsList.TransformToVisual(visual).TransformBounds(rect); + return rect; + } + else return Rect.Empty; + } + + #region IKeyboardCommand Members + + void IKeyTipControl.ExecuteKeyTip() + { + this.Focus(); + this.IsOpen = true; + } + + #endregion + + protected override System.Collections.IEnumerator LogicalChildren + { + get + { + return GetLogicalChildren().GetEnumerator(); + } + } + + private IEnumerable GetLogicalChildren() + { + if (Header != null) yield return Header; + IEnumerator e = base.LogicalChildren; + while (e.MoveNext()) + { + yield return e.Current; + } + if (Footer != null) yield return Footer; + } + } +} diff --git a/Odyssey/Odyssey/Ribbon/Controls/RibbonApplicationMenuItem.cs b/Odyssey/Odyssey/Ribbon/Controls/RibbonApplicationMenuItem.cs new file mode 100644 index 0000000..ff493a3 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Controls/RibbonApplicationMenuItem.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + public class RibbonApplicationMenuItem:RibbonMenuItem + { + static RibbonApplicationMenuItem() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonApplicationMenuItem), new FrameworkPropertyMetadata(typeof(RibbonApplicationMenuItem))); + } + + protected override bool IsItemItsOwnContainerOverride(object item) + { + return item is RibbonMenuItem; + } + + protected override DependencyObject GetContainerForItemOverride() + { + return new RibbonApplicationMenuItem(); + } + + + + /// + /// Gets or sets the title for the sub menu popup. + /// This is a dependency property. + /// + public object SubMenuTitle + { + get { return (object)GetValue(SubMenuTitleProperty); } + set { SetValue(SubMenuTitleProperty, value); } + } + + public static readonly DependencyProperty SubMenuTitleProperty = + DependencyProperty.Register("SubMenuTitle", typeof(object), typeof(RibbonApplicationMenuItem), new UIPropertyMetadata(null)); + + + + /// + /// Gets or sets the content that appears in the right part of the ApplicationMenu. + /// + public object SubMenuContent + { + get { return (object)GetValue(SubMenuContentProperty); } + set { SetValue(SubMenuContentProperty, value); } + } + + public static readonly DependencyProperty SubMenuContentProperty = + DependencyProperty.Register("SubMenuContent", typeof(object), typeof(RibbonApplicationMenuItem), new UIPropertyMetadata(null)); + + + + } +} diff --git a/Odyssey/Odyssey/Ribbon/Controls/RibbonBar.Commands.cs b/Odyssey/Odyssey/Ribbon/Controls/RibbonBar.Commands.cs new file mode 100644 index 0000000..2fc8d3f --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Controls/RibbonBar.Commands.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Input; +using System.Windows; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + partial class RibbonBar + { + public static readonly RoutedUICommand AlignGroupsLeftCommand = new RoutedUICommand("Align Left", "AlignGroupsLeftCommand", typeof(RibbonBar)); + public static readonly RoutedUICommand AlignGroupsRightCommand = new RoutedUICommand("Align Right", "AlignGroupsRightCommand", typeof(RibbonBar)); + public static readonly RoutedUICommand CollapseRibbonBarCommand = new RoutedUICommand("", "CollapseRibbonBarCommand", typeof(RibbonBar)); + + public static readonly RoutedUICommand QAPlacementTopCommand = new RoutedUICommand("Show Above the Ribbon.", "QAPlacementTopCommand", typeof(RibbonBar)); + public static readonly RoutedUICommand QAPlacementBottomCommand = new RoutedUICommand("Show Below the Ribbon.", "QAPlacementBottomCommand", typeof(RibbonBar)); + + private static void RegisterCommands() + { + CommandManager.RegisterClassCommandBinding(typeof(RibbonBar), new CommandBinding(AlignGroupsLeftCommand, alignGroupsLeft)); + CommandManager.RegisterClassCommandBinding(typeof(RibbonBar), new CommandBinding(AlignGroupsRightCommand, alignGroupsRight)); + CommandManager.RegisterClassCommandBinding(typeof(RibbonBar), new CommandBinding(CollapseRibbonBarCommand, collapseRibbonBar)); + + CommandManager.RegisterClassCommandBinding(typeof(RibbonBar), new CommandBinding(QAPlacementTopCommand, QAPlacementTop, IsQAPlacementTopEnabled)); + CommandManager.RegisterClassCommandBinding(typeof(RibbonBar), new CommandBinding(QAPlacementBottomCommand, QAPlacementBottom, IsQAPlacementBottomEnabled)); + } + + private static void QAPlacementTop(object sender, ExecutedRoutedEventArgs e) + { + RibbonBar bar = (RibbonBar)sender; + bar.ToolbarPlacement = QAPlacement.Top; + } + + private static void QAPlacementBottom(object sender, ExecutedRoutedEventArgs e) + { + RibbonBar bar = (RibbonBar)sender; + bar.ToolbarPlacement = QAPlacement.Bottom; + } + + private static void IsQAPlacementTopEnabled(object sender, CanExecuteRoutedEventArgs e) + { + RibbonBar bar = (RibbonBar)sender; + e.CanExecute = bar.ToolbarPlacement == QAPlacement.Bottom; + } + + private static void IsQAPlacementBottomEnabled(object sender, CanExecuteRoutedEventArgs e) + { + RibbonBar bar = (RibbonBar)sender; + e.CanExecute = bar.ToolbarPlacement == QAPlacement.Top; + } + + private static void collapseRibbonBar(object sender, ExecutedRoutedEventArgs e) + { + RibbonBar bar = (RibbonBar)sender; + bar.IsExpanded = false; + + } + + private static void alignGroupsLeft(object sender, ExecutedRoutedEventArgs e) + { + RibbonBar bar = (RibbonBar)sender; + bar.AlignGroupsLeft(); + } + + + private static void alignGroupsRight(object sender, ExecutedRoutedEventArgs e) + { + RibbonBar bar = (RibbonBar)sender; + bar.AlignGroupsRight(); + } + } +} diff --git a/Odyssey/Odyssey/Ribbon/Controls/RibbonBar.Handlers.cs b/Odyssey/Odyssey/Ribbon/Controls/RibbonBar.Handlers.cs new file mode 100644 index 0000000..1cb61c0 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Controls/RibbonBar.Handlers.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Diagnostics; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + partial class RibbonBar + { + private void RegisterHandlers() + { + //AddHandler(RibbonBar.AlignGroupsLeftEvent, new RoutedEventHandler(OnAlignGroupsLeft)); + } + } +} diff --git a/Odyssey/Odyssey/Ribbon/Controls/RibbonBar.cs b/Odyssey/Odyssey/Ribbon/Controls/RibbonBar.cs new file mode 100644 index 0000000..f42eb23 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Controls/RibbonBar.cs @@ -0,0 +1,1436 @@ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using System.Windows.Controls.Primitives; +using System.Collections.ObjectModel; +using System.Windows.Markup; +using System.Diagnostics; +using System.ComponentModel; +using Odyssey.Controls.Classes; +using Odyssey.Controls.Ribbon.Interfaces; +using System.Collections.Specialized; +using System.Collections; +using Odyssey.Ribbon.EventArgs; +using Odyssey.Common; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + [ContentProperty("Tabs")] + [TemplatePart(Name = partGroupPanel)] + [TemplatePart(Name = partPopup)] + [TemplatePart(Name = partTabItemContainer)] + [TemplatePart(Name = partPopupGroupPanel)] + public partial class RibbonBar : Selector + { + const string partGroupPanel = "PART_GroupPanel"; + const string partPopupGroupPanel = "PART_PopupGroupPanel"; + const string partPopup = "PART_Popup"; + const string partTabItemContainer = "PART_TabItemContainer"; + const string partLeftTitle = "PART_LeftWindowTitlePlaceHolder"; + const string partRightTitle = "PART_RightWindowTitlePlaceHolder"; + const string partTopQAPresenter = "PART_TopQaPresenter"; + + static RibbonBar() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonBar), new FrameworkPropertyMetadata(typeof(RibbonBar))); + RegisterCommands(); + } + + public RibbonBar() + : base() + { + AddHandler(LoadedEvent, new RoutedEventHandler(OnLoaded)); + + AddHandler(Button.ClickEvent, new RoutedEventHandler(OnChildClick)); + AddHandler(MenuItem.ClickEvent, new RoutedEventHandler(OnMenuItemClick2)); + AddHandler(RibbonComboBox.DropDownClosedEvent, new RoutedEventHandler(OnMenuItemClick)); + AddHandler(RibbonSplitButton.ClickEvent, new RoutedEventHandler(OnMenuItemClick2)); + AddHandler(RibbonGallery.SelectionChangedEvent, new RoutedEventHandler(OnMenuItemClick)); + RegisterHandlers(); + } + + + /// + /// Closes any popups when some known routed events occured. + /// + private void OnMenuItemClick(object sender, RoutedEventArgs e) + { + FrameworkElement fe = e.OriginalSource as FrameworkElement; + if (fe != null && fe.TemplatedParent is RibbonGroup) return; + if (fe != null && fe.TemplatedParent != null) return; + if (popup != null && !(e.OriginalSource is RibbonDropDownButton)) popup.IsOpen = false; + IsMenuOpen = false; + RibbonGroup.CloseOpenedPopup(); + } + + /// + /// Closes any popups when some known routed events occured. + /// + private void OnMenuItemClick2(object sender, RoutedEventArgs e) + { + FrameworkElement fe = e.OriginalSource as FrameworkElement; + if (fe != null && fe.TemplatedParent is RibbonGroup) return; + if (fe != null && fe.TemplatedParent != null) return; + if (popup != null) popup.IsOpen = false; + IsMenuOpen = false; + RibbonGroup.CloseOpenedPopup(); + } + + + /// + /// Closes any popups when some known routed events occured. + /// + private void OnChildClick(object sender, RoutedEventArgs e) + { + if (((e.OriginalSource is IRibbonButton) || (e.OriginalSource is MenuItem))) + { + RibbonButton btn = e.OriginalSource as RibbonButton; + if (btn != null) + { + if (btn.TemplatedParent is RibbonGallery) + { + return; + } + } + RibbonGroup.CloseOpenedPopup(); + + if (popup != null) + { + // check if the source is either the left or right scrollbar button for the overlapped tab and don't + // collapse the ribbon in that case: + RibbonButton b = e.OriginalSource as RibbonButton; + if (b != null) + { + ICommand command = b.Command; + if (command == RibbonTabScroller.ScrollRightCommand || command == RibbonTabScroller.ScrollLeftCommand) return; + } + + popup.IsOpen = false; + } + } + } + + private void OnLoaded(object sender, RoutedEventArgs e) + { + AddHandler(RibbonBar.SelectedTabIndexChangedEvent, new RoutedEventHandler(SelectedIndexChanged)); + if (SelectedTabIndex >= 0) SelectedIndexChanged(this, e); + } + + /// + /// Occurs when the selected tab is changed. + /// + public event RoutedPropertyChangedEventHandler SelectedTabIndexChanged + { + add { AddHandler(RibbonBar.SelectedTabIndexChangedEvent, value); } + remove { RemoveHandler(RibbonBar.SelectedTabIndexChangedEvent, value); } + } + + private Control groupPanel; + private Control popupGroupPanel; + private Popup popup; + private Panel tabItemContainer; + private FrameworkElement leftTitleControl; + private FrameworkElement rightTitleControl; + private FrameworkElement topQAPresenterControl; + + /// + /// Gets the popup content. This property is used by RibbonToolTip. + /// + protected internal Popup Popup { get { return popup; } } + + public override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + groupPanel = GetTemplateChild(partGroupPanel) as Control; + popupGroupPanel = GetTemplateChild(partPopupGroupPanel) as Control; + + if (popup != null) + { + popup.Opened -= OnPopupOpened; + popup.Closed -= OnPopupClosed; + } + + leftTitleControl = GetTemplateChild(partLeftTitle) as FrameworkElement; + rightTitleControl = GetTemplateChild(partRightTitle) as FrameworkElement; + topQAPresenterControl = GetTemplateChild(partTopQAPresenter) as FrameworkElement; + + popup = GetTemplateChild(partPopup) as Popup; + + if (popup != null) + { + popup.Opened += new EventHandler(OnPopupOpened); + popup.Closed += new EventHandler(OnPopupClosed); + } + + if (tabItemContainer != null) tabItemContainer.Children.Clear(); + tabItemContainer = GetTemplateChild(partTabItemContainer) as Panel; + if (tabItemContainer != null) + { + foreach (RibbonTabItem tab in Tabs) + { + tabItemContainer.Children.Add(tab); + } + SetContextualTabs(ContextualTabSet); + } + + } + + protected override void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e) + { + base.OnLostKeyboardFocus(e); + if (!this.IsKeyboardFocusWithin) + { + DependencyObject focused = Keyboard.FocusedElement as DependencyObject; + if (focused == null || !this.IsAncestorOf(focused)) + { + IsMenuOpen = false; + } + } + } + + + protected virtual void OnPopupOpened(object sender, EventArgs e) + { + MeasureGroups(); + Mouse.Capture(this, CaptureMode.SubTree); + } + + protected virtual void OnPopupClosed(object sender, EventArgs e) + { + IsMenuOpen = false; + IsExpanded = false; + Mouse.Capture(null); + } + + + protected override void OnVisualParentChanged(DependencyObject oldParent) + { + FrameworkElement parent = oldParent as FrameworkElement; + if (parent != null) parent.SizeChanged -= ParentSizeChanged; + base.OnVisualParentChanged(oldParent); + + parent = this.Parent as FrameworkElement; + if (parent != null) + { + parent.SizeChanged += new SizeChangedEventHandler(ParentSizeChanged); + } + } + + public static Size InfiniteSize = new Size(double.PositiveInfinity, double.PositiveInfinity); + + void ParentSizeChanged(object sender, SizeChangedEventArgs e) + { + if (e.WidthChanged) MeasureGroups(); + } + + protected override Size ArrangeOverride(Size arrangeBounds) + { + Size size = base.ArrangeOverride(arrangeBounds); + + CalculateContextualTabSetPos(); + CalculateTitleBarPosition(); + return size; + } + + private void CalculateContextualTabSetPos() + { + RibbonContextualTabSet set = ContextualTabSet; + if (set != null) + { + double w = 0.0; + foreach (var tab in set.Tabs) w += tab.DesiredSize.Width; + set.Width = w; + } + } + + /// + /// Calculates the position and size for the title bar depending on some named template controls. + /// + private void CalculateTitleBarPosition() + { + if (!IsContextualTabVisible) + { + CalculateTitleBarPosWithoutContextualTab(); + } + else + { + CalculateTitleBarPosWithContextualTab(); + } + } + + private FrameworkElement windowTitle + { + get { return this.GetTemplateChild("PART_WindowTitle") as FrameworkElement; } + } + + private FrameworkElement buttonsPlaceHolder + { + get { return this.GetTemplateChild("PART_ButtonsPlaceholder") as FrameworkElement; } + } + + private FrameworkElement titlePlaceHolder + { + get { return this.GetTemplateChild("PART_TitlePlaceholder") as FrameworkElement; } + } + + + + private void CalculateTitleBarPosWithContextualTab() + { + + Point p1 = rightTitleControl.TranslatePoint(new Point(), titlePlaceHolder); + + Point p2 = buttonsPlaceHolder.TranslatePoint(new Point(), titlePlaceHolder); + Double w = p2.X - p1.X; + Double w2 = leftTitleControl.ActualWidth; + if (w2 > w) + { + p1 = leftTitleControl.TranslatePoint(new Point(), titlePlaceHolder); + w = w2; + } + Canvas.SetLeft(windowTitle, p1.X); + windowTitle.Width = Math.Max(0.0, w); + } + + + private void CalculateTitleBarPosWithoutContextualTab() + { + Point p1 = topQAPresenterControl.TranslatePoint(new Point(topQAPresenterControl.ActualWidth, 0), titlePlaceHolder); + Point p2 = buttonsPlaceHolder.TranslatePoint(new Point(), titlePlaceHolder); + Canvas.SetLeft(windowTitle, p1.X); + windowTitle.Width = Math.Max(0.0, p2.X - p1.X); + } + + + + const double MIN_RIBBON_WIDTH = 240; + + private void MeasureGroups() + { + Control parent = null; + if (CanMinimize) + { + parent = popupGroupPanel != null ? popupGroupPanel.Parent as Control : null; + } + else + { + parent = groupPanel != null ? groupPanel.Parent as Control : null; + } + double actualWidth = parent != null ? parent.ActualWidth : this.ActualWidth; + IsMinimized = ActualWidth < MIN_RIBBON_WIDTH; + if (!IsMinimized) + { + double left = CalculateGroupsWidth(); + + left = ExpandOrReduceGroups(left, actualWidth); + + AdjustGroupAlignment(left, actualWidth); + } + } + + private void AdjustGroupAlignment(double left, double actualWidth) + { + if (left > actualWidth) + { + if (GroupAlignment == RibbonBarAlignment.Full) GroupAlignment = RibbonBarAlignment.Left; + } + else + { + GroupAlignment = RibbonBarAlignment.Full; + } + } + + private double ExpandOrReduceGroups(double left, double actualWidth) + { + int level = GetMaxLevel(); + while (level >= 0 && left < actualWidth) + { + left = ExpandGroups(left, actualWidth, level--); + } + + if (left > actualWidth) + { + level = GetMinLevel(); + while (CanReduce() && left > actualWidth) + { + left = ReduceGroups(left, actualWidth, level++); + } + } + return left; + } + + + private double CalculateGroupsWidth() + { + double width = 0; + + foreach (RibbonGroup group in GetSelectedGroups()) + { + if (!group.IsMeasureValid) + { + group.Measure(InfiniteSize); + } + width += group.DesiredSize.Width; + } + return width; + } + + private int GetMinLevel() + { + int level = int.MaxValue; + foreach (RibbonGroup g in GetWrapPanelsByReductionOrder()) + { + level = Math.Min(level, g.ReductionLevel); + if (level == 0) break; + } + return level; + } + + private int GetMaxLevel() + { + int level = 0; + foreach (RibbonGroup g in GetWrapPanelsByReductionOrder()) + { + level = Math.Max(level, g.ReductionLevel); + } + return level; + } + + private bool CanExpand() + { + foreach (RibbonGroup g in GetWrapPanelsByReductionOrder()) + { + if (g.ReductionLevel > 0) return true; + } + return false; + } + + private bool CanReduce() + { + foreach (RibbonGroup g in GetWrapPanelsByReductionOrder()) + { + if (!g.IsMinimized) return true; + } + return false; + } + + + + public static bool GetCloseDropDownOnClick(DependencyObject obj) + { + return (bool)obj.GetValue(CloseDropDownOnClickProperty); + } + + public static void SetCloseDropDownOnClick(DependencyObject obj, bool value) + { + obj.SetValue(CloseDropDownOnClickProperty, value); + } + + // Using a DependencyProperty as the backing store for AffectsControlDropDown. This enables animation, styling, binding, etc... + public static readonly DependencyProperty CloseDropDownOnClickProperty = + DependencyProperty.RegisterAttached("CloseDropDownOnClickProperty", typeof(bool), typeof(Control), + new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender | FrameworkPropertyMetadataOptions.Inherits)); + + + + + /// + /// Gets wether the controls affects a dropdown window to close when it is clicked. + /// This is an ineritable attached property. + /// + public static bool GetAffectsDropDown(DependencyObject obj) + { + return (bool)obj.GetValue(AffectsDropDownProperty); + } + + /// + /// Sets wether the controls affects a dropdown window to close when it is clicked. + /// This is an inheritable attached property. + /// + public static void SetAffectsDropDown(DependencyObject obj, bool value) + { + obj.SetValue(AffectsDropDownProperty, value); + } + + public static readonly DependencyProperty AffectsDropDownProperty = + DependencyProperty.RegisterAttached("AffectsDropDown", typeof(bool), typeof(RibbonBar), + new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender | FrameworkPropertyMetadataOptions.Inherits)); + + + + /// + /// Gets or sets the selected TabItem + /// This is a dependency property. + /// + public RibbonTabItem SelectedTabItem + { + get { return GetTabItemFromIndex(SelectedTabIndex); } + set + { + Select(value); + } + } + + private double ReduceGroups(double left, double actualWidth, int level) + { + if (left > actualWidth) + { + foreach (RibbonGroup group in GetWrapPanelsByReductionOrder()) + { + if (group.ReductionLevel <= level) + { + double w0 = group.DesiredSize.Width; + group.Reduce(); + double w1 = group.DesiredSize.Width; + double dw = w0 - w1; + + left -= dw; + } + if (left <= actualWidth) break; + } + } + return left; + } + + private double ExpandGroups(double left, double actualWidth, int level) + { + foreach (RibbonGroup group in GetWrapPanelsByReductionOrder().Reverse()) + { + if (group.ReductionLevel > level) + { + double w0 = group.DesiredSize.Width; + group.Expand(); + group.UpdateLayout(); + double w1 = group.DesiredSize.Width; + double dw = w0 - w1; + + left -= dw; + } + if (left > actualWidth) break; + } + + return left; + } + + protected override void OnLostMouseCapture(MouseEventArgs e) + { + CheckPopupTabToClose(e); + base.OnLostMouseCapture(e); + } + + private void CheckPopupTabToClose(MouseEventArgs e) + { + if (popup == null) return; + if (!CanMinimize) return; + FrameworkElement fe = e.OriginalSource as FrameworkElement; + // if (fe != null && fe.TemplatedParent != null) return; + FrameworkElement captured = Mouse.Captured as FrameworkElement; + if (captured != this && popup != null) + { + UIElement child = this.popup.Child; + if (e.OriginalSource == this) + { + if (captured != null && captured.TemplatedParent != null) return; + if ((Mouse.Captured == null) || !child.IsAncestorOf(Mouse.Captured as DependencyObject)) + { + this.IsExpanded = false; + e.Handled = true; + } + } + else if (child.IsAncestorOf(e.OriginalSource as DependencyObject)) + { + if (this.IsExpanded && (Mouse.Captured == null)) + { + Mouse.Capture(this, CaptureMode.SubTree); + e.Handled = true; + } + } + else + { + + if (!child.IsLogicalAncestorOf(captured)) + { + this.IsExpanded = false; + e.Handled = true; + } + } + } + } + + protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e) + { + base.OnPreviewMouseLeftButtonDown(e); + if (e.OriginalSource == this) + { + Mouse.Capture(null); + } + } + + + private IEnumerable GetWrapPanelsByReductionOrder() + { + RibbonTabItem item = SelectedTabItem; + if (item != null && item.ReductionOrder != null) + { + return GetWrapPanelsFromCollection(item.ReductionOrder); + } + return GetDefaultWrapPanelOrder(); + } + + private IEnumerable GetDefaultWrapPanelOrder() + { + if (SelectedGroups != null) + { + for (int i = this.SelectedGroups.Count - 1; i >= 0; i--) + { + RibbonGroup group = SelectedGroups[i] as RibbonGroup; + if (group != null) yield return group; + } + } + } + + + private IEnumerable GetWrapPanelsFromCollection(StringCollection names) + { + if (SelectedGroups != null) + { + foreach (var item in SelectedGroups) + { + RibbonGroup group = (RibbonGroup)item; + foreach (string name in names) + { + if (group.Name.Equals(name)) yield return group; + } + } + } + } + + + private void SelectedIndexChanged(object sender, RoutedEventArgs e) + { + IsMenuOpen = false; + if (SelectedTabIndex >= 0) + { + RibbonTabItem item = GetTabItemFromIndex(SelectedTabIndex); + SetSelectedTabItem(item); + } + MeasureGroups(); + } + + + private RibbonTabItem GetTabItemFromIndex(int index) + { + if (index < 0) return null; + if (index < Tabs.Count && Tabs.Count > 0) return Tabs[index]; + + index -= Tabs.Count; + if (ContextualTabSet != null) + { + int c = ContextualTabSet.Tabs.Count; + if (c > 0 && c > index) return ContextualTabSet.Tabs[index]; + } + return null; + } + + private void SetSelectedTabItem(RibbonTabItem item) + { + SelectedGroups = item != null ? item.Items : null; + ItemsSource = SelectedGroups; + int index = SelectedTabIndex; + for (int i = 0; i < Tabs.Count; i++) + { + item = Tabs[i]; + item.IsSelected = i == index; + } + RibbonContextualTabSet currentSet = ContextualTabSet; + if (currentSet != null) + { + index -= Tabs.Count; + currentSet.SetSelectedTabItem(index); + } + if (contextualTabSets != null) + { + foreach (var set in contextualTabSets) + { + if (set != currentSet) + { + set.SetSelectedTabItem(-1); + } + } + } + } + + IEnumerable GetSelectedGroups() + { + if (SelectedGroups != null) + { + foreach (var item in SelectedGroups) + { + yield return (RibbonGroup)item; + } + } + } + + + + /// + /// Gets the groups of the selected tab item. + /// This is a dependency property. + /// + /// + /// This dependency property is required for the ControlTemplate to attach the groups. + /// + public ItemCollection SelectedGroups + { + get { return (ItemCollection)GetValue(SelectedGroupsProperty); } + private set { SetValue(SelectedGroupsProperty, value); } + } + + // Using a DependencyProperty as the backing store for SelectedGroup. This enables animation, styling, binding, etc... + public static readonly DependencyProperty SelectedGroupsProperty = + DependencyProperty.Register("SelectedGroups", typeof(ItemCollection), typeof(RibbonBar), + new FrameworkPropertyMetadata(null, + FrameworkPropertyMetadataOptions.None, + SelectedGroupsPropertyChanged)); + + public static void SelectedGroupsPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + RibbonBar bar = (RibbonBar)o; + + bar.SelectedItem = e.NewValue; + bar.OnSelectedGroupsChanged(e); + } + + protected virtual void OnSelectedGroupsChanged(DependencyPropertyChangedEventArgs e) + { + } + + + /// + /// Selects the specified Tabitem. + /// + private void Select(RibbonTabItem tabItem) + { + int index = GetTabIndex(tabItem); + bool isCurrent = index == this.SelectedTabIndex; + this.SelectedTabIndex = index; + if (CanMinimize) + { + if (isCurrent && IsExpanded) this.IsExpanded ^= true; else this.IsExpanded = true; + } + } + + private int GetTabIndex(RibbonTabItem tabItem) + { + int index = Tabs.IndexOf(tabItem); + if (index >= 0) return index; + + if (ContextualTabSet != null) + { + index = ContextualTabSet.Tabs.IndexOf(tabItem); + return index + Tabs.Count; + } + return -1; + } + + + + /// + /// Gets or sets whether the tab is expanded when CanMinimize is set to true. + /// This is a dependency property. + /// + public bool IsExpanded + { + get { return (bool)GetValue(IsExpandedProperty); } + set { SetValue(IsExpandedProperty, value); } + } + + // Using a DependencyProperty as the backing store for IsExpanded. This enables animation, styling, binding, etc... + public static readonly DependencyProperty IsExpandedProperty = + DependencyProperty.Register("IsExpanded", typeof(bool), typeof(RibbonBar), + new UIPropertyMetadata(false, ExpandedPropertyChanged)); + + + static void ExpandedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + RibbonBar bar = (RibbonBar)d; + bar.OnExpandedChanged(e); + } + + /// + /// Occurs when the IsExpanded property has changed. + /// + /// + protected virtual void OnExpandedChanged(DependencyPropertyChangedEventArgs e) + { + bool isExpanded = (bool)e.NewValue; + if (popup != null) + { + if (GroupAlignment == RibbonBarAlignment.Right) GroupAlignment = RibbonBarAlignment.Left; + popup.IsOpen = isExpanded; + } + if (SelectedTabItem != null && CanMinimize) + { + SelectedTabItem.IsSelected = isExpanded; + } + } + + + internal RibbonBarAlignment GroupAlignment + { + get { return (RibbonBarAlignment)GetValue(GroupAlignmentProperty); } + set { SetValue(GroupAlignmentProperty, value); } + } + + // Using a DependencyProperty as the backing store for GroupAlignment. This enables animation, styling, binding, etc... + internal static readonly DependencyProperty GroupAlignmentProperty = + DependencyProperty.Register("GroupAlignment", typeof(RibbonBarAlignment), typeof(RibbonBar), + new UIPropertyMetadata(RibbonBarAlignment.Full, GroupAlignmentPropertyChanged)); + + + public static void GroupAlignmentPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + RibbonBar bar = (RibbonBar)d; + RibbonBarAlignment alignment = (RibbonBarAlignment)e.NewValue; + + bar.SetGroupAlignment(alignment); + } + + protected virtual void SetGroupAlignment(RibbonBarAlignment alignment) + { + } + + + + /// + /// Gets or sets whether the ribbon bar can minimize the tab. + /// This is a dependency property. + /// + public bool CanMinimize + { + get { return (bool)GetValue(CanMinimizeProperty); } + set { SetValue(CanMinimizeProperty, value); } + } + + // Using a DependencyProperty as the backing store for CanMinimize. This enables animation, styling, binding, etc... + public static readonly DependencyProperty CanMinimizeProperty = + DependencyProperty.Register("CanMinimize", typeof(bool), typeof(RibbonBar), + new FrameworkPropertyMetadata(false, + FrameworkPropertyMetadataOptions.AffectsRender | + FrameworkPropertyMetadataOptions.AffectsMeasure | + FrameworkPropertyMetadataOptions.AffectsRender, + MinimizePropertyChanged)); + + + private static void MinimizePropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + RibbonBar bar = (RibbonBar)o; + bool canMinimize = (bool)e.NewValue; + + bar.OnMinimizeChanged(canMinimize); + } + + protected virtual void OnMinimizeChanged(bool canMinimize) + { + if (SelectedTabItem != null) + { + SelectedTabItem.IsSelected = !IsExpanded; + } + } + + + /// + /// Gets or sets the index of the selected tab. + /// The index is equivalent to the order of the visual tab items, including possible contextual tabs. + /// This is a dependency property. + /// + public int SelectedTabIndex + { + get { return (int)GetValue(SelectedTabIndexProperty); } + set { SetValue(SelectedTabIndexProperty, value); } + } + + // Using a DependencyProperty as the backing store for SelectedIndex. This enables animation, styling, binding, etc... + public static readonly DependencyProperty SelectedTabIndexProperty = + DependencyProperty.Register("SelectedTabIndex", typeof(int), typeof(RibbonBar), new UIPropertyMetadata(0, SelectedTabIndexPropertyChanged)); + + + public static readonly RoutedEvent SelectedTabIndexChangedEvent = EventManager.RegisterRoutedEvent("SelectedTabIndexChanged", + RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler), typeof(RibbonBar)); + + + static void SelectedTabIndexPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + RibbonBar bar = (RibbonBar)d; + int oldIndex = (int)e.OldValue; + int newIndex = (int)e.NewValue; + + + RoutedPropertyChangedEventArgs args = new RoutedPropertyChangedEventArgs(oldIndex, newIndex, SelectedTabIndexChangedEvent); + bar.RaiseEvent(args); + bar.OnSelectedTabIndexChanged(args); + } + + protected virtual void OnSelectedTabIndexChanged(RoutedPropertyChangedEventArgs e) + { + Color = SelectedTabItem != null && SelectedTabItem.tabSet != null ? SelectedTabItem.tabSet.Color : Colors.Transparent; + } + + + + private ObservableCollection tabs; + + /// + /// Gets the collection of RibbonTabItems. + /// + public Collection Tabs + { + get + { + if (tabs == null) + { + tabs = new ObservableCollection(); + tabs.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(OnTabItemCollectionChanged); + } + return tabs; + } + } + + + /// + /// Occurs when the TabItems collection has changed. + /// + protected virtual void OnTabItemCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) + { + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + foreach (RibbonTabItem tab in e.NewItems) + { + tab.RibbonBar = this; + } + break; + + case NotifyCollectionChangedAction.Remove: + foreach (RibbonTabItem tab in e.OldItems) + { + tab.RibbonBar = null; + } + break; + } + } + + private ObservableCollection contextualTabSets; + + /// + /// Gets the collection of ContextualTabSets. + /// + public Collection ContextualTabSets + { + get + { + if (contextualTabSets == null) + { + contextualTabSets = new ObservableCollection(); + contextualTabSets.CollectionChanged += new NotifyCollectionChangedEventHandler(OnContextualTabSetsChanged); + } + return contextualTabSets; + } + } + + /// + /// Occurs when the ContextualTabSets collection has changed. + /// + protected virtual void OnContextualTabSetsChanged(object sender, NotifyCollectionChangedEventArgs e) + { + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + foreach (RibbonContextualTabSet set in e.NewItems) + { + set.RibbonBar = this; + } + break; + + case NotifyCollectionChangedAction.Remove: + foreach (RibbonContextualTabSet set in e.OldItems) + { + set.RibbonBar = null; + } + break; + } + } + + + /// + /// Gets whether a contextual tab is visible. + /// + public bool IsContextualTabVisible + { + get { return (bool)GetValue(IsContextualTabVisibleProperty); } + protected set { SetValue(IsContextualTabVisiblePropertyKey, value); } + } + + private static readonly DependencyPropertyKey IsContextualTabVisiblePropertyKey = + DependencyProperty.RegisterReadOnly("IsContextualTabVisible", typeof(bool), typeof(RibbonBar), new FrameworkPropertyMetadata(false, + FrameworkPropertyMetadataOptions.AffectsArrange | + FrameworkPropertyMetadataOptions.AffectsMeasure | + FrameworkPropertyMetadataOptions.AffectsParentMeasure, + OnIsContextualTabVisiblePropertyChanged)); + + public static readonly DependencyProperty IsContextualTabVisibleProperty = IsContextualTabVisiblePropertyKey.DependencyProperty; + + + private static void OnIsContextualTabVisiblePropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + RibbonBar bar = (RibbonBar)o; + bool newValue = (bool)e.NewValue; + + bar.InvalidateMeasure(); + } + + /// + /// Gets or sets the selected ContextualTabSet. + /// This is a dependency property. + /// + public RibbonContextualTabSet ContextualTabSet + { + get { return (RibbonContextualTabSet)GetValue(ContextualTabSetProperty); } + set { SetValue(ContextualTabSetProperty, value); } + } + + // Using a DependencyProperty as the backing store for ContextualTabSet. This enables animation, styling, binding, etc... + public static readonly DependencyProperty ContextualTabSetProperty = + DependencyProperty.Register("ContextualTabSet", typeof(RibbonContextualTabSet), typeof(RibbonBar), + new FrameworkPropertyMetadata(null, + FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, + ContextualTabSetPropertyChanged)); + + public static void ContextualTabSetPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + RibbonBar bar = (RibbonBar)o; + bar.OnContextualTabSetChanged(e); + } + + protected virtual void OnContextualTabSetChanged(DependencyPropertyChangedEventArgs e) + { + RibbonContextualTabSet oldSet = e.OldValue as RibbonContextualTabSet; + RibbonContextualTabSet newSet = e.NewValue as RibbonContextualTabSet; + + IsContextualTabVisible = newSet != null; + + RemovePreviousContextualTabs(oldSet); + SetContextualTabs(newSet); + + AdjustContextualTabSet(e.NewValue as RibbonContextualTabSet); + int index = GetTabIndex(SelectedTabItem); + if (index < 0) SelectedTabIndex = 0; + + } + + /// + /// Gets the first visible RibbonTabItem otherwise null. + /// + public RibbonTabItem FirstVisibleTabItem + { + get + { + foreach (RibbonTabItem tab in Tabs) + { + if (tab.IsVisible) return tab; + } + return null; + } + } + + /// + /// Ensures that only a visible RibbonTabItem is selected. + /// + public void EnsureTabIsVisible() + { + + RibbonTabItem item = SelectedTabItem; + if (item == null || (item.Visibility != Visibility.Visible)) + { + SelectedTabItem = FirstVisibleTabItem; + } + } + + private void AdjustContextualTabSet(RibbonContextualTabSet set) + { + } + + private void SetContextualTabs(RibbonContextualTabSet newSet) + { + if (newSet != null) newSet.IsSelected = true; + if (tabItemContainer != null && newSet != null) + { + foreach (RibbonTabItem tab in newSet.Tabs) + { + tabItemContainer.Children.Add(tab); + tab.IsContextual = true; + } + } + } + + private void RemovePreviousContextualTabs(RibbonContextualTabSet oldSet) + { + if (oldSet != null) oldSet.IsSelected = false; + if (tabItemContainer != null && oldSet != null) + { + foreach (RibbonTabItem tab in oldSet.Tabs) + { + tab.IsContextual = false; + tabItemContainer.Children.Remove(tab); + } + } + } + + /// + /// Gets an enumeration of the currently visible tabs including the contextual tabs. + /// + public IEnumerable VisibleTabs + { + get + { + foreach (var tab in Tabs) if (tab.IsVisible) yield return tab; + if (ContextualTabSet != null) + { + foreach (var tab in ContextualTabSet.Tabs) if (tab.IsVisible) yield return tab; + } + } + } + + /// + /// Gets the index for VisibleTabs of the specified tab, otherwise -1. + /// + /// The TabItem for which to determine the index. + /// The index related to , otherwise -1. + public int IndexOfVisibleTab(RibbonTabItem item) + { + if (item == null) return -1; + int index = 0; + foreach (var tab in VisibleTabs) + { + if (tab == item) return index; + index++; + } + return -1; + } + + /// + /// Gets the by index. + /// + /// The index of the tab. + /// , otherwise null. + public RibbonTabItem VisibleTabFromIndex(int index) + { + if (index < 0) return null; + return VisibleTabs.Skip(index).FirstOrDefault(); + } + + + + + + /// + /// Gets an enumerator for logical child elements of this element. + /// + /// + /// + /// An enumerator for logical child elements of this element. + /// + protected override IEnumerator LogicalChildren + { + get + { + return GetLogicalChildren().GetEnumerator(); + } + } + + private IEnumerable GetLogicalChildren() + { + if (ApplicationMenu != null) yield return ApplicationMenu; + foreach (var tab in Tabs) yield return tab; + foreach (var ct in ContextualTabSets) yield return ct; + if (QAToolBar != null) yield return QAToolBar; + + } + + private void AlignGroupsLeft() + { + GroupAlignment = RibbonBarAlignment.Left; + InvalidateArrange(); + } + + private void AlignGroupsRight() + { + GroupAlignment = RibbonBarAlignment.Right; + InvalidateArrange(); + } + + + /// + /// Gets or sets the RibbonApplicationMenu for the RibbonBar. + /// This is a dependency property. + /// + public RibbonApplicationMenu ApplicationMenu + { + get { return (RibbonApplicationMenu)GetValue(ApplicationMenuProperty); } + set { SetValue(ApplicationMenuProperty, value); } + } + + // Using a DependencyProperty as the backing store for ApplicationMenu. This enables animation, styling, binding, etc... + public static readonly DependencyProperty ApplicationMenuProperty = + DependencyProperty.Register("ApplicationMenu", typeof(RibbonApplicationMenu), typeof(RibbonBar), new UIPropertyMetadata(null)); + + + + /// + /// Gets or sets whether the ApplicationMenu is open. + /// Thisis a dependency property. + /// + public bool IsMenuOpen + { + get { return (bool)GetValue(IsMenuOpenProperty); } + set { SetValue(IsMenuOpenProperty, value); } + } + + public static readonly DependencyProperty IsMenuOpenProperty = + DependencyProperty.Register("IsMenuOpen", typeof(bool), typeof(RibbonBar), new UIPropertyMetadata(false, MenuOpenPropertyChanged)); + + private static void MenuOpenPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + RibbonBar bar = o as RibbonBar; + bool newValue = (bool)e.NewValue; + if (newValue && bar.IsExpanded && bar.CanMinimize) + { + bar.IsExpanded = false; + } + + if (((bool)e.NewValue) == false) Mouse.Capture(null); + bar.OnMenuOpenedChanged(newValue); + } + + protected virtual void OnMenuOpenedChanged(bool newValue) + { + } + + [AttachedPropertyBrowsableForChildren] + public static RibbonSize GetSize(DependencyObject obj) + { + return (RibbonSize)obj.GetValue(SizeProperty); + } + + public static void SetSize(DependencyObject obj, RibbonSize value) + { + obj.SetValue(SizeProperty, value); + } + + public static readonly DependencyProperty SizeProperty = + DependencyProperty.RegisterAttached("Size", typeof(RibbonSize), typeof(RibbonBar), + new FrameworkPropertyMetadata(RibbonSize.Large, + FrameworkPropertyMetadataOptions.None)); + + /// + /// Gets the custom Reduction sizes for a IRibbonControl. + /// This is an attached dependency property. + /// + [AttachedPropertyBrowsableForChildren] + public static RibbonSizeCollection GetReduction(DependencyObject obj) + { + return (RibbonSizeCollection)obj.GetValue(ReductionProperty); + } + + /// + /// Sets the custom Reduction sizes for a IRibbonControl. + /// This is an attached dependency property. + /// + [TypeConverter(typeof(RibbonReductionCollectionConverter))] + public static void SetReduction(DependencyObject obj, RibbonSizeCollection value) + { + obj.SetValue(ReductionProperty, value); + } + + public static readonly DependencyProperty ReductionProperty = + DependencyProperty.RegisterAttached("Reduction", typeof(RibbonSizeCollection), typeof(RibbonBar), new UIPropertyMetadata(null)); + + /// + /// Gets the minimum size for a IRibbonControl. + /// This is an attached dependency property. + /// + public static RibbonSize GetMinSize(DependencyObject obj) + { + return (RibbonSize)obj.GetValue(MinSizeProperty); + } + + /// + /// Sets the minimum size for a IRibbonControl. + /// This is an attached dependency property. + /// + public static void SetMinSize(DependencyObject obj, RibbonSize value) + { + obj.SetValue(MinSizeProperty, value); + } + + public static readonly DependencyProperty MinSizeProperty = + DependencyProperty.RegisterAttached("MinSize", typeof(RibbonSize), typeof(RibbonBar), new UIPropertyMetadata(RibbonSize.Minimized)); + + /// + /// Gets the maximum size for a IRibbonControl. + /// This is an attached dependency property. + /// + public static RibbonSize GetMaxSize(DependencyObject obj) + { + return (RibbonSize)obj.GetValue(MaxSizeProperty); + } + + /// + /// Sets the maximum size for a IRibbonControl. + /// This is an attached dependency property. + /// + public static void SetMaxSize(DependencyObject obj, RibbonSize value) + { + obj.SetValue(MaxSizeProperty, value); + } + + public static readonly DependencyProperty MaxSizeProperty = + DependencyProperty.RegisterAttached("MaxSize", typeof(RibbonSize), typeof(RibbonBar), new UIPropertyMetadata(RibbonSize.Large)); + + /// + /// Gets the color for the active tab. + /// This is a dependency property. + /// + public Color Color + { + get { return (Color)GetValue(ColorProperty); } + private set { SetValue(ColorPropertyKey, value); } + } + + private static readonly DependencyPropertyKey ColorPropertyKey = + DependencyProperty.RegisterReadOnly("Color", typeof(Color), typeof(RibbonBar), new UIPropertyMetadata(Colors.Transparent)); + + + public static readonly DependencyProperty ColorProperty = ColorPropertyKey.DependencyProperty; + + + /// + /// Gets or sets the QuickAccess Toolbar. + /// This is a dependency property. + /// + public RibbonQAToolBar QAToolBar + { + get { return (RibbonQAToolBar)GetValue(QAToolBarProperty); } + set { SetValue(QAToolBarProperty, value); } + } + + public static readonly DependencyProperty QAToolBarProperty = + DependencyProperty.Register("QAToolBar", typeof(RibbonQAToolBar), typeof(RibbonBar), new UIPropertyMetadata(null)); + + + + /// + /// Gets or sets the placement for the QuickAccess Toolbar. + /// This is a dependency property. + /// + public QAPlacement ToolbarPlacement + { + get { return (QAPlacement)GetValue(ToolbarPlacementProperty); } + set { SetValue(ToolbarPlacementProperty, value); } + } + + public static readonly DependencyProperty ToolbarPlacementProperty = + DependencyProperty.Register("ToolbarPlacement", typeof(QAPlacement), typeof(RibbonBar), + new FrameworkPropertyMetadata(QAPlacement.Top, + FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, + ToolbarPlacementPropertyChanged)); + + public static void ToolbarPlacementPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + RibbonBar bar = (RibbonBar)o; + if (bar.QAToolBar != null) bar.QAToolBar.ToolBarPlacement = (QAPlacement)e.NewValue; + } + + + + + /// + /// Gets whether the ribbon bar and tabs are visible. + /// If set to false, only the ApplicationMenu and the QuickAccessToolbar is available. + /// This is a dependency property. + /// + public bool IsRibbonVisible + { + get { return (bool)GetValue(IsRibbonVisibleProperty); } + set { SetValue(IsRibbonVisibleProperty, value); } + } + + public static readonly DependencyProperty IsRibbonVisibleProperty = + DependencyProperty.Register("IsRibbonVisible", typeof(bool), typeof(RibbonBar), new FrameworkPropertyMetadata(true)); + + + /// + /// Gets whether the Ribbonbar is minimized due to a thresold underrun of the minimum width. + /// This is a dependency property. + /// + public bool IsMinimized + { + get { return (bool)GetValue(IsMinimizedProperty); } + private set { SetValue(IsMinimizedPropertyKey, value); } + } + + private static readonly DependencyPropertyKey IsMinimizedPropertyKey = + DependencyProperty.RegisterReadOnly("IsMinimized", typeof(bool), typeof(RibbonBar), new FrameworkPropertyMetadata(false)); + + public static readonly DependencyProperty IsMinimizedProperty = IsMinimizedPropertyKey.DependencyProperty; + + + + + /// + /// Gets the left position for the Window Title. + /// + public double TitleLeft + { + get { return (double)GetValue(TitleLeftProperty); } + private set { SetValue(TitleLeftProperty, value); } + } + + // Using a DependencyProperty as the backing store for TitleLeft. This enables animation, styling, binding, etc... + public static readonly DependencyProperty TitleLeftProperty = + DependencyProperty.Register("TitleLeft", typeof(double), typeof(RibbonBar), new UIPropertyMetadata(32.0)); + + + + public bool ShowTitleOnRight + { + get { return (bool)GetValue(ShowTitleOnRightProperty); } + set { SetValue(ShowTitleOnRightProperty, value); } + } + + // Using a DependencyProperty as the backing store for ShowTitleOnRight. This enables animation, styling, binding, etc... + public static readonly DependencyProperty ShowTitleOnRightProperty = + DependencyProperty.Register("ShowTitleOnRight", typeof(bool), typeof(RibbonBar), new UIPropertyMetadata(false)); + + + + } + +} diff --git a/Odyssey/Odyssey/Ribbon/Controls/RibbonButton.cs b/Odyssey/Odyssey/Ribbon/Controls/RibbonButton.cs new file mode 100644 index 0000000..fd9d017 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Controls/RibbonButton.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using System.Windows.Controls.Primitives; +using System.Windows.Markup; +using Odyssey.Controls.Ribbon.Interfaces; +using System.Windows.Media.Effects; +using Odyssey.Controls.Interfaces; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + [ContentProperty("Content")] + public class RibbonButton : Button, IRibbonButton,IKeyTipControl + { + + static RibbonButton() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonButton), new FrameworkPropertyMetadata(typeof(RibbonButton))); + } + + public CornerRadius CornerRadius + { + get { return (CornerRadius)GetValue(CornerRadiusProperty); } + set { SetValue(CornerRadiusProperty, value); } + } + + // Using a DependencyProperty as the backing store for CornerRadius. This enables animation, styling, binding, etc... + public static readonly DependencyProperty CornerRadiusProperty = + DependencyProperty.Register("CornerRadius", typeof(CornerRadius), typeof(RibbonButton), new UIPropertyMetadata(new CornerRadius(3))); + + + public ImageSource LargeImage + { + get { return (ImageSource)GetValue(LargeImageProperty); } + set { SetValue(LargeImageProperty, value); } + } + + // Using a DependencyProperty as the backing store for LargeImage. This enables animation, styling, binding, etc... + public static readonly DependencyProperty LargeImageProperty = + DependencyProperty.Register("LargeImage", typeof(ImageSource), typeof(RibbonButton), new FrameworkPropertyMetadata(null)); + + + public ImageSource SmallImage + { + get { return (ImageSource)GetValue(SmallImageProperty); } + set { SetValue(SmallImageProperty, value); } + } + + // Using a DependencyProperty as the backing store for SmallImage. This enables animation, styling, binding, etc... + public static readonly DependencyProperty SmallImageProperty = + DependencyProperty.Register("SmallImage", typeof(ImageSource), typeof(RibbonButton), new FrameworkPropertyMetadata(null)); + + + + public bool IsFlat + { + get { return (bool)GetValue(IsFlatProperty); } + set { SetValue(IsFlatProperty, value); } + } + + // Using a DependencyProperty as the backing store for IsFlat. This enables animation, styling, binding, etc... + public static readonly DependencyProperty IsFlatProperty = + DependencyProperty.Register("IsFlat", typeof(bool), typeof(RibbonButton), new UIPropertyMetadata(true)); + + + + + /// + /// Gets or sets how to stretch an image inside an IRibbonButton + /// This is an attached dependency property. + /// + public static Stretch GetImageStretch(DependencyObject obj) + { + return (Stretch)obj.GetValue(ImageStretchProperty); + } + + public static void SetImageStretch(DependencyObject obj, Stretch value) + { + obj.SetValue(ImageStretchProperty, value); + } + + // Using a DependencyProperty as the backing store for ImageStretch. This enables animation, styling, binding, etc... + public static readonly DependencyProperty ImageStretchProperty = + DependencyProperty.RegisterAttached("ImageStretch", typeof(Stretch), typeof(RibbonButton), new UIPropertyMetadata(Stretch.Uniform)); + + + + + //public BitmapScalingMode LargeImageScalingMode + //{ + // get { return (BitmapScalingMode)GetValue(LargeImageScalingModeProperty); } + // set { SetValue(LargeImageScalingModeProperty, value); } + //} + + //public static readonly DependencyProperty LargeImageScalingModeProperty = RibbonBar.LargeImageScalingModeProperty.AddOwner(typeof(RibbonButton)); + + + + #region IKeyboardCommand Members + + public void ExecuteKeyTip() + { + OnClick(); + } + + #endregion + } +} diff --git a/Odyssey/Odyssey/Ribbon/Controls/RibbonButtonGroup.cs b/Odyssey/Odyssey/Ribbon/Controls/RibbonButtonGroup.cs new file mode 100644 index 0000000..fa0fac1 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Controls/RibbonButtonGroup.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using System.Windows.Markup; +using Odyssey.Controls.Ribbon.Interfaces; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + + [ContentProperty("Items")] + public class RibbonButtonGroup : ItemsControl + { + static RibbonButtonGroup() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonButtonGroup), new FrameworkPropertyMetadata(typeof(RibbonButtonGroup))); + } + + + public CornerRadius CornerRadius + { + get { return (CornerRadius)GetValue(CornerRadiusProperty); } + set { SetValue(CornerRadiusProperty, value); } + } + + // Using a DependencyProperty as the backing store for CornerRadius. This enables animation, styling, binding, etc... + public static readonly DependencyProperty CornerRadiusProperty = + DependencyProperty.Register("CornerRadius", typeof(CornerRadius), typeof(RibbonButtonGroup), new UIPropertyMetadata(new CornerRadius(3))); + + + + protected override void OnItemsChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e) + { + base.OnItemsChanged(e); + int max = Items.Count-1; + CornerRadius r = CornerRadius; + for (int i = 0; i <= max; i++) + { + IRibbonButton b = Items[i] as IRibbonButton; + if (i == 0 && i == max) b.CornerRadius = CornerRadius; + else if (i == 0) b.CornerRadius = new CornerRadius(r.TopLeft, 0d, 0d, r.BottomLeft); + else if (i == max) b.CornerRadius = new CornerRadius(0d, r.TopRight, r.BottomRight, 0d); + else b.CornerRadius = new CornerRadius(0); + } + } + + + protected override DependencyObject GetContainerForItemOverride() + { + return new RibbonButton(); + } + + protected override void PrepareContainerForItemOverride(DependencyObject element, object item) + { + base.PrepareContainerForItemOverride(element, item); + IRibbonButton b = element as IRibbonButton; + if (b != null) + { + RibbonBar.SetSize(element, RibbonSize.Small); + } + } + } +} diff --git a/Odyssey/Odyssey/Ribbon/Controls/RibbonButtonStyle.cs b/Odyssey/Odyssey/Ribbon/Controls/RibbonButtonStyle.cs new file mode 100644 index 0000000..fc7722b --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Controls/RibbonButtonStyle.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + public enum RibbonButtonStyle + { + /// + /// Button only. + /// + Button, + + /// + /// Includes a drop down button. + /// + DropDown, + + /// + /// Appears as a popup button with a popup symbol. + /// + Popup + } +} diff --git a/Odyssey/Odyssey/Ribbon/Controls/RibbonChrome.cs b/Odyssey/Odyssey/Ribbon/Controls/RibbonChrome.cs new file mode 100644 index 0000000..32d4130 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Controls/RibbonChrome.cs @@ -0,0 +1,192 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Controls; +using System.Windows; +using System.Windows.Media; +using System.Windows.Markup; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + /// + /// Renders the chrome for all Ribbon controls. + /// + [ContentProperty("Content")] + public class RibbonChrome:ContentControl + { + static RibbonChrome() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonChrome), new FrameworkPropertyMetadata(typeof(RibbonChrome))); + } + + public bool RenderPressed + { + get { return (bool)GetValue(RenderPressedProperty); } + set { SetValue(RenderPressedProperty, value); } + } + + public static readonly DependencyProperty RenderPressedProperty = + DependencyProperty.Register("RenderPressed", typeof(bool), typeof(RibbonChrome), new UIPropertyMetadata(false)); + + + public bool RenderMouseOver + { + get { return (bool)GetValue(RenderMouseOverProperty); } + set { SetValue(RenderMouseOverProperty, value); } + } + + // Using a DependencyProperty as the backing store for RenderMouseOver. This enables animation, styling, binding, etc... + public static readonly DependencyProperty RenderMouseOverProperty = + DependencyProperty.Register("RenderMouseOver", typeof(bool), typeof(RibbonChrome), new UIPropertyMetadata(false)); + + + + public Brush MouseOverBackground + { + get { return (Brush)GetValue(MouseOverBackgroundProperty); } + set { SetValue(MouseOverBackgroundProperty, value); } + } + + public static readonly DependencyProperty MouseOverBackgroundProperty = + DependencyProperty.Register("MouseOverBackground", typeof(Brush), typeof(RibbonChrome), new UIPropertyMetadata(null)); + + + + public Brush MousePressedBackground + { + get { return (Brush)GetValue(MousePressedBackgroundProperty); } + set { SetValue(MousePressedBackgroundProperty, value); } + } + + // Using a DependencyProperty as the backing store for MousePressedBackground. This enables animation, styling, binding, etc... + public static readonly DependencyProperty MousePressedBackgroundProperty = + DependencyProperty.Register("MousePressedBackground", typeof(Brush), typeof(RibbonChrome), new UIPropertyMetadata(null)); + + + public Brush MouseCheckedBackground + { + get { return (Brush)GetValue(MouseCheckedBackgroundProperty); } + set { SetValue(MouseCheckedBackgroundProperty, value); } + } + + // Using a DependencyProperty as the backing store for MouseCheckedBackground. This enables animation, styling, binding, etc... + public static readonly DependencyProperty MouseCheckedBackgroundProperty = + DependencyProperty.Register("MouseCheckedBackground", typeof(Brush), typeof(RibbonChrome), new UIPropertyMetadata(null)); + + + public CornerRadius CornerRadius + { + get { return (CornerRadius)GetValue(CornerRadiusProperty); } + set { SetValue(CornerRadiusProperty, value); } + } + + // Using a DependencyProperty as the backing store for CornerRadius. This enables animation, styling, binding, etc... + public static readonly DependencyProperty CornerRadiusProperty = + DependencyProperty.Register("CornerRadius", typeof(CornerRadius), typeof(RibbonChrome), new UIPropertyMetadata(new CornerRadius(0))); + + + public bool RenderFlat + { + get { return (bool)GetValue(RenderFlatProperty); } + set { SetValue(RenderFlatProperty, value); } + } + + // Using a DependencyProperty as the backing store for RenderFlat. This enables animation, styling, binding, etc... + public static readonly DependencyProperty RenderFlatProperty = + DependencyProperty.Register("RenderFlat", typeof(bool), typeof(RibbonChrome), new UIPropertyMetadata(true)); + + + + + + public static bool GetAnimateTransition(DependencyObject obj) + { + return (bool)obj.GetValue(AnimateTransitionProperty); + } + + public static void SetAnimateTransition(DependencyObject obj, bool value) + { + obj.SetValue(AnimateTransitionProperty, value); + } + + public static readonly DependencyProperty AnimateTransitionProperty = + DependencyProperty.RegisterAttached("AnimateTransition", typeof(bool), typeof(RibbonChrome), new FrameworkPropertyMetadata(false,FrameworkPropertyMetadataOptions.Inherits)); + + + + public bool ShowInnerMouseOverBorder + { + get { return (bool)GetValue(ShowInnerMouseOverBorderProperty); } + set { SetValue(ShowInnerMouseOverBorderProperty, value); } + } + + public static readonly DependencyProperty ShowInnerMouseOverBorderProperty = + DependencyProperty.Register("ShowInnerMouseOverBorder", typeof(bool), typeof(RibbonChrome), new UIPropertyMetadata(true)); + + + public Thickness InnerBorderThickness + { + get { return (Thickness)GetValue(InnerBorderThicknessProperty); } + set { SetValue(InnerBorderThicknessProperty, value); } + } + + public static readonly DependencyProperty InnerBorderThicknessProperty = + DependencyProperty.Register("InnerBorderThickness", typeof(Thickness), typeof(RibbonChrome), new UIPropertyMetadata(new Thickness(1,1,0,0))); + + + public bool RenderEnabled + { + get { return (bool)GetValue(RenderEnabledProperty); } + set { SetValue(RenderEnabledProperty, value); } + } + + public static readonly DependencyProperty RenderEnabledProperty = + DependencyProperty.Register("RenderEnabled", typeof(bool), typeof(RibbonChrome), new UIPropertyMetadata(true)); + + + public Brush MouseOverBorderBrush + { + get { return (Brush)GetValue(MouseOverBorderBrushProperty); } + set { SetValue(MouseOverBorderBrushProperty, value); } + } + + // Using a DependencyProperty as the backing store for MouseOverBorderBrush. This enables animation, styling, binding, etc... + public static readonly DependencyProperty MouseOverBorderBrushProperty = + DependencyProperty.Register("MouseOverBorderBrush", typeof(Brush), typeof(RibbonChrome), new UIPropertyMetadata(null)); + + + + /// + /// Gets wether a control is rendered in grayscale when IsEnabled is set to true. + /// This is an attached inheritable dependency property. The default value is true. + /// + public static bool GetIsGrayScaleEnabled(DependencyObject obj) + { + return (bool)obj.GetValue(IsGrayScaleEnabledProperty); + } + + /// + /// Sets wether to render a control in grayscale when IsEnabled is set to true. + /// This is an attached inheritable dependency property. The default value is true. + /// + public static void SetIsGrayScaleEnabled(DependencyObject obj, bool value) + { + obj.SetValue(IsGrayScaleEnabledProperty, value); + } + + // Using a DependencyProperty as the backing store for IsGrayScaleEnabled. This enables animation, styling, binding, etc... + public static readonly DependencyProperty IsGrayScaleEnabledProperty = + DependencyProperty.RegisterAttached("IsGrayScaleEnabled", typeof(bool), typeof(RibbonChrome), + new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.Inherits)); + + + + + } +} diff --git a/Odyssey/Odyssey/Ribbon/Controls/RibbonComboBox.cs b/Odyssey/Odyssey/Ribbon/Controls/RibbonComboBox.cs new file mode 100644 index 0000000..18670cf --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Controls/RibbonComboBox.cs @@ -0,0 +1,192 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Controls; +using System.Windows; +using System.Windows.Media; +using System.Windows.Controls.Primitives; +using System.ComponentModel; +using Odyssey.Controls.Ribbon.Interfaces; +using System.Diagnostics; +using Odyssey.Controls.Interfaces; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + public class RibbonComboBox : ComboBox, IRibbonControl, IRibbonStretch,IKeyTipControl + { + static RibbonComboBox() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonComboBox), new FrameworkPropertyMetadata(typeof(RibbonComboBox))); + } + + public RibbonComboBox() + : base() + { + } + + + /// + /// Gets or sets the Image of the combobox. + /// + public ImageSource Image + { + get { return (ImageSource)GetValue(ImageProperty); } + set { SetValue(ImageProperty, value); } + } + + public static readonly DependencyProperty ImageProperty = + DependencyProperty.Register("Image", typeof(ImageSource), typeof(RibbonComboBox), new UIPropertyMetadata(null)); + + + /// + /// Gets or sets the title of the combobox that appears with Appearance = Medium. + /// + public string Title + { + get { return (string)GetValue(TitleProperty); } + set { SetValue(TitleProperty, value); } + } + + public static readonly DependencyProperty TitleProperty = + DependencyProperty.Register("Title", typeof(string), typeof(RibbonComboBox), new UIPropertyMetadata("")); + + + + /// + /// Gets or sets the width for the label. + /// + public double LabelWidth + { + get { return (double)GetValue(LabelWidthProperty); } + set { SetValue(LabelWidthProperty, value); } + } + + public static readonly DependencyProperty LabelWidthProperty = + DependencyProperty.Register("LabelWidth", typeof(double), typeof(RibbonComboBox), new UIPropertyMetadata(double.NaN)); + + + + /// + /// Gets or sets the with for the combobox. + /// This is a dependency property. + /// + public double ContentWidth + { + get { return (double)GetValue(ContentWidthProperty); } + set { SetValue(ContentWidthProperty, value); } + } + + public static readonly DependencyProperty ContentWidthProperty = + DependencyProperty.Register("ContentWidth", typeof(double), typeof(RibbonComboBox), new UIPropertyMetadata(double.NaN)); + + + protected override bool IsItemItsOwnContainerOverride(object item) + { + //return item is RibbonMenuItem; + return item is RibbonComboBoxItem; + } + + protected override DependencyObject GetContainerForItemOverride() + { + return new RibbonComboBoxItem(); + } + + protected override void OnDropDownClosed(EventArgs e) + { + base.OnDropDownClosed(e); + RoutedEventArgs args = new RoutedEventArgs(RibbonComboBox.DropDownClosedEvent); + RaiseEvent(args); + } + + public static readonly RoutedEvent DropDownClosedEvent = EventManager.RegisterRoutedEvent("DropDownClosedEvent", + RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(RibbonComboBox)); + + /// + /// Occurs when the DropDown is closed. + /// The Ribbonbar uses this event to determine wether to collapse a collapsible ribbon after a combobox is closed. + /// + public event RoutedEventHandler RoutedDropDownClosed + { + add { AddHandler(DropDownClosedEvent, value); } + remove { RemoveHandler(DropDownClosedEvent, value); } + } + + public object DropDownFooter + { + get { return (object)GetValue(DropDownFooterProperty); } + set { SetValue(DropDownFooterProperty, value); } + } + + // Using a DependencyProperty as the backing store for DropDownFooter. This enables animation, styling, binding, etc... + public static readonly DependencyProperty DropDownFooterProperty = + DependencyProperty.Register("DropDownFooter", typeof(object), typeof(RibbonComboBox), new UIPropertyMetadata(null)); + + + + + public object DropDownHeader + { + get { return (object)GetValue(DropDownHeaderProperty); } + set { SetValue(DropDownHeaderProperty, value); } + } + + // Using a DependencyProperty as the backing store for DropDownHeader. This enables animation, styling, binding, etc... + public static readonly DependencyProperty DropDownHeaderProperty = + DependencyProperty.Register("DropDownHeader", typeof(object), typeof(RibbonComboBox), new UIPropertyMetadata(null)); + + + + public DataTemplate DropDownHeaderTemplate + { + get { return (DataTemplate)GetValue(DropDownHeaderTemplateProperty); } + set { SetValue(DropDownHeaderTemplateProperty, value); } + } + + // Using a DependencyProperty as the backing store for DropDownHeaderTemplate. This enables animation, styling, binding, etc... + public static readonly DependencyProperty DropDownHeaderTemplateProperty = + DependencyProperty.Register("DropDownHeaderTemplate", typeof(DataTemplate), typeof(RibbonComboBox), new UIPropertyMetadata(null)); + + + + public DataTemplate DropDownFooterTemplate + { + get { return (DataTemplate)GetValue(DropDownFooterTemplateProperty); } + set { SetValue(DropDownFooterTemplateProperty, value); } + } + + // Using a DependencyProperty as the backing store for DropDownFooterTemplate. This enables animation, styling, binding, etc... + public static readonly DependencyProperty DropDownFooterTemplateProperty = + DependencyProperty.Register("DropDownFooterTemplate", typeof(DataTemplate), typeof(RibbonComboBox), new UIPropertyMetadata(null)); + + + + protected override void OnPreviewMouseLeftButtonDown(System.Windows.Input.MouseButtonEventArgs e) + { + if (!IsEditable && e.Source==this) + { + this.IsDropDownOpen ^= true; + e.Handled = true; + } + base.OnPreviewMouseLeftButtonDown(e); + } + + #region IKeyboardCommand Members + + public void ExecuteKeyTip() + { + Focus(); + if (HasItems) + { + IsDropDownOpen = true; + } + } + + #endregion + } +} diff --git a/Odyssey/Odyssey/Ribbon/Controls/RibbonComboBoxItem.cs b/Odyssey/Odyssey/Ribbon/Controls/RibbonComboBoxItem.cs new file mode 100644 index 0000000..f9794d0 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Controls/RibbonComboBoxItem.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Controls; +using System.Windows; +using System.Windows.Media; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + public class RibbonComboBoxItem:ComboBoxItem + { + static RibbonComboBoxItem() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonComboBoxItem), new FrameworkPropertyMetadata(typeof(RibbonComboBoxItem))); + } + + + + public ImageSource Image + { + get { return (ImageSource)GetValue(ImageProperty); } + set { SetValue(ImageProperty, value); } + } + + // Using a DependencyProperty as the backing store for Image. This enables animation, styling, binding, etc... + public static readonly DependencyProperty ImageProperty = + DependencyProperty.Register("Image", typeof(ImageSource), typeof(RibbonComboBoxItem), new UIPropertyMetadata(null)); + + } +} diff --git a/Odyssey/Odyssey/Ribbon/Controls/RibbonContextualTabSet.cs b/Odyssey/Odyssey/Ribbon/Controls/RibbonContextualTabSet.cs new file mode 100644 index 0000000..0d302a0 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Controls/RibbonContextualTabSet.cs @@ -0,0 +1,168 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Controls; +using System.Collections.ObjectModel; +using System.Windows.Media; +using Odyssey.Controls.Ribbon.Interfaces; +using System.Windows; +using System.Windows.Markup; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + [ContentProperty("Tabs")] + public class RibbonContextualTabSet:Control,IRibbonControl + { + static RibbonContextualTabSet() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonContextualTabSet), new FrameworkPropertyMetadata(typeof(RibbonContextualTabSet))); + } + + + private ObservableCollection tabs; + + /// + /// Gets the collection of RibbonTabItems. + /// + public Collection Tabs + { + get + { + if (tabs == null) + { + tabs = new ObservableCollection(); + tabs.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(OnTabItemCollectionChanged); + } + return tabs; + } + } + + /// + /// Occurs when the TabItems collection has changed. + /// + protected virtual void OnTabItemCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) + { + switch (e.Action) + { + case System.Collections.Specialized.NotifyCollectionChangedAction.Add: + foreach (RibbonTabItem tab in e.NewItems) + { + tab.tabSet = this; + tab.RibbonBar = RibbonBar; + } + break; + + case System.Collections.Specialized.NotifyCollectionChangedAction.Remove: + foreach (RibbonTabItem tab in e.OldItems) + { + tab.tabSet = null; + tab.RibbonBar = null; + } + break; + } + } + + private RibbonBar ribbonBar; + public RibbonBar RibbonBar + { + get {return ribbonBar;} + internal set + { + if (ribbonBar != value) + { + ribbonBar = value; + foreach (var tab in Tabs) tab.RibbonBar = value; + } + } + } + + + /// + /// Gets or sets the color for the ContextualTabSet. + /// This is a dependency property. + /// + public Color Color + { + get { return (Color)GetValue(ColorProperty); } + set { SetValue(ColorProperty, value); } + } + + // Using a DependencyProperty as the backing store for Color. This enables animation, styling, binding, etc... + public static readonly DependencyProperty ColorProperty = + DependencyProperty.Register("Color", typeof(Color), typeof(RibbonContextualTabSet), new UIPropertyMetadata(Color.FromArgb(64, 255, 255, 255))); + + + + + /// + /// Gets or sets the title for the ContextualTabSet. + /// This is a dependency property. + /// + public string Title + { + get { return (string)GetValue(TitleProperty); } + set { SetValue(TitleProperty, value); } + } + + // Using a DependencyProperty as the backing store for Title. This enables animation, styling, binding, etc... + public static readonly DependencyProperty TitleProperty = + DependencyProperty.Register("Title", typeof(string), typeof(RibbonContextualTabSet), new UIPropertyMetadata("")); + + + internal void SetSelectedTabItem(int index) + { + for (int i = 0; i < Tabs.Count; i++) + { + RibbonTabItem item = Tabs[i]; + item.IsSelected = i == index; + } + } + + + + /// + /// Gets whether the Tabset is selected. + /// + public bool IsSelected + { + get { return (bool)GetValue(IsSelectedProperty); } + internal set { SetValue(IsSelectedPropertyKey, value); } + } + + + + // Using a DependencyProperty as the backing store for IsSelected. This enables animation, styling, binding, etc... + private static readonly DependencyPropertyKey IsSelectedPropertyKey = + DependencyProperty.RegisterReadOnly("IsSelected", typeof(bool), typeof(RibbonContextualTabSet), new UIPropertyMetadata(false)); + + private static readonly DependencyProperty IsSelectedProperty = IsSelectedPropertyKey.DependencyProperty; + + /// + /// Select the first tab in the RibbonBar of the contextual tab set when the control is clicked: + /// + /// + protected override void OnMouseLeftButtonDown(System.Windows.Input.MouseButtonEventArgs e) + { + base.OnMouseLeftButtonDown(e); + if (this.tabs != null && this.tabs.Count > 0) + { + RibbonBar.SelectedTabItem = tabs[0]; + } + } + + protected override System.Collections.IEnumerator LogicalChildren + { + get + { + return Tabs.GetEnumerator(); + } + } + + } +} diff --git a/Odyssey/Odyssey/Ribbon/Controls/RibbonDropDownButton.cs b/Odyssey/Odyssey/Ribbon/Controls/RibbonDropDownButton.cs new file mode 100644 index 0000000..8aed1fc --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Controls/RibbonDropDownButton.cs @@ -0,0 +1,887 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using System.Windows.Controls.Primitives; +using System.Windows.Markup; +using System.Diagnostics; +using Odyssey.Controls.Ribbon.Interfaces; +using System.ComponentModel; +using Odyssey.Common; +using Odyssey.Controls.Interfaces; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + + [TemplatePart(Name = partPopup)] + [ContentProperty("Items")] + public class RibbonDropDownButton : ItemsControl, IRibbonButton, ICommandSource,IKeyTipControl + { + const string partPopup = "PART_Popup"; + + static RibbonDropDownButton() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonDropDownButton), new FrameworkPropertyMetadata(typeof(RibbonDropDownButton))); + } + + public RibbonDropDownButton() + { + AddHandler(MenuItem.ClickEvent, new RoutedEventHandler(OnMenuItemClickedEvent)); + AddHandler(RibbonButton.ClickEvent, new RoutedEventHandler(OnButtonClickEvent)); + AddHandler(RibbonComboBox.DropDownClosedEvent, new RoutedEventHandler(OnMenuItemClickedEvent)); + AddHandler(RibbonDropDownButton.PopupClosedEvent, new RoutedEventHandler(OnPopupClosedEvent)); + AddHandler(RibbonGallery.SelectionChangedEvent, new RoutedEventHandler(OnGalerySelected)); + + } + + protected virtual void OnGalerySelected(object sender, RoutedEventArgs e) + { + IsDropDownPressed = false; + } + + /// + /// If any RibbonDropDownButton has closed it's popup, so also close it. + /// This is necassary for nested RibbonDropDownButtons to ensure that all are properly closed. + /// + protected virtual void OnPopupClosedEvent(object sender, RoutedEventArgs e) + { + if (IsDropDownPressed) + { + IsDropDownPressed = false; + } + } + + protected virtual void OnButtonClickEvent(object sender, RoutedEventArgs e) + { + if (IsDropDownPressed) + { + if (e.OriginalSource == this) return; + DependencyObject dep = e.OriginalSource as DependencyObject; + if (!(e.OriginalSource is RibbonButton) && !(e.OriginalSource is RibbonDropDownButton)) + { + if (dep != null && !RibbonOption.GetCloseDropDownOnClick(dep)) return; + } + else + { + if (dep != null && !RibbonBar.GetAffectsDropDown(dep)) return; + } + if (IsAncestorType(e.OriginalSource, typeof(RibbonComboBox))) return; + IsDropDownPressed = false; + } + } + + protected virtual void OnMenuItemClickedEvent(object sender, RoutedEventArgs e) + { + if (IsDropDownPressed) + { + if (e.OriginalSource == this) return; + IsDropDownPressed = false; + } + } + + private bool IsAncestorType(object child, Type type) + { + FrameworkElement parent = child as FrameworkElement; + while (parent != null) + { + if (parent.TemplatedParent != null && parent.TemplatedParent.GetType() == type) return true; + if (parent.GetType() == type) return true; + parent = parent.Parent as FrameworkElement; + } + return false; + } + + + internal protected Popup Popup { get; private set; } + + + + + public override void OnApplyTemplate() + { + if (Popup != null) + { + Popup.Closed -= OnPopupClosed; + Popup.Opened -= OnPopupOpened; + } + Popup = GetTemplateChild(partPopup) as Popup; + if (Popup != null) + { + Popup.Closed += new EventHandler(OnPopupClosed); + Popup.Opened += new EventHandler(OnPopupOpened); + Popup.StaysOpen = true; + } + + // base.OnApplyTemplate(); + } + + public static readonly RoutedEvent ClickEvent = EventManager.RegisterRoutedEvent("Click", + RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(RibbonDropDownButton)); + + + + + public bool IsCheckable + { + get { return (bool)GetValue(IsCheckableProperty); } + set { SetValue(IsCheckableProperty, value); } + } + + // Using a DependencyProperty as the backing store for IsCheckable. This enables animation, styling, binding, etc... + public static readonly DependencyProperty IsCheckableProperty = + DependencyProperty.Register("IsCheckable", typeof(bool), typeof(RibbonDropDownButton), new UIPropertyMetadata(false)); + + + + public bool IsChecked + { + get { return (bool)GetValue(IsCheckedProperty); } + set { SetValue(IsCheckedProperty, value); } + } + + public static readonly DependencyProperty IsCheckedProperty = + DependencyProperty.Register("IsChecked", typeof(bool), typeof(RibbonDropDownButton), new UIPropertyMetadata(false, CheckedPropertyChanged)); + + + + private static void CheckedPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + } + + public bool IsPressed + { + get { return (bool)GetValue(IsPressedProperty); } + protected set { SetValue(IsPressedPropertyKey, value); } + } + + private static readonly DependencyPropertyKey IsPressedPropertyKey = + DependencyProperty.RegisterReadOnly("IsPressed", typeof(bool), typeof(RibbonDropDownButton), new UIPropertyMetadata(false)); + + public static DependencyProperty IsPressedProperty = IsPressedPropertyKey.DependencyProperty; + + + protected virtual void OnClick() + { + ToggleChecked(); + } + + private void ToggleChecked() + { + if (IsCheckable) + { + Rect rect = new Rect(new Point(), base.RenderSize); + if (((Mouse.LeftButton == MouseButtonState.Pressed) && base.IsMouseOver) && rect.Contains(Mouse.GetPosition(this))) + { + + if (IsChecked) base.ClearValue(IsCheckedProperty); else IsChecked = true; + } + } + } + + + public static readonly RoutedEvent PopupOpenedEvent = EventManager.RegisterRoutedEvent("PopupOpened", + RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(RibbonDropDownButton)); + + public static readonly RoutedEvent PopupClosedEvent = EventManager.RegisterRoutedEvent("PopupClosed", + RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(RibbonDropDownButton)); + + public event RoutedEventHandler PopupOpened + { + add { AddHandler(PopupOpenedEvent, value); } + remove { RemoveHandler(PopupOpenedEvent, value); } + } + + public event RoutedEventHandler PopupClosed + { + add { AddHandler(PopupClosedEvent, value); } + remove { RemoveHandler(PopupClosedEvent, value); } + } + + protected virtual void OnPopupOpened(object sender, EventArgs e) + { + + IsDropDownPressed = true; + RoutedEventArgs args = new RoutedEventArgs(RibbonDropDownButton.PopupOpenedEvent); + if (Popup != null && Popup.Child != null) Popup.Child.Focus(); + RaiseEvent(args); + // Mouse.Capture(this, CaptureMode.SubTree); + } + + protected virtual void OnPopupClosed(object sender, EventArgs e) + { + if (Mouse.Captured == this) + { + Mouse.Capture(null); + } + + IsChecked = false; + //base.ClearValue(IsDropDownPressedProperty); + isDropDownOpen = false; + RoutedEventArgs args = new RoutedEventArgs(RibbonDropDownButton.PopupClosedEvent); + RaiseEvent(args); + } + + + + public object Content + { + get { return (object)GetValue(ContentProperty); } + set { SetValue(ContentProperty, value); } + } + + // Using a DependencyProperty as the backing store for Content. This enables animation, styling, binding, etc... + public static readonly DependencyProperty ContentProperty = + DependencyProperty.Register("Content", typeof(object), typeof(RibbonDropDownButton), new UIPropertyMetadata(null)); + + + + + public ImageSource SmallImage + { + get { return (ImageSource)GetValue(SmallImageProperty); } + set { SetValue(SmallImageProperty, value); } + } + + // Using a DependencyProperty as the backing store for SmallImage. This enables animation, styling, binding, etc... + public static readonly DependencyProperty SmallImageProperty = + DependencyProperty.Register("SmallImage", typeof(ImageSource), typeof(RibbonDropDownButton), new UIPropertyMetadata(null)); + + + + public ImageSource LargeImage + { + get { return (ImageSource)GetValue(LargeImageProperty); } + set { SetValue(LargeImageProperty, value); } + } + + // Using a DependencyProperty as the backing store for LargeImage. This enables animation, styling, binding, etc... + public static readonly DependencyProperty LargeImageProperty = + DependencyProperty.Register("LargeImage", typeof(ImageSource), typeof(RibbonDropDownButton), new UIPropertyMetadata(null)); + + public CornerRadius CornerRadius + { + get { return (CornerRadius)GetValue(CornerRadiusProperty); } + set { SetValue(CornerRadiusProperty, value); } + } + + // Using a DependencyProperty as the backing store for CornerRadius. This enables animation, styling, binding, etc... + public static readonly DependencyProperty CornerRadiusProperty = + DependencyProperty.Register("CornerRadius", typeof(CornerRadius), typeof(RibbonDropDownButton), new UIPropertyMetadata(new CornerRadius(3))); + + + /// + /// Gets or sets whether the drop down button is down. + /// + public bool IsDropDownPressed + { + get { return (bool)GetValue(IsDropDownPressedProperty); } + set + { + isDropDownOpen = value; + SetValue(IsDropDownPressedProperty, value); + } + } + + // Using a DependencyProperty as the backing store for IsDropDown. This enables animation, styling, binding, etc... + public static readonly DependencyProperty IsDropDownPressedProperty = + DependencyProperty.Register("IsDropDownPressed", typeof(bool), typeof(RibbonDropDownButton), + new UIPropertyMetadata(false, IsDropDownPressedPropertyChangedCallback)); + + static void IsDropDownPressedPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + bool newValue = (bool)e.NewValue; + RibbonDropDownButton btn = d as RibbonDropDownButton; + + if (newValue) Mouse.Capture(btn, CaptureMode.SubTree); + btn.OnDropDownPressedChanged((bool)e.OldValue, (bool)e.NewValue); + + } + + + /// + /// Gets the target for the popup placement. + /// + protected virtual UIElement PlacementTarget + { + get { return this; } + } + + protected virtual void OnDropDownPressedChanged(bool oldValue, bool newValue) + { + UpdateToolTip(newValue); + + if (Popup != null) + { + if (newValue) + { + Popup.PlacementTarget = PlacementTarget; + CloseOpenedPopup(this); + } + else + { + CloseOpenedPopup(null); + } + Popup.IsOpen = newValue; + } + } + + object toolTip; + + /// + /// When the popup is open, the tooltip must not be shown. + /// + private void UpdateToolTip(bool newValue) + { + if (newValue) + { + toolTip = ToolTip; + ToolTip = null; + } + else + { + ToolTip = toolTip; + } + } + + protected override void OnKeyDown(KeyEventArgs e) + { + if (e.Key == Key.Enter || e.Key == Key.Space) { IsDropDownPressed = true; e.Handled = true; } + switch (e.Key) + { + case Key.Escape: + IsDropDownPressed = false; + e.Handled = true; + break; + + //case Key.Down: + // SelectNext(); + // e.Handled = true; + // break; + } + base.OnKeyDown(e); + } + + private void SelectNext() + { + ItemsPresenter ip = GetTemplateChild("PART_Items") as ItemsPresenter; + if (ip != null) + { + ip.Focus(); + } + } + + private bool isDropDownOpen = false; + + private static RibbonDropDownButton DroppedDownButton; + + protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) + { + HandleMouseLeftButtonDown(e); + base.OnMouseLeftButtonDown(e); + } + + protected virtual void HandleMouseLeftButtonDown(MouseButtonEventArgs e) + { + if (!e.Handled) + { + UpdateIsPressed(); + if (!IsDropDownPressed) + { + EnsurePopupRemainsOnMouseUp(); + if (this.IsAncestorOf(e.OriginalSource as DependencyObject)) + { + ToggleDropDownState(); + e.Handled = true; + } + } + else + { + ToggleDropDownState(); + } + OnClick(); + } + } + + protected override void OnMouseLeave(MouseEventArgs e) + { + base.OnMouseLeave(e); + this.UpdateIsPressed(); + } + + /// + /// Code snipped from original MenuItem class using Reflector: + /// + private void UpdateIsPressed() + { + Rect rect = new Rect(new Point(), base.RenderSize); + if (((Mouse.LeftButton == MouseButtonState.Pressed) && base.IsMouseOver) && rect.Contains(Mouse.GetPosition(this))) + { + this.IsPressed = true; + } + else + { + base.ClearValue(IsPressedPropertyKey); + } + + } + + protected override void OnIsKeyboardFocusedChanged(DependencyPropertyChangedEventArgs e) + { + base.OnIsKeyboardFocusedChanged(e); + if (this.IsDropDownPressed && !base.IsKeyboardFocusWithin) + { + DependencyObject focusedElement = Keyboard.FocusedElement as DependencyObject; + if ((focusedElement == null) || (!this.IsDropDownPressed && (ItemsControl.ItemsControlFromItemContainer(focusedElement) != this))) + { + IsDropDownPressed = false; + } + } + } + protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e) + { + if (IsDropDownPressed) + { + UIElement originalSource = e.OriginalSource as UIElement; + + if (!(this.IsLogicalAncestorOf(originalSource))) + { + IsDropDownPressed = false; + e.Handled = true; + } + } + base.OnPreviewMouseLeftButtonDown(e); + + } + + protected override void OnLostMouseCapture(MouseEventArgs e) + { + base.OnLostMouseCapture(e); + if (IsDropDownPressed) + { + FrameworkElement fe = e.OriginalSource as FrameworkElement; + FrameworkElement captured = Mouse.Captured as FrameworkElement; + if (captured != this && Popup != null) + { + UIElement child = this.Popup.Child; + if (e.OriginalSource == this) + { + if ((Mouse.Captured == null) || !child.IsLogicalAncestorOf(Mouse.Captured as UIElement)) + { + this.IsDropDownPressed = false; + e.Handled = true; + } + } + else if (child.IsAncestorOf(e.OriginalSource as DependencyObject)) + { + if (this.IsDropDownPressed && (Mouse.Captured == null)) + { + Mouse.Capture(this, CaptureMode.SubTree); + e.Handled = true; + } + } + else if (!this.IsLogicalAncestorOf(Mouse.Captured as UIElement)) + { + IsDropDownPressed = false; + } + } + // if (IsDropDownPressed) Mouse.Capture(this, CaptureMode.SubTree); + } + } + + + + + protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) + { + HandleMouseLeftButtonUp(e); + base.OnMouseLeftButtonUp(e); + } + + protected virtual void HandleMouseLeftButtonUp(MouseButtonEventArgs e) + { + if (!e.Handled) + { + UpdateIsPressed(); + isDropDownOpen = IsDropDownPressed; + } + } + + protected virtual void ToggleDropDownState() + { + Rect rect = new Rect(new Point(), base.RenderSize); + if (((Mouse.LeftButton == MouseButtonState.Pressed) && base.IsMouseOver) && rect.Contains(Mouse.GetPosition(this))) + { + isDropDownOpen ^= true; + SetValue(IsDropDownPressedProperty, isDropDownOpen); + } + } + + + public static void CloseOpenedPopup(RibbonDropDownButton caller) + { + RibbonDropDownButton btn = DroppedDownButton; + if (btn != null && (btn != caller)) + { + FrameworkElement parent = btn.Popup != null ? btn.Popup.Child as FrameworkElement : btn; + if (!parent.IsLogicalAncestorOf(caller)) + { + if (btn.Popup != null) btn.Popup.IsOpen = false; + btn.IsDropDownPressed = false; + } + } + DroppedDownButton = caller; + } + + + public bool IsFlat + { + get { return (bool)GetValue(IsFlatProperty); } + set { SetValue(IsFlatProperty, value); } + } + + // Using a DependencyProperty as the backing store for IsFlat. This enables animation, styling, binding, etc... + public static readonly DependencyProperty IsFlatProperty = + DependencyProperty.Register("IsFlat", typeof(bool), typeof(RibbonDropDownButton), new UIPropertyMetadata(true)); + + + public bool ShowDropDownSymbol + { + get { return (bool)GetValue(ShowDropDownSymbolProperty); } + set { SetValue(ShowDropDownSymbolProperty, value); } + } + + // Using a DependencyProperty as the backing store for ShowDropDownSymbol. This enables animation, styling, binding, etc... + public static readonly DependencyProperty ShowDropDownSymbolProperty = + DependencyProperty.Register("ShowDropDownSymbol", typeof(bool), typeof(RibbonDropDownButton), new UIPropertyMetadata(true)); + + + + /// + /// Gets or sets the maximum height for the dropdown panel. + /// This is a dependency property. + /// + public double MaxDropDownHeight + { + get { return (double)GetValue(MaxDropDownHeightProperty); } + set { SetValue(MaxDropDownHeightProperty, value); } + } + + // Using a DependencyProperty as the backing store for MaxDropDownHeight. This enables animation, styling, binding, etc... + public static readonly DependencyProperty MaxDropDownHeightProperty = + DependencyProperty.Register("MaxDropDownHeight", typeof(double), typeof(RibbonDropDownButton), new UIPropertyMetadata(double.NaN)); + + + /// + /// An item can be any possible element, not necassarily (but prefered) a RibbonMenuItem: + /// + protected override bool IsItemItsOwnContainerOverride(object item) + { + return item is MenuItem || item is Separator; + } + + protected override DependencyObject GetContainerForItemOverride() + { + return new RibbonMenuItem(); + } + + protected override void PrepareContainerForItemOverride(DependencyObject element, object item) + { + base.PrepareContainerForItemOverride(element, item); + if (item is Separator) + { + Control c = element as Control; + if (c != null) + { + c.IsEnabled = false; + c.HorizontalContentAlignment = HorizontalAlignment.Stretch; + } + } + } + + + public object DropDownFooter + { + get { return (object)GetValue(DropDownFooterProperty); } + set { SetValue(DropDownFooterProperty, value); } + } + + // Using a DependencyProperty as the backing store for DropDownFooter. This enables animation, styling, binding, etc... + public static readonly DependencyProperty DropDownFooterProperty = + DependencyProperty.Register("DropDownFooter", typeof(object), typeof(RibbonDropDownButton), new UIPropertyMetadata(null)); + + + + + public object DropDownHeader + { + get { return (object)GetValue(DropDownHeaderProperty); } + set { SetValue(DropDownHeaderProperty, value); } + } + + // Using a DependencyProperty as the backing store for DropDownHeader. This enables animation, styling, binding, etc... + public static readonly DependencyProperty DropDownHeaderProperty = + DependencyProperty.Register("DropDownHeader", typeof(object), typeof(RibbonDropDownButton), new UIPropertyMetadata(null)); + + + + public DataTemplate DropDownHeaderTemplate + { + get { return (DataTemplate)GetValue(DropDownHeaderTemplateProperty); } + set { SetValue(DropDownHeaderTemplateProperty, value); } + } + + // Using a DependencyProperty as the backing store for DropDownHeaderTemplate. This enables animation, styling, binding, etc... + public static readonly DependencyProperty DropDownHeaderTemplateProperty = + DependencyProperty.Register("DropDownHeaderTemplate", typeof(DataTemplate), typeof(RibbonDropDownButton), new UIPropertyMetadata(null)); + + + + public DataTemplate DropDownFooterTemplate + { + get { return (DataTemplate)GetValue(DropDownFooterTemplateProperty); } + set { SetValue(DropDownFooterTemplateProperty, value); } + } + + // Using a DependencyProperty as the backing store for DropDownFooterTemplate. This enables animation, styling, binding, etc... + public static readonly DependencyProperty DropDownFooterTemplateProperty = + DependencyProperty.Register("DropDownFooterTemplate", typeof(DataTemplate), typeof(RibbonDropDownButton), new UIPropertyMetadata(null)); + + protected void EnsurePopupRemainsOnMouseUp() + { + if (Popup != null) Popup.StaysOpen = true; + } + + protected void EnsurePopupDoesNotStayOpen() + { + //if (popup != null) + //{ + // popup.StaysOpen = false; + //} + } + + + + public PopupAnimation PopupAnimation + { + get { return (PopupAnimation)GetValue(PopupAnimationProperty); } + set { SetValue(PopupAnimationProperty, value); } + } + + // Using a DependencyProperty as the backing store for PopupAnimation. This enables animation, styling, binding, etc... + public static readonly DependencyProperty PopupAnimationProperty = + DependencyProperty.Register("PopupAnimation", typeof(PopupAnimation), typeof(RibbonDropDownButton), new UIPropertyMetadata(PopupAnimation.Fade)); + + + + + public PlacementMode PopupPlacement + { + get { return (PlacementMode)GetValue(PopupPlacementProperty); } + set { SetValue(PopupPlacementProperty, value); } + } + + // Using a DependencyProperty as the backing store for PopupPlacement. This enables animation, styling, binding, etc... + public static readonly DependencyProperty PopupPlacementProperty = + DependencyProperty.Register("PopupPlacement", typeof(PlacementMode), typeof(RibbonDropDownButton), new UIPropertyMetadata(PlacementMode.Bottom)); + + + + + /// + /// Gets or sets the with for the drop down menu. + /// This is a dependency property. + /// + public double DropDownWidth + { + get { return (double)GetValue(DropDownWidthProperty); } + set { SetValue(DropDownWidthProperty, value); } + } + + // Using a DependencyProperty as the backing store for DropDownWidth. This enables animation, styling, binding, etc... + public static readonly DependencyProperty DropDownWidthProperty = + DependencyProperty.Register("DropDownWidth", typeof(double), typeof(RibbonDropDownButton), new UIPropertyMetadata(double.NaN)); + + + + public BitmapScalingMode BitmapScalingMode + { + get { return (BitmapScalingMode)GetValue(BitmapScalingModeProperty); } + set { SetValue(BitmapScalingModeProperty, value); } + } + + // Using a DependencyProperty as the backing store for BitmapScalingMode. This enables animation, styling, binding, etc... + public static readonly DependencyProperty BitmapScalingModeProperty = + DependencyProperty.Register("BitmapScalingMode", typeof(BitmapScalingMode), typeof(RibbonDropDownButton), new UIPropertyMetadata(BitmapScalingMode.NearestNeighbor)); + + + + + public EdgeMode EdgeMode + { + get { return (EdgeMode)GetValue(EdgeModeProperty); } + set { SetValue(EdgeModeProperty, value); } + } + + // Using a DependencyProperty as the backing store for EdgeMode. This enables animation, styling, binding, etc... + public static readonly DependencyProperty EdgeModeProperty = + DependencyProperty.Register("EdgeMode", typeof(EdgeMode), typeof(RibbonDropDownButton), new UIPropertyMetadata(EdgeMode.Aliased)); + + + protected override System.Collections.IEnumerator LogicalChildren + { + get + { + List list = new List(); + if (DropDownHeader != null) list.Add(DropDownHeader); + foreach (var item in Items) + { + list.Add(item); + } + if (DropDownFooter != null) list.Add(DropDownFooter); + return list.GetEnumerator(); + } + } + + #region ICommandSource Members + + + + public ICommand Command + { + get { return (ICommand)GetValue(CommandProperty); } + set { SetValue(CommandProperty, value); } + } + + public static readonly DependencyProperty CommandProperty = + DependencyProperty.Register("Command", typeof(ICommand), typeof(RibbonDropDownButton), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnCommandPropertyChanged))); + + + private static void OnCommandPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + RibbonDropDownButton btn = (RibbonDropDownButton)d; + btn.OnCommandChanged(btn, (ICommand)e.OldValue, (ICommand)e.NewValue); + } + + // Keep a copy of the handler so it doesn't get garbage collected. + private EventHandler canExecuteChangedHandler; + + protected virtual void OnCommandChanged(object sender, ICommand oldCommand, ICommand newCommand) + { + if (oldCommand != null) oldCommand.CanExecuteChanged -= OnCanExecuteChanged; + if (newCommand != null) + { + canExecuteChangedHandler = new EventHandler(OnCanExecuteChanged); + newCommand.CanExecuteChanged += canExecuteChangedHandler; + } + + UpdateCanExecute(); + } + + protected virtual void OnCanExecuteChanged(object sender, EventArgs e) + { + this.UpdateCanExecute(); + } + + private void UpdateCanExecute() + { + if (this.Command != null) + { + this.CanExecute = CanExecuteCommandSource(this); + } + else + { + this.CanExecute = true; + } + + // finally notify the ui to reflect the changes: + CoerceValue(IsEnabledProperty); + } + + private bool CanExecuteCommandSource(ICommandSource commandSource) + { + ICommand command = commandSource.Command; + if (command == null) + { + return false; + } + object commandParameter = commandSource.CommandParameter; + IInputElement commandTarget = commandSource.CommandTarget; + RoutedCommand command2 = command as RoutedCommand; + if (command2 == null) + { + return command.CanExecute(commandParameter); + } + if (commandTarget == null) + { + commandTarget = commandSource as IInputElement; + } + return command2.CanExecute(commandParameter, commandTarget); + } + + + private bool CanExecute = true; + + + + protected override bool IsEnabledCore + { + get + { + return base.IsEnabledCore && CanExecute; + } + } + + + public object CommandParameter + { + get { return (object)GetValue(CommandParameterProperty); } + set { SetValue(CommandParameterProperty, value); } + } + + // Using a DependencyProperty as the backing store for CommandParameter. This enables animation, styling, binding, etc... + public static readonly DependencyProperty CommandParameterProperty = + DependencyProperty.Register("CommandParameter", typeof(object), typeof(RibbonDropDownButton), new UIPropertyMetadata(null)); + + + public IInputElement CommandTarget + { + get { return (IInputElement)GetValue(CommandTargetProperty); } + set { SetValue(CommandTargetProperty, value); } + } + + // Using a DependencyProperty as the backing store for CommandTarget. This enables animation, styling, binding, etc... + public static readonly DependencyProperty CommandTargetProperty = + DependencyProperty.Register("CommandTarget", typeof(IInputElement), typeof(RibbonDropDownButton), new UIPropertyMetadata(null)); + + + + #endregion + + #region IKeyboardCommand Members + + public void ExecuteKeyTip() + { + Focus(); + IsDropDownPressed = true; + } + + #endregion + + + } +} + diff --git a/Odyssey/Odyssey/Ribbon/Controls/RibbonFlowGroup.cs b/Odyssey/Odyssey/Ribbon/Controls/RibbonFlowGroup.cs new file mode 100644 index 0000000..bfe4733 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Controls/RibbonFlowGroup.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using System.Windows.Markup; +using Odyssey.Controls.Classes; +using Odyssey.Controls.Ribbon.Interfaces; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + [ContentProperty("Items")] + public class RibbonFlowGroup : ItemsControl, IRibbonControl, IRibbonLargeControl + { + static RibbonFlowGroup() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonFlowGroup), new FrameworkPropertyMetadata(typeof(RibbonFlowGroup))); + } + } +} diff --git a/Odyssey/Odyssey/Ribbon/Controls/RibbonGallery.Commands.cs b/Odyssey/Odyssey/Ribbon/Controls/RibbonGallery.Commands.cs new file mode 100644 index 0000000..9299e49 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Controls/RibbonGallery.Commands.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Input; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media.Animation; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + partial class RibbonGallery + { + public static RoutedUICommand ScrollUpCommand = new RoutedUICommand("ScrollUpCommand", "ScrollUpCommand", typeof(RibbonGallery)); + public static RoutedUICommand ScrollDownCommand = new RoutedUICommand("ScrollDownCommand", "ScrollDownCommand", typeof(RibbonGallery)); + + private static void RegisterCommands() + { + CommandManager.RegisterClassCommandBinding(typeof(RibbonGallery), new CommandBinding(ScrollUpCommand, scrollUpCommand)); + CommandManager.RegisterClassCommandBinding(typeof(RibbonGallery), new CommandBinding(ScrollDownCommand, scrollDownCommand)); + } + + private static void scrollUpCommand(object sender, ExecutedRoutedEventArgs e) + { + RibbonGallery gallery = (RibbonGallery)sender; + //RoutedEventArgs args = new RoutedEventArgs(LaunchDialogEvent); + //gallery.RaiseEvent(args); + gallery.ScrollUp(); + } + + + + private static void scrollDownCommand(object sender, ExecutedRoutedEventArgs e) + { + RibbonGallery gallery = (RibbonGallery)sender; + //RoutedEventArgs args = new RoutedEventArgs(LaunchDialogEvent); + //gallery.RaiseEvent(args); + gallery.ScrollDown(); + + + } + + /// + /// Gets the ScrollViewer for the In-Ribbon Panel. + /// + protected virtual ScrollViewer ScrollViewer + { + get { return wrapPanel.Parent as ScrollViewer; } + } + + /// + /// Scrolls the In-Ribbon Gallery on row down. + /// + public void ScrollDown() + { + ScrollViewer sv = ScrollViewer; + if (sv != null) + { + double h = CalculateInRibbonThumbnailHeight(); + double offset = Math.Floor(sv.VerticalOffset / h) * h + h; + ScrollTo(offset); + } + } + + + /// + /// Scrolls the In-Ribbon Gallery on row up. + /// + public void ScrollUp() + { + ScrollViewer sv = ScrollViewer; + if (sv != null) + { + double h = CalculateInRibbonThumbnailHeight(); + double offset = Math.Ceiling(sv.VerticalOffset / h) * h - h; + ScrollTo(offset); + } + } + + private void ScrollTo(double verticalOffset) + { + VerticalScrollOffset = ScrollViewer.VerticalOffset; + DoubleAnimation a = new DoubleAnimation(verticalOffset, new Duration(TimeSpan.FromMilliseconds(250))); + a.DecelerationRatio = 1.0; + BeginAnimation(VerticalScrollOffsetProperty, a); + } + + + /// + /// Gets or set the vertical scroll offset for the In-Ribbon Gallery. + /// This a dependency property + /// + /// + /// This property is used to perform scrolling animation. + /// + public double VerticalScrollOffset + { + get { return (double)GetValue(VerticalScrollOffsetProperty); } + set { SetValue(VerticalScrollOffsetProperty, value); } + } + + // Using a DependencyProperty as the backing store for VerticalScrollOffset. This enables animation, styling, binding, etc... + public static readonly DependencyProperty VerticalScrollOffsetProperty = + DependencyProperty.Register("VerticalScrollOffset", typeof(double), typeof(RibbonGallery), new UIPropertyMetadata(0.0, VerticalScrollOffsetPropertyChanged)); + + private static void VerticalScrollOffsetPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + RibbonGallery g = (RibbonGallery)o; + double value = (double)e.NewValue; + g.OnVerticalScrollOffsetPropertychanged(value); + } + + private void OnVerticalScrollOffsetPropertychanged(double value) + { + ScrollViewer sv = ScrollViewer; + if (sv != null) sv.ScrollToVerticalOffset(value); + } + + + + } +} diff --git a/Odyssey/Odyssey/Ribbon/Controls/RibbonGallery.cs b/Odyssey/Odyssey/Ribbon/Controls/RibbonGallery.cs new file mode 100644 index 0000000..b00b470 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Controls/RibbonGallery.cs @@ -0,0 +1,708 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Controls; +using System.Windows; +using Odyssey.Controls.Ribbon.Interfaces; +using System.Windows.Controls.Primitives; +using System.Diagnostics; +using System.Windows.Data; +using System.ComponentModel; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Windows.Media; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + public partial class RibbonGallery : Selector, IRibbonControl, IRibbonLargeControl, IRibbonGallery + { + const string partPopupupBtn = "PART_PopupBtn"; + const string partWrapPanel = "PART_WrapPanel"; + const string partItemsPresenter = "PART_ItemsPresenter"; + + static RibbonGallery() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonGallery), new FrameworkPropertyMetadata(typeof(RibbonGallery))); + } + + public RibbonGallery() + : base() + { + RegisterCommands(); + } + + + + /// + /// Gets the item that is under the mouse cursor. + /// This is a dependency property. + /// + public object HotItem + { + get { return (object)GetValue(HotItemProperty); } + private set { SetValue(HotItemPropertyKey, value); } + } + + // Using a DependencyProperty as the backing store for HotItem. This enables animation, styling, binding, etc... + private static readonly DependencyPropertyKey HotItemPropertyKey = + DependencyProperty.RegisterReadOnly("HotItem", typeof(object), typeof(RibbonGallery), new UIPropertyMetadata(null)); + + public static readonly DependencyProperty HotItemProperty = HotItemPropertyKey.DependencyProperty; + + + + /// + /// Gets the RibbonThumbnail that is under the mouse cursor. + /// This is a dependency property. + /// + public RibbonThumbnail HotThumbnail + { + get { return (RibbonThumbnail)GetValue(HotThumbnailProperty); } + internal set { SetValue(HotThumbnailPropertyKey, value); } + } + + // Using a DependencyProperty as the backing store for HotThumbnail. This enables animation, styling, binding, etc... + private static readonly DependencyPropertyKey HotThumbnailPropertyKey = + DependencyProperty.RegisterReadOnly("HotThumbnail", typeof(RibbonThumbnail), typeof(RibbonGallery), + new UIPropertyMetadata(null, HotThumbnailPropertyChanged)); + + public static readonly DependencyProperty HotThumbnailProperty = HotThumbnailPropertyKey.DependencyProperty; + + public static void HotThumbnailPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + RibbonGallery g = (RibbonGallery)o; + g.OnHotThumbnailChanged(e); + } + + protected virtual void OnHotThumbnailChanged(DependencyPropertyChangedEventArgs e) + { + RibbonThumbnail hot = HotThumbnail; + HotItem = hot == null || IsItemItsOwnContainerOverride(hot) ? hot : ItemContainerGenerator.ItemFromContainer(hot); + + RoutedEventArgs args = new RoutedEventArgs(RibbonGallery.HotThumbnailChangedEvent); + RaiseEvent(args); + } + + public static readonly RoutedEvent HotThumbnailChangedEvent = EventManager.RegisterRoutedEvent("HotThumbnailChangedEvent", + RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(RibbonGallery)); + + public event RoutedEventHandler HotThumbnailChanged + { + add { AddHandler(HotThumbnailChangedEvent, value); } + remove { RemoveHandler(HotThumbnailChangedEvent, value); } + } + + + private RibbonThumbnail SelectedThumbnail + { + get { return SelectedItem as RibbonThumbnail; } + set { SelectedItem = value; } + } + + + protected override void OnSelectionChanged(SelectionChangedEventArgs e) + { + base.OnSelectionChanged(e); + IsDropDownOpen = false; + if (SelectedItem != null) + { + MakeSelectedItemVisible(); + } + } + + private void MakeSelectedItemVisible() + { + foreach (RibbonThumbnail thumb in WrapPanel.Children) + { + if (thumb.IsSelected) + { + thumb.BringIntoView(); + break; + } + } + } + + protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e) + { + base.OnItemsChanged(e); + AttachThumbnailsToPanel(); + + } + + + private RibbonDropDownButton popupBtn; + private Panel wrapPanel; + private FrameworkElement itemsPresenter; + + public Panel WrapPanel + { + get + { + if (wrapPanel == null) + { + wrapPanel = GetTemplateChild(partWrapPanel) as Panel; + if (wrapPanel == null) + { + ApplyTemplate(); + } + } + return wrapPanel; + } + } + + public override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + if (popupBtn != null) + { + popupBtn.PopupOpened -= OnPopupOpenend; + } + popupBtn = GetTemplateChild(partPopupupBtn) as RibbonDropDownButton; + if (popupBtn != null) + { + popupBtn.PopupOpened += new RoutedEventHandler(OnPopupOpenend); + } + + if (wrapPanel != null) wrapPanel.Children.Clear(); + wrapPanel = GetTemplateChild(partWrapPanel) as Panel; + if (wrapPanel == null) throw new ArgumentNullException(partWrapPanel); + + itemsPresenter = GetTemplateChild(partItemsPresenter) as FrameworkElement; + if (wrapPanel == null) throw new ArgumentNullException(partItemsPresenter); + AttachThumbnailsToPanel(); + } + + private void AttachThumbnailsToPanel() + { + if (wrapPanel != null) + { + wrapPanel.Children.Clear(); + foreach (object item in Items) + { + RibbonThumbnail container = item as RibbonThumbnail; + if (container == null) container = ItemContainerGenerator.ContainerFromItem(item) as RibbonThumbnail; + if (container != null) + { + RibbonThumbnail thumbnail = new RibbonThumbnail(); + thumbnail.ImageSource = container.ImageSource; + Binding b = new Binding("IsSelected") + { + Mode = BindingMode.TwoWay, + Source = container + }; + thumbnail.Original = container; + thumbnail.SetBinding(RibbonThumbnail.IsSelectedProperty, b); + PrepareContainerForItemOverride(thumbnail, item); + wrapPanel.Children.Add(thumbnail); + } + } + } + } + + public RibbonThumbnail Original { get; internal set; } + + + protected virtual void OnPopupOpenend(object sender, RoutedEventArgs e) + { + Popup popup = popupBtn.Popup; + + + Thickness margin = new Thickness(); + + FrameworkElement fe = popup.Child as FrameworkElement; + while (fe != null) + { + margin.Left += fe.Margin.Left; + margin.Right += fe.Margin.Right; + margin.Top += fe.Margin.Top; + margin.Bottom += fe.Margin.Bottom; + Decorator c = fe as Decorator; + fe = c != null ? c.Child as FrameworkElement : null; + } + + double w = ActualWidth + margin.Right; + double h = ActualHeight + margin.Bottom; + Rect rect = new Rect(-margin.Left, -margin.Top, w, h); + + rect = this.TransformToVisual(popup).TransformBounds(rect); + if (!IsCollapsed) + { + popup.Placement = PlacementMode.Relative; + popup.VerticalOffset = rect.Top; + popup.HorizontalOffset = rect.Left; + + } + + + if (Columns > 0) popup.MinWidth = rect.Width; + popup.MinHeight = rect.Height; + itemsPresenter.MaxWidth = DropDownMaxSize.Width; + itemsPresenter.MaxHeight = DropDownMaxSize.Height; + itemsPresenter.Width = ActualWidth; + } + + + /// + /// Gets or sets the size that a RibbonThumbnail will have. + /// This is a dependency property. + /// + public Size ThumbnailSize + { + get { return (Size)GetValue(ThumbnailSizeProperty); } + set { SetValue(ThumbnailSizeProperty, value); } + } + + // Using a DependencyProperty as the backing store for ItemSize. This enables animation, styling, binding, etc... + public static readonly DependencyProperty ThumbnailSizeProperty = + DependencyProperty.Register("ThumbnailSize", typeof(Size), typeof(RibbonGallery), new UIPropertyMetadata(new Size(48.0, 48.0))); + + + + /// + /// Gets or sets how many columns are visible in the collapsed Gallery. + /// This is a dependency property. + /// + public int Columns + { + get { return (int)GetValue(ColumnsProperty); } + set { SetValue(ColumnsProperty, value); } + } + + public static readonly DependencyProperty ColumnsProperty = + DependencyProperty.Register("Columns", typeof(int), typeof(RibbonGallery), + new FrameworkPropertyMetadata(3, + FrameworkPropertyMetadataOptions.AffectsArrange | + FrameworkPropertyMetadataOptions.AffectsRender | + FrameworkPropertyMetadataOptions.AffectsMeasure + , ColumnsPropertyChanged)); + + + + /// + /// Gets or sets the number of columns that appear in the drop down menu. + /// Thisis a dependency property. + /// + public int DropDownColumns + { + get { return (int)GetValue(DropDownColumnsProperty); } + set { SetValue(DropDownColumnsProperty, value); } + } + + // Using a DependencyProperty as the backing store for DropDownColumns. This enables animation, styling, binding, etc... + public static readonly DependencyProperty DropDownColumnsProperty = + DependencyProperty.Register("DropDownColumns", typeof(int), typeof(RibbonGallery), new UIPropertyMetadata(0)); + + + + public static void ColumnsPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + RibbonGallery g = (RibbonGallery)o; + g.OnColumnsChanged(e); + } + + protected virtual void OnColumnsChanged(DependencyPropertyChangedEventArgs e) + { + UpdateLayout(); + } + + + + /// + /// Gets or sets whether the ListBox with details is collapsed. + /// This is a dependency property. + /// + public bool IsCollapsed + { + get { return (bool)GetValue(IsCollapsedProperty); } + set { SetValue(IsCollapsedProperty, value); } + } + + public static readonly DependencyProperty IsCollapsedProperty = + DependencyProperty.Register("IsCollapsed", typeof(bool), typeof(RibbonGallery), new UIPropertyMetadata(false)); + + + /// + /// Gets how many columns are available for the in-ribbon panel. + /// + public int ActualColumns + { + get + { + double w = WrapPanel.DesiredSize.Width; + return (int)Math.Floor(w / ThumbnailSize.Width); + } + } + + protected override Size MeasureOverride(Size constraint) + { + double maxWrapPanelWidth = ThumbnailSize.Width * Columns; + if (WrapPanel.MaxWidth != maxWrapPanelWidth) + { + WrapPanel.MaxWidth = maxWrapPanelWidth; + WrapPanel.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + } + Size size = base.MeasureOverride(constraint); + + double w = CalculateInRibbonThumbnailWidth(); + if (WrapPanel.MaxWidth != w) + { + WrapPanel.MaxWidth = w; + WrapPanel.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + size = base.MeasureOverride(constraint); + } + int visibleItems = (int)(size.Width / ThumbnailSize.Width); + IsDropDownEnabled = visibleItems < Items.Count; + + + return size; + } + + public int VisibleItems + { + get + { + return (int)(ActualWidth / ThumbnailSize.Width); + } + } + + + + /// + /// Gets whether the drop down button is enabled. this is dependency property. + /// + public bool IsDropDownEnabled + { + get { return (bool)GetValue(IsDropDownEnabledProperty); } + private set { SetValue(IsDropDownEnabledPropertyKey, value); } + } + + private static readonly DependencyPropertyKey IsDropDownEnabledPropertyKey = + DependencyProperty.RegisterReadOnly("IsDropDownEnabled", typeof(bool), typeof(RibbonGallery), new UIPropertyMetadata(false)); + + public static readonly DependencyProperty IsDropDownEnabledProperty = IsDropDownEnabledPropertyKey.DependencyProperty; + + + private double CalculateInRibbonThumbnailWidth() + { + double w = WrapPanel.DesiredSize.Width; + return this.ThumbnailSize.Width * Math.Floor(w / ThumbnailSize.Width); + } + + private double CalculateInRibbonThumbnailHeight() + { + double height = ThumbnailSize.Height; + double smallHeight = InternalGroupPanel.MaxSmallHeight; + double rows; + if (height <= smallHeight) rows = 3.0; + else if (height <= (2.0 * smallHeight)) rows = 2.0; + else rows = 1.0; + + + return ((3.0 * smallHeight - 3.0) / rows); + } + + + public bool IsDropDownOpen + { + get { return (bool)GetValue(IsDropDownOpenProperty); } + set { SetValue(IsDropDownOpenProperty, value); } + } + + // Using a DependencyProperty as the backing store for IsDropDownOpen. This enables animation, styling, binding, etc... + public static readonly DependencyProperty IsDropDownOpenProperty = + DependencyProperty.Register("IsDropDownOpen", typeof(bool), typeof(RibbonGallery), + new FrameworkPropertyMetadata(false, + FrameworkPropertyMetadataOptions.AffectsArrange | + FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | + FrameworkPropertyMetadataOptions.AffectsRender | + FrameworkPropertyMetadataOptions.AffectsMeasure, + DropDownOpenPropertyChanged)); + + + public static void DropDownOpenPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + RibbonGallery g = (RibbonGallery)o; + g.DropDownOpenChanged(e); + } + + protected virtual void DropDownOpenChanged(DependencyPropertyChangedEventArgs e) + { + return; + //Panel dropDownPanel = GetTemplateChild("xPART_ListBox") as Panel; + //Panel mainPanel = GetTemplateChild("PART_WrapPanel") as Panel; + //if (dropDownPanel != null) + //{ + // bool opened = (bool)e.NewValue; + // if (opened) + // { + // dropDownPanel.Children.Clear(); + // foreach (object o in Items) + // { + // RibbonMenuItem item = o as RibbonMenuItem; + // if (item == null) + // { + // Binding b = new Binding(); + // b.Source = o; + // item = new RibbonMenuItem(); + // item.SetBinding(RibbonMenuItem.HeaderProperty, b); + // } + // dropDownPanel.Children.Add(item); + // } + // } + // else + // { + // mainPanel.Children.Clear(); + // foreach (object o in Items) + // { + // RibbonMenuItem item = o as RibbonMenuItem; + // if (item == null) + // { + // Binding b = new Binding(); + // b.Source = o; + // item = new RibbonMenuItem(); + // item.SetBinding(RibbonMenuItem.HeaderProperty, b); + // } + // mainPanel.Children.Add(item); + // } + // } + //} + } + + protected override bool IsItemItsOwnContainerOverride(object item) + { + return item is RibbonThumbnail; + } + + protected override DependencyObject GetContainerForItemOverride() + { + return new RibbonThumbnail(); + } + + protected override void PrepareContainerForItemOverride(DependencyObject element, object item) + { + base.PrepareContainerForItemOverride(element, item); + RibbonThumbnail tn = element as RibbonThumbnail; + if (tn != null) + { + tn.Width = ThumbnailSize.Width; + tn.Height = CalculateInRibbonThumbnailHeight(); + Stretch stretch = RibbonGallery.GetStretch(this); + RibbonGallery.SetStretch(tn, stretch); + } + } + + + public Size DropDownMaxSize + { + get { return (Size)GetValue(DropDownMaxSizeProperty); } + set { SetValue(DropDownMaxSizeProperty, value); } + } + + public static readonly DependencyProperty DropDownMaxSizeProperty = + DependencyProperty.Register("DropDownMaxSize", typeof(Size), typeof(RibbonGallery), new UIPropertyMetadata(new Size(double.PositiveInfinity, double.PositiveInfinity))); + + + + /// + /// Gets the collection of RibbonThumbnails. + /// This is a dependency property + /// + /// + /// This dependency property is used to bind the generated collection of RibbonThumbnails with the listboxes inside the ControlTemplate. + /// + public object Thumbnails + { + get { return (object)GetValue(ThumbnailsProperty); } + set { SetValue(ThumbnailsProperty, value); } + } + + public static readonly DependencyProperty ThumbnailsProperty = + DependencyProperty.Register("Thumbnails", typeof(object), typeof(RibbonGallery), new UIPropertyMetadata(null)); + + + public object Header + { + get { return (object)GetValue(HeaderProperty); } + set { SetValue(HeaderProperty, value); } + } + + public static readonly DependencyProperty HeaderProperty = + DependencyProperty.Register("Header", typeof(object), typeof(RibbonGallery), new UIPropertyMetadata(null)); + + + public object Footer + { + get { return (object)GetValue(FooterProperty); } + set { SetValue(FooterProperty, value); } + } + + public static readonly DependencyProperty FooterProperty = + DependencyProperty.Register("Footer", typeof(object), typeof(RibbonGallery), new UIPropertyMetadata(null)); + + + public DataTemplate HeaderTemplate + { + get { return (DataTemplate)GetValue(HeaderTemplateProperty); } + set { SetValue(HeaderTemplateProperty, value); } + } + + public static readonly DependencyProperty HeaderTemplateProperty = + DependencyProperty.Register("HeaderTemplate", typeof(DataTemplate), typeof(RibbonGallery), new UIPropertyMetadata(null)); + + + public DataTemplate FooterTemplate + { + get { return (DataTemplate)GetValue(FooterTemplateProperty); } + set { SetValue(FooterTemplateProperty, value); } + } + + public static readonly DependencyProperty FooterTemplateProperty = + DependencyProperty.Register("FooterTemplate", typeof(DataTemplate), typeof(RibbonGallery), new UIPropertyMetadata(null)); + + public static RibbonGalleryColumns GetReductionColumns(DependencyObject obj) + { + return (RibbonGalleryColumns)obj.GetValue(ReductionColumnsProperty); + } + + public static void SetReductionColumns(DependencyObject obj, RibbonGalleryColumns value) + { + obj.SetValue(ReductionColumnsProperty, value); + } + + public static readonly DependencyProperty ReductionColumnsProperty = + DependencyProperty.RegisterAttached("ReductionColumns", typeof(RibbonGalleryColumns), typeof(RibbonGallery), + new UIPropertyMetadata(null, ReductionColumnsPropertyChanged)); + + static void ReductionColumnsPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + RibbonGallery g = o as RibbonGallery; + if (g != null) + { + RibbonGalleryColumns columns = e.NewValue as RibbonGalleryColumns; + if (columns != null && columns.Count > 0) + { + g.Columns = columns[0]; + } + } + } + + private int ActualDropDownColumns; + + void IRibbonGallery.SetDropDownColumns(int columns) + { + ActualDropDownColumns = DropDownColumns > 0 ? DropDownColumns : columns; + } + + + /// + /// Gets or sets the image that appears when the Gallery is collapsed. + /// This is a dependency property. + /// + public ImageSource LargeImage + { + get { return (ImageSource)GetValue(LargeImageProperty); } + set { SetValue(LargeImageProperty, value); } + } + + // Using a DependencyProperty as the backing store for LargeImage. This enables animation, styling, binding, etc... + public static readonly DependencyProperty LargeImageProperty = + DependencyProperty.Register("LargeImage", typeof(ImageSource), typeof(RibbonGallery), new UIPropertyMetadata(null)); + + + + + /// + /// Gets or sets the title that appears when the gallery is collapsed. + /// This is a dependency property. + /// + public string Title + { + get { return (string)GetValue(TitleProperty); } + set { SetValue(TitleProperty, value); } + } + + // Using a DependencyProperty as the backing store for Title. This enables animation, styling, binding, etc... + public static readonly DependencyProperty TitleProperty = + DependencyProperty.Register("Title", typeof(string), typeof(RibbonGallery), new UIPropertyMetadata("")); + + + + /// + /// Specifies how to stretch each image of a RibbonThumbnail. + /// Attach this property to the RibbonGallery rather than to each RibbonThumbnail (see example). + /// This is a dependency property. + /// + /// + /// ]]> + /// + [AttachedPropertyBrowsableForType(typeof(DependencyObject))] + public static Stretch GetStretch(DependencyObject obj) + { + return (Stretch)obj.GetValue(StretchProperty); + } + + public static void SetStretch(DependencyObject obj, Stretch value) + { + obj.SetValue(StretchProperty, value); + } + + // Using a DependencyProperty as the backing store for Stretch. This enables animation, styling, binding, etc... + public static readonly DependencyProperty StretchProperty = + DependencyProperty.RegisterAttached("Stretch", typeof(Stretch), typeof(RibbonGallery), new UIPropertyMetadata(Stretch.None)); + + + /// + /// Gets or sets the with for the drop down menu. + /// This is a dependency property. + /// + public double DropDownWidth + { + get { return (double)GetValue(DropDownWidthProperty); } + set { SetValue(DropDownWidthProperty, value); } + } + + // Using a DependencyProperty as the backing store for DropDownWidth. This enables animation, styling, binding, etc... + public static readonly DependencyProperty DropDownWidthProperty = + DependencyProperty.Register("DropDownWidth", typeof(double), typeof(RibbonGallery), new UIPropertyMetadata(double.NaN)); + + + + /// + /// Gets or sets how a thumbnail image is scaled. + /// This is a dependency property. + /// + public BitmapScalingMode BitmapScalingMode + { + get { return (BitmapScalingMode)GetValue(BitmapScalingModeProperty); } + set { SetValue(BitmapScalingModeProperty, value); } + } + + public static readonly DependencyProperty BitmapScalingModeProperty = + DependencyProperty.Register("BitmapScalingMode", typeof(BitmapScalingMode), typeof(RibbonGallery), new UIPropertyMetadata(BitmapScalingMode.NearestNeighbor)); + + + + + /// + /// Gets or sets the edge mode when rendering a thumbnail image. + /// This is a dependency property. + /// + public EdgeMode EdgeMode + { + get { return (EdgeMode)GetValue(EdgeModeProperty); } + set { SetValue(EdgeModeProperty, value); } + } + + public static readonly DependencyProperty EdgeModeProperty = + DependencyProperty.Register("EdgeMode", typeof(EdgeMode), typeof(RibbonGallery), new UIPropertyMetadata(EdgeMode.Aliased)); + + + } +} \ No newline at end of file diff --git a/Odyssey/Odyssey/Ribbon/Controls/RibbonGroup.Commands.cs b/Odyssey/Odyssey/Ribbon/Controls/RibbonGroup.Commands.cs new file mode 100644 index 0000000..26ea857 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Controls/RibbonGroup.Commands.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Input; +using System.Windows; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + partial class RibbonGroup + { + private static RoutedUICommand launchDialogCommand = new RoutedUICommand("Launch", "LaunchDialogCommand", typeof(RibbonGroup)); + + private static void RegisterCommands() + { + CommandManager.RegisterClassCommandBinding(typeof(RibbonGroup), + new CommandBinding(launchDialogCommand, launchDialog)); + } + + private static void launchDialog(object sender, ExecutedRoutedEventArgs e) + { + RibbonGroup group = (RibbonGroup)sender; + RoutedEventArgs args = new RoutedEventArgs(ExecuteLauncherEvent); + group.RaiseEvent(args); + + } + + public static RoutedUICommand LaunchDialogCommand + { + get { return launchDialogCommand; } + } + } +} diff --git a/Odyssey/Odyssey/Ribbon/Controls/RibbonGroup.Handlers.cs b/Odyssey/Odyssey/Ribbon/Controls/RibbonGroup.Handlers.cs new file mode 100644 index 0000000..ca09708 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Controls/RibbonGroup.Handlers.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + partial class RibbonGroup + { + private void RegisterHandlers() + { + AddHandler(RibbonGroup.ExecuteLauncherEvent, new RoutedEventHandler(OnExecuteLauncher)); + } + + /// + /// Occurs when the DialogLauncher Button is clicked. + /// + public static readonly RoutedEvent ExecuteLauncherEvent = EventManager.RegisterRoutedEvent("ExecuteLauncherEvent", + RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(RibbonGroup)); + + /// + /// Occurs when the DialogLauncher Button is clicked. + /// + public event RoutedEventHandler ExecuteLauncher + { + add { AddHandler(ExecuteLauncherEvent, value); } + remove { RemoveHandler(ExecuteLauncherEvent, value); } + } + + /// + /// Occurs when the DialogLauncher Button is clicked. + /// + protected virtual void OnExecuteLauncher(object sender, RoutedEventArgs e) + { + } + + } +} diff --git a/Odyssey/Odyssey/Ribbon/Controls/RibbonGroup.cs b/Odyssey/Odyssey/Ribbon/Controls/RibbonGroup.cs new file mode 100644 index 0000000..93401c8 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Controls/RibbonGroup.cs @@ -0,0 +1,626 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using System.Windows.Markup; +using System.Collections.ObjectModel; +using System.Windows.Controls.Primitives; +using System.Diagnostics; +using Odyssey.Controls.Classes; +using Odyssey.Controls.Ribbon.Interfaces; +using System.Collections.Specialized; +using Odyssey.Controls.Interfaces; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + + [ContentProperty("Controls")] + [TemplatePart(Name = partItemsPanelHost)] + [TemplatePart(Name = partPopupItemsPanelHost)] + [TemplatePart(Name = partPopup)] + public partial class RibbonGroup : Control,IKeyTipControl + { + + const string partItemsPanelHost = "PART_ItemsPanelHost"; + const string partPopupItemsPanelHost = "PART_PopupItemsPanelHost"; + const string partPopup = "PART_Popup"; + + static RibbonGroup() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonGroup), new FrameworkPropertyMetadata(typeof(RibbonGroup))); + RegisterCommands(); + } + + + public RibbonGroup() + : base() + { + controls.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(OnControlCollectionChanged); + RegisterHandlers(); + } + + /// + /// Occurs when the Controls collection has changed. + /// + protected virtual void OnControlCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) + { + reducableControlIndexes = null; + ChangeControlSizes(); + } + + private Panel itemPanelInstance = null; + private Panel ItemPanelInstance + { + get + { + if (itemPanelInstance == null) + { + if (ItemsPanel != null) + { + itemPanelInstance = ItemsPanel; + } + else itemPanelInstance = new RibbonWrapPanel(); + } + return itemPanelInstance; + } + } + + private Decorator itemsPanelHost; + private Decorator popupItemsPanelHost; + private Popup popup; + + public override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + if (ItemPanelInstance != null) + { + ItemPanelInstance.Children.Clear(); + foreach (UIElement e in Controls) + { + ItemPanelInstance.Children.Add(e); + } + } + + itemsPanelHost = GetTemplateChild(partItemsPanelHost) as Decorator; + popupItemsPanelHost = GetTemplateChild(partPopupItemsPanelHost) as Decorator; + + if (popup != null) + { + popup.Opened -= OnPopupOpened; + popup.Closed -= OnPopupClosed; + } + popup = GetTemplateChild(partPopup) as Popup; + if (popup != null) + { + popup.Opened += new EventHandler(OnPopupOpened); + popup.Closed += new EventHandler(OnPopupClosed); + } + + AttachControlsToItemsPanel(); + } + + + public static RibbonGroup PoppedUpGroup; + + void OnPopupClosed(object sender, EventArgs e) + { + if (PoppedUpGroup.popup == sender) PoppedUpGroup = null; + } + + void OnPopupOpened(object sender, EventArgs e) + { + if (PoppedUpGroup != null && PoppedUpGroup.popup != null && PoppedUpGroup.popup != sender) + { + PoppedUpGroup.popup.IsOpen = false; + } + PoppedUpGroup = this; + + } + + protected override void OnKeyDown(KeyEventArgs e) + { + if (IsMinimized) + { + switch (e.Key) + { + case Key.Space: + case Key.Down: + IsDropDownOpen = true; + e.Handled = true; + break; + } + } + base.OnKeyDown(e); + } + + + /// + /// CAUTION: + /// Call ApplyTemplate after EndInit to ensure that all controls properties could have applied to a Binding. + /// If ApplyTemplate is not executed here, bindings like "Binding IsChecked, ElementName=anyname" would not work if this group is + /// not yet visibile, for instance, if it is in a RibbonTab that is not the initial first tab. + /// + public override void EndInit() + { + base.EndInit(); + ApplyTemplate(); + } + + public bool IsDropDownOpen + { + get { return (bool)GetValue(IsDropDownOpenProperty); } + set { SetValue(IsDropDownOpenProperty, value); } + } + + // Using a DependencyProperty as the backing store for IsDropDownOpen. This enables animation, styling, binding, etc... + public static readonly DependencyProperty IsDropDownOpenProperty = + DependencyProperty.Register("IsDropDownOpen", typeof(bool), typeof(RibbonGroup), + new UIPropertyMetadata(false, DropDownOpenPropertyChanged)); + + private static void DropDownOpenPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + RibbonGroup g = (RibbonGroup)o; + } + + + + public bool IsMinimized + { + get { return (bool)GetValue(IsMinimizedProperty); } + set { SetValue(IsMinimizedProperty, value); } + } + + // Using a DependencyProperty as the backing store for IsMinimized. This enables animation, styling, binding, etc... + public static readonly DependencyProperty IsMinimizedProperty = + DependencyProperty.Register("IsMinimized", typeof(bool), typeof(RibbonGroup), + new UIPropertyMetadata(false, MinimizedPropertyChanged)); + + public static void MinimizedPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + RibbonGroup g = (RibbonGroup)o; + g.AttachControlsToItemsPanel(); + } + + + private void AttachControlsToItemsPanel() + { + + if (ItemPanelInstance != null) + { + Decorator parent = ItemPanelInstance.Parent as Decorator; + if (parent != null) parent.Child = null; + + Decorator host = IsMinimized ? popupItemsPanelHost : itemsPanelHost; + if (host != null) + { + host.Child = ItemPanelInstance; + } + } + } + + public string Title + { + get { return (string)GetValue(TitleProperty); } + set { SetValue(TitleProperty, value); } + } + + // Using a DependencyProperty as the backing store for Title. This enables animation, styling, binding, etc... + public static readonly DependencyProperty TitleProperty = + DependencyProperty.Register("Title", typeof(string), typeof(RibbonGroup), new UIPropertyMetadata("")); + + + + public bool IsDialogLauncherVisible + { + get { return (bool)GetValue(IsDialogLauncherVisibleProperty); } + set { SetValue(IsDialogLauncherVisibleProperty, value); } + } + + // Using a DependencyProperty as the backing store for IsDialogLauncherVisible. This enables animation, styling, binding, etc... + public static readonly DependencyProperty IsDialogLauncherVisibleProperty = + DependencyProperty.Register("IsDialogLauncherVisible", typeof(bool), typeof(RibbonGroup), new UIPropertyMetadata(false)); + + + public ImageSource Image + { + get { return (ImageSource)GetValue(ImageProperty); } + set { SetValue(ImageProperty, value); } + } + + // Using a DependencyProperty as the backing store for Image. This enables animation, styling, binding, etc... + public static readonly DependencyProperty ImageProperty = + DependencyProperty.Register("Image", typeof(ImageSource), typeof(RibbonGroup), new UIPropertyMetadata(null)); + + /// + /// Gets or sets the Panel that contains the Controls. + /// This is a dependency property. + /// + public Panel ItemsPanel + { + get { return (Panel)GetValue(ItemsPanelProperty); } + set { SetValue(ItemsPanelProperty, value); } + } + + // Using a DependencyProperty as the backing store for ItemsPanel. This enables animation, styling, binding, etc... + public static readonly DependencyProperty ItemsPanelProperty = + DependencyProperty.Register("ItemsPanel", typeof(Panel), typeof(RibbonGroup), new UIPropertyMetadata(null, ItemsPanelPropertyChanged)); + + static void ItemsPanelPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + RibbonGroup grp = (RibbonGroup)d; + grp.OnItemsPanelChanged(e); + } + + protected virtual void OnItemsPanelChanged(DependencyPropertyChangedEventArgs e) + { + itemPanelInstance = null; + AttachControlsToItemsPanel(); + } + + //protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e) + //{ + // base.OnPreviewMouseLeftButtonDown(e); + + // if (!(e.Source is MenuItem) && !(e.Source is RibbonThumbnail)) + // { + // RibbonDropDownButton.CloseOpenedPopup(null); + // } + //} + + private ObservableCollection controls = new ObservableCollection(); + + /// + /// Gets the Collection of controls for this gorup. + /// + public Collection Controls { get { return controls; } } + + protected override System.Collections.IEnumerator LogicalChildren + { + get + { + return controls.GetEnumerator(); + } + } + + private void ClosePopup() + { + if (popup != null) popup.IsOpen = false; + } + + public static void CloseOpenedPopup() + { + if (PoppedUpGroup != null) + { + PoppedUpGroup.ClosePopup(); + } + } + + static RibbonSizeCollection first = new RibbonSizeCollection { RibbonSize.Large, RibbonSize.Large, RibbonSize.Large, RibbonSize.Minimized }; + + int GetGalleryColumns(DependencyObject control, int level, int maxLevels) + { + RibbonGalleryColumns columns = RibbonGallery.GetReductionColumns(control); + if (columns != null && columns.Count > 0) + { + level = Math.Max(0, Math.Min(level, columns.Count - 1)); + return columns[level]; + } + + if (level == maxLevels) return 0; + + if (maxLevels > 4) + { + level = Math.Max(level, 8); + return 3 + (4 - level / 2); + } + else + { + return 3 + (2 - level) * 2; + } + + } + + /// + /// Gets the RibbonSize for a control for a specific level. + /// + /// The control for which to retreive a RibbonSize. + /// The reduction Level (0=large, 2=medium,3=small,4=minimized,...). + /// The index of the control in the group. + /// The RibbonSize for the control. + RibbonSize GetControlSize(DependencyObject control, int level, int index) + { + RibbonSizeCollection reductions = RibbonBar.GetReduction(control); + if (reductions != null && reductions.Count > 0) + { + level = Math.Max(0, Math.Min(level, reductions.Count - 1)); + return reductions[level]; + } + RibbonSize size; + switch (level) + { + case 0: size = GetDefaultSizeForLevel0(index); break; + case 1: size = GetDefaultSizeForLevel1(index); break; + case 2: size = GetDefaultSizeForLevel2(index); break; + default: size = RibbonSize.Minimized; break; + } + + RibbonSize min = RibbonBar.GetMinSize(control); + RibbonSize max = RibbonBar.GetMaxSize(control); + if (size < min) size = min; + if (size > max) size = max; + + return size; + } + + private int CountRibbonControls() + { + int count = 0; + foreach (UIElement e in Controls) + { + if (e is IRibbonControl) count++; + } + return count; + } + + private RibbonSize GetDefaultSizeForLevel2(int index) + { + if (!ReducableControlIndexes.ContainsKey(index)) return RibbonSize.Large; + return ReducableControlIndexes[index]; + } + + private RibbonSize GetDefaultSizeForLevel1(int index) + { + if (!ReducableControlIndexes.ContainsKey(index)) return RibbonSize.Large; + return RibbonSize.Medium; + } + + private RibbonSize GetDefaultSizeForLevel0(int index) + { + return RibbonSize.Large; + } + + + private int reductionLevel; + /// + /// Gets or sets the reduction level. + /// + public int ReductionLevel + { + get { return reductionLevel; } + set + { + if (value < 0) value = 0; + if (reductionLevel != value) + { + reductionLevel = value; + ChangeControlSizes(); + } + } + } + + /// + /// Resets the ReductionLevel to it's default value (0); + /// + public void ResetSize() + { + ReductionLevel = 0; + } + + /// + /// Reduce the ReductionLevel one step. + /// + /// + public bool Reduce() + { + if (ReductionLevel < GetMaxLevel() + 1) + { + ReductionLevel++; + return true; + } + else return false; + } + + /// + /// Expand the ReductionLevel one step. + /// + /// + public bool Expand() + { + if (ReductionLevel > 0) + { + ReductionLevel--; + return true; + } + else return false; + } + + //Gets the maximum possible ReductionLevel that would change any of the controls. + private int GetMaxLevel() + { + int max = 1; + foreach (UIElement e in Controls) + { + if (e is IRibbonControl) + { + RibbonSizeCollection reduction = RibbonBar.GetReduction(e); + int m = reduction != null ? reduction.Count : 3; + max = Math.Max(max, m); + } + if (e is IRibbonGallery) + { + RibbonGalleryColumns columns = RibbonGallery.GetReductionColumns(e); + int m = columns != null ? columns.Count : 3; + max = Math.Max(max, m); + } + } + return max; + } + + + /// + /// Change the RibbonSize for every IRibbonControl inside this group. + /// + private void ChangeControlSizes() + { + int level = ReductionLevel; + int maxLevels = GetMaxLevel(); + bool minimized = level >= maxLevels; + IsMinimized = minimized; + if (minimized) level = 0; + + for (int i = 0; i < Controls.Count; i++) + { + UIElement e = Controls[i]; + IRibbonGallery gallery = e as IRibbonGallery; + if (gallery != null) + { + int columns = GetGalleryColumns(e, level, maxLevels); + gallery.Columns = columns; + gallery.IsCollapsed = columns == 0; + gallery.SetDropDownColumns(GetGalleryColumns(e, 0, maxLevels)); + } + else + { + if (e is IRibbonControl) + { + RibbonSize size = GetControlSize(e, level, i); + RibbonBar.SetSize(e, size); + } + } + } + InvalidateMeasure(); + UpdateLayout(); + + } + + + #region AutoReduction + + class GroupBucket : List + { + } + + class GroupBucketCollection : List + { + } + + GroupBucketCollection GetBuckets() + { + GroupBucketCollection buckets = new GroupBucketCollection(); + GroupBucket bucket = new GroupBucket(); + buckets.Add(bucket); + + int index = 0; + foreach (UIElement e in Controls) + { + bool canBeSmall = CanControlBeSmall(e); + if (!canBeSmall && bucket.Count > 0) + { + bucket = new GroupBucket(); + buckets.Add(bucket); + } + else + { + bucket.Add(index); + } + index++; + } + return buckets; + } + + private bool CanControlBeSmall(UIElement e) + { + + if ((e is IRibbonControl) && !(e is IRibbonLargeControl)) + { + RibbonSize min = RibbonBar.GetMinSize(e); + return min < RibbonSize.Large; + } + return e.DesiredSize.Height <= InternalGroupPanel.MaxSmallHeight; + } + + + private Dictionary reducableControlIndexes; + + private Dictionary ReducableControlIndexes + { + get + { + if (reducableControlIndexes == null) reducableControlIndexes = GetReducableControlIndexes(); + return reducableControlIndexes; + } + } + + + /// + /// Gets a Dictionary of all control indexs (from Controls) which can be reduced and to which size. + /// + /// + /// To automatically determine which controls can be reduced and to which level, the controls are first grouped into buckets that contain continous controls + /// that can be reduced to a small size so that 3 of them can share a row. For each of this buckets, the available RibbonSize is determined of where the + /// control is placed inside the bucket. + /// + /// + private Dictionary GetReducableControlIndexes() + { + Dictionary reducable = new Dictionary(); + GroupBucketCollection buckets = GetBuckets(); + + foreach (GroupBucket b in buckets) + { + int smallLevel = Math.Max(0, b.Count - 3); + int startIndex = Controls.Count == 2 ? 0 : b.Count % 3; + + for (int i = startIndex; i < b.Count; i++) + { + RibbonSize minSize = i >= smallLevel ? RibbonSize.Small : RibbonSize.Medium; + reducable.Add(b[i], minSize); + } + } + int index = 0; + foreach (UIElement e in Controls) + { + if (e is IRibbonLargeControl) reducable.Add(index, RibbonSize.Small); + index++; + } + return reducable; + } + + + + #endregion + + /// + /// Executes the launcher command. + /// + public virtual void DoExecuteLauncher() + { + RoutedEventArgs args = new RoutedEventArgs(RibbonGroup.ExecuteLauncherEvent); + RaiseEvent(args); + } + + #region IKeyTipControl Members + + void IKeyTipControl.ExecuteKeyTip() + { + DoExecuteLauncher(); + } + + #endregion + } +} diff --git a/Odyssey/Odyssey/Ribbon/Controls/RibbonMenuItem.cs b/Odyssey/Odyssey/Ribbon/Controls/RibbonMenuItem.cs new file mode 100644 index 0000000..241b1e1 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Controls/RibbonMenuItem.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Controls; +using System.Windows; +using System.Windows.Markup; +using System.Windows.Controls.Primitives; +using System.Windows.Media; +using System.Windows.Input; +using Odyssey.Controls.Ribbon.Interfaces; +using Odyssey.Controls.Interfaces; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + [ContentProperty("Items")] + public class RibbonMenuItem:MenuItem,IKeyTipControl + { + const string partPopup = "PART_Popup"; + + static RibbonMenuItem() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonMenuItem), new FrameworkPropertyMetadata(typeof(RibbonMenuItem))); + } + + protected override DependencyObject GetContainerForItemOverride() + { + return new RibbonMenuItem(); + } + + + + public ImageSource Image + { + get { return (ImageSource)GetValue(ImageProperty); } + set { SetValue(ImageProperty, value); } + } + + // Using a DependencyProperty as the backing store for Image. This enables animation, styling, binding, etc... + public static readonly DependencyProperty ImageProperty = + DependencyProperty.Register("Image", typeof(ImageSource), typeof(RibbonMenuItem), new UIPropertyMetadata(null)); + + + private Popup popup; + + public override void OnApplyTemplate() + { + base.OnApplyTemplate(); + if (popup != null) + { + popup.Opened -= OnPopupOpenend; + popup.Closed -= OnPopupClosed; + } + popup = GetTemplateChild(partPopup) as Popup; + if (popup != null) + { + popup.Opened += new EventHandler(OnPopupOpenend); + popup.Closed += new EventHandler(OnPopupClosed); + } + } + + protected virtual void OnPopupClosed(object sender, EventArgs e) + { + IsSubmenuOpen = false; + } + + protected virtual void OnPopupOpenend(object sender, EventArgs e) + { + RibbonApplicationMenu menu = ItemsControl.ItemsControlFromItemContainer(this) as RibbonApplicationMenu; + if (menu != null) + { + Rect subMenuRect = menu.GetSubMenuRect(this); + popup.Placement = PlacementMode.Relative; + popup.VerticalOffset = subMenuRect.Top; + popup.HorizontalOffset = subMenuRect.Left; + popup.Width = subMenuRect.Width; + popup.Height = subMenuRect.Height; + } + } + + + + protected override void OnSubmenuOpened(RoutedEventArgs e) + { + base.OnSubmenuOpened(e); + if (popup != null) popup.IsOpen = true; + } + + protected override void OnSubmenuClosed(RoutedEventArgs e) + { + base.OnSubmenuClosed(e); + if (popup != null) popup.IsOpen = false; + } + + + + #region IKeyboardCommand Members + + public void ExecuteKeyTip() + { + if (this.HasItems) + { + IsSubmenuOpen ^= true; + } + else + { + OnClick(); + } + } + + #endregion + } +} diff --git a/Odyssey/Odyssey/Ribbon/Controls/RibbonQAToolBar.cs b/Odyssey/Odyssey/Ribbon/Controls/RibbonQAToolBar.cs new file mode 100644 index 0000000..d21ac40 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Controls/RibbonQAToolBar.cs @@ -0,0 +1,364 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Controls; +using System.Windows; +using Odyssey.Controls.Ribbon.Interfaces; +using System.Diagnostics; +using System.Windows.Media; +using System.Windows.Input; +using System.Windows.Threading; +using System.Windows.Data; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + /// + /// RibbonQuickAccessToolbar + /// + [TemplatePart(Name = partItemsHost)] + [TemplatePart(Name = partOverflowHost)] + [TemplatePart(Name = partMenuItemsHost)] + public class RibbonQAToolBar : ItemsControl + { + const string partItemsHost = "PART_ToolBarPanel"; + const string partOverflowHost = "PART_OverflowHost"; + const string partMenuItemsHost = "PART_MenuItemHost"; + const string partMenuItemsOverflowHost = "PART_MenuItemOverflowHost"; + + static RibbonQAToolBar() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonQAToolBar), new FrameworkPropertyMetadata(typeof(RibbonQAToolBar))); + } + + public RibbonQAToolBar() + : base() + { + AddHandler(RibbonButton.ClickEvent, new RoutedEventHandler(OnClick)); + AddHandler(RibbonSplitButton.ClickEvent, new RoutedEventHandler(OnClick)); + AddHandler(MenuItem.ClickEvent, new RoutedEventHandler(OnMenuItemClick)); + } + + void OnClick(object sender, RoutedEventArgs e) + { + if (e.Source != this) + { + IsOverflowOpen = false; + IsMenuOpen = false; + } + } + + void OnMenuItemClick(object sender, RoutedEventArgs e) + { + IsMenuOpen = false; + IsOverflowOpen = false; + } + + + private Panel itemsHost; + private Panel overflowHost; + private Panel menuItemsHost; + private Panel menuItemsOverflowHost; + + + public override void OnApplyTemplate() + { + if (menuItemsOverflowHost != null) menuItemsOverflowHost.Children.Clear(); + if (menuItemsHost != null) menuItemsHost.Children.Clear(); + if (itemsHost != null) itemsHost.Children.Clear(); + if (overflowHost != null) overflowHost.Children.Clear(); + + base.OnApplyTemplate(); + itemsHost = GetTemplateChild(partItemsHost) as Panel; + overflowHost = GetTemplateChild(partOverflowHost) as Panel; + + menuItemsHost = GetTemplateChild(partMenuItemsHost) as Panel; + menuItemsOverflowHost = GetTemplateChild(partMenuItemsOverflowHost) as Panel; + + if (itemsHost == null) throw new ArgumentException(partItemsHost + " is not defined in ControlTemplate."); + } + + private IEnumerable GetMenuItems() + { + foreach (UIElement e in Items) + { + if (!(e is IRibbonButton)) yield return e; + } + } + + private IEnumerable GetToolbarItems() + { + foreach (UIElement e in Items) + { + if ((e is IRibbonButton)) yield return e; + } + } + + private void CreateMenuItems() + { + if (menuItemsHost != null) + { + HasMenuItems = false; + if (menuItemsOverflowHost != null) menuItemsOverflowHost.Children.Clear(); + menuItemsHost.Children.Clear(); + Panel host = HasOverflowItems && menuItemsOverflowHost != null ? menuItemsOverflowHost : menuItemsHost; + foreach (UIElement e in GetMenuItems()) + { + host.Children.Add(e); + //RibbonMenuItem mi = new RibbonMenuItem(); + //RibbonMenuItem rm = e as RibbonMenuItem; + //if (rm != null) + //{ + // Binding b = new Binding("IsChecked"); + // b.Source = e; + // b.Mode = BindingMode.TwoWay; + // mi.SetBinding(RibbonMenuItem.IsCheckedProperty, b); + // mi.Image = (e as RibbonMenuItem).Image; + // mi.Header = (e as RibbonMenuItem).Header; + // mi.IsCheckable = rm.IsCheckable; + //} + //host.Children.Add(mi); + HasMenuItems = true; + } + } + } + + + protected override System.Windows.Size MeasureOverride(System.Windows.Size constraint) + { + itemsHost.Children.Clear(); + if (overflowHost != null) overflowHost.Children.Clear(); + CreateMenuItems(); + foreach (UIElement e in GetToolbarItems()) + { + RibbonBar.SetSize(e, RibbonSize.Small); + itemsHost.Children.Add(e); + } + HasOverflowItems = false; + Size maxSize = base.MeasureOverride(new Size(double.PositiveInfinity, constraint.Height)); + while (maxSize.Width > constraint.Width) + { + int n = itemsHost.Children.Count; + if (n == 0) break; + UIElement e = itemsHost.Children[n - 1]; + InvalidateAncestorMeasure(e); + itemsHost.Children.RemoveAt(n - 1); + if (overflowHost != null) + { + overflowHost.Children.Insert(0, e); + HasOverflowItems = true; + } + maxSize = base.MeasureOverride(new Size(double.PositiveInfinity, constraint.Height)); + } + Size size = base.MeasureOverride(constraint); + return size; + } + + private void InvalidateAncestorMeasure(DependencyObject obj) + { + UIElement element = obj as UIElement; + if (element != null) + { + element.InvalidateMeasure(); + } + if ((obj != this) && (obj != null)) + { + this.InvalidateAncestorMeasure(VisualTreeHelper.GetParent(obj)); + } + } + + + /// + /// Specifies where the QuickAccessToolBar is supposed to be placed. + /// Depending on this information, the layout and design may vary. + /// This is a dependency property. + /// + public QAPlacement ToolBarPlacement + { + get { return (QAPlacement)GetValue(ToolBarPlacementProperty); } + set { SetValue(ToolBarPlacementProperty, value); } + } + + // Using a DependencyProperty as the backing store for Placement. This enables animation, styling, binding, etc... + public static readonly DependencyProperty ToolBarPlacementProperty = + DependencyProperty.Register("ToolBarPlacement", typeof(QAPlacement), typeof(RibbonQAToolBar), + new FrameworkPropertyMetadata(QAPlacement.Top, + FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsRender)); + + + + + + /// + /// Gets or sets whether the overflow panel button is visible. + /// This is a dependency property. + /// + public bool HasOverflowItems + { + get { return (bool)GetValue(IsOverflowVisibleProperty); } + set { SetValue(IsOverflowVisibleProperty, value); } + } + + // Using a DependencyProperty as the backing store for IsDropDownVisible. This enables animation, styling, binding, etc... + public static readonly DependencyProperty IsOverflowVisibleProperty = + DependencyProperty.Register("HasOverflowItems", typeof(bool), typeof(RibbonQAToolBar), + new UIPropertyMetadata(false, HasOverflowItemsPropertyChanged)); + + private static void HasOverflowItemsPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + RibbonQAToolBar toolbar = (RibbonQAToolBar)o; + toolbar.CreateMenuItems(); + } + + /// + /// Gets or sets whether the overflow panel is open. + /// This is a dependency property. + /// + public bool IsOverflowOpen + { + get { return (bool)GetValue(IsOverflowOpenProperty); } + set { SetValue(IsOverflowOpenProperty, value); } + } + + // Using a DependencyProperty as the backing store for IsDropDownVisible. This enables animation, styling, binding, etc... + public static readonly DependencyProperty IsOverflowOpenProperty = + DependencyProperty.Register("IsOverflowOpen", typeof(bool), typeof(RibbonQAToolBar), new FrameworkPropertyMetadata(false, + FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OverflowOpenPropertyChanged)); + + public static void OverflowOpenPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + RibbonQAToolBar tb = (RibbonQAToolBar)o; + tb.IsMenuOpen=false; + } + + + + /// + /// Gets or sets whether the menu is open. + /// This is a dependency property. + /// + public bool IsMenuOpen + { + get { return (bool)GetValue(IsMenuOpenProperty); } + set { SetValue(IsMenuOpenProperty, value); } + } + + // Using a DependencyProperty as the backing store for IsMenuOpen. This enables animation, styling, binding, etc... + public static readonly DependencyProperty IsMenuOpenProperty = + DependencyProperty.Register("IsMenuOpen", typeof(bool), typeof(RibbonQAToolBar), + new FrameworkPropertyMetadata(false, + FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); + + + + /// + /// Gets or sets whether the menu button is visible. + /// This is a dependency property. + /// + public bool HasMenuItems + { + get { return (bool)GetValue(HasMenuItemsProperty); } + set { SetValue(HasMenuItemsProperty, value); } + } + + // Using a DependencyProperty as the backing store for HasMenuItems. This enables animation, styling, binding, etc... + public static readonly DependencyProperty HasMenuItemsProperty = + DependencyProperty.Register("HasMenuItems", typeof(bool), typeof(RibbonQAToolBar), new UIPropertyMetadata(false)); + + + + + + /// + /// Gets or sets the header that appears in the menu. + /// + public object MenuHeader + { + get { return (object)GetValue(MenuHeaderProperty); } + set { SetValue(MenuHeaderProperty, value); } + } + + // Using a DependencyProperty as the backing store for MenuHeader. This enables animation, styling, binding, etc... + public static readonly DependencyProperty MenuHeaderProperty = + DependencyProperty.Register("MenuHeader", typeof(object), typeof(RibbonQAToolBar), new UIPropertyMetadata(null)); + + + + + public DataTemplate MenuHeaderTemplate + { + get { return (DataTemplate)GetValue(MenuHeaderTemplateProperty); } + set { SetValue(MenuHeaderTemplateProperty, value); } + } + + // Using a DependencyProperty as the backing store for MenuHeaderTemplate. This enables animation, styling, binding, etc... + public static readonly DependencyProperty MenuHeaderTemplateProperty = + DependencyProperty.Register("MenuHeaderTemplate", typeof(DataTemplate), typeof(RibbonQAToolBar), new UIPropertyMetadata(null)); + + + + /// + /// Gets or sets the footer that appears in the menu panel. + /// + public object MenuFooter + { + get { return (object)GetValue(MenuFooterProperty); } + set { SetValue(MenuFooterProperty, value); } + } + + // Using a DependencyProperty as the backing store for PopupFooter. This enables animation, styling, binding, etc... + public static readonly DependencyProperty MenuFooterProperty = + DependencyProperty.Register("MenuFooter", typeof(object), typeof(RibbonQAToolBar), new UIPropertyMetadata(null)); + + + + + public DataTemplate MenuFooterTemplate + { + get { return (DataTemplate)GetValue(MenuFooterTemplateProperty); } + set { SetValue(MenuFooterTemplateProperty, value); } + } + + // Using a DependencyProperty as the backing store for MenuFooterTemplate. This enables animation, styling, binding, etc... + public static readonly DependencyProperty MenuFooterTemplateProperty = + DependencyProperty.Register("MenuFooterTemplate", typeof(DataTemplate), typeof(RibbonQAToolBar), new UIPropertyMetadata(null)); + + + + + [AttachedPropertyBrowsableForChildren] + public static QAItemPlacement GetPlacement(DependencyObject obj) + { + return (QAItemPlacement)obj.GetValue(PlacementProperty); + } + + public static void SetPlacement(DependencyObject obj, QAItemPlacement value) + { + obj.SetValue(PlacementProperty, value); + } + + // Using a DependencyProperty as the backing store for Placement. This enables animation, styling, binding, etc... + public static readonly DependencyProperty PlacementProperty = + DependencyProperty.RegisterAttached("Placement", typeof(QAItemPlacement), typeof(RibbonQAToolBar), new UIPropertyMetadata(QAItemPlacement.ToolBar)); + + + + + public DataTemplate ItemsTemplate + { + get { return (DataTemplate)GetValue(ItemsTemplateProperty); } + set { SetValue(ItemsTemplateProperty, value); } + } + + // Using a DependencyProperty as the backing store for ItemsTemplate. This enables animation, styling, binding, etc... + public static readonly DependencyProperty ItemsTemplateProperty = + DependencyProperty.Register("ItemsTemplate", typeof(DataTemplate), typeof(RibbonQAToolBar), new UIPropertyMetadata(null)); + + + } +} diff --git a/Odyssey/Odyssey/Ribbon/Controls/RibbonQAToolbarPanel.cs b/Odyssey/Odyssey/Ribbon/Controls/RibbonQAToolbarPanel.cs new file mode 100644 index 0000000..f9cca46 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Controls/RibbonQAToolbarPanel.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Controls; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + public class RibbonQAToolbarPanel:StackPanel + { + public RibbonQAToolbarPanel() + : base() + { + //Orientation = Orientation.Horizontal; + } + + /// + /// Overriding this to enable a templated parent to add or remove children to this panel within OnMeasureOverride or somewhere else. + /// + protected override UIElementCollection CreateUIElementCollection(System.Windows.FrameworkElement logicalParent) + { + return new UIElementCollection(this, (base.TemplatedParent == null) ? logicalParent : null); + } + } +} diff --git a/Odyssey/Odyssey/Ribbon/Controls/RibbonSeparator.cs b/Odyssey/Odyssey/Ribbon/Controls/RibbonSeparator.cs new file mode 100644 index 0000000..f9eeda8 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Controls/RibbonSeparator.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Controls; +using System.Windows; +using Odyssey.Controls.Ribbon.Interfaces; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + public class RibbonSeparator:Separator,IRibbonControl + { + static RibbonSeparator() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonSeparator), new FrameworkPropertyMetadata(typeof(RibbonSeparator))); + } + + } +} diff --git a/Odyssey/Odyssey/Ribbon/Controls/RibbonSplitButton.cs b/Odyssey/Odyssey/Ribbon/Controls/RibbonSplitButton.cs new file mode 100644 index 0000000..039cf27 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Controls/RibbonSplitButton.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Input; +using System.Windows.Controls; +using System.Windows.Markup; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + [TemplatePart(Name = partDropDown)] + [ContentProperty("Items")] + public class RibbonSplitButton : RibbonDropDownButton + { + const string partDropDown = "PART_DropDown"; + + static RibbonSplitButton() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonSplitButton), new FrameworkPropertyMetadata(typeof(RibbonSplitButton))); + } + + + protected Control DropDownButton {get;private set;} + + + public override void OnApplyTemplate() + { + base.OnApplyTemplate(); + if (DropDownButton != null) + { + DropDownButton.MouseDown -= OnDropDownButtonDown; + DropDownButton.MouseUp -= OnDropDownButtonUp; + } + DropDownButton = GetTemplateChild(partDropDown) as Control; + if (DropDownButton != null) + { + DropDownButton.MouseLeftButtonDown += new MouseButtonEventHandler(OnDropDownButtonDown); + DropDownButton.MouseLeftButtonUp += new MouseButtonEventHandler(OnDropDownButtonUp); + } + } + + protected virtual void OnDropDownButtonDown(object sender, MouseButtonEventArgs e) + { + e.Handled = true; + + IsDropDownPressed ^= true; + EnsurePopupRemainsOnMouseUp(); + } + + protected virtual void OnDropDownButtonUp(object sender, MouseButtonEventArgs e) + { + EnsurePopupDoesNotStayOpen(); + } + + + + + public ClickMode ClickMode + { + get { return (ClickMode)GetValue(ClickModeProperty); } + set { SetValue(ClickModeProperty, value); } + } + + public static readonly DependencyProperty ClickModeProperty = + DependencyProperty.Register("ClickMode", typeof(ClickMode), typeof(RibbonSplitButton), new UIPropertyMetadata(ClickMode.Release)); + + + protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) + { + base.OnMouseLeftButtonDown(e); + IsPressed = true; + EnsurePopupRemainsOnMouseUp(); + if (ClickMode == ClickMode.Press) PerformClick(); + } + + protected override void HandleMouseLeftButtonDown(MouseButtonEventArgs e) + { + //base.HandleMouseLeftButtonDown(e); + } + + protected override void HandleMouseLeftButtonUp(MouseButtonEventArgs e) + { + //base.HandleMouseLeftButtonUp(e); + } + + + private void PerformClick() + { + OnClick(); + } + + protected override void OnClick() + { + if ((Command != null) && Command.CanExecute(CommandParameter)) Command.Execute(CommandParameter); + base.OnClick(); + } + + protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) + { + bool wasPressed = IsPressed; + IsPressed = false; + base.OnMouseLeftButtonUp(e); + EnsurePopupDoesNotStayOpen(); + if (wasPressed && ClickMode == ClickMode.Release) PerformClick(); + + } + + protected override void ToggleDropDownState() + { + // do not show the popup menu at this place. + } + + } +} diff --git a/Odyssey/Odyssey/Ribbon/Controls/RibbonTabItem.cs b/Odyssey/Odyssey/Ribbon/Controls/RibbonTabItem.cs new file mode 100644 index 0000000..0edad13 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Controls/RibbonTabItem.cs @@ -0,0 +1,276 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using System.Windows.Markup; +using System.Diagnostics; +using System.Collections.ObjectModel; +using System.Collections; +using System.Collections.Specialized; +using System.ComponentModel; +using Odyssey.Controls.Classes; +using Odyssey.Controls.Ribbon.Interfaces; +using Odyssey.Controls.Interfaces; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + + [ContentProperty("Items")] + public class RibbonTabItem : ItemsControl, IKeyTipControl + { + + static RibbonTabItem() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonTabItem), new FrameworkPropertyMetadata(typeof(RibbonTabItem))); + + // changing the visibility requires the RibbonBar to be rendered again, so ensure the correct metadata options to be set: + VisibilityProperty.OverrideMetadata( + typeof(RibbonTabItem), + new FrameworkPropertyMetadata(Visibility.Visible, + FrameworkPropertyMetadataOptions.AffectsParentArrange + | FrameworkPropertyMetadataOptions.AffectsParentArrange + | FrameworkPropertyMetadataOptions.AffectsParentMeasure + | FrameworkPropertyMetadataOptions.AffectsRender)); + } + + public RibbonTabItem() + : base() + { + IsVisibleChanged += new DependencyPropertyChangedEventHandler(OnVisibleChanged); + } + + /// + /// CAUTION: + /// Call ApplyTemplate after EndInit to ensure that all controls properties could have applied to a Binding. + /// If ApplyTemplate is not executed here, bindings like "Binding IsChecked, ElementName=anyname" would not work if this group is + /// not yet visibile, for instance, if it is in a RibbonTab that is not the initial first tab. + /// + public override void EndInit() + { + base.EndInit(); + ApplyTemplate(); + } + + protected virtual void OnVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) + { + bool visible = (bool)e.NewValue; + if (!visible) this.RibbonBar.EnsureTabIsVisible(); + } + + protected override bool IsItemItsOwnContainerOverride(object item) + { + return item is RibbonGroup; + } + + protected override DependencyObject GetContainerForItemOverride() + { + return new RibbonGroup(); + } + + + public override void OnApplyTemplate() + { + base.OnApplyTemplate(); + } + + static Size infiniteSize = new Size(double.PositiveInfinity, double.PositiveInfinity); + + private ObservableCollection groups = new ObservableCollection(); + + [Obsolete("for backward compatibility only. Use Items instead!")] + public ItemCollection Groups { get { return Items; } } + + /// + /// Gets or sets the title for the tab item. + /// This is a dependency property. + /// + public string Title + { + get { return (string)GetValue(TitleProperty); } + set { SetValue(TitleProperty, value); } + } + + // Using a DependencyProperty as the backing store for Title. This enables animation, styling, binding, etc... + public static readonly DependencyProperty TitleProperty = + DependencyProperty.Register("Title", typeof(string), typeof(RibbonTabItem), new UIPropertyMetadata("")); + + + /// + /// Gets whether the tab item is a contextual tab item. + /// This is a dependency property. + /// + public bool IsContextual + { + get { return (bool)GetValue(IsContextualProperty); } + internal set { SetValue(IsContextualPropertyKey, value); } + } + + internal RibbonContextualTabSet tabSet; + + // Using a DependencyProperty as the backing store for IsContextual. This enables animation, styling, binding, etc... + private static readonly DependencyPropertyKey IsContextualPropertyKey = + DependencyProperty.RegisterReadOnly("IsContextual", typeof(bool), typeof(RibbonTabItem), new UIPropertyMetadata(false)); + + public static DependencyProperty IsContextualProperty = IsContextualPropertyKey.DependencyProperty; + + + /// + /// Gets whether the tab item is selected. + /// This is a dependency propert. + /// + public bool IsSelected + { + get { return (bool)GetValue(IsSelectedProperty); } + internal set { SetValue(IsSelectedPropertyKey, value); } + } + + // Using a DependencyProperty as the backing store for IsSelected. This enables animation, styling, binding, etc... + private static readonly DependencyPropertyKey IsSelectedPropertyKey = + DependencyProperty.RegisterReadOnly("IsSelected", typeof(bool), typeof(RibbonTabItem), new UIPropertyMetadata(false, null, CoerceIsSelected)); + + public static object CoerceIsSelected(DependencyObject d, object baseValue) + { + RibbonTabItem item = (RibbonTabItem)d; + if (item.RibbonBar.CanMinimize && !item.RibbonBar.IsExpanded) + { + return false; + } + return baseValue; + } + + public static readonly DependencyProperty IsSelectedProperty = IsSelectedPropertyKey.DependencyProperty; + + + protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) + { + base.OnMouseLeftButtonDown(e); + if (RibbonBar != null) RibbonBar.SelectedTabItem = this; + e.Handled = true; + } + + private RibbonBar ribbon; + + /// + /// Gets the RibbonBar to which this tab item is added. + /// + public RibbonBar RibbonBar + { + get { return ribbon; } + internal set { ribbon = value; } + } + + protected override void OnMouseDoubleClick(MouseButtonEventArgs e) + { + if (e.LeftButton == MouseButtonState.Pressed) + { + RibbonBar.IsExpanded = false; + RibbonBar.CanMinimize ^= true; + e.Handled = true; + } + base.OnMouseDoubleClick(e); + } + + protected override void OnKeyDown(KeyEventArgs e) + { + if (!e.Handled) + { + switch (e.Key) + { + case Key.Left: + SelectPreviousTab(); + e.Handled = true; + break; + + case Key.Right: + SelectNextTab(); + e.Handled = true; + break; + } + } + base.OnKeyDown(e); + if (e.Key == Key.Space) RibbonBar.SelectedTabItem = this; + } + + private StringCollection reductionOrder; + + /// + /// Gets or sets the StringCollection with Name of each group to be reducted in that order. + /// + [TypeConverter(typeof(RibbonGroupReductionOrderConverter))] + public StringCollection ReductionOrder + { + get { return reductionOrder; } + set { reductionOrder = value; } + } + + + /// + /// Gets the color for the TabItem. + /// + public Color Color + { + get { return tabSet != null ? tabSet.Color : Colors.Transparent; } + } + + + private void SelectNextTab() + { + if (IsKeyboardFocused) + { + int index = ribbon.IndexOfVisibleTab(ribbon.SelectedTabItem) + 1; + RibbonTabItem tab = ribbon.VisibleTabFromIndex(index); + if (tab != null) + { + ribbon.SelectedTabItem = tab; + FocusSelectedTab(); + } + } + } + + private void FocusSelectedTab() + { + RibbonTabItem tab = ribbon.SelectedTabItem; + if (tab != null) tab.Focus(); + } + + private void SelectPreviousTab() + { + if (IsKeyboardFocused) + { + int index = ribbon.IndexOfVisibleTab(ribbon.SelectedTabItem) - 1; + RibbonTabItem tab = ribbon.VisibleTabFromIndex(index); + if (tab != null) + { + ribbon.SelectedTabItem = tab; + FocusSelectedTab(); + } + } + } + + + + #region IKeyboardCommand Members + + void IKeyTipControl.ExecuteKeyTip() + { + Focus(); + ribbon.SelectedTabItem = this; + } + + #endregion + } +} diff --git a/Odyssey/Odyssey/Ribbon/Controls/RibbonTabItemPanel.cs b/Odyssey/Odyssey/Ribbon/Controls/RibbonTabItemPanel.cs new file mode 100644 index 0000000..48e360c --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Controls/RibbonTabItemPanel.cs @@ -0,0 +1,138 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Controls; +using System.Windows; +using System.Windows.Media; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + /// + /// Uses to layout tab items and paint the separator if necassary. + /// + internal class RibbonTabItemPanel : Panel + { + + static Size infiniteSize = new Size(double.PositiveInfinity, double.PositiveInfinity); + + private double scaleX; + + protected override Size MeasureOverride(Size availableSize) + { + double width = 0; + double height = 0; + foreach (UIElement e in VisibleChildren) + { + e.Measure(infiniteSize); + height = Math.Max(height, e.DesiredSize.Height); + width += e.DesiredSize.Width; + } + + width = Math.Min(width, availableSize.Width); + double scaleX = CalculateScaleX(new Size(width, availableSize.Height)); + + if (this.scaleX != scaleX) InvalidateVisual(); + this.scaleX = scaleX; + if (scaleX < 1) + { + width = 0; + foreach (UIElement e in VisibleChildren) + { + e.Measure(new Size(e.DesiredSize.Width * scaleX, e.DesiredSize.Height)); + width += e.DesiredSize.Width; + } + } + return new Size(width, height); + } + + protected override System.Windows.Size ArrangeOverride(System.Windows.Size finalSize) + { + double scaleX = CalculateScaleX(finalSize); + + double x = 0; + double height = finalSize.Height; + foreach (UIElement e in VisibleChildren) + { + double w = e.DesiredSize.Width * scaleX; + e.Arrange(new Rect(x, 0.0, w, height)); + x += w; + } + return new Size(x, height); + } + + + IEnumerable VisibleChildren + { + get + { + foreach (UIElement e in Children) + { + if (e!=null && e.Visibility == Visibility.Visible) yield return e; + } + } + } + + private double GetMaxWidth() + { + double width = 0; + foreach (UIElement e in VisibleChildren) + { + width += e.DesiredSize.Width; + } + return width; + } + + double CalculateScaleX(Size finalSize) + { + double MaxWidth = GetMaxWidth(); + double scaleX = MaxWidth > 0 ? Math.Min(1, finalSize.Width / MaxWidth) : 1.0; + if (double.IsPositiveInfinity(scaleX)) scaleX = 1.0; + return scaleX; + } + + protected override void OnRender(System.Windows.Media.DrawingContext dc) + { + base.OnRender(dc); + double scaleX = this.scaleX; + if (scaleX < 1d) + { + Brush brush = SeparatorBrush.Clone(); + brush.Opacity = 1d - scaleX * 0.8d; + Pen pen = new Pen(brush, 1); + + double x = 0; + foreach (UIElement e in Children) + { + if (x > 0) + { + dc.DrawLine(pen, new Point(x, 2), new Point(x, ActualHeight - 8)); + } + x += e.DesiredSize.Width; + } + } + } + + + + /// + /// Gets or sets the brush that is used to paint the separators between each tab item if + /// the available width is too small. + /// + public Brush SeparatorBrush + { + get { return (Brush)GetValue(SeparatorBrushProperty); } + set { SetValue(SeparatorBrushProperty, value); } + } + + // Using a DependencyProperty as the backing store for SeparatorBrush. This enables animation, styling, binding, etc... + public static readonly DependencyProperty SeparatorBrushProperty = + DependencyProperty.Register("SeparatorBrush", typeof(Brush), typeof(RibbonTabItemPanel), new UIPropertyMetadata(new SolidColorBrush(Colors.SlateBlue))); + + } +} diff --git a/Odyssey/Odyssey/Ribbon/Controls/RibbonTabScroller.cs b/Odyssey/Odyssey/Ribbon/Controls/RibbonTabScroller.cs new file mode 100644 index 0000000..87d68e2 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Controls/RibbonTabScroller.cs @@ -0,0 +1,215 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Controls; +using System.Windows; +using System.Windows.Media; +using System.Windows.Input; +using System.Windows.Media.Animation; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + /// + /// used to scroll a RibbonTab on demand if it does not fit the available width. + /// + [TemplatePart(Name = partScrollViewer)] + internal class RibbonTabScroller : ContentControl + { + const string partScrollViewer = "PART_ScrollViewer"; + + static RibbonTabScroller() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonTabScroller), new FrameworkPropertyMetadata(typeof(RibbonTabScroller))); + RegisterCommands(); + } + + + private static void RegisterCommands() + { + CommandManager.RegisterClassCommandBinding(typeof(RibbonTabScroller), new CommandBinding(scrollLeftCommand, ScrollLeftExecute)); + CommandManager.RegisterClassCommandBinding(typeof(RibbonTabScroller), new CommandBinding(scrollRightCommand, ScrollRightExecute)); + } + + private static void ScrollLeftExecute(object sender, ExecutedRoutedEventArgs e) + { + RibbonTabScroller scroller = (RibbonTabScroller)sender; + scroller.Alignment = RibbonBarAlignment.Left; + } + + + private static void ScrollRightExecute(object sender, ExecutedRoutedEventArgs e) + { + RibbonTabScroller scroller = (RibbonTabScroller)sender; + scroller.Alignment = RibbonBarAlignment.Right; + } + + + private static RoutedUICommand scrollLeftCommand = new RoutedUICommand("Scroll Left", "ScrollLeftCommand", typeof(RibbonTabScroller)); + private static RoutedUICommand scrollRightCommand = new RoutedUICommand("Scroll Right", "ScrollRightCommand", typeof(RibbonTabScroller)); + + public static RoutedUICommand ScrollLeftCommand { get { return scrollLeftCommand; } } + public static RoutedUICommand ScrollRightCommand { get { return scrollRightCommand; } } + + + + private ScrollViewer scrollViewer; + + public override void OnApplyTemplate() + { + base.OnApplyTemplate(); + scrollViewer = GetTemplateChild(partScrollViewer) as ScrollViewer; + } + + + + public RibbonBarAlignment Alignment + { + get { return (RibbonBarAlignment)GetValue(AlignmentProperty); } + set { SetValue(AlignmentProperty, value); } + } + + + + public bool CanAnimate + { + get { return (bool)GetValue(CanAnimateProperty); } + set { SetValue(CanAnimateProperty, value); } + } + + // Using a DependencyProperty as the backing store for CanAnimate. This enables animation, styling, binding, etc... + public static readonly DependencyProperty CanAnimateProperty = + DependencyProperty.Register("CanAnimate", typeof(bool), typeof(RibbonTabScroller), new UIPropertyMetadata(true)); + + + + private void SetOffset(double offset) + { + if (scrollViewer != null) + { + scrollViewer.ScrollToHorizontalOffset(offset); + } + } + + + public double Offset + { + get { return (double)GetValue(OffsetProperty); } + set { SetValue(OffsetProperty, value); } + } + + + // Using a DependencyProperty as the backing store for Offset. This enables animation, styling, binding, etc... + public static readonly DependencyProperty OffsetProperty = + DependencyProperty.Register("Offset", typeof(double), typeof(RibbonTabScroller), + new UIPropertyMetadata(0.0, OffsetPropertyChanged)); + + public static void OffsetPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + RibbonTabScroller scroller = (RibbonTabScroller)d; + double offset = (double)e.NewValue; + scroller.SetOffset(offset); + + } + + + // Using a DependencyProperty as the backing store for Alignment. This enables animation, styling, binding, etc... + public static readonly DependencyProperty AlignmentProperty = + DependencyProperty.Register("Alignment", typeof(RibbonBarAlignment), typeof(RibbonTabScroller), + new UIPropertyMetadata(RibbonBarAlignment.Full, AlignmentPropertyChanged)); + + public static void AlignmentPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + RibbonTabScroller scroller = (RibbonTabScroller)d; + RibbonBarAlignment alignment = (RibbonBarAlignment)e.NewValue; + + switch(alignment) + { + case RibbonBarAlignment.Right: + scroller.ScrollRight(); + break; + + default: + scroller.ScrollLeft(); + break; + } + + scroller.InvalidateMeasure(); + scroller.InvalidateArrange(); + + } + + + + public Color Color + { + get { return (Color)GetValue(ColorProperty); } + set { SetValue(ColorProperty, value); } + } + + // Using a DependencyProperty as the backing store for Color. This enables animation, styling, binding, etc... + public static readonly DependencyProperty ColorProperty = + DependencyProperty.Register("Color", typeof(Color), typeof(RibbonTabScroller), new UIPropertyMetadata(Colors.Transparent, ColorPropertyChanged)); + + public static void ColorPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + RibbonTabScroller ts = (RibbonTabScroller)o; + Color color = (Color)e.NewValue; + ts.IsColorized = color != Colors.Transparent; + ts.OnColorPopertyChanged(e); + } + + protected virtual void OnColorPopertyChanged(DependencyPropertyChangedEventArgs e) + { + + } + + + + + public bool IsColorized + { + get { return (bool)GetValue(IsColorizedProperty); } + set { SetValue(IsColorizedProperty, value); } + } + + // Using a DependencyProperty as the backing store for IsColorized. This enables animation, styling, binding, etc... + public static readonly DependencyProperty IsColorizedProperty = + DependencyProperty.Register("IsColorized", typeof(bool), typeof(RibbonTabScroller), new UIPropertyMetadata(false)); + + + + private void ScrollLeft() + { + MoveTo(0.0); + } + + private void MoveTo(double offset) + { + if (CanAnimate) + { + DoubleAnimation a = new DoubleAnimation(offset, new Duration(TimeSpan.FromMilliseconds(250))); + a.DecelerationRatio = 1.0; + BeginAnimation(OffsetProperty, a); + } + else + { + Offset = offset; + } + } + + private void ScrollRight() + { + if (scrollViewer!=null) + { + double w = Math.Max(0, scrollViewer.ExtentWidth - scrollViewer.ActualWidth); + MoveTo(w); + } + } + } +} diff --git a/Odyssey/Odyssey/Ribbon/Controls/RibbonTextBox.cs b/Odyssey/Odyssey/Ribbon/Controls/RibbonTextBox.cs new file mode 100644 index 0000000..4c13e14 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Controls/RibbonTextBox.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Controls; +using System.Windows; +using System.Windows.Media; +using System.Windows.Controls.Primitives; +using Odyssey.Controls.Ribbon.Interfaces; +using System.Diagnostics; +using System.Windows.Input; +using Odyssey.Controls.Interfaces; + + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + public class RibbonTextBox : TextBox, IRibbonControl, IRibbonStretch,IKeyTipControl + { + + static RibbonTextBox() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonTextBox), new FrameworkPropertyMetadata(typeof(RibbonTextBox))); + KeyboardNavigation.TabNavigationProperty.OverrideMetadata(typeof(RibbonTextBox), new FrameworkPropertyMetadata(KeyboardNavigationMode.Local)); + KeyboardNavigation.ControlTabNavigationProperty.OverrideMetadata(typeof(RibbonTextBox), new FrameworkPropertyMetadata(KeyboardNavigationMode.None)); + KeyboardNavigation.DirectionalNavigationProperty.OverrideMetadata(typeof(RibbonTextBox), new FrameworkPropertyMetadata(KeyboardNavigationMode.None)); + + } + + + + /// + /// Gets or sets the image of the textbox. + /// + public ImageSource Image + { + get { return (ImageSource)GetValue(ImageProperty); } + set { SetValue(ImageProperty, value); } + } + + // Using a DependencyProperty as the backing store for Image. This enables animation, styling, binding, etc... + public static readonly DependencyProperty ImageProperty = + DependencyProperty.Register("Image", typeof(ImageSource), typeof(RibbonTextBox), new UIPropertyMetadata(null)); + + + /// + /// Gets or sets the title of the textbox that appears with Appearance = Medium. + /// + public string Title + { + get { return (string)GetValue(TitleProperty); } + set { SetValue(TitleProperty, value); } + } + + // Using a DependencyProperty as the backing store for Title. This enables animation, styling, binding, etc... + public static readonly DependencyProperty TitleProperty = + DependencyProperty.Register("Title", typeof(string), typeof(RibbonTextBox), new UIPropertyMetadata("")); + + + + + /// + /// Gets or sets the width for the label. + /// + public double LabelWidth + { + get { return (double)GetValue(LabelWidthProperty); } + set { SetValue(LabelWidthProperty, value); } + } + + public static readonly DependencyProperty LabelWidthProperty = + DependencyProperty.Register("LabelWidth", typeof(double), typeof(RibbonTextBox), new UIPropertyMetadata(double.NaN)); + + + + /// + /// Gets or sets the with for the textbox. + /// This is a dependency property. + /// + public double ContentWidth + { + get { return (double)GetValue(ContentWidthProperty); } + set { SetValue(ContentWidthProperty, value); } + } + + // Using a DependencyProperty as the backing store for ContentWidth. This enables animation, styling, binding, etc... + public static readonly DependencyProperty ContentWidthProperty = + DependencyProperty.Register("ContentWidth", typeof(double), typeof(RibbonTextBox), new UIPropertyMetadata(double.NaN)); + + + #region IKeyboardCommand Members + + public void ExecuteKeyTip() + { + Focus(); + SelectAll(); + } + + #endregion + } +} diff --git a/Odyssey/Odyssey/Ribbon/Controls/RibbonThumbnail.cs b/Odyssey/Odyssey/Ribbon/Controls/RibbonThumbnail.cs new file mode 100644 index 0000000..392149e --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Controls/RibbonThumbnail.cs @@ -0,0 +1,119 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Controls; +using System.Windows.Media; +using System.Windows; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + /// + /// Represents an item container for a RibbonGallery. + /// + public class RibbonThumbnail:ListBoxItem + { + static RibbonThumbnail() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonThumbnail), new FrameworkPropertyMetadata(typeof(RibbonThumbnail))); + } + + public ImageSource ImageSource + { + get { return (ImageSource)GetValue(ImageSourceProperty); } + set { SetValue(ImageSourceProperty, value); } + } + + // Using a DependencyProperty as the backing store for ImageSource. This enables animation, styling, binding, etc... + public static readonly DependencyProperty ImageSourceProperty = + DependencyProperty.Register("ImageSource", typeof(ImageSource), typeof(RibbonThumbnail), new UIPropertyMetadata(null)); + + + public RibbonThumbnail Original { get; internal set; } + + protected override void OnMouseLeftButtonDown(System.Windows.Input.MouseButtonEventArgs e) + { + base.OnMouseLeftButtonDown(e); + Select(); + e.Handled = true; + } + + private void Select() + { + RibbonThumbnail original = this.Original != null ? Original : this; + if (Gallery != null) Gallery.SelectedItem = original; + } + + private RibbonGallery Gallery + { + get { + RibbonGallery gallery = Parent as RibbonGallery; + if (gallery == null) + { + FrameworkElement e = Parent as FrameworkElement; + gallery = e != null ? e.TemplatedParent as RibbonGallery : null; + } + return gallery; + } + } + + protected override void OnMouseLeftButtonUp(System.Windows.Input.MouseButtonEventArgs e) + { + base.OnMouseLeftButtonUp(e); + + // also close the poupup when the item is already selected: + if (this.IsFocused) + { + RibbonGallery g = Gallery; + if (g != null) g.IsDropDownOpen = false; + } + } + + protected override void OnMouseEnter(System.Windows.Input.MouseEventArgs e) + { + base.OnMouseEnter(e); + RibbonThumbnail thumb = Original != null ? Original : this; + if (Gallery != null) Gallery.HotThumbnail = thumb; + } + + protected override void OnMouseLeave(System.Windows.Input.MouseEventArgs e) + { + base.OnMouseLeave(e); + if (Gallery != null) Gallery.HotThumbnail = null; + } + + + protected override void OnKeyUp(System.Windows.Input.KeyEventArgs e) + { + if (!e.Handled) + { + switch (e.Key) + { + case System.Windows.Input.Key.Enter: + case System.Windows.Input.Key.Space: + Select(); + break; + } + } + base.OnKeyUp(e); + } + + public Stretch Stretch + { + get { return (Stretch)GetValue(StretchProperty); } + set { SetValue(StretchProperty, value); } + } + + // Using a DependencyProperty as the backing store for Stretch. This enables animation, styling, binding, etc... + public static readonly DependencyProperty StretchProperty = + DependencyProperty.Register("Stretch", typeof(Stretch), typeof(RibbonThumbnail), new UIPropertyMetadata(Stretch.Fill)); + + + + } +} diff --git a/Odyssey/Odyssey/Ribbon/Controls/RibbonToggleButton.cs b/Odyssey/Odyssey/Ribbon/Controls/RibbonToggleButton.cs new file mode 100644 index 0000000..edafc3c --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Controls/RibbonToggleButton.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using System.Windows.Controls.Primitives; +using Odyssey.Controls.Ribbon.Interfaces; +using Odyssey.Controls.Interfaces; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + + public class RibbonToggleButton : ToggleButton,IRibbonButton,IKeyTipControl + { + static RibbonToggleButton() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonToggleButton), new FrameworkPropertyMetadata(typeof(RibbonToggleButton))); + } + + + + public bool IsFlat + { + get { return (bool)GetValue(IsFlatProperty); } + set { SetValue(IsFlatProperty, value); } + } + + // Using a DependencyProperty as the backing store for IsFlat. This enables animation, styling, binding, etc... + public static readonly DependencyProperty IsFlatProperty = + DependencyProperty.Register("IsFlat", typeof(bool), typeof(RibbonToggleButton), new UIPropertyMetadata(true)); + + + public ImageSource LargeImage + { + get { return (ImageSource)GetValue(LargeImageProperty); } + set { SetValue(LargeImageProperty, value); } + } + + // Using a DependencyProperty as the backing store for LargeImage. This enables animation, styling, binding, etc... + public static readonly DependencyProperty LargeImageProperty = + DependencyProperty.Register("LargeImage", typeof(ImageSource), typeof(RibbonToggleButton), new UIPropertyMetadata(null)); + + + + public ImageSource SmallImage + { + get { return (ImageSource)GetValue(SmallImageProperty); } + set { SetValue(SmallImageProperty, value); } + } + + // Using a DependencyProperty as the backing store for SmallImage. This enables animation, styling, binding, etc... + public static readonly DependencyProperty SmallImageProperty = + DependencyProperty.Register("SmallImage", typeof(ImageSource), typeof(RibbonToggleButton), new UIPropertyMetadata(null)); + + + + public CornerRadius CornerRadius + { + get { return (CornerRadius)GetValue(CornerRadiusProperty); } + set { SetValue(CornerRadiusProperty, value); } + } + + // Using a DependencyProperty as the backing store for CornerRadius. This enables animation, styling, binding, etc... + public static readonly DependencyProperty CornerRadiusProperty = + DependencyProperty.Register("CornerRadius", typeof(CornerRadius), typeof(RibbonToggleButton), new UIPropertyMetadata(new CornerRadius(4))); + + + #region IKeyboardCommand Members + + public void ExecuteKeyTip() + { + OnClick(); + } + + #endregion + } +} diff --git a/Odyssey/Odyssey/Ribbon/Controls/RibbonToolTip.cs b/Odyssey/Odyssey/Ribbon/Controls/RibbonToolTip.cs new file mode 100644 index 0000000..d35846b --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Controls/RibbonToolTip.cs @@ -0,0 +1,159 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using System.Windows.Controls.Primitives; + +namespace Odyssey.Controls +{ + /// + /// Enhanced tooltip with additional Properties that appears below the ribbon bar. + /// + public class RibbonToolTip : ToolTip + { + static RibbonToolTip() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonToolTip), new FrameworkPropertyMetadata(typeof(RibbonToolTip))); + } + + public RibbonToolTip() + : base() + { + Placement = System.Windows.Controls.Primitives.PlacementMode.Custom; + CustomPopupPlacementCallback = new System.Windows.Controls.Primitives.CustomPopupPlacementCallback(PopupPlacement); + } + + /// + /// Ensure the position of the tooltip to be exactly below the if available, otherwise use default positioning. + /// + private CustomPopupPlacement[] PopupPlacement(Size popupSize, Size targetSize, Point offset) + { + RibbonBar ribbon = GetRibbonBar(); + double y = ActualHeight; + double x = 0.0d; + + if (ribbon != null) + { + FrameworkElement owner = this.PlacementTarget as FrameworkElement; + if (!(owner is RibbonApplicationMenu)) + { + if (owner.IsDescendantOf(ribbon)) + { + Point p = ribbon.TranslatePoint(new Point(), owner); + y = ribbon.ActualHeight + p.Y; + } + else + { + Popup popup = ribbon.Popup; + FrameworkElement child = popup.Child as FrameworkElement; + if (child != null) + { + Point p = child.TranslatePoint(new Point(), owner); + y = child.ActualHeight + p.Y; + } + } + } + else + { + y = owner.ActualHeight + 4.0d; + x = 0d; + } + } + + CustomPopupPlacement placement = new CustomPopupPlacement(new Point(x, y), PopupPrimaryAxis.Vertical); + return new CustomPopupPlacement[] { placement }; + } + + private RibbonBar GetRibbonBar() + { + FrameworkElement parent = this.PlacementTarget as FrameworkElement; + while (parent != null && !(parent is RibbonBar)) + { + parent = parent.Parent != null ? parent.Parent as FrameworkElement : parent.TemplatedParent as FrameworkElement; + } + return parent as RibbonBar; + } + + + /// + /// Gets or sets the title for the tooltip. + /// This is a dependency property. + /// + public string Title + { + get { return (string)GetValue(TitleProperty); } + set { SetValue(TitleProperty, value); } + } + + public static readonly DependencyProperty TitleProperty = + DependencyProperty.Register("Title", typeof(string), typeof(RibbonToolTip), new UIPropertyMetadata(null)); + + + + /// + /// Gets or sets the footer for the tooltip. + /// This is a dependency property. + /// + public string Footer + { + get { return (string)GetValue(FooterProperty); } + set { SetValue(FooterProperty, value); } + } + + public static readonly DependencyProperty FooterProperty = + DependencyProperty.Register("Footer", typeof(string), typeof(RibbonToolTip), new UIPropertyMetadata(null)); + + + + /// + /// Gets or sets the description for the tooltip. + /// This is a dependency property. + /// + public string Description + { + get { return (string)GetValue(DescriptionProperty); } + set { SetValue(DescriptionProperty, value); } + } + + public static readonly DependencyProperty DescriptionProperty = + DependencyProperty.Register("Description", typeof(string), typeof(RibbonToolTip), new UIPropertyMetadata(null)); + + + /// + /// Gets or sets the Image for the tooltip. + /// This is a dependency property. + /// + public ImageSource Image + { + get { return (ImageSource)GetValue(ImageProperty); } + set { SetValue(ImageProperty, value); } + } + + public static readonly DependencyProperty ImageProperty = + DependencyProperty.Register("Image", typeof(ImageSource), typeof(RibbonToolTip), new UIPropertyMetadata(null)); + + + /// + /// Gets or sets the Image for the footer of the tooltip. + /// This is a dependency property. + /// + public ImageSource FooterImage + { + get { return (ImageSource)GetValue(FooterImageProperty); } + set { SetValue(FooterImageProperty, value); } + } + + public static readonly DependencyProperty FooterImageProperty = + DependencyProperty.Register("FooterImage", typeof(ImageSource), typeof(RibbonToolTip), new UIPropertyMetadata(null)); + + } +} diff --git a/Odyssey/Odyssey/Ribbon/Controls/RibbonWindow.Commands.cs b/Odyssey/Odyssey/Ribbon/Controls/RibbonWindow.Commands.cs new file mode 100644 index 0000000..acef6a9 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Controls/RibbonWindow.Commands.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Input; +using System.Windows; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + public partial class RibbonWindow + { + public static readonly RoutedUICommand CloseCommand = new RoutedUICommand("Close", "CloseCommand", typeof(RibbonWindow)); + public static readonly RoutedUICommand MinimizeCommand = new RoutedUICommand("Minimize", "MinimizeCommand", typeof(RibbonWindow)); + public static readonly RoutedUICommand MaximizeCommand = new RoutedUICommand("Maximize", "MaximizeCommand", typeof(RibbonWindow)); + + private static void RegisterCommands() + { + CommandManager.RegisterClassCommandBinding(typeof(RibbonWindow), new CommandBinding(CloseCommand, PerformClose)); + CommandManager.RegisterClassCommandBinding(typeof(RibbonWindow), new CommandBinding(MinimizeCommand, PerformMinimize)); + CommandManager.RegisterClassCommandBinding(typeof(RibbonWindow), new CommandBinding(MaximizeCommand, PerformMaximize)); + + } + + + private static void PerformClose(object sender, ExecutedRoutedEventArgs e) + { + RibbonWindow window = (RibbonWindow)sender; + window.Close(); + } + + private static void PerformMinimize(object sender, ExecutedRoutedEventArgs e) + { + RibbonWindow window = (RibbonWindow)sender; + window.WindowState = WindowState.Minimized; + } + + private static void PerformMaximize(object sender, ExecutedRoutedEventArgs e) + { + RibbonWindow window = (RibbonWindow)sender; + window.WindowState = window.WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized; + } + } +} diff --git a/Odyssey/Odyssey/Ribbon/Controls/RibbonWindow.cs b/Odyssey/Odyssey/Ribbon/Controls/RibbonWindow.cs new file mode 100644 index 0000000..d8045f4 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Controls/RibbonWindow.cs @@ -0,0 +1,462 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Interop; +using Odyssey.Native; +using System.Windows.Media; +using System.Diagnostics; +using System.Windows.Controls; +using Odyssey.Controls.Classes; +using System.Runtime.InteropServices; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + public partial class RibbonWindow : Window + { + const string partOuterBorder = "PART_OuterBorder"; + + static RibbonWindow() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonWindow), new FrameworkPropertyMetadata(typeof(RibbonWindow))); + } + + public RibbonWindow() + : base() + { + SizeChanged += new SizeChangedEventHandler(OnSizeChanged); + HookWndProc(); + RegisterCommands(); + SkinManager.SkinChanged += new EventHandler(OnSkinChanged); + } + + protected virtual void OnSkinChanged(object sender, EventArgs e) + { + SetWindowTitleBrush(); + } + + protected override void OnSourceInitialized(EventArgs e) + { + base.OnSourceInitialized(e); + SetGlassOn(); + } + + /// + /// Gets whether glass is available. + /// + public bool IsGlassAvailable + { + get + { + if (Environment.OSVersion.Version.Major >= 6) + { + if (NativeMethods.DwmIsCompositionEnabled()) return true; + } + return false; + } + } + + private void SetGlassOn() + { + if (IsGlassAvailable) + { + IntPtr hwnd = new WindowInteropHelper(this).Handle; + HwndSource src = HwndSource.FromHwnd(hwnd); + + // settings the following value is necassary to have a transparent glass background instead of black: + src.CompositionTarget.BackgroundColor = Color.FromArgb(0, 0, 0, 0); + + const int GlassBorderSize = 8; + const int GlassTitleBorderHeight = 31; + + NativeMethods.MARGINS margins = new NativeMethods.MARGINS(); + margins.cxLeftWidth = GlassBorderSize; + margins.cxRightWidth = GlassBorderSize; + margins.cyTopHeight = GlassTitleBorderHeight; + margins.cyBottomHeight = GlassBorderSize; + NativeMethods.DwmExtendFrameIntoClientArea(hwnd, ref margins); + } + } + + + private void HookWndProc() + { + EventHandler handler = delegate(object sender, EventArgs e) + { + ((HwndSource)PresentationSource.FromVisual(this)).AddHook(new HwndSourceHook(this.WndProc)); + }; + base.SourceInitialized += handler; + } + + protected override Size ArrangeOverride(Size arrangeBounds) + { + VerifyAccess(); + UIElement child = this.VisualChildrenCount > 0 ? GetVisualChild(0) as UIElement : null; + if (child != null) + { + Size size = new Size(arrangeBounds.Width, arrangeBounds.Height); + + child.Arrange(new Rect(size)); + return size; + + } + return arrangeBounds; + } + + protected override Size MeasureOverride(Size availableSize) + { + if (VisualChildrenCount > 0) + { + UIElement visualChild = GetVisualChild(0) as UIElement; + if (visualChild != null) + { + visualChild.Measure(availableSize); + return visualChild.DesiredSize; + } + } + return base.MeasureOverride(availableSize); + } + + + /// + /// Gets or sets whether Glass is enabled for Vista Aero. This does not necassarily mean that glass is applied but only when the conditions for glass match. + /// This is a dependency property. + /// + public bool IsGlassEnabled + { + get { return (bool)GetValue(IsGlassEnabledProperty); } + set { SetValue(IsGlassEnabledProperty, value); } + } + + public static readonly DependencyProperty IsGlassEnabledProperty = + DependencyProperty.Register("IsGlassEnabled", typeof(bool), typeof(RibbonWindow), + new FrameworkPropertyMetadata(false, GlassEnabledPropertyChanged)); + + + + + /// + /// Gets whether Glass is on. + /// This is a dependency property. + /// + public bool IsGlassOn + { + get { return (bool)GetValue(IsGlassOnProperty); } + private set { SetValue(IsGlassOnPropertyKey, value); } + } + + // Using a DependencyProperty as the backing store for IsGlassOn. This enables animation, styling, binding, etc... + private static readonly DependencyPropertyKey IsGlassOnPropertyKey = + DependencyProperty.RegisterReadOnly("IsGlassOn", typeof(bool), typeof(RibbonWindow), new UIPropertyMetadata(false, GlassOnPropertyChanged)); + + public static DependencyProperty IsGlassOnProperty = IsGlassOnPropertyKey.DependencyProperty; + + static void GlassOnPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + RibbonWindow w = (RibbonWindow)o; + w.SetWindowTitleBrush(); + } + + static void GlassEnabledPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + RibbonWindow w = (RibbonWindow)o; + w.SetIsGlassOnState(); + } + + private void SetIsGlassOnState() + { + IsGlassOn = IsGlassEnabled && (Environment.OSVersion.Version.Major >= 6) && NativeMethods.DwmIsCompositionEnabled(); + AttachRegion(); + } + + + + protected virtual void OnSizeChanged(object sender, SizeChangedEventArgs e) + { + AttachRegion(); + SetWindowTitleBrush(); + } + + + + private void AttachRegion() + { + if (!IsGlassOn) + { + NativeMethods.RECT rect; + IntPtr hwnd = new WindowInteropHelper(this).Handle; + NativeMethods.GetWindowRect(hwnd, out rect); + int w = rect.Width + 1; + int h = rect.Height + 1; + if (WindowState != WindowState.Maximized && RoundedCornerMode != RibbonWindowCornerMode.None) + { + // note: the last two parameters are the diameter, not the radius: + IntPtr rgn = NativeMethods.CreateRoundRectRgn(0, 0, w, h, 12, 12); + if (RoundedCornerMode == RibbonWindowCornerMode.Top) + { + IntPtr rgn2 = NativeMethods.CreateRectRgn(0, 6, w, h); + NativeMethods.CombineRgn(rgn, rgn2, rgn, 2); + } + NativeMethods.SetWindowRgn(hwnd, rgn, NativeMethods.IsWindowVisible(hwnd)); + } + else + { + IntPtr rgn = NativeMethods.CreateRectRgn(0, 0, w, h); + NativeMethods.SetWindowRgn(hwnd, rgn, NativeMethods.IsWindowVisible(hwnd)); + } + } + else + { + IntPtr hwnd = new WindowInteropHelper(this).Handle; + NativeMethods.SetWindowRgn(hwnd, IntPtr.Zero, NativeMethods.IsWindowVisible(hwnd)); + } + } + + /// + /// Handles the WndProc events to enable drawing inside the title bar. + /// + protected virtual IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) + { + IntPtr result = IntPtr.Zero; + NativeMethods.WM wm = (NativeMethods.WM)msg; + if (Environment.OSVersion.Version.Major >= 6) + { + handled = NativeMethods.DwmDefWindowProc(hwnd, msg, wParam, lParam, out result); + } + if (!handled) + switch (wm) + { + + case NativeMethods.WM.SIZE: + handled = false; + break; + + //TODO: This causes a uncontrolled moving of the window in windows 7 while dragging from WindowState.Maximized. + // don't paint the border and title: + case NativeMethods.WM.NCCALCSIZE: + handled = true; + break; + + case NativeMethods.WM.SETICON: + handled = true; + return IntPtr.Zero; + + case NativeMethods.WM.SETTEXT: + handled = true; + InvalidateArrange(); + break; + + + case NativeMethods.WM.NCACTIVATE: + IsWindowActive = wParam.ToInt32() == 1; + handled = true; + result = NativeMethods.DefWindowProc(hwnd, NativeMethods.WM.NCACTIVATE, wParam, new IntPtr(-1)); + break; + + // determine if the titlebar, or any of the borders is under the cursor position coded in m.lParam: + case NativeMethods.WM.NCHITTEST: + if (result == IntPtr.Zero) + { + WndProcHitTest(hwnd, lParam, ref handled, ref result); + } + break; + + + //TODO: changing the DWMCOMPOSITION currently causes a "This freezable cannot be frozen" exception. + case NativeMethods.WM.DWMCOMPOSITIONCHANGED: + SetIsGlassOnState(); + AttachRegion(); + InvalidateVisual(); + UpdateLayout(); + handled = true; + result = IntPtr.Zero; + break; + + } + + return result; + } + + + /// + /// Determine if any of the window borders or the titlebar is hit: + /// + private void WndProcHitTest(IntPtr hwnd, IntPtr lParam, ref bool handled, ref IntPtr result) + { + int xy = lParam.ToInt32(); + Point p = new Point(NativeMethods.SignedLoWord(xy), NativeMethods.SignedHiWord(xy)); + NativeMethods.RECT rect = new NativeMethods.RECT(); + NativeMethods.GetWindowRect(hwnd, out rect); + Rect windowRect = new Rect(rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top); + NativeMethods.HT ht = NCHitTest(p, windowRect); + result = new IntPtr((int)ht); + handled = ht != NativeMethods.HT.NOWHERE; + } + + + private NativeMethods.HT NCHitTest(Point p, Rect rect) + { + const double borderSize = 6.0; + const double titleHeight = 34.0; + + if (p.Y < rect.Top + borderSize) + { + if (p.X < rect.Left + borderSize) return NativeMethods.HT.TOPLEFT; + if (p.X > rect.Right - borderSize) return NativeMethods.HT.TOPRIGHT; + return NativeMethods.HT.TOP; + } + if (p.Y > rect.Bottom - borderSize) + { + if (p.X < rect.Left + borderSize) return NativeMethods.HT.BOTTOMLEFT; + if (p.X > rect.Right - borderSize) return NativeMethods.HT.BOTTOMRIGHT; + return NativeMethods.HT.BOTTOM; + } + if (p.X < rect.Left + borderSize) return NativeMethods.HT.LEFT; + if (p.X > rect.Right - borderSize) return NativeMethods.HT.RIGHT; + if (p.Y < rect.Top + titleHeight) + { + Point localPoint = ScreenToLocal(p); + IInputElement e = InputHitTest(localPoint); + if (e == null) + { + return NativeMethods.HT.CAPTION; + } + else + { + if (e == outerBorder) return NativeMethods.HT.CAPTION; + UIElement ue = e as UIElement; + if (ue == null || !ue.IsHitTestVisible) return NativeMethods.HT.CAPTION; + } + } + + return NativeMethods.HT.NOWHERE; + } + + private Point ScreenToLocal(Point point) + { + NativeMethods.RECT rect; + NativeMethods.GetWindowRect(new WindowInteropHelper(this).Handle, out rect); + Matrix matrix = PresentationSource.FromVisual(this).CompositionTarget.TransformFromDevice; + point.Offset((double)(-1 * rect.Left), (double)(-1 * rect.Top)); + point.X *= matrix.M11; + point.Y *= matrix.M22; + return point; + } + + private DependencyObject outerBorder; + + public override void OnApplyTemplate() + { + base.OnApplyTemplate(); + outerBorder = GetTemplateChild(partOuterBorder); + } + + + /// + /// Gets whether the window is active. + /// This is a dependency property. + /// + public bool IsWindowActive + { + get { return (bool)GetValue(IsWindowActiveProperty); } + private set { SetValue(IsWindowActivePropertyKey, value); } + } + + // Using a DependencyProperty as the backing store for IsWindowActive. This enables animation, styling, binding, etc... + private static readonly DependencyPropertyKey IsWindowActivePropertyKey = + DependencyProperty.RegisterReadOnly("IsWindowActive", typeof(bool), typeof(RibbonWindow), + new FrameworkPropertyMetadata(false, + FrameworkPropertyMetadataOptions.AffectsRender, + WindowActivePropertyChanged)); + + public static readonly DependencyProperty IsWindowActiveProperty = IsWindowActivePropertyKey.DependencyProperty; + + static void WindowActivePropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + RibbonWindow w = (RibbonWindow)o; + w.SetWindowTitleBrush(); + } + + void SetWindowTitleBrush() + { + if (IsGlassOn) + { + WindowTitleBrush = IsWindowActive ? SystemColors.ActiveCaptionTextBrush : SystemColors.InactiveCaptionTextBrush; + } + else + { + WindowTitleBrush = IsWindowActive ? ActiveTitleBrush : InactiveTitleBrush; + } + } + + + public RibbonWindowCornerMode RoundedCornerMode + { + get { return (RibbonWindowCornerMode)GetValue(RoundedCornerModeProperty); } + set { SetValue(RoundedCornerModeProperty, value); } + } + + // Using a DependencyProperty as the backing store for RoundedCornerMode. This enables animation, styling, binding, etc... + public static readonly DependencyProperty RoundedCornerModeProperty = + DependencyProperty.Register("RoundedCornerMode", typeof(RibbonWindowCornerMode), typeof(RibbonWindow), + new FrameworkPropertyMetadata(RibbonWindowCornerMode.Top, + FrameworkPropertyMetadataOptions.AffectsRender, + RoundedCornerModePropertyChanged)); + + + public static void RoundedCornerModePropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + RibbonWindow window = (RibbonWindow)o; + window.AttachRegion(); + } + + + /// + /// Gets the Brush for the window title. + /// This is a dependency property. + /// + public Brush WindowTitleBrush + { + get { return (Brush)GetValue(WindowTitleBrushProperty); } + private set { SetValue(WindowTitleBrushPropertyKey, value); } + } + + private static readonly DependencyPropertyKey WindowTitleBrushPropertyKey = + DependencyProperty.RegisterReadOnly("WindowTitleBrush", typeof(Brush), typeof(RibbonWindow), new UIPropertyMetadata(null)); + + public static DependencyProperty WindowTitleBrushProperty = WindowTitleBrushPropertyKey.DependencyProperty; + + + + + public Brush ActiveTitleBrush + { + get { return (Brush)GetValue(ActiveTitleBrushProperty); } + set { SetValue(ActiveTitleBrushProperty, value); } + } + + public static readonly DependencyProperty ActiveTitleBrushProperty = + DependencyProperty.Register("ActiveTitleBrush", typeof(Brush), typeof(RibbonWindow), new UIPropertyMetadata(null)); + + + + + public Brush InactiveTitleBrush + { + get { return (Brush)GetValue(InactiveTitleBrushProperty); } + set { SetValue(InactiveTitleBrushProperty, value); } + } + + public static readonly DependencyProperty InactiveTitleBrushProperty = + DependencyProperty.Register("InactiveTitleBrush", typeof(Brush), typeof(RibbonWindow), new UIPropertyMetadata(null)); + + + + } +} diff --git a/Odyssey/Odyssey/Ribbon/Controls/RibbonWrapPanel.cs b/Odyssey/Odyssey/Ribbon/Controls/RibbonWrapPanel.cs new file mode 100644 index 0000000..44433d9 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Controls/RibbonWrapPanel.cs @@ -0,0 +1,147 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using System.Windows.Markup; +using System.Collections; +using System.Diagnostics; +using Odyssey.Controls.Ribbon.Interfaces; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls +{ + public class RibbonWrapPanel : Panel + { + static RibbonWrapPanel() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonWrapPanel), new FrameworkPropertyMetadata(typeof(RibbonWrapPanel))); + } + + + const double smallHeight = 24; + + + protected override Size MeasureOverride(Size availableSize) + { + foreach (UIElement e in Children) e.Measure(infiniteSize); + return ArrangeOrMeasure(false); + } + + + private static Size infiniteSize = new Size(double.PositiveInfinity, double.PositiveInfinity); + + protected override Size ArrangeOverride(Size finalSize) + { + return ArrangeOrMeasure(true); + } + + private Size ArrangeOrMeasure(bool arrange) + { + double left = 0; + int rowIndex = 0; + int maxRows = 3; + + List rowElements = new List(maxRows); + + foreach (UIElement e in Children) + { + if (e.Visibility != Visibility.Visible) continue; + IRibbonControl ribbonControl = e as IRibbonControl; + Size dsize = e.DesiredSize; + if (dsize.Height > smallHeight) + { + if (rowIndex > 0) + { + left += ArrangeRow(rowElements, left, arrange); + rowIndex = 0; + } + if (arrange) + { + Size size = e.DesiredSize; + double h = Math.Max(smallHeight, size.Height); + e.Arrange(new Rect(left, 0, size.Width, h)); + } + left += e.DesiredSize.Width; + } + else + { + RibbonSize size = RibbonBar.GetSize(e); + if (size != RibbonSize.Minimized) + { + rowElements.Add(e); + if (++rowIndex == maxRows) + { + left += ArrangeRow(rowElements, left, arrange); + rowIndex = 0; + } + } + } + } + left += ArrangeRow(rowElements, left, arrange); + + left = Math.Max(32, left); + return new Size(left, smallHeight * 3); + } + + + protected double ArrangeRow(List rowElements, double left, bool arrange) + { + double max = 0; + + double rowHeight = smallHeight + (rowElements.Count == 2 ? (smallHeight / 3) : 0); + double topOffset = rowElements.Count == 2 ? smallHeight / 3 : 0; + + foreach (UIElement e in rowElements) + { + max = Math.Max(e.DesiredSize.Width, max); + } + foreach (UIElement e in rowElements) + { + if (arrange) + { + double h = Math.Max(smallHeight, e.DesiredSize.Height); + double w = e is IRibbonStretch ? max : e.DesiredSize.Width; + + FrameworkElement fe = e as FrameworkElement; + if (fe != null && fe.HorizontalAlignment != HorizontalAlignment.Left) + { + switch (fe.HorizontalAlignment) + { + case HorizontalAlignment.Right: + e.Arrange(new Rect(max - w + left, topOffset, w, h)); + break; + + case HorizontalAlignment.Center: + e.Arrange(new Rect((max - w) / 2 + left, topOffset, w, h)); + break; + + case HorizontalAlignment.Left: + case HorizontalAlignment.Stretch: + e.Arrange(new Rect(left, topOffset, w, h)); + break; + } + } + else e.Arrange(new Rect(left, topOffset, w, h)); + } + topOffset += rowHeight; + } + rowElements.Clear(); + return max; + } + + + } +} diff --git a/Odyssey/Odyssey/Ribbon/EventArgs/SelectedTabIndexChangedEvent.cs b/Odyssey/Odyssey/Ribbon/EventArgs/SelectedTabIndexChangedEvent.cs new file mode 100644 index 0000000..8a5ee79 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/EventArgs/SelectedTabIndexChangedEvent.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; + +namespace Odyssey.Ribbon.EventArgs +{ + public class SelectedTabIndexChangedEvent : RoutedPropertyChangedEventArgs + { + public SelectedTabIndexChangedEvent(int oldValue, int newValue, RoutedEvent routedEvent) + : base(oldValue, newValue, routedEvent) + { + } + } + + // public delegate void SelectedTabIndexChangedHandler(object sender, SelectedTabIndexChangedEvent e); +} diff --git a/Odyssey/Odyssey/Ribbon/Interfaces/IRibbonButton.cs b/Odyssey/Odyssey/Ribbon/Interfaces/IRibbonButton.cs new file mode 100644 index 0000000..ae6f0c3 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Interfaces/IRibbonButton.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Media; +using System.Windows; +using Odyssey.Controls.Interfaces; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls.Ribbon.Interfaces +{ + public interface IRibbonButton:IRibbonControl,IKeyTipControl + { + object Content { get; set; } + + ImageSource LargeImage { get; set; } + + ImageSource SmallImage { get; set; } + + CornerRadius CornerRadius { get; set; } + } +} diff --git a/Odyssey/Odyssey/Ribbon/Interfaces/IRibbonControl.cs b/Odyssey/Odyssey/Ribbon/Interfaces/IRibbonControl.cs new file mode 100644 index 0000000..e79713d --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Interfaces/IRibbonControl.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls.Ribbon.Interfaces +{ + public interface IRibbonControl + { + } +} diff --git a/Odyssey/Odyssey/Ribbon/Interfaces/IRibbonGallery.cs b/Odyssey/Odyssey/Ribbon/Interfaces/IRibbonGallery.cs new file mode 100644 index 0000000..71337a2 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Interfaces/IRibbonGallery.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls.Ribbon.Interfaces +{ + public interface IRibbonGallery:IRibbonControl + { + int Columns { get; set; } + bool IsCollapsed { get; set; } + int DropDownColumns { get; set; } + + void SetDropDownColumns(int columns); + } +} diff --git a/Odyssey/Odyssey/Ribbon/Interfaces/IRibbonLargeControl.cs b/Odyssey/Odyssey/Ribbon/Interfaces/IRibbonLargeControl.cs new file mode 100644 index 0000000..516676e --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Interfaces/IRibbonLargeControl.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls.Ribbon.Interfaces +{ + /// + /// Marks a control to always be large. + /// + public interface IRibbonLargeControl + { + } +} diff --git a/Odyssey/Odyssey/Ribbon/Interfaces/IRibbonStretch.cs b/Odyssey/Odyssey/Ribbon/Interfaces/IRibbonStretch.cs new file mode 100644 index 0000000..a38a000 --- /dev/null +++ b/Odyssey/Odyssey/Ribbon/Interfaces/IRibbonStretch.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +#region Copyright +// Odyssey.Controls.Ribbonbar +// (c) copyright 2009 Thomas Gerber +// This source code and files, is licensed under The Microsoft Public License (Ms-PL) +#endregion +namespace Odyssey.Controls.Ribbon.Interfaces +{ + /// + /// Defines controls that are stretched horizontally in a RibbonWrapPanel. + /// + public interface IRibbonStretch + { + } +} diff --git a/Odyssey/Odyssey/Skins/BlackSkin.xaml b/Odyssey/Odyssey/Skins/BlackSkin.xaml new file mode 100644 index 0000000..d979aff --- /dev/null +++ b/Odyssey/Odyssey/Skins/BlackSkin.xaml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Skins/BlueSkin.xaml b/Odyssey/Odyssey/Skins/BlueSkin.xaml new file mode 100644 index 0000000..a6ad92e --- /dev/null +++ b/Odyssey/Odyssey/Skins/BlueSkin.xaml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Skins/OutlookBar/OutlookBlackSkin.xaml b/Odyssey/Odyssey/Skins/OutlookBar/OutlookBlackSkin.xaml new file mode 100644 index 0000000..5071f46 --- /dev/null +++ b/Odyssey/Odyssey/Skins/OutlookBar/OutlookBlackSkin.xaml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Skins/OutlookBar/OutlookBlueSkin.xaml b/Odyssey/Odyssey/Skins/OutlookBar/OutlookBlueSkin.xaml new file mode 100644 index 0000000..b8d3e66 --- /dev/null +++ b/Odyssey/Odyssey/Skins/OutlookBar/OutlookBlueSkin.xaml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Skins/OutlookBar/OutlookSilverSkin.xaml b/Odyssey/Odyssey/Skins/OutlookBar/OutlookSilverSkin.xaml new file mode 100644 index 0000000..524a605 --- /dev/null +++ b/Odyssey/Odyssey/Skins/OutlookBar/OutlookSilverSkin.xaml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Skins/OutlookBar/Win7Skin.xaml b/Odyssey/Odyssey/Skins/OutlookBar/Win7Skin.xaml new file mode 100644 index 0000000..34ef156 --- /dev/null +++ b/Odyssey/Odyssey/Skins/OutlookBar/Win7Skin.xaml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Skins/Ribbon/OfficeBlackSkin.xaml b/Odyssey/Odyssey/Skins/Ribbon/OfficeBlackSkin.xaml new file mode 100644 index 0000000..b7418ca --- /dev/null +++ b/Odyssey/Odyssey/Skins/Ribbon/OfficeBlackSkin.xaml @@ -0,0 +1,203 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Skins/Ribbon/OfficeBlueSkin.xaml b/Odyssey/Odyssey/Skins/Ribbon/OfficeBlueSkin.xaml new file mode 100644 index 0000000..c43fb40 --- /dev/null +++ b/Odyssey/Odyssey/Skins/Ribbon/OfficeBlueSkin.xaml @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Skins/Ribbon/VistaSkin.xaml b/Odyssey/Odyssey/Skins/Ribbon/VistaSkin.xaml new file mode 100644 index 0000000..53a8657 --- /dev/null +++ b/Odyssey/Odyssey/Skins/Ribbon/VistaSkin.xaml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Skins/Ribbon/Window7Skin.xaml b/Odyssey/Odyssey/Skins/Ribbon/Window7Skin.xaml new file mode 100644 index 0000000..235c62c --- /dev/null +++ b/Odyssey/Odyssey/Skins/Ribbon/Window7Skin.xaml @@ -0,0 +1,343 @@ + + + + + + + + + + 0,2,0,2 + 0 + 0 + + 1,2,3,0 + 0 + 0 + Collapsed + + + + 1,0,1,0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Skins/SilverSkin.xaml b/Odyssey/Odyssey/Skins/SilverSkin.xaml new file mode 100644 index 0000000..36044aa --- /dev/null +++ b/Odyssey/Odyssey/Skins/SilverSkin.xaml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Skins/VistaSkin.xaml b/Odyssey/Odyssey/Skins/VistaSkin.xaml new file mode 100644 index 0000000..ea9eb8a --- /dev/null +++ b/Odyssey/Odyssey/Skins/VistaSkin.xaml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Skins/Win7Skin.xaml b/Odyssey/Odyssey/Skins/Win7Skin.xaml new file mode 100644 index 0000000..a78678a --- /dev/null +++ b/Odyssey/Odyssey/Skins/Win7Skin.xaml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Aero.NormalColor.xaml b/Odyssey/Odyssey/Themes/Aero.NormalColor.xaml new file mode 100644 index 0000000..19b9d54 --- /dev/null +++ b/Odyssey/Odyssey/Themes/Aero.NormalColor.xaml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/BreadcrumbBar/AeroChrome.xaml b/Odyssey/Odyssey/Themes/BreadcrumbBar/AeroChrome.xaml new file mode 100644 index 0000000..a9e5a5a --- /dev/null +++ b/Odyssey/Odyssey/Themes/BreadcrumbBar/AeroChrome.xaml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/BreadcrumbBar/BreadcrumbButton.xaml b/Odyssey/Odyssey/Themes/BreadcrumbBar/BreadcrumbButton.xaml new file mode 100644 index 0000000..9637919 --- /dev/null +++ b/Odyssey/Odyssey/Themes/BreadcrumbBar/BreadcrumbButton.xaml @@ -0,0 +1,168 @@ + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/BreadcrumbBar/BreadcrumbItem.xaml b/Odyssey/Odyssey/Themes/BreadcrumbBar/BreadcrumbItem.xaml new file mode 100644 index 0000000..6c6f703 --- /dev/null +++ b/Odyssey/Odyssey/Themes/BreadcrumbBar/BreadcrumbItem.xaml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/BreadcrumbBar/Brushes.xaml b/Odyssey/Odyssey/Themes/BreadcrumbBar/Brushes.xaml new file mode 100644 index 0000000..43cae9b --- /dev/null +++ b/Odyssey/Odyssey/Themes/BreadcrumbBar/Brushes.xaml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + M0,0 L5,3.5 0,7 z + M0,3 L7,3 3.5,7 z + F1 M7,0 L4,3 7,6 M3,0 L0,3 3,6 + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/BreadcrumbBar/ButtonTemplates.xaml b/Odyssey/Odyssey/Themes/BreadcrumbBar/ButtonTemplates.xaml new file mode 100644 index 0000000..9711a07 --- /dev/null +++ b/Odyssey/Odyssey/Themes/BreadcrumbBar/ButtonTemplates.xaml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/BreadcrumbBar/Generic.xaml b/Odyssey/Odyssey/Themes/BreadcrumbBar/Generic.xaml new file mode 100644 index 0000000..c0e297e --- /dev/null +++ b/Odyssey/Odyssey/Themes/BreadcrumbBar/Generic.xaml @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Odyssey/Odyssey/Themes/BreadcrumbBar/OdcTextBox.xaml b/Odyssey/Odyssey/Themes/BreadcrumbBar/OdcTextBox.xaml new file mode 100644 index 0000000..cdf10b9 --- /dev/null +++ b/Odyssey/Odyssey/Themes/BreadcrumbBar/OdcTextBox.xaml @@ -0,0 +1,108 @@ + + + + + + + + + + White + + + 0.65 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Controls/OdcTextBox.xaml b/Odyssey/Odyssey/Themes/Controls/OdcTextBox.xaml new file mode 100644 index 0000000..e5f4a9e --- /dev/null +++ b/Odyssey/Odyssey/Themes/Controls/OdcTextBox.xaml @@ -0,0 +1,94 @@ + + + + + + + + + + White + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Expander/Aero.NormalColor.ExpandHeader.xaml b/Odyssey/Odyssey/Themes/Expander/Aero.NormalColor.ExpandHeader.xaml new file mode 100644 index 0000000..c0ed939 --- /dev/null +++ b/Odyssey/Odyssey/Themes/Expander/Aero.NormalColor.ExpandHeader.xaml @@ -0,0 +1,89 @@ + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Expander/Aero.NormalColor.xaml b/Odyssey/Odyssey/Themes/Expander/Aero.NormalColor.xaml new file mode 100644 index 0000000..0a2e43e --- /dev/null +++ b/Odyssey/Odyssey/Themes/Expander/Aero.NormalColor.xaml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Odyssey/Odyssey/Themes/Expander/Classic.ExpandHeader.xaml b/Odyssey/Odyssey/Themes/Expander/Classic.ExpandHeader.xaml new file mode 100644 index 0000000..dae4a73 --- /dev/null +++ b/Odyssey/Odyssey/Themes/Expander/Classic.ExpandHeader.xaml @@ -0,0 +1,81 @@ + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Expander/Classic.xaml b/Odyssey/Odyssey/Themes/Expander/Classic.xaml new file mode 100644 index 0000000..e300e14 --- /dev/null +++ b/Odyssey/Odyssey/Themes/Expander/Classic.xaml @@ -0,0 +1,33 @@ + + + + + + + + + + + diff --git a/Odyssey/Odyssey/Themes/Expander/Generic.ExpandHeader.xaml b/Odyssey/Odyssey/Themes/Expander/Generic.ExpandHeader.xaml new file mode 100644 index 0000000..e090e78 --- /dev/null +++ b/Odyssey/Odyssey/Themes/Expander/Generic.ExpandHeader.xaml @@ -0,0 +1,78 @@ + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Expander/Generic.Expander.xaml b/Odyssey/Odyssey/Themes/Expander/Generic.Expander.xaml new file mode 100644 index 0000000..43680d6 --- /dev/null +++ b/Odyssey/Odyssey/Themes/Expander/Generic.Expander.xaml @@ -0,0 +1,132 @@ + + + + 0:0:0.20 + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Expander/Generic.xaml b/Odyssey/Odyssey/Themes/Expander/Generic.xaml new file mode 100644 index 0000000..1bb5dd7 --- /dev/null +++ b/Odyssey/Odyssey/Themes/Expander/Generic.xaml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + diff --git a/Odyssey/Odyssey/Themes/Expander/Luna.Homestead.xaml b/Odyssey/Odyssey/Themes/Expander/Luna.Homestead.xaml new file mode 100644 index 0000000..5a9cd0a --- /dev/null +++ b/Odyssey/Odyssey/Themes/Expander/Luna.Homestead.xaml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Odyssey/Odyssey/Themes/Expander/Luna.Metallic.xaml b/Odyssey/Odyssey/Themes/Expander/Luna.Metallic.xaml new file mode 100644 index 0000000..200ffc6 --- /dev/null +++ b/Odyssey/Odyssey/Themes/Expander/Luna.Metallic.xaml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Odyssey/Odyssey/Themes/Expander/Luna.NormalColor.xaml b/Odyssey/Odyssey/Themes/Expander/Luna.NormalColor.xaml new file mode 100644 index 0000000..0ec7c02 --- /dev/null +++ b/Odyssey/Odyssey/Themes/Expander/Luna.NormalColor.xaml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Odyssey/Odyssey/Themes/Generic.xaml b/Odyssey/Odyssey/Themes/Generic.xaml new file mode 100644 index 0000000..353f888 --- /dev/null +++ b/Odyssey/Odyssey/Themes/Generic.xaml @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/Odyssey/Odyssey/Themes/Luna.Homestead.xaml b/Odyssey/Odyssey/Themes/Luna.Homestead.xaml new file mode 100644 index 0000000..2acb5b4 --- /dev/null +++ b/Odyssey/Odyssey/Themes/Luna.Homestead.xaml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Luna.Metallic.xaml b/Odyssey/Odyssey/Themes/Luna.Metallic.xaml new file mode 100644 index 0000000..fdecda1 --- /dev/null +++ b/Odyssey/Odyssey/Themes/Luna.Metallic.xaml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Luna.NormalColor.xaml b/Odyssey/Odyssey/Themes/Luna.NormalColor.xaml new file mode 100644 index 0000000..fd95e20 --- /dev/null +++ b/Odyssey/Odyssey/Themes/Luna.NormalColor.xaml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/OutlookBar/Generic.xaml b/Odyssey/Odyssey/Themes/OutlookBar/Generic.xaml new file mode 100644 index 0000000..1546c2d --- /dev/null +++ b/Odyssey/Odyssey/Themes/OutlookBar/Generic.xaml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/OutlookBar/OutlookBar.xaml b/Odyssey/Odyssey/Themes/OutlookBar/OutlookBar.xaml new file mode 100644 index 0000000..4cbb122 --- /dev/null +++ b/Odyssey/Odyssey/Themes/OutlookBar/OutlookBar.xaml @@ -0,0 +1,364 @@ + + + + + #FFFFD76A + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/OutlookBar/ToggleButton.xaml b/Odyssey/Odyssey/Themes/OutlookBar/ToggleButton.xaml new file mode 100644 index 0000000..4b6ca9d --- /dev/null +++ b/Odyssey/Odyssey/Themes/OutlookBar/ToggleButton.xaml @@ -0,0 +1,37 @@ + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Ribbon/DefaultRibbonButtonBrushes.xaml b/Odyssey/Odyssey/Themes/Ribbon/DefaultRibbonButtonBrushes.xaml new file mode 100644 index 0000000..80674d1 --- /dev/null +++ b/Odyssey/Odyssey/Themes/Ribbon/DefaultRibbonButtonBrushes.xaml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #FF0094FF + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Ribbon/DefaultWindowButtonStyles.xaml b/Odyssey/Odyssey/Themes/Ribbon/DefaultWindowButtonStyles.xaml new file mode 100644 index 0000000..2b6f174 --- /dev/null +++ b/Odyssey/Odyssey/Themes/Ribbon/DefaultWindowButtonStyles.xaml @@ -0,0 +1,88 @@ + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Ribbon/Generic.xaml b/Odyssey/Odyssey/Themes/Ribbon/Generic.xaml new file mode 100644 index 0000000..b20c4d4 --- /dev/null +++ b/Odyssey/Odyssey/Themes/Ribbon/Generic.xaml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Odyssey/Odyssey/Themes/Ribbon/HighlightedBackgrounds.xaml b/Odyssey/Odyssey/Themes/Ribbon/HighlightedBackgrounds.xaml new file mode 100644 index 0000000..8d2a94f --- /dev/null +++ b/Odyssey/Odyssey/Themes/Ribbon/HighlightedBackgrounds.xaml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Ribbon/InternalRibbonButton.xaml b/Odyssey/Odyssey/Themes/Ribbon/InternalRibbonButton.xaml new file mode 100644 index 0000000..6968e03 --- /dev/null +++ b/Odyssey/Odyssey/Themes/Ribbon/InternalRibbonButton.xaml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Ribbon/QuickAccessKey.xaml b/Odyssey/Odyssey/Themes/Ribbon/QuickAccessKey.xaml new file mode 100644 index 0000000..30475bf --- /dev/null +++ b/Odyssey/Odyssey/Themes/Ribbon/QuickAccessKey.xaml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Ribbon/RibbonAppMenuItem.xaml b/Odyssey/Odyssey/Themes/Ribbon/RibbonAppMenuItem.xaml new file mode 100644 index 0000000..6b4989a --- /dev/null +++ b/Odyssey/Odyssey/Themes/Ribbon/RibbonAppMenuItem.xaml @@ -0,0 +1,146 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Ribbon/RibbonApplicationButton.xaml b/Odyssey/Odyssey/Themes/Ribbon/RibbonApplicationButton.xaml new file mode 100644 index 0000000..b9df9e4 --- /dev/null +++ b/Odyssey/Odyssey/Themes/Ribbon/RibbonApplicationButton.xaml @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Ribbon/RibbonApplicationMenu.xaml b/Odyssey/Odyssey/Themes/Ribbon/RibbonApplicationMenu.xaml new file mode 100644 index 0000000..cf17b7e --- /dev/null +++ b/Odyssey/Odyssey/Themes/Ribbon/RibbonApplicationMenu.xaml @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Ribbon/RibbonBar.xaml b/Odyssey/Odyssey/Themes/Ribbon/RibbonBar.xaml new file mode 100644 index 0000000..0fecd54 --- /dev/null +++ b/Odyssey/Odyssey/Themes/Ribbon/RibbonBar.xaml @@ -0,0 +1,185 @@ + + + + + + + Center + Center + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Ribbon/RibbonButton.xaml b/Odyssey/Odyssey/Themes/Ribbon/RibbonButton.xaml new file mode 100644 index 0000000..837c368 --- /dev/null +++ b/Odyssey/Odyssey/Themes/Ribbon/RibbonButton.xaml @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Odyssey/Odyssey/Themes/Ribbon/RibbonButtonGroup.xaml b/Odyssey/Odyssey/Themes/Ribbon/RibbonButtonGroup.xaml new file mode 100644 index 0000000..d88756f --- /dev/null +++ b/Odyssey/Odyssey/Themes/Ribbon/RibbonButtonGroup.xaml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Ribbon/RibbonChrome.xaml b/Odyssey/Odyssey/Themes/Ribbon/RibbonChrome.xaml new file mode 100644 index 0000000..34324dd --- /dev/null +++ b/Odyssey/Odyssey/Themes/Ribbon/RibbonChrome.xaml @@ -0,0 +1,116 @@ + + + + + + + + 0.50 + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Ribbon/RibbonComboBox.xaml b/Odyssey/Odyssey/Themes/Ribbon/RibbonComboBox.xaml new file mode 100644 index 0000000..7f677ef --- /dev/null +++ b/Odyssey/Odyssey/Themes/Ribbon/RibbonComboBox.xaml @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Ribbon/RibbonComboBoxItem.xaml b/Odyssey/Odyssey/Themes/Ribbon/RibbonComboBoxItem.xaml new file mode 100644 index 0000000..d1c4fb0 --- /dev/null +++ b/Odyssey/Odyssey/Themes/Ribbon/RibbonComboBoxItem.xaml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Ribbon/RibbonContextualTabSet.xaml b/Odyssey/Odyssey/Themes/Ribbon/RibbonContextualTabSet.xaml new file mode 100644 index 0000000..a13df9f --- /dev/null +++ b/Odyssey/Odyssey/Themes/Ribbon/RibbonContextualTabSet.xaml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Ribbon/RibbonDropDownButton.xaml b/Odyssey/Odyssey/Themes/Ribbon/RibbonDropDownButton.xaml new file mode 100644 index 0000000..fbaf0cc --- /dev/null +++ b/Odyssey/Odyssey/Themes/Ribbon/RibbonDropDownButton.xaml @@ -0,0 +1,253 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Ribbon/RibbonFlowGroup.xaml b/Odyssey/Odyssey/Themes/Ribbon/RibbonFlowGroup.xaml new file mode 100644 index 0000000..517ebaf --- /dev/null +++ b/Odyssey/Odyssey/Themes/Ribbon/RibbonFlowGroup.xaml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Ribbon/RibbonGallery.xaml b/Odyssey/Odyssey/Themes/Ribbon/RibbonGallery.xaml new file mode 100644 index 0000000..94ba150 --- /dev/null +++ b/Odyssey/Odyssey/Themes/Ribbon/RibbonGallery.xaml @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Ribbon/RibbonGroup.xaml b/Odyssey/Odyssey/Themes/Ribbon/RibbonGroup.xaml new file mode 100644 index 0000000..2fbd200 --- /dev/null +++ b/Odyssey/Odyssey/Themes/Ribbon/RibbonGroup.xaml @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Ribbon/RibbonGroupBrushes.xaml b/Odyssey/Odyssey/Themes/Ribbon/RibbonGroupBrushes.xaml new file mode 100644 index 0000000..4e4c424 --- /dev/null +++ b/Odyssey/Odyssey/Themes/Ribbon/RibbonGroupBrushes.xaml @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Ribbon/RibbonGroupDropDownButton.xaml b/Odyssey/Odyssey/Themes/Ribbon/RibbonGroupDropDownButton.xaml new file mode 100644 index 0000000..32115a3 --- /dev/null +++ b/Odyssey/Odyssey/Themes/Ribbon/RibbonGroupDropDownButton.xaml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Ribbon/RibbonHLChromeStyle.xaml b/Odyssey/Odyssey/Themes/Ribbon/RibbonHLChromeStyle.xaml new file mode 100644 index 0000000..22f11e8 --- /dev/null +++ b/Odyssey/Odyssey/Themes/Ribbon/RibbonHLChromeStyle.xaml @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Ribbon/RibbonImages.xaml b/Odyssey/Odyssey/Themes/Ribbon/RibbonImages.xaml new file mode 100644 index 0000000..9fc15b0 --- /dev/null +++ b/Odyssey/Odyssey/Themes/Ribbon/RibbonImages.xaml @@ -0,0 +1,207 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Ribbon/RibbonMenuItem.xaml b/Odyssey/Odyssey/Themes/Ribbon/RibbonMenuItem.xaml new file mode 100644 index 0000000..ae6d389 --- /dev/null +++ b/Odyssey/Odyssey/Themes/Ribbon/RibbonMenuItem.xaml @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Ribbon/RibbonQAToolBar.xaml b/Odyssey/Odyssey/Themes/Ribbon/RibbonQAToolBar.xaml new file mode 100644 index 0000000..6434ff9 --- /dev/null +++ b/Odyssey/Odyssey/Themes/Ribbon/RibbonQAToolBar.xaml @@ -0,0 +1,232 @@ + + + + + + + 2,4,2,4 + 3 + 1 + + + -16,2,0,0 + 0,10,10,0 + 0,1,1,1 + Visible + + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Ribbon/RibbonSeparator.xaml b/Odyssey/Odyssey/Themes/Ribbon/RibbonSeparator.xaml new file mode 100644 index 0000000..31dbc91 --- /dev/null +++ b/Odyssey/Odyssey/Themes/Ribbon/RibbonSeparator.xaml @@ -0,0 +1,42 @@ + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Ribbon/RibbonSplitButton.xaml b/Odyssey/Odyssey/Themes/Ribbon/RibbonSplitButton.xaml new file mode 100644 index 0000000..def0d02 --- /dev/null +++ b/Odyssey/Odyssey/Themes/Ribbon/RibbonSplitButton.xaml @@ -0,0 +1,274 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Ribbon/RibbonTabItem.xaml b/Odyssey/Odyssey/Themes/Ribbon/RibbonTabItem.xaml new file mode 100644 index 0000000..2a8748e --- /dev/null +++ b/Odyssey/Odyssey/Themes/Ribbon/RibbonTabItem.xaml @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Ribbon/RibbonTabScroller.xaml b/Odyssey/Odyssey/Themes/Ribbon/RibbonTabScroller.xaml new file mode 100644 index 0000000..4470828 --- /dev/null +++ b/Odyssey/Odyssey/Themes/Ribbon/RibbonTabScroller.xaml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Ribbon/RibbonTextBox.xaml b/Odyssey/Odyssey/Themes/Ribbon/RibbonTextBox.xaml new file mode 100644 index 0000000..1f56182 --- /dev/null +++ b/Odyssey/Odyssey/Themes/Ribbon/RibbonTextBox.xaml @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Ribbon/RibbonThumbnail.xaml b/Odyssey/Odyssey/Themes/Ribbon/RibbonThumbnail.xaml new file mode 100644 index 0000000..8a82fa7 --- /dev/null +++ b/Odyssey/Odyssey/Themes/Ribbon/RibbonThumbnail.xaml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Ribbon/RibbonToggleButton.xaml b/Odyssey/Odyssey/Themes/Ribbon/RibbonToggleButton.xaml new file mode 100644 index 0000000..52c861f --- /dev/null +++ b/Odyssey/Odyssey/Themes/Ribbon/RibbonToggleButton.xaml @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Ribbon/RibbonToolTip.xaml b/Odyssey/Odyssey/Themes/Ribbon/RibbonToolTip.xaml new file mode 100644 index 0000000..d546dd4 --- /dev/null +++ b/Odyssey/Odyssey/Themes/Ribbon/RibbonToolTip.xaml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/Ribbon/RibbonWindow.xaml b/Odyssey/Odyssey/Themes/Ribbon/RibbonWindow.xaml new file mode 100644 index 0000000..ef0af8e --- /dev/null +++ b/Odyssey/Odyssey/Themes/Ribbon/RibbonWindow.xaml @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/Themes/TreeViewStyle.xaml b/Odyssey/Odyssey/Themes/TreeViewStyle.xaml new file mode 100644 index 0000000..2c2050c --- /dev/null +++ b/Odyssey/Odyssey/Themes/TreeViewStyle.xaml @@ -0,0 +1,190 @@ + + + 0:0:0.15 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Odyssey/Odyssey/app.config b/Odyssey/Odyssey/app.config new file mode 100644 index 0000000..fa2534b --- /dev/null +++ b/Odyssey/Odyssey/app.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/PasswordSafe/PasswordSafe.Data/Biz/BaseObject.cs b/PasswordSafe/PasswordSafe.Data/Biz/BaseObject.cs new file mode 100644 index 0000000..5787b97 --- /dev/null +++ b/PasswordSafe/PasswordSafe.Data/Biz/BaseObject.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.ComponentModel; +using System.Diagnostics; + +namespace PasswordSafe.Data.Biz +{ + + /// + /// Represents the base type of all business objects. + /// + public class BaseObject : INotifyPropertyChanged + { + public BaseObject() + : base() + { + } + + public BaseObject(int id, string name) + : base() + { + this.name = name; + this.Id = id; + } + + #region INotifyPropertyChanged Members + + + protected virtual void OnPropertyChanged(string propertyName) + { + if (!Notifying) + { + // Notifying = true; + try + { + //Debug.WriteLine(string.Format("> Notify on {0}.{1}: {2}", GetType().Name, Name, propertyName)); + //Debug.Indent(); + IsModified = true; + if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); + //Debug.Unindent(); + } + finally + { + Notifying = false; + } + } + } + + + protected bool Notifying { get; private set; } + + public void RaisePropertyChanged(string propertyName) + { + OnPropertyChanged(propertyName); + } + + public event PropertyChangedEventHandler PropertyChanged; + + #endregion + + private bool isModified; + + /// + /// Gets whether data is modified. + /// + public bool IsModified + { + get { return isModified; } + internal set + { + if (isModified != value) + { + isModified = value; + OnModifiedChanged(); + if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("IsModified")); + isModified = value; + } + } + } + + protected virtual void OnModifiedChanged() + { + } + + /// + /// Gets the id of the data repository. + /// + public int Id { get; internal set; } + + protected short order; + + /// + /// Gets the order in the repository. + /// + public short Order + { + get { return order; } + internal set + { + if (order != value) + { + order = value; + IsModified = true; + } + } + } + + private string name; + + /// + /// Gets or sets the name. + /// + public virtual string Name + { + get + { + return name != null ? name : string.Empty; + } + set + { + if (name != null) + { + name = value; + OnPropertyChanged("Name"); + } + } + } + + /// + /// Gets the . + /// + internal protected virtual BizContext Context + { + get { return BizContext.Instance; } + } + + /// + /// Resets all data. + /// + internal protected virtual void Reset() + { + IsModified = false; + } + + } +} diff --git a/PasswordSafe/PasswordSafe.Data/Biz/BizContext.cs b/PasswordSafe/PasswordSafe.Data/Biz/BizContext.cs new file mode 100644 index 0000000..c778b65 --- /dev/null +++ b/PasswordSafe/PasswordSafe.Data/Biz/BizContext.cs @@ -0,0 +1,548 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using PasswordSafe.Data.Biz; +using System.Diagnostics; + +namespace PasswordSafe.Data.Biz +{ + /// + /// The business context to exchange business objects with the Data Access Layer. + /// + public class BizContext : IDisposable + { + private static object lockObject = new object(); + private static BizContext instance; + + /// + /// Opens the connection to use for the database. + /// + /// The connection string about the database. + /// True, if the connection is opened, otherwise false. + public static bool OpenConnection(string connectionString) + { + lock (lockObject) + { + CloseConnection(); + instance = new BizContext(connectionString); + } + return true; + } + + /// + /// Closes an existing connection. + /// + public static void CloseConnection() + { + if (Instance != null) + { + Instance.Dispose(); + instance = null; + } + } + + /// + /// Gets the instance of . + /// + public static BizContext Instance + { + get + { + if (instance == null) instance = new BizContext(""); + return instance; + } + } + + #region IDisposable Members + + public void Dispose() + { + Dal.Dispose(); + } + + #endregion + + private BizContext(string connectionString) + : base() + { + Dal = new PasswordSafe.Data.DAL.Context(connectionString); + } + + /// + /// Gets the data access layer. + /// + internal DAL.Context Dal { get; private set; } + + + private NotifyList folders; + private NotifyList categories; + + /// + /// Gets a list of all root s. + /// + public NotifyList Folders + { + get + { + if (folders == null) + { + lock (Dal) + { + if (folders == null) + { + folders = new NotifyList(PersistentFolders(Dal.GetRootFolders().ToArray())); + if (folders.Count == 0 && Dal.IsConnected) + { + // add a root folder if none exists: + folders.Add(new Folder(0, "Folders")); + } + } + } + } + return folders; + } + } + + /// + /// Gets the root . + /// + public Folder RootFolder + { + get + { + return Folders.FirstOrDefault(); + } + } + + public Category RootCategory + { + get { return Categories.FirstOrDefault(); } + } + + + /// + /// Gets a list of all root s. + /// + public NotifyList Categories + { + get + { + if (categories == null || categories.Count == 0) + { + lock (Dal) + { + if (categories == null || categories.Count == 0) + { + categories = new NotifyList(PersistentCategories(Dal.GetRootCategories())); + if (categories.Count == 0 && Dal.IsConnected) + { + // add a root if it does not exist: + //categories.Add(new Category(0, "Categories", (short)categories.Count)); + } + } + } + } + return categories; + } + } + + internal IEnumerable GetCategories(Category parent) + { + return PersistentCategories(Dal.GetCategories(parent)); + } + + internal void SavePassword(Password password) + { + using (var transaction = Dal.BeginTransaction()) + { + if (password.Id != 0) + { + Dal.UpdatePassword(password); + } + else + { + Dal.CreatePassword(password); + } + ReorderObjects(password.Fields); + UpdateFields(password); + UpdateFavorites(password); + password.IsModified = false; + transaction.Commit(); + } + } + + internal void UndoPassword(Password password) + { + if (password.Id != 0) + { + if (password.IsModified) + { + Password original = Dal.GetPassword(password.Id); + password.Name = original.Name; + password.Category = original.Category; + password.Order = original.Order; + password.Reset(); + password.IsModified = false; + } + } + else + { + // if the Id is null, the the password is new created and not commited, hence undoing means deleting: + password.Delete(); + } + } + + internal IEnumerable GetPasswords(Category category) + { + return PersistentPasswords(Dal.GetPasswordsByCategory(category)); + } + + /// + /// Using dictionaries to ensure that there is always only one instance of Password, Folder or Category with the same Id persistent + /// + private Dictionary passwordDictionary = new Dictionary(); + private Dictionary folderDictionary = new Dictionary(); + private Dictionary categoryDictionary = new Dictionary(); + + /// + /// Checks to not return duplicate Folders with same id. + /// + private IEnumerable PersistentCategories(IEnumerable categories) + { + foreach (var c in categories) + { + if (categoryDictionary.ContainsKey(c.Id)) yield return categoryDictionary[c.Id]; + else + { + categoryDictionary.Add(c.Id, c); + yield return c; + } + } + } + /// + /// Checks to not return duplicate Folders with same id. + /// + private IEnumerable PersistentFolders(IEnumerable folders) + { + foreach (var f in folders) + { + int key = f.Id; + if (folderDictionary.ContainsKey(key)) yield return folderDictionary[key]; + else + { + //folderDictionary[key] = f; + folderDictionary.Add(key, f); + yield return f; + } + } + } + + /// + /// Checks to not return duplicate passwords with same id. + /// + private IEnumerable PersistentPasswords(IEnumerable passwords) + { + foreach (var p in passwords) + { + if (passwordDictionary.ContainsKey(p.Id)) yield return passwordDictionary[p.Id]; + else + { + passwordDictionary.Add(p.Id, p); + yield return p; + } + } + } + + internal IEnumerable GetFolders(int passwordId) + { + return PersistentFolders(Dal.GetFolderByPasswordId(passwordId)).ToArray(); + } + + internal IEnumerable GetFoldersByParentFolder(Folder parent) + { + return PersistentFolders(Dal.GetFoldersByParentFolder(parent)); + } + + internal IEnumerable GetPasswordsByFolderId(int folderId) + { + return PersistentPasswords(Dal.GetPasswordsByFolderId(folderId)); + } + + internal void DeletePassword(Password password) + { + if (password.Id != 0) + { + passwordDictionary.Remove(password.Id); + Dal.DeletePassword(password.Id); + } + } + + internal IEnumerable GetFields(Password password) + { + return Dal.GetFields(password); + } + + private void ReorderObjects(IEnumerable objects) where T : BaseObject + { + short order = 0; + foreach (BaseObject o in objects) o.Order = order++; + } + + private void ResetModified(IEnumerable objects) where T : BaseObject + { + foreach (BaseObject o in objects) o.IsModified = false; + } + + private void UpdateFavorites(Password password) + { + int pwId = password.Id; + + IEnumerable existingFaves = Dal.GetExistingFaves(password.Id); + IEnumerable deletedFaves = existingFaves.Except(password.Folders.Select(f => f.Id)); + IEnumerable newFaves = password.Folders.Select(f => f.Id).Except(existingFaves); + + foreach (int id in deletedFaves) + { + Dal.DeleteFavorite(password.Id, id); + } + + foreach (int id in newFaves) + { + Dal.CreateFavorite(password.Id, id); + } + + } + + private void UpdateFields(Password password) + { + foreach (FieldType type in new FieldType[] { FieldType.Text, FieldType.Date, FieldType.Int, FieldType.Memo }) + { + UpdateFields(password, type); + } + } + + private void UpdateFields(Password password, FieldType type) + { + + int[] fieldIds = Dal.GetFieldIds(password.Id, type).ToArray(); + + // determine the field ids to delete: + IEnumerable deletedIds = fieldIds.Except(password.Fields.Where(f => f.Id != 0).Select(f => f.Id)); + foreach (var id in deletedIds) + { + Dal.DeleteField(id, type); + } + + // modify existing fields: + var existingFields = password.Fields.Where(f => f.Id > 0); + foreach (var field in existingFields) + { + if (field.IsModified) + { + Dal.UpdateField(field); + } + } + + // create the new fields: + var newFields = password.Fields.Where(f => f.Id == 0); + foreach (var field in newFields) + { + field.Id = Dal.CreateField(field); + } + + ResetModified(password.Fields); + } + + + internal Category GetCategory(int categoryId) + { + if (categoryDictionary.ContainsKey(categoryId)) return categoryDictionary[categoryId]; + + Category c = Dal.GetCategoryById(categoryId); + categoryDictionary.Add(c.Id, c); + return c; + } + + internal Folder GetFolderById(int folderId) + { + if (folderDictionary.ContainsKey(folderId)) return folderDictionary[folderId]; + Folder folder = Dal.GetFolderById(folderId); + folderDictionary.Add(folder.Id, folder); + return folder; + } + + public Folder CreateFolder(Folder folder) + { + Dal.CreateFolder(folder); + folderDictionary.Add(folder.Id, folder); + return folder; + } + + internal void DeleteFolder(Folder folder) + { + if (folder.Folders.Count > 0) + { + throw new ArgumentException("Cannot delete Folder that has Children."); + } + + + if (folder.Id != 0) + { + Dal.DeleteFolder(folder.Id); + if (folderDictionary.ContainsKey(folder.Id)) + { + folderDictionary.Remove(folder.Id); + } + } + } + + internal Category CreateCategory(Category parent) + { + Category category = Dal.CreateCategory(parent); + categoryDictionary.Add(category.Id, category); + return category; + } + + internal void DeleteCategory(Category category) + { + if (category.Passwords.Count > 0) + { + throw new ArgumentException("Cannot delete category that has passwords associated."); + } + if (category.Categories.Count > 0) + { + throw new ArgumentException("Cannot delete category that has Children."); + } + + if (category.Id != 0) + { + Dal.DeleteCategory(category.Id); + if (categoryDictionary.ContainsKey(category.Id)) + { + categoryDictionary.Remove(category.Id); + } + } + } + + internal IEnumerable GetTemplates(Category category) + { + return Dal.GetTemplates(category); + } + + public void Commit() + { + // throw new NotImplementedException(); + } + + + internal void SaveCategory(Category category) + { + using (var transaction = Dal.BeginTransaction()) + { + if (category.Id == 0) Dal.CreateCategory(category); + Dal.UpdateCategory(category); + category.IsModified = false; + transaction.Commit(); + } + } + + internal void SaveTemplate(Category category) + { + using (var transaction = Dal.BeginTransaction()) + { + ReorderObjects(category.Fields); + UpdateTemplateFields(category.Id, category.Fields); + category.IsModified = false; + transaction.Commit(); + } + } + + private void UpdateTemplateFields(int categoryId, IEnumerable fields) + { + IEnumerable ids = Dal.GetTemplateFields(categoryId); + + IEnumerable deleted = ids.Except(fields.Select(f => f.Id)); + foreach (int deletedId in deleted) + { + Dal.DeleteTemplateField(deletedId); + } + + foreach (TemplateField field in fields) + { + if (field.IsModified) + { + if (field.Id == 0) Dal.CreateTemplateField(field); + Dal.UpdateTemplateField(field); + field.IsModified = false; + } + } + } + + internal void SaveFolder(Folder folder) + { + using (var transaction = Dal.BeginTransaction()) + { + if (folder.Id == 0) Dal.CreateFolder(folder); + Dal.UpdateFolder(folder); + folder.IsModified = false; + transaction.Commit(); + } + } + + public bool ChangePassword(string connectionString, string oldPassword, string newPassword) + { + return Dal.ChangePassword(connectionString, oldPassword, newPassword); + } + + /// + /// Saves all changes that where made to any password or template. + /// + public void Save() + { + Category root = RootCategory; + if (root != null) + { + SaveCategoryNested(root); + foreach (var password in root.NestedPasswords) + { + if (password.IsModified) password.Save(); + } + } + } + + /// + /// Undos all changes that where made to any password or template. + /// + public void Undo() + { + Category root = RootCategory; + if (root != null) + { + UndoCategoryNested(root); + foreach (var password in root.NestedPasswords) + { + if (password.IsModified) password.Undo(); + } + } + } + + private void UndoCategoryNested(Category c) + { + if (c.IsModified) c.Save(); + foreach (var sub in c.Categories) + { + sub.UndoTemplate(); + } + } + + private void SaveCategoryNested(Category c) + { + if (c.IsModified) c.Save(); + foreach (var sub in c.Categories) + { + sub.Save(); + } + } + } +} diff --git a/PasswordSafe/PasswordSafe.Data/Biz/Category.cs b/PasswordSafe/PasswordSafe.Data/Biz/Category.cs new file mode 100644 index 0000000..e45cbc8 --- /dev/null +++ b/PasswordSafe/PasswordSafe.Data/Biz/Category.cs @@ -0,0 +1,379 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.ComponentModel; + +namespace PasswordSafe.Data.Biz +{ + public class Category : NodeBase + { + public Category(int id, string name, short order, Category parent) + : base(id, name) + { + this.parent = parent; + this.order = order; + } + public override IEnumerable Children + { + get + { + return Categories.Cast(); + } + set + { + base.Children = value; + } + } + + internal Category(int id, string name, short order, int? parentId) + : base(id, name) + { + this.parentId = parentId; + this.Order = order; + } + + public Category(int id, string name, short order) + : base(id, name) + { + this.Order = order; + } + protected override void OnModifiedChanged() + { + base.OnModifiedChanged(); + } + + protected override void OnPropertyChanged(string propertyName) + { + base.OnPropertyChanged(propertyName); + switch (propertyName) + { + case "Order": + if (Parent != null) Parent.ReorderChildren(); + break; + + case "Name": + Save(); + break; + + case "Passwords": + OnPasswordPropertyChanged(); + break; + + case "NestedPasswords": + OnPropertyChanged("NestedPasswordCount"); + break; + } + } + + private void ReorderChildren() + { + short order = 0; + foreach (Category c in Categories) + { + short oldOrder = c.Order; + c.Order = order; + if (oldOrder != order) c.Save(); + order++; + } + } + + /// + /// Saves the to the data repositoy. + /// + public void Save() + { + Context.SaveCategory(this); + IsModified = false; + } + + private void OnPasswordPropertyChanged() + { + Category category = this; + while (category != null) + { + category.OnPropertyChanged("NestedPasswords"); + category = category.Parent; + } + } + + private Category parent; + private int? parentId; + + /// + /// Gets the parent , otherwise null. + /// + public Category Parent + { + get + { + if (parentId.HasValue && parent == null) + { + parent = Context.GetCategory(parentId.Value); + } + return parent; + } + } + + private NotifyList passwords; + + /// + /// Gets a list of all s associated with to category. + /// + public NotifyList Passwords + { + get + { + if (passwords == null) + { + passwords = Id == 0 ? new NotifyList() : new NotifyList(Context.GetPasswords(this)); + passwords.Changed += new EventHandler>(OnPasswordsChanged); + } + return passwords; + } + } + + private NotifyList fields; + + /// + /// Gets a list of all s associated to this category. + /// + public NotifyList Fields + { + get + { + if (fields == null) + { + fields = Id == 0 ? new NotifyList() : new NotifyList(Context.GetTemplates(this)); + fields.Changed += new EventHandler>(OnFieldsChanged); + } + return fields; + } + } + + void OnFieldsChanged(object sender, NotifyEventArgs e) + { + OnPropertyChanged("Fields"); + } + + /// + /// Gets an enumeration of all nested s that are associated + /// either to this Category or any descendent . + /// + public IEnumerable NestedPasswords + { + get + { + foreach (var p in Passwords) yield return p; + foreach (var c in Categories) + { + foreach (var p in c.NestedPasswords) + { + yield return p; + } + } + } + } + + + public NotifyList categories; + + /// + /// Gets a list ofild ch s. + /// + public NotifyList Categories + { + get + { + if (categories == null) + { + categories = Id == 0 ? new NotifyList() : new NotifyList(Context.GetCategories(this)); + categories.Changed += new EventHandler>(OnCategoriesChanged); + + } + return categories; + } + } + + /// + /// Occurs when the list of categories has changed. + /// + protected void OnCategoriesChanged(object sender, NotifyEventArgs e) + { + ReorderChildren(); + OnPropertyChanged("Categories"); + } + + /// + /// Occurs when the list of passwords has changed. + /// + protected void OnPasswordsChanged(object sender, NotifyEventArgs e) + { + OnPropertyChanged("Passwords"); + + Category parent = Parent; + while (parent != null) + { + parent.OnPropertyChanged("NestedPasswords"); + parent = parent.Parent; + } + } + + /// + /// Gets the number of nested s. + /// + public int NestedPasswordCount + { + get { return this.NestedPasswords.Count(); } + } + + /// + /// Resets all data. + /// + protected internal override void Reset() + { + categories = null; + passwords = null; + fields = null; + OnPropertyChanged("Categories"); + OnPropertyChanged("Passwords"); + OnPropertyChanged("Fields"); + base.Reset(); + } + + /// + /// Gets the parent of this + /// + protected internal override NodeBase ParentNode + { + get + { + return Parent != null ? Parent : base.ParentNode; + } + set + { + base.ParentNode = value; + } + } + + + /// + /// Creates a new password for this . + /// + /// The new + public Password CreatePassword() + { + Password newPassword = new Password(this, 0, "New Password"); + + short order = 0; + foreach (TemplateField tempField in this.Fields) + { + Field field = new Field(newPassword, 0, tempField.Name, tempField.Type, null, order++); + newPassword.Fields.Add(field); + } + + this.Passwords.Add(newPassword); + return newPassword; + } + + + internal void ResetPasswords() + { + OnPropertyChanged("Passwords"); + } + + /// + /// Moves the on level up in order. + /// + public void Up() + { + if (Parent != null) Parent.Categories.MoveUp(this); + } + + /// + /// Moves the on level down in order. + /// + public void Down() + { + if (Parent != null) Parent.Categories.MoveDown(this); + } + + /// + /// Moves the to the top of order. + /// + public void Top() + { + if (Parent != null) Parent.Categories.MoveToTop(this); + } + + + /// + /// Moves the to the bottom of order. + /// + public void Bottom() + { + if (Parent != null) Parent.Categories.MoveToBottom(this); + } + + public void Delete() + { + if (Categories.Any()) throw new ArgumentOutOfRangeException("Category has descendants."); + if (Passwords.Any()) throw new ArgumentOutOfRangeException("Category has associated Passwords."); + if (Parent != null) + { + Parent.Categories.Remove(this); + Context.DeleteCategory(this); + } + } + + /// + /// Adds a new child Category. + /// + /// The name of the new child Category. + /// The new Category. + public Category AddCategory(string name) + { + Category category = new Category(0, name, (short)Categories.Count, this); + Categories.Add(category); + category.Save(); + foreach (TemplateField field in Fields) + { + TemplateField copy = new TemplateField(0, field.Name, field.Type, category, field.MinRange, field.MaxRange, (short)Fields.Count); + copy.IsModified = true; + category.Fields.Add(copy); + } + category.SaveTemplate(); + return category; + } + + public void SaveTemplate() + { + Context.SaveTemplate(this); + } + + /// + /// Undoing a template is very easy: + /// Since there are only the TemplateFields affected, the Fields list must only be recreated to gather them from Database again. + /// + public void UndoTemplate() + { + fields = null; + RaisePropertyChanged("Fields"); + IsModified = false; + } + + /// + /// Adds a to the template. + /// + /// The type of the new field. + /// The name of the new field. + /// The new . + public TemplateField AddField(FieldType type, string name) + { + TemplateField field = new TemplateField(0, name, type, this, "", "", (short)Fields.Count); + field.IsModified = true; + Fields.Add(field); + return field; + } + } +} diff --git a/PasswordSafe/PasswordSafe.Data/Biz/CustomNode.cs b/PasswordSafe/PasswordSafe.Data/Biz/CustomNode.cs new file mode 100644 index 0000000..b17ed40 --- /dev/null +++ b/PasswordSafe/PasswordSafe.Data/Biz/CustomNode.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace PasswordSafe.Data.Biz +{ + public class CustomNode:NodeBase + { + public CustomNode(string name, IEnumerable children) + : base(0, name) + { + this.Children = children; + } + + } +} diff --git a/PasswordSafe/PasswordSafe.Data/Biz/Field.cs b/PasswordSafe/PasswordSafe.Data/Biz/Field.cs new file mode 100644 index 0000000..9c3b2a8 --- /dev/null +++ b/PasswordSafe/PasswordSafe.Data/Biz/Field.cs @@ -0,0 +1,431 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Security; +using System.Runtime.InteropServices; + +namespace PasswordSafe.Data.Biz +{ + /// + /// is disposable since it contains confidential data that should be disposed from memory asap. + /// + public class Field : BaseObject, IDisposable + { + public Field(Password password, int id, string name, FieldType type, object value, short order) + : base(id, name) + { + this.Password = password; + this.Type = type; + if (IsString) ChangeSecureString(value, false); else this.value = value; + this.Order = order; + } + + #region IDisposable Members + + public void Dispose() + { + secureStringValue.Dispose(); + } + + #endregion + + protected override void OnPropertyChanged(string propertyName) + { + base.OnPropertyChanged(propertyName); + switch (propertyName) + { + case "Value": + base.OnPropertyChanged("StringValue"); + break; + + case "StringValue": + base.OnPropertyChanged("Value"); + break; + } + if (propertyName != "IsSelected") + { + Password.IsModified = true; + Password.RaisePropertyChanged("Fields"); + } + } + + protected internal override BizContext Context + { + get + { + return Password.Context; + } + } + + public Password Password { get; private set; } + + private bool isSelected; + + public bool IsSelected + { + get { return isSelected; } + set + { + if (isSelected != value) + { + isSelected = value; + OnPropertyChanged("IsSelected"); + } + } + } + + public bool HasLines { get { return Type == FieldType.Memo; } } + public bool HasRange { get { return Type != FieldType.Separator && Type != FieldType.Bool && Type != FieldType.Memo; } } + + private int? minLines; + private int? maxLines; + private object minRange; + private object maxRange; + + /// + /// Gets or sets the minimum number of lines, otherwise null. + /// + public int? MinLines + { + get { return minLines; } + set + { + if (minLines != value) + { + minLines = value; + OnPropertyChanged("MinLines"); + } + } + } + + /// + /// Gets or sets the maximum number of lines, otherwise null. + /// + public int? MaxLines + { + get { return maxLines; } + set + { + if (maxLines != value) + { + maxLines = value; + OnPropertyChanged("MaxLines"); + } + } + } + + /// + /// Gets or sets the minimum range converted as string. + /// + public string MinRangeString + { + get { return ValueToString(minRange, RangeType); } + set + { + minRange = StringToValue(value, RangeType); + OnPropertyChanged("MinRangeString"); + } + } + + /// + /// Gets or sets the maximum range converted as string. + /// + public string MaxRangeString + { + get { return ValueToString(maxRange, RangeType); } + set + { + maxRange = StringToValue(value, RangeType); + OnPropertyChanged("MaxRangeString"); + } + } + + /// + /// Gets or sets the minimum range. The value type is depending on . + /// + public object MinRange + { + get { return minRange; } + set + { + if (minRange != value) + { + minRange = value; + OnPropertyChanged("MinRange"); + } + } + } + + /// + /// Gets or sets the maximum range. The value type is depending on . + /// + public object MaxRange + { + get { return maxRange; } + set + { + if (maxRange != value) + { + maxRange = value; + OnPropertyChanged("MaxRange"); + } + } + } + + /// + /// Gets the type to be used for MimimumRange and MaximumRange. + /// + protected FieldType RangeType + { + get + { + switch (Type) + { + case FieldType.Memo: + case FieldType.Text: + case FieldType.Password: + return FieldType.Int; + + default: return Type; + } + } + } + + /// + /// Converts a string to a value type. + /// + /// The string representation of the value to convert. + /// The type to convert to. + /// The converted value. + protected object StringToValue(string str, FieldType type) + { + switch (type) + { + case FieldType.Date: + case FieldType.Time: + if (str == null) return null; + DateTime dval; + if (DateTime.TryParse(str, out dval)) return dval; + return null; + + case FieldType.Int: + if (str == null) return null; + int iVal; + if (int.TryParse(str, out iVal)) return iVal; + return null; + + default: + return str; + } + } + + /// + /// Converts a value to string. + /// + /// The value to convert. + /// The value type. + /// The converted value as string representation. + protected string ValueToString(object value, FieldType type) + { + if (value == null) return null; + switch (type) + { + case FieldType.Date: + return ((DateTime)value).ToShortDateString(); + + case FieldType.Time: + return ((DateTime)value).ToLongTimeString(); + + case FieldType.Int: + return ((int)value).ToString(); + + case FieldType.Text: + case FieldType.Password: + case FieldType.Memo: + return value.ToString(); + + default: + return String.Empty; + } + } + + internal void SetRange(object min, object max) + { + minRange = min; + maxRange = max; + } + + internal void SetLines(int? min, int? max) + { + minLines = min; + maxLines = max; + } + + private object value; + + private SecureString secureStringValue = new SecureString(); + + /// + /// Gets or sets the Value of the Field. The type of the Value depends on . + /// + public object Value + { + get + { + return IsString ? Marshal.PtrToStringBSTR(Marshal.SecureStringToBSTR(secureStringValue)) : value; + } + set + { + if (IsString) + { + ChangeSecureString(value, true); + } + else + { + ChangeValue(value); + } + } + } + + /// + /// Gets or sets the Value as string representation. + /// This Property is usefull for instance for transfering a value from and to a TextBox. + /// + public string StringValue + { + get { return ValueToString(Value, Type); } + set + { + object v = StringToValue(value, Type); + if (v != this.value) + { + this.Value = v; + OnPropertyChanged("StringValue"); + } + } + } + + public bool BoolValue + { + get { return (value != null && value.Equals(1)); } + set + { + Value = value ? 1 : 0; + } + } + + private void ChangeValue(object value) + { + if (this.value != value) + { + this.value = value; + OnPropertyChanged("Value"); + } + } + + private void ChangeSecureString(object value, bool signal) + { + if (Value != value) + { + string s = value as string; + secureStringValue.Clear(); + if (!string.IsNullOrEmpty(s)) + { + foreach (char c in s.ToCharArray()) secureStringValue.AppendChar(c); + } + } + if (signal) OnPropertyChanged("Value"); + } + + private static FieldType[] stringTypes = new FieldType[] { FieldType.Text, FieldType.Password, FieldType.Memo }; + + public bool IsString + { + get + { + return stringTypes.Contains(Type); + } + } + + public FieldType Type { get; private set; } + + /// + /// Deletes the Field and all dependencies. + /// + public void Delete() + { + Password.Fields.Remove(this); + } + + /// + /// Moves the Field on level up in the Password. + /// + public void Up() + { + Password.Fields.MoveUp(this); + } + + /// + /// Moves the Field on level down in the Password. + /// + public void Down() + { + Password.Fields.MoveDown(this); + } + + /// + /// Moves the Field to the top of the Password. + /// + public void Top() + { + Password.Fields.MoveToTop(this); + } + + /// + /// Moves the Field to the bottom of the Password. + /// + public void Bottom() + { + Password.Fields.MoveToBottom(this); + } + + /// + /// Converts the value to string. + /// + /// + public string ValueAsString() + { + string value = ValueToString(this.value, Type); + return value != null ? value : String.Empty; + } + + /// + /// Validates a value by Range. + /// + /// The value to validate. + /// True, if the value is valid and not outside range, otherwise false. + public bool Validate(string value) + { + object native = StringToValue(value, Type); + if (native == null) return true; + if (minRange != null && Compare(minRange, native) < 0) return false; + if (maxRange != null && Compare(maxRange, native) > 0) return false; + return true; + } + + private int Compare(object a, object b) + { + if ((a is DateTime) && (b is DateTime)) return ((DateTime)a).CompareTo((DateTime)b); + if ((a is int) && (b is int)) return ((int)a).CompareTo((int)b); + + if (a is int) + { + int n = (b.ToString()).Length; + return ((int)a).CompareTo(n); + } + return 0; + } + + } +} diff --git a/PasswordSafe/PasswordSafe.Data/Biz/FieldType.cs b/PasswordSafe/PasswordSafe.Data/Biz/FieldType.cs new file mode 100644 index 0000000..da9b3cd --- /dev/null +++ b/PasswordSafe/PasswordSafe.Data/Biz/FieldType.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace PasswordSafe.Data.Biz +{ + /// + /// Enumeration of possible field types.S + /// + public enum FieldType + { + Unknown = 0, + Text = 1, + Password = 2, + Date = 3, + Time = 4, + Int = 5, + Memo = 6, + Separator = 7, + Bool = 8 + } +} diff --git a/PasswordSafe/PasswordSafe.Data/Biz/Folder.cs b/PasswordSafe/PasswordSafe.Data/Biz/Folder.cs new file mode 100644 index 0000000..5be4d9b --- /dev/null +++ b/PasswordSafe/PasswordSafe.Data/Biz/Folder.cs @@ -0,0 +1,287 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.ComponentModel; + +namespace PasswordSafe.Data.Biz +{ + public class Folder : NodeBase + { + public Folder(int id, string name) + : base(id, name) + { + } + + public Folder(int id, string name, Folder parent) + : base(id, name) + { + this.parent = parent; + } + + public Folder(int id, string name, int? parentId) + : base(id, name) + { + this.parentId = parentId; + } + + public int title; + + protected override void OnPropertyChanged(string propertyName) + { + base.OnPropertyChanged(propertyName); + switch (propertyName) + { + case "Name": + Save(); + break; + + case "Passwords": + OnPasswordPropertyChanged(); + break; + + case "NestedPasswords": + OnPropertyChanged("NestedPasswordCount"); + break; + } + } + + + private int? parentId; + private Folder parent; + + /// + /// Gets the parent , otherwise null. + /// + public Folder Parent + { + get + { + if (parent == null && parentId.HasValue) + { + parent = Context.GetFolderById(parentId.Value); + } + return parent; + } + } + + private NotifyList folders; + private NotifyList passwords; + + /// + /// Resets the list of s. + /// + public void ResetPasswords() + { + passwords = null; + OnPropertyChanged("Passwords"); + } + + /// + /// Gets the list of all s associated to this Folder. + /// + public NotifyList Passwords + { + get + { + EnsurePasswords(); + return passwords; + } + } + + private void EnsurePasswords() + { + if (passwords == null) + { + passwords = Id != 0 ? new NotifyList(Context.GetPasswordsByFolderId(Id)) : new NotifyList(); + passwords.Changed += new EventHandler>(OnPasswordsChanged); + } + } + + /// + /// Gets the list of all s that are either associated to this . + /// or any descendend . + /// + private IEnumerable AllNestedPasswords() + { + foreach (var p in Passwords) yield return p; + foreach (var folder in Folders) + { + foreach (var p in folder.NestedPasswords) + { + yield return p; + } + } + } + + public IEnumerable NestedPasswords + { + get + { + var array = AllNestedPasswords().ToArray(); + return array.Distinct().OrderBy(p => p.Order); + } + } + + public int NestedPasswordCount + { + get { return this.NestedPasswords.Count(); } + } + + + void OnPasswordsChanged(object sender, NotifyEventArgs e) + { + OnPropertyChanged("Passwords"); + } + + void OnPasswordPropertyChanged() + { + Folder folder = this; + while (folder != null) + { + folder.EnsurePasswords(); + folder.OnPropertyChanged("NestedPasswords"); + folder = folder.Parent; + } + } + + /// + /// Gets the list of child s. + /// + public NotifyList Folders + { + get + { + if (folders == null) + { + folders = Id == 0 ? new NotifyList() : new NotifyList(Context.GetFoldersByParentFolder(this)); + folders.Changed += new EventHandler>(OnFoldersChanged); + } + return folders; + } + } + + void OnFoldersChanged(object sender, NotifyEventArgs e) + { + ReorderChildren(); + + Folder folder = e.Item; + if (folder != null) folder.NotifyPasswordChanged(); + OnPropertyChanged("Folders"); + } + + private void ReorderChildren() + { + short order = 0; + foreach (Folder folder in Folders) + { + short oldOrder = folder.Order; + folder.Order = order; + if (oldOrder != order) folder.Save(); + order++; + } + } + + /// + /// Saves the to the data repository. + /// + public void Save() + { + Context.SaveFolder(this); + IsModified = false; + } + + /// + /// Notify the folder that the list of s has changed. + /// + public void NotifyPasswordChanged() + { + OnPropertyChanged("Passwords"); + } + + /// + /// Resets all data. + /// + protected internal override void Reset() + { + folders = null; + passwords = null; + OnPropertyChanged("Folders"); + OnPropertyChanged("Passwords"); + base.Reset(); + } + + + /// + /// Gets the parent of this + /// + protected internal override NodeBase ParentNode + { + get + { + return Parent != null ? Parent : base.ParentNode; + } + set + { + base.ParentNode = value; + } + } + + /// + /// Moves this on level up in order. + /// + public void Up() + { + if (Parent != null) Parent.Folders.MoveUp(this); + } + + /// + /// Moves this on level down in order. + /// + public void Down() + { + if (Parent != null) Parent.Folders.MoveDown(this); + } + + /// + /// Moves this to the top of order. + /// + public void Top() + { + if (Parent != null) Parent.Folders.MoveToTop(this); + } + + /// + /// Moves this to the bottom of order. + /// + public void Bottom() + { + if (Parent != null) Parent.Folders.MoveToBottom(this); + } + + public Folder AddFolder(string name) + { + Folder folder = new Folder(0, name, this); + Folders.Add(folder); + folder.Save(); + return folder; + } + + public void Delete() + { + if (Folders.Any()) throw new ArgumentOutOfRangeException("Folder has descendants."); + if (Parent != null) + { + foreach (Password pw in Passwords) + { + foreach (PasswordFolder pf in pw.PasswordFolders.Where(x => x.Folder == this)) + { + pf.IsFavorite = false; + } + } + Parent.Folders.Remove(this); + Context.DeleteFolder(this); + } + } + } +} diff --git a/PasswordSafe/PasswordSafe.Data/Biz/NodeBase.cs b/PasswordSafe/PasswordSafe.Data/Biz/NodeBase.cs new file mode 100644 index 0000000..2913c7f --- /dev/null +++ b/PasswordSafe/PasswordSafe.Data/Biz/NodeBase.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace PasswordSafe.Data.Biz +{ + /// + /// Represents the base class for hierarchical objects. + /// + public class NodeBase:BaseObject + { + public NodeBase(int id, string name) + : base(id, name) + { + } + + /// + /// Gets the hierarchical Path of all descendant Name properties separated with a backslash. + /// + public string Path + { + get + { + StringBuilder sb = new StringBuilder(); + NodeBase parent = this; + + while (parent != null) + { + sb.Insert(0, parent.Name); + parent = parent.ParentNode; + if (parent != null) sb.Insert(0, '\\'); + } + return sb.ToString(); + } + } + + + /// + /// Gets the parent otherwise null. + /// + internal protected virtual NodeBase ParentNode { get; set; } + + public string GetPath(int depth) + { + StringBuilder sb = new StringBuilder(); + NodeBase parent = this; + + while (parent != null && parent.ParentNode != null) + { + sb.Insert(0, parent.Name); + parent = parent.ParentNode; + if (parent != null && parent.ParentNode != null) sb.Insert(0, '\\'); + } + string s = sb.ToString(); + if (depth > 1) + { + while (--depth > 0) + { + int idx = s.IndexOf('\\'); + if (idx > 0) s = s.Substring(idx + 1); + } + } + return s; + } + + private IEnumerable children; + + /// + /// Gets all child of this NodeBase. + /// + public virtual IEnumerable Children + { + get { return children; } + set + { + children = value; + foreach (var child in children) child.ParentNode = this; + } + } + + } +} \ No newline at end of file diff --git a/PasswordSafe/PasswordSafe.Data/Biz/NotifyEventArgs.cs b/PasswordSafe/PasswordSafe.Data/Biz/NotifyEventArgs.cs new file mode 100644 index 0000000..383d4c8 --- /dev/null +++ b/PasswordSafe/PasswordSafe.Data/Biz/NotifyEventArgs.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace PasswordSafe.Data.Biz +{ + /// + /// EventArgs for . + /// + /// + public class NotifyEventArgs : EventArgs + { + public NotifyEventArgs(ChangedEventType type, T item) + : base() + { + Type = type; + Item = item; + } + + /// + /// Gets the change type. + /// + public ChangedEventType Type { get; private set; } + + /// + /// Gets the item that has changed, otherwise null + /// + public T Item { get; private set; } + } + + /// + /// Enumeration of possible changes. + /// + public enum ChangedEventType + { + Added, + Removed, + Modified, + Reset + } +} diff --git a/PasswordSafe/PasswordSafe.Data/Biz/NotifyList.cs b/PasswordSafe/PasswordSafe.Data/Biz/NotifyList.cs new file mode 100644 index 0000000..847ed22 --- /dev/null +++ b/PasswordSafe/PasswordSafe.Data/Biz/NotifyList.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.ComponentModel; +using System.Diagnostics; + +namespace PasswordSafe.Data.Biz +{ + + /// + /// A List that supports notifications when the list or any item has changed. + /// + /// The type of item in this list. + public class NotifyList : BindingList where T : BaseObject + { + public NotifyList() + : base() + { + } + + public NotifyList(IEnumerable array) + : base() + { + this.RaiseListChangedEvents = false; + foreach (var item in array.ToArray()) + { + this.Add(item); + } + this.RaiseListChangedEvents = true; + } + + + public void MoveUp(T item) + { + int oldIndex = IndexOf(item); + int newIndex = Math.Max(0, oldIndex - 1); + ChangePosition(item, oldIndex, newIndex); + } + + private void ChangePosition(T item, int oldIndex, int newIndex) + { + if (oldIndex != newIndex) + { + RaiseListChangedEvents = false; + RemoveAt(oldIndex); + Insert(newIndex, item); + RaiseListChangedEvents = true; + // OnChanged(ChangedEventType.Reset, null); + ResetBindings(); + OnChanged(ChangedEventType.Reset, null); + } + } + + public void MoveDown(T item) + { + int oldIndex = IndexOf(item); + int newIndex = Math.Min(Count - 1, oldIndex + 1); + ChangePosition(item, oldIndex, newIndex); + } + + public void MoveToTop(T item) + { + int oldIndex = IndexOf(item); + ChangePosition(item, oldIndex, 0); + } + + public void MoveToBottom(T item) + { + int oldIndex = IndexOf(item); + ChangePosition(item, oldIndex, Count - 1); + } + + protected void OnChanged(ChangedEventType type, T item) + { + // if (RaiseListChangedEvents) + { + if (Changed != null) Changed(this, new NotifyEventArgs(type, item)); + } + } + + /// + /// Occurs when the list has changed. + /// + public event EventHandler> Changed; + + protected override void RemoveItem(int index) + { + T item = this[index]; + base.RemoveItem(index); + if (RaiseListChangedEvents) OnChanged(ChangedEventType.Removed, item); + } + + + protected override void InsertItem(int index, T item) + { + base.InsertItem(index, item); + if (RaiseListChangedEvents) OnChanged(ChangedEventType.Added, this[index]); + } + + } +} diff --git a/PasswordSafe/PasswordSafe.Data/Biz/Password.cs b/PasswordSafe/PasswordSafe.Data/Biz/Password.cs new file mode 100644 index 0000000..31df954 --- /dev/null +++ b/PasswordSafe/PasswordSafe.Data/Biz/Password.cs @@ -0,0 +1,304 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.ComponentModel; +using System.Diagnostics; + +namespace PasswordSafe.Data.Biz +{ + public class Password : BaseObject, IDisposable + { + /// + /// Creates a new instance. + /// + public Password(Category category) + : base() + { + this.category = category; + } + + /// + /// Creates a new instance. + /// + public Password(Category category, int id, string name) + : base(id, name) + { + this.category = category; + } + + /// + /// Creates a new instance. + /// + public Password(int categoryId, int id, string name) + : base(id, name) + { + this.category = Context.GetCategory(categoryId); + } + + #region IDisposable Members + + public void Dispose() + { + ClearFields(); + } + + #endregion + + protected override void OnModifiedChanged() + { + base.OnModifiedChanged(); + } + + + private Category category; + + /// + /// Gets or sets the category of this password. + /// + public Category Category + { + get { return category; } + set + { + if (category != value) + { + Category oldCategory = category; + oldCategory.Passwords.RaiseListChangedEvents = false; + oldCategory.Passwords.Remove(this); + category = value; + category.Passwords.RaiseListChangedEvents = false; + category.Passwords.Add(this); + oldCategory.Passwords.RaiseListChangedEvents = true; + category.Passwords.RaiseListChangedEvents = true; + category.ResetPasswords(); + oldCategory.ResetPasswords(); + OnPropertyChanged("Category"); + } + } + } + + public string CategoryPath + { + get + { + if (Category == null) return string.Empty; + string path = Category.Path; + int index = path.IndexOf('\\'); + if (index > 0) path = path.Substring(index + 1); + return path; + } + + set + { + if (CategoryPath != value) + { + SetCategoryByPath(value); + } + } + } + + private void SetCategoryByPath(string value) + { + if (!string.IsNullOrEmpty(value)) + { + // first get the root: + Category category = Category; + while (category.Parent is Category) category = category.Parent; + + string[] names = value.Split('\\'); + + foreach (string name in names) + { + category = GetCategoryByName(category, name); + if (category == null) break; + } + if (category != null) + { + Category = category; + } + } + } + + private Category GetCategoryByName(Category category, string name) + { + return category.Categories.Where(c => c.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault(); + } + + private NotifyList folders; + private NotifyList fields; + + public NotifyList Folders + { + get + { + if (folders == null) + { + lock (this) + { + if (folders == null) + { + folders = Id != 0 ? new NotifyList(Context.GetFolders(Id)) : new NotifyList(); + folders.Changed += new EventHandler>(OnFoldersChanged); + } + } + } + return folders; + } + } + + /// + /// Gets whether this password is added to the favorites. + /// + public bool IsFavorite + { + get + { + return Folders.Any(); + } + } + + void OnFoldersChanged(object sender, NotifyEventArgs e) + { + OnPropertyChanged("Folders"); + switch (e.Type) + { + case ChangedEventType.Added: + e.Item.Passwords.Add(this); + break; + + case ChangedEventType.Removed: + e.Item.Passwords.Remove(this); + break; + } + } + + /// + /// Gets a list with all fields of this password. + /// + public NotifyList Fields + { + get + { + if (fields == null) + { + fields = Id == 0 ? new NotifyList() : new NotifyList(Context.GetFields(this)); + + fields.Changed += new EventHandler>(OnFieldsChanged); + } + return fields; + } + } + + private void OnFieldsChanged(object sender, NotifyEventArgs e) + { + OnPropertyChanged("Fields"); + } + + + /// + /// Gets an enumeration of associated to this password. + /// + public IEnumerable PasswordFolders + { + get + { + // this list is temporarily for good reasons: + // since the folders might change this list is recreated every time to reflect the actual hierarchy of folders. + return GetPasswordFolders(); + } + } + + private NotifyList GetPasswordFolders() + { + return new NotifyList(Context.Folders.Select(f => new PasswordFolder(f, this))); + //foreach (var folder in Context.Folders) + //{ + // yield return new PasswordFolder(folder, this); + //} + } + + /// + /// Deletes the password from datastore. + /// + public void Delete() + { + Category.Passwords.Remove(this); + + foreach (var folder in Folders) + { + + folder.Passwords.Remove(this); + } + Folders.Clear(); + Context.DeletePassword(this); + } + + /// + /// Saves the changes to datastore. + /// + public void Save() + { + Context.SavePassword(this); + IsModified = false; + } + + /// + /// Undos the changes made. + /// + public void Undo() + { + Context.UndoPassword(this); + IsModified = false; + } + + protected internal override void Reset() + { + foreach (var folder in Folders) + { + folder.ResetPasswords(); + } + + fields = null; + folders = null; + OnPropertyChanged("Fields"); + OnPropertyChanged("Folders"); + OnPropertyChanged("Name"); + OnPropertyChanged("IsFavorite"); + OnPropertyChanged("PasswordFolders"); + base.Reset(); + } + + /// + /// Adds a new to the password. + /// + /// The type of the new + /// The name of the new + public void AddField(FieldType type, string name) + { + Field field = new Field(this, 0, name, type, null, (short)Fields.Count); + this.Fields.Add(field); + } + + /// + /// Clears all fields from this password. + /// + private void ClearFields() + { + if (fields != null) + { + foreach (Field field in fields) + { + field.Dispose(); + } + fields.Clear(); + fields = null; + } + } + + public Field this[int index] + { + get { return Fields[index]; } + } + } +} diff --git a/PasswordSafe/PasswordSafe.Data/Biz/PasswordFolder.cs b/PasswordSafe/PasswordSafe.Data/Biz/PasswordFolder.cs new file mode 100644 index 0000000..f24c912 --- /dev/null +++ b/PasswordSafe/PasswordSafe.Data/Biz/PasswordFolder.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Diagnostics; + +namespace PasswordSafe.Data.Biz +{ + /// + /// Represents a link of one n:m relation between and + /// This is a helper class for UI. + /// + public class PasswordFolder:BaseObject + { + public PasswordFolder(Folder folder, Password password) + : base() + { + this.Folder = folder; + this.Password = password; + } + + public PasswordFolder(Folder folder, Password password, PasswordFolder parent) + : base() + { + this.Folder = folder; + this.Password = password; + this.Parent = parent; + } + + public PasswordFolder Parent { get; private set; } + + + protected override void OnPropertyChanged(string propertyName) + { + if (propertyName == "Folders") + { + folders = null; + OnPropertyChanged("IsFavorite"); + } + base.OnPropertyChanged(propertyName); + } + + /// + /// Gets the + /// + public Password Password { get; private set; } + + /// + /// Gets the + /// + public Folder Folder { get; private set; } + + /// + /// Gets the name of the . + /// + public override string Name { get { return Folder.Name; } } + + private IEnumerable folders; + + /// + /// Gets an enumeration of all child s otherwise null. + /// + public IEnumerable Folders + { + get + { + if (folders == null) + { + folders = Folder.Folders.Select(x => new PasswordFolder(x, Password, this)).ToArray(); + } + return folders; + } + } + + /// + /// Gets sets whether the link is a favorite. + /// + public bool IsFavorite + { + get + { + return Password.Folders.Where(f => f.Id == Folder.Id).Any(); + } + set + { + if (IsFavorite != value) + { + + bool isPasswordFavorite = Password.IsFavorite; + Folder folder = Folder; + if (folder != null) + { + if (!value) + { + folder.Passwords.Remove(Password); + Password.Folders.Remove(folder); + } + else + { + folder.Passwords.Add(Password); + Password.Folders.Add(folder); + } + OnPropertyChanged("IsFavorite"); + + if (!value) + { + ForwardIsFaovriteChanged(); + } + else + { + if (Parent != null) Parent.IsFavorite = true; + } + } + Password.IsModified = true; + + //// allways notify, even if IsFavorite has not changed just to mark Password as being modified: + Password.RaisePropertyChanged("IsFavorite"); + folder.RaisePropertyChanged("PasswordFolders"); + } + } + } + + + private void ForwardIsFaovriteChanged() + { + foreach (PasswordFolder folder in this.Folders) + { + folder.IsFavorite = false; + } + } + } +} diff --git a/PasswordSafe/PasswordSafe.Data/Biz/TemplateField.cs b/PasswordSafe/PasswordSafe.Data/Biz/TemplateField.cs new file mode 100644 index 0000000..13c4d1b --- /dev/null +++ b/PasswordSafe/PasswordSafe.Data/Biz/TemplateField.cs @@ -0,0 +1,177 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace PasswordSafe.Data.Biz +{ + /// + /// Represents a field for a categegory template. + /// + public class TemplateField : BaseObject + { + /// + /// Creates a new . + /// + /// The id from the database, 0 if not attaced. + /// The name of the field. + /// The type of the field. + /// + public TemplateField(int id, string name, FieldType type, Category category, string minRange, string maxRange, short order) + : base(id, name) + { + this.Category = category; + this.Type = type; + this.minRange = minRange; + this.minRange = maxRange; + this.order = order; + } + + private bool isSelected; + + public bool IsSelected + { + get { return isSelected; } + set + { + if (isSelected != value) + { + isSelected = value; + OnPropertyChanged("IsSelected"); + } + } + } + + + protected override void OnPropertyChanged(string propertyName) + { + base.OnPropertyChanged(propertyName); + if (propertyName != "IsSelected") + { + Category.IsModified = true; + Category.RaisePropertyChanged("Fields"); + } + } + + private string minRange; + private string maxRange; + + public string MinRange + { + get { return FormatRange(minRange); } + set + { + if (minRange != value) + { + minRange = value; + OnPropertyChanged("MinRange"); + } + } + } + + public string MaxRange + { + get { return FormatRange(maxRange); } + + set + { + if (maxRange != value) + { + maxRange = value; + OnPropertyChanged("MaxRange"); + } + } + } + + private static DateTime? ToDateTime(string value) + { + if (value == null) return null; + DateTime result; + if (DateTime.TryParse(value, out result)) return result; + return null; + } + + private static int? ToInt(string value) + { + if (value == null) return null; + int result; + if (int.TryParse(value,out result)) return result; + return null; + } + + + private string FormatRange(string value) + { + if (value == null) return null; + switch (Type) + { + case FieldType.Date: + DateTime? dt = ToDateTime(value); + return dt.HasValue ? dt.Value.ToShortDateString() : ""; + + case FieldType.Time: + dt = ToDateTime(value); + return dt.HasValue ? dt.Value.ToLongTimeString() : ""; + + case FieldType.Int: + int? ival = ToInt(value); + return ival.HasValue ? ival.Value.ToString() : ""; + + + default: + return value; + + } + } + + /// + /// Gets the . + /// + protected internal override BizContext Context + { + get + { + return Category.Context; + } + } + + /// + /// Gets the . + /// + public Category Category { get; private set; } + + /// + /// Gets the . + /// + public FieldType Type { get; private set; } + + + + public void Top() + { + Category.Fields.MoveToTop(this); + } + + public void Bottom() + { + Category.Fields.MoveToBottom(this); + } + + public void Up() + { + Category.Fields.MoveUp(this); + + } + + public void Down() + { + Category.Fields.MoveDown(this); + } + + + public void Delete() + { + Category.Fields.Remove(this); + } + } +} diff --git a/PasswordSafe/PasswordSafe.Data/DAL/DAL.cs b/PasswordSafe/PasswordSafe.Data/DAL/DAL.cs new file mode 100644 index 0000000..16bc28d --- /dev/null +++ b/PasswordSafe/PasswordSafe.Data/DAL/DAL.cs @@ -0,0 +1,667 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using PasswordSafe.Data.Biz; +using System.Data.SqlServerCe; +using System.Data; +using System.Diagnostics; +using System.Data.SqlClient; + +namespace PasswordSafe.Data.DAL +{ + /// + /// Data Access Layer to exchange data from business objects with the Database. + /// + internal class Context : IDisposable + { + internal Context(string connectionString) + : base() + { + if (connectionString != null) Open(connectionString); + } + + private void Open(string connectionString) + { + if (connection != null) + { + connection.Close(); + connection.Dispose(); + } + if (!string.IsNullOrEmpty(connectionString)) + { + connection = new SqlCeConnection(connectionString); + } + if (connection != null && connection.State == ConnectionState.Closed) connection.Open(); + } + + + #region IDisposable Members + + public void Dispose() + { + if (connection != null) + { + connection.Close(); + connection.Dispose(); + } + } + + #endregion + + public bool IsConnected + { + get + { + return Connection != null && Connection.State == ConnectionState.Open; + } + } + + private SqlCeConnection connection; + + /// + /// Gets the Connection String for the database. + /// + public SqlCeConnection Connection + { + get + { + return connection; + } + } + + /// + /// Executes a T-SQL with parameters. + /// + /// The T-SQL command to execute. + /// The parameters. + /// The number data affected. + protected int Execute(string command, params object[] param) + { + if (Connection.State == System.Data.ConnectionState.Closed) Connection.Open(); + using (SqlCeCommand cmd = Connection.CreateCommand()) + { + cmd.CommandText = command; + int id = 1; + foreach (object value in param) + { + cmd.Parameters.AddWithValue("@v" + id.ToString(), value != null ? value : DBNull.Value); + id++; + } + return cmd.ExecuteNonQuery(); + } + } + + /// + /// Reads data from database and converts it into business objects. + /// + /// The type of business object. + /// The T-SQL command to read from database. + /// A that converts a to . + /// The parameters for the T-SQL. + /// An enumeration of + protected IEnumerable ReadData(string command, Func convert, params object[] param) + { + if (Connection.State == System.Data.ConnectionState.Closed) Connection.Open(); + lock (this) + { + using (DataTable table = new DataTable()) + { + using (SqlCeCommand cmd = Connection.CreateCommand()) + { + cmd.CommandText = command; + int id = 1; + foreach (object value in param) + { + cmd.Parameters.AddWithValue("@v" + id.ToString(), value); + id++; + } + using (SqlCeDataAdapter adapter = new SqlCeDataAdapter(cmd)) + { + adapter.Fill(table); + } + } + return table.AsEnumerable().Select(f => convert(f)); + } + } + } + + private int GetIdentity() + { + if (Connection.State == System.Data.ConnectionState.Closed) Connection.Open(); + using (SqlCeCommand cmd = Connection.CreateCommand()) + { + cmd.CommandText = "select @@IDENTITY;"; + return (int)(decimal)cmd.ExecuteScalar(); + } + } + + public void CreateFavorite(int passwordId, int folderId) + { + int n = Execute("insert into Favorite (PasswordId,folderId) VALUES (@v1,@v2)", passwordId, folderId); + if (n != 1) throw new DBEntityNotUpdatedException(); + } + + public void DeleteFavorite(int passwordId, int folderId) + { + int n = Execute("delete from Favorite where passwordId=@v1 and folderId=@v2", passwordId, folderId); + if (n != 1) throw new DBEntityNotUpdatedException(); + + } + + public IEnumerable GetExistingFaves(int passwordId) + { + + return ReadData("select FolderId from Favorite where PasswordId=@v1", + r => (int)r[0], + passwordId); + } + + private string TableNameFromFieldType(FieldType type) + { + switch (type) + { + case FieldType.Text: + case FieldType.Password: + return "StringField"; + + case FieldType.Int: + case FieldType.Bool: + case FieldType.Separator: + return "IntField"; + + case FieldType.Date: + case FieldType.Time: + return "DateField"; + + case FieldType.Memo: + return "MemoField"; + + default: + throw new NotImplementedException(); + } + } + + public void UpdateField(Field field) + { + string sql = string.Format("update {0} set Name=@v1, [Value]=@v2,[Order]=@v3", TableNameFromFieldType(field.Type)); + + object p1 = null; + object p2 = null; + if (field.HasRange) + { + sql += ",[MinRange]=@v5, [MaxRange]=@v6"; + p1 = CheckOptValue(field.MinRange, field); + p2 = CheckOptValue(field.MaxRange, field); + } + if (field.HasLines) + { + sql += ",[MinLines]=@v5,[MaxLines]=@v6"; + p1 = field.MinLines; + p2 = field.MaxLines; + } + sql += " where Id=@v4"; + + int n = Execute( + sql, + field.Name, CheckValue(field.Value, field), field.Order, field.Id, p1, p2); + if (n != 1) throw new DBEntityNotUpdatedException(); + } + + public void UpdateTemplateField(TemplateField field) + { + string sql = "update Template set [Type]=@v1, [Name]=@v2, [Order]=@v3, [MinRange]=@v4, [MaxRange]=@v5 where [Id]=@v6"; + + int n = Execute( + sql, + (int)field.Type, field.Name, field.Order, field.MinRange, field.MaxRange, field.Id); + if (n != 1) throw new DBEntityNotUpdatedException(); + } + + public int CreateTemplateField(TemplateField field) + { + int n = Execute( + "Insert into Template ([Name], [CategoryId], [Type], [Order]) values (@v1, @v2, @v3, 3200)", + field.Name, field.Category.Id, (int)field.Type, field.Order); + + if (n != 1) throw new DBEntityNotUpdatedException(); + int id = GetIdentity(); + field.Id = id; + return id; + } + + private readonly DateTime minDate = new DateTime(1753, 1, 1); + private readonly DateTime maxDate = new DateTime(9999, 12, 31); + + private object CheckValue(object value, Field field) + { + switch (field.Type) + { + case FieldType.Text: + case FieldType.Password: + case FieldType.Memo: + if (value is string) return (string)value; else return ""; + + case FieldType.Separator: + case FieldType.Int: + if (value is int) return (int)value; else return default(int); + + case FieldType.Bool: + if (value is int) + { + return ((int)value) == 0 ? 0 : 1; + } + else return 0; + + case FieldType.Time: + case FieldType.Date: + if (value is DateTime) + { + DateTime dt = (DateTime)value; + if (dt < minDate) dt = minDate; + if (dt > maxDate) dt = maxDate; + return dt; + } + else return maxDate; + + default: + throw new NotImplementedException(); + } + } + + private object CheckOptValue(object value, Field field) + { + switch (field.Type) + { + case FieldType.Text: + case FieldType.Password: + case FieldType.Memo: + if (value is string) return (string)value; else return null; + + case FieldType.Separator: + case FieldType.Int: + if (value is int) return (int)value; else return null; + + + case FieldType.Time: + case FieldType.Date: + if (value is DateTime) + { + DateTime dt = (DateTime)value; + if (dt < minDate) dt = minDate; + if (dt > maxDate) dt = maxDate; + return dt; + } + else return null; + + default: + return null; + } + } + + public int CreateField(Field field) + { + int n = Execute( + string.Format("Insert into {0} (Name,[Value],PasswordId,Type,[Order]) values (@v1,@v2,@v3,@v4,@v5)", TableNameFromFieldType(field.Type)), + field.Name, CheckValue(field.Value, field), field.Password.Id, (short)field.Type, (short)field.Order); + + if (n != 1) throw new DBEntityNotUpdatedException(); + return GetIdentity(); + } + + public void DeleteField(int id, FieldType type) + { + int n = Execute( + string.Format("delete from {0} where Id=@v1", TableNameFromFieldType(type)), + id); + } + + public IEnumerable GetFieldIds(int passwordId, FieldType type) + { + return ReadData( + string.Format("select Id from {0} where PasswordId=@v1 order by [Order]", TableNameFromFieldType(type)), + r => r.Field("Id"), + passwordId); + } + + private Category ToCategory(DataRow row, Category parent) + { + return new Category( + row.Field("Id"), + row.Field("Name"), + row.Field("Order"), + parent); + } + + private Category ToCategory2(DataRow row) + { + return new Category( + row.Field("Id"), + row.Field("Name"), + row.Field("Order"), + row.Field("ParentId")); + } + + private Password ToPassword(DataRow row, Category category) + { + return new Password( + category, + row.Field("Id"), + row.Field("Name")); + } + + private Password ToPassword2(DataRow row) + { + return new Password( + row.Field("CategoryId"), + row.Field("Id"), + row.Field("Name")); + } + + + public Folder ToFolder(DataRow row, Folder parent) + { + var folder = new Folder( + row.Field("Id"), + row.Field("Name"), + parent); + + folder.Order = row.Field("Order"); + return folder; + } + + public Folder ToFolder2(DataRow row) + { + var folder = new Folder( + row.Field("Id"), + row.Field("Name"), + row.Field("ParentId")); + folder.Order = row.Field("Order"); + return folder; + } + + + public Field ToField(DataRow row, Password password) + { + Field field = new Field(password, + row.Field("Id"), + row.Field("Name"), + (FieldType)row.Field("Type"), + row["Value"], + row.Field("Order")); + + if (field.HasRange) + { + try + { + field.SetRange(row["MinRange"], row["MaxRange"]); + } + catch (Exception) + { + } + } + if (field.HasLines) + { + try + { + field.SetLines(row.Field("MinLines"), row.Field("MaxLines")); + } + catch (Exception) + { + } + } + return field; + } + + public TemplateField ToTemplateField(DataRow row, Category category) + { + return new TemplateField( + row.Field("Id"), + row.Field("Name"), + (FieldType)row.Field("Type"), + category, + row.Field("MinRange"), + row.Field("MaxRange"), + row.Field("Order") + ); + } + + public IEnumerable GetRootCategories() + { + if (!IsDbAvailable()) return Enumerable.Empty(); + return ReadData( + "select * from Category where ParentId is null order by [Order],Name", + r => ToCategory(r, null)); + } + + private bool IsDbAvailable() + { + return IsConnected; + } + + public IEnumerable GetCategories(Category parent) + { + return ReadData( + "select * from Category where ParentId=@v1 order by [Order],Name", + r => ToCategory(r, parent), + parent.Id); + } + + public void UpdatePassword(Password password) + { + int n = Execute("update Password set Name=@v1,Modified=@v2, [Order]=@v3 where Id=@v4", + password.Name, + DateTime.Now, + password.Order, + password.Id); + if (n != 1) throw new DBEntityNotUpdatedException(); + } + + public IEnumerable GetPasswordsByCategory(Category category) + { + return ReadData("select * from Password where CategoryId=@v1 order by [Order],Name", + r => ToPassword(r, category), + category.Id); + } + + public IEnumerable GetFolderByPasswordId(int passwordId) + { + return ReadData("select f.* from Folder as f join Favorite as x on x.FolderId=f.Id where x.PasswordId=@v1", + r => ToFolder2(r), passwordId); + } + + public IEnumerable GetFoldersByParentFolder(Folder parent) + { + return ReadData("select * from Folder where ParentId=@v1 order by [Order],Name", + r => ToFolder(r, parent), + parent.Id); + } + + public IEnumerable GetPasswordsByFolderId(int folderId) + { + return ReadData("select p.* from Password as p join Favorite as f on f.PasswordId=p.Id where f.FolderId=@v1", + r => ToPassword2(r), + folderId); + } + + public void DeletePassword(int passwordId) + { + using (var transaction = Connection.BeginTransaction()) + { + Execute("delete from StringField where PasswordId=@v1", passwordId); + Execute("delete from IntField where PasswordId=@v1", passwordId); + Execute("delete from MemoField where PasswordId=@v1", passwordId); + Execute("delete from DateField where PasswordId=@v1", passwordId); + Execute("delete from Favorite where PasswordId=@v1", passwordId); + int n = Execute("delete from Password where Id=@v1", passwordId); + // if (n != 1) throw new DBEntityNotUpdatedException(); + transaction.Commit(); + } + } + + public IEnumerable GetRootFolders() + { + + if (!IsDbAvailable()) return Enumerable.Empty(); + return ReadData("select * from Folder where ParentId is null order by [Order],Name", + r => ToFolder(r, null)); + } + + public IEnumerable GetFields(Password password) + { + var f1 = GetFields(password, FieldType.Text).ToArray(); + var f2 = GetFields(password, FieldType.Int).ToArray(); + var f3 = GetFields(password, FieldType.Date).ToArray(); + var f4 = GetFields(password, FieldType.Memo).ToArray(); + + var result = f1.Union(f2).Union(f2).Union(f3).Union(f4).OrderBy(f => f.Order).ToArray(); + return result; + } + + private IEnumerable GetFields(Password password, FieldType fieldType) + { + return ReadData(string.Format("select * from {0} where PasswordId=@v1", TableNameFromFieldType(fieldType)), + r => ToField(r, password), + password.Id); + } + + public Category GetCategoryById(int categoryId) + { + var c = ReadData("select * from Category where Id=@v1", + r => ToCategory2(r), + categoryId); + + return c.FirstOrDefault(); + } + + public Folder GetFolderById(int folderId) + { + var folders = ReadData("select * from Folder where Id=@v1", + r => ToFolder2(r), + folderId); + + return folders.FirstOrDefault(); + } + + public Folder CreateFolder(Folder folder) + { + int n = Execute("insert into Folder ([Order], [ParentId]) values (3200, @v1)", folder.Parent.Id); + if (n != 1) throw new DBEntityNotUpdatedException(); + int id = GetIdentity(); + folder.Id = id; + return folder; + } + + public void DeleteFolder(int folderId) + { + using (var transaction = Connection.BeginTransaction()) + { + Execute("delete from Favorite where FolderId=@v1", folderId); + Execute("delete from Folder where Id=@v1", folderId); + transaction.Commit(); + } + } + + public Category CreateCategory(Category category) + { + int n = Execute("insert into Category ([Order], [ParentId], [Name]) values (@v1, @v2, @v3)", (short)category.Order, category.Parent.Id, category.Name); + if (n != 1) throw new DBEntityNotUpdatedException(); + int id = GetIdentity(); + category.Id = id; + return category; + } + + public void DeleteCategory(int categoryId) + { + using (var scope = Connection.BeginTransaction()) + { + Execute("delete from Template where [CategoryId]=@v1", categoryId); + Execute("delete from Category where [Id]=@v1", categoryId); + scope.Commit(); + } + } + + + public void CreatePassword(Password password) + { + DateTime now = DateTime.Now; + int n = Execute("insert into Password (Name,CategoryId,Created,Modified,[Order]) values(@v1,@v2,@v3,@v4,32700)", + password.Name, + password.Category.Id, + now, now); + + if (n != 1) throw new DBEntityNotUpdatedException(); + password.Id = GetIdentity(); + } + + public IEnumerable GetTemplates(Category category) + { + return ReadData("select * from Template where CategoryId=@v1", + r => ToTemplateField(r, category), + category.Id).OrderBy(f => f.Order); + } + + public void DeleteTemplateField(int id) + { + Execute("delete from Template where Id=@v1", id); + } + + public Password GetPassword(int passwordId) + { + return ReadData("select * from Password where Id=@v1", + r => ToPassword2(r), + passwordId).FirstOrDefault(); + } + + public IDbTransaction BeginTransaction() + { + return Connection.BeginTransaction(); + } + + public void UpdateCategory(Category category) + { + int n = Execute("update Category set Name=@v1, [Order]=@v2 where Id=@v3", + category.Name, + category.Order, + category.Id); + if (n != 1) throw new DBEntityNotUpdatedException(); + } + + internal void UpdateFolder(Folder folder) + { + int n = Execute("update Folder set Name=@v1, [Order]=@v2 where Id=@v3", + folder.Name, + folder.Order, + folder.Id); + if (n != 1) throw new DBEntityNotUpdatedException(); + } + + public IEnumerable GetTemplateFields(int categoryId) + { + return ReadData( + "select Id from Template where CategoryId=@v1", + row => row.Field("Id"), + categoryId); + } + + internal bool ChangePassword(string connectionString, string oldPassword, string newPassword) + { + try + { + SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(connectionString); + builder.Password = oldPassword; + SqlCeConnection con = new SqlCeConnection(builder.ConnectionString); + SqlCeEngine engine = new SqlCeEngine(builder.ConnectionString); + builder.Encrypt = true; + + builder.Password = newPassword; + engine.Compact(builder.ConnectionString); + return true; + } + catch (Exception) + { + return false; + } + } + } +} diff --git a/PasswordSafe/PasswordSafe.Data/DAL/DBEntityNotUpdatedException.cs b/PasswordSafe/PasswordSafe.Data/DAL/DBEntityNotUpdatedException.cs new file mode 100644 index 0000000..eb76685 --- /dev/null +++ b/PasswordSafe/PasswordSafe.Data/DAL/DBEntityNotUpdatedException.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace PasswordSafe.Data.DAL +{ + public class DBEntityNotUpdatedException : ArgumentNullException + { + } +} diff --git a/PasswordSafe/PasswordSafe.Data/PasswordSafe.Data.csproj b/PasswordSafe/PasswordSafe.Data/PasswordSafe.Data.csproj new file mode 100644 index 0000000..2e86fd5 --- /dev/null +++ b/PasswordSafe/PasswordSafe.Data/PasswordSafe.Data.csproj @@ -0,0 +1,157 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {E4DA0115-54F3-4DA0-813B-CDEBF4D205E5} + Library + Properties + PasswordSafe.Data + PasswordSafe.Data + v3.5 + 512 + SAK + SAK + SAK + SAK + + + false + + + + + 3.5 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + AllRules.ruleset + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + AllRules.ruleset + + + + + 3.5 + + + + + + 3.5 + + + + + + + + + + + + + + + + + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + False + .NET Framework Client Profile + false + + + False + .NET Framework 2.0 %28x86%29 + false + + + False + .NET Framework 3.0 %28x86%29 + false + + + False + .NET Framework 3.5 + false + + + False + .NET Framework 3.5 SP1 + true + + + False + SQL Server Compact 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + \ No newline at end of file diff --git a/PasswordSafe/PasswordSafe.Data/Properties/AssemblyInfo.cs b/PasswordSafe/PasswordSafe.Data/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..4de3678 --- /dev/null +++ b/PasswordSafe/PasswordSafe.Data/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("PasswordSafe.Data")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("www.tomssoftware.net")] +[assembly: AssemblyProduct("PasswordSafe.Data")] +[assembly: AssemblyCopyright("Copyright © Thomas Gerber 2009")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("abd75e9a-48e9-4216-8823-6015d8e65e35")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("0.7.1.*")] +[assembly: AssemblyFileVersion("0.7.1.200")] diff --git a/PasswordSafe/PasswordSafe.Data/Properties/Resources.Designer.cs b/PasswordSafe/PasswordSafe.Data/Properties/Resources.Designer.cs new file mode 100644 index 0000000..a955ba5 --- /dev/null +++ b/PasswordSafe/PasswordSafe.Data/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.20506.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace PasswordSafe.Data.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PasswordSafe.Data.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/PasswordSafe/PasswordSafe.Data/Properties/Resources.resx b/PasswordSafe/PasswordSafe.Data/Properties/Resources.resx new file mode 100644 index 0000000..ffecec8 --- /dev/null +++ b/PasswordSafe/PasswordSafe.Data/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/PasswordSafe/PasswordSafe.Data/Properties/Settings.Designer.cs b/PasswordSafe/PasswordSafe.Data/Properties/Settings.Designer.cs new file mode 100644 index 0000000..cbe598c --- /dev/null +++ b/PasswordSafe/PasswordSafe.Data/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.20506.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace PasswordSafe.Data.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/PasswordSafe/PasswordSafe.Data/Properties/Settings.settings b/PasswordSafe/PasswordSafe.Data/Properties/Settings.settings new file mode 100644 index 0000000..abf36c5 --- /dev/null +++ b/PasswordSafe/PasswordSafe.Data/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/PasswordSafe/PasswordSafe/App.xaml b/PasswordSafe/PasswordSafe/App.xaml new file mode 100644 index 0000000..71ad6d6 --- /dev/null +++ b/PasswordSafe/PasswordSafe/App.xaml @@ -0,0 +1,5 @@ + + + + diff --git a/PasswordSafe/PasswordSafe/App.xaml.cs b/PasswordSafe/PasswordSafe/App.xaml.cs new file mode 100644 index 0000000..cda9a6b --- /dev/null +++ b/PasswordSafe/PasswordSafe/App.xaml.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Data; +using System.Linq; +using System.Windows; + +namespace PasswordSafe +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application + { + } +} diff --git a/PasswordSafe/PasswordSafe/Classes/UIContext.cs b/PasswordSafe/PasswordSafe/Classes/UIContext.cs new file mode 100644 index 0000000..fef2cc9 --- /dev/null +++ b/PasswordSafe/PasswordSafe/Classes/UIContext.cs @@ -0,0 +1,154 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using PasswordSafe.Data.Biz; + +namespace PasswordSafe.Classes +{ + /// + /// Gets that from and is used by s. + /// + public class UIContext + { + private BizContext context + { + get + { + return BizContext.Instance; + } + } + + /// + /// Gets the root . + /// + public Category RootCategory + { + get + { + return context.Categories.FirstOrDefault(); + } + } + + /// + /// Gets the root . + /// + public Folder RootFolder + { + get + { + return context.Folders.FirstOrDefault(); + } + } + + + /// + /// Gets an enumeration of all root s. + /// + public IEnumerable GetCategories() + { + return context.Categories; + } + + /// + /// Gets an enumeration of all root s. + /// + public IEnumerable GetFolders() + { + return context.Folders; + } + + /// + /// Gets an enumeration of all s. + /// + /// An enumeration of see cref="T:FieldType"/>. + public IEnumerable Types() + { + return Enum.GetValues(typeof(FieldType)).Cast(); + } + + private NodeBase breadcrumbRoot; + + /// + /// Get the root Node of type for the main breadcrumb. + /// + /// An enumeration of see cref="T:NodeBase"/>. + public NodeBase GetBreadcrumbRoot() + { + lock (this) + { + if (breadcrumbRoot == null) + { + if (RootFolder != null && RootCategory != null) + { + breadcrumbRoot = new CustomNode( + "Home", + new NodeBase[] + { + RootFolder, + RootCategory + } + ); + } + else return null; + } + return breadcrumbRoot; + } + } + + + /// + /// Gets the root Category. + /// + /// The root Category. + public Category GetCategoryRoot() + { + return RootCategory; + } + + /// + /// Gets an enumeration of strings of all avaiable s used for + /// a breadcrumb bar. + /// + /// An enumeration of + public static IEnumerable GetBreadcrumbDropDownData(NodeBase root) + { + if (root != null && root.Children != null) + { + foreach (NodeBase node in root.Children) + { + yield return node.GetPath(1); + var subNodes = GetSubPath(node); + if (subNodes != null) + { + foreach (string subPath in subNodes) + { + yield return subPath; + } + } + } + } + } + + private static IEnumerable GetSubPath(NodeBase node) + { + foreach (NodeBase child in GetChildren(node)) + { + yield return child.GetPath(1); + foreach (string subPath in GetSubPath(child)) yield return subPath; + } + } + + private static IEnumerable GetChildren(NodeBase node) + { + Category c = node as Category; + if (c != null) return c.Categories.Cast(); + + Folder f = node as Folder; + if (f != null) return f.Folders.Cast(); + + CustomNode n = node as CustomNode; + return n.Children; + } + } +} diff --git a/PasswordSafe/PasswordSafe/Commands.cs b/PasswordSafe/PasswordSafe/Commands.cs new file mode 100644 index 0000000..4902e90 --- /dev/null +++ b/PasswordSafe/PasswordSafe/Commands.cs @@ -0,0 +1,793 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Input; +using Odyssey.Controls; +using System.Windows; +using PasswordSafe.Controls; +using System.Data.Linq; +using System.Diagnostics; +using System.Windows.Controls; +using PasswordSafe.Classes; +using PasswordSafe.Data; +using PasswordSafe.Data.Biz; +using System.Windows.Controls.Primitives; +using PasswordSafe.Export; +using Microsoft.Win32; +using System.IO; + +namespace PasswordSafe.Controls +{ + public class Commands + { + public static readonly RoutedUICommand TestCommand = new RoutedUICommand("Test", "TestCommand", typeof(Main)); + public static readonly RoutedUICommand ExportCommand = new RoutedUICommand("Test", "ExportCommand", typeof(Main)); + + public static readonly RoutedUICommand SelectNextFieldCommand = new RoutedUICommand("Next", "SelectNextFieldCommand", typeof(Main)); + public static readonly RoutedUICommand SelectPreviousFieldCommand = new RoutedUICommand("Previous", "SelectPreviousFieldCommand", typeof(Main)); + + public static readonly RoutedUICommand LogoutCommand = new RoutedUICommand("Logout", "LogoutCommand", typeof(Main), + new InputGestureCollection(new KeyGesture[] { + new KeyGesture(Key.L,ModifierKeys.Control,"Ctrl+L")})); + + public static readonly RoutedUICommand ChangePasswordCommand = new RoutedUICommand("New", "ChangePasswordCommand", typeof(Main)); + public static readonly RoutedUICommand NewPasswordCommand = new RoutedUICommand("New", "NewPasswordCommand", typeof(Main)); + public static readonly RoutedUICommand DuplicateCommand = new RoutedUICommand("Duplicate", "DuplicateCommand", typeof(Main)); + + public static readonly RoutedUICommand SaveTemplateCommand = new RoutedUICommand("Save", "SaveTemplateCommand", typeof(Main), + new InputGestureCollection(new KeyGesture[] { new KeyGesture(Key.S, ModifierKeys.Control, "Ctrl+S") })); + + public static readonly RoutedUICommand SaveCommand = new RoutedUICommand("Save", "SaveCommand", typeof(Main), + new InputGestureCollection(new KeyGesture[] { new KeyGesture(Key.S, ModifierKeys.Control, "Ctrl+S") })); + + /// + /// Used for templates to copy the Value property of the DataContext of a FrameworkControl to clipboard. + /// + public static readonly RoutedUICommand CopyCommand = new RoutedUICommand("Copy", "CopyCommand", typeof(Main), + new InputGestureCollection(new KeyGesture[] { new KeyGesture(Key.C, ModifierKeys.Control, "Ctrl+C") })); + + public static readonly RoutedUICommand UndoAllCommand = new RoutedUICommand("Undo All", "UndoAllCommand", typeof(Main), + new InputGestureCollection(new KeyGesture[] { + new KeyGesture(Key.Z,ModifierKeys.Control|ModifierKeys.Shift,"Shift+Ctrl+Z"), + new KeyGesture(Key.U,ModifierKeys.Control|ModifierKeys.Shift,"Shift+Ctrl+U") + })); + + public static readonly RoutedUICommand SaveAllCommand = new RoutedUICommand("Save All", "SaveAllCommand", typeof(Main), + new InputGestureCollection(new KeyGesture[] { new KeyGesture(Key.S, ModifierKeys.Control | ModifierKeys.Shift, "Shift+Ctrl+S") })); + + public static readonly RoutedUICommand UndoTemplateCommand = new RoutedUICommand("Undo", "UndoTemplateCommand", typeof(Main), + new InputGestureCollection(new KeyGesture[] { + new KeyGesture(Key.Z,ModifierKeys.Control,"Ctrl+Z"), + new KeyGesture(Key.U,ModifierKeys.Control,"Ctrl+U") + })); + + public static readonly RoutedUICommand UndoCommand = new RoutedUICommand("Undo", "UndoCommand", typeof(Main), + new InputGestureCollection(new KeyGesture[] { + new KeyGesture(Key.Z,ModifierKeys.Control,"Ctrl+Z"), + new KeyGesture(Key.U,ModifierKeys.Control,"Ctrl+U") + })); + + + public static readonly RoutedUICommand DeleteCommand = new RoutedUICommand("Delete", "DeleteCommand", typeof(Main)); + public static readonly RoutedUICommand SelectItemCommand = new RoutedUICommand("Select", "SelectItemCommand", typeof(Main)); + public static readonly RoutedUICommand SelectTemplateItemCommand = new RoutedUICommand("Select", "SelectTemplateItemCommand", typeof(Main)); + public static readonly RoutedUICommand CopyUserNameCommand = new RoutedUICommand("Copy User Name", "CopyUserNameCommand", typeof(Main)); + public static readonly RoutedUICommand DataModifiedCommand = new RoutedUICommand("", "DataModifiedCommand", typeof(Main)); + + public static readonly RoutedUICommand FieldUpCommand = new RoutedUICommand("Up", "FieldUpCommand", typeof(Main)); + public static readonly RoutedUICommand FieldTopCommand = new RoutedUICommand("Down", "FieldDownCommand", typeof(Main)); + public static readonly RoutedUICommand FieldBottomCommand = new RoutedUICommand("Top", "FieldTopCommand", typeof(Main)); + public static readonly RoutedUICommand FieldDownCommand = new RoutedUICommand("Bottom", "FieldBottomCommand", typeof(Main)); + public static readonly RoutedUICommand FieldDeleteCommand = new RoutedUICommand("Delete", "FieldDeleteCommand", typeof(Main)); + public static readonly RoutedUICommand NewFieldCommand = new RoutedUICommand("New Field", "NewFieldCommand", typeof(Main), + new InputGestureCollection(new KeyGesture[] { new KeyGesture(Key.N, ModifierKeys.Control, "Ctrl+N") })); + + public static readonly RoutedUICommand NewTemplateFieldCommand = new RoutedUICommand("New Field", "NewTemplateFieldCommand", typeof(Main), + new InputGestureCollection(new KeyGesture[] { new KeyGesture(Key.N, ModifierKeys.Control, "Ctrl+N") })); + public static readonly RoutedUICommand DeleteTemplateFieldCommand = new RoutedUICommand("Delete", "DeleteTemplateFieldCommand", typeof(Main)); + + public static readonly RoutedUICommand CategoryUpCommand = new RoutedUICommand("Up", "CategoryUpCommand", typeof(Main)); + public static readonly RoutedUICommand CategoryTopCommand = new RoutedUICommand("Down", "CategoryTopCommand", typeof(Main)); + public static readonly RoutedUICommand CategoryBottomCommand = new RoutedUICommand("Top", "CategoryBottomCommand", typeof(Main)); + public static readonly RoutedUICommand CategoryDownCommand = new RoutedUICommand("Bottom", "CategoryDownCommand", typeof(Main)); + + public static readonly RoutedUICommand TemplateUpCommand = new RoutedUICommand("Up", "TemplateUpCommand", typeof(Main)); + public static readonly RoutedUICommand TemplateTopCommand = new RoutedUICommand("Down", "TemplateTopCommand", typeof(Main)); + public static readonly RoutedUICommand TemplateBottomCommand = new RoutedUICommand("Top", "TemplateBottomCommand", typeof(Main)); + public static readonly RoutedUICommand TemplateDownCommand = new RoutedUICommand("Bottom", "TemplateDownCommand", typeof(Main)); + + + public static readonly RoutedUICommand FolderUpCommand = new RoutedUICommand("Up", "FolderUpCommand", typeof(Main)); + public static readonly RoutedUICommand FolderTopCommand = new RoutedUICommand("Down", "FolderTopCommand", typeof(Main)); + public static readonly RoutedUICommand FolderBottomCommand = new RoutedUICommand("Top", "FolderBottomCommand", typeof(Main)); + public static readonly RoutedUICommand FolderDownCommand = new RoutedUICommand("Bottom", "FolderDownCommand", typeof(Main)); + + /// + /// Does nothing but changing IsEnabled. + /// + public static readonly RoutedUICommand PasswordSelectedCommand = new RoutedUICommand("", "PasswordSelectedCommand", typeof(Main)); + public static readonly RoutedUICommand FieldSelectedCommand = new RoutedUICommand("", "FieldSelectedCommand", typeof(Main)); + + /// + /// Toggles the IsFavorite state of a class which is the DataContext of a FrameworkControl. + /// This command is used for the TreeViews to change the favorite association of a . + /// + public static readonly RoutedUICommand ToggleFaveCommand = new RoutedUICommand("", "ToggleFaveCommand", typeof(Main)); + + public static readonly RoutedUICommand AddFolderCommand = new RoutedUICommand("Add Folder", "AddFolderCommand", typeof(Main)); + public static readonly RoutedUICommand DeleteFolderCommand = new RoutedUICommand("Delete Folder", "DeleteFolderCommand", typeof(Main)); + + public static readonly RoutedUICommand AddCategoryCommand = new RoutedUICommand("Add Category", "AddCategoryCommand", typeof(Main)); + public static readonly RoutedUICommand DeleteCategoryCommand = new RoutedUICommand("Delete Category", "DeleteCategoryCommand", typeof(Main)); + + static Commands() + { + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(TestCommand, Test)); + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(ExportCommand, Export)); + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(SelectNextFieldCommand, SelectNextPasswordField, IsPasswordSelected)); + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(SelectPreviousFieldCommand, SelectPrevPasswordField, IsPasswordSelected)); + + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(LogoutCommand, Logout)); + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(ChangePasswordCommand, ChangePassword)); + + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(SaveAllCommand, SaveAll, IsAnyPasswordOrTemplateModified)); + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(UndoAllCommand, UndoAll, IsAnyPasswordOrTemplateModified)); + + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(SaveTemplateCommand, SaveTemplate, IsTemplateModified)); + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(UndoTemplateCommand, UndoTemplate, IsTemplateModified)); + + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(NewPasswordCommand, NewPassword)); + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(DuplicateCommand, Duplicate, IsPasswordSelected)); + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(SaveCommand, SavePassword, IsPasswordModified)); + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(UndoCommand, UndoPassword, IsPasswordModified)); + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(DeleteCommand, DeletePassword, IsPasswordSelected)); + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(SelectItemCommand, SelectItem)); + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(SelectTemplateItemCommand, SelectTemplateItem)); + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(CopyUserNameCommand, CopyUserName)); + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(DataModifiedCommand, DataModified)); + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(FieldUpCommand, FieldUp, IsFieldSelected)); + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(FieldDownCommand, FieldDown, IsFieldSelected)); + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(FieldTopCommand, FieldTop, IsFieldSelected)); + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(FieldBottomCommand, FieldBottom, IsFieldSelected)); + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(FieldDeleteCommand, FieldDelete, IsFieldSelected)); + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(NewFieldCommand, NewField, IsPasswordSelected)); + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(PasswordSelectedCommand, Nothing, IsPasswordSelected)); + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(FieldSelectedCommand, Nothing, IsFieldSelected)); + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(NewTemplateFieldCommand, NewTemplateField, IsCategorySelected)); + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(DeleteTemplateFieldCommand, DeleteTemplateField, IsTemplateSelected)); + + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(CategoryUpCommand, CategoryUp, IsCategorySelected)); + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(CategoryDownCommand, CategoryDown, IsCategorySelected)); + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(CategoryTopCommand, CategoryTop, IsCategorySelected)); + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(CategoryBottomCommand, CategoryBottom, IsCategorySelected)); + + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(TemplateUpCommand, TemplateUp, IsTemplateSelected)); + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(TemplateDownCommand, TemplateDown, IsTemplateSelected)); + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(TemplateTopCommand, TemplateTop, IsTemplateSelected)); + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(TemplateBottomCommand, TemplateBottom, IsTemplateSelected)); + + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(FolderUpCommand, FolderUp, IsFolderSelected)); + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(FolderDownCommand, FolderDown, IsFolderSelected)); + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(FolderTopCommand, FolderTop, IsFolderSelected)); + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(FolderBottomCommand, FolderBottom, IsFolderSelected)); + + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(CopyCommand, CopyField, IsCopyEnabled)); + + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(ToggleFaveCommand, ToggleFave)); + + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(AddFolderCommand, AddFolder, IsFolderSelected)); + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(DeleteFolderCommand, DeleteFolder, CanDeleteFolder)); + + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(AddCategoryCommand, AddCategory, IsCategorySelected)); + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(DeleteCategoryCommand, DeleteCategory, CanDeleteCategory)); + } + + + + private static void Nothing(object sender, ExecutedRoutedEventArgs e) + { + Main main = (Main)sender; + } + + private static void SaveAll(object sender, ExecutedRoutedEventArgs e) + { + BizContext.Instance.Save(); + } + + private static void UndoAll(object sender, ExecutedRoutedEventArgs e) + { + BizContext.Instance.Undo(); + } + + private static void Export(object sender, ExecutedRoutedEventArgs e) + { + Main main = (Main)sender; + SaveFileDialog saveDialog = new SaveFileDialog(); + saveDialog.DefaultExt = ".xml"; + saveDialog.Title = "Export to..."; + saveDialog.AddExtension = true; + if (saveDialog.ShowDialog(main).Value) + { + using (FileStream stream = new FileStream(saveDialog.FileName, FileMode.Create, FileAccess.Write)) + { + XmlExporter.Export(stream); + } + } + } + + + private static void SelectPrevPasswordField(object sender, ExecutedRoutedEventArgs e) + { + Main main = (Main)sender; + Password pw = main.SelectedPassword; + + + if (pw != null && pw.Fields.Count > 0) + { + int n = pw.Fields.Count - 1; + int index = n; + for (int i = n; i > 0; i--) + { + if (pw[i].IsSelected) + { + index = i - 1; + break; + } + } + main.SelectedPasswordField = pw[index]; + } + } + + + private static void SelectNextPasswordField(object sender, ExecutedRoutedEventArgs e) + { + Main main = (Main)sender; + Password pw = main.SelectedPassword; + + if (pw != null && pw.Fields.Count > 0) + { + int index = 0; + for (int i = 0; i < pw.Fields.Count - 1; i++) + { + if (pw[i].IsSelected) + { + index = i + 1; + break; + } + } + main.SelectedPasswordField = pw[index]; + } + } + + + private static void Logout(object sender, ExecutedRoutedEventArgs e) + { + Main main = (Main)sender; + main.IsLocked = true; + main.DisplayType = DisplayType.Login; + } + + private static void AddCategory(object sender, ExecutedRoutedEventArgs e) + { + Main main = (Main)sender; + + Category category = main.SelectedCategory; + if (category != null) + { + Category newCategory = category.AddCategory("New Category"); + if (newCategory != null) + { + main.SelectedCategory = newCategory; + main.FocusEditCategoryName(); + } + } + } + + private static void DeleteCategory(object sender, ExecutedRoutedEventArgs e) + { + Main main = (Main)sender; + + Category category = main.SelectedCategory; + if (category != null) + { + category.Delete(); + main.SelectedCategory = category.Parent; + } + } + + private static void CanDeleteCategory(object sender, CanExecuteRoutedEventArgs e) + { + Main main = (Main)sender; + Category category = main.SelectedCategory; + e.CanExecute = category != null && category.Parent != null && !category.Categories.Any() && !category.Passwords.Any(); + } + + + private static void AddFolder(object sender, ExecutedRoutedEventArgs e) + { + Main main = (Main)sender; + + Folder folder = main.SelectedFolder; + if (folder != null) + { + Folder newFolder = folder.AddFolder("New Folder"); + if (newFolder != null) + { + main.SelectedFolder = newFolder; + main.FocusEditFolderName(); + } + } + } + + private static void DeleteFolder(object sender, ExecutedRoutedEventArgs e) + { + Main main = (Main)sender; + + Folder folder = main.SelectedFolder; + if (folder != null) + { + folder.Delete(); + main.SelectedFolder = folder.Parent; + } + } + + private static void CanDeleteFolder(object sender, CanExecuteRoutedEventArgs e) + { + Main main = (Main)sender; + Folder folder = main.SelectedFolder; + e.CanExecute = folder != null && folder.Parent != null && !folder.Folders.Any(); + } + + + private static void ToggleFave(object sender, ExecutedRoutedEventArgs e) + { + FrameworkElement fe = (e.OriginalSource as FrameworkElement); + PasswordFolder folder = fe != null ? fe.DataContext as PasswordFolder : null; + if (folder != null) folder.IsFavorite ^= true; + + } + + private static void CopyField(object sender, ExecutedRoutedEventArgs e) + { + FrameworkElement fe = (e.OriginalSource) as FrameworkElement; + Field field = (fe != null) ? fe.DataContext as Field : null; + if (field != null && field.Value != null) Clipboard.SetData(DataFormats.Text, field.ValueAsString()); + } + + private static void IsCopyEnabled(object sender, CanExecuteRoutedEventArgs e) + { + FrameworkElement fe = (e.OriginalSource) as FrameworkElement; + Field field = (fe != null) ? fe.DataContext as Field : null; + e.CanExecute = field != null && field.Value != null && !string.IsNullOrEmpty(field.Value.ToString()); + } + + private static void FolderTop(object sender, ExecutedRoutedEventArgs e) + { + Main main = (Main)sender; + Folder folder = main.SelectedFolder; + if (folder != null) + { + folder.Top(); + main.SelectedFolder = folder; + + EnsureFolderTreeDoesNotSelectRoot(main); + } + } + + + private static void FolderBottom(object sender, ExecutedRoutedEventArgs e) + { + Main main = (Main)sender; + Folder folder = main.SelectedFolder; + if (folder != null) + { + folder.Bottom(); + main.SelectedFolder = folder; + EnsureFolderTreeDoesNotSelectRoot(main); + } + } + + private static void FolderUp(object sender, ExecutedRoutedEventArgs e) + { + Main main = (Main)sender; + Folder folder = main.SelectedFolder; + if (folder != null) + { + folder.Up(); + main.SelectedFolder = folder; + EnsureFolderTreeDoesNotSelectRoot(main); + + } + } + + private static void FolderDown(object sender, ExecutedRoutedEventArgs e) + { + Main main = (Main)sender; + Folder folder = main.SelectedFolder; + if (folder != null) + { + folder.Down(); + main.SelectedFolder = folder; + EnsureFolderTreeDoesNotSelectRoot(main); + } + } + + private static void EnsureFolderTreeDoesNotSelectRoot(Main main) + { + main.foldersTreeView.Focus(); + } + + private static void TemplateTop(object sender, ExecutedRoutedEventArgs e) + { + Main main = (Main)sender; + TemplateField field = main.SelectedTemplateField; + if (field != null) + { + field.Top(); + main.SelectedTemplateField = field; + + EnsureTemplateTreeDoesNotSelectRoot(main); + } + } + + private static void TemplateBottom(object sender, ExecutedRoutedEventArgs e) + { + Main main = (Main)sender; + TemplateField field = main.SelectedTemplateField; + if (field != null) + { + field.Bottom(); + main.SelectedTemplateField = field; + EnsureTemplateTreeDoesNotSelectRoot(main); + } + } + + private static void TemplateUp(object sender, ExecutedRoutedEventArgs e) + { + Main main = (Main)sender; + TemplateField field = main.SelectedTemplateField; + if (field != null) + { + field.Up(); + main.SelectedTemplateField = field; + EnsureTemplateTreeDoesNotSelectRoot(main); + + } + } + + private static void TemplateDown(object sender, ExecutedRoutedEventArgs e) + { + Main main = (Main)sender; + TemplateField field = main.SelectedTemplateField; + if (field != null) + { + field.Down(); + main.SelectedTemplateField = field; + EnsureTemplateTreeDoesNotSelectRoot(main); + } + } + + private static void EnsureTemplateTreeDoesNotSelectRoot(Main main) + { + main.foldersTreeView.Focus(); + } + + private static void IsTemplateSelected(object sender, CanExecuteRoutedEventArgs e) + { + Main main = (Main)sender; + e.CanExecute = main.SelectedTemplateField != null; + } + + private static void CategoryTop(object sender, ExecutedRoutedEventArgs e) + { + Main main = (Main)sender; + Category category = main.SelectedCategory; + if (category != null) + { + category.Top(); + main.SelectedCategory = category; + + EnsureCategoryTreeDoesNotSelectRoot(main); + } + } + + private static void CategoryBottom(object sender, ExecutedRoutedEventArgs e) + { + Main main = (Main)sender; + Category category = main.SelectedCategory; + if (category != null) + { + category.Bottom(); + main.SelectedCategory = category; + EnsureCategoryTreeDoesNotSelectRoot(main); + } + } + + private static void CategoryUp(object sender, ExecutedRoutedEventArgs e) + { + Main main = (Main)sender; + Category category = main.SelectedCategory; + if (category != null) + { + category.Up(); + main.SelectedCategory = category; + EnsureCategoryTreeDoesNotSelectRoot(main); + + } + } + + private static void CategoryDown(object sender, ExecutedRoutedEventArgs e) + { + Main main = (Main)sender; + Category category = main.SelectedCategory; + if (category != null) + { + category.Down(); + main.SelectedCategory = category; + EnsureCategoryTreeDoesNotSelectRoot(main); + } + } + + /// + /// When performing an Up,Down, Top or Bottom to the Category TreeView, + /// the TreeView automatically selects the root node. + /// To prevent this, thr workaround is to Focus the TreeView directly after one of this operations. + /// + private static void EnsureCategoryTreeDoesNotSelectRoot(Main main) + { + main.categoriesTree.Focus(); + } + + private static void NewTemplateField(object sender, ExecutedRoutedEventArgs e) + { + Main main = (Main)sender; + + Category category = main.SelectedCategory; + if (category != null) + { + FieldType type = (e.Parameter is FieldType) ? (FieldType)e.Parameter : (FieldType)Enum.Parse(typeof(FieldType), e.Parameter as string); + TemplateField field = category.AddField(type, "New Field"); + main.SelectedTemplateField = field; + main.SelectTemplateFieldName(); + } + } + + + private static void NewField(object sender, ExecutedRoutedEventArgs e) + { + Main main = (Main)sender; + + Password password = main.SelectedPassword; + if (password != null) + { + FieldType type = (e.Parameter is FieldType) ? (FieldType)e.Parameter : (FieldType)Enum.Parse(typeof(FieldType), e.Parameter as string); + password.AddField(type, "New Field"); + Field field = password.Fields[password.Fields.Count - 1]; + main.SelectedPasswordField = field; + main.SelectPasswordFieldName(); + } + } + + private static void DeleteTemplateField(object sender, ExecutedRoutedEventArgs e) + { + Main main = (Main)sender; + TemplateField field = main.SelectedTemplateField; + if (field != null) + { + field.Delete(); + main.SelectedTemplateField = null; + } + } + + + + private static void FieldDelete(object sender, ExecutedRoutedEventArgs e) + { + Main main = (Main)sender; + Field field = main.SelectedPasswordField; + if (field != null) + { + field.Delete(); + main.SelectedPasswordField = null; + } + } + + + private static void FieldTop(object sender, ExecutedRoutedEventArgs e) + { + Main main = (Main)sender; + Field field = main.SelectedPasswordField; + if (field != null) + { + field.Top(); + } + } + + private static void FieldBottom(object sender, ExecutedRoutedEventArgs e) + { + Main main = (Main)sender; + Field field = main.SelectedPasswordField; + if (field != null) + { + field.Bottom(); + } + } + + private static void FieldUp(object sender, ExecutedRoutedEventArgs e) + { + Main main = (Main)sender; + Field field = main.SelectedPasswordField; + if (field != null) + { + field.Up(); + } + } + + private static void FieldDown(object sender, ExecutedRoutedEventArgs e) + { + Main main = (Main)sender; + Field field = main.SelectedPasswordField; + if (field != null) + { + field.Down(); + } + } + + private static void DataModified(object sender, ExecutedRoutedEventArgs e) + { + Main main = (Main)sender; + BaseObject entity = (e.OriginalSource as FrameworkElement).DataContext as BaseObject; + } + + + + private static void CopyUserName(object sender, ExecutedRoutedEventArgs e) + { + Main main = (Main)sender; + //main.IsModified ^= true; + + } + + private static void SelectItem(object sender, ExecutedRoutedEventArgs e) + { + RibbonToggleButton btn = e.OriginalSource as RibbonToggleButton; + if (btn != null) + { + if (!btn.IsChecked.Value) btn = null; + Main main = (Main)sender; + main.SelectedPasswordField = btn != null ? btn.DataContext as Field : null; + } + } + + private static void SelectTemplateItem(object sender, ExecutedRoutedEventArgs e) + { + RibbonToggleButton btn = e.OriginalSource as RibbonToggleButton; + if (btn != null) + { + if (!btn.IsChecked.Value) btn = null; + Main main = (Main)sender; + main.SelectedTemplateField = btn != null ? btn.DataContext as TemplateField : null; + } + } + private static void NewPassword(object sender, ExecutedRoutedEventArgs e) + { + Main main = (Main)sender; + FrameworkElement fe = e.OriginalSource as FrameworkElement; + Category category = fe != null ? fe.DataContext as Category : null; + if (category != null) + { + main.NewPassword(category); + } + } + + private static void IsFolderSelected(object sender, CanExecuteRoutedEventArgs e) + { + Main main = (Main)sender; + e.CanExecute = main.SelectedFolder != null; + } + + private static void IsPasswordSelected(object sender, CanExecuteRoutedEventArgs e) + { + Main main = (Main)sender; + e.CanExecute = main.SelectedPassword != null; + } + + private static void IsFieldSelected(object sender, CanExecuteRoutedEventArgs e) + { + Main main = (Main)sender; + e.CanExecute = main.SelectedPasswordField != null; + } + + private static void IsCategorySelected(object sender, CanExecuteRoutedEventArgs e) + { + Main main = (Main)sender; + e.CanExecute = main.SelectedCategory != null; + } + + private static void IsPasswordModified(object sender, CanExecuteRoutedEventArgs e) + { + Main main = (Main)sender; + e.CanExecute = main.IsPasswordModified; + } + + private static void Test(object sender, ExecutedRoutedEventArgs e) + { + Main main = (Main)sender; + } + + private static void ChangePassword(object sender, ExecutedRoutedEventArgs e) + { + Main main = (Main)sender; + main.DisplayType = DisplayType.ChangePassword; + } + + + private static void Duplicate(object sender, ExecutedRoutedEventArgs e) + { + Main main = (Main)sender; + } + + private static void SavePassword(object sender, ExecutedRoutedEventArgs e) + { + + Main main = (Main)sender; + Password password = main.SelectedPassword; + if (password != null) password.Save(); + + } + + private static void UndoPassword(object sender, ExecutedRoutedEventArgs e) + { + Main main = (Main)sender; + Password password = main.SelectedPassword; + if (password != null) + { + main.SelectedPasswordField = null; + password.Undo(); + } + } + + private static void SaveTemplate(object sender, ExecutedRoutedEventArgs e) + { + + Main main = (Main)sender; + Category category = main.SelectedCategory; + if (category != null) category.SaveTemplate(); + + } + + private static void UndoTemplate(object sender, ExecutedRoutedEventArgs e) + { + Main main = (Main)sender; + Category category = main.SelectedCategory; + if (category != null) + { + main.SelectedPasswordField = null; + category.UndoTemplate(); + } + } + + private static void IsTemplateModified(object sender, CanExecuteRoutedEventArgs e) + { + Main main = (Main)sender; + + e.CanExecute = main.IsTemplateModified; + } + + private static void IsAnyPasswordOrTemplateModified(object sender, CanExecuteRoutedEventArgs e) + { + Main main = (Main)sender; + + e.CanExecute = main.IsModified; + } + + private static void DeletePassword(object sender, ExecutedRoutedEventArgs e) + { + Main main = (Main)sender; + Password password = main.SelectedPassword; + if (password != null) password.Delete(); + + } + } +} diff --git a/PasswordSafe/PasswordSafe/Controls/EditLabel.cs b/PasswordSafe/PasswordSafe/Controls/EditLabel.cs new file mode 100644 index 0000000..b175eee --- /dev/null +++ b/PasswordSafe/PasswordSafe/Controls/EditLabel.cs @@ -0,0 +1,179 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using System.Windows.Controls.Primitives; +using System.Diagnostics; +using System.Windows.Threading; +using System.Threading; + +namespace PasswordSafe.Controls +{ + public class EditLabel : Control + { + public static readonly RoutedCommand EditCommand = new RoutedCommand("Edit", typeof(EditLabel), + new InputGestureCollection(new KeyGesture[] { new KeyGesture(Key.F2, ModifierKeys.None, "F2") })); + + static EditLabel() + { + CommandManager.RegisterClassCommandBinding(typeof(MenuItem), new CommandBinding(EditCommand, OnEditLabel)); + DefaultStyleKeyProperty.OverrideMetadata(typeof(EditLabel), new FrameworkPropertyMetadata(typeof(EditLabel))); + } + + public EditLabel() + : base() + { + } + + + /// + /// This is a very special command execution that expects the sender to be a MenuItem that is a direct child of ContextMenu which again + /// belongs to an EditLabel. + /// + private static void OnEditLabel(object sender, ExecutedRoutedEventArgs e) + { + MenuItem item = sender as MenuItem; + if (item != null) + { + + EditLabel label = (item.Parent as ContextMenu).PlacementTarget as EditLabel; + if (label != null) + { + label.EditMode = true; + label.SelectAll(); + } + } + } + + public void SelectAll() + { + TextBox.Focus(); + TextBox.SelectAll(); + } + + + public object Text + { + get { return (object)GetValue(TextProperty); } + set { SetValue(TextProperty, value); } + } + + public static readonly DependencyProperty TextProperty = + DependencyProperty.Register("Text", typeof(object), typeof(EditLabel), new UIPropertyMetadata(null)); + + + + + public bool EditMode + { + get { return (bool)GetValue(EditModeProperty); } + set { SetValue(EditModeProperty, value); } + } + + public static readonly DependencyProperty EditModeProperty = + DependencyProperty.Register("EditMode", typeof(bool), typeof(EditLabel), new FrameworkPropertyMetadata(false, OnEditModePropertyChanged)); + + + private static void OnEditModePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + bool newValue = (bool)e.NewValue; + if (newValue) (d as EditLabel).FocusTextBox(); else (d as EditLabel).UnfocusTextBox(); + } + + private void UnfocusTextBox() + { + } + + private void FocusTextBox() + { + TextBox.Focus(); + TextBox.SelectAll(); + TextBox.Focus(); + } + + public override void OnApplyTemplate() + { + TextBox.LostFocus += new RoutedEventHandler(TextBox_LostFocus); + base.OnApplyTemplate(); + } + + void TextBox_LostFocus(object sender, RoutedEventArgs e) + { + EditMode = false; + } + + protected override void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e) + { + EditMode = false; + base.OnLostKeyboardFocus(e); + } + + protected override void OnLostFocus(RoutedEventArgs e) + { + base.OnLostFocus(e); + EditMode = false; + } + + + private TextBox TextBox { get { return GetTemplateChild("PART_TextBox") as TextBox; } } + + + + protected override void OnMouseDoubleClick(MouseButtonEventArgs e) + { + EditMode = true; + e.Handled = true; + base.OnMouseDoubleClick(e); + TextBox.Focus(); + TextBox.SelectAll(); + + } + + protected override void OnMouseDown(MouseButtonEventArgs e) + { + // if (EditMode) + { + base.OnMouseDown(e); + } + } + + + + protected override void OnKeyDown(KeyEventArgs e) + { + switch (e.Key) + { + case Key.F2: + if (!EditMode) + { + e.Handled = true; + EditMode = true; + // FocusTextBox(); + } + break; + + case Key.Return: + EditMode = false; + e.Handled = true; + break; + + case Key.Escape: + TextBox.Undo(); + EditMode = false; + e.Handled = true; + break; + } + base.OnKeyDown(e); + } + + } +} diff --git a/PasswordSafe/PasswordSafe/Converter/CopyFieldConverter.cs b/PasswordSafe/PasswordSafe/Converter/CopyFieldConverter.cs new file mode 100644 index 0000000..2a7b25d --- /dev/null +++ b/PasswordSafe/PasswordSafe/Converter/CopyFieldConverter.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Data; +using PasswordSafe.Data.Biz; + +namespace PasswordSafe.Converter +{ + public class CopyFieldConverter:IValueConverter + { + #region IValueConverter Members + + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + IEnumerable fields = value as IEnumerable; + if (fields == null) return Binding.DoNothing; + + return fields.Where(f => f.Type != FieldType.Separator); + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + + #endregion + } +} diff --git a/PasswordSafe/PasswordSafe/Converter/CountConverter.cs b/PasswordSafe/PasswordSafe/Converter/CountConverter.cs new file mode 100644 index 0000000..35a0812 --- /dev/null +++ b/PasswordSafe/PasswordSafe/Converter/CountConverter.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Data; + +namespace PasswordSafe.Converter +{ + public class CountConverter:IValueConverter + { + #region IValueConverter Members + + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + int count = value is int ? (int)value : 0; + return count > 0 ? string.Format("({0})", count) : ""; + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + + #endregion + } +} diff --git a/PasswordSafe/PasswordSafe/Converter/DateConverter.cs b/PasswordSafe/PasswordSafe/Converter/DateConverter.cs new file mode 100644 index 0000000..cf44f6f --- /dev/null +++ b/PasswordSafe/PasswordSafe/Converter/DateConverter.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Data; + +namespace PasswordSafe.Converter +{ + public class DateConverter:IValueConverter + { + #region IValueConverter Members + + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + if (value is DateTime) + { + DateTime date = (DateTime)value; + return date.ToShortDateString(); + } + else return ""; + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + DateTime date; + if (DateTime.TryParse(value as string,out date)) return date; else return null; + } + + #endregion + } +} diff --git a/PasswordSafe/PasswordSafe/Converter/DisplayTypeToVisibleConverter.cs b/PasswordSafe/PasswordSafe/Converter/DisplayTypeToVisibleConverter.cs new file mode 100644 index 0000000..778ff2d --- /dev/null +++ b/PasswordSafe/PasswordSafe/Converter/DisplayTypeToVisibleConverter.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Data; +using PasswordSafe.Controls; +using System.Windows; + +namespace PasswordSafe.Converter +{ + public class DisplayTypeToVisibleConverter:IValueConverter + { + #region IValueConverter Members + + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + DisplayType desiredType = (DisplayType)Enum.Parse(typeof(DisplayType), parameter.ToString()); + DisplayType type = (DisplayType)value; + + return type == desiredType ? Visibility.Visible : Visibility.Hidden; + + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + + #endregion + } +} diff --git a/PasswordSafe/PasswordSafe/Converter/FavoriteImageConverter.cs b/PasswordSafe/PasswordSafe/Converter/FavoriteImageConverter.cs new file mode 100644 index 0000000..f8737b3 --- /dev/null +++ b/PasswordSafe/PasswordSafe/Converter/FavoriteImageConverter.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Data; +using System.Windows.Media.Imaging; +using PasswordSafe.Classes; +using System.Windows; +using PasswordSafe.Data.Biz; + +namespace PasswordSafe.Converter +{ + /// + /// A Value Converter that expects a FrameworkElement as Value that has a DataContext of Password. + /// It returns an ImageSource that displays the state of Password.IsFavorite. + /// + public class FavoriteImageConverter:IValueConverter + { + #region IValueConverter Members + + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + //Password password = value as Password; + //bool isFavorite = password != null && password.IsFavorite; + + bool isFavorite = (value!=null) && (bool)value; + + int size = (parameter == null) ? 32 : int.Parse(parameter as string); + + string name = isFavorite ? "Favorites_" : "NoFaves"; + return new BitmapImage(new Uri(string.Format("pack://application:,,,/img/{0}{1}.png", name, size))); + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + + #endregion + } +} diff --git a/PasswordSafe/PasswordSafe/Converter/FieldTypeConverter.cs b/PasswordSafe/PasswordSafe/Converter/FieldTypeConverter.cs new file mode 100644 index 0000000..b6ade87 --- /dev/null +++ b/PasswordSafe/PasswordSafe/Converter/FieldTypeConverter.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Data; + +namespace PasswordSafe.Converter +{ + public class FieldTypeConverter:IValueConverter + { + #region IValueConverter Members + + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + return Binding.DoNothing; + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + + #endregion + } +} diff --git a/PasswordSafe/PasswordSafe/Converter/FieldValidatonRule.cs b/PasswordSafe/PasswordSafe/Converter/FieldValidatonRule.cs new file mode 100644 index 0000000..2ca191c --- /dev/null +++ b/PasswordSafe/PasswordSafe/Converter/FieldValidatonRule.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Data; +using System.Windows.Controls; +using PasswordSafe.Data.Biz; + +namespace PasswordSafe.Converter +{ + public class FieldValidationRule : ValidationRule + { + private FieldType type = FieldType.Text; + + public FieldType Type { get { return type; } set { type = value; } } + + + public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo) + { + if (value is string) + { + switch (Type) + { + case FieldType.Date: + DateTime dt; + if (!DateTime.TryParse(value as string, out dt) || dt.Hour != 0 || dt.Minute != 0) return new ValidationResult(false, "Invalid time format."); + break; + + case FieldType.Time: + if (!DateTime.TryParse(value as string, out dt)) return new ValidationResult(false, "Invalid date format."); + break; + + case FieldType.Int: + int ival; + if (!(int.TryParse(value as string, out ival))) return new ValidationResult(false, "Invalid integer format."); + break; + } + + } + return new ValidationResult(true, null); + } + } +} diff --git a/PasswordSafe/PasswordSafe/Converter/ImageConverter.cs b/PasswordSafe/PasswordSafe/Converter/ImageConverter.cs new file mode 100644 index 0000000..fa97d9d --- /dev/null +++ b/PasswordSafe/PasswordSafe/Converter/ImageConverter.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Data; +using System.Windows.Media.Imaging; +using PasswordSafe.Data.Biz; + +namespace PasswordSafe.Converter +{ + /// + /// Gets the Images for the breadcrumbs from a Node. + /// + public class ImageConverter:IValueConverter + { + #region IValueConverter Members + + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + string image = "pack://application:,,,/img/category_16.png"; + CustomNode node = value as CustomNode; + + if (node != null) image = "pack://application:,,,/img/home_16.png"; + else + { + Category category = value as Category; + Folder folder = value as Folder; + + if (category != null ) image = "pack://application:,,,/img/category_16.png"; + else if (folder != null) image = "pack://application:,,,/img/faves16.png"; + } + return new BitmapImage(new Uri(image)); + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + + #endregion + } +} diff --git a/PasswordSafe/PasswordSafe/Converter/IntConverter.cs b/PasswordSafe/PasswordSafe/Converter/IntConverter.cs new file mode 100644 index 0000000..8cc244a --- /dev/null +++ b/PasswordSafe/PasswordSafe/Converter/IntConverter.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Data; +using System.Windows.Controls; + +namespace PasswordSafe.Converter +{ + public class IntConverter:IValueConverter + { + #region IValueConverter Members + + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + int ival = (int)value; + return ival.ToString(); + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + int ival; + if (!int.TryParse(value as string, out ival)) ival = default(int); + return ival; + } + + #endregion + } + public class Rule : ValidationRule + { + public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo) + { + //xx + throw new NotImplementedException(); + } + } +} diff --git a/PasswordSafe/PasswordSafe/Converter/IsModifiedFromUIPasswordConverter.cs b/PasswordSafe/PasswordSafe/Converter/IsModifiedFromUIPasswordConverter.cs new file mode 100644 index 0000000..2db3c4d --- /dev/null +++ b/PasswordSafe/PasswordSafe/Converter/IsModifiedFromUIPasswordConverter.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Data; +using PasswordSafe.Classes; +using PasswordSafe.Data.Biz; + +namespace PasswordSafe.Converter +{ + public class IsModifiedFromUIPasswordConverter:IValueConverter + { + #region IValueConverter Members + + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + Password password = value as Password; + if (password != null) + { + return password.IsModified; + } + else return Binding.DoNothing; + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + + #endregion + } +} diff --git a/PasswordSafe/PasswordSafe/Converter/NullToBoolConverter.cs b/PasswordSafe/PasswordSafe/Converter/NullToBoolConverter.cs new file mode 100644 index 0000000..479190e --- /dev/null +++ b/PasswordSafe/PasswordSafe/Converter/NullToBoolConverter.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Data; +using PasswordSafe.Data.Biz; + +namespace PasswordSafe.Converter +{ + class NullToBoolConverter : IValueConverter + { + #region IValueConverter Members + + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + if (value == null) return false; + if ("HasLines".Equals(parameter)) return (value as Field).HasLines; + if ("HasRange".Equals(parameter)) return (value as Field).HasRange; + return true; + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + + #endregion + } +} diff --git a/PasswordSafe/PasswordSafe/Converter/NullToVisibleConverter.cs b/PasswordSafe/PasswordSafe/Converter/NullToVisibleConverter.cs new file mode 100644 index 0000000..4ad5cea --- /dev/null +++ b/PasswordSafe/PasswordSafe/Converter/NullToVisibleConverter.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Data; +using System.Windows; + +namespace PasswordSafe.Controls +{ + [ValueConversion(typeof(object), typeof(Visibility))] + class NullToVisibleConverter : IValueConverter + { + #region IValueConverter Members + + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + return value != null ? Visibility.Visible : Visibility.Hidden; + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + + #endregion + } +} diff --git a/PasswordSafe/PasswordSafe/Converter/TimeConverter.cs b/PasswordSafe/PasswordSafe/Converter/TimeConverter.cs new file mode 100644 index 0000000..d66aa51 --- /dev/null +++ b/PasswordSafe/PasswordSafe/Converter/TimeConverter.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Data; + +namespace PasswordSafe.Converter +{ + public class TimeConverter:IValueConverter + { + #region IValueConverter Members + + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + if (value is DateTime) + { + DateTime time = (DateTime)value; + return time.ToLongTimeString(); + } + else return ""; + + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + DateTime date; + if (DateTime.TryParse(value as string, out date)) return date; else return null; + } + + #endregion + } +} diff --git a/PasswordSafe/PasswordSafe/Converter/VisibilityConverter.cs b/PasswordSafe/PasswordSafe/Converter/VisibilityConverter.cs new file mode 100644 index 0000000..219b0eb --- /dev/null +++ b/PasswordSafe/PasswordSafe/Converter/VisibilityConverter.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Data; +using System.Windows; + +namespace PasswordSafe.Controls +{ + public class VisibilityConverter : IValueConverter + { + #region IValueConverter Members + + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + bool idNegative = parameter != null && parameter.Equals("-"); + bool bit = (bool)value; + if (idNegative) bit = !bit; + + return bit ? Visibility.Visible : Visibility.Hidden; + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + + #endregion + } +} diff --git a/PasswordSafe/PasswordSafe/DataTemplateSelectors/BreadcrumbItemSelector.cs b/PasswordSafe/PasswordSafe/DataTemplateSelectors/BreadcrumbItemSelector.cs new file mode 100644 index 0000000..513b2df --- /dev/null +++ b/PasswordSafe/PasswordSafe/DataTemplateSelectors/BreadcrumbItemSelector.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Controls; +using PasswordSafe.Classes; +using System.Windows; +using System.ComponentModel; +using PasswordSafe.Data.Biz; + +namespace PasswordSafe.DataTemplateSelectors +{ + public class BreadcrumbItemSelector : DataTemplateSelector + { + public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container) + { + if (!DesignerProperties.GetIsInDesignMode(container)) + { + Window window = Application.Current.MainWindow; + + CustomNode node = item as CustomNode; + if (node != null) return window.FindResource("bcBreadcrumbItemTemplate") as DataTemplate; + + Category category = item as Category; + if (category != null) return window.FindResource("bcCategoryItemTemplate") as DataTemplate; + + Folder folder = item as Folder; + if (folder != null) return window.FindResource("bcFolderItemTemplate") as DataTemplate; + } + + + return base.SelectTemplate(item, container); + } + } +} diff --git a/PasswordSafe/PasswordSafe/DataTemplateSelectors/CopyPasswordSelector.cs b/PasswordSafe/PasswordSafe/DataTemplateSelectors/CopyPasswordSelector.cs new file mode 100644 index 0000000..bc8fb6f --- /dev/null +++ b/PasswordSafe/PasswordSafe/DataTemplateSelectors/CopyPasswordSelector.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Controls; +using System.Windows; +using PasswordSafe.Classes; +using PasswordSafe.Data.Biz; + +namespace PasswordSafe.DataTemplateSelectors +{ + public class CopyPasswordSelector:DataTemplateSelector + { + public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container) + { + Field field = item as Field; + Window window = Application.Current.MainWindow; + if (field != null) + { + switch (field.Type) + { + case FieldType.Separator: + return window.FindResource("DropDownPasswordSeparatorFieldTemplate") as DataTemplate; + } + } + return base.SelectTemplate(item, container); + } + } +} diff --git a/PasswordSafe/PasswordSafe/DataTemplateSelectors/FieldSelector.cs b/PasswordSafe/PasswordSafe/DataTemplateSelectors/FieldSelector.cs new file mode 100644 index 0000000..bd0404c --- /dev/null +++ b/PasswordSafe/PasswordSafe/DataTemplateSelectors/FieldSelector.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Controls; +using System.Windows; +using PasswordSafe.Classes; +using PasswordSafe.Data.Biz; + +namespace PasswordSafe.DataTemplateSelectors +{ + public class FieldSelector : DataTemplateSelector + { + public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container) + { + Field field = item as Field; + Window window = Application.Current.MainWindow; + if (field != null && field.Type != FieldType.Unknown) + { + switch (field.Type) + { + case FieldType.Bool: + return window.FindResource("BoolFieldTemplate") as DataTemplate; + + case FieldType.Int: + return window.FindResource("IntFieldTemplate") as DataTemplate; + + case FieldType.Date: + return window.FindResource("DateFieldTemplate") as DataTemplate; + + case FieldType.Time: + return window.FindResource("TimeFieldTemplate") as DataTemplate; + + case FieldType.Memo: + return window.FindResource("MemoFieldTemplate") as DataTemplate; + + case FieldType.Separator: + return window.FindResource("SeparatorFieldTemplate") as DataTemplate; + + default: + return window.FindResource("PasswordFieldTemplate") as DataTemplate; + } + } + return base.SelectTemplate(item, container); + } + } +} diff --git a/PasswordSafe/PasswordSafe/DataTemplateSelectors/TemplateFieldSelector.cs b/PasswordSafe/PasswordSafe/DataTemplateSelectors/TemplateFieldSelector.cs new file mode 100644 index 0000000..63a8298 --- /dev/null +++ b/PasswordSafe/PasswordSafe/DataTemplateSelectors/TemplateFieldSelector.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Controls; +using PasswordSafe.Data.Biz; +using System.Windows; + +namespace PasswordSafe.DataTemplateSelectors +{ + public class TemplateFieldSelector : DataTemplateSelector + { + public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container) + { + TemplateField field = item as TemplateField; + Window window = Application.Current.MainWindow; + if (field != null && field.Type != FieldType.Unknown) + { + switch (field.Type) + { + case FieldType.Bool: + return window.FindResource("BoolFieldTempTemplate") as DataTemplate; + + case FieldType.Int: + return window.FindResource("IntFieldTempTemplate") as DataTemplate; + + case FieldType.Time: + case FieldType.Date: + return window.FindResource("DateFieldTempTemplate") as DataTemplate; + + case FieldType.Memo: + return window.FindResource("MemoFieldTempTemplate") as DataTemplate; + + case FieldType.Separator: + return window.FindResource("SeparatorFieldTempTemplate") as DataTemplate; + + default: + return window.FindResource("PasswordFieldTempTemplate") as DataTemplate; + } + } + return base.SelectTemplate(item, container); + } + } +} diff --git a/PasswordSafe/PasswordSafe/DataTemplates.xaml b/PasswordSafe/PasswordSafe/DataTemplates.xaml new file mode 100644 index 0000000..780965b --- /dev/null +++ b/PasswordSafe/PasswordSafe/DataTemplates.xaml @@ -0,0 +1,220 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PasswordSafe/PasswordSafe/Enums/DisplayMode.cs b/PasswordSafe/PasswordSafe/Enums/DisplayMode.cs new file mode 100644 index 0000000..e81efcb --- /dev/null +++ b/PasswordSafe/PasswordSafe/Enums/DisplayMode.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace PasswordSafe.Controls +{ + public enum DisplayMode + { + Passwords=0, + Templates=1 + } +} diff --git a/PasswordSafe/PasswordSafe/Enums/DisplayType.cs b/PasswordSafe/PasswordSafe/Enums/DisplayType.cs new file mode 100644 index 0000000..773e587 --- /dev/null +++ b/PasswordSafe/PasswordSafe/Enums/DisplayType.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace PasswordSafe.Controls +{ + public enum DisplayType + { + /// + /// Show the login screen + /// + Login, + + /// + /// Show the change password screen + /// + ChangePassword, + + /// + /// Show the passwords + /// + Passwords + } +} diff --git a/PasswordSafe/PasswordSafe/Export/XmlExporter.cs b/PasswordSafe/PasswordSafe/Export/XmlExporter.cs new file mode 100644 index 0000000..c57239a --- /dev/null +++ b/PasswordSafe/PasswordSafe/Export/XmlExporter.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using PasswordSafe.Data.Biz; +using System.Xml.Serialization; +using System.IO; + +namespace PasswordSafe.Export +{ + static class XmlExporter + { + + public static string Export() + { + XmlSerializer serializer = new XmlSerializer(typeof(PasswordExport)); + + PasswordExport data = new PasswordExport { Passwords = GetPasswords() }; + using (StringWriter writer = new StringWriter()) + { + serializer.Serialize(writer, data); + writer.Flush(); + string s = writer.ToString(); + return s; + } + } + + public static void Export(Stream stream) + { + XmlSerializer serializer = new XmlSerializer(typeof(PasswordExport)); + + PasswordExport data = new PasswordExport { Passwords = GetPasswords() }; + serializer.Serialize(stream, data); + } + + + private static XmlPassword[] GetPasswords() + { + BizContext context = BizContext.Instance; + + return context.Categories.FirstOrDefault().NestedPasswords.Select(p => new XmlPassword + { + Name = p.Name, + Category = p.Category.GetPath(2), + Fields = p.Fields.Where(f => f.Type != FieldType.Separator).Select(f => new XmlField { Name = f.Name, Value = f.StringValue, Type = f.Type.ToString() }).ToArray() + }).ToArray(); + } + + + } + + [XmlRoot(ElementName = "PasswordSafe")] + public class PasswordExport + { + [XmlArrayItem("Password")] + public XmlPassword[] Passwords { get; set; } + } +} diff --git a/PasswordSafe/PasswordSafe/Export/XmlField.cs b/PasswordSafe/PasswordSafe/Export/XmlField.cs new file mode 100644 index 0000000..3717a77 --- /dev/null +++ b/PasswordSafe/PasswordSafe/Export/XmlField.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Serialization; + +namespace PasswordSafe.Export +{ + [XmlRoot(ElementName="Field")] + public class XmlField + { + [XmlAttribute] + public string Name { get; set; } + + [XmlText] + public string Value { get; set; } + + [XmlAttribute] + public string Type { get; set; } + } +} diff --git a/PasswordSafe/PasswordSafe/Export/XmlPassword.cs b/PasswordSafe/PasswordSafe/Export/XmlPassword.cs new file mode 100644 index 0000000..d8c155a --- /dev/null +++ b/PasswordSafe/PasswordSafe/Export/XmlPassword.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Serialization; + +namespace PasswordSafe.Export +{ + [XmlRoot(ElementName="Password")] + public class XmlPassword + { + [XmlAttribute] + public string Name { get; set; } + + [XmlAttribute] + public string Category { get; set; } + + [XmlArrayItem("Field")] + public XmlField[] Fields { get; set; } + + } +} diff --git a/PasswordSafe/PasswordSafe/FieldTemplates.xaml b/PasswordSafe/PasswordSafe/FieldTemplates.xaml new file mode 100644 index 0000000..b7090d4 --- /dev/null +++ b/PasswordSafe/PasswordSafe/FieldTemplates.xaml @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PasswordSafe/PasswordSafe/Main.xaml b/PasswordSafe/PasswordSafe/Main.xaml new file mode 100644 index 0000000..9331a91 --- /dev/null +++ b/PasswordSafe/PasswordSafe/Main.xaml @@ -0,0 +1,552 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Exports Passwords to an unencrypted xml file. + + + + + + Exit PasswordSafe + + + + + + + + + + + + + + Save + Save + Print + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PasswordSafe/PasswordSafe/Main.xaml.cs b/PasswordSafe/PasswordSafe/Main.xaml.cs new file mode 100644 index 0000000..b816442 --- /dev/null +++ b/PasswordSafe/PasswordSafe/Main.xaml.cs @@ -0,0 +1,939 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using Odyssey.Controls; +using System.Diagnostics; +using Odyssey.Controls.Classes; +using PasswordSafe.Tools; +using System.Threading; +using PasswordSafe.Data; +using PasswordSafe.Properties; +using System.Windows.Media.Effects; +using Odyssey.Ribbon.EventArgs; +using PasswordSafe.Classes; +using PasswordSafe.Data.Biz; +using PasswordSafe.Converter; +using System.Collections.ObjectModel; +using System.Data.SqlClient; +using System.Reflection; +using System.Data.SqlServerCe; +using System.Globalization; + +namespace PasswordSafe.Controls +{ + /// + /// Interaction logic for Window1.xaml + /// + public partial class Main : RibbonWindow + { + public Main() + { + //BizContext.ConnectionString = Settings.Default.PasswordsConnectionString; + InitializeComponent(); + LoadSettings(); + ribbon.ApplicationMenu.Opened += new EventHandler(OnMenuOpened); + IsModified = true; + IsModified = false; + + } + + /// + /// Get all categories when the menu is opened to select one for a new password: + /// + void OnMenuOpened(object sender, EventArgs e) + { + } + + static Main() + { + // Register a static MouseDownEvent for a TreeViewItem to enable select with a mouse-right-click: + EventManager.RegisterClassHandler(typeof(TreeViewItem), + Mouse.MouseDownEvent, new MouseButtonEventHandler(OnTreeViewItemMouseDown), false); + } + + private static void OnTreeViewItemMouseDown(object sender, MouseButtonEventArgs e) + { + var item = sender as TreeViewItem; + if (e.RightButton == MouseButtonState.Pressed) + { + item.IsSelected = true; + e.Handled = true; + } + } + + + private void LoadData(string connectionString) + { + BizContext.OpenConnection(connectionString); + RefreshData(); + InitialShow(); + connectionString = ""; + SelectedNode = BizContext.Instance.RootCategory; + } + + private void UnloadData() + { + BizContext.CloseConnection(); + RefreshData(); + } + + private void RefreshData() + { + UIContext context = new UIContext(); + PasswordGrid.BreadcrumbRoot = context.GetBreadcrumbRoot(); + + TemplateGrid.BreadcrumbRoot = + PasswordGrid.RootCategory = context.GetCategoryRoot(); + + foreach (string res in new string[] { + "fieldTypesData" + ,"categoryData" + ,"folderData" + }) + { + ObjectDataProvider provider = Resources[res] as ObjectDataProvider; + if (provider != null) + { + provider.InitialLoad(); + provider.Refresh(); + } + } + + } + + private void LoadSettings() + { + Settings settings = Settings.Default; + outlook.MaxNumberOfButtons = settings.MaxSections; + outlook.IsMaximized = settings.IsSidebarExpanded; + ribbon.CanMinimize = settings.CanRibbonMinimize; + this.PasswordGrid.PasswordColumn.Width = new GridLength(settings.PasswordWith); + ribbon.IsExpanded = settings.IsRibbonExpanded && ribbon.CanMinimize; + if (settings.Left != -1) Left = settings.Left; + if (settings.Top != -1) Top = settings.Top; + if (settings.Width > 48) Width = settings.Width; + if (settings.Height > 32) Height = settings.Height; + // IsGlassEnabled = settings.IsGlassEnabled; + Skin = settings.Skin; + } + + private void SaveSettings() + { + Settings settings = Settings.Default; + settings.IsSidebarExpanded = outlook.IsMaximized; + settings.IsRibbonExpanded = ribbon.IsExpanded; + settings.CanRibbonMinimize = ribbon.CanMinimize; + Rect bounds = RestoreBounds; + settings.Left = bounds.Left; + settings.Top = bounds.Top; + settings.Width = bounds.Width; + settings.Height = bounds.Height; + settings.WindowState = WindowState; + settings.Skin = Skin; + settings.MaxSections = outlook.MaxNumberOfButtons; + settings.IsGlassEnabled = IsGlassEnabled; + settings.PasswordWith = this.PasswordGrid.PasswordColumn.ActualWidth; + settings.Save(); + } + + + + protected override void OnContentRendered(EventArgs e) + { + WindowState = Settings.Default.WindowState; + IsGlassEnabled = Settings.Default.IsGlassEnabled; + + base.OnContentRendered(e); + lockScreen.FocusPassword(); + } + + private void InitialShow() + { + TreeViewItem item = this.categoriesTree.FirstItem(); + if (item != null) + { + item.IsSelected = true; + item.IsExpanded = true; + } + + item = this.foldersTreeView.FirstItem(); + if (item != null) + { + item.IsExpanded = true; + } + SelectedCategory = BizContext.Instance.RootCategory; + } + + protected override void OnClosing(System.ComponentModel.CancelEventArgs e) + { + BizContext.Instance.Save(); + SaveSettings(); + this.ribbon.Focus(); + BizContext.Instance.Commit(); + base.OnClosing(e); + } + + +#if false + private void InitializeDataAsync() + { + var ctx = SynchronizationContext.Current; + ThreadPool.QueueUserWorkItem(state => + { + // this runs in a separate thread: + var bcRoot = Domain.Nodes; + var categories = new CategoryBase[] { Domain.RootCategory }; + var rootFolders = new NodeBase[] { Domain.RootFolder }; + var rootCategory = Domain.RootCategory; + + ctx.Post(ctxState => + { + // this runs in the application thread: + categoriesTree.SelectFirst(); + }, null); + }); + } +#endif + + protected void OnSelectedCategoryChanged(object sender, RoutedPropertyChangedEventArgs e) + { + Category c = e.NewValue as Category; + SelectedCategory = c; + } + + protected void OnSelectedFolderChanged(object sender, RoutedPropertyChangedEventArgs e) + { + Folder folder = e.NewValue as Folder; + SelectedFolder = folder; + } + + + + /// + /// Occurs when the Category TreeView got the focus. + /// + private void OnCategoriesGotFocus(object sender, RoutedEventArgs e) + { + if (this.categoriesTree.SelectedItem == null) categoriesTree.Select(new UIContext().RootCategory); + ChangedTabsVisibility(); + if (ribbon.SelectedTabItem == folderTab) ribbon.SelectedTabItem = categoryTab; + } + + /// + /// Occurs when the Favorites TreeView got the focus. + /// + private void OnFavoritesGotFocus(object sender, RoutedEventArgs e) + { + if (this.foldersTreeView.SelectedItem == null) foldersTreeView.Select(new UIContext().RootFolder); + ChangedTabsVisibility(); + if (ribbon.SelectedTabItem == categoryTab) ribbon.SelectedTabItem = folderTab; + } + + private void ChangedTabsVisibility() + { + if (ribbon.ContextualTabSet != SidebarTabs) + { + ribbon.ContextualTabSet = null; + ribbon.ContextualTabSet = SidebarTabs; + } + } + + + /// + /// Occurs when the Password Area (Passwords List, Password Details, Search, Breadcrumb,etc.) got the focus. + /// + private void OnPasswordsGotFocus(object sender, RoutedEventArgs e) + { + ribbon.ContextualTabSet = null; + } + + + /// + /// Gets whether the currently selected password has been modified. + /// + public bool IsTemplateModified + { + get { return (bool)GetValue(IsTemplateModifiedProperty); } + private set { SetValue(IsTemplateModifiedPropertyKey, value); } + } + + public static readonly DependencyPropertyKey IsTemplateModifiedPropertyKey = + DependencyProperty.RegisterReadOnly("IsTemplateModified", typeof(bool), typeof(Main), new UIPropertyMetadata(false, TemplateModifiedPropertyChanged)); + + private static readonly DependencyProperty IsTemplateModifiedProperty = IsTemplateModifiedPropertyKey.DependencyProperty; + + private static void TemplateModifiedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((Main)d).TemplateModified((bool)e.NewValue); + } + + protected virtual void TemplateModified(bool newValue) + { + CheckIsModified(newValue); + } + + private void CheckIsModified(bool value) + { + if (value) + { + IsModified = true; + } + else + { + BizContext context = BizContext.Instance; + Category root = context.Categories.FirstOrDefault(); + if (root != null) + { + if (root.NestedPasswords.Any(p => p.IsModified)) + { + IsModified = true; + return; + } + if (IsNestedTemplateModified(root)) + { + IsModified = true; + return; + } + } + IsModified = false; + } + } + + private bool IsNestedTemplateModified(Category category) + { + if (category.IsModified) return true; + return category.Categories.Any(c => IsNestedTemplateModified(c)); + } + + /// + /// Gets or sets whether either a template or password is modified. + /// + public bool IsModified + { + get { return (bool)GetValue(IsModifiedProperty); } + set { SetValue(IsModifiedProperty, value); } + } + + public static readonly DependencyProperty IsModifiedProperty = + DependencyProperty.Register("IsModified", typeof(bool), typeof(Main), new UIPropertyMetadata(false, ModifiedPropertyChanged)); + + private static void ModifiedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((Main)d).ModifiedChanged((bool)e.NewValue); + } + + protected virtual void ModifiedChanged(bool newValue) + { + UpdateMenuItemEnabledStates(newValue); + } + + //TODO: + /// + /// There is currently a bug that Command Bindings to MenuItems in the RibbonApplicatioMenu do not update the IsEnabled state, so this is done handcrafted: + /// + private void UpdateMenuItemEnabledStates(bool isEnabled) + { + SaveAllMenuItem.IsEnabled = isEnabled; + UndoAllMenuItem.IsEnabled = isEnabled; + } + + + + /// + /// Gets whether the currently selected password has been modified. + /// + public bool IsPasswordModified + { + get { return (bool)GetValue(IsPasswordModifiedProperty); } + private set { SetValue(IsPasswordModifiedPropertyKey, value); } + } + + public static readonly DependencyPropertyKey IsPasswordModifiedPropertyKey = + DependencyProperty.RegisterReadOnly("IsPasswordModified", typeof(bool), typeof(Main), new UIPropertyMetadata(false, PasswordModifiedPropertyChanged)); + + private static void PasswordModifiedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((Main)d).PasswordModified((bool)e.NewValue); + } + + protected virtual void PasswordModified(bool newValue) + { + CheckIsModified(newValue); + } + + private static readonly DependencyProperty IsPasswordModifiedProperty = IsPasswordModifiedPropertyKey.DependencyProperty; + + + + private void OnSelectedTabIndexChanged(object sender, RoutedPropertyChangedEventArgs e) + { + + RibbonTabItem selectedTab = ribbon.SelectedTabItem; + DisplayMode = selectedTab == categoryTab ? DisplayMode.Templates : DisplayMode.Passwords; + } + + + private void SelectCategoryTreeViewItemClicked(object sender, RoutedEventArgs e) + { + //ClickableTreeViewItem item = e.OriginalSource as ClickableTreeViewItem; + //WPFNode node = item.DataContext as WPFNode; + + //MessageBox.Show("Clicked: " + node.Path); + } + + + public DisplayMode DisplayMode + { + get { return (DisplayMode)GetValue(DisplayModeProperty); } + set { SetValue(DisplayModeProperty, value); } + } + + public static readonly DependencyProperty DisplayModeProperty = + DependencyProperty.Register("DisplayMode", typeof(DisplayMode), typeof(Main), + new FrameworkPropertyMetadata( + DisplayMode.Passwords, + FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, + DisplayModePropertyChanged + )); + + + private static void DisplayModePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((Main)d).OnDisplayModeChanged(d, (DisplayMode)e.OldValue, (DisplayMode)e.NewValue); + } + + protected virtual void OnDisplayModeChanged(object sende, DisplayMode oldMode, DisplayMode newMode) + { + } + + + + public int SectionIndex + { + get { return (int)GetValue(SectionIndexProperty); } + set { SetValue(SectionIndexProperty, value); } + } + + // Using a DependencyProperty as the backing store for SectionIndex. This enables animation, styling, binding, etc... + public static readonly DependencyProperty SectionIndexProperty = + DependencyProperty.Register("SectionIndex", typeof(int), typeof(Main), new FrameworkPropertyMetadata( + 0, + FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, + OnSectionIndexPropertyChanged + )); + + + private static void OnSectionIndexPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((Main)d).OnSectionIndexChanged((int)e.OldValue, (int)e.NewValue); + } + + protected virtual void OnSectionIndexChanged(int oldIndex, int newIndex) + { + ribbon.ContextualTabSet = null; + foreach (RibbonTabItem item in ribbon.Tabs) + { + item.Visibility = (item == TemplateTab) ^ (newIndex == 0) ? Visibility.Visible : Visibility.Collapsed; + } + ribbon.EnsureTabIsVisible(); + + PasswordGrid.Visibility = newIndex == 0 ? Visibility.Visible : Visibility.Hidden; + TemplateCategoryTab.Visibility = + TemplateGrid.Visibility = (newIndex == 1 ? Visibility.Visible : Visibility.Hidden); + + } + + public SkinId Skin + { + get { return (SkinId)GetValue(SkinProperty); } + set { SetValue(SkinProperty, value); } + } + + public static readonly DependencyProperty SkinProperty = + DependencyProperty.Register("Skin", typeof(SkinId), typeof(Main), new FrameworkPropertyMetadata(SkinId.OfficeBlue, SkinPropertyChanged)); + + + private static void SkinPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + SkinId id = (SkinId)e.NewValue; + SkinManager.SkinId = id; + Main main = (Main)o; + int index = SkinToId(id); + if (main.StyleGallery.SelectedIndex != index) + { + main.StyleGallery.SelectedIndex = index; + } + + } + + private static int SkinToId(SkinId id) + { + switch (id) + { + case SkinId.Vista: return 0; + case SkinId.Windows7: return 1; + case SkinId.OfficeBlue: return 2; + case SkinId.OfficeSilver: return 3; + case SkinId.OfficeBlack: return 4; + default: throw new ArgumentOutOfRangeException(); + } + } + + private void RibbonGallery_HotThumbnailChanged(object sender, RoutedEventArgs e) + { + RibbonGallery g = sender as RibbonGallery; + RibbonThumbnail thumb = g.HotThumbnail; + if (thumb != null) + { + SkinManager.SkinId = IdToSkin(g.Items.IndexOf(g.HotItem)); + } + else SkinManager.SkinId = Skin; + } + + private SkinId IdToSkin(int id) + { + + switch (id) + { + case 0: return SkinId.Vista; + case 1: return SkinId.Windows7; + case 2: return SkinId.OfficeBlue; + case 3: return SkinId.OfficeSilver; + case 4: return SkinId.OfficeBlack; + default: throw new ArgumentOutOfRangeException(); + } + } + + private void RibbonGallery_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + RibbonGallery g = sender as RibbonGallery; + SkinId skin = IdToSkin(g.SelectedIndex); + if (Skin != skin) Skin = skin; + } + + + + /// + /// Gets or sets whether the database is locked. This is a dp. + /// + public bool IsLocked + { + get { return (bool)GetValue(IsLockedProperty); } + set { SetValue(IsLockedProperty, value); } + } + + public static readonly DependencyProperty IsLockedProperty = + DependencyProperty.Register("IsLocked", typeof(bool), typeof(Main), new UIPropertyMetadata(true, OnLockedPropertyChanged)); + + private static void OnLockedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((Main)d).OnLockedChanged(d, (bool)e.OldValue, (bool)e.NewValue); + } + + protected virtual void OnLockedChanged(object sender, bool oldvalue, bool newValue) + { + if (newValue) + { + UnloadData(); + lockScreen.Dispatcher.BeginInvoke((Func)delegate() + { + lockScreen.FocusPassword(); + return 0; + }); + } + } + + + + protected override void OnPreviewKeyDown(KeyEventArgs e) + { + if (e.SystemKey == (Key.M) && Keyboard.Modifiers == ModifierKeys.Alt) + { + ribbon.ApplicationMenu.IsOpen ^= true; + } + + base.OnPreviewKeyDown(e); + } + + + public Folder SelectedFolder + { + get { return (Folder)GetValue(SelectedFolderProperty); } + set { SetValue(SelectedFolderProperty, value); } + } + + public static readonly DependencyProperty SelectedFolderProperty = + DependencyProperty.Register("SelectedFolder", typeof(Folder), typeof(Main), + new FrameworkPropertyMetadata( + null, + FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, + OnSelectedFolderPropertyChanged + )); + + private static void OnSelectedFolderPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((Main)d).OnSelectedFolderChanged((Folder)e.OldValue, (Folder)e.NewValue); + + } + + protected virtual void OnSelectedFolderChanged(Folder oldFolder, Folder newFolder) + { + if (newFolder != null) this.categoriesTree.Deselect(); + this.PasswordGrid.DataContext = newFolder; + foldersTreeView.Select(newFolder); + SelectedNode = newFolder; + + } + + public TemplateField SelectedTemplateField + { + get { return (TemplateField)GetValue(SelectedTemplateFieldProperty); } + set { SetValue(SelectedTemplateFieldProperty, value); } + } + + public static readonly DependencyProperty SelectedTemplateFieldProperty = + DependencyProperty.Register("SelectedTemplateField", typeof(TemplateField), typeof(Main), new FrameworkPropertyMetadata(null, + SelectedTemplateFieldPropertyChanged)); + + + private static void SelectedTemplateFieldPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + TemplateField oldField = e.OldValue as TemplateField; + if (oldField != null) oldField.IsSelected = false; + Main main = (Main)d; + TemplateField newField = (e.NewValue as TemplateField); + if (newField != null) newField.IsSelected = true; + main.OnSelectedTemplateFieldChanged(main, oldField, newField); + } + + protected virtual void OnSelectedTemplateFieldChanged(object sender, TemplateField oldField, TemplateField newField) + { + } + + /// + /// Gets or sets the selected password field. + /// This is a dependency property. + /// + public Field SelectedPasswordField + { + get { return (Field)GetValue(SelectedPasswordFieldProperty); } + set { SetValue(SelectedPasswordFieldProperty, value); } + } + + public static readonly DependencyProperty SelectedPasswordFieldProperty = + DependencyProperty.Register("SelectedPasswordField", typeof(Field), typeof(Main), new FrameworkPropertyMetadata(null, + FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, + SelectedPasswordFieldPropertyChanged, CoerceSelectedPasswordField)); + + private static void SelectedPasswordFieldPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + Field oldField = e.OldValue as Field; + if (oldField != null) oldField.IsSelected = false; + Main main = (Main)d; + Field newField = (e.NewValue as Field); + if (newField != null) + { + newField.IsSelected = true; + } + main.OnSelectedPasswordFieldChanged(main, oldField, newField); + } + + private void OnSelectedPasswordFieldChanged(object sender, Field oldField, Field newField) + { + } + + private static object CoerceSelectedPasswordField(DependencyObject d, object baseValue) + { + Main main = (Main)d; + return main.SelectedPassword == null ? null : baseValue; + } + + + + public Category SelectedCategory + { + get { return (Category)GetValue(SelectedCategoryProperty); } + set { SetValue(SelectedCategoryProperty, value); } + } + + public static readonly DependencyProperty SelectedCategoryProperty = + DependencyProperty.Register("SelectedCategory", typeof(Category), typeof(Main), + new FrameworkPropertyMetadata( + null, + FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, + SelectedCategoryPropertyChanged)); + + + private static void SelectedCategoryPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((Main)d).OnSelectedCategoryChanged(d, (Category)e.OldValue, (Category)e.NewValue); + + } + + protected virtual void OnSelectedCategoryChanged(object sender, Category oldCategory, Category newCategory) + { + if (newCategory != null) + { + newCategory.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(SelectedCategoryPropertyChanged); + } + if (oldCategory != null) + { + oldCategory.PropertyChanged -= SelectedCategoryPropertyChanged; + } + if (newCategory != null) this.foldersTreeView.Deselect(); + this.PasswordGrid.DataContext = newCategory; + + templateCategoryTree.Select(newCategory); + categoriesTree.Select(newCategory); + SelectedNode = newCategory; + SelectedTemplateField = null; + + IsTemplateModified = newCategory != null ? newCategory.IsModified : false; + } + + void SelectedCategoryPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + { + // some bindings have IsAsync=True, so we need to do this invoked: + Dispatcher.BeginInvoke((Func)delegate() + { + Category category = (Category)sender; + IsTemplateModified = category != null ? category.IsModified : false; + return 0; + }); + } + + public void NewPassword(Category category) + { + Password password = category.CreatePassword(); + if (password != null) + { + password.Name = "New Password"; + SelectedCategory = password.Category; + this.SelectedPassword = password; + this.PasswordGrid.FocusPassword(); + + } + } + + + + /// + /// Gets the currently selected password. + /// + public Password SelectedPassword + { + get { return (Password)GetValue(SelectedPasswordProperty); } + set { SetValue(SelectedPasswordProperty, value); } + } + + public static readonly DependencyProperty SelectedPasswordProperty = + DependencyProperty.Register("SelectedPassword", typeof(Password), typeof(Main), new FrameworkPropertyMetadata(null, + FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, + OnSelectedPasswordPropertyChanged)); + + + private static void OnSelectedPasswordPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (e.OldValue != e.NewValue) + { + Password oldPassword = (Password)e.OldValue; + Password newPassword = (Password)e.NewValue; + Main main = (Main)d; + + if (oldPassword != null) oldPassword.PropertyChanged -= main.OnPasswordPropertyChanged; + if (newPassword != null) newPassword.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(main.OnPasswordPropertyChanged); + main.SelectedPasswordField = null; + main.IsPasswordModified = newPassword != null && newPassword.IsModified; + main.IsPasswordSelected = newPassword != null; + + } + } + + void OnPasswordPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + { + // some bindings have IsAsync=True, so we need to do this invoked: + Dispatcher.BeginInvoke((Func)delegate() + { + Password password = (Password)sender; + IsPasswordModified = password != null ? password.IsModified : false; + return 0; + }); + } + + + + /// + /// Gets whether a password is selected. + /// This is a dependency property. + /// + public bool IsPasswordSelected + { + get { return (bool)GetValue(IsPasswordSelectedProperty); } + private set { SetValue(IsPasswordSelectedPropertyKey, value); } + } + + public static readonly DependencyPropertyKey IsPasswordSelectedPropertyKey = + DependencyProperty.RegisterReadOnly("IsPasswordSelected", typeof(bool), typeof(Main), new UIPropertyMetadata(false)); + + private static readonly DependencyProperty IsPasswordSelectedProperty = IsPasswordModifiedPropertyKey.DependencyProperty; + + + + /// + /// Gets or sets the selected node for the breadcrumb. + /// + public NodeBase SelectedNode + { + get { return (NodeBase)GetValue(SelectedNodeProperty); } + set { SetValue(SelectedNodeProperty, value); } + } + + public static readonly DependencyProperty SelectedNodeProperty = + DependencyProperty.Register("SelectedNode", typeof(NodeBase), typeof(Main), new FrameworkPropertyMetadata(null, + FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedNodePropertyChanged)); + + private static void OnSelectedNodePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((Main)d).OnSelectedNodeChanged((NodeBase)e.OldValue, (NodeBase)e.NewValue); + } + + protected virtual void OnSelectedNodeChanged(NodeBase oldNode, NodeBase newNode) + { + SelectedCategory = newNode as Category; + SelectedFolder = newNode as Folder; + } + + + + /// + /// Selects the textbox to edit the name of the selected Password Field. + /// + public void SelectPasswordFieldName() + { + fieldName.Focus(); + fieldName.SelectAll(); + } + + + public void FocusEditFolderName() + { + folderName.Focus(); + folderName.SelectAll(); + } + + public void FocusEditCategoryName() + { + if (!TemplateTab.IsVisible) + { + categoryName.Focus(); + categoryName.SelectAll(); + } + else + { + categoryName2.Focus(); + categoryName2.SelectAll(); + } + } + + + internal void SelectTemplateFieldName() + { + TemplateFieldName.Focus(); + TemplateFieldName.SelectAll(); + + } + + private void LaunchConfiguration(object sender, RoutedEventArgs e) + { + MessageBox.Show("Configuration..."); + } + + public bool Login(string password) + { + SqlConnectionStringBuilder sb = new SqlConnectionStringBuilder(Settings.Default.PasswordsConnectionString); + try + { + sb.PersistSecurityInfo = false; + sb.Password = password; + LoadData(sb.ConnectionString); + DisplayType = DisplayType.Passwords; + IsLocked = false; + sb.Password = ""; + return true; + } + catch (SqlCeException) + { + sb.Password = ""; + DisplayType = DisplayType.Login; + IsLocked = true; + return false; + } + } + + + + public DisplayType DisplayType + { + get { return (DisplayType)GetValue(DisplayTypeProperty); } + set { SetValue(DisplayTypeProperty, value); } + } + + public static readonly DependencyProperty DisplayTypeProperty = + DependencyProperty.Register("DisplayType", typeof(DisplayType), typeof(Main), new UIPropertyMetadata(DisplayType.Login, OnDisplayTypePropertyChanged)); + + private static void OnDisplayTypePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((Main)d).OnDisplayTypeChanged(d, (DisplayType)e.OldValue, (DisplayType)e.NewValue); + } + + protected virtual void OnDisplayTypeChanged(object sender, DisplayType oldDisplayType, DisplayType newDisplayType) + { + switch (newDisplayType) + { + case DisplayType.Login: + BizContext.CloseConnection(); + lockScreen.Dispatcher.BeginInvoke((Func)delegate() + { + lockScreen.FocusPassword(); + return 0; + }); + break; + + case DisplayType.ChangePassword: + BizContext.CloseConnection(); + this.changePassword.Dispatcher.BeginInvoke((Func)delegate() + { + changePassword.FocusPassword(); + return 0; + }); + break; + } + } + + + + + public IEnumerable Categories + { + get { return (IEnumerable)GetValue(CategoriesProperty); } + set { SetValue(CategoriesProperty, value); } + } + + public static readonly DependencyProperty CategoriesProperty = + DependencyProperty.Register("Categories", typeof(IEnumerable), typeof(Main), new UIPropertyMetadata(null)); + + + + + } +} diff --git a/PasswordSafe/PasswordSafe/PasswordSafe.csproj b/PasswordSafe/PasswordSafe/PasswordSafe.csproj new file mode 100644 index 0000000..d8ae3dc --- /dev/null +++ b/PasswordSafe/PasswordSafe/PasswordSafe.csproj @@ -0,0 +1,432 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {24CBC0A1-F7C4-45A0-8D58-E1BB1B4F0629} + WinExe + Properties + PasswordSafe + PasswordSafe + v3.5 + 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 4 + SAK + SAK + SAK + SAK + DC5153CC1108F6979177444F8EE11070DD681C50 + WPFPasswordSafe_TemporaryKey.pfx + true + false + false + + + true + + + + + 4.0 + c:\temp\pws\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 18 + 1.0.0.%2a + false + true + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + AllRules.ruleset + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + AllRules.ruleset + + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + true + GlobalSuppressions.cs + prompt + AllRules.ruleset + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + true + GlobalSuppressions.cs + prompt + AllRules.ruleset + + + + + + 3.5 + + + 3.5 + + + + + 3.5 + + + 3.5 + + + + + 3.0 + + + 3.0 + + + 3.0 + + + 3.0 + + + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + MSBuild:Compile + Designer + + + App.xaml + Code + + + + + Main.xaml + Code + + + + + + + + + + + + + + + + + + + + + + + + + ChangePassword.xaml + + + LockScreen.xaml + + + TemplateGrid.xaml + + + + + Code + + + + Code + + + True + True + Resources.resx + + + True + Settings.settings + True + + + + + + PasswordGrid.xaml + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + .NET Framework Client Profile + false + + + False + .NET Framework 2.0 %28x86%29 + false + + + False + .NET Framework 3.0 %28x86%29 + false + + + False + .NET Framework 3.5 + false + + + False + .NET Framework 3.5 SP1 + true + + + False + SQL Server Compact 3.5 + true + + + False + Windows Installer 3.1 + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PreserveNewest + + + + + + + + {333FDC55-6B47-4A64-A2DF-A4C5823FAC74} + Odyssey + + + {E4DA0115-54F3-4DA0-813B-CDEBF4D205E5} + PasswordSafe.Data + + + + + + + + + + + + \ No newline at end of file diff --git a/PasswordSafe/PasswordSafe/Properties/AssemblyInfo.cs b/PasswordSafe/PasswordSafe/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..cae0ce5 --- /dev/null +++ b/PasswordSafe/PasswordSafe/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Windows; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("PasswordSafe")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("www.tomssoftware.net")] +[assembly: AssemblyProduct("PasswordSafe")] +[assembly: AssemblyCopyright("Copyright © Thomas Gerber 2009")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +//In order to begin building localizable applications, set +//CultureYouAreCodingWith in your .csproj file +//inside a . For example, if you are using US english +//in your source files, set the to en-US. Then uncomment +//the NeutralResourceLanguage attribute below. Update the "en-US" in +//the line below to match the UICulture setting in the project file. + +//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] + + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] + + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("0.7.1.*")] +[assembly: AssemblyFileVersion("0.7.1.200")] diff --git a/PasswordSafe/PasswordSafe/Properties/Resources.Designer.cs b/PasswordSafe/PasswordSafe/Properties/Resources.Designer.cs new file mode 100644 index 0000000..78b18b4 --- /dev/null +++ b/PasswordSafe/PasswordSafe/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.20506.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace PasswordSafe.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PasswordSafe.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/PasswordSafe/PasswordSafe/Properties/Resources.resx b/PasswordSafe/PasswordSafe/Properties/Resources.resx new file mode 100644 index 0000000..ffecec8 --- /dev/null +++ b/PasswordSafe/PasswordSafe/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/PasswordSafe/PasswordSafe/Properties/Settings.Designer.cs b/PasswordSafe/PasswordSafe/Properties/Settings.Designer.cs new file mode 100644 index 0000000..19d1cd4 --- /dev/null +++ b/PasswordSafe/PasswordSafe/Properties/Settings.Designer.cs @@ -0,0 +1,180 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:2.0.50727.4918 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace PasswordSafe.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "9.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.ApplicationScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.SpecialSettingAttribute(global::System.Configuration.SpecialSetting.ConnectionString)] + [global::System.Configuration.DefaultSettingValueAttribute("Data Source=|DataDirectory|\\Passwords.sdf")] + public string PasswordsConnectionString { + get { + return ((string)(this["PasswordsConnectionString"])); + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool IsSidebarExpanded { + get { + return ((bool)(this["IsSidebarExpanded"])); + } + set { + this["IsSidebarExpanded"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool IsRibbonExpanded { + get { + return ((bool)(this["IsRibbonExpanded"])); + } + set { + this["IsRibbonExpanded"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool CanRibbonMinimize { + get { + return ((bool)(this["CanRibbonMinimize"])); + } + set { + this["CanRibbonMinimize"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public double Left { + get { + return ((double)(this["Left"])); + } + set { + this["Left"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public double Top { + get { + return ((double)(this["Top"])); + } + set { + this["Top"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public double Width { + get { + return ((double)(this["Width"])); + } + set { + this["Width"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public double Height { + get { + return ((double)(this["Height"])); + } + set { + this["Height"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Normal")] + public global::System.Windows.WindowState WindowState { + get { + return ((global::System.Windows.WindowState)(this["WindowState"])); + } + set { + this["WindowState"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("OfficeBlue")] + public global::Odyssey.Controls.Classes.SkinId Skin { + get { + return ((global::Odyssey.Controls.Classes.SkinId)(this["Skin"])); + } + set { + this["Skin"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool IsGlassEnabled { + get { + return ((bool)(this["IsGlassEnabled"])); + } + set { + this["IsGlassEnabled"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("240")] + public double PasswordWith { + get { + return ((double)(this["PasswordWith"])); + } + set { + this["PasswordWith"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("4")] + public int MaxSections { + get { + return ((int)(this["MaxSections"])); + } + set { + this["MaxSections"] = value; + } + } + } +} diff --git a/PasswordSafe/PasswordSafe/Properties/Settings.settings b/PasswordSafe/PasswordSafe/Properties/Settings.settings new file mode 100644 index 0000000..d2c5156 --- /dev/null +++ b/PasswordSafe/PasswordSafe/Properties/Settings.settings @@ -0,0 +1,50 @@ + + + + + + <?xml version="1.0" encoding="utf-16"?> +<SerializableConnectionString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> + <ConnectionString>Data Source=|DataDirectory|\Passwords.sdf</ConnectionString> + <ProviderName>Microsoft.SqlServerCe.Client.3.5</ProviderName> +</SerializableConnectionString> + Data Source=|DataDirectory|\Passwords.sdf + + + True + + + True + + + False + + + -1 + + + -1 + + + -1 + + + -1 + + + Normal + + + OfficeBlue + + + True + + + 240 + + + 4 + + + \ No newline at end of file diff --git a/PasswordSafe/PasswordSafe/Settings.cs b/PasswordSafe/PasswordSafe/Settings.cs new file mode 100644 index 0000000..71a11d4 --- /dev/null +++ b/PasswordSafe/PasswordSafe/Settings.cs @@ -0,0 +1,28 @@ +namespace PasswordSafe.Properties { + + + // This class allows you to handle specific events on the settings class: + // The SettingChanging event is raised before a setting's value is changed. + // The PropertyChanged event is raised after a setting's value is changed. + // The SettingsLoaded event is raised after the setting values are loaded. + // The SettingsSaving event is raised before the setting values are saved. + internal sealed partial class Settings { + + public Settings() { + // // To add event handlers for saving and changing settings, uncomment the lines below: + // + // this.SettingChanging += this.SettingChangingEventHandler; + // + // this.SettingsSaving += this.SettingsSavingEventHandler; + // + } + + private void SettingChangingEventHandler(object sender, System.Configuration.SettingChangingEventArgs e) { + // Add code to handle the SettingChangingEvent event here. + } + + private void SettingsSavingEventHandler(object sender, System.ComponentModel.CancelEventArgs e) { + // Add code to handle the SettingsSaving event here. + } + } +} diff --git a/PasswordSafe/PasswordSafe/TemplateFieldTemplates.xaml b/PasswordSafe/PasswordSafe/TemplateFieldTemplates.xaml new file mode 100644 index 0000000..cc5108a --- /dev/null +++ b/PasswordSafe/PasswordSafe/TemplateFieldTemplates.xaml @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PasswordSafe/PasswordSafe/Themes/Generic.xaml b/PasswordSafe/PasswordSafe/Themes/Generic.xaml new file mode 100644 index 0000000..de13a94 --- /dev/null +++ b/PasswordSafe/PasswordSafe/Themes/Generic.xaml @@ -0,0 +1,35 @@ + + + + + diff --git a/PasswordSafe/PasswordSafe/Tools/RibbonSrc.cs b/PasswordSafe/PasswordSafe/Tools/RibbonSrc.cs new file mode 100644 index 0000000..a35db09 --- /dev/null +++ b/PasswordSafe/PasswordSafe/Tools/RibbonSrc.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; +using Odyssey.Controls.Ribbon.Interfaces; +using PasswordSafe.Properties; +using System.Windows.Media.Imaging; +using System.Diagnostics; +using System.Windows.Data; + +namespace Odyssey.Controls +{ + public class RibbonSrc : FrameworkElement + { + private static string formatString = "pack://application:,,,/img/{0}_{1}.png"; + + /// + /// Gets or sets the Format String to convert a Name value to a path the contains the image. + /// Warning: this value has a global scope! + /// + public string FormatString + { + get { return formatString; } + set { formatString = value; } + } + + + public static string GetName(DependencyObject obj) + { + return (string)obj.GetValue(NameProperty); + } + + /// + /// Set the name of an image to attach to an IRibbonButton control. + /// + public static void SetName(DependencyObject obj, string value) + { + obj.SetValue(NameProperty, value); + } + + public static new readonly DependencyProperty NameProperty = + DependencyProperty.RegisterAttached("Name", typeof(string), typeof(RibbonSrc), new UIPropertyMetadata(null, NamePropertyChanged)); + + private static void NamePropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + string value = e.NewValue as string; + + IRibbonButton btn = o as IRibbonButton; + if (btn != null) + { + btn.SmallImage = CreateImageSource(value, 16); + btn.LargeImage = CreateImageSource(value, 32); + return; + } + + RibbonApplicationMenuItem apItem = o as RibbonApplicationMenuItem; + if (apItem != null) + { + apItem.Image = CreateImageSource(value, 32); + return; + } + + RibbonMenuItem item = o as RibbonMenuItem; + if (item != null) + { + item.Image = CreateImageSource(value, 16); + return; + } + + RibbonComboBox box = o as RibbonComboBox; + if (box != null) + { + box.Image = CreateImageSource(value, 16); + return; + } + + RibbonTextBox tb = o as RibbonTextBox; + if (tb != null) + { + tb.Image = CreateImageSource(value, 16); + return; + } + + throw new ArgumentNullException("RibbonSrc.Name can only by attached to controls that implement IRibbonButton."); + } + + private static ImageSource CreateImageSource(string value, int size) + { + string s; + if (size == 16 && value.Equals("favorites", StringComparison.InvariantCultureIgnoreCase)) s = "pack://application:,,,/img/faves16.png"; + else s = string.Format(formatString, value, size); + BitmapImage img = new BitmapImage(new Uri(s)); + return img; + } + } + +} diff --git a/PasswordSafe/PasswordSafe/Tools/TreeViewExtender.cs b/PasswordSafe/PasswordSafe/Tools/TreeViewExtender.cs new file mode 100644 index 0000000..f263b55 --- /dev/null +++ b/PasswordSafe/PasswordSafe/Tools/TreeViewExtender.cs @@ -0,0 +1,185 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Controls; +using System.Windows; +using PasswordSafe.Data.Biz; +using System.Windows.Media; +using System.Diagnostics; + +namespace PasswordSafe.Tools +{ + //EXPLAIN: + /// + /// Extends a TreeView to modify the selected item. + /// + public static class TreeViewExtender + { + /// + /// Deselects a selected item from a TreeView. + /// + /// The TreeView. + public static void Deselect(this TreeView treeView) + { + if (treeView.Items.Count > 0 && treeView.SelectedItem != null) + { + foreach (var o in treeView.Items) + { + var item = treeView.ItemContainerGenerator.ContainerFromItem(o) as TreeViewItem; + item.IsSelected = false; + DeselectItems(item); + } + } + } + + private static void DeselectItems(TreeViewItem item) + { + foreach (var o in item.Items) + { + var sub = item.ItemContainerGenerator.ContainerFromItem(o) as TreeViewItem; + if (sub != null) + { + sub.IsSelected = false; + DeselectItems(sub); + } + } + } + + public static void SelectFirst(this TreeView treeView) + { + if (treeView.Items.Count > 0) + { + var item = treeView.ItemContainerGenerator.ContainerFromItem(treeView.Items[0]) as TreeViewItem; + if (item != null) item.IsSelected = true; + + } + } + + public static void Select(this TreeView treeView, int index) + { + object o = treeView.Items[index]; + TreeViewItem item = Select(treeView, o); + } + + public static void Select(this TreeView treeView, Category category) + { + SelectItem(treeView, category); + } + + + private static void SelectItem(ItemsControl itemsControl, Category category) + { + ItemContainerGenerator gen = itemsControl.ItemContainerGenerator; + if (gen.Status != System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated) + { + EventHandler eh = null; + eh = new EventHandler(delegate + { + if (gen.Status == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated) + { + gen.StatusChanged -= eh; + SelectItem(itemsControl, category); + } + }); + gen.StatusChanged += eh; + } + else + { + TreeViewItem root = gen.ContainerFromItem(category) as TreeViewItem; + if (root != null) + { + root.IsSelected = true; + return; + } + + Category original = category; + while (category != null) + { + foreach (var item in itemsControl.Items) + { + TreeViewItem container = gen.ContainerFromItem(item) as TreeViewItem; + if (item == original) + { + if (container != null) + { + container.IsSelected = true; + } + return; + } + else + { + if (item == category) + { + + if (container != null) + { + container.IsExpanded = true; + SelectItem(container, original); + } + } + } + } + category = category.Parent; + } + } + } + + public static TreeViewItem Select(this TreeView treeView, object value) + { + if (treeView.SelectedItem == value) return null; + + TreeViewItem container = treeView.ItemContainerGenerator.ContainerFromItem(value) as TreeViewItem; + if (container == null) + { + foreach (var item in treeView.Items) + { + TreeViewItem c = treeView.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem; + if (c != null) + { + container = Select(c, value); + if (container != null) break; + } + } + } + if (container != null) container.IsSelected = true; + return container; + } + + private static TreeViewItem Select(TreeViewItem tvItem, object value) + { + TreeViewItem tv = tvItem.ItemContainerGenerator.ContainerFromItem(value) as TreeViewItem; + if (tv != null) + { + tvItem.IsExpanded = true; + return tv; + } + foreach (var item in tvItem.Items) + { + TreeViewItem c = tvItem.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem; + if (c != null) + { + tv = Select(c, value); + if (tv != null) + { + tvItem.IsExpanded = true; + return tv; + } + } + + } + return null; + } + + public static TreeViewItem FirstItem(this TreeView treeView) + { + if (treeView.Items.Count > 0) + { + object value = treeView.Items[0]; + TreeViewItem item = treeView.ItemContainerGenerator.ContainerFromItem(value) as TreeViewItem; + return item; + } + else return null; + } + } +} diff --git a/PasswordSafe/PasswordSafe/UserControls/ChangePassword.xaml b/PasswordSafe/PasswordSafe/UserControls/ChangePassword.xaml new file mode 100644 index 0000000..b3b53b6 --- /dev/null +++ b/PasswordSafe/PasswordSafe/UserControls/ChangePassword.xaml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PasswordSafe/PasswordSafe/UserControls/LockScreen.xaml.cs b/PasswordSafe/PasswordSafe/UserControls/LockScreen.xaml.cs new file mode 100644 index 0000000..42bc319 --- /dev/null +++ b/PasswordSafe/PasswordSafe/UserControls/LockScreen.xaml.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using PasswordSafe.Controls; + +namespace PasswordSafe.UserControls +{ + /// + /// Interaction logic for LockScreen.xaml + /// + public partial class LockScreen : UserControl + { + + public LockScreen() + { + InitializeComponent(); + } + + static LockScreen() + { + CommandManager.RegisterClassCommandBinding(typeof(LockScreen), new CommandBinding(LoginCommand, Login)); + } + + public static readonly RoutedUICommand LoginCommand = new RoutedUICommand("Login", "LoginCommand", typeof(LockScreen)); + + private static void Login(object sender, ExecutedRoutedEventArgs e) + { + LockScreen screen = (LockScreen)sender; + screen.Login(); + } + + public void FocusPassword() + { + password.Focus(); + password.SelectAll(); + } + + private void Login() + { + Main main = GetMain(); + if (main != null) + { + string password = this.password.Password; + if (!main.Login(password)) + { + LoginInfo = "Wrong Password, try again..."; + FocusPassword(); + } + else + { + // clear the typed in right after login to make sure it won't appear after logging out: + this.password.Password = ""; + LoginInfo = ""; + } + } + + } + + private Main GetMain() + { + FrameworkElement parent = this.Parent as FrameworkElement; + while (parent != null && !(parent is Main)) + { + parent = parent.Parent as FrameworkElement; + } + Main main = parent as Main; + return main; + } + + protected override void OnKeyDown(KeyEventArgs e) + { + if (e.Key == Key.Enter) + { + e.Handled = true; + Login(); + } + base.OnKeyDown(e); + } + + + + public string LoginInfo + { + get { return (string)GetValue(LoginInfoProperty); } + set { SetValue(LoginInfoProperty, value); } + } + + // Using a DependencyProperty as the backing store for LoginInfo. This enables animation, styling, binding, etc... + public static readonly DependencyProperty LoginInfoProperty = + DependencyProperty.Register("LoginInfo", typeof(string), typeof(LockScreen), new UIPropertyMetadata("")); + + private void OnChangePasswordClick(object sender, RoutedEventArgs e) + { + Main main = GetMain(); + main.DisplayType = DisplayType.ChangePassword; + } + + + } +} diff --git a/PasswordSafe/PasswordSafe/UserControls/PasswordGrid.xaml b/PasswordSafe/PasswordSafe/UserControls/PasswordGrid.xaml new file mode 100644 index 0000000..08daba7 --- /dev/null +++ b/PasswordSafe/PasswordSafe/UserControls/PasswordGrid.xaml @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PasswordSafe/PasswordSafe/UserControls/PasswordGrid.xaml.cs b/PasswordSafe/PasswordSafe/UserControls/PasswordGrid.xaml.cs new file mode 100644 index 0000000..571fd69 --- /dev/null +++ b/PasswordSafe/PasswordSafe/UserControls/PasswordGrid.xaml.cs @@ -0,0 +1,325 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using System.Diagnostics; +using PasswordSafe.Data; +using PasswordSafe.Classes; +using System.Collections.ObjectModel; +using PasswordSafe.Data.Biz; +using Odyssey.Controls; +using System.Threading; +using PasswordSafe.Controls; + +namespace PasswordSafe.UserControls +{ + /// + /// Interaction logic for PasswordGrid.xaml + /// + public partial class PasswordGrid : UserControl + { + public PasswordGrid() + { + InitializeComponent(); + CommandBindings.Add(new CommandBinding(FocusSearchBoxCommand, FocusSearchBox)); + } + + static PasswordGrid() + { + CommandManager.RegisterClassCommandBinding(typeof(Main), new CommandBinding(FocusSearchBoxCommand, FocusSearchBoxStatic, CanFocusSearchBox)); + } + + + + /// + /// Gets whether data in the datacontext has changed since last call of ResetChanges. + /// + public bool DataChanged + { + get { return (bool)GetValue(DataChangedProperty); } + private set { SetValue(DataChangedProperty, value); } + } + + public static readonly DependencyProperty DataChangedProperty = + DependencyProperty.Register("DataChanged", typeof(bool), typeof(PasswordGrid), new UIPropertyMetadata(false)); + + + public Password SelectedPassword + { + get { return (Password)GetValue(SelectedPasswordProperty); } + set { SetValue(SelectedPasswordProperty, value); } + } + + public static readonly DependencyProperty SelectedPasswordProperty = + DependencyProperty.Register("SelectedPassword", typeof(Password), typeof(PasswordGrid), new FrameworkPropertyMetadata(null, + FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedPasswordPropertyChanged)); + + + private static void OnSelectedPasswordPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((PasswordGrid)d).OnSelectedPasswordChanged(d, (Password)e.OldValue, (Password)e.NewValue); + } + + protected virtual void OnSelectedPasswordChanged(object sender, Password oldPassword, Password newPassword) + { + + } + + + /// + /// Gets the password of the selected category including only those that match with the text typed in the Search box. + /// + public IEnumerable Passwords + { + get { return (IEnumerable)GetValue(PasswordsProperty); } + set { SetValue(PasswordsProperty, value); } + } + + public static readonly DependencyProperty PasswordsProperty = + DependencyProperty.Register("Passwords", typeof(IEnumerable), typeof(PasswordGrid), new UIPropertyMetadata(null)); + + + + /// + /// Gets or sets the selected node of the breadcrumb. + /// + public NodeBase SelectedNode + { + get { return (NodeBase)GetValue(SelectedNodeProperty); } + set { SetValue(SelectedNodeProperty, value); } + } + + public static readonly DependencyProperty SelectedNodeProperty = + DependencyProperty.Register("SelectedNode", typeof(NodeBase), typeof(PasswordGrid), new FrameworkPropertyMetadata(null, + FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedNodePropertyChanged)); + + private static void OnSelectedNodePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((PasswordGrid)d).OnSelectedNodeChanged((NodeBase)e.OldValue, (NodeBase)e.NewValue); + } + + protected virtual void OnSelectedNodeChanged(NodeBase oldNode, NodeBase newNode) + { + PasswordsListView.SelectedIndex = 0; + UpdatePasswords(newNode); + if (newNode != null) + { + newNode.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(newNode_PropertyChanged); + } + if (oldNode != null) + { + oldNode.PropertyChanged -= newNode_PropertyChanged; + } + + } + + private void UpdatePasswords(NodeBase newNode) + { + Category c = newNode as Category; + if (c != null) + { + SetPasswords(c.NestedPasswords); + } + else + { + Folder f = newNode as Folder; + SetPasswords(f != null ? f.NestedPasswords : null); + } + } + + void newNode_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + { + if (e.PropertyName == "NestedPasswords") + { + UpdatePasswords(SelectedNode); + } + } + + private Thread filterThread; + + private void SetPasswords(IEnumerable passwords) + { + if (filterThread != null) + { + filterThread.Abort(); + filterThread.Join(); + } + + if (!string.IsNullOrEmpty(SearchText)) + { + filterThread = new Thread(delegate(object text) + { + passwords = FilteredPasswords(passwords, text as string); + this.Dispatcher.Invoke((ThreadStart)delegate() + { + Passwords = passwords != null ? new ObservableCollection(passwords) : null; + if (Passwords != null) SelectedPassword = Passwords.FirstOrDefault(); + }); + }); + filterThread.Start(SearchText); + } + else + { + Passwords = passwords != null ? new ObservableCollection(passwords) : null; + if (Passwords != null) SelectedPassword = Passwords.FirstOrDefault(); + } + + } + + private IEnumerable FilteredPasswords(IEnumerable passwords, string text) + { + if (passwords != null) + { + return string.IsNullOrEmpty(text) ? passwords : passwords.Where(p => MatchFilter(p, text)); + } + else + { + return null; + } + } + + private bool MatchFilter(Password password, string text) + { + if (password.Name.IndexOf(text, StringComparison.InvariantCultureIgnoreCase) >= 0) return true; + foreach (Field field in password.Fields) + { + string value = field.StringValue; + if (value!=null && value.IndexOf(text, StringComparison.InvariantCultureIgnoreCase) >= 0) return true; + } + return false; + } + + + internal void FocusPassword() + { + this.PasswordName.Focus(); + this.PasswordName.SelectAll(); + } + + private void OnCategoryBreadcrumbItemSelected(object sender, RoutedPropertyChangedEventArgs e) + { + if (e.NewValue != null) + { + NodeBase node = e.NewValue.Data as NodeBase; + SelectedNode = node; + } + } + + public NodeBase BreadcrumbRoot + { + get { return (NodeBase)GetValue(BreadcrumbRootProperty); } + set { SetValue(BreadcrumbRootProperty, value); } + } + + public static readonly DependencyProperty BreadcrumbRootProperty = + DependencyProperty.Register("BreadcrumbRoot", typeof(NodeBase), typeof(PasswordGrid), new UIPropertyMetadata(null, OnBreadcrumbRootChanged)); + + private static void OnBreadcrumbRootChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((PasswordGrid)d).OnBreadcrumbRootChanged((NodeBase)e.OldValue, (NodeBase)e.NewValue); + } + + protected virtual void OnBreadcrumbRootChanged(NodeBase oldNode, NodeBase newNode) + { + BreadcrumbHierarchy = UIContext.GetBreadcrumbDropDownData(newNode); + CategoryHierarchy = UIContext.GetBreadcrumbDropDownData(BizContext.Instance.Categories.FirstOrDefault()); + } + + + public Category RootCategory + { + get { return (Category)GetValue(RootCategoryProperty); } + set { SetValue(RootCategoryProperty, value); } + } + + public static readonly DependencyProperty RootCategoryProperty = + DependencyProperty.Register("RootCategory", typeof(Category), typeof(PasswordGrid), new UIPropertyMetadata(null)); + + + + public IEnumerable BreadcrumbHierarchy + { + get { return (IEnumerable)GetValue(BreadcrumbHierarchyProperty); } + set { SetValue(BreadcrumbHierarchyProperty, value); } + } + + public static readonly DependencyProperty BreadcrumbHierarchyProperty = + DependencyProperty.Register("BreadcrumbHierarchy", typeof(IEnumerable), typeof(PasswordGrid), new UIPropertyMetadata(null)); + + + + + public IEnumerable CategoryHierarchy + { + get { return (IEnumerable)GetValue(CategoryHierarchyProperty); } + set { SetValue(CategoryHierarchyProperty, value); } + } + + public static readonly DependencyProperty CategoryHierarchyProperty = + DependencyProperty.Register("CategoryHierarchy", typeof(IEnumerable), typeof(PasswordGrid), new UIPropertyMetadata(null)); + + + /// + /// Gets or sets the text for the search textbox. + /// + public string SearchText + { + get { return (string)GetValue(SearchTextProperty); } + set { SetValue(SearchTextProperty, value); } + } + + public static readonly DependencyProperty SearchTextProperty = + DependencyProperty.Register("SearchText", typeof(string), typeof(PasswordGrid), new UIPropertyMetadata("", OnSearchTextPropertyChanged)); + + private static void OnSearchTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((PasswordGrid)d).OnSearchTextChanged((string)e.OldValue, (string)e.NewValue); + } + + protected virtual void OnSearchTextChanged(string oldValue, string newValue) + { + SelectedNode = new UIContext().RootCategory; + bool hasText = !string.IsNullOrEmpty(newValue); + delBtn.Visibility = hasText ? Visibility.Visible : Visibility.Collapsed; + fndBtn.Visibility = !hasText ? Visibility.Visible : Visibility.Collapsed; + UpdatePasswords(SelectedNode); + } + + private void OnSearchBoxDelBtnClick(object sender, RoutedEventArgs e) + { + SearchText = ""; + } + + + public static readonly RoutedUICommand FocusSearchBoxCommand = new RoutedUICommand("Focus", "FocusSearchBoxCommand", typeof(PasswordGrid)); + + public void FocusSearchBox(object sender, ExecutedRoutedEventArgs e) + { + searchBox.Focus(); + searchBox.SelectAll(); + } + + + + public static void FocusSearchBoxStatic(object sender, ExecutedRoutedEventArgs e) + { + ((Main)sender).PasswordGrid.FocusSearchBox(sender, e); + } + + public static void CanFocusSearchBox(object sender, CanExecuteRoutedEventArgs e) + { + Main main = (Main)sender; + e.CanExecute = (main.DisplayType == DisplayType.Passwords && main.DisplayMode == DisplayMode.Passwords); + } + + } +} diff --git a/PasswordSafe/PasswordSafe/UserControls/TemplateGrid.xaml b/PasswordSafe/PasswordSafe/UserControls/TemplateGrid.xaml new file mode 100644 index 0000000..78afca4 --- /dev/null +++ b/PasswordSafe/PasswordSafe/UserControls/TemplateGrid.xaml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PasswordSafe/PasswordSafe/UserControls/TemplateGrid.xaml.cs b/PasswordSafe/PasswordSafe/UserControls/TemplateGrid.xaml.cs new file mode 100644 index 0000000..a838ffa --- /dev/null +++ b/PasswordSafe/PasswordSafe/UserControls/TemplateGrid.xaml.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using PasswordSafe.Classes; +using Odyssey.Controls; +using PasswordSafe.Data.Biz; + +namespace PasswordSafe.UserControls +{ + /// + /// Interaction logic for TemplateGrid.xaml + /// + public partial class TemplateGrid : UserControl + { + public TemplateGrid() + { + InitializeComponent(); + } + + private void edit_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) + { + + //SelectableCategoryField field = (e.OriginalSource as Control).DataContext as SelectableCategoryField; + //field.IsSelected = true; + + } + + private void OnCategoryBreadcrumbItemSelected(object sender, RoutedPropertyChangedEventArgs e) + { + if (e.NewValue != null) + { + NodeBase node = e.NewValue.Data as NodeBase; + SelectedNode = node; + } + } + + + public Category SelectedCategory + { + get { return (Category)GetValue(SelectedCategoryProperty); } + set { SetValue(SelectedCategoryProperty, value); } + } + + public static readonly DependencyProperty SelectedCategoryProperty = + DependencyProperty.Register("SelectedCategory", typeof(Category), typeof(TemplateGrid), + new FrameworkPropertyMetadata( + null, + FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, + SelectedCategoryPropertyChanged)); + + + private static void SelectedCategoryPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((TemplateGrid)d).OnSelectedCategoryChanged(d, (Category)e.OldValue, (Category)e.NewValue); + + } + + protected virtual void OnSelectedCategoryChanged(object sender, Category oldCategory, Category newCategory) + { + } + + /// + /// Gets or sets the selected node of the breadcrumb. + /// + public NodeBase SelectedNode + { + get { return (NodeBase)GetValue(SelectedNodeProperty); } + set { SetValue(SelectedNodeProperty, value); } + } + + public static readonly DependencyProperty SelectedNodeProperty = + DependencyProperty.Register("SelectedNode", typeof(NodeBase), typeof(TemplateGrid), new FrameworkPropertyMetadata(null, + FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedNodePropertyChanged)); + + private static void OnSelectedNodePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((TemplateGrid)d).OnSelectedNodeChanged((NodeBase)e.OldValue, (NodeBase)e.NewValue); + } + + protected virtual void OnSelectedNodeChanged(NodeBase oldNode, NodeBase newNode) + { + } + + public NodeBase BreadcrumbRoot + { + get { return (NodeBase)GetValue(BreadcrumbRootProperty); } + set { SetValue(BreadcrumbRootProperty, value); } + } + + public static readonly DependencyProperty BreadcrumbRootProperty = + DependencyProperty.Register("BreadcrumbRoot", typeof(NodeBase), typeof(TemplateGrid), new UIPropertyMetadata(null, OnBreadcrumbRootChanged)); + + private static void OnBreadcrumbRootChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((TemplateGrid)d).OnBreadcrumbRootChanged(d, (NodeBase)e.OldValue, (NodeBase)e.NewValue); + } + + protected virtual void OnBreadcrumbRootChanged(object sender, NodeBase oldNode, NodeBase newNode) + { + BreadcrumbHierarchy = UIContext.GetBreadcrumbDropDownData(newNode); + } + + + + public IEnumerable BreadcrumbHierarchy + { + get { return (IEnumerable)GetValue(BreadcrumbHierarchyProperty); } + set { SetValue(BreadcrumbHierarchyProperty, value); } + } + + public static readonly DependencyProperty BreadcrumbHierarchyProperty = + DependencyProperty.Register("BreadcrumbHierarchy", typeof(IEnumerable), typeof(TemplateGrid), new UIPropertyMetadata(null)); + + + } +} diff --git a/PasswordSafe/PasswordSafe/app.config b/PasswordSafe/PasswordSafe/app.config new file mode 100644 index 0000000..c68c049 --- /dev/null +++ b/PasswordSafe/PasswordSafe/app.config @@ -0,0 +1,53 @@ + + + + +
+ + + + + + + + + True + + + True + + + False + + + -1 + + + -1 + + + -1 + + + -1 + + + Normal + + + OfficeBlue + + + True + + + 240 + + + 4 + + + + \ No newline at end of file diff --git a/PasswordSafe/PasswordSafe/img/Accept_16.png b/PasswordSafe/PasswordSafe/img/Accept_16.png new file mode 100644 index 0000000..88664f9 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Accept_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/Accept_32.png b/PasswordSafe/PasswordSafe/img/Accept_32.png new file mode 100644 index 0000000..cf427bf Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Accept_32.png differ diff --git a/PasswordSafe/PasswordSafe/img/Add_16.png b/PasswordSafe/PasswordSafe/img/Add_16.png new file mode 100644 index 0000000..b77b5ab Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Add_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/Add_32.png b/PasswordSafe/PasswordSafe/img/Add_32.png new file mode 100644 index 0000000..63f6173 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Add_32.png differ diff --git a/PasswordSafe/PasswordSafe/img/Black48.png b/PasswordSafe/PasswordSafe/img/Black48.png new file mode 100644 index 0000000..d1985a4 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Black48.png differ diff --git a/PasswordSafe/PasswordSafe/img/Blue48.png b/PasswordSafe/PasswordSafe/img/Blue48.png new file mode 100644 index 0000000..8b45764 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Blue48.png differ diff --git a/PasswordSafe/PasswordSafe/img/Bottom_16.png b/PasswordSafe/PasswordSafe/img/Bottom_16.png new file mode 100644 index 0000000..bc92f76 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Bottom_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/Bottom_32.png b/PasswordSafe/PasswordSafe/img/Bottom_32.png new file mode 100644 index 0000000..80d60e1 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Bottom_32.png differ diff --git a/PasswordSafe/PasswordSafe/img/Comment_16.png b/PasswordSafe/PasswordSafe/img/Comment_16.png new file mode 100644 index 0000000..a744e0e Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Comment_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/DateTime_16.png b/PasswordSafe/PasswordSafe/img/DateTime_16.png new file mode 100644 index 0000000..35b631a Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/DateTime_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/DefineName_16.png b/PasswordSafe/PasswordSafe/img/DefineName_16.png new file mode 100644 index 0000000..512504e Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/DefineName_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/DeleteTb_32.png b/PasswordSafe/PasswordSafe/img/DeleteTb_32.png new file mode 100644 index 0000000..4acd5be Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/DeleteTb_32.png differ diff --git a/PasswordSafe/PasswordSafe/img/Delete_32.png b/PasswordSafe/PasswordSafe/img/Delete_32.png new file mode 100644 index 0000000..2c29f11 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Delete_32.png differ diff --git a/PasswordSafe/PasswordSafe/img/Down_16.png b/PasswordSafe/PasswordSafe/img/Down_16.png new file mode 100644 index 0000000..cede028 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Down_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/Down_32.png b/PasswordSafe/PasswordSafe/img/Down_32.png new file mode 100644 index 0000000..4b165d9 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Down_32.png differ diff --git a/PasswordSafe/PasswordSafe/img/Export_32.png b/PasswordSafe/PasswordSafe/img/Export_32.png new file mode 100644 index 0000000..6370ae8 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Export_32.png differ diff --git a/PasswordSafe/PasswordSafe/img/Glass_32.png b/PasswordSafe/PasswordSafe/img/Glass_32.png new file mode 100644 index 0000000..1000cf4 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Glass_32.png differ diff --git a/PasswordSafe/PasswordSafe/img/InsertB_32.png b/PasswordSafe/PasswordSafe/img/InsertB_32.png new file mode 100644 index 0000000..3c134a1 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/InsertB_32.png differ diff --git a/PasswordSafe/PasswordSafe/img/Layout_16.png b/PasswordSafe/PasswordSafe/img/Layout_16.png new file mode 100644 index 0000000..5369af6 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Layout_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/Layout_32.png b/PasswordSafe/PasswordSafe/img/Layout_32.png new file mode 100644 index 0000000..000148d Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Layout_32.png differ diff --git a/PasswordSafe/PasswordSafe/img/Left_16.png b/PasswordSafe/PasswordSafe/img/Left_16.png new file mode 100644 index 0000000..5ae6044 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Left_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/Left_32.png b/PasswordSafe/PasswordSafe/img/Left_32.png new file mode 100644 index 0000000..2574a30 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Left_32.png differ diff --git a/PasswordSafe/PasswordSafe/img/Lock_16.png b/PasswordSafe/PasswordSafe/img/Lock_16.png new file mode 100644 index 0000000..1b4c8ea Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Lock_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/Lock_32.png b/PasswordSafe/PasswordSafe/img/Lock_32.png new file mode 100644 index 0000000..6735b75 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Lock_32.png differ diff --git a/PasswordSafe/PasswordSafe/img/Max_16.png b/PasswordSafe/PasswordSafe/img/Max_16.png new file mode 100644 index 0000000..ddc27b1 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Max_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/Min_16.png b/PasswordSafe/PasswordSafe/img/Min_16.png new file mode 100644 index 0000000..4693dc3 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Min_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/NewPassword_32.png b/PasswordSafe/PasswordSafe/img/NewPassword_32.png new file mode 100644 index 0000000..788e81c Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/NewPassword_32.png differ diff --git a/PasswordSafe/PasswordSafe/img/NoFaves32.png b/PasswordSafe/PasswordSafe/img/NoFaves32.png new file mode 100644 index 0000000..d8ab699 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/NoFaves32.png differ diff --git a/PasswordSafe/PasswordSafe/img/Numeric_16.png b/PasswordSafe/PasswordSafe/img/Numeric_16.png new file mode 100644 index 0000000..ee1451f Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Numeric_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/Pushpin_16.png b/PasswordSafe/PasswordSafe/img/Pushpin_16.png new file mode 100644 index 0000000..a4e0ac2 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Pushpin_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/QuickParts_16.png b/PasswordSafe/PasswordSafe/img/QuickParts_16.png new file mode 100644 index 0000000..2ebb159 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/QuickParts_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/QuickParts_32.png b/PasswordSafe/PasswordSafe/img/QuickParts_32.png new file mode 100644 index 0000000..6bcbffe Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/QuickParts_32.png differ diff --git a/PasswordSafe/PasswordSafe/img/Reject_16.png b/PasswordSafe/PasswordSafe/img/Reject_16.png new file mode 100644 index 0000000..16c3250 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Reject_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/Reject_32.png b/PasswordSafe/PasswordSafe/img/Reject_32.png new file mode 100644 index 0000000..828c0b3 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Reject_32.png differ diff --git a/PasswordSafe/PasswordSafe/img/Remove_16.png b/PasswordSafe/PasswordSafe/img/Remove_16.png new file mode 100644 index 0000000..4acbdb9 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Remove_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/Remove_32.png b/PasswordSafe/PasswordSafe/img/Remove_32.png new file mode 100644 index 0000000..374a4fd Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Remove_32.png differ diff --git a/PasswordSafe/PasswordSafe/img/RightArrowHS_16.png b/PasswordSafe/PasswordSafe/img/RightArrowHS_16.png new file mode 100644 index 0000000..ad160a5 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/RightArrowHS_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/Right_16.png b/PasswordSafe/PasswordSafe/img/Right_16.png new file mode 100644 index 0000000..bf83eb1 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Right_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/Right_32.png b/PasswordSafe/PasswordSafe/img/Right_32.png new file mode 100644 index 0000000..fc44e30 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Right_32.png differ diff --git a/PasswordSafe/PasswordSafe/img/Search16.png b/PasswordSafe/PasswordSafe/img/Search16.png new file mode 100644 index 0000000..79f7b1e Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Search16.png differ diff --git a/PasswordSafe/PasswordSafe/img/Separator_16.png b/PasswordSafe/PasswordSafe/img/Separator_16.png new file mode 100644 index 0000000..74407b9 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Separator_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/Silver48.png b/PasswordSafe/PasswordSafe/img/Silver48.png new file mode 100644 index 0000000..5bfde1a Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Silver48.png differ diff --git a/PasswordSafe/PasswordSafe/img/Task_16.png b/PasswordSafe/PasswordSafe/img/Task_16.png new file mode 100644 index 0000000..471d1a3 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Task_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/TextBox_32.png b/PasswordSafe/PasswordSafe/img/TextBox_32.png new file mode 100644 index 0000000..e832c3a Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/TextBox_32.png differ diff --git a/PasswordSafe/PasswordSafe/img/Text_16.png b/PasswordSafe/PasswordSafe/img/Text_16.png new file mode 100644 index 0000000..e7a6531 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Text_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/Textbox_16.png b/PasswordSafe/PasswordSafe/img/Textbox_16.png new file mode 100644 index 0000000..dabaefa Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Textbox_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/Top_16.png b/PasswordSafe/PasswordSafe/img/Top_16.png new file mode 100644 index 0000000..4e58457 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Top_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/Top_32.png b/PasswordSafe/PasswordSafe/img/Top_32.png new file mode 100644 index 0000000..db73e21 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Top_32.png differ diff --git a/PasswordSafe/PasswordSafe/img/Up_16.png b/PasswordSafe/PasswordSafe/img/Up_16.png new file mode 100644 index 0000000..26ef077 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Up_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/Up_32.png b/PasswordSafe/PasswordSafe/img/Up_32.png new file mode 100644 index 0000000..6bb1104 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Up_32.png differ diff --git a/PasswordSafe/PasswordSafe/img/Vista48.png b/PasswordSafe/PasswordSafe/img/Vista48.png new file mode 100644 index 0000000..43c25ce Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Vista48.png differ diff --git a/PasswordSafe/PasswordSafe/img/Win7.48.png b/PasswordSafe/PasswordSafe/img/Win7.48.png new file mode 100644 index 0000000..3126911 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Win7.48.png differ diff --git a/PasswordSafe/PasswordSafe/img/Zoom_16.png b/PasswordSafe/PasswordSafe/img/Zoom_16.png new file mode 100644 index 0000000..cb7aab7 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Zoom_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/Zoom_32.png b/PasswordSafe/PasswordSafe/img/Zoom_32.png new file mode 100644 index 0000000..3ff8f70 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/Zoom_32.png differ diff --git a/PasswordSafe/PasswordSafe/img/arrow_left_16.png b/PasswordSafe/PasswordSafe/img/arrow_left_16.png new file mode 100644 index 0000000..09cf661 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/arrow_left_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/arrow_left_32.png b/PasswordSafe/PasswordSafe/img/arrow_left_32.png new file mode 100644 index 0000000..c4c877c Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/arrow_left_32.png differ diff --git a/PasswordSafe/PasswordSafe/img/arrow_right_16.png b/PasswordSafe/PasswordSafe/img/arrow_right_16.png new file mode 100644 index 0000000..03826c8 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/arrow_right_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/arrow_right_24.png b/PasswordSafe/PasswordSafe/img/arrow_right_24.png new file mode 100644 index 0000000..5e0c1fb Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/arrow_right_24.png differ diff --git a/PasswordSafe/PasswordSafe/img/arrow_right_24_d.png b/PasswordSafe/PasswordSafe/img/arrow_right_24_d.png new file mode 100644 index 0000000..b51dbff Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/arrow_right_24_d.png differ diff --git a/PasswordSafe/PasswordSafe/img/arrow_right_24_h.png b/PasswordSafe/PasswordSafe/img/arrow_right_24_h.png new file mode 100644 index 0000000..cbb2651 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/arrow_right_24_h.png differ diff --git a/PasswordSafe/PasswordSafe/img/arrow_right_24_p.png b/PasswordSafe/PasswordSafe/img/arrow_right_24_p.png new file mode 100644 index 0000000..e4435ee Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/arrow_right_24_p.png differ diff --git a/PasswordSafe/PasswordSafe/img/arrow_right_32.png b/PasswordSafe/PasswordSafe/img/arrow_right_32.png new file mode 100644 index 0000000..e7d8a45 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/arrow_right_32.png differ diff --git a/PasswordSafe/PasswordSafe/img/category_16.png b/PasswordSafe/PasswordSafe/img/category_16.png new file mode 100644 index 0000000..9e67c09 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/category_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/category_32.png b/PasswordSafe/PasswordSafe/img/category_32.png new file mode 100644 index 0000000..c74a178 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/category_32.png differ diff --git a/PasswordSafe/PasswordSafe/img/checkbox_16.png b/PasswordSafe/PasswordSafe/img/checkbox_16.png new file mode 100644 index 0000000..7ce3a48 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/checkbox_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/copy_16.png b/PasswordSafe/PasswordSafe/img/copy_16.png new file mode 100644 index 0000000..dc3dbf4 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/copy_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/copy_32.png b/PasswordSafe/PasswordSafe/img/copy_32.png new file mode 100644 index 0000000..f7c0fa3 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/copy_32.png differ diff --git a/PasswordSafe/PasswordSafe/img/cut_clipboard_16.png b/PasswordSafe/PasswordSafe/img/cut_clipboard_16.png new file mode 100644 index 0000000..150740a Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/cut_clipboard_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/del16.png b/PasswordSafe/PasswordSafe/img/del16.png new file mode 100644 index 0000000..3dacb2e Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/del16.png differ diff --git a/PasswordSafe/PasswordSafe/img/delete_16.png b/PasswordSafe/PasswordSafe/img/delete_16.png new file mode 100644 index 0000000..198af93 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/delete_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/faves16.png b/PasswordSafe/PasswordSafe/img/faves16.png new file mode 100644 index 0000000..162cf0f Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/faves16.png differ diff --git a/PasswordSafe/PasswordSafe/img/favorites_16.png b/PasswordSafe/PasswordSafe/img/favorites_16.png new file mode 100644 index 0000000..9653cd0 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/favorites_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/favorites_24.png b/PasswordSafe/PasswordSafe/img/favorites_24.png new file mode 100644 index 0000000..4c52a81 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/favorites_24.png differ diff --git a/PasswordSafe/PasswordSafe/img/favorites_32.png b/PasswordSafe/PasswordSafe/img/favorites_32.png new file mode 100644 index 0000000..24d3af9 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/favorites_32.png differ diff --git a/PasswordSafe/PasswordSafe/img/glass_16.png b/PasswordSafe/PasswordSafe/img/glass_16.png new file mode 100644 index 0000000..d351643 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/glass_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/halfselected_16.png b/PasswordSafe/PasswordSafe/img/halfselected_16.png new file mode 100644 index 0000000..45dd78a Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/halfselected_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/home_16.png b/PasswordSafe/PasswordSafe/img/home_16.png new file mode 100644 index 0000000..1fed06f Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/home_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/home_24.png b/PasswordSafe/PasswordSafe/img/home_24.png new file mode 100644 index 0000000..f1b435f Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/home_24.png differ diff --git a/PasswordSafe/PasswordSafe/img/home_32.png b/PasswordSafe/PasswordSafe/img/home_32.png new file mode 100644 index 0000000..4146f86 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/home_32.png differ diff --git a/PasswordSafe/PasswordSafe/img/new_document_16.png b/PasswordSafe/PasswordSafe/img/new_document_16.png new file mode 100644 index 0000000..a9679f2 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/new_document_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/new_document_32.png b/PasswordSafe/PasswordSafe/img/new_document_32.png new file mode 100644 index 0000000..c31f0f2 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/new_document_32.png differ diff --git a/PasswordSafe/PasswordSafe/img/nofaves16.png b/PasswordSafe/PasswordSafe/img/nofaves16.png new file mode 100644 index 0000000..5d71913 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/nofaves16.png differ diff --git a/PasswordSafe/PasswordSafe/img/open_document_16.png b/PasswordSafe/PasswordSafe/img/open_document_16.png new file mode 100644 index 0000000..9afe028 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/open_document_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/open_document_32.png b/PasswordSafe/PasswordSafe/img/open_document_32.png new file mode 100644 index 0000000..26cbc5b Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/open_document_32.png differ diff --git a/PasswordSafe/PasswordSafe/img/print_16.png b/PasswordSafe/PasswordSafe/img/print_16.png new file mode 100644 index 0000000..1afce8f Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/print_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/print_32.png b/PasswordSafe/PasswordSafe/img/print_32.png new file mode 100644 index 0000000..d7eac3c Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/print_32.png differ diff --git a/PasswordSafe/PasswordSafe/img/properties_document_16.png b/PasswordSafe/PasswordSafe/img/properties_document_16.png new file mode 100644 index 0000000..7de98d9 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/properties_document_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/properties_document_32.png b/PasswordSafe/PasswordSafe/img/properties_document_32.png new file mode 100644 index 0000000..37ed2a9 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/properties_document_32.png differ diff --git a/PasswordSafe/PasswordSafe/img/pwField_16.png b/PasswordSafe/PasswordSafe/img/pwField_16.png new file mode 100644 index 0000000..58e04df Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/pwField_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/save_16.png b/PasswordSafe/PasswordSafe/img/save_16.png new file mode 100644 index 0000000..f843724 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/save_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/save_32.png b/PasswordSafe/PasswordSafe/img/save_32.png new file mode 100644 index 0000000..fd5eaa0 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/save_32.png differ diff --git a/PasswordSafe/PasswordSafe/img/search_16.png b/PasswordSafe/PasswordSafe/img/search_16.png new file mode 100644 index 0000000..d61dfba Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/search_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/search_32.png b/PasswordSafe/PasswordSafe/img/search_32.png new file mode 100644 index 0000000..d773fc3 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/search_32.png differ diff --git a/PasswordSafe/PasswordSafe/img/stop_16.png b/PasswordSafe/PasswordSafe/img/stop_16.png new file mode 100644 index 0000000..4cb911f Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/stop_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/stop_24.png b/PasswordSafe/PasswordSafe/img/stop_24.png new file mode 100644 index 0000000..9ac48ab Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/stop_24.png differ diff --git a/PasswordSafe/PasswordSafe/img/undo_16.png b/PasswordSafe/PasswordSafe/img/undo_16.png new file mode 100644 index 0000000..7440090 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/undo_16.png differ diff --git a/PasswordSafe/PasswordSafe/img/undo_32.png b/PasswordSafe/PasswordSafe/img/undo_32.png new file mode 100644 index 0000000..412ba95 Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/undo_32.png differ diff --git a/PasswordSafe/PasswordSafe/img/view_24.png b/PasswordSafe/PasswordSafe/img/view_24.png new file mode 100644 index 0000000..63e06dd Binary files /dev/null and b/PasswordSafe/PasswordSafe/img/view_24.png differ