1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2020 - Raw Material Software Limited
6 
7    JUCE is an open source library subject to commercial or open-source
8    licensing.
9 
10    By using JUCE, you agree to the terms of both the JUCE 6 End-User License
11    Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
12 
13    End User License Agreement: www.juce.com/juce-6-licence
14    Privacy Policy: www.juce.com/juce-privacy-policy
15 
16    Or: You may also use this code under the terms of the GPL v3 (see
17    www.gnu.org/licenses).
18 
19    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21    DISCLAIMED.
22 
23   ==============================================================================
24 */
25 
26 #include "../Application/jucer_Headers.h"
27 #include "../Application/jucer_Application.h"
28 #include "jucer_AppearanceSettings.h"
29 
30 //==============================================================================
AppearanceSettings(bool updateAppWhenChanged)31 AppearanceSettings::AppearanceSettings (bool updateAppWhenChanged)
32     : settings ("COLOUR_SCHEME")
33 {
34     CodeDocument doc;
35     CPlusPlusCodeTokeniser tokeniser;
36     CodeEditorComponent editor (doc, &tokeniser);
37 
38     CodeEditorComponent::ColourScheme cs (editor.getColourScheme());
39 
40     for (int i = cs.types.size(); --i >= 0;)
41     {
42         auto& t = cs.types.getReference(i);
43         getColourValue (t.name) = t.colour.toString();
44     }
45 
46     getCodeFontValue() = getDefaultCodeFont().toString();
47 
48     if (updateAppWhenChanged)
49         settings.addListener (this);
50 }
51 
getSchemesFolder()52 File AppearanceSettings::getSchemesFolder()
53 {
54     File f (getGlobalProperties().getFile().getSiblingFile ("Schemes"));
55     f.createDirectory();
56     return f;
57 }
58 
writeDefaultSchemeFile(const String & xmlString,const String & name)59 void AppearanceSettings::writeDefaultSchemeFile (const String& xmlString, const String& name)
60 {
61     auto file = getSchemesFolder().getChildFile (name).withFileExtension (getSchemeFileSuffix());
62 
63     AppearanceSettings settings (false);
64 
65     if (auto xml = parseXML (xmlString))
66         settings.readFromXML (*xml);
67 
68     settings.writeToFile (file);
69 }
70 
refreshPresetSchemeList()71 void AppearanceSettings::refreshPresetSchemeList()
72 {
73     writeDefaultSchemeFile (BinaryData::colourscheme_dark_xml,  "Default (Dark)");
74     writeDefaultSchemeFile (BinaryData::colourscheme_light_xml, "Default (Light)");
75 
76     auto newSchemes = getSchemesFolder().findChildFiles (File::findFiles, false, String ("*") + getSchemeFileSuffix());
77 
78     if (newSchemes != presetSchemeFiles)
79     {
80         presetSchemeFiles.swapWith (newSchemes);
81         ProjucerApplication::getCommandManager().commandStatusChanged();
82     }
83 }
84 
getPresetSchemes()85 StringArray AppearanceSettings::getPresetSchemes()
86 {
87     StringArray s;
88     for (int i = 0; i < presetSchemeFiles.size(); ++i)
89         s.add (presetSchemeFiles.getReference(i).getFileNameWithoutExtension());
90 
91     return s;
92 }
93 
selectPresetScheme(int index)94 void AppearanceSettings::selectPresetScheme (int index)
95 {
96     readFromFile (presetSchemeFiles [index]);
97 }
98 
readFromXML(const XmlElement & xml)99 bool AppearanceSettings::readFromXML (const XmlElement& xml)
100 {
101     if (xml.hasTagName (settings.getType().toString()))
102     {
103         const ValueTree newSettings (ValueTree::fromXml (xml));
104 
105         // we'll manually copy across the new properties to the existing tree so that
106         // any open editors will be kept up to date..
107         settings.copyPropertiesFrom (newSettings, nullptr);
108 
109         for (int i = settings.getNumChildren(); --i >= 0;)
110         {
111             ValueTree c (settings.getChild (i));
112 
113             const ValueTree newValue (newSettings.getChildWithProperty (Ids::name, c.getProperty (Ids::name)));
114 
115             if (newValue.isValid())
116                 c.copyPropertiesFrom (newValue, nullptr);
117         }
118 
119         return true;
120     }
121 
122     return false;
123 }
124 
readFromFile(const File & file)125 bool AppearanceSettings::readFromFile (const File& file)
126 {
127     if (auto xml = parseXML (file))
128         return readFromXML (*xml);
129 
130     return false;
131 }
132 
writeToFile(const File & file) const133 bool AppearanceSettings::writeToFile (const File& file) const
134 {
135     if (auto xml = settings.createXml())
136         return xml->writeTo (file, {});
137 
138     return false;
139 }
140 
getDefaultCodeFont()141 Font AppearanceSettings::getDefaultCodeFont()
142 {
143     return Font (Font::getDefaultMonospacedFontName(), Font::getDefaultStyle(), 13.0f);
144 }
145 
getColourNames() const146 StringArray AppearanceSettings::getColourNames() const
147 {
148     StringArray s;
149 
150     for (auto c : settings)
151         if (c.hasType ("COLOUR"))
152             s.add (c[Ids::name]);
153 
154     return s;
155 }
156 
updateColourScheme()157 void AppearanceSettings::updateColourScheme()
158 {
159     ProjucerApplication::getApp().mainWindowList.sendLookAndFeelChange();
160 }
161 
applyToCodeEditor(CodeEditorComponent & editor) const162 void AppearanceSettings::applyToCodeEditor (CodeEditorComponent& editor) const
163 {
164     CodeEditorComponent::ColourScheme cs (editor.getColourScheme());
165 
166     for (int i = cs.types.size(); --i >= 0;)
167     {
168         CodeEditorComponent::ColourScheme::TokenType& t = cs.types.getReference(i);
169         getColour (t.name, t.colour);
170     }
171 
172     editor.setColourScheme (cs);
173     editor.setFont (getCodeFont());
174 
175     editor.setColour (ScrollBar::thumbColourId, editor.findColour (CodeEditorComponent::backgroundColourId)
176                                                       .contrasting()
177                                                       .withAlpha (0.13f));
178 }
179 
getCodeFont() const180 Font AppearanceSettings::getCodeFont() const
181 {
182     const String fontString (settings [Ids::font].toString());
183 
184     if (fontString.isEmpty())
185         return getDefaultCodeFont();
186 
187     return Font::fromString (fontString);
188 }
189 
getCodeFontValue()190 Value AppearanceSettings::getCodeFontValue()
191 {
192     return settings.getPropertyAsValue (Ids::font, nullptr);
193 }
194 
getColourValue(const String & colourName)195 Value AppearanceSettings::getColourValue (const String& colourName)
196 {
197     ValueTree c (settings.getChildWithProperty (Ids::name, colourName));
198 
199     if (! c.isValid())
200     {
201         c = ValueTree ("COLOUR");
202         c.setProperty (Ids::name, colourName, nullptr);
203         settings.appendChild (c, nullptr);
204     }
205 
206     return c.getPropertyAsValue (Ids::colour, nullptr);
207 }
208 
getColour(const String & name,Colour & result) const209 bool AppearanceSettings::getColour (const String& name, Colour& result) const
210 {
211     const ValueTree colour (settings.getChildWithProperty (Ids::name, name));
212 
213     if (colour.isValid())
214     {
215         result = Colour::fromString (colour [Ids::colour].toString());
216         return true;
217     }
218 
219     return false;
220 }
221