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.

1184 lines
40KB

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