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.

968 lines
31KB

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