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.

832 lines
28KB

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