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.

440 lines
14KB

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