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.

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