From 6f7a76bdd837b4635c8adc7962e1b798ec621f1d Mon Sep 17 00:00:00 2001 From: Chris Dziemborowicz Date: Wed, 7 Feb 2018 01:25:22 -0800 Subject: [PATCH 1/4] Update year in copyright notices --- Hourglass.Bundle/MIT.rtf | Bin 1266 -> 1266 bytes Hourglass.Test/Properties/AssemblyInfo.cs | 2 +- Hourglass/Properties/AssemblyInfo.cs | 2 +- LICENSE.md | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Hourglass.Bundle/MIT.rtf b/Hourglass.Bundle/MIT.rtf index fca9b2f9288de01e6d304b579c670b93290ee058..055b3b7c762d7ec7350eac80e92f5ad2a9cfee7a 100644 GIT binary patch delta 12 Tcmeyw`H6GFaYl=cC$v}qCJzNJ delta 12 Tcmeyw`H6GFaYplvC$v}qCJF^D diff --git a/Hourglass.Test/Properties/AssemblyInfo.cs b/Hourglass.Test/Properties/AssemblyInfo.cs index 0ba8dc6..fda0681 100644 --- a/Hourglass.Test/Properties/AssemblyInfo.cs +++ b/Hourglass.Test/Properties/AssemblyInfo.cs @@ -12,7 +12,7 @@ [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)] diff --git a/Hourglass/Properties/AssemblyInfo.cs b/Hourglass/Properties/AssemblyInfo.cs index 4ca6c25..f09278a 100644 --- a/Hourglass/Properties/AssemblyInfo.cs +++ b/Hourglass/Properties/AssemblyInfo.cs @@ -14,7 +14,7 @@ [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")] 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 From c35ba2838be893e6ca1ef6e5ce8b5aaf7ca2e564 Mon Sep 17 00:00:00 2001 From: Chris Dziemborowicz Date: Sun, 25 Feb 2018 20:52:55 -0800 Subject: [PATCH 2/4] Resolve #157: Add options to disable certain functions, such as pause, stop, or reset --- Hourglass/CommandLineArguments.cs | 27 ++- Hourglass/Managers/TimerManager.cs | 1 + Hourglass/Managers/TimerOptionsManager.cs | 3 +- Hourglass/Parsing/DateTimeToken.cs | 2 +- Hourglass/Parsing/TimeSpanToken.cs | 2 +- Hourglass/Properties/Resources.Designer.cs | 9 + Hourglass/Properties/Resources.resx | 4 + Hourglass/Resources/Usage.txt | 16 ++ Hourglass/Serialization/TimerOptionsInfo.cs | 6 + Hourglass/Timing/TimerOptions.cs | 41 ++++- Hourglass/Timing/TimerStart.cs | 11 +- Hourglass/Windows/ContextMenu.cs | 7 + Hourglass/Windows/TimerWindow.xaml.cs | 184 ++++++++++++++++---- 13 files changed, 272 insertions(+), 41 deletions(-) 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/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) { From e6eed506022d53c25c7ee3bb30ece60d5e132e57 Mon Sep 17 00:00:00 2001 From: foxx1337 Date: Sun, 15 Jul 2018 08:58:57 +0200 Subject: [PATCH 3/4] Resolve #165: Support different install path from the bundle installer --- Hourglass.Bundle/Bundle.wxs | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/Hourglass.Bundle/Bundle.wxs b/Hourglass.Bundle/Bundle.wxs index 63e716d..cb338eb 100644 --- a/Hourglass.Bundle/Bundle.wxs +++ b/Hourglass.Bundle/Bundle.wxs @@ -1,13 +1,25 @@ + - - - - - - - - - + + + + + + + + + + + + + + + + From b44e06a48894e26e9621e395cef270fc1de6d44a Mon Sep 17 00:00:00 2001 From: Chris Dziemborowicz Date: Sun, 15 Jul 2018 00:00:57 -0700 Subject: [PATCH 4/4] Bump version --- Hourglass.Bundle/Bundle.wxs | 2 +- Hourglass.Setup/Product.wxs | 2 +- Hourglass.Test/Properties/AssemblyInfo.cs | 4 ++-- Hourglass/Properties/App.manifest | 2 +- Hourglass/Properties/AssemblyInfo.cs | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Hourglass.Bundle/Bundle.wxs b/Hourglass.Bundle/Bundle.wxs index cb338eb..1e1fb49 100644 --- a/Hourglass.Bundle/Bundle.wxs +++ b/Hourglass.Bundle/Bundle.wxs @@ -3,7 +3,7 @@ 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 fda0681..87919af 100644 --- a/Hourglass.Test/Properties/AssemblyInfo.cs +++ b/Hourglass.Test/Properties/AssemblyInfo.cs @@ -17,5 +17,5 @@ [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/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 f09278a..d48e180 100644 --- a/Hourglass/Properties/AssemblyInfo.cs +++ b/Hourglass/Properties/AssemblyInfo.cs @@ -17,8 +17,8 @@ [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)]