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.

372 lines
13KB

  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_GlobalPreferences.h"
  21. #include "../Utility/jucer_FloatingToolWindow.h"
  22. #include "../Utility/jucer_ColourPropertyComponent.h"
  23. //==============================================================================
  24. PathSettingsTab::PathSettingsTab (DependencyPathOS os)
  25. {
  26. const int maxChars = 1024;
  27. StoredSettings& settings = getAppSettings();
  28. vst3PathComponent = pathComponents.add (new TextPropertyComponent (settings.getGlobalPath (Ids::vst3Path, os), "VST3 SDK", maxChars, false));
  29. #if ! JUCE_LINUX
  30. rtasPathComponent = pathComponents.add (new TextPropertyComponent (settings.getGlobalPath (Ids::rtasPath, os), "RTAS SDK", maxChars, false));
  31. aaxPathComponent = pathComponents.add (new TextPropertyComponent (settings.getGlobalPath (Ids::aaxPath, os), "AAX SDK", maxChars, false));
  32. #endif
  33. androidSdkPathComponent = pathComponents.add (new TextPropertyComponent (settings.getGlobalPath (Ids::androidSDKPath, os), "Android SDK", maxChars, false));
  34. androidNdkPathComponent = pathComponents.add (new TextPropertyComponent (settings.getGlobalPath (Ids::androidNDKPath, os), "Android NDK", maxChars, false));
  35. for (TextPropertyComponent** component = pathComponents.begin(); component != pathComponents.end(); ++component)
  36. {
  37. addAndMakeVisible (**component);
  38. (*component)->addListener (this);
  39. textPropertyComponentChanged (*component);
  40. }
  41. }
  42. PathSettingsTab::~PathSettingsTab()
  43. {
  44. }
  45. void PathSettingsTab::textPropertyComponentChanged (TextPropertyComponent* textPropertyComponent)
  46. {
  47. Identifier keyName = getKeyForPropertyComponent (textPropertyComponent);
  48. Colour textColour = getAppSettings().isGlobalPathValid (File::getCurrentWorkingDirectory(), keyName, textPropertyComponent->getText())
  49. ? findColour (widgetTextColourId)
  50. : Colours::red;
  51. textPropertyComponent->setColour (TextPropertyComponent::textColourId, textColour);
  52. }
  53. Identifier PathSettingsTab::getKeyForPropertyComponent (TextPropertyComponent* component) const
  54. {
  55. if (component == vst3PathComponent) return Ids::vst3Path;
  56. if (component == rtasPathComponent) return Ids::rtasPath;
  57. if (component == aaxPathComponent) return Ids::aaxPath;
  58. if (component == androidSdkPathComponent) return Ids::androidSDKPath;
  59. if (component == androidNdkPathComponent) return Ids::androidNDKPath;
  60. // this property component does not have a key associated to it!
  61. jassertfalse;
  62. return {};
  63. }
  64. Component* PathSettingsTab::getContent()
  65. {
  66. return this;
  67. }
  68. String PathSettingsTab::getName() const noexcept
  69. {
  70. return "Paths";
  71. }
  72. void PathSettingsTab::resized()
  73. {
  74. const int componentHeight = 25;
  75. for (TextPropertyComponent** component = pathComponents.begin(); component != pathComponents.end(); ++component)
  76. {
  77. const int elementNumber = pathComponents.indexOf (*component);
  78. (*component)->setBounds (0, componentHeight * elementNumber, getWidth(), componentHeight);
  79. }
  80. }
  81. //==============================================================================
  82. struct AppearanceEditor
  83. {
  84. struct FontScanPanel : public Component,
  85. private Timer
  86. {
  87. FontScanPanel()
  88. {
  89. fontsToScan = Font::findAllTypefaceNames();
  90. startTimer (1);
  91. }
  92. void paint (Graphics& g) override
  93. {
  94. g.fillAll (findColour (backgroundColourId));
  95. g.setFont (14.0f);
  96. g.setColour (findColour (defaultTextColourId));
  97. g.drawFittedText ("Scanning for fonts..", getLocalBounds(), Justification::centred, 2);
  98. const int size = 30;
  99. getLookAndFeel().drawSpinningWaitAnimation (g, Colours::white, (getWidth() - size) / 2, getHeight() / 2 - 50, size, size);
  100. }
  101. void timerCallback() override
  102. {
  103. repaint();
  104. if (fontsToScan.size() == 0)
  105. {
  106. getAppSettings().monospacedFontNames = fontsFound;
  107. if (AppearanceSettingsTab* tab = findParentComponentOfClass<AppearanceSettingsTab>())
  108. tab->changeContent (new EditorPanel());
  109. }
  110. else
  111. {
  112. if (isMonospacedTypeface (fontsToScan[0]))
  113. fontsFound.add (fontsToScan[0]);
  114. fontsToScan.remove (0);
  115. }
  116. }
  117. // A rather hacky trick to select only the fixed-pitch fonts..
  118. // This is unfortunately a bit slow, but will work on all platforms.
  119. static bool isMonospacedTypeface (const String& name)
  120. {
  121. const Font font (name, 20.0f, Font::plain);
  122. const int width = font.getStringWidth ("....");
  123. return width == font.getStringWidth ("WWWW")
  124. && width == font.getStringWidth ("0000")
  125. && width == font.getStringWidth ("1111")
  126. && width == font.getStringWidth ("iiii");
  127. }
  128. StringArray fontsToScan, fontsFound;
  129. };
  130. //==============================================================================
  131. struct EditorPanel : public Component,
  132. private ButtonListener
  133. {
  134. EditorPanel()
  135. : loadButton ("Load Scheme..."),
  136. saveButton ("Save Scheme...")
  137. {
  138. rebuildProperties();
  139. addAndMakeVisible (panel);
  140. addAndMakeVisible (loadButton);
  141. addAndMakeVisible (saveButton);
  142. loadButton.addListener (this);
  143. saveButton.addListener (this);
  144. }
  145. void rebuildProperties()
  146. {
  147. AppearanceSettings& scheme = getAppSettings().appearance;
  148. Array<PropertyComponent*> props;
  149. Value fontValue (scheme.getCodeFontValue());
  150. props.add (FontNameValueSource::createProperty ("Code Editor Font", fontValue));
  151. props.add (FontSizeValueSource::createProperty ("Font Size", fontValue));
  152. const StringArray colourNames (scheme.getColourNames());
  153. for (int i = 0; i < colourNames.size(); ++i)
  154. props.add (new ColourPropertyComponent (nullptr, colourNames[i],
  155. scheme.getColourValue (colourNames[i]),
  156. Colours::white, false));
  157. panel.clear();
  158. panel.addProperties (props);
  159. }
  160. void resized() override
  161. {
  162. Rectangle<int> r (getLocalBounds());
  163. panel.setBounds (r.removeFromTop (getHeight() - 28).reduced (4, 2));
  164. loadButton.setBounds (r.removeFromLeft (getWidth() / 2).reduced (10, 4));
  165. saveButton.setBounds (r.reduced (10, 3));
  166. }
  167. private:
  168. PropertyPanel panel;
  169. TextButton loadButton, saveButton;
  170. void buttonClicked (Button* b) override
  171. {
  172. if (b == &loadButton)
  173. loadScheme();
  174. else
  175. saveScheme();
  176. }
  177. void saveScheme()
  178. {
  179. FileChooser fc ("Select a file in which to save this colour-scheme...",
  180. getAppSettings().appearance.getSchemesFolder()
  181. .getNonexistentChildFile ("Scheme", AppearanceSettings::getSchemeFileSuffix()),
  182. AppearanceSettings::getSchemeFileWildCard());
  183. if (fc.browseForFileToSave (true))
  184. {
  185. File file (fc.getResult().withFileExtension (AppearanceSettings::getSchemeFileSuffix()));
  186. getAppSettings().appearance.writeToFile (file);
  187. getAppSettings().appearance.refreshPresetSchemeList();
  188. }
  189. }
  190. void loadScheme()
  191. {
  192. FileChooser fc ("Please select a colour-scheme file to load...",
  193. getAppSettings().appearance.getSchemesFolder(),
  194. AppearanceSettings::getSchemeFileWildCard());
  195. if (fc.browseForFileToOpen())
  196. if (getAppSettings().appearance.readFromFile (fc.getResult()))
  197. rebuildProperties();
  198. }
  199. JUCE_DECLARE_NON_COPYABLE (EditorPanel)
  200. };
  201. //==============================================================================
  202. struct FontNameValueSource : public ValueSourceFilter
  203. {
  204. FontNameValueSource (const Value& source) : ValueSourceFilter (source) {}
  205. var getValue() const override
  206. {
  207. return Font::fromString (sourceValue.toString()).getTypefaceName();
  208. }
  209. void setValue (const var& newValue) override
  210. {
  211. Font font (Font::fromString (sourceValue.toString()));
  212. font.setTypefaceName (newValue.toString().isEmpty() ? Font::getDefaultMonospacedFontName()
  213. : newValue.toString());
  214. sourceValue = font.toString();
  215. }
  216. static ChoicePropertyComponent* createProperty (const String& title, const Value& value)
  217. {
  218. StringArray fontNames = getAppSettings().monospacedFontNames;
  219. Array<var> values;
  220. values.add (Font::getDefaultMonospacedFontName());
  221. values.add (var());
  222. for (int i = 0; i < fontNames.size(); ++i)
  223. values.add (fontNames[i]);
  224. StringArray names;
  225. names.add ("<Default Monospaced>");
  226. names.add (String());
  227. names.addArray (getAppSettings().monospacedFontNames);
  228. return new ChoicePropertyComponent (Value (new FontNameValueSource (value)),
  229. title, names, values);
  230. }
  231. };
  232. //==============================================================================
  233. struct FontSizeValueSource : public ValueSourceFilter
  234. {
  235. FontSizeValueSource (const Value& source) : ValueSourceFilter (source) {}
  236. var getValue() const override
  237. {
  238. return Font::fromString (sourceValue.toString()).getHeight();
  239. }
  240. void setValue (const var& newValue) override
  241. {
  242. sourceValue = Font::fromString (sourceValue.toString()).withHeight (newValue).toString();
  243. }
  244. static PropertyComponent* createProperty (const String& title, const Value& value)
  245. {
  246. return new SliderPropertyComponent (Value (new FontSizeValueSource (value)),
  247. title, 5.0, 40.0, 0.1, 0.5);
  248. }
  249. };
  250. };
  251. void AppearanceSettings::showGlobalPreferences (ScopedPointer<Component>& ownerPointer)
  252. {
  253. if (ownerPointer != nullptr)
  254. ownerPointer->toFront (true);
  255. else
  256. new FloatingToolWindow ("Preferences",
  257. "globalPreferencesEditorPos",
  258. new GlobalPreferencesComponent,
  259. ownerPointer, false,
  260. 500, 500, 500, 500, 500, 500);
  261. }
  262. //==============================================================================
  263. AppearanceSettingsTab::AppearanceSettingsTab()
  264. {
  265. if (getAppSettings().monospacedFontNames.size() == 0)
  266. content = new AppearanceEditor::FontScanPanel();
  267. else
  268. content = new AppearanceEditor::EditorPanel();
  269. changeContent (content);
  270. }
  271. Component* AppearanceSettingsTab::getContent()
  272. {
  273. return this;
  274. }
  275. void AppearanceSettingsTab::changeContent (Component* newContent)
  276. {
  277. content = newContent;
  278. addAndMakeVisible (content);
  279. content->setBounds (getLocalBounds());
  280. }
  281. String AppearanceSettingsTab::getName() const noexcept
  282. {
  283. return "Code Editor";
  284. }
  285. void AppearanceSettingsTab::resized()
  286. {
  287. content->setBounds (getLocalBounds());
  288. }
  289. //==============================================================================
  290. GlobalPreferencesComponent::GlobalPreferencesComponent()
  291. : TabbedComponent (TabbedButtonBar::TabsAtTop)
  292. {
  293. preferenceTabs.add (new PathSettingsTab (TargetOS::getThisOS()));
  294. preferenceTabs.add (new AppearanceSettingsTab);
  295. for (GlobalPreferencesTab** tab = preferenceTabs.begin(); tab != preferenceTabs.end(); ++tab)
  296. addTab ((*tab)->getName(), findColour (backgroundColourId, true), (*tab)->getContent(), true);
  297. }