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.

863 lines
29KB

  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_ComponentDocument.h"
  19. #include "Types/jucer_ComponentTypeManager.h"
  20. #include "../../utility/jucer_CoordinatePropertyComponent.h"
  21. //==============================================================================
  22. static const Identifier componentDocumentTag ("COMPONENT");
  23. static const Identifier componentGroupTag ("COMPONENTS");
  24. static const Identifier markersGroupXTag ("MARKERS_X");
  25. static const Identifier markersGroupYTag ("MARKERS_Y");
  26. static const char* const metadataTagStart = "JUCER_" "COMPONENT_METADATA_START"; // written like this to avoid thinking this file is a component!
  27. static const char* const metadataTagEnd = "JUCER_" "COMPONENT_METADATA_END";
  28. const Identifier ComponentDocument::idProperty ("id");
  29. const Identifier ComponentDocument::compBoundsProperty ("position");
  30. const Identifier ComponentDocument::memberNameProperty ("memberName");
  31. const Identifier ComponentDocument::compNameProperty ("name");
  32. const Identifier ComponentDocument::compTooltipProperty ("tooltip");
  33. const Identifier ComponentDocument::compFocusOrderProperty ("focusOrder");
  34. const Identifier ComponentDocument::jucerIDProperty ("jucerID");
  35. //==============================================================================
  36. ComponentDocument::ComponentDocument (Project* project_, const File& cppFile_)
  37. : project (project_),
  38. cppFile (cppFile_),
  39. root (componentDocumentTag),
  40. changedSinceSaved (false),
  41. usingTemporaryCanvasSize (false)
  42. {
  43. checkRootObject();
  44. root.addListener (this);
  45. }
  46. ComponentDocument::ComponentDocument (const ComponentDocument& other)
  47. : project (other.project),
  48. cppFile (other.cppFile),
  49. root (other.root),
  50. changedSinceSaved (false)
  51. {
  52. checkRootObject();
  53. root.addListener (this);
  54. }
  55. ComponentDocument::~ComponentDocument()
  56. {
  57. root.removeListener (this);
  58. }
  59. void ComponentDocument::beginNewTransaction()
  60. {
  61. undoManager.beginNewTransaction();
  62. }
  63. void ComponentDocument::changed()
  64. {
  65. changedSinceSaved = true;
  66. }
  67. void ComponentDocument::valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, const Identifier& property)
  68. {
  69. changed();
  70. }
  71. void ComponentDocument::valueTreeChildrenChanged (ValueTree& treeWhoseChildHasChanged)
  72. {
  73. changed();
  74. }
  75. void ComponentDocument::valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged)
  76. {
  77. changed();
  78. }
  79. bool ComponentDocument::isComponentFile (const File& file)
  80. {
  81. if (! file.hasFileExtension (".cpp"))
  82. return false;
  83. InputStream* in = file.createInputStream();
  84. if (in != 0)
  85. {
  86. BufferedInputStream buf (in, 8192, true);
  87. while (! buf.isExhausted())
  88. if (buf.readNextLine().contains (metadataTagStart))
  89. return true;
  90. }
  91. return false;
  92. }
  93. const String ComponentDocument::getCppTemplate() const { return String (BinaryData::jucer_ComponentTemplate_cpp); }
  94. const String ComponentDocument::getHeaderTemplate() const { return String (BinaryData::jucer_ComponentTemplate_h); }
  95. const String ComponentDocument::getCppContent()
  96. {
  97. MemoryOutputStream cpp, header;
  98. writeCode (cpp, header);
  99. return cpp.toUTF8();
  100. }
  101. const String ComponentDocument::getHeaderContent()
  102. {
  103. MemoryOutputStream cpp, header;
  104. writeCode (cpp, header);
  105. return header.toUTF8();
  106. }
  107. void ComponentDocument::writeCode (OutputStream& cpp, OutputStream& header)
  108. {
  109. CodeGenerator codeGen;
  110. codeGen.className = getClassName().toString();
  111. codeGen.parentClasses = "public Component";
  112. {
  113. MemoryOutputStream stateStream (1024, 1024, &codeGen.componentStateData);
  114. root.writeToStream (stateStream);
  115. }
  116. for (int i = 0; i < getNumComponents(); ++i)
  117. {
  118. ComponentTypeInstance item (*this, getComponent (i));
  119. item.createCode (codeGen);
  120. }
  121. {
  122. MemoryOutputStream metaData;
  123. writeMetadata (metaData);
  124. codeGen.jucerMetadata = metaData.toUTF8();
  125. }
  126. {
  127. String code (getCppTemplate());
  128. String oldContent;
  129. codeGen.applyToCode (code, cppFile, false, project);
  130. customCode.applyTo (code);
  131. cpp << code;
  132. }
  133. {
  134. String code (getHeaderTemplate());
  135. String oldContent;
  136. codeGen.applyToCode (code, cppFile.withFileExtension (".h"), false, project);
  137. customCode.applyTo (code);
  138. header << code;
  139. }
  140. }
  141. void ComponentDocument::writeMetadata (OutputStream& out)
  142. {
  143. out << metadataTagStart << newLine << newLine;
  144. ScopedPointer<XmlElement> xml (root.createXml());
  145. jassert (xml != 0);
  146. if (xml != 0)
  147. xml->writeToStream (out, String::empty, false, false);
  148. out << newLine << metadataTagEnd;
  149. }
  150. bool ComponentDocument::save()
  151. {
  152. MemoryOutputStream cpp, header;
  153. writeCode (cpp, header);
  154. bool savedOk = FileHelpers::overwriteFileWithNewDataIfDifferent (cppFile, cpp)
  155. && FileHelpers::overwriteFileWithNewDataIfDifferent (cppFile.withFileExtension (".h"), header);
  156. if (savedOk)
  157. changedSinceSaved = false;
  158. return savedOk;
  159. }
  160. bool ComponentDocument::reload()
  161. {
  162. String xmlString;
  163. bool hadMetaDataTags = false;
  164. {
  165. InputStream* in = cppFile.createInputStream();
  166. if (in == 0)
  167. return false;
  168. BufferedInputStream buf (in, 8192, true);
  169. String::Concatenator xml (xmlString);
  170. while (! buf.isExhausted())
  171. {
  172. String line (buf.readNextLine());
  173. if (line.contains (metadataTagStart))
  174. {
  175. while (! buf.isExhausted())
  176. {
  177. line = buf.readNextLine();
  178. if (line.contains (metadataTagEnd))
  179. {
  180. hadMetaDataTags = true;
  181. break;
  182. }
  183. xml.append (line);
  184. xml.append (newLine);
  185. }
  186. break;
  187. }
  188. }
  189. }
  190. XmlDocument doc (xmlString);
  191. ScopedPointer<XmlElement> xml (doc.getDocumentElement());
  192. if (xml == 0 && hadMetaDataTags)
  193. xml = new XmlElement (componentDocumentTag.toString());
  194. if (xml != 0 && xml->hasTagName (componentDocumentTag.toString()))
  195. {
  196. ValueTree newTree (ValueTree::fromXml (*xml));
  197. if (newTree.isValid())
  198. {
  199. root = newTree;
  200. markersX = 0;
  201. markersY = 0;
  202. checkRootObject();
  203. customCode.reloadFrom (cppFile.loadFileAsString());
  204. root.addChild (ValueTree ("dummy"), 0, 0);
  205. root.removeChild (root.getChildWithName("dummy"), 0);
  206. undoManager.clearUndoHistory();
  207. changedSinceSaved = false;
  208. return true;
  209. }
  210. }
  211. return false;
  212. }
  213. bool ComponentDocument::hasChangedSinceLastSave()
  214. {
  215. return changedSinceSaved || customCode.needsSaving();
  216. }
  217. void ComponentDocument::createSubTreeIfNotThere (const Identifier& name)
  218. {
  219. if (! root.getChildWithName (name).isValid())
  220. root.addChild (ValueTree (name), -1, 0);
  221. }
  222. void ComponentDocument::checkRootObject()
  223. {
  224. jassert (root.hasType (componentDocumentTag));
  225. if (root [idProperty].toString().isEmpty())
  226. root.setProperty (idProperty, createAlphaNumericUID(), 0);
  227. createSubTreeIfNotThere (componentGroupTag);
  228. createSubTreeIfNotThere (markersGroupXTag);
  229. createSubTreeIfNotThere (markersGroupYTag);
  230. if (markersX == 0)
  231. markersX = new MarkerList (*this, true);
  232. if (markersY == 0)
  233. markersY = new MarkerList (*this, false);
  234. if (getClassName().toString().isEmpty())
  235. getClassName() = "NewComponent";
  236. if ((int) getCanvasWidth().getValue() <= 0)
  237. getCanvasWidth() = 640;
  238. if ((int) getCanvasHeight().getValue() <= 0)
  239. getCanvasHeight() = 480;
  240. if (! root.hasProperty (Ids::background))
  241. getBackgroundColour() = Colours::white.toString();
  242. }
  243. void ComponentDocument::setUsingTemporaryCanvasSize (bool b)
  244. {
  245. tempCanvasWidth = root.getProperty (Ids::width);
  246. tempCanvasHeight = root.getProperty (Ids::height);
  247. usingTemporaryCanvasSize = b;
  248. }
  249. Value ComponentDocument::getCanvasWidth() const
  250. {
  251. return usingTemporaryCanvasSize ? tempCanvasWidth : getRootValueNonUndoable (Ids::width);
  252. }
  253. Value ComponentDocument::getCanvasHeight() const
  254. {
  255. return usingTemporaryCanvasSize ? tempCanvasHeight : getRootValueNonUndoable (Ids::height);
  256. }
  257. Value ComponentDocument::getBackgroundColour() const
  258. {
  259. return getRootValueUndoable (Ids::background);
  260. }
  261. //==============================================================================
  262. const int menuItemOffset = 0x63451fa4;
  263. void ComponentDocument::addNewComponentMenuItems (PopupMenu& menu) const
  264. {
  265. const StringArray displayNames (ComponentTypeManager::getInstance()->getDisplayNames());
  266. for (int i = 0; i < displayNames.size(); ++i)
  267. menu.addItem (i + menuItemOffset, "New " + displayNames[i]);
  268. }
  269. const ValueTree ComponentDocument::performNewComponentMenuItem (int menuResultCode)
  270. {
  271. const StringArray displayNames (ComponentTypeManager::getInstance()->getDisplayNames());
  272. if (menuResultCode >= menuItemOffset && menuResultCode < menuItemOffset + displayNames.size())
  273. {
  274. ComponentTypeHandler* handler = ComponentTypeManager::getInstance()->getHandler (menuResultCode - menuItemOffset);
  275. jassert (handler != 0);
  276. if (handler != 0)
  277. {
  278. ValueTree state (handler->getValueTreeType());
  279. state.setProperty (idProperty, createAlphaNumericUID(), 0);
  280. ComponentTypeInstance comp (*this, state);
  281. handler->initialiseNewItem (comp);
  282. getComponentGroup().addChild (state, -1, getUndoManager());
  283. return state;
  284. }
  285. }
  286. return ValueTree::invalid;
  287. }
  288. void ComponentDocument::componentDoubleClicked (const MouseEvent& e, const ValueTree& state)
  289. {
  290. ComponentTypeInstance item (*this, state);
  291. item.getHandler()->itemDoubleClicked (e, item);
  292. }
  293. void ComponentDocument::updateComponentsIn (Component* compHolder)
  294. {
  295. int i;
  296. for (i = compHolder->getNumChildComponents(); --i >= 0;)
  297. {
  298. Component* c = compHolder->getChildComponent (i);
  299. if (! containsComponent (c))
  300. delete c;
  301. }
  302. Array <Component*> componentsInOrder;
  303. const int num = getNumComponents();
  304. for (i = 0; i < num; ++i)
  305. {
  306. const ValueTree v (getComponent (i));
  307. Component* c = findComponentForState (compHolder, v);
  308. if (c == 0)
  309. compHolder->addAndMakeVisible (c = createComponent (i));
  310. else
  311. updateComponent (c);
  312. componentsInOrder.add (c);
  313. }
  314. // Make sure the z-order is correct..
  315. if (num > 0)
  316. {
  317. componentsInOrder.getLast()->toFront (false);
  318. for (i = num - 1; --i >= 0;)
  319. componentsInOrder.getUnchecked(i)->toBehind (componentsInOrder.getUnchecked (i + 1));
  320. }
  321. }
  322. //==============================================================================
  323. ValueTree ComponentDocument::getComponentGroup() const
  324. {
  325. return root.getChildWithName (componentGroupTag);
  326. }
  327. int ComponentDocument::getNumComponents() const
  328. {
  329. return getComponentGroup().getNumChildren();
  330. }
  331. const ValueTree ComponentDocument::getComponent (int index) const
  332. {
  333. return getComponentGroup().getChild (index);
  334. }
  335. const ValueTree ComponentDocument::getComponentWithMemberName (const String& name) const
  336. {
  337. return getComponentGroup().getChildWithProperty (memberNameProperty, name);
  338. }
  339. const ValueTree ComponentDocument::getComponentWithID (const String& uid) const
  340. {
  341. return getComponentGroup().getChildWithProperty (idProperty, uid);
  342. }
  343. Component* ComponentDocument::createComponent (int index)
  344. {
  345. const ValueTree v (getComponentGroup().getChild (index));
  346. if (v.isValid())
  347. {
  348. Component* c = ComponentTypeManager::getInstance()->createFromStoredType (*this, v);
  349. c->getProperties().set (jucerIDProperty, v[idProperty]);
  350. jassert (getJucerIDFor (c).isNotEmpty());
  351. return c;
  352. }
  353. return 0;
  354. }
  355. //==============================================================================
  356. const RelativeCoordinate ComponentDocument::findNamedCoordinate (const String& objectName, const String& edge) const
  357. {
  358. if (objectName == RelativeCoordinate::Strings::parent)
  359. {
  360. if (edge == RelativeCoordinate::Strings::right) return RelativeCoordinate ((double) getCanvasWidth().getValue(), true);
  361. if (edge == RelativeCoordinate::Strings::bottom) return RelativeCoordinate ((double) getCanvasHeight().getValue(), false);
  362. }
  363. if (objectName.isNotEmpty() && edge.isNotEmpty())
  364. {
  365. const ValueTree comp (getComponentWithMemberName (objectName));
  366. if (comp.isValid())
  367. {
  368. const RelativeRectangle coords (getCoordsFor (comp));
  369. if (edge == RelativeCoordinate::Strings::left) return coords.left;
  370. if (edge == RelativeCoordinate::Strings::right) return coords.right;
  371. if (edge == RelativeCoordinate::Strings::top) return coords.top;
  372. if (edge == RelativeCoordinate::Strings::bottom) return coords.bottom;
  373. }
  374. }
  375. {
  376. const ValueTree marker (getMarkerListX().getMarkerNamed (objectName));
  377. if (marker.isValid())
  378. return getMarkerListX().getCoordinate (marker);
  379. }
  380. {
  381. const ValueTree marker (getMarkerListY().getMarkerNamed (objectName));
  382. if (marker.isValid())
  383. return getMarkerListY().getCoordinate (marker);
  384. }
  385. return RelativeCoordinate();
  386. }
  387. const RelativeRectangle ComponentDocument::getCoordsFor (const ValueTree& state) const
  388. {
  389. return RelativeRectangle (state [compBoundsProperty]);
  390. }
  391. void ComponentDocument::setCoordsFor (ValueTree& state, const RelativeRectangle& pr)
  392. {
  393. const String newBoundsString (pr.toString());
  394. if (state[compBoundsProperty] != newBoundsString)
  395. state.setProperty (compBoundsProperty, newBoundsString, getUndoManager());
  396. }
  397. const String ComponentDocument::getNonexistentMemberName (String name)
  398. {
  399. String n (CodeHelpers::makeValidIdentifier (name, false, true, false));
  400. int suffix = 2;
  401. while (markersX->getMarkerNamed (n).isValid() || markersY->getMarkerNamed (n).isValid()
  402. || getComponentWithMemberName (n).isValid())
  403. n = n.trimCharactersAtEnd ("0123456789") + String (suffix++);
  404. return n;
  405. }
  406. void ComponentDocument::renameAnchor (const String& oldName, const String& newName)
  407. {
  408. int i;
  409. for (i = getNumComponents(); --i >= 0;)
  410. {
  411. ValueTree v (getComponent(i));
  412. RelativeRectangle coords (getCoordsFor (v));
  413. coords.renameAnchorIfUsed (oldName, newName, this);
  414. setCoordsFor (v, coords);
  415. }
  416. markersX->renameAnchorInMarkers (oldName, newName);
  417. markersY->renameAnchorInMarkers (oldName, newName);
  418. }
  419. void ComponentDocument::addMarkerMenuItem (int i, const RelativeCoordinate& coord,
  420. const String& objectName, const String& edge, PopupMenu& menu,
  421. bool isAnchor1, const String& fullCoordName)
  422. {
  423. RelativeCoordinate requestedCoord (findNamedCoordinate (objectName, edge));
  424. String name (objectName);
  425. if (edge.isNotEmpty())
  426. name << '.' << edge;
  427. menu.addItem (i, name,
  428. ! (name == fullCoordName || requestedCoord.references (fullCoordName, this)),
  429. name == (isAnchor1 ? coord.getAnchorName1() : coord.getAnchorName2()));
  430. }
  431. void ComponentDocument::addComponentMarkerMenuItems (const ValueTree& componentState, const String& coordName,
  432. RelativeCoordinate& coord, PopupMenu& menu, bool isAnchor1, bool isHorizontal)
  433. {
  434. const String componentName (componentState [memberNameProperty].toString());
  435. const String fullCoordName (componentName + "." + coordName);
  436. if (isHorizontal)
  437. {
  438. addMarkerMenuItem (1, coord, RelativeCoordinate::Strings::parent, RelativeCoordinate::Strings::left, menu, isAnchor1, fullCoordName);
  439. addMarkerMenuItem (2, coord, RelativeCoordinate::Strings::parent, RelativeCoordinate::Strings::right, menu, isAnchor1, fullCoordName);
  440. menu.addSeparator();
  441. addMarkerMenuItem (3, coord, componentName, RelativeCoordinate::Strings::left, menu, isAnchor1, fullCoordName);
  442. addMarkerMenuItem (4, coord, componentName, RelativeCoordinate::Strings::right, menu, isAnchor1, fullCoordName);
  443. }
  444. else
  445. {
  446. addMarkerMenuItem (1, coord, RelativeCoordinate::Strings::parent, RelativeCoordinate::Strings::top, menu, isAnchor1, fullCoordName);
  447. addMarkerMenuItem (2, coord, RelativeCoordinate::Strings::parent, RelativeCoordinate::Strings::bottom, menu, isAnchor1, fullCoordName);
  448. menu.addSeparator();
  449. addMarkerMenuItem (3, coord, componentName, RelativeCoordinate::Strings::top, menu, isAnchor1, fullCoordName);
  450. addMarkerMenuItem (4, coord, componentName, RelativeCoordinate::Strings::bottom, menu, isAnchor1, fullCoordName);
  451. }
  452. menu.addSeparator();
  453. const MarkerList& markerList = getMarkerList (isHorizontal);
  454. int i;
  455. for (i = 0; i < markerList.size(); ++i)
  456. addMarkerMenuItem (100 + i, coord, markerList.getName (markerList.getMarker (i)), String::empty, menu, isAnchor1, fullCoordName);
  457. menu.addSeparator();
  458. for (i = 0; i < getNumComponents(); ++i)
  459. {
  460. const String compName (getComponent (i) [memberNameProperty].toString());
  461. if (compName != componentName)
  462. {
  463. if (isHorizontal)
  464. {
  465. addMarkerMenuItem (10000 + i * 4, coord, compName, RelativeCoordinate::Strings::left, menu, isAnchor1, fullCoordName);
  466. addMarkerMenuItem (10001 + i * 4, coord, compName, RelativeCoordinate::Strings::right, menu, isAnchor1, fullCoordName);
  467. }
  468. else
  469. {
  470. addMarkerMenuItem (10002 + i * 4, coord, compName, RelativeCoordinate::Strings::top, menu, isAnchor1, fullCoordName);
  471. addMarkerMenuItem (10003 + i * 4, coord, compName, RelativeCoordinate::Strings::bottom, menu, isAnchor1, fullCoordName);
  472. }
  473. }
  474. }
  475. }
  476. const String ComponentDocument::getChosenMarkerMenuItem (const ValueTree& componentState, RelativeCoordinate& coord, int i, bool isHorizontal) const
  477. {
  478. const String componentName (componentState [memberNameProperty].toString());
  479. if (i == 1) return isHorizontal ? RelativeCoordinate::Strings::parentLeft : RelativeCoordinate::Strings::parentTop;
  480. if (i == 2) return isHorizontal ? RelativeCoordinate::Strings::parentRight : RelativeCoordinate::Strings::parentBottom;
  481. if (i == 3) return componentName + (isHorizontal ? ".left" : ".top");
  482. if (i == 4) return componentName + (isHorizontal ? ".right" : ".bottom");
  483. const MarkerList& markerList = getMarkerList (isHorizontal);
  484. if (i >= 100 && i < 10000)
  485. return markerList.getName (markerList.getMarker (i - 100));
  486. if (i >= 10000)
  487. {
  488. const String compName (getComponent ((i - 10000) / 4) [memberNameProperty].toString());
  489. switch (i & 3)
  490. {
  491. case 0: return compName + ".left";
  492. case 1: return compName + ".right";
  493. case 2: return compName + ".top";
  494. case 3: return compName + ".bottom";
  495. default: break;
  496. }
  497. }
  498. jassertfalse;
  499. return String::empty;
  500. }
  501. void ComponentDocument::updateComponent (Component* comp)
  502. {
  503. const ValueTree v (getComponentState (comp));
  504. if (v.isValid())
  505. {
  506. ComponentTypeInstance item (*this, v);
  507. item.updateComponent (comp);
  508. }
  509. }
  510. bool ComponentDocument::containsComponent (Component* comp) const
  511. {
  512. const ValueTree comps (getComponentGroup());
  513. for (int i = 0; i < comps.getNumChildren(); ++i)
  514. if (isStateForComponent (comps.getChild(i), comp))
  515. return true;
  516. return false;
  517. }
  518. const ValueTree ComponentDocument::getComponentState (Component* comp) const
  519. {
  520. jassert (comp != 0);
  521. return getComponentGroup().getChildWithProperty (idProperty, getJucerIDFor (comp));
  522. }
  523. Component* ComponentDocument::findComponentForState (Component* compHolder, const ValueTree& state)
  524. {
  525. const String compId (state [idProperty].toString());
  526. for (int i = compHolder->getNumChildComponents(); --i >= 0;)
  527. {
  528. Component* const c = compHolder->getChildComponent (i);
  529. if (getJucerIDFor (c) == compId)
  530. return c;
  531. }
  532. return 0;
  533. }
  534. bool ComponentDocument::isStateForComponent (const ValueTree& storedState, Component* comp) const
  535. {
  536. jassert (comp != 0);
  537. jassert (! storedState [idProperty].isVoid());
  538. return storedState [idProperty] == getJucerIDFor (comp);
  539. }
  540. void ComponentDocument::removeComponent (const ValueTree& state)
  541. {
  542. jassert (state.isAChildOf (getComponentGroup()));
  543. renameAnchor (state [memberNameProperty], String::empty);
  544. getComponentGroup().removeChild (state, getUndoManager());
  545. }
  546. //==============================================================================
  547. ComponentDocument::MarkerList::MarkerList (ComponentDocument& document_, const bool isX_)
  548. : MarkerListBase (isX_),
  549. document (document_),
  550. group (document_.getRoot().getChildWithName (isX_ ? markersGroupXTag : markersGroupYTag))
  551. {
  552. jassert (group.isValid());
  553. jassert (group.isAChildOf (document_.getRoot()));
  554. }
  555. UndoManager* ComponentDocument::MarkerList::getUndoManager() const
  556. {
  557. return document.getUndoManager();
  558. }
  559. const String ComponentDocument::MarkerList::getNonexistentMarkerName (const String& name)
  560. {
  561. return document.getNonexistentMemberName (name);
  562. }
  563. void ComponentDocument::MarkerList::renameAnchor (const String& oldName, const String& newName)
  564. {
  565. document.renameAnchor (oldName, newName);
  566. }
  567. void ComponentDocument::MarkerList::createMarker (const String& name, double position)
  568. {
  569. ValueTree marker (getMarkerTag());
  570. marker.setProperty (getMarkerNameProperty(), name, 0);
  571. marker.setProperty (getMarkerPosProperty(), RelativeCoordinate (position, isX).toString(), 0);
  572. marker.setProperty (Ids::id_, createAlphaNumericUID(), 0);
  573. group.addChild (marker, -1, getUndoManager());
  574. }
  575. void ComponentDocument::MarkerList::deleteMarker (ValueTree& markerState)
  576. {
  577. renameAnchor (getName (markerState), String::empty);
  578. group.removeChild (markerState, getUndoManager());
  579. }
  580. const RelativeCoordinate ComponentDocument::MarkerList::findNamedCoordinate (const String& objectName, const String& edge) const
  581. {
  582. if (objectName == RelativeCoordinate::Strings::parent)
  583. {
  584. if (edge == RelativeCoordinate::Strings::right) return RelativeCoordinate ((double) document.getCanvasWidth().getValue(), true);
  585. if (edge == RelativeCoordinate::Strings::bottom) return RelativeCoordinate ((double) document.getCanvasHeight().getValue(), false);
  586. }
  587. const ValueTree marker (getMarkerNamed (objectName));
  588. if (marker.isValid())
  589. return getCoordinate (marker);
  590. return RelativeCoordinate();
  591. }
  592. void ComponentDocument::MarkerList::addMarkerMenuItems (const ValueTree& markerState, const RelativeCoordinate& coord, PopupMenu& menu, bool isAnchor1)
  593. {
  594. const String fullCoordName (getName (markerState));
  595. if (isX)
  596. {
  597. document.addMarkerMenuItem (1, coord, RelativeCoordinate::Strings::parent, RelativeCoordinate::Strings::left, menu, isAnchor1, fullCoordName);
  598. document.addMarkerMenuItem (2, coord, RelativeCoordinate::Strings::parent, RelativeCoordinate::Strings::right, menu, isAnchor1, fullCoordName);
  599. }
  600. else
  601. {
  602. document.addMarkerMenuItem (1, coord, RelativeCoordinate::Strings::parent, RelativeCoordinate::Strings::top, menu, isAnchor1, fullCoordName);
  603. document.addMarkerMenuItem (2, coord, RelativeCoordinate::Strings::parent, RelativeCoordinate::Strings::bottom, menu, isAnchor1, fullCoordName);
  604. }
  605. menu.addSeparator();
  606. for (int i = 0; i < size(); ++i)
  607. document.addMarkerMenuItem (100 + i, coord, getName (getMarker (i)),
  608. String::empty, menu, isAnchor1, fullCoordName);
  609. }
  610. const String ComponentDocument::MarkerList::getChosenMarkerMenuItem (const RelativeCoordinate& coord, int i) const
  611. {
  612. if (i == 1) return isX ? "parent.left" : "parent.top";
  613. if (i == 2) return isX ? "parent.right" : "parent.bottom";
  614. if (i >= 100 && i < 10000)
  615. return getName (getMarker (i - 100));
  616. jassertfalse;
  617. return String::empty;
  618. }
  619. //==============================================================================
  620. bool ComponentDocument::MarkerList::createProperties (Array <PropertyComponent*>& props, const String& itemId)
  621. {
  622. ValueTree marker (group.getChildWithProperty (idProperty, itemId));
  623. if (marker.isValid())
  624. {
  625. props.add (new TextPropertyComponent (Value (new MarkerListBase::MarkerNameValueSource (this, getNameAsValue (marker))),
  626. "Marker Name", 256, false));
  627. props.add (new MarkerListBase::PositionPropertyComponent (*this, "Position", marker,
  628. marker.getPropertyAsValue (getMarkerPosProperty(), document.getUndoManager())));
  629. return true;
  630. }
  631. return false;
  632. }
  633. //==============================================================================
  634. bool ComponentDocument::createItemProperties (Array <PropertyComponent*>& props, const String& itemId)
  635. {
  636. ValueTree comp (getComponentWithID (itemId));
  637. if (comp.isValid())
  638. {
  639. ComponentTypeInstance item (*this, comp);
  640. item.createProperties (props);
  641. return true;
  642. }
  643. if (markersX->createProperties (props, itemId)
  644. || markersY->createProperties (props, itemId))
  645. return true;
  646. return false;
  647. }
  648. void ComponentDocument::createItemProperties (Array <PropertyComponent*>& props, const StringArray& selectedItemIds)
  649. {
  650. if (selectedItemIds.size() != 1)
  651. return; //xxx
  652. for (int i = 0; i < selectedItemIds.size(); ++i)
  653. createItemProperties (props, selectedItemIds[i]);
  654. }
  655. //==============================================================================
  656. UndoManager* ComponentDocument::getUndoManager() const
  657. {
  658. return &undoManager;
  659. }
  660. //==============================================================================
  661. const String ComponentDocument::getJucerIDFor (Component* c)
  662. {
  663. if (c == 0)
  664. {
  665. jassertfalse;
  666. return String::empty;
  667. }
  668. jassert (c->getProperties().contains (jucerIDProperty));
  669. return c->getProperties() [jucerIDProperty];
  670. }
  671. //==============================================================================
  672. void ComponentDocument::createClassProperties (Array <PropertyComponent*>& props)
  673. {
  674. props.add (new TextPropertyComponent (getClassName(), "Class Name", 256, false));
  675. props.getLast()->setTooltip ("The C++ class name for the component class.");
  676. props.add (new TextPropertyComponent (getClassDescription(), "Description", 512, false));
  677. props.getLast()->setTooltip ("A freeform description of the component.");
  678. props.add (new SliderPropertyComponent (getCanvasWidth(), "Initial Width", 1.0, 8192.0, 1.0));
  679. props.getLast()->setTooltip ("The initial width of the component when it is created.");
  680. props.add (new SliderPropertyComponent (getCanvasHeight(), "Initial Height", 1.0, 8192.0, 1.0));
  681. props.getLast()->setTooltip ("The initial height of the component when it is created.");
  682. }