1717#endregion
1818
1919using System ;
20+ using System . Collections ;
21+ using System . Collections . Generic ;
22+ using System . Collections . Specialized ;
2023using System . IO ;
24+ using System . Linq ;
25+ using System . Reactive . Linq ;
2126using System . Security ;
27+ using System . Text . RegularExpressions ;
28+ using System . Windows ;
29+ using DynamicData ;
30+ using DynamicData . Binding ;
31+ using IX . Observable ;
2232using MahApps . Metro . Controls ;
2333using MahApps . Metro . Controls . Dialogs ;
2434using Microsoft . Win32 ;
@@ -34,32 +44,45 @@ interface ISettings : IReactiveObject
3444 TimeSpan HighlightDuration { get ; set ; }
3545 TimeSpan RefreshInterval { get ; set ; }
3646 bool StartImmediately { get ; set ; }
47+ WindowState WindowState { get ; set ; }
48+ Rect WindowPosition { get ; set ; }
49+ IDictionary < string , int > ColumnWidths { get ; }
3750
38- void Load ( ) ;
3951 void Save ( ) ;
4052 }
4153
4254 class Settings : ReactiveValidationObject < Settings > , ISettings
4355 {
4456 private const string regPath = @"SOFTWARE\PipeExplorer" ;
4557
46- private bool isLoading ;
47-
4858 [ Reactive ] public TimeSpan HighlightDuration { get ; set ; } = TimeSpan . FromSeconds ( 4 ) ; // sec
4959 [ Reactive ] public TimeSpan RefreshInterval { get ; set ; } = TimeSpan . FromSeconds ( 1 ) ; // sec
5060 [ Reactive ] public bool StartImmediately { get ; set ; } = true ;
61+ [ Reactive ] public WindowState WindowState { get ; set ; } = WindowState . Normal ;
62+ [ Reactive ] public Rect WindowPosition { get ; set ; }
63+ public ObservableDictionary < string , int > ColumnWidths { get ; } = new ObservableDictionary < string , int > ( ) ;
64+
65+ IDictionary < string , int > ISettings . ColumnWidths => ColumnWidths ;
5166
5267 public Settings ( )
5368 {
5469 this . ValidationRule ( x => x . HighlightDuration , duration => duration . Ticks >= 0 , Properties . Resources . ErrorMustBeNonNegative ) ;
5570 this . ValidationRule ( x => x . RefreshInterval , interval => interval . Ticks > 0 , Properties . Resources . ErrorMustBePositive ) ;
5671
57- PropertyChanged += delegate { if ( ! isLoading ) Save ( ) ; } ;
72+ Load ( ) ;
73+
74+ ColumnWidths . ToObservableChangeSet < ObservableDictionary < string , int > , KeyValuePair < string , int > > ( )
75+ . CastToObject ( )
76+ . Concat ( this . WhenAnyPropertyChanged ( ) . ToObservableChangeSet ( ) . CastToObject ( ) )
77+ . Throttle ( TimeSpan . FromSeconds ( 1 ) )
78+ . Subscribe ( _ => Save ( ) ) ;
5879 }
5980
60- public void Load ( )
81+ private static readonly Regex windowPosRegex = new Regex ( @"^\s*([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s*$" ) ;
82+ private static readonly Regex colSpecRegex = new Regex ( @"^(\w+)=(\d+)$" ) ;
83+
84+ private void Load ( )
6185 {
62- isLoading = true ;
6386 try
6487 {
6588 var regKey = Registry . CurrentUser . OpenSubKey ( regPath ) ?? Registry . LocalMachine . OpenSubKey ( regPath ) ;
@@ -75,15 +98,44 @@ public void Load()
7598 RefreshInterval = TimeSpan . FromSeconds ( interval ) ;
7699 if ( regKey . GetValue ( "Start immediately" , 1 ) is int n )
77100 StartImmediately = n != 0 ;
101+ if ( regKey . GetValue ( "Window state" ) is string stateStr && Enum . TryParse < WindowState > ( stateStr , true , out var state ) )
102+ WindowState = state ;
103+
104+ if ( WindowState == WindowState . Normal && regKey . GetValue ( "Window position" ) is string posStr )
105+ {
106+ var match = windowPosRegex . Match ( posStr ) ;
107+ if ( match . Success )
108+ {
109+ var leftStr = match . Groups [ 1 ] . Value ;
110+ var topStr = match . Groups [ 2 ] . Value ;
111+ var widthStr = match . Groups [ 3 ] . Value ;
112+ var heightStr = match . Groups [ 4 ] . Value ;
113+ if ( int . TryParse ( leftStr , out var left ) &&
114+ int . TryParse ( topStr , out var top ) &&
115+ int . TryParse ( widthStr , out var width ) && width > 0 &&
116+ int . TryParse ( heightStr , out var height ) && height > 0 )
117+ {
118+ WindowPosition = new Rect ( left , top , width , height ) ;
119+ }
120+ }
121+ }
122+
123+ ColumnWidths . Clear ( ) ;
124+ if ( regKey . GetValue ( "Columns" ) is string colStr && ! string . IsNullOrWhiteSpace ( colStr ) )
125+ {
126+ var colSpecs = colStr . Split ( " \t " . ToCharArray ( ) , StringSplitOptions . RemoveEmptyEntries ) ;
127+ foreach ( var spec in colSpecs )
128+ {
129+ var match = colSpecRegex . Match ( spec ) ;
130+ if ( match . Success && int . TryParse ( match . Groups [ 2 ] . Value , out var width ) )
131+ ColumnWidths . Add ( match . Groups [ 1 ] . Value , width ) ;
132+ }
133+ }
78134 }
79135 }
80136 catch ( SecurityException ) { }
81137 catch ( UnauthorizedAccessException ) { }
82138 catch ( IOException ) { }
83- finally
84- {
85- isLoading = false ;
86- }
87139 }
88140
89141 public async void Save ( )
@@ -101,6 +153,17 @@ public async void Save()
101153 regKey . SetValue ( "Highlight duration" , ( int ) HighlightDuration . TotalSeconds , RegistryValueKind . DWord ) ;
102154 regKey . SetValue ( "Refresh interval" , ( int ) RefreshInterval . TotalSeconds , RegistryValueKind . DWord ) ;
103155 regKey . SetValue ( "Start immediately" , StartImmediately , RegistryValueKind . DWord ) ;
156+ regKey . SetValue ( "Window state" , WindowState . ToString ( ) , RegistryValueKind . String ) ;
157+
158+ if ( WindowState == WindowState . Normal )
159+ regKey . SetValue ( "Window position" , $ "{ ( int ) WindowPosition . Left } { ( int ) WindowPosition . Top } { ( int ) WindowPosition . Width } { ( int ) WindowPosition . Height } ", RegistryValueKind . String ) ;
160+ else
161+ regKey . DeleteValue ( "Window position" , false ) ;
162+
163+ if ( ColumnWidths . Count > 0 )
164+ regKey . SetValue ( "Columns" , string . Join ( " " , ColumnWidths . Select ( kv => $ "{ kv . Key } ={ kv . Value } ") ) , RegistryValueKind . String ) ;
165+ else
166+ regKey . DeleteValue ( "Columns" , false ) ;
104167 }
105168 }
106169 catch ( SecurityException sex )
0 commit comments