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.

478 lines
17KB

  1. /*
  2. ==============================================================================
  3. jucer_GlobalPreferences.cpp
  4. Created: 22 Jul 2015 11:05:35am
  5. Author: Timur Doumler
  6. ==============================================================================
  7. */
  8. #include "../jucer_Headers.h"
  9. #include "jucer_GlobalPreferences.h"
  10. //==============================================================================
  11. class AppearanceSettingsTab : public GlobalPreferencesTab,
  12. public Component
  13. {
  14. public:
  15. AppearanceSettingsTab();
  16. Component* getContent() override;
  17. void changeContent (Component* newContent);
  18. String getName() const noexcept override;
  19. ScopedPointer<Component> content;
  20. };
  21. //==============================================================================
  22. namespace PathSettingsHelpers
  23. {
  24. bool checkSdkPathContainsFile (const String& path, const String& fileToCheckFor)
  25. {
  26. return File::getCurrentWorkingDirectory().getChildFile( path + "/" + fileToCheckFor).existsAsFile();
  27. }
  28. }
  29. PathSettingsTab::PathSettingsTab (DependencyPathOS os)
  30. {
  31. const int maxChars = 1024;
  32. vst2PathComponent = pathComponents.add (new TextPropertyComponent (getPathByKey (DependencyPath::vst2KeyName, os), "VST SDK", maxChars, false));
  33. vst3PathComponent = pathComponents.add (new TextPropertyComponent (getPathByKey (DependencyPath::vst3KeyName, os), "VST3 SDK", maxChars, false));
  34. #if ! JUCE_LINUX
  35. rtasPathComponent = pathComponents.add (new TextPropertyComponent (getPathByKey (DependencyPath::rtasKeyName, os), "RTAS SDK", maxChars, false));
  36. aaxPathComponent = pathComponents.add (new TextPropertyComponent (getPathByKey (DependencyPath::aaxKeyName, os), "AAX SDK", maxChars, false));
  37. #endif
  38. androidSdkPathComponent = pathComponents.add (new TextPropertyComponent (getPathByKey (DependencyPath::androidSdkKeyName, os), "Android SDK", maxChars, false));
  39. androidNdkPathComponent = pathComponents.add (new TextPropertyComponent (getPathByKey (DependencyPath::androidNdkKeyName, os), "Android NDK", maxChars, false));
  40. for (TextPropertyComponent** component = pathComponents.begin(); component != pathComponents.end(); ++component)
  41. {
  42. addAndMakeVisible (**component);
  43. (*component)->addListener (this);
  44. textPropertyComponentChanged (*component);
  45. }
  46. }
  47. PathSettingsTab::~PathSettingsTab()
  48. {
  49. }
  50. void PathSettingsTab::textPropertyComponentChanged (TextPropertyComponent* textPropertyComponent)
  51. {
  52. String keyName = getKeyForPropertyComponent (textPropertyComponent);
  53. Colour textColour = checkPathByKey (keyName, textPropertyComponent->getText()) ? Colours::black : Colours::red;
  54. textPropertyComponent->setColour (TextPropertyComponent::textColourId, textColour);
  55. }
  56. String PathSettingsTab::getKeyForPropertyComponent (TextPropertyComponent* component) const
  57. {
  58. if (component == vst2PathComponent) return DependencyPath::vst2KeyName;
  59. if (component == vst3PathComponent) return DependencyPath::vst3KeyName;
  60. if (component == rtasPathComponent) return DependencyPath::rtasKeyName;
  61. if (component == aaxPathComponent) return DependencyPath::aaxKeyName;
  62. if (component == androidSdkPathComponent) return DependencyPath::androidSdkKeyName;
  63. if (component == androidNdkPathComponent) return DependencyPath::androidNdkKeyName;
  64. // this property component does not have a key associated to it!
  65. jassertfalse;
  66. return String::empty;
  67. }
  68. Component* PathSettingsTab::getContent()
  69. {
  70. return this;
  71. }
  72. String PathSettingsTab::getName() const noexcept
  73. {
  74. return "Paths";
  75. }
  76. void PathSettingsTab::resized()
  77. {
  78. const int componentHeight = 25;
  79. for (TextPropertyComponent** component = pathComponents.begin(); component != pathComponents.end(); ++component)
  80. {
  81. const int elementNumber = pathComponents.indexOf (*component);
  82. (*component)->setBounds (0, componentHeight * elementNumber, getWidth(), componentHeight);
  83. }
  84. }
  85. //==============================================================================
  86. Value& PathSettingsTab::getPathByKey (const String& key, DependencyPathOS os)
  87. {
  88. getAppSettings().pathValues[key].referTo (getAppSettings().projectDefaults.getPropertyAsValue (key, nullptr));
  89. Value& value = getAppSettings().pathValues[key];
  90. if (value.toString().isEmpty())
  91. value = getFallbackPathByKey (key, os);
  92. return value;
  93. }
  94. //==============================================================================
  95. String PathSettingsTab::getFallbackPathByKey (const String& key, DependencyPathOS os)
  96. {
  97. if (key == DependencyPath::vst2KeyName || key == DependencyPath::vst3KeyName)
  98. return os == DependencyPath::windows ? "c:\\SDKs\\VST3 SDK"
  99. : "~/SDKs/VST3 SDK";
  100. if (key == DependencyPath::rtasKeyName)
  101. {
  102. if (os == DependencyPath::windows) return "c:\\SDKs\\PT_80_SDK";
  103. if (os == DependencyPath::osx) return "~/SDKs/PT_80_SDK";
  104. // no RTAS on this OS!
  105. jassertfalse;
  106. return String();
  107. }
  108. if (key == DependencyPath::aaxKeyName)
  109. {
  110. if (os == DependencyPath::windows) return "c:\\SDKs\\AAX";
  111. if (os == DependencyPath::osx) return "~/SDKs/AAX" ;
  112. // no RTAS on this OS!
  113. jassertfalse;
  114. return String();
  115. }
  116. if (key == DependencyPath::androidSdkKeyName)
  117. return os == DependencyPath::windows ? "c:\\SDKs\\android-sdk"
  118. : "~/Library/Android/sdk";
  119. if (key == DependencyPath::androidNdkKeyName)
  120. return os == DependencyPath::windows ? "c:\\SDKs\\android-ndk"
  121. : "~/Library/Android/ndk";
  122. // didn't recognise the key provided!
  123. jassertfalse;
  124. return String();
  125. }
  126. //==============================================================================
  127. bool PathSettingsTab::checkPathByKey (const String& key, const String& path)
  128. {
  129. String fileToCheckFor;
  130. if (key == DependencyPath::vst2KeyName)
  131. {
  132. fileToCheckFor = "public.sdk/source/vst2.x/audioeffectx.h";
  133. }
  134. else if (key == DependencyPath::vst3KeyName)
  135. {
  136. fileToCheckFor = "base/source/baseiids.cpp";
  137. }
  138. else if (key == DependencyPath::rtasKeyName)
  139. {
  140. fileToCheckFor = "AlturaPorts/TDMPlugIns/PlugInLibrary/EffectClasses/CEffectProcessMIDI.cpp";
  141. }
  142. else if (key == DependencyPath::aaxKeyName)
  143. {
  144. fileToCheckFor = "Interfaces/AAX_Exports.cpp";
  145. }
  146. else if (key == DependencyPath::androidSdkKeyName)
  147. {
  148. #if JUCE_WINDOWS
  149. fileToCheckFor = "platform-tools/adb.exe";
  150. #else
  151. fileToCheckFor = "platform-tools/adb";
  152. #endif
  153. }
  154. else if (key == DependencyPath::androidNdkKeyName)
  155. {
  156. #if JUCE_WINDOWS
  157. fileToCheckFor = "ndk-depends.exe";
  158. #else
  159. fileToCheckFor = "ndk-depends";
  160. #endif
  161. }
  162. else
  163. {
  164. // didn't recognise the key provided!
  165. jassertfalse;
  166. return false;
  167. }
  168. return PathSettingsHelpers::checkSdkPathContainsFile (path, fileToCheckFor);
  169. }
  170. //==============================================================================
  171. struct AppearanceEditor
  172. {
  173. struct FontScanPanel : public Component,
  174. private Timer
  175. {
  176. FontScanPanel()
  177. {
  178. fontsToScan = Font::findAllTypefaceNames();
  179. startTimer (1);
  180. }
  181. void paint (Graphics& g) override
  182. {
  183. g.fillAll (Colours::darkgrey);
  184. g.setFont (14.0f);
  185. g.setColour (Colours::white);
  186. g.drawFittedText ("Scanning for fonts..", getLocalBounds(), Justification::centred, 2);
  187. const int size = 30;
  188. getLookAndFeel().drawSpinningWaitAnimation (g, Colours::white, (getWidth() - size) / 2, getHeight() / 2 - 50, size, size);
  189. }
  190. void timerCallback() override
  191. {
  192. repaint();
  193. if (fontsToScan.size() == 0)
  194. {
  195. getAppSettings().monospacedFontNames = fontsFound;
  196. if (AppearanceSettingsTab* tab = findParentComponentOfClass<AppearanceSettingsTab>())
  197. tab->changeContent (new EditorPanel());
  198. }
  199. else
  200. {
  201. if (isMonospacedTypeface (fontsToScan[0]))
  202. fontsFound.add (fontsToScan[0]);
  203. fontsToScan.remove (0);
  204. }
  205. }
  206. // A rather hacky trick to select only the fixed-pitch fonts..
  207. // This is unfortunately a bit slow, but will work on all platforms.
  208. static bool isMonospacedTypeface (const String& name)
  209. {
  210. const Font font (name, 20.0f, Font::plain);
  211. const int width = font.getStringWidth ("....");
  212. return width == font.getStringWidth ("WWWW")
  213. && width == font.getStringWidth ("0000")
  214. && width == font.getStringWidth ("1111")
  215. && width == font.getStringWidth ("iiii");
  216. }
  217. StringArray fontsToScan, fontsFound;
  218. };
  219. //==============================================================================
  220. struct EditorPanel : public Component,
  221. private ButtonListener
  222. {
  223. EditorPanel()
  224. : loadButton ("Load Scheme..."),
  225. saveButton ("Save Scheme...")
  226. {
  227. rebuildProperties();
  228. addAndMakeVisible (panel);
  229. loadButton.setColour (TextButton::buttonColourId, Colours::lightgrey.withAlpha (0.5f));
  230. saveButton.setColour (TextButton::buttonColourId, Colours::lightgrey.withAlpha (0.5f));
  231. loadButton.setColour (TextButton::textColourOffId, Colours::white);
  232. saveButton.setColour (TextButton::textColourOffId, Colours::white);
  233. addAndMakeVisible (loadButton);
  234. addAndMakeVisible (saveButton);
  235. loadButton.addListener (this);
  236. saveButton.addListener (this);
  237. }
  238. void rebuildProperties()
  239. {
  240. AppearanceSettings& scheme = getAppSettings().appearance;
  241. Array<PropertyComponent*> props;
  242. Value fontValue (scheme.getCodeFontValue());
  243. props.add (FontNameValueSource::createProperty ("Code Editor Font", fontValue));
  244. props.add (FontSizeValueSource::createProperty ("Font Size", fontValue));
  245. const StringArray colourNames (scheme.getColourNames());
  246. for (int i = 0; i < colourNames.size(); ++i)
  247. props.add (new ColourPropertyComponent (nullptr, colourNames[i],
  248. scheme.getColourValue (colourNames[i]),
  249. Colours::white, false));
  250. panel.clear();
  251. panel.addProperties (props);
  252. }
  253. void resized() override
  254. {
  255. Rectangle<int> r (getLocalBounds());
  256. panel.setBounds (r.removeFromTop (getHeight() - 28).reduced (4, 2));
  257. loadButton.setBounds (r.removeFromLeft (getWidth() / 2).reduced (10, 4));
  258. saveButton.setBounds (r.reduced (10, 3));
  259. }
  260. private:
  261. PropertyPanel panel;
  262. TextButton loadButton, saveButton;
  263. void buttonClicked (Button* b) override
  264. {
  265. if (b == &loadButton)
  266. loadScheme();
  267. else
  268. saveScheme();
  269. }
  270. void saveScheme()
  271. {
  272. FileChooser fc ("Select a file in which to save this colour-scheme...",
  273. getAppSettings().appearance.getSchemesFolder()
  274. .getNonexistentChildFile ("Scheme", AppearanceSettings::getSchemeFileSuffix()),
  275. AppearanceSettings::getSchemeFileWildCard());
  276. if (fc.browseForFileToSave (true))
  277. {
  278. File file (fc.getResult().withFileExtension (AppearanceSettings::getSchemeFileSuffix()));
  279. getAppSettings().appearance.writeToFile (file);
  280. getAppSettings().appearance.refreshPresetSchemeList();
  281. }
  282. }
  283. void loadScheme()
  284. {
  285. FileChooser fc ("Please select a colour-scheme file to load...",
  286. getAppSettings().appearance.getSchemesFolder(),
  287. AppearanceSettings::getSchemeFileWildCard());
  288. if (fc.browseForFileToOpen())
  289. if (getAppSettings().appearance.readFromFile (fc.getResult()))
  290. rebuildProperties();
  291. }
  292. JUCE_DECLARE_NON_COPYABLE (EditorPanel)
  293. };
  294. //==============================================================================
  295. struct FontNameValueSource : public ValueSourceFilter
  296. {
  297. FontNameValueSource (const Value& source) : ValueSourceFilter (source) {}
  298. var getValue() const override
  299. {
  300. return Font::fromString (sourceValue.toString()).getTypefaceName();
  301. }
  302. void setValue (const var& newValue) override
  303. {
  304. Font font (Font::fromString (sourceValue.toString()));
  305. font.setTypefaceName (newValue.toString().isEmpty() ? Font::getDefaultMonospacedFontName()
  306. : newValue.toString());
  307. sourceValue = font.toString();
  308. }
  309. static ChoicePropertyComponent* createProperty (const String& title, const Value& value)
  310. {
  311. StringArray fontNames = getAppSettings().monospacedFontNames;
  312. Array<var> values;
  313. values.add (Font::getDefaultMonospacedFontName());
  314. values.add (var());
  315. for (int i = 0; i < fontNames.size(); ++i)
  316. values.add (fontNames[i]);
  317. StringArray names;
  318. names.add ("<Default Monospaced>");
  319. names.add (String::empty);
  320. names.addArray (getAppSettings().monospacedFontNames);
  321. return new ChoicePropertyComponent (Value (new FontNameValueSource (value)),
  322. title, names, values);
  323. }
  324. };
  325. //==============================================================================
  326. struct FontSizeValueSource : public ValueSourceFilter
  327. {
  328. FontSizeValueSource (const Value& source) : ValueSourceFilter (source) {}
  329. var getValue() const override
  330. {
  331. return Font::fromString (sourceValue.toString()).getHeight();
  332. }
  333. void setValue (const var& newValue) override
  334. {
  335. sourceValue = Font::fromString (sourceValue.toString()).withHeight (newValue).toString();
  336. }
  337. static PropertyComponent* createProperty (const String& title, const Value& value)
  338. {
  339. return new SliderPropertyComponent (Value (new FontSizeValueSource (value)),
  340. title, 5.0, 40.0, 0.1, 0.5);
  341. }
  342. };
  343. };
  344. void AppearanceSettings::showGlobalPreferences (ScopedPointer<Component>& ownerPointer)
  345. {
  346. if (ownerPointer != nullptr)
  347. {
  348. ownerPointer->toFront (true);
  349. }
  350. else
  351. {
  352. new FloatingToolWindow ("Global Preferences",
  353. "globalPreferencesEditorPos",
  354. new GlobalPreferencesComponent,
  355. ownerPointer,
  356. 500, 500, 500, 500, 500, 500);
  357. }
  358. }
  359. //==============================================================================
  360. AppearanceSettingsTab::AppearanceSettingsTab()
  361. {
  362. if (getAppSettings().monospacedFontNames.size() == 0)
  363. content = new AppearanceEditor::FontScanPanel();
  364. else
  365. content = new AppearanceEditor::EditorPanel();
  366. changeContent (content);
  367. }
  368. Component* AppearanceSettingsTab::getContent()
  369. {
  370. return this;
  371. }
  372. void AppearanceSettingsTab::changeContent (Component* newContent)
  373. {
  374. content = newContent;
  375. addAndMakeVisible(content);
  376. content->setBoundsInset(BorderSize<int>());
  377. }
  378. String AppearanceSettingsTab::getName() const noexcept
  379. {
  380. return "Code Editor";
  381. }
  382. //==============================================================================
  383. GlobalPreferencesComponent::GlobalPreferencesComponent()
  384. : TabbedComponent (TabbedButtonBar::TabsAtTop)
  385. {
  386. preferenceTabs.add (new PathSettingsTab (DependencyPath::getThisOS()));
  387. preferenceTabs.add (new AppearanceSettingsTab);
  388. for (GlobalPreferencesTab** tab = preferenceTabs.begin(); tab != preferenceTabs.end(); ++tab)
  389. addTab ((*tab)->getName(), findColour(mainBackgroundColourId, true), (*tab)->getContent(), true);
  390. }