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.

412 lines
13KB

  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. //==============================================================================
  20. static const char* const drawableTag = "DRAWABLE";
  21. static const char* const markersGroupXTag = "MARKERS_X";
  22. static const char* const markersGroupYTag = "MARKERS_Y";
  23. //==============================================================================
  24. DrawableDocument::DrawableDocument (Project* project_)
  25. : project (project_),
  26. root (drawableTag),
  27. saveAsXml (true),
  28. needsSaving (false)
  29. {
  30. DrawableComposite dc;
  31. root.addChild (dc.createValueTree(), -1, 0);
  32. setName ("Drawable");
  33. checkRootObject();
  34. root.addListener (this);
  35. }
  36. DrawableDocument::~DrawableDocument()
  37. {
  38. root.removeListener (this);
  39. }
  40. ValueTree DrawableDocument::getRootDrawableNode() const
  41. {
  42. return root.getChild (0);
  43. }
  44. void DrawableDocument::checkRootObject()
  45. {
  46. if (markersX == 0)
  47. markersX = new MarkerList (*this, true);
  48. if (markersY == 0)
  49. markersY = new MarkerList (*this, false);
  50. if ((int) getCanvasWidth().getValue() <= 0)
  51. getCanvasWidth() = 500;
  52. if ((int) getCanvasHeight().getValue() <= 0)
  53. getCanvasHeight() = 500;
  54. }
  55. const String DrawableDocument::getIdFor (const ValueTree& object)
  56. {
  57. return object ["id"];
  58. }
  59. //==============================================================================
  60. void DrawableDocument::setName (const String& name)
  61. {
  62. root.setProperty ("name", name, getUndoManager());
  63. }
  64. const String DrawableDocument::getName() const
  65. {
  66. return root ["name"];
  67. }
  68. void DrawableDocument::addMissingIds (ValueTree tree) const
  69. {
  70. if (! tree.hasProperty ("id"))
  71. tree.setProperty ("id", createAlphaNumericUID(), 0);
  72. for (int i = tree.getNumChildren(); --i >= 0;)
  73. addMissingIds (tree.getChild(i));
  74. }
  75. bool DrawableDocument::hasChangedSinceLastSave() const
  76. {
  77. return needsSaving;
  78. }
  79. bool DrawableDocument::reload (const File& drawableFile)
  80. {
  81. ScopedPointer <InputStream> stream (drawableFile.createInputStream());
  82. if (stream != 0 && load (*stream))
  83. {
  84. checkRootObject();
  85. undoManager.clearUndoHistory();
  86. needsSaving = false;
  87. return true;
  88. }
  89. return false;
  90. }
  91. bool DrawableDocument::save (const File& drawableFile)
  92. {
  93. TemporaryFile tempFile (drawableFile);
  94. ScopedPointer <OutputStream> out (tempFile.getFile().createOutputStream());
  95. if (out == 0)
  96. return false;
  97. save (*out);
  98. needsSaving = ! tempFile.overwriteTargetFileWithTemporary();
  99. return ! needsSaving;
  100. }
  101. void DrawableDocument::save (OutputStream& output)
  102. {
  103. if (saveAsXml)
  104. {
  105. ScopedPointer <XmlElement> xml (root.createXml());
  106. jassert (xml != 0);
  107. if (xml != 0)
  108. xml->writeToStream (output, String::empty, false, false);
  109. }
  110. else
  111. {
  112. root.writeToStream (output);
  113. }
  114. }
  115. bool DrawableDocument::load (InputStream& input)
  116. {
  117. int64 originalPos = input.getPosition();
  118. ValueTree loadedTree ("dummy");
  119. XmlDocument xmlDoc (input.readEntireStreamAsString());
  120. ScopedPointer <XmlElement> xml (xmlDoc.getDocumentElement());
  121. if (xml != 0)
  122. {
  123. loadedTree = ValueTree::fromXml (*xml);
  124. }
  125. else
  126. {
  127. input.setPosition (originalPos);
  128. loadedTree = ValueTree::readFromStream (input);
  129. }
  130. if (loadedTree.hasType (drawableTag))
  131. {
  132. addMissingIds (loadedTree);
  133. root.removeListener (this);
  134. root = loadedTree;
  135. root.addListener (this);
  136. valueTreeParentChanged (loadedTree);
  137. needsSaving = false;
  138. undoManager.clearUndoHistory();
  139. return true;
  140. }
  141. return false;
  142. }
  143. void DrawableDocument::changed()
  144. {
  145. needsSaving = true;
  146. sendChangeMessage (this);
  147. }
  148. //==============================================================================
  149. static const Colour getRandomColour()
  150. {
  151. return Colours::red.withHue (Random::getSystemRandom().nextFloat());
  152. }
  153. void DrawableDocument::addDrawable (Drawable& d)
  154. {
  155. DrawableComposite dc;
  156. dc.insertDrawable (d.createCopy());
  157. ValueTree dcNode (dc.createValueTree());
  158. ValueTree subNode (dcNode.getChild(0));
  159. dcNode.removeChild (subNode, 0);
  160. addMissingIds (subNode);
  161. getRootDrawableNode().addChild (subNode, -1, getUndoManager());
  162. }
  163. void DrawableDocument::addRectangle()
  164. {
  165. Path p;
  166. p.addRectangle ((float) Random::getSystemRandom().nextInt (500),
  167. (float) Random::getSystemRandom().nextInt (500),
  168. 100.0f, 100.0f);
  169. DrawablePath d;
  170. d.setPath (p);
  171. d.setFill (FillType (getRandomColour()));
  172. addDrawable (d);
  173. }
  174. void DrawableDocument::addCircle()
  175. {
  176. Path p;
  177. p.addEllipse ((float) Random::getSystemRandom().nextInt (500),
  178. (float) Random::getSystemRandom().nextInt (500),
  179. 100.0f, 100.0f);
  180. DrawablePath d;
  181. d.setPath (p);
  182. d.setFill (FillType (getRandomColour()));
  183. addDrawable (d);
  184. }
  185. void DrawableDocument::addImage (const File& imageFile)
  186. {
  187. jassertfalse
  188. DrawableImage d;
  189. addDrawable (d);
  190. }
  191. //==============================================================================
  192. void DrawableDocument::valueTreePropertyChanged (ValueTree& tree, const Identifier& name)
  193. {
  194. changed();
  195. }
  196. void DrawableDocument::valueTreeChildrenChanged (ValueTree& tree)
  197. {
  198. changed();
  199. }
  200. void DrawableDocument::valueTreeParentChanged (ValueTree& tree)
  201. {
  202. changed();
  203. }
  204. //==============================================================================
  205. const Coordinate DrawableDocument::findMarker (const String& name, bool isHorizontal) const
  206. {
  207. if (name == Coordinate::parentRightMarkerName) return Coordinate ((double) getCanvasWidth().getValue(), isHorizontal);
  208. if (name == Coordinate::parentBottomMarkerName) return Coordinate ((double) getCanvasHeight().getValue(), isHorizontal);
  209. if (name.containsChar ('.'))
  210. {
  211. const String compName (name.upToFirstOccurrenceOf (".", false, false).trim());
  212. const String edge (name.fromFirstOccurrenceOf (".", false, false).trim());
  213. if (compName.isNotEmpty() && edge.isNotEmpty())
  214. {
  215. /* const ValueTree comp (getComponentWithMemberName (compName));
  216. if (comp.isValid())
  217. {
  218. const RectangleCoordinates coords (getCoordsFor (comp));
  219. if (edge == "left") return coords.left;
  220. if (edge == "right") return coords.right;
  221. if (edge == "top") return coords.top;
  222. if (edge == "bottom") return coords.bottom;
  223. }*/
  224. }
  225. }
  226. const ValueTree marker (getMarkerList (isHorizontal).getMarkerNamed (name));
  227. if (marker.isValid())
  228. return getMarkerList (isHorizontal).getCoordinate (marker);
  229. return Coordinate (isHorizontal);
  230. }
  231. DrawableDocument::MarkerList::MarkerList (DrawableDocument& document_, bool isX_)
  232. : MarkerListBase (document_.getRoot().getChildWithName (isX_ ? markersGroupXTag : markersGroupYTag), isX_),
  233. document (document_)
  234. {
  235. }
  236. const Coordinate DrawableDocument::MarkerList::findMarker (const String& name, bool isHorizontal_) const
  237. {
  238. if (isHorizontal_ == isX)
  239. {
  240. if (name == Coordinate::parentRightMarkerName) return Coordinate ((double) document.getCanvasWidth().getValue(), isX);
  241. if (name == Coordinate::parentBottomMarkerName) return Coordinate ((double) document.getCanvasHeight().getValue(), isX);
  242. const ValueTree marker (document.getMarkerList (isX).getMarkerNamed (name));
  243. if (marker.isValid())
  244. return document.getMarkerList (isX).getCoordinate (marker);
  245. }
  246. return Coordinate (isX);
  247. }
  248. bool DrawableDocument::MarkerList::createProperties (Array <PropertyComponent*>& props, const String& itemId)
  249. {
  250. ValueTree marker (group.getChildWithProperty (getIdProperty(), itemId));
  251. if (marker.isValid())
  252. {
  253. props.add (new TextPropertyComponent (getNameAsValue (marker), "Marker Name", 256, false));
  254. // props.add (new MarkerPositionComponent (document, "Position", marker,
  255. // marker.getPropertyAsValue (markerPosProperty, document.getUndoManager()),
  256. // contains (marker)));
  257. return true;
  258. }
  259. return false;
  260. }
  261. void DrawableDocument::addMarkerMenuItem (int i, const Coordinate& coord, const String& name, PopupMenu& menu,
  262. bool isAnchor1, const String& fullCoordName)
  263. {
  264. Coordinate requestedCoord (findMarker (name, coord.isHorizontal()));
  265. // menu.addItem (i, name,
  266. // ! (name == fullCoordName || requestedCoord.referencesIndirectly (fullCoordName, *this)),
  267. // name == (isAnchor1 ? coord.getAnchor1() : coord.getAnchor2()));
  268. }
  269. void DrawableDocument::MarkerList::addMarkerMenuItems (const ValueTree& markerState, const Coordinate& coord, PopupMenu& menu, bool isAnchor1)
  270. {
  271. const String fullCoordName (getName (markerState));
  272. if (coord.isHorizontal())
  273. {
  274. document.addMarkerMenuItem (1, coord, Coordinate::parentLeftMarkerName, menu, isAnchor1, fullCoordName);
  275. document.addMarkerMenuItem (2, coord, Coordinate::parentRightMarkerName, menu, isAnchor1, fullCoordName);
  276. }
  277. else
  278. {
  279. document.addMarkerMenuItem (1, coord, Coordinate::parentTopMarkerName, menu, isAnchor1, fullCoordName);
  280. document.addMarkerMenuItem (2, coord, Coordinate::parentBottomMarkerName, menu, isAnchor1, fullCoordName);
  281. }
  282. menu.addSeparator();
  283. const MarkerList& markerList = document.getMarkerList (coord.isHorizontal());
  284. for (int i = 0; i < markerList.size(); ++i)
  285. document.addMarkerMenuItem (100 + i, coord, markerList.getName (markerList.getMarker (i)), menu, isAnchor1, fullCoordName);
  286. }
  287. const String DrawableDocument::MarkerList::getChosenMarkerMenuItem (const Coordinate& coord, int i) const
  288. {
  289. if (i == 1) return coord.isHorizontal() ? Coordinate::parentLeftMarkerName : Coordinate::parentTopMarkerName;
  290. if (i == 2) return coord.isHorizontal() ? Coordinate::parentRightMarkerName : Coordinate::parentBottomMarkerName;
  291. const MarkerList& markerList = document.getMarkerList (coord.isHorizontal());
  292. if (i >= 100 && i < 10000)
  293. return markerList.getName (markerList.getMarker (i - 100));
  294. jassertfalse;
  295. return String::empty;
  296. }
  297. UndoManager* DrawableDocument::MarkerList::getUndoManager() const
  298. {
  299. return document.getUndoManager();
  300. }
  301. const String DrawableDocument::MarkerList::getNonexistentMarkerName (const String& name)
  302. {
  303. return document.getNonexistentMarkerName (name);
  304. }
  305. const String DrawableDocument::getNonexistentMarkerName (const String& name)
  306. {
  307. String n (CodeHelpers::makeValidIdentifier (name, false, true, false));
  308. int suffix = 2;
  309. while (markersX->getMarkerNamed (n).isValid() || markersY->getMarkerNamed (n).isValid())
  310. n = n.trimCharactersAtEnd ("0123456789") + String (suffix++);
  311. return n;
  312. }
  313. void DrawableDocument::MarkerList::renameAnchor (const String& oldName, const String& newName)
  314. {
  315. document.renameAnchor (oldName, newName);
  316. }
  317. void DrawableDocument::renameAnchor (const String& oldName, const String& newName)
  318. {
  319. }