The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1194 lines
41KB

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