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.

809 lines
27KB

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