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 "../jucer_Headers.h"
27 #include "../jucer_Application.h"
28 
29 #include "jucer_StartPageComponent.h"
30 #include "jucer_StartPageTreeHolder.h"
31 #include "jucer_NewProjectTemplates.h"
32 #include "jucer_ContentComponents.h"
33 
34 //==============================================================================
35 struct ContentComponent  : public Component
36 {
resizedContentComponent37     void resized() override
38     {
39         if (content != nullptr)
40             content->setBounds (getLocalBounds());
41     }
42 
setContentContentComponent43     void setContent (std::unique_ptr<Component>&& newContent)
44     {
45         if (content.get() != newContent.get())
46         {
47             content = std::move (newContent);
48             addAndMakeVisible (content.get());
49             resized();
50         }
51     }
52 
53 private:
54     std::unique_ptr<Component> content;
55 
56     //==============================================================================
57     JUCE_LEAK_DETECTOR (ContentComponent)
58 };
59 
60 //==============================================================================
findExampleFile(int dirIndex,int index)61 static File findExampleFile (int dirIndex, int index)
62 {
63     auto dir = ProjucerApplication::getSortedExampleDirectories()[dirIndex];
64     return ProjucerApplication::getSortedExampleFilesInDirectory (dir)[index];
65 }
66 
createExampleProjectsTab(ContentComponent & content,std::function<void (const File &)> cb)67 static std::unique_ptr<Component> createExampleProjectsTab (ContentComponent& content, std::function<void (const File&)> cb)
68 {
69     StringArray exampleCategories;
70     std::vector<StringArray> examples;
71 
72     for (auto& dir : ProjucerApplication::getSortedExampleDirectories())
73     {
74         exampleCategories.add (dir.getFileName());
75 
76         StringArray ex;
77         for (auto& f : ProjucerApplication::getSortedExampleFilesInDirectory (dir))
78             ex.add (f.getFileNameWithoutExtension());
79 
80         examples.push_back (ex);
81     }
82 
83     if (exampleCategories.isEmpty())
84         return nullptr;
85 
86     auto selectedCallback = [&, cb] (int category, int index) mutable
87     {
88         content.setContent (std::make_unique<ExampleComponent> (findExampleFile (category, index), cb));
89     };
90 
91     return std::make_unique<StartPageTreeHolder> (exampleCategories,
92                                                   examples,
93                                                   std::move (selectedCallback),
94                                                   StartPageTreeHolder::Open::no);
95 }
96 
97 //==============================================================================
getAllTemplateCategoryStrings()98 static StringArray getAllTemplateCategoryStrings()
99 {
100     StringArray categories;
101 
102     for (auto& t : NewProjectTemplates::getAllTemplates())
103         categories.addIfNotAlreadyThere (NewProjectTemplates::getProjectCategoryString (t.category));
104 
105     return categories;
106 }
107 
getTemplatesInCategory(const String & category)108 static std::vector<NewProjectTemplates::ProjectTemplate> getTemplatesInCategory (const String& category)
109 {
110     std::vector<NewProjectTemplates::ProjectTemplate> templates;
111 
112     for (auto& t : NewProjectTemplates::getAllTemplates())
113         if (NewProjectTemplates::getProjectCategoryString (t.category) == category)
114             templates.push_back (t);
115 
116     return templates;
117 }
118 
getAllTemplateNamesForCategory(const String & category)119 static StringArray getAllTemplateNamesForCategory (const String& category)
120 {
121     StringArray types;
122 
123     for (auto& t : getTemplatesInCategory (category))
124         types.add (t.displayName);
125 
126     return types;
127 }
128 
createProjectTemplatesTab(ContentComponent & content,std::function<void (std::unique_ptr<Project> &&)> && cb)129 static std::unique_ptr<Component> createProjectTemplatesTab (ContentComponent& content,
130                                                              std::function<void (std::unique_ptr<Project>&&)>&& cb)
131 {
132     auto categories = getAllTemplateCategoryStrings();
133 
134     std::vector<StringArray> templateNames;
135 
136     for (auto& c : categories)
137         templateNames.push_back (getAllTemplateNamesForCategory (c));
138 
139     auto selectedCallback = [&, cb] (int category, int index)
140     {
141         auto categoryString = getAllTemplateCategoryStrings()[category];
142         auto templates = getTemplatesInCategory (categoryString);
143 
144         content.setContent (std::make_unique<TemplateComponent> (templates[(size_t) index], std::move (cb)));
145     };
146 
147     auto holder = std::make_unique<StartPageTreeHolder> (categories,
148                                                          templateNames,
149                                                          std::move (selectedCallback),
150                                                          StartPageTreeHolder::Open::yes);
151     holder->setSelectedItem (categories[0], 1);
152 
153     JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wredundant-move")
154     return std::move (holder);
155     JUCE_END_IGNORE_WARNINGS_GCC_LIKE
156 }
157 
158 //==============================================================================
159 struct ProjectTemplatesAndExamples  : public TabbedComponent
160 {
ProjectTemplatesAndExamplesProjectTemplatesAndExamples161     ProjectTemplatesAndExamples (ContentComponent& c,
162                                  std::function<void (std::unique_ptr<Project>&&)>&& newProjectCb,
163                                  std::function<void (const File&)>&& exampleCb)
164         : TabbedComponent (TabbedButtonBar::Orientation::TabsAtTop),
165           content (c),
166           exampleSelectedCallback (std::move (exampleCb))
167     {
168         addTab ("New Project", Colours::transparentBlack, createProjectTemplatesTab (content, std::move (newProjectCb)).release(), true);
169         refreshExamplesTab();
170     }
171 
refreshExamplesTabProjectTemplatesAndExamples172     void refreshExamplesTab()
173     {
174         auto wasOpen = (getCurrentTabIndex() == 1);
175 
176         removeTab (1);
177 
178         auto exampleTabs = createExampleProjectsTab (content, exampleSelectedCallback);
179 
180         addTab ("Open Example", Colours::transparentBlack, exampleTabs == nullptr ? new SetJUCEPathComponent (*this)
181                                                                                   : exampleTabs.release(),
182                 true);
183 
184         if (wasOpen)
185             setCurrentTabIndex (1);
186     }
187 
188 private:
189     //==============================================================================
190     struct SetJUCEPathComponent    : public Component,
191                                      private ChangeListener
192     {
SetJUCEPathComponentProjectTemplatesAndExamples::SetJUCEPathComponent193         explicit SetJUCEPathComponent (ProjectTemplatesAndExamples& o)
194             : owner (o)
195         {
196             getGlobalProperties().addChangeListener (this);
197 
198             setPathButton.setButtonText ("Set path to JUCE...");
199             setPathButton.onClick = [] { ProjucerApplication::getApp().showPathsWindow (true); };
200 
201             addAndMakeVisible (setPathButton);
202         }
203 
~SetJUCEPathComponentProjectTemplatesAndExamples::SetJUCEPathComponent204         ~SetJUCEPathComponent() override
205         {
206             getGlobalProperties().removeChangeListener (this);
207         }
208 
paintProjectTemplatesAndExamples::SetJUCEPathComponent209         void paint (Graphics& g) override
210         {
211             g.fillAll (findColour (secondaryBackgroundColourId));
212         }
213 
resizedProjectTemplatesAndExamples::SetJUCEPathComponent214         void resized() override
215         {
216             auto bounds = getLocalBounds().reduced (5);
217             bounds.removeFromTop (25);
218 
219             setPathButton.setBounds (bounds.removeFromTop (25));
220         }
221 
222     private:
changeListenerCallbackProjectTemplatesAndExamples::SetJUCEPathComponent223         void changeListenerCallback (ChangeBroadcaster*) override
224         {
225             if (isValidJUCEExamplesDirectory (ProjucerApplication::getJUCEExamplesDirectoryPathFromGlobal()))
226                 owner.refreshExamplesTab();
227         }
228 
229         ProjectTemplatesAndExamples& owner;
230         TextButton setPathButton;
231     };
232 
233     ContentComponent& content;
234     std::function<void (const File&)> exampleSelectedCallback;
235 };
236 
237 //==============================================================================
StartPageComponent(std::function<void (std::unique_ptr<Project> &&)> && newProjectCb,std::function<void (const File &)> && exampleCb)238 StartPageComponent::StartPageComponent (std::function<void (std::unique_ptr<Project>&&)>&& newProjectCb,
239                                         std::function<void (const File&)>&& exampleCb)
240     : content (std::make_unique<ContentComponent>()),
241       tabs (std::make_unique<ProjectTemplatesAndExamples> (*content, std::move (newProjectCb), std::move (exampleCb)))
242 {
243     tabs->setOutline (0);
244     addAndMakeVisible (*tabs);
245 
246     addAndMakeVisible (openExistingButton);
247     openExistingButton.setCommandToTrigger (&ProjucerApplication::getCommandManager(), CommandIDs::open, true);
248 
249     addAndMakeVisible (*content);
250 
251     setSize (900, 600);
252 }
253 
paint(Graphics & g)254 void StartPageComponent::paint (Graphics& g)
255 {
256     g.fillAll (findColour (backgroundColourId));
257 }
258 
resized()259 void StartPageComponent::resized()
260 {
261     auto bounds = getLocalBounds().reduced (10);
262 
263     auto tabBounds = bounds.removeFromLeft (bounds.getWidth() / 3);
264 
265     openExistingButton.setBounds (tabBounds.removeFromBottom (30).reduced (10, 0));
266     tabBounds.removeFromBottom (5);
267 
268     tabs->setBounds (tabBounds);
269     bounds.removeFromLeft (10);
270 
271     content->setBounds (bounds);
272 }
273