The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

358 lines
11KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. #include "../jucer_Headers.h"
  20. #include "jucer_StoredSettings.h"
  21. #include "../Application/jucer_Application.h"
  22. //==============================================================================
  23. StoredSettings& getAppSettings()
  24. {
  25. return *ProjucerApplication::getApp().settings;
  26. }
  27. PropertiesFile& getGlobalProperties()
  28. {
  29. return getAppSettings().getGlobalProperties();
  30. }
  31. //==============================================================================
  32. StoredSettings::StoredSettings()
  33. : appearance (true),
  34. projectDefaults ("PROJECT_DEFAULT_SETTINGS"),
  35. fallbackPaths ("FALLBACK_PATHS")
  36. {
  37. updateOldProjectSettingsFiles();
  38. reload();
  39. projectDefaults.addListener (this);
  40. fallbackPaths.addListener (this);
  41. }
  42. StoredSettings::~StoredSettings()
  43. {
  44. projectDefaults.removeListener (this);
  45. fallbackPaths.removeListener (this);
  46. flush();
  47. }
  48. PropertiesFile& StoredSettings::getGlobalProperties()
  49. {
  50. return *propertyFiles.getUnchecked (0);
  51. }
  52. static PropertiesFile* createPropsFile (const String& filename, bool isProjectSettings)
  53. {
  54. return new PropertiesFile (ProjucerApplication::getApp()
  55. .getPropertyFileOptionsFor (filename, isProjectSettings));
  56. }
  57. PropertiesFile& StoredSettings::getProjectProperties (const String& projectUID)
  58. {
  59. const auto filename = String ("Projucer_Project_" + projectUID);
  60. for (auto i = propertyFiles.size(); --i >= 0;)
  61. {
  62. auto* const props = propertyFiles.getUnchecked(i);
  63. if (props->getFile().getFileNameWithoutExtension() == filename)
  64. return *props;
  65. }
  66. auto* p = createPropsFile (filename, true);
  67. propertyFiles.add (p);
  68. return *p;
  69. }
  70. void StoredSettings::updateGlobalPreferences()
  71. {
  72. // update 'invisible' global settings
  73. updateRecentFiles();
  74. updateKeyMappings();
  75. }
  76. void StoredSettings::updateRecentFiles()
  77. {
  78. getGlobalProperties().setValue ("recentFiles", recentFiles.toString());
  79. }
  80. void StoredSettings::updateKeyMappings()
  81. {
  82. getGlobalProperties().removeValue ("keyMappings");
  83. if (auto* commandManager = ProjucerApplication::getApp().commandManager.get())
  84. {
  85. const ScopedPointer<XmlElement> keys (commandManager->getKeyMappings()->createXml (true));
  86. if (keys != nullptr)
  87. getGlobalProperties().setValue ("keyMappings", keys);
  88. }
  89. }
  90. void StoredSettings::flush()
  91. {
  92. updateGlobalPreferences();
  93. saveSwatchColours();
  94. for (auto i = propertyFiles.size(); --i >= 0;)
  95. propertyFiles.getUnchecked(i)->saveIfNeeded();
  96. }
  97. void StoredSettings::reload()
  98. {
  99. propertyFiles.clear();
  100. propertyFiles.add (createPropsFile ("Projucer", false));
  101. ScopedPointer<XmlElement> projectDefaultsXml (propertyFiles.getFirst()->getXmlValue ("PROJECT_DEFAULT_SETTINGS"));
  102. if (projectDefaultsXml != nullptr)
  103. projectDefaults = ValueTree::fromXml (*projectDefaultsXml);
  104. ScopedPointer<XmlElement> fallbackPathsXml (propertyFiles.getFirst()->getXmlValue ("FALLBACK_PATHS"));
  105. if (fallbackPathsXml != nullptr)
  106. fallbackPaths = ValueTree::fromXml (*fallbackPathsXml);
  107. // recent files...
  108. recentFiles.restoreFromString (getGlobalProperties().getValue ("recentFiles"));
  109. recentFiles.removeNonExistentFiles();
  110. loadSwatchColours();
  111. }
  112. Array<File> StoredSettings::getLastProjects()
  113. {
  114. StringArray s;
  115. s.addTokens (getGlobalProperties().getValue ("lastProjects"), "|", "");
  116. Array<File> f;
  117. for (int i = 0; i < s.size(); ++i)
  118. f.add (File (s[i]));
  119. return f;
  120. }
  121. void StoredSettings::setLastProjects (const Array<File>& files)
  122. {
  123. StringArray s;
  124. for (int i = 0; i < files.size(); ++i)
  125. s.add (files.getReference(i).getFullPathName());
  126. getGlobalProperties().setValue ("lastProjects", s.joinIntoString ("|"));
  127. }
  128. void StoredSettings::updateOldProjectSettingsFiles()
  129. {
  130. // Global properties file hasn't been created yet so create a dummy file
  131. auto projucerSettingsDirectory = ProjucerApplication::getApp().getPropertyFileOptionsFor ("Dummy", false)
  132. .getDefaultFile().getParentDirectory();
  133. auto newProjectSettingsDir = projucerSettingsDirectory.getChildFile ("ProjectSettings");
  134. newProjectSettingsDir.createDirectory();
  135. DirectoryIterator iter (projucerSettingsDirectory, false, "*.settings");
  136. while (iter.next())
  137. {
  138. auto f = iter.getFile();
  139. auto oldFileName = f.getFileName();
  140. if (oldFileName.contains ("Introjucer"))
  141. {
  142. auto newFileName = oldFileName.replace ("Introjucer", "Projucer");
  143. if (oldFileName.contains ("_Project"))
  144. f.moveFileTo (f.getSiblingFile (newProjectSettingsDir.getFileName()).getChildFile (newFileName));
  145. else
  146. f.moveFileTo (f.getSiblingFile (newFileName));
  147. }
  148. }
  149. }
  150. //==============================================================================
  151. void StoredSettings::loadSwatchColours()
  152. {
  153. swatchColours.clear();
  154. #define COL(col) Colours::col,
  155. const Colour colours[] =
  156. {
  157. #include "jucer_Colours.h"
  158. Colours::transparentBlack
  159. };
  160. #undef COL
  161. const auto numSwatchColours = 24;
  162. auto& props = getGlobalProperties();
  163. for (auto i = 0; i < numSwatchColours; ++i)
  164. swatchColours.add (Colour::fromString (props.getValue ("swatchColour" + String (i),
  165. colours [2 + i].toString())));
  166. }
  167. void StoredSettings::saveSwatchColours()
  168. {
  169. auto& props = getGlobalProperties();
  170. for (auto i = 0; i < swatchColours.size(); ++i)
  171. props.setValue ("swatchColour" + String (i), swatchColours.getReference(i).toString());
  172. }
  173. int StoredSettings::ColourSelectorWithSwatches::getNumSwatches() const
  174. {
  175. return getAppSettings().swatchColours.size();
  176. }
  177. Colour StoredSettings::ColourSelectorWithSwatches::getSwatchColour (int index) const
  178. {
  179. return getAppSettings().swatchColours [index];
  180. }
  181. void StoredSettings::ColourSelectorWithSwatches::setSwatchColour (int index, const Colour& newColour)
  182. {
  183. getAppSettings().swatchColours.set (index, newColour);
  184. }
  185. //==============================================================================
  186. Value StoredSettings::getStoredPath (const Identifier& key)
  187. {
  188. auto v = projectDefaults.getPropertyAsValue (key, nullptr);
  189. if (v.toString().isEmpty())
  190. v = getFallbackPathForOS (key, TargetOS::getThisOS()).toString();
  191. return v;
  192. }
  193. Value StoredSettings::getFallbackPathForOS (const Identifier& key, DependencyPathOS os)
  194. {
  195. auto id = Identifier();
  196. if (os == TargetOS::osx) id = Ids::osxFallback;
  197. else if (os == TargetOS::windows) id = Ids::windowsFallback;
  198. else if (os == TargetOS::linux) id = Ids::linuxFallback;
  199. if (id == Identifier())
  200. jassertfalse;
  201. auto v = fallbackPaths.getOrCreateChildWithName (id, nullptr)
  202. .getPropertyAsValue (key, nullptr);
  203. if (v.toString().isEmpty())
  204. {
  205. if (key == Ids::defaultJuceModulePath)
  206. {
  207. v = (os == TargetOS::windows ? "C:\\JUCE\\modules"
  208. : "~/JUCE/modules");
  209. }
  210. else if (key == Ids::defaultUserModulePath)
  211. {
  212. v = (os == TargetOS::windows ? "C:\\modules"
  213. : "~/modules");
  214. }
  215. else if (key == Ids::vst3Path)
  216. {
  217. v = (os == TargetOS::windows ? "C:\\SDKs\\VST_SDK\\VST3_SDK"
  218. : "~/SDKs/VST_SDK/VST3_SDK");
  219. }
  220. else if (key == Ids::rtasPath)
  221. {
  222. if (os == TargetOS::windows) v = "C:\\SDKs\\PT_90_SDK";
  223. else if (os == TargetOS::osx) v = "~/SDKs/PT_90_SDK";
  224. else jassertfalse; // no RTAS on this OS!
  225. }
  226. else if (key == Ids::aaxPath)
  227. {
  228. if (os == TargetOS::windows) v = "C:\\SDKs\\AAX";
  229. else if (os == TargetOS::osx) v = "~/SDKs/AAX" ;
  230. else jassertfalse; // no AAX on this OS!
  231. }
  232. else if (key == Ids::androidSDKPath)
  233. {
  234. v = "${user.home}/Library/Android/sdk";
  235. }
  236. else if (key == Ids::androidNDKPath)
  237. {
  238. v = "${user.home}/Library/Android/sdk/ndk-bundle";
  239. }
  240. }
  241. return v;
  242. }
  243. static bool doesSDKPathContainFile (const File& relativeTo, const String& path, const String& fileToCheckFor)
  244. {
  245. auto actualPath = path.replace ("${user.home}", File::getSpecialLocation (File::userHomeDirectory).getFullPathName());
  246. return relativeTo.getChildFile (actualPath + "/" + fileToCheckFor).exists();
  247. }
  248. bool StoredSettings::isGlobalPathValid (const File& relativeTo, const Identifier& key, const String& path)
  249. {
  250. String fileToCheckFor;
  251. if (key == Ids::vst3Path)
  252. {
  253. fileToCheckFor = "base/source/baseiids.cpp";
  254. }
  255. else if (key == Ids::rtasPath)
  256. {
  257. fileToCheckFor = "AlturaPorts/TDMPlugIns/PlugInLibrary/EffectClasses/CEffectProcessMIDI.cpp";
  258. }
  259. else if (key == Ids::aaxPath)
  260. {
  261. fileToCheckFor = "Interfaces/AAX_Exports.cpp";
  262. }
  263. else if (key == Ids::androidSDKPath)
  264. {
  265. #if JUCE_WINDOWS
  266. fileToCheckFor = "platform-tools/adb.exe";
  267. #else
  268. fileToCheckFor = "platform-tools/adb";
  269. #endif
  270. }
  271. else if (key == Ids::androidNDKPath)
  272. {
  273. #if JUCE_WINDOWS
  274. fileToCheckFor = "ndk-depends.cmd";
  275. #else
  276. fileToCheckFor = "ndk-depends";
  277. #endif
  278. }
  279. else if (key == Ids::defaultJuceModulePath)
  280. {
  281. fileToCheckFor = "juce_core";
  282. }
  283. else if (key == Ids::defaultUserModulePath)
  284. {
  285. fileToCheckFor = {};
  286. }
  287. else
  288. {
  289. // didn't recognise the key provided!
  290. jassertfalse;
  291. return false;
  292. }
  293. return doesSDKPathContainFile (relativeTo, path, fileToCheckFor);
  294. }