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.

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