/ src / common / SettingsAPI / settings_objects.cpp
settings_objects.cpp
  1  #include "pch.h"
  2  #include "settings_objects.h"
  3  #include "settings_helpers.h"
  4  
  5  namespace PowerToysSettings
  6  {
  7      Settings::Settings(const HINSTANCE hinstance, std::wstring_view powertoy_name)
  8      {
  9          m_instance = hinstance;
 10          m_json.SetNamedValue(L"version", json::value(L"1.0"));
 11          m_json.SetNamedValue(L"name", json::value(powertoy_name));
 12          m_json.SetNamedValue(L"properties", json::JsonObject{});
 13      }
 14  
 15      void Settings::set_description(UINT resource_id)
 16      {
 17          m_json.SetNamedValue(L"description", json::value(get_resource(resource_id)));
 18      }
 19  
 20      void Settings::set_description(std::wstring_view description)
 21      {
 22          m_json.SetNamedValue(L"description", json::value(description));
 23      }
 24  
 25      void Settings::set_icon_key(std::wstring_view icon_key)
 26      {
 27          m_json.SetNamedValue(L"icon_key", json::value(icon_key));
 28      }
 29  
 30      void Settings::set_overview_link(std::wstring_view overview_link)
 31      {
 32          m_json.SetNamedValue(L"overview_link", json::value(overview_link));
 33      }
 34  
 35      void Settings::set_video_link(std::wstring_view video_link)
 36      {
 37          m_json.SetNamedValue(L"video_link", json::value(video_link));
 38      }
 39  
 40      // add_bool_toggle overloads.
 41      void Settings::add_bool_toggle(std::wstring_view name, UINT description_resource_id, bool value)
 42      {
 43          add_bool_toggle(name, get_resource(description_resource_id), value);
 44      }
 45  
 46      void Settings::add_bool_toggle(std::wstring_view name, std::wstring_view description, bool value)
 47      {
 48          json::JsonObject toggle;
 49          toggle.SetNamedValue(L"display_name", json::value(description));
 50          toggle.SetNamedValue(L"editor_type", json::value(L"bool_toggle"));
 51          toggle.SetNamedValue(L"value", json::value(value));
 52          toggle.SetNamedValue(L"order", json::value(++m_curr_priority));
 53  
 54          m_json.GetNamedObject(L"properties").SetNamedValue(name, toggle);
 55      }
 56  
 57      // add_int_spinner overloads.
 58      void Settings::add_int_spinner(std::wstring_view name, UINT description_resource_id, int value, int min, int max, int step)
 59      {
 60          add_int_spinner(name, get_resource(description_resource_id), value, min, max, step);
 61      }
 62  
 63      void Settings::add_int_spinner(std::wstring_view name, std::wstring_view description, int value, int min, int max, int step)
 64      {
 65          json::JsonObject spinner;
 66          spinner.SetNamedValue(L"display_name", json::value(description));
 67          spinner.SetNamedValue(L"editor_type", json::value(L"int_spinner"));
 68          spinner.SetNamedValue(L"value", json::value(value));
 69          spinner.SetNamedValue(L"min", json::value(min));
 70          spinner.SetNamedValue(L"max", json::value(max));
 71          spinner.SetNamedValue(L"step", json::value(step));
 72          spinner.SetNamedValue(L"order", json::value(++m_curr_priority));
 73  
 74          m_json.GetNamedObject(L"properties").SetNamedValue(name, spinner);
 75      }
 76  
 77      // add_string overloads.
 78      void Settings::add_string(std::wstring_view name, UINT description_resource_id, std::wstring_view value)
 79      {
 80          add_string(name, get_resource(description_resource_id), value);
 81      }
 82  
 83      void Settings::add_string(std::wstring_view name, std::wstring_view description, std::wstring_view value)
 84      {
 85          json::JsonObject string;
 86          string.SetNamedValue(L"display_name", json::value(description));
 87          string.SetNamedValue(L"editor_type", json::value(L"string_text"));
 88          string.SetNamedValue(L"value", json::value(value));
 89          string.SetNamedValue(L"order", json::value(++m_curr_priority));
 90  
 91          m_json.GetNamedObject(L"properties").SetNamedValue(name, string);
 92      }
 93  
 94      // add_multiline_string overloads.
 95      void Settings::add_multiline_string(std::wstring_view name, UINT description_resource_id, std::wstring_view value)
 96      {
 97          add_multiline_string(name, get_resource(description_resource_id), value);
 98      }
 99  
100      void Settings::add_multiline_string(std::wstring_view name, std::wstring_view description, std::wstring_view value)
101      {
102          json::JsonObject ml_string;
103          ml_string.SetNamedValue(L"display_name", json::value(description));
104          ml_string.SetNamedValue(L"editor_type", json::value(L"string_text"));
105          ml_string.SetNamedValue(L"value", json::value(value));
106          ml_string.SetNamedValue(L"order", json::value(++m_curr_priority));
107          ml_string.SetNamedValue(L"multiline", json::value(true));
108  
109          m_json.GetNamedObject(L"properties").SetNamedValue(name, ml_string);
110      }
111  
112      void Settings::add_header_szLarge(std::wstring_view name, std::wstring_view description, std::wstring_view value)
113      {
114          json::JsonObject string;
115          string.SetNamedValue(L"display_name", json::value(description));
116          string.SetNamedValue(L"editor_type", json::value(L"header_large"));
117          string.SetNamedValue(L"value", json::value(value));
118          string.SetNamedValue(L"order", json::value(++m_curr_priority));
119  
120          m_json.GetNamedObject(L"properties").SetNamedValue(name, string);
121      }
122  
123      // add_color_picker overloads.
124      void Settings::add_color_picker(std::wstring_view name, UINT description_resource_id, std::wstring_view value)
125      {
126          add_color_picker(name, get_resource(description_resource_id), value);
127      }
128  
129      void Settings::add_color_picker(std::wstring_view name, std::wstring_view description, std::wstring_view value)
130      {
131          json::JsonObject picker;
132          picker.SetNamedValue(L"display_name", json::value(description));
133          picker.SetNamedValue(L"editor_type", json::value(L"color_picker"));
134          picker.SetNamedValue(L"value", json::value(value));
135          picker.SetNamedValue(L"order", json::value(++m_curr_priority));
136  
137          m_json.GetNamedObject(L"properties").SetNamedValue(name, picker);
138      }
139  
140      void Settings::add_hotkey(std::wstring_view name, UINT description_resource_id, const HotkeyObject& hotkey)
141      {
142          add_hotkey(name, get_resource(description_resource_id), hotkey);
143      }
144  
145      void Settings::add_hotkey(std::wstring_view name, std::wstring_view description, const HotkeyObject& hotkey_obj)
146      {
147          json::JsonObject hotkey;
148          hotkey.SetNamedValue(L"display_name", json::value(description));
149          hotkey.SetNamedValue(L"editor_type", json::value(L"hotkey"));
150          hotkey.SetNamedValue(L"value", hotkey_obj.get_json());
151          hotkey.SetNamedValue(L"order", json::value(++m_curr_priority));
152  
153          m_json.GetNamedObject(L"properties").SetNamedValue(name, hotkey);
154      }
155  
156      void Settings::add_choice_group(std::wstring_view name, UINT description_resource_id, std::wstring_view value, const std::vector<std::pair<std::wstring, UINT>>& keys_and_text_ids)
157      {
158          std::vector<std::pair<std::wstring, std::wstring>> keys_and_texts;
159          keys_and_texts.reserve(keys_and_text_ids.size());
160          for (const auto& kv : keys_and_text_ids)
161          {
162              keys_and_texts.emplace_back(kv.first, get_resource(kv.second));
163          }
164          add_choice_group(name, get_resource(description_resource_id), value, keys_and_texts);
165      }
166  
167      void Settings::add_choice_group(std::wstring_view name, std::wstring_view description, std::wstring_view value, const std::vector<std::pair<std::wstring, std::wstring>>& keys_and_texts)
168      {
169          json::JsonObject choice_group;
170          choice_group.SetNamedValue(L"display_name", json::value(description));
171          choice_group.SetNamedValue(L"editor_type", json::value(L"choice_group"));
172          json::JsonArray options;
173          for (const auto& [key, text] : keys_and_texts)
174          {
175              json::JsonObject entry;
176              entry.SetNamedValue(L"key", json::value(key));
177              entry.SetNamedValue(L"text", json::value(text));
178              options.Append(std::move(entry));
179          }
180          choice_group.SetNamedValue(L"options", std::move(options));
181          choice_group.SetNamedValue(L"value", json::value(value));
182          choice_group.SetNamedValue(L"order", json::value(++m_curr_priority));
183  
184          m_json.GetNamedObject(L"properties").SetNamedValue(name, choice_group);
185      }
186  
187      void Settings::add_dropdown(std::wstring_view name, UINT description_resource_id, std::wstring_view value, const std::vector<std::pair<std::wstring, UINT>>& keys_and_text_ids)
188      {
189          std::vector<std::pair<std::wstring, std::wstring>> keys_and_texts;
190          keys_and_texts.reserve(keys_and_text_ids.size());
191          for (const auto& kv : keys_and_text_ids)
192          {
193              keys_and_texts.emplace_back(kv.first, get_resource(kv.second));
194          }
195          add_dropdown(name, get_resource(description_resource_id), value, keys_and_texts);
196      }
197  
198      void Settings::add_dropdown(std::wstring_view name, std::wstring_view description, std::wstring_view value, const std::vector<std::pair<std::wstring, std::wstring>>& keys_and_texts)
199      {
200          json::JsonObject dropdown;
201          dropdown.SetNamedValue(L"display_name", json::value(description));
202          dropdown.SetNamedValue(L"editor_type", json::value(L"dropdown"));
203          json::JsonArray options;
204          for (const auto& [key, text] : keys_and_texts)
205          {
206              json::JsonObject entry;
207              entry.SetNamedValue(L"key", json::value(key));
208              entry.SetNamedValue(L"text", json::value(text));
209              options.Append(std::move(entry));
210          }
211          dropdown.SetNamedValue(L"options", std::move(options));
212          dropdown.SetNamedValue(L"value", json::value(value));
213          dropdown.SetNamedValue(L"order", json::value(++m_curr_priority));
214  
215          m_json.GetNamedObject(L"properties").SetNamedValue(name, dropdown);
216      }
217  
218      // add_custom_action overloads.
219      void Settings::add_custom_action(std::wstring_view name, UINT description_resource_id, UINT button_text_resource_id, UINT ext_description_resource_id)
220      {
221          add_custom_action(name, get_resource(description_resource_id), get_resource(button_text_resource_id), get_resource(ext_description_resource_id));
222      }
223  
224      void Settings::add_custom_action(std::wstring_view name, UINT description_resource_id, UINT button_text_resource_id, std::wstring_view value)
225      {
226          add_custom_action(name, get_resource(description_resource_id), get_resource(button_text_resource_id), value);
227      }
228  
229      void Settings::add_custom_action(std::wstring_view name, std::wstring_view description, std::wstring_view button_text, std::wstring_view value)
230      {
231          json::JsonObject custom_action;
232          custom_action.SetNamedValue(L"display_name", json::value(description));
233          custom_action.SetNamedValue(L"button_text", json::value(button_text));
234          custom_action.SetNamedValue(L"editor_type", json::value(L"custom_action"));
235          custom_action.SetNamedValue(L"value", json::value(value));
236          custom_action.SetNamedValue(L"order", json::value(++m_curr_priority));
237  
238          m_json.GetNamedObject(L"properties").SetNamedValue(name, custom_action);
239      }
240  
241      // Serialization methods.
242      std::wstring Settings::serialize()
243      {
244          return m_json.Stringify().c_str();
245      }
246  
247      bool Settings::serialize_to_buffer(wchar_t* buffer, int* buffer_size)
248      {
249          auto result = m_json.Stringify();
250          const int result_len = static_cast<int>(result.size() + 1);
251  
252          if (buffer == nullptr || *buffer_size < result_len)
253          {
254              *buffer_size = result_len;
255              return false;
256          }
257          else
258          {
259              wcscpy_s(buffer, *buffer_size, result.c_str());
260              return true;
261          }
262      }
263  
264      // Resource helper.
265      std::wstring Settings::get_resource(UINT resource_id)
266      {
267          if (resource_id != 0)
268          {
269              wchar_t* res_ptr;
270              const size_t resource_length = LoadStringW(m_instance, resource_id, reinterpret_cast<wchar_t*>(&res_ptr), 0);
271              if (resource_length != 0)
272              {
273                  return { *reinterpret_cast<wchar_t**>(&res_ptr), resource_length };
274              }
275          }
276  
277          return L"RESOURCE ID NOT FOUND: " + std::to_wstring(resource_id);
278      }
279  
280      PowerToyValues::PowerToyValues(std::wstring_view powertoy_name, std::wstring_view powertoy_key)
281      {
282          _key = powertoy_key;
283          set_version();
284          m_json.SetNamedValue(L"name", json::value(powertoy_name));
285          m_json.SetNamedValue(L"properties", json::JsonObject{});
286      }
287  
288      PowerToyValues PowerToyValues::from_json_string(std::wstring_view json, std::wstring_view powertoy_key)
289      {
290          PowerToyValues result = PowerToyValues();
291          json::JsonObject jsonObject = json::JsonValue::Parse(json).GetObjectW();
292          if (!jsonObject.HasKey(L"name"))
293          {
294              throw winrt::hresult_error(E_NOT_SET, L"name field not set");
295          }
296  
297          result.m_json = json::JsonValue::Parse(json).GetObjectW();
298          result._key = powertoy_key;
299          return result;
300      }
301  
302      PowerToyValues PowerToyValues::load_from_settings_file(std::wstring_view powertoy_key)
303      {
304          PowerToyValues result = PowerToyValues();
305          result.m_json = PTSettingsHelper::load_module_settings(powertoy_key);
306          result._key = powertoy_key;
307          return result;
308      }
309  
310      inline bool has_property(const json::JsonObject& o, std::wstring_view name, const json::JsonValueType type)
311      {
312          const json::JsonObject props = o.GetNamedObject(L"properties", json::JsonObject{});
313          return json::has(props, name) && json::has(props.GetNamedObject(name), L"value", type);
314      }
315  
316      std::optional<bool> PowerToyValues::get_bool_value(std::wstring_view property_name) const
317      {
318          if (!has_property(m_json, property_name, json::JsonValueType::Boolean))
319          {
320              return std::nullopt;
321          }
322          return m_json.GetNamedObject(L"properties").GetNamedObject(property_name).GetNamedBoolean(L"value");
323      }
324  
325      std::optional<int> PowerToyValues::get_int_value(std::wstring_view property_name) const
326      {
327          if (!has_property(m_json, property_name, json::JsonValueType::Number))
328          {
329              return std::nullopt;
330          }
331          return static_cast<int>(m_json.GetNamedObject(L"properties").GetNamedObject(property_name).GetNamedNumber(L"value"));
332      }
333  
334      std::optional<unsigned int> PowerToyValues::get_uint_value(std::wstring_view property_name) const
335      {
336          if (!has_property(m_json, property_name, json::JsonValueType::Number))
337          {
338              return std::nullopt;
339          }
340          return static_cast<unsigned int>(m_json.GetNamedObject(L"properties").GetNamedObject(property_name).GetNamedNumber(L"value"));
341      }
342  
343      std::optional<std::wstring> PowerToyValues::get_string_value(std::wstring_view property_name) const
344      {
345          if (!has_property(m_json, property_name, json::JsonValueType::String))
346          {
347              return std::nullopt;
348          }
349          return m_json.GetNamedObject(L"properties").GetNamedObject(property_name).GetNamedString(L"value").c_str();
350      }
351  
352      std::optional<json::JsonObject> PowerToyValues::get_json(std::wstring_view property_name) const
353      {
354          if (!has_property(m_json, property_name, json::JsonValueType::Object))
355          {
356              return std::nullopt;
357          }
358          return m_json.GetNamedObject(L"properties").GetNamedObject(property_name).GetNamedObject(L"value");
359      }
360  
361      json::JsonObject PowerToyValues::get_raw_json()
362      {
363          return m_json;
364      }
365  
366      std::wstring PowerToyValues::serialize()
367      {
368          set_version();
369          return m_json.Stringify().c_str();
370      }
371  
372      void PowerToyValues::save_to_settings_file()
373      {
374          set_version();
375          PTSettingsHelper::save_module_settings(_key, m_json);
376      }
377  
378      void PowerToyValues::set_version()
379      {
380          m_json.SetNamedValue(L"version", json::value(m_version));
381      }
382  }