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.

407 lines
12KB

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