diff --git a/.gitignore b/.gitignore index abd2844..3e352b1 100644 --- a/.gitignore +++ b/.gitignore @@ -153,3 +153,5 @@ $RECYCLE.BIN/ # Mac desktop service store files .DS_Store + +.vs/ \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..b2a9186 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "Common"] + path = Common + url = https://github.com/ckaczor/Common.git +[submodule "Common.Wpf"] + path = Common.Wpf + url = https://github.com/ckaczor/Common.Wpf.git diff --git a/App.xaml b/App.xaml index 25f2952..6e76e0f 100644 --- a/App.xaml +++ b/App.xaml @@ -1,6 +1,7 @@  + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + ShutdownMode="OnExplicitShutdown"> diff --git a/Common b/Common new file mode 160000 index 0000000..81ef8f4 --- /dev/null +++ b/Common @@ -0,0 +1 @@ +Subproject commit 81ef8f451c5ceada2ed704ecaaa789e623a7352d diff --git a/Common.Wpf b/Common.Wpf new file mode 160000 index 0000000..8a82786 --- /dev/null +++ b/Common.Wpf @@ -0,0 +1 @@ +Subproject commit 8a82786166d49271b22e48aba0ca34cf8d366872 diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..de8cb70 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Chris Kaczor + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/LightController.cs b/LightController.cs index f3b1567..aadeb91 100644 --- a/LightController.cs +++ b/LightController.cs @@ -1,7 +1,8 @@ -using System; +using Common.Native; +using System; using System.Collections.Generic; +using System.Linq; using System.Text.RegularExpressions; -using Common.Native; using WorkIndicator.Delcom; namespace WorkIndicator @@ -17,27 +18,47 @@ namespace WorkIndicator public static class LightController { + private static WindowPatterns _windowPatterns; private static StoplightIndicator _stoplightIndicator; private static bool _initialized; private static Status _status = Status.Auto; public static void Initialize() { + WindowPatterns.Changed += HandleWindowPatternsChanged; + _stoplightIndicator = new StoplightIndicator(); _stoplightIndicator.SetLight(StoplightIndicator.Light.Green, StoplightIndicator.LightState.On); - InitializeWindowDetection(); + LoadPatterns(); _initialized = true; } + private static void LoadPatterns() + { + _windowPatterns = WindowPatterns.Load(); + + if (_initialized) + TerminateWindowDetection(); + + InitializeWindowDetection(); + + UpdateLights(); + } + + private static void HandleWindowPatternsChanged(object sender, EventArgs e) + { + LoadPatterns(); + } + public static void Dispose() { if (!_initialized) return; TerminateWindowDetection(); - + _stoplightIndicator.SetLights(StoplightIndicator.LightState.Off, StoplightIndicator.LightState.Off, StoplightIndicator.LightState.Off); _stoplightIndicator.Dispose(); @@ -46,7 +67,7 @@ namespace WorkIndicator public static Status Status { - get { return _status; } + get => _status; set { _status = value; @@ -102,10 +123,18 @@ namespace WorkIndicator private static readonly List WindowEventHooks = new List(); private static readonly List WindowHandles = new List(); - private static readonly Regex WindowMatchRegex = new Regex(Properties.Settings.Default.WindowPattern); + private static readonly List WindowMatchRegexList = new List(); + + private static string WildcardToRegexPattern(string value) + { + return "^" + Regex.Escape(value).Replace("\\?", ".").Replace("\\*", ".*") + "$"; + } private static void InitializeWindowDetection() { + foreach (var windowPattern in _windowPatterns.Where(w => w.Enabled)) + WindowMatchRegexList.Add(new Regex(WildcardToRegexPattern(windowPattern.Pattern))); + Functions.User32.EnumWindows(EnumWindowProc, IntPtr.Zero); IntPtr hook = WinEvent.SetWinEventHook(WinEvent.Event.ObjectCreate, WinEvent.Event.ObjectDestroy, IntPtr.Zero, ProcDelegate, 0, 0, WinEvent.SetWinEventHookFlags.OutOfContext | WinEvent.SetWinEventHookFlags.SkipOwnProcess); @@ -120,6 +149,10 @@ namespace WorkIndicator private static void TerminateWindowDetection() { WindowEventHooks.ForEach(h => WinEvent.UnhookWinEvent(h)); + + WindowMatchRegexList.Clear(); + + WindowHandles.Clear(); } private static bool EnumWindowProc(IntPtr hWnd, IntPtr lParam) @@ -140,10 +173,13 @@ namespace WorkIndicator if (WindowHandles.Contains(hwnd)) WindowHandles.Remove(hwnd); - if (WindowMatchRegex.IsMatch(Functions.Window.GetText(hwnd))) + foreach (var regex in WindowMatchRegexList) { - WindowHandles.Add(hwnd); - UpdateLights(); + if (regex.IsMatch(Functions.Window.GetText(hwnd))) + { + WindowHandles.Add(hwnd); + UpdateLights(); + } } break; diff --git a/Options/AboutOptionsPanel.xaml b/Options/AboutOptionsPanel.xaml new file mode 100644 index 0000000..a437ade --- /dev/null +++ b/Options/AboutOptionsPanel.xaml @@ -0,0 +1,24 @@ + + + + + + + diff --git a/Options/AboutOptionsPanel.xaml.cs b/Options/AboutOptionsPanel.xaml.cs new file mode 100644 index 0000000..0f08288 --- /dev/null +++ b/Options/AboutOptionsPanel.xaml.cs @@ -0,0 +1,36 @@ +using Common.Update; +using System.Reflection; + +namespace WorkIndicator.Options +{ + public partial class AboutOptionsPanel + { + public AboutOptionsPanel() + { + InitializeComponent(); + } + + public override void LoadPanel(object data) + { + base.LoadPanel(data); + + ApplicationNameLabel.Text = Properties.Resources.ApplicationName; + + var version = UpdateCheck.LocalVersion.ToString(); + VersionLabel.Text = string.Format(Properties.Resources.About_Version, version); + + CompanyLabel.Text = ((AssemblyCompanyAttribute)Assembly.GetEntryAssembly().GetCustomAttributes(typeof(AssemblyCompanyAttribute), false)[0]).Company; + } + + public override bool ValidatePanel() + { + return true; + } + + public override void SavePanel() + { + } + + public override string CategoryName => Properties.Resources.OptionCategory_About; + } +} diff --git a/Options/GeneralOptionsPanel.xaml b/Options/GeneralOptionsPanel.xaml new file mode 100644 index 0000000..199b3af --- /dev/null +++ b/Options/GeneralOptionsPanel.xaml @@ -0,0 +1,22 @@ + + + + + + + + + diff --git a/Options/GeneralOptionsPanel.xaml.cs b/Options/GeneralOptionsPanel.xaml.cs new file mode 100644 index 0000000..dd34f50 --- /dev/null +++ b/Options/GeneralOptionsPanel.xaml.cs @@ -0,0 +1,39 @@ +using Common.Wpf.Extensions; +using System.Windows; + +namespace WorkIndicator.Options +{ + public partial class GeneralOptionsPanel + { + public GeneralOptionsPanel() + { + InitializeComponent(); + } + + public override void LoadPanel(object data) + { + base.LoadPanel(data); + + var settings = Properties.Settings.Default; + + StartWithWindows.IsChecked = settings.StartWithWindows; + } + + public override bool ValidatePanel() + { + return true; + } + + public override void SavePanel() + { + var settings = Properties.Settings.Default; + + if (StartWithWindows.IsChecked.HasValue && settings.StartWithWindows != StartWithWindows.IsChecked.Value) + settings.StartWithWindows = StartWithWindows.IsChecked.Value; + + Application.Current.SetStartWithWindows(settings.StartWithWindows); + } + + public override string CategoryName => Properties.Resources.OptionCategory_General; + } +} diff --git a/Options/WindowPatternWindow.xaml b/Options/WindowPatternWindow.xaml new file mode 100644 index 0000000..8083b16 --- /dev/null +++ b/Options/WindowPatternWindow.xaml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + +