NewPlusViewModel.cs
1 // Copyright (c) Microsoft Corporation 2 // The Microsoft Corporation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System; 6 using System.Diagnostics; 7 using System.Globalization; 8 using System.IO; 9 using System.Text.Json; 10 using System.Threading.Tasks; 11 using System.Windows; 12 using global::PowerToys.GPOWrapper; 13 using ManagedCommon; 14 using Microsoft.PowerToys.Settings.UI.Library; 15 using Microsoft.PowerToys.Settings.UI.Library.Helpers; 16 using Microsoft.PowerToys.Settings.UI.Library.Interfaces; 17 using Microsoft.PowerToys.Settings.UI.Library.Utilities; 18 using Microsoft.PowerToys.Settings.UI.Library.ViewModels.Commands; 19 using Microsoft.PowerToys.Settings.UI.SerializationContext; 20 21 using static Microsoft.PowerToys.Settings.UI.Helpers.ShellGetFolder; 22 23 namespace Microsoft.PowerToys.Settings.UI.ViewModels 24 { 25 public partial class NewPlusViewModel : Observable 26 { 27 private GeneralSettings GeneralSettingsConfig { get; set; } 28 29 private readonly SettingsUtils _settingsUtils; 30 31 private NewPlusSettings Settings { get; set; } 32 33 private const string ModuleName = NewPlusSettings.ModuleName; 34 35 public NewPlusViewModel(SettingsUtils settingsUtils, ISettingsRepository<GeneralSettings> settingsRepository, Func<string, int> ipcMSGCallBackFunc) 36 { 37 _settingsUtils = settingsUtils ?? throw new ArgumentNullException(nameof(settingsUtils)); 38 39 // To obtain the general settings configurations of PowerToys Settings. 40 ArgumentNullException.ThrowIfNull(settingsRepository); 41 42 GeneralSettingsConfig = settingsRepository.SettingsConfig; 43 44 Settings = LoadSettings(settingsUtils); 45 46 // Initialize properties 47 _hideFileExtension = Settings.Properties.HideFileExtension.Value; 48 _hideStartingDigits = Settings.Properties.HideStartingDigits.Value; 49 _templateLocation = Settings.Properties.TemplateLocation.Value; 50 _replaceVariables = Settings.Properties.ReplaceVariables.Value; 51 InitializeEnabledValue(); 52 InitializeGpoValues(); 53 54 // set the callback functions value to handle outgoing IPC message. 55 SendConfigMSG = ipcMSGCallBackFunc; 56 } 57 58 private void InitializeEnabledValue() 59 { 60 _enabledGpoRuleConfiguration = GPOWrapper.GetConfiguredNewPlusEnabledValue(); 61 if (_enabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled) 62 { 63 // Get the enabled state from GPO. 64 _enabledStateIsGPOConfigured = true; 65 _isNewPlusEnabled = _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled; 66 } 67 else 68 { 69 _isNewPlusEnabled = GeneralSettingsConfig.Enabled.NewPlus; 70 } 71 } 72 73 private void InitializeGpoValues() 74 { 75 // Policy for hide file extension setting 76 _hideFileExtensionGpoRuleConfiguration = GPOWrapper.GetConfiguredNewPlusHideTemplateFilenameExtensionValue(); 77 _hideFileExtensionIsGPOConfigured = _hideFileExtensionGpoRuleConfiguration == GpoRuleConfigured.Disabled || _hideFileExtensionGpoRuleConfiguration == GpoRuleConfigured.Enabled; 78 79 // Same for Replace Variables 80 _replaceVariablesIsGPOConfigured = GPOWrapper.GetConfiguredNewPlusReplaceVariablesValue() == GpoRuleConfigured.Enabled 81 || GPOWrapper.GetConfiguredNewPlusReplaceVariablesValue() == GpoRuleConfigured.Disabled; 82 } 83 84 public bool IsEnabled 85 { 86 get => _isNewPlusEnabled; 87 set 88 { 89 if (_isNewPlusEnabled != value) 90 { 91 _isNewPlusEnabled = value; 92 93 GeneralSettingsConfig.Enabled.NewPlus = value; 94 OnPropertyChanged(nameof(IsEnabled)); 95 OnPropertyChanged(nameof(IsHideFileExtSettingsCardEnabled)); 96 OnPropertyChanged(nameof(IsHideFileExtSettingGPOConfigured)); 97 OnPropertyChanged(nameof(IsReplaceVariablesSettingGPOConfigured)); 98 OnPropertyChanged(nameof(IsReplaceVariablesSettingsCardEnabled)); 99 100 OutGoingGeneralSettings outgoingMessage = new OutGoingGeneralSettings(GeneralSettingsConfig); 101 SendConfigMSG(outgoingMessage.ToString()); 102 103 NotifySettingsChanged(); 104 105 if (_isNewPlusEnabled == true) 106 { 107 CopyTemplateExamples(_templateLocation); 108 } 109 } 110 } 111 } 112 113 public bool IsWin10OrLower 114 { 115 get => !OSVersionHelper.IsWindows11(); 116 } 117 118 public string TemplateLocation 119 { 120 get => _templateLocation; 121 set 122 { 123 if (_templateLocation != value) 124 { 125 _templateLocation = value; 126 Settings.Properties.TemplateLocation.Value = value; 127 OnPropertyChanged(nameof(TemplateLocation)); 128 129 NotifySettingsChanged(); 130 } 131 } 132 } 133 134 public bool HideFileExtension 135 { 136 get 137 { 138 if (_hideFileExtensionIsGPOConfigured) 139 { 140 return _hideFileExtensionGpoRuleConfiguration == GpoRuleConfigured.Enabled; 141 } 142 143 return _hideFileExtension; 144 } 145 146 set 147 { 148 if (_hideFileExtension != value && !_hideFileExtensionIsGPOConfigured) 149 { 150 _hideFileExtension = value; 151 Settings.Properties.HideFileExtension.Value = value; 152 OnPropertyChanged(nameof(HideFileExtension)); 153 154 NotifySettingsChanged(); 155 } 156 } 157 } 158 159 public bool IsHideFileExtSettingsCardEnabled => _isNewPlusEnabled && !_hideFileExtensionIsGPOConfigured; 160 161 public bool IsHideFileExtSettingGPOConfigured => _isNewPlusEnabled && _hideFileExtensionIsGPOConfigured; 162 163 public bool IsReplaceVariablesSettingsCardEnabled => _isNewPlusEnabled && !_replaceVariablesIsGPOConfigured; 164 165 public bool IsReplaceVariablesSettingGPOConfigured => _isNewPlusEnabled && _replaceVariablesIsGPOConfigured; 166 167 public bool HideStartingDigits 168 { 169 get => _hideStartingDigits; 170 set 171 { 172 if (_hideStartingDigits != value) 173 { 174 _hideStartingDigits = value; 175 Settings.Properties.HideStartingDigits.Value = value; 176 OnPropertyChanged(nameof(HideStartingDigits)); 177 178 NotifySettingsChanged(); 179 } 180 } 181 } 182 183 public bool ReplaceVariables 184 { 185 get 186 { 187 // Check to see if setting has been enabled or disabled via GPO, and if so, use that value 188 if (IsReplaceVariablesSettingGPOConfigured) 189 { 190 return GPOWrapper.GetConfiguredNewPlusReplaceVariablesValue() == GpoRuleConfigured.Enabled; 191 } 192 193 return _replaceVariables; 194 } 195 196 set 197 { 198 if (_replaceVariables != value) 199 { 200 _replaceVariables = value; 201 Settings.Properties.ReplaceVariables.Value = value; 202 OnPropertyChanged(nameof(ReplaceVariables)); 203 204 NotifySettingsChanged(); 205 } 206 } 207 } 208 209 public bool IsEnabledGpoConfigured 210 { 211 get => _enabledStateIsGPOConfigured; 212 } 213 214 public ButtonClickCommand OpenCurrentNewTemplateFolder => new ButtonClickCommand(OpenNewTemplateFolder); 215 216 public ButtonClickCommand PickAnotherNewTemplateFolder => new ButtonClickCommand(PickNewTemplateFolder); 217 218 private void NotifySettingsChanged() 219 { 220 // Using InvariantCulture as this is an IPC message 221 SendConfigMSG( 222 string.Format( 223 CultureInfo.InvariantCulture, 224 "{{ \"powertoys\": {{ \"{0}\": {1} }} }}", 225 ModuleName, 226 JsonSerializer.Serialize(Settings, SourceGenerationContextContext.Default.NewPlusSettings))); 227 } 228 229 private Func<string, int> SendConfigMSG { get; } 230 231 public static NewPlusSettings LoadSettings(SettingsUtils settingsUtils) 232 { 233 NewPlusSettings settings = null; 234 235 try 236 { 237 settings = settingsUtils.GetSettingsOrDefault<NewPlusSettings>(NewPlusSettings.ModuleName); 238 239 if (string.IsNullOrEmpty(settings.Properties.TemplateLocation.Value)) 240 { 241 // This can happen when running the DEBUG Settings application without first letting the runner create the default settings file. 242 settings.Properties.TemplateLocation.Value = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Microsoft", "PowerToys", "NewPlus", "Templates"); 243 } 244 } 245 catch (Exception e) 246 { 247 Logger.LogError($"Exception encountered while reading {NewPlusSettings.ModuleName} settings.", e); 248 } 249 250 return settings; 251 } 252 253 public static void CopyTemplateExamples(string templateLocation) 254 { 255 if (!Directory.Exists(templateLocation)) 256 { 257 Directory.CreateDirectory(templateLocation); 258 } 259 260 if (Directory.GetFiles(templateLocation).Length == 0 && Directory.GetDirectories(templateLocation).Length == 0) 261 { 262 // No files in templateLocation directory 263 // Copy over examples files from <Program Files>\PowerToys\WinUI3Apps\Assets\NewPlus\Templates 264 var example_templates = Path.Combine(Helper.GetPowerToysInstallationWinUI3AppsAssetsFolder(), "NewPlus", "Templates"); 265 Helper.CopyDirectory(example_templates, templateLocation, true); 266 } 267 } 268 269 private bool _isNewPlusEnabled; 270 private string _templateLocation; 271 private bool _hideFileExtension; 272 private bool _hideStartingDigits; 273 private bool _replaceVariables; 274 275 private GpoRuleConfigured _enabledGpoRuleConfiguration; 276 private bool _enabledStateIsGPOConfigured; 277 private GpoRuleConfigured _hideFileExtensionGpoRuleConfiguration; 278 private bool _hideFileExtensionIsGPOConfigured; 279 private bool _replaceVariablesIsGPOConfigured; 280 281 public void RefreshEnabledState() 282 { 283 InitializeEnabledValue(); 284 OnPropertyChanged(nameof(IsEnabled)); 285 } 286 287 private void OpenNewTemplateFolder() 288 { 289 try 290 { 291 CopyTemplateExamples(_templateLocation); 292 293 var process = new ProcessStartInfo() 294 { 295 FileName = _templateLocation, 296 UseShellExecute = true, 297 }; 298 Process.Start(process); 299 } 300 catch (Exception ex) 301 { 302 Logger.LogError("Failed to show NewPlus template folder.", ex); 303 } 304 } 305 306 private async void PickNewTemplateFolder() 307 { 308 var newPath = await PickFolderDialog(); 309 if (!string.IsNullOrEmpty(newPath)) 310 { 311 TemplateLocation = newPath; 312 } 313 } 314 315 private async Task<string> PickFolderDialog() 316 { 317 var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(App.GetSettingsWindow()); 318 return await Task.FromResult(GetFolderDialogWithFlags(hwnd, FolderDialogFlags._BIF_NEWDIALOGSTYLE)); 319 } 320 } 321 }