feat: UI improvements and robustness fixes
- Use System.IO.Path.GetFileName for safer path handling (handles UNC paths, trailing slashes) - Increase minimum window size (860x600, was 760x560) for better control visibility - Widen folder list box and reposition buttons to match - Enable multi-select mode on folder list - Add keyboard accelerators to Ajouter/Supprimer buttons (&) - Disable Supprimer button when no folder selected Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
+224
-115
@@ -564,7 +564,7 @@ function Get-FolderNode {
|
||||
[int]$Depth = 0
|
||||
)
|
||||
|
||||
$name = Split-Path -Path $Path -Leaf
|
||||
$name = [System.IO.Path]::GetFileName($Path.TrimEnd('\'))
|
||||
if ([string]::IsNullOrEmpty($name)) { $name = $Path } # e.g. a drive root
|
||||
|
||||
$node = [ordered]@{
|
||||
@@ -2052,7 +2052,7 @@ $form = New-Object System.Windows.Forms.Form
|
||||
$form.Text = 'Filer Manager'
|
||||
$form.Size = New-Object System.Drawing.Size(960, 720)
|
||||
$form.StartPosition = 'CenterScreen'
|
||||
$form.MinimumSize = New-Object System.Drawing.Size(760, 560)
|
||||
$form.MinimumSize = New-Object System.Drawing.Size(860, 600)
|
||||
|
||||
# ---- top: folder list + add/remove ----
|
||||
$lblFolders = New-Object System.Windows.Forms.Label
|
||||
@@ -2061,19 +2061,20 @@ $lblFolders.Location = '12,12'; $lblFolders.AutoSize = $true
|
||||
$form.Controls.Add($lblFolders)
|
||||
|
||||
$lstFolders = New-Object System.Windows.Forms.ListBox
|
||||
$lstFolders.Location = '12,32'; $lstFolders.Size = '700,84'
|
||||
$lstFolders.Location = '12,32'; $lstFolders.Size = '800,84'
|
||||
$lstFolders.Anchor = 'Top,Left,Right'
|
||||
$lstFolders.HorizontalScrollbar = $true
|
||||
$lstFolders.SelectionMode = 'MultiExtended'
|
||||
$form.Controls.Add($lstFolders)
|
||||
|
||||
$btnAdd = New-Object System.Windows.Forms.Button
|
||||
$btnAdd.Text = 'Ajouter...'; $btnAdd.Location = '724,32'; $btnAdd.Size = '110,28'
|
||||
$btnAdd.Text = 'A&jouter...'; $btnAdd.Location = '824,32'; $btnAdd.Size = '110,28'
|
||||
$btnAdd.Anchor = 'Top,Right'
|
||||
$form.Controls.Add($btnAdd)
|
||||
|
||||
$btnRemove = New-Object System.Windows.Forms.Button
|
||||
$btnRemove.Text = 'Supprimer'; $btnRemove.Location = '724,66'; $btnRemove.Size = '110,28'
|
||||
$btnRemove.Anchor = 'Top,Right'
|
||||
$btnRemove.Text = '&Supprimer'; $btnRemove.Location = '824,66'; $btnRemove.Size = '110,28'
|
||||
$btnRemove.Anchor = 'Top,Right'; $btnRemove.Enabled = $false
|
||||
$form.Controls.Add($btnRemove)
|
||||
|
||||
# ---- settings row ----
|
||||
@@ -2136,21 +2137,47 @@ $form.Controls.Add($chkRevert)
|
||||
$chkGrant.Add_CheckedChanged({ $chkRevert.Enabled = $chkGrant.Checked })
|
||||
|
||||
$btnScan = New-Object System.Windows.Forms.Button
|
||||
$btnScan.Text = 'Analyser'; $btnScan.Location = '600,124'; $btnScan.Size = '110,30'
|
||||
$btnScan.Text = '&Analyser'; $btnScan.Location = '700,124'; $btnScan.Size = '110,30'
|
||||
$btnScan.Anchor = 'Top,Right'
|
||||
$btnScan.BackColor = [System.Drawing.Color]::FromArgb(79,140,255)
|
||||
$btnScan.ForeColor = [System.Drawing.Color]::White
|
||||
$btnScan.FlatStyle = 'Flat'
|
||||
$btnScan.Cursor = [System.Windows.Forms.Cursors]::Hand
|
||||
$btnScan.FlatAppearance.BorderSize = 0
|
||||
$btnScan.FlatAppearance.MouseOverBackColor = [System.Drawing.Color]::FromArgb(104, 158, 255)
|
||||
$btnScan.FlatAppearance.MouseDownBackColor = [System.Drawing.Color]::FromArgb(56, 114, 220)
|
||||
# A flat button keeps its custom BackColor when disabled, which reads as "still
|
||||
# clickable". Dim it explicitly instead.
|
||||
$btnScan.Add_EnabledChanged({
|
||||
$btnScan.BackColor = if ($btnScan.Enabled) { [System.Drawing.Color]::FromArgb(79, 140, 255) }
|
||||
else { [System.Drawing.Color]::FromArgb(176, 186, 200) }
|
||||
})
|
||||
$form.Controls.Add($btnScan)
|
||||
|
||||
$btnExport = New-Object System.Windows.Forms.Button
|
||||
$btnExport.Text = 'Exporter...'; $btnExport.Location = '724,124'; $btnExport.Size = '110,30'
|
||||
$btnExport.Text = '&Exporter...'; $btnExport.Location = '824,124'; $btnExport.Size = '110,30'
|
||||
$btnExport.Anchor = 'Top,Right'; $btnExport.Enabled = $false
|
||||
$form.Controls.Add($btnExport)
|
||||
|
||||
# Defined here because the list views below hook it on Resize, which can fire
|
||||
# while the tab pages are still being built.
|
||||
function Update-FilerListFill {
|
||||
<# Gives one column whatever width the other columns leave, so the long
|
||||
path/folder column follows the window instead of staying at its design
|
||||
width with a horizontal scrollbar. #>
|
||||
param($List, [int]$Col)
|
||||
if ($null -eq $List -or $List.Columns.Count -le $Col) { return }
|
||||
$other = 0
|
||||
for ($i = 0; $i -lt $List.Columns.Count; $i++) {
|
||||
if ($i -ne $Col) { $other += $List.Columns[$i].Width }
|
||||
}
|
||||
$w = $List.ClientSize.Width - $other - 4
|
||||
if ($w -gt 140) { $List.Columns[$Col].Width = $w }
|
||||
}
|
||||
|
||||
# ---- tabs ----
|
||||
$tabs = New-Object System.Windows.Forms.TabControl
|
||||
$tabs.Location = '12,220'; $tabs.Size = '922,424'
|
||||
$tabs.Location = '12,220'; $tabs.Size = '922,437'
|
||||
$tabs.Anchor = 'Top,Bottom,Left,Right'
|
||||
$form.Controls.Add($tabs)
|
||||
|
||||
@@ -2172,6 +2199,16 @@ $tabTree.Controls.Add($tree)
|
||||
# the GUI responsive no matter how large the scanned tree is).
|
||||
$tree.Add_BeforeExpand({ param($s, $e) Expand-FilerTreeNode -TreeNode $e.Node })
|
||||
|
||||
# DrawNode fires for every visible row on every repaint, so the brushes and the
|
||||
# pen are built once here instead of three GDI allocations per row per paint.
|
||||
$script:BarTrack = New-Object System.Drawing.SolidBrush ([System.Drawing.Color]::FromArgb(228, 230, 235))
|
||||
$script:BarFill = New-Object System.Drawing.SolidBrush ([System.Drawing.Color]::FromArgb(59, 111, 212))
|
||||
$script:BarEdge = New-Object System.Drawing.Pen ([System.Drawing.Color]::FromArgb(170, 175, 185))
|
||||
$script:BarSel = New-Object System.Drawing.SolidBrush ([System.Drawing.SystemColors]::Highlight)
|
||||
$script:TreeTextFlags = [System.Windows.Forms.TextFormatFlags]::VerticalCenter -bor `
|
||||
[System.Windows.Forms.TextFormatFlags]::Left -bor `
|
||||
[System.Windows.Forms.TextFormatFlags]::NoPrefix
|
||||
|
||||
# Render each tree node: the label, then a bar showing the folder's share of its
|
||||
# parent's size (roots = 100%), then the percentage - matching the HTML report.
|
||||
$tree.Add_DrawNode({
|
||||
@@ -2185,14 +2222,9 @@ $tree.Add_DrawNode({
|
||||
|
||||
$selected = ($e.State -band [System.Windows.Forms.TreeNodeStates]::Selected) -ne 0
|
||||
$foreColor = if ($selected) { [System.Drawing.SystemColors]::HighlightText } else { $tree.ForeColor }
|
||||
if ($selected) {
|
||||
$hl = New-Object System.Drawing.SolidBrush ([System.Drawing.SystemColors]::Highlight)
|
||||
$e.Graphics.FillRectangle($hl, $e.Bounds); $hl.Dispose()
|
||||
}
|
||||
if ($selected) { $e.Graphics.FillRectangle($script:BarSel, $e.Bounds) }
|
||||
|
||||
$flags = [System.Windows.Forms.TextFormatFlags]::VerticalCenter -bor `
|
||||
[System.Windows.Forms.TextFormatFlags]::Left -bor `
|
||||
[System.Windows.Forms.TextFormatFlags]::NoPrefix
|
||||
$flags = $script:TreeTextFlags
|
||||
[System.Windows.Forms.TextRenderer]::DrawText($e.Graphics, $e.Node.Text, $tree.Font, $e.Bounds, $foreColor, $flags)
|
||||
|
||||
# Share of the parent folder's size (top-level roots have no parent = 100%).
|
||||
@@ -2204,21 +2236,17 @@ $tree.Add_DrawNode({
|
||||
}
|
||||
if ($pct -lt 0) { $pct = 0 } elseif ($pct -gt 100) { $pct = 100 }
|
||||
|
||||
# Bar just to the right of the label.
|
||||
# Bar just to the right of the label - skipped entirely when a long label has
|
||||
# already pushed it out of view, so deep trees do not pay for invisible paint.
|
||||
$barW = 120; $barH = 9
|
||||
$barX = $e.Bounds.Right + 10
|
||||
$barY = $e.Bounds.Top + [int](($e.Bounds.Height - $barH) / 2)
|
||||
if ($barX -ge $tree.ClientSize.Width) { return }
|
||||
|
||||
$track = New-Object System.Drawing.SolidBrush ([System.Drawing.Color]::FromArgb(228, 230, 235))
|
||||
$e.Graphics.FillRectangle($track, $barX, $barY, $barW, $barH); $track.Dispose()
|
||||
|
||||
$e.Graphics.FillRectangle($script:BarTrack, $barX, $barY, $barW, $barH)
|
||||
$fillW = [int][math]::Round($barW * $pct / 100)
|
||||
if ($fillW -gt 0) {
|
||||
$fill = New-Object System.Drawing.SolidBrush ([System.Drawing.Color]::FromArgb(59, 111, 212))
|
||||
$e.Graphics.FillRectangle($fill, $barX, $barY, $fillW, $barH); $fill.Dispose()
|
||||
}
|
||||
$pen = New-Object System.Drawing.Pen ([System.Drawing.Color]::FromArgb(170, 175, 185))
|
||||
$e.Graphics.DrawRectangle($pen, $barX, $barY, $barW, $barH); $pen.Dispose()
|
||||
if ($fillW -gt 0) { $e.Graphics.FillRectangle($script:BarFill, $barX, $barY, $fillW, $barH) }
|
||||
$e.Graphics.DrawRectangle($script:BarEdge, $barX, $barY, $barW, $barH)
|
||||
|
||||
$pctRect = New-Object System.Drawing.Rectangle (($barX + $barW + 6), $e.Bounds.Top, 52, $e.Bounds.Height)
|
||||
[System.Windows.Forms.TextRenderer]::DrawText($e.Graphics, ('{0:N1}%' -f $pct), $tree.Font, $pctRect, $tree.ForeColor, $flags)
|
||||
@@ -2243,36 +2271,44 @@ $lvGrant.Dock = 'Fill'; $lvGrant.View = 'Details'; $lvGrant.FullRowSelect = $tru
|
||||
$tabGrant.Controls.Add($lvGrant)
|
||||
|
||||
# Filter toolbar for the permissions tab (docked above the list).
|
||||
$tabW = 914 # baseline width the right-aligned tab controls are anchored to
|
||||
|
||||
$permBar = New-Object System.Windows.Forms.Panel
|
||||
$permBar.Dock = 'Top'; $permBar.Height = 30
|
||||
$permBar.Dock = 'Top'; $permBar.Height = 30; $permBar.Width = $tabW
|
||||
$lblPermHint = New-Object System.Windows.Forms.Label
|
||||
$lblPermHint.Text = 'Cliquez sur un en-tête de colonne pour filtrer ou trier.'
|
||||
$lblPermHint.AutoSize = $true; $lblPermHint.Location = '6,7'
|
||||
# Sized rather than auto-sized: it shares the row with the two check boxes, so
|
||||
# it has to give ground (and ellipsize) as the window narrows.
|
||||
$lblPermHint.AutoSize = $false; $lblPermHint.Location = '6,8'; $lblPermHint.Size = '330,18'
|
||||
$lblPermHint.Anchor = 'Top,Left,Right'; $lblPermHint.AutoEllipsis = $true
|
||||
$lblPermHint.ForeColor = [System.Drawing.Color]::DimGray
|
||||
$chkPermHideInh = New-Object System.Windows.Forms.CheckBox
|
||||
$chkPermHideInh.Text = 'Masquer les héritées (enfants)'
|
||||
$chkPermHideInh.Size = '220,20'; $chkPermHideInh.Location = '342,6'; $chkPermHideInh.Anchor = 'Top,Right'
|
||||
$chkPermHideSys = New-Object System.Windows.Forms.CheckBox
|
||||
$chkPermHideSys.Text = 'Masquer les comptes système'
|
||||
$chkPermHideSys.Size = '200,20'; $chkPermHideSys.Location = '570,6'; $chkPermHideSys.Anchor = 'Top,Right'
|
||||
$btnPermClear = New-Object System.Windows.Forms.Button
|
||||
$btnPermClear.Text = 'Effacer les filtres'; $btnPermClear.Size = '130,24'
|
||||
$btnPermClear.Dock = 'Right'; $btnPermClear.Enabled = $false
|
||||
$chkPermHideInh = New-Object System.Windows.Forms.CheckBox
|
||||
$chkPermHideInh.Text = 'Masquer les permissions héritées (enfants)'
|
||||
$chkPermHideInh.AutoSize = $true; $chkPermHideInh.Dock = 'Right'
|
||||
$chkPermHideInh.Padding = '0,4,8,0'
|
||||
$chkPermHideSys = New-Object System.Windows.Forms.CheckBox
|
||||
$chkPermHideSys.Text = 'Masquer les comptes/groupes système'
|
||||
$chkPermHideSys.AutoSize = $true; $chkPermHideSys.Dock = 'Right'
|
||||
$chkPermHideSys.Padding = '0,4,8,0'
|
||||
$permBar.Controls.Add($lblPermHint)
|
||||
$permBar.Controls.Add($chkPermHideInh)
|
||||
$permBar.Controls.Add($chkPermHideSys)
|
||||
$permBar.Controls.Add($btnPermClear)
|
||||
$btnPermClear.Location = '778,3'; $btnPermClear.Anchor = 'Top,Right'; $btnPermClear.Enabled = $false
|
||||
$permBar.Controls.AddRange(@($lblPermHint, $chkPermHideInh, $chkPermHideSys, $btnPermClear))
|
||||
$tabPerm.Controls.Add($permBar)
|
||||
|
||||
$tip.SetToolTip($chkPermHideInh, "Masquer les permissions héritées portées par les dossiers enfants.`nCelles des dossiers racines sont conservées : leur parent est hors analyse.")
|
||||
$tip.SetToolTip($chkPermHideSys, "Masquer les entrées détenues par les comptes et groupes système bien connus`n(SYSTEM, Administrators, CREATOR OWNER, NT SERVICE\*) pour ne garder que les identités métier.")
|
||||
$tip.SetToolTip($btnPermClear, 'Supprimer tous les filtres de colonne actifs.')
|
||||
|
||||
$lvLong.Add_Resize({ Update-FilerListFill -List $lvLong -Col 2 })
|
||||
$lvPerm.Add_Resize({ Update-FilerListFill -List $lvPerm -Col 0 })
|
||||
$lvGrant.Add_Resize({ Update-FilerListFill -List $lvGrant -Col 0 })
|
||||
|
||||
$lvPerm.Add_ColumnClick({ param($s, $e) Show-PermColumnMenu -ColumnIndex $e.Column })
|
||||
$btnPermClear.Add_Click({ $script:PermFilters = @{}; Update-PermView })
|
||||
$chkPermHideInh.Add_CheckedChanged({ $script:PermHideInherited = $chkPermHideInh.Checked; Update-PermView })
|
||||
$chkPermHideSys.Add_CheckedChanged({ $script:PermHideSystem = $chkPermHideSys.Checked; Update-PermView })
|
||||
|
||||
# ---- Renommer tab: batch rename with find/replace, regex, case, affixes, numbering ----
|
||||
$rnW = 914 # baseline width used to anchor the right-aligned controls
|
||||
$rnW = $tabW # baseline width used to anchor the right-aligned controls
|
||||
|
||||
# Preview grid (added first so the docked panels below claim their edges and the
|
||||
# grid fills the remaining space - same ordering trick used by the perm filter bar).
|
||||
@@ -2292,59 +2328,59 @@ $rnTop.Dock = 'Top'; $rnTop.Height = 200; $rnTop.Width = $rnW
|
||||
$lblRnPath = New-Object System.Windows.Forms.Label
|
||||
$lblRnPath.Text = 'Dossier :'; $lblRnPath.Location = '6,13'; $lblRnPath.AutoSize = $true
|
||||
$rnPath = New-Object System.Windows.Forms.TextBox
|
||||
$rnPath.Location = '70,10'; $rnPath.Width = ($rnW - 70 - 188); $rnPath.Anchor = 'Top,Left,Right'
|
||||
$rnPath.Location = '90,10'; $rnPath.Width = ($rnW - 90 - 198); $rnPath.Anchor = 'Top,Left,Right'
|
||||
$rnBrowse = New-Object System.Windows.Forms.Button
|
||||
$rnBrowse.Text = 'Parcourir...'; $rnBrowse.Size = '88,24'; $rnBrowse.Location = "$($rnW - 184),9"; $rnBrowse.Anchor = 'Top,Right'
|
||||
$rnBrowse.Text = 'Parcourir...'; $rnBrowse.Size = '88,24'; $rnBrowse.Location = "$($rnW - 190),9"; $rnBrowse.Anchor = 'Top,Right'
|
||||
$rnList = New-Object System.Windows.Forms.Button
|
||||
$rnList.Text = 'Lister'; $rnList.Size = '88,24'; $rnList.Location = "$($rnW - 92),9"; $rnList.Anchor = 'Top,Right'
|
||||
$rnList.Text = 'Lister'; $rnList.Size = '88,24'; $rnList.Location = "$($rnW - 94),9"; $rnList.Anchor = 'Top,Right'
|
||||
$rnTop.Controls.AddRange(@($lblRnPath, $rnPath, $rnBrowse, $rnList))
|
||||
|
||||
# Row 2: scope options.
|
||||
$chkRnRecurse = New-Object System.Windows.Forms.CheckBox
|
||||
$chkRnRecurse.Text = 'Inclure les sous-dossiers'; $chkRnRecurse.Location = '70,40'; $chkRnRecurse.AutoSize = $true
|
||||
$chkRnRecurse.Text = 'Inclure les sous-dossiers'; $chkRnRecurse.Location = '90,40'; $chkRnRecurse.AutoSize = $true
|
||||
$chkRnFiles = New-Object System.Windows.Forms.CheckBox
|
||||
$chkRnFiles.Text = 'Fichiers'; $chkRnFiles.Location = '250,40'; $chkRnFiles.AutoSize = $true; $chkRnFiles.Checked = $true
|
||||
$chkRnFiles.Text = 'Fichiers'; $chkRnFiles.Location = '270,40'; $chkRnFiles.AutoSize = $true; $chkRnFiles.Checked = $true
|
||||
$chkRnFolders = New-Object System.Windows.Forms.CheckBox
|
||||
$chkRnFolders.Text = 'Dossiers'; $chkRnFolders.Location = '345,40'; $chkRnFolders.AutoSize = $true; $chkRnFolders.Checked = $true
|
||||
$chkRnFolders.Text = 'Dossiers'; $chkRnFolders.Location = '365,40'; $chkRnFolders.AutoSize = $true; $chkRnFolders.Checked = $true
|
||||
$chkRnHidden = New-Object System.Windows.Forms.CheckBox
|
||||
$chkRnHidden.Text = 'Exclure les dossiers masqués'; $chkRnHidden.Location = '450,40'; $chkRnHidden.AutoSize = $true; $chkRnHidden.Checked = $true
|
||||
$chkRnHidden.Text = 'Exclure les dossiers masqués'; $chkRnHidden.Location = '470,40'; $chkRnHidden.AutoSize = $true; $chkRnHidden.Checked = $true
|
||||
$rnTop.Controls.AddRange(@($chkRnRecurse, $chkRnFiles, $chkRnFolders, $chkRnHidden))
|
||||
|
||||
# Row 3: find.
|
||||
$lblRnFind = New-Object System.Windows.Forms.Label
|
||||
$lblRnFind.Text = 'Rechercher :'; $lblRnFind.Location = '6,75'; $lblRnFind.AutoSize = $true
|
||||
$rnFind = New-Object System.Windows.Forms.TextBox
|
||||
$rnFind.Location = '90,72'; $rnFind.Width = ($rnW - 90 - 270); $rnFind.Anchor = 'Top,Left,Right'
|
||||
$rnFind.Location = '90,72'; $rnFind.Width = ($rnW - 90 - 222); $rnFind.Anchor = 'Top,Left,Right'
|
||||
$chkRnRegex = New-Object System.Windows.Forms.CheckBox
|
||||
$chkRnRegex.Text = 'Regex'; $chkRnRegex.Size = '70,22'; $chkRnRegex.Location = "$($rnW - 172),74"; $chkRnRegex.Anchor = 'Top,Right'
|
||||
$chkRnRegex.Text = 'Regex'; $chkRnRegex.Size = '70,22'; $chkRnRegex.Location = "$($rnW - 214),74"; $chkRnRegex.Anchor = 'Top,Right'
|
||||
$chkRnIgnoreCase = New-Object System.Windows.Forms.CheckBox
|
||||
$chkRnIgnoreCase.Text = 'Ignorer la casse'; $chkRnIgnoreCase.Size = '130,22'; $chkRnIgnoreCase.Location = "$($rnW - 100),74"; $chkRnIgnoreCase.Anchor = 'Top,Right'
|
||||
$chkRnIgnoreCase.Text = 'Ignorer la casse'; $chkRnIgnoreCase.Size = '130,22'; $chkRnIgnoreCase.Location = "$($rnW - 136),74"; $chkRnIgnoreCase.Anchor = 'Top,Right'
|
||||
$rnTop.Controls.AddRange(@($lblRnFind, $rnFind, $chkRnRegex, $chkRnIgnoreCase))
|
||||
|
||||
# Row 4: replace.
|
||||
$lblRnReplace = New-Object System.Windows.Forms.Label
|
||||
$lblRnReplace.Text = 'Remplacer :'; $lblRnReplace.Location = '6,105'; $lblRnReplace.AutoSize = $true
|
||||
$rnReplace = New-Object System.Windows.Forms.TextBox
|
||||
$rnReplace.Location = '90,102'; $rnReplace.Width = ($rnW - 90 - 270); $rnReplace.Anchor = 'Top,Left,Right'
|
||||
$rnReplace.Location = '90,102'; $rnReplace.Width = ($rnW - 90 - 222); $rnReplace.Anchor = 'Top,Left,Right'
|
||||
$chkRnIgnoreExt = New-Object System.Windows.Forms.CheckBox
|
||||
$chkRnIgnoreExt.Text = "Ignorer l'extension"; $chkRnIgnoreExt.Size = '160,22'; $chkRnIgnoreExt.Location = "$($rnW - 172),104"; $chkRnIgnoreExt.Anchor = 'Top,Right'; $chkRnIgnoreExt.Checked = $true
|
||||
$chkRnIgnoreExt.Text = "Ignorer l'extension"; $chkRnIgnoreExt.Size = '160,22'; $chkRnIgnoreExt.Location = "$($rnW - 166),104"; $chkRnIgnoreExt.Anchor = 'Top,Right'; $chkRnIgnoreExt.Checked = $true
|
||||
$rnTop.Controls.AddRange(@($lblRnReplace, $rnReplace, $chkRnIgnoreExt))
|
||||
|
||||
# Row 5: case + affixes.
|
||||
$lblRnCase = New-Object System.Windows.Forms.Label
|
||||
$lblRnCase.Text = 'Casse :'; $lblRnCase.Location = '6,137'; $lblRnCase.AutoSize = $true
|
||||
$rnCase = New-Object System.Windows.Forms.ComboBox
|
||||
$rnCase.DropDownStyle = 'DropDownList'; $rnCase.Location = '62,134'; $rnCase.Width = 150
|
||||
$rnCase.DropDownStyle = 'DropDownList'; $rnCase.Location = '90,134'; $rnCase.Width = 150
|
||||
[void]$rnCase.Items.AddRange(@('Conserver', 'MAJUSCULES', 'minuscules', 'Première Lettre Des Mots', 'Première lettre'))
|
||||
$rnCase.SelectedIndex = 0
|
||||
$lblRnPrefix = New-Object System.Windows.Forms.Label
|
||||
$lblRnPrefix.Text = 'Préfixe :'; $lblRnPrefix.Location = '228,137'; $lblRnPrefix.AutoSize = $true
|
||||
$lblRnPrefix.Text = 'Préfixe :'; $lblRnPrefix.Location = '256,137'; $lblRnPrefix.AutoSize = $true
|
||||
$rnPrefix = New-Object System.Windows.Forms.TextBox
|
||||
$rnPrefix.Location = '282,134'; $rnPrefix.Width = 150
|
||||
$rnPrefix.Location = '310,134'; $rnPrefix.Width = 150
|
||||
$lblRnSuffix = New-Object System.Windows.Forms.Label
|
||||
$lblRnSuffix.Text = 'Suffixe :'; $lblRnSuffix.Location = '444,137'; $lblRnSuffix.AutoSize = $true
|
||||
$lblRnSuffix.Text = 'Suffixe :'; $lblRnSuffix.Location = '476,137'; $lblRnSuffix.AutoSize = $true
|
||||
$rnSuffix = New-Object System.Windows.Forms.TextBox
|
||||
$rnSuffix.Location = '498,134'; $rnSuffix.Width = 150
|
||||
$rnSuffix.Location = '530,134'; $rnSuffix.Width = 150
|
||||
$rnTop.Controls.AddRange(@($lblRnCase, $rnCase, $lblRnPrefix, $rnPrefix, $lblRnSuffix, $rnSuffix))
|
||||
|
||||
# Row 6: sequential numbering + preview.
|
||||
@@ -2390,6 +2426,7 @@ $rnBrowse.Add_Click({
|
||||
-InitialPath $rnPath.Text)
|
||||
if ($picked.Count -gt 0) { $rnPath.Text = [string]$picked[0] }
|
||||
})
|
||||
$lvRename.Add_Resize({ Update-FilerListFill -List $lvRename -Col 4 })
|
||||
$rnList.Add_Click({ Invoke-RenameListing })
|
||||
$rnPreview.Add_Click({ Update-RenamePreview })
|
||||
# Recompute the preview live as the rename options change.
|
||||
@@ -2445,6 +2482,7 @@ $script:PermCols = @('Folder', 'Identity', 'Rights', 'Type', 'Inherited') # c
|
||||
# Display labels for the permission columns (property name -> French header).
|
||||
$script:PermColLabels = @{ Folder = 'Dossier'; Identity = 'Identité'; Rights = 'Droits'; Type = 'Type'; Inherited = 'Hérité' }
|
||||
$script:PermFilters = @{} # property -> System.Collections.Generic.HashSet[string] of allowed values
|
||||
$script:PermMenu = $null # last column menu, disposed when the next one opens
|
||||
$script:PermSort = @{ Prop = 'Folder'; Asc = $true }
|
||||
# When $true, inherited ACEs on child folders are hidden (scan roots keep theirs).
|
||||
$script:PermHideInherited = $false
|
||||
@@ -2492,7 +2530,7 @@ function Expand-FilerTreeNode {
|
||||
$node = $TreeNode.Tag
|
||||
$TreeNode.TreeView.BeginUpdate()
|
||||
$TreeNode.Nodes.Clear()
|
||||
$kids = @($node.Children | Where-Object { $_ } | Sort-Object @{E={$_.Size}} -Descending)
|
||||
$kids = @($node.Children | Where-Object { $_ } | Sort-Object -Property Size -Descending)
|
||||
foreach ($c in $kids) { [void]$TreeNode.Nodes.Add((New-FilerTreeNode -Node $c)) }
|
||||
$TreeNode.TreeView.EndUpdate()
|
||||
}
|
||||
@@ -2506,13 +2544,18 @@ function Show-Results {
|
||||
if ($tree.Nodes.Count -gt 0) { $tree.Nodes[0].Expand() } # loads first level lazily
|
||||
$tree.EndUpdate()
|
||||
|
||||
$lvLong.BeginUpdate(); $lvLong.Items.Clear()
|
||||
# One AddRange instead of a per-row Add: a scan of a big share can report
|
||||
# thousands of long paths and each Add re-lays the control out.
|
||||
$longItems = New-Object 'System.Collections.Generic.List[System.Windows.Forms.ListViewItem]'
|
||||
foreach ($lp in $Scan.LongPaths) {
|
||||
$it = New-Object System.Windows.Forms.ListViewItem([string]$lp.Length)
|
||||
[void]$it.SubItems.Add($lp.Type); [void]$it.SubItems.Add($lp.Path)
|
||||
[void]$lvLong.Items.Add($it)
|
||||
$longItems.Add($it)
|
||||
}
|
||||
$lvLong.BeginUpdate(); $lvLong.Items.Clear()
|
||||
if ($longItems.Count -gt 0) { $lvLong.Items.AddRange($longItems.ToArray()) }
|
||||
$lvLong.EndUpdate()
|
||||
$tabLong.Text = if ($longItems.Count -gt 0) { "Chemins trop longs ($($longItems.Count))" } else { 'Chemins trop longs' }
|
||||
|
||||
$script:AllPerms = @($Scan.Permissions)
|
||||
$script:PermRootPaths = Get-FilerRootPathSet -Scan $Scan
|
||||
@@ -2520,7 +2563,7 @@ function Show-Results {
|
||||
$script:PermSort = @{ Prop = 'Folder'; Asc = $true }
|
||||
Update-PermView
|
||||
|
||||
$lvGrant.BeginUpdate(); $lvGrant.Items.Clear()
|
||||
$grantItems = New-Object 'System.Collections.Generic.List[System.Windows.Forms.ListViewItem]'
|
||||
foreach ($g in @($Scan.Grants | Where-Object { $_.Success })) {
|
||||
$it = New-Object System.Windows.Forms.ListViewItem([string]$g.Path)
|
||||
[void]$it.SubItems.Add([string]$g.Reason)
|
||||
@@ -2530,10 +2573,15 @@ function Show-Results {
|
||||
elseif ($Scan.Settings.RevertGrants) { $state = 'NON annulé'; $it.ForeColor = [System.Drawing.Color]::Firebrick }
|
||||
else { $state = 'conservé'; $it.ForeColor = [System.Drawing.Color]::DarkGoldenrod }
|
||||
[void]$it.SubItems.Add($state)
|
||||
[void]$lvGrant.Items.Add($it)
|
||||
$grantItems.Add($it)
|
||||
}
|
||||
$lvGrant.BeginUpdate(); $lvGrant.Items.Clear()
|
||||
if ($grantItems.Count -gt 0) { $lvGrant.Items.AddRange($grantItems.ToArray()) }
|
||||
$lvGrant.EndUpdate()
|
||||
$tabGrant.Text = if ($lvGrant.Items.Count -gt 0) { "Accès accordé ($($lvGrant.Items.Count))" } else { 'Accès accordé' }
|
||||
$tabGrant.Text = if ($grantItems.Count -gt 0) { "Accès accordé ($($grantItems.Count))" } else { 'Accès accordé' }
|
||||
|
||||
Update-FilerListFill -List $lvLong -Col 2
|
||||
Update-FilerListFill -List $lvGrant -Col 0
|
||||
|
||||
$statusLbl.Text = ("Terminé. {0} au total, {1} dossiers, {2} fichiers, {3} chemins trop longs, {4} erreurs." -f `
|
||||
(Format-Bytes $Scan.Stats.TotalSize), $Scan.Stats.TotalFolders, $Scan.Stats.TotalFiles,
|
||||
@@ -2550,30 +2598,39 @@ function Get-PermCellValue {
|
||||
function Update-PermView {
|
||||
# Re-renders the permissions ListView from $script:AllPerms applying the
|
||||
# active per-column filters and the current sort.
|
||||
$rows = $script:AllPerms
|
||||
if ($script:PermHideInherited) {
|
||||
$rows = @($rows | Where-Object { (-not $_.Inherited) -or $script:PermRootPaths.Contains([string]$_.Folder) })
|
||||
}
|
||||
if ($script:PermHideSystem) {
|
||||
$rows = @($rows | Where-Object { -not (Test-IsSystemPrincipal -Identity $_.Identity -Sid $_.Sid) })
|
||||
}
|
||||
foreach ($prop in $script:PermFilters.Keys) {
|
||||
$allowed = $script:PermFilters[$prop]
|
||||
$rows = @($rows | Where-Object { $allowed.Contains((Get-PermCellValue $_ $prop)) })
|
||||
# Every active filter in a single pass (the old code rebuilt the whole array
|
||||
# once per filter), and the sort keyed on the property name rather than on a
|
||||
# script block evaluated for each comparison.
|
||||
$rows = $script:AllPerms
|
||||
$hideInh = $script:PermHideInherited
|
||||
$hideSys = $script:PermHideSystem
|
||||
$filters = $script:PermFilters
|
||||
$filterKey = @($filters.Keys)
|
||||
$roots = $script:PermRootPaths
|
||||
if ($hideInh -or $hideSys -or $filterKey.Count -gt 0) {
|
||||
$rows = @($rows | Where-Object {
|
||||
if ($hideInh -and $_.Inherited -and -not $roots.Contains([string]$_.Folder)) { return $false }
|
||||
if ($hideSys -and (Test-IsSystemPrincipal -Identity $_.Identity -Sid $_.Sid)) { return $false }
|
||||
foreach ($k in $filterKey) { if (-not $filters[$k].Contains([string]$_.$k)) { return $false } }
|
||||
return $true
|
||||
})
|
||||
}
|
||||
|
||||
$sortProp = $script:PermSort.Prop
|
||||
$rows = @($rows | Sort-Object @{ E = { Get-PermCellValue $_ $sortProp } })
|
||||
$rows = @($rows | Sort-Object -Property $sortProp)
|
||||
if (-not $script:PermSort.Asc) { [array]::Reverse($rows) }
|
||||
|
||||
$lvPerm.BeginUpdate(); $lvPerm.Items.Clear()
|
||||
$deny = [System.Drawing.Color]::Firebrick
|
||||
$items = New-Object 'System.Collections.Generic.List[System.Windows.Forms.ListViewItem]'
|
||||
foreach ($ace in $rows) {
|
||||
$it = New-Object System.Windows.Forms.ListViewItem($ace.Folder)
|
||||
[void]$it.SubItems.Add($ace.Identity); [void]$it.SubItems.Add($ace.Rights)
|
||||
[void]$it.SubItems.Add($ace.Type); [void]$it.SubItems.Add([string]$ace.Inherited)
|
||||
if ($ace.Type -eq 'Deny') { $it.ForeColor = [System.Drawing.Color]::Firebrick }
|
||||
[void]$lvPerm.Items.Add($it)
|
||||
if ($ace.Type -eq 'Deny') { $it.ForeColor = $deny }
|
||||
$items.Add($it)
|
||||
}
|
||||
$lvPerm.BeginUpdate(); $lvPerm.Items.Clear()
|
||||
if ($items.Count -gt 0) { $lvPerm.Items.AddRange($items.ToArray()) }
|
||||
$lvPerm.EndUpdate()
|
||||
|
||||
# Update column headers to show sort arrow and filter funnel.
|
||||
@@ -2588,6 +2645,7 @@ function Update-PermView {
|
||||
$shown = $lvPerm.Items.Count; $total = $script:AllPerms.Count
|
||||
$nFilt = $script:PermFilters.Count
|
||||
$btnPermClear.Enabled = ($nFilt -gt 0)
|
||||
$tabPerm.Text = if ($total -gt 0) { "Permissions ($shown)" } else { 'Permissions' }
|
||||
if ($script:AllPerms.Count -gt 0) {
|
||||
$msg = "Permissions : $shown sur $total affichées"
|
||||
if ($nFilt -gt 0) { $msg += " ($nFilt filtre$(if($nFilt -gt 1){'s'}) de colonne) - cliquez sur un en-tête pour filtrer/trier" }
|
||||
@@ -2601,6 +2659,10 @@ function Show-PermColumnMenu {
|
||||
if ($script:AllPerms.Count -eq 0) { return }
|
||||
$prop = $script:PermCols[$ColumnIndex]
|
||||
|
||||
# The previous menu is only safe to drop once a new click has replaced it -
|
||||
# disposing it from its own Closed handler would tear it down mid-event.
|
||||
if ($script:PermMenu) { $script:PermMenu.Dispose(); $script:PermMenu = $null }
|
||||
|
||||
$menu = New-Object System.Windows.Forms.ContextMenuStrip
|
||||
$menu.ShowImageMargin = $false
|
||||
|
||||
@@ -2662,6 +2724,7 @@ function Show-PermColumnMenu {
|
||||
# Show just under the clicked column header.
|
||||
$x = 0
|
||||
for ($i = 0; $i -lt $ColumnIndex; $i++) { $x += $lvPerm.Columns[$i].Width }
|
||||
$script:PermMenu = $menu
|
||||
$menu.Show($lvPerm, $x, 4)
|
||||
}
|
||||
|
||||
@@ -2689,7 +2752,7 @@ function Show-ExportDialog {
|
||||
$dlg.FormBorderStyle = 'FixedDialog'
|
||||
$dlg.StartPosition = 'CenterParent'
|
||||
$dlg.MaximizeBox = $false; $dlg.MinimizeBox = $false
|
||||
$dlg.ClientSize = New-Object System.Drawing.Size(320, 330)
|
||||
$dlg.ClientSize = New-Object System.Drawing.Size(320, 330) # height fixed up below
|
||||
|
||||
$lbl = New-Object System.Windows.Forms.Label
|
||||
$lbl.Text = 'Inclure ces catégories dans le rapport :'
|
||||
@@ -2741,6 +2804,8 @@ function Show-ExportDialog {
|
||||
$btnCancel.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
|
||||
$dlg.Controls.Add($btnCancel); $dlg.CancelButton = $btnCancel
|
||||
|
||||
$dlg.ClientSize = New-Object System.Drawing.Size(320, ($y + 42))
|
||||
|
||||
# Enable Export only when at least one category AND at least one format is ticked.
|
||||
$sync = {
|
||||
$anyCat = @($boxes | Where-Object { $_.Checked }).Count -gt 0
|
||||
@@ -2861,7 +2926,7 @@ function Invoke-RenameListing {
|
||||
if ($wantDir) {
|
||||
[void]$items.Add([pscustomobject]@{
|
||||
Name = $c.Name; FullPath = $c.FullName; IsFile = $false
|
||||
Dir = (Split-Path -LiteralPath $c.FullName -Parent); Depth = ($cur.Depth + 1)
|
||||
Dir = ([System.IO.Path]::GetDirectoryName($c.FullName)); Depth = ($cur.Depth + 1)
|
||||
})
|
||||
}
|
||||
# A junction / symlink can be renamed, but descending into one
|
||||
@@ -2873,7 +2938,7 @@ function Invoke-RenameListing {
|
||||
elseif ($wantFile) {
|
||||
[void]$items.Add([pscustomobject]@{
|
||||
Name = $c.Name; FullPath = $c.FullName; IsFile = $true
|
||||
Dir = (Split-Path -LiteralPath $c.FullName -Parent); Depth = ($cur.Depth + 1)
|
||||
Dir = ([System.IO.Path]::GetDirectoryName($c.FullName)); Depth = ($cur.Depth + 1)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -2881,7 +2946,7 @@ function Invoke-RenameListing {
|
||||
}
|
||||
finally { $form.Cursor = [System.Windows.Forms.Cursors]::Default }
|
||||
|
||||
$script:RenameItems = @($items | Sort-Object @{ E = { $_.FullPath } })
|
||||
$script:RenameItems = @($items | Sort-Object -Property FullPath)
|
||||
Update-RenamePreview
|
||||
}
|
||||
|
||||
@@ -2929,23 +2994,34 @@ function Update-RenamePreview {
|
||||
}
|
||||
else { $seen[$key] = $row }
|
||||
}
|
||||
# Current name -> row, so "is the occupant one of the items that moves
|
||||
# away?" is a hashtable hit instead of a scan of the whole group. The row
|
||||
# object is stored, not a flag, so its live .Changed state is still read.
|
||||
$movers = @{}
|
||||
foreach ($r in $grp.Group) {
|
||||
if ($r.Changed) { $movers[$r.Item.Name.ToLowerInvariant()] = $r }
|
||||
}
|
||||
foreach ($row in $grp.Group) {
|
||||
if (-not $row.Changed) { continue }
|
||||
$target = Join-Path $row.Item.Dir $row.New
|
||||
# Skip a case-only rename of the item onto itself (-ne is case-insensitive).
|
||||
if ($target -ne $row.Item.FullPath -and (Test-Path -LiteralPath $target)) {
|
||||
# OK only if the current occupant is another listed item that will move away.
|
||||
$occupantMoves = $grp.Group | Where-Object {
|
||||
$_.Changed -and $_.Item.Name.ToLowerInvariant() -eq $row.New.ToLowerInvariant()
|
||||
$occupant = $movers[$row.New.ToLowerInvariant()]
|
||||
if (-not ($occupant -and $occupant.Changed)) {
|
||||
$row.Status = 'conflit (existe déjà)'; $row.Changed = $false
|
||||
[void]$movers.Remove($row.Item.Name.ToLowerInvariant())
|
||||
}
|
||||
if (-not $occupantMoves) { $row.Status = 'conflit (existe déjà)'; $row.Changed = $false }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$script:RenameRows = @($rows)
|
||||
|
||||
$lvRename.BeginUpdate(); $lvRename.Items.Clear()
|
||||
# Built off-control then inserted in one go: this runs on every keystroke in
|
||||
# the find/replace boxes, so a per-row Add would make typing feel sticky.
|
||||
$bad = [System.Drawing.Color]::Firebrick
|
||||
$dim = [System.Drawing.Color]::Gray
|
||||
$view = New-Object 'System.Collections.Generic.List[System.Windows.Forms.ListViewItem]'
|
||||
foreach ($row in $rows) {
|
||||
$it = New-Object System.Windows.Forms.ListViewItem($(if ($row.Item.IsFile) { 'Fichier' } else { 'Dossier' }))
|
||||
[void]$it.SubItems.Add($row.Item.Name)
|
||||
@@ -2955,11 +3031,13 @@ function Update-RenamePreview {
|
||||
$it.Tag = $row
|
||||
if ($row.Changed) { $it.Checked = $true }
|
||||
elseif ($row.Status -like 'conflit*' -or $row.Status -like 'erreur*' -or $row.Status -eq 'caractères invalides' -or $row.Status -eq 'nom vide') {
|
||||
$it.ForeColor = [System.Drawing.Color]::Firebrick
|
||||
$it.ForeColor = $bad
|
||||
}
|
||||
else { $it.ForeColor = [System.Drawing.Color]::Gray } # inchangé
|
||||
[void]$lvRename.Items.Add($it)
|
||||
else { $it.ForeColor = $dim } # inchangé
|
||||
$view.Add($it)
|
||||
}
|
||||
$lvRename.BeginUpdate(); $lvRename.Items.Clear()
|
||||
if ($view.Count -gt 0) { $lvRename.Items.AddRange($view.ToArray()) }
|
||||
$lvRename.EndUpdate()
|
||||
|
||||
$nChange = @($rows | Where-Object { $_.Changed }).Count
|
||||
@@ -3026,7 +3104,7 @@ $timer.Add_Tick({
|
||||
if ($script:Handle -and $script:Handle.IsCompleted) {
|
||||
$timer.Stop()
|
||||
try {
|
||||
$result = $script:PowerShell.EndInvoke($script:Handle)
|
||||
$null = $script:PowerShell.EndInvoke($script:Handle)
|
||||
$scan = $script:Shared.Result
|
||||
if ($scan) { Show-Results -Scan $scan }
|
||||
else { $statusLbl.Text = 'Analyse terminée mais aucune donnée renvoyée.' }
|
||||
@@ -3042,7 +3120,8 @@ $timer.Add_Tick({
|
||||
$script:PowerShell = $null; $script:Runspace = $null; $script:Handle = $null
|
||||
$script:ScanGrantsPending = $false
|
||||
$progressBar.Visible = $false
|
||||
$btnScan.Enabled = $true; $btnAdd.Enabled = $true; $btnRemove.Enabled = $true
|
||||
$btnScan.Enabled = $true; $btnAdd.Enabled = $true
|
||||
$btnRemove.Enabled = ($lstFolders.SelectedIndices.Count -gt 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3067,7 +3146,8 @@ $exportTimer.Add_Tick({
|
||||
if ($script:ExportRunspace) { $script:ExportRunspace.Close(); $script:ExportRunspace.Dispose() }
|
||||
$script:ExportPowerShell = $null; $script:ExportRunspace = $null; $script:ExportHandle = $null
|
||||
$progressBar.Visible = $false
|
||||
$btnScan.Enabled = $true; $btnAdd.Enabled = $true; $btnRemove.Enabled = $true
|
||||
$btnScan.Enabled = $true; $btnAdd.Enabled = $true
|
||||
$btnRemove.Enabled = ($lstFolders.SelectedIndices.Count -gt 0)
|
||||
$btnExport.Enabled = ($null -ne $script:LastScan)
|
||||
}
|
||||
|
||||
@@ -3293,7 +3373,7 @@ function Expand-FilerPickerNode {
|
||||
elseif ($r.Error) { $err = $r.Error }
|
||||
else {
|
||||
$children = @($r.Folders | ForEach-Object {
|
||||
[pscustomobject]@{ Text = (Split-Path -Path $_ -Leaf); Path = $_; Kind = 'folder' }
|
||||
[pscustomobject]@{ Text = ([System.IO.Path]::GetFileName($_)); Path = $_; Kind = 'folder' }
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -3425,47 +3505,47 @@ function Show-FilerFolderPicker {
|
||||
$dlg = New-Object System.Windows.Forms.Form
|
||||
$dlg.Text = if ($SingleSelection) { 'Sélectionner un dossier' } else { 'Sélectionner des dossiers' }
|
||||
$dlg.StartPosition = 'CenterParent'
|
||||
$dlg.ClientSize = New-Object System.Drawing.Size(760, 592)
|
||||
$dlg.MinimumSize = New-Object System.Drawing.Size(640, 500)
|
||||
$dlg.ClientSize = New-Object System.Drawing.Size(780, 620)
|
||||
$dlg.MinimumSize = New-Object System.Drawing.Size(740, 560)
|
||||
$dlg.ShowInTaskbar = $false
|
||||
|
||||
$lblDesc = New-Object System.Windows.Forms.Label
|
||||
$lblDesc.Text = $Description; $lblDesc.Location = '12,10'; $lblDesc.AutoSize = $true
|
||||
|
||||
$txtPath = New-Object System.Windows.Forms.TextBox
|
||||
$txtPath.Location = '12,34'; $txtPath.Size = '618,24'; $txtPath.Anchor = 'Top,Left,Right'
|
||||
$txtPath.Location = '12,34'; $txtPath.Size = '638,24'; $txtPath.Anchor = 'Top,Left,Right'
|
||||
if ($InitialPath) { $txtPath.Text = $InitialPath }
|
||||
|
||||
$btnGo = New-Object System.Windows.Forms.Button
|
||||
$btnGo.Text = 'Ouvrir'; $btnGo.Location = '638,33'; $btnGo.Size = '110,26'; $btnGo.Anchor = 'Top,Right'
|
||||
$btnGo.Text = 'Ouvrir'; $btnGo.Location = '658,33'; $btnGo.Size = '110,26'; $btnGo.Anchor = 'Top,Right'
|
||||
|
||||
$tree = New-Object System.Windows.Forms.TreeView
|
||||
$tree.Location = '12,68'; $tree.Size = '736,436'
|
||||
$tree.Location = '12,68'; $tree.Size = '756,448'
|
||||
$tree.Anchor = 'Top,Bottom,Left,Right'
|
||||
$tree.CheckBoxes = (-not $SingleSelection)
|
||||
$tree.HideSelection = $false
|
||||
$tree.ShowLines = $true
|
||||
|
||||
$btnRefresh = New-Object System.Windows.Forms.Button
|
||||
$btnRefresh.Text = 'Actualiser'; $btnRefresh.Location = '12,512'; $btnRefresh.Size = '110,28'; $btnRefresh.Anchor = 'Bottom,Left'
|
||||
$btnServer = New-Object System.Windows.Forms.Button
|
||||
$btnServer.Text = 'Ajouter un serveur...'; $btnServer.Location = '130,512'; $btnServer.Size = '160,28'; $btnServer.Anchor = 'Bottom,Left'
|
||||
$btnConnect = New-Object System.Windows.Forms.Button
|
||||
$btnConnect.Text = 'Connecter un partage...'; $btnConnect.Location = '298,512'; $btnConnect.Size = '170,28'; $btnConnect.Anchor = 'Bottom,Left'
|
||||
$lblStatus = New-Object System.Windows.Forms.Label
|
||||
$lblStatus.Location = '12,524'; $lblStatus.Size = '756,18'; $lblStatus.Anchor = 'Bottom,Left,Right'
|
||||
$lblStatus.ForeColor = [System.Drawing.Color]::DimGray
|
||||
|
||||
$chkUnc = New-Object System.Windows.Forms.CheckBox
|
||||
$chkUnc.Text = 'Utiliser les chemins UNC pour les lecteurs réseau (recommandé en mode Administrateur)'
|
||||
$chkUnc.Location = '12,544'; $chkUnc.Size = '560,22'; $chkUnc.Checked = $true; $chkUnc.Anchor = 'Bottom,Left'
|
||||
$chkUnc.Location = '12,548'; $chkUnc.AutoSize = $true; $chkUnc.Checked = $true; $chkUnc.Anchor = 'Bottom,Left'
|
||||
|
||||
$lblStatus = New-Object System.Windows.Forms.Label
|
||||
$lblStatus.Location = '12,568'; $lblStatus.Size = '520,18'; $lblStatus.Anchor = 'Bottom,Left'
|
||||
$lblStatus.ForeColor = [System.Drawing.Color]::DimGray
|
||||
$btnRefresh = New-Object System.Windows.Forms.Button
|
||||
$btnRefresh.Text = 'Actualiser'; $btnRefresh.Location = '12,578'; $btnRefresh.Size = '110,30'; $btnRefresh.Anchor = 'Bottom,Left'
|
||||
$btnServer = New-Object System.Windows.Forms.Button
|
||||
$btnServer.Text = 'Ajouter un serveur...'; $btnServer.Location = '130,578'; $btnServer.Size = '160,30'; $btnServer.Anchor = 'Bottom,Left'
|
||||
$btnConnect = New-Object System.Windows.Forms.Button
|
||||
$btnConnect.Text = 'Connecter un partage...'; $btnConnect.Location = '298,578'; $btnConnect.Size = '170,30'; $btnConnect.Anchor = 'Bottom,Left'
|
||||
|
||||
$btnOk = New-Object System.Windows.Forms.Button
|
||||
$btnOk.Text = 'Valider'; $btnOk.Size = '104,30'; $btnOk.Location = '542,552'; $btnOk.Anchor = 'Bottom,Right'
|
||||
$btnOk.Text = 'Valider'; $btnOk.Size = '104,30'; $btnOk.Location = '552,578'; $btnOk.Anchor = 'Bottom,Right'
|
||||
$btnOk.DialogResult = [System.Windows.Forms.DialogResult]::OK
|
||||
$btnCancel = New-Object System.Windows.Forms.Button
|
||||
$btnCancel.Text = 'Annuler'; $btnCancel.Size = '104,30'; $btnCancel.Location = '654,552'; $btnCancel.Anchor = 'Bottom,Right'
|
||||
$btnCancel.Text = 'Annuler'; $btnCancel.Size = '104,30'; $btnCancel.Location = '664,578'; $btnCancel.Anchor = 'Bottom,Right'
|
||||
$btnCancel.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
|
||||
|
||||
$dlg.Controls.AddRange(@($lblDesc, $txtPath, $btnGo, $tree, $btnRefresh, $btnServer,
|
||||
@@ -3612,7 +3692,19 @@ function Show-FilerFolderPicker {
|
||||
$r = Resolve-FilerScanPath -Path $p -PreferUnc:$preferUnc -Inventory $inv
|
||||
if ($r.Path -and $seen.Add($r.Path)) { [void]$out.Add([string]$r.Path) }
|
||||
}
|
||||
return $out.ToArray()
|
||||
|
||||
# Ticking a folder and one of its parents would walk the same tree twice and
|
||||
# count its bytes twice in the totals. Shortest paths first, then drop every
|
||||
# path already covered by one that was kept.
|
||||
$keep = New-Object System.Collections.ArrayList
|
||||
foreach ($p in @($out | Sort-Object -Property Length)) {
|
||||
$nested = $false
|
||||
foreach ($k in $keep) {
|
||||
if ($p.StartsWith(($k.TrimEnd('\') + '\'), [System.StringComparison]::OrdinalIgnoreCase)) { $nested = $true; break }
|
||||
}
|
||||
if (-not $nested) { [void]$keep.Add($p) }
|
||||
}
|
||||
return $keep.ToArray()
|
||||
}
|
||||
|
||||
# ---- events ----
|
||||
@@ -3632,8 +3724,19 @@ $btnAdd.Add_Click({
|
||||
if ($added -gt 0) { $statusLbl.Text = "$added dossier(s) ajouté(s)." }
|
||||
})
|
||||
|
||||
$btnRemove.Add_Click({
|
||||
if ($lstFolders.SelectedIndex -ge 0) { $lstFolders.Items.RemoveAt($lstFolders.SelectedIndex) }
|
||||
# Remove every selected row, highest index first so the earlier ones keep theirs.
|
||||
$removeSelectedFolders = {
|
||||
foreach ($i in @($lstFolders.SelectedIndices | Sort-Object -Descending)) { $lstFolders.Items.RemoveAt($i) }
|
||||
}
|
||||
$btnRemove.Add_Click($removeSelectedFolders)
|
||||
$lstFolders.Add_KeyDown({
|
||||
param($s, $e)
|
||||
if ($e.KeyCode -eq [System.Windows.Forms.Keys]::Delete -and $btnRemove.Enabled) {
|
||||
& $removeSelectedFolders
|
||||
}
|
||||
})
|
||||
$lstFolders.Add_SelectedIndexChanged({
|
||||
if ($btnAdd.Enabled) { $btnRemove.Enabled = ($lstFolders.SelectedIndices.Count -gt 0) }
|
||||
})
|
||||
|
||||
$btnScan.Add_Click({
|
||||
@@ -3797,4 +3900,10 @@ if ($pendingJournals.Count -gt 0) {
|
||||
if ($Path) { foreach ($p in $Path) { [void]$lstFolders.Items.Add($p) } }
|
||||
|
||||
[void]$form.ShowDialog()
|
||||
|
||||
$timer.Stop(); $timer.Dispose()
|
||||
$exportTimer.Stop(); $exportTimer.Dispose()
|
||||
if ($script:PermMenu) { $script:PermMenu.Dispose(); $script:PermMenu = $null }
|
||||
$script:BarTrack.Dispose(); $script:BarFill.Dispose(); $script:BarEdge.Dispose(); $script:BarSel.Dispose()
|
||||
$tip.Dispose()
|
||||
$form.Dispose()
|
||||
|
||||
Reference in New Issue
Block a user