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.

453 lines
15KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. #ifndef JUCER_NEWPROJECTWIZARDCOMPONENT_H_INCLUDED
  18. #define JUCER_NEWPROJECTWIZARDCOMPONENT_H_INCLUDED
  19. class ModulesFolderPathBox : public Component,
  20. private ButtonListener,
  21. private ComboBoxListener
  22. {
  23. public:
  24. ModulesFolderPathBox (File initialFileOrDirectory)
  25. : currentPathBox ("currentPathBox"),
  26. openFolderButton (TRANS("...")),
  27. modulesLabel (String::empty, TRANS("Modules Folder") + ":")
  28. {
  29. if (initialFileOrDirectory == File::nonexistent)
  30. initialFileOrDirectory = findDefaultModulesFolder();
  31. setModulesFolder (initialFileOrDirectory);
  32. addAndMakeVisible (currentPathBox);
  33. currentPathBox.setEditableText (true);
  34. currentPathBox.addListener (this);
  35. addAndMakeVisible (openFolderButton);
  36. openFolderButton.addListener (this);
  37. openFolderButton.setTooltip (TRANS ("Select JUCE modules folder"));
  38. addAndMakeVisible (modulesLabel);
  39. modulesLabel.attachToComponent (&currentPathBox, true);
  40. }
  41. void resized() override
  42. {
  43. Rectangle<int> r = getLocalBounds();
  44. modulesLabel.setBounds (r.removeFromLeft (110));
  45. openFolderButton.setBounds (r.removeFromRight (40));
  46. r.removeFromRight (5);
  47. currentPathBox.setBounds (r);
  48. }
  49. static bool selectJuceFolder (File& result)
  50. {
  51. for (;;)
  52. {
  53. FileChooser fc ("Select your JUCE modules folder...",
  54. findDefaultModulesFolder(),
  55. "*");
  56. if (! fc.browseForDirectory())
  57. return false;
  58. if (isJuceModulesFolder (fc.getResult()))
  59. {
  60. result = fc.getResult();
  61. return true;
  62. }
  63. AlertWindow::showMessageBox (AlertWindow::WarningIcon,
  64. "Not a valid JUCE modules folder!",
  65. "Please select the folder containing your juce_* modules!\n\n"
  66. "This is required so that the new project can be given some essential core modules.");
  67. }
  68. }
  69. void selectJuceFolder()
  70. {
  71. File result;
  72. if (selectJuceFolder (result))
  73. setModulesFolder (result);
  74. }
  75. void setModulesFolder (const File& newFolder)
  76. {
  77. if (modulesFolder != newFolder)
  78. {
  79. modulesFolder = newFolder;
  80. currentPathBox.setText (modulesFolder.getFullPathName(), dontSendNotification);
  81. }
  82. }
  83. void buttonClicked (Button*) override
  84. {
  85. selectJuceFolder();
  86. }
  87. void comboBoxChanged (ComboBox*) override
  88. {
  89. setModulesFolder (File::getCurrentWorkingDirectory().getChildFile (currentPathBox.getText()));
  90. }
  91. File modulesFolder;
  92. private:
  93. ComboBox currentPathBox;
  94. TextButton openFolderButton;
  95. Label modulesLabel;
  96. };
  97. /** The target platforms chooser for the chosen template. */
  98. class PlatformTargetsComp : public Component,
  99. private ListBoxModel
  100. {
  101. public:
  102. PlatformTargetsComp()
  103. {
  104. setOpaque (false);
  105. const Array<ProjectExporter::ExporterTypeInfo> types (ProjectExporter::getExporterTypes());
  106. for (int i = 0; i < types.size(); ++i)
  107. {
  108. const ProjectExporter::ExporterTypeInfo& type = types.getReference (i);
  109. platforms.add (new PlatformType (ImageCache::getFromMemory (type.iconData, type.iconDataSize), type.name));
  110. }
  111. listBox.setRowHeight (35);
  112. listBox.setModel (this);
  113. listBox.setOpaque (false);
  114. listBox.setMultipleSelectionEnabled (true);
  115. listBox.setClickingTogglesRowSelection (true);
  116. listBox.setColour (ListBox::backgroundColourId, Colours::white.withAlpha (0.0f));
  117. addAndMakeVisible (listBox);
  118. selectDefaultExporterIfNoneSelected();
  119. }
  120. StringArray getSelectedPlatforms() const
  121. {
  122. StringArray list;
  123. for (int i = 0; i < platforms.size(); ++i)
  124. if (listBox.isRowSelected (i))
  125. list.add (platforms.getUnchecked(i)->name);
  126. return list;
  127. }
  128. void selectDefaultExporterIfNoneSelected()
  129. {
  130. if (listBox.getNumSelectedRows() == 0)
  131. {
  132. for (int i = platforms.size(); --i >= 0;)
  133. {
  134. if (platforms.getUnchecked(i)->name == ProjectExporter::getCurrentPlatformExporterName())
  135. {
  136. listBox.selectRow (i);
  137. break;
  138. }
  139. }
  140. }
  141. }
  142. void resized() override
  143. {
  144. listBox.setBounds (getLocalBounds());
  145. }
  146. int getNumRows() override
  147. {
  148. return platforms.size();
  149. }
  150. void paintListBoxItem (int rowNumber, Graphics& g, int width, int height, bool rowIsSelected) override
  151. {
  152. if (PlatformType* platform = platforms[rowNumber])
  153. {
  154. if (rowIsSelected)
  155. g.fillAll (Colour (0x99f29000));
  156. Rectangle<float> dotSelect ((float) height, (float) height);
  157. dotSelect.reduce (12, 12);
  158. g.setColour (Colour (0x33ffffff));
  159. g.fillEllipse (dotSelect);
  160. if (rowIsSelected)
  161. {
  162. const float tx = dotSelect.getCentreX();
  163. const float ty = dotSelect.getCentreY() + 1.0f;
  164. Path tick;
  165. tick.startNewSubPath (tx - 5.0f, ty - 6.0f);
  166. tick.lineTo (tx, ty);
  167. tick.lineTo (tx + 8.0f, ty - 13.0f);
  168. g.setColour (Colours::white);
  169. g.strokePath (tick, PathStrokeType (3.0f));
  170. }
  171. g.setColour (Colours::black);
  172. g.drawImageWithin (platform->icon, 40, 0, height, height, RectanglePlacement::stretchToFit);
  173. g.drawText (platform->name, 90, 0, width, height, Justification::left);
  174. }
  175. }
  176. void selectedRowsChanged (int) override
  177. {
  178. selectDefaultExporterIfNoneSelected();
  179. }
  180. private:
  181. struct PlatformType
  182. {
  183. PlatformType (const Image& platformIcon, const String& platformName)
  184. : icon (platformIcon), name (platformName)
  185. {
  186. }
  187. Image icon;
  188. String name;
  189. };
  190. ListBox listBox;
  191. OwnedArray<PlatformType> platforms;
  192. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PlatformTargetsComp)
  193. };
  194. //==============================================================================
  195. /**
  196. The Component for project creation.
  197. Features a file browser to select project destination and
  198. a list box of platform targets to generate.
  199. */
  200. class WizardComp : public Component,
  201. private ButtonListener,
  202. private ComboBoxListener,
  203. private TextEditorListener,
  204. private FileBrowserListener
  205. {
  206. public:
  207. WizardComp()
  208. : platformTargets(),
  209. projectName (TRANS("Project name")),
  210. nameLabel (String::empty, TRANS("Project Name") + ":"),
  211. typeLabel (String::empty, TRANS("Project Type") + ":"),
  212. fileBrowser (FileBrowserComponent::saveMode
  213. | FileBrowserComponent::canSelectDirectories
  214. | FileBrowserComponent::doNotClearFileNameOnRootChange,
  215. NewProjectWizardClasses::getLastWizardFolder(), nullptr, nullptr),
  216. fileOutline (String::empty, TRANS("Project Folder") + ":"),
  217. targetsOutline (String::empty, TRANS("Target Platforms") + ":"),
  218. createButton (TRANS("Create") + "..."),
  219. cancelButton (TRANS("Cancel")),
  220. modulesPathBox (findDefaultModulesFolder())
  221. {
  222. setOpaque (false);
  223. addChildAndSetID (&projectName, "projectName");
  224. projectName.setText ("NewProject");
  225. projectName.setBounds ("120, 34, parent.width / 2 - 10, top + 22");
  226. nameLabel.attachToComponent (&projectName, true);
  227. projectName.addListener (this);
  228. addChildAndSetID (&projectType, "projectType");
  229. projectType.addItemList (getWizardNames(), 1);
  230. projectType.setSelectedId (1, dontSendNotification);
  231. projectType.setBounds ("120, projectName.bottom + 4, projectName.right, top + 22");
  232. typeLabel.attachToComponent (&projectType, true);
  233. projectType.addListener (this);
  234. addChildAndSetID (&fileOutline, "fileOutline");
  235. fileOutline.setColour (GroupComponent::outlineColourId, Colours::black.withAlpha (0.2f));
  236. fileOutline.setTextLabelPosition (Justification::centred);
  237. fileOutline.setBounds ("30, projectType.bottom + 20, projectType.right, parent.height - 30");
  238. addChildAndSetID (&targetsOutline, "targetsOutline");
  239. targetsOutline.setColour (GroupComponent::outlineColourId, Colours::black.withAlpha (0.2f));
  240. targetsOutline.setTextLabelPosition (Justification::centred);
  241. targetsOutline.setBounds ("fileOutline.right + 20, projectType.bottom + 20, parent.width - 30, parent.height - 70");
  242. addChildAndSetID (&platformTargets, "platformTargets");
  243. platformTargets.setBounds ("targetsOutline.left + 15, projectType.bottom + 45, parent.width - 40, parent.height - 90");
  244. addChildAndSetID (&fileBrowser, "fileBrowser");
  245. fileBrowser.setBounds ("fileOutline.left + 10, fileOutline.top + 20, fileOutline.right - 10, fileOutline.bottom - 32");
  246. fileBrowser.setFilenameBoxLabel ("Folder:");
  247. fileBrowser.setFileName (File::createLegalFileName (projectName.getText()));
  248. fileBrowser.addListener (this);
  249. addChildAndSetID (&createButton, "createButton");
  250. createButton.setBounds ("right - 130, bottom - 34, parent.width - 30, parent.height - 30");
  251. createButton.addListener (this);
  252. addChildAndSetID (&cancelButton, "cancelButton");
  253. cancelButton.addShortcut (KeyPress (KeyPress::escapeKey));
  254. cancelButton.setBounds ("right - 130, createButton.top, createButton.left - 10, createButton.bottom");
  255. cancelButton.addListener (this);
  256. addChildAndSetID (&modulesPathBox, "modulesPathBox");
  257. modulesPathBox.setBounds ("targetsOutline.left, targetsOutline.top - 45, targetsOutline.right, targetsOutline.top - 20");
  258. updateCustomItems();
  259. updateCreateButton();
  260. }
  261. void paint (Graphics& g) override
  262. {
  263. Rectangle<int> rect = getLocalBounds().reduced (10, 10);
  264. g.setColour (Colours::white.withAlpha (0.3f));
  265. g.fillRect (rect);
  266. g.fillRect (rect.reduced (10, 10));
  267. }
  268. void buttonClicked (Button* b) override
  269. {
  270. if (b == &createButton)
  271. {
  272. createProject();
  273. }
  274. else if (b == &cancelButton)
  275. {
  276. returnToTemplatesPage();
  277. }
  278. }
  279. void returnToTemplatesPage()
  280. {
  281. if (SlidingPanelComponent* parent = findParentComponentOfClass<SlidingPanelComponent>())
  282. {
  283. if (parent->getNumTabs() > 0)
  284. parent->goToTab (parent->getCurrentTabIndex() - 1);
  285. }
  286. else
  287. {
  288. jassertfalse;
  289. }
  290. }
  291. void createProject()
  292. {
  293. MainWindow* mw = Component::findParentComponentOfClass<MainWindow>();
  294. jassert (mw != nullptr);
  295. ScopedPointer<NewProjectWizardClasses::NewProjectWizard> wizard (createWizard());
  296. if (wizard != nullptr)
  297. {
  298. Result result (wizard->processResultsFromSetupItems (*this));
  299. if (result.failed())
  300. {
  301. AlertWindow::showMessageBox (AlertWindow::WarningIcon,
  302. TRANS("Create Project"),
  303. result.getErrorMessage());
  304. return;
  305. }
  306. wizard->modulesFolder = modulesPathBox.modulesFolder;
  307. if (! isJuceModulesFolder (wizard->modulesFolder))
  308. if (! wizard->selectJuceFolder())
  309. return;
  310. ScopedPointer<Project> project (wizard->runWizard (*this, projectName.getText(),
  311. fileBrowser.getSelectedFile (0)));
  312. if (project != nullptr)
  313. mw->setProject (project.release());
  314. }
  315. }
  316. void updateCustomItems()
  317. {
  318. customItems.clear();
  319. ScopedPointer<NewProjectWizardClasses::NewProjectWizard> wizard (createWizard());
  320. if (wizard != nullptr)
  321. wizard->addSetupItems (*this, customItems);
  322. }
  323. void comboBoxChanged (ComboBox*) override
  324. {
  325. updateCustomItems();
  326. }
  327. void textEditorTextChanged (TextEditor&) override
  328. {
  329. updateCreateButton();
  330. fileBrowser.setFileName (File::createLegalFileName (projectName.getText()));
  331. }
  332. void selectionChanged() override {}
  333. void fileClicked (const File&, const MouseEvent&) override {}
  334. void fileDoubleClicked (const File&) override {}
  335. void browserRootChanged (const File&) override
  336. {
  337. fileBrowser.setFileName (File::createLegalFileName (projectName.getText()));
  338. }
  339. ComboBox projectType;
  340. PlatformTargetsComp platformTargets;
  341. private:
  342. TextEditor projectName;
  343. Label nameLabel, typeLabel;
  344. FileBrowserComponent fileBrowser;
  345. GroupComponent fileOutline;
  346. GroupComponent targetsOutline;
  347. TextButton createButton, cancelButton;
  348. OwnedArray<Component> customItems;
  349. ModulesFolderPathBox modulesPathBox;
  350. NewProjectWizardClasses::NewProjectWizard* createWizard()
  351. {
  352. return createWizardType (projectType.getSelectedItemIndex());
  353. }
  354. void updateCreateButton()
  355. {
  356. createButton.setEnabled (projectName.getText().trim().isNotEmpty());
  357. }
  358. };
  359. #endif // JUCER_NEWPROJECTWIZARDCOMPONENT_H_INCLUDED