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.

496 lines
19KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-9 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_ProjectWizard.h"
  19. //==============================================================================
  20. class GUIAppWizard : public ProjectWizard
  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() = (int) 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 (createIncludeStatement (project.getAppIncludeFile(), mainCppFile));
  61. String initCode, shutdownCode, anotherInstanceStartedCode, privateMembers, memberInitialisers;
  62. if (createWindow)
  63. {
  64. appHeaders << newLine << 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", createIncludeStatement (project.getAppIncludeFile(), mainWindowH), false)
  71. .replace ("WINDOWCLASS", windowClassName, false)
  72. .replace ("HEADERGUARD", makeHeaderGuardName (mainWindowH), false);
  73. String windowCpp = project.getFileTemplate ("jucer_WindowTemplate_cpp")
  74. .replace ("INCLUDES", createIncludeStatement (mainWindowH, mainWindowCpp), false)
  75. .replace ("WINDOWCLASS", windowClassName, false);
  76. if (! overwriteFileWithNewDataIfDifferent (mainWindowH, windowH))
  77. failedFiles.add (mainWindowH.getFullPathName());
  78. if (! 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", makeValidCppIdentifier (appTitle + "Application", false, true, false), false)
  88. .replace ("MEMBERINITIALISERS", memberInitialisers, false)
  89. .replace ("APPINITCODE", initCode, false)
  90. .replace ("APPSHUTDOWNCODE", shutdownCode, false)
  91. .replace ("APPNAME", replaceCEscapeChars (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 (! 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 ProjectWizard
  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() = (int) 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 (createIncludeStatement (project.getAppIncludeFile(), mainCppFile));
  144. String mainCpp = project.getFileTemplate ("jucer_MainConsoleAppTemplate_cpp")
  145. .replace ("APPHEADERS", appHeaders, false);
  146. if (! 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 ProjectWizard
  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 = makeValidCppIdentifier (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() = (int) Project::audioPlugin;
  182. Project::Item group (project.createNewGroup());
  183. project.getMainGroup().addChild (group, 0);
  184. group.getName() = "Source";
  185. project.getJuceConfigFlag ("JUCE_QUICKTIME") = 2; // disabled because it interferes with RTAS build on PC
  186. for (int i = project.getNumConfigurations(); --i >= 0;)
  187. project.getConfiguration(i).getTargetBinaryName() = File::createLegalFileName (appTitle);
  188. String appHeaders (createIncludeStatement (project.getAppIncludeFile(), filterCppFile));
  189. appHeaders << newLine << createIncludeStatement (project.getPluginCharacteristicsFile(), filterCppFile);
  190. String filterCpp = project.getFileTemplate ("jucer_AudioPluginFilterTemplate_cpp")
  191. .replace ("FILTERHEADERS", createIncludeStatement (filterHFile, filterCppFile)
  192. + newLine + createIncludeStatement (editorHFile, filterCppFile), false)
  193. .replace ("FILTERCLASSNAME", filterClassName, false)
  194. .replace ("EDITORCLASSNAME", editorClassName, false);
  195. String filterH = project.getFileTemplate ("jucer_AudioPluginFilterTemplate_h")
  196. .replace ("APPHEADERS", appHeaders, false)
  197. .replace ("FILTERCLASSNAME", filterClassName, false)
  198. .replace ("HEADERGUARD", makeHeaderGuardName (filterHFile), false);
  199. String editorCpp = project.getFileTemplate ("jucer_AudioPluginEditorTemplate_cpp")
  200. .replace ("EDITORCPPHEADERS", createIncludeStatement (filterHFile, filterCppFile)
  201. + newLine + createIncludeStatement (editorHFile, filterCppFile), false)
  202. .replace ("FILTERCLASSNAME", filterClassName, false)
  203. .replace ("EDITORCLASSNAME", editorClassName, false);
  204. String editorH = project.getFileTemplate ("jucer_AudioPluginEditorTemplate_h")
  205. .replace ("EDITORHEADERS", appHeaders + newLine + createIncludeStatement (filterHFile, filterCppFile), false)
  206. .replace ("FILTERCLASSNAME", filterClassName, false)
  207. .replace ("EDITORCLASSNAME", editorClassName, false)
  208. .replace ("HEADERGUARD", makeHeaderGuardName (editorHFile), false);
  209. if (! overwriteFileWithNewDataIfDifferent (filterCppFile, filterCpp))
  210. failedFiles.add (filterCppFile.getFullPathName());
  211. if (! overwriteFileWithNewDataIfDifferent (filterHFile, filterH))
  212. failedFiles.add (filterHFile.getFullPathName());
  213. if (! overwriteFileWithNewDataIfDifferent (editorCppFile, editorCpp))
  214. failedFiles.add (editorCppFile.getFullPathName());
  215. if (! overwriteFileWithNewDataIfDifferent (editorHFile, editorH))
  216. failedFiles.add (editorHFile.getFullPathName());
  217. group.addFile (filterCppFile, -1);
  218. group.addFile (filterHFile, -1);
  219. group.addFile (editorCppFile, -1);
  220. group.addFile (editorHFile, -1);
  221. return true;
  222. }
  223. };
  224. //==============================================================================
  225. /*class BrowserPluginAppWizard : public ProjectWizard
  226. {
  227. public:
  228. BrowserPluginAppWizard() {}
  229. ~BrowserPluginAppWizard() {}
  230. const String getName() { return "Browser Plug-In"; }
  231. const String getDescription() { return "Creates an audio plugin project"; }
  232. void addItemsToAlertWindow (AlertWindow& aw)
  233. {
  234. }
  235. const String processResultsFromAlertWindow (AlertWindow& aw)
  236. {
  237. return String::empty;
  238. }
  239. bool initialiseProject (Project& project)
  240. {
  241. return true;
  242. }
  243. };*/
  244. //==============================================================================
  245. //==============================================================================
  246. ProjectWizard::ProjectWizard()
  247. {
  248. }
  249. ProjectWizard::~ProjectWizard()
  250. {
  251. }
  252. const StringArray ProjectWizard::getWizards()
  253. {
  254. StringArray s;
  255. for (int i = 0; i < getNumWizards(); ++i)
  256. {
  257. ScopedPointer <ProjectWizard> wiz (createWizard (i));
  258. s.add (wiz->getName());
  259. }
  260. return s;
  261. }
  262. int ProjectWizard::getNumWizards()
  263. {
  264. return 3;
  265. }
  266. ProjectWizard* ProjectWizard::createWizard (int index)
  267. {
  268. switch (index)
  269. {
  270. case 0: return new GUIAppWizard();
  271. case 1: return new ConsoleAppWizard();
  272. case 2: return new AudioPluginAppWizard();
  273. //case 3: return new BrowserPluginAppWizard();
  274. default: jassertfalse; break;
  275. }
  276. return 0;
  277. }
  278. //==============================================================================
  279. Project* ProjectWizard::runWizard (Component* ownerWindow_)
  280. {
  281. ownerWindow = ownerWindow_;
  282. {
  283. static File newProjectFolder;
  284. FileChooser fc ("New Juce Project", newProjectFolder, "*");
  285. if (! fc.browseForDirectory())
  286. return 0;
  287. targetFolder = newProjectFolder = fc.getResult();
  288. if (! newProjectFolder.exists())
  289. {
  290. if (! newProjectFolder.createDirectory())
  291. failedFiles.add (newProjectFolder.getFullPathName());
  292. }
  293. if (containsAnyNonHiddenFiles (newProjectFolder))
  294. {
  295. if (! AlertWindow::showOkCancelBox (AlertWindow::InfoIcon, "New Juce Project",
  296. "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."))
  297. return 0;
  298. }
  299. }
  300. if (failedFiles.size() == 0)
  301. {
  302. AlertWindow aw ("New " + getName(),
  303. "Please choose some basic project options...",
  304. AlertWindow::NoIcon, ownerWindow);
  305. aw.addTextEditor ("name", "", "Project Name", false);
  306. addItemsToAlertWindow (aw);
  307. aw.addButton ("Create Project", 1, KeyPress (KeyPress::returnKey));
  308. aw.addButton ("Cancel", 0, KeyPress (KeyPress::escapeKey));
  309. for (;;)
  310. {
  311. if (aw.runModalLoop() == 0)
  312. return 0;
  313. appTitle = aw.getTextEditorContents ("name").trim();
  314. String error (processResultsFromAlertWindow (aw));
  315. if (error.isEmpty() && appTitle.isEmpty())
  316. error = "Please enter a sensible project title!";
  317. if (error.isEmpty())
  318. break;
  319. aw.setColour (AlertWindow::textColourId, Colours::red);
  320. aw.setMessage (error);
  321. }
  322. }
  323. projectFile = targetFolder.getChildFile (File::createLegalFileName (appTitle))
  324. .withFileExtension (Project::projectFileExtension);
  325. ScopedPointer <Project> project (new Project (projectFile));
  326. if (failedFiles.size() == 0)
  327. {
  328. project->setFile (projectFile);
  329. project->setTitle (appTitle);
  330. project->setBundleIdentifierToDefault();
  331. if (! initialiseProject (*project))
  332. return 0;
  333. if (project->save (false, true) != FileBasedDocument::savedOk)
  334. return 0;
  335. project->setChangedFlag (false);
  336. }
  337. if (failedFiles.size() > 0)
  338. {
  339. AlertWindow::showMessageBox (AlertWindow::WarningIcon,
  340. "Errors in Creating Project!",
  341. "The following files couldn't be written:\n\n"
  342. + failedFiles.joinIntoString ("\n", 0, 10));
  343. return 0;
  344. }
  345. return project.release();
  346. }
  347. Project* ProjectWizard::runNewProjectWizard (Component* ownerWindow)
  348. {
  349. ScopedPointer <ProjectWizard> wizard;
  350. {
  351. AlertWindow aw ("New Juce Project",
  352. "Select the type of project to create, and the location of your Juce folder",
  353. AlertWindow::NoIcon,
  354. ownerWindow);
  355. aw.addComboBox ("type", getWizards(), "Project Type");
  356. FilenameComponent juceFolderSelector ("Juce Library Location", StoredSettings::getInstance()->getLastKnownJuceFolder(),
  357. true, true, false, "*", String::empty, "(Please select the folder containing Juce!)");
  358. juceFolderSelector.setSize (350, 22);
  359. aw.addCustomComponent (&juceFolderSelector);
  360. aw.addButton ("Next", 1, KeyPress (KeyPress::returnKey));
  361. aw.addButton ("Cancel", 0, KeyPress (KeyPress::escapeKey));
  362. for (;;)
  363. {
  364. if (aw.runModalLoop() == 0)
  365. return 0;
  366. if (isJuceFolder (juceFolderSelector.getCurrentFile()))
  367. {
  368. wizard = createWizard (aw.getComboBoxComponent ("type")->getSelectedItemIndex());
  369. break;
  370. }
  371. aw.setColour (AlertWindow::textColourId, Colours::red);
  372. aw.setMessage ("Please select a valid Juce folder for the project to use!");
  373. }
  374. }
  375. return wizard != 0 ? wizard->runWizard (ownerWindow) : 0;
  376. }