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.

956 lines
30KB

  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_Project.h"
  19. #include "jucer_ProjectType.h"
  20. #include "../Project Saving/jucer_ProjectExporter.h"
  21. #include "../Project Saving/jucer_ProjectSaver.h"
  22. #include "../Application/jucer_OpenDocumentManager.h"
  23. //==============================================================================
  24. namespace Tags
  25. {
  26. const Identifier projectRoot ("JUCERPROJECT");
  27. const Identifier projectMainGroup ("MAINGROUP");
  28. const Identifier group ("GROUP");
  29. const Identifier file ("FILE");
  30. const Identifier exporters ("EXPORTFORMATS");
  31. const Identifier configGroup ("JUCEOPTIONS");
  32. const Identifier modulesGroup ("MODULES");
  33. const Identifier module ("MODULE");
  34. }
  35. const char* Project::projectFileExtension = ".jucer";
  36. //==============================================================================
  37. Project::Project (const File& file_)
  38. : FileBasedDocument (projectFileExtension,
  39. String ("*") + projectFileExtension,
  40. "Choose a Jucer project to load",
  41. "Save Jucer project"),
  42. projectRoot (Tags::projectRoot)
  43. {
  44. setFile (file_);
  45. setMissingDefaultValues();
  46. setChangedFlag (false);
  47. mainProjectIcon.setImage (ImageCache::getFromMemory (BinaryData::juce_icon_png, BinaryData::juce_icon_pngSize));
  48. projectRoot.addListener (this);
  49. }
  50. Project::~Project()
  51. {
  52. projectRoot.removeListener (this);
  53. OpenDocumentManager::getInstance()->closeAllDocumentsUsingProject (*this, false);
  54. }
  55. //==============================================================================
  56. void Project::setTitle (const String& newTitle)
  57. {
  58. projectRoot.setProperty (Ids::name, newTitle, getUndoManagerFor (projectRoot));
  59. getMainGroup().getName() = newTitle;
  60. }
  61. const String Project::getDocumentTitle()
  62. {
  63. return getProjectName().toString();
  64. }
  65. void Project::updateProjectSettings()
  66. {
  67. projectRoot.setProperty (Ids::jucerVersion, ProjectInfo::versionString, 0);
  68. projectRoot.setProperty (Ids::name, getDocumentTitle(), 0);
  69. }
  70. void Project::setMissingDefaultValues()
  71. {
  72. if (! projectRoot.hasProperty (ComponentBuilder::idProperty))
  73. projectRoot.setProperty (ComponentBuilder::idProperty, createAlphaNumericUID(), nullptr);
  74. // Create main file group if missing
  75. if (! projectRoot.getChildWithName (Tags::projectMainGroup).isValid())
  76. {
  77. Item mainGroup (*this, ValueTree (Tags::projectMainGroup));
  78. projectRoot.addChild (mainGroup.state, 0, 0);
  79. }
  80. getMainGroup().initialiseMissingProperties();
  81. if (getDocumentTitle().isEmpty())
  82. setTitle ("Juce Project");
  83. if (! projectRoot.hasProperty (Ids::projectType))
  84. getProjectTypeValue() = ProjectType::getGUIAppTypeName();
  85. if (! projectRoot.hasProperty (Ids::version))
  86. getVersion() = "1.0.0";
  87. updateOldStyleConfigList();
  88. moveOldPropertyFromProjectToAllExporters (Ids::bigIcon);
  89. moveOldPropertyFromProjectToAllExporters (Ids::smallIcon);
  90. for (Project::ExporterIterator exporter (*this); exporter.next();)
  91. if (exporter->getNumConfigurations() == 0)
  92. exporter->createDefaultConfigs();
  93. if (! projectRoot.getChildWithName (Tags::exporters).isValid())
  94. createDefaultExporters();
  95. getProjectType().setMissingProjectProperties (*this);
  96. if (! projectRoot.hasProperty (Ids::bundleIdentifier))
  97. setBundleIdentifierToDefault();
  98. if (! projectRoot.getChildWithName (Tags::modulesGroup).isValid())
  99. addDefaultModules (false);
  100. }
  101. void Project::updateOldStyleConfigList()
  102. {
  103. ValueTree deprecatedConfigsList (projectRoot.getChildWithName (ProjectExporter::configurations));
  104. if (deprecatedConfigsList.isValid())
  105. {
  106. projectRoot.removeChild (deprecatedConfigsList, nullptr);
  107. for (Project::ExporterIterator exporter (*this); exporter.next();)
  108. {
  109. if (exporter->getNumConfigurations() == 0)
  110. {
  111. ValueTree newConfigs (deprecatedConfigsList.createCopy());
  112. if (! exporter->isXcode())
  113. {
  114. for (int j = newConfigs.getNumChildren(); --j >= 0;)
  115. {
  116. ValueTree config (newConfigs.getChild(j));
  117. config.removeProperty (Ids::osxSDK, nullptr);
  118. config.removeProperty (Ids::osxCompatibility, nullptr);
  119. config.removeProperty (Ids::osxArchitecture, nullptr);
  120. }
  121. }
  122. exporter->settings.addChild (newConfigs, 0, nullptr);
  123. }
  124. }
  125. }
  126. }
  127. void Project::moveOldPropertyFromProjectToAllExporters (Identifier name)
  128. {
  129. if (projectRoot.hasProperty (name))
  130. {
  131. for (Project::ExporterIterator exporter (*this); exporter.next();)
  132. exporter->settings.setProperty (name, projectRoot [name], nullptr);
  133. projectRoot.removeProperty (name, nullptr);
  134. }
  135. }
  136. void Project::addDefaultModules (bool shouldCopyFilesLocally)
  137. {
  138. addModule ("juce_core", shouldCopyFilesLocally);
  139. if (! isConfigFlagEnabled ("JUCE_ONLY_BUILD_CORE_LIBRARY"))
  140. {
  141. addModule ("juce_events", shouldCopyFilesLocally);
  142. addModule ("juce_graphics", shouldCopyFilesLocally);
  143. addModule ("juce_data_structures", shouldCopyFilesLocally);
  144. addModule ("juce_gui_basics", shouldCopyFilesLocally);
  145. addModule ("juce_gui_extra", shouldCopyFilesLocally);
  146. addModule ("juce_gui_audio", shouldCopyFilesLocally);
  147. addModule ("juce_cryptography", shouldCopyFilesLocally);
  148. addModule ("juce_video", shouldCopyFilesLocally);
  149. addModule ("juce_opengl", shouldCopyFilesLocally);
  150. addModule ("juce_audio_basics", shouldCopyFilesLocally);
  151. addModule ("juce_audio_devices", shouldCopyFilesLocally);
  152. addModule ("juce_audio_formats", shouldCopyFilesLocally);
  153. addModule ("juce_audio_processors", shouldCopyFilesLocally);
  154. }
  155. }
  156. //==============================================================================
  157. const String Project::loadDocument (const File& file)
  158. {
  159. ScopedPointer <XmlElement> xml (XmlDocument::parse (file));
  160. if (xml == nullptr || ! xml->hasTagName (Tags::projectRoot.toString()))
  161. return "Not a valid Jucer project!";
  162. ValueTree newTree (ValueTree::fromXml (*xml));
  163. if (! newTree.hasType (Tags::projectRoot))
  164. return "The document contains errors and couldn't be parsed!";
  165. StoredSettings::getInstance()->recentFiles.addFile (file);
  166. StoredSettings::getInstance()->flush();
  167. projectRoot = newTree;
  168. setMissingDefaultValues();
  169. return String::empty;
  170. }
  171. const String Project::saveDocument (const File& file)
  172. {
  173. updateProjectSettings();
  174. sanitiseConfigFlags();
  175. StoredSettings::getInstance()->recentFiles.addFile (file);
  176. ProjectSaver saver (*this, file);
  177. return saver.save();
  178. }
  179. //==============================================================================
  180. File Project::lastDocumentOpened;
  181. const File Project::getLastDocumentOpened()
  182. {
  183. return lastDocumentOpened;
  184. }
  185. void Project::setLastDocumentOpened (const File& file)
  186. {
  187. lastDocumentOpened = file;
  188. }
  189. //==============================================================================
  190. void Project::valueTreePropertyChanged (ValueTree& tree, const Identifier& property)
  191. {
  192. if (property == Ids::projectType)
  193. setMissingDefaultValues();
  194. changed();
  195. }
  196. void Project::valueTreeChildAdded (ValueTree& parentTree, ValueTree& childWhichHasBeenAdded)
  197. {
  198. changed();
  199. }
  200. void Project::valueTreeChildRemoved (ValueTree& parentTree, ValueTree& childWhichHasBeenRemoved)
  201. {
  202. changed();
  203. }
  204. void Project::valueTreeChildOrderChanged (ValueTree& parentTree)
  205. {
  206. changed();
  207. }
  208. void Project::valueTreeParentChanged (ValueTree& tree)
  209. {
  210. }
  211. //==============================================================================
  212. File Project::resolveFilename (String filename) const
  213. {
  214. if (filename.isEmpty())
  215. return File::nonexistent;
  216. filename = replacePreprocessorDefs (getPreprocessorDefs(), filename)
  217. .replaceCharacter ('\\', '/');
  218. if (File::isAbsolutePath (filename))
  219. return File (filename);
  220. return getFile().getSiblingFile (filename);
  221. }
  222. String Project::getRelativePathForFile (const File& file) const
  223. {
  224. String filename (file.getFullPathName());
  225. File relativePathBase (getFile().getParentDirectory());
  226. String p1 (relativePathBase.getFullPathName());
  227. String p2 (file.getFullPathName());
  228. while (p1.startsWithChar (File::separator))
  229. p1 = p1.substring (1);
  230. while (p2.startsWithChar (File::separator))
  231. p2 = p2.substring (1);
  232. if (p1.upToFirstOccurrenceOf (File::separatorString, true, false)
  233. .equalsIgnoreCase (p2.upToFirstOccurrenceOf (File::separatorString, true, false)))
  234. {
  235. filename = file.getRelativePathFrom (relativePathBase);
  236. }
  237. return filename;
  238. }
  239. //==============================================================================
  240. const ProjectType& Project::getProjectType() const
  241. {
  242. const ProjectType* type = ProjectType::findType (getProjectTypeValue().toString());
  243. jassert (type != nullptr);
  244. if (type == nullptr)
  245. {
  246. type = ProjectType::findType (ProjectType::getGUIAppTypeName());
  247. jassert (type != nullptr);
  248. }
  249. return *type;
  250. }
  251. //==============================================================================
  252. void Project::createPropertyEditors (PropertyListBuilder& props)
  253. {
  254. props.add (new TextPropertyComponent (getProjectName(), "Project Name", 256, false),
  255. "The name of the project.");
  256. props.add (new TextPropertyComponent (getVersion(), "Project Version", 16, false),
  257. "The project's version number, This should be in the format major.minor.point");
  258. {
  259. StringArray projectTypeNames;
  260. Array<var> projectTypeCodes;
  261. const Array<ProjectType*>& types = ProjectType::getAllTypes();
  262. for (int i = 0; i < types.size(); ++i)
  263. {
  264. projectTypeNames.add (types.getUnchecked(i)->getDescription());
  265. projectTypeCodes.add (types.getUnchecked(i)->getType());
  266. }
  267. props.add (new ChoicePropertyComponent (getProjectTypeValue(), "Project Type", projectTypeNames, projectTypeCodes));
  268. }
  269. props.add (new TextPropertyComponent (getBundleIdentifier(), "Bundle Identifier", 256, false),
  270. "A unique identifier for this product, mainly for use in Mac builds. It should be something like 'com.yourcompanyname.yourproductname'");
  271. getProjectType().createPropertyEditors (*this, props);
  272. props.add (new TextPropertyComponent (getProjectPreprocessorDefs(), "Preprocessor definitions", 32768, false),
  273. "Extra preprocessor definitions. Use the form \"NAME1=value NAME2=value\", using whitespace or commas to separate the items - to include a space or comma in a definition, precede it with a backslash.");
  274. props.setPreferredHeight (22);
  275. }
  276. String Project::getVersionAsHex() const
  277. {
  278. StringArray configs;
  279. configs.addTokens (getVersion().toString(), ",.", String::empty);
  280. configs.trim();
  281. configs.removeEmptyStrings();
  282. int value = (configs[0].getIntValue() << 16) + (configs[1].getIntValue() << 8) + configs[2].getIntValue();
  283. if (configs.size() >= 4)
  284. value = (value << 8) + configs[3].getIntValue();
  285. return "0x" + String::toHexString (value);
  286. }
  287. StringPairArray Project::getPreprocessorDefs() const
  288. {
  289. return parsePreprocessorDefs (getProjectPreprocessorDefs().toString());
  290. }
  291. //==============================================================================
  292. Project::Item Project::getMainGroup()
  293. {
  294. return Item (*this, projectRoot.getChildWithName (Tags::projectMainGroup));
  295. }
  296. static void findImages (const Project::Item& item, OwnedArray<Project::Item>& found)
  297. {
  298. if (item.isImageFile())
  299. {
  300. found.add (new Project::Item (item));
  301. }
  302. else if (item.isGroup())
  303. {
  304. for (int i = 0; i < item.getNumChildren(); ++i)
  305. findImages (item.getChild (i), found);
  306. }
  307. }
  308. void Project::findAllImageItems (OwnedArray<Project::Item>& items)
  309. {
  310. findImages (getMainGroup(), items);
  311. }
  312. //==============================================================================
  313. Project::Item::Item (Project& project_, const ValueTree& state_)
  314. : project (project_), state (state_)
  315. {
  316. }
  317. Project::Item::Item (const Item& other)
  318. : project (other.project), state (other.state)
  319. {
  320. }
  321. Project::Item Project::Item::createCopy() { Item i (*this); i.state = i.state.createCopy(); return i; }
  322. String Project::Item::getID() const { return state [ComponentBuilder::idProperty]; }
  323. void Project::Item::setID (const String& newID) { state.setProperty (ComponentBuilder::idProperty, newID, nullptr); }
  324. String Project::Item::getImageFileID() const { return "id:" + getID(); }
  325. Image Project::Item::loadAsImageFile() const
  326. {
  327. return isValid() ? ImageCache::getFromFile (getFile())
  328. : Image::null;
  329. }
  330. Project::Item Project::Item::createGroup (Project& project, const String& name, const String& uid)
  331. {
  332. Item group (project, ValueTree (Tags::group));
  333. group.setID (uid);
  334. group.initialiseMissingProperties();
  335. group.getName() = name;
  336. return group;
  337. }
  338. bool Project::Item::isFile() const { return state.hasType (Tags::file); }
  339. bool Project::Item::isGroup() const { return state.hasType (Tags::group) || isMainGroup(); }
  340. bool Project::Item::isMainGroup() const { return state.hasType (Tags::projectMainGroup); }
  341. bool Project::Item::isImageFile() const { return isFile() && getFile().hasFileExtension ("png;jpg;jpeg;gif;drawable"); }
  342. Project::Item Project::Item::findItemWithID (const String& targetId) const
  343. {
  344. if (state [ComponentBuilder::idProperty] == targetId)
  345. return *this;
  346. if (isGroup())
  347. {
  348. for (int i = getNumChildren(); --i >= 0;)
  349. {
  350. Item found (getChild(i).findItemWithID (targetId));
  351. if (found.isValid())
  352. return found;
  353. }
  354. }
  355. return Item (project, ValueTree::invalid);
  356. }
  357. bool Project::Item::canContain (const Item& child) const
  358. {
  359. if (isFile())
  360. return false;
  361. if (isGroup())
  362. return child.isFile() || child.isGroup();
  363. jassertfalse
  364. return false;
  365. }
  366. bool Project::Item::shouldBeAddedToTargetProject() const { return isFile(); }
  367. bool Project::Item::shouldBeCompiled() const { return getShouldCompileValue().getValue(); }
  368. Value Project::Item::getShouldCompileValue() const { return state.getPropertyAsValue (Ids::compile, getUndoManager()); }
  369. bool Project::Item::shouldBeAddedToBinaryResources() const { return getShouldAddToResourceValue().getValue(); }
  370. Value Project::Item::getShouldAddToResourceValue() const { return state.getPropertyAsValue (Ids::resource, getUndoManager()); }
  371. Value Project::Item::getShouldInhibitWarningsValue() const { return state.getPropertyAsValue (Ids::noWarnings, getUndoManager()); }
  372. Value Project::Item::getShouldUseStdCallValue() const { return state.getPropertyAsValue (Ids::useStdCall, nullptr); }
  373. String Project::Item::getFilePath() const
  374. {
  375. if (isFile())
  376. return state [Ids::file].toString();
  377. else
  378. return String::empty;
  379. }
  380. File Project::Item::getFile() const
  381. {
  382. if (isFile())
  383. return project.resolveFilename (state [Ids::file].toString());
  384. else
  385. return File::nonexistent;
  386. }
  387. void Project::Item::setFile (const File& file)
  388. {
  389. setFile (RelativePath (project.getRelativePathForFile (file), RelativePath::projectFolder));
  390. jassert (getFile() == file);
  391. }
  392. void Project::Item::setFile (const RelativePath& file)
  393. {
  394. jassert (file.getRoot() == RelativePath::projectFolder);
  395. jassert (isFile());
  396. state.setProperty (Ids::file, file.toUnixStyle(), getUndoManager());
  397. state.setProperty (Ids::name, file.getFileName(), getUndoManager());
  398. }
  399. bool Project::Item::renameFile (const File& newFile)
  400. {
  401. const File oldFile (getFile());
  402. if (oldFile.moveFileTo (newFile))
  403. {
  404. setFile (newFile);
  405. OpenDocumentManager::getInstance()->fileHasBeenRenamed (oldFile, newFile);
  406. return true;
  407. }
  408. return false;
  409. }
  410. bool Project::Item::containsChildForFile (const RelativePath& file) const
  411. {
  412. return state.getChildWithProperty (Ids::file, file.toUnixStyle()).isValid();
  413. }
  414. Project::Item Project::Item::findItemForFile (const File& file) const
  415. {
  416. if (getFile() == file)
  417. return *this;
  418. if (isGroup())
  419. {
  420. for (int i = getNumChildren(); --i >= 0;)
  421. {
  422. Item found (getChild(i).findItemForFile (file));
  423. if (found.isValid())
  424. return found;
  425. }
  426. }
  427. return Item (project, ValueTree::invalid);
  428. }
  429. File Project::Item::determineGroupFolder() const
  430. {
  431. jassert (isGroup());
  432. File f;
  433. for (int i = 0; i < getNumChildren(); ++i)
  434. {
  435. f = getChild(i).getFile();
  436. if (f.exists())
  437. return f.getParentDirectory();
  438. }
  439. Item parent (getParent());
  440. if (parent != *this)
  441. {
  442. f = parent.determineGroupFolder();
  443. if (f.getChildFile (getName().toString()).isDirectory())
  444. f = f.getChildFile (getName().toString());
  445. }
  446. else
  447. {
  448. f = project.getFile().getParentDirectory();
  449. if (f.getChildFile ("Source").isDirectory())
  450. f = f.getChildFile ("Source");
  451. }
  452. return f;
  453. }
  454. void Project::Item::initialiseMissingProperties()
  455. {
  456. if (! state.hasProperty (ComponentBuilder::idProperty))
  457. setID (createAlphaNumericUID());
  458. if (isFile())
  459. {
  460. state.setProperty (Ids::name, getFile().getFileName(), 0);
  461. }
  462. else if (isGroup())
  463. {
  464. for (int i = getNumChildren(); --i >= 0;)
  465. getChild(i).initialiseMissingProperties();
  466. }
  467. }
  468. Value Project::Item::getName() const
  469. {
  470. return state.getPropertyAsValue (Ids::name, getUndoManager());
  471. }
  472. void Project::Item::addChild (const Item& newChild, int insertIndex)
  473. {
  474. state.addChild (newChild.state, insertIndex, getUndoManager());
  475. }
  476. void Project::Item::removeItemFromProject()
  477. {
  478. state.getParent().removeChild (state, getUndoManager());
  479. }
  480. Project::Item Project::Item::getParent() const
  481. {
  482. if (isMainGroup() || ! isGroup())
  483. return *this;
  484. return Item (project, state.getParent());
  485. }
  486. struct ItemSorter
  487. {
  488. static int compareElements (const ValueTree& first, const ValueTree& second)
  489. {
  490. return first [Ids::name].toString().compareIgnoreCase (second [Ids::name].toString());
  491. }
  492. };
  493. struct ItemSorterWithGroupsAtStart
  494. {
  495. static int compareElements (const ValueTree& first, const ValueTree& second)
  496. {
  497. const bool firstIsGroup = first.hasType (Tags::group);
  498. const bool secondIsGroup = second.hasType (Tags::group);
  499. if (firstIsGroup == secondIsGroup)
  500. return first [Ids::name].toString().compareIgnoreCase (second [Ids::name].toString());
  501. else
  502. return firstIsGroup ? -1 : 1;
  503. }
  504. };
  505. void Project::Item::sortAlphabetically (bool keepGroupsAtStart)
  506. {
  507. if (keepGroupsAtStart)
  508. {
  509. ItemSorterWithGroupsAtStart sorter;
  510. state.sort (sorter, getUndoManager(), true);
  511. }
  512. else
  513. {
  514. ItemSorter sorter;
  515. state.sort (sorter, getUndoManager(), true);
  516. }
  517. }
  518. Project::Item Project::Item::getOrCreateSubGroup (const String& name)
  519. {
  520. for (int i = state.getNumChildren(); --i >= 0;)
  521. {
  522. const ValueTree child (state.getChild (i));
  523. if (child.getProperty (Ids::name) == name && child.hasType (Tags::group))
  524. return Item (project, child);
  525. }
  526. return addNewSubGroup (name, -1);
  527. }
  528. Project::Item Project::Item::addNewSubGroup (const String& name, int insertIndex)
  529. {
  530. Item group (createGroup (project, name, createGUID (getID() + name + String (getNumChildren()))));
  531. jassert (canContain (group));
  532. addChild (group, insertIndex);
  533. return group;
  534. }
  535. bool Project::Item::addFile (const File& file, int insertIndex, const bool shouldCompile)
  536. {
  537. if (file == File::nonexistent || file.isHidden() || file.getFileName().startsWithChar ('.'))
  538. return false;
  539. if (file.isDirectory())
  540. {
  541. Item group (addNewSubGroup (file.getFileNameWithoutExtension(), insertIndex));
  542. DirectoryIterator iter (file, false, "*", File::findFilesAndDirectories);
  543. while (iter.next())
  544. {
  545. if (! project.getMainGroup().findItemForFile (iter.getFile()).isValid())
  546. group.addFile (iter.getFile(), -1, shouldCompile);
  547. }
  548. group.sortAlphabetically (false);
  549. }
  550. else if (file.existsAsFile())
  551. {
  552. if (! project.getMainGroup().findItemForFile (file).isValid())
  553. addFileUnchecked (file, insertIndex, shouldCompile);
  554. }
  555. else
  556. {
  557. jassertfalse;
  558. }
  559. return true;
  560. }
  561. void Project::Item::addFileUnchecked (const File& file, int insertIndex, const bool shouldCompile)
  562. {
  563. Item item (project, ValueTree (Tags::file));
  564. item.initialiseMissingProperties();
  565. item.getName() = file.getFileName();
  566. item.getShouldCompileValue() = shouldCompile && file.hasFileExtension ("cpp;mm;c;m;cc;cxx;r");
  567. item.getShouldAddToResourceValue() = project.shouldBeAddedToBinaryResourcesByDefault (file);
  568. if (canContain (item))
  569. {
  570. item.setFile (file);
  571. addChild (item, insertIndex);
  572. }
  573. }
  574. bool Project::Item::addRelativeFile (const RelativePath& file, int insertIndex, bool shouldCompile)
  575. {
  576. Item item (project, ValueTree (Tags::file));
  577. item.initialiseMissingProperties();
  578. item.getName() = file.getFileName();
  579. item.getShouldCompileValue() = shouldCompile;
  580. item.getShouldAddToResourceValue() = project.shouldBeAddedToBinaryResourcesByDefault (file);
  581. if (canContain (item))
  582. {
  583. item.setFile (file);
  584. addChild (item, insertIndex);
  585. return true;
  586. }
  587. return false;
  588. }
  589. const Drawable* Project::Item::getIcon() const
  590. {
  591. if (isFile())
  592. {
  593. if (isImageFile())
  594. return StoredSettings::getInstance()->getImageFileIcon();
  595. return LookAndFeel::getDefaultLookAndFeel().getDefaultDocumentFileImage();
  596. }
  597. else if (isMainGroup())
  598. {
  599. return &(project.mainProjectIcon);
  600. }
  601. return LookAndFeel::getDefaultLookAndFeel().getDefaultFolderImage();
  602. }
  603. //==============================================================================
  604. ValueTree Project::getConfigNode()
  605. {
  606. return projectRoot.getOrCreateChildWithName (Tags::configGroup, nullptr);
  607. }
  608. const char* const Project::configFlagDefault = "default";
  609. const char* const Project::configFlagEnabled = "enabled";
  610. const char* const Project::configFlagDisabled = "disabled";
  611. Value Project::getConfigFlag (const String& name)
  612. {
  613. const ValueTree configNode (getConfigNode());
  614. Value v (configNode.getPropertyAsValue (name, getUndoManagerFor (configNode)));
  615. if (v.getValue().toString().isEmpty())
  616. v = configFlagDefault;
  617. return v;
  618. }
  619. bool Project::isConfigFlagEnabled (const String& name) const
  620. {
  621. return projectRoot.getChildWithName (Tags::configGroup).getProperty (name) == configFlagEnabled;
  622. }
  623. void Project::sanitiseConfigFlags()
  624. {
  625. ValueTree configNode (getConfigNode());
  626. for (int i = configNode.getNumProperties(); --i >= 0;)
  627. {
  628. const var value (configNode [configNode.getPropertyName(i)]);
  629. if (value != configFlagEnabled && value != configFlagDisabled)
  630. configNode.removeProperty (configNode.getPropertyName(i), getUndoManagerFor (configNode));
  631. }
  632. }
  633. //==============================================================================
  634. ValueTree Project::getModulesNode()
  635. {
  636. return projectRoot.getOrCreateChildWithName (Tags::modulesGroup, nullptr);
  637. }
  638. bool Project::isModuleEnabled (const String& moduleID) const
  639. {
  640. ValueTree modules (projectRoot.getChildWithName (Tags::modulesGroup));
  641. for (int i = 0; i < modules.getNumChildren(); ++i)
  642. if (modules.getChild(i) [ComponentBuilder::idProperty] == moduleID)
  643. return true;
  644. return false;
  645. }
  646. Value Project::shouldShowAllModuleFilesInProject (const String& moduleID)
  647. {
  648. return getModulesNode().getChildWithProperty (ComponentBuilder::idProperty, moduleID)
  649. .getPropertyAsValue (Ids::showAllCode, getUndoManagerFor (getModulesNode()));
  650. }
  651. Value Project::shouldCopyModuleFilesLocally (const String& moduleID)
  652. {
  653. return getModulesNode().getChildWithProperty (ComponentBuilder::idProperty, moduleID)
  654. .getPropertyAsValue (Ids::useLocalCopy, getUndoManagerFor (getModulesNode()));
  655. }
  656. void Project::addModule (const String& moduleID, bool shouldCopyFilesLocally)
  657. {
  658. if (! isModuleEnabled (moduleID))
  659. {
  660. ValueTree module (Tags::module);
  661. module.setProperty (ComponentBuilder::idProperty, moduleID, nullptr);
  662. ValueTree modules (getModulesNode());
  663. modules.addChild (module, -1, getUndoManagerFor (modules));
  664. shouldShowAllModuleFilesInProject (moduleID) = true;
  665. }
  666. if (shouldCopyFilesLocally)
  667. shouldCopyModuleFilesLocally (moduleID) = true;
  668. }
  669. void Project::removeModule (const String& moduleID)
  670. {
  671. ValueTree modules (getModulesNode());
  672. for (int i = 0; i < modules.getNumChildren(); ++i)
  673. if (modules.getChild(i) [ComponentBuilder::idProperty] == moduleID)
  674. modules.removeChild (i, getUndoManagerFor (modules));
  675. }
  676. void Project::createRequiredModules (const ModuleList& availableModules, OwnedArray<LibraryModule>& modules) const
  677. {
  678. for (int i = 0; i < availableModules.modules.size(); ++i)
  679. if (isModuleEnabled (availableModules.modules.getUnchecked(i)->uid))
  680. modules.add (availableModules.modules.getUnchecked(i)->create());
  681. }
  682. int Project::getNumModules() const
  683. {
  684. return projectRoot.getChildWithName (Tags::modulesGroup).getNumChildren();
  685. }
  686. String Project::getModuleID (int index) const
  687. {
  688. return projectRoot.getChildWithName (Tags::modulesGroup).getChild (index) [ComponentBuilder::idProperty].toString();
  689. }
  690. //==============================================================================
  691. ValueTree Project::getExporters()
  692. {
  693. ValueTree exporters (projectRoot.getChildWithName (Tags::exporters));
  694. if (! exporters.isValid())
  695. {
  696. projectRoot.addChild (ValueTree (Tags::exporters), 0, getUndoManagerFor (projectRoot));
  697. exporters = getExporters();
  698. }
  699. return exporters;
  700. }
  701. int Project::getNumExporters()
  702. {
  703. return getExporters().getNumChildren();
  704. }
  705. ProjectExporter* Project::createExporter (int index)
  706. {
  707. jassert (index >= 0 && index < getNumExporters());
  708. return ProjectExporter::createExporter (*this, getExporters().getChild (index));
  709. }
  710. void Project::addNewExporter (const String& exporterName)
  711. {
  712. ScopedPointer<ProjectExporter> exp (ProjectExporter::createNewExporter (*this, exporterName));
  713. ValueTree exporters (getExporters());
  714. exporters.addChild (exp->getSettings(), -1, getUndoManagerFor (exporters));
  715. }
  716. void Project::deleteExporter (int index)
  717. {
  718. ValueTree exporters (getExporters());
  719. exporters.removeChild (index, getUndoManagerFor (exporters));
  720. }
  721. void Project::createDefaultExporters()
  722. {
  723. ValueTree exporters (getExporters());
  724. exporters.removeAllChildren (getUndoManagerFor (exporters));
  725. const StringArray exporterNames (ProjectExporter::getDefaultExporters());
  726. for (int i = 0; i < exporterNames.size(); ++i)
  727. addNewExporter (exporterNames[i]);
  728. }
  729. //==============================================================================
  730. String Project::getFileTemplate (const String& templateName)
  731. {
  732. int dataSize;
  733. const char* data = BinaryData::getNamedResource (templateName.toUTF8(), dataSize);
  734. if (data == nullptr)
  735. {
  736. jassertfalse;
  737. return String::empty;
  738. }
  739. return String::fromUTF8 (data, dataSize);
  740. }
  741. //==============================================================================
  742. Project::ExporterIterator::ExporterIterator (Project& project_) : index (-1), project (project_) {}
  743. Project::ExporterIterator::~ExporterIterator() {}
  744. bool Project::ExporterIterator::next()
  745. {
  746. if (++index >= project.getNumExporters())
  747. return false;
  748. exporter = project.createExporter (index);
  749. if (exporter == nullptr)
  750. {
  751. jassertfalse; // corrupted project file?
  752. return next();
  753. }
  754. return true;
  755. }