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.

1195 lines
41KB

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