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.

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