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.

486 lines
19KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-10 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. //==============================================================================
  21. class GUIAppWizard : public NewProjectWizard
  22. {
  23. public:
  24. GUIAppWizard() {}
  25. String getName() { return "GUI Application"; }
  26. String getDescription() { return "Creates a standard application"; }
  27. void addItemsToAlertWindow (AlertWindow& aw)
  28. {
  29. const char* fileOptions[] = { "Create a Main.cpp file",
  30. "Create a Main.cpp file and a basic window",
  31. "Don't create any files", 0 };
  32. aw.addComboBox ("files", StringArray (fileOptions), "Files to Auto-Generate");
  33. }
  34. String processResultsFromAlertWindow (AlertWindow& aw)
  35. {
  36. createMainCpp = createWindow = false;
  37. switch (aw.getComboBoxComponent ("files")->getSelectedItemIndex())
  38. {
  39. case 0: createMainCpp = true; break;
  40. case 1: createMainCpp = createWindow = true; break;
  41. case 2: break;
  42. default: jassertfalse; break;
  43. }
  44. return String::empty;
  45. }
  46. bool initialiseProject (Project& project)
  47. {
  48. if (! getSourceFilesFolder().createDirectory())
  49. failedFiles.add (getSourceFilesFolder().getFullPathName());
  50. File mainCppFile = getSourceFilesFolder().getChildFile ("Main.cpp");
  51. File mainWindowCpp = getSourceFilesFolder().getChildFile ("MainWindow.cpp");
  52. File mainWindowH = mainWindowCpp.withFileExtension (".h");
  53. String windowClassName = "MainAppWindow";
  54. project.getProjectTypeValue() = ProjectType::getGUIAppTypeName();
  55. Project::Item sourceGroup (project.getMainGroup().addNewSubGroup ("Source", 0));
  56. for (int i = project.getNumConfigurations(); --i >= 0;)
  57. project.getConfiguration(i).getTargetBinaryName() = File::createLegalFileName (appTitle);
  58. String appHeaders (CodeHelpers::createIncludeStatement (project.getAppIncludeFile(), mainCppFile));
  59. String initCode, shutdownCode, anotherInstanceStartedCode, privateMembers, memberInitialisers;
  60. if (createWindow)
  61. {
  62. appHeaders << newLine << CodeHelpers::createIncludeStatement (mainWindowH, mainCppFile);
  63. initCode = "mainWindow = new " + windowClassName + "();";
  64. shutdownCode = "mainWindow = 0;";
  65. privateMembers = "ScopedPointer <" + windowClassName + "> mainWindow;";
  66. String windowH = project.getFileTemplate ("jucer_WindowTemplate_h")
  67. .replace ("INCLUDES", CodeHelpers::createIncludeStatement (project.getAppIncludeFile(), mainWindowH), false)
  68. .replace ("WINDOWCLASS", windowClassName, false)
  69. .replace ("HEADERGUARD", CodeHelpers::makeHeaderGuardName (mainWindowH), false);
  70. String windowCpp = project.getFileTemplate ("jucer_WindowTemplate_cpp")
  71. .replace ("INCLUDES", CodeHelpers::createIncludeStatement (mainWindowH, mainWindowCpp), false)
  72. .replace ("WINDOWCLASS", windowClassName, false);
  73. if (! FileHelpers::overwriteFileWithNewDataIfDifferent (mainWindowH, windowH))
  74. failedFiles.add (mainWindowH.getFullPathName());
  75. if (! FileHelpers::overwriteFileWithNewDataIfDifferent (mainWindowCpp, windowCpp))
  76. failedFiles.add (mainWindowCpp.getFullPathName());
  77. sourceGroup.addFile (mainWindowCpp, -1);
  78. sourceGroup.addFile (mainWindowH, -1);
  79. }
  80. if (createMainCpp)
  81. {
  82. String mainCpp = project.getFileTemplate ("jucer_MainTemplate_cpp")
  83. .replace ("APPHEADERS", appHeaders, false)
  84. .replace ("APPCLASSNAME", CodeHelpers::makeValidIdentifier (appTitle + "Application", false, true, false), false)
  85. .replace ("MEMBERINITIALISERS", memberInitialisers, false)
  86. .replace ("APPINITCODE", initCode, false)
  87. .replace ("APPSHUTDOWNCODE", shutdownCode, false)
  88. .replace ("APPNAME", CodeHelpers::addEscapeChars (appTitle), false)
  89. .replace ("APPVERSION", "1.0", false)
  90. .replace ("ALLOWMORETHANONEINSTANCE", "true", false)
  91. .replace ("ANOTHERINSTANCECODE", anotherInstanceStartedCode, false)
  92. .replace ("PRIVATEMEMBERS", privateMembers, false);
  93. if (! FileHelpers::overwriteFileWithNewDataIfDifferent (mainCppFile, mainCpp))
  94. failedFiles.add (mainCppFile.getFullPathName());
  95. sourceGroup.addFile (mainCppFile, -1);
  96. }
  97. return true;
  98. }
  99. private:
  100. bool createMainCpp, createWindow;
  101. };
  102. //==============================================================================
  103. class ConsoleAppWizard : public NewProjectWizard
  104. {
  105. public:
  106. ConsoleAppWizard() {}
  107. String getName() { return "Console Application"; }
  108. String getDescription() { return "Creates a command-line application with no GUI features"; }
  109. void addItemsToAlertWindow (AlertWindow& aw)
  110. {
  111. const char* fileOptions[] = { "Create a Main.cpp file",
  112. "Don't create any files", 0 };
  113. aw.addComboBox ("files", StringArray (fileOptions), "Files to Auto-Generate");
  114. }
  115. String processResultsFromAlertWindow (AlertWindow& aw)
  116. {
  117. createMainCpp = false;
  118. switch (aw.getComboBoxComponent ("files")->getSelectedItemIndex())
  119. {
  120. case 0: createMainCpp = true; break;
  121. case 1: break;
  122. default: jassertfalse; break;
  123. }
  124. return String::empty;
  125. }
  126. bool initialiseProject (Project& project)
  127. {
  128. if (! getSourceFilesFolder().createDirectory())
  129. failedFiles.add (getSourceFilesFolder().getFullPathName());
  130. File mainCppFile = getSourceFilesFolder().getChildFile ("Main.cpp");
  131. project.getProjectTypeValue() = ProjectType::getConsoleAppTypeName();
  132. Project::Item sourceGroup (project.getMainGroup().addNewSubGroup ("Source", 0));
  133. for (int i = project.getNumConfigurations(); --i >= 0;)
  134. project.getConfiguration(i).getTargetBinaryName() = File::createLegalFileName (appTitle);
  135. if (createMainCpp)
  136. {
  137. String appHeaders (CodeHelpers::createIncludeStatement (project.getAppIncludeFile(), mainCppFile));
  138. String mainCpp = project.getFileTemplate ("jucer_MainConsoleAppTemplate_cpp")
  139. .replace ("APPHEADERS", appHeaders, false);
  140. if (! FileHelpers::overwriteFileWithNewDataIfDifferent (mainCppFile, mainCpp))
  141. failedFiles.add (mainCppFile.getFullPathName());
  142. sourceGroup.addFile (mainCppFile, -1);
  143. }
  144. return true;
  145. }
  146. private:
  147. bool createMainCpp;
  148. };
  149. //==============================================================================
  150. class AudioPluginAppWizard : public NewProjectWizard
  151. {
  152. public:
  153. AudioPluginAppWizard() {}
  154. String getName() { return "Audio Plug-In"; }
  155. String getDescription() { return "Creates an audio plugin project"; }
  156. void addItemsToAlertWindow (AlertWindow& aw)
  157. {
  158. }
  159. String processResultsFromAlertWindow (AlertWindow& aw)
  160. {
  161. return String::empty;
  162. }
  163. bool initialiseProject (Project& project)
  164. {
  165. if (! getSourceFilesFolder().createDirectory())
  166. failedFiles.add (getSourceFilesFolder().getFullPathName());
  167. String filterClassName = CodeHelpers::makeValidIdentifier (appTitle, true, true, false) + "AudioProcessor";
  168. filterClassName = filterClassName.substring (0, 1).toUpperCase() + filterClassName.substring (1);
  169. String editorClassName = filterClassName + "Editor";
  170. File filterCppFile = getSourceFilesFolder().getChildFile ("PluginProcessor.cpp");
  171. File filterHFile = filterCppFile.withFileExtension (".h");
  172. File editorCppFile = getSourceFilesFolder().getChildFile ("PluginEditor.cpp");
  173. File editorHFile = editorCppFile.withFileExtension (".h");
  174. project.getProjectTypeValue() = ProjectType::getAudioPluginTypeName();
  175. Project::Item sourceGroup (project.getMainGroup().addNewSubGroup ("Source", 0));
  176. project.getConfigFlag ("JUCE_QUICKTIME") = Project::configFlagDisabled; // disabled because it interferes with RTAS build on PC
  177. for (int i = project.getNumConfigurations(); --i >= 0;)
  178. project.getConfiguration(i).getTargetBinaryName() = File::createLegalFileName (appTitle);
  179. String appHeaders (CodeHelpers::createIncludeStatement (project.getAppIncludeFile(), filterCppFile));
  180. appHeaders << newLine << CodeHelpers::createIncludeStatement (project.getPluginCharacteristicsFile(), filterCppFile);
  181. String filterCpp = project.getFileTemplate ("jucer_AudioPluginFilterTemplate_cpp")
  182. .replace ("FILTERHEADERS", CodeHelpers::createIncludeStatement (filterHFile, filterCppFile)
  183. + newLine + CodeHelpers::createIncludeStatement (editorHFile, filterCppFile), false)
  184. .replace ("FILTERCLASSNAME", filterClassName, false)
  185. .replace ("EDITORCLASSNAME", editorClassName, false);
  186. String filterH = project.getFileTemplate ("jucer_AudioPluginFilterTemplate_h")
  187. .replace ("APPHEADERS", appHeaders, false)
  188. .replace ("FILTERCLASSNAME", filterClassName, false)
  189. .replace ("HEADERGUARD", CodeHelpers::makeHeaderGuardName (filterHFile), false);
  190. String editorCpp = project.getFileTemplate ("jucer_AudioPluginEditorTemplate_cpp")
  191. .replace ("EDITORCPPHEADERS", CodeHelpers::createIncludeStatement (filterHFile, filterCppFile)
  192. + newLine + CodeHelpers::createIncludeStatement (editorHFile, filterCppFile), false)
  193. .replace ("FILTERCLASSNAME", filterClassName, false)
  194. .replace ("EDITORCLASSNAME", editorClassName, false);
  195. String editorH = project.getFileTemplate ("jucer_AudioPluginEditorTemplate_h")
  196. .replace ("EDITORHEADERS", appHeaders + newLine + CodeHelpers::createIncludeStatement (filterHFile, filterCppFile), false)
  197. .replace ("FILTERCLASSNAME", filterClassName, false)
  198. .replace ("EDITORCLASSNAME", editorClassName, false)
  199. .replace ("HEADERGUARD", CodeHelpers::makeHeaderGuardName (editorHFile), false);
  200. if (! FileHelpers::overwriteFileWithNewDataIfDifferent (filterCppFile, filterCpp))
  201. failedFiles.add (filterCppFile.getFullPathName());
  202. if (! FileHelpers::overwriteFileWithNewDataIfDifferent (filterHFile, filterH))
  203. failedFiles.add (filterHFile.getFullPathName());
  204. if (! FileHelpers::overwriteFileWithNewDataIfDifferent (editorCppFile, editorCpp))
  205. failedFiles.add (editorCppFile.getFullPathName());
  206. if (! FileHelpers::overwriteFileWithNewDataIfDifferent (editorHFile, editorH))
  207. failedFiles.add (editorHFile.getFullPathName());
  208. sourceGroup.addFile (filterCppFile, -1);
  209. sourceGroup.addFile (filterHFile, -1);
  210. sourceGroup.addFile (editorCppFile, -1);
  211. sourceGroup.addFile (editorHFile, -1);
  212. return true;
  213. }
  214. };
  215. //==============================================================================
  216. /*class BrowserPluginAppWizard : public NewProjectWizard
  217. {
  218. public:
  219. BrowserPluginAppWizard() {}
  220. ~BrowserPluginAppWizard() {}
  221. String getName() { return "Browser Plug-In"; }
  222. String getDescription() { return "Creates an audio plugin project"; }
  223. void addItemsToAlertWindow (AlertWindow& aw)
  224. {
  225. }
  226. String processResultsFromAlertWindow (AlertWindow& aw)
  227. {
  228. return String::empty;
  229. }
  230. bool initialiseProject (Project& project)
  231. {
  232. return true;
  233. }
  234. };*/
  235. //==============================================================================
  236. //==============================================================================
  237. NewProjectWizard::NewProjectWizard()
  238. {
  239. }
  240. NewProjectWizard::~NewProjectWizard()
  241. {
  242. }
  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. //==============================================================================
  270. Project* NewProjectWizard::runWizard (Component* ownerWindow_)
  271. {
  272. ownerWindow = ownerWindow_;
  273. {
  274. static File newProjectFolder;
  275. FileChooser fc ("New Juce Project", newProjectFolder, "*");
  276. if (! fc.browseForDirectory())
  277. return 0;
  278. targetFolder = newProjectFolder = fc.getResult();
  279. if (! newProjectFolder.exists())
  280. {
  281. if (! newProjectFolder.createDirectory())
  282. failedFiles.add (newProjectFolder.getFullPathName());
  283. }
  284. if (FileHelpers::containsAnyNonHiddenFiles (newProjectFolder))
  285. {
  286. if (! AlertWindow::showOkCancelBox (AlertWindow::InfoIcon, "New Juce Project",
  287. "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."))
  288. return 0;
  289. }
  290. }
  291. if (failedFiles.size() == 0)
  292. {
  293. AlertWindow aw ("New " + getName(),
  294. "Please choose some basic project options...",
  295. AlertWindow::NoIcon, ownerWindow);
  296. aw.addTextEditor ("name", "", "Project Name", false);
  297. addItemsToAlertWindow (aw);
  298. aw.addButton ("Create Project", 1, KeyPress (KeyPress::returnKey));
  299. aw.addButton ("Cancel", 0, KeyPress (KeyPress::escapeKey));
  300. for (;;)
  301. {
  302. if (aw.runModalLoop() == 0)
  303. return 0;
  304. appTitle = aw.getTextEditorContents ("name").trim();
  305. String error (processResultsFromAlertWindow (aw));
  306. if (error.isEmpty() && appTitle.isEmpty())
  307. error = "Please enter a sensible project title!";
  308. if (error.isEmpty())
  309. break;
  310. aw.setColour (AlertWindow::textColourId, Colours::red);
  311. aw.setMessage (error);
  312. }
  313. }
  314. projectFile = targetFolder.getChildFile (File::createLegalFileName (appTitle))
  315. .withFileExtension (Project::projectFileExtension);
  316. ScopedPointer <Project> project (new Project (projectFile));
  317. if (failedFiles.size() == 0)
  318. {
  319. project->setFile (projectFile);
  320. project->setTitle (appTitle);
  321. project->setBundleIdentifierToDefault();
  322. if (! initialiseProject (*project))
  323. return 0;
  324. if (project->save (false, true) != FileBasedDocument::savedOk)
  325. return 0;
  326. project->setChangedFlag (false);
  327. }
  328. if (failedFiles.size() > 0)
  329. {
  330. AlertWindow::showMessageBox (AlertWindow::WarningIcon,
  331. "Errors in Creating Project!",
  332. "The following files couldn't be written:\n\n"
  333. + failedFiles.joinIntoString ("\n", 0, 10));
  334. return 0;
  335. }
  336. return project.release();
  337. }
  338. Project* NewProjectWizard::runNewProjectWizard (Component* ownerWindow)
  339. {
  340. ScopedPointer <NewProjectWizard> wizard;
  341. {
  342. AlertWindow aw ("New Juce Project",
  343. "Select the type of project to create, and the location of your Juce folder",
  344. AlertWindow::NoIcon,
  345. ownerWindow);
  346. aw.addComboBox ("type", getWizards(), "Project Type");
  347. FilenameComponent juceFolderSelector ("Juce Library Location", StoredSettings::getInstance()->getLastKnownJuceFolder(),
  348. true, true, false, "*", String::empty, "(Please select the folder containing Juce!)");
  349. juceFolderSelector.setSize (350, 22);
  350. aw.addCustomComponent (&juceFolderSelector);
  351. aw.addButton ("Next", 1, KeyPress (KeyPress::returnKey));
  352. aw.addButton ("Cancel", 0, KeyPress (KeyPress::escapeKey));
  353. for (;;)
  354. {
  355. if (aw.runModalLoop() == 0)
  356. return 0;
  357. if (FileHelpers::isJuceFolder (juceFolderSelector.getCurrentFile()))
  358. {
  359. wizard = createWizard (aw.getComboBoxComponent ("type")->getSelectedItemIndex());
  360. break;
  361. }
  362. aw.setColour (AlertWindow::textColourId, Colours::red);
  363. aw.setMessage ("Please select a valid Juce folder for the project to use!");
  364. }
  365. }
  366. return wizard != nullptr ? wizard->runWizard (ownerWindow) : 0;
  367. }