summaryrefslogtreecommitdiffstats
path: root/win/CS/HandBrakeWPF
diff options
context:
space:
mode:
authorsr55 <[email protected]>2013-01-23 21:19:45 +0000
committersr55 <[email protected]>2013-01-23 21:19:45 +0000
commit80d03c0eafabea810371ee2dddb48000a783ce76 (patch)
tree56e3a3cc8ad977dccea0d082760ee5024977e95e /win/CS/HandBrakeWPF
parent25b86fdefe519095609aa1061aba60249d910b82 (diff)
WinGui: For Point to Point Frames: Use Numeric Up Down Control. For PTP Seconds, use Timespan Up down so the user can enter a time in format hh:mm:ss
git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@5199 b64f7644-9d1e-0410-96f1-a4d463321fa5
Diffstat (limited to 'win/CS/HandBrakeWPF')
-rw-r--r--win/CS/HandBrakeWPF/Controls/TimeSpanBox.xaml175
-rw-r--r--win/CS/HandBrakeWPF/Controls/TimeSpanBox.xaml.cs721
-rw-r--r--win/CS/HandBrakeWPF/HandBrakeWPF.csproj7
-rw-r--r--win/CS/HandBrakeWPF/ViewModels/MainViewModel.cs41
-rw-r--r--win/CS/HandBrakeWPF/Views/MainView.xaml30
5 files changed, 955 insertions, 19 deletions
diff --git a/win/CS/HandBrakeWPF/Controls/TimeSpanBox.xaml b/win/CS/HandBrakeWPF/Controls/TimeSpanBox.xaml
new file mode 100644
index 000000000..3fdc8d22f
--- /dev/null
+++ b/win/CS/HandBrakeWPF/Controls/TimeSpanBox.xaml
@@ -0,0 +1,175 @@
+<UserControl x:Class="HandBrakeWPF.Controls.TimeSpanBox"
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ Loaded="UserControl_Loaded">
+ <UserControl.Resources>
+ <Style x:Key="Arrow" TargetType="Polygon">
+ <Setter Property="Fill" Value="#333" />
+ </Style>
+ <!-- UpButton and DownButton are identical except for the border radius in the control template.
+ We can't pass that through so we need to duplicate the template. After a change is made from one button
+ template it should be copied to the other. -->
+ <Style x:Key="UpButton" TargetType="Button">
+ <Setter Property="Margin" Value="0"/>
+ <Setter Property="Background" >
+ <Setter.Value>
+ <LinearGradientBrush StartPoint="0,0" EndPoint="0,1" >
+ <GradientStop Color="#EEE" Offset="0.4"/>
+ <GradientStop Color="#BBB" Offset="1"/>
+ </LinearGradientBrush>
+ </Setter.Value>
+ </Setter>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="Button">
+ <Border
+ Name="border"
+ BorderThickness="1"
+ BorderBrush="#888"
+ CornerRadius="0,2,0,0"
+ Background="{TemplateBinding Background}">
+ <ContentPresenter
+ Name="content"
+ HorizontalAlignment="Center" VerticalAlignment="Center" />
+ </Border>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsMouseOver" Value="True">
+ <Setter TargetName="border" Property="BorderBrush" Value="#FF4788c8" />
+ <Setter Property="Foreground" Value="#FF4788c8" />
+ </Trigger>
+ <Trigger Property="IsPressed" Value="True">
+ <Setter Property="Background" >
+ <Setter.Value>
+ <LinearGradientBrush StartPoint="0,0" EndPoint="0,1" >
+ <GradientStop Color="#CCC" Offset="0.4"/>
+ <GradientStop Color="#999" Offset="1"/>
+ </LinearGradientBrush>
+ </Setter.Value>
+ </Setter>
+ <Setter TargetName="content" Property="RenderTransform" >
+ <Setter.Value>
+ <TranslateTransform Y="1.0" />
+ </Setter.Value>
+ </Setter>
+ </Trigger>
+ <Trigger Property="IsDefaulted" Value="True">
+ <Setter TargetName="border" Property="BorderBrush" Value="#FF282828" />
+ </Trigger>
+ <Trigger Property="IsFocused" Value="True">
+ <Setter TargetName="border" Property="BorderBrush" Value="#FF282828" />
+ </Trigger>
+ <Trigger Property="IsEnabled" Value="False">
+ <Setter TargetName="border" Property="Opacity" Value="0.7" />
+ <Setter Property="Foreground" Value="Gray" />
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+ <Style x:Key="DownButton" TargetType="Button">
+ <Setter Property="Margin" Value="0"/>
+ <Setter Property="Background" >
+ <Setter.Value>
+ <LinearGradientBrush StartPoint="0,0" EndPoint="0,1" >
+ <GradientStop Color="#EEE" Offset="0.4"/>
+ <GradientStop Color="#BBB" Offset="1"/>
+ </LinearGradientBrush>
+ </Setter.Value>
+ </Setter>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="Button">
+ <Border
+ Name="border"
+ BorderThickness="1"
+ BorderBrush="#888"
+ CornerRadius="0,0,2,0"
+ Background="{TemplateBinding Background}">
+ <ContentPresenter
+ Name="content"
+ HorizontalAlignment="Center" VerticalAlignment="Center" />
+ </Border>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsMouseOver" Value="True">
+ <Setter TargetName="border" Property="BorderBrush" Value="#FF4788c8" />
+ <Setter Property="Foreground" Value="#FF4788c8" />
+ </Trigger>
+ <Trigger Property="IsPressed" Value="True">
+ <Setter Property="Background" >
+ <Setter.Value>
+ <LinearGradientBrush StartPoint="0,0" EndPoint="0,1" >
+ <GradientStop Color="#CCC" Offset="0.4"/>
+ <GradientStop Color="#999" Offset="1"/>
+ </LinearGradientBrush>
+ </Setter.Value>
+ </Setter>
+ <Setter TargetName="content" Property="RenderTransform" >
+ <Setter.Value>
+ <TranslateTransform Y="1.0" />
+ </Setter.Value>
+ </Setter>
+ </Trigger>
+ <Trigger Property="IsDefaulted" Value="True">
+ <Setter TargetName="border" Property="BorderBrush" Value="#FF282828" />
+ </Trigger>
+ <Trigger Property="IsFocused" Value="True">
+ <Setter TargetName="border" Property="BorderBrush" Value="#FF282828" />
+ </Trigger>
+ <Trigger Property="IsEnabled" Value="False">
+ <Setter TargetName="border" Property="Opacity" Value="0.7" />
+ <Setter Property="Foreground" Value="Gray" />
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+ </UserControl.Resources>
+ <Grid>
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="*" />
+ <ColumnDefinition Width="Auto" />
+ </Grid.ColumnDefinitions>
+ <TextBox
+ Name="numberBox"
+ Grid.Column="0"
+ GotFocus="NumberBoxGotFocus"
+ LostFocus="NumberBoxLostFocus"
+ PreviewTextInput="NumberBoxPreviewTextInput"
+ PreviewKeyDown="NumberBoxPreviewKeyDown"
+ PreviewMouseDown="NumberBoxPreviewMouseDown"
+ PreviewMouseUp="NumberBoxPreviewMouseUp"
+ TextChanged="NumberBoxTextChanged"
+ VerticalContentAlignment="Center"/>
+ <Grid
+ Name="incrementButtonsGrid"
+ Grid.Column="1"
+ Width="16">
+ <Grid.RowDefinitions>
+ <RowDefinition Height="*" />
+ <RowDefinition Height="*" />
+ </Grid.RowDefinitions>
+ <Button
+ Style="{StaticResource UpButton}"
+ Grid.Row="0"
+ FontSize="4"
+ PreviewMouseLeftButtonDown="UpButtonMouseLeftButtonDown"
+ PreviewMouseLeftButtonUp="UpButtonMouseLeftButtonUp">
+ <Polygon
+ Style="{StaticResource Arrow}"
+ Points="4,0 0,4 8,4" />
+ </Button>
+ <Button
+ Style="{StaticResource DownButton}"
+ Grid.Row="1"
+ FontSize="4"
+ PreviewMouseLeftButtonDown="DownButtonMouseLeftButtonDown"
+ PreviewMouseLeftButtonUp="DownButtonMouseLeftButtonUp">
+ <Polygon
+ Style="{StaticResource Arrow}"
+ Points="0,0 8,0 4,4" />
+ </Button>
+ </Grid>
+ </Grid>
+</UserControl>
diff --git a/win/CS/HandBrakeWPF/Controls/TimeSpanBox.xaml.cs b/win/CS/HandBrakeWPF/Controls/TimeSpanBox.xaml.cs
new file mode 100644
index 000000000..5f983a699
--- /dev/null
+++ b/win/CS/HandBrakeWPF/Controls/TimeSpanBox.xaml.cs
@@ -0,0 +1,721 @@
+// --------------------------------------------------------------------------------------------------------------------
+// <copyright file="TimeSpanBox.xaml.cs" company="HandBrake Project (http://handbrake.fr)">
+// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License.
+// </copyright>
+// <summary>
+// Interaction logic for TimeSpanBox.xaml
+// </summary>
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace HandBrakeWPF.Controls
+{
+ using System;
+ using System.Globalization;
+ using System.Linq;
+ using System.Windows;
+ using System.Windows.Controls;
+ using System.Windows.Input;
+ using System.Windows.Media;
+
+ /// <summary>
+ /// Interaction logic for TimeSpanBox.xaml
+ /// </summary>
+ public partial class TimeSpanBox
+ {
+ #region Constants and Fields
+
+ /// <summary>
+ /// The maximum property name.
+ /// </summary>
+ public const string MaximumPropertyName = "Maximum";
+
+ /// <summary>
+ /// The minimum property name.
+ /// </summary>
+ public const string MinimumPropertyName = "Minimum";
+
+ /// <summary>
+ /// The modulus property name.
+ /// </summary>
+ public const string ModulusPropertyName = "Modulus";
+
+ /// <summary>
+ /// The allow empty property.
+ /// </summary>
+ public static readonly DependencyProperty AllowEmptyProperty = DependencyProperty.Register(
+ "AllowEmpty", typeof(bool), typeof(TimeSpanBox), new PropertyMetadata(true, OnAllowEmptyChanged));
+
+ /// <summary>
+ /// The maximum property.
+ /// </summary>
+ public static readonly DependencyProperty MaximumProperty = DependencyProperty.Register(
+ MaximumPropertyName, typeof(int), typeof(TimeSpanBox), new UIPropertyMetadata(int.MaxValue));
+
+ /// <summary>
+ /// The minimum property.
+ /// </summary>
+ public static readonly DependencyProperty MinimumProperty = DependencyProperty.Register(
+ MinimumPropertyName, typeof(int), typeof(TimeSpanBox), new UIPropertyMetadata(int.MinValue));
+
+ /// <summary>
+ /// The modulus property.
+ /// </summary>
+ public static readonly DependencyProperty ModulusProperty = DependencyProperty.Register(
+ ModulusPropertyName, typeof(int), typeof(TimeSpanBox), new UIPropertyMetadata(0));
+
+ /// <summary>
+ /// The number property.
+ /// </summary>
+ public static readonly DependencyProperty NumberProperty = DependencyProperty.Register(
+ "Number", typeof(int), typeof(TimeSpanBox), new PropertyMetadata(OnNumberChanged));
+
+ /// <summary>
+ /// The show time span property.
+ /// </summary>
+ public static readonly DependencyProperty ShowTimeSpanProperty = DependencyProperty.Register(
+ "ShowTimeSpan", typeof(bool), typeof(TimeSpanBox), new PropertyMetadata(OnShowTimeSpanChanged));
+
+ /// <summary>
+ /// The select all threshold.
+ /// </summary>
+ private static readonly TimeSpan SelectAllThreshold = TimeSpan.FromMilliseconds(500);
+
+ /// <summary>
+ /// The has focus.
+ /// </summary>
+ private bool hasFocus;
+
+ /// <summary>
+ /// The last focus mouse down.
+ /// </summary>
+ private DateTime lastFocusMouseDown;
+
+ /// <summary>
+ /// The none caption.
+ /// </summary>
+ private string noneCaption;
+
+ /// <summary>
+ /// The refire control.
+ /// </summary>
+ private RefireControl refireControl;
+
+ /// <summary>
+ /// The suppress refresh.
+ /// </summary>
+ private bool suppressRefresh;
+
+ #endregion
+
+ #region Constructors and Destructors
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TimeSpanBox"/> class.
+ /// </summary>
+ public TimeSpanBox()
+ {
+ this.noneCaption = "(none)";
+ this.UpdateBindingOnTextChange = true;
+ this.ShowIncrementButtons = true;
+ this.SelectAllOnClick = true;
+
+ this.InitializeComponent();
+
+ this.RefreshNumberBox();
+ }
+
+ #endregion
+
+ #region Properties
+
+ /// <summary>
+ /// Gets or sets a value indicating whether show time span.
+ /// </summary>
+ public bool ShowTimeSpan
+ {
+ get
+ {
+ return (bool)this.GetValue(ShowTimeSpanProperty);
+ }
+
+ set
+ {
+ this.SetValue(ShowTimeSpanProperty, value);
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether allow empty.
+ /// </summary>
+ public bool AllowEmpty
+ {
+ get
+ {
+ return (bool)this.GetValue(AllowEmptyProperty);
+ }
+
+ set
+ {
+ this.SetValue(AllowEmptyProperty, value);
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the maximum.
+ /// </summary>
+ public int Maximum
+ {
+ get
+ {
+ return (int)this.GetValue(MaximumProperty);
+ }
+ set
+ {
+ this.SetValue(MaximumProperty, value);
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the minimum.
+ /// </summary>
+ public int Minimum
+ {
+ get
+ {
+ return (int)this.GetValue(MinimumProperty);
+ }
+ set
+ {
+ this.SetValue(MinimumProperty, value);
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the modulus.
+ /// </summary>
+ public int Modulus
+ {
+ get
+ {
+ return (int)this.GetValue(ModulusProperty);
+ }
+ set
+ {
+ this.SetValue(ModulusProperty, value);
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the none caption.
+ /// </summary>
+ public string NoneCaption
+ {
+ get
+ {
+ return this.noneCaption;
+ }
+
+ set
+ {
+ this.noneCaption = value;
+ this.RefreshNumberBox();
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the number.
+ /// </summary>
+ public int Number
+ {
+ get
+ {
+ return (int)this.GetValue(NumberProperty);
+ }
+
+ set
+ {
+ this.SetValue(NumberProperty, value);
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether select all on click.
+ /// </summary>
+ public bool SelectAllOnClick { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether show increment buttons.
+ /// </summary>
+ public bool ShowIncrementButtons { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether update binding on text change.
+ /// </summary>
+ public bool UpdateBindingOnTextChange { get; set; }
+
+ /// <summary>
+ /// Gets the increment.
+ /// </summary>
+ private int Increment
+ {
+ get
+ {
+ return this.Modulus > 0 ? this.Modulus : 1;
+ }
+ }
+
+ #endregion
+
+ #region Methods
+
+ /// <summary>
+ /// The on allow empty changed.
+ /// </summary>
+ /// <param name="dependencyObject">
+ /// The dependency object.
+ /// </param>
+ /// <param name="eventArgs">
+ /// The event args.
+ /// </param>
+ private static void OnAllowEmptyChanged(
+ DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
+ {
+ var numBox = dependencyObject as TimeSpanBox;
+ if (numBox != null)
+ {
+ numBox.RefreshNumberBox();
+ }
+ }
+
+ /// <summary>
+ /// The on number changed.
+ /// </summary>
+ /// <param name="dependencyObject">
+ /// The dependency object.
+ /// </param>
+ /// <param name="eventArgs">
+ /// The event args.
+ /// </param>
+ private static void OnNumberChanged(
+ DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
+ {
+ if (eventArgs.NewValue != eventArgs.OldValue)
+ {
+ var numBox = dependencyObject as TimeSpanBox;
+
+ if (!numBox.suppressRefresh)
+ {
+ numBox.RefreshNumberBox();
+ }
+ }
+ }
+
+ /// <summary>
+ /// The on show timespan number changed.
+ /// </summary>
+ /// <param name="dependencyObject">
+ /// The dependency object.
+ /// </param>
+ /// <param name="eventArgs">
+ /// The event args.
+ /// </param>
+ private static void OnShowTimeSpanChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
+ {
+ if (eventArgs.NewValue != eventArgs.OldValue)
+ {
+ var numBox = dependencyObject as TimeSpanBox;
+
+ if (!numBox.suppressRefresh)
+ {
+ numBox.RefreshNumberBox();
+ }
+ }
+ }
+
+ /// <summary>
+ /// The decrement number.
+ /// </summary>
+ private void DecrementNumber()
+ {
+ int newNumber;
+ if (this.AllowEmpty && this.Number == 0)
+ {
+ newNumber = Math.Min(this.Maximum, -this.Increment);
+ }
+ else
+ {
+ newNumber = this.Number - this.Increment;
+ }
+
+ if (newNumber < this.Minimum)
+ {
+ newNumber = this.Minimum;
+ }
+
+ if (newNumber != this.Number)
+ {
+ this.Number = newNumber;
+ }
+ }
+
+ /// <summary>
+ /// The down button mouse left button down.
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The e.
+ /// </param>
+ private void DownButtonMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
+ {
+ this.refireControl = new RefireControl(this.DecrementNumber);
+ this.refireControl.Begin();
+ }
+
+ /// <summary>
+ /// The down button mouse left button up.
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The e.
+ /// </param>
+ private void DownButtonMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
+ {
+ this.refireControl.Stop();
+ }
+
+ /// <summary>
+ /// The get nearest value.
+ /// </summary>
+ /// <param name="number">
+ /// The number.
+ /// </param>
+ /// <param name="modulus">
+ /// The modulus.
+ /// </param>
+ /// <returns>
+ /// The <see cref="double"/>.
+ /// </returns>
+ private int GetNearestValue(int number, int modulus)
+ {
+ int remainder = number % modulus;
+
+ if (remainder == 0)
+ {
+ return number;
+ }
+
+ return remainder >= (modulus / 2) ? number + (modulus - remainder) : number - remainder;
+ }
+
+ /// <summary>
+ /// The increment number.
+ /// </summary>
+ private void IncrementNumber()
+ {
+ int newNumber;
+ if (this.AllowEmpty && this.Number == 0)
+ {
+ newNumber = Math.Max(this.Minimum, this.Increment);
+ }
+ else
+ {
+ newNumber = this.Number + this.Increment;
+ }
+
+ if (newNumber > this.Maximum)
+ {
+ newNumber = this.Maximum;
+ }
+
+ if (newNumber != this.Number)
+ {
+ this.Number = newNumber;
+ }
+ }
+
+ /// <summary>
+ /// The number box got focus.
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The e.
+ /// </param>
+ private void NumberBoxGotFocus(object sender, RoutedEventArgs e)
+ {
+ this.hasFocus = true;
+
+ if (this.AllowEmpty)
+ {
+ if (this.Number == 0)
+ {
+ this.numberBox.Text = string.Empty;
+ }
+
+ this.numberBox.Foreground = new SolidColorBrush(Colors.Black);
+ }
+ }
+
+ /// <summary>
+ /// The number box lost focus.
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The e.
+ /// </param>
+ private void NumberBoxLostFocus(object sender, RoutedEventArgs e)
+ {
+ this.hasFocus = false;
+
+ if (this.AllowEmpty && this.numberBox.Text == string.Empty)
+ {
+ this.Number = 0;
+ this.RefreshNumberBox();
+ return;
+ }
+
+ this.UpdateNumberBindingFromBox();
+ this.RefreshNumberBox();
+ }
+
+ /// <summary>
+ /// The number box preview key down.
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The e.
+ /// </param>
+ private void NumberBoxPreviewKeyDown(object sender, KeyEventArgs e)
+ {
+ if (e.Key == Key.Space)
+ {
+ e.Handled = true;
+ }
+ }
+
+ /// <summary>
+ /// The number box preview mouse down.
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The e.
+ /// </param>
+ private void NumberBoxPreviewMouseDown(object sender, MouseButtonEventArgs e)
+ {
+ if (!this.hasFocus)
+ {
+ this.lastFocusMouseDown = DateTime.Now;
+ }
+ }
+
+ /// <summary>
+ /// The number box preview mouse up.
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The e.
+ /// </param>
+ private void NumberBoxPreviewMouseUp(object sender, MouseButtonEventArgs e)
+ {
+ // If this mouse up is soon enough after an initial click on the box, select all.
+ if (this.SelectAllOnClick && DateTime.Now - this.lastFocusMouseDown < SelectAllThreshold)
+ {
+ this.Dispatcher.BeginInvoke(new Action(() => this.numberBox.SelectAll()));
+ }
+ }
+
+ /// <summary>
+ /// The number box preview text input.
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The e.
+ /// </param>
+ private void NumberBoxPreviewTextInput(object sender, TextCompositionEventArgs e)
+ {
+ if (e.Text.Any(c => !char.IsNumber(c) && c != '.' && c != ':' && (this.Minimum >= 0 || c != '-')))
+ {
+ e.Handled = true;
+ }
+ }
+
+ /// <summary>
+ /// The number box text changed.
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The e.
+ /// </param>
+ private void NumberBoxTextChanged(object sender, TextChangedEventArgs e)
+ {
+ if (this.UpdateBindingOnTextChange)
+ {
+ if (this.AllowEmpty && this.numberBox.Text == string.Empty)
+ {
+ this.Number = 0;
+ return;
+ }
+
+ this.UpdateNumberBindingFromBox();
+ }
+
+ this.RefreshNumberBoxColor();
+ }
+
+ /// <summary>
+ /// The number is valid.
+ /// </summary>
+ /// <param name="number">
+ /// The number.
+ /// </param>
+ /// <returns>
+ /// The <see cref="bool"/>.
+ /// </returns>
+ private bool NumberIsValid(double number)
+ {
+ return number >= this.Minimum && number <= this.Maximum;
+ }
+
+ /// <summary>
+ /// The refresh number box.
+ /// </summary>
+ private void RefreshNumberBox()
+ {
+ if (this.AllowEmpty && this.Number == 0)
+ {
+ this.numberBox.Text = this.hasFocus ? string.Empty : this.NoneCaption;
+
+ // this.numberBox.Foreground = new SolidColorBrush(Colors.Gray);
+ }
+ else
+ {
+ if (this.ShowTimeSpan)
+ {
+ this.numberBox.Text = TimeSpan.FromSeconds(this.Number).ToString();
+ }
+ else
+ {
+ this.numberBox.Text = this.Number.ToString(CultureInfo.InvariantCulture);
+ }
+
+ // this.numberBox.Foreground = new SolidColorBrush(Colors.Black);
+ }
+
+ this.RefreshNumberBoxColor();
+ }
+
+ /// <summary>
+ /// The refresh number box color.
+ /// </summary>
+ private void RefreshNumberBoxColor()
+ {
+ this.numberBox.Foreground = this.numberBox.Text == this.NoneCaption ? new SolidColorBrush(Colors.Gray) : new SolidColorBrush(Colors.Black);
+ }
+
+ /// <summary>
+ /// The up button mouse left button down.
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The e.
+ /// </param>
+ private void UpButtonMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
+ {
+ this.refireControl = new RefireControl(this.IncrementNumber);
+ this.refireControl.Begin();
+ }
+
+ /// <summary>
+ /// The up button mouse left button up.
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The e.
+ /// </param>
+ private void UpButtonMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
+ {
+ this.refireControl.Stop();
+ }
+
+ /// <summary>
+ /// The update number binding from box.
+ /// </summary>
+ private void UpdateNumberBindingFromBox()
+ {
+ int newNumber;
+ TimeSpan newTimespanNumber;
+ if (int.TryParse(this.numberBox.Text, out newNumber))
+ {
+ if (this.NumberIsValid(newNumber))
+ {
+ if (this.Modulus != 0)
+ {
+ newNumber = this.GetNearestValue(newNumber, this.Modulus);
+ }
+
+ if (newNumber != this.Number)
+ {
+ // While updating the binding we don't need to react to the change.
+ this.suppressRefresh = true;
+ this.Number = newNumber;
+ this.suppressRefresh = false;
+ }
+ }
+ }
+ else if (TimeSpan.TryParse(this.numberBox.Text, out newTimespanNumber))
+ {
+ if (newTimespanNumber != TimeSpan.Zero)
+ {
+ int seconds = (int)Math.Round(newTimespanNumber.TotalSeconds, 0);
+ if (seconds != this.Number)
+ {
+ // While updating the binding we don't need to react to the change.
+ this.suppressRefresh = true;
+ this.Number = seconds;
+ this.suppressRefresh = false;
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// The user control_ loaded.
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The e.
+ /// </param>
+ private void UserControl_Loaded(object sender, RoutedEventArgs e)
+ {
+ if (!this.ShowIncrementButtons)
+ {
+ this.incrementButtonsGrid.Visibility = Visibility.Collapsed;
+ }
+ }
+
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/win/CS/HandBrakeWPF/HandBrakeWPF.csproj b/win/CS/HandBrakeWPF/HandBrakeWPF.csproj
index e0d2e7696..c2214eee4 100644
--- a/win/CS/HandBrakeWPF/HandBrakeWPF.csproj
+++ b/win/CS/HandBrakeWPF/HandBrakeWPF.csproj
@@ -129,6 +129,9 @@
<Compile Include="Commands\ProcessShortcutCommand.cs" />
<Compile Include="Commands\SourceMenuCommand.cs" />
<Compile Include="Commands\AdvancedEncoderOptionsCommand.cs" />
+ <Compile Include="Controls\TimeSpanBox.xaml.cs">
+ <DependentUpon>TimeSpanBox.xaml</DependentUpon>
+ </Compile>
<Compile Include="Controls\Loading.xaml.cs">
<DependentUpon>Loading.xaml</DependentUpon>
</Compile>
@@ -311,6 +314,10 @@
<AppDesigner Include="Properties\" />
</ItemGroup>
<ItemGroup>
+ <Page Include="Controls\TimeSpanBox.xaml">
+ <Generator>MSBuild:Compile</Generator>
+ <SubType>Designer</SubType>
+ </Page>
<Page Include="Controls\Loading.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
diff --git a/win/CS/HandBrakeWPF/ViewModels/MainViewModel.cs b/win/CS/HandBrakeWPF/ViewModels/MainViewModel.cs
index 1d4d0e7ed..c4e45208c 100644
--- a/win/CS/HandBrakeWPF/ViewModels/MainViewModel.cs
+++ b/win/CS/HandBrakeWPF/ViewModels/MainViewModel.cs
@@ -440,6 +440,12 @@ namespace HandBrakeWPF.ViewModels
{
get
{
+ // Sanity Check
+ if (ScannedSource == null || ScannedSource.ScanPath == null)
+ {
+ return string.Empty;
+ }
+
// The title that is selected has a source name. This means it's part of a batch scan.
if (selectedTitle != null && !string.IsNullOrEmpty(selectedTitle.SourceName))
{
@@ -699,6 +705,7 @@ namespace HandBrakeWPF.ViewModels
{
return this.CurrentTask.StartPoint;
}
+
set
{
this.CurrentTask.Angle = value;
@@ -707,6 +714,11 @@ namespace HandBrakeWPF.ViewModels
}
/// <summary>
+ /// Gets or sets a value indicating whether is timespan range.
+ /// </summary>
+ public bool IsTimespanRange { get; set; }
+
+ /// <summary>
/// Gets or sets SelectedStartPoint.
/// </summary>
public int SelectedStartPoint
@@ -721,7 +733,7 @@ namespace HandBrakeWPF.ViewModels
this.NotifyOfPropertyChange(() => this.SelectedStartPoint);
this.Duration = this.DurationCalculation();
- if (this.UserSettingService.GetUserSetting<bool>(UserSettingConstants.AutoNaming))
+ if (this.UserSettingService.GetUserSetting<bool>(UserSettingConstants.AutoNaming) && this.ScannedSource.ScanPath != null)
{
this.Destination = AutoNameHelper.AutoName(this.CurrentTask, this.SourceName);
}
@@ -743,7 +755,7 @@ namespace HandBrakeWPF.ViewModels
this.NotifyOfPropertyChange(() => this.SelectedEndPoint);
this.Duration = this.DurationCalculation();
- if (this.UserSettingService.GetUserSetting<bool>(UserSettingConstants.AutoNaming))
+ if (this.UserSettingService.GetUserSetting<bool>(UserSettingConstants.AutoNaming) && this.ScannedSource.ScanPath != null)
{
this.Destination = AutoNameHelper.AutoName(this.CurrentTask, this.SourceName);
}
@@ -767,21 +779,41 @@ namespace HandBrakeWPF.ViewModels
if (value == PointToPointMode.Chapters && this.SelectedTitle != null)
{
+ if (this.selectedTitle == null)
+ {
+ return;
+ }
+
+
this.SelectedStartPoint = 1;
this.SelectedEndPoint = selectedTitle.Chapters.Last().ChapterNumber;
}
else if (value == PointToPointMode.Seconds)
{
+ if (this.selectedTitle == null)
+ {
+ return;
+ }
+
+
this.SelectedStartPoint = 0;
int timeInSeconds;
if (int.TryParse(selectedTitle.Duration.TotalSeconds.ToString(CultureInfo.InvariantCulture), out timeInSeconds))
{
this.SelectedEndPoint = timeInSeconds;
- }
+ }
+
+ this.IsTimespanRange = true;
+ this.NotifyOfPropertyChange(() => this.IsTimespanRange);
}
else
{
+ if (this.selectedTitle == null)
+ {
+ return;
+ }
+
// Note this does not account for VFR. It's only a guesstimate.
double estimatedTotalFrames = selectedTitle.Fps * selectedTitle.Duration.TotalSeconds;
@@ -791,6 +823,9 @@ namespace HandBrakeWPF.ViewModels
{
this.SelectedEndPoint = totalFrames;
}
+
+ this.IsTimespanRange = false;
+ this.NotifyOfPropertyChange(() => this.IsTimespanRange);
}
}
}
diff --git a/win/CS/HandBrakeWPF/Views/MainView.xaml b/win/CS/HandBrakeWPF/Views/MainView.xaml
index 1131e5bdd..5fbc3cc93 100644
--- a/win/CS/HandBrakeWPF/Views/MainView.xaml
+++ b/win/CS/HandBrakeWPF/Views/MainView.xaml
@@ -371,14 +371,13 @@
Converter={StaticResource boolToVisConverter},
ConverterParameter=true}"
/>
- <TextBox Name="StartPointText"
- MinWidth="60"
- Margin="8,0,0,0"
- Text="{Binding SelectedStartPoint, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
- Visibility="{Binding ShowTextEntryForPointToPointMode,
- Converter={StaticResource boolToVisConverter},
- ConverterParameter=false}"
- />
+ <Controls:TimeSpanBox Number="{Binding SelectedStartPoint, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
+ Minimum="0" AllowEmpty="False" ShowTimeSpan="{Binding IsTimespanRange}"
+ MinWidth="80" Margin="8,0,0,0" Visibility="{Binding ShowTextEntryForPointToPointMode,
+ Converter={StaticResource boolToVisConverter},
+ ConverterParameter=false}"
+ />
+
<Label Margin="8,0,0,0" Content="through" />
<ComboBox Name="EndPoint"
MinWidth="60"
@@ -389,14 +388,13 @@
Converter={StaticResource boolToVisConverter},
ConverterParameter=true}"
/>
- <TextBox Name="EndPointText"
- MinWidth="60"
- Margin="8,0,0,0"
- Text="{Binding SelectedEndPoint, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
- Visibility="{Binding ShowTextEntryForPointToPointMode,
- Converter={StaticResource boolToVisConverter},
- ConverterParameter=false}"
- />
+ <Controls:TimeSpanBox Number="{Binding SelectedEndPoint, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
+ Minimum="0" AllowEmpty="False" ShowTimeSpan="{Binding IsTimespanRange}"
+ MinWidth="80" Margin="8,0,0,0" Visibility="{Binding ShowTextEntryForPointToPointMode,
+ Converter={StaticResource boolToVisConverter},
+ ConverterParameter=false}"
+ />
+
<Label Margin="8,0,0,0" Content="Duration" />
<Label Margin="8,0,0,0" Content="{Binding Duration}" />
</StackPanel>