diff --git a/Hourglass.Bundle/Bundle.wxs b/Hourglass.Bundle/Bundle.wxs
index 63e716d..1e1fb49 100644
--- a/Hourglass.Bundle/Bundle.wxs
+++ b/Hourglass.Bundle/Bundle.wxs
@@ -1,13 +1,25 @@
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Hourglass.Bundle/MIT.rtf b/Hourglass.Bundle/MIT.rtf
index fca9b2f..055b3b7 100644
Binary files a/Hourglass.Bundle/MIT.rtf and b/Hourglass.Bundle/MIT.rtf differ
diff --git a/Hourglass.Setup/Product.wxs b/Hourglass.Setup/Product.wxs
index 73f8ace..0173753 100644
--- a/Hourglass.Setup/Product.wxs
+++ b/Hourglass.Setup/Product.wxs
@@ -1,7 +1,7 @@
-
+
diff --git a/Hourglass.Test/Properties/AssemblyInfo.cs b/Hourglass.Test/Properties/AssemblyInfo.cs
index 0ba8dc6..87919af 100644
--- a/Hourglass.Test/Properties/AssemblyInfo.cs
+++ b/Hourglass.Test/Properties/AssemblyInfo.cs
@@ -12,10 +12,10 @@
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Hourglass.Test")]
-[assembly: AssemblyCopyright("Copyright © 2017 Chris Dziemborowicz")]
+[assembly: AssemblyCopyright("Copyright © 2018 Chris Dziemborowicz")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("002a4be7-7323-4bf9-ab08-5fc8978d9eb0")]
-[assembly: AssemblyVersion("1.9.0.0")]
-[assembly: AssemblyFileVersion("1.9.0.0")]
+[assembly: AssemblyVersion("1.10.0.0")]
+[assembly: AssemblyFileVersion("1.10.0.0")]
diff --git a/Hourglass/CommandLineArguments.cs b/Hourglass/CommandLineArguments.cs
index b437e86..5927bb8 100644
--- a/Hourglass/CommandLineArguments.cs
+++ b/Hourglass/CommandLineArguments.cs
@@ -159,6 +159,12 @@ public static string Usage
///
public Rect WindowBounds { get; private set; }
+ ///
+ /// Gets a value indicating whether the user interface should be locked, preventing the user from taking any
+ /// action until the timer expires.
+ ///
+ public bool LockInterface { get; private set; }
+
#endregion
#region Public Methods
@@ -224,7 +230,8 @@ public TimerOptions GetTimerOptions()
Sound = this.Sound,
LoopSound = this.LoopSound,
WindowTitleMode = this.WindowTitleMode,
- WindowSize = this.GetWindowSize()
+ WindowSize = this.GetWindowSize(),
+ LockInterface = this.LockInterface
};
}
@@ -274,7 +281,8 @@ private static CommandLineArguments GetArgumentsFromMostRecentOptions()
WindowTitleMode = options.WindowTitleMode,
WindowState = windowSize.WindowState != WindowState.Minimized ? windowSize.WindowState : windowSize.RestoreWindowState,
RestoreWindowState = windowSize.RestoreWindowState,
- WindowBounds = windowSize.RestoreBounds
+ WindowBounds = windowSize.RestoreBounds,
+ LockInterface = options.LockInterface
};
}
@@ -310,7 +318,8 @@ private static CommandLineArguments GetArgumentsFromFactoryDefaults()
WindowTitleMode = WindowTitleMode.ApplicationName,
WindowState = defaultOptions.WindowSize.WindowState,
RestoreWindowState = defaultOptions.WindowSize.RestoreWindowState,
- WindowBounds = defaultWindowBoundsWithLocation
+ WindowBounds = defaultWindowBoundsWithLocation,
+ LockInterface = defaultOptions.LockInterface
};
}
@@ -575,6 +584,18 @@ private static CommandLineArguments GetCommandLineArguments(IEnumerable
argumentsBasedOnFactoryDefaults.WindowBounds = argumentsBasedOnFactoryDefaults.WindowBounds.Merge(windowBounds);
break;
+ case "--lock-interface":
+ case "-z":
+ ThrowIfDuplicateSwitch(specifiedSwitches, "--lock-interface");
+
+ bool lockInterface = GetBoolValue(
+ arg,
+ remainingArgs);
+
+ argumentsBasedOnMostRecentOptions.LockInterface = lockInterface;
+ argumentsBasedOnFactoryDefaults.LockInterface = lockInterface;
+ break;
+
case "--use-factory-defaults":
case "-d":
ThrowIfDuplicateSwitch(specifiedSwitches, "--use-factory-defaults");
diff --git a/Hourglass/Managers/TimerManager.cs b/Hourglass/Managers/TimerManager.cs
index ac6e2c1..650e7e3 100644
--- a/Hourglass/Managers/TimerManager.cs
+++ b/Hourglass/Managers/TimerManager.cs
@@ -84,6 +84,7 @@ public override void Persist()
{
Settings.Default.Timers = this.timers
.Where(t => t.State != TimerState.Stopped && t.State != TimerState.Expired)
+ .Where(t => !t.Options.LockInterface)
.Take(MaxSavedTimers)
.ToList();
}
diff --git a/Hourglass/Managers/TimerOptionsManager.cs b/Hourglass/Managers/TimerOptionsManager.cs
index fc6872f..46cb664 100644
--- a/Hourglass/Managers/TimerOptionsManager.cs
+++ b/Hourglass/Managers/TimerOptionsManager.cs
@@ -85,8 +85,9 @@ orderby window.Menu.LastShowed descending
// Never save a title
this.mostRecentOptions.Title = string.Empty;
- // Never save shutting down when expired
+ // Never save shutting down when expired or lock interface options
this.mostRecentOptions.ShutDownWhenExpired = false;
+ this.mostRecentOptions.LockInterface = false;
}
}
}
diff --git a/Hourglass/Parsing/DateTimeToken.cs b/Hourglass/Parsing/DateTimeToken.cs
index deeed01..aedbc58 100644
--- a/Hourglass/Parsing/DateTimeToken.cs
+++ b/Hourglass/Parsing/DateTimeToken.cs
@@ -76,7 +76,7 @@ public override DateTime GetEndTime(DateTime startTime)
dateTime = this.TimeToken.ToDateTime(startTime, datePart);
}
- if (dateTime <= startTime)
+ if (dateTime < startTime)
{
throw new InvalidOperationException();
}
diff --git a/Hourglass/Parsing/TimeSpanToken.cs b/Hourglass/Parsing/TimeSpanToken.cs
index e2434f0..05c12a3 100644
--- a/Hourglass/Parsing/TimeSpanToken.cs
+++ b/Hourglass/Parsing/TimeSpanToken.cs
@@ -90,7 +90,7 @@ public override DateTime GetEndTime(DateTime startTime)
endTime = endTime.AddMonths(this.Months);
endTime = endTime.AddYears(this.Years);
- if (endTime <= startTime)
+ if (endTime < startTime)
{
throw new InvalidOperationException();
}
diff --git a/Hourglass/Properties/App.manifest b/Hourglass/Properties/App.manifest
index 7217a9e..2daaac3 100644
--- a/Hourglass/Properties/App.manifest
+++ b/Hourglass/Properties/App.manifest
@@ -1,6 +1,6 @@
-
+
diff --git a/Hourglass/Properties/AssemblyInfo.cs b/Hourglass/Properties/AssemblyInfo.cs
index 4ca6c25..d48e180 100644
--- a/Hourglass/Properties/AssemblyInfo.cs
+++ b/Hourglass/Properties/AssemblyInfo.cs
@@ -14,11 +14,11 @@
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Chris Dziemborowicz")]
[assembly: AssemblyProduct("Hourglass")]
-[assembly: AssemblyCopyright("Copyright © 2017 Chris Dziemborowicz")]
+[assembly: AssemblyCopyright("Copyright © 2018 Chris Dziemborowicz")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
-[assembly: AssemblyVersion("1.9.0.0")]
-[assembly: AssemblyFileVersion("1.9.0.0")]
+[assembly: AssemblyVersion("1.10.0.0")]
+[assembly: AssemblyFileVersion("1.10.0.0")]
[assembly: NeutralResourcesLanguageAttribute("en-US")]
[assembly: Guid("83DBAA61-6193-4288-BBB7-BEAEC33FE321")]
[assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)]
diff --git a/Hourglass/Properties/Resources.Designer.cs b/Hourglass/Properties/Resources.Designer.cs
index 13c3c89..e3ed664 100644
--- a/Hourglass/Properties/Resources.Designer.cs
+++ b/Hourglass/Properties/Resources.Designer.cs
@@ -1717,6 +1717,15 @@ internal static string TimerStartTokenUseDateTimeParserPattern {
}
}
+ ///
+ /// Looks up a localized string similar to 0 seconds.
+ ///
+ internal static string TimerStartZero {
+ get {
+ return ResourceManager.GetString("TimerStartZero", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Stopped.
///
diff --git a/Hourglass/Properties/Resources.resx b/Hourglass/Properties/Resources.resx
index a17056d..312c595 100644
--- a/Hourglass/Properties/Resources.resx
+++ b/Hourglass/Properties/Resources.resx
@@ -1229,4 +1229,8 @@ $
·
The separator for parts of the window title for the timer window
+
+ 0 seconds
+ The zero-length value for a timer
+
\ No newline at end of file
diff --git a/Hourglass/Resources/Usage.txt b/Hourglass/Resources/Usage.txt
index 34423f2..66f9b0c 100644
--- a/Hourglass/Resources/Usage.txt
+++ b/Hourglass/Resources/Usage.txt
@@ -191,6 +191,21 @@ Options:
Default value last
Alias -b
+ --lock-interface on|off
+ Prevents the user from starting, pausing, resetting, or stopping the
+ timer, changing the timer options, or closing the timer window until the
+ timer expires.
+
+ This option is never remembered. It must be specified as a command-line
+ argument each time the timer is started.
+
+ Timers that are started with this option turned on never appear in the
+ "Saved timers" list.
+
+ Required no
+ Default value off
+ Alias -z
+
--use-factory-defaults
If specified, any options that are not explicitly set with another
switch are set to their factory default setting rather than the last
@@ -214,6 +229,7 @@ Options:
--open-saved-timers off
--window-state normal
--window-bounds auto,auto,350,150
+ --lock-interface off
Required no
Alias -d
diff --git a/Hourglass/Serialization/TimerOptionsInfo.cs b/Hourglass/Serialization/TimerOptionsInfo.cs
index 26fa81d..ec21782 100644
--- a/Hourglass/Serialization/TimerOptionsInfo.cs
+++ b/Hourglass/Serialization/TimerOptionsInfo.cs
@@ -87,6 +87,12 @@ public class TimerOptionsInfo
///
public WindowSizeInfo WindowSize { get; set; }
+ ///
+ /// Gets or sets a value indicating whether the user interface should be locked, preventing the user from taking
+ /// any action until the timer expires.
+ ///
+ public bool LockInterface { get; set; }
+
///
/// Returns a for the specified .
///
diff --git a/Hourglass/Timing/TimerOptions.cs b/Hourglass/Timing/TimerOptions.cs
index b7a3520..3981566 100644
--- a/Hourglass/Timing/TimerOptions.cs
+++ b/Hourglass/Timing/TimerOptions.cs
@@ -138,6 +138,12 @@ public class TimerOptions : INotifyPropertyChanged
///
private WindowSize windowSize;
+ ///
+ /// A value indicating whether the user interface should be locked, preventing the user from taking any action
+ /// until the timer expires.
+ ///
+ private bool lockInterface;
+
#endregion
#region Constructors
@@ -165,6 +171,7 @@ public TimerOptions()
WindowState.Normal,
WindowState.Normal,
false /* isFullScreen */);
+ this.lockInterface = false;
}
///
@@ -510,6 +517,29 @@ public WindowSize WindowSize
}
}
+ ///
+ /// Gets or sets a value indicating whether the user interface should be locked, preventing the user from taking
+ /// any action until the timer expires.
+ ///
+ public bool LockInterface
+ {
+ get
+ {
+ return this.lockInterface;
+ }
+
+ set
+ {
+ if (this.lockInterface == value)
+ {
+ return;
+ }
+
+ this.lockInterface = value;
+ this.OnPropertyChanged("LockInterface");
+ }
+ }
+
#endregion
#region Public Methods
@@ -563,6 +593,7 @@ public void Set(TimerOptions options)
this.loopSound = options.loopSound;
this.windowTitleMode = options.windowTitleMode;
this.windowSize = WindowSize.FromWindowSize(options.WindowSize);
+ this.lockInterface = options.lockInterface;
this.OnPropertyChanged(
"Title",
@@ -578,7 +609,8 @@ public void Set(TimerOptions options)
"Sound",
"LoopSound",
"WindowTitleMode",
- "WindowSize");
+ "WindowSize",
+ "LockInterface");
}
///
@@ -606,6 +638,7 @@ public void Set(TimerOptionsInfo info)
this.loopSound = info.LoopSound;
this.windowTitleMode = info.WindowTitleMode;
this.windowSize = WindowSize.FromWindowSizeInfo(info.WindowSize);
+ this.lockInterface = info.LockInterface;
this.OnPropertyChanged(
"Title",
@@ -621,7 +654,8 @@ public void Set(TimerOptionsInfo info)
"Sound",
"LoopSound",
"WindowTitleMode",
- "WindowSize");
+ "WindowSize",
+ "LockInterface");
}
///
@@ -645,7 +679,8 @@ public TimerOptionsInfo ToTimerOptionsInfo()
SoundIdentifier = this.sound?.Identifier,
LoopSound = this.loopSound,
WindowTitleMode = this.windowTitleMode,
- WindowSize = WindowSizeInfo.FromWindowSize(this.windowSize)
+ WindowSize = WindowSizeInfo.FromWindowSize(this.windowSize),
+ LockInterface = this.lockInterface
};
}
diff --git a/Hourglass/Timing/TimerStart.cs b/Hourglass/Timing/TimerStart.cs
index 379bf3c..f79213f 100644
--- a/Hourglass/Timing/TimerStart.cs
+++ b/Hourglass/Timing/TimerStart.cs
@@ -74,6 +74,14 @@ public static TimerStart Default
get { return TimerStart.FromString(Resources.TimerStartDefault); }
}
+ ///
+ /// Gets the zero-length object.
+ ///
+ public static TimerStart Zero
+ {
+ get { return TimerStart.FromString(Resources.TimerStartZero); }
+ }
+
///
/// Gets a value indicating whether the can be used to start a timer now.
///
@@ -81,8 +89,9 @@ public bool IsCurrent
{
get
{
+ DateTime now = DateTime.Now;
DateTime endTime;
- return this.timerStartToken.TryGetEndTime(DateTime.Now, out endTime) && endTime > DateTime.Now;
+ return this.timerStartToken.TryGetEndTime(now, out endTime) && endTime >= now;
}
}
diff --git a/Hourglass/Windows/ContextMenu.cs b/Hourglass/Windows/ContextMenu.cs
index 3739e4d..dded80f 100644
--- a/Hourglass/Windows/ContextMenu.cs
+++ b/Hourglass/Windows/ContextMenu.cs
@@ -290,6 +290,13 @@ public void Bind(TimerWindow window)
/// The event data.
private void WindowContextMenuOpening(object sender, ContextMenuEventArgs e)
{
+ // Do not show the context menu if the user interface is locked
+ if (this.timerWindow.Options.LockInterface)
+ {
+ e.Handled = true;
+ return;
+ }
+
// Update dynamic items
this.UpdateRecentInputsMenuItem();
this.UpdateSavedTimersMenuItem();
diff --git a/Hourglass/Windows/TimerWindow.xaml.cs b/Hourglass/Windows/TimerWindow.xaml.cs
index bb27139..065a994 100644
--- a/Hourglass/Windows/TimerWindow.xaml.cs
+++ b/Hourglass/Windows/TimerWindow.xaml.cs
@@ -15,7 +15,7 @@ namespace Hourglass.Windows
using System.Windows.Input;
using System.Windows.Media.Animation;
using System.Windows.Shell;
-
+
using Hourglass.Extensions;
using Hourglass.Managers;
using Hourglass.Properties;
@@ -414,7 +414,9 @@ public WindowState RestoreWindowState
/// specified .
///
/// A .
- public void Show(TimerStart timerStart)
+ /// A value indicating whether to remember the as a recent
+ /// input.
+ public void Show(TimerStart timerStart, bool remember = true)
{
// Keep track of the input
this.LastTimerStart = timerStart;
@@ -423,14 +425,30 @@ public void Show(TimerStart timerStart)
Timer newTimer = new Timer(this.Options);
if (!newTimer.Start(timerStart))
{
- this.Show();
- this.SwitchToInputMode();
- this.BeginValidationErrorAnimation();
- return;
+ // The user has started a timer that expired in the past
+ if (this.Options.LockInterface)
+ {
+ // If the interface is locked, there is nothing the user can do or should be able to do other than
+ // close the window, so pretend that the timer expired immediately
+ this.Show(TimerStart.Zero, false /* remember */);
+ return;
+ }
+ else
+ {
+ // Otherwise, assume the user made an error and display a validation error animation
+ this.Show();
+ this.SwitchToInputMode();
+ this.BeginValidationErrorAnimation();
+ return;
+ }
}
TimerManager.Instance.Add(newTimer);
- TimerStartManager.Instance.Add(timerStart);
+
+ if (remember)
+ {
+ TimerStartManager.Instance.Add(timerStart);
+ }
// Show the window
this.Show(newTimer);
@@ -445,7 +463,7 @@ public void Show(Timer existingTimer)
// Show the status of the existing timer
this.Timer = existingTimer;
this.SwitchToStatusMode();
-
+
// Show the window if it is not already open
if (!this.IsVisible)
{
@@ -628,7 +646,7 @@ private bool CancelOrReset()
this.SwitchToStatusMode();
return true;
}
-
+
// Stop playing the notification sound if it is playing
if (this.soundPlayer.IsPlaying)
{
@@ -639,14 +657,14 @@ private bool CancelOrReset()
return false;
case TimerWindowMode.Status:
- // Switch to input mode if the timer is expired
- if (this.Timer.State == TimerState.Expired)
+ // Switch to input mode if the timer is expired and the interface is not locked
+ if (this.Timer.State == TimerState.Expired && !this.Options.LockInterface)
{
this.Timer.Stop();
this.SwitchToInputMode();
return true;
}
-
+
// Stop playing the notification sound if it is playing
if (this.soundPlayer.IsPlaying)
{
@@ -949,16 +967,16 @@ private void SoundPlayerPlaybackCompleted(object sender, EventArgs e)
#endregion
#region Private Methods (Update Button)
-
+
///
/// Initializes the update button.
///
private void InitializeUpdateButton()
{
UpdateManager.Instance.PropertyChanged += this.UpdateManagerPropertyChanged;
- this.UpdateButton.IsEnabled = UpdateManager.Instance.HasUpdates;
+ this.UpdateButton.IsEnabled = UpdateManager.Instance.HasUpdates && (this.Mode == TimerWindowMode.Input || !this.Options.LockInterface);
}
-
+
///
/// Invoked when a property value changes.
///
@@ -968,7 +986,7 @@ private void UpdateManagerPropertyChanged(object sender, PropertyChangedEventArg
{
this.Dispatcher.BeginInvoke(new Action(() =>
{
- this.UpdateButton.IsEnabled = UpdateManager.Instance.HasUpdates;
+ this.UpdateButton.IsEnabled = UpdateManager.Instance.HasUpdates && (this.Mode == TimerWindowMode.Input || !this.Options.LockInterface);
}));
}
@@ -1007,6 +1025,7 @@ private void UpdateBoundControls()
this.ProgressBar.Value = this.Timer.TimeLeftAsPercentage ?? 0.0;
this.UpdateTaskbarProgress();
+ // Enable and disable command buttons as required
this.StartButton.IsEnabled = true;
this.PauseButton.IsEnabled = false;
this.ResumeButton.IsEnabled = false;
@@ -1014,6 +1033,15 @@ private void UpdateBoundControls()
this.ResetButton.IsEnabled = false;
this.CloseButton.IsEnabled = false;
this.CancelButton.IsEnabled = this.Timer.State != TimerState.Stopped && this.Timer.State != TimerState.Expired;
+ this.UpdateButton.IsEnabled = UpdateManager.Instance.HasUpdates;
+
+ // Restore the border, context menu, and watermark text that appear for the text boxes
+ this.TitleTextBox.BorderThickness = new Thickness(1);
+ this.TimerTextBox.BorderThickness = new Thickness(1);
+ this.TitleTextBox.IsReadOnly = false;
+ this.TimerTextBox.IsReadOnly = false;
+ Watermark.SetHint(this.TitleTextBox, Properties.Resources.TimerWindowTitleTextHint);
+ Watermark.SetHint(this.TimerTextBox, Properties.Resources.TimerWindowTimerTextHint);
this.Topmost = this.Options.AlwaysOnTop;
@@ -1029,13 +1057,46 @@ private void UpdateBoundControls()
this.ProgressBar.Value = this.Timer.TimeLeftAsPercentage ?? 0.0;
this.UpdateTaskbarProgress();
- this.StartButton.IsEnabled = false;
- this.PauseButton.IsEnabled = this.Timer.State == TimerState.Running && this.Timer.SupportsPause;
- this.ResumeButton.IsEnabled = this.Timer.State == TimerState.Paused;
- this.StopButton.IsEnabled = this.Timer.State != TimerState.Stopped && this.Timer.State != TimerState.Expired;
- this.ResetButton.IsEnabled = this.Timer.State == TimerState.Stopped || this.Timer.State == TimerState.Expired;
- this.CloseButton.IsEnabled = this.Timer.State == TimerState.Stopped || this.Timer.State == TimerState.Expired;
- this.CancelButton.IsEnabled = false;
+ if (this.Options.LockInterface)
+ {
+ // Disable command buttons except for close when stopped or expired
+ this.StartButton.IsEnabled = false;
+ this.PauseButton.IsEnabled = false;
+ this.ResumeButton.IsEnabled = false;
+ this.StopButton.IsEnabled = false;
+ this.ResetButton.IsEnabled = false;
+ this.CloseButton.IsEnabled = this.Timer.State == TimerState.Stopped || this.Timer.State == TimerState.Expired;
+ this.CancelButton.IsEnabled = false;
+ this.UpdateButton.IsEnabled = false;
+
+ // Hide the border, context menu, and watermark text that appear for the text boxes
+ this.TitleTextBox.BorderThickness = new Thickness(0);
+ this.TimerTextBox.BorderThickness = new Thickness(0);
+ this.TitleTextBox.IsReadOnly = true;
+ this.TimerTextBox.IsReadOnly = true;
+ Watermark.SetHint(this.TitleTextBox, null);
+ Watermark.SetHint(this.TimerTextBox, null);
+ }
+ else
+ {
+ // Enable and disable command buttons as required
+ this.StartButton.IsEnabled = false;
+ this.PauseButton.IsEnabled = this.Timer.State == TimerState.Running && this.Timer.SupportsPause;
+ this.ResumeButton.IsEnabled = this.Timer.State == TimerState.Paused;
+ this.StopButton.IsEnabled = this.Timer.State != TimerState.Stopped && this.Timer.State != TimerState.Expired;
+ this.ResetButton.IsEnabled = this.Timer.State == TimerState.Stopped || this.Timer.State == TimerState.Expired;
+ this.CloseButton.IsEnabled = this.Timer.State == TimerState.Stopped || this.Timer.State == TimerState.Expired;
+ this.CancelButton.IsEnabled = false;
+ this.UpdateButton.IsEnabled = UpdateManager.Instance.HasUpdates;
+
+ // Restore the border, context menu, and watermark text that appear for the text boxes
+ this.TitleTextBox.BorderThickness = new Thickness(1);
+ this.TimerTextBox.BorderThickness = new Thickness(1);
+ this.TitleTextBox.IsReadOnly = false;
+ this.TimerTextBox.IsReadOnly = false;
+ Watermark.SetHint(this.TitleTextBox, Properties.Resources.TimerWindowTitleTextHint);
+ Watermark.SetHint(this.TimerTextBox, Properties.Resources.TimerWindowTimerTextHint);
+ }
this.Topmost = this.Options.AlwaysOnTop;
@@ -1398,6 +1459,9 @@ private void StartCommandExecuted(object sender, ExecutedRoutedEventArgs e)
return;
}
+ // If the interface was previously locked, unlock it when a new timer is started
+ this.Options.LockInterface = false;
+
this.Show(timerStart);
this.StartButton.Unfocus();
}
@@ -1409,6 +1473,11 @@ private void StartCommandExecuted(object sender, ExecutedRoutedEventArgs e)
/// The event data.
private void PauseCommandExecuted(object sender, ExecutedRoutedEventArgs e)
{
+ if (this.Options.LockInterface)
+ {
+ return;
+ }
+
this.Timer.Pause();
this.PauseButton.Unfocus();
}
@@ -1420,6 +1489,11 @@ private void PauseCommandExecuted(object sender, ExecutedRoutedEventArgs e)
/// The event data.
private void ResumeCommandExecuted(object sender, ExecutedRoutedEventArgs e)
{
+ if (this.Options.LockInterface)
+ {
+ return;
+ }
+
this.Timer.Resume();
this.ResumeButton.Unfocus();
}
@@ -1431,6 +1505,11 @@ private void ResumeCommandExecuted(object sender, ExecutedRoutedEventArgs e)
/// The event data.
private void PauseResumeCommandExecuted(object sender, ExecutedRoutedEventArgs e)
{
+ if (this.Options.LockInterface)
+ {
+ return;
+ }
+
if (this.Timer.State == TimerState.Running)
{
this.Timer.Pause();
@@ -1450,6 +1529,11 @@ private void PauseResumeCommandExecuted(object sender, ExecutedRoutedEventArgs e
/// The event data.
private void StopCommandExecuted(object sender, ExecutedRoutedEventArgs e)
{
+ if (this.Options.LockInterface)
+ {
+ return;
+ }
+
this.Timer = new Timer(this.Options);
TimerManager.Instance.Add(this.Timer);
@@ -1464,6 +1548,11 @@ private void StopCommandExecuted(object sender, ExecutedRoutedEventArgs e)
/// The event data.
private void ResetCommandExecuted(object sender, ExecutedRoutedEventArgs e)
{
+ if (this.Options.LockInterface)
+ {
+ return;
+ }
+
this.Timer.Stop();
this.SwitchToInputMode();
this.ResetButton.Unfocus();
@@ -1476,6 +1565,11 @@ private void ResetCommandExecuted(object sender, ExecutedRoutedEventArgs e)
/// The event data.
private void CloseCommandExecuted(object sender, ExecutedRoutedEventArgs e)
{
+ if (this.Options.LockInterface && this.Timer.State != TimerState.Stopped && this.Timer.State != TimerState.Expired)
+ {
+ return;
+ }
+
this.Close();
this.CloseButton.Unfocus();
}
@@ -1598,7 +1692,11 @@ private void TitleTextBoxKeyDown(object sender, KeyEventArgs e)
/// The event data.
private void TitleTextBoxPreviewMouseDown(object sender, MouseButtonEventArgs e)
{
- if (this.Mode != TimerWindowMode.Input && (this.Timer.State == TimerState.Stopped || this.Timer.State == TimerState.Expired))
+ if (this.Options.LockInterface && this.Mode != TimerWindowMode.Input)
+ {
+ e.Handled = true;
+ }
+ else if (this.Mode != TimerWindowMode.Input && (this.Timer.State == TimerState.Stopped || this.Timer.State == TimerState.Expired))
{
this.SwitchToInputMode(this.TitleTextBox /* textBoxToFocus */);
e.Handled = true;
@@ -1618,7 +1716,11 @@ private void TitleTextBoxPreviewMouseDown(object sender, MouseButtonEventArgs e)
/// The event data.
private void TitleTextBoxPreviewGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
- if (this.Mode != TimerWindowMode.Input && (this.Timer.State == TimerState.Stopped || this.Timer.State == TimerState.Expired))
+ if (this.Options.LockInterface && this.Mode != TimerWindowMode.Input)
+ {
+ e.Handled = true;
+ }
+ else if (this.Mode != TimerWindowMode.Input && (this.Timer.State == TimerState.Stopped || this.Timer.State == TimerState.Expired))
{
this.SwitchToInputMode(this.TitleTextBox /* textBoxToFocus */);
e.Handled = true;
@@ -1636,7 +1738,11 @@ private void TitleTextBoxPreviewGotKeyboardFocus(object sender, KeyboardFocusCha
/// The event data.
private void TimerTextBoxPreviewMouseDown(object sender, MouseButtonEventArgs e)
{
- if (this.Mode != TimerWindowMode.Input)
+ if (this.Options.LockInterface && this.Mode != TimerWindowMode.Input)
+ {
+ e.Handled = true;
+ }
+ else if (this.Mode != TimerWindowMode.Input)
{
this.SwitchToInputMode();
e.Handled = true;
@@ -1656,7 +1762,11 @@ private void TimerTextBoxPreviewMouseDown(object sender, MouseButtonEventArgs e)
/// The event data.
private void TimerTextBoxPreviewGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
- if (this.Mode != TimerWindowMode.Input)
+ if (this.Options.LockInterface && this.Mode != TimerWindowMode.Input)
+ {
+ e.Handled = true;
+ }
+ else if (this.Mode != TimerWindowMode.Input)
{
this.SwitchToInputMode();
e.Handled = true;
@@ -1678,16 +1788,21 @@ private void WindowLoaded(object sender, RoutedEventArgs e)
if (this.timerStartToStartOnLoad != null)
{
this.Show(this.timerStartToStartOnLoad);
- this.timerStartToStartOnLoad = null;
- this.timerToResumeOnLoad = null;
+ }
+ else if (this.Options.LockInterface)
+ {
+ // If the interface is locked but no timer input was specified, there is nothing the user can do or
+ // should be able to do other than close the window, so pretend that the timer expired immediately
+ this.Show(TimerStart.Zero, false /* remember */);
}
else if (this.timerToResumeOnLoad != null)
{
this.Show(this.timerToResumeOnLoad);
- this.timerStartToStartOnLoad = null;
- this.timerToResumeOnLoad = null;
}
+ this.timerStartToStartOnLoad = null;
+ this.timerToResumeOnLoad = null;
+
// Minimize to notification area if required
if (this.WindowState == WindowState.Minimized && Settings.Default.ShowInNotificationArea)
{
@@ -1750,6 +1865,13 @@ private void WindowStateChanged(object sender, EventArgs e)
/// The event data.
private void WindowClosing(object sender, CancelEventArgs e)
{
+ // Do not allow the window to be closed if the interface is locked and the timer is running
+ if (this.Options.LockInterface && this.Timer.State != TimerState.Stopped && this.Timer.State != TimerState.Expired)
+ {
+ e.Cancel = true;
+ return;
+ }
+
// Prompt for confirmation if required
if (this.Options.PromptOnExit && this.Timer.State != TimerState.Stopped && this.Timer.State != TimerState.Expired)
{
diff --git a/LICENSE.md b/LICENSE.md
index ee59062..eadb739 100644
--- a/LICENSE.md
+++ b/LICENSE.md
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2017 Chris Dziemborowicz
+Copyright (c) 2018 Chris Dziemborowicz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal