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.

564 lines
22KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. #include "jucer_NewProjectWizard.h"
  19. #include "jucer_ProjectType.h"
  20. #include "jucer_Module.h"
  21. #include "../Project Saving/jucer_ProjectExporter.h"
  22. #include "../Application/jucer_Application.h"
  23. #include "../Application/jucer_MainWindow.h"
  24. static void createFileCreationOptionComboBox (Component& setupComp,
  25. OwnedArray<Component>& itemsCreated,
  26. const char** fileOptions)
  27. {
  28. ComboBox* c = new ComboBox();
  29. itemsCreated.add (c);
  30. setupComp.addChildAndSetID (c, "filesToCreate");
  31. c->addItemList (StringArray (fileOptions), 1);
  32. c->setSelectedId (1, false);
  33. Label* l = new Label (String::empty, "Files to Auto-Generate:");
  34. l->attachToComponent (c, true);
  35. itemsCreated.add (l);
  36. c->setBounds ("parent.width / 2 + 160, 10, parent.width - 10, top + 22");
  37. }
  38. static void setExecutableNameForAllTargets (Project& project, const String& exeName)
  39. {
  40. for (Project::ExporterIterator exporter (project); exporter.next();)
  41. for (ProjectExporter::ConfigIterator config (*exporter); config.next();)
  42. config->getTargetBinaryName() = exeName;
  43. }
  44. //==============================================================================
  45. class GUIAppWizard : public NewProjectWizard
  46. {
  47. public:
  48. GUIAppWizard() {}
  49. String getName() { return "GUI Application"; }
  50. String getDescription() { return "Creates a standard application"; }
  51. void addSetupItems (Component& setupComp, OwnedArray<Component>& itemsCreated)
  52. {
  53. const char* fileOptions[] = { "Create a Main.cpp file",
  54. "Create a Main.cpp file and a basic window",
  55. "Don't create any files", 0 };
  56. createFileCreationOptionComboBox (setupComp, itemsCreated, fileOptions);
  57. }
  58. Result processResultsFromSetupItems (Component& setupComp)
  59. {
  60. ComboBox* cb = dynamic_cast<ComboBox*> (setupComp.findChildWithID ("filesToCreate"));
  61. jassert (cb != nullptr);
  62. createMainCpp = createWindow = false;
  63. switch (cb->getSelectedItemIndex())
  64. {
  65. case 0: createMainCpp = true; break;
  66. case 1: createMainCpp = createWindow = true; break;
  67. case 2: break;
  68. default: jassertfalse; break;
  69. }
  70. return Result::ok();
  71. }
  72. bool initialiseProject (Project& project)
  73. {
  74. if (! getSourceFilesFolder().createDirectory())
  75. failedFiles.add (getSourceFilesFolder().getFullPathName());
  76. File mainCppFile = getSourceFilesFolder().getChildFile ("Main.cpp");
  77. File contentCompCpp = getSourceFilesFolder().getChildFile ("MainComponent.cpp");
  78. File contentCompH = contentCompCpp.withFileExtension (".h");
  79. String contentCompName = "MainContentComponent";
  80. project.getProjectTypeValue() = ProjectType::getGUIAppTypeName();
  81. Project::Item sourceGroup (project.getMainGroup().addNewSubGroup ("Source", 0));
  82. setExecutableNameForAllTargets (project, File::createLegalFileName (appTitle));
  83. String appHeaders (CodeHelpers::createIncludeStatement (project.getAppIncludeFile(), mainCppFile));
  84. if (createWindow)
  85. {
  86. appHeaders << newLine << CodeHelpers::createIncludeStatement (contentCompH, mainCppFile);
  87. String windowH = project.getFileTemplate ("jucer_ContentCompTemplate_h")
  88. .replace ("INCLUDE_JUCE", CodeHelpers::createIncludeStatement (project.getAppIncludeFile(), contentCompH), false)
  89. .replace ("CONTENTCOMPCLASS", contentCompName, false)
  90. .replace ("HEADERGUARD", CodeHelpers::makeHeaderGuardName (contentCompH), false);
  91. String windowCpp = project.getFileTemplate ("jucer_ContentCompTemplate_cpp")
  92. .replace ("INCLUDE_JUCE", CodeHelpers::createIncludeStatement (project.getAppIncludeFile(), contentCompCpp), false)
  93. .replace ("INCLUDE_CORRESPONDING_HEADER", CodeHelpers::createIncludeStatement (contentCompH, contentCompCpp), false)
  94. .replace ("CONTENTCOMPCLASS", contentCompName, false);
  95. if (! FileHelpers::overwriteFileWithNewDataIfDifferent (contentCompH, windowH))
  96. failedFiles.add (contentCompH.getFullPathName());
  97. if (! FileHelpers::overwriteFileWithNewDataIfDifferent (contentCompCpp, windowCpp))
  98. failedFiles.add (contentCompCpp.getFullPathName());
  99. sourceGroup.addFile (contentCompCpp, -1, true);
  100. sourceGroup.addFile (contentCompH, -1, false);
  101. }
  102. if (createMainCpp)
  103. {
  104. String mainCpp = project.getFileTemplate (createWindow ? "jucer_MainTemplate_Window_cpp"
  105. : "jucer_MainTemplate_NoWindow_cpp")
  106. .replace ("APPHEADERS", appHeaders, false)
  107. .replace ("APPCLASSNAME", CodeHelpers::makeValidIdentifier (appTitle + "Application", false, true, false), false)
  108. .replace ("APPNAME", CodeHelpers::addEscapeChars (appTitle), false)
  109. .replace ("CONTENTCOMPCLASS", contentCompName, false)
  110. .replace ("ALLOWMORETHANONEINSTANCE", "true", false);
  111. if (! FileHelpers::overwriteFileWithNewDataIfDifferent (mainCppFile, mainCpp))
  112. failedFiles.add (mainCppFile.getFullPathName());
  113. sourceGroup.addFile (mainCppFile, -1, true);
  114. }
  115. project.createExporterForCurrentPlatform();
  116. return true;
  117. }
  118. private:
  119. bool createMainCpp, createWindow;
  120. };
  121. //==============================================================================
  122. class ConsoleAppWizard : public NewProjectWizard
  123. {
  124. public:
  125. ConsoleAppWizard() {}
  126. String getName() { return "Console Application"; }
  127. String getDescription() { return "Creates a command-line application with no GUI features"; }
  128. void addSetupItems (Component& setupComp, OwnedArray<Component>& itemsCreated)
  129. {
  130. const char* fileOptions[] = { "Create a Main.cpp file",
  131. "Don't create any files", 0 };
  132. createFileCreationOptionComboBox (setupComp, itemsCreated, fileOptions);
  133. }
  134. Result processResultsFromSetupItems (Component& setupComp)
  135. {
  136. ComboBox* cb = dynamic_cast<ComboBox*> (setupComp.findChildWithID ("filesToCreate"));
  137. jassert (cb != nullptr);
  138. createMainCpp = false;
  139. switch (cb->getSelectedItemIndex())
  140. {
  141. case 0: createMainCpp = true; break;
  142. case 1: break;
  143. default: jassertfalse; break;
  144. }
  145. return Result::ok();
  146. }
  147. bool initialiseProject (Project& project)
  148. {
  149. if (! getSourceFilesFolder().createDirectory())
  150. failedFiles.add (getSourceFilesFolder().getFullPathName());
  151. File mainCppFile = getSourceFilesFolder().getChildFile ("Main.cpp");
  152. project.getProjectTypeValue() = ProjectType::getConsoleAppTypeName();
  153. Project::Item sourceGroup (project.getMainGroup().addNewSubGroup ("Source", 0));
  154. setExecutableNameForAllTargets (project, File::createLegalFileName (appTitle));
  155. if (createMainCpp)
  156. {
  157. String appHeaders (CodeHelpers::createIncludeStatement (project.getAppIncludeFile(), mainCppFile));
  158. String mainCpp = project.getFileTemplate ("jucer_MainConsoleAppTemplate_cpp")
  159. .replace ("APPHEADERS", appHeaders, false);
  160. if (! FileHelpers::overwriteFileWithNewDataIfDifferent (mainCppFile, mainCpp))
  161. failedFiles.add (mainCppFile.getFullPathName());
  162. sourceGroup.addFile (mainCppFile, -1, true);
  163. }
  164. project.createExporterForCurrentPlatform();
  165. return true;
  166. }
  167. private:
  168. bool createMainCpp;
  169. };
  170. //==============================================================================
  171. class AudioPluginAppWizard : public NewProjectWizard
  172. {
  173. public:
  174. AudioPluginAppWizard() {}
  175. String getName() { return "Audio Plug-In"; }
  176. String getDescription() { return "Creates an audio plugin project"; }
  177. void addSetupItems (Component& setupComp, OwnedArray<Component>& itemsCreated)
  178. {
  179. }
  180. Result processResultsFromSetupItems (Component& setupComp)
  181. {
  182. return Result::ok();
  183. }
  184. bool initialiseProject (Project& project)
  185. {
  186. if (! getSourceFilesFolder().createDirectory())
  187. failedFiles.add (getSourceFilesFolder().getFullPathName());
  188. String filterClassName = CodeHelpers::makeValidIdentifier (appTitle, true, true, false) + "AudioProcessor";
  189. filterClassName = filterClassName.substring (0, 1).toUpperCase() + filterClassName.substring (1);
  190. String editorClassName = filterClassName + "Editor";
  191. File filterCppFile = getSourceFilesFolder().getChildFile ("PluginProcessor.cpp");
  192. File filterHFile = filterCppFile.withFileExtension (".h");
  193. File editorCppFile = getSourceFilesFolder().getChildFile ("PluginEditor.cpp");
  194. File editorHFile = editorCppFile.withFileExtension (".h");
  195. project.getProjectTypeValue() = ProjectType::getAudioPluginTypeName();
  196. project.addModule ("juce_audio_plugin_client", true);
  197. Project::Item sourceGroup (project.getMainGroup().addNewSubGroup ("Source", 0));
  198. project.getConfigFlag ("JUCE_QUICKTIME") = Project::configFlagDisabled; // disabled because it interferes with RTAS build on PC
  199. setExecutableNameForAllTargets (project, File::createLegalFileName (appTitle));
  200. String appHeaders (CodeHelpers::createIncludeStatement (project.getAppIncludeFile(), filterCppFile));
  201. String filterCpp = project.getFileTemplate ("jucer_AudioPluginFilterTemplate_cpp")
  202. .replace ("FILTERHEADERS", CodeHelpers::createIncludeStatement (filterHFile, filterCppFile)
  203. + newLine + CodeHelpers::createIncludeStatement (editorHFile, filterCppFile), false)
  204. .replace ("FILTERCLASSNAME", filterClassName, false)
  205. .replace ("EDITORCLASSNAME", editorClassName, false);
  206. String filterH = project.getFileTemplate ("jucer_AudioPluginFilterTemplate_h")
  207. .replace ("APPHEADERS", appHeaders, false)
  208. .replace ("FILTERCLASSNAME", filterClassName, false)
  209. .replace ("HEADERGUARD", CodeHelpers::makeHeaderGuardName (filterHFile), false);
  210. String editorCpp = project.getFileTemplate ("jucer_AudioPluginEditorTemplate_cpp")
  211. .replace ("EDITORCPPHEADERS", CodeHelpers::createIncludeStatement (filterHFile, filterCppFile)
  212. + newLine + CodeHelpers::createIncludeStatement (editorHFile, filterCppFile), false)
  213. .replace ("FILTERCLASSNAME", filterClassName, false)
  214. .replace ("EDITORCLASSNAME", editorClassName, false);
  215. String editorH = project.getFileTemplate ("jucer_AudioPluginEditorTemplate_h")
  216. .replace ("EDITORHEADERS", appHeaders + newLine + CodeHelpers::createIncludeStatement (filterHFile, filterCppFile), false)
  217. .replace ("FILTERCLASSNAME", filterClassName, false)
  218. .replace ("EDITORCLASSNAME", editorClassName, false)
  219. .replace ("HEADERGUARD", CodeHelpers::makeHeaderGuardName (editorHFile), false);
  220. if (! FileHelpers::overwriteFileWithNewDataIfDifferent (filterCppFile, filterCpp))
  221. failedFiles.add (filterCppFile.getFullPathName());
  222. if (! FileHelpers::overwriteFileWithNewDataIfDifferent (filterHFile, filterH))
  223. failedFiles.add (filterHFile.getFullPathName());
  224. if (! FileHelpers::overwriteFileWithNewDataIfDifferent (editorCppFile, editorCpp))
  225. failedFiles.add (editorCppFile.getFullPathName());
  226. if (! FileHelpers::overwriteFileWithNewDataIfDifferent (editorHFile, editorH))
  227. failedFiles.add (editorHFile.getFullPathName());
  228. sourceGroup.addFile (filterCppFile, -1, true);
  229. sourceGroup.addFile (filterHFile, -1, false);
  230. sourceGroup.addFile (editorCppFile, -1, true);
  231. sourceGroup.addFile (editorHFile, -1, false);
  232. project.createExporterForCurrentPlatform();
  233. return true;
  234. }
  235. };
  236. //==============================================================================
  237. //==============================================================================
  238. NewProjectWizard::NewProjectWizard() {}
  239. NewProjectWizard::~NewProjectWizard() {}
  240. StringArray NewProjectWizard::getWizards()
  241. {
  242. StringArray s;
  243. for (int i = 0; i < getNumWizards(); ++i)
  244. {
  245. ScopedPointer <NewProjectWizard> wiz (createWizard (i));
  246. s.add (wiz->getName());
  247. }
  248. return s;
  249. }
  250. int NewProjectWizard::getNumWizards()
  251. {
  252. return 3;
  253. }
  254. NewProjectWizard* NewProjectWizard::createWizard (int index)
  255. {
  256. switch (index)
  257. {
  258. case 0: return new GUIAppWizard();
  259. case 1: return new ConsoleAppWizard();
  260. case 2: return new AudioPluginAppWizard();
  261. //case 3: return new BrowserPluginAppWizard();
  262. default: jassertfalse; break;
  263. }
  264. return 0;
  265. }
  266. File& NewProjectWizard::getLastWizardFolder()
  267. {
  268. #if JUCE_WINDOWS
  269. static File lastFolder (File::getSpecialLocation (File::userDocumentsDirectory));
  270. #else
  271. static File lastFolder (File::getSpecialLocation (File::userHomeDirectory));
  272. #endif
  273. return lastFolder;
  274. }
  275. //==============================================================================
  276. Project* NewProjectWizard::runWizard (Component* ownerWindow_,
  277. const String& projectName,
  278. const File& targetFolder_)
  279. {
  280. ownerWindow = ownerWindow_;
  281. appTitle = projectName;
  282. targetFolder = targetFolder_;
  283. if (! targetFolder.exists())
  284. {
  285. if (! targetFolder.createDirectory())
  286. failedFiles.add (targetFolder.getFullPathName());
  287. }
  288. else if (FileHelpers::containsAnyNonHiddenFiles (targetFolder))
  289. {
  290. if (! AlertWindow::showOkCancelBox (AlertWindow::InfoIcon, "New Juce Project",
  291. "The folder you chose isn't empty - are you sure you want to create the project there?\n\nAny existing files with the same names may be overwritten by the new files."))
  292. return nullptr;
  293. }
  294. projectFile = targetFolder.getChildFile (File::createLegalFileName (appTitle))
  295. .withFileExtension (Project::projectFileExtension);
  296. ScopedPointer<Project> project (new Project (projectFile));
  297. project->addDefaultModules (true);
  298. if (failedFiles.size() == 0)
  299. {
  300. project->setFile (projectFile);
  301. project->setTitle (appTitle);
  302. project->getBundleIdentifier() = project->getDefaultBundleIdentifier();
  303. if (! initialiseProject (*project))
  304. return nullptr;
  305. if (project->save (false, true) != FileBasedDocument::savedOk)
  306. return nullptr;
  307. project->setChangedFlag (false);
  308. }
  309. if (failedFiles.size() > 0)
  310. {
  311. AlertWindow::showMessageBox (AlertWindow::WarningIcon,
  312. "Errors in Creating Project!",
  313. "The following files couldn't be written:\n\n"
  314. + failedFiles.joinIntoString ("\n", 0, 10));
  315. return nullptr;
  316. }
  317. return project.release();
  318. }
  319. //==============================================================================
  320. class NewProjectWizard::WizardComp : public Component,
  321. private ButtonListener,
  322. private ComboBoxListener,
  323. private TextEditorListener
  324. {
  325. public:
  326. WizardComp()
  327. : projectName ("Project name"),
  328. nameLabel (String::empty, "Project Name:"),
  329. typeLabel (String::empty, "Project Type:"),
  330. fileBrowser (FileBrowserComponent::saveMode | FileBrowserComponent::canSelectDirectories,
  331. getLastWizardFolder(), nullptr, nullptr),
  332. fileOutline (String::empty, "Project Folder:"),
  333. createButton ("Create..."),
  334. cancelButton ("Cancel")
  335. {
  336. setOpaque (true);
  337. setSize (600, 500);
  338. addChildAndSetID (&projectName, "projectName");
  339. projectName.setText ("NewProject");
  340. projectName.setBounds ("100, 14, parent.width / 2 - 10, top + 22");
  341. nameLabel.attachToComponent (&projectName, true);
  342. projectName.addListener (this);
  343. addChildAndSetID (&projectType, "projectType");
  344. projectType.addItemList (getWizards(), 1);
  345. projectType.setSelectedId (1, true);
  346. projectType.setBounds ("100, projectName.bottom + 4, projectName.right, top + 22");
  347. typeLabel.attachToComponent (&projectType, true);
  348. projectType.addListener (this);
  349. addChildAndSetID (&fileOutline, "fileOutline");
  350. fileOutline.setColour (GroupComponent::outlineColourId, Colours::black.withAlpha (0.2f));
  351. fileOutline.setTextLabelPosition (Justification::centred);
  352. fileOutline.setBounds ("10, projectType.bottom + 20, projectType.right, parent.height - 10");
  353. addChildAndSetID (&fileBrowser, "fileBrowser");
  354. fileBrowser.setBounds ("fileOutline.left + 10, fileOutline.top + 20, fileOutline.right - 10, fileOutline.bottom - 12");
  355. fileBrowser.setFilenameBoxLabel ("Folder:");
  356. addChildAndSetID (&createButton, "createButton");
  357. createButton.setBounds ("right - 140, bottom - 24, parent.width - 10, parent.height - 10");
  358. createButton.addListener (this);
  359. addChildAndSetID (&cancelButton, "cancelButton");
  360. cancelButton.addShortcut (KeyPress (KeyPress::escapeKey));
  361. cancelButton.setBounds ("right - 140, createButton.top, createButton.left - 10, createButton.bottom");
  362. cancelButton.addListener (this);
  363. updateCustomItems();
  364. updateCreateButton();
  365. }
  366. void paint (Graphics& g)
  367. {
  368. g.fillAll (Colour::greyLevel (0.93f));
  369. }
  370. void buttonClicked (Button* b)
  371. {
  372. if (b == &createButton)
  373. {
  374. createProject();
  375. }
  376. else
  377. {
  378. MainWindow* mw = dynamic_cast<MainWindow*> (getTopLevelComponent());
  379. jassert (mw != nullptr);
  380. IntrojucerApp::getApp().mainWindowList.closeWindow (mw);
  381. }
  382. }
  383. void createProject()
  384. {
  385. MainWindow* mw = Component::findParentComponentOfClass<MainWindow>();
  386. jassert (mw != nullptr);
  387. ScopedPointer <NewProjectWizard> wizard (createWizard());
  388. if (wizard != nullptr)
  389. {
  390. Result result (wizard->processResultsFromSetupItems (*this));
  391. if (result.failed())
  392. {
  393. AlertWindow::showMessageBox (AlertWindow::WarningIcon, "Create Project", result.getErrorMessage());
  394. return;
  395. }
  396. ScopedPointer<Project> project (wizard->runWizard (mw, projectName.getText(),
  397. fileBrowser.getSelectedFile (0)));
  398. if (project != nullptr)
  399. mw->setProject (project.release());
  400. }
  401. }
  402. void updateCustomItems()
  403. {
  404. customItems.clear();
  405. ScopedPointer <NewProjectWizard> wizard (createWizard());
  406. if (wizard != nullptr)
  407. wizard->addSetupItems (*this, customItems);
  408. }
  409. void comboBoxChanged (ComboBox*)
  410. {
  411. updateCustomItems();
  412. }
  413. void textEditorTextChanged (TextEditor&)
  414. {
  415. updateCreateButton();
  416. fileBrowser.setFileName (File::createLegalFileName (projectName.getText()));
  417. }
  418. private:
  419. ComboBox projectType;
  420. TextEditor projectName;
  421. Label nameLabel, typeLabel;
  422. FileBrowserComponent fileBrowser;
  423. GroupComponent fileOutline;
  424. TextButton createButton, cancelButton;
  425. OwnedArray<Component> customItems;
  426. NewProjectWizard* createWizard()
  427. {
  428. return NewProjectWizard::createWizard (projectType.getSelectedItemIndex());
  429. }
  430. void updateCreateButton()
  431. {
  432. createButton.setEnabled (projectName.getText().trim().isNotEmpty());
  433. }
  434. };
  435. Component* NewProjectWizard::createComponent()
  436. {
  437. return new WizardComp();
  438. }