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.

453 lines
14KB

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