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.

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