/ src / settings-ui / Settings.UI / ViewModels / NewPlusViewModel.cs
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  }