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.

1183 lines
40KB

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