11using Dragablz ;
2- using MahApps . Metro . Controls . Dialogs ;
32using NETworkManager . Controls ;
43using NETworkManager . Localization . Resources ;
54using NETworkManager . Models ;
@@ -22,8 +21,7 @@ namespace NETworkManager.ViewModels;
2221public class DNSLookupHostViewModel : ViewModelBase , IProfileManager
2322{
2423 #region Variables
25-
26- private readonly IDialogCoordinator _dialogCoordinator ;
24+
2725 private readonly DispatcherTimer _searchDispatcherTimer = new ( ) ;
2826
2927 public IInterTabClient InterTabClient { get ; }
@@ -129,6 +127,70 @@ public bool IsSearching
129127 OnPropertyChanged ( ) ;
130128 }
131129 }
130+
131+ private bool _profileFilterIsOpen ;
132+
133+ public bool ProfileFilterIsOpen
134+ {
135+ get => _profileFilterIsOpen ;
136+ set
137+ {
138+ if ( value == _profileFilterIsOpen )
139+ return ;
140+
141+ _profileFilterIsOpen = value ;
142+ OnPropertyChanged ( ) ;
143+ }
144+ }
145+
146+ public ICollectionView ProfileFilterTagsView { get ; }
147+
148+ private ObservableCollection < ProfileFilterTagsInfo > ProfileFilterTags { get ; } = [ ] ;
149+
150+ private bool _profileFilterTagsMatchAny = GlobalStaticConfiguration . Profile_TagsMatchAny ;
151+
152+ public bool ProfileFilterTagsMatchAny
153+ {
154+ get => _profileFilterTagsMatchAny ;
155+ set
156+ {
157+ if ( value == _profileFilterTagsMatchAny )
158+ return ;
159+
160+ _profileFilterTagsMatchAny = value ;
161+ OnPropertyChanged ( ) ;
162+ }
163+ }
164+
165+ private bool _profileFilterTagsMatchAll ;
166+
167+ public bool ProfileFilterTagsMatchAll
168+ {
169+ get => _profileFilterTagsMatchAll ;
170+ set
171+ {
172+ if ( value == _profileFilterTagsMatchAll )
173+ return ;
174+
175+ _profileFilterTagsMatchAll = value ;
176+ OnPropertyChanged ( ) ;
177+ }
178+ }
179+
180+ private bool _isProfileFilterSet ;
181+
182+ public bool IsProfileFilterSet
183+ {
184+ get => _isProfileFilterSet ;
185+ set
186+ {
187+ if ( value == _isProfileFilterSet )
188+ return ;
189+
190+ _isProfileFilterSet = value ;
191+ OnPropertyChanged ( ) ;
192+ }
193+ }
132194
133195 private bool _canProfileWidthChange = true ;
134196 private double _tempProfileWidth ;
@@ -184,14 +246,13 @@ public GridLength ProfileWidth
184246
185247 #region Constructor, load settings
186248
187- public DNSLookupHostViewModel ( IDialogCoordinator instance )
249+ public DNSLookupHostViewModel ( )
188250 {
189251 _isLoading = true ;
190252
191- _dialogCoordinator = instance ;
192253
193254 InterTabClient = new DragablzInterTabClient ( ApplicationName . DNSLookup ) ;
194- InterTabPartition = ApplicationName . DNSLookup . ToString ( ) ;
255+ InterTabPartition = nameof ( ApplicationName . DNSLookup ) ;
195256
196257 var tabId = Guid . NewGuid ( ) ;
197258
@@ -201,7 +262,12 @@ public DNSLookupHostViewModel(IDialogCoordinator instance)
201262 ] ;
202263
203264 // Profiles
204- SetProfilesView ( ) ;
265+ CreateTags ( ) ;
266+
267+ ProfileFilterTagsView = CollectionViewSource . GetDefaultView ( ProfileFilterTags ) ;
268+ ProfileFilterTagsView . SortDescriptions . Add ( new SortDescription ( nameof ( ProfileFilterTagsInfo . Name ) , ListSortDirection . Ascending ) ) ;
269+
270+ SetProfilesView ( new ProfileFilterInfo ( ) ) ;
205271
206272 ProfileManager . OnProfilesUpdated += ProfileManager_OnProfilesUpdated ;
207273
@@ -298,6 +364,36 @@ private void ClearSearchAction()
298364 Search = string . Empty ;
299365 }
300366
367+ public ICommand OpenProfileFilterCommand => new RelayCommand ( _ => OpenProfileFilterAction ( ) ) ;
368+
369+ private void OpenProfileFilterAction ( )
370+ {
371+ ProfileFilterIsOpen = true ;
372+ }
373+
374+ public ICommand ApplyProfileFilterCommand => new RelayCommand ( _ => ApplyProfileFilterAction ( ) ) ;
375+
376+ private void ApplyProfileFilterAction ( )
377+ {
378+ RefreshProfiles ( ) ;
379+
380+ IsProfileFilterSet = true ;
381+ ProfileFilterIsOpen = false ;
382+ }
383+
384+ public ICommand ClearProfileFilterCommand => new RelayCommand ( _ => ClearProfileFilterAction ( ) ) ;
385+
386+ private void ClearProfileFilterAction ( )
387+ {
388+ foreach ( var tag in ProfileFilterTags )
389+ tag . IsSelected = false ;
390+
391+ RefreshProfiles ( ) ;
392+
393+ IsProfileFilterSet = false ;
394+ ProfileFilterIsOpen = false ;
395+ }
396+
301397 public ItemActionCallback CloseItemCommand => CloseItemAction ;
302398
303399 private static void CloseItemAction ( ItemActionCallbackArgs < TabablzControl > args )
@@ -360,38 +456,42 @@ public void OnViewHide()
360456 _isViewActive = false ;
361457 }
362458
363- private void SetProfilesView ( ProfileInfo profile = null )
459+ private void CreateTags ( )
364460 {
365- Profiles = new CollectionViewSource
366- {
367- Source = ProfileManager . Groups . SelectMany ( x => x . Profiles ) . Where ( x => x . DNSLookup_Enabled )
368- . OrderBy ( x => x . Group ) . ThenBy ( x => x . Name )
369- } . View ;
461+ var tags = ProfileManager . Groups . SelectMany ( x => x . Profiles ) . Where ( x => x . DNSLookup_Enabled ) . SelectMany ( x => x . TagsCollection ) . Distinct ( ) . ToList ( ) ;
370462
371- Profiles . GroupDescriptions . Add ( new PropertyGroupDescription ( nameof ( ProfileInfo . Group ) ) ) ;
463+ var tagSet = new HashSet < string > ( tags ) ;
372464
373- Profiles . Filter = o =>
465+ for ( var i = ProfileFilterTags . Count - 1 ; i >= 0 ; i -- )
374466 {
375- if ( string . IsNullOrEmpty ( Search ) )
376- return true ;
377-
378- if ( o is not ProfileInfo info )
379- return false ;
380-
381-
467+ if ( ! tagSet . Contains ( ProfileFilterTags [ i ] . Name ) )
468+ ProfileFilterTags . RemoveAt ( i ) ;
469+ }
382470
383- var search = Search . Trim ( ) ;
471+ var existingTagNames = new HashSet < string > ( ProfileFilterTags . Select ( ft => ft . Name ) ) ;
384472
385- // Search by: Tag=xxx (exact match, ignore case)
386- /*
387- if (search.StartsWith(ProfileManager.TagIdentifier, StringComparison.OrdinalIgnoreCase))
388- return !string.IsNullOrEmpty(info.Tags) && info.PingMonitor_Enabled && info.Tags.Replace(" ", "").Split(';').Any(str => search.Substring(ProfileManager.TagIdentifier.Length, search.Length - ProfileManager.TagIdentifier.Length).Equals(str, StringComparison.OrdinalIgnoreCase));
389- */
473+ foreach ( var tag in tags . Where ( tag => ! existingTagNames . Contains ( tag ) ) )
474+ {
475+ ProfileFilterTags . Add ( new ProfileFilterTagsInfo ( false , tag ) ) ;
476+ }
477+ }
478+
479+ private void SetProfilesView ( ProfileFilterInfo filter , ProfileInfo profile = null )
480+ {
481+ Profiles = new CollectionViewSource
482+ {
483+ Source = ProfileManager . Groups . SelectMany ( x => x . Profiles ) . Where ( x => x . DNSLookup_Enabled && (
484+ string . IsNullOrEmpty ( filter . Search ) || x . Name . IndexOf ( filter . Search , StringComparison . Ordinal ) > - 1 || x . DNSLookup_Host . IndexOf ( filter . Search , StringComparison . Ordinal ) > - 1 ) && (
485+ // If no tags are selected, show all profiles
486+ ( ! filter . Tags . Any ( ) ) ||
487+ // Any tag can match
488+ ( filter . TagsFilterMatch == ProfileFilterTagsMatch . Any && filter . Tags . Any ( tag => x . TagsCollection . Contains ( tag ) ) ) ||
489+ // All tags must match
490+ ( filter . TagsFilterMatch == ProfileFilterTagsMatch . All && filter . Tags . All ( tag => x . TagsCollection . Contains ( tag ) ) ) )
491+ ) . OrderBy ( x => x . Group ) . ThenBy ( x => x . Name )
492+ } . View ;
390493
391- // Search by: Name, DNSLookup_Host
392- return info . Name . IndexOf ( search , StringComparison . OrdinalIgnoreCase ) > - 1 ||
393- info . DNSLookup_Host . IndexOf ( search , StringComparison . OrdinalIgnoreCase ) > - 1 ;
394- } ;
494+ Profiles . GroupDescriptions . Add ( new PropertyGroupDescription ( nameof ( ProfileInfo . Group ) ) ) ;
395495
396496 // Set specific profile or first if null
397497 SelectedProfile = null ;
@@ -408,7 +508,12 @@ private void RefreshProfiles()
408508 if ( ! _isViewActive )
409509 return ;
410510
411- SetProfilesView ( SelectedProfile ) ;
511+ SetProfilesView ( new ProfileFilterInfo
512+ {
513+ Search = Search ,
514+ Tags = [ .. ProfileFilterTags . Where ( x => x . IsSelected ) . Select ( x => x . Name ) ] ,
515+ TagsFilterMatch = ProfileFilterTagsMatchAny ? ProfileFilterTagsMatch . Any : ProfileFilterTagsMatch . All
516+ } , SelectedProfile ) ;
412517 }
413518
414519 #endregion
@@ -417,6 +522,8 @@ private void RefreshProfiles()
417522
418523 private void ProfileManager_OnProfilesUpdated ( object sender , EventArgs e )
419524 {
525+ CreateTags ( ) ;
526+
420527 RefreshProfiles ( ) ;
421528 }
422529
0 commit comments