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.

425 lines
14KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. #include "jucer_Application.h"
  19. #include "jucer_AppearanceSettings.h"
  20. namespace AppearanceColours
  21. {
  22. struct ColourInfo
  23. {
  24. const char* name;
  25. uint32 colourID;
  26. bool mustBeOpaque;
  27. };
  28. static const ColourInfo colours[] =
  29. {
  30. { "Code Background", CodeEditorComponent::backgroundColourId, true },
  31. { "Line Number Bkgd", CodeEditorComponent::lineNumberBackgroundId, false },
  32. { "Line Numbers", CodeEditorComponent::lineNumberTextId, false },
  33. { "Plain Text", CodeEditorComponent::defaultTextColourId, false },
  34. { "Selected Text Bkgd", CodeEditorComponent::highlightColourId, false },
  35. { "Caret", CaretComponent::caretColourId, false },
  36. { "Main Window Bkgd", mainBackgroundColourId, true },
  37. { "Project Panel Bkgd", projectPanelBackgroundColourId, true },
  38. { "Treeview Highlight", treeviewHighlightColourId, false }
  39. };
  40. }
  41. //==============================================================================
  42. AppearanceSettings::AppearanceSettings (const CodeEditorComponent& editor)
  43. : settings ("COLOUR_SCHEME")
  44. {
  45. IntrojucerLookAndFeel lf;
  46. for (int i = 0; i < sizeof (AppearanceColours::colours) / sizeof (AppearanceColours::colours[0]); ++i)
  47. getColourValue (AppearanceColours::colours[i].name) = lf.findColour (AppearanceColours::colours[i].colourID).toString();
  48. const CodeEditorComponent::ColourScheme cs (editor.getColourScheme());
  49. for (int i = cs.types.size(); --i >= 0;)
  50. {
  51. CodeEditorComponent::ColourScheme::TokenType& t = cs.types.getReference(i);
  52. getColourValue (t.name) = t.colour.toString();
  53. }
  54. Font f (editor.getFont());
  55. f.setTypefaceName (f.getTypeface()->getName());
  56. getCodeFontValue() = f.toString();
  57. settings.addListener (this);
  58. }
  59. bool AppearanceSettings::readFromXML (const XmlElement& xml)
  60. {
  61. if (xml.hasTagName (settings.getType().toString()))
  62. {
  63. ValueTree newSettings (ValueTree::fromXml (xml));
  64. for (int i = settings.getNumChildren(); --i >= 0;)
  65. {
  66. const ValueTree c (settings.getChild (i));
  67. if (! newSettings.getChildWithProperty (Ids::name, c.getProperty (Ids::name)).isValid())
  68. newSettings.addChild (c.createCopy(), 0, nullptr);
  69. }
  70. settings = newSettings;
  71. return true;
  72. }
  73. return false;
  74. }
  75. bool AppearanceSettings::readFromFile (const File& file)
  76. {
  77. const ScopedPointer<XmlElement> xml (XmlDocument::parse (file));
  78. return xml != nullptr && readFromXML (*xml);
  79. }
  80. bool AppearanceSettings::writeToFile (const File& file) const
  81. {
  82. const ScopedPointer<XmlElement> xml (settings.createXml());
  83. return xml != nullptr && xml->writeToFile (file, String::empty);
  84. }
  85. StringArray AppearanceSettings::getColourNames() const
  86. {
  87. StringArray s;
  88. for (int i = 0; i < settings.getNumChildren(); ++i)
  89. {
  90. const ValueTree c (settings.getChild(i));
  91. if (c.hasType ("COLOUR"))
  92. s.add (c [Ids::name]);
  93. }
  94. return s;
  95. }
  96. void AppearanceSettings::updateColourScheme()
  97. {
  98. applyToLookAndFeel (LookAndFeel::getDefaultLookAndFeel());
  99. JucerApplication::getApp().mainWindowList.sendLookAndFeelChange();
  100. }
  101. void AppearanceSettings::applyToLookAndFeel (LookAndFeel& lf) const
  102. {
  103. for (int i = 0; i < sizeof (AppearanceColours::colours) / sizeof (AppearanceColours::colours[0]); ++i)
  104. {
  105. Colour col;
  106. if (getColour (AppearanceColours::colours[i].name, col))
  107. {
  108. if (AppearanceColours::colours[i].mustBeOpaque)
  109. col = Colours::white.overlaidWith (col);
  110. lf.setColour (AppearanceColours::colours[i].colourID, col);
  111. }
  112. }
  113. }
  114. void AppearanceSettings::applyToCodeEditor (CodeEditorComponent& editor) const
  115. {
  116. CodeEditorComponent::ColourScheme cs (editor.getColourScheme());
  117. for (int i = cs.types.size(); --i >= 0;)
  118. {
  119. CodeEditorComponent::ColourScheme::TokenType& t = cs.types.getReference(i);
  120. getColour (t.name, t.colour);
  121. }
  122. editor.setColourScheme (cs);
  123. editor.setFont (getCodeFont());
  124. }
  125. Font AppearanceSettings::getCodeFont() const
  126. {
  127. const String fontString (settings [Ids::font].toString());
  128. if (fontString.isEmpty())
  129. {
  130. #if JUCE_MAC
  131. Font font (13.0f);
  132. font.setTypefaceName ("Menlo");
  133. #else
  134. Font font (10.0f);
  135. font.setTypefaceName (Font::getDefaultMonospacedFontName());
  136. #endif
  137. return font;
  138. }
  139. return Font::fromString (fontString);
  140. }
  141. Value AppearanceSettings::getCodeFontValue()
  142. {
  143. return settings.getPropertyAsValue (Ids::font, nullptr);
  144. }
  145. Value AppearanceSettings::getColourValue (const String& colourName)
  146. {
  147. ValueTree c (settings.getChildWithProperty (Ids::name, colourName));
  148. if (! c.isValid())
  149. {
  150. c = ValueTree ("COLOUR");
  151. c.setProperty (Ids::name, colourName, nullptr);
  152. settings.addChild (c, -1, nullptr);
  153. }
  154. return c.getPropertyAsValue (Ids::colour, nullptr);
  155. }
  156. bool AppearanceSettings::getColour (const String& name, Colour& result) const
  157. {
  158. const ValueTree colour (settings.getChildWithProperty (Ids::name, name));
  159. if (colour.isValid())
  160. {
  161. result = Colour::fromString (colour [Ids::colour].toString());
  162. return true;
  163. }
  164. return false;
  165. }
  166. //==============================================================================
  167. struct AppearanceEditor
  168. {
  169. class Window : public DialogWindow
  170. {
  171. public:
  172. Window() : DialogWindow ("Appearance Settings", Colours::black, true, true)
  173. {
  174. setUsingNativeTitleBar (true);
  175. setContentOwned (new EditorPanel(), false);
  176. setResizable (true, true);
  177. const int width = 350;
  178. setResizeLimits (width, 200, width, 1000);
  179. String windowState (getAppProperties().getValue (getWindowPosName()));
  180. if (windowState.isNotEmpty())
  181. restoreWindowStateFromString (windowState);
  182. else
  183. centreAroundComponent (Component::getCurrentlyFocusedComponent(), width, 500);
  184. setVisible (true);
  185. }
  186. ~Window()
  187. {
  188. getAppProperties().setValue (getWindowPosName(), getWindowStateAsString());
  189. }
  190. void closeButtonPressed()
  191. {
  192. JucerApplication::getApp().appearanceEditorWindow = nullptr;
  193. }
  194. private:
  195. static const char* getWindowPosName() { return "colourSchemeEditorPos"; }
  196. JUCE_DECLARE_NON_COPYABLE (Window);
  197. };
  198. //==============================================================================
  199. class EditorPanel : public Component,
  200. private Button::Listener
  201. {
  202. public:
  203. EditorPanel()
  204. : loadButton ("Load Scheme..."),
  205. saveButton ("Save Scheme...")
  206. {
  207. setOpaque (true);
  208. rebuildProperties();
  209. addAndMakeVisible (&panel);
  210. loadButton.setColour (TextButton::buttonColourId, Colours::grey);
  211. saveButton.setColour (TextButton::buttonColourId, Colours::grey);
  212. addAndMakeVisible (&loadButton);
  213. addAndMakeVisible (&saveButton);
  214. loadButton.addListener (this);
  215. saveButton.addListener (this);
  216. }
  217. void rebuildProperties()
  218. {
  219. AppearanceSettings& scheme = getAppSettings().appearance;
  220. Array <PropertyComponent*> props;
  221. Value fontValue (scheme.getCodeFontValue());
  222. props.add (FontNameValueSource::createProperty ("Code Editor Font", fontValue));
  223. props.add (FontSizeValueSource::createProperty ("Font Size", fontValue));
  224. const StringArray colourNames (scheme.getColourNames());
  225. for (int i = 0; i < colourNames.size(); ++i)
  226. props.add (new ColourPropertyComponent (nullptr, colourNames[i],
  227. scheme.getColourValue (colourNames[i]),
  228. Colours::white, false));
  229. panel.clear();
  230. panel.addProperties (props);
  231. }
  232. void paint (Graphics& g)
  233. {
  234. g.fillAll (Colours::black);
  235. }
  236. void resized()
  237. {
  238. Rectangle<int> r (getLocalBounds());
  239. panel.setBounds (r.removeFromTop (getHeight() - 26).reduced (4, 3));
  240. loadButton.setBounds (r.removeFromLeft (getWidth() / 2).reduced (10, 3));
  241. saveButton.setBounds (r.reduced (10, 3));
  242. }
  243. private:
  244. PropertyPanel panel;
  245. TextButton loadButton, saveButton;
  246. void buttonClicked (Button* b)
  247. {
  248. if (b == &loadButton)
  249. loadScheme();
  250. else
  251. saveScheme();
  252. }
  253. void saveScheme()
  254. {
  255. FileChooser fc ("Select a file in which to save this colour-scheme...",
  256. getAppSettings().getSchemesFolder().getNonexistentChildFile ("Scheme", ".editorscheme"),
  257. "*.editorscheme");
  258. if (fc.browseForFileToSave (true))
  259. {
  260. File file (fc.getResult().withFileExtension (".editorscheme"));
  261. getAppSettings().appearance.writeToFile (file);
  262. }
  263. }
  264. void loadScheme()
  265. {
  266. FileChooser fc ("Please select a colour-scheme file to load...",
  267. getAppSettings().getSchemesFolder(),
  268. "*.editorscheme");
  269. if (fc.browseForFileToOpen())
  270. {
  271. if (getAppSettings().appearance.readFromFile (fc.getResult()))
  272. rebuildProperties();
  273. }
  274. }
  275. JUCE_DECLARE_NON_COPYABLE (EditorPanel);
  276. };
  277. //==============================================================================
  278. class FontNameValueSource : public ValueSourceFilter
  279. {
  280. public:
  281. FontNameValueSource (const Value& source) : ValueSourceFilter (source) {}
  282. var getValue() const
  283. {
  284. return Font::fromString (sourceValue.toString()).getTypefaceName();
  285. }
  286. void setValue (const var& newValue)
  287. {
  288. Font font (Font::fromString (sourceValue.toString()));
  289. font.setTypefaceName (newValue.toString());
  290. sourceValue = font.toString();
  291. }
  292. static ChoicePropertyComponent* createProperty (const String& title, const Value& value)
  293. {
  294. const StringArray& fontNames = getAppSettings().getFontNames();
  295. Array<var> values;
  296. for (int i = 0; i < fontNames.size(); ++i)
  297. values.add (fontNames[i]);
  298. return new ChoicePropertyComponent (Value (new FontNameValueSource (value)),
  299. title, fontNames, values);
  300. }
  301. };
  302. //==============================================================================
  303. class FontSizeValueSource : public ValueSourceFilter
  304. {
  305. public:
  306. FontSizeValueSource (const Value& source) : ValueSourceFilter (source) {}
  307. var getValue() const
  308. {
  309. return Font::fromString (sourceValue.toString()).getHeight();
  310. }
  311. void setValue (const var& newValue)
  312. {
  313. sourceValue = Font::fromString (sourceValue.toString()).withHeight (newValue).toString();
  314. }
  315. static PropertyComponent* createProperty (const String& title, const Value& value)
  316. {
  317. return new SliderPropertyComponent (Value (new FontSizeValueSource (value)),
  318. title, 5.0, 40.0, 0.1, 0.5);
  319. }
  320. };
  321. };
  322. Component* AppearanceSettings::createEditorWindow()
  323. {
  324. return new AppearanceEditor::Window();
  325. }
  326. //==============================================================================
  327. IntrojucerLookAndFeel::IntrojucerLookAndFeel()
  328. {
  329. setColour (mainBackgroundColourId, Colour::greyLevel (0.8f));
  330. setColour (projectPanelBackgroundColourId, Colour::greyLevel (0.93f));
  331. setColour (treeviewHighlightColourId, Colour (0x401111ee));
  332. }
  333. Rectangle<int> IntrojucerLookAndFeel::getPropertyComponentContentPosition (PropertyComponent& component)
  334. {
  335. if (component.findParentComponentOfClass<AppearanceEditor::EditorPanel>() != nullptr)
  336. return component.getLocalBounds().reduced (1, 1).removeFromRight (component.getWidth() / 2);
  337. return LookAndFeel::getPropertyComponentContentPosition (component);
  338. }