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.

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