mirror of
https://github.com/Raphire/Win11Debloat.git
synced 2026-10-08 06:26:45 +00:00
Add localization framework for the GUI (en-US baseline) (#764)
Credit to @jmolinap2 for their work in #643, which was used as inspiration.
This commit is contained in:
@@ -142,6 +142,15 @@ function Update-AppsPanelSort {
|
||||
}
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Updates the "N apps selected" status text and shows or hides the app-removal scope section.
|
||||
|
||||
.DESCRIPTION
|
||||
The scope section only makes sense once at least one app is selected, so it's hidden
|
||||
entirely at zero and its description text is refreshed via Update-AppRemovalScopeDescription
|
||||
whenever the selection count changes.
|
||||
#>
|
||||
function Update-AppSelectionStatus {
|
||||
param(
|
||||
[System.Windows.Controls.Panel]$AppsPanel,
|
||||
@@ -158,7 +167,7 @@ function Update-AppSelectionStatus {
|
||||
$selectedCount++
|
||||
}
|
||||
}
|
||||
$AppSelectionStatus.Text = "$selectedCount app(s) selected for removal"
|
||||
$AppSelectionStatus.Text = Get-Translation -Key 'AppSelectionStatusSelected' -Count $selectedCount -FormatArgs @($selectedCount)
|
||||
|
||||
if ($AppRemovalScopeCombo -and $AppRemovalScopeSection -and $AppRemovalScopeDescription) {
|
||||
if ($selectedCount -gt 0) {
|
||||
@@ -189,13 +198,13 @@ function Update-AppRemovalScopeDescription {
|
||||
# Content is the display text and will change once translated; Name is stable.
|
||||
switch ($selectedItem.Name) {
|
||||
"AppRemovalScopeAllUsers" {
|
||||
$AppRemovalScopeDescription.Text = "Apps will be removed for all users and from the Windows image to prevent reinstallation for new users."
|
||||
$AppRemovalScopeDescription.Text = Get-Translation -Key 'AppRemovalScopeDescriptionAllUsers'
|
||||
}
|
||||
"AppRemovalScopeCurrentUser" {
|
||||
$AppRemovalScopeDescription.Text = "Apps will only be removed for the current user."
|
||||
$AppRemovalScopeDescription.Text = Get-Translation -Key 'AppRemovalScopeDescriptionCurrentUser'
|
||||
}
|
||||
"AppRemovalScopeTargetUser" {
|
||||
$AppRemovalScopeDescription.Text = "Apps will only be removed for the specified target user."
|
||||
$AppRemovalScopeDescription.Text = Get-Translation -Key 'AppRemovalScopeDescriptionTargetUser'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -448,9 +457,9 @@ function Add-AppsToMainWindow {
|
||||
$dot.Style = $Window.Resources['AppRecommendationDotStyle']
|
||||
$dot.Fill = switch ($app.Recommendation) { 'safe' { $brushSafe } 'unsafe' { $brushUnsafe } default { $brushDefault } }
|
||||
$dot.ToolTip = switch ($app.Recommendation) {
|
||||
'safe' { '[Recommended] Safe to remove for most users' }
|
||||
'unsafe' { '[Not Recommended] Only remove if you know what you are doing' }
|
||||
default { "[Optional] Can be safely removed if you don't need this app" }
|
||||
'safe' { Get-Translation -Key 'AppLegendRecommendedTooltip' }
|
||||
'unsafe' { Get-Translation -Key 'AppLegendNotRecommendedTooltip' }
|
||||
default { Get-Translation -Key 'AppLegendOptionalTooltip' }
|
||||
}
|
||||
[System.Windows.Controls.Grid]::SetColumn($dot, 0)
|
||||
|
||||
@@ -608,7 +617,7 @@ function Initialize-MainWindowApps {
|
||||
|
||||
if ($null -eq $listOfApps) {
|
||||
Write-Warning "WinGet returned no data (command timed out or failed)"
|
||||
Show-MessageBox -Message 'Unable to load list of installed apps via WinGet.' -Title 'Error' -Button 'OK' -Icon 'Error' | Out-Null
|
||||
Show-MessageBox -Message (Get-Translation -Key 'AppSelectionWinGetLoadFailedMessage') -Title (Get-Translation -Key 'ErrorTitle') -Button 'OK' -Icon 'Error' | Out-Null
|
||||
$OnlyInstalledAppsBox.IsChecked = $false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,6 +96,17 @@ function Get-PendingTweakActions {
|
||||
return @($actions.ToArray())
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Builds the plain-text list of pending changes: apps to remove and tweaks to apply or undo.
|
||||
|
||||
.PARAMETER ShowCurrentlyAppliedTweaksCheckBox
|
||||
When checked, pending tweak actions are computed against the system's live applied
|
||||
state instead of the controls' initial values.
|
||||
|
||||
.OUTPUTS
|
||||
System.String[]. One line per pending change, or an empty array if nothing is selected.
|
||||
#>
|
||||
function New-Overview {
|
||||
param(
|
||||
[System.Windows.Window]$Window,
|
||||
@@ -114,12 +125,12 @@ function New-Overview {
|
||||
}
|
||||
}
|
||||
if ($selectedAppsCount -gt 0) {
|
||||
$changesList += "Remove $selectedAppsCount application(s)"
|
||||
$changesList += Get-Translation -Key 'DeployChangeRemoveApps' -Count $selectedAppsCount -FormatArgs @($selectedAppsCount)
|
||||
}
|
||||
|
||||
foreach ($tweakAction in @(Get-PendingTweakActions -Window $Window -ShowAppliedTweaksMode:$showAppliedTweaksMode)) {
|
||||
if ($tweakAction.Action -eq 'Undo') {
|
||||
$changesList += "Undo: $($tweakAction.Label)"
|
||||
$changesList += Get-Translation -Key 'DeployChangeUndoPrefix' -FormatArgs @($tweakAction.Label)
|
||||
}
|
||||
else {
|
||||
$changesList += $tweakAction.Label
|
||||
@@ -129,6 +140,11 @@ function New-Overview {
|
||||
return $changesList
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Shows the pending-changes list from New-Overview in a message box, or a
|
||||
"no changes selected" message when there's nothing to show.
|
||||
#>
|
||||
function Invoke-ShowChangesOverview {
|
||||
param(
|
||||
[System.Windows.Window]$Window,
|
||||
@@ -139,12 +155,12 @@ function Invoke-ShowChangesOverview {
|
||||
$changesList = New-Overview -Window $Window -AppsPanel $AppsPanel -ShowCurrentlyAppliedTweaksCheckBox $ShowCurrentlyAppliedTweaksCheckBox
|
||||
|
||||
if ($changesList.Count -eq 0) {
|
||||
Show-MessageBox -Message 'No changes have been selected.' -Title 'Selected Changes' -Button 'OK' -Icon 'Information'
|
||||
Show-MessageBox -Message (Get-Translation -Key 'DeployNoChangesSelectedMessage') -Title (Get-Translation -Key 'DeploySelectedChangesTitle') -Button 'OK' -Icon 'Information'
|
||||
return
|
||||
}
|
||||
|
||||
$message = ($changesList | ForEach-Object { "$([char]0x2022) $_" }) -join "`n"
|
||||
Show-MessageBox -Message $message -Title 'Selected Changes' -Button 'OK' -Icon 'None' -Width 600
|
||||
Show-MessageBox -Message $message -Title (Get-Translation -Key 'DeploySelectedChangesTitle') -Button 'OK' -Icon 'None' -Width 600
|
||||
}
|
||||
|
||||
<#
|
||||
@@ -437,6 +453,14 @@ function Update-AppliedTweaksUserModeState {
|
||||
}
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Updates the deployment-target description text to match the selected user option.
|
||||
|
||||
.DESCRIPTION
|
||||
Falls back to a generic description (current user, other user, or Sysprep default
|
||||
profile) when a username can't be resolved yet, and to a specific one once it can.
|
||||
#>
|
||||
function Update-UserSelectionDescription {
|
||||
param(
|
||||
[System.Windows.Window]$Window,
|
||||
@@ -449,23 +473,23 @@ function Update-UserSelectionDescription {
|
||||
0 {
|
||||
$currentUserName = Get-UserName
|
||||
if ([string]::IsNullOrWhiteSpace($currentUserName)) {
|
||||
$UserSelectionDescription.Text = "The currently logged-in user profile"
|
||||
$UserSelectionDescription.Text = Get-Translation -Key 'DeployUserDescriptionCurrentUser'
|
||||
}
|
||||
else {
|
||||
$UserSelectionDescription.Text = "The currently logged-in user profile: $currentUserName"
|
||||
$UserSelectionDescription.Text = Get-Translation -Key 'DeployUserDescriptionCurrentUserNamed' -FormatArgs @($currentUserName)
|
||||
}
|
||||
}
|
||||
1 {
|
||||
$targetUserName = $OtherUsernameTextBox.Text.Trim()
|
||||
if ([string]::IsNullOrWhiteSpace($targetUserName)) {
|
||||
$UserSelectionDescription.Text = "A different user profile on this system"
|
||||
$UserSelectionDescription.Text = Get-Translation -Key 'DeployUserDescriptionOtherUser'
|
||||
}
|
||||
else {
|
||||
$UserSelectionDescription.Text = "A different user profile on this system: $targetUserName"
|
||||
$UserSelectionDescription.Text = Get-Translation -Key 'DeployUserDescriptionOtherUserNamed' -FormatArgs @($targetUserName)
|
||||
}
|
||||
}
|
||||
default {
|
||||
$UserSelectionDescription.Text = "The default user template, affecting all new users created after this point. Useful for Sysprep deployment."
|
||||
$UserSelectionDescription.Text = Get-Translation -Key 'DeployUserDescriptionSysprep'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -154,7 +154,8 @@ function New-DynamicTweakControls {
|
||||
Returns the existing category panel or creates and registers a new one.
|
||||
|
||||
.PARAMETER CategoryObj
|
||||
The category definition containing Name and Icon properties.
|
||||
The category definition, with Name and Icon for internal use (the map key, the
|
||||
wiki-link slug) and CategoryId for the translated header shown to the user.
|
||||
|
||||
.OUTPUTS
|
||||
System.Windows.Controls.StackPanel. The category's content panel.
|
||||
@@ -162,6 +163,7 @@ function New-DynamicTweakControls {
|
||||
function Get-OrCreateCategoryCard($categoryObj) {
|
||||
$categoryName = $categoryObj.Name
|
||||
$categoryIcon = $categoryObj.Icon
|
||||
$categoryLabel = Get-Translation -Key $categoryObj.CategoryId -Field 'Label' -Section 'Categories'
|
||||
|
||||
if ($script:CategoryCardMap.ContainsKey($categoryName)) { return $script:CategoryCardMap[$categoryName] }
|
||||
|
||||
@@ -193,7 +195,7 @@ function New-DynamicTweakControls {
|
||||
$headerRow.Children.Add($icon) | Out-Null
|
||||
|
||||
$header = New-Object System.Windows.Controls.TextBlock
|
||||
$header.Text = $categoryName
|
||||
$header.Text = $categoryLabel
|
||||
$header.Style = $Window.Resources['CategoryHeaderTextBlock']
|
||||
$headerRow.Children.Add($header) | Out-Null
|
||||
|
||||
@@ -203,7 +205,7 @@ function New-DynamicTweakControls {
|
||||
|
||||
$helpBtn = New-Object System.Windows.Controls.Button
|
||||
$helpBtn.Content = $helpIcon
|
||||
$helpBtn.ToolTip = "Open the wiki for more info on '$categoryName' tweaks"
|
||||
$helpBtn.ToolTip = Get-Translation -Key 'TweaksCategoryHelpTooltip' -FormatArgs @($categoryLabel)
|
||||
$helpBtn.Tag = (Get-WikiUrlForCategory -category $categoryName)
|
||||
$helpBtn.Style = $Window.Resources['CategoryHelpLinkButtonStyle']
|
||||
$helpBtn.Add_Click({
|
||||
@@ -347,17 +349,18 @@ function New-DynamicTweakControls {
|
||||
|
||||
if ($featureMap.ContainsKey($soleFid)) {
|
||||
$soleFeature = $featureMap[$soleFid]
|
||||
$opt = 'Apply'
|
||||
if ($soleFeature.FeatureId -match '^Disable') { $opt = 'Disable' } elseif ($soleFeature.FeatureId -match '^Enable') { $opt = 'Enable' }
|
||||
$items = @('No Change', $opt)
|
||||
$soleFeatureLabel = Get-Translation -Key $soleFeature.FeatureId -Field 'Label' -Section 'Features'
|
||||
$opt = Get-Translation -Key 'TweaksOptionApply'
|
||||
if ($soleFeature.FeatureId -match '^Disable') { $opt = Get-Translation -Key 'TweaksOptionDisable' } elseif ($soleFeature.FeatureId -match '^Enable') { $opt = Get-Translation -Key 'TweaksOptionEnable' }
|
||||
$items = @((Get-Translation -Key 'TweaksOptionNoChange'), $opt)
|
||||
$comboName = ("Feature_{0}_Combo" -f $soleFeature.FeatureId) -replace '[^a-zA-Z0-9_]', ''
|
||||
if (-not $panel) { $panel = Get-OrCreateCategoryCard -categoryObj $categoryObj }
|
||||
$combo = New-LabeledCombo -parent $panel -labelText $soleFeature.Label -comboName $comboName -items $items
|
||||
$combo = New-LabeledCombo -parent $panel -labelText $soleFeatureLabel -comboName $comboName -items $items
|
||||
# attach tooltip from Features.json if present
|
||||
if ($soleFeature.ToolTip -or $soleFeature.DisableWhenApplied -eq $true) {
|
||||
$tooltipText = $soleFeature.ToolTip
|
||||
$tooltipText = Get-Translation -Key $soleFeature.FeatureId -Field 'ToolTip' -Section 'Features'
|
||||
if ($soleFeature.DisableWhenApplied -eq $true) {
|
||||
$tooltipText = "This tweak is already applied and cannot be undone automatically. Visit the Win11Debloat wiki for instructions on how to manually revert this change."
|
||||
$tooltipText = Get-Translation -Key 'TweaksAlreadyAppliedTooltip'
|
||||
}
|
||||
$tipBlock = New-Object System.Windows.Controls.TextBlock
|
||||
$tipBlock.Text = $tooltipText
|
||||
@@ -369,19 +372,20 @@ function New-DynamicTweakControls {
|
||||
try { $lblBorderObj = $Window.FindName("$comboName`_LabelBorder") } catch {}
|
||||
if ($lblBorderObj) { $lblBorderObj.ToolTip = $tipBlock }
|
||||
}
|
||||
$script:UiControlMappings[$comboName] = @{ Type = 'feature'; FeatureId = $soleFeature.FeatureId; Label = $soleFeature.Label; CategoryId = $categoryId }
|
||||
$script:UiControlMappings[$comboName] = @{ Type = 'feature'; FeatureId = $soleFeature.FeatureId; Label = $soleFeatureLabel; CategoryId = $categoryId }
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
$items = @('No Change') + ($filteredValues | ForEach-Object { $_.Label })
|
||||
$groupLabel = Get-Translation -Key $group.GroupId -Field 'Label' -Section 'UiGroups'
|
||||
$items = @((Get-Translation -Key 'TweaksOptionNoChange')) + ($filteredValues | ForEach-Object { Get-GroupValueTranslation -GroupId $group.GroupId -FeatureId $_.FeatureIds[0] -FallbackLabel $_.Label })
|
||||
$comboName = 'Group_{0}Combo' -f $group.GroupId
|
||||
if (-not $panel) { $panel = Get-OrCreateCategoryCard -categoryObj $categoryObj }
|
||||
$combo = New-LabeledCombo -parent $panel -labelText $group.Label -comboName $comboName -items $items
|
||||
$combo = New-LabeledCombo -parent $panel -labelText $groupLabel -comboName $comboName -items $items
|
||||
# attach tooltip from UiGroups if present
|
||||
if ($group.ToolTip) {
|
||||
$tipBlock = New-Object System.Windows.Controls.TextBlock
|
||||
$tipBlock.Text = $group.ToolTip
|
||||
$tipBlock.Text = Get-Translation -Key $group.GroupId -Field 'ToolTip' -Section 'UiGroups'
|
||||
$tipBlock.TextWrapping = 'Wrap'
|
||||
$tipBlock.MaxWidth = 420
|
||||
$combo.ToolTip = $tipBlock
|
||||
@@ -389,21 +393,22 @@ function New-DynamicTweakControls {
|
||||
try { $lblBorderObj = $Window.FindName("$comboName`_LabelBorder") } catch {}
|
||||
if ($lblBorderObj) { $lblBorderObj.ToolTip = $tipBlock }
|
||||
}
|
||||
$script:UiControlMappings[$comboName] = @{ Type = 'group'; Values = $filteredValues; Label = $group.Label; CategoryId = $categoryId }
|
||||
$script:UiControlMappings[$comboName] = @{ Type = 'group'; Values = $filteredValues; Label = $groupLabel; CategoryId = $categoryId }
|
||||
}
|
||||
elseif ($item.Type -eq 'feature') {
|
||||
$feature = $item.Data
|
||||
$opt = 'Apply'
|
||||
if ($feature.FeatureId -match '^Disable') { $opt = 'Disable' } elseif ($feature.FeatureId -match '^Enable') { $opt = 'Enable' }
|
||||
$items = @('No Change', $opt)
|
||||
$featureLabel = Get-Translation -Key $feature.FeatureId -Field 'Label' -Section 'Features'
|
||||
$opt = Get-Translation -Key 'TweaksOptionApply'
|
||||
if ($feature.FeatureId -match '^Disable') { $opt = Get-Translation -Key 'TweaksOptionDisable' } elseif ($feature.FeatureId -match '^Enable') { $opt = Get-Translation -Key 'TweaksOptionEnable' }
|
||||
$items = @((Get-Translation -Key 'TweaksOptionNoChange'), $opt)
|
||||
$comboName = ("Feature_{0}_Combo" -f $feature.FeatureId) -replace '[^a-zA-Z0-9_]', ''
|
||||
if (-not $panel) { $panel = Get-OrCreateCategoryCard -categoryObj $categoryObj }
|
||||
$combo = New-LabeledCombo -parent $panel -labelText $feature.Label -comboName $comboName -items $items
|
||||
$combo = New-LabeledCombo -parent $panel -labelText $featureLabel -comboName $comboName -items $items
|
||||
# attach tooltip from Features.json if present, and include the disabled-state reason
|
||||
if ($feature.ToolTip -or $feature.DisableWhenApplied -eq $true) {
|
||||
$tooltipText = $feature.ToolTip
|
||||
$tooltipText = Get-Translation -Key $feature.FeatureId -Field 'ToolTip' -Section 'Features'
|
||||
if ($feature.DisableWhenApplied -eq $true) {
|
||||
$tooltipText = "This tweak is already applied and cannot be undone automatically. Visit the Win11Debloat wiki for instructions on how to manually revert this change."
|
||||
$tooltipText = Get-Translation -Key 'TweaksAlreadyAppliedTooltip'
|
||||
}
|
||||
|
||||
$tipBlock = New-Object System.Windows.Controls.TextBlock
|
||||
@@ -425,8 +430,10 @@ function New-DynamicTweakControls {
|
||||
$script:FeatureLabelLookup = @{}
|
||||
$script:UndoFeatureLabelLookup = @{}
|
||||
foreach ($f in $featuresJson.Features) {
|
||||
$script:FeatureLabelLookup[$f.FeatureId] = $f.Label
|
||||
$script:UndoFeatureLabelLookup[$f.FeatureId] = $f.UndoLabel
|
||||
$script:FeatureLabelLookup[$f.FeatureId] = Get-Translation -Key $f.FeatureId -Field 'Label' -Section 'Features'
|
||||
if ($f.UndoLabel) {
|
||||
$script:UndoFeatureLabelLookup[$f.FeatureId] = Get-Translation -Key $f.FeatureId -Field 'UndoLabel' -Section 'Features'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -81,8 +81,9 @@ function Test-RestoreDialogFeatureCanAutoRevert {
|
||||
Resolves a human-readable label for a feature shown in the restore dialog.
|
||||
|
||||
.DESCRIPTION
|
||||
Returns the feature's Label from Features.json when found, falling back
|
||||
to the raw FeatureId string. For null/empty FeatureIds returns 'Unknown feature'.
|
||||
Returns the feature's translated Label when found, falling back to the
|
||||
raw FeatureId string. For null/empty FeatureIds returns the translated
|
||||
"unknown feature" placeholder.
|
||||
|
||||
.PARAMETER FeatureId
|
||||
The feature identifier to resolve a label for.
|
||||
@@ -97,12 +98,12 @@ function Get-RestoreDialogFeatureDisplayLabel {
|
||||
)
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($FeatureId)) {
|
||||
return 'Unknown feature'
|
||||
return Get-Translation -Key 'RestoreUnknownFeature'
|
||||
}
|
||||
|
||||
$featureDefinition = Get-RestoreDialogFeatureDefinition -FeatureId $FeatureId -Features $Features
|
||||
if ($featureDefinition) {
|
||||
return [string]$featureDefinition.Label
|
||||
return Get-Translation -Key $FeatureId -Field 'Label' -Section 'Features'
|
||||
}
|
||||
|
||||
return $FeatureId
|
||||
|
||||
@@ -32,6 +32,7 @@ function Show-AboutDialog {
|
||||
|
||||
# Load XAML from file
|
||||
$xaml = Get-Content -Path $script:AboutWindowSchema -Raw
|
||||
$xaml = ConvertTo-LocalizedXaml -Xaml $xaml
|
||||
$reader = [System.Xml.XmlReader]::Create([System.IO.StringReader]::new($xaml))
|
||||
try {
|
||||
$aboutWindow = [System.Windows.Markup.XamlReader]::Load($reader)
|
||||
|
||||
@@ -24,6 +24,7 @@ function Show-AppSelectionWindow {
|
||||
|
||||
# Load XAML from file
|
||||
$xaml = Get-Content -Path $script:AppSelectionSchema -Raw
|
||||
$xaml = ConvertTo-LocalizedXaml -Xaml $xaml
|
||||
$reader = [System.Xml.XmlReader]::Create([System.IO.StringReader]::new($xaml))
|
||||
try {
|
||||
$window = [System.Windows.Markup.XamlReader]::Load($reader)
|
||||
@@ -72,7 +73,7 @@ function Show-AppSelectionWindow {
|
||||
$listOfApps = Get-WingetInstalledApps -TimeOut 10 -NonBlocking
|
||||
if ($null -eq $listOfApps) {
|
||||
# Show error that the script was unable to get list of apps from WinGet
|
||||
Show-MessageBox -Message 'Unable to load list of installed apps via WinGet.' -Title 'Error' -Button 'OK' -Icon 'Error' -Owner $window | Out-Null
|
||||
Show-MessageBox -Message (Get-Translation -Key 'AppSelectionWinGetLoadFailedMessage') -Title (Get-Translation -Key 'ErrorTitle') -Button 'OK' -Icon 'Error' -Owner $window | Out-Null
|
||||
$onlyInstalledBox.IsChecked = $false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ function Show-ApplyModal {
|
||||
|
||||
# Load XAML from file
|
||||
$xaml = Get-Content -Path $script:ApplyChangesWindowSchema -Raw
|
||||
$xaml = ConvertTo-LocalizedXaml -Xaml $xaml
|
||||
$reader = [System.Xml.XmlReader]::Create([System.IO.StringReader]::new($xaml))
|
||||
try {
|
||||
$applyWindow = [System.Windows.Markup.XamlReader]::Load($reader)
|
||||
@@ -74,8 +75,8 @@ function Show-ApplyModal {
|
||||
# Initialize in-progress state
|
||||
$script:ApplyInProgressPanel.Visibility = 'Visible'
|
||||
$script:ApplyCompletionPanel.Visibility = 'Collapsed'
|
||||
$script:ApplyStepNameEl.Text = "Preparing..."
|
||||
$script:ApplyStepCounterEl.Text = "Preparing..."
|
||||
$script:ApplyStepNameEl.Text = Get-Translation -Key 'ApplyPreparing'
|
||||
$script:ApplyStepCounterEl.Text = Get-Translation -Key 'ApplyStepCounter' -FormatArgs @(0, 0)
|
||||
$script:ApplyProgressBarEl.Value = 0
|
||||
$script:ApplyModalInErrorState = $false
|
||||
|
||||
@@ -83,7 +84,7 @@ function Show-ApplyModal {
|
||||
$script:ApplyProgressCallback = {
|
||||
param($currentStep, $totalSteps, $stepName)
|
||||
$script:ApplyStepNameEl.Text = $stepName
|
||||
$script:ApplyStepCounterEl.Text = "Step $currentStep of $totalSteps"
|
||||
$script:ApplyStepCounterEl.Text = Get-Translation -Key 'ApplyStepCounter' -FormatArgs @($currentStep, $totalSteps)
|
||||
# Store current step/total in Tag properties for sub-step interpolation
|
||||
$script:ApplyStepCounterEl.Tag = $currentStep
|
||||
$script:ApplyProgressBarEl.Tag = $totalSteps
|
||||
@@ -140,8 +141,8 @@ function Show-ApplyModal {
|
||||
|
||||
$script:ApplyCompletionIconEl.Text = [char]0xE7BA
|
||||
$script:ApplyCompletionIconEl.Foreground = [System.Windows.Media.SolidColorBrush]::new([System.Windows.Media.ColorConverter]::ConvertFromString("#e8912d"))
|
||||
$script:ApplyCompletionTitleEl.Text = "Cancelled"
|
||||
$script:ApplyCompletionMessageEl.Text = "Script execution was cancelled by the user."
|
||||
$script:ApplyCompletionTitleEl.Text = Get-Translation -Key 'ApplyCompletionTitleCancelled'
|
||||
$script:ApplyCompletionMessageEl.Text = Get-Translation -Key 'ApplyCompletionMessageCancelled'
|
||||
} elseif ($failureCount -gt 0 -or $appRemovalVerificationUnavailable) {
|
||||
if ($failureCount -gt 0) {
|
||||
Write-Host "Script completed with $failureCount error(s)."
|
||||
@@ -150,17 +151,17 @@ function Show-ApplyModal {
|
||||
$script:ApplyCompletionIconEl.Text = [char]0xE7BA
|
||||
$script:ApplyCompletionIconEl.Foreground = [System.Windows.Media.SolidColorBrush]::new([System.Windows.Media.ColorConverter]::ConvertFromString("#e8912d"))
|
||||
if ($failureCount -eq 0 -and $appRemovalVerificationUnavailable) {
|
||||
$script:ApplyCompletionTitleEl.Text = "Changes Applied"
|
||||
$script:ApplyCompletionMessageEl.Text = "All changes were applied without errors, but Win11Debloat could not confirm that all selected apps were successfully uninstalled."
|
||||
$script:ApplyCompletionTitleEl.Text = Get-Translation -Key 'ApplyCompletionTitleSuccess'
|
||||
$script:ApplyCompletionMessageEl.Text = Get-Translation -Key 'ApplyCompletionMessageVerificationUnavailable'
|
||||
}
|
||||
else {
|
||||
$script:ApplyCompletionTitleEl.Text = "Changes Applied with Errors"
|
||||
$script:ApplyCompletionMessageEl.Text = "$failureCount change(s) failed. See console for details."
|
||||
$script:ApplyCompletionTitleEl.Text = Get-Translation -Key 'ApplyCompletionTitleErrors'
|
||||
$script:ApplyCompletionMessageEl.Text = Get-Translation -Key 'ApplyCompletionMessageFailures' -Count $failureCount -FormatArgs @($failureCount)
|
||||
}
|
||||
} else {
|
||||
Write-Host "All changes have been applied successfully!"
|
||||
|
||||
$script:ApplyCompletionTitleEl.Text = "Changes Applied"
|
||||
$script:ApplyCompletionTitleEl.Text = Get-Translation -Key 'ApplyCompletionTitleSuccess'
|
||||
|
||||
# Show completion message with reboot instructions if any applied features require reboot
|
||||
if ($InvokeRestartExplorer) {
|
||||
@@ -179,7 +180,7 @@ function Show-ApplyModal {
|
||||
$applyRebootPanel.Visibility = 'Visible'
|
||||
}
|
||||
else {
|
||||
$script:ApplyCompletionMessageEl.Text = "Your system is ready. Thanks for using Win11Debloat!"
|
||||
$script:ApplyCompletionMessageEl.Text = Get-Translation -Key 'ApplyCompletionMessageReady'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -191,24 +192,24 @@ function Show-ApplyModal {
|
||||
$script:ApplyCompletionPanel.Visibility = 'Visible'
|
||||
$script:ApplyCompletionIconEl.Text = [char]0xEA39
|
||||
$script:ApplyCompletionIconEl.Foreground = [System.Windows.Media.SolidColorBrush]::new([System.Windows.Media.ColorConverter]::ConvertFromString("#c42b1c"))
|
||||
$script:ApplyCompletionTitleEl.Text = "Error"
|
||||
$script:ApplyCompletionMessageEl.Text = "An error occurred while applying changes: $($_.Exception.Message)"
|
||||
|
||||
$script:ApplyCompletionTitleEl.Text = Get-Translation -Key 'ApplyCompletionTitleError'
|
||||
$script:ApplyCompletionMessageEl.Text = Get-Translation -Key 'ApplyCompletionMessageError' -FormatArgs @($_.Exception.Message)
|
||||
|
||||
# Set error state to change Kofi button to report link
|
||||
$script:ApplyModalInErrorState = $true
|
||||
|
||||
# Update Kofi button to be a report issue button
|
||||
$applyKofiBtn.Content = $null
|
||||
|
||||
|
||||
$reportText = [System.Windows.Controls.TextBlock]::new()
|
||||
$reportText.Text = 'Report a bug'
|
||||
$reportText.Text = Get-Translation -Key 'MenuReportBug'
|
||||
$reportText.VerticalAlignment = 'Center'
|
||||
$reportText.FontSize = 14
|
||||
$reportText.Margin = [System.Windows.Thickness]::new(0, 0, 0, 1)
|
||||
|
||||
$applyKofiBtn.Content = $reportText
|
||||
|
||||
[System.Windows.Automation.AutomationProperties]::SetName($applyKofiBtn, 'Report a bug')
|
||||
|
||||
[System.Windows.Automation.AutomationProperties]::SetName($applyKofiBtn, (Get-Translation -Key 'MenuReportBug'))
|
||||
|
||||
$applyWindow.Dispatcher.Invoke([System.Windows.Threading.DispatcherPriority]::Render, [action]{})
|
||||
}
|
||||
|
||||
@@ -72,8 +72,8 @@ function Hide-Bubble {
|
||||
parameter is mandatory.
|
||||
|
||||
.PARAMETER Message
|
||||
The text message to display inside the bubble. Defaults to
|
||||
'View the selected changes here'.
|
||||
The text message to display inside the bubble. Defaults to the
|
||||
translated "view the selected changes" prompt.
|
||||
|
||||
.PARAMETER DurationSeconds
|
||||
The number of seconds the bubble remains visible before auto-hiding.
|
||||
@@ -91,7 +91,7 @@ function Show-Bubble {
|
||||
[System.Windows.Controls.Control]$TargetControl,
|
||||
|
||||
[Parameter(Mandatory=$false)]
|
||||
[string]$Message = 'View the selected changes here',
|
||||
[string]$Message = (Get-Translation -Key 'BubbleViewChanges'),
|
||||
|
||||
[Parameter(Mandatory=$false)]
|
||||
[int]$DurationSeconds = 5
|
||||
@@ -104,6 +104,7 @@ function Show-Bubble {
|
||||
Hide-Bubble -Immediate
|
||||
|
||||
$xaml = Get-Content -Path $script:BubbleHintSchema -Raw
|
||||
$xaml = ConvertTo-LocalizedXaml -Xaml $xaml
|
||||
$reader = [System.Xml.XmlReader]::Create([System.IO.StringReader]::new($xaml))
|
||||
try {
|
||||
$bubblePanel = [System.Windows.Markup.XamlReader]::Load($reader)
|
||||
|
||||
@@ -1,3 +1,26 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Maps an internal import/export category ID ('Applications', 'System Tweaks',
|
||||
'Deployment Settings') to its Chrome.json translation key.
|
||||
|
||||
.DESCRIPTION
|
||||
The category strings stay as the stable internal IDs used for -contains checks and
|
||||
hashtable lookups throughout this file; only the label shown to the user is translated.
|
||||
#>
|
||||
function Get-ImportExportCategoryLabelKey {
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[string]$Category
|
||||
)
|
||||
|
||||
switch ($Category) {
|
||||
'Applications' { return 'ImportExportCategoryApplications' }
|
||||
'System Tweaks' { return 'ImportExportCategorySystemTweaks' }
|
||||
'Deployment Settings' { return 'ImportExportCategoryDeploymentSettings' }
|
||||
default { return $Category }
|
||||
}
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Shows a modal category-selection dialog for importing or exporting configuration.
|
||||
@@ -11,7 +34,7 @@ function Show-ImportExportConfigWindow {
|
||||
[string[]]$Categories = @('Applications', 'System Tweaks', 'Deployment Settings'),
|
||||
[string[]]$DisabledCategories = @(),
|
||||
[hashtable]$CategoryDetails = @(),
|
||||
[string]$ActionLabel = 'OK'
|
||||
[string]$ActionLabel = (Get-Translation -Key 'MessageBoxOk')
|
||||
)
|
||||
|
||||
# Show overlay on owner window
|
||||
@@ -32,7 +55,7 @@ function Show-ImportExportConfigWindow {
|
||||
$schemaPath = $script:ImportExportConfigSchema
|
||||
|
||||
if (-not $schemaPath -or -not (Test-Path $schemaPath)) {
|
||||
Show-MessageBox -Message 'Import/Export window schema file could not be found.' -Title 'Error' -Button 'OK' -Icon 'Error' -Owner $Owner | Out-Null
|
||||
Show-MessageBox -Message (Get-Translation -Key 'ImportExportSchemaMissingMessage') -Title (Get-Translation -Key 'ErrorTitle') -Button 'OK' -Icon 'Error' -Owner $Owner | Out-Null
|
||||
if ($overlay -and -not $overlayWasAlreadyVisible) {
|
||||
try { $Owner.Dispatcher.Invoke([action]{ $overlay.Visibility = 'Collapsed' }) } catch { }
|
||||
}
|
||||
@@ -40,6 +63,7 @@ function Show-ImportExportConfigWindow {
|
||||
}
|
||||
|
||||
$xaml = Get-Content -Path $schemaPath -Raw
|
||||
$xaml = ConvertTo-LocalizedXaml -Xaml $xaml
|
||||
$reader = [System.Xml.XmlReader]::Create([System.IO.StringReader]::new($xaml))
|
||||
try {
|
||||
$dlg = [System.Windows.Markup.XamlReader]::Load($reader)
|
||||
@@ -79,7 +103,7 @@ function Show-ImportExportConfigWindow {
|
||||
|
||||
# Create checkbox
|
||||
$cb = New-Object System.Windows.Controls.CheckBox
|
||||
$cb.Content = $cat
|
||||
$cb.Content = Get-Translation -Key (Get-ImportExportCategoryLabelKey -Category $cat)
|
||||
$cb.IsChecked = $true
|
||||
$cb.Margin = [System.Windows.Thickness]::new(0,0,0,4)
|
||||
$cb.FontSize = 14
|
||||
@@ -89,7 +113,7 @@ function Show-ImportExportConfigWindow {
|
||||
$cb.IsChecked = $false
|
||||
$cb.IsEnabled = $false
|
||||
$cb.Opacity = 0.65
|
||||
$cb.ToolTip = 'No selected settings available in this category.'
|
||||
$cb.ToolTip = Get-Translation -Key 'ImportExportCategoryDisabledTooltip'
|
||||
}
|
||||
|
||||
$container.Children.Add($cb) | Out-Null
|
||||
@@ -245,6 +269,11 @@ function Get-AvailableImportExportCategories {
|
||||
return $availableCategories
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Summarizes a deployment settings array into the short description shown under the
|
||||
"Deployment Settings" checkbox in the import/export category picker.
|
||||
#>
|
||||
function Get-DeploymentCategoryDetailString {
|
||||
param (
|
||||
[array]$DeploymentSettings
|
||||
@@ -261,33 +290,38 @@ function Get-DeploymentCategoryDetailString {
|
||||
|
||||
if ($lookup.ContainsKey('UserSelectionIndex')) {
|
||||
switch ([int]$lookup['UserSelectionIndex']) {
|
||||
0 { $line1 += 'User: Current User' }
|
||||
1 { $line1 += "User: $(if ($lookup['OtherUsername']) { $lookup['OtherUsername'] } else { 'Other User' })" }
|
||||
2 { $line1 += 'User: Sysprep' }
|
||||
0 { $line1 += Get-Translation -Key 'ImportExportDeployUserCurrentUser' }
|
||||
1 { $line1 += Get-Translation -Key 'ImportExportDeployUserOther' -FormatArgs @($(if ($lookup['OtherUsername']) { $lookup['OtherUsername'] } else { Get-Translation -Key 'ImportExportDeployUserOtherFallback' })) }
|
||||
2 { $line1 += Get-Translation -Key 'ImportExportDeployUserSysprep' }
|
||||
}
|
||||
}
|
||||
|
||||
if ($lookup.ContainsKey('AppRemovalScopeIndex')) {
|
||||
switch ([int]$lookup['AppRemovalScopeIndex']) {
|
||||
0 { $line1 += 'App Removal: All Users' }
|
||||
1 { $line1 += 'App Removal: Current User' }
|
||||
2 { $line1 += "App Removal: $(if ($lookup['OtherUsername']) { $lookup['OtherUsername'] } else { 'Other User' })" }
|
||||
0 { $line1 += Get-Translation -Key 'ImportExportAppRemovalAllUsers' }
|
||||
1 { $line1 += Get-Translation -Key 'ImportExportAppRemovalCurrentUser' }
|
||||
2 { $line1 += Get-Translation -Key 'ImportExportAppRemovalOther' -FormatArgs @($(if ($lookup['OtherUsername']) { $lookup['OtherUsername'] } else { Get-Translation -Key 'ImportExportDeployUserOtherFallback' })) }
|
||||
}
|
||||
}
|
||||
|
||||
$options = @()
|
||||
if ($lookup.ContainsKey('CreateRestorePoint') -and [bool]$lookup['CreateRestorePoint']) { $options += 'Restore Point' }
|
||||
if (-not ($lookup.ContainsKey('SkipRegistryBackup') -and [bool]$lookup['SkipRegistryBackup'])) { $options += 'Registry Backup' }
|
||||
if ($lookup.ContainsKey('RestartExplorer') -and [bool]$lookup['RestartExplorer']) { $options += 'Restart Explorer' }
|
||||
if ($lookup.ContainsKey('CreateRestorePoint') -and [bool]$lookup['CreateRestorePoint']) { $options += Get-Translation -Key 'ImportExportOptionRestorePoint' }
|
||||
if (-not ($lookup.ContainsKey('SkipRegistryBackup') -and [bool]$lookup['SkipRegistryBackup'])) { $options += Get-Translation -Key 'ImportExportOptionRegistryBackup' }
|
||||
if ($lookup.ContainsKey('RestartExplorer') -and [bool]$lookup['RestartExplorer']) { $options += Get-Translation -Key 'ImportExportOptionRestartExplorer' }
|
||||
|
||||
$lines = @()
|
||||
if ($line1.Count -gt 0) { $lines += $line1 -join ', ' }
|
||||
if ($options.Count -gt 0) { $lines += "Options: $($options -join ', ')" }
|
||||
if ($options.Count -gt 0) { $lines += Get-Translation -Key 'ImportExportOptionsPrefix' -FormatArgs @($options -join ', ') }
|
||||
|
||||
if ($lines.Count -gt 0) { return $lines -join "`n" }
|
||||
return 'Default deployment settings'
|
||||
return Get-Translation -Key 'ImportExportDefaultDeploymentSettings'
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Builds the per-category description text shown under each checkbox in the
|
||||
import/export category picker.
|
||||
#>
|
||||
function Build-CategoryDetails {
|
||||
param (
|
||||
[int]$AppCount = 0,
|
||||
@@ -298,17 +332,17 @@ function Build-CategoryDetails {
|
||||
$details = @{}
|
||||
|
||||
if ($AppCount -gt 0) {
|
||||
$details['Applications'] = "$AppCount app$(if ($AppCount -ne 1) { 's' }) selected"
|
||||
$details['Applications'] = Get-Translation -Key 'ImportExportAppsSelected' -Count $AppCount -FormatArgs @($AppCount)
|
||||
}
|
||||
else {
|
||||
$details['Applications'] = 'No apps selected'
|
||||
$details['Applications'] = Get-Translation -Key 'ImportExportNoAppsSelected'
|
||||
}
|
||||
|
||||
if ($TweakCount -gt 0) {
|
||||
$details['System Tweaks'] = "$TweakCount tweak$(if ($TweakCount -ne 1) { 's' }) selected"
|
||||
$details['System Tweaks'] = Get-Translation -Key 'ImportExportTweaksSelected' -Count $TweakCount -FormatArgs @($TweakCount)
|
||||
}
|
||||
else {
|
||||
$details['System Tweaks'] = 'No tweaks selected'
|
||||
$details['System Tweaks'] = Get-Translation -Key 'ImportExportNoTweaksSelected'
|
||||
}
|
||||
|
||||
if ($DeploymentSettings) {
|
||||
@@ -422,7 +456,7 @@ function Export-Configuration {
|
||||
$deploymentSettings = Get-DeploymentSettings -Owner $Owner -UserSelectionCombo $UserSelectionCombo -OtherUsernameTextBox $OtherUsernameTextBox
|
||||
$categoryDetails = Build-CategoryDetails -AppCount $selectedApps.Count -TweakCount $tweakSettings.Count -DeploymentSettings $deploymentSettings
|
||||
|
||||
$categories = Show-ImportExportConfigWindow -Owner $Owner -UsesDarkMode $UsesDarkMode -Title 'Export Configuration' -Prompt 'Create a configuration file based on the currently selected settings. You can choose which settings categories you wish to include in the export.' -DisabledCategories $disabledCategories -CategoryDetails $categoryDetails -ActionLabel 'Export Settings'
|
||||
$categories = Show-ImportExportConfigWindow -Owner $Owner -UsesDarkMode $UsesDarkMode -Title (Get-Translation -Key 'ImportExportExportTitle') -Prompt (Get-Translation -Key 'ImportExportExportPrompt') -DisabledCategories $disabledCategories -CategoryDetails $categoryDetails -ActionLabel (Get-Translation -Key 'ImportExportExportActionLabel')
|
||||
if (-not $categories) {
|
||||
Write-Host 'Export canceled.'
|
||||
return
|
||||
@@ -442,7 +476,7 @@ function Export-Configuration {
|
||||
|
||||
# Show native save-file dialog
|
||||
$saveDialog = New-Object Microsoft.Win32.SaveFileDialog
|
||||
$saveDialog.Title = 'Export Configuration'
|
||||
$saveDialog.Title = Get-Translation -Key 'ImportExportSelectExportFileDialogTitle'
|
||||
$saveDialog.Filter = 'JSON files (*.json)|*.json|All files (*.*)|*.*'
|
||||
$saveDialog.DefaultExt = '.json'
|
||||
$saveDialog.FileName = "Win11Debloat-Config-$(Get-Date -Format 'yyyyMMdd').json"
|
||||
@@ -456,17 +490,17 @@ function Export-Configuration {
|
||||
|
||||
if ($script:Params.ContainsKey("WhatIf")) {
|
||||
Write-Host "[WhatIf] Export configuration to '$($saveDialog.FileName)'" -ForegroundColor Cyan
|
||||
Show-MessageBox -Message "[WhatIf] Configuration would be exported to this file (no file written)." -Title 'Export Configuration' -Button 'OK' -Icon 'Information' | Out-Null
|
||||
Show-MessageBox -Message (Get-Translation -Key 'ImportExportWhatIfExportMessage') -Title (Get-Translation -Key 'ImportExportExportTitle') -Button 'OK' -Icon 'Information' | Out-Null
|
||||
return
|
||||
}
|
||||
|
||||
if (Save-ToFile -Config $config -FilePath $saveDialog.FileName) {
|
||||
Write-Host "Configuration exported successfully: $($saveDialog.FileName)"
|
||||
Show-MessageBox -Message "Configuration exported successfully." -Title 'Export Configuration' -Button 'OK' -Icon 'Information' | Out-Null
|
||||
Show-MessageBox -Message (Get-Translation -Key 'ImportExportExportSuccessMessage') -Title (Get-Translation -Key 'ImportExportExportTitle') -Button 'OK' -Icon 'Information' | Out-Null
|
||||
}
|
||||
else {
|
||||
Write-Error "Failed to export configuration to '$($saveDialog.FileName)'"
|
||||
Show-MessageBox -Message "Failed to export configuration" -Title 'Error' -Button 'OK' -Icon 'Error' | Out-Null
|
||||
Show-MessageBox -Message (Get-Translation -Key 'ImportExportExportFailedMessage') -Title (Get-Translation -Key 'ErrorTitle') -Button 'OK' -Icon 'Error' | Out-Null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -488,7 +522,7 @@ function Import-Configuration {
|
||||
|
||||
# Show native open-file dialog
|
||||
$openDialog = New-Object Microsoft.Win32.OpenFileDialog
|
||||
$openDialog.Title = 'Select Configuration File'
|
||||
$openDialog.Title = Get-Translation -Key 'ImportExportSelectImportFileDialogTitle'
|
||||
$openDialog.Filter = 'JSON files (*.json)|*.json|All files (*.*)|*.*'
|
||||
$openDialog.DefaultExt = '.json'
|
||||
|
||||
@@ -502,14 +536,14 @@ function Import-Configuration {
|
||||
$config = Import-JsonFile -filePath $openDialog.FileName -expectedVersion '1.0'
|
||||
if (-not $config) {
|
||||
Write-Error "Failed to read configuration file '$($openDialog.FileName)'"
|
||||
Show-MessageBox -Message "Failed to read configuration file" -Title 'Invalid Config' -Button 'OK' -Icon 'Error' | Out-Null
|
||||
Show-MessageBox -Message (Get-Translation -Key 'ImportExportReadFailedMessage') -Title (Get-Translation -Key 'ImportExportInvalidConfigTitle') -Button 'OK' -Icon 'Error' | Out-Null
|
||||
return
|
||||
}
|
||||
|
||||
$consistencyError = Test-ConfigConsistency -Config $config
|
||||
if ($consistencyError) {
|
||||
Write-Error "Invalid configuration file '$($openDialog.FileName)': $consistencyError"
|
||||
Show-MessageBox -Message "Invalid configuration file: $consistencyError" -Title 'Invalid Config' -Button 'OK' -Icon 'Error' | Out-Null
|
||||
Show-MessageBox -Message (Get-Translation -Key 'ImportExportInvalidConfigMessage' -FormatArgs @($consistencyError)) -Title (Get-Translation -Key 'ImportExportInvalidConfigTitle') -Button 'OK' -Icon 'Error' | Out-Null
|
||||
return
|
||||
}
|
||||
|
||||
@@ -521,7 +555,7 @@ function Import-Configuration {
|
||||
$tweakCount = @($config.Tweaks | Where-Object { $_ -and $_.Name -and $_.Value -eq $true }).Count
|
||||
$categoryDetails = Build-CategoryDetails -AppCount $appCount -TweakCount $tweakCount -DeploymentSettings @($config.Deployment)
|
||||
|
||||
$categories = Show-ImportExportConfigWindow -Owner $Owner -UsesDarkMode $UsesDarkMode -Title 'Import Configuration' -Prompt 'Choose the settings categories that you wish to import. You can review and modify the imported settings before they are applied.' -Categories $availableCategories -CategoryDetails $categoryDetails -ActionLabel 'Import Settings'
|
||||
$categories = Show-ImportExportConfigWindow -Owner $Owner -UsesDarkMode $UsesDarkMode -Title (Get-Translation -Key 'ImportExportImportTitle') -Prompt (Get-Translation -Key 'ImportExportImportPrompt') -Categories $availableCategories -CategoryDetails $categoryDetails -ActionLabel (Get-Translation -Key 'ImportExportImportActionLabel')
|
||||
if (-not $categories) {
|
||||
Write-Host 'Import canceled.'
|
||||
return
|
||||
@@ -553,7 +587,7 @@ function Import-Configuration {
|
||||
}
|
||||
|
||||
Write-Host 'Configuration imported successfully.'
|
||||
Show-MessageBox -Message "Configuration imported successfully." -Title 'Import Configuration' -Button 'OK' -Icon 'Information' | Out-Null
|
||||
Show-MessageBox -Message (Get-Translation -Key 'ImportExportImportSuccessMessage') -Title (Get-Translation -Key 'ImportExportImportTitle') -Button 'OK' -Icon 'Information' | Out-Null
|
||||
|
||||
if ($OnImportCompleted) {
|
||||
& $OnImportCompleted $categories
|
||||
|
||||
@@ -10,6 +10,7 @@ function Show-MainWindow {
|
||||
|
||||
# ---- Load XAML ----
|
||||
$xaml = Get-Content -Path $script:MainWindowSchema -Raw
|
||||
$xaml = ConvertTo-LocalizedXaml -Xaml $xaml
|
||||
$reader = [System.Xml.XmlReader]::Create([System.IO.StringReader]::new($xaml))
|
||||
try {
|
||||
$window = [System.Windows.Markup.XamlReader]::Load($reader)
|
||||
@@ -97,7 +98,7 @@ function Show-MainWindow {
|
||||
Start-Process "explorer.exe" -ArgumentList $logsFolder
|
||||
}
|
||||
else {
|
||||
Show-MessageBox -Message "No logs folder found at: $logsFolder" -Title "Logs" -Button 'OK' -Icon 'Information'
|
||||
Show-MessageBox -Message (Get-Translation -Key 'LogsFolderNotFoundMessage' -FormatArgs @($logsFolder)) -Title (Get-Translation -Key 'LogsMenuTitle') -Button 'OK' -Icon 'Information'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -170,7 +171,7 @@ function Show-MainWindow {
|
||||
}
|
||||
catch {
|
||||
Write-Warning "Export configuration failed: $($_.Exception.Message)"
|
||||
Show-MessageBox -Owner $window -Message "Unable to open export configuration dialog: $($_.Exception.Message)" -Title 'Export Configuration Failed' -Button 'OK' -Icon 'Error' | Out-Null
|
||||
Show-MessageBox -Owner $window -Message (Get-Translation -Key 'ExportConfigFailedMessage' -FormatArgs @($_.Exception.Message)) -Title (Get-Translation -Key 'ExportConfigFailedTitle') -Button 'OK' -Icon 'Error' | Out-Null
|
||||
}
|
||||
})
|
||||
|
||||
@@ -180,13 +181,13 @@ function Show-MainWindow {
|
||||
$tabControl.SelectedIndex = 3
|
||||
Update-NavigationButtons -Window $window -TabControl $tabControl
|
||||
$window.Dispatcher.BeginInvoke([System.Windows.Threading.DispatcherPriority]::Loaded, [action]{
|
||||
Show-Bubble -TargetControl $reviewChangesBtn -Message 'View the selected changes here'
|
||||
Show-Bubble -TargetControl $reviewChangesBtn -Message (Get-Translation -Key 'BubbleViewChanges')
|
||||
}) | Out-Null
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-Warning "Import configuration failed: $($_.Exception.Message)"
|
||||
Show-MessageBox -Owner $window -Message "Unable to open import configuration dialog: $($_.Exception.Message)" -Title 'Import Configuration Failed' -Button 'OK' -Icon 'Error' | Out-Null
|
||||
Show-MessageBox -Owner $window -Message (Get-Translation -Key 'ImportConfigFailedMessage' -FormatArgs @($_.Exception.Message)) -Title (Get-Translation -Key 'ImportConfigFailedTitle') -Button 'OK' -Icon 'Error' | Out-Null
|
||||
}
|
||||
})
|
||||
|
||||
@@ -206,7 +207,7 @@ function Show-MainWindow {
|
||||
}
|
||||
catch {
|
||||
Write-Warning "Restore backup action failed: $($_.Exception.Message)"
|
||||
Show-MessageBox -Owner $window -Message "Unable to open restore backup dialog: $($_.Exception.Message)" -Title 'Restore Backup Failed' -Button 'OK' -Icon 'Error' | Out-Null
|
||||
Show-MessageBox -Owner $window -Message (Get-Translation -Key 'RestoreBackupFailedMessage' -FormatArgs @($_.Exception.Message)) -Title (Get-Translation -Key 'RestoreBackupFailedTitle') -Button 'OK' -Icon 'Error' | Out-Null
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -232,7 +233,7 @@ function Show-MainWindow {
|
||||
$checkbox.Content = $preset.Name
|
||||
$checkbox.IsThreeState = $true
|
||||
$checkbox.Style = $window.Resources['PresetCheckBoxStyle']
|
||||
$checkbox.ToolTip = "Select $($preset.Name)"
|
||||
$checkbox.ToolTip = Get-Translation -Key 'AppPresetSelectTooltip' -FormatArgs @($preset.Name)
|
||||
$checkbox.SetValue([System.Windows.Automation.AutomationProperties]::NameProperty, $preset.Name)
|
||||
Add-TriStateClickBehavior -CheckBox $checkbox
|
||||
Add-Member -InputObject $checkbox -MemberType NoteProperty -Name 'PresetAppIds' -Value $preset.AppIds
|
||||
@@ -501,12 +502,11 @@ function Show-MainWindow {
|
||||
param($sourceControl, $e)
|
||||
if ($e.Key -eq [System.Windows.Input.Key]::F -and
|
||||
([System.Windows.Input.Keyboard]::Modifiers -band [System.Windows.Input.ModifierKeys]::Control)) {
|
||||
$currentTab = $tabControl.SelectedItem
|
||||
if ($currentTab.Header -eq "App Removal" -and $appSearchBox) {
|
||||
if ($tabControl.SelectedIndex -eq 1 -and $appSearchBox) {
|
||||
$appSearchBox.Focus()
|
||||
$e.Handled = $true
|
||||
}
|
||||
elseif ($currentTab.Header -eq "Tweaks" -and $tweakSearchBox) {
|
||||
elseif ($tabControl.SelectedIndex -eq 2 -and $tweakSearchBox) {
|
||||
$tweakSearchBox.Focus()
|
||||
$e.Handled = $true
|
||||
}
|
||||
@@ -599,9 +599,9 @@ function Show-MainWindow {
|
||||
$usernameValidationMessage.Text
|
||||
}
|
||||
else {
|
||||
"Please enter a valid username."
|
||||
Get-Translation -Key 'InvalidUsernameFallbackMessage'
|
||||
}
|
||||
Show-MessageBox -Message $validationMessage -Title "Invalid Username" -Button 'OK' -Icon 'Warning' | Out-Null
|
||||
Show-MessageBox -Message $validationMessage -Title (Get-Translation -Key 'InvalidUsernameTitle') -Button 'OK' -Icon 'Warning' | Out-Null
|
||||
return $false
|
||||
}
|
||||
return $true
|
||||
@@ -638,7 +638,7 @@ function Show-MainWindow {
|
||||
Invoke-NavigationUpdate
|
||||
|
||||
$window.Dispatcher.BeginInvoke([System.Windows.Threading.DispatcherPriority]::Loaded, [action]{
|
||||
Show-Bubble -TargetControl $reviewChangesBtn -Message 'View the selected changes here'
|
||||
Show-Bubble -TargetControl $reviewChangesBtn -Message (Get-Translation -Key 'BubbleViewChanges')
|
||||
}) | Out-Null
|
||||
})
|
||||
|
||||
@@ -692,7 +692,7 @@ function Show-MainWindow {
|
||||
}
|
||||
|
||||
if (-not $hasAppSelection -and $selectedForwardFeatureIds.Count -eq 0 -and $script:UndoParams.Count -eq 0) {
|
||||
Show-MessageBox -Message 'No changes have been selected, please select at least one option to proceed.' -Title 'No Changes Selected' -Button 'OK' -Icon 'Information'
|
||||
Show-MessageBox -Message (Get-Translation -Key 'NoChangesSelectedMessage') -Title (Get-Translation -Key 'NoChangesSelectedTitle') -Button 'OK' -Icon 'Information'
|
||||
return
|
||||
}
|
||||
|
||||
@@ -764,7 +764,7 @@ function Show-MainWindow {
|
||||
if ($userSelectionCombo -and $userSelectionCombo.Items.Count -gt 0) {
|
||||
$currentUserItem = $userSelectionCombo.Items[0]
|
||||
if ($currentUserItem -is [System.Windows.Controls.ComboBoxItem]) {
|
||||
$currentUserItem.Content = "Current User ($(Get-UserName))"
|
||||
$currentUserItem.Content = Get-Translation -Key 'HomeCurrentUserWithName' -FormatArgs @(Get-UserName)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -810,7 +810,7 @@ function Show-MainWindow {
|
||||
catch {
|
||||
Write-Warning "Error during GUI initialization: $($_.Exception.Message)"
|
||||
Write-Warning "Stack trace: $($_.Exception.StackTrace)"
|
||||
Show-MessageBox -Message "An error occurred during initialization: $($_.Exception.Message)" -Title "Initialization Error" -Button 'OK' -Icon 'Error' | Out-Null
|
||||
Show-MessageBox -Message (Get-Translation -Key 'InitializationErrorMessage' -FormatArgs @($_.Exception.Message)) -Title (Get-Translation -Key 'InitializationErrorTitle') -Button 'OK' -Icon 'Error' | Out-Null
|
||||
}
|
||||
})
|
||||
|
||||
@@ -900,7 +900,7 @@ function Show-MainWindow {
|
||||
# If WhatIf mode is enabled, notify the user that no changes will be made
|
||||
if ($script:Params.ContainsKey("WhatIf")) {
|
||||
$window.Dispatcher.BeginInvoke([System.Windows.Threading.DispatcherPriority]::Loaded, [action]{
|
||||
Show-MessageBox -Message "WhatIf mode is enabled. The script will not make any changes to your system in this mode.`n`nYou can observe the actions that would be taken by the script in the console output." -Title 'WhatIf Mode' -Button 'OK' -Icon 'Information' -Owner $window
|
||||
Show-MessageBox -Message (Get-Translation -Key 'WhatIfModeMessage') -Title (Get-Translation -Key 'WhatIfModeTitle') -Button 'OK' -Icon 'Information' -Owner $window
|
||||
}) | Out-Null
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Shows a themed Windows 11-style message box.
|
||||
|
||||
.DESCRIPTION
|
||||
Falls back to a plain native MessageBox if the themed dialog's own schema fails to
|
||||
localize (a bad %LANG:% marker in MessageBox.xaml, not a missing app-wide translation),
|
||||
so a broken schema can't take down every error dialog in the app at once.
|
||||
|
||||
.OUTPUTS
|
||||
System.String. One of 'OK', 'Cancel', 'Yes', or 'No', matching the pressed button
|
||||
in both the themed dialog and the native fallback.
|
||||
#>
|
||||
function Show-MessageBox {
|
||||
param (
|
||||
@@ -50,6 +59,41 @@ function Show-MessageBox {
|
||||
|
||||
# Load XAML from file
|
||||
$xaml = Get-Content -Path $script:MessageBoxSchema -Raw
|
||||
try {
|
||||
$xaml = ConvertTo-LocalizedXaml -Xaml $xaml
|
||||
}
|
||||
catch {
|
||||
# The themed dialog depends on the schema's own %LANG:% markers resolving. If that ever
|
||||
# breaks (a bad marker added to MessageBoxSchema, not a missing app-wide translation - that
|
||||
# never reaches this function), fall back to a plain native MessageBox rather than losing
|
||||
# the ability to show any error dialog at all.
|
||||
Write-Warning "Falling back to a native message box, themed dialog failed to localize: $($_.Exception.Message)"
|
||||
|
||||
# Hide overlay before showing the fallback (only if this dialog was the one that showed it)
|
||||
if ($overlay -and -not $overlayWasAlreadyVisible) {
|
||||
try {
|
||||
$ownerWindow.Dispatcher.Invoke([action]{ $overlay.Visibility = 'Collapsed' })
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
$nativeButton = switch ($Button) {
|
||||
'OKCancel' { [System.Windows.MessageBoxButton]::OKCancel }
|
||||
'YesNo' { [System.Windows.MessageBoxButton]::YesNo }
|
||||
default { [System.Windows.MessageBoxButton]::OK }
|
||||
}
|
||||
$nativeIcon = switch ($Icon) {
|
||||
'Warning' { [System.Windows.MessageBoxImage]::Warning }
|
||||
'Error' { [System.Windows.MessageBoxImage]::Error }
|
||||
'Question' { [System.Windows.MessageBoxImage]::Question }
|
||||
'Information' { [System.Windows.MessageBoxImage]::Information }
|
||||
'Success' { [System.Windows.MessageBoxImage]::Information }
|
||||
default { [System.Windows.MessageBoxImage]::None }
|
||||
}
|
||||
|
||||
$nativeResult = [System.Windows.MessageBox]::Show($Message, $Title, $nativeButton, $nativeIcon)
|
||||
return [string]$nativeResult
|
||||
}
|
||||
$reader = [System.Xml.XmlReader]::Create([System.IO.StringReader]::new($xaml))
|
||||
try {
|
||||
$msgWindow = [System.Windows.Markup.XamlReader]::Load($reader)
|
||||
@@ -121,22 +165,22 @@ function Show-MessageBox {
|
||||
# Configure buttons - store result in window's Tag property
|
||||
switch ($Button) {
|
||||
'OK' {
|
||||
$button1.Content = 'OK'
|
||||
$button1.Content = Get-Translation -Key 'MessageBoxOk'
|
||||
$button1.Add_Click({ $msgWindow.Tag = 'OK'; $msgWindow.Close() })
|
||||
$button2.Visibility = 'Collapsed'
|
||||
# Right-align sole button by moving it to column 1
|
||||
[System.Windows.Controls.Grid]::SetColumn($button1, 1)
|
||||
}
|
||||
'OKCancel' {
|
||||
$button1.Content = 'OK'
|
||||
$button2.Content = 'Cancel'
|
||||
$button1.Content = Get-Translation -Key 'MessageBoxOk'
|
||||
$button2.Content = Get-Translation -Key 'MessageBoxCancel'
|
||||
$button1.Add_Click({ $msgWindow.Tag = 'OK'; $msgWindow.Close() })
|
||||
$button2.Add_Click({ $msgWindow.Tag = 'Cancel'; $msgWindow.Close() })
|
||||
$button2.Visibility = 'Visible'
|
||||
}
|
||||
'YesNo' {
|
||||
$button1.Content = 'Yes'
|
||||
$button2.Content = 'No'
|
||||
$button1.Content = Get-Translation -Key 'MessageBoxYes'
|
||||
$button2.Content = Get-Translation -Key 'MessageBoxNo'
|
||||
$button1.Add_Click({ $msgWindow.Tag = 'Yes'; $msgWindow.Close() })
|
||||
$button2.Add_Click({ $msgWindow.Tag = 'No'; $msgWindow.Close() })
|
||||
$button2.Visibility = 'Visible'
|
||||
|
||||
@@ -44,422 +44,429 @@ function Show-RestoreBackupDialog {
|
||||
catch { }
|
||||
}
|
||||
|
||||
$schemaPath = $script:RestoreBackupWindowSchema
|
||||
if (-not $schemaPath -or -not (Test-Path $schemaPath)) {
|
||||
throw 'Restore backup window schema file could not be found.'
|
||||
}
|
||||
|
||||
$xaml = Get-Content -Path $schemaPath -Raw
|
||||
|
||||
$reader = [System.Xml.XmlReader]::Create([System.IO.StringReader]::new($xaml))
|
||||
# Everything from here through ShowDialog() sits inside one outer try/finally. The overlay
|
||||
# was already made visible above, and nothing hides it again until ShowDialog() returns, so
|
||||
# a failure anywhere in schema loading, control wiring, or event registration would otherwise
|
||||
# leave the owner window permanently dimmed.
|
||||
try {
|
||||
$window = [System.Windows.Markup.XamlReader]::Load($reader)
|
||||
}
|
||||
finally {
|
||||
$reader.Close()
|
||||
}
|
||||
$schemaPath = $script:RestoreBackupWindowSchema
|
||||
if (-not $schemaPath -or -not (Test-Path $schemaPath)) {
|
||||
throw 'Restore backup window schema file could not be found.'
|
||||
}
|
||||
|
||||
if ($ownerWindow) {
|
||||
$xaml = Get-Content -Path $schemaPath -Raw
|
||||
$xaml = ConvertTo-LocalizedXaml -Xaml $xaml
|
||||
|
||||
$reader = [System.Xml.XmlReader]::Create([System.IO.StringReader]::new($xaml))
|
||||
try {
|
||||
$window.Owner = $ownerWindow
|
||||
$window = [System.Windows.Markup.XamlReader]::Load($reader)
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
try {
|
||||
Set-WindowThemeResources -window $window -usesDarkMode $usesDarkMode
|
||||
}
|
||||
catch { }
|
||||
|
||||
$titleBar = $window.FindName('TitleBar')
|
||||
$titleText = $window.FindName('TitleText')
|
||||
$closeBtn = $window.FindName('CloseBtn')
|
||||
$backBtn = $window.FindName('BackBtn')
|
||||
$primaryActionBtn = $window.FindName('PrimaryActionBtn')
|
||||
$chooseRegistryBtn = $window.FindName('ChooseRegistryBtn')
|
||||
$chooseStartMenuBtn = $window.FindName('ChooseStartMenuBtn')
|
||||
$restoreModeTabs = $window.FindName('RestoreModeTabs')
|
||||
$startMenuIntroPanel = $window.FindName('StartMenuIntroPanel')
|
||||
$startMenuScopeCombo = $window.FindName('StartMenuScopeCombo')
|
||||
$startMenuAutoBackupCheck = $window.FindName('StartMenuAutoBackupCheck')
|
||||
$introInfoPanel = $window.FindName('IntroInfoPanel')
|
||||
$overviewPanel = $window.FindName('OverviewPanel')
|
||||
$overviewFeaturesSection = $window.FindName('OverviewFeaturesSection')
|
||||
$overviewSummaryText = $window.FindName('OverviewSummaryText')
|
||||
$backupFileText = $window.FindName('BackupFileText')
|
||||
$backupCreatedText = $window.FindName('BackupCreatedText')
|
||||
$backupTargetText = $window.FindName('BackupTargetText')
|
||||
$featuresItemsControl = $window.FindName('FeaturesItemsControl')
|
||||
$reappliedSeparator = $window.FindName('ReappliedSeparator')
|
||||
$reappliedPanel = $window.FindName('ReappliedPanel')
|
||||
$reappliedFeaturesItemsControl = $window.FindName('ReappliedFeaturesItemsControl')
|
||||
$nonRevertibleSeparator = $window.FindName('NonRevertibleSeparator')
|
||||
$nonRevertiblePanel = $window.FindName('NonRevertiblePanel')
|
||||
$nonRevertibleFeaturesItemsControl = $window.FindName('NonRevertibleFeaturesItemsControl')
|
||||
$nonRevertibleWikiLink = $window.FindName('NonRevertibleWikiLink')
|
||||
|
||||
$titleBar.Add_MouseLeftButtonDown({ $window.DragMove() })
|
||||
$window.Tag = New-RestoreDialogState
|
||||
$chooseRegistryBtn.IsDefault = $true
|
||||
|
||||
$state = @{ WizardStep = 'SelectType'; SelectedRegistryBackup = $null; SelectedStartMenuBackupFilePath = $null }
|
||||
|
||||
$getStartMenuScopeInfo = {
|
||||
$isAllUsersScope = ($startMenuScopeCombo.SelectedItem.Tag -eq 'AllUsers')
|
||||
$scopeValue = if ($isAllUsersScope) { 'AllUsers' } else { 'CurrentUser' }
|
||||
$summaryScopeText = if ($isAllUsersScope) { 'all users' } else { 'the current user' }
|
||||
|
||||
return [PSCustomObject]@{
|
||||
Scope = $scopeValue
|
||||
Target = $scopeValue
|
||||
SummaryText = $summaryScopeText
|
||||
}
|
||||
}
|
||||
|
||||
$showStartMenuIntroState = {
|
||||
$backupFileText.Text = 'Not selected'
|
||||
$backupCreatedText.Text = 'N/A'
|
||||
$overviewSummaryText.Visibility = 'Collapsed'
|
||||
$overviewPanel.Visibility = 'Collapsed'
|
||||
$startMenuIntroPanel.Visibility = 'Visible'
|
||||
$restoreModeTabs.SelectedIndex = 2
|
||||
}
|
||||
|
||||
$showStartMenuOverviewState = {
|
||||
param([string]$BackupFilePath)
|
||||
|
||||
$scopeInfo = & $getStartMenuScopeInfo
|
||||
$backupTargetText.Text = Get-FriendlyRegistryBackupTarget -Target $scopeInfo.Target
|
||||
$overviewSummaryText.Text = "This will replace the current Start Menu pinned apps layout for $($scopeInfo.SummaryText) with the selected backup."
|
||||
$backupFileText.Text = Split-Path -Path $BackupFilePath -Leaf
|
||||
|
||||
$createdText = 'Unknown'
|
||||
try {
|
||||
$createdText = (Get-Item -LiteralPath $BackupFilePath -ErrorAction Stop).LastWriteTime.ToString('yyyy-MM-dd HH:mm')
|
||||
}
|
||||
catch { }
|
||||
$backupCreatedText.Text = $createdText
|
||||
|
||||
$overviewFeaturesSection.Visibility = 'Collapsed'
|
||||
$overviewSummaryText.Visibility = 'Visible'
|
||||
$reappliedSeparator.Visibility = 'Collapsed'
|
||||
$reappliedPanel.Visibility = 'Collapsed'
|
||||
$nonRevertibleSeparator.Visibility = 'Collapsed'
|
||||
$nonRevertiblePanel.Visibility = 'Collapsed'
|
||||
$introInfoPanel.Visibility = 'Collapsed'
|
||||
$overviewPanel.Visibility = 'Visible'
|
||||
$restoreModeTabs.SelectedIndex = 1
|
||||
}
|
||||
|
||||
$updateStartMenuOverviewPanel = {
|
||||
if ($state.WizardStep -ne 'StartMenu') {
|
||||
return
|
||||
finally {
|
||||
$reader.Close()
|
||||
}
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($state.SelectedStartMenuBackupFilePath)) {
|
||||
& $showStartMenuIntroState
|
||||
return
|
||||
}
|
||||
|
||||
& $showStartMenuOverviewState $state.SelectedStartMenuBackupFilePath
|
||||
}
|
||||
|
||||
$updateStartMenuPrimaryActionText = {
|
||||
if ($state.WizardStep -ne 'StartMenu') {
|
||||
return
|
||||
}
|
||||
|
||||
$isAutoBackupEnabled = ($startMenuAutoBackupCheck.IsChecked -eq $true)
|
||||
$hasSelectedManualFile = -not [string]::IsNullOrWhiteSpace($state.SelectedStartMenuBackupFilePath)
|
||||
if ($isAutoBackupEnabled -or $hasSelectedManualFile) {
|
||||
$primaryActionBtn.Content = 'Restore backup'
|
||||
}
|
||||
else {
|
||||
$primaryActionBtn.Content = 'Select backup file'
|
||||
}
|
||||
}
|
||||
|
||||
$refreshStartMenuUi = {
|
||||
& $updateStartMenuOverviewPanel
|
||||
& $updateStartMenuPrimaryActionText
|
||||
}
|
||||
|
||||
$enterSelectTypeStep = {
|
||||
$titleText.Text = 'Restore Backup'
|
||||
$restoreModeTabs.SelectedIndex = 0
|
||||
$backBtn.Visibility = 'Visible'
|
||||
$backBtn.Content = 'Cancel'
|
||||
$primaryActionBtn.Visibility = 'Collapsed'
|
||||
$chooseRegistryBtn.IsDefault = $true
|
||||
$primaryActionBtn.IsDefault = $false
|
||||
}
|
||||
|
||||
$enterRegistryStep = {
|
||||
$titleText.Text = 'Restore Registry Backup'
|
||||
$restoreModeTabs.SelectedIndex = 1
|
||||
$introInfoPanel.Visibility = 'Visible'
|
||||
$overviewPanel.Visibility = 'Collapsed'
|
||||
$overviewFeaturesSection.Visibility = 'Visible'
|
||||
$overviewSummaryText.Visibility = 'Collapsed'
|
||||
$backBtn.Visibility = 'Visible'
|
||||
$backBtn.Content = 'Back'
|
||||
$primaryActionBtn.Visibility = 'Visible'
|
||||
$primaryActionBtn.Content = 'Select backup file'
|
||||
$primaryActionBtn.IsDefault = $true
|
||||
$chooseRegistryBtn.IsDefault = $false
|
||||
}
|
||||
|
||||
$enterStartMenuStep = {
|
||||
$titleText.Text = 'Restore Start Menu Backup'
|
||||
$restoreModeTabs.SelectedIndex = 2
|
||||
$backBtn.Visibility = 'Visible'
|
||||
$backBtn.Content = 'Back'
|
||||
$primaryActionBtn.Visibility = 'Visible'
|
||||
$primaryActionBtn.IsDefault = $true
|
||||
$chooseRegistryBtn.IsDefault = $false
|
||||
|
||||
# Show intro panel so user can configure scope & auto-detect
|
||||
$startMenuAutoBackupCheck.IsChecked = $true
|
||||
$state.SelectedStartMenuBackupFilePath = $null
|
||||
& $refreshStartMenuUi
|
||||
}
|
||||
|
||||
$showRegistryOverview = {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
$SelectedBackup,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$SelectedBackupFilePath
|
||||
)
|
||||
|
||||
$createdText = if ([string]::IsNullOrWhiteSpace($SelectedBackup.CreatedAt)) {
|
||||
'Unknown'
|
||||
}
|
||||
else {
|
||||
if ($ownerWindow) {
|
||||
try {
|
||||
[DateTime]::Parse($SelectedBackup.CreatedAt).ToString('yyyy-MM-dd HH:mm')
|
||||
}
|
||||
catch {
|
||||
$SelectedBackup.CreatedAt
|
||||
$window.Owner = $ownerWindow
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
$selectedForwardFeatureIds = @(Get-SelectedForwardFeatureIdsFromBackup -SelectedBackup $SelectedBackup)
|
||||
$selectedUndoFeatureIds = @(Get-SelectedUndoFeatureIdsFromBackup -SelectedBackup $SelectedBackup)
|
||||
|
||||
$seenForwardFeatureIds = New-Object 'System.Collections.Generic.HashSet[string]' ([System.StringComparer]::OrdinalIgnoreCase)
|
||||
foreach ($featureId in $selectedForwardFeatureIds) {
|
||||
[void]$seenForwardFeatureIds.Add([string]$featureId)
|
||||
}
|
||||
|
||||
$filteredUndoFeatureIds = New-Object System.Collections.Generic.List[string]
|
||||
foreach ($featureId in $selectedUndoFeatureIds) {
|
||||
if ($seenForwardFeatureIds.Contains([string]$featureId)) {
|
||||
continue
|
||||
}
|
||||
|
||||
$filteredUndoFeatureIds.Add([string]$featureId)
|
||||
}
|
||||
|
||||
$forwardFeatureLists = Get-RestoreBackupFeatureLists -SelectedFeatureIds $selectedForwardFeatureIds -Features $script:Features
|
||||
$undoFeatureLists = Get-RestoreBackupFeatureLists -SelectedFeatureIds @($filteredUndoFeatureIds.ToArray()) -Features $script:Features
|
||||
$combinedFeatureLists = Get-RestoreBackupFeatureLists -SelectedFeatureIds (Get-SelectedFeatureIdsFromBackup -SelectedBackup $SelectedBackup) -Features $script:Features
|
||||
|
||||
$revertibleFeaturesList = @($forwardFeatureLists.Revertible)
|
||||
$reappliedFeaturesList = @($undoFeatureLists.Revertible)
|
||||
$nonRevertibleFeaturesList = @($combinedFeatureLists.NonRevertible)
|
||||
Write-Host "Backup overview prepared. Reverted=$($revertibleFeaturesList.Count), ReApplied=$($reappliedFeaturesList.Count), NonRevertible=$($nonRevertibleFeaturesList.Count)"
|
||||
|
||||
if ($revertibleFeaturesList.Count -eq 0 -and $reappliedFeaturesList.Count -eq 0) {
|
||||
throw 'The selected backup does not contain any changes that can be restored.'
|
||||
}
|
||||
|
||||
$backupFileText.Text = Split-Path $SelectedBackupFilePath -Leaf
|
||||
$backupCreatedText.Text = $createdText
|
||||
$backupTargetText.Text = Get-FriendlyRegistryBackupTarget -Target ([string]$SelectedBackup.Target)
|
||||
$featuresItemsControl.ItemsSource = $revertibleFeaturesList
|
||||
$overviewFeaturesSection.Visibility = if ($revertibleFeaturesList.Count -gt 0) { 'Visible' } else { 'Collapsed' }
|
||||
$reappliedFeaturesItemsControl.ItemsSource = $reappliedFeaturesList
|
||||
if ($reappliedFeaturesList.Count -gt 0) { $reappliedPanel.Visibility = 'Visible' } else { $reappliedPanel.Visibility = 'Collapsed' }
|
||||
if ($revertibleFeaturesList.Count -gt 0 -and $reappliedFeaturesList.Count -gt 0) { $reappliedSeparator.Visibility = 'Visible' } else { $reappliedSeparator.Visibility = 'Collapsed' }
|
||||
$overviewSummaryText.Visibility = 'Collapsed'
|
||||
$nonRevertibleFeaturesItemsControl.ItemsSource = $nonRevertibleFeaturesList
|
||||
|
||||
$hasNonRevertibleItems = ($nonRevertibleFeaturesList.Count -gt 0)
|
||||
if ($hasNonRevertibleItems) { $nonRevertiblePanel.Visibility = 'Visible' } else { $nonRevertiblePanel.Visibility = 'Collapsed' }
|
||||
if ($hasNonRevertibleItems -and ($revertibleFeaturesList.Count -gt 0 -or $reappliedFeaturesList.Count -gt 0)) { $nonRevertibleSeparator.Visibility = 'Visible' } else { $nonRevertibleSeparator.Visibility = 'Collapsed' }
|
||||
$introInfoPanel.Visibility = 'Collapsed'
|
||||
$overviewPanel.Visibility = 'Visible'
|
||||
|
||||
return $true
|
||||
}
|
||||
|
||||
$handleRegistryPrimaryAction = {
|
||||
if ($state.SelectedRegistryBackup) {
|
||||
$window.Tag = @{
|
||||
Result = 'RestoreRegistry'
|
||||
Backup = $state.SelectedRegistryBackup
|
||||
}
|
||||
$window.DialogResult = $true
|
||||
$window.Close()
|
||||
return
|
||||
}
|
||||
|
||||
$openDialog = New-Object Microsoft.Win32.OpenFileDialog
|
||||
$openDialog.Title = 'Select Registry Backup File'
|
||||
$openDialog.Filter = 'Registry backup (*.json)|*.json'
|
||||
$openDialog.DefaultExt = '.json'
|
||||
$openDialog.InitialDirectory = $script:RegistryBackupsPath
|
||||
|
||||
if ($openDialog.ShowDialog($window) -ne $true) {
|
||||
return
|
||||
}
|
||||
|
||||
Write-Host "Backup file selected: $($openDialog.FileName)"
|
||||
|
||||
try {
|
||||
$selectedBackup = Import-RegistryBackup -FilePath $openDialog.FileName
|
||||
Set-WindowThemeResources -window $window -usesDarkMode $usesDarkMode
|
||||
}
|
||||
catch { }
|
||||
|
||||
if (-not (& $showRegistryOverview -SelectedBackup $selectedBackup -SelectedBackupFilePath $openDialog.FileName)) {
|
||||
$titleBar = $window.FindName('TitleBar')
|
||||
$titleText = $window.FindName('TitleText')
|
||||
$closeBtn = $window.FindName('CloseBtn')
|
||||
$backBtn = $window.FindName('BackBtn')
|
||||
$primaryActionBtn = $window.FindName('PrimaryActionBtn')
|
||||
$chooseRegistryBtn = $window.FindName('ChooseRegistryBtn')
|
||||
$chooseStartMenuBtn = $window.FindName('ChooseStartMenuBtn')
|
||||
$restoreModeTabs = $window.FindName('RestoreModeTabs')
|
||||
$startMenuIntroPanel = $window.FindName('StartMenuIntroPanel')
|
||||
$startMenuScopeCombo = $window.FindName('StartMenuScopeCombo')
|
||||
$startMenuAutoBackupCheck = $window.FindName('StartMenuAutoBackupCheck')
|
||||
$introInfoPanel = $window.FindName('IntroInfoPanel')
|
||||
$overviewPanel = $window.FindName('OverviewPanel')
|
||||
$overviewFeaturesSection = $window.FindName('OverviewFeaturesSection')
|
||||
$overviewSummaryText = $window.FindName('OverviewSummaryText')
|
||||
$backupFileText = $window.FindName('BackupFileText')
|
||||
$backupCreatedText = $window.FindName('BackupCreatedText')
|
||||
$backupTargetText = $window.FindName('BackupTargetText')
|
||||
$featuresItemsControl = $window.FindName('FeaturesItemsControl')
|
||||
$reappliedSeparator = $window.FindName('ReappliedSeparator')
|
||||
$reappliedPanel = $window.FindName('ReappliedPanel')
|
||||
$reappliedFeaturesItemsControl = $window.FindName('ReappliedFeaturesItemsControl')
|
||||
$nonRevertibleSeparator = $window.FindName('NonRevertibleSeparator')
|
||||
$nonRevertiblePanel = $window.FindName('NonRevertiblePanel')
|
||||
$nonRevertibleFeaturesItemsControl = $window.FindName('NonRevertibleFeaturesItemsControl')
|
||||
$nonRevertibleWikiLink = $window.FindName('NonRevertibleWikiLink')
|
||||
|
||||
$titleBar.Add_MouseLeftButtonDown({ $window.DragMove() })
|
||||
$window.Tag = New-RestoreDialogState
|
||||
$chooseRegistryBtn.IsDefault = $true
|
||||
|
||||
$state = @{ WizardStep = 'SelectType'; SelectedRegistryBackup = $null; SelectedStartMenuBackupFilePath = $null }
|
||||
|
||||
$getStartMenuScopeInfo = {
|
||||
$isAllUsersScope = ($startMenuScopeCombo.SelectedItem.Tag -eq 'AllUsers')
|
||||
$scopeValue = if ($isAllUsersScope) { 'AllUsers' } else { 'CurrentUser' }
|
||||
$summaryScopeText = if ($isAllUsersScope) { Get-Translation -Key 'RestoreStartMenuSummaryScopeAllUsers' } else { Get-Translation -Key 'RestoreStartMenuSummaryScopeCurrentUser' }
|
||||
|
||||
return [PSCustomObject]@{
|
||||
Scope = $scopeValue
|
||||
Target = $scopeValue
|
||||
SummaryText = $summaryScopeText
|
||||
}
|
||||
}
|
||||
|
||||
$showStartMenuIntroState = {
|
||||
$backupFileText.Text = Get-Translation -Key 'RestoreFileNotSelected'
|
||||
$backupCreatedText.Text = Get-Translation -Key 'RestoreCreatedUnavailable'
|
||||
$overviewSummaryText.Visibility = 'Collapsed'
|
||||
$overviewPanel.Visibility = 'Collapsed'
|
||||
$startMenuIntroPanel.Visibility = 'Visible'
|
||||
$restoreModeTabs.SelectedIndex = 2
|
||||
}
|
||||
|
||||
$showStartMenuOverviewState = {
|
||||
param([string]$BackupFilePath)
|
||||
|
||||
$scopeInfo = & $getStartMenuScopeInfo
|
||||
$backupTargetText.Text = Get-FriendlyRegistryBackupTarget -Target $scopeInfo.Target
|
||||
$overviewSummaryText.Text = Get-Translation -Key 'RestoreStartMenuSummary' -FormatArgs @($scopeInfo.SummaryText)
|
||||
$backupFileText.Text = Split-Path -Path $BackupFilePath -Leaf
|
||||
|
||||
$createdText = Get-Translation -Key 'RestoreCreatedUnknown'
|
||||
try {
|
||||
$createdText = (Get-Item -LiteralPath $BackupFilePath -ErrorAction Stop).LastWriteTime.ToString('yyyy-MM-dd HH:mm')
|
||||
}
|
||||
catch { }
|
||||
$backupCreatedText.Text = $createdText
|
||||
|
||||
$overviewFeaturesSection.Visibility = 'Collapsed'
|
||||
$overviewSummaryText.Visibility = 'Visible'
|
||||
$reappliedSeparator.Visibility = 'Collapsed'
|
||||
$reappliedPanel.Visibility = 'Collapsed'
|
||||
$nonRevertibleSeparator.Visibility = 'Collapsed'
|
||||
$nonRevertiblePanel.Visibility = 'Collapsed'
|
||||
$introInfoPanel.Visibility = 'Collapsed'
|
||||
$overviewPanel.Visibility = 'Visible'
|
||||
$restoreModeTabs.SelectedIndex = 1
|
||||
}
|
||||
|
||||
$updateStartMenuOverviewPanel = {
|
||||
if ($state.WizardStep -ne 'StartMenu') {
|
||||
return
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Show-MessageBox -Owner $window -Title 'Invalid Backup File' -Message "The selected file could not be loaded:`n$($_.Exception.Message)" -Button 'OK' -Icon 'Error' | Out-Null
|
||||
return
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($state.SelectedStartMenuBackupFilePath)) {
|
||||
& $showStartMenuIntroState
|
||||
return
|
||||
}
|
||||
|
||||
& $showStartMenuOverviewState $state.SelectedStartMenuBackupFilePath
|
||||
}
|
||||
|
||||
$state.SelectedRegistryBackup = $selectedBackup
|
||||
$primaryActionBtn.Content = 'Restore from backup'
|
||||
}
|
||||
$updateStartMenuPrimaryActionText = {
|
||||
if ($state.WizardStep -ne 'StartMenu') {
|
||||
return
|
||||
}
|
||||
|
||||
$handleStartMenuPrimaryAction = {
|
||||
$scope = (& $getStartMenuScopeInfo).Scope
|
||||
$useManualBackupFile = -not ($startMenuAutoBackupCheck.IsChecked -eq $true)
|
||||
$isAutoBackupEnabled = ($startMenuAutoBackupCheck.IsChecked -eq $true)
|
||||
$hasSelectedManualFile = -not [string]::IsNullOrWhiteSpace($state.SelectedStartMenuBackupFilePath)
|
||||
if ($isAutoBackupEnabled -or $hasSelectedManualFile) {
|
||||
$primaryActionBtn.Content = Get-Translation -Key 'RestorePrimaryActionRestoreBackup'
|
||||
}
|
||||
else {
|
||||
$primaryActionBtn.Content = Get-Translation -Key 'RestorePrimaryActionSelectBackupFile'
|
||||
}
|
||||
}
|
||||
|
||||
$refreshStartMenuUi = {
|
||||
& $updateStartMenuOverviewPanel
|
||||
& $updateStartMenuPrimaryActionText
|
||||
}
|
||||
|
||||
$enterSelectTypeStep = {
|
||||
$titleText.Text = Get-Translation -Key 'RestoreWindowTitle'
|
||||
$restoreModeTabs.SelectedIndex = 0
|
||||
$backBtn.Visibility = 'Visible'
|
||||
$backBtn.Content = Get-Translation -Key 'RestoreBackBtnCancel'
|
||||
$primaryActionBtn.Visibility = 'Collapsed'
|
||||
$chooseRegistryBtn.IsDefault = $true
|
||||
$primaryActionBtn.IsDefault = $false
|
||||
}
|
||||
|
||||
$enterRegistryStep = {
|
||||
$titleText.Text = Get-Translation -Key 'RestoreRegistryStepTitle'
|
||||
$restoreModeTabs.SelectedIndex = 1
|
||||
$introInfoPanel.Visibility = 'Visible'
|
||||
$overviewPanel.Visibility = 'Collapsed'
|
||||
$overviewFeaturesSection.Visibility = 'Visible'
|
||||
$overviewSummaryText.Visibility = 'Collapsed'
|
||||
$backBtn.Visibility = 'Visible'
|
||||
$backBtn.Content = Get-Translation -Key 'RestoreBackBtnBack'
|
||||
$primaryActionBtn.Visibility = 'Visible'
|
||||
$primaryActionBtn.Content = Get-Translation -Key 'RestorePrimaryActionSelectBackupFile'
|
||||
$primaryActionBtn.IsDefault = $true
|
||||
$chooseRegistryBtn.IsDefault = $false
|
||||
}
|
||||
|
||||
$enterStartMenuStep = {
|
||||
$titleText.Text = Get-Translation -Key 'RestoreStartMenuStepTitle'
|
||||
$restoreModeTabs.SelectedIndex = 2
|
||||
$backBtn.Visibility = 'Visible'
|
||||
$backBtn.Content = Get-Translation -Key 'RestoreBackBtnBack'
|
||||
$primaryActionBtn.Visibility = 'Visible'
|
||||
$primaryActionBtn.IsDefault = $true
|
||||
$chooseRegistryBtn.IsDefault = $false
|
||||
|
||||
# Show intro panel so user can configure scope & auto-detect
|
||||
$startMenuAutoBackupCheck.IsChecked = $true
|
||||
$state.SelectedStartMenuBackupFilePath = $null
|
||||
& $refreshStartMenuUi
|
||||
}
|
||||
|
||||
$showRegistryOverview = {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
$SelectedBackup,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$SelectedBackupFilePath
|
||||
)
|
||||
|
||||
$createdText = if ([string]::IsNullOrWhiteSpace($SelectedBackup.CreatedAt)) {
|
||||
Get-Translation -Key 'RestoreCreatedUnknown'
|
||||
}
|
||||
else {
|
||||
try {
|
||||
[DateTime]::Parse($SelectedBackup.CreatedAt).ToString('yyyy-MM-dd HH:mm')
|
||||
}
|
||||
catch {
|
||||
$SelectedBackup.CreatedAt
|
||||
}
|
||||
}
|
||||
|
||||
$selectedForwardFeatureIds = @(Get-SelectedForwardFeatureIdsFromBackup -SelectedBackup $SelectedBackup)
|
||||
$selectedUndoFeatureIds = @(Get-SelectedUndoFeatureIdsFromBackup -SelectedBackup $SelectedBackup)
|
||||
|
||||
$seenForwardFeatureIds = New-Object 'System.Collections.Generic.HashSet[string]' ([System.StringComparer]::OrdinalIgnoreCase)
|
||||
foreach ($featureId in $selectedForwardFeatureIds) {
|
||||
[void]$seenForwardFeatureIds.Add([string]$featureId)
|
||||
}
|
||||
|
||||
$filteredUndoFeatureIds = New-Object System.Collections.Generic.List[string]
|
||||
foreach ($featureId in $selectedUndoFeatureIds) {
|
||||
if ($seenForwardFeatureIds.Contains([string]$featureId)) {
|
||||
continue
|
||||
}
|
||||
|
||||
$filteredUndoFeatureIds.Add([string]$featureId)
|
||||
}
|
||||
|
||||
$forwardFeatureLists = Get-RestoreBackupFeatureLists -SelectedFeatureIds $selectedForwardFeatureIds -Features $script:Features
|
||||
$undoFeatureLists = Get-RestoreBackupFeatureLists -SelectedFeatureIds @($filteredUndoFeatureIds.ToArray()) -Features $script:Features
|
||||
$combinedFeatureLists = Get-RestoreBackupFeatureLists -SelectedFeatureIds (Get-SelectedFeatureIdsFromBackup -SelectedBackup $SelectedBackup) -Features $script:Features
|
||||
|
||||
$revertibleFeaturesList = @($forwardFeatureLists.Revertible)
|
||||
$reappliedFeaturesList = @($undoFeatureLists.Revertible)
|
||||
$nonRevertibleFeaturesList = @($combinedFeatureLists.NonRevertible)
|
||||
Write-Host "Backup overview prepared. Reverted=$($revertibleFeaturesList.Count), ReApplied=$($reappliedFeaturesList.Count), NonRevertible=$($nonRevertibleFeaturesList.Count)"
|
||||
|
||||
if ($revertibleFeaturesList.Count -eq 0 -and $reappliedFeaturesList.Count -eq 0) {
|
||||
throw (Get-Translation -Key 'RestoreNoRestorableChangesError')
|
||||
}
|
||||
|
||||
$backupFileText.Text = Split-Path $SelectedBackupFilePath -Leaf
|
||||
$backupCreatedText.Text = $createdText
|
||||
$backupTargetText.Text = Get-FriendlyRegistryBackupTarget -Target ([string]$SelectedBackup.Target)
|
||||
$featuresItemsControl.ItemsSource = $revertibleFeaturesList
|
||||
$overviewFeaturesSection.Visibility = if ($revertibleFeaturesList.Count -gt 0) { 'Visible' } else { 'Collapsed' }
|
||||
$reappliedFeaturesItemsControl.ItemsSource = $reappliedFeaturesList
|
||||
if ($reappliedFeaturesList.Count -gt 0) { $reappliedPanel.Visibility = 'Visible' } else { $reappliedPanel.Visibility = 'Collapsed' }
|
||||
if ($revertibleFeaturesList.Count -gt 0 -and $reappliedFeaturesList.Count -gt 0) { $reappliedSeparator.Visibility = 'Visible' } else { $reappliedSeparator.Visibility = 'Collapsed' }
|
||||
$overviewSummaryText.Visibility = 'Collapsed'
|
||||
$nonRevertibleFeaturesItemsControl.ItemsSource = $nonRevertibleFeaturesList
|
||||
|
||||
$hasNonRevertibleItems = ($nonRevertibleFeaturesList.Count -gt 0)
|
||||
if ($hasNonRevertibleItems) { $nonRevertiblePanel.Visibility = 'Visible' } else { $nonRevertiblePanel.Visibility = 'Collapsed' }
|
||||
if ($hasNonRevertibleItems -and ($revertibleFeaturesList.Count -gt 0 -or $reappliedFeaturesList.Count -gt 0)) { $nonRevertibleSeparator.Visibility = 'Visible' } else { $nonRevertibleSeparator.Visibility = 'Collapsed' }
|
||||
$introInfoPanel.Visibility = 'Collapsed'
|
||||
$overviewPanel.Visibility = 'Visible'
|
||||
|
||||
return $true
|
||||
}
|
||||
|
||||
$handleRegistryPrimaryAction = {
|
||||
if ($state.SelectedRegistryBackup) {
|
||||
$window.Tag = @{
|
||||
Result = 'RestoreRegistry'
|
||||
Backup = $state.SelectedRegistryBackup
|
||||
}
|
||||
$window.DialogResult = $true
|
||||
$window.Close()
|
||||
return
|
||||
}
|
||||
|
||||
if ($useManualBackupFile -and [string]::IsNullOrWhiteSpace($state.SelectedStartMenuBackupFilePath)) {
|
||||
$openDialog = New-Object Microsoft.Win32.OpenFileDialog
|
||||
$openDialog.Title = 'Select Start Menu Backup File'
|
||||
$openDialog.Filter = 'Start Menu backup (*.bak)|*.bak'
|
||||
$openDialog.InitialDirectory = "$env:LOCALAPPDATA\Packages\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\LocalState"
|
||||
$openDialog.DefaultExt = '.bak'
|
||||
$openDialog.Title = Get-Translation -Key 'RestoreSelectRegistryFileDialogTitle'
|
||||
$openDialog.Filter = 'Registry backup (*.json)|*.json'
|
||||
$openDialog.DefaultExt = '.json'
|
||||
$openDialog.InitialDirectory = $script:RegistryBackupsPath
|
||||
|
||||
if ($openDialog.ShowDialog($window) -ne $true) {
|
||||
return
|
||||
}
|
||||
|
||||
$state.SelectedStartMenuBackupFilePath = $openDialog.FileName
|
||||
Write-Host "Selected Start Menu backup file: $($state.SelectedStartMenuBackupFilePath)"
|
||||
& $refreshStartMenuUi
|
||||
return
|
||||
}
|
||||
Write-Host "Backup file selected: $($openDialog.FileName)"
|
||||
|
||||
if (-not $useManualBackupFile) {
|
||||
$scopeInfo = & $getStartMenuScopeInfo
|
||||
$autoBackupPath = Get-StartMenuBackupPath -Scope $scopeInfo.Scope
|
||||
if ($null -eq $autoBackupPath) {
|
||||
$scopeText = $scopeInfo.SummaryText
|
||||
Show-MessageBox -Owner $window -Title 'No Backup Found' -Message "No Start Menu backup file was found for $scopeText. Uncheck 'Automatically find Start Menu backup' to select a backup file manually." -Button 'OK' -Icon 'Warning' | Out-Null
|
||||
try {
|
||||
$selectedBackup = Import-RegistryBackup -FilePath $openDialog.FileName
|
||||
|
||||
if (-not (& $showRegistryOverview -SelectedBackup $selectedBackup -SelectedBackupFilePath $openDialog.FileName)) {
|
||||
return
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Show-MessageBox -Owner $window -Title (Get-Translation -Key 'RestoreInvalidBackupTitle') -Message (Get-Translation -Key 'RestoreInvalidBackupMessage' -FormatArgs @($_.Exception.Message)) -Button 'OK' -Icon 'Error' | Out-Null
|
||||
return
|
||||
}
|
||||
$state.SelectedStartMenuBackupFilePath = if ($scopeInfo.Scope -eq 'CurrentUser') { $autoBackupPath } else { $null }
|
||||
|
||||
$state.SelectedRegistryBackup = $selectedBackup
|
||||
$primaryActionBtn.Content = Get-Translation -Key 'RestorePrimaryActionRestoreFromBackup'
|
||||
}
|
||||
|
||||
$window.Tag = @{
|
||||
Result = 'Restore-StartMenu'
|
||||
StartMenuScope = $scope
|
||||
UseManualBackupFile = $useManualBackupFile
|
||||
BackupFilePath = $state.SelectedStartMenuBackupFilePath
|
||||
$handleStartMenuPrimaryAction = {
|
||||
$scope = (& $getStartMenuScopeInfo).Scope
|
||||
$useManualBackupFile = -not ($startMenuAutoBackupCheck.IsChecked -eq $true)
|
||||
|
||||
if ($useManualBackupFile -and [string]::IsNullOrWhiteSpace($state.SelectedStartMenuBackupFilePath)) {
|
||||
$openDialog = New-Object Microsoft.Win32.OpenFileDialog
|
||||
$openDialog.Title = Get-Translation -Key 'RestoreSelectStartMenuFileDialogTitle'
|
||||
$openDialog.Filter = 'Start Menu backup (*.bak)|*.bak'
|
||||
$openDialog.InitialDirectory = "$env:LOCALAPPDATA\Packages\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\LocalState"
|
||||
$openDialog.DefaultExt = '.bak'
|
||||
|
||||
if ($openDialog.ShowDialog($window) -ne $true) {
|
||||
return
|
||||
}
|
||||
|
||||
$state.SelectedStartMenuBackupFilePath = $openDialog.FileName
|
||||
Write-Host "Selected Start Menu backup file: $($state.SelectedStartMenuBackupFilePath)"
|
||||
& $refreshStartMenuUi
|
||||
return
|
||||
}
|
||||
|
||||
if (-not $useManualBackupFile) {
|
||||
$scopeInfo = & $getStartMenuScopeInfo
|
||||
$autoBackupPath = Get-StartMenuBackupPath -Scope $scopeInfo.Scope
|
||||
if ($null -eq $autoBackupPath) {
|
||||
$scopeText = $scopeInfo.SummaryText
|
||||
Show-MessageBox -Owner $window -Title (Get-Translation -Key 'RestoreNoBackupFoundTitle') -Message (Get-Translation -Key 'RestoreNoBackupFoundMessage' -FormatArgs @($scopeText)) -Button 'OK' -Icon 'Warning' | Out-Null
|
||||
return
|
||||
}
|
||||
$state.SelectedStartMenuBackupFilePath = if ($scopeInfo.Scope -eq 'CurrentUser') { $autoBackupPath } else { $null }
|
||||
}
|
||||
|
||||
$window.Tag = @{
|
||||
Result = 'Restore-StartMenu'
|
||||
StartMenuScope = $scope
|
||||
UseManualBackupFile = $useManualBackupFile
|
||||
BackupFilePath = $state.SelectedStartMenuBackupFilePath
|
||||
}
|
||||
$window.DialogResult = $true
|
||||
$window.Close()
|
||||
}
|
||||
$window.DialogResult = $true
|
||||
$window.Close()
|
||||
}
|
||||
|
||||
$setWizardStep = {
|
||||
param([string]$step)
|
||||
$setWizardStep = {
|
||||
param([string]$step)
|
||||
|
||||
$state.WizardStep = $step
|
||||
$state.WizardStep = $step
|
||||
|
||||
switch ($step) {
|
||||
'SelectType' { & $enterSelectTypeStep }
|
||||
'Registry' { & $enterRegistryStep }
|
||||
'StartMenu' { & $enterStartMenuStep }
|
||||
switch ($step) {
|
||||
'SelectType' { & $enterSelectTypeStep }
|
||||
'Registry' { & $enterRegistryStep }
|
||||
'StartMenu' { & $enterStartMenuStep }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$startMenuAutoBackupCheck.Add_Checked({
|
||||
$state.SelectedStartMenuBackupFilePath = $null
|
||||
& $refreshStartMenuUi
|
||||
})
|
||||
$startMenuAutoBackupCheck.Add_Unchecked({
|
||||
& $refreshStartMenuUi
|
||||
})
|
||||
$startMenuAutoBackupCheck.Add_Checked({
|
||||
$state.SelectedStartMenuBackupFilePath = $null
|
||||
& $refreshStartMenuUi
|
||||
})
|
||||
$startMenuAutoBackupCheck.Add_Unchecked({
|
||||
& $refreshStartMenuUi
|
||||
})
|
||||
|
||||
$startMenuScopeCombo.Add_SelectionChanged({
|
||||
$state.SelectedStartMenuBackupFilePath = $null
|
||||
& $refreshStartMenuUi
|
||||
})
|
||||
$startMenuScopeCombo.Add_SelectionChanged({
|
||||
$state.SelectedStartMenuBackupFilePath = $null
|
||||
& $refreshStartMenuUi
|
||||
})
|
||||
|
||||
$nonRevertibleWikiLink.Add_MouseLeftButtonUp({
|
||||
try {
|
||||
Start-Process 'https://github.com/Raphire/Win11Debloat/wiki/Reverting-Changes' | Out-Null
|
||||
}
|
||||
catch { }
|
||||
})
|
||||
$nonRevertibleWikiLink.Add_MouseLeftButtonUp({
|
||||
try {
|
||||
Start-Process 'https://github.com/Raphire/Win11Debloat/wiki/Reverting-Changes' | Out-Null
|
||||
}
|
||||
catch { }
|
||||
})
|
||||
|
||||
$closeBtn.Add_Click({
|
||||
$window.Tag = New-RestoreDialogState
|
||||
$window.DialogResult = $false
|
||||
$window.Close()
|
||||
})
|
||||
|
||||
$chooseRegistryBtn.Add_Click({ & $setWizardStep 'Registry' })
|
||||
$chooseStartMenuBtn.Add_Click({ & $setWizardStep 'StartMenu' })
|
||||
|
||||
$backBtn.Add_Click({
|
||||
if ($state.WizardStep -eq 'SelectType') {
|
||||
$closeBtn.Add_Click({
|
||||
$window.Tag = New-RestoreDialogState
|
||||
$window.DialogResult = $false
|
||||
$window.Close()
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
if ($state.WizardStep -eq 'Registry') {
|
||||
$state.SelectedRegistryBackup = $null
|
||||
}
|
||||
$chooseRegistryBtn.Add_Click({ & $setWizardStep 'Registry' })
|
||||
$chooseStartMenuBtn.Add_Click({ & $setWizardStep 'StartMenu' })
|
||||
|
||||
if ($state.WizardStep -eq 'StartMenu') {
|
||||
$state.SelectedStartMenuBackupFilePath = $null
|
||||
$startMenuAutoBackupCheck.IsChecked = $true
|
||||
}
|
||||
$backBtn.Add_Click({
|
||||
if ($state.WizardStep -eq 'SelectType') {
|
||||
$window.Tag = New-RestoreDialogState
|
||||
$window.DialogResult = $false
|
||||
$window.Close()
|
||||
return
|
||||
}
|
||||
|
||||
if ($state.WizardStep -eq 'Registry') {
|
||||
$state.SelectedRegistryBackup = $null
|
||||
}
|
||||
|
||||
if ($state.WizardStep -eq 'StartMenu') {
|
||||
$state.SelectedStartMenuBackupFilePath = $null
|
||||
$startMenuAutoBackupCheck.IsChecked = $true
|
||||
}
|
||||
|
||||
& $setWizardStep 'SelectType'
|
||||
})
|
||||
|
||||
$primaryActionBtn.Add_Click({
|
||||
switch ($state.WizardStep) {
|
||||
'Registry' { & $handleRegistryPrimaryAction }
|
||||
'StartMenu' { & $handleStartMenuPrimaryAction }
|
||||
}
|
||||
})
|
||||
|
||||
$window.Add_KeyDown({
|
||||
param($source, $e)
|
||||
if ($e.Key -eq 'Escape') {
|
||||
$window.Tag = New-RestoreDialogState
|
||||
$window.DialogResult = $false
|
||||
$window.Close()
|
||||
}
|
||||
})
|
||||
|
||||
& $setWizardStep 'SelectType'
|
||||
})
|
||||
|
||||
$primaryActionBtn.Add_Click({
|
||||
switch ($state.WizardStep) {
|
||||
'Registry' { & $handleRegistryPrimaryAction }
|
||||
'StartMenu' { & $handleStartMenuPrimaryAction }
|
||||
try {
|
||||
$null = $window.ShowDialog()
|
||||
}
|
||||
})
|
||||
|
||||
$window.Add_KeyDown({
|
||||
param($source, $e)
|
||||
if ($e.Key -eq 'Escape') {
|
||||
$window.Tag = New-RestoreDialogState
|
||||
$window.DialogResult = $false
|
||||
$window.Close()
|
||||
catch {
|
||||
$innerMessage = if ($_.Exception.InnerException) { $_.Exception.InnerException.Message } else { 'None' }
|
||||
throw "Failed to show restore backup dialog. Error: $($_.Exception.Message) Inner: $innerMessage"
|
||||
}
|
||||
})
|
||||
|
||||
& $setWizardStep 'SelectType'
|
||||
|
||||
try {
|
||||
$null = $window.ShowDialog()
|
||||
}
|
||||
catch {
|
||||
$innerMessage = if ($_.Exception.InnerException) { $_.Exception.InnerException.Message } else { 'None' }
|
||||
throw "Failed to show restore backup dialog. Error: $($_.Exception.Message) Inner: $innerMessage"
|
||||
}
|
||||
finally {
|
||||
if ($overlay -and -not $overlayWasAlreadyVisible) {
|
||||
|
||||
@@ -35,10 +35,10 @@ function Show-RestoreBackupWindow {
|
||||
if ($restoreOpResult -and $restoreOpResult.Result) {
|
||||
$restoreResult.RestoredRegistry = $true
|
||||
if ($script:Params.ContainsKey("WhatIf")) {
|
||||
$successMessage = '[WhatIf] Registry backup would be restored (no changes made).'
|
||||
$successMessage = Get-Translation -Key 'RestoreRegistryWhatIfMessage'
|
||||
}
|
||||
else {
|
||||
$successMessage = 'Registry backup restored successfully. Some changes may require a restart to take effect.'
|
||||
$successMessage = Get-Translation -Key 'RestoreRegistrySuccessMessage'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -70,22 +70,22 @@ function Show-RestoreBackupWindow {
|
||||
|
||||
if ($successCount -eq 0) {
|
||||
$errorSummary = ($resultEntries | ForEach-Object { $_.Message }) -join [Environment]::NewLine
|
||||
throw "Failed to restore the Start Menu backup.`n$errorSummary"
|
||||
throw (Get-Translation -Key 'RestoreStartMenuFailedMessage' -FormatArgs @($errorSummary))
|
||||
}
|
||||
|
||||
if ($failedEntries.Count -gt 0) {
|
||||
$failureSummary = ($failedEntries | ForEach-Object { $_.Message }) -join [Environment]::NewLine
|
||||
$warningMessage = "The Start Menu backup was successfully restored for $successCount user(s).`nSome users could not be restored:`n$failureSummary"
|
||||
$warningMessage = Get-Translation -Key 'RestoreStartMenuPartialSuccessMessage' -FormatArgs @($successCount, $failureSummary)
|
||||
}
|
||||
else {
|
||||
if ($script:Params.ContainsKey("WhatIf")) {
|
||||
$successMessage = '[WhatIf] Start Menu backup would be restored (no changes made).'
|
||||
$successMessage = Get-Translation -Key 'RestoreStartMenuWhatIfMessage'
|
||||
}
|
||||
elseif ($scope -eq 'AllUsers') {
|
||||
$successMessage = "The Start Menu backup was successfully restored for all users. The changes will apply the next time users sign in."
|
||||
$successMessage = Get-Translation -Key 'RestoreStartMenuAllUsersSuccessMessage'
|
||||
}
|
||||
else {
|
||||
$successMessage = "The Start Menu backup was successfully restored for the current user. The changes will apply the next time you sign in."
|
||||
$successMessage = Get-Translation -Key 'RestoreStartMenuCurrentUserSuccessMessage'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,19 +94,19 @@ function Show-RestoreBackupWindow {
|
||||
|
||||
if ($warningMessage) {
|
||||
Write-Host "$warningMessage"
|
||||
Show-MessageBox -Title 'Backup Restored' -Message $warningMessage -Icon Warning
|
||||
Show-MessageBox -Title (Get-Translation -Key 'RestoreBackupRestoredTitle') -Message $warningMessage -Icon Warning
|
||||
}
|
||||
elseif ($successMessage) {
|
||||
Write-Host "$successMessage"
|
||||
Show-MessageBox -Title 'Backup Restored' -Message $successMessage -Icon Success
|
||||
Show-MessageBox -Title (Get-Translation -Key 'RestoreBackupRestoredTitle') -Message $successMessage -Icon Success
|
||||
}
|
||||
|
||||
return $restoreResult
|
||||
}
|
||||
catch {
|
||||
$errorMessage = if ($_.Exception.Message) { $_.Exception.Message } else { 'An unexpected error occurred.' }
|
||||
$errorMessage = if ($_.Exception.Message) { $_.Exception.Message } else { Get-Translation -Key 'RestoreUnexpectedError' }
|
||||
Write-Error "Restore operation failed: $errorMessage"
|
||||
Show-MessageBox -Title 'Error' -Message "Restore failed: $errorMessage" -Icon Error
|
||||
Show-MessageBox -Title (Get-Translation -Key 'ErrorTitle') -Message (Get-Translation -Key 'RestoreOperationFailedMessage' -FormatArgs @($errorMessage)) -Icon Error
|
||||
return [PSCustomObject]@{
|
||||
RestoredRegistry = $false
|
||||
RestoredStartMenu = $false
|
||||
|
||||
Reference in New Issue
Block a user