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.

774 lines
26KB

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