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.

888 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 ("background"))
  241. getBackgroundColour() = Colours::white.toString();
  242. }
  243. void ComponentDocument::setUsingTemporaryCanvasSize (bool b)
  244. {
  245. tempCanvasWidth = root.getProperty ("width");
  246. tempCanvasHeight = root.getProperty ("height");
  247. usingTemporaryCanvasSize = b;
  248. }
  249. Value ComponentDocument::getCanvasWidth() const
  250. {
  251. return usingTemporaryCanvasSize ? tempCanvasWidth : getRootValueNonUndoable ("width");
  252. }
  253. Value ComponentDocument::getCanvasHeight() const
  254. {
  255. return usingTemporaryCanvasSize ? tempCanvasHeight : getRootValueNonUndoable ("height");
  256. }
  257. Value ComponentDocument::getBackgroundColour() const
  258. {
  259. return getRootValueUndoable ("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->getXmlTag());
  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. Component* ComponentDocument::findComponentForState (Component* compHolder, const ValueTree& state)
  289. {
  290. for (int i = compHolder->getNumChildComponents(); --i >= 0;)
  291. {
  292. Component* const c = compHolder->getChildComponent (i);
  293. if (isStateForComponent (state, c))
  294. return c;
  295. }
  296. return 0;
  297. }
  298. void ComponentDocument::updateComponentsIn (Component* compHolder)
  299. {
  300. int i;
  301. for (i = compHolder->getNumChildComponents(); --i >= 0;)
  302. {
  303. Component* c = compHolder->getChildComponent (i);
  304. if (! containsComponent (c))
  305. delete c;
  306. }
  307. Array <Component*> componentsInOrder;
  308. const int num = getNumComponents();
  309. for (i = 0; i < num; ++i)
  310. {
  311. const ValueTree v (getComponent (i));
  312. Component* c = findComponentForState (compHolder, v);
  313. if (c == 0)
  314. {
  315. c = createComponent (i);
  316. compHolder->addAndMakeVisible (c);
  317. }
  318. updateComponent (c);
  319. componentsInOrder.add (c);
  320. }
  321. // Make sure the z-order is correct..
  322. if (num > 0)
  323. {
  324. componentsInOrder.getLast()->toFront (false);
  325. for (i = num - 1; --i >= 0;)
  326. componentsInOrder.getUnchecked(i)->toBehind (componentsInOrder.getUnchecked (i + 1));
  327. }
  328. }
  329. //==============================================================================
  330. ComponentDocument::TestComponent::TestComponent (ComponentDocument& document_)
  331. : document (new ComponentDocument (document_))
  332. {
  333. setupDocument();
  334. }
  335. ComponentDocument::TestComponent::TestComponent (Project* project, const File& cppFile)
  336. : document (new ComponentDocument (project, cppFile))
  337. {
  338. if (document->reload())
  339. setupDocument();
  340. else
  341. document = 0;
  342. }
  343. ComponentDocument::TestComponent::~TestComponent()
  344. {
  345. deleteAllChildren();
  346. }
  347. void ComponentDocument::TestComponent::setupDocument()
  348. {
  349. document->setUsingTemporaryCanvasSize (true);
  350. setSize (document->getCanvasWidth().getValue(),
  351. document->getCanvasHeight().getValue());
  352. }
  353. void ComponentDocument::TestComponent::resized()
  354. {
  355. if (document != 0)
  356. {
  357. document->getCanvasWidth() = getWidth();
  358. document->getCanvasHeight() = getHeight();
  359. document->updateComponentsIn (this);
  360. }
  361. }
  362. void ComponentDocument::TestComponent::paint (Graphics& g)
  363. {
  364. if (document == 0)
  365. drawComponentPlaceholder (g, getWidth(), getHeight(), "(Not a valid Jucer component)");
  366. }
  367. //==============================================================================
  368. ValueTree ComponentDocument::getComponentGroup() const
  369. {
  370. return root.getChildWithName (componentGroupTag);
  371. }
  372. int ComponentDocument::getNumComponents() const
  373. {
  374. return getComponentGroup().getNumChildren();
  375. }
  376. const ValueTree ComponentDocument::getComponent (int index) const
  377. {
  378. return getComponentGroup().getChild (index);
  379. }
  380. const ValueTree ComponentDocument::getComponentWithMemberName (const String& name) const
  381. {
  382. return getComponentGroup().getChildWithProperty (memberNameProperty, name);
  383. }
  384. const ValueTree ComponentDocument::getComponentWithID (const String& uid) const
  385. {
  386. return getComponentGroup().getChildWithProperty (idProperty, uid);
  387. }
  388. Component* ComponentDocument::createComponent (int index)
  389. {
  390. const ValueTree v (getComponentGroup().getChild (index));
  391. if (v.isValid())
  392. {
  393. Component* c = ComponentTypeManager::getInstance()->createFromStoredType (*this, v);
  394. c->getProperties().set (jucerIDProperty, v[idProperty]);
  395. jassert (getJucerIDFor (c).isNotEmpty());
  396. return c;
  397. }
  398. return 0;
  399. }
  400. //==============================================================================
  401. const Coordinate ComponentDocument::findMarker (const String& name, bool isHorizontal) const
  402. {
  403. if (name == Coordinate::parentRightMarkerName) return Coordinate ((double) getCanvasWidth().getValue(), isHorizontal);
  404. if (name == Coordinate::parentBottomMarkerName) return Coordinate ((double) getCanvasHeight().getValue(), isHorizontal);
  405. if (name.containsChar ('.'))
  406. {
  407. const String compName (name.upToFirstOccurrenceOf (".", false, false).trim());
  408. const String edge (name.fromFirstOccurrenceOf (".", false, false).trim());
  409. if (compName.isNotEmpty() && edge.isNotEmpty())
  410. {
  411. const ValueTree comp (getComponentWithMemberName (compName));
  412. if (comp.isValid())
  413. {
  414. const RectangleCoordinates coords (getCoordsFor (comp));
  415. if (edge == "left") return coords.left;
  416. if (edge == "right") return coords.right;
  417. if (edge == "top") return coords.top;
  418. if (edge == "bottom") return coords.bottom;
  419. }
  420. }
  421. }
  422. const ValueTree marker (getMarkerList (isHorizontal).getMarkerNamed (name));
  423. if (marker.isValid())
  424. return getMarkerList (isHorizontal).getCoordinate (marker);
  425. return Coordinate (isHorizontal);
  426. }
  427. const RectangleCoordinates ComponentDocument::getCoordsFor (const ValueTree& state) const
  428. {
  429. return RectangleCoordinates (state [compBoundsProperty]);
  430. }
  431. bool ComponentDocument::setCoordsFor (ValueTree& state, const RectangleCoordinates& pr)
  432. {
  433. const String newBoundsString (pr.toString());
  434. if (state[compBoundsProperty] == newBoundsString)
  435. return false;
  436. state.setProperty (compBoundsProperty, newBoundsString, getUndoManager());
  437. return true;
  438. }
  439. const String ComponentDocument::getNonexistentMemberName (String name)
  440. {
  441. String n (CodeHelpers::makeValidIdentifier (name, false, true, false));
  442. int suffix = 2;
  443. while (markersX->getMarkerNamed (n).isValid() || markersY->getMarkerNamed (n).isValid()
  444. || getComponentWithMemberName (n).isValid())
  445. n = n.trimCharactersAtEnd ("0123456789") + String (suffix++);
  446. return n;
  447. }
  448. void ComponentDocument::renameAnchor (const String& oldName, const String& newName)
  449. {
  450. int i;
  451. for (i = getNumComponents(); --i >= 0;)
  452. {
  453. ValueTree v (getComponent(i));
  454. RectangleCoordinates coords (getCoordsFor (v));
  455. coords.renameAnchorIfUsed (oldName, newName, *this);
  456. setCoordsFor (v, coords);
  457. }
  458. markersX->renameAnchorInMarkers (oldName, newName);
  459. markersY->renameAnchorInMarkers (oldName, newName);
  460. }
  461. void ComponentDocument::addMarkerMenuItem (int i, const Coordinate& coord, const String& name, PopupMenu& menu,
  462. bool isAnchor1, const String& fullCoordName)
  463. {
  464. Coordinate requestedCoord (findMarker (name, coord.isHorizontal()));
  465. menu.addItem (i, name,
  466. ! (name == fullCoordName || requestedCoord.referencesIndirectly (fullCoordName, *this)),
  467. name == (isAnchor1 ? coord.getAnchor1() : coord.getAnchor2()));
  468. }
  469. void ComponentDocument::addComponentMarkerMenuItems (const ValueTree& componentState, const String& coordName,
  470. Coordinate& coord, PopupMenu& menu, bool isAnchor1)
  471. {
  472. const String componentName (componentState [memberNameProperty].toString());
  473. const String fullCoordName (componentName + "." + coordName);
  474. if (coord.isHorizontal())
  475. {
  476. addMarkerMenuItem (1, coord, Coordinate::parentLeftMarkerName, menu, isAnchor1, fullCoordName);
  477. addMarkerMenuItem (2, coord, Coordinate::parentRightMarkerName, menu, isAnchor1, fullCoordName);
  478. menu.addSeparator();
  479. addMarkerMenuItem (3, coord, componentName + ".left", menu, isAnchor1, fullCoordName);
  480. addMarkerMenuItem (4, coord, componentName + ".right", menu, isAnchor1, fullCoordName);
  481. }
  482. else
  483. {
  484. addMarkerMenuItem (1, coord, Coordinate::parentTopMarkerName, menu, isAnchor1, fullCoordName);
  485. addMarkerMenuItem (2, coord, Coordinate::parentBottomMarkerName, menu, isAnchor1, fullCoordName);
  486. menu.addSeparator();
  487. addMarkerMenuItem (3, coord, componentName + ".top", menu, isAnchor1, fullCoordName);
  488. addMarkerMenuItem (4, coord, componentName + ".bottom", menu, isAnchor1, fullCoordName);
  489. }
  490. menu.addSeparator();
  491. const MarkerList& markerList = getMarkerList (coord.isHorizontal());
  492. int i;
  493. for (i = 0; i < markerList.size(); ++i)
  494. addMarkerMenuItem (100 + i, coord, markerList.getName (markerList.getMarker (i)), menu, isAnchor1, fullCoordName);
  495. menu.addSeparator();
  496. for (i = 0; i < getNumComponents(); ++i)
  497. {
  498. const String compName (getComponent (i) [memberNameProperty].toString());
  499. if (compName != componentName)
  500. {
  501. if (coord.isHorizontal())
  502. {
  503. addMarkerMenuItem (10000 + i * 4, coord, compName + ".left", menu, isAnchor1, fullCoordName);
  504. addMarkerMenuItem (10001 + i * 4, coord, compName + ".right", menu, isAnchor1, fullCoordName);
  505. }
  506. else
  507. {
  508. addMarkerMenuItem (10002 + i * 4, coord, compName + ".top", menu, isAnchor1, fullCoordName);
  509. addMarkerMenuItem (10003 + i * 4, coord, compName + ".bottom", menu, isAnchor1, fullCoordName);
  510. }
  511. }
  512. }
  513. }
  514. const String ComponentDocument::getChosenMarkerMenuItem (const ValueTree& componentState, Coordinate& coord, int i) const
  515. {
  516. const String componentName (componentState [memberNameProperty].toString());
  517. if (i == 1) return coord.isHorizontal() ? Coordinate::parentLeftMarkerName : Coordinate::parentTopMarkerName;
  518. if (i == 2) return coord.isHorizontal() ? Coordinate::parentRightMarkerName : Coordinate::parentBottomMarkerName;
  519. if (i == 3) return componentName + (coord.isHorizontal() ? ".left" : ".top");
  520. if (i == 4) return componentName + (coord.isHorizontal() ? ".right" : ".bottom");
  521. const MarkerList& markerList = getMarkerList (coord.isHorizontal());
  522. if (i >= 100 && i < 10000)
  523. return markerList.getName (markerList.getMarker (i - 100));
  524. if (i >= 10000)
  525. {
  526. const String compName (getComponent ((i - 10000) / 4) [memberNameProperty].toString());
  527. switch (i & 3)
  528. {
  529. case 0: return compName + ".left";
  530. case 1: return compName + ".right";
  531. case 2: return compName + ".top";
  532. case 3: return compName + ".bottom";
  533. default: break;
  534. }
  535. }
  536. jassertfalse;
  537. return String::empty;
  538. }
  539. void ComponentDocument::updateComponent (Component* comp)
  540. {
  541. const ValueTree v (getComponentState (comp));
  542. if (v.isValid())
  543. {
  544. ComponentTypeInstance item (*this, v);
  545. item.updateComponent (comp);
  546. }
  547. }
  548. bool ComponentDocument::containsComponent (Component* comp) const
  549. {
  550. const ValueTree comps (getComponentGroup());
  551. for (int i = 0; i < comps.getNumChildren(); ++i)
  552. if (isStateForComponent (comps.getChild(i), comp))
  553. return true;
  554. return false;
  555. }
  556. const ValueTree ComponentDocument::getComponentState (Component* comp) const
  557. {
  558. jassert (comp != 0);
  559. const ValueTree comps (getComponentGroup());
  560. for (int i = 0; i < comps.getNumChildren(); ++i)
  561. if (isStateForComponent (comps.getChild(i), comp))
  562. return comps.getChild(i);
  563. jassertfalse;
  564. return ValueTree::invalid;
  565. }
  566. bool ComponentDocument::isStateForComponent (const ValueTree& storedState, Component* comp) const
  567. {
  568. jassert (comp != 0);
  569. jassert (! storedState [idProperty].isVoid());
  570. return storedState [idProperty] == getJucerIDFor (comp);
  571. }
  572. void ComponentDocument::removeComponent (const ValueTree& state)
  573. {
  574. jassert (state.isAChildOf (getComponentGroup()));
  575. renameAnchor (state [memberNameProperty], String::empty);
  576. getComponentGroup().removeChild (state, getUndoManager());
  577. }
  578. //==============================================================================
  579. ComponentDocument::MarkerList::MarkerList (ComponentDocument& document_, const bool isX_)
  580. : MarkerListBase (document_.getRoot().getChildWithName (isX_ ? markersGroupXTag : markersGroupYTag), isX_),
  581. document (document_)
  582. {
  583. jassert (group.isValid());
  584. jassert (group.isAChildOf (document_.getRoot()));
  585. }
  586. UndoManager* ComponentDocument::MarkerList::getUndoManager() const
  587. {
  588. return document.getUndoManager();
  589. }
  590. const String ComponentDocument::MarkerList::getNonexistentMarkerName (const String& name)
  591. {
  592. return document.getNonexistentMemberName (name);
  593. }
  594. void ComponentDocument::MarkerList::renameAnchor (const String& oldName, const String& newName)
  595. {
  596. document.renameAnchor (oldName, newName);
  597. }
  598. const Coordinate ComponentDocument::MarkerList::findMarker (const String& name, bool isHorizontal_) const
  599. {
  600. if (isHorizontal_ == isX)
  601. {
  602. if (name == Coordinate::parentRightMarkerName) return Coordinate ((double) document.getCanvasWidth().getValue(), isX);
  603. if (name == Coordinate::parentBottomMarkerName) return Coordinate ((double) document.getCanvasHeight().getValue(), isX);
  604. const ValueTree marker (document.getMarkerList (isX).getMarkerNamed (name));
  605. if (marker.isValid())
  606. return document.getMarkerList (isX).getCoordinate (marker);
  607. }
  608. return Coordinate (isX);
  609. }
  610. void ComponentDocument::MarkerList::addMarkerMenuItems (const ValueTree& markerState, const Coordinate& coord, PopupMenu& menu, bool isAnchor1)
  611. {
  612. const String fullCoordName (getName (markerState));
  613. if (coord.isHorizontal())
  614. {
  615. document.addMarkerMenuItem (1, coord, Coordinate::parentLeftMarkerName, menu, isAnchor1, fullCoordName);
  616. document.addMarkerMenuItem (2, coord, Coordinate::parentRightMarkerName, menu, isAnchor1, fullCoordName);
  617. }
  618. else
  619. {
  620. document.addMarkerMenuItem (1, coord, Coordinate::parentTopMarkerName, menu, isAnchor1, fullCoordName);
  621. document.addMarkerMenuItem (2, coord, Coordinate::parentBottomMarkerName, menu, isAnchor1, fullCoordName);
  622. }
  623. menu.addSeparator();
  624. const MarkerList& markerList = document.getMarkerList (coord.isHorizontal());
  625. for (int i = 0; i < markerList.size(); ++i)
  626. document.addMarkerMenuItem (100 + i, coord, markerList.getName (markerList.getMarker (i)), menu, isAnchor1, fullCoordName);
  627. }
  628. const String ComponentDocument::MarkerList::getChosenMarkerMenuItem (const Coordinate& coord, int i) const
  629. {
  630. if (i == 1) return coord.isHorizontal() ? Coordinate::parentLeftMarkerName : Coordinate::parentTopMarkerName;
  631. if (i == 2) return coord.isHorizontal() ? Coordinate::parentRightMarkerName : Coordinate::parentBottomMarkerName;
  632. const MarkerList& markerList = document.getMarkerList (coord.isHorizontal());
  633. if (i >= 100 && i < 10000)
  634. return markerList.getName (markerList.getMarker (i - 100));
  635. jassertfalse;
  636. return String::empty;
  637. }
  638. //==============================================================================
  639. bool ComponentDocument::MarkerList::createProperties (Array <PropertyComponent*>& props, const String& itemId)
  640. {
  641. ValueTree marker (group.getChildWithProperty (idProperty, itemId));
  642. if (marker.isValid())
  643. {
  644. props.add (new TextPropertyComponent (Value (new MarkerListBase::MarkerNameValueSource (this, getNameAsValue (marker))),
  645. "Marker Name", 256, false));
  646. props.add (new MarkerListBase::PositionPropertyComponent (document, *this, "Position", marker,
  647. marker.getPropertyAsValue (getMarkerPosProperty(), document.getUndoManager())));
  648. return true;
  649. }
  650. return false;
  651. }
  652. //==============================================================================
  653. bool ComponentDocument::createItemProperties (Array <PropertyComponent*>& props, const String& itemId)
  654. {
  655. ValueTree comp (getComponentWithID (itemId));
  656. if (comp.isValid())
  657. {
  658. ComponentTypeInstance item (*this, comp);
  659. item.createProperties (props);
  660. return true;
  661. }
  662. if (markersX->createProperties (props, itemId)
  663. || markersY->createProperties (props, itemId))
  664. return true;
  665. return false;
  666. }
  667. void ComponentDocument::createItemProperties (Array <PropertyComponent*>& props, const StringArray& selectedItemIds)
  668. {
  669. if (selectedItemIds.size() != 1)
  670. return; //xxx
  671. for (int i = 0; i < selectedItemIds.size(); ++i)
  672. createItemProperties (props, selectedItemIds[i]);
  673. }
  674. //==============================================================================
  675. UndoManager* ComponentDocument::getUndoManager() const
  676. {
  677. return &undoManager;
  678. }
  679. //==============================================================================
  680. const String ComponentDocument::getJucerIDFor (Component* c)
  681. {
  682. if (c == 0)
  683. {
  684. jassertfalse;
  685. return String::empty;
  686. }
  687. jassert (c->getProperties().contains (jucerIDProperty));
  688. return c->getProperties() [jucerIDProperty];
  689. }
  690. //==============================================================================
  691. void ComponentDocument::createClassProperties (Array <PropertyComponent*>& props)
  692. {
  693. props.add (new TextPropertyComponent (getClassName(), "Class Name", 256, false));
  694. props.getLast()->setTooltip ("The C++ class name for the component class.");
  695. props.add (new TextPropertyComponent (getClassDescription(), "Description", 512, false));
  696. props.getLast()->setTooltip ("A freeform description of the component.");
  697. props.add (new SliderPropertyComponent (getCanvasWidth(), "Initial Width", 1.0, 8192.0, 1.0));
  698. props.getLast()->setTooltip ("The initial width of the component when it is created.");
  699. props.add (new SliderPropertyComponent (getCanvasHeight(), "Initial Height", 1.0, 8192.0, 1.0));
  700. props.getLast()->setTooltip ("The initial height of the component when it is created.");
  701. }