From 1c67cb26301b18263b0c197650032d276a9b25a6 Mon Sep 17 00:00:00 2001 From: Benoit LORAND Date: Thu, 15 Oct 2026 23:45:54 +0200 Subject: [PATCH] =?UTF-8?q?Ajout=20d'un=20profil=20avec=20deux=20partition?= =?UTF-8?q?s=20Correction=20de=20la=20mise=20en=20page=20et=20ajout=20de?= =?UTF-8?q?=20la=20demande=20du=20nom=20d'h=C3=B4te?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DeployCenter.ps1 | 232 +++++++++----- Modules/UI.psm1 | 299 +++++++++++++++++- Partitioning/Profiles/UEFI-GPT-WinRE-C-D.psd1 | 50 +++ .../Profiles/UEFI-GPT-WinRE-before-C.psd1 | 1 + README.md | 16 +- 5 files changed, 519 insertions(+), 79 deletions(-) create mode 100644 Partitioning/Profiles/UEFI-GPT-WinRE-C-D.psd1 diff --git a/DeployCenter.ps1 b/DeployCenter.ps1 index 8a7947d..9e9a846 100644 --- a/DeployCenter.ps1 +++ b/DeployCenter.ps1 @@ -123,6 +123,11 @@ for ($profileIndex = 0; $profileIndex -lt $script:PartitionProfiles.Count; $prof } } $script:SelectedPartitionProfile = $script:PartitionProfiles[$script:SelectedPartitionProfileIndex] +$script:SelectedPartitionSizing = $null +$script:SelectedDiskIndex = $null +$script:SelectedWindowsEdition = $null +$script:RefreshingDiskList = $false +$script:ChangingPartitionProfileSelection = $false # Inventaire de session et UltraVNC portable : tout reste dans le dossier temporaire. $script:InventoryDir = Join-Path $env:TEMP 'DeployCenter' @@ -649,54 +654,40 @@ $script:groupPC.Controls.AddRange(@( $script:form.Controls.Add($script:groupPC) # ============================================================ -# DISQUE PHYSIQUE - SELECTEUR COMPACT +# PREPARATION DU DEPLOIEMENT # ============================================================ -$script:groupDisks = New-GroupBox -Text "Disque et partitionnement" ` - -X $margin -Y 220 -Width $usableWidth -Height 116 +$script:groupDisks = New-GroupBox -Text "Préparation du déploiement" ` + -X $margin -Y 220 -Width $usableWidth -Height 226 -$script:cmbDisk = New-Object System.Windows.Forms.ComboBox -$script:cmbDisk.Name = "cmbDisk" -$script:cmbDisk.Location = New-Object System.Drawing.Point(12, 38) -$script:cmbDisk.Size = New-Object System.Drawing.Size(($usableWidth - 24), 26) -$script:cmbDisk.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Right -$script:cmbDisk.DropDownStyle = [System.Windows.Forms.ComboBoxStyle]::DropDownList -$script:cmbDisk.FlatStyle = [System.Windows.Forms.FlatStyle]::Flat -$script:cmbDisk.BackColor = $script:Theme.PanelAlt -$script:cmbDisk.ForeColor = $script:Theme.Text -$script:cmbDisk.Font = $script:FontSmall -$script:cmbDisk.IntegralHeight = $false -$script:cmbDisk.MaxDropDownItems = 12 - -$partitionProfileSelector = New-DeployCenterPartitionProfileSelector ` +$deploymentControls = New-DeployCenterDeploymentControls ` -Parent $script:groupDisks ` -ProfileNames @($script:PartitionProfiles | ForEach-Object { [string]$_.Name }) ` -Theme $script:Theme ` -Font $script:FontSmall -$script:lblPartitionProfile = $partitionProfileSelector.Label -$script:cmbPartitionProfile = $partitionProfileSelector.ComboBox -$script:cmbPartitionProfile.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Right +$script:cmbDisk = $deploymentControls.DiskComboBox +$script:btnChooseWindows = $deploymentControls.ChooseSystemButton +$script:lblSelectedWindows = $deploymentControls.SelectedSystemLabel +$script:lblPartitionProfile = $deploymentControls.PartitionProfileLabel +$script:cmbPartitionProfile = $deploymentControls.PartitionProfileComboBox +$script:txtHostName = $deploymentControls.HostNameTextBox $script:cmbPartitionProfile.SelectedIndex = $script:SelectedPartitionProfileIndex -$script:groupDisks.Controls.Add($script:cmbDisk) $script:form.Controls.Add($script:groupDisks) function Set-DiskSelectorWidth { if ($null -eq $script:cmbDisk -or $null -eq $script:groupDisks) { return } - $selectorWidth = [Math]::Max(250, $script:groupDisks.ClientSize.Width - 24) - $script:cmbDisk.Location = New-Object System.Drawing.Point(12, 38) - $script:cmbDisk.Size = New-Object System.Drawing.Size($selectorWidth, 26) - Set-DeployCenterPartitionProfileSelectorLayout ` - -Label $script:lblPartitionProfile ` - -ComboBox $script:cmbPartitionProfile ` - -Width $selectorWidth + Set-DeployCenterDeploymentControlsLayout -Parent $script:groupDisks -Controls $deploymentControls } # ============================================================ -# JOURNAL +# JOURNAL DANS UNE FENETRE DEDIEE # ============================================================ -$logY = $script:groupDisks.Bottom + 15 +$logWindow = New-DeployCenterLogWindow -Theme $script:Theme -Font $script:FontRegular +$script:logForm = $logWindow.Form +$script:txtLog = $logWindow.TextBox + $buttonHeight = 38 # En plein ecran, rapprocher les boutons du bas sans empiéter sur le pied de page. # En mode fenetre, conserver la position actuelle. @@ -705,41 +696,16 @@ $buttonY = if ($script:Windowed) { } else { $screenHeight - 42 - $buttonHeight } -$logHeight = [math]::Max(100, $buttonY - $logY - 15) - -$script:groupLog = New-GroupBox -Text "Journal" ` - -X $margin -Y $logY -Width $usableWidth -Height $logHeight - -$script:txtLog = New-Object System.Windows.Forms.TextBox -$script:txtLog.Location = New-Object System.Drawing.Point(12, 28) -$script:txtLog.Size = New-Object System.Drawing.Size( - ($usableWidth - 28), ($logHeight - 44) -) -$script:txtLog.Multiline = $true -$script:txtLog.ReadOnly = $true -$script:txtLog.WordWrap = $false -$script:txtLog.ScrollBars = [System.Windows.Forms.ScrollBars]::Both -$script:txtLog.BackColor = $script:Theme.PanelAlt -$script:txtLog.ForeColor = $script:Theme.Text -$script:txtLog.BorderStyle = [System.Windows.Forms.BorderStyle]::FixedSingle -$script:txtLog.Font = [System.Drawing.Font]::new("Consolas", [single]9, [System.Drawing.FontStyle]::Regular) -$script:groupLog.Controls.Add($script:txtLog) -$script:form.Controls.Add($script:groupLog) - -# ============================================================ -# BOUTONS ACTUALISER ET FERMER +# BOUTONS JOURNAL, ACTUALISER ET FERMER # ============================================================ $buttonWidth = 140 $buttonGap = 12 $closeX = $screenWidth - $margin - $buttonWidth $refreshX = $closeX - $buttonGap - $buttonWidth -$sourcesX = $refreshX - $buttonGap - 170 - -$script:btnSources = New-Button -Text "Sources Windows" ` - -X $sourcesX -Y $buttonY -Width 170 -Height $buttonHeight ` +$script:btnLog = New-Button -Text "Journal" ` + -X $margin -Y $buttonY -Width $buttonWidth -Height $buttonHeight ` -Role "Button" -$script:btnSources.ForeColor = $script:Theme.Text $script:btnRefresh = New-Button -Text "Actualiser" ` -X $refreshX -Y $buttonY -Width $buttonWidth -Height $buttonHeight ` @@ -749,7 +715,7 @@ $script:btnClose = New-Button -Text "Fermer" ` -X $closeX -Y $buttonY -Width $buttonWidth -Height $buttonHeight ` -Role "DangerButton" -$script:form.Controls.Add($script:btnSources) +$script:form.Controls.Add($script:btnLog) $script:form.Controls.Add($script:btnRefresh) $script:form.Controls.Add($script:btnClose) Add-SessionFooter -TargetForm $script:form -ShowPassword $true @@ -776,6 +742,9 @@ function Set-ControlTheme { } elseif ($Control -is [System.Windows.Forms.Label]) { $Control.ForeColor = $script:Theme.Text + if ($Control.Name -eq 'lblSelectedWindows') { + $Control.ForeColor = if ($script:SelectedWindowsEdition) { $script:Theme.Success } else { $script:Theme.Secondary } + } # Le pied de page VNC suit lui aussi la palette du thème actif. if ($Control.Name -eq 'DeployCenterSessionFooter') { $Control.BackColor = $script:Theme.PanelAlt @@ -818,6 +787,7 @@ function Set-DeployCenterTheme { $script:cmbTheme.Refresh() Set-ControlTheme -Control $script:form + Set-ControlTheme -Control $script:logForm # Met a jour le logo si une variante propre au theme existe. Update-DeployCenterLogo -ThemeName $script:CurrentTheme @@ -832,7 +802,8 @@ function Set-DeployCenterTheme { $script:lblWinPE.ForeColor = $script:Theme.Secondary } - Set-DeployCenterButtonStyle -Button $script:btnSources -Theme $script:Theme -Role 'Button' + Set-DeployCenterButtonStyle -Button $script:btnChooseWindows -Theme $script:Theme -Role 'Button' + Set-DeployCenterButtonStyle -Button $script:btnLog -Theme $script:Theme -Role 'Button' Set-DeployCenterButtonStyle -Button $script:btnRefresh -Theme $script:Theme -Role 'PrimaryButton' Set-DeployCenterButtonStyle -Button $script:btnClose -Theme $script:Theme -Role 'DangerButton' # Actualise explicitement le pied de page principal lors du changement de thème. @@ -846,7 +817,6 @@ function Set-DeployCenterTheme { $script:cmbTheme.Invalidate() $script:groupPC.Invalidate() $script:groupDisks.Invalidate() - $script:groupLog.Invalidate() $script:cmbDisk.BackColor = $script:Theme.PanelAlt $script:cmbDisk.ForeColor = $script:Theme.Text $script:cmbDisk.Invalidate() @@ -1620,6 +1590,8 @@ function Show-WindowsSourcesDialog { $script:SelectedWindowsEdition = $chosenEdition $lblStatus.Text = "Édition sélectionnée : $($chosenEdition.Name) (index $($chosenEdition.Index))." Write-Log "Edition selectionnee : $($chosenEdition.Name), index $($chosenEdition.Index), image $imagePath" 'SUCCESS' + $dlg.DialogResult = [System.Windows.Forms.DialogResult]::OK + $dlg.Close() } else { $lblStatus.Text = "$($editions.Count) édition(s) trouvée(s) dans $selectedName. Aucune nouvelle édition sélectionnée." } @@ -1635,6 +1607,12 @@ function Show-WindowsSourcesDialog { } }) + $list.Add_DoubleClick({ + if ($list.SelectedItems.Count -gt 0 -and $btnEditions.Enabled) { + $btnEditions.PerformClick() + } + }) + $btnScan.Add_Click({ $btnScan.Enabled = $false $dlg.Cursor = [System.Windows.Forms.Cursors]::WaitCursor @@ -1695,10 +1673,12 @@ function Update-HardwareView { $script:lblWinPE.ForeColor = $script:Theme.Secondary } + $script:HardwareInventory = $info $previousDiskIndex = $null if ($null -ne $script:cmbDisk.SelectedItem -and $null -ne $script:cmbDisk.SelectedItem.Tag) { $previousDiskIndex = [string]$script:cmbDisk.SelectedItem.Tag } + $script:RefreshingDiskList = $true $script:cmbDisk.BeginUpdate() try { $script:cmbDisk.Items.Clear() @@ -1721,7 +1701,9 @@ function Update-HardwareView { } finally { $script:cmbDisk.EndUpdate() + $script:RefreshingDiskList = $false } + $script:SelectedDiskIndex = if ($script:cmbDisk.SelectedIndex -ge 0) { [string]$script:cmbDisk.SelectedItem.Tag } else { $null } Write-DeployInventory -HardwareInfo $info @@ -1746,6 +1728,51 @@ function Update-HardwareView { } } +function Get-SelectedDeployDiskInfo { + if ($null -eq $script:cmbDisk.SelectedItem -or $null -eq $script:HardwareInventory) { return $null } + $diskIndex = [int]$script:cmbDisk.SelectedItem.Tag + $matches = @($script:HardwareInventory.Disks | Where-Object { [int]$_.Index -eq $diskIndex } | Select-Object -First 1) + if ($matches.Count -gt 0) { return $matches[0] } + return $null +} + +function Get-PartitionProfileSizingSelection { + param( + [Parameter(Mandatory=$true)][System.Collections.IDictionary]$Profile, + [Parameter(Mandatory=$true)]$DiskInfo, + [System.Collections.IDictionary]$InitialValues = @{} + ) + + $capacity = Get-DeployCenterPartitionSizingCapacity -Profile $Profile -DiskSizeBytes ([long]$DiskInfo.SizeBytes) + return Show-DeployCenterPartitionSizingDialog ` + -Profile $Profile ` + -AvailableGB $capacity.UsableGB ` + -Theme $script:Theme ` + -Font $script:FontRegular ` + -Owner $script:form ` + -InitialValues $InitialValues +} + +function Set-PartitionProfileSizingSelection { + param( + [Parameter(Mandatory=$true)][System.Collections.IDictionary]$Profile, + [Parameter(Mandatory=$true)]$DiskInfo, + [System.Collections.IDictionary]$InitialValues = @{} + ) + + $selection = Get-PartitionProfileSizingSelection -Profile $Profile -DiskInfo $DiskInfo -InitialValues $InitialValues + if ($null -eq $selection) { return $false } + $script:SelectedPartitionSizing = [pscustomobject]@{ + ProfileId = [string]$Profile.Id + DiskIndex = [int]$DiskInfo.Index + Inputs = $selection.Inputs + RemainderRole = $selection.RemainderRole + RemainderGB = [double]$selection.RemainderGB + AvailableGB = [double]$selection.AvailableGB + } + return $true +} + # ============================================================ # MISE EN PAGE ADAPTATIVE # Les grandes zones suivent la fenetre ; les marges et hauteurs de controles restent fixes. @@ -1767,28 +1794,21 @@ function Update-DeployCenterLayout { $script:groupPC.Location = New-Object System.Drawing.Point($m, 100) $script:groupPC.Size = New-Object System.Drawing.Size($contentWidth, 105) $script:groupDisks.Location = New-Object System.Drawing.Point($m, 220) - $script:groupDisks.Size = New-Object System.Drawing.Size($contentWidth, 116) + $script:groupDisks.Size = New-Object System.Drawing.Size($contentWidth, 226) Set-DiskSelectorWidth - # Le journal absorbe l'espace vertical restant au-dessus des actions et du pied de page. $footerHeight = 26 $buttonHeightLocal = 38 $buttonYLocal = $h - $footerHeight - 14 - $buttonHeightLocal - $logYLocal = $script:groupDisks.Bottom + 14 - $logHeightLocal = [Math]::Max(90, $buttonYLocal - $logYLocal - 12) - $script:groupLog.Location = New-Object System.Drawing.Point($m, $logYLocal) - $script:groupLog.Size = New-Object System.Drawing.Size($contentWidth, $logHeightLocal) - $script:txtLog.Location = New-Object System.Drawing.Point(12, 28) - $script:txtLog.Size = New-Object System.Drawing.Size([Math]::Max(250, $script:groupLog.ClientSize.Width - 24), [Math]::Max(50, $script:groupLog.ClientSize.Height - 40)) $buttonWidthLocal = 140 $buttonGapLocal = 12 $script:btnClose.Size = New-Object System.Drawing.Size($buttonWidthLocal, $buttonHeightLocal) $script:btnRefresh.Size = New-Object System.Drawing.Size($buttonWidthLocal, $buttonHeightLocal) - $script:btnSources.Size = New-Object System.Drawing.Size(170, $buttonHeightLocal) + $script:btnLog.Size = New-Object System.Drawing.Size($buttonWidthLocal, $buttonHeightLocal) $script:btnClose.Location = New-Object System.Drawing.Point([Math]::Max($m, $w - $m - $buttonWidthLocal), $buttonYLocal) $script:btnRefresh.Location = New-Object System.Drawing.Point([Math]::Max($m, $script:btnClose.Left - $buttonGapLocal - $buttonWidthLocal), $buttonYLocal) - $script:btnSources.Location = New-Object System.Drawing.Point([Math]::Max($m, $script:btnRefresh.Left - $buttonGapLocal - 170), $buttonYLocal) + $script:btnLog.Location = New-Object System.Drawing.Point($m, $buttonYLocal) Set-DiskSelectorWidth } @@ -1796,8 +1816,22 @@ function Update-DeployCenterLayout { # EVENEMENTS # ============================================================ -$script:btnSources.Add_Click({ +$script:btnChooseWindows.Add_Click({ Show-WindowsSourcesDialog + if ($script:SelectedWindowsEdition) { + $edition = $script:SelectedWindowsEdition + $sourceDescription = if ($edition.ImagePath) { [string]$edition.ImagePath } else { [string]$edition.SourcePath } + $script:lblSelectedWindows.Text = "{0} (index {1}) — {2}" -f $edition.Name, $edition.Index, $sourceDescription + $script:lblSelectedWindows.ForeColor = $script:Theme.Success + $script:lblSelectedWindows.Name = 'lblSelectedWindows' + $script:cmbPartitionProfile.Enabled = $true + } +}) + +$script:btnLog.Add_Click({ + if ($script:logForm.IsDisposed) { return } + if (-not $script:logForm.Visible) { $script:logForm.Show($script:form) } + $script:logForm.BringToFront() }) $script:btnRefresh.Add_Click({ @@ -1819,14 +1853,64 @@ $script:cmbDisk.Add_SizeChanged({ }) $script:cmbPartitionProfile.Add_SelectedIndexChanged({ + if ($script:ChangingPartitionProfileSelection) { return } $selectedIndex = $script:cmbPartitionProfile.SelectedIndex if ($selectedIndex -ge 0 -and $selectedIndex -lt $script:PartitionProfiles.Count) { + $candidateProfile = $script:PartitionProfiles[$selectedIndex] + $sizeInputs = @($candidateProfile.Partitions | Where-Object { $_.Size.Policy -eq 'UserInputGB' }) + if ($sizeInputs.Count -gt 0) { + $diskInfo = Get-SelectedDeployDiskInfo + $initialValues = @{} + if ($script:SelectedPartitionSizing -and $script:SelectedPartitionSizing.ProfileId -eq $candidateProfile.Id) { + $initialValues = $script:SelectedPartitionSizing.Inputs + } + if ($null -eq $diskInfo) { + Show-DeployCenterMessage -Message "Sélectionne d’abord un disque physique." -Title 'Partitionnement' -Icon Information -Owner $script:form -Theme $script:Theme -Font $script:FontRegular + } + if ($null -eq $diskInfo -or -not (Set-PartitionProfileSizingSelection -Profile $candidateProfile -DiskInfo $diskInfo -InitialValues $initialValues)) { + $script:ChangingPartitionProfileSelection = $true + try { $script:cmbPartitionProfile.SelectedIndex = $script:SelectedPartitionProfileIndex } + finally { $script:ChangingPartitionProfileSelection = $false } + return + } + } else { + $script:SelectedPartitionSizing = $null + } + $script:SelectedPartitionProfileIndex = $selectedIndex - $script:SelectedPartitionProfile = $script:PartitionProfiles[$selectedIndex] - Write-Log "Profil de partitionnement sélectionné : $($script:SelectedPartitionProfile.Name)" 'INFO' + $script:SelectedPartitionProfile = $candidateProfile + Write-Log "Profil de partitionnement sélectionné : $($candidateProfile.Name)" 'INFO' + if ($script:SelectedPartitionSizing) { + foreach ($role in $script:SelectedPartitionSizing.Inputs.Keys) { + Write-Log "Taille saisie pour $role : $($script:SelectedPartitionSizing.Inputs[$role]) Go ; espace restant : $($script:SelectedPartitionSizing.RemainderGB) Go" 'INFO' + } + } } }) +$script:cmbDisk.Add_SelectedIndexChanged({ + if ($script:RefreshingDiskList -or $script:ChangingPartitionProfileSelection) { return } + $diskInfo = Get-SelectedDeployDiskInfo + if ($null -eq $diskInfo) { $script:SelectedDiskIndex = $null; return } + + $profileSizeInputs = @($script:SelectedPartitionProfile.Partitions | Where-Object { $_.Size.Policy -eq 'UserInputGB' }) + if ($profileSizeInputs.Count -gt 0) { + $initialValues = if ($script:SelectedPartitionSizing -and $script:SelectedPartitionSizing.ProfileId -eq $script:SelectedPartitionProfile.Id) { + $script:SelectedPartitionSizing.Inputs + } else { @{} } + if (-not (Set-PartitionProfileSizingSelection -Profile $script:SelectedPartitionProfile -DiskInfo $diskInfo -InitialValues $initialValues)) { + $script:RefreshingDiskList = $true + try { + for ($i = 0; $i -lt $script:cmbDisk.Items.Count; $i++) { + if ([string]$script:cmbDisk.Items[$i].Tag -eq [string]$script:SelectedDiskIndex) { $script:cmbDisk.SelectedIndex = $i; break } + } + } finally { $script:RefreshingDiskList = $false } + return + } + } + $script:SelectedDiskIndex = [string]$diskInfo.Index +}) + $script:form.Add_SizeChanged({ Update-DeployCenterLayout }) diff --git a/Modules/UI.psm1 b/Modules/UI.psm1 index 463dea5..7167f01 100644 --- a/Modules/UI.psm1 +++ b/Modules/UI.psm1 @@ -24,7 +24,7 @@ function New-DeployCenterComboBox { [Parameter(Mandatory=$true)][string]$Name, [Parameter(Mandatory=$true)][System.Collections.IDictionary]$Theme, [Parameter(Mandatory=$true)][System.Drawing.Font]$Font, - [Parameter(Mandatory=$true)][string[]]$Items + [Parameter(Mandatory=$true)][AllowEmptyCollection()][string[]]$Items ) $comboBox = New-Object System.Windows.Forms.ComboBox @@ -78,11 +78,302 @@ function Set-DeployCenterPartitionProfileSelectorLayout { [Parameter(Mandatory=$true)][System.Windows.Forms.ComboBox]$ComboBox, [Parameter(Mandatory=$true)][int]$Width ) - $Label.Location = New-Object System.Drawing.Point(12, 66) - $ComboBox.Location = New-Object System.Drawing.Point(12, 84) + $Label.Location = New-Object System.Drawing.Point(12, 139) + $ComboBox.Location = New-Object System.Drawing.Point(12, 157) $ComboBox.Size = New-Object System.Drawing.Size([Math]::Max(250, $Width), 26) } +function New-DeployCenterDeploymentControls { + param( + [Parameter(Mandatory=$true)][System.Windows.Forms.GroupBox]$Parent, + [Parameter(Mandatory=$true)][string[]]$ProfileNames, + [Parameter(Mandatory=$true)][System.Collections.IDictionary]$Theme, + [Parameter(Mandatory=$true)][System.Drawing.Font]$Font + ) + + $diskLabel = New-Object System.Windows.Forms.Label + $diskLabel.Text = 'Disque physique' + $diskLabel.Location = New-Object System.Drawing.Point(12, 20) + $diskLabel.Size = New-Object System.Drawing.Size(220, 18) + $diskLabel.Font = $Font + $diskLabel.ForeColor = $Theme.Text + $diskLabel.BackColor = [System.Drawing.Color]::Transparent + + $diskCombo = New-DeployCenterComboBox -Name 'cmbDisk' -Theme $Theme -Font $Font -Items @() + $diskCombo.Location = New-Object System.Drawing.Point(12, 39) + $diskCombo.Size = New-Object System.Drawing.Size([Math]::Max(250, $Parent.ClientSize.Width - 24), 26) + $diskCombo.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Right + + $systemLabel = New-Object System.Windows.Forms.Label + $systemLabel.Text = 'Système à installer' + $systemLabel.Location = New-Object System.Drawing.Point(12, 71) + $systemLabel.Size = New-Object System.Drawing.Size(240, 18) + $systemLabel.Font = $Font + $systemLabel.ForeColor = $Theme.Text + $systemLabel.BackColor = [System.Drawing.Color]::Transparent + + $chooseSystemButton = New-Object System.Windows.Forms.Button + $chooseSystemButton.Name = 'btnChooseWindows' + $chooseSystemButton.Text = 'Choisir…' + $chooseSystemButton.Size = New-Object System.Drawing.Size(132, 28) + $chooseSystemButton.Location = New-Object System.Drawing.Point([Math]::Max(12, $Parent.ClientSize.Width - 144), 67) + $chooseSystemButton.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Right + Set-DeployCenterButtonStyle -Button $chooseSystemButton -Theme $Theme -Role 'Button' + + $selectedSystemLabel = New-Object System.Windows.Forms.Label + $selectedSystemLabel.Name = 'lblSelectedWindows' + $selectedSystemLabel.Text = 'Aucun système sélectionné' + $selectedSystemLabel.Location = New-Object System.Drawing.Point(12, 98) + $selectedSystemLabel.Size = New-Object System.Drawing.Size([Math]::Max(250, $Parent.ClientSize.Width - 24), 22) + $selectedSystemLabel.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Right + $selectedSystemLabel.Font = $Font + $selectedSystemLabel.AutoEllipsis = $true + $selectedSystemLabel.ForeColor = $Theme.Secondary + $selectedSystemLabel.BackColor = [System.Drawing.Color]::Transparent + + $profileSelector = New-DeployCenterPartitionProfileSelector -Parent $Parent -ProfileNames $ProfileNames -Theme $Theme -Font $Font + $profileSelector.ComboBox.Enabled = $false + + $hostLabel = New-Object System.Windows.Forms.Label + $hostLabel.Text = "Nom d’hôte" + $hostLabel.Location = New-Object System.Drawing.Point(12, 190) + $hostLabel.Size = New-Object System.Drawing.Size(220, 24) + $hostLabel.Font = $Font + $hostLabel.ForeColor = $Theme.Text + $hostLabel.BackColor = [System.Drawing.Color]::Transparent + + $hostTextBox = New-Object System.Windows.Forms.TextBox + $hostTextBox.Name = 'txtHostName' + $hostTextBox.Location = New-Object System.Drawing.Point([Math]::Max(244, $Parent.ClientSize.Width - 202), 187) + $hostTextBox.Size = New-Object System.Drawing.Size(190, 26) + $hostTextBox.MaxLength = 15 + $hostTextBox.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Right + $hostTextBox.BackColor = $Theme.PanelAlt + $hostTextBox.ForeColor = $Theme.Text + $hostTextBox.Font = $Font + + $Parent.Controls.AddRange(@($diskLabel, $diskCombo, $systemLabel, $chooseSystemButton, $selectedSystemLabel, $hostLabel, $hostTextBox)) + return [pscustomobject]@{ + DiskComboBox = $diskCombo + ChooseSystemButton = $chooseSystemButton + SelectedSystemLabel = $selectedSystemLabel + PartitionProfileLabel = $profileSelector.Label + PartitionProfileComboBox = $profileSelector.ComboBox + HostNameTextBox = $hostTextBox + } +} + +function Set-DeployCenterDeploymentControlsLayout { + param( + [Parameter(Mandatory=$true)][System.Windows.Forms.GroupBox]$Parent, + [Parameter(Mandatory=$true)]$Controls + ) + $width = [Math]::Max(250, $Parent.ClientSize.Width - 24) + $Controls.DiskComboBox.Size = New-Object System.Drawing.Size($width, 26) + $Controls.ChooseSystemButton.Location = New-Object System.Drawing.Point([Math]::Max(12, $Parent.ClientSize.Width - 144), 67) + $Controls.SelectedSystemLabel.Size = New-Object System.Drawing.Size($width, 22) + Set-DeployCenterPartitionProfileSelectorLayout -Label $Controls.PartitionProfileLabel -ComboBox $Controls.PartitionProfileComboBox -Width $width + $Controls.HostNameTextBox.Location = New-Object System.Drawing.Point([Math]::Max(244, $Parent.ClientSize.Width - 202), 187) + $Controls.HostNameTextBox.Size = New-Object System.Drawing.Size(190, 26) +} + +function New-DeployCenterLogWindow { + param( + [Parameter(Mandatory=$true)][System.Collections.IDictionary]$Theme, + [Parameter(Mandatory=$true)][System.Drawing.Font]$Font + ) + $form = New-Object System.Windows.Forms.Form + $form.Text = 'Journal DeployCenter' + $form.StartPosition = 'CenterParent' + $form.Size = New-Object System.Drawing.Size(900, 500) + $form.MinimumSize = New-Object System.Drawing.Size(560, 300) + $form.ShowInTaskbar = $false + $form.BackColor = $Theme.Background + $form.ForeColor = $Theme.Text + $form.Font = $Font + + $textBox = New-Object System.Windows.Forms.TextBox + $textBox.Name = 'txtLog' + $textBox.Location = New-Object System.Drawing.Point(12, 12) + $textBox.Size = New-Object System.Drawing.Size(860, 440) + $textBox.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Right + $textBox.Multiline = $true + $textBox.ReadOnly = $true + $textBox.WordWrap = $false + $textBox.ScrollBars = [System.Windows.Forms.ScrollBars]::Both + $textBox.BackColor = $Theme.PanelAlt + $textBox.ForeColor = $Theme.Text + $textBox.BorderStyle = [System.Windows.Forms.BorderStyle]::FixedSingle + $textBox.Font = [System.Drawing.Font]::new('Consolas', [single]9, [System.Drawing.FontStyle]::Regular) + $form.Controls.Add($textBox) + + $form.Add_FormClosing({ if ($_.CloseReason -eq [System.Windows.Forms.CloseReason]::UserClosing) { $_.Cancel = $true; $form.Hide() } }.GetNewClosure()) + return [pscustomobject]@{ Form = $form; TextBox = $textBox } +} + +function Show-DeployCenterPartitionSizingDialog { + param( + [Parameter(Mandatory=$true)][System.Collections.IDictionary]$Profile, + [Parameter(Mandatory=$true)][double]$AvailableGB, + [Parameter(Mandatory=$true)][System.Collections.IDictionary]$Theme, + [Parameter(Mandatory=$true)][System.Drawing.Font]$Font, + [System.Windows.Forms.Form]$Owner = $null, + [System.Collections.IDictionary]$InitialValues = @{} + ) + + $inputs = @($Profile.Partitions | Where-Object { [string]$_.Size.Policy -eq 'UserInputGB' }) + $remainder = @($Profile.Partitions | Where-Object { [string]$_.Size.Policy -eq 'RemainderAfterInputs' })[0] + if ($inputs.Count -eq 0 -or $null -eq $remainder) { return $null } + + $minimumRemainder = [double]$remainder.Size.MinimumGB + $minimumInputs = 0.0 + foreach ($inputDefinition in $inputs) { $minimumInputs += [double]$inputDefinition.Size.MinimumGB } + if ($AvailableGB -lt ($minimumInputs + $minimumRemainder)) { + Show-DeployCenterMessage ` + -Message "L'espace disponible sur ce disque est insuffisant pour le profil '$($Profile.Name)'." ` + -Title 'Partitionnement impossible' -Icon Warning -Owner $Owner -Theme $Theme -Font $Font + return $null + } + + $dialog = New-Object System.Windows.Forms.Form + $dialog.Text = 'Tailles des partitions' + $dialog.StartPosition = if ($Owner) { 'CenterParent' } else { 'CenterScreen' } + $dialog.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedDialog + $dialog.ShowInTaskbar = $false + $dialog.MinimizeBox = $false + $dialog.MaximizeBox = $false + $dialog.BackColor = $Theme.Background + $dialog.ForeColor = $Theme.Text + $dialog.Font = $Font + $dialog.ClientSize = New-Object System.Drawing.Size(470, (152 + ($inputs.Count * 42))) + + $capacityLabel = New-Object System.Windows.Forms.Label + $capacityLabel.Location = New-Object System.Drawing.Point(18, 16) + $capacityLabel.Size = New-Object System.Drawing.Size(430, 34) + $capacityLabel.Text = "Capacité estimée après réservation EFI, MSR et WinRE : {0:N1} Go.`r`nLa taille WinRE exacte sera recalculée lors du partitionnement." -f $AvailableGB + $capacityLabel.ForeColor = $Theme.Text + $capacityLabel.BackColor = [System.Drawing.Color]::Transparent + $capacityLabel.Font = $Font + $dialog.Controls.Add($capacityLabel) + + $inputControls = [System.Collections.Generic.List[object]]::new() + for ($i = 0; $i -lt $inputs.Count; $i++) { + $definition = $inputs[$i] + $rowY = 60 + ($i * 42) + + $label = New-Object System.Windows.Forms.Label + $label.Location = New-Object System.Drawing.Point(18, ($rowY + 4)) + $label.Size = New-Object System.Drawing.Size(250, 26) + $label.Text = [string]$definition.Size.Label + $label.ForeColor = $Theme.Text + $label.BackColor = [System.Drawing.Color]::Transparent + $label.Font = $Font + $dialog.Controls.Add($label) + + $otherMinimums = 0.0 + for ($j = 0; $j -lt $inputs.Count; $j++) { + if ($j -ne $i) { $otherMinimums += [double]$inputs[$j].Size.MinimumGB } + } + $maximum = [math]::Floor($AvailableGB - $minimumRemainder - $otherMinimums) + $minimum = [double]$definition.Size.MinimumGB + $numeric = New-Object System.Windows.Forms.NumericUpDown + $numeric.Location = New-Object System.Drawing.Point(292, $rowY) + $numeric.Size = New-Object System.Drawing.Size(150, 28) + $numeric.Minimum = [decimal]$minimum + $numeric.Maximum = [decimal]$maximum + $numeric.DecimalPlaces = 0 + $numeric.Increment = 1 + $numeric.BackColor = $Theme.PanelAlt + $numeric.ForeColor = $Theme.Text + $numeric.Font = $Font + + $initialValue = $null + if ($InitialValues.Contains([string]$definition.Role)) { $initialValue = [double]$InitialValues[[string]$definition.Role] } + if ($null -eq $initialValue) { + $initialValue = [math]::Floor($AvailableGB * ([double]$definition.Size.DefaultPercent / 100)) + } + $initialValue = [math]::Max($minimum, [math]::Min($maximum, $initialValue)) + $numeric.Value = [decimal]$initialValue + $inputControls.Add([pscustomobject]@{ Role = [string]$definition.Role; Control = $numeric }) + $dialog.Controls.Add($numeric) + } + + $remainderLabel = New-Object System.Windows.Forms.Label + $remainderLabel.Location = New-Object System.Drawing.Point(18, (62 + ($inputs.Count * 42))) + $remainderLabel.Size = New-Object System.Drawing.Size(430, 26) + $remainderLabel.ForeColor = $Theme.Secondary + $remainderLabel.BackColor = [System.Drawing.Color]::Transparent + $remainderLabel.Font = $Font + $dialog.Controls.Add($remainderLabel) + + $buttonPanel = New-Object System.Windows.Forms.Panel + $buttonPanel.Dock = [System.Windows.Forms.DockStyle]::Bottom + $buttonPanel.Height = 58 + $buttonPanel.BackColor = $Theme.PanelAlt + $dialog.Controls.Add($buttonPanel) + + $cancelButton = New-Object System.Windows.Forms.Button + $cancelButton.Text = 'Annuler' + $cancelButton.Location = New-Object System.Drawing.Point(258, 10) + $cancelButton.Size = New-Object System.Drawing.Size(92, 36) + $cancelButton.Font = $Font + $cancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel + Set-DeployCenterButtonStyle -Button $cancelButton -Theme $Theme -Role 'Button' + $buttonPanel.Controls.Add($cancelButton) + + $validateButton = New-Object System.Windows.Forms.Button + $validateButton.Text = 'Valider' + $validateButton.Location = New-Object System.Drawing.Point(358, 10) + $validateButton.Size = New-Object System.Drawing.Size(92, 36) + $validateButton.Font = $Font + Set-DeployCenterButtonStyle -Button $validateButton -Theme $Theme -Role 'PrimaryButton' + $buttonPanel.Controls.Add($validateButton) + + $dialog.Tag = [pscustomobject]@{ InputControls = $inputControls; AvailableGB = $AvailableGB } + $updateRemainder = { + $sum = 0.0 + foreach ($inputControl in $dialog.Tag.InputControls) { + $inputValue = [double]$inputControl.Control.Value + $typedValue = 0.0 + if ([double]::TryParse([string]$inputControl.Control.Text, [ref]$typedValue)) { $inputValue = $typedValue } + $sum += $inputValue + } + $remainingGB = $dialog.Tag.AvailableGB - $sum + $remainderDrive = [string]$remainder.DriveLetter + $remainderName = if ($remainderDrive) { "$remainderDrive`:" } else { [string]$remainder.Name } + $remainderLabel.Text = "{0} : {1:N1} Go (espace restant)" -f $remainderName, $remainingGB + $remainderLabel.ForeColor = if ($remainingGB -lt $minimumRemainder) { $Theme.Danger } else { $Theme.Secondary } + $validateButton.Enabled = ($remainingGB -ge $minimumRemainder) + }.GetNewClosure() + foreach ($inputControl in $inputControls) { + $inputControl.Control.Add_ValueChanged($updateRemainder) + $inputControl.Control.Add_TextChanged($updateRemainder) + } + & $updateRemainder + + $validateButton.Add_Click({ + $inputValues = @{} + foreach ($inputControl in $dialog.Tag.InputControls) { $inputValues[$inputControl.Role] = [double]$inputControl.Control.Value } + $sum = 0.0 + foreach ($value in $inputValues.Values) { $sum += [double]$value } + $dialog.Tag = [pscustomobject]@{ + Inputs = $inputValues + RemainderRole = [string]$remainder.Role + RemainderGB = [math]::Round(($dialog.Tag.AvailableGB - $sum), 1) + AvailableGB = $dialog.Tag.AvailableGB + } + $dialog.DialogResult = [System.Windows.Forms.DialogResult]::OK + $dialog.Close() + }) + $dialog.AcceptButton = $validateButton + $dialog.CancelButton = $cancelButton + + if ($Owner) { $result = $dialog.ShowDialog($Owner) } else { $result = $dialog.ShowDialog() } + $selection = if ($result -eq [System.Windows.Forms.DialogResult]::OK) { $dialog.Tag } else { $null } + $dialog.Dispose() + return $selection +} + function Show-DeployCenterMessage { param( [Parameter(Mandatory=$true)][string]$Message, @@ -160,4 +451,4 @@ function Show-DeployCenterMessage { $messageForm.Dispose() } -Export-ModuleMember -Function Set-DeployCenterButtonStyle, New-DeployCenterComboBox, Set-DeployCenterComboBoxTheme, New-DeployCenterPartitionProfileSelector, Set-DeployCenterPartitionProfileSelectorLayout, Show-DeployCenterMessage +Export-ModuleMember -Function Set-DeployCenterButtonStyle, New-DeployCenterComboBox, Set-DeployCenterComboBoxTheme, New-DeployCenterPartitionProfileSelector, Set-DeployCenterPartitionProfileSelectorLayout, New-DeployCenterDeploymentControls, Set-DeployCenterDeploymentControlsLayout, New-DeployCenterLogWindow, Show-DeployCenterPartitionSizingDialog, Show-DeployCenterMessage diff --git a/Partitioning/Profiles/UEFI-GPT-WinRE-C-D.psd1 b/Partitioning/Profiles/UEFI-GPT-WinRE-C-D.psd1 new file mode 100644 index 0000000..f14c5fc --- /dev/null +++ b/Partitioning/Profiles/UEFI-GPT-WinRE-C-D.psd1 @@ -0,0 +1,50 @@ +@{ + Id = 'UEFI-GPT-WinRE-C-D' + Name = 'UEFI / GPT — WinRE, C: et D:' + Description = "Crée EFI, MSR et WinRE, puis C: selon la taille choisie et D: avec l’espace restant." + Default = $false + Firmware = 'UEFI' + PartitionStyle = 'GPT' + Partitions = @( + @{ + Name = 'EFI' + Role = 'System' + FileSystem = 'FAT32' + GptTypeId = 'C12A7328-F81F-11D2-BA4B-00A0C93EC93B' + Size = @{ Policy = 'FixedMB'; Value = 300 } + } + @{ + Name = 'MSR' + Role = 'Reserved' + FileSystem = $null + GptTypeId = 'E3C9E316-0B5C-4DB8-817D-F92DF00215AE' + Size = @{ Policy = 'FixedMB'; Value = 16 } + } + @{ + Name = 'WinRE' + Role = 'Recovery' + FileSystem = 'NTFS' + GptTypeId = 'DE94BBA4-06D1-4D40-A16A-BFD50179D6AC' + GptAttributes = '0x8000000000000001' + Size = @{ Policy = 'ImageSizePlusFreeSpace'; FreeSpaceMB = 250; PreviewMB = 1024 } + } + @{ + Name = 'Windows' + Role = 'Windows' + FileSystem = 'NTFS' + GptTypeId = 'EBD0A0A2-B9E5-4433-87C0-68B6B72699C7' + DriveLetter = 'C' + DriveLetterAfterReboot = 'C' + Size = @{ Policy = 'UserInputGB'; MinimumGB = 40; DefaultPercent = 60; Label = 'Taille de C:' } + } + @{ + Name = 'Data' + Role = 'Data' + FileSystem = 'NTFS' + GptTypeId = 'EBD0A0A2-B9E5-4433-87C0-68B6B72699C7' + DriveLetter = 'D' + DriveLetterAfterReboot = 'D' + Size = @{ Policy = 'RemainderAfterInputs'; MinimumGB = 1; Label = 'Taille de D: (espace restant)' } + } + ) +} diff --git a/Partitioning/Profiles/UEFI-GPT-WinRE-before-C.psd1 b/Partitioning/Profiles/UEFI-GPT-WinRE-before-C.psd1 index c9fc6fb..e87143e 100644 --- a/Partitioning/Profiles/UEFI-GPT-WinRE-before-C.psd1 +++ b/Partitioning/Profiles/UEFI-GPT-WinRE-before-C.psd1 @@ -33,6 +33,7 @@ Role = 'Windows' FileSystem = 'NTFS' GptTypeId = 'EBD0A0A2-B9E5-4433-87C0-68B6B72699C7' + DriveLetter = 'C' DriveLetterAfterReboot = 'C' Size = @{ Policy = 'Remaining' } } diff --git a/README.md b/README.md index cc455f4..9a57455 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ La gestion d'UltraVNC est isolée dans `Modules/VNC.psm1`. Le fichier est charg ## Profils de partitionnement -Les fichiers PSD1 de `Partitioning/Profiles` décrivent les schémas de partitionnement comme des données, sans exécuter de code contenu dans les profils. Le profil actuellement disponible est « UEFI / GPT — WinRE avant C: » : EFI (300 Mo), MSR (16 Mo), WinRE (taille de `winre.wim` plus 250 Mo libres), puis Windows sur l’espace restant. Il est marqué comme profil par défaut. La sélection est visible dans l’interface ; le calcul effectif de la taille WinRE et l’exécution du partitionnement ne sont pas encore implémentés. +Les fichiers PSD1 de `Partitioning/Profiles` décrivent les schémas de partitionnement comme des données, sans exécuter de code contenu dans les profils. Le profil par défaut « UEFI / GPT — WinRE avant C: » crée EFI (300 Mo), MSR (16 Mo), WinRE (taille de `winre.wim` plus 250 Mo libres), puis Windows sur l’espace restant. Le profil « UEFI / GPT — WinRE, C: et D: » demande la taille de C: et attribue le reste à D:, dans l’ordre physique C: puis D:. Les propriétés de taille déclarées dans le profil alimentent un formulaire générique. Le profil fournit une estimation de la capacité disponible avec une réserve WinRE de 1 Go ; le calcul effectif de WinRE et l’exécution DiskPart ne sont pas encore implémentés. ## Historique des changements @@ -117,3 +117,17 @@ Les fichiers PSD1 de `Partitioning/Profiles` décrivent les schémas de partitio - Ajout du chargement et de la validation de profils de partitionnement PSD1, ainsi que d’un sélecteur dans l’interface. Le profil UEFI/GPT place EFI, MSR et WinRE avant Windows, qui reçoit l’espace restant ; aucun script DiskPart n’est exécuté à cette étape. - Ajout du BOM UTF-8 aux nouveaux fichiers PowerShell et PSD1 pour préserver les caractères accentués lors de leur lecture par Windows PowerShell 5.1. - Correction de l’apostrophe dans la description du profil UEFI/GPT, qui empêchait le chargement du fichier PSD1. +- Ajout du profil UEFI/GPT avec C: et D:. Les politiques de taille du PSD1 pilotent un formulaire générique : saisie de C: et calcul de D: comme espace restant estimé. Le profil place C: avant D:. +- Correction d’une syntaxe PowerShell incompatible dans les filtres de politique de taille du profil, qui empêchait le lancement du script. +- Correction du message de sélection du disque : son apostrophe typographique fermait prématurément une chaîne PowerShell et bloquait l’analyse du script. +- Correction du placement d’un libellé du formulaire C:/D: : l’expression de coordonnées était interprétée comme un troisième argument du constructeur `Point`. Le profil PSD1 reçoit un BOM UTF-8 et utilise des guillemets doubles autour de sa description, qui contient une apostrophe typographique. +- Le formulaire du profil C:/D: recalcule D: pendant la saisie de C: et accepte désormais une taille minimale de 40 Go pour C:. +- Réorganisation de la première page : sélection du disque, du système, du profil et du nom d’hôte dans le panneau de préparation ; le journal est accessible dans une fenêtre dédiée depuis la barre d’actions. Les nouveaux contrôles et la fenêtre du journal sont construits dans `UI.psm1` et suivent le thème actif. +- Intégration du nouveau panneau de préparation dans la fenêtre principale : le profil est activé après la sélection d’une édition Windows, et le journal est ouvert depuis le bouton placé à gauche des actions principales. +- Le journal reste alimenté par `Logging.psm1` ; `UI.psm1` fournit uniquement sa fenêtre thémée et son contrôle d’affichage. +- Le libellé du système sélectionné conserve la couleur de statut du thème et tronque les chemins longs sans déformer le panneau. +- Le constructeur de ComboBox accepte une collection vide à l’initialisation du sélecteur de disque, ensuite alimentée par l’inventaire matériel. +- Le champ du nom d’hôte est dimensionné pour un nom NetBIOS et limite la saisie à 15 caractères. +- Le champ du nom d’hôte est aligné à droite du panneau avec un espacement réservé après son libellé afin d’éviter tout chevauchement. +- Dans Sources Windows, le double-clic sur une ligne sélectionnée lance aussi l’affichage des éditions, comme le bouton dédié. +- Après validation d’une édition Windows, la fenêtre Sources Windows se ferme pour revenir directement à l’écran principal.