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.

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