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.

570 lines
23KB

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