@@ -27,6 +27,9 @@ public sealed class SettingsWindow : Form
2727 AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode . Fill , SelectionMode = DataGridViewSelectionMode . FullRowSelect , BackgroundColor = SystemColors . Window ,
2828 } ;
2929 readonly Label envInfo = new ( ) { Dock = DockStyle . Fill , AutoEllipsis = true } ;
30+ readonly TextBox envFilter = new ( ) { Width = 160 } ;
31+ readonly ComboBox envRepo = new ( ) { DropDownStyle = ComboBoxStyle . DropDownList , Width = 200 } ;
32+ readonly CheckBox envNamed = new ( ) { AutoSize = true } ;
3033
3134 readonly TextBox status = new ( ) { Multiline = true , ReadOnly = true , ScrollBars = ScrollBars . Both , WordWrap = false , Dock = DockStyle . Fill , Font = new Font ( FontFamily . GenericMonospace , 9 ) } ;
3235
@@ -278,6 +281,8 @@ TabPage EnvironmentsPage()
278281 envGrid . Columns . Add ( new DataGridViewTextBoxColumn { Name = "repo" , HeaderText = Ui . T ( "Repository (empty = all)" , "Repozytorium (puste = wszystkie)" ) , FillWeight = 25 } ) ;
279282 envGrid . Columns . Add ( new DataGridViewTextBoxColumn { Name = "key" , HeaderText = Ui . T ( "Branch or release stage (Definition/Stage)" , "Gałąź albo etap release'u (Definicja/Etap)" ) , FillWeight = 40 } ) ;
280283 envGrid . Columns . Add ( new DataGridViewTextBoxColumn { Name = "name" , HeaderText = Ui . T ( "Environment, as people say it (empty = skip)" , "Środowisko, jak o nim mówicie (puste = pomiń)" ) , FillWeight = 35 } ) ;
284+ envGrid . Columns . Add ( new DataGridViewTextBoxColumn { Name = "hint" , HeaderText = Ui . T ( "Looks like" , "Wygląda na" ) , FillWeight = 12 , ReadOnly = true ,
285+ DefaultCellStyle = new DataGridViewCellStyle { ForeColor = SystemColors . GrayText } } ) ;
281286 envConnection . SelectedIndexChanged += async ( _ , _ ) => await LoadEnvironmentsAsync ( detect : false ) ;
282287
283288 var top = new FlowLayoutPanel { Dock = DockStyle . Fill , AutoSize = true , WrapContents = false } ;
@@ -289,11 +294,31 @@ TabPage EnvironmentsPage()
289294 top . Controls . Add ( Button ( "↓" , ( ) => MoveRow ( + 1 ) ) ) ;
290295 top . Controls . Add ( Button ( Ui . T ( "Remove row" , "Usuń wiersz" ) , ( ) => { foreach ( DataGridViewRow r in envGrid . SelectedRows ) if ( ! r . IsNewRow ) envGrid . Rows . Remove ( r ) ; } ) ) ;
291296
297+ // Many repositories give many rows: narrow them down, then fill the names of the rows left.
298+ var filter = new FlowLayoutPanel { Dock = DockStyle . Fill , AutoSize = true , WrapContents = false } ;
299+ filter . Controls . Add ( new Label { Text = Ui . T ( "Filter:" , "Filtr:" ) , AutoSize = true , Padding = new Padding ( 0 , 6 , 0 , 0 ) } ) ;
300+ filter . Controls . Add ( envFilter ) ;
301+ filter . Controls . Add ( new Label { Text = Ui . T ( "Repository:" , "Repozytorium:" ) , AutoSize = true , Padding = new Padding ( 8 , 6 , 0 , 0 ) } ) ;
302+ filter . Controls . Add ( envRepo ) ;
303+ envNamed . Text = Ui . T ( "named only" , "tylko nazwane" ) ;
304+ envNamed . Padding = new Padding ( 8 , 4 , 0 , 0 ) ;
305+ filter . Controls . Add ( envNamed ) ;
306+ filter . Controls . Add ( Button ( Ui . T ( "Use the hints for the rows shown" , "Użyj podpowiedzi dla widocznych" ) , ( ) =>
307+ {
308+ foreach ( DataGridViewRow row in envGrid . Rows )
309+ if ( ! row . IsNewRow && row . Visible && string . IsNullOrWhiteSpace ( row . Cells [ 2 ] . Value as string ) && row . Cells [ 3 ] . Value is string { Length : > 0 } hint )
310+ row . Cells [ 2 ] . Value = hint ;
311+ } ) ) ;
312+ envFilter . TextChanged += ( _ , _ ) => ApplyFilter ( ) ;
313+ envRepo . SelectedIndexChanged += ( _ , _ ) => ApplyFilter ( ) ;
314+ envNamed . CheckedChanged += ( _ , _ ) => ApplyFilter ( ) ;
315+
292316 var bottom = new FlowLayoutPanel { Dock = DockStyle . Fill , AutoSize = true } ;
293317 bottom . Controls . Add ( Button ( Ui . T ( "Save for me" , "Zapisz dla mnie" ) , async ( ) => await SaveEnvironmentsAsync ( team : false ) ) ) ;
294318 bottom . Controls . Add ( Button ( Ui . T ( "Save for the whole team (wiki page \" ADO Lens\" )" , "Zapisz dla zespołu (strona wiki \" ADO Lens\" )" ) , async ( ) => await SaveEnvironmentsAsync ( team : true ) ) ) ;
295319
296- var layout = new TableLayoutPanel { Dock = DockStyle . Fill , ColumnCount = 1 , RowCount = 5 } ;
320+ var layout = new TableLayoutPanel { Dock = DockStyle . Fill , ColumnCount = 1 , RowCount = 6 } ;
321+ layout . RowStyles . Add ( new RowStyle ( SizeType . AutoSize ) ) ;
297322 layout . RowStyles . Add ( new RowStyle ( SizeType . AutoSize ) ) ;
298323 layout . RowStyles . Add ( new RowStyle ( SizeType . AutoSize ) ) ;
299324 layout . RowStyles . Add ( new RowStyle ( SizeType . Percent , 100 ) ) ;
@@ -302,9 +327,10 @@ TabPage EnvironmentsPage()
302327 layout . Controls . Add ( top , 0 , 0 ) ;
303328 layout . Controls . Add ( Note ( Ui . T ( "In flow order: DEV first, production last. A repository with rows of its own uses only those (the same branch may mean something else, or nothing, in another repository)." ,
304329 "W kolejności przepływu: najpierw DEV, na końcu produkcja. Repozytorium z własnymi wierszami używa tylko ich (ta sama gałąź w innym repozytorium może znaczyć co innego albo nic)." ) ) , 0 , 1 ) ;
305- layout . Controls . Add ( envGrid , 0 , 2 ) ;
306- layout . Controls . Add ( envInfo , 0 , 3 ) ;
307- layout . Controls . Add ( bottom , 0 , 4 ) ;
330+ layout . Controls . Add ( filter , 0 , 2 ) ;
331+ layout . Controls . Add ( envGrid , 0 , 3 ) ;
332+ layout . Controls . Add ( envInfo , 0 , 4 ) ;
333+ layout . Controls . Add ( bottom , 0 , 5 ) ;
308334 return Page ( Ui . T ( "Environments" , "Środowiska" ) , layout ) ;
309335 }
310336
@@ -332,24 +358,60 @@ async Task LoadEnvironmentsAsync(bool detect)
332358 var project = session . Project ( null ) ;
333359 var ( map , source ) = await EnvironmentFlow . EffectiveAsync ( session , project , default ) ;
334360 envGrid . Rows . Clear ( ) ;
335- foreach ( var ( key , label ) in map ) AddRow ( key , label ) ;
361+ foreach ( var ( key , label ) in map ) AddRow ( key , label , null ) ;
336362 var found = 0 ;
363+ // Found rows get no name yet, only the hint: with many repositories, saving would otherwise take them all.
337364 if ( detect || map . Count == 0 )
338365 foreach ( var ( key , guess ) in await EnvironmentFlow . DetectAsync ( session , project , default ) )
339- if ( ! map . ContainsKey ( key ) ) { AddRow ( key , guess ?? "" ) ; found ++ ; }
366+ if ( ! map . ContainsKey ( key ) ) { AddRow ( key , "" , guess ) ; found ++ ; }
367+ FillRepositories ( ) ;
340368 var code = session . Config . CodeProject is { } cp && ! cp . Equals ( project , StringComparison . OrdinalIgnoreCase ) ? Ui . T ( $ " (code in { cp } )", $ " (kod w { cp } )") : "" ;
341369 envInfo . Text = $ "{ project } { code } : " + ( map . Count > 0 ? Ui . T ( $ "in use: { source } ", $ "w użyciu: { source } ") : Ui . T ( "no map yet" , "brak mapy" ) )
342- + ( found > 0 ? Ui . T ( $ "; { found } found (check the names)", $ "; znalezione: { found } (sprawdź nazwy)") : "" ) ;
370+ + ( found > 0 ? Ui . T ( $ "; { found } found - name the ones that are environments (or filter, then use the hints)",
371+ $ "; znalezione: { found } - nazwij te, które są środowiskami (albo przefiltruj i użyj podpowiedzi)") : "" ) ;
343372 }
344373 catch ( Exception ex ) when ( ex is InvalidOperationException or AdoApiException or HttpRequestException )
345374 {
346375 envInfo . Text = ex . Message ;
376+ FillRepositories ( ) ;
347377 }
348378 finally { UseWaitCursor = false ; }
349379 }
350380
351- void AddRow ( string key , string label ) =>
352- envGrid . Rows . Add ( EnvMap . RepoOf ( key ) ?? "" , EnvMap . BranchOf ( key ) , label ) ;
381+ void AddRow ( string key , string label , string ? hint ) =>
382+ envGrid . Rows . Add ( EnvMap . RepoOf ( key ) ?? "" , EnvMap . BranchOf ( key ) , label , hint ?? "" ) ;
383+
384+ const int AllRepositories = 0 , NoRepository = 1 ;
385+
386+ /// <summary>The repository filter: everything, the rows without a repository (TFVC branches, release stages), each repository.</summary>
387+ void FillRepositories ( )
388+ {
389+ var keep = envRepo . SelectedItem as string ;
390+ envRepo . Items . Clear ( ) ;
391+ envRepo . Items . Add ( Ui . T ( "(all)" , "(wszystkie)" ) ) ;
392+ envRepo . Items . Add ( Ui . T ( "(none: TFVC branches, release stages)" , "(bez: gałęzie TFVC, etapy release'ów)" ) ) ;
393+ envRepo . Items . AddRange ( envGrid . Rows . Cast < DataGridViewRow > ( ) . Where ( r => ! r . IsNewRow ) . Select ( r => r . Cells [ 0 ] . Value as string )
394+ . Where ( v => ! string . IsNullOrWhiteSpace ( v ) ) . Distinct ( StringComparer . OrdinalIgnoreCase ) . Order ( StringComparer . OrdinalIgnoreCase ) . Cast < object > ( ) . ToArray ( ) ) ;
395+ envRepo . SelectedIndex = keep is not null && envRepo . Items . IndexOf ( keep ) is var i and >= 0 ? i : AllRepositories ;
396+ ApplyFilter ( ) ;
397+ }
398+
399+ /// <summary>Hides the rows that do not match; hidden rows keep their names and are saved like the others.</summary>
400+ void ApplyFilter ( )
401+ {
402+ var text = envFilter . Text . Trim ( ) ;
403+ envGrid . CurrentCell = null ;
404+ foreach ( DataGridViewRow row in envGrid . Rows )
405+ {
406+ if ( row . IsNewRow ) continue ;
407+ var repo = row . Cells [ 0 ] . Value as string ?? "" ;
408+ var cells = string . Join ( " " , row . Cells . Cast < DataGridViewCell > ( ) . Select ( c => c . Value as string ) ) ;
409+ var index = envRepo . SelectedIndex ;
410+ var inRepo = index <= AllRepositories || ( index == NoRepository ? repo . Length == 0 : repo . Equals ( envRepo . SelectedItem as string , StringComparison . OrdinalIgnoreCase ) ) ;
411+ row . Visible = ( text . Length == 0 || cells . Contains ( text , StringComparison . OrdinalIgnoreCase ) ) && inRepo
412+ && ( ! envNamed . Checked || ! string . IsNullOrWhiteSpace ( row . Cells [ 2 ] . Value as string ) ) ;
413+ }
414+ }
353415
354416 /// <summary>The rows as a map in their order; a row without a name is left out.</summary>
355417 internal static Dictionary < string , string > MapOf ( IEnumerable < ( string ? Repo , string ? Key , string ? Name ) > rows )
0 commit comments