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.

1193 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 (const Colour& newColour)
  661. {
  662. document.getUndoManager().undoCurrentTransactionOnly();
  663. document.perform (new TabColourChangeAction (component, *document.getComponentLayout(), tabIndex, newColour),
  664. "Change tab colour");
  665. }
  666. Colour getColour() const
  667. {
  668. return component->getTabBackgroundColour (tabIndex);
  669. }
  670. void resetToDefault()
  671. {
  672. jassertfalse; // shouldn't get called
  673. }
  674. void changeListenerCallback (ChangeBroadcaster*) { refresh(); }
  675. private:
  676. TabbedComponent* component;
  677. JucerDocument& document;
  678. int tabIndex;
  679. class TabColourChangeAction : public ComponentUndoableAction <TabbedComponent>
  680. {
  681. public:
  682. TabColourChangeAction (TabbedComponent* const comp, ComponentLayout& layout, const int tabIndex_, const Colour& newValue_)
  683. : ComponentUndoableAction <TabbedComponent> (comp, layout),
  684. tabIndex (tabIndex_),
  685. newValue (newValue_)
  686. {
  687. oldValue = comp->getTabBackgroundColour (tabIndex);
  688. }
  689. bool perform()
  690. {
  691. showCorrectTab();
  692. getComponent()->setTabBackgroundColour (tabIndex, newValue);
  693. changed();
  694. return true;
  695. }
  696. bool undo()
  697. {
  698. showCorrectTab();
  699. getComponent()->setTabBackgroundColour (tabIndex, oldValue);
  700. changed();
  701. return true;
  702. }
  703. private:
  704. const int tabIndex;
  705. Colour newValue, oldValue;
  706. };
  707. };
  708. //==============================================================================
  709. class TabContentTypeProperty : public ComponentChoiceProperty <TabbedComponent>
  710. {
  711. public:
  712. TabContentTypeProperty (TabbedComponent* comp, JucerDocument& document, const int tabIndex_)
  713. : ComponentChoiceProperty <TabbedComponent> ("content type", comp, document),
  714. tabIndex (tabIndex_)
  715. {
  716. choices.add ("Jucer content component");
  717. choices.add ("Named content component");
  718. }
  719. void setIndex (int newIndex)
  720. {
  721. document.perform (new TabContentTypeChangeAction (component, *document.getComponentLayout(), tabIndex, newIndex == 0),
  722. "Change tab content type");
  723. }
  724. int getIndex() const
  725. {
  726. return isTabUsingJucerComp (component, tabIndex) ? 0 : 1;
  727. }
  728. private:
  729. int tabIndex;
  730. class TabContentTypeChangeAction : public ComponentUndoableAction <TabbedComponent>
  731. {
  732. public:
  733. TabContentTypeChangeAction (TabbedComponent* const comp, ComponentLayout& layout, const int tabIndex_, const bool newValue_)
  734. : ComponentUndoableAction <TabbedComponent> (comp, layout),
  735. tabIndex (tabIndex_),
  736. newValue (newValue_)
  737. {
  738. oldValue = isTabUsingJucerComp (comp, tabIndex);
  739. }
  740. bool perform()
  741. {
  742. showCorrectTab();
  743. setTabUsingJucerComp (getComponent(), tabIndex, newValue);
  744. layout.getDocument()->refreshAllPropertyComps();
  745. changed();
  746. return true;
  747. }
  748. bool undo()
  749. {
  750. showCorrectTab();
  751. setTabUsingJucerComp (getComponent(), tabIndex, oldValue);
  752. layout.getDocument()->refreshAllPropertyComps();
  753. changed();
  754. return true;
  755. }
  756. private:
  757. int tabIndex;
  758. bool newValue, oldValue;
  759. };
  760. };
  761. //==============================================================================
  762. class TabJucerFileProperty : public FilePropertyComponent,
  763. public ChangeListener
  764. {
  765. public:
  766. TabJucerFileProperty (TabbedComponent* const comp, JucerDocument& doc, const int tabIndex_)
  767. : FilePropertyComponent ("jucer file", false, true),
  768. component (comp),
  769. document (doc),
  770. tabIndex (tabIndex_)
  771. {
  772. document.addChangeListener (this);
  773. }
  774. ~TabJucerFileProperty()
  775. {
  776. document.removeChangeListener (this);
  777. }
  778. //==============================================================================
  779. void setFile (const File& newFile)
  780. {
  781. document.perform (new JucerCompFileChangeAction (component, *document.getComponentLayout(), tabIndex,
  782. newFile.getRelativePathFrom (document.getCppFile().getParentDirectory())
  783. .replaceCharacter ('\\', '/')),
  784. "Change tab component file");
  785. }
  786. File getFile() const
  787. {
  788. return document.getCppFile().getSiblingFile (getTabJucerFile (component, tabIndex));
  789. }
  790. void changeListenerCallback (ChangeBroadcaster*) { refresh(); }
  791. private:
  792. TabbedComponent* const component;
  793. JucerDocument& document;
  794. int tabIndex;
  795. class JucerCompFileChangeAction : public ComponentUndoableAction <TabbedComponent>
  796. {
  797. public:
  798. JucerCompFileChangeAction (TabbedComponent* const comp, ComponentLayout& layout, const int tabIndex_, const String& newState_)
  799. : ComponentUndoableAction <TabbedComponent> (comp, layout),
  800. tabIndex (tabIndex_),
  801. newState (newState_)
  802. {
  803. oldState = getTabJucerFile (comp, tabIndex);
  804. }
  805. bool perform()
  806. {
  807. showCorrectTab();
  808. setTabJucerFile (getComponent(), tabIndex, newState);
  809. changed();
  810. return true;
  811. }
  812. bool undo()
  813. {
  814. showCorrectTab();
  815. setTabJucerFile (getComponent(), tabIndex, oldState);
  816. changed();
  817. return true;
  818. }
  819. int tabIndex;
  820. String newState, oldState;
  821. };
  822. };
  823. //==============================================================================
  824. class TabContentClassProperty : public ComponentTextProperty <TabbedComponent>
  825. {
  826. public:
  827. TabContentClassProperty (TabbedComponent* comp, JucerDocument& document, const int tabIndex_)
  828. : ComponentTextProperty <TabbedComponent> ("content class", 256, false, comp, document),
  829. tabIndex (tabIndex_)
  830. {
  831. }
  832. void setText (const String& newText)
  833. {
  834. document.perform (new TabClassNameChangeAction (component, *document.getComponentLayout(), tabIndex, newText),
  835. "Change TabbedComponent content class");
  836. }
  837. String getText() const
  838. {
  839. return getTabClassName (component, tabIndex);
  840. }
  841. private:
  842. int tabIndex;
  843. class TabClassNameChangeAction : public ComponentUndoableAction <TabbedComponent>
  844. {
  845. public:
  846. TabClassNameChangeAction (TabbedComponent* const comp, ComponentLayout& layout, const int tabIndex_, const String& newValue_)
  847. : ComponentUndoableAction <TabbedComponent> (comp, layout),
  848. tabIndex (tabIndex_),
  849. newValue (newValue_)
  850. {
  851. oldValue = getTabClassName (comp, tabIndex);
  852. }
  853. bool perform()
  854. {
  855. showCorrectTab();
  856. setTabClassName (getComponent(), tabIndex, newValue);
  857. changed();
  858. layout.getDocument()->refreshAllPropertyComps();
  859. return true;
  860. }
  861. bool undo()
  862. {
  863. showCorrectTab();
  864. setTabClassName (getComponent(), tabIndex, oldValue);
  865. changed();
  866. layout.getDocument()->refreshAllPropertyComps();
  867. return true;
  868. }
  869. int tabIndex;
  870. String newValue, oldValue;
  871. };
  872. };
  873. //==============================================================================
  874. class TabContentConstructorParamsProperty : public ComponentTextProperty <TabbedComponent>
  875. {
  876. public:
  877. TabContentConstructorParamsProperty (TabbedComponent* comp, JucerDocument& document, const int tabIndex_)
  878. : ComponentTextProperty <TabbedComponent> ("constructor params", 512, false, comp, document),
  879. tabIndex (tabIndex_)
  880. {
  881. }
  882. void setText (const String& newText)
  883. {
  884. document.perform (new TabConstructorParamChangeAction (component, *document.getComponentLayout(), tabIndex, newText),
  885. "Change TabbedComponent content constructor param");
  886. }
  887. String getText() const
  888. {
  889. return getTabConstructorParams (component, tabIndex);
  890. }
  891. private:
  892. int tabIndex;
  893. class TabConstructorParamChangeAction : public ComponentUndoableAction <TabbedComponent>
  894. {
  895. public:
  896. TabConstructorParamChangeAction (TabbedComponent* const comp, ComponentLayout& layout, const int tabIndex_, const String& newValue_)
  897. : ComponentUndoableAction <TabbedComponent> (comp, layout),
  898. tabIndex (tabIndex_),
  899. newValue (newValue_)
  900. {
  901. oldValue = getTabConstructorParams (comp, tabIndex);
  902. }
  903. bool perform()
  904. {
  905. showCorrectTab();
  906. setTabConstructorParams (getComponent(), tabIndex, newValue);
  907. changed();
  908. layout.getDocument()->refreshAllPropertyComps();
  909. return true;
  910. }
  911. bool undo()
  912. {
  913. showCorrectTab();
  914. setTabConstructorParams (getComponent(), tabIndex, oldValue);
  915. changed();
  916. layout.getDocument()->refreshAllPropertyComps();
  917. return true;
  918. }
  919. int tabIndex;
  920. String newValue, oldValue;
  921. };
  922. };
  923. //==============================================================================
  924. class TabMoveProperty : public ButtonPropertyComponent
  925. {
  926. public:
  927. TabMoveProperty (TabbedComponent* comp, JucerDocument& doc,
  928. const int tabIndex_, const int totalNumTabs_)
  929. : ButtonPropertyComponent ("move tab", false),
  930. component (comp),
  931. document (doc),
  932. tabIndex (tabIndex_),
  933. totalNumTabs (totalNumTabs_)
  934. {
  935. }
  936. void buttonClicked()
  937. {
  938. PopupMenu m;
  939. m.addItem (1, "Move this tab up", tabIndex > 0);
  940. m.addItem (2, "Move this tab down", tabIndex < totalNumTabs - 1);
  941. const int r = m.showAt (this);
  942. if (r != 0)
  943. document.perform (new MoveTabAction (component, *document.getComponentLayout(), tabIndex, tabIndex + (r == 2 ? 1 : -1)),
  944. "Move a tab");
  945. }
  946. String getButtonText() const
  947. {
  948. return "Move this tab...";
  949. }
  950. TabbedComponent* const component;
  951. JucerDocument& document;
  952. const int tabIndex, totalNumTabs;
  953. private:
  954. class MoveTabAction : public ComponentUndoableAction <TabbedComponent>
  955. {
  956. public:
  957. MoveTabAction (TabbedComponent* const comp, ComponentLayout& layout,
  958. const int oldIndex_, const int newIndex_)
  959. : ComponentUndoableAction <TabbedComponent> (comp, layout),
  960. oldIndex (oldIndex_),
  961. newIndex (newIndex_)
  962. {
  963. }
  964. void move (int from, int to)
  965. {
  966. showCorrectTab();
  967. ScopedPointer<XmlElement> state (getTabState (getComponent(), from));
  968. getComponent()->removeTab (from);
  969. addNewTab (getComponent(), to);
  970. restoreTabState (getComponent(), to, *state);
  971. layout.getDocument()->refreshAllPropertyComps();
  972. changed();
  973. }
  974. bool perform()
  975. {
  976. move (oldIndex, newIndex);
  977. return true;
  978. }
  979. bool undo()
  980. {
  981. move (newIndex, oldIndex);
  982. return true;
  983. }
  984. private:
  985. const int oldIndex, newIndex;
  986. };
  987. };
  988. };