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.

1194 lines
41KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2020 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 6 End-User License
  8. Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
  9. End User License Agreement: www.juce.com/juce-6-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. #pragma once
  19. //==============================================================================
  20. class TabbedComponentHandler : public ComponentTypeHandler
  21. {
  22. public:
  23. TabbedComponentHandler()
  24. : ComponentTypeHandler ("Tabbed Component", "juce::TabbedComponent", typeid (TabbedComponent), 200, 150)
  25. {}
  26. Component* createNewComponent (JucerDocument*) override
  27. {
  28. TabbedComponent* const t = new TabbedComponent (TabbedButtonBar::TabsAtTop);
  29. t->setName ("new tabbed component");
  30. for (int i = 3; --i >= 0;)
  31. addNewTab (t);
  32. return t;
  33. }
  34. XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
  35. {
  36. TabbedComponent* const t = dynamic_cast<TabbedComponent*> (comp);
  37. XmlElement* const e = ComponentTypeHandler::createXmlFor (comp, layout);
  38. if (t->getOrientation() == TabbedButtonBar::TabsAtTop) e->setAttribute ("orientation", "top");
  39. else if (t->getOrientation() == TabbedButtonBar::TabsAtBottom) e->setAttribute ("orientation", "bottom");
  40. else if (t->getOrientation() == TabbedButtonBar::TabsAtLeft) e->setAttribute ("orientation", "left");
  41. else if (t->getOrientation() == TabbedButtonBar::TabsAtRight) e->setAttribute ("orientation", "right");
  42. e->setAttribute ("tabBarDepth", t->getTabBarDepth());
  43. e->setAttribute ("initialTab", t->getCurrentTabIndex());
  44. for (int i = 0; i < t->getNumTabs(); ++i)
  45. e->addChildElement (getTabState (t, i).release());
  46. return e;
  47. }
  48. bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
  49. {
  50. if (! ComponentTypeHandler::restoreFromXml (xml, comp, layout))
  51. return false;
  52. TabbedComponent* const t = dynamic_cast<TabbedComponent*> (comp);
  53. if (xml.getStringAttribute ("orientation") == "top") t->setOrientation (TabbedButtonBar::TabsAtTop);
  54. else if (xml.getStringAttribute ("orientation") == "bottom") t->setOrientation (TabbedButtonBar::TabsAtBottom);
  55. else if (xml.getStringAttribute ("orientation") == "left") t->setOrientation (TabbedButtonBar::TabsAtLeft);
  56. else if (xml.getStringAttribute ("orientation") == "right") t->setOrientation (TabbedButtonBar::TabsAtRight);
  57. TabbedComponent defaultTabComp (TabbedButtonBar::TabsAtTop);
  58. t->setTabBarDepth (xml.getIntAttribute ("tabBarDepth", defaultTabComp.getTabBarDepth()));
  59. t->clearTabs();
  60. forEachXmlChildElement (xml, e)
  61. {
  62. addNewTab (t);
  63. restoreTabState (t, t->getNumTabs() - 1, *e);
  64. }
  65. t->setCurrentTabIndex (xml.getIntAttribute ("initialTab", 0));
  66. return true;
  67. }
  68. void getEditableProperties (Component* component, JucerDocument& doc,
  69. Array<PropertyComponent*>& props, bool multipleSelected) override
  70. {
  71. ComponentTypeHandler::getEditableProperties (component, doc, props, multipleSelected);
  72. if (multipleSelected)
  73. return;
  74. if (auto* t = dynamic_cast<TabbedComponent*> (component))
  75. {
  76. props.add (new TabOrientationProperty (t, doc));
  77. props.add (new TabDepthProperty (t, doc));
  78. if (t->getNumTabs() > 0)
  79. props.add (new TabInitialTabProperty (t, doc));
  80. props.add (new TabAddTabProperty (t, doc));
  81. if (t->getNumTabs() > 0)
  82. props.add (new TabRemoveTabProperty (t, doc));
  83. }
  84. }
  85. void addPropertiesToPropertyPanel (Component* comp, JucerDocument& doc,
  86. PropertyPanel& panel, bool multipleSelected) override
  87. {
  88. ComponentTypeHandler::addPropertiesToPropertyPanel (comp, doc, panel, multipleSelected);
  89. TabbedComponent* const t = dynamic_cast<TabbedComponent*> (comp);
  90. for (int i = 0; i < t->getNumTabs(); ++i)
  91. {
  92. Array<PropertyComponent*> properties;
  93. properties.add (new TabNameProperty (t, doc, i));
  94. properties.add (new TabColourProperty (t, doc, i));
  95. properties.add (new TabContentTypeProperty (t, doc, i));
  96. if (isTabUsingJucerComp (t, i))
  97. properties.add (new TabJucerFileProperty (t, doc, i));
  98. else
  99. properties.add (new TabContentClassProperty (t, doc, i));
  100. properties.add (new TabContentConstructorParamsProperty (t, doc, i));
  101. properties.add (new TabMoveProperty (t, doc, i, t->getNumTabs()));
  102. panel.addSection ("Tab " + String (i), properties);
  103. }
  104. }
  105. String getCreationParameters (GeneratedCode&, Component* comp) override
  106. {
  107. TabbedComponent* const t = dynamic_cast<TabbedComponent*> (comp);
  108. switch (t->getOrientation())
  109. {
  110. case TabbedButtonBar::TabsAtTop: return "juce::TabbedButtonBar::TabsAtTop";
  111. case TabbedButtonBar::TabsAtBottom: return "juce::TabbedButtonBar::TabsAtBottom";
  112. case TabbedButtonBar::TabsAtLeft: return "juce::TabbedButtonBar::TabsAtLeft";
  113. case TabbedButtonBar::TabsAtRight: return "juce::TabbedButtonBar::TabsAtRight";
  114. default: jassertfalse; break;
  115. }
  116. return {};
  117. }
  118. void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
  119. {
  120. TabbedComponent* const t = dynamic_cast<TabbedComponent*> (component);
  121. ComponentTypeHandler::fillInCreationCode (code, component, memberVariableName);
  122. code.constructorCode
  123. << memberVariableName << "->setTabBarDepth (" << t->getTabBarDepth() << ");\n";
  124. for (int i = 0; i < t->getNumTabs(); ++i)
  125. {
  126. String contentClassName;
  127. if (isTabUsingJucerComp (t, i))
  128. {
  129. File jucerCpp = code.document->getCppFile().getSiblingFile (getTabJucerFile (t, i));
  130. std::unique_ptr<JucerDocument> doc (JucerDocument::createForCppFile (nullptr, jucerCpp));
  131. if (doc != nullptr)
  132. {
  133. code.includeFilesCPP.add (jucerCpp.withFileExtension (".h"));
  134. contentClassName = doc->getClassName();
  135. }
  136. }
  137. else
  138. {
  139. contentClassName = getTabClassName (t, i);
  140. }
  141. code.constructorCode
  142. << memberVariableName
  143. << "->addTab ("
  144. << quotedString (t->getTabNames() [i], code.shouldUseTransMacro())
  145. << ", "
  146. << CodeHelpers::colourToCode (t->getTabBackgroundColour (i));
  147. if (contentClassName.isNotEmpty())
  148. {
  149. code.constructorCode << ", new " << contentClassName;
  150. if (getTabConstructorParams (t, i).trim().isNotEmpty())
  151. code.constructorCode << " ";
  152. code.constructorCode << "(" << getTabConstructorParams (t, i).trim() << "), true);\n";
  153. }
  154. else
  155. {
  156. code.constructorCode << ", 0, false);\n";
  157. }
  158. }
  159. code.constructorCode
  160. << memberVariableName << "->setCurrentTabIndex (" << t->getCurrentTabIndex() << ");\n";
  161. code.constructorCode << "\n";
  162. }
  163. //==============================================================================
  164. static void addNewTab (TabbedComponent* tc, const int insertIndex = -1)
  165. {
  166. tc->addTab ("Tab " + String (tc->getNumTabs()), Colours::lightgrey,
  167. new TabDemoContentComp(), true, insertIndex);
  168. }
  169. //==============================================================================
  170. static std::unique_ptr<XmlElement> getTabState (TabbedComponent* tc, int tabIndex)
  171. {
  172. auto xml = std::make_unique<XmlElement> ("TAB");
  173. xml->setAttribute ("name", tc->getTabNames() [tabIndex]);
  174. xml->setAttribute ("colour", tc->getTabBackgroundColour (tabIndex).toString());
  175. if (TabDemoContentComp* const tdc = dynamic_cast<TabDemoContentComp*> (tc->getTabContentComponent (tabIndex)))
  176. {
  177. xml->setAttribute ("useJucerComp", tdc->isUsingJucerComp);
  178. xml->setAttribute ("contentClassName", tdc->contentClassName);
  179. xml->setAttribute ("constructorParams", tdc->constructorParams);
  180. xml->setAttribute ("jucerComponentFile", tdc->jucerComponentFile);
  181. }
  182. return xml;
  183. }
  184. static void restoreTabState (TabbedComponent* tc, int tabIndex, const XmlElement& xml)
  185. {
  186. tc->setTabName (tabIndex, xml.getStringAttribute ("name", "Tab"));
  187. tc->setTabBackgroundColour (tabIndex, Colour::fromString (xml.getStringAttribute ("colour", Colours::lightgrey.toString())));
  188. if (TabDemoContentComp* const tdc = dynamic_cast<TabDemoContentComp*> (tc->getTabContentComponent (tabIndex)))
  189. {
  190. tdc->isUsingJucerComp = xml.getBoolAttribute ("useJucerComp", false);
  191. tdc->contentClassName = xml.getStringAttribute ("contentClassName");
  192. tdc->constructorParams = xml.getStringAttribute ("constructorParams");
  193. tdc->jucerComponentFile = xml.getStringAttribute ("jucerComponentFile");
  194. tdc->updateContent();
  195. }
  196. }
  197. //==============================================================================
  198. static bool isTabUsingJucerComp (TabbedComponent* tc, int tabIndex)
  199. {
  200. auto tdc = dynamic_cast<TabDemoContentComp*> (tc->getTabContentComponent (tabIndex));
  201. jassert (tdc != nullptr);
  202. return tdc != nullptr && tdc->isUsingJucerComp;
  203. }
  204. static void setTabUsingJucerComp (TabbedComponent* tc, int tabIndex, const bool b)
  205. {
  206. auto tdc = dynamic_cast<TabDemoContentComp*> (tc->getTabContentComponent (tabIndex));
  207. jassert (tdc != nullptr);
  208. if (tdc != nullptr)
  209. {
  210. tdc->isUsingJucerComp = b;
  211. tdc->updateContent();
  212. }
  213. }
  214. static String getTabClassName (TabbedComponent* tc, int tabIndex)
  215. {
  216. auto tdc = dynamic_cast<TabDemoContentComp*> (tc->getTabContentComponent (tabIndex));
  217. jassert (tdc != nullptr);
  218. return tdc != nullptr ? tdc->contentClassName : String();
  219. }
  220. static void setTabClassName (TabbedComponent* tc, int tabIndex, const String& newName)
  221. {
  222. TabDemoContentComp* const tdc = dynamic_cast<TabDemoContentComp*> (tc->getTabContentComponent (tabIndex));
  223. jassert (tdc != nullptr);
  224. if (tdc != nullptr)
  225. {
  226. tdc->contentClassName = newName;
  227. tdc->updateContent();
  228. }
  229. }
  230. static String getTabConstructorParams (TabbedComponent* tc, int tabIndex)
  231. {
  232. auto tdc = dynamic_cast<TabDemoContentComp*> (tc->getTabContentComponent (tabIndex));
  233. jassert (tdc != nullptr);
  234. return tdc != nullptr ? tdc->constructorParams : String();
  235. }
  236. static void setTabConstructorParams (TabbedComponent* tc, int tabIndex, const String& newParams)
  237. {
  238. auto tdc = dynamic_cast<TabDemoContentComp*> (tc->getTabContentComponent (tabIndex));
  239. jassert (tdc != nullptr);
  240. if (tdc != nullptr)
  241. {
  242. tdc->constructorParams = newParams;
  243. tdc->updateContent();
  244. }
  245. }
  246. static String getTabJucerFile (TabbedComponent* tc, int tabIndex)
  247. {
  248. auto tdc = dynamic_cast<TabDemoContentComp*> (tc->getTabContentComponent (tabIndex));
  249. jassert (tdc != nullptr);
  250. return tdc != nullptr ? tdc->jucerComponentFile : String();
  251. }
  252. static void setTabJucerFile (TabbedComponent* tc, int tabIndex, const String& newFile)
  253. {
  254. auto tdc = dynamic_cast<TabDemoContentComp*> (tc->getTabContentComponent (tabIndex));
  255. jassert (tdc != nullptr);
  256. if (tdc != nullptr)
  257. {
  258. tdc->jucerComponentFile = newFile;
  259. tdc->updateContent();
  260. }
  261. }
  262. private:
  263. //==============================================================================
  264. class TabDemoContentComp : public Component
  265. {
  266. public:
  267. TabDemoContentComp()
  268. : isUsingJucerComp (false)
  269. {
  270. setSize (2048, 2048);
  271. }
  272. void paint (Graphics& g) override
  273. {
  274. if (jucerComp == nullptr)
  275. g.fillCheckerBoard (getLocalBounds().toFloat(), 50.0f, 50.0f,
  276. Colour::greyLevel (0.9f).withAlpha (0.4f),
  277. Colour::greyLevel (0.8f).withAlpha (0.4f));
  278. }
  279. void resized() override
  280. {
  281. if (jucerComp != nullptr)
  282. {
  283. jucerComp->setBounds (getLocalBounds());
  284. setOpaque (jucerComp->isOpaque());
  285. }
  286. }
  287. void updateContent()
  288. {
  289. if (isUsingJucerComp)
  290. {
  291. if (jucerComp == nullptr
  292. || jucerComp->getOwnerDocument() == nullptr
  293. || jucerComp->getFilename() != jucerComponentFile)
  294. {
  295. jucerComp.reset();
  296. jucerComp.reset (new TestComponent (ComponentTypeHandler::findParentDocument (this), nullptr, false));
  297. jucerComp->setFilename (jucerComponentFile);
  298. jucerComp->setToInitialSize();
  299. addAndMakeVisible (jucerComp.get());
  300. }
  301. }
  302. else
  303. {
  304. jucerComp.reset();
  305. }
  306. resized();
  307. }
  308. void parentHierarchyChanged() override
  309. {
  310. updateContent();
  311. }
  312. bool isUsingJucerComp;
  313. String contentClassName, constructorParams;
  314. String jucerComponentFile;
  315. std::unique_ptr<TestComponent> jucerComp;
  316. };
  317. //==============================================================================
  318. class TabOrientationProperty : public ComponentChoiceProperty<TabbedComponent>
  319. {
  320. public:
  321. TabOrientationProperty (TabbedComponent* comp, JucerDocument& doc)
  322. : ComponentChoiceProperty<TabbedComponent> ("tab position", comp, doc)
  323. {
  324. choices.add ("Tabs at top");
  325. choices.add ("Tabs at bottom");
  326. choices.add ("Tabs at left");
  327. choices.add ("Tabs at right");
  328. }
  329. void setIndex (int newIndex)
  330. {
  331. const TabbedButtonBar::Orientation orientations[] = { TabbedButtonBar::TabsAtTop,
  332. TabbedButtonBar::TabsAtBottom,
  333. TabbedButtonBar::TabsAtLeft,
  334. TabbedButtonBar::TabsAtRight };
  335. document.perform (new TabOrienationChangeAction (component, *document.getComponentLayout(), orientations [newIndex]),
  336. "Change TabComponent orientation");
  337. }
  338. int getIndex() const
  339. {
  340. switch (component->getOrientation())
  341. {
  342. case TabbedButtonBar::TabsAtTop: return 0;
  343. case TabbedButtonBar::TabsAtBottom: return 1;
  344. case TabbedButtonBar::TabsAtLeft: return 2;
  345. case TabbedButtonBar::TabsAtRight: return 3;
  346. default: jassertfalse; break;
  347. }
  348. return 0;
  349. }
  350. private:
  351. class TabOrienationChangeAction : public ComponentUndoableAction<TabbedComponent>
  352. {
  353. public:
  354. TabOrienationChangeAction (TabbedComponent* const comp, ComponentLayout& l, const TabbedButtonBar::Orientation newState_)
  355. : ComponentUndoableAction<TabbedComponent> (comp, l),
  356. newState (newState_)
  357. {
  358. oldState = comp->getOrientation();
  359. }
  360. bool perform()
  361. {
  362. showCorrectTab();
  363. getComponent()->setOrientation (newState);
  364. changed();
  365. return true;
  366. }
  367. bool undo()
  368. {
  369. showCorrectTab();
  370. getComponent()->setOrientation (oldState);
  371. changed();
  372. return true;
  373. }
  374. TabbedButtonBar::Orientation newState, oldState;
  375. };
  376. };
  377. //==============================================================================
  378. class TabInitialTabProperty : public ComponentChoiceProperty<TabbedComponent>
  379. {
  380. public:
  381. TabInitialTabProperty (TabbedComponent* comp, JucerDocument& doc)
  382. : ComponentChoiceProperty<TabbedComponent> ("initial tab", comp, doc)
  383. {
  384. for (int i = 0; i < comp->getNumTabs(); ++i)
  385. choices.add ("Tab " + String (i) + ": \"" + comp->getTabNames() [i] + "\"");
  386. }
  387. void setIndex (int newIndex)
  388. {
  389. document.perform (new InitialTabChangeAction (component, *document.getComponentLayout(), newIndex),
  390. "Change initial tab");
  391. }
  392. int getIndex() const
  393. {
  394. return component->getCurrentTabIndex();
  395. }
  396. private:
  397. class InitialTabChangeAction : public ComponentUndoableAction<TabbedComponent>
  398. {
  399. public:
  400. InitialTabChangeAction (TabbedComponent* const comp, ComponentLayout& l, const int newValue_)
  401. : ComponentUndoableAction<TabbedComponent> (comp, l),
  402. newValue (newValue_)
  403. {
  404. oldValue = comp->getCurrentTabIndex();
  405. }
  406. bool perform()
  407. {
  408. showCorrectTab();
  409. getComponent()->setCurrentTabIndex (newValue);
  410. changed();
  411. return true;
  412. }
  413. bool undo()
  414. {
  415. showCorrectTab();
  416. getComponent()->setCurrentTabIndex (oldValue);
  417. changed();
  418. return true;
  419. }
  420. private:
  421. int newValue, oldValue;
  422. };
  423. };
  424. //==============================================================================
  425. class TabDepthProperty : public SliderPropertyComponent,
  426. private ChangeListener
  427. {
  428. public:
  429. TabDepthProperty (TabbedComponent* comp, JucerDocument& doc)
  430. : SliderPropertyComponent ("tab depth", 10.0, 80.0, 1.0, 1.0),
  431. component (comp),
  432. document (doc)
  433. {
  434. document.addChangeListener (this);
  435. }
  436. ~TabDepthProperty() override
  437. {
  438. document.removeChangeListener (this);
  439. }
  440. void setValue (double newValue) override
  441. {
  442. document.getUndoManager().undoCurrentTransactionOnly();
  443. document.perform (new TabDepthChangeAction (component, *document.getComponentLayout(), roundToInt (newValue)),
  444. "Change TabComponent tab depth");
  445. }
  446. double getValue() const override
  447. {
  448. return component->getTabBarDepth();
  449. }
  450. TabbedComponent* const component;
  451. JucerDocument& document;
  452. private:
  453. void changeListenerCallback (ChangeBroadcaster*) override
  454. {
  455. refresh();
  456. }
  457. class TabDepthChangeAction : public ComponentUndoableAction<TabbedComponent>
  458. {
  459. public:
  460. TabDepthChangeAction (TabbedComponent* const comp, ComponentLayout& l, const int newState_)
  461. : ComponentUndoableAction<TabbedComponent> (comp, l),
  462. newState (newState_)
  463. {
  464. oldState = comp->getTabBarDepth();
  465. }
  466. bool perform()
  467. {
  468. showCorrectTab();
  469. getComponent()->setTabBarDepth (newState);
  470. changed();
  471. return true;
  472. }
  473. bool undo()
  474. {
  475. showCorrectTab();
  476. getComponent()->setTabBarDepth (oldState);
  477. changed();
  478. return true;
  479. }
  480. int newState, oldState;
  481. };
  482. };
  483. //==============================================================================
  484. class TabAddTabProperty : public ButtonPropertyComponent
  485. {
  486. public:
  487. TabAddTabProperty (TabbedComponent* comp, JucerDocument& doc)
  488. : ButtonPropertyComponent ("add tab", false),
  489. component (comp),
  490. document (doc)
  491. {
  492. }
  493. void buttonClicked()
  494. {
  495. document.perform (new AddTabAction (component, *document.getComponentLayout()),
  496. "Add a new tab");
  497. }
  498. String getButtonText() const
  499. {
  500. return "Create a new tab";
  501. }
  502. TabbedComponent* const component;
  503. JucerDocument& document;
  504. private:
  505. class AddTabAction : public ComponentUndoableAction<TabbedComponent>
  506. {
  507. public:
  508. AddTabAction (TabbedComponent* const comp, ComponentLayout& l)
  509. : ComponentUndoableAction<TabbedComponent> (comp, l)
  510. {
  511. }
  512. bool perform()
  513. {
  514. showCorrectTab();
  515. addNewTab (getComponent());
  516. layout.getDocument()->refreshAllPropertyComps();
  517. changed();
  518. return true;
  519. }
  520. bool undo()
  521. {
  522. showCorrectTab();
  523. getComponent()->removeTab (getComponent()->getNumTabs() - 1);
  524. layout.getDocument()->refreshAllPropertyComps();
  525. changed();
  526. return true;
  527. }
  528. };
  529. };
  530. //==============================================================================
  531. class TabRemoveTabProperty : public ButtonPropertyComponent
  532. {
  533. public:
  534. TabRemoveTabProperty (TabbedComponent* comp, JucerDocument& doc)
  535. : ButtonPropertyComponent ("remove tab", true),
  536. component (comp),
  537. document (doc)
  538. {
  539. }
  540. void buttonClicked()
  541. {
  542. const StringArray names (component->getTabNames());
  543. PopupMenu m;
  544. for (int i = 0; i < component->getNumTabs(); ++i)
  545. m.addItem (i + 1, "Delete tab " + String (i)
  546. + ": \"" + names[i] + "\"");
  547. const int r = m.showAt (this);
  548. if (r > 0)
  549. {
  550. document.perform (new RemoveTabAction (component, *document.getComponentLayout(), r - 1),
  551. "Remove a tab");
  552. }
  553. }
  554. String getButtonText() const
  555. {
  556. return "Delete a tab...";
  557. }
  558. TabbedComponent* const component;
  559. JucerDocument& document;
  560. private:
  561. class RemoveTabAction : public ComponentUndoableAction<TabbedComponent>
  562. {
  563. public:
  564. RemoveTabAction (TabbedComponent* const comp, ComponentLayout& l, int indexToRemove_)
  565. : ComponentUndoableAction<TabbedComponent> (comp, l),
  566. indexToRemove (indexToRemove_)
  567. {
  568. previousState = getTabState (comp, indexToRemove);
  569. }
  570. bool perform()
  571. {
  572. showCorrectTab();
  573. getComponent()->removeTab (indexToRemove);
  574. layout.getDocument()->refreshAllPropertyComps();
  575. changed();
  576. return true;
  577. }
  578. bool undo()
  579. {
  580. showCorrectTab();
  581. addNewTab (getComponent(), indexToRemove);
  582. restoreTabState (getComponent(), indexToRemove, *previousState);
  583. layout.getDocument()->refreshAllPropertyComps();
  584. changed();
  585. return true;
  586. }
  587. private:
  588. int indexToRemove;
  589. std::unique_ptr<XmlElement> previousState;
  590. };
  591. };
  592. //==============================================================================
  593. class TabNameProperty : public ComponentTextProperty<TabbedComponent>
  594. {
  595. public:
  596. TabNameProperty (TabbedComponent* comp, JucerDocument& doc, const int tabIndex_)
  597. : ComponentTextProperty<TabbedComponent> ("name", 200, false, comp, doc),
  598. tabIndex (tabIndex_)
  599. {
  600. }
  601. void setText (const String& newText) override
  602. {
  603. document.perform (new TabNameChangeAction (component, *document.getComponentLayout(), tabIndex, newText),
  604. "Change tab name");
  605. }
  606. String getText() const override
  607. {
  608. return component->getTabNames() [tabIndex];
  609. }
  610. private:
  611. int tabIndex;
  612. class TabNameChangeAction : public ComponentUndoableAction<TabbedComponent>
  613. {
  614. public:
  615. TabNameChangeAction (TabbedComponent* const comp, ComponentLayout& l, const int tabIndex_, const String& newValue_)
  616. : ComponentUndoableAction<TabbedComponent> (comp, l),
  617. tabIndex (tabIndex_),
  618. newValue (newValue_)
  619. {
  620. oldValue = comp->getTabNames() [tabIndex];
  621. }
  622. bool perform()
  623. {
  624. showCorrectTab();
  625. getComponent()->setTabName (tabIndex, newValue);
  626. changed();
  627. return true;
  628. }
  629. bool undo()
  630. {
  631. showCorrectTab();
  632. getComponent()->setTabName (tabIndex, oldValue);
  633. changed();
  634. return true;
  635. }
  636. private:
  637. const int tabIndex;
  638. String newValue, oldValue;
  639. };
  640. };
  641. //==============================================================================
  642. class TabColourProperty : public JucerColourPropertyComponent,
  643. private ChangeListener
  644. {
  645. public:
  646. TabColourProperty (TabbedComponent* comp, JucerDocument& doc, const int tabIndex_)
  647. : JucerColourPropertyComponent ("colour", false),
  648. component (comp),
  649. document (doc),
  650. tabIndex (tabIndex_)
  651. {
  652. document.addChangeListener (this);
  653. }
  654. ~TabColourProperty() override
  655. {
  656. document.removeChangeListener (this);
  657. }
  658. void setColour (Colour newColour) override
  659. {
  660. document.getUndoManager().undoCurrentTransactionOnly();
  661. document.perform (new TabColourChangeAction (component, *document.getComponentLayout(), tabIndex, newColour),
  662. "Change tab colour");
  663. }
  664. Colour getColour() const override
  665. {
  666. return component->getTabBackgroundColour (tabIndex);
  667. }
  668. void resetToDefault() override
  669. {
  670. jassertfalse; // shouldn't get called
  671. }
  672. void changeListenerCallback (ChangeBroadcaster*) override { refresh(); }
  673. private:
  674. TabbedComponent* component;
  675. JucerDocument& document;
  676. int tabIndex;
  677. class TabColourChangeAction : public ComponentUndoableAction<TabbedComponent>
  678. {
  679. public:
  680. TabColourChangeAction (TabbedComponent* comp, ComponentLayout& l,
  681. int tabIndex_, Colour newValue_)
  682. : ComponentUndoableAction<TabbedComponent> (comp, l),
  683. tabIndex (tabIndex_),
  684. newValue (newValue_)
  685. {
  686. oldValue = comp->getTabBackgroundColour (tabIndex);
  687. }
  688. bool perform()
  689. {
  690. showCorrectTab();
  691. getComponent()->setTabBackgroundColour (tabIndex, newValue);
  692. changed();
  693. return true;
  694. }
  695. bool undo()
  696. {
  697. showCorrectTab();
  698. getComponent()->setTabBackgroundColour (tabIndex, oldValue);
  699. changed();
  700. return true;
  701. }
  702. private:
  703. const int tabIndex;
  704. Colour newValue, oldValue;
  705. };
  706. };
  707. //==============================================================================
  708. class TabContentTypeProperty : public ComponentChoiceProperty<TabbedComponent>
  709. {
  710. public:
  711. TabContentTypeProperty (TabbedComponent* comp, JucerDocument& doc, const int tabIndex_)
  712. : ComponentChoiceProperty<TabbedComponent> ("content type", comp, doc),
  713. tabIndex (tabIndex_)
  714. {
  715. choices.add ("Jucer content component");
  716. choices.add ("Named content component");
  717. }
  718. void setIndex (int newIndex)
  719. {
  720. document.perform (new TabContentTypeChangeAction (component, *document.getComponentLayout(), tabIndex, newIndex == 0),
  721. "Change tab content type");
  722. }
  723. int getIndex() const
  724. {
  725. return isTabUsingJucerComp (component, tabIndex) ? 0 : 1;
  726. }
  727. private:
  728. int tabIndex;
  729. class TabContentTypeChangeAction : public ComponentUndoableAction<TabbedComponent>
  730. {
  731. public:
  732. TabContentTypeChangeAction (TabbedComponent* const comp, ComponentLayout& l, const int tabIndex_, const bool newValue_)
  733. : ComponentUndoableAction<TabbedComponent> (comp, l),
  734. tabIndex (tabIndex_),
  735. newValue (newValue_)
  736. {
  737. oldValue = isTabUsingJucerComp (comp, tabIndex);
  738. }
  739. bool perform()
  740. {
  741. showCorrectTab();
  742. setTabUsingJucerComp (getComponent(), tabIndex, newValue);
  743. layout.getDocument()->refreshAllPropertyComps();
  744. changed();
  745. return true;
  746. }
  747. bool undo()
  748. {
  749. showCorrectTab();
  750. setTabUsingJucerComp (getComponent(), tabIndex, oldValue);
  751. layout.getDocument()->refreshAllPropertyComps();
  752. changed();
  753. return true;
  754. }
  755. private:
  756. int tabIndex;
  757. bool newValue, oldValue;
  758. };
  759. };
  760. //==============================================================================
  761. class TabJucerFileProperty : public FilePropertyComponent,
  762. private ChangeListener
  763. {
  764. public:
  765. TabJucerFileProperty (TabbedComponent* const comp, JucerDocument& doc, const int tabIndex_)
  766. : FilePropertyComponent ("jucer file", false, true),
  767. component (comp),
  768. document (doc),
  769. tabIndex (tabIndex_)
  770. {
  771. document.addChangeListener (this);
  772. }
  773. ~TabJucerFileProperty() override
  774. {
  775. document.removeChangeListener (this);
  776. }
  777. //==============================================================================
  778. void setFile (const File& newFile) override
  779. {
  780. document.perform (new JucerCompFileChangeAction (component, *document.getComponentLayout(), tabIndex,
  781. newFile.getRelativePathFrom (document.getCppFile().getParentDirectory())
  782. .replaceCharacter ('\\', '/')),
  783. "Change tab component file");
  784. }
  785. File getFile() const override
  786. {
  787. return document.getCppFile().getSiblingFile (getTabJucerFile (component, tabIndex));
  788. }
  789. private:
  790. void changeListenerCallback (ChangeBroadcaster*) override { refresh(); }
  791. TabbedComponent* const component;
  792. JucerDocument& document;
  793. int tabIndex;
  794. class JucerCompFileChangeAction : public ComponentUndoableAction<TabbedComponent>
  795. {
  796. public:
  797. JucerCompFileChangeAction (TabbedComponent* const comp, ComponentLayout& l, const int tabIndex_, const String& newState_)
  798. : ComponentUndoableAction<TabbedComponent> (comp, l),
  799. tabIndex (tabIndex_),
  800. newState (newState_)
  801. {
  802. oldState = getTabJucerFile (comp, tabIndex);
  803. }
  804. bool perform()
  805. {
  806. showCorrectTab();
  807. setTabJucerFile (getComponent(), tabIndex, newState);
  808. changed();
  809. return true;
  810. }
  811. bool undo()
  812. {
  813. showCorrectTab();
  814. setTabJucerFile (getComponent(), tabIndex, oldState);
  815. changed();
  816. return true;
  817. }
  818. int tabIndex;
  819. String newState, oldState;
  820. };
  821. };
  822. //==============================================================================
  823. class TabContentClassProperty : public ComponentTextProperty<TabbedComponent>
  824. {
  825. public:
  826. TabContentClassProperty (TabbedComponent* comp, JucerDocument& doc, const int tabIndex_)
  827. : ComponentTextProperty<TabbedComponent> ("content class", 256, false, comp, doc),
  828. tabIndex (tabIndex_)
  829. {
  830. }
  831. void setText (const String& newText) override
  832. {
  833. document.perform (new TabClassNameChangeAction (component, *document.getComponentLayout(), tabIndex, newText),
  834. "Change TabbedComponent content class");
  835. }
  836. String getText() const override
  837. {
  838. return getTabClassName (component, tabIndex);
  839. }
  840. private:
  841. int tabIndex;
  842. class TabClassNameChangeAction : public ComponentUndoableAction<TabbedComponent>
  843. {
  844. public:
  845. TabClassNameChangeAction (TabbedComponent* const comp, ComponentLayout& l, const int tabIndex_, const String& newValue_)
  846. : ComponentUndoableAction<TabbedComponent> (comp, l),
  847. tabIndex (tabIndex_),
  848. newValue (newValue_)
  849. {
  850. oldValue = getTabClassName (comp, tabIndex);
  851. }
  852. bool perform()
  853. {
  854. showCorrectTab();
  855. setTabClassName (getComponent(), tabIndex, newValue);
  856. changed();
  857. layout.getDocument()->refreshAllPropertyComps();
  858. return true;
  859. }
  860. bool undo()
  861. {
  862. showCorrectTab();
  863. setTabClassName (getComponent(), tabIndex, oldValue);
  864. changed();
  865. layout.getDocument()->refreshAllPropertyComps();
  866. return true;
  867. }
  868. int tabIndex;
  869. String newValue, oldValue;
  870. };
  871. };
  872. //==============================================================================
  873. class TabContentConstructorParamsProperty : public ComponentTextProperty<TabbedComponent>
  874. {
  875. public:
  876. TabContentConstructorParamsProperty (TabbedComponent* comp, JucerDocument& doc, const int tabIndex_)
  877. : ComponentTextProperty<TabbedComponent> ("constructor params", 512, false, comp, doc),
  878. tabIndex (tabIndex_)
  879. {
  880. }
  881. void setText (const String& newText) override
  882. {
  883. document.perform (new TabConstructorParamChangeAction (component, *document.getComponentLayout(), tabIndex, newText),
  884. "Change TabbedComponent content constructor param");
  885. }
  886. String getText() const override
  887. {
  888. return getTabConstructorParams (component, tabIndex);
  889. }
  890. private:
  891. int tabIndex;
  892. class TabConstructorParamChangeAction : public ComponentUndoableAction<TabbedComponent>
  893. {
  894. public:
  895. TabConstructorParamChangeAction (TabbedComponent* const comp, ComponentLayout& l, const int tabIndex_, const String& newValue_)
  896. : ComponentUndoableAction<TabbedComponent> (comp, l),
  897. tabIndex (tabIndex_),
  898. newValue (newValue_)
  899. {
  900. oldValue = getTabConstructorParams (comp, tabIndex);
  901. }
  902. bool perform()
  903. {
  904. showCorrectTab();
  905. setTabConstructorParams (getComponent(), tabIndex, newValue);
  906. changed();
  907. layout.getDocument()->refreshAllPropertyComps();
  908. return true;
  909. }
  910. bool undo()
  911. {
  912. showCorrectTab();
  913. setTabConstructorParams (getComponent(), tabIndex, oldValue);
  914. changed();
  915. layout.getDocument()->refreshAllPropertyComps();
  916. return true;
  917. }
  918. int tabIndex;
  919. String newValue, oldValue;
  920. };
  921. };
  922. //==============================================================================
  923. class TabMoveProperty : public ButtonPropertyComponent
  924. {
  925. public:
  926. TabMoveProperty (TabbedComponent* comp, JucerDocument& doc,
  927. const int tabIndex_, const int totalNumTabs_)
  928. : ButtonPropertyComponent ("move tab", false),
  929. component (comp),
  930. document (doc),
  931. tabIndex (tabIndex_),
  932. totalNumTabs (totalNumTabs_)
  933. {
  934. }
  935. void buttonClicked()
  936. {
  937. PopupMenu m;
  938. m.addItem (1, "Move this tab up", tabIndex > 0);
  939. m.addItem (2, "Move this tab down", tabIndex < totalNumTabs - 1);
  940. const int r = m.showAt (this);
  941. if (r != 0)
  942. document.perform (new MoveTabAction (component, *document.getComponentLayout(), tabIndex, tabIndex + (r == 2 ? 1 : -1)),
  943. "Move a tab");
  944. }
  945. String getButtonText() const
  946. {
  947. return "Move this tab...";
  948. }
  949. TabbedComponent* const component;
  950. JucerDocument& document;
  951. const int tabIndex, totalNumTabs;
  952. private:
  953. class MoveTabAction : public ComponentUndoableAction<TabbedComponent>
  954. {
  955. public:
  956. MoveTabAction (TabbedComponent* const comp, ComponentLayout& l,
  957. const int oldIndex_, const int newIndex_)
  958. : ComponentUndoableAction<TabbedComponent> (comp, l),
  959. oldIndex (oldIndex_),
  960. newIndex (newIndex_)
  961. {
  962. }
  963. void move (int from, int to)
  964. {
  965. showCorrectTab();
  966. auto state = getTabState (getComponent(), from);
  967. getComponent()->removeTab (from);
  968. addNewTab (getComponent(), to);
  969. restoreTabState (getComponent(), to, *state);
  970. layout.getDocument()->refreshAllPropertyComps();
  971. changed();
  972. }
  973. bool perform()
  974. {
  975. move (oldIndex, newIndex);
  976. return true;
  977. }
  978. bool undo()
  979. {
  980. move (newIndex, oldIndex);
  981. return true;
  982. }
  983. private:
  984. const int oldIndex, newIndex;
  985. };
  986. };
  987. };