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.

1185 lines
40KB

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