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.

969 lines
31KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2013 - Raw Material Software Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. #include "../jucer_Headers.h"
  18. #include "jucer_JucerDocument.h"
  19. #include "jucer_ObjectTypes.h"
  20. #include "ui/jucer_JucerDocumentEditor.h"
  21. #include "components/jucer_ComponentUndoableAction.h"
  22. //==============================================================================
  23. ComponentLayout::ComponentLayout()
  24. : document (nullptr),
  25. nextCompUID (1)
  26. {
  27. }
  28. ComponentLayout::~ComponentLayout()
  29. {
  30. components.clear();
  31. }
  32. //==============================================================================
  33. void ComponentLayout::changed()
  34. {
  35. if (document != nullptr)
  36. document->changed();
  37. }
  38. void ComponentLayout::perform (UndoableAction* action, const String& actionName)
  39. {
  40. jassert (document != nullptr);
  41. if (document != nullptr)
  42. {
  43. document->getUndoManager().perform (action, actionName);
  44. }
  45. else
  46. {
  47. ScopedPointer<UndoableAction> deleter (action);
  48. action->perform();
  49. }
  50. }
  51. //==============================================================================
  52. void ComponentLayout::clearComponents()
  53. {
  54. selected.deselectAll();
  55. selected.dispatchPendingMessages();
  56. components.clear();
  57. changed();
  58. }
  59. //==============================================================================
  60. class AddCompAction : public UndoableAction
  61. {
  62. public:
  63. AddCompAction (XmlElement* const xml_, ComponentLayout& layout_)
  64. : indexAdded (-1),
  65. xml (xml_),
  66. layout (layout_)
  67. {
  68. }
  69. bool perform()
  70. {
  71. showCorrectTab();
  72. Component* const newComp = layout.addComponentFromXml (*xml, false);
  73. jassert (newComp != nullptr);
  74. indexAdded = layout.indexOfComponent (newComp);
  75. jassert (indexAdded >= 0);
  76. return indexAdded >= 0;
  77. }
  78. bool undo()
  79. {
  80. showCorrectTab();
  81. layout.removeComponent (layout.getComponent (indexAdded), false);
  82. return true;
  83. }
  84. int getSizeInUnits() { return 10; }
  85. int indexAdded;
  86. private:
  87. ScopedPointer<XmlElement> xml;
  88. ComponentLayout& layout;
  89. static void showCorrectTab()
  90. {
  91. if (JucerDocumentEditor* const ed = JucerDocumentEditor::getActiveDocumentHolder())
  92. ed->showLayout();
  93. }
  94. AddCompAction (const AddCompAction&);
  95. AddCompAction& operator= (const AddCompAction&);
  96. };
  97. //==============================================================================
  98. class DeleteCompAction : public ComponentUndoableAction <Component>
  99. {
  100. public:
  101. DeleteCompAction (Component* const comp, ComponentLayout& layout)
  102. : ComponentUndoableAction <Component> (comp, layout),
  103. oldIndex (-1)
  104. {
  105. if (ComponentTypeHandler* const h = ComponentTypeHandler::getHandlerFor (*comp))
  106. xml = h->createXmlFor (comp, &layout);
  107. else
  108. jassertfalse;
  109. oldIndex = layout.indexOfComponent (comp);
  110. }
  111. bool perform()
  112. {
  113. showCorrectTab();
  114. layout.removeComponent (getComponent(), false);
  115. return true;
  116. }
  117. bool undo()
  118. {
  119. Component* c = layout.addComponentFromXml (*xml, false);
  120. jassert (c != nullptr);
  121. layout.moveComponentZOrder (layout.indexOfComponent (c), oldIndex);
  122. showCorrectTab();
  123. return c != nullptr;
  124. }
  125. private:
  126. ScopedPointer<XmlElement> xml;
  127. int oldIndex;
  128. };
  129. void ComponentLayout::removeComponent (Component* comp, const bool undoable)
  130. {
  131. if (comp != nullptr && components.contains (comp))
  132. {
  133. if (undoable)
  134. {
  135. perform (new DeleteCompAction (comp, *this), "Delete components");
  136. }
  137. else
  138. {
  139. selected.deselect (comp);
  140. selected.changed (true); // synchronous message to get rid of any property components
  141. components.removeObject (comp);
  142. changed();
  143. }
  144. }
  145. }
  146. //==============================================================================
  147. class FrontBackCompAction : public ComponentUndoableAction <Component>
  148. {
  149. public:
  150. FrontBackCompAction (Component* const comp, ComponentLayout& layout, int newIndex_)
  151. : ComponentUndoableAction <Component> (comp, layout),
  152. newIndex (newIndex_)
  153. {
  154. oldIndex = layout.indexOfComponent (comp);
  155. }
  156. bool perform()
  157. {
  158. showCorrectTab();
  159. Component* comp = layout.getComponent (oldIndex);
  160. layout.moveComponentZOrder (oldIndex, newIndex);
  161. newIndex = layout.indexOfComponent (comp);
  162. return true;
  163. }
  164. bool undo()
  165. {
  166. showCorrectTab();
  167. layout.moveComponentZOrder (newIndex, oldIndex);
  168. return true;
  169. }
  170. private:
  171. int newIndex, oldIndex;
  172. };
  173. void ComponentLayout::moveComponentZOrder (int oldIndex, int newIndex)
  174. {
  175. jassert (components [oldIndex] != nullptr);
  176. if (oldIndex != newIndex && components [oldIndex] != nullptr)
  177. {
  178. components.move (oldIndex, newIndex);
  179. changed();
  180. }
  181. }
  182. void ComponentLayout::componentToFront (Component* comp, const bool undoable)
  183. {
  184. if (comp != nullptr && components.contains (comp))
  185. {
  186. if (undoable)
  187. perform (new FrontBackCompAction (comp, *this, -1), "Move components to front");
  188. else
  189. moveComponentZOrder (components.indexOf (comp), -1);
  190. }
  191. }
  192. void ComponentLayout::componentToBack (Component* comp, const bool undoable)
  193. {
  194. if (comp != nullptr && components.contains (comp))
  195. {
  196. if (undoable)
  197. perform (new FrontBackCompAction (comp, *this, 0), "Move components to back");
  198. else
  199. moveComponentZOrder (components.indexOf (comp), 0);
  200. }
  201. }
  202. //==============================================================================
  203. const char* const ComponentLayout::clipboardXmlTag = "COMPONENTS";
  204. void ComponentLayout::copySelectedToClipboard()
  205. {
  206. if (selected.getNumSelected() == 0)
  207. return;
  208. XmlElement clip (clipboardXmlTag);
  209. for (int i = 0; i < components.size(); ++i)
  210. {
  211. Component* const c = components.getUnchecked(i);
  212. if (selected.isSelected (c))
  213. {
  214. if (ComponentTypeHandler* const type = ComponentTypeHandler::getHandlerFor (*c))
  215. {
  216. XmlElement* const e = type->createXmlFor (c, this);
  217. clip.addChildElement (e);
  218. }
  219. }
  220. }
  221. SystemClipboard::copyTextToClipboard (clip.createDocument (String::empty, false, false));
  222. }
  223. void ComponentLayout::paste()
  224. {
  225. XmlDocument clip (SystemClipboard::getTextFromClipboard());
  226. ScopedPointer<XmlElement> doc (clip.getDocumentElement());
  227. if (doc != nullptr && doc->hasTagName (clipboardXmlTag))
  228. {
  229. selected.deselectAll();
  230. forEachXmlChildElement (*doc, e)
  231. if (Component* newComp = addComponentFromXml (*e, true))
  232. selected.addToSelection (newComp);
  233. startDragging();
  234. dragSelectedComps (Random::getSystemRandom().nextInt (40),
  235. Random::getSystemRandom().nextInt (40));
  236. endDragging();
  237. }
  238. }
  239. void ComponentLayout::deleteSelected()
  240. {
  241. const SelectedItemSet <Component*> temp (selected);
  242. selected.deselectAll();
  243. selected.changed (true); // synchronous message to get rid of any property components
  244. if (temp.getNumSelected() > 0)
  245. {
  246. for (int i = temp.getNumSelected(); --i >= 0;)
  247. removeComponent (temp.getSelectedItem (i), true);
  248. changed();
  249. if (document != nullptr)
  250. document->dispatchPendingMessages(); // forces the change to propagate before a paint() callback can happen,
  251. // in case there are components floating around that are now dangling pointers
  252. }
  253. }
  254. void ComponentLayout::selectAll()
  255. {
  256. for (int i = 0; i < components.size(); ++i)
  257. selected.addToSelection (components.getUnchecked (i));
  258. }
  259. void ComponentLayout::selectedToFront()
  260. {
  261. const SelectedItemSet <Component*> temp (selected);
  262. for (int i = temp.getNumSelected(); --i >= 0;)
  263. componentToFront (temp.getSelectedItem(i), true);
  264. }
  265. void ComponentLayout::selectedToBack()
  266. {
  267. const SelectedItemSet <Component*> temp (selected);
  268. for (int i = 0; i < temp.getNumSelected(); ++i)
  269. componentToBack (temp.getSelectedItem(i), true);
  270. }
  271. void ComponentLayout::bringLostItemsBackOnScreen (int width, int height)
  272. {
  273. for (int i = components.size(); --i >= 0;)
  274. {
  275. Component* const c = components[i];
  276. if (! c->getBounds().intersects (Rectangle<int> (0, 0, width, height)))
  277. {
  278. c->setTopLeftPosition (width / 2, height / 2);
  279. updateStoredComponentPosition (c, false);
  280. }
  281. }
  282. }
  283. Component* ComponentLayout::addNewComponent (ComponentTypeHandler* const type, int x, int y)
  284. {
  285. ScopedPointer<Component> c (type->createNewComponent (getDocument()));
  286. jassert (c != nullptr);
  287. if (c != nullptr)
  288. {
  289. c->setSize (type->getDefaultWidth(), type->getDefaultHeight());
  290. c->setCentrePosition (x, y);
  291. updateStoredComponentPosition (c, false);
  292. c->getProperties().set ("id", nextCompUID++);
  293. ScopedPointer<XmlElement> xml (type->createXmlFor (c, this));
  294. c = nullptr;
  295. c = addComponentFromXml (*xml, true);
  296. String memberName (CodeHelpers::makeValidIdentifier (type->getClassName (c), true, true, false));
  297. setComponentMemberVariableName (c, memberName);
  298. selected.selectOnly (c);
  299. }
  300. return c.release();
  301. }
  302. Component* ComponentLayout::addComponentFromXml (const XmlElement& xml, const bool undoable)
  303. {
  304. if (undoable)
  305. {
  306. AddCompAction* const action = new AddCompAction (new XmlElement (xml), *this);
  307. perform (action, "Add new components");
  308. return components [action->indexAdded];
  309. }
  310. if (ComponentTypeHandler* const type
  311. = ComponentTypeHandler::getHandlerForXmlTag (xml.getTagName()))
  312. {
  313. ScopedPointer<Component> newComp (type->createNewComponent (getDocument()));
  314. if (type->restoreFromXml (xml, newComp, this))
  315. {
  316. // ensure that the new comp's name is unique
  317. setComponentMemberVariableName (newComp, getComponentMemberVariableName (newComp));
  318. // check for duped IDs..
  319. while (findComponentWithId (ComponentTypeHandler::getComponentId (newComp)) != nullptr)
  320. ComponentTypeHandler::setComponentId (newComp, Random::getSystemRandom().nextInt64());
  321. components.add (newComp);
  322. changed();
  323. return newComp.release();
  324. }
  325. }
  326. return nullptr;
  327. }
  328. Component* ComponentLayout::findComponentWithId (const int64 componentId) const
  329. {
  330. for (int i = 0; i < components.size(); ++i)
  331. if (ComponentTypeHandler::getComponentId (components.getUnchecked(i)) == componentId)
  332. return components.getUnchecked(i);
  333. return nullptr;
  334. }
  335. //==============================================================================
  336. static const char* const dimensionSuffixes[] = { "X", "Y", "W", "H" };
  337. Component* ComponentLayout::getComponentRelativePosTarget (Component* comp, int whichDimension) const
  338. {
  339. jassert (comp != nullptr);
  340. if (PaintElement* const pe = dynamic_cast <PaintElement*> (comp))
  341. {
  342. int64 compId;
  343. if (whichDimension == 0) compId = pe->getPosition().relativeToX;
  344. else if (whichDimension == 1) compId = pe->getPosition().relativeToY;
  345. else if (whichDimension == 2) compId = pe->getPosition().relativeToW;
  346. else compId = pe->getPosition().relativeToH;
  347. return findComponentWithId (compId);
  348. }
  349. return findComponentWithId (comp->getProperties() [String ("relativeTo") + dimensionSuffixes [whichDimension]]
  350. .toString().getHexValue64());
  351. }
  352. void ComponentLayout::setComponentRelativeTarget (Component* comp, int whichDimension, Component* compToBeRelativeTo)
  353. {
  354. PaintElement* const pe = dynamic_cast <PaintElement*> (comp);
  355. jassert (comp != nullptr);
  356. jassert (pe != nullptr || components.contains (comp));
  357. jassert (compToBeRelativeTo == 0 || components.contains (compToBeRelativeTo));
  358. jassert (compToBeRelativeTo == 0 || ! dependsOnComponentForRelativePos (compToBeRelativeTo, comp));
  359. if (compToBeRelativeTo != getComponentRelativePosTarget (comp, whichDimension)
  360. && (compToBeRelativeTo == 0 || ! dependsOnComponentForRelativePos (compToBeRelativeTo, comp)))
  361. {
  362. const int64 compId = ComponentTypeHandler::getComponentId (compToBeRelativeTo);
  363. Rectangle<int> oldBounds (comp->getBounds());
  364. RelativePositionedRectangle pos;
  365. if (pe != nullptr)
  366. {
  367. oldBounds = pe->getCurrentBounds (dynamic_cast <PaintRoutineEditor*> (pe->getParentComponent())->getComponentArea());
  368. pos = pe->getPosition();
  369. }
  370. else
  371. {
  372. pos = ComponentTypeHandler::getComponentPosition (comp);
  373. }
  374. if (whichDimension == 0) pos.relativeToX = compId;
  375. else if (whichDimension == 1) pos.relativeToY = compId;
  376. else if (whichDimension == 2) pos.relativeToW = compId;
  377. else if (whichDimension == 3) pos.relativeToH = compId;
  378. if (pe != nullptr)
  379. {
  380. pe->setPosition (pos, true);
  381. pe->setCurrentBounds (oldBounds, dynamic_cast <PaintRoutineEditor*> (pe->getParentComponent())->getComponentArea(), true);
  382. }
  383. else
  384. {
  385. setComponentPosition (comp, pos, true);
  386. comp->setBounds (oldBounds);
  387. updateStoredComponentPosition (comp, false);
  388. }
  389. changed();
  390. }
  391. }
  392. bool ComponentLayout::dependsOnComponentForRelativePos (Component* comp, Component* possibleDependee) const
  393. {
  394. for (int i = 0; i < 4; ++i)
  395. if (Component* const c = getComponentRelativePosTarget (comp, i))
  396. if (c == possibleDependee || dependsOnComponentForRelativePos (c, possibleDependee))
  397. return true;
  398. return false;
  399. }
  400. const int menuIdBase = 0x63240000;
  401. PopupMenu ComponentLayout::getRelativeTargetMenu (Component* comp, int whichDimension) const
  402. {
  403. PopupMenu m;
  404. Component* const current = getComponentRelativePosTarget (comp, whichDimension);
  405. m.addItem (menuIdBase, "Relative to parent component", true, current == 0);
  406. m.addSeparator();
  407. for (int i = 0; i < components.size(); ++i)
  408. {
  409. Component* const c = components.getUnchecked(i);
  410. if (c != comp)
  411. m.addItem (menuIdBase + i + 1,
  412. "Relative to " + getComponentMemberVariableName (c)
  413. + " (class: " + ComponentTypeHandler::getHandlerFor (*c)->getClassName (c) + ")",
  414. ! dependsOnComponentForRelativePos (c, comp),
  415. current == c);
  416. }
  417. return m;
  418. }
  419. void ComponentLayout::processRelativeTargetMenuResult (Component* comp, int whichDimension, int menuResultID)
  420. {
  421. if (menuResultID != 0)
  422. {
  423. Component* const newTarget = components [menuResultID - menuIdBase - 1];
  424. setComponentRelativeTarget (comp, whichDimension, newTarget);
  425. }
  426. }
  427. //==============================================================================
  428. class ChangeCompPositionAction : public ComponentUndoableAction <Component>
  429. {
  430. public:
  431. ChangeCompPositionAction (Component* const comp, ComponentLayout& layout,
  432. const RelativePositionedRectangle& newPos_)
  433. : ComponentUndoableAction <Component> (comp, layout),
  434. newPos (newPos_)
  435. {
  436. oldPos = ComponentTypeHandler::getComponentPosition (comp);
  437. }
  438. bool perform()
  439. {
  440. showCorrectTab();
  441. layout.setComponentPosition (getComponent(), newPos, false);
  442. return true;
  443. }
  444. bool undo()
  445. {
  446. showCorrectTab();
  447. layout.setComponentPosition (getComponent(), oldPos, false);
  448. return true;
  449. }
  450. private:
  451. RelativePositionedRectangle newPos, oldPos;
  452. };
  453. void ComponentLayout::setComponentPosition (Component* comp,
  454. const RelativePositionedRectangle& newPos,
  455. const bool undoable)
  456. {
  457. if (ComponentTypeHandler::getComponentPosition (comp) != newPos)
  458. {
  459. if (undoable)
  460. {
  461. perform (new ChangeCompPositionAction (comp, *this, newPos), "Move components");
  462. }
  463. else
  464. {
  465. ComponentTypeHandler::setComponentPosition (comp, newPos, this);
  466. changed();
  467. }
  468. }
  469. }
  470. void ComponentLayout::updateStoredComponentPosition (Component* comp, const bool undoable)
  471. {
  472. RelativePositionedRectangle newPos (ComponentTypeHandler::getComponentPosition (comp));
  473. newPos.updateFromComponent (*comp, this);
  474. setComponentPosition (comp, newPos, undoable);
  475. }
  476. //==============================================================================
  477. void ComponentLayout::startDragging()
  478. {
  479. for (int i = 0; i < components.size(); ++i)
  480. {
  481. Component* const c = components[i];
  482. c->getProperties().set ("xDragStart", c->getX());
  483. c->getProperties().set ("yDragStart", c->getY());
  484. }
  485. jassert (document != nullptr);
  486. document->beginTransaction();
  487. }
  488. void ComponentLayout::dragSelectedComps (int dx, int dy, const bool allowSnap)
  489. {
  490. if (allowSnap && document != nullptr && selected.getNumSelected() > 1)
  491. {
  492. dx = document->snapPosition (dx);
  493. dy = document->snapPosition (dy);
  494. }
  495. for (int i = 0; i < selected.getNumSelected(); ++i)
  496. {
  497. Component* const c = selected.getSelectedItem (i);
  498. const int startX = c->getProperties() ["xDragStart"];
  499. const int startY = c->getProperties() ["yDragStart"];
  500. if (allowSnap && document != nullptr && selected.getNumSelected() == 1)
  501. {
  502. c->setTopLeftPosition (document->snapPosition (startX + dx),
  503. document->snapPosition (startY + dy));
  504. }
  505. else
  506. {
  507. c->setTopLeftPosition (startX + dx,
  508. startY + dy);
  509. }
  510. updateStoredComponentPosition (c, false);
  511. }
  512. }
  513. void ComponentLayout::endDragging()
  514. {
  515. // after the drag, roll back all the comps to their start position, then
  516. // back to their finish positions using an undoable command.
  517. document->beginTransaction();
  518. for (int i = 0; i < selected.getNumSelected(); ++i)
  519. {
  520. Component* const c = selected.getSelectedItem (i);
  521. const int newX = c->getX();
  522. const int newY = c->getY();
  523. const int startX = c->getProperties() ["xDragStart"];
  524. const int startY = c->getProperties() ["yDragStart"];
  525. c->setTopLeftPosition (startX, startY);
  526. updateStoredComponentPosition (c, false);
  527. c->setTopLeftPosition (newX, newY);
  528. updateStoredComponentPosition (c, true);
  529. }
  530. document->beginTransaction();
  531. }
  532. void ComponentLayout::moveSelectedComps (int dx, int dy, bool snap)
  533. {
  534. startDragging();
  535. dragSelectedComps (dx, dy, snap);
  536. endDragging();
  537. }
  538. void ComponentLayout::stretchSelectedComps (int dw, int dh, bool allowSnap)
  539. {
  540. int neww, newh;
  541. if (document != nullptr && selected.getNumSelected() == 1)
  542. {
  543. Component* const c = selected.getSelectedItem (0);
  544. if (allowSnap)
  545. {
  546. int bot = c->getBottom() + dh;
  547. int right = c->getRight() + dw;
  548. bot = (dh != 0) ? document->snapPosition (bot) : bot;
  549. right = (dw != 0) ? document->snapPosition (right) : right;
  550. newh = bot - c->getY();
  551. neww = right - c->getX();
  552. }
  553. else
  554. {
  555. newh = c->getHeight() + dh;
  556. neww = c->getWidth() + dw;
  557. }
  558. c->setSize (neww, newh);
  559. updateStoredComponentPosition (c, true);
  560. }
  561. else
  562. {
  563. for (int i = 0; i < selected.getNumSelected(); ++i)
  564. {
  565. Component* const c = selected.getSelectedItem (i);
  566. neww = c->getWidth() + dw;
  567. newh = c->getHeight() + dh;
  568. c->setSize (neww, newh);
  569. updateStoredComponentPosition (c, true);
  570. }
  571. }
  572. }
  573. //==============================================================================
  574. void ComponentLayout::fillInGeneratedCode (GeneratedCode& code) const
  575. {
  576. for (int i = 0; i < components.size(); ++i)
  577. if (Component* const comp = components.getUnchecked(i))
  578. if (ComponentTypeHandler* const type = ComponentTypeHandler::getHandlerFor (*comp))
  579. type->fillInGeneratedCode (comp, code);
  580. }
  581. //==============================================================================
  582. String ComponentLayout::getComponentMemberVariableName (Component* comp) const
  583. {
  584. if (comp == nullptr)
  585. return String::empty;
  586. String name (comp->getProperties() ["memberName"].toString());
  587. if (name.isEmpty())
  588. name = getUnusedMemberName (CodeHelpers::makeValidIdentifier (comp->getName(), true, true, false), comp);
  589. return name;
  590. }
  591. void ComponentLayout::setComponentMemberVariableName (Component* comp, const String& newName)
  592. {
  593. jassert (comp != nullptr);
  594. const String oldName (getComponentMemberVariableName (comp));
  595. comp->getProperties().set ("memberName", String::empty);
  596. const String n (getUnusedMemberName (CodeHelpers::makeValidIdentifier (newName, false, true, false), comp));
  597. comp->getProperties().set ("memberName", n);
  598. if (n != oldName)
  599. changed();
  600. }
  601. String ComponentLayout::getUnusedMemberName (String nameRoot, Component* comp) const
  602. {
  603. String n (nameRoot);
  604. while (CharacterFunctions::isDigit (nameRoot.getLastCharacter()))
  605. nameRoot = nameRoot.dropLastCharacters (1);
  606. int suffix = 2;
  607. for (;;)
  608. {
  609. bool alreadyUsed = false;
  610. for (int i = 0; i < components.size(); ++i)
  611. {
  612. if (components[i] != comp
  613. && components[i]->getProperties() ["memberName"] == n)
  614. {
  615. alreadyUsed = true;
  616. break;
  617. }
  618. }
  619. if (! alreadyUsed)
  620. break;
  621. n = nameRoot + String (suffix++);
  622. }
  623. return n;
  624. }
  625. //==============================================================================
  626. String ComponentLayout::getComponentVirtualClassName (Component* comp) const
  627. {
  628. if (comp == nullptr)
  629. return String::empty;
  630. return comp->getProperties() ["virtualName"];
  631. }
  632. void ComponentLayout::setComponentVirtualClassName (Component* comp, const String& newName)
  633. {
  634. jassert (comp != nullptr);
  635. const String name (CodeHelpers::makeValidIdentifier (newName, false, false, true));
  636. if (name != getComponentVirtualClassName (comp))
  637. {
  638. comp->getProperties().set ("virtualName", name);
  639. changed();
  640. }
  641. }
  642. //==============================================================================
  643. void ComponentLayout::addToXml (XmlElement& xml) const
  644. {
  645. for (int i = 0; i < components.size(); ++i)
  646. if (ComponentTypeHandler* h = ComponentTypeHandler::getHandlerFor (*components [i]))
  647. xml.addChildElement (h->createXmlFor (components [i], this));
  648. }
  649. //==============================================================================
  650. void positionToCode (const RelativePositionedRectangle& position,
  651. const ComponentLayout* layout,
  652. String& x, String& y, String& w, String& h)
  653. {
  654. // these are the code sections for the positions of the relative comps
  655. String xrx, xry, xrw, xrh;
  656. if (Component* const relCompX = layout != nullptr ? layout->findComponentWithId (position.relativeToX) : nullptr)
  657. positionToCode (ComponentTypeHandler::getComponentPosition (relCompX), layout, xrx, xry, xrw, xrh);
  658. String yrx, yry, yrw, yrh;
  659. if (Component* const relCompY = layout != nullptr ? layout->findComponentWithId (position.relativeToY) : nullptr)
  660. positionToCode (ComponentTypeHandler::getComponentPosition (relCompY), layout, yrx, yry, yrw, yrh);
  661. String wrx, wry, wrw, wrh;
  662. if (Component* const relCompW = (layout != nullptr && position.rect.getWidthMode() != PositionedRectangle::absoluteSize)
  663. ? layout->findComponentWithId (position.relativeToW) : nullptr)
  664. positionToCode (ComponentTypeHandler::getComponentPosition (relCompW), layout, wrx, wry, wrw, wrh);
  665. String hrx, hry, hrw, hrh;
  666. if (Component* const relCompH = (layout != nullptr && position.rect.getHeightMode() != PositionedRectangle::absoluteSize)
  667. ? layout->findComponentWithId (position.relativeToH) : nullptr)
  668. positionToCode (ComponentTypeHandler::getComponentPosition (relCompH), layout, hrx, hry, hrw, hrh);
  669. // width
  670. if (position.rect.getWidthMode() == PositionedRectangle::proportionalSize)
  671. {
  672. if (wrw.isNotEmpty())
  673. w << "roundFloatToInt ((" << wrw << ") * " << CodeHelpers::floatLiteral (position.rect.getWidth(), 4) << ")";
  674. else
  675. w << "proportionOfWidth (" << CodeHelpers::floatLiteral (position.rect.getWidth(), 4) << ")";
  676. }
  677. else if (position.rect.getWidthMode() == PositionedRectangle::parentSizeMinusAbsolute)
  678. {
  679. if (wrw.isNotEmpty())
  680. w << "(" << wrw << ") - " << roundToInt (position.rect.getWidth());
  681. else
  682. w << "getWidth() - " << roundToInt (position.rect.getWidth());
  683. }
  684. else
  685. {
  686. if (wrw.isNotEmpty())
  687. w << "(" << wrw << ") + ";
  688. w << roundToInt (position.rect.getWidth());
  689. }
  690. // height
  691. if (position.rect.getHeightMode() == PositionedRectangle::proportionalSize)
  692. {
  693. if (hrh.isNotEmpty())
  694. h << "roundFloatToInt ((" << hrh << ") * " << CodeHelpers::floatLiteral (position.rect.getHeight(), 4) << ")";
  695. else
  696. h << "proportionOfHeight (" << CodeHelpers::floatLiteral (position.rect.getHeight(), 4) << ")";
  697. }
  698. else if (position.rect.getHeightMode() == PositionedRectangle::parentSizeMinusAbsolute)
  699. {
  700. if (hrh.isNotEmpty())
  701. h << "(" << hrh << ") - " << roundToInt (position.rect.getHeight());
  702. else
  703. h << "getHeight() - " << roundToInt (position.rect.getHeight());
  704. }
  705. else
  706. {
  707. if (hrh.isNotEmpty())
  708. h << "(" << hrh << ") + ";
  709. h << roundToInt (position.rect.getHeight());
  710. }
  711. // x-pos
  712. if (position.rect.getPositionModeX() == PositionedRectangle::proportionOfParentSize)
  713. {
  714. if (xrx.isNotEmpty() && xrw.isNotEmpty())
  715. x << "(" << xrx << ") + roundFloatToInt ((" << xrw << ") * " << CodeHelpers::floatLiteral (position.rect.getX(), 4) << ")";
  716. else
  717. x << "proportionOfWidth (" << CodeHelpers::floatLiteral (position.rect.getX(), 4) << ")";
  718. }
  719. else if (position.rect.getPositionModeX() == PositionedRectangle::absoluteFromParentTopLeft)
  720. {
  721. if (xrx.isNotEmpty())
  722. x << "(" << xrx << ") + ";
  723. x << roundToInt (position.rect.getX());
  724. }
  725. else if (position.rect.getPositionModeX() == PositionedRectangle::absoluteFromParentBottomRight)
  726. {
  727. if (xrx.isNotEmpty())
  728. x << "(" << xrx << ") + (" << xrw << ")";
  729. else
  730. x << "getWidth()";
  731. const int d = roundToInt (position.rect.getX());
  732. if (d != 0)
  733. x << " - " << d;
  734. }
  735. else if (position.rect.getPositionModeX() == PositionedRectangle::absoluteFromParentCentre)
  736. {
  737. if (xrx.isNotEmpty())
  738. x << "(" << xrx << ") + (" << xrw << ") / 2";
  739. else
  740. x << "(getWidth() / 2)";
  741. const int d = roundToInt (position.rect.getX());
  742. if (d != 0)
  743. x << " + " << d;
  744. }
  745. if (w != "0")
  746. {
  747. if (position.rect.getAnchorPointX() == PositionedRectangle::anchorAtRightOrBottom)
  748. x << " - " << w;
  749. else if (position.rect.getAnchorPointX() == PositionedRectangle::anchorAtCentre)
  750. x << " - ((" << w << ") / 2)";
  751. }
  752. // y-pos
  753. if (position.rect.getPositionModeY() == PositionedRectangle::proportionOfParentSize)
  754. {
  755. if (yry.isNotEmpty() && yrh.isNotEmpty())
  756. y << "(" << yry << ") + roundFloatToInt ((" << yrh << ") * " << CodeHelpers::floatLiteral (position.rect.getY(), 4) << ")";
  757. else
  758. y << "proportionOfHeight (" << CodeHelpers::floatLiteral (position.rect.getY(), 4) << ")";
  759. }
  760. else if (position.rect.getPositionModeY() == PositionedRectangle::absoluteFromParentTopLeft)
  761. {
  762. if (yry.isNotEmpty())
  763. y << "(" << yry << ") + ";
  764. y << roundToInt (position.rect.getY());
  765. }
  766. else if (position.rect.getPositionModeY() == PositionedRectangle::absoluteFromParentBottomRight)
  767. {
  768. if (yry.isNotEmpty())
  769. y << "(" << yry << ") + (" << yrh << ")";
  770. else
  771. y << "getHeight()";
  772. const int d = roundToInt (position.rect.getY());
  773. if (d != 0)
  774. y << " - " << d;
  775. }
  776. else if (position.rect.getPositionModeY() == PositionedRectangle::absoluteFromParentCentre)
  777. {
  778. if (yry.isNotEmpty())
  779. y << "(" << yry << ") + (" << yrh << ") / 2";
  780. else
  781. y << "(getHeight() / 2)";
  782. const int d = roundToInt (position.rect.getY());
  783. if (d != 0)
  784. y << " + " << d;
  785. }
  786. if (h != "0")
  787. {
  788. if (position.rect.getAnchorPointY() == PositionedRectangle::anchorAtRightOrBottom)
  789. y << " - " << h;
  790. else if (position.rect.getAnchorPointY() == PositionedRectangle::anchorAtCentre)
  791. y << " - ((" << h << ") / 2)";
  792. }
  793. }