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.

1195 lines
41KB

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