mirror of
https://github.com/ckaczor/WorkIndicator.git
synced 2026-01-14 01:25:45 -05:00
WIP - microphone in use detection
This commit is contained in:
92
AudioWatcher.cs
Normal file
92
AudioWatcher.cs
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
using CSCore.CoreAudioAPI;
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace WorkIndicator
|
||||||
|
{
|
||||||
|
public static class AudioWatcher
|
||||||
|
{
|
||||||
|
public delegate void MicrophoneInUseChangedDelegate(bool microphoneInUse);
|
||||||
|
|
||||||
|
private static ManualResetEvent _manualResetEvent;
|
||||||
|
|
||||||
|
private static int _activeSessionCount;
|
||||||
|
|
||||||
|
public static event MicrophoneInUseChangedDelegate MicrophoneInUseChanged;
|
||||||
|
|
||||||
|
public static void Start()
|
||||||
|
{
|
||||||
|
_manualResetEvent = new ManualResetEvent(false);
|
||||||
|
|
||||||
|
var thread = new Thread(delegate ()
|
||||||
|
{
|
||||||
|
var deviceEnumerator = new MMDeviceEnumerator();
|
||||||
|
|
||||||
|
foreach (var device in deviceEnumerator.EnumAudioEndpoints(DataFlow.Capture, DeviceState.Active))
|
||||||
|
{
|
||||||
|
var sessionManager = AudioSessionManager2.FromMMDevice(device);
|
||||||
|
|
||||||
|
var sessionEnumerator = sessionManager.GetSessionEnumerator();
|
||||||
|
|
||||||
|
sessionManager.SessionCreated += HandleAudioSessionCreated;
|
||||||
|
|
||||||
|
foreach (var audioSessionControl in sessionEnumerator)
|
||||||
|
{
|
||||||
|
HandleAudioStateChanged(audioSessionControl, new AudioSessionStateChangedEventArgs(audioSessionControl.SessionState));
|
||||||
|
|
||||||
|
audioSessionControl.StateChanged += HandleAudioStateChanged;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_manualResetEvent.WaitOne();
|
||||||
|
});
|
||||||
|
|
||||||
|
thread.SetApartmentState(ApartmentState.MTA);
|
||||||
|
thread.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Stop()
|
||||||
|
{
|
||||||
|
_manualResetEvent?.Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void HandleAudioSessionCreated(object sender, SessionCreatedEventArgs e)
|
||||||
|
{
|
||||||
|
HandleAudioStateChanged(null, new AudioSessionStateChangedEventArgs(e.NewSession.SessionState));
|
||||||
|
|
||||||
|
e.NewSession.StateChanged += HandleAudioStateChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void HandleAudioStateChanged(object sender, AudioSessionStateChangedEventArgs e)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"{e.NewState}");
|
||||||
|
|
||||||
|
switch (e.NewState)
|
||||||
|
{
|
||||||
|
case AudioSessionState.AudioSessionStateActive:
|
||||||
|
_activeSessionCount++;
|
||||||
|
break;
|
||||||
|
case AudioSessionState.AudioSessionStateInactive:
|
||||||
|
_activeSessionCount--;
|
||||||
|
break;
|
||||||
|
case AudioSessionState.AudioSessionStateExpired:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_activeSessionCount < 0)
|
||||||
|
_activeSessionCount = 0;
|
||||||
|
|
||||||
|
Debug.WriteLine($"{_activeSessionCount}");
|
||||||
|
|
||||||
|
MicrophoneInUseChanged?.Invoke(MicrophoneInUse());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool MicrophoneInUse()
|
||||||
|
{
|
||||||
|
return _activeSessionCount > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -154,7 +154,7 @@ namespace WorkIndicator.Delcom
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!_deviceHandle.IsClosed)
|
if (_deviceHandle != null && !_deviceHandle.IsClosed)
|
||||||
_deviceHandle.Close();
|
_deviceHandle.Close();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using System;
|
using Common.Extensions;
|
||||||
|
using System;
|
||||||
using Common.Extensions;
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace WorkIndicator.Delcom
|
namespace WorkIndicator.Delcom
|
||||||
{
|
{
|
||||||
@@ -80,6 +80,8 @@ namespace WorkIndicator.Delcom
|
|||||||
_yellow = yellow;
|
_yellow = yellow;
|
||||||
_green = green;
|
_green = green;
|
||||||
|
|
||||||
|
Debug.WriteLine($"Red: {_red}, Yellow: {_yellow}, Green: {_green}");
|
||||||
|
|
||||||
port1 = port1.SetBitValue((int) Light.Green, green == LightState.Off);
|
port1 = port1.SetBitValue((int) Light.Green, green == LightState.Off);
|
||||||
port1 = port1.SetBitValue((int) Light.Yellow, yellow == LightState.Off);
|
port1 = port1.SetBitValue((int) Light.Yellow, yellow == LightState.Off);
|
||||||
port1 = port1.SetBitValue((int) Light.Red, red == LightState.Off);
|
port1 = port1.SetBitValue((int) Light.Red, red == LightState.Off);
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
using Common.Native;
|
using System;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using WorkIndicator.Delcom;
|
using WorkIndicator.Delcom;
|
||||||
|
|
||||||
namespace WorkIndicator
|
namespace WorkIndicator
|
||||||
@@ -18,46 +14,32 @@ namespace WorkIndicator
|
|||||||
|
|
||||||
public static class LightController
|
public static class LightController
|
||||||
{
|
{
|
||||||
private static WindowPatterns _windowPatterns;
|
|
||||||
private static StoplightIndicator _stoplightIndicator;
|
private static StoplightIndicator _stoplightIndicator;
|
||||||
private static bool _initialized;
|
private static bool _initialized;
|
||||||
private static Status _status = Status.Auto;
|
private static Status _status = Status.Auto;
|
||||||
|
|
||||||
public static void Initialize()
|
public static void Initialize()
|
||||||
{
|
{
|
||||||
WindowPatterns.Changed += HandleWindowPatternsChanged;
|
|
||||||
|
|
||||||
_stoplightIndicator = new StoplightIndicator();
|
_stoplightIndicator = new StoplightIndicator();
|
||||||
_stoplightIndicator.SetLight(StoplightIndicator.Light.Green, StoplightIndicator.LightState.On);
|
_stoplightIndicator.SetLight(StoplightIndicator.Light.Yellow, StoplightIndicator.LightState.On);
|
||||||
|
|
||||||
LoadPatterns();
|
AudioWatcher.MicrophoneInUseChanged += AudioWatcher_MicrophoneInUseChanged;
|
||||||
|
AudioWatcher.Start();
|
||||||
|
|
||||||
_initialized = true;
|
_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void LoadPatterns()
|
private static void AudioWatcher_MicrophoneInUseChanged(bool microphoneInUse)
|
||||||
{
|
{
|
||||||
_windowPatterns = WindowPatterns.Load();
|
|
||||||
|
|
||||||
if (_initialized)
|
|
||||||
TerminateWindowDetection();
|
|
||||||
|
|
||||||
InitializeWindowDetection();
|
|
||||||
|
|
||||||
UpdateLights();
|
UpdateLights();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void HandleWindowPatternsChanged(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
LoadPatterns();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Dispose()
|
public static void Dispose()
|
||||||
{
|
{
|
||||||
if (!_initialized)
|
if (!_initialized)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
TerminateWindowDetection();
|
AudioWatcher.Stop();
|
||||||
|
|
||||||
_stoplightIndicator.SetLights(StoplightIndicator.LightState.Off, StoplightIndicator.LightState.Off, StoplightIndicator.LightState.Off);
|
_stoplightIndicator.SetLights(StoplightIndicator.LightState.Off, StoplightIndicator.LightState.Off, StoplightIndicator.LightState.Off);
|
||||||
_stoplightIndicator.Dispose();
|
_stoplightIndicator.Dispose();
|
||||||
@@ -83,13 +65,13 @@ namespace WorkIndicator
|
|||||||
|
|
||||||
if (_status == Status.Auto)
|
if (_status == Status.Auto)
|
||||||
{
|
{
|
||||||
if (WindowHandles.Count > 0)
|
if (AudioWatcher.MicrophoneInUse())
|
||||||
{
|
{
|
||||||
yellow = StoplightIndicator.LightState.On;
|
red = StoplightIndicator.LightState.On;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
green = StoplightIndicator.LightState.On;
|
yellow = StoplightIndicator.LightState.On;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -115,87 +97,5 @@ namespace WorkIndicator
|
|||||||
|
|
||||||
_stoplightIndicator.SetLights(red, yellow, green);
|
_stoplightIndicator.SetLights(red, yellow, green);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Window detection
|
|
||||||
|
|
||||||
private static readonly WinEvent.WinEventDelegate ProcDelegate = WinEventProc;
|
|
||||||
|
|
||||||
private static readonly List<IntPtr> WindowEventHooks = new List<IntPtr>();
|
|
||||||
private static readonly List<IntPtr> WindowHandles = new List<IntPtr>();
|
|
||||||
|
|
||||||
private static readonly List<Regex> WindowMatchRegexList = new List<Regex>();
|
|
||||||
|
|
||||||
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);
|
|
||||||
if (hook != IntPtr.Zero)
|
|
||||||
WindowEventHooks.Add(hook);
|
|
||||||
|
|
||||||
hook = WinEvent.SetWinEventHook(WinEvent.Event.ObjectNameChange, WinEvent.Event.ObjectNameChange, IntPtr.Zero, ProcDelegate, 0, 0, WinEvent.SetWinEventHookFlags.OutOfContext | WinEvent.SetWinEventHookFlags.SkipOwnProcess);
|
|
||||||
if (hook != IntPtr.Zero)
|
|
||||||
WindowEventHooks.Add(hook);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void TerminateWindowDetection()
|
|
||||||
{
|
|
||||||
WindowEventHooks.ForEach(h => WinEvent.UnhookWinEvent(h));
|
|
||||||
|
|
||||||
WindowMatchRegexList.Clear();
|
|
||||||
|
|
||||||
WindowHandles.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool EnumWindowProc(IntPtr hWnd, IntPtr lParam)
|
|
||||||
{
|
|
||||||
WinEventProc(IntPtr.Zero, WinEvent.Event.ObjectCreate, hWnd, WinEvent.ObjectIdentifier.Window, 0, 0, 0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void WinEventProc(IntPtr hWinEventHook, WinEvent.Event eventType, IntPtr hwnd, WinEvent.ObjectIdentifier idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
|
|
||||||
{
|
|
||||||
if (idObject == WinEvent.ObjectIdentifier.Window && idChild == (int) WinEvent.ChildIdentifier.Self)
|
|
||||||
{
|
|
||||||
switch (eventType)
|
|
||||||
{
|
|
||||||
case WinEvent.Event.ObjectCreate:
|
|
||||||
case WinEvent.Event.ObjectNameChange:
|
|
||||||
|
|
||||||
if (WindowHandles.Contains(hwnd))
|
|
||||||
WindowHandles.Remove(hwnd);
|
|
||||||
|
|
||||||
foreach (var regex in WindowMatchRegexList)
|
|
||||||
{
|
|
||||||
if (regex.IsMatch(Functions.Window.GetText(hwnd)))
|
|
||||||
{
|
|
||||||
WindowHandles.Add(hwnd);
|
|
||||||
UpdateLights();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case WinEvent.Event.ObjectDestroy:
|
|
||||||
if (WindowHandles.Contains(hwnd))
|
|
||||||
{
|
|
||||||
WindowHandles.Remove(hwnd);
|
|
||||||
UpdateLights();
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,87 +0,0 @@
|
|||||||
<Window x:Class="WorkIndicator.Options.WindowPatternWindow"
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
xmlns:properties="clr-namespace:WorkIndicator.Properties"
|
|
||||||
mc:Ignorable="d"
|
|
||||||
WindowStartupLocation="CenterOwner"
|
|
||||||
Title="WindowPatternWindow"
|
|
||||||
Height="160"
|
|
||||||
Width="350"
|
|
||||||
FocusManager.FocusedElement="{Binding ElementName=NameTextBox}">
|
|
||||||
<Grid>
|
|
||||||
<Grid>
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition Height="*" />
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="Auto" />
|
|
||||||
<ColumnDefinition Width="*" />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
<Label Content="{x:Static properties:Resources.WindowPatternNameLabel}"
|
|
||||||
VerticalContentAlignment="Center"
|
|
||||||
Target="{Binding ElementName=NameTextBox}"
|
|
||||||
Grid.Row="0"
|
|
||||||
Grid.Column="0"
|
|
||||||
Margin="6"
|
|
||||||
Padding="0" />
|
|
||||||
<TextBox Name="NameTextBox"
|
|
||||||
Grid.Column="1"
|
|
||||||
Text="{Binding Path=Name, UpdateSourceTrigger=Explicit, ValidatesOnExceptions=true}"
|
|
||||||
Grid.Row="0"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Margin="6,0,6,0" />
|
|
||||||
|
|
||||||
<Label Content="{x:Static properties:Resources.WindowPatternPatternLabel}"
|
|
||||||
VerticalContentAlignment="Center"
|
|
||||||
Target="{Binding ElementName=PatternTextBox}"
|
|
||||||
Grid.Row="1"
|
|
||||||
Grid.Column="0"
|
|
||||||
Margin="6"
|
|
||||||
Padding="0" />
|
|
||||||
<TextBox Name="PatternTextBox"
|
|
||||||
Grid.Column="1"
|
|
||||||
Text="{Binding Path=Pattern, UpdateSourceTrigger=Explicit, ValidatesOnExceptions=true}"
|
|
||||||
Grid.Row="1"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Margin="6,0,6,0" />
|
|
||||||
|
|
||||||
|
|
||||||
<Label Content="{x:Static properties:Resources.WindowPatternEnabledLabel}"
|
|
||||||
VerticalContentAlignment="Center"
|
|
||||||
Target="{Binding ElementName=NameTextBox}"
|
|
||||||
Grid.Row="2"
|
|
||||||
Grid.Column="0"
|
|
||||||
Margin="6"
|
|
||||||
Padding="0" />
|
|
||||||
<CheckBox Grid.Column="1"
|
|
||||||
IsChecked="{Binding Path=Enabled, UpdateSourceTrigger=Explicit, ValidatesOnExceptions=true}"
|
|
||||||
Grid.Row="2"
|
|
||||||
Margin="5,0,5,0"
|
|
||||||
VerticalAlignment="Center" />
|
|
||||||
<StackPanel Grid.Column="0"
|
|
||||||
Grid.ColumnSpan="2"
|
|
||||||
Grid.Row="3"
|
|
||||||
Orientation="Horizontal"
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
Margin="6">
|
|
||||||
<Button Content="{x:Static properties:Resources.OkayButton}"
|
|
||||||
Height="23"
|
|
||||||
VerticalAlignment="Bottom"
|
|
||||||
Width="75"
|
|
||||||
IsDefault="True"
|
|
||||||
Click="HandleOkayButtonClick"
|
|
||||||
Margin="0,0,8,0" />
|
|
||||||
<Button Content="{x:Static properties:Resources.CancelButton}"
|
|
||||||
Height="23"
|
|
||||||
VerticalAlignment="Bottom"
|
|
||||||
Width="75"
|
|
||||||
IsCancel="True" />
|
|
||||||
</StackPanel>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</Window>
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
using Common.Wpf.Extensions;
|
|
||||||
using System.Drawing;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Windows;
|
|
||||||
using System.Windows.Data;
|
|
||||||
|
|
||||||
namespace WorkIndicator.Options
|
|
||||||
{
|
|
||||||
public partial class WindowPatternWindow
|
|
||||||
{
|
|
||||||
public WindowPatternWindow()
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
|
|
||||||
Icon = ((Icon)Properties.Resources.ResourceManager.GetObject("ApplicationIcon")).ToImageSource();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool? Display(WindowPattern windowPattern, Window owner)
|
|
||||||
{
|
|
||||||
// Set the data context
|
|
||||||
DataContext = windowPattern;
|
|
||||||
|
|
||||||
// Set the title based on the state of the item
|
|
||||||
Title = string.IsNullOrWhiteSpace(windowPattern.Name) ? Properties.Resources.WindowPatternWindowAdd : Properties.Resources.WindowPatternWindowEdit;
|
|
||||||
|
|
||||||
// Set the window owner
|
|
||||||
Owner = owner;
|
|
||||||
|
|
||||||
// Show the dialog and result the result
|
|
||||||
return ShowDialog();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void HandleOkayButtonClick(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
// Get a list of all framework elements and explicit binding expressions
|
|
||||||
var bindingExpressions = this.GetBindingExpressions(new[] { UpdateSourceTrigger.Explicit });
|
|
||||||
|
|
||||||
// Loop over each binding expression and clear any existing error
|
|
||||||
this.ClearAllValidationErrors(bindingExpressions);
|
|
||||||
|
|
||||||
// Force all explicit bindings to update the source
|
|
||||||
this.UpdateAllSources(bindingExpressions);
|
|
||||||
|
|
||||||
// See if there are any errors
|
|
||||||
var hasError = bindingExpressions.Any(b => b.BindingExpression.HasError);
|
|
||||||
|
|
||||||
// If there was an error then set focus to the bad controls
|
|
||||||
if (hasError)
|
|
||||||
{
|
|
||||||
// Get the first framework element with an error
|
|
||||||
var firstErrorElement = bindingExpressions.First(b => b.BindingExpression.HasError).FrameworkElement;
|
|
||||||
|
|
||||||
// Set focus
|
|
||||||
firstErrorElement.Focus();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dialog is good
|
|
||||||
DialogResult = true;
|
|
||||||
|
|
||||||
// Close the dialog
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
<windows:CategoryPanel x:Class="WorkIndicator.Options.WindowPatternsOptionsPanel"
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
xmlns:windows="clr-namespace:Common.Wpf.Windows;assembly=Common.Wpf"
|
|
||||||
xmlns:linkControl="clr-namespace:Common.Wpf.LinkControl;assembly=Common.Wpf"
|
|
||||||
xmlns:properties="clr-namespace:WorkIndicator.Properties"
|
|
||||||
mc:Ignorable="d"
|
|
||||||
d:DesignHeight="300"
|
|
||||||
d:DesignWidth="300">
|
|
||||||
<Grid>
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="*" />
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="*" />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
<DataGrid x:Name="WindowPatternsGrid"
|
|
||||||
SelectionMode="Extended"
|
|
||||||
Grid.Column="0"
|
|
||||||
Grid.Row="0"
|
|
||||||
AutoGenerateColumns="False"
|
|
||||||
GridLinesVisibility="None"
|
|
||||||
CanUserResizeRows="False"
|
|
||||||
IsReadOnly="True"
|
|
||||||
HeadersVisibility="Column"
|
|
||||||
SelectionUnit="FullRow"
|
|
||||||
SelectionChanged="HandleWindowPatternsSelectionChanged"
|
|
||||||
Background="{x:Null}"
|
|
||||||
MouseDoubleClick="HandleWindowPatternsDoubleClick">
|
|
||||||
<DataGrid.Columns>
|
|
||||||
<DataGridCheckBoxColumn Binding="{Binding Enabled}"
|
|
||||||
Header="{x:Static properties:Resources.ColumnHeader_Enabled}" />
|
|
||||||
<DataGridTextColumn Binding="{Binding Name}"
|
|
||||||
Header="{x:Static properties:Resources.ColumnHeader_Name}"
|
|
||||||
SortDirection="Ascending"
|
|
||||||
Width="*" />
|
|
||||||
<DataGridTextColumn Binding="{Binding Pattern}"
|
|
||||||
Header="{x:Static properties:Resources.ColumnHeader_Pattern}"
|
|
||||||
Width="2*" />
|
|
||||||
</DataGrid.Columns>
|
|
||||||
<DataGrid.CellStyle>
|
|
||||||
<Style TargetType="DataGridCell">
|
|
||||||
<Setter Property="BorderThickness"
|
|
||||||
Value="0"></Setter>
|
|
||||||
<Setter Property="FocusVisualStyle"
|
|
||||||
Value="{x:Null}" />
|
|
||||||
</Style>
|
|
||||||
</DataGrid.CellStyle>
|
|
||||||
</DataGrid>
|
|
||||||
<Border Grid.Column="0"
|
|
||||||
Grid.Row="1"
|
|
||||||
BorderThickness="1,0,1,1"
|
|
||||||
BorderBrush="{DynamicResource {x:Static SystemColors.ActiveBorderBrushKey}}">
|
|
||||||
<StackPanel Orientation="Horizontal"
|
|
||||||
Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}">
|
|
||||||
<linkControl:LinkControl Margin="2"
|
|
||||||
Click="HandleAddWindowPatternButtonClick"
|
|
||||||
Text="{x:Static properties:Resources.AddWindowPattern}"
|
|
||||||
ToolTip="{x:Static properties:Resources.AddWindowPattern_ToolTip}">
|
|
||||||
</linkControl:LinkControl>
|
|
||||||
<linkControl:LinkControl Name="EditWindowPatternButton"
|
|
||||||
Margin="2"
|
|
||||||
Click="HandleEditWindowPatternButtonClick"
|
|
||||||
Text="{x:Static properties:Resources.EditWindowPattern}"
|
|
||||||
ToolTip="{x:Static properties:Resources.EditWindowPattern_ToolTip}">
|
|
||||||
</linkControl:LinkControl>
|
|
||||||
<linkControl:LinkControl Name="DeleteWindowPatternButton"
|
|
||||||
Margin="2"
|
|
||||||
Click="HandleDeleteWindowPatternButtonClick"
|
|
||||||
Text="{x:Static properties:Resources.DeleteWindowPattern}"
|
|
||||||
ToolTip="{x:Static properties:Resources.DeleteWindowPattern_ToolTip}">
|
|
||||||
</linkControl:LinkControl>
|
|
||||||
</StackPanel>
|
|
||||||
</Border>
|
|
||||||
</Grid>
|
|
||||||
</windows:CategoryPanel>
|
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
using System.Linq;
|
|
||||||
using System.Windows;
|
|
||||||
using System.Windows.Controls;
|
|
||||||
|
|
||||||
namespace WorkIndicator.Options
|
|
||||||
{
|
|
||||||
public partial class WindowPatternsOptionsPanel
|
|
||||||
{
|
|
||||||
public WindowPatternsOptionsPanel()
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
private WindowPatterns WindowPatterns => Data as WindowPatterns;
|
|
||||||
|
|
||||||
public override void LoadPanel(object data)
|
|
||||||
{
|
|
||||||
base.LoadPanel(data);
|
|
||||||
|
|
||||||
WindowPatternsGrid.ItemsSource = WindowPatterns;
|
|
||||||
|
|
||||||
WindowPatternsGrid.SelectedItem = WindowPatterns.FirstOrDefault();
|
|
||||||
SetButtonStates();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool ValidatePanel()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void SavePanel()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string CategoryName => Properties.Resources.OptionCategory_WindowPatterns;
|
|
||||||
|
|
||||||
private void SetButtonStates()
|
|
||||||
{
|
|
||||||
EditWindowPatternButton.IsEnabled = (WindowPatternsGrid.SelectedItems.Count == 1);
|
|
||||||
DeleteWindowPatternButton.IsEnabled = (WindowPatternsGrid.SelectedItems.Count > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddWindowPattern()
|
|
||||||
{
|
|
||||||
var windowPattern = new WindowPattern();
|
|
||||||
|
|
||||||
var windowPatternWindow = new WindowPatternWindow();
|
|
||||||
|
|
||||||
var result = windowPatternWindow.Display(windowPattern, Window.GetWindow(this));
|
|
||||||
|
|
||||||
if (result.HasValue && result.Value)
|
|
||||||
{
|
|
||||||
WindowPatterns.Add(windowPattern);
|
|
||||||
|
|
||||||
WindowPatternsGrid.SelectedItem = windowPattern;
|
|
||||||
|
|
||||||
SetButtonStates();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void EditSelectedWindowPattern()
|
|
||||||
{
|
|
||||||
if (WindowPatternsGrid.SelectedItem == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var windowPattern = WindowPatternsGrid.SelectedItem as WindowPattern;
|
|
||||||
|
|
||||||
var windowPatternWindow = new WindowPatternWindow();
|
|
||||||
|
|
||||||
windowPatternWindow.Display(windowPattern, Window.GetWindow(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DeleteSelectedWindowPattern()
|
|
||||||
{
|
|
||||||
var windowPattern = WindowPatternsGrid.SelectedItem as WindowPattern;
|
|
||||||
var index = WindowPatternsGrid.SelectedIndex;
|
|
||||||
|
|
||||||
WindowPatterns.Remove(windowPattern);
|
|
||||||
|
|
||||||
if (WindowPatternsGrid.Items.Count == index)
|
|
||||||
WindowPatternsGrid.SelectedIndex = WindowPatternsGrid.Items.Count - 1;
|
|
||||||
else if (WindowPatternsGrid.Items.Count >= index)
|
|
||||||
WindowPatternsGrid.SelectedIndex = index;
|
|
||||||
|
|
||||||
SetButtonStates();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void HandleAddWindowPatternButtonClick(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
AddWindowPattern();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void HandleEditWindowPatternButtonClick(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
EditSelectedWindowPattern();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void HandleDeleteWindowPatternButtonClick(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
DeleteSelectedWindowPattern();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void HandleWindowPatternsSelectionChanged(object sender, SelectionChangedEventArgs e)
|
|
||||||
{
|
|
||||||
SetButtonStates();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void HandleWindowPatternsDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
|
|
||||||
{
|
|
||||||
EditSelectedWindowPattern();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -113,20 +113,17 @@ namespace WorkIndicator
|
|||||||
_initialized = false;
|
_initialized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ShowSettings()
|
private static void ShowSettings()
|
||||||
{
|
{
|
||||||
var panels = new List<CategoryPanel>
|
var panels = new List<CategoryPanel>
|
||||||
{
|
{
|
||||||
new GeneralOptionsPanel(),
|
new GeneralOptionsPanel(),
|
||||||
new WindowPatternsOptionsPanel(),
|
|
||||||
new AboutOptionsPanel()
|
new AboutOptionsPanel()
|
||||||
};
|
};
|
||||||
|
|
||||||
var windowPatterns = WindowPatterns.Load();
|
|
||||||
|
|
||||||
if (_optionsWindow == null)
|
if (_optionsWindow == null)
|
||||||
{
|
{
|
||||||
_optionsWindow = new CategoryWindow(windowPatterns, panels, Resources.ResourceManager, "OptionsWindow");
|
_optionsWindow = new CategoryWindow(null, panels, Resources.ResourceManager, "OptionsWindow");
|
||||||
_optionsWindow.Closed += (o, args) => { _optionsWindow = null; };
|
_optionsWindow.Closed += (o, args) => { _optionsWindow = null; };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,7 +131,6 @@ namespace WorkIndicator
|
|||||||
|
|
||||||
if (dialogResult.HasValue && dialogResult.Value)
|
if (dialogResult.HasValue && dialogResult.Value)
|
||||||
{
|
{
|
||||||
windowPatterns.Save();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
namespace WorkIndicator
|
|
||||||
{
|
|
||||||
public class WindowPattern
|
|
||||||
{
|
|
||||||
public string Name { get; set; } = string.Empty;
|
|
||||||
public string Pattern { get; set; } = string.Empty;
|
|
||||||
public bool Enabled { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
using Newtonsoft.Json;
|
|
||||||
using System;
|
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using WorkIndicator.Properties;
|
|
||||||
|
|
||||||
namespace WorkIndicator
|
|
||||||
{
|
|
||||||
public class WindowPatterns : ObservableCollection<WindowPattern>
|
|
||||||
{
|
|
||||||
public static event EventHandler Changed;
|
|
||||||
|
|
||||||
public static WindowPatterns Load()
|
|
||||||
{
|
|
||||||
var windowPatterns = Load(Settings.Default.WindowPatterns);
|
|
||||||
|
|
||||||
return windowPatterns;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static WindowPatterns Load(string serializedData)
|
|
||||||
{
|
|
||||||
var windowPatterns = JsonConvert.DeserializeObject<WindowPatterns>(serializedData) ?? new WindowPatterns();
|
|
||||||
|
|
||||||
return windowPatterns;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Save()
|
|
||||||
{
|
|
||||||
Settings.Default.WindowPatterns = Serialize();
|
|
||||||
|
|
||||||
Settings.Default.Save();
|
|
||||||
|
|
||||||
Changed?.Invoke(this, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Serialize()
|
|
||||||
{
|
|
||||||
return JsonConvert.SerializeObject(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public WindowPatterns Clone()
|
|
||||||
{
|
|
||||||
var data = Serialize();
|
|
||||||
|
|
||||||
return Load(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -147,6 +147,7 @@
|
|||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
</ApplicationDefinition>
|
</ApplicationDefinition>
|
||||||
|
<Compile Include="AudioWatcher.cs" />
|
||||||
<Compile Include="LightController.cs" />
|
<Compile Include="LightController.cs" />
|
||||||
<Compile Include="Delcom\StoplightIndicator.cs" />
|
<Compile Include="Delcom\StoplightIndicator.cs" />
|
||||||
<Compile Include="App.xaml.cs">
|
<Compile Include="App.xaml.cs">
|
||||||
@@ -165,15 +166,7 @@
|
|||||||
<Compile Include="Options\GeneralOptionsPanel.xaml.cs">
|
<Compile Include="Options\GeneralOptionsPanel.xaml.cs">
|
||||||
<DependentUpon>GeneralOptionsPanel.xaml</DependentUpon>
|
<DependentUpon>GeneralOptionsPanel.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Options\WindowPatternsOptionsPanel.xaml.cs">
|
|
||||||
<DependentUpon>WindowPatternsOptionsPanel.xaml</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Options\WindowPatternWindow.xaml.cs">
|
|
||||||
<DependentUpon>WindowPatternWindow.xaml</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="TrayIcon.cs" />
|
<Compile Include="TrayIcon.cs" />
|
||||||
<Compile Include="WindowPattern.cs" />
|
|
||||||
<Compile Include="WindowPatterns.cs" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Properties\AssemblyInfo.cs">
|
<Compile Include="Properties\AssemblyInfo.cs">
|
||||||
@@ -232,18 +225,13 @@
|
|||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
</Page>
|
</Page>
|
||||||
<Page Include="Options\WindowPatternsOptionsPanel.xaml">
|
|
||||||
<Generator>MSBuild:Compile</Generator>
|
|
||||||
<SubType>Designer</SubType>
|
|
||||||
</Page>
|
|
||||||
<Page Include="Options\WindowPatternWindow.xaml">
|
|
||||||
<Generator>MSBuild:Compile</Generator>
|
|
||||||
<SubType>Designer</SubType>
|
|
||||||
</Page>
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="CSCore">
|
||||||
|
<Version>1.2.1.2</Version>
|
||||||
|
</PackageReference>
|
||||||
<PackageReference Include="Newtonsoft.Json">
|
<PackageReference Include="Newtonsoft.Json">
|
||||||
<Version>10.0.3</Version>
|
<Version>13.0.1</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
2
WorkIndicator.sln.DotSettings
Normal file
2
WorkIndicator.sln.DotSettings
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Delcom/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||||
Reference in New Issue
Block a user