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.

482 lines
15KB

  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_DrawableDocument.h"
  19. #include "jucer_DrawableTypeHandler.h"
  20. //==============================================================================
  21. namespace Tags
  22. {
  23. const Identifier drawableTag ("DRAWABLE");
  24. const Identifier markersGroupXTag ("MARKERS_X");
  25. const Identifier markersGroupYTag ("MARKERS_Y");
  26. }
  27. //==============================================================================
  28. DrawableDocument::DrawableDocument (Project* project_)
  29. : project (project_),
  30. root (Tags::drawableTag),
  31. saveAsXml (true),
  32. needsSaving (false)
  33. {
  34. DrawableComposite dc;
  35. root.addChild (dc.createValueTree (0), -1, 0);
  36. setName ("Drawable");
  37. checkRootObject();
  38. root.addListener (this);
  39. }
  40. DrawableDocument::~DrawableDocument()
  41. {
  42. root.removeListener (this);
  43. }
  44. void DrawableDocument::recursivelyUpdateIDs (Drawable::ValueTreeWrapperBase& d)
  45. {
  46. if (d.getID().isEmpty())
  47. d.setID (createUniqueID (d.getState().getType().toString().toLowerCase() + "1"), 0);
  48. if (d.getState().getType() == DrawableComposite::valueTreeType)
  49. {
  50. const DrawableComposite::ValueTreeWrapper composite (d.getState());
  51. for (int i = 0; i < composite.getNumDrawables(); ++i)
  52. {
  53. Drawable::ValueTreeWrapperBase child (composite.getDrawableState (i));
  54. recursivelyUpdateIDs (child);
  55. }
  56. }
  57. }
  58. void DrawableDocument::checkRootObject()
  59. {
  60. if (! root.hasProperty (Ids::id_))
  61. root.setProperty (Ids::id_, createAlphaNumericUID(), 0);
  62. if (markersX == 0)
  63. markersX = new MarkerList (*this, true);
  64. if (markersY == 0)
  65. markersY = new MarkerList (*this, false);
  66. DrawableComposite::ValueTreeWrapper rootObject (getRootDrawableNode());
  67. recursivelyUpdateIDs (rootObject);
  68. }
  69. //==============================================================================
  70. void DrawableDocument::setName (const String& name)
  71. {
  72. root.setProperty (Ids::name, name, getUndoManager());
  73. }
  74. const String DrawableDocument::getName() const
  75. {
  76. return root [Ids::name];
  77. }
  78. bool DrawableDocument::hasChangedSinceLastSave() const
  79. {
  80. return needsSaving;
  81. }
  82. bool DrawableDocument::reload (const File& drawableFile)
  83. {
  84. ScopedPointer <InputStream> stream (drawableFile.createInputStream());
  85. if (stream != 0 && load (*stream))
  86. {
  87. checkRootObject();
  88. undoManager.clearUndoHistory();
  89. needsSaving = false;
  90. return true;
  91. }
  92. return false;
  93. }
  94. bool DrawableDocument::save (const File& drawableFile)
  95. {
  96. TemporaryFile tempFile (drawableFile);
  97. ScopedPointer <OutputStream> out (tempFile.getFile().createOutputStream());
  98. if (out == 0)
  99. return false;
  100. save (*out);
  101. needsSaving = ! tempFile.overwriteTargetFileWithTemporary();
  102. return ! needsSaving;
  103. }
  104. void DrawableDocument::save (OutputStream& output)
  105. {
  106. if (saveAsXml)
  107. {
  108. ScopedPointer <XmlElement> xml (root.createXml());
  109. jassert (xml != 0);
  110. if (xml != 0)
  111. xml->writeToStream (output, String::empty, false, false);
  112. }
  113. else
  114. {
  115. root.writeToStream (output);
  116. }
  117. }
  118. bool DrawableDocument::load (InputStream& input)
  119. {
  120. int64 originalPos = input.getPosition();
  121. ValueTree loadedTree;
  122. XmlDocument xmlDoc (input.readEntireStreamAsString());
  123. ScopedPointer <XmlElement> xml (xmlDoc.getDocumentElement());
  124. if (xml != 0)
  125. {
  126. loadedTree = ValueTree::fromXml (*xml);
  127. }
  128. else
  129. {
  130. input.setPosition (originalPos);
  131. loadedTree = ValueTree::readFromStream (input);
  132. }
  133. if (loadedTree.hasType (Tags::drawableTag))
  134. {
  135. root.removeListener (this);
  136. root = loadedTree;
  137. root.addListener (this);
  138. markersX = 0;
  139. markersY = 0;
  140. valueTreeParentChanged (loadedTree);
  141. needsSaving = false;
  142. undoManager.clearUndoHistory();
  143. return true;
  144. }
  145. return false;
  146. }
  147. void DrawableDocument::changed()
  148. {
  149. needsSaving = true;
  150. }
  151. DrawableComposite::ValueTreeWrapper DrawableDocument::getRootDrawableNode() const
  152. {
  153. return DrawableComposite::ValueTreeWrapper (root.getChild (0));
  154. }
  155. ValueTree DrawableDocument::findDrawableState (const String& objectId, bool recursive) const
  156. {
  157. return getRootDrawableNode().getDrawableWithId (objectId, recursive);
  158. }
  159. const String DrawableDocument::createUniqueID (const String& name) const
  160. {
  161. String n (CodeHelpers::makeValidIdentifier (name, false, true, false));
  162. int suffix = 2;
  163. while (markersX->getMarkerNamed (n).isValid() || markersY->getMarkerNamed (n).isValid()
  164. || findDrawableState (n, true).isValid())
  165. n = n.trimCharactersAtEnd ("0123456789") + String (suffix++);
  166. return n;
  167. }
  168. bool DrawableDocument::createItemProperties (Array <PropertyComponent*>& props, const String& itemId)
  169. {
  170. ValueTree drawable (findDrawableState (itemId, false));
  171. if (drawable.isValid())
  172. {
  173. DrawableTypeInstance item (*this, drawable);
  174. item.createProperties (props);
  175. return true;
  176. }
  177. if (markersX->createProperties (props, itemId)
  178. || markersY->createProperties (props, itemId))
  179. return true;
  180. return false;
  181. }
  182. void DrawableDocument::createItemProperties (Array <PropertyComponent*>& props, const StringArray& selectedItemIds)
  183. {
  184. if (selectedItemIds.size() != 1)
  185. return; //xxx
  186. for (int i = 0; i < selectedItemIds.size(); ++i)
  187. createItemProperties (props, selectedItemIds[i]);
  188. }
  189. //==============================================================================
  190. const int menuItemOffset = 0x63451fa4;
  191. void DrawableDocument::addNewItemMenuItems (PopupMenu& menu) const
  192. {
  193. const StringArray newItems (DrawableTypeManager::getInstance()->getNewItemList());
  194. for (int i = 0; i < newItems.size(); ++i)
  195. menu.addItem (i + menuItemOffset, newItems[i]);
  196. }
  197. const ValueTree DrawableDocument::performNewItemMenuItem (int menuResultCode)
  198. {
  199. const StringArray newItems (DrawableTypeManager::getInstance()->getNewItemList());
  200. int index = menuResultCode - menuItemOffset;
  201. if (index >= 0 && index < newItems.size())
  202. {
  203. ValueTree state (DrawableTypeManager::getInstance()
  204. ->createNewItem (index, *this,
  205. Point<float> (Random::getSystemRandom().nextFloat() * 100.0f + 100.0f,
  206. Random::getSystemRandom().nextFloat() * 100.0f + 100.0f)));
  207. Drawable::ValueTreeWrapperBase wrapper (state);
  208. recursivelyUpdateIDs (wrapper);
  209. getRootDrawableNode().addDrawable (state, -1, getUndoManager());
  210. return state;
  211. }
  212. return ValueTree::invalid;
  213. }
  214. //==============================================================================
  215. Image* DrawableDocument::getImageForIdentifier (const var& imageIdentifier)
  216. {
  217. return ImageCache::getFromMemory (BinaryData::juce_icon_png, BinaryData::juce_icon_pngSize);
  218. }
  219. const var DrawableDocument::getIdentifierForImage (Image* image)
  220. {
  221. return var::null; //xxx todo
  222. }
  223. //==============================================================================
  224. void DrawableDocument::valueTreePropertyChanged (ValueTree& tree, const Identifier& name)
  225. {
  226. changed();
  227. }
  228. void DrawableDocument::valueTreeChildrenChanged (ValueTree& tree)
  229. {
  230. changed();
  231. }
  232. void DrawableDocument::valueTreeParentChanged (ValueTree& tree)
  233. {
  234. changed();
  235. }
  236. //==============================================================================
  237. const RelativeCoordinate DrawableDocument::findNamedCoordinate (const String& objectName, const String& edge) const
  238. {
  239. if (objectName == "parent")
  240. {
  241. jassert (edge != "right" && edge != "bottom"); // drawables don't have a canvas size..
  242. }
  243. if (objectName.isNotEmpty() && edge.isNotEmpty())
  244. {
  245. }
  246. {
  247. const ValueTree marker (getMarkerListX().getMarkerNamed (objectName));
  248. if (marker.isValid())
  249. return getMarkerListX().getCoordinate (marker);
  250. }
  251. {
  252. const ValueTree marker (getMarkerListY().getMarkerNamed (objectName));
  253. if (marker.isValid())
  254. return getMarkerListY().getCoordinate (marker);
  255. }
  256. return RelativeCoordinate();
  257. }
  258. //==============================================================================
  259. DrawableDocument::MarkerList::MarkerList (DrawableDocument& document_, bool isX_)
  260. : MarkerListBase (isX_),
  261. document (document_),
  262. object (document_.getRootDrawableNode())
  263. {
  264. }
  265. const String DrawableDocument::MarkerList::getId (const ValueTree& markerState)
  266. {
  267. return markerState [DrawableComposite::ValueTreeWrapper::nameProperty];
  268. }
  269. int DrawableDocument::MarkerList::size() const
  270. {
  271. return object.getNumMarkers (isX);
  272. }
  273. ValueTree DrawableDocument::MarkerList::getMarker (int index) const
  274. {
  275. return object.getMarkerState (isX, index);
  276. }
  277. ValueTree DrawableDocument::MarkerList::getMarkerNamed (const String& name) const
  278. {
  279. return object.getMarkerState (isX, name);
  280. }
  281. bool DrawableDocument::MarkerList::contains (const ValueTree& markerState) const
  282. {
  283. return object.containsMarker (isX, markerState);
  284. }
  285. void DrawableDocument::MarkerList::createMarker (const String& name, int position)
  286. {
  287. object.setMarker (isX, DrawableComposite::Marker (name, RelativeCoordinate ((double) position, isX)),
  288. getUndoManager());
  289. }
  290. void DrawableDocument::MarkerList::deleteMarker (ValueTree& markerState)
  291. {
  292. object.removeMarker (isX, markerState, getUndoManager());
  293. }
  294. const RelativeCoordinate DrawableDocument::MarkerList::findNamedCoordinate (const String& objectName, const String& edge) const
  295. {
  296. if (objectName == "parent")
  297. {
  298. jassert (edge != "right" && edge != "bottom"); // drawables don't have a canvas size..
  299. }
  300. const ValueTree marker (getMarkerNamed (objectName));
  301. if (marker.isValid())
  302. return getCoordinate (marker);
  303. return RelativeCoordinate();
  304. }
  305. bool DrawableDocument::MarkerList::createProperties (Array <PropertyComponent*>& props, const String& itemId)
  306. {
  307. ValueTree marker (getMarkerNamed (itemId));
  308. if (marker.isValid())
  309. {
  310. props.add (new TextPropertyComponent (marker.getPropertyAsValue (DrawableComposite::ValueTreeWrapper::nameProperty, getUndoManager()),
  311. "Marker Name", 256, false));
  312. props.add (new MarkerListBase::PositionPropertyComponent (*this, "Position", marker,
  313. marker.getPropertyAsValue (DrawableComposite::ValueTreeWrapper::posProperty, getUndoManager())));
  314. return true;
  315. }
  316. return false;
  317. }
  318. void DrawableDocument::addMarkerMenuItem (int i, const RelativeCoordinate& coord, const String& objectName, const String& edge, PopupMenu& menu,
  319. bool isAnchor1, const String& fullCoordName)
  320. {
  321. // RelativeCoordinate requestedCoord (findNamedCoordinate (objectName, edge, coord.isHorizontal()));
  322. // menu.addItem (i, name,
  323. // ! (name == fullCoordName || requestedCoord.referencesIndirectly (fullCoordName, *this)),
  324. // name == (isAnchor1 ? coord.getAnchor1() : coord.getAnchor2()));
  325. }
  326. void DrawableDocument::MarkerList::addMarkerMenuItems (const ValueTree& markerState, const RelativeCoordinate& coord, PopupMenu& menu, bool isAnchor1)
  327. {
  328. /* const String fullCoordName (getName (markerState));
  329. if (coord.isHorizontal())
  330. {
  331. document.addMarkerMenuItem (1, coord, "parent", "left", menu, isAnchor1, fullCoordName);
  332. document.addMarkerMenuItem (2, coord, "parent", "right", menu, isAnchor1, fullCoordName);
  333. }
  334. else
  335. {
  336. document.addMarkerMenuItem (1, coord, "parent", "top", menu, isAnchor1, fullCoordName);
  337. document.addMarkerMenuItem (2, coord, "parent", "bottom", menu, isAnchor1, fullCoordName);
  338. }
  339. menu.addSeparator();
  340. const MarkerList& markerList = document.getMarkerList (coord.isHorizontal());
  341. for (int i = 0; i < markerList.size(); ++i)
  342. document.addMarkerMenuItem (100 + i, coord, markerList.getName (markerList.getMarker (i)),
  343. String::empty, menu, isAnchor1, fullCoordName);*/
  344. }
  345. const String DrawableDocument::MarkerList::getChosenMarkerMenuItem (const RelativeCoordinate& coord, int i) const
  346. {
  347. /* if (i == 1) return coord.isHorizontal() ? "parent.left" : "parent.top";
  348. if (i == 2) return coord.isHorizontal() ? "parent.right" : "parent.bottom";
  349. const MarkerList& markerList = document.getMarkerList (coord.isHorizontal());
  350. if (i >= 100 && i < 10000)
  351. return markerList.getName (markerList.getMarker (i - 100));
  352. jassertfalse;*/
  353. return String::empty;
  354. }
  355. UndoManager* DrawableDocument::MarkerList::getUndoManager() const
  356. {
  357. return document.getUndoManager();
  358. }
  359. const String DrawableDocument::MarkerList::getNonexistentMarkerName (const String& name)
  360. {
  361. return document.getNonexistentMarkerName (name);
  362. }
  363. const String DrawableDocument::getNonexistentMarkerName (const String& name)
  364. {
  365. String n (CodeHelpers::makeValidIdentifier (name, false, true, false));
  366. int suffix = 2;
  367. while (markersX->getMarkerNamed (n).isValid() || markersY->getMarkerNamed (n).isValid())
  368. n = n.trimCharactersAtEnd ("0123456789") + String (suffix++);
  369. return n;
  370. }
  371. void DrawableDocument::MarkerList::renameAnchor (const String& oldName, const String& newName)
  372. {
  373. document.renameAnchor (oldName, newName);
  374. }
  375. void DrawableDocument::renameAnchor (const String& oldName, const String& newName)
  376. {
  377. }