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.

874 lines
29KB

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