Adding the UI to the same repo. Made some default theme progress
@ -29,6 +29,8 @@ syntax: glob
|
||||
obj/
|
||||
[Rr]elease*/
|
||||
ProgramData*/
|
||||
ProgramData-Server*/
|
||||
ProgramData-UI*/
|
||||
_ReSharper*/
|
||||
[Tt]humbs.db
|
||||
[Tt]est[Rr]esult*
|
||||
|
81
MediaBrowser.Api/Drawing/DrawingUtils.cs
Normal file
@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
namespace MediaBrowser.Api.Drawing
|
||||
{
|
||||
public static class DrawingUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Resizes a set of dimensions
|
||||
/// </summary>
|
||||
public static Size Resize(int currentWidth, int currentHeight, int? width, int? height, int? maxWidth, int? maxHeight)
|
||||
{
|
||||
return Resize(new Size(currentWidth, currentHeight), width, height, maxWidth, maxHeight);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resizes a set of dimensions
|
||||
/// </summary>
|
||||
/// <param name="size">The original size object</param>
|
||||
/// <param name="width">A new fixed width, if desired</param>
|
||||
/// <param name="height">A new fixed neight, if desired</param>
|
||||
/// <param name="maxWidth">A max fixed width, if desired</param>
|
||||
/// <param name="maxHeight">A max fixed height, if desired</param>
|
||||
/// <returns>A new size object</returns>
|
||||
public static Size Resize(Size size, int? width, int? height, int? maxWidth, int? maxHeight)
|
||||
{
|
||||
decimal newWidth = size.Width;
|
||||
decimal newHeight = size.Height;
|
||||
|
||||
if (width.HasValue && height.HasValue)
|
||||
{
|
||||
newWidth = width.Value;
|
||||
newHeight = height.Value;
|
||||
}
|
||||
|
||||
else if (height.HasValue)
|
||||
{
|
||||
newWidth = GetNewWidth(newHeight, newWidth, height.Value);
|
||||
newHeight = height.Value;
|
||||
}
|
||||
|
||||
else if (width.HasValue)
|
||||
{
|
||||
newHeight = GetNewHeight(newHeight, newWidth, width.Value);
|
||||
newWidth = width.Value;
|
||||
}
|
||||
|
||||
if (maxHeight.HasValue && maxHeight < newHeight)
|
||||
{
|
||||
newWidth = GetNewWidth(newHeight, newWidth, maxHeight.Value);
|
||||
newHeight = maxHeight.Value;
|
||||
}
|
||||
|
||||
if (maxWidth.HasValue && maxWidth < newWidth)
|
||||
{
|
||||
newHeight = GetNewHeight(newHeight, newWidth, maxWidth.Value);
|
||||
newWidth = maxWidth.Value;
|
||||
}
|
||||
|
||||
return new Size(Convert.ToInt32(newWidth), Convert.ToInt32(newHeight));
|
||||
}
|
||||
|
||||
private static decimal GetNewWidth(decimal currentHeight, decimal currentWidth, int newHeight)
|
||||
{
|
||||
decimal scaleFactor = newHeight;
|
||||
scaleFactor /= currentHeight;
|
||||
scaleFactor *= currentWidth;
|
||||
|
||||
return scaleFactor;
|
||||
}
|
||||
|
||||
private static decimal GetNewHeight(decimal currentHeight, decimal currentWidth, int newWidth)
|
||||
{
|
||||
decimal scaleFactor = newWidth;
|
||||
scaleFactor /= currentWidth;
|
||||
scaleFactor *= currentHeight;
|
||||
|
||||
return scaleFactor;
|
||||
}
|
||||
}
|
||||
}
|
148
MediaBrowser.Api/Drawing/ImageProcessor.cs
Normal file
@ -0,0 +1,148 @@
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace MediaBrowser.Api.Drawing
|
||||
{
|
||||
public static class ImageProcessor
|
||||
{
|
||||
/// <summary>
|
||||
/// Processes an image by resizing to target dimensions
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity that owns the image</param>
|
||||
/// <param name="imageType">The image type</param>
|
||||
/// <param name="imageIndex">The image index (currently only used with backdrops)</param>
|
||||
/// <param name="toStream">The stream to save the new image to</param>
|
||||
/// <param name="width">Use if a fixed width is required. Aspect ratio will be preserved.</param>
|
||||
/// <param name="height">Use if a fixed height is required. Aspect ratio will be preserved.</param>
|
||||
/// <param name="maxWidth">Use if a max width is required. Aspect ratio will be preserved.</param>
|
||||
/// <param name="maxHeight">Use if a max height is required. Aspect ratio will be preserved.</param>
|
||||
/// <param name="quality">Quality level, from 0-100. Currently only applies to JPG. The default value should suffice.</param>
|
||||
public static void ProcessImage(BaseEntity entity, ImageType imageType, int imageIndex, Stream toStream, int? width, int? height, int? maxWidth, int? maxHeight, int? quality)
|
||||
{
|
||||
Image originalImage = Image.FromFile(GetImagePath(entity, imageType, imageIndex));
|
||||
|
||||
// Determine the output size based on incoming parameters
|
||||
Size newSize = DrawingUtils.Resize(originalImage.Size, width, height, maxWidth, maxHeight);
|
||||
|
||||
Bitmap thumbnail;
|
||||
|
||||
// Graphics.FromImage will throw an exception if the PixelFormat is Indexed, so we need to handle that here
|
||||
if (originalImage.PixelFormat.HasFlag(PixelFormat.Indexed))
|
||||
{
|
||||
thumbnail = new Bitmap(originalImage, newSize.Width, newSize.Height);
|
||||
}
|
||||
else
|
||||
{
|
||||
thumbnail = new Bitmap(newSize.Width, newSize.Height, originalImage.PixelFormat);
|
||||
}
|
||||
|
||||
thumbnail.MakeTransparent();
|
||||
|
||||
// Preserve the original resolution
|
||||
thumbnail.SetResolution(originalImage.HorizontalResolution, originalImage.VerticalResolution);
|
||||
|
||||
Graphics thumbnailGraph = Graphics.FromImage(thumbnail);
|
||||
|
||||
thumbnailGraph.CompositingQuality = CompositingQuality.HighQuality;
|
||||
thumbnailGraph.SmoothingMode = SmoothingMode.HighQuality;
|
||||
thumbnailGraph.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
||||
thumbnailGraph.PixelOffsetMode = PixelOffsetMode.HighQuality;
|
||||
thumbnailGraph.CompositingMode = CompositingMode.SourceOver;
|
||||
|
||||
thumbnailGraph.DrawImage(originalImage, 0, 0, newSize.Width, newSize.Height);
|
||||
|
||||
ImageFormat outputFormat = originalImage.RawFormat;
|
||||
|
||||
// Write to the output stream
|
||||
SaveImage(outputFormat, thumbnail, toStream, quality);
|
||||
|
||||
thumbnailGraph.Dispose();
|
||||
thumbnail.Dispose();
|
||||
originalImage.Dispose();
|
||||
}
|
||||
|
||||
public static string GetImagePath(BaseEntity entity, ImageType imageType, int imageIndex)
|
||||
{
|
||||
var item = entity as BaseItem;
|
||||
|
||||
if (item != null)
|
||||
{
|
||||
if (imageType == ImageType.Logo)
|
||||
{
|
||||
return item.LogoImagePath;
|
||||
}
|
||||
if (imageType == ImageType.Backdrop)
|
||||
{
|
||||
return item.BackdropImagePaths.ElementAt(imageIndex);
|
||||
}
|
||||
if (imageType == ImageType.Banner)
|
||||
{
|
||||
return item.BannerImagePath;
|
||||
}
|
||||
if (imageType == ImageType.Art)
|
||||
{
|
||||
return item.ArtImagePath;
|
||||
}
|
||||
if (imageType == ImageType.Thumbnail)
|
||||
{
|
||||
return item.ThumbnailImagePath;
|
||||
}
|
||||
}
|
||||
|
||||
return entity.PrimaryImagePath;
|
||||
}
|
||||
|
||||
public static void SaveImage(ImageFormat outputFormat, Image newImage, Stream toStream, int? quality)
|
||||
{
|
||||
// Use special save methods for jpeg and png that will result in a much higher quality image
|
||||
// All other formats use the generic Image.Save
|
||||
if (ImageFormat.Jpeg.Equals(outputFormat))
|
||||
{
|
||||
SaveJpeg(newImage, toStream, quality);
|
||||
}
|
||||
else if (ImageFormat.Png.Equals(outputFormat))
|
||||
{
|
||||
newImage.Save(toStream, ImageFormat.Png);
|
||||
}
|
||||
else
|
||||
{
|
||||
newImage.Save(toStream, outputFormat);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SaveJpeg(Image image, Stream target, int? quality)
|
||||
{
|
||||
if (!quality.HasValue)
|
||||
{
|
||||
quality = 90;
|
||||
}
|
||||
|
||||
using (var encoderParameters = new EncoderParameters(1))
|
||||
{
|
||||
encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, quality.Value);
|
||||
image.Save(target, GetImageCodecInfo("image/jpeg"), encoderParameters);
|
||||
}
|
||||
}
|
||||
|
||||
public static ImageCodecInfo GetImageCodecInfo(string mimeType)
|
||||
{
|
||||
ImageCodecInfo[] info = ImageCodecInfo.GetImageEncoders();
|
||||
|
||||
for (int i = 0; i < info.Length; i++)
|
||||
{
|
||||
ImageCodecInfo ici = info[i];
|
||||
if (ici.MimeType.Equals(mimeType, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return ici;
|
||||
}
|
||||
}
|
||||
return info[1];
|
||||
}
|
||||
}
|
||||
}
|
@ -105,7 +105,7 @@
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>xcopy "$(TargetPath)" "$(SolutionDir)\ProgramData\Plugins\" /y</PostBuildEvent>
|
||||
<PostBuildEvent>xcopy "$(TargetPath)" "$(SolutionDir)\ProgramData-Server\Plugins\" /y</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
|
@ -1,5 +1,6 @@
|
||||
using MediaBrowser.Common.Events;
|
||||
using MediaBrowser.Common.Logging;
|
||||
using MediaBrowser.Common.Mef;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Common.Net.Handlers;
|
||||
using MediaBrowser.Common.Plugins;
|
||||
@ -10,6 +11,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.ComponentModel.Composition.Hosting;
|
||||
using System.ComponentModel.Composition.Primitives;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
@ -88,6 +90,9 @@ namespace MediaBrowser.Common.Kernel
|
||||
/// </summary>
|
||||
private IDisposable HttpListener { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the MEF CompositionContainer
|
||||
/// </summary>
|
||||
private CompositionContainer CompositionContainer { get; set; }
|
||||
|
||||
protected virtual string HttpServerUrlPrefix
|
||||
@ -184,18 +189,20 @@ namespace MediaBrowser.Common.Kernel
|
||||
// This will prevent the .dll file from getting locked, and allow us to replace it when needed
|
||||
IEnumerable<Assembly> pluginAssemblies = Directory.GetFiles(ApplicationPaths.PluginsPath, "*.dll", SearchOption.TopDirectoryOnly).Select(f => Assembly.Load(File.ReadAllBytes((f))));
|
||||
|
||||
var catalog = new AggregateCatalog(pluginAssemblies.Select(a => new AssemblyCatalog(a)));
|
||||
var catalogs = new List<ComposablePartCatalog>();
|
||||
|
||||
catalogs.AddRange(pluginAssemblies.Select(a => new AssemblyCatalog(a)));
|
||||
|
||||
// Include composable parts in the Common assembly
|
||||
catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
|
||||
catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
|
||||
|
||||
if (includeCurrentAssembly)
|
||||
{
|
||||
// Include composable parts in the subclass assembly
|
||||
catalog.Catalogs.Add(new AssemblyCatalog(GetType().Assembly));
|
||||
catalogs.Add(new AssemblyCatalog(GetType().Assembly));
|
||||
}
|
||||
|
||||
return new CompositionContainer(catalog);
|
||||
return MefUtils.GetSafeCompositionContainer(catalogs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -86,6 +86,7 @@
|
||||
<Compile Include="Logging\BaseLogger.cs" />
|
||||
<Compile Include="Logging\LogSeverity.cs" />
|
||||
<Compile Include="Logging\TraceFileLogger.cs" />
|
||||
<Compile Include="Mef\MefUtils.cs" />
|
||||
<Compile Include="Net\Handlers\StaticFileHandler.cs" />
|
||||
<Compile Include="Net\MimeTypes.cs" />
|
||||
<Compile Include="Plugins\BaseTheme.cs" />
|
||||
|
43
MediaBrowser.Common/Mef/MefUtils.cs
Normal file
@ -0,0 +1,43 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition.Hosting;
|
||||
using System.ComponentModel.Composition.Primitives;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace MediaBrowser.Common.Mef
|
||||
{
|
||||
public static class MefUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Plugins that live on both the server and UI are going to have references to assemblies from both sides.
|
||||
/// But looks for Parts on one side, it will throw an exception when it seems Types from the other side that it doesn't have a reference to.
|
||||
/// For example, a plugin provides a Resolver. When MEF runs in the UI, it will throw an exception when it sees the resolver because there won't be a reference to the base class.
|
||||
/// This method will catch those exceptions while retining the list of Types that MEF is able to resolve.
|
||||
/// </summary>
|
||||
public static CompositionContainer GetSafeCompositionContainer(IEnumerable<ComposablePartCatalog> catalogs)
|
||||
{
|
||||
var newList = new List<ComposablePartCatalog>();
|
||||
|
||||
// Go through each Catalog
|
||||
foreach (var catalog in catalogs)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Try to have MEF find Parts
|
||||
catalog.Parts.ToArray();
|
||||
|
||||
// If it succeeds we can use the entire catalog
|
||||
newList.Add(catalog);
|
||||
}
|
||||
catch (ReflectionTypeLoadException ex)
|
||||
{
|
||||
// If it fails we can still get a list of the Types it was able to resolve and create TypeCatalogs
|
||||
var typeCatalogs = ex.Types.Where(t => t != null).Select(t => new TypeCatalog(t));
|
||||
newList.AddRange(typeCatalogs);
|
||||
}
|
||||
}
|
||||
|
||||
return new CompositionContainer(new AggregateCatalog(newList));
|
||||
}
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ using MediaBrowser.Common.Serialization;
|
||||
using MediaBrowser.Model.Plugins;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
namespace MediaBrowser.Common.Plugins
|
||||
{
|
||||
@ -12,7 +13,7 @@ namespace MediaBrowser.Common.Plugins
|
||||
/// </summary>
|
||||
public abstract class BasePlugin : IDisposable
|
||||
{
|
||||
private IKernel Kernel { get; set; }
|
||||
protected IKernel Kernel { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the plugin's current context
|
||||
|
@ -1,4 +1,12 @@
|
||||
|
||||
using MediaBrowser.Common.Mef;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.ComponentModel.Composition.Hosting;
|
||||
using System.ComponentModel.Composition.Primitives;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace MediaBrowser.Common.Plugins
|
||||
{
|
||||
public abstract class BaseTheme : BasePlugin
|
||||
@ -10,5 +18,61 @@ namespace MediaBrowser.Common.Plugins
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the MEF CompositionContainer
|
||||
/// </summary>
|
||||
private CompositionContainer CompositionContainer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of global resources
|
||||
/// </summary>
|
||||
[ImportMany(typeof(ResourceDictionary))]
|
||||
public IEnumerable<ResourceDictionary> GlobalResources { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of pages
|
||||
/// </summary>
|
||||
[ImportMany(typeof(Page))]
|
||||
public IEnumerable<Page> Pages { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pack Uri of the Login page
|
||||
/// </summary>
|
||||
public abstract Uri LoginPageUri { get; }
|
||||
|
||||
protected override void InitializeInUi()
|
||||
{
|
||||
base.InitializeInUi();
|
||||
|
||||
ComposeParts();
|
||||
}
|
||||
|
||||
private void ComposeParts()
|
||||
{
|
||||
var catalog = new AssemblyCatalog(GetType().Assembly);
|
||||
|
||||
CompositionContainer = MefUtils.GetSafeCompositionContainer(new ComposablePartCatalog[] { catalog });
|
||||
|
||||
CompositionContainer.ComposeParts(this);
|
||||
|
||||
CompositionContainer.Catalog.Dispose();
|
||||
}
|
||||
|
||||
protected override void DisposeInUi()
|
||||
{
|
||||
base.DisposeInUi();
|
||||
|
||||
CompositionContainer.Dispose();
|
||||
}
|
||||
|
||||
protected Uri GeneratePackUri(string relativePath)
|
||||
{
|
||||
string assemblyName = GetType().Assembly.GetName().Name;
|
||||
|
||||
string uri = string.Format("pack://application:,,,/{0};component/{1}", assemblyName, relativePath);
|
||||
|
||||
return new Uri(uri, UriKind.Absolute);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
using MediaBrowser.Common.Kernel;
|
||||
using MediaBrowser.Common.Logging;
|
||||
using MediaBrowser.Controller.Drawing;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.IO;
|
||||
|
@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace MediaBrowser.Plugins.DefaultTheme.Converters
|
||||
{
|
||||
public class TileBackgroundConverter : IValueConverter
|
||||
{
|
||||
private static readonly Brush[] TileColors = new Brush[] {
|
||||
new SolidColorBrush(Color.FromRgb((byte)111,(byte)189,(byte)69)),
|
||||
new SolidColorBrush(Color.FromRgb((byte)75,(byte)179,(byte)221)),
|
||||
new SolidColorBrush(Color.FromRgb((byte)65,(byte)100,(byte)165)),
|
||||
new SolidColorBrush(Color.FromRgb((byte)225,(byte)32,(byte)38)),
|
||||
new SolidColorBrush(Color.FromRgb((byte)128,(byte)0,(byte)128)),
|
||||
new SolidColorBrush(Color.FromRgb((byte)0,(byte)128,(byte)64)),
|
||||
new SolidColorBrush(Color.FromRgb((byte)0,(byte)148,(byte)255)),
|
||||
new SolidColorBrush(Color.FromRgb((byte)255,(byte)0,(byte)199)),
|
||||
new SolidColorBrush(Color.FromRgb((byte)255,(byte)135,(byte)15)),
|
||||
new SolidColorBrush(Color.FromRgb((byte)127,(byte)0,(byte)55))
|
||||
|
||||
};
|
||||
|
||||
private static int _currentIndex = new Random(DateTime.Now.Millisecond).Next(0, TileColors.Length);
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
int index;
|
||||
|
||||
lock (TileColors)
|
||||
{
|
||||
index = (_currentIndex++) % TileColors.Length;
|
||||
}
|
||||
|
||||
return TileColors[index++];
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
using MediaBrowser.Model.Weather;
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace MediaBrowser.Plugins.DefaultTheme.Converters
|
||||
{
|
||||
[PartNotDiscoverable]
|
||||
public class WeatherImageConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
var weather = value as WeatherInfo;
|
||||
|
||||
if (weather != null)
|
||||
{
|
||||
switch (weather.CurrentWeather.Condition)
|
||||
{
|
||||
case WeatherConditions.Thunderstorm:
|
||||
return "../Images/Weather/Thunder.png";
|
||||
case WeatherConditions.Overcast:
|
||||
return "../Images/Weather/Overcast.png";
|
||||
case WeatherConditions.Mist:
|
||||
case WeatherConditions.Sleet:
|
||||
case WeatherConditions.Rain:
|
||||
return "../Images/Weather/Rain.png";
|
||||
case WeatherConditions.Blizzard:
|
||||
case WeatherConditions.Snow:
|
||||
return "../Images/Weather/Snow.png";
|
||||
default:
|
||||
return "../Images/Weather/Sunny.png";
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
@ -31,6 +31,9 @@
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<RunPostBuildEvent>Always</RunPostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.ComponentModel.Composition" />
|
||||
@ -48,6 +51,12 @@
|
||||
<Reference Include="PresentationFramework" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Pages\LoginPage.xaml.cs">
|
||||
<DependentUpon>LoginPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Resources\AppResources.cs" />
|
||||
<Compile Include="Converters\TileBackgroundConverter.cs" />
|
||||
<Compile Include="Converters\WeatherImageConverter.cs" />
|
||||
<Compile Include="Plugin.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs">
|
||||
<SubType>Code</SubType>
|
||||
@ -90,9 +99,28 @@
|
||||
<Name>MediaBrowser.UI</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Include="Pages\LoginPage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Resources\AppResources.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="Resources\Images\CurrentUserDefault.png" />
|
||||
<Resource Include="Resources\Images\UserLoginDefault.png" />
|
||||
<Resource Include="Resources\Images\Weather\Overcast.png" />
|
||||
<Resource Include="Resources\Images\Weather\Rain.png" />
|
||||
<Resource Include="Resources\Images\Weather\Snow.png" />
|
||||
<Resource Include="Resources\Images\Weather\Sunny.png" />
|
||||
<Resource Include="Resources\Images\Weather\Thunder.png" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>xcopy "$(TargetPath)" "$(SolutionDir)\ProgramData\Plugins\" /y</PostBuildEvent>
|
||||
<PostBuildEvent>xcopy "$(TargetPath)" "$(SolutionDir)\ProgramData-Server\Plugins\" /y</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
|
64
MediaBrowser.Plugins.DefaultTheme/Pages/LoginPage.xaml
Normal file
@ -0,0 +1,64 @@
|
||||
<base:BaseLoginPage x:Class="MediaBrowser.Plugins.DefaultTheme.Pages.LoginPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:base="clr-namespace:MediaBrowser.UI.Pages;assembly=MediaBrowser.UI"
|
||||
xmlns:DTO="clr-namespace:MediaBrowser.Model.DTO;assembly=MediaBrowser.Model"
|
||||
xmlns:controls="clr-namespace:MediaBrowser.UI.Controls;assembly=MediaBrowser.UI" mc:Ignorable="d"
|
||||
d:DesignHeight="300"
|
||||
d:DesignWidth="300"
|
||||
Title="LoginPage">
|
||||
|
||||
<Page.Resources>
|
||||
<ResourceDictionary>
|
||||
<DataTemplate DataType="{x:Type DTO:DtoUser}">
|
||||
<Grid HorizontalAlignment="Left" Margin="3">
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"></ColumnDefinition>
|
||||
<ColumnDefinition Width="475"></ColumnDefinition>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<controls:ExtendedImage HasImage="{Binding HasImage}"
|
||||
PlaceHolderSource="../Resources/Images/UserLoginDefault.png"
|
||||
Source="{Binding Converter={StaticResource UserImageConverter}, ConverterParameter='225,225,0,0'}"
|
||||
Stretch="Uniform"
|
||||
Width="225"
|
||||
Height="225"
|
||||
Background="{Binding Converter={StaticResource TileBackgroundConverter}}"/>
|
||||
<TextBlock Text="{Binding Name}" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="1" Grid.Row="0" Margin="25 30 0 0" FontSize="{StaticResource Heading2FontSize}"></TextBlock>
|
||||
<TextBlock Text="{Binding Converter={StaticResource LastSeenTextConverter}}" VerticalAlignment="Center" HorizontalAlignment="Left" Grid.Column="1" Grid.Row="0" Margin="25 80 0 0"></TextBlock>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
|
||||
</ResourceDictionary>
|
||||
</Page.Resources>
|
||||
<Grid>
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto"></RowDefinition>
|
||||
<RowDefinition Height="*"></RowDefinition>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Image Style="{StaticResource MBLogoImageBlack}" Margin="0 0 0 10" Height="125" Stretch="Uniform" HorizontalAlignment="Left"></Image>
|
||||
|
||||
<Grid VerticalAlignment="Stretch" HorizontalAlignment="Center" Grid.Row="1">
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto"></RowDefinition>
|
||||
<RowDefinition Height="*"></RowDefinition>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock FontSize="{StaticResource Heading2FontSize}" Grid.Row="0" Margin="0 0 0 30">Select Profile</TextBlock>
|
||||
|
||||
<ListView HorizontalAlignment="Center" Grid.Row="1" ScrollViewer.VerticalScrollBarVisibility="Disabled" ScrollViewer.HorizontalScrollBarVisibility="Hidden" ItemsSource="{Binding Path=Users}" Style="{StaticResource ListViewStyle}" ItemContainerStyle="{StaticResource ListViewItemStyle}">
|
||||
<ListView.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<WrapPanel Orientation="Vertical" />
|
||||
</ItemsPanelTemplate>
|
||||
</ListView.ItemsPanel>
|
||||
</ListView>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</base:BaseLoginPage>
|
15
MediaBrowser.Plugins.DefaultTheme/Pages/LoginPage.xaml.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using MediaBrowser.UI.Pages;
|
||||
|
||||
namespace MediaBrowser.Plugins.DefaultTheme.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for LoginPage.xaml
|
||||
/// </summary>
|
||||
public partial class LoginPage : BaseLoginPage
|
||||
{
|
||||
public LoginPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using MediaBrowser.Common.Plugins;
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
|
||||
namespace MediaBrowser.Plugins.DefaultTheme
|
||||
@ -11,9 +12,9 @@ namespace MediaBrowser.Plugins.DefaultTheme
|
||||
get { return "Default Theme"; }
|
||||
}
|
||||
|
||||
protected override void InitializeInUi()
|
||||
public override Uri LoginPageUri
|
||||
{
|
||||
base.InitializeInUi();
|
||||
get { return GeneratePackUri("Pages/LoginPage.xaml"); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
14
MediaBrowser.Plugins.DefaultTheme/Resources/AppResources.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Windows;
|
||||
|
||||
namespace MediaBrowser.Plugins.DefaultTheme.Resources
|
||||
{
|
||||
[Export(typeof(ResourceDictionary))]
|
||||
public partial class AppResources : ResourceDictionary
|
||||
{
|
||||
public AppResources()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:themeconverters="clr-namespace:MediaBrowser.Plugins.DefaultTheme.Converters"
|
||||
x:Class="MediaBrowser.Plugins.DefaultTheme.Resources.AppResources">
|
||||
|
||||
<themeconverters:WeatherImageConverter x:Key="WeatherImageConverter"></themeconverters:WeatherImageConverter>
|
||||
<themeconverters:TileBackgroundConverter x:Key="TileBackgroundConverter"></themeconverters:TileBackgroundConverter>
|
||||
|
||||
<Style x:Key="ListViewItemStyle" TargetType="{x:Type ListViewItem}" BasedOn="{StaticResource BaseListViewItemStyle}">
|
||||
|
||||
<Style.Resources>
|
||||
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="LightBlue"/>
|
||||
</Style.Resources>
|
||||
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Opacity" Value=".85" />
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
<!--Override MainWindow style-->
|
||||
<Style TargetType="Window" x:Key="MainWindow" BasedOn="{StaticResource BaseWindow}">
|
||||
<Setter Property="Background">
|
||||
<Setter.Value>
|
||||
<RadialGradientBrush RadiusX=".75" RadiusY=".75">
|
||||
<GradientStop Color="White" Offset="0.0"/>
|
||||
<GradientStop Color="WhiteSmoke" Offset="0.5"/>
|
||||
<GradientStop Color="#cfcfcf" Offset="1.0"/>
|
||||
</RadialGradientBrush>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!--Override PageContentTemplate-->
|
||||
<ControlTemplate x:Key="PageContentTemplate">
|
||||
|
||||
<Grid Margin="20 15 20 20">
|
||||
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Right">
|
||||
|
||||
<!--Display CurrentUser-->
|
||||
<StackPanel Orientation="Horizontal" Margin="0 0 30 0" Visibility="{Binding Path=CurrentUser,Converter={StaticResource CurrentUserVisibilityConverter}}">
|
||||
<TextBlock FontSize="{StaticResource Heading2FontSize}" Text="{Binding Path=CurrentUser.Name}" Margin="0 0 5 0">
|
||||
</TextBlock>
|
||||
<Image>
|
||||
<Image.Style>
|
||||
<Style TargetType="{x:Type Image}">
|
||||
<Setter Property="Image.Source" Value="Images\CurrentUserDefault.png" />
|
||||
<Setter Property="Stretch" Value="None" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding Path=CurrentUser.HasImage}" Value="true">
|
||||
<Setter Property="Image.Source" Value="{Binding Path=CurrentUser,Converter={StaticResource UserImageConverter}, ConverterParameter='0,64,0,0'}" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Image.Style>
|
||||
</Image>
|
||||
</StackPanel>
|
||||
|
||||
<!--Display Weather-->
|
||||
<StackPanel Orientation="Horizontal" Margin="0 0 30 0" Visibility="{Binding Path=CurrentWeather,Converter={StaticResource WeatherVisibilityConverter}}">
|
||||
|
||||
<TextBlock FontSize="{StaticResource Heading2FontSize}" Text="{Binding Path=CurrentWeather,Converter={StaticResource WeatherTemperatureConverter}}" Margin="0 0 5 0">
|
||||
</TextBlock>
|
||||
<Image Stretch="None" Source="{Binding Path=CurrentWeather,Converter={StaticResource WeatherImageConverter}}"></Image>
|
||||
</StackPanel>
|
||||
|
||||
<!--Display Clock-->
|
||||
<TextBlock FontSize="{StaticResource Heading2FontSize}">
|
||||
<TextBlock.Text>
|
||||
<Binding Path="CurrentTime" Converter="{StaticResource DateTimeToStringConverter}" ConverterParameter="h:mm" />
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<Frame x:Name="PageFrame"></Frame>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
|
||||
</ResourceDictionary>
|
After Width: | Height: | Size: 968 B |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.1 KiB |
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<appSettings>
|
||||
<add key="ProgramDataPath" value="..\..\..\ProgramData" />
|
||||
<add key="ProgramDataPath" value="..\..\..\ProgramData-Server" />
|
||||
</appSettings>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
|
||||
|
@ -1,11 +1,7 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 2012
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Plugins.DefaultTheme", "MediaBrowser.Plugins.DefaultTheme\MediaBrowser.Plugins.DefaultTheme.csproj", "{6E892999-711D-4E24-8BAC-DACF5BFA783A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Controller", "MediaBrowser.Controller\MediaBrowser.Controller.csproj", "{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.UI", "..\MediaBrowserUI\MediaBrowser.UI\MediaBrowser.UI.csproj", "{B5ECE1FB-618E-420B-9A99-8E972D76920A}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.UI", "MediaBrowser.UI\MediaBrowser.UI.csproj", "{B5ECE1FB-618E-420B-9A99-8E972D76920A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Common", "MediaBrowser.Common\MediaBrowser.Common.csproj", "{9142EEFA-7570-41E1-BFCC-468BB571AF2F}"
|
||||
EndProject
|
||||
@ -13,36 +9,48 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Model", "Media
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.ApiInteraction", "MediaBrowser.ApiInteraction\MediaBrowser.ApiInteraction.csproj", "{921C0F64-FDA7-4E9F-9E73-0CB0EEDB2422}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Plugins.DefaultTheme", "MediaBrowser.Plugins.DefaultTheme\MediaBrowser.Plugins.DefaultTheme.csproj", "{6E892999-711D-4E24-8BAC-DACF5BFA783A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Controller", "MediaBrowser.Controller\MediaBrowser.Controller.csproj", "{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}"
|
||||
EndProject
|
||||
Global
|
||||
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
|
||||
{6E892999-711D-4E24-8BAC-DACF5BFA783A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6E892999-711D-4E24-8BAC-DACF5BFA783A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6E892999-711D-4E24-8BAC-DACF5BFA783A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6E892999-711D-4E24-8BAC-DACF5BFA783A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B5ECE1FB-618E-420B-9A99-8E972D76920A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B5ECE1FB-618E-420B-9A99-8E972D76920A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B5ECE1FB-618E-420B-9A99-8E972D76920A}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{B5ECE1FB-618E-420B-9A99-8E972D76920A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B5ECE1FB-618E-420B-9A99-8E972D76920A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B5ECE1FB-618E-420B-9A99-8E972D76920A}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{921C0F64-FDA7-4E9F-9E73-0CB0EEDB2422}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{921C0F64-FDA7-4E9F-9E73-0CB0EEDB2422}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{921C0F64-FDA7-4E9F-9E73-0CB0EEDB2422}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{921C0F64-FDA7-4E9F-9E73-0CB0EEDB2422}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{921C0F64-FDA7-4E9F-9E73-0CB0EEDB2422}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{921C0F64-FDA7-4E9F-9E73-0CB0EEDB2422}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{6E892999-711D-4E24-8BAC-DACF5BFA783A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6E892999-711D-4E24-8BAC-DACF5BFA783A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6E892999-711D-4E24-8BAC-DACF5BFA783A}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{6E892999-711D-4E24-8BAC-DACF5BFA783A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6E892999-711D-4E24-8BAC-DACF5BFA783A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6E892999-711D-4E24-8BAC-DACF5BFA783A}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
9
MediaBrowser.UI/App.config
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<appSettings>
|
||||
<add key="ProgramDataPath" value="..\..\..\ProgramData-UI" />
|
||||
</appSettings>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
|
||||
</startup>
|
||||
</configuration>
|
14
MediaBrowser.UI/App.xaml
Normal file
@ -0,0 +1,14 @@
|
||||
<z:BaseApplication x:Class="MediaBrowser.UI.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:z="clr-namespace:MediaBrowser.Common.UI;assembly=MediaBrowser.Common">
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="Resources/AppResources.xaml" />
|
||||
<ResourceDictionary Source="Resources/MainWindowResources.xaml" />
|
||||
<ResourceDictionary Source="Resources/NavBarResources.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
</z:BaseApplication>
|
213
MediaBrowser.UI/App.xaml.cs
Normal file
@ -0,0 +1,213 @@
|
||||
using MediaBrowser.Common.Kernel;
|
||||
using MediaBrowser.Common.Plugins;
|
||||
using MediaBrowser.Common.UI;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
using MediaBrowser.Model.DTO;
|
||||
using MediaBrowser.Model.Weather;
|
||||
using MediaBrowser.UI.Controller;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace MediaBrowser.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for App.xaml
|
||||
/// </summary>
|
||||
public partial class App : BaseApplication, IApplication
|
||||
{
|
||||
private Timer ClockTimer { get; set; }
|
||||
private Timer ServerConfigurationTimer { get; set; }
|
||||
|
||||
public static App Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
return Application.Current as App;
|
||||
}
|
||||
}
|
||||
|
||||
public DtoUser CurrentUser
|
||||
{
|
||||
get
|
||||
{
|
||||
return UIKernel.Instance.CurrentUser;
|
||||
}
|
||||
set
|
||||
{
|
||||
UIKernel.Instance.CurrentUser = value;
|
||||
OnPropertyChanged("CurrentUser");
|
||||
}
|
||||
}
|
||||
|
||||
public ServerConfiguration ServerConfiguration
|
||||
{
|
||||
get
|
||||
{
|
||||
return UIKernel.Instance.ServerConfiguration;
|
||||
}
|
||||
set
|
||||
{
|
||||
UIKernel.Instance.ServerConfiguration = value;
|
||||
OnPropertyChanged("ServerConfiguration");
|
||||
}
|
||||
}
|
||||
|
||||
private DateTime _currentTime = DateTime.Now;
|
||||
public DateTime CurrentTime
|
||||
{
|
||||
get
|
||||
{
|
||||
return _currentTime;
|
||||
}
|
||||
private set
|
||||
{
|
||||
_currentTime = value;
|
||||
OnPropertyChanged("CurrentTime");
|
||||
}
|
||||
}
|
||||
|
||||
private WeatherInfo _currentWeather;
|
||||
public WeatherInfo CurrentWeather
|
||||
{
|
||||
get
|
||||
{
|
||||
return _currentWeather;
|
||||
}
|
||||
private set
|
||||
{
|
||||
_currentWeather = value;
|
||||
OnPropertyChanged("CurrentWeather");
|
||||
}
|
||||
}
|
||||
|
||||
private BaseTheme _currentTheme;
|
||||
public BaseTheme CurrentTheme
|
||||
{
|
||||
get
|
||||
{
|
||||
return _currentTheme;
|
||||
}
|
||||
private set
|
||||
{
|
||||
_currentTheme = value;
|
||||
OnPropertyChanged("CurrentTheme");
|
||||
}
|
||||
}
|
||||
|
||||
[STAThread]
|
||||
public static void Main()
|
||||
{
|
||||
RunApplication<App>("MediaBrowserUI");
|
||||
}
|
||||
|
||||
#region BaseApplication Overrides
|
||||
protected override IKernel InstantiateKernel()
|
||||
{
|
||||
return new UIKernel();
|
||||
}
|
||||
|
||||
protected override Window InstantiateMainWindow()
|
||||
{
|
||||
return new MainWindow();
|
||||
}
|
||||
|
||||
protected override void OnKernelLoaded()
|
||||
{
|
||||
base.OnKernelLoaded();
|
||||
|
||||
PropertyChanged += AppPropertyChanged;
|
||||
|
||||
// Update every 10 seconds
|
||||
ClockTimer = new Timer(ClockTimerCallback, null, 0, 10000);
|
||||
|
||||
// Update every 30 minutes
|
||||
ServerConfigurationTimer = new Timer(ServerConfigurationTimerCallback, null, 0, 1800000);
|
||||
|
||||
CurrentTheme = UIKernel.Instance.Plugins.OfType<BaseTheme>().First();
|
||||
|
||||
foreach (var resource in CurrentTheme.GlobalResources)
|
||||
{
|
||||
Resources.MergedDictionaries.Add(resource);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
async void AppPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName.Equals("ServerConfiguration"))
|
||||
{
|
||||
if (string.IsNullOrEmpty(ServerConfiguration.WeatherZipCode))
|
||||
{
|
||||
CurrentWeather = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentWeather = await UIKernel.Instance.ApiClient.GetWeatherInfoAsync(ServerConfiguration.WeatherZipCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ClockTimerCallback(object stateInfo)
|
||||
{
|
||||
CurrentTime = DateTime.Now;
|
||||
}
|
||||
|
||||
private async void ServerConfigurationTimerCallback(object stateInfo)
|
||||
{
|
||||
ServerConfiguration = await UIKernel.Instance.ApiClient.GetServerConfigurationAsync();
|
||||
}
|
||||
|
||||
public async Task<Image> GetImage(string url)
|
||||
{
|
||||
var image = new Image();
|
||||
|
||||
image.Source = await GetBitmapImage(url);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
public async Task<BitmapImage> GetBitmapImage(string url)
|
||||
{
|
||||
Stream stream = await UIKernel.Instance.ApiClient.GetImageStreamAsync(url);
|
||||
|
||||
BitmapImage bitmap = new BitmapImage();
|
||||
|
||||
bitmap.CacheOption = BitmapCacheOption.Default;
|
||||
|
||||
bitmap.BeginInit();
|
||||
bitmap.StreamSource = stream;
|
||||
bitmap.EndInit();
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
public async Task LogoutUser()
|
||||
{
|
||||
CurrentUser = null;
|
||||
|
||||
if (ServerConfiguration.EnableUserProfiles)
|
||||
{
|
||||
Navigate(CurrentTheme.LoginPageUri);
|
||||
}
|
||||
else
|
||||
{
|
||||
DtoUser defaultUser = await UIKernel.Instance.ApiClient.GetDefaultUserAsync();
|
||||
CurrentUser = defaultUser;
|
||||
|
||||
Navigate(new Uri("/Pages/HomePage.xaml", UriKind.Relative));
|
||||
}
|
||||
}
|
||||
|
||||
public void Navigate(Uri uri)
|
||||
{
|
||||
(MainWindow as MainWindow).Navigate(uri);
|
||||
}
|
||||
}
|
||||
}
|
27
MediaBrowser.UI/Configuration/UIApplicationConfiguration.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using MediaBrowser.Model.Configuration;
|
||||
|
||||
namespace MediaBrowser.UI.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// This is the UI's device configuration that applies regardless of which user is logged in.
|
||||
/// </summary>
|
||||
public class UIApplicationConfiguration : BaseApplicationConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the server host name (myserver or 192.168.x.x)
|
||||
/// </summary>
|
||||
public string ServerHostName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the port number used by the API
|
||||
/// </summary>
|
||||
public int ServerApiPort { get; set; }
|
||||
|
||||
public UIApplicationConfiguration()
|
||||
: base()
|
||||
{
|
||||
ServerHostName = "localhost";
|
||||
ServerApiPort = 8096;
|
||||
}
|
||||
}
|
||||
}
|
8
MediaBrowser.UI/Configuration/UIApplicationPaths.cs
Normal file
@ -0,0 +1,8 @@
|
||||
using MediaBrowser.Common.Kernel;
|
||||
|
||||
namespace MediaBrowser.UI.Configuration
|
||||
{
|
||||
public class UIApplicationPaths : BaseApplicationPaths
|
||||
{
|
||||
}
|
||||
}
|
231
MediaBrowser.UI/Controller/PluginUpdater.cs
Normal file
@ -0,0 +1,231 @@
|
||||
using MediaBrowser.Common.Logging;
|
||||
using MediaBrowser.Common.Plugins;
|
||||
using MediaBrowser.Common.Serialization;
|
||||
using MediaBrowser.Model.DTO;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.ComponentModel.Composition.Hosting;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.UI.Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// This keeps ui plugin assemblies in sync with plugins installed on the server
|
||||
/// </summary>
|
||||
public class PluginUpdater
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the list of currently installed UI plugins
|
||||
/// </summary>
|
||||
[ImportMany(typeof(BasePlugin))]
|
||||
private IEnumerable<BasePlugin> CurrentPlugins { get; set; }
|
||||
|
||||
private CompositionContainer CompositionContainer { get; set; }
|
||||
|
||||
public async Task<PluginUpdateResult> UpdatePlugins()
|
||||
{
|
||||
// First load the plugins that are currently installed
|
||||
ReloadComposableParts();
|
||||
|
||||
Logger.LogInfo("Downloading list of installed plugins");
|
||||
PluginInfo[] allInstalledPlugins = await UIKernel.Instance.ApiClient.GetInstalledPluginsAsync().ConfigureAwait(false);
|
||||
|
||||
IEnumerable<PluginInfo> uiPlugins = allInstalledPlugins.Where(p => p.DownloadToUI);
|
||||
|
||||
PluginUpdateResult result = new PluginUpdateResult();
|
||||
|
||||
result.DeletedPlugins = DeleteUninstalledPlugins(uiPlugins);
|
||||
|
||||
await DownloadPluginAssemblies(uiPlugins, result).ConfigureAwait(false);
|
||||
|
||||
// If any new assemblies were downloaded we'll have to reload the CurrentPlugins list
|
||||
if (result.NewlyInstalledPlugins.Any())
|
||||
{
|
||||
ReloadComposableParts();
|
||||
}
|
||||
|
||||
result.UpdatedConfigurations = await DownloadPluginConfigurations(uiPlugins).ConfigureAwait(false);
|
||||
|
||||
CompositionContainer.Dispose();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Downloads plugin assemblies from the server, if they need to be installed or updated.
|
||||
/// </summary>
|
||||
private async Task DownloadPluginAssemblies(IEnumerable<PluginInfo> uiPlugins, PluginUpdateResult result)
|
||||
{
|
||||
List<PluginInfo> newlyInstalledPlugins = new List<PluginInfo>();
|
||||
List<PluginInfo> updatedPlugins = new List<PluginInfo>();
|
||||
|
||||
// Loop through the list of plugins that are on the server
|
||||
foreach (PluginInfo pluginInfo in uiPlugins)
|
||||
{
|
||||
// See if it is already installed in the UI
|
||||
BasePlugin installedPlugin = CurrentPlugins.FirstOrDefault(p => p.AssemblyFileName.Equals(pluginInfo.AssemblyFileName, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
// Download the plugin if it is not present, or if the current version is out of date
|
||||
bool downloadPlugin = installedPlugin == null;
|
||||
|
||||
if (installedPlugin != null)
|
||||
{
|
||||
Version serverVersion = Version.Parse(pluginInfo.Version);
|
||||
|
||||
downloadPlugin = serverVersion > installedPlugin.Version;
|
||||
}
|
||||
|
||||
if (downloadPlugin)
|
||||
{
|
||||
await DownloadPlugin(pluginInfo).ConfigureAwait(false);
|
||||
|
||||
if (installedPlugin == null)
|
||||
{
|
||||
newlyInstalledPlugins.Add(pluginInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
updatedPlugins.Add(pluginInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.NewlyInstalledPlugins = newlyInstalledPlugins;
|
||||
result.UpdatedPlugins = updatedPlugins;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Downloads plugin configurations from the server.
|
||||
/// </summary>
|
||||
private async Task<List<PluginInfo>> DownloadPluginConfigurations(IEnumerable<PluginInfo> uiPlugins)
|
||||
{
|
||||
List<PluginInfo> updatedPlugins = new List<PluginInfo>();
|
||||
|
||||
// Loop through the list of plugins that are on the server
|
||||
foreach (PluginInfo pluginInfo in uiPlugins)
|
||||
{
|
||||
// See if it is already installed in the UI
|
||||
BasePlugin installedPlugin = CurrentPlugins.First(p => p.AssemblyFileName.Equals(pluginInfo.AssemblyFileName, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (installedPlugin.ConfigurationDateLastModified < pluginInfo.ConfigurationDateLastModified)
|
||||
{
|
||||
await DownloadPluginConfiguration(installedPlugin, pluginInfo).ConfigureAwait(false);
|
||||
|
||||
updatedPlugins.Add(pluginInfo);
|
||||
}
|
||||
}
|
||||
|
||||
return updatedPlugins;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Downloads a plugin assembly from the server
|
||||
/// </summary>
|
||||
private async Task DownloadPlugin(PluginInfo plugin)
|
||||
{
|
||||
Logger.LogInfo("Downloading {0} Plugin", plugin.Name);
|
||||
|
||||
string path = Path.Combine(UIKernel.Instance.ApplicationPaths.PluginsPath, plugin.AssemblyFileName);
|
||||
|
||||
// First download to a MemoryStream. This way if the download is cut off, we won't be left with a partial file
|
||||
using (MemoryStream memoryStream = new MemoryStream())
|
||||
{
|
||||
Stream assemblyStream = await UIKernel.Instance.ApiClient.GetPluginAssemblyAsync(plugin).ConfigureAwait(false);
|
||||
|
||||
await assemblyStream.CopyToAsync(memoryStream).ConfigureAwait(false);
|
||||
|
||||
memoryStream.Position = 0;
|
||||
|
||||
using (FileStream fileStream = new FileStream(path, FileMode.Create))
|
||||
{
|
||||
await memoryStream.CopyToAsync(fileStream).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Downloads the latest configuration for a plugin
|
||||
/// </summary>
|
||||
private async Task DownloadPluginConfiguration(BasePlugin plugin, PluginInfo pluginInfo)
|
||||
{
|
||||
Logger.LogInfo("Downloading {0} Configuration", plugin.Name);
|
||||
|
||||
object config = await UIKernel.Instance.ApiClient.GetPluginConfigurationAsync(pluginInfo, plugin.ConfigurationType).ConfigureAwait(false);
|
||||
|
||||
XmlSerializer.SerializeToFile(config, plugin.ConfigurationFilePath);
|
||||
|
||||
File.SetLastWriteTimeUtc(plugin.ConfigurationFilePath, pluginInfo.ConfigurationDateLastModified);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes any plugins that have been uninstalled from the server
|
||||
/// </summary>
|
||||
private IEnumerable<string> DeleteUninstalledPlugins(IEnumerable<PluginInfo> uiPlugins)
|
||||
{
|
||||
var deletedPlugins = new List<string>();
|
||||
|
||||
foreach (BasePlugin plugin in CurrentPlugins)
|
||||
{
|
||||
PluginInfo latest = uiPlugins.FirstOrDefault(p => p.AssemblyFileName.Equals(plugin.AssemblyFileName, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (latest == null)
|
||||
{
|
||||
DeletePlugin(plugin);
|
||||
|
||||
deletedPlugins.Add(plugin.Name);
|
||||
}
|
||||
}
|
||||
|
||||
return deletedPlugins;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes an installed ui plugin.
|
||||
/// Leaves config and data behind in the event it is later re-installed
|
||||
/// </summary>
|
||||
private void DeletePlugin(BasePlugin plugin)
|
||||
{
|
||||
Logger.LogInfo("Deleting {0} Plugin", plugin.Name);
|
||||
|
||||
string path = plugin.AssemblyFilePath;
|
||||
|
||||
if (File.Exists(path))
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Re-uses MEF within the kernel to discover installed plugins
|
||||
/// </summary>
|
||||
private void ReloadComposableParts()
|
||||
{
|
||||
if (CompositionContainer != null)
|
||||
{
|
||||
CompositionContainer.Dispose();
|
||||
}
|
||||
|
||||
CompositionContainer = UIKernel.Instance.GetCompositionContainer();
|
||||
|
||||
CompositionContainer.ComposeParts(this);
|
||||
|
||||
CompositionContainer.Catalog.Dispose();
|
||||
|
||||
foreach (BasePlugin plugin in CurrentPlugins)
|
||||
{
|
||||
plugin.Initialize(UIKernel.Instance, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class PluginUpdateResult
|
||||
{
|
||||
public IEnumerable<string> DeletedPlugins { get; set; }
|
||||
public IEnumerable<PluginInfo> NewlyInstalledPlugins { get; set; }
|
||||
public IEnumerable<PluginInfo> UpdatedPlugins { get; set; }
|
||||
public IEnumerable<PluginInfo> UpdatedConfigurations { get; set; }
|
||||
}
|
||||
}
|
97
MediaBrowser.UI/Controller/UIKernel.cs
Normal file
@ -0,0 +1,97 @@
|
||||
using MediaBrowser.ApiInteraction;
|
||||
using MediaBrowser.Common.Kernel;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
using MediaBrowser.Model.DTO;
|
||||
using MediaBrowser.Model.Progress;
|
||||
using MediaBrowser.UI.Configuration;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.UI.Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// This controls application logic as well as server interaction within the UI.
|
||||
/// </summary>
|
||||
public class UIKernel : BaseKernel<UIApplicationConfiguration, UIApplicationPaths>
|
||||
{
|
||||
public static UIKernel Instance { get; private set; }
|
||||
|
||||
public ApiClient ApiClient { get; private set; }
|
||||
public DtoUser CurrentUser { get; set; }
|
||||
public ServerConfiguration ServerConfiguration { get; set; }
|
||||
|
||||
public UIKernel()
|
||||
: base()
|
||||
{
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
public override KernelContext KernelContext
|
||||
{
|
||||
get { return KernelContext.Ui; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Give the UI a different url prefix so that they can share the same port, in case they are installed on the same machine.
|
||||
/// </summary>
|
||||
protected override string HttpServerUrlPrefix
|
||||
{
|
||||
get
|
||||
{
|
||||
return "http://+:" + Configuration.HttpServerPortNumber + "/mediabrowser/ui/";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs initializations that can be reloaded at anytime
|
||||
/// </summary>
|
||||
protected override async Task ReloadInternal(IProgress<TaskProgress> progress)
|
||||
{
|
||||
ReloadApiClient();
|
||||
|
||||
await new PluginUpdater().UpdatePlugins().ConfigureAwait(false);
|
||||
|
||||
await base.ReloadInternal(progress).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates and installs new plugin assemblies and configurations from the server
|
||||
/// </summary>
|
||||
protected async Task<PluginUpdateResult> UpdatePlugins()
|
||||
{
|
||||
return await new PluginUpdater().UpdatePlugins().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the current ApiClient and creates a new one
|
||||
/// </summary>
|
||||
private void ReloadApiClient()
|
||||
{
|
||||
DisposeApiClient();
|
||||
|
||||
ApiClient = new ApiClient
|
||||
{
|
||||
ServerHostName = Configuration.ServerHostName,
|
||||
ServerApiPort = Configuration.ServerApiPort
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the current ApiClient
|
||||
/// </summary>
|
||||
private void DisposeApiClient()
|
||||
{
|
||||
if (ApiClient != null)
|
||||
{
|
||||
ApiClient.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
base.Dispose();
|
||||
|
||||
DisposeApiClient();
|
||||
}
|
||||
}
|
||||
}
|
73
MediaBrowser.UI/Controls/EnhancedScrollViewer.cs
Normal file
@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace MediaBrowser.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a ScrollViewer that can be scrolled by dragging the mouse
|
||||
/// </summary>
|
||||
public class EnhancedScrollViewer : ScrollViewer
|
||||
{
|
||||
private Point _scrollTarget;
|
||||
private Point _scrollStartPoint;
|
||||
private Point _scrollStartOffset;
|
||||
private const int PixelsToMoveToBeConsideredScroll = 5;
|
||||
|
||||
protected override void OnPreviewMouseDown(MouseButtonEventArgs e)
|
||||
{
|
||||
if (IsMouseOver)
|
||||
{
|
||||
// Save starting point, used later when determining how much to scroll.
|
||||
_scrollStartPoint = e.GetPosition(this);
|
||||
_scrollStartOffset.X = HorizontalOffset;
|
||||
_scrollStartOffset.Y = VerticalOffset;
|
||||
|
||||
// Update the cursor if can scroll or not.
|
||||
Cursor = (ExtentWidth > ViewportWidth) ||
|
||||
(ExtentHeight > ViewportHeight) ?
|
||||
Cursors.ScrollAll : Cursors.Arrow;
|
||||
|
||||
CaptureMouse();
|
||||
}
|
||||
|
||||
base.OnPreviewMouseDown(e);
|
||||
}
|
||||
|
||||
protected override void OnPreviewMouseMove(MouseEventArgs e)
|
||||
{
|
||||
if (IsMouseCaptured)
|
||||
{
|
||||
Point currentPoint = e.GetPosition(this);
|
||||
|
||||
// Determine the new amount to scroll.
|
||||
var delta = new Point(_scrollStartPoint.X - currentPoint.X, _scrollStartPoint.Y - currentPoint.Y);
|
||||
|
||||
if (Math.Abs(delta.X) < PixelsToMoveToBeConsideredScroll &&
|
||||
Math.Abs(delta.Y) < PixelsToMoveToBeConsideredScroll)
|
||||
return;
|
||||
|
||||
_scrollTarget.X = _scrollStartOffset.X + delta.X;
|
||||
_scrollTarget.Y = _scrollStartOffset.Y + delta.Y;
|
||||
|
||||
// Scroll to the new position.
|
||||
ScrollToHorizontalOffset(_scrollTarget.X);
|
||||
ScrollToVerticalOffset(_scrollTarget.Y);
|
||||
}
|
||||
|
||||
base.OnPreviewMouseMove(e);
|
||||
}
|
||||
|
||||
protected override void OnPreviewMouseUp(MouseButtonEventArgs e)
|
||||
{
|
||||
if (IsMouseCaptured)
|
||||
{
|
||||
Cursor = Cursors.Arrow;
|
||||
ReleaseMouseCapture();
|
||||
}
|
||||
|
||||
base.OnPreviewMouseUp(e);
|
||||
}
|
||||
}
|
||||
}
|
92
MediaBrowser.UI/Controls/ExtendedImage.cs
Normal file
@ -0,0 +1,92 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace MediaBrowser.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Follow steps 1a or 1b and then 2 to use this custom control in a XAML file.
|
||||
///
|
||||
/// Step 1a) Using this custom control in a XAML file that exists in the current project.
|
||||
/// Add this XmlNamespace attribute to the root element of the markup file where it is
|
||||
/// to be used:
|
||||
///
|
||||
/// xmlns:MyNamespace="clr-namespace:MediaBrowser.UI.Controls"
|
||||
///
|
||||
///
|
||||
/// Step 1b) Using this custom control in a XAML file that exists in a different project.
|
||||
/// Add this XmlNamespace attribute to the root element of the markup file where it is
|
||||
/// to be used:
|
||||
///
|
||||
/// xmlns:MyNamespace="clr-namespace:MediaBrowser.UI.Controls;assembly=MediaBrowser.UI.Controls"
|
||||
///
|
||||
/// You will also need to add a project reference from the project where the XAML file lives
|
||||
/// to this project and Rebuild to avoid compilation errors:
|
||||
///
|
||||
/// Right click on the target project in the Solution Explorer and
|
||||
/// "Add Reference"->"Projects"->[Browse to and select this project]
|
||||
///
|
||||
///
|
||||
/// Step 2)
|
||||
/// Go ahead and use your control in the XAML file.
|
||||
///
|
||||
/// <MyNamespace:ExtendedImage/>
|
||||
///
|
||||
/// </summary>
|
||||
public class ExtendedImage : Control
|
||||
{
|
||||
public static readonly DependencyProperty HasImageProperty = DependencyProperty.Register(
|
||||
"HasImage",
|
||||
typeof (bool),
|
||||
typeof (ExtendedImage),
|
||||
new PropertyMetadata(default(bool)));
|
||||
|
||||
public bool HasImage
|
||||
{
|
||||
get { return (bool)GetValue(HasImageProperty); }
|
||||
set { SetValue(HasImageProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty SourceProperty = DependencyProperty.Register(
|
||||
"Source",
|
||||
typeof(ImageSource),
|
||||
typeof(ExtendedImage),
|
||||
new PropertyMetadata(default(ImageBrush)));
|
||||
|
||||
public ImageSource Source
|
||||
{
|
||||
get { return (ImageSource)GetValue(SourceProperty); }
|
||||
set { SetValue(SourceProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty StretchProperty = DependencyProperty.Register(
|
||||
"Stretch",
|
||||
typeof (Stretch),
|
||||
typeof (ExtendedImage),
|
||||
new PropertyMetadata(default(Stretch)));
|
||||
|
||||
public Stretch Stretch
|
||||
{
|
||||
get { return (Stretch) GetValue(StretchProperty); }
|
||||
set { SetValue(StretchProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty PlaceHolderSourceProperty = DependencyProperty.Register(
|
||||
"PlaceHolderSource",
|
||||
typeof(ImageSource),
|
||||
typeof(ExtendedImage),
|
||||
new PropertyMetadata(default(ImageBrush)));
|
||||
|
||||
public ImageSource PlaceHolderSource
|
||||
{
|
||||
get { return (ImageSource)GetValue(PlaceHolderSourceProperty); }
|
||||
set { SetValue(PlaceHolderSourceProperty, value); }
|
||||
}
|
||||
|
||||
static ExtendedImage()
|
||||
{
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(ExtendedImage),
|
||||
new FrameworkPropertyMetadata(typeof(ExtendedImage)));
|
||||
}
|
||||
}
|
||||
}
|
226
MediaBrowser.UI/Controls/TreeHelper.cs
Normal file
@ -0,0 +1,226 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace MediaBrowser.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper methods for UI-related tasks.
|
||||
/// </summary>
|
||||
public static class TreeHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Finds a Child of a given item in the visual tree.
|
||||
/// </summary>
|
||||
/// <param name="parent">A direct parent of the queried item.</param>
|
||||
/// <typeparam name="T">The type of the queried item.</typeparam>
|
||||
/// <param name="childName">x:Name or Name of child. </param>
|
||||
/// <returns>The first parent item that matches the submitted type parameter.
|
||||
/// If not matching item can be found,
|
||||
/// a null parent is being returned.</returns>
|
||||
public static T FindChild<T>(DependencyObject parent, string childName)
|
||||
where T : DependencyObject
|
||||
{
|
||||
// Confirm parent and childName are valid.
|
||||
if (parent == null) return null;
|
||||
|
||||
T foundChild = null;
|
||||
|
||||
int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
|
||||
for (int i = 0; i < childrenCount; i++)
|
||||
{
|
||||
var child = VisualTreeHelper.GetChild(parent, i);
|
||||
// If the child is not of the request child type child
|
||||
T childType = child as T;
|
||||
if (childType == null)
|
||||
{
|
||||
// recursively drill down the tree
|
||||
foundChild = FindChild<T>(child, childName);
|
||||
|
||||
// If the child is found, break so we do not overwrite the found child.
|
||||
if (foundChild != null) break;
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(childName))
|
||||
{
|
||||
var frameworkElement = child as FrameworkElement;
|
||||
// If the child's name is set for search
|
||||
if (frameworkElement != null && frameworkElement.Name == childName)
|
||||
{
|
||||
// if the child's name is of the request name
|
||||
foundChild = (T)child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// child element found.
|
||||
foundChild = (T)child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return foundChild;
|
||||
}
|
||||
|
||||
#region find parent
|
||||
|
||||
/// <summary>
|
||||
/// Finds a parent of a given item on the visual tree.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the queried item.</typeparam>
|
||||
/// <param name="child">A direct or indirect child of the
|
||||
/// queried item.</param>
|
||||
/// <returns>The first parent item that matches the submitted
|
||||
/// type parameter. If not matching item can be found, a null
|
||||
/// reference is being returned.</returns>
|
||||
public static T TryFindParent<T>(this DependencyObject child)
|
||||
where T : DependencyObject
|
||||
{
|
||||
//get parent item
|
||||
DependencyObject parentObject = GetParentObject(child);
|
||||
|
||||
//we've reached the end of the tree
|
||||
if (parentObject == null) return null;
|
||||
|
||||
//check if the parent matches the type we're looking for
|
||||
T parent = parentObject as T;
|
||||
if (parent != null)
|
||||
{
|
||||
return parent;
|
||||
}
|
||||
|
||||
//use recursion to proceed with next level
|
||||
return TryFindParent<T>(parentObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method is an alternative to WPF's
|
||||
/// <see cref="VisualTreeHelper.GetParent"/> method, which also
|
||||
/// supports content elements. Keep in mind that for content element,
|
||||
/// this method falls back to the logical tree of the element!
|
||||
/// </summary>
|
||||
/// <param name="child">The item to be processed.</param>
|
||||
/// <returns>The submitted item's parent, if available. Otherwise
|
||||
/// null.</returns>
|
||||
public static DependencyObject GetParentObject(this DependencyObject child)
|
||||
{
|
||||
if (child == null) return null;
|
||||
|
||||
//handle content elements separately
|
||||
ContentElement contentElement = child as ContentElement;
|
||||
if (contentElement != null)
|
||||
{
|
||||
DependencyObject parent = ContentOperations.GetParent(contentElement);
|
||||
if (parent != null) return parent;
|
||||
|
||||
FrameworkContentElement fce = contentElement as FrameworkContentElement;
|
||||
return fce != null ? fce.Parent : null;
|
||||
}
|
||||
|
||||
//also try searching for parent in framework elements (such as DockPanel, etc)
|
||||
FrameworkElement frameworkElement = child as FrameworkElement;
|
||||
if (frameworkElement != null)
|
||||
{
|
||||
DependencyObject parent = frameworkElement.Parent;
|
||||
if (parent != null) return parent;
|
||||
}
|
||||
|
||||
//if it's not a ContentElement/FrameworkElement, rely on VisualTreeHelper
|
||||
return VisualTreeHelper.GetParent(child);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region find children
|
||||
|
||||
/// <summary>
|
||||
/// Analyzes both visual and logical tree in order to find all elements of a given
|
||||
/// type that are descendants of the <paramref name="source"/> item.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the queried items.</typeparam>
|
||||
/// <param name="source">The root element that marks the source of the search. If the
|
||||
/// source is already of the requested type, it will not be included in the result.</param>
|
||||
/// <returns>All descendants of <paramref name="source"/> that match the requested type.</returns>
|
||||
public static IEnumerable<T> FindChildren<T>(this DependencyObject source) where T : DependencyObject
|
||||
{
|
||||
if (source != null)
|
||||
{
|
||||
var childs = GetChildObjects(source);
|
||||
foreach (DependencyObject child in childs)
|
||||
{
|
||||
//analyze if children match the requested type
|
||||
if (child is T)
|
||||
{
|
||||
yield return (T)child;
|
||||
}
|
||||
|
||||
//recurse tree
|
||||
foreach (T descendant in FindChildren<T>(child))
|
||||
{
|
||||
yield return descendant;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// This method is an alternative to WPF's
|
||||
/// <see cref="VisualTreeHelper.GetChild"/> method, which also
|
||||
/// supports content elements. Keep in mind that for content elements,
|
||||
/// this method falls back to the logical tree of the element.
|
||||
/// </summary>
|
||||
/// <param name="parent">The item to be processed.</param>
|
||||
/// <returns>The submitted item's child elements, if available.</returns>
|
||||
public static IEnumerable<DependencyObject> GetChildObjects(this DependencyObject parent)
|
||||
{
|
||||
if (parent == null) yield break;
|
||||
|
||||
if (parent is ContentElement || parent is FrameworkElement)
|
||||
{
|
||||
//use the logical tree for content / framework elements
|
||||
foreach (object obj in LogicalTreeHelper.GetChildren(parent))
|
||||
{
|
||||
var depObj = obj as DependencyObject;
|
||||
if (depObj != null) yield return (DependencyObject)obj;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//use the visual tree per default
|
||||
int count = VisualTreeHelper.GetChildrenCount(parent);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
yield return VisualTreeHelper.GetChild(parent, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region find from point
|
||||
|
||||
/// <summary>
|
||||
/// Tries to locate a given item within the visual tree,
|
||||
/// starting with the dependency object at a given position.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the element to be found
|
||||
/// on the visual tree of the element at the given location.</typeparam>
|
||||
/// <param name="reference">The main element which is used to perform
|
||||
/// hit testing.</param>
|
||||
/// <param name="point">The position to be evaluated on the origin.</param>
|
||||
public static T TryFindFromPoint<T>(UIElement reference, Point point)
|
||||
where T : DependencyObject
|
||||
{
|
||||
DependencyObject element = reference.InputHitTest(point) as DependencyObject;
|
||||
|
||||
if (element == null) return null;
|
||||
|
||||
if (element is T) return (T)element;
|
||||
|
||||
return TryFindParent<T>(element);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
91
MediaBrowser.UI/Controls/WindowCommands.xaml
Normal file
@ -0,0 +1,91 @@
|
||||
<UserControl x:Class="MediaBrowser.UI.Controls.WindowCommands"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="300" d:DesignWidth="300">
|
||||
|
||||
<UserControl.Resources>
|
||||
|
||||
<Style TargetType="StackPanel" x:Key="WindowCommandsPanel">
|
||||
<Setter Property="Orientation" Value="Horizontal"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Right"/>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="Button" x:Key="WebdingsButton" BasedOn="{StaticResource ImageButton}">
|
||||
<Setter Property="Margin" Value="0 0 15 0"/>
|
||||
<Setter Property="KeyboardNavigation.IsTabStop" Value="false"/>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="TextBlock" x:Key="WebdingsTextBlock">
|
||||
<Setter Property="FontFamily" Value="Webdings"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="Foreground" Value="{StaticResource DefaultForeground}"/>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="Button" x:Key="MinimizeApplicationButton" BasedOn="{StaticResource WebdingsButton}">
|
||||
<Setter Property="ToolTip" Value="Minimize"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate>
|
||||
<TextBlock Style="{StaticResource WebdingsTextBlock}">0</TextBlock>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="Button" x:Key="MaximizeApplicationButton" BasedOn="{StaticResource WebdingsButton}">
|
||||
<Setter Property="ToolTip" Value="Maximize"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate>
|
||||
<TextBlock Style="{StaticResource WebdingsTextBlock}">1</TextBlock>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding Path=WindowState, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" Value="Maximized">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="Button" x:Key="UndoMaximizeApplicationButton" BasedOn="{StaticResource WebdingsButton}">
|
||||
<Setter Property="Visibility" Value="Collapsed"/>
|
||||
<Setter Property="ToolTip" Value="Restore"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate>
|
||||
<TextBlock Style="{StaticResource WebdingsTextBlock}">2</TextBlock>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding Path=WindowState, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" Value="Maximized">
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="Button" x:Key="CloseApplicationButton" BasedOn="{StaticResource WebdingsButton}">
|
||||
<Setter Property="ToolTip" Value="Close"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate>
|
||||
<TextBlock Style="{StaticResource WebdingsTextBlock}">r</TextBlock>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
</UserControl.Resources>
|
||||
|
||||
<StackPanel Style="{StaticResource WindowCommandsPanel}">
|
||||
<Button x:Name="MinimizeApplicationButton" Style="{StaticResource MinimizeApplicationButton}"></Button>
|
||||
<Button x:Name="MaximizeApplicationButton" Style="{StaticResource MaximizeApplicationButton}"></Button>
|
||||
<Button x:Name="UndoMaximizeApplicationButton" Style="{StaticResource UndoMaximizeApplicationButton}"></Button>
|
||||
<Button x:Name="CloseApplicationButton" Style="{StaticResource CloseApplicationButton}"></Button>
|
||||
</StackPanel>
|
||||
|
||||
</UserControl>
|
50
MediaBrowser.UI/Controls/WindowCommands.xaml.cs
Normal file
@ -0,0 +1,50 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace MediaBrowser.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for WindowCommands.xaml
|
||||
/// </summary>
|
||||
public partial class WindowCommands : UserControl
|
||||
{
|
||||
public Window ParentWindow
|
||||
{
|
||||
get { return TreeHelper.TryFindParent<Window>(this); }
|
||||
}
|
||||
|
||||
public WindowCommands()
|
||||
{
|
||||
InitializeComponent();
|
||||
Loaded += WindowCommandsLoaded;
|
||||
}
|
||||
|
||||
void WindowCommandsLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
CloseApplicationButton.Click += CloseApplicationButtonClick;
|
||||
MinimizeApplicationButton.Click += MinimizeApplicationButtonClick;
|
||||
MaximizeApplicationButton.Click += MaximizeApplicationButtonClick;
|
||||
UndoMaximizeApplicationButton.Click += UndoMaximizeApplicationButtonClick;
|
||||
}
|
||||
|
||||
void UndoMaximizeApplicationButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ParentWindow.WindowState = WindowState.Normal;
|
||||
}
|
||||
|
||||
void MaximizeApplicationButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ParentWindow.WindowState = WindowState.Maximized;
|
||||
}
|
||||
|
||||
void MinimizeApplicationButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ParentWindow.WindowState = WindowState.Minimized;
|
||||
}
|
||||
|
||||
void CloseApplicationButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ParentWindow.Close();
|
||||
}
|
||||
}
|
||||
}
|
26
MediaBrowser.UI/Converters/CurrentUserVisibilityConverter.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace MediaBrowser.UI.Converters
|
||||
{
|
||||
public class CurrentUserVisibilityConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (App.Instance.ServerConfiguration == null || !App.Instance.ServerConfiguration.EnableUserProfiles)
|
||||
{
|
||||
return Visibility.Collapsed;
|
||||
}
|
||||
|
||||
return value == null ? Visibility.Collapsed : Visibility.Visible;
|
||||
}
|
||||
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
34
MediaBrowser.UI/Converters/DateTimeToStringConverter.cs
Normal file
@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace MediaBrowser.UI.Converters
|
||||
{
|
||||
public class DateTimeToStringConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
var date = (DateTime)value;
|
||||
|
||||
string format = parameter as string;
|
||||
|
||||
if (string.IsNullOrEmpty(format))
|
||||
{
|
||||
return date.ToString();
|
||||
}
|
||||
|
||||
if (format.Equals("shorttime", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return date.ToShortTimeString();
|
||||
}
|
||||
|
||||
return date.ToString(format);
|
||||
}
|
||||
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
86
MediaBrowser.UI/Converters/LastSeenTextConverter.cs
Normal file
@ -0,0 +1,86 @@
|
||||
using MediaBrowser.Model.DTO;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace MediaBrowser.UI.Converters
|
||||
{
|
||||
public class LastSeenTextConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
var user = value as DtoUser;
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
if (user.LastActivityDate.HasValue)
|
||||
{
|
||||
DateTime date = user.LastActivityDate.Value.ToLocalTime();
|
||||
|
||||
return "Last seen " + GetRelativeTimeText(date);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string GetRelativeTimeText(DateTime date)
|
||||
{
|
||||
TimeSpan ts = DateTime.Now - date;
|
||||
|
||||
const int second = 1;
|
||||
const int minute = 60 * second;
|
||||
const int hour = 60 * minute;
|
||||
const int day = 24 * hour;
|
||||
const int month = 30 * day;
|
||||
|
||||
int delta = System.Convert.ToInt32(ts.TotalSeconds);
|
||||
|
||||
if (delta < 0)
|
||||
{
|
||||
return "not yet";
|
||||
}
|
||||
if (delta < 1 * minute)
|
||||
{
|
||||
return ts.Seconds == 1 ? "one second ago" : ts.Seconds + " seconds ago";
|
||||
}
|
||||
if (delta < 2 * minute)
|
||||
{
|
||||
return "a minute ago";
|
||||
}
|
||||
if (delta < 45 * minute)
|
||||
{
|
||||
return ts.Minutes + " minutes ago";
|
||||
}
|
||||
if (delta < 90 * minute)
|
||||
{
|
||||
return "an hour ago";
|
||||
}
|
||||
if (delta < 24 * hour)
|
||||
{
|
||||
return ts.Hours + " hours ago";
|
||||
}
|
||||
if (delta < 48 * hour)
|
||||
{
|
||||
return "yesterday";
|
||||
}
|
||||
if (delta < 30 * day)
|
||||
{
|
||||
return ts.Days + " days ago";
|
||||
}
|
||||
if (delta < 12 * month)
|
||||
{
|
||||
int months = System.Convert.ToInt32(Math.Floor((double)ts.Days / 30));
|
||||
return months <= 1 ? "one month ago" : months + " months ago";
|
||||
}
|
||||
|
||||
int years = System.Convert.ToInt32(Math.Floor((double)ts.Days / 365));
|
||||
return years <= 1 ? "one year ago" : years + " years ago";
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
60
MediaBrowser.UI/Converters/UserImageConverter.cs
Normal file
@ -0,0 +1,60 @@
|
||||
using MediaBrowser.Model.DTO;
|
||||
using MediaBrowser.UI.Controller;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Net.Cache;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace MediaBrowser.UI.Converters
|
||||
{
|
||||
public class UserImageConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
var user = value as DtoUser;
|
||||
|
||||
if (user != null && user.HasImage)
|
||||
{
|
||||
var config = parameter as string;
|
||||
|
||||
int? maxWidth = null;
|
||||
int? maxHeight = null;
|
||||
int? width = null;
|
||||
int? height = null;
|
||||
|
||||
if (!string.IsNullOrEmpty(config))
|
||||
{
|
||||
var vals = config.Split(',');
|
||||
|
||||
width = GetSize(vals[0]);
|
||||
height = GetSize(vals[1]);
|
||||
maxWidth = GetSize(vals[2]);
|
||||
maxHeight = GetSize(vals[3]);
|
||||
}
|
||||
|
||||
var uri = UIKernel.Instance.ApiClient.GetUserImageUrl(user.Id, width, height, maxWidth, maxHeight, 100);
|
||||
|
||||
return new BitmapImage(new Uri(uri), new RequestCachePolicy(RequestCacheLevel.Revalidate));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private int? GetSize(string val)
|
||||
{
|
||||
if (string.IsNullOrEmpty(val) || val == "0")
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return int.Parse(val);
|
||||
}
|
||||
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
31
MediaBrowser.UI/Converters/WeatherTemperatureConverter.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using MediaBrowser.Model.Weather;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace MediaBrowser.UI.Converters
|
||||
{
|
||||
public class WeatherTemperatureConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
var weather = value as WeatherInfo;
|
||||
|
||||
if (weather != null)
|
||||
{
|
||||
if (App.Instance.ServerConfiguration.WeatherUnit == WeatherUnits.Celsius)
|
||||
{
|
||||
return weather.CurrentWeather.TemperatureCelsius + "°C";
|
||||
}
|
||||
|
||||
return weather.CurrentWeather.TemperatureFahrenheit + "°F";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
20
MediaBrowser.UI/Converters/WeatherVisibilityConverter.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace MediaBrowser.UI.Converters
|
||||
{
|
||||
public class WeatherVisibilityConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return value == null ? Visibility.Collapsed : Visibility.Visible;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
50
MediaBrowser.UI/MainWindow.xaml
Normal file
@ -0,0 +1,50 @@
|
||||
<Window x:Class="MediaBrowser.UI.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="clr-namespace:MediaBrowser.UI.Controls"
|
||||
Title="media browser"
|
||||
Style="{StaticResource MainWindow}"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
AllowsTransparency="True"
|
||||
WindowStyle="None"
|
||||
ResizeMode="CanResizeWithGrip"
|
||||
KeyboardNavigation.DirectionalNavigation="Contained">
|
||||
|
||||
<!--The window itself is a tabstop, and it can't be disabled. So this is a workaround.-->
|
||||
<Grid>
|
||||
|
||||
<Grid x:Name="BackdropGrid" Style="{StaticResource BackdropGrid}">
|
||||
</Grid>
|
||||
|
||||
<!--This allows the user to drag the window.-->
|
||||
<Grid x:Name="DragBar" Style="{StaticResource DragBar}"></Grid>
|
||||
|
||||
<!--This allows the user to drag the window.-->
|
||||
<controls:WindowCommands x:Name="WindowCommands" Style="{StaticResource WindowCommands}"></controls:WindowCommands>
|
||||
|
||||
<!--Themes will supply this template to outline the window structure.-->
|
||||
<ContentControl x:Name="PageContent" Template="{StaticResource PageContentTemplate}"></ContentControl>
|
||||
|
||||
<Grid x:Name="NavBarGrid" Style="{StaticResource NavBarGrid}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"></ColumnDefinition>
|
||||
<ColumnDefinition Width="*"></ColumnDefinition>
|
||||
<ColumnDefinition Width="auto"></ColumnDefinition>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<StackPanel Style="{StaticResource NavBarGridLeftPanel}">
|
||||
<Button x:Name="BackButton" Style="{StaticResource BackButton}"></Button>
|
||||
<Button x:Name="ForwardButton" Style="{StaticResource ForwardButton}"></Button>
|
||||
</StackPanel>
|
||||
<StackPanel Style="{StaticResource NavBarGridCenterPanel}">
|
||||
<Button x:Name="MuteButton" Style="{StaticResource MuteButton}"></Button>
|
||||
<Button x:Name="VolumeDownButton" Style="{StaticResource VolumeDownButton}"></Button>
|
||||
<Button x:Name="VolumeUpButton" Style="{StaticResource VolumeUpButton}"></Button>
|
||||
</StackPanel>
|
||||
<StackPanel Style="{StaticResource NavBarGridRightPanel}">
|
||||
<Button x:Name="SettingsButton" Style="{StaticResource SettingsButton}"></Button>
|
||||
<Button x:Name="ExitButton" Style="{StaticResource ExitButton}"></Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Window>
|
368
MediaBrowser.UI/MainWindow.xaml.cs
Normal file
@ -0,0 +1,368 @@
|
||||
using MediaBrowser.Model.DTO;
|
||||
using MediaBrowser.UI.Controller;
|
||||
using MediaBrowser.UI.Controls;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media.Animation;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace MediaBrowser.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for MainWindow.xaml
|
||||
/// </summary>
|
||||
public partial class MainWindow : Window, INotifyPropertyChanged
|
||||
{
|
||||
private Timer MouseIdleTimer { get; set; }
|
||||
private Timer BackdropTimer { get; set; }
|
||||
private Image BackdropImage { get; set; }
|
||||
private string[] CurrentBackdrops { get; set; }
|
||||
private int CurrentBackdropIndex { get; set; }
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
BackButton.Click += BtnApplicationBackClick;
|
||||
ExitButton.Click += ExitButtonClick;
|
||||
ForwardButton.Click += ForwardButtonClick;
|
||||
DragBar.MouseDown += DragableGridMouseDown;
|
||||
Loaded += MainWindowLoaded;
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
public void OnPropertyChanged(String info)
|
||||
{
|
||||
if (PropertyChanged != null)
|
||||
{
|
||||
PropertyChanged(this, new PropertyChangedEventArgs(info));
|
||||
}
|
||||
}
|
||||
|
||||
private bool _isMouseIdle = true;
|
||||
public bool IsMouseIdle
|
||||
{
|
||||
get { return _isMouseIdle; }
|
||||
set
|
||||
{
|
||||
_isMouseIdle = value;
|
||||
OnPropertyChanged("IsMouseIdle");
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindowLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
DataContext = App.Instance;
|
||||
|
||||
if (App.Instance.ServerConfiguration == null)
|
||||
{
|
||||
App.Instance.PropertyChanged += ApplicationPropertyChanged;
|
||||
}
|
||||
else
|
||||
{
|
||||
LoadInitialPage();
|
||||
}
|
||||
}
|
||||
|
||||
void ForwardButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
NavigateForward();
|
||||
}
|
||||
|
||||
void ExitButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
void ApplicationPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName.Equals("ServerConfiguration"))
|
||||
{
|
||||
App.Instance.PropertyChanged -= ApplicationPropertyChanged;
|
||||
LoadInitialPage();
|
||||
}
|
||||
}
|
||||
|
||||
private async void LoadInitialPage()
|
||||
{
|
||||
await App.Instance.LogoutUser().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private void DragableGridMouseDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (e.ClickCount == 2)
|
||||
{
|
||||
WindowState = WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;
|
||||
}
|
||||
else if (e.LeftButton == MouseButtonState.Pressed)
|
||||
{
|
||||
DragMove();
|
||||
}
|
||||
}
|
||||
|
||||
void BtnApplicationBackClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
NavigateBack();
|
||||
}
|
||||
|
||||
private Frame PageFrame
|
||||
{
|
||||
get
|
||||
{
|
||||
// Finding the grid that is generated by the ControlTemplate of the Button
|
||||
return TreeHelper.FindChild<Frame>(PageContent, "PageFrame");
|
||||
}
|
||||
}
|
||||
|
||||
public void Navigate(Uri uri)
|
||||
{
|
||||
PageFrame.Navigate(uri);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the backdrop based on an ApiBaseItemWrapper
|
||||
/// </summary>
|
||||
public void SetBackdrops(DtoBaseItem item)
|
||||
{
|
||||
SetBackdrops(UIKernel.Instance.ApiClient.GetBackdropImageUrls(item, null, null, 1920, 1080));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the backdrop based on a list of image files
|
||||
/// </summary>
|
||||
public async void SetBackdrops(string[] backdrops)
|
||||
{
|
||||
// Don't reload the same backdrops
|
||||
if (CurrentBackdrops != null && backdrops.SequenceEqual(CurrentBackdrops))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (BackdropTimer != null)
|
||||
{
|
||||
BackdropTimer.Dispose();
|
||||
}
|
||||
|
||||
BackdropGrid.Children.Clear();
|
||||
|
||||
if (backdrops.Length == 0)
|
||||
{
|
||||
CurrentBackdrops = null;
|
||||
return;
|
||||
}
|
||||
|
||||
CurrentBackdropIndex = GetFirstBackdropIndex();
|
||||
|
||||
Image image = await App.Instance.GetImage(backdrops.ElementAt(CurrentBackdropIndex));
|
||||
image.SetResourceReference(Image.StyleProperty, "BackdropImage");
|
||||
|
||||
BackdropGrid.Children.Add(image);
|
||||
|
||||
CurrentBackdrops = backdrops;
|
||||
BackdropImage = image;
|
||||
|
||||
const int backdropRotationTime = 7000;
|
||||
|
||||
if (backdrops.Count() > 1)
|
||||
{
|
||||
BackdropTimer = new Timer(BackdropTimerCallback, null, backdropRotationTime, backdropRotationTime);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearBackdrops()
|
||||
{
|
||||
if (BackdropTimer != null)
|
||||
{
|
||||
BackdropTimer.Dispose();
|
||||
}
|
||||
|
||||
BackdropGrid.Children.Clear();
|
||||
|
||||
CurrentBackdrops = null;
|
||||
}
|
||||
|
||||
private void BackdropTimerCallback(object stateInfo)
|
||||
{
|
||||
// Need to do this on the UI thread
|
||||
Application.Current.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
var animFadeOut = new Storyboard();
|
||||
animFadeOut.Completed += AnimFadeOutCompleted;
|
||||
|
||||
var fadeOut = new DoubleAnimation();
|
||||
fadeOut.From = 1.0;
|
||||
fadeOut.To = 0.5;
|
||||
fadeOut.Duration = new Duration(TimeSpan.FromSeconds(1));
|
||||
|
||||
animFadeOut.Children.Add(fadeOut);
|
||||
Storyboard.SetTarget(fadeOut, BackdropImage);
|
||||
Storyboard.SetTargetProperty(fadeOut, new PropertyPath(Image.OpacityProperty));
|
||||
|
||||
animFadeOut.Begin(this);
|
||||
});
|
||||
}
|
||||
|
||||
async void AnimFadeOutCompleted(object sender, System.EventArgs e)
|
||||
{
|
||||
if (CurrentBackdrops == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int backdropIndex = GetNextBackdropIndex();
|
||||
|
||||
BitmapImage image = await App.Instance.GetBitmapImage(CurrentBackdrops[backdropIndex]);
|
||||
CurrentBackdropIndex = backdropIndex;
|
||||
|
||||
// Need to do this on the UI thread
|
||||
BackdropImage.Source = image;
|
||||
Storyboard imageFadeIn = new Storyboard();
|
||||
|
||||
DoubleAnimation fadeIn = new DoubleAnimation();
|
||||
|
||||
fadeIn.From = 0.25;
|
||||
fadeIn.To = 1.0;
|
||||
fadeIn.Duration = new Duration(TimeSpan.FromSeconds(1));
|
||||
|
||||
imageFadeIn.Children.Add(fadeIn);
|
||||
Storyboard.SetTarget(fadeIn, BackdropImage);
|
||||
Storyboard.SetTargetProperty(fadeIn, new PropertyPath(Image.OpacityProperty));
|
||||
imageFadeIn.Begin(this);
|
||||
}
|
||||
|
||||
private int GetFirstBackdropIndex()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int GetNextBackdropIndex()
|
||||
{
|
||||
if (CurrentBackdropIndex < CurrentBackdrops.Length - 1)
|
||||
{
|
||||
return CurrentBackdropIndex + 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void NavigateBack()
|
||||
{
|
||||
if (PageFrame.NavigationService.CanGoBack)
|
||||
{
|
||||
PageFrame.NavigationService.GoBack();
|
||||
}
|
||||
}
|
||||
|
||||
public void NavigateForward()
|
||||
{
|
||||
if (PageFrame.NavigationService.CanGoForward)
|
||||
{
|
||||
PageFrame.NavigationService.GoForward();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows the control bar then starts a timer to hide it
|
||||
/// </summary>
|
||||
private void StartMouseIdleTimer()
|
||||
{
|
||||
IsMouseIdle = false;
|
||||
|
||||
const int duration = 10000;
|
||||
|
||||
// Start the timer if it's null, otherwise reset it
|
||||
if (MouseIdleTimer == null)
|
||||
{
|
||||
MouseIdleTimer = new Timer(MouseIdleTimerCallback, null, duration, Timeout.Infinite);
|
||||
}
|
||||
else
|
||||
{
|
||||
MouseIdleTimer.Change(duration, Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is the Timer callback method to hide the control bar
|
||||
/// </summary>
|
||||
private void MouseIdleTimerCallback(object stateInfo)
|
||||
{
|
||||
IsMouseIdle = true;
|
||||
|
||||
if (MouseIdleTimer != null)
|
||||
{
|
||||
MouseIdleTimer.Dispose();
|
||||
MouseIdleTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles OnMouseMove to show the control box
|
||||
/// </summary>
|
||||
protected override void OnMouseMove(MouseEventArgs e)
|
||||
{
|
||||
base.OnMouseMove(e);
|
||||
|
||||
StartMouseIdleTimer();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles OnKeyUp to provide keyboard based navigation
|
||||
/// </summary>
|
||||
protected override void OnKeyUp(KeyEventArgs e)
|
||||
{
|
||||
base.OnKeyUp(e);
|
||||
|
||||
if (IsBackPress(e))
|
||||
{
|
||||
NavigateBack();
|
||||
}
|
||||
|
||||
else if (IsForwardPress(e))
|
||||
{
|
||||
NavigateForward();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a keypress should be treated as a backward press
|
||||
/// </summary>
|
||||
private bool IsBackPress(KeyEventArgs e)
|
||||
{
|
||||
if (e.Key == Key.BrowserBack || e.Key == Key.Back)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (e.SystemKey == Key.Left && e.KeyboardDevice.Modifiers.HasFlag(ModifierKeys.Alt))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a keypress should be treated as a forward press
|
||||
/// </summary>
|
||||
private bool IsForwardPress(KeyEventArgs e)
|
||||
{
|
||||
if (e.Key == Key.BrowserForward)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (e.SystemKey == Key.RightAlt && e.KeyboardDevice.Modifiers.HasFlag(ModifierKeys.Alt))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
196
MediaBrowser.UI/MediaBrowser.UI.csproj
Normal file
@ -0,0 +1,196 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{B5ECE1FB-618E-420B-9A99-8E972D76920A}</ProjectGuid>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>MediaBrowser.UI</RootNamespace>
|
||||
<AssemblyName>MediaBrowser.UI</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<StartupObject>MediaBrowser.UI.App</StartupObject>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationIcon>Resources\Images\Icon.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.ComponentModel.Composition" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Net.Http.WebRequest" />
|
||||
<Reference Include="System.Web" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="System.Xaml">
|
||||
<RequiredTargetFramework>4.0</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="WindowsBase" />
|
||||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="PresentationFramework" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Configuration\UIApplicationConfiguration.cs" />
|
||||
<Compile Include="Configuration\UIApplicationPaths.cs" />
|
||||
<Compile Include="Controller\PluginUpdater.cs" />
|
||||
<Compile Include="Controls\EnhancedScrollViewer.cs" />
|
||||
<Compile Include="Controls\ExtendedImage.cs" />
|
||||
<Compile Include="Controls\TreeHelper.cs" />
|
||||
<Compile Include="Controls\WindowCommands.xaml.cs">
|
||||
<DependentUpon>WindowCommands.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Converters\CurrentUserVisibilityConverter.cs" />
|
||||
<Compile Include="Converters\DateTimeToStringConverter.cs" />
|
||||
<Compile Include="Converters\LastSeenTextConverter.cs" />
|
||||
<Compile Include="Converters\WeatherTemperatureConverter.cs" />
|
||||
<Compile Include="Converters\WeatherVisibilityConverter.cs" />
|
||||
<Compile Include="Controller\UIKernel.cs" />
|
||||
<Compile Include="Pages\BaseLoginPage.cs" />
|
||||
<Page Include="App.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Controls\WindowCommands.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="MainWindow.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Compile Include="App.xaml.cs">
|
||||
<DependentUpon>App.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Pages\BasePage.cs" />
|
||||
<Compile Include="Converters\UserImageConverter.cs" />
|
||||
<Compile Include="MainWindow.xaml.cs">
|
||||
<DependentUpon>MainWindow.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Page Include="Resources\AppResources.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Resources\MainWindowResources.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Resources\NavBarResources.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Themes\Generic.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Properties\Settings.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
</Compile>
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
<None Include="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
<AppDesigner Include="Properties\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\MediaBrowserServer\MediaBrowser.ApiInteraction\MediaBrowser.ApiInteraction.csproj">
|
||||
<Project>{921c0f64-fda7-4e9f-9e73-0cb0eedb2422}</Project>
|
||||
<Name>MediaBrowser.ApiInteraction</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\MediaBrowserServer\MediaBrowser.Common\MediaBrowser.Common.csproj">
|
||||
<Project>{9142eefa-7570-41e1-bfcc-468bb571af2f}</Project>
|
||||
<Name>MediaBrowser.Common</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\MediaBrowserServer\MediaBrowser.Model\MediaBrowser.Model.csproj">
|
||||
<Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
|
||||
<Name>MediaBrowser.Model</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="Resources\Images\BackButton.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="Resources\Images\ForwardButton.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="Resources\Images\ExitButton.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="Resources\Images\SettingsButton.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="Resources\Images\VolumeUpButton.png" />
|
||||
<Resource Include="Resources\Images\VolumeDownButton.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="Resources\Images\MuteButton.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="Resources\Images\Icon.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="Resources\Images\mblogoblack.png" />
|
||||
<Resource Include="Resources\Images\mblogowhite.png" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
33
MediaBrowser.UI/Pages/BaseLoginPage.cs
Normal file
@ -0,0 +1,33 @@
|
||||
using MediaBrowser.Model.DTO;
|
||||
using MediaBrowser.UI.Controller;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.UI.Pages
|
||||
{
|
||||
public class BaseLoginPage : BasePage
|
||||
{
|
||||
private DtoUser[] _users;
|
||||
public DtoUser[] Users
|
||||
{
|
||||
get { return _users; }
|
||||
|
||||
set
|
||||
{
|
||||
_users = value;
|
||||
OnPropertyChanged("Users");
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task LoadData()
|
||||
{
|
||||
Users = await UIKernel.Instance.ApiClient.GetAllUsersAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
protected void UserClicked(DtoUser user)
|
||||
{
|
||||
App.Instance.CurrentUser = user;
|
||||
//App.Instance.Navigate(new Uri("/Pages/HomePage.xaml", UriKind.Relative));
|
||||
}
|
||||
}
|
||||
}
|
79
MediaBrowser.UI/Pages/BasePage.cs
Normal file
@ -0,0 +1,79 @@
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace MediaBrowser.UI.Pages
|
||||
{
|
||||
public abstract class BasePage : Page, INotifyPropertyChanged
|
||||
{
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
public void OnPropertyChanged(String info)
|
||||
{
|
||||
if (PropertyChanged != null)
|
||||
{
|
||||
PropertyChanged(this, new PropertyChangedEventArgs(info));
|
||||
}
|
||||
}
|
||||
|
||||
protected Uri Uri
|
||||
{
|
||||
get
|
||||
{
|
||||
return NavigationService.CurrentSource;
|
||||
}
|
||||
}
|
||||
|
||||
protected MainWindow MainWindow
|
||||
{
|
||||
get
|
||||
{
|
||||
return App.Instance.MainWindow as MainWindow;
|
||||
}
|
||||
}
|
||||
|
||||
private NameValueCollection _queryString;
|
||||
protected NameValueCollection QueryString
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_queryString == null)
|
||||
{
|
||||
string url = Uri.ToString();
|
||||
|
||||
int index = url.IndexOf('?');
|
||||
|
||||
if (index == -1)
|
||||
{
|
||||
_queryString = new NameValueCollection();
|
||||
}
|
||||
else
|
||||
{
|
||||
_queryString = HttpUtility.ParseQueryString(url.Substring(index + 1));
|
||||
}
|
||||
}
|
||||
|
||||
return _queryString;
|
||||
}
|
||||
}
|
||||
|
||||
protected BasePage()
|
||||
: base()
|
||||
{
|
||||
Loaded += BasePageLoaded;
|
||||
}
|
||||
|
||||
async void BasePageLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await LoadData();
|
||||
|
||||
DataContext = this;
|
||||
}
|
||||
|
||||
protected abstract Task LoadData();
|
||||
}
|
||||
}
|
53
MediaBrowser.UI/Properties/AssemblyInfo.cs
Normal file
@ -0,0 +1,53 @@
|
||||
using System.Reflection;
|
||||
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("MediaBrowser.UI")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("MediaBrowser.UI")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2012")]
|
||||
[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
|
||||
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
|
||||
//inside a <PropertyGroup>. For example, if you are using US english
|
||||
//in your source files, set the <UICulture> 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.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
71
MediaBrowser.UI/Properties/Resources.Designer.cs
generated
Normal file
@ -0,0 +1,71 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.17626
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace MediaBrowser.UI.Properties
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// 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()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[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("MediaBrowser.UI.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture
|
||||
{
|
||||
get
|
||||
{
|
||||
return resourceCulture;
|
||||
}
|
||||
set
|
||||
{
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
117
MediaBrowser.UI/Properties/Resources.resx
Normal file
@ -0,0 +1,117 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
30
MediaBrowser.UI/Properties/Settings.Designer.cs
generated
Normal file
@ -0,0 +1,30 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.17626
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace MediaBrowser.UI.Properties
|
||||
{
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
7
MediaBrowser.UI/Properties/Settings.settings
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
|
||||
<Profiles>
|
||||
<Profile Name="(Default)" />
|
||||
</Profiles>
|
||||
<Settings />
|
||||
</SettingsFile>
|
122
MediaBrowser.UI/Resources/AppResources.xaml
Normal file
@ -0,0 +1,122 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:System="clr-namespace:System;assembly=mscorlib"
|
||||
xmlns:converters="clr-namespace:MediaBrowser.UI.Converters">
|
||||
|
||||
<!--Themes should override these as needed-->
|
||||
<FontFamily x:Key="DefaultFontFamily">Segoe UI, Lucida Sans Unicode, Verdana</FontFamily>
|
||||
<FontWeight x:Key="DefaultFontWeight">Thin</FontWeight>
|
||||
<Brush x:Key="DefaultForeground">Black</Brush>
|
||||
<System:Double x:Key="DefaultFontSize">36</System:Double>
|
||||
<System:Double x:Key="Heading1FontSize">84</System:Double>
|
||||
<System:Double x:Key="Heading2FontSize">60</System:Double>
|
||||
|
||||
<!--Define all the standard converters here in one place-->
|
||||
<converters:DateTimeToStringConverter x:Key="DateTimeToStringConverter"></converters:DateTimeToStringConverter>
|
||||
<converters:UserImageConverter x:Key="UserImageConverter"></converters:UserImageConverter>
|
||||
<converters:WeatherTemperatureConverter x:Key="WeatherTemperatureConverter"></converters:WeatherTemperatureConverter>
|
||||
<converters:LastSeenTextConverter x:Key="LastSeenTextConverter"></converters:LastSeenTextConverter>
|
||||
<converters:WeatherVisibilityConverter x:Key="WeatherVisibilityConverter"></converters:WeatherVisibilityConverter>
|
||||
<converters:CurrentUserVisibilityConverter x:Key="CurrentUserVisibilityConverter"></converters:CurrentUserVisibilityConverter>
|
||||
|
||||
<!--Default Frame style. -->
|
||||
<Style TargetType="Frame">
|
||||
<Setter Property="NavigationUIVisibility" Value="Hidden"/>
|
||||
<Setter Property="KeyboardNavigation.IsTabStop" Value="false"/>
|
||||
</Style>
|
||||
|
||||
<!--Default Frame style. -->
|
||||
<Style TargetType="ContentControl">
|
||||
<Setter Property="KeyboardNavigation.IsTabStop" Value="false"/>
|
||||
</Style>
|
||||
|
||||
<!--Default Window style. -->
|
||||
<Style TargetType="Window" x:Key="BaseWindow">
|
||||
<Setter Property="FontSize" Value="{StaticResource DefaultFontSize}"/>
|
||||
<Setter Property="FontFamily" Value="{StaticResource DefaultFontFamily}"/>
|
||||
<Setter Property="FontWeight" Value="{StaticResource DefaultFontWeight}"/>
|
||||
<Setter Property="Foreground" Value="{StaticResource DefaultForeground}"/>
|
||||
<Setter Property="BorderBrush" Value="#cccccc"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
</Style>
|
||||
|
||||
<!--Default TextBlock style. -->
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="FontSize" Value="{StaticResource DefaultFontSize}"/>
|
||||
<Setter Property="FontFamily" Value="{StaticResource DefaultFontFamily}"/>
|
||||
<Setter Property="FontWeight" Value="{StaticResource DefaultFontWeight}"/>
|
||||
<Setter Property="Foreground" Value="{StaticResource DefaultForeground}"/>
|
||||
<Setter Property="TextWrapping" Value="Wrap" />
|
||||
</Style>
|
||||
|
||||
<!--Default Label style. -->
|
||||
<Style TargetType="Label">
|
||||
<Setter Property="FontSize" Value="{StaticResource DefaultFontSize}"/>
|
||||
<Setter Property="FontFamily" Value="{StaticResource DefaultFontFamily}"/>
|
||||
<Setter Property="FontWeight" Value="{StaticResource DefaultFontWeight}"/>
|
||||
<Setter Property="Foreground" Value="{StaticResource DefaultForeground}"/>
|
||||
</Style>
|
||||
|
||||
<!--Default Button style. -->
|
||||
<Style TargetType="Button">
|
||||
<Setter Property="FontSize" Value="{StaticResource DefaultFontSize}"/>
|
||||
<Setter Property="FontFamily" Value="{StaticResource DefaultFontFamily}"/>
|
||||
<Setter Property="FontWeight" Value="{StaticResource DefaultFontWeight}"/>
|
||||
<Setter Property="Foreground" Value="{StaticResource DefaultForeground}"/>
|
||||
</Style>
|
||||
|
||||
<!--Default style for buttons that have images. -->
|
||||
<Style TargetType="Button" x:Key="ImageButton" BasedOn="{StaticResource {x:Static ToolBar.ButtonStyleKey}}">
|
||||
<Setter Property="Margin" Value="0"/>
|
||||
<Setter Property="Padding" Value="0"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Opacity" Value=".5" />
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
<!--Default ListViewItem style. -->
|
||||
<Style x:Key="BaseListViewItemStyle" TargetType="{x:Type ListViewItem}">
|
||||
|
||||
<Setter Property="Padding" Value="0" />
|
||||
<Setter Property="Margin" Value="0" />
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsKeyboardFocusWithin" Value="True">
|
||||
<Setter Property="IsSelected" Value="True" />
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
<!--Themes should override this -->
|
||||
<Style x:Key="ListViewItemStyle" TargetType="{x:Type ListViewItem}" BasedOn="{StaticResource BaseListViewItemStyle}">
|
||||
</Style>
|
||||
|
||||
<!--Default ListView style. -->
|
||||
<Style TargetType="ListView" x:Key="BaseListViewStyle">
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="KeyboardNavigation.IsTabStop" Value="False"/>
|
||||
<Setter Property="KeyboardNavigation.DirectionalNavigation" Value="Continue"/>
|
||||
<Setter Property="VirtualizingPanel.IsVirtualizing" Value="True"/>
|
||||
<Setter Property="IsSynchronizedWithCurrentItem" Value="True"/>
|
||||
</Style>
|
||||
|
||||
<!--Themes should override this -->
|
||||
<Style x:Key="ListViewStyle" TargetType="{x:Type ListView}" BasedOn="{StaticResource BaseListViewStyle}">
|
||||
</Style>
|
||||
|
||||
<!--MB Logo, black text. -->
|
||||
<Style TargetType="Image" x:Key="MBLogoImageBlack">
|
||||
<Setter Property="Source" Value="Images/mblogoblack.png"/>
|
||||
</Style>
|
||||
|
||||
<!--MB Logo, white text. -->
|
||||
<Style TargetType="Image" x:Key="MBLogoImageWhite">
|
||||
<Setter Property="Source" Value="Images/mblogowhite.png"/>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
BIN
MediaBrowser.UI/Resources/Images/BackButton.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
MediaBrowser.UI/Resources/Images/ExitButton.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
MediaBrowser.UI/Resources/Images/ForwardButton.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
MediaBrowser.UI/Resources/Images/Icon.ico
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
MediaBrowser.UI/Resources/Images/MuteButton.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
MediaBrowser.UI/Resources/Images/SettingsButton.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
MediaBrowser.UI/Resources/Images/VolumeDownButton.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
MediaBrowser.UI/Resources/Images/VolumeUpButton.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
MediaBrowser.UI/Resources/Images/mblogoblack.png
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
MediaBrowser.UI/Resources/Images/mblogowhite.png
Normal file
After Width: | Height: | Size: 26 KiB |
43
MediaBrowser.UI/Resources/MainWindowResources.xaml
Normal file
@ -0,0 +1,43 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
|
||||
<!--Themes should override this to style the window-->
|
||||
<Style TargetType="Window" x:Key="MainWindow" BasedOn="{StaticResource BaseWindow}">
|
||||
</Style>
|
||||
|
||||
<!--Themes may want to override this to adjust the backdrop container style-->
|
||||
<Style TargetType="Grid" x:Key="BackdropGrid">
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="Opacity" Value=".15"/>
|
||||
</Style>
|
||||
|
||||
<!--Themes may want to override this to adjust the backdrop image style-->
|
||||
<Style TargetType="Image" x:Key="BackdropImage">
|
||||
<Setter Property="Stretch" Value="UniformToFill"/>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="Grid" x:Key="DragBar">
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="Height" Value="50"/>
|
||||
<Setter Property="VerticalAlignment" Value="Top"/>
|
||||
<Setter Property="Panel.ZIndex" Value="1"/>
|
||||
</Style>
|
||||
<Style TargetType="UserControl" x:Key="WindowCommands">
|
||||
<Setter Property="Margin" Value="0 10 0 0"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Right"/>
|
||||
<Setter Property="VerticalAlignment" Value="Top"/>
|
||||
<Setter Property="Panel.ZIndex" Value="2"/>
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding Path=MainWindow.IsMouseIdle}" Value="false">
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
<!--Themes should override this to layout window content-->
|
||||
<ControlTemplate x:Key="PageContentTemplate">
|
||||
<Frame x:Name="PageFrame"></Frame>
|
||||
</ControlTemplate>
|
||||
|
||||
</ResourceDictionary>
|
122
MediaBrowser.UI/Resources/NavBarResources.xaml
Normal file
@ -0,0 +1,122 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<Style TargetType="Button" x:Key="NavBarButton" BasedOn="{StaticResource ImageButton}">
|
||||
<Setter Property="Margin" Value="10 0 10 0"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Left"/>
|
||||
<Setter Property="KeyboardNavigation.IsTabStop" Value="False"/>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="Button" x:Key="BackButton" BasedOn="{StaticResource NavBarButton}">
|
||||
<Setter Property="ToolTip" Value="Back"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate>
|
||||
<Image x:Name="img" Source="..\Resources\Images\BackButton.png" Stretch="None" />
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="Button" x:Key="ForwardButton" BasedOn="{StaticResource NavBarButton}">
|
||||
<Setter Property="ToolTip" Value="Forward"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate>
|
||||
<Image x:Name="img" Source="..\Resources\Images\ForwardButton.png" Stretch="None" />
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="Button" x:Key="ExitButton" BasedOn="{StaticResource NavBarButton}">
|
||||
<Setter Property="ToolTip" Value="Exit"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate>
|
||||
<Image x:Name="img" Source="..\Resources\Images\ExitButton.png" Stretch="None" />
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="Button" x:Key="SettingsButton" BasedOn="{StaticResource NavBarButton}">
|
||||
<Setter Property="ToolTip" Value="Settings"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate>
|
||||
<Image x:Name="img" Source="..\Resources\Images\SettingsButton.png" Stretch="None" />
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="Button" x:Key="VolumeUpButton" BasedOn="{StaticResource NavBarButton}">
|
||||
<Setter Property="ToolTip" Value="Increase Volume"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate>
|
||||
<Image x:Name="img" Source="..\Resources\Images\VolumeUpButton.png" Stretch="None" />
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="Button" x:Key="VolumeDownButton" BasedOn="{StaticResource NavBarButton}">
|
||||
<Setter Property="ToolTip" Value="Decrease Volume"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate>
|
||||
<Image x:Name="img" Source="..\Resources\Images\VolumeDownButton.png" Stretch="None" />
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="Button" x:Key="MuteButton" BasedOn="{StaticResource NavBarButton}">
|
||||
<Setter Property="ToolTip" Value="Mute"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate>
|
||||
<Image x:Name="img" Source="..\Resources\Images\MuteButton.png" Stretch="None" />
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="Grid" x:Key="NavBarGrid">
|
||||
<Setter Property="VerticalAlignment" Value="Bottom"/>
|
||||
<Setter Property="Background">
|
||||
<Setter.Value>
|
||||
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1" Opacity=".8">
|
||||
<GradientStop Color="#333333" Offset="0.0"/>
|
||||
<GradientStop Color="Black" Offset="1.0"/>
|
||||
</LinearGradientBrush>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding Path=MainWindow.IsMouseIdle}" Value="false">
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
<Style TargetType="StackPanel" x:Key="NavBarGridLeftPanel">
|
||||
<Setter Property="Grid.Column" Value="0"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Left"/>
|
||||
<Setter Property="Orientation" Value="Horizontal"/>
|
||||
<Setter Property="Margin" Value="15 20 15 20"/>
|
||||
</Style>
|
||||
<Style TargetType="StackPanel" x:Key="NavBarGridCenterPanel">
|
||||
<Setter Property="Grid.Column" Value="1"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Center"/>
|
||||
<Setter Property="Orientation" Value="Horizontal"/>
|
||||
<Setter Property="Margin" Value="15 20 15 20"/>
|
||||
</Style>
|
||||
<Style TargetType="StackPanel" x:Key="NavBarGridRightPanel">
|
||||
<Setter Property="Grid.Column" Value="2"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Right"/>
|
||||
<Setter Property="Orientation" Value="Horizontal"/>
|
||||
<Setter Property="Margin" Value="15 20 15 20"/>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
32
MediaBrowser.UI/Themes/Generic.xaml
Normal file
@ -0,0 +1,32 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:MediaBrowser.UI.Controls">
|
||||
|
||||
<Style TargetType="{x:Type local:ExtendedImage}">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type local:ExtendedImage}">
|
||||
<Border Background="{TemplateBinding Background}">
|
||||
<Image x:Name="theImage">
|
||||
<Image.Style>
|
||||
<Style TargetType="{x:Type Image}">
|
||||
<Setter Property="Source"
|
||||
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=PlaceHolderSource}" />
|
||||
<Setter Property="Stretch"
|
||||
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path= Stretch}" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=HasImage}" Value="True">
|
||||
<Setter Property="Source"
|
||||
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Source}" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Image.Style>
|
||||
</Image>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
@ -58,7 +58,7 @@
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>xcopy "$(TargetPath)" "$(SolutionDir)\ProgramData\Plugins\" /y</PostBuildEvent>
|
||||
<PostBuildEvent>xcopy "$(TargetPath)" "$(SolutionDir)\ProgramData-Server\Plugins\" /y</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
|