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.

753 lines
24KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-10 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. #include "../../jucer_Headers.h"
  19. #include "jucer_ComponentEditor.h"
  20. #include "../jucer_JucerTreeViewBase.h"
  21. //==============================================================================
  22. namespace ComponentTree
  23. {
  24. //==============================================================================
  25. class Base : public JucerTreeViewBase,
  26. public ValueTree::Listener,
  27. public ChangeListener
  28. {
  29. public:
  30. Base (ComponentEditor& editor_)
  31. : editor (editor_)
  32. {
  33. editor.getCanvas()->getSelection().addChangeListener (this);
  34. }
  35. ~Base()
  36. {
  37. editor.getCanvas()->getSelection().removeChangeListener (this);
  38. }
  39. //==============================================================================
  40. void valueTreePropertyChanged (ValueTree& tree, const var::identifier& property) {}
  41. void valueTreeParentChanged (ValueTree& tree) {}
  42. void valueTreeChildrenChanged (ValueTree& tree) {}
  43. const String getUniqueName() const
  44. {
  45. jassert (getItemId().isNotEmpty());
  46. return getItemId();
  47. }
  48. //==============================================================================
  49. void itemOpennessChanged (bool isNowOpen)
  50. {
  51. if (isNowOpen)
  52. refreshSubItems();
  53. }
  54. virtual void refreshSubItems() = 0;
  55. virtual const String getItemId() const = 0;
  56. void setName (const String& newName) {}
  57. void itemClicked (const MouseEvent& e) {}
  58. void itemDoubleClicked (const MouseEvent& e) {}
  59. void itemSelectionChanged (bool isNowSelected)
  60. {
  61. if (isNowSelected)
  62. editor.getCanvas()->getSelection().addToSelection (getItemId());
  63. else
  64. editor.getCanvas()->getSelection().deselect (getItemId());
  65. }
  66. void changeListenerCallback (void*) { updateSelectionState(); }
  67. void updateSelectionState()
  68. {
  69. setSelected (editor.getCanvas()->getSelection().isSelected (getItemId()), false);
  70. }
  71. bool isMissing() { return false; }
  72. const String getTooltip() { return String::empty; }
  73. protected:
  74. //==============================================================================
  75. ComponentEditor& editor;
  76. };
  77. //==============================================================================
  78. class ComponentItem : public Base
  79. {
  80. public:
  81. ComponentItem (ComponentEditor& editor_, const ValueTree& componentState_)
  82. : Base (editor_), componentState (componentState_)
  83. {
  84. componentState.addListener (this);
  85. updateSelectionState();
  86. }
  87. ~ComponentItem()
  88. {
  89. componentState.removeListener (this);
  90. }
  91. //==============================================================================
  92. const String getItemId() const { return componentState [ComponentDocument::idProperty]; }
  93. bool mightContainSubItems() { return false; }
  94. void refreshSubItems() {}
  95. const String getDisplayName() const { return getRenamingName(); }
  96. const String getRenamingName() const { return componentState [ComponentDocument::memberNameProperty]; }
  97. Image* getIcon() const { return LookAndFeel::getDefaultLookAndFeel().getDefaultDocumentFileImage(); }
  98. const String getDragSourceDescription() { return componentItemDragType; }
  99. void valueTreePropertyChanged (ValueTree& tree, const var::identifier& property)
  100. {
  101. if (property == ComponentDocument::memberNameProperty)
  102. repaintItem();
  103. }
  104. private:
  105. //==============================================================================
  106. ValueTree componentState;
  107. };
  108. //==============================================================================
  109. class ComponentList : public Base
  110. {
  111. public:
  112. ComponentList (ComponentEditor& editor_)
  113. : Base (editor_), componentTree (editor.getDocument().getComponentGroup())
  114. {
  115. componentTree.addListener (this);
  116. }
  117. ~ComponentList()
  118. {
  119. componentTree.removeListener (this);
  120. }
  121. //==============================================================================
  122. const String getItemId() const { return "components"; }
  123. bool mightContainSubItems() { return true; }
  124. void valueTreeChildrenChanged (ValueTree& tree)
  125. {
  126. if (tree == componentTree)
  127. refreshSubItems();
  128. }
  129. void refreshSubItems()
  130. {
  131. ScopedPointer <XmlElement> openness (getOpennessState());
  132. clearSubItems();
  133. ComponentDocument& doc = editor.getDocument();
  134. const int num = doc.getNumComponents();
  135. for (int i = 0; i < num; ++i)
  136. addSubItem (new ComponentItem (editor, doc.getComponent (i)));
  137. if (openness != 0)
  138. restoreOpennessState (*openness);
  139. }
  140. const String getDisplayName() const { return getRenamingName(); }
  141. const String getRenamingName() const { return "Components"; }
  142. Image* getIcon() const { return LookAndFeel::getDefaultLookAndFeel().getDefaultFolderImage(); }
  143. const String getDragSourceDescription() { return String::empty; }
  144. private:
  145. ValueTree componentTree;
  146. };
  147. //==============================================================================
  148. class MarkerItem : public Base
  149. {
  150. public:
  151. MarkerItem (ComponentEditor& editor_, const ValueTree& markerState_)
  152. : Base (editor_), markerState (markerState_)
  153. {
  154. markerState.addListener (this);
  155. updateSelectionState();
  156. }
  157. ~MarkerItem()
  158. {
  159. markerState.removeListener (this);
  160. }
  161. //==============================================================================
  162. const String getItemId() const { return markerState [ComponentDocument::idProperty]; }
  163. bool mightContainSubItems() { return false; }
  164. void refreshSubItems() {}
  165. const String getDisplayName() const { return getRenamingName(); }
  166. const String getRenamingName() const { return markerState [ComponentDocument::markerNameProperty]; }
  167. Image* getIcon() const { return LookAndFeel::getDefaultLookAndFeel().getDefaultDocumentFileImage(); }
  168. const String getDragSourceDescription() { return componentItemDragType; }
  169. void valueTreePropertyChanged (ValueTree& tree, const var::identifier& property)
  170. {
  171. if (property == ComponentDocument::markerNameProperty)
  172. repaintItem();
  173. }
  174. private:
  175. //==============================================================================
  176. ValueTree markerState;
  177. };
  178. //==============================================================================
  179. class MarkerList : public Base
  180. {
  181. public:
  182. MarkerList (ComponentEditor& editor_, bool isX_)
  183. : Base (editor_), markerList (editor_.getDocument().getMarkerList (isX_).getGroup()), isX (isX_)
  184. {
  185. markerList.addListener (this);
  186. }
  187. ~MarkerList()
  188. {
  189. markerList.removeListener (this);
  190. }
  191. //==============================================================================
  192. const String getItemId() const { return isX ? "markersX" : "markersY"; }
  193. bool mightContainSubItems() { return true; }
  194. void valueTreeChildrenChanged (ValueTree& tree)
  195. {
  196. refreshSubItems();
  197. }
  198. void refreshSubItems()
  199. {
  200. ScopedPointer <XmlElement> openness (getOpennessState());
  201. clearSubItems();
  202. ComponentDocument::MarkerList& markers = editor.getDocument().getMarkerList (isX);
  203. const int num = markers.size();
  204. for (int i = 0; i < num; ++i)
  205. addSubItem (new MarkerItem (editor, markers.getMarker (i)));
  206. if (openness != 0)
  207. restoreOpennessState (*openness);
  208. }
  209. const String getDisplayName() const { return getRenamingName(); }
  210. const String getRenamingName() const { return isX ? "Markers (X-axis)" : "Markers (Y-axis)"; }
  211. Image* getIcon() const { return LookAndFeel::getDefaultLookAndFeel().getDefaultFolderImage(); }
  212. const String getDragSourceDescription() { return String::empty; }
  213. private:
  214. ValueTree markerList;
  215. bool isX;
  216. };
  217. //==============================================================================
  218. class Root : public Base
  219. {
  220. public:
  221. Root (ComponentEditor& editor_) : Base (editor_) {}
  222. ~Root() {}
  223. //==============================================================================
  224. const String getItemId() const { return "root"; }
  225. bool mightContainSubItems() { return true; }
  226. void refreshSubItems()
  227. {
  228. ScopedPointer <XmlElement> openness (getOpennessState());
  229. clearSubItems();
  230. addSubItem (new ComponentList (editor));
  231. addSubItem (new MarkerList (editor, true));
  232. addSubItem (new MarkerList (editor, false));
  233. if (openness != 0)
  234. restoreOpennessState (*openness);
  235. }
  236. const String getDisplayName() const { return getRenamingName(); }
  237. const String getRenamingName() const { return editor.getDocument().getClassName().toString(); }
  238. Image* getIcon() const { return LookAndFeel::getDefaultLookAndFeel().getDefaultFolderImage(); }
  239. const String getDragSourceDescription() { return String::empty; }
  240. };
  241. }
  242. //==============================================================================
  243. class ComponentEditor::ClassInfoHolder : public Component
  244. {
  245. public:
  246. ClassInfoHolder (ComponentEditor& editor_)
  247. : editor (editor_)
  248. {
  249. addAndMakeVisible (panel = new PropertyPanelWithTooltips());
  250. Array <PropertyComponent*> props;
  251. editor.getDocument().createClassProperties (props);
  252. panel->getPanel()->addSection ("Component Properties", props, true);
  253. }
  254. ~ClassInfoHolder()
  255. {
  256. deleteAllChildren();
  257. }
  258. void resized()
  259. {
  260. panel->setBounds (getLocalBounds());
  261. }
  262. private:
  263. ComponentEditor& editor;
  264. PropertyPanelWithTooltips* panel;
  265. };
  266. //==============================================================================
  267. class ComponentEditor::LayoutEditorHolder : public Component,
  268. public ToolbarItemFactory
  269. {
  270. public:
  271. LayoutEditorHolder (ComponentEditor& editor_)
  272. : editor (editor_), infoPanel (0), tree (0)
  273. {
  274. addAndMakeVisible (toolbar = new Toolbar());
  275. toolbar->addDefaultItems (*this);
  276. toolbar->setStyle (Toolbar::textOnly);
  277. addAndMakeVisible (viewport = new Viewport());
  278. addChildComponent (tree = new TreeView());
  279. tree->setRootItemVisible (true);
  280. tree->setMultiSelectEnabled (true);
  281. tree->setDefaultOpenness (true);
  282. tree->setColour (TreeView::backgroundColourId, Colours::white);
  283. tree->setIndentSize (15);
  284. }
  285. ~LayoutEditorHolder()
  286. {
  287. tree->deleteRootItem();
  288. deleteAndZero (infoPanel);
  289. deleteAllChildren();
  290. }
  291. void createCanvas()
  292. {
  293. viewport->setViewedComponent (new ComponentEditorCanvas (editor));
  294. addAndMakeVisible (infoPanel = new InfoPanel (editor));
  295. tree->setRootItem (new ComponentTree::Root (editor));
  296. resized();
  297. }
  298. void resized()
  299. {
  300. const int toolbarHeight = 22;
  301. toolbar->setBounds (0, 0, getWidth(), toolbarHeight);
  302. int infoPanelWidth = 200;
  303. if (infoPanel != 0 && infoPanel->isVisible())
  304. infoPanel->setBounds (getWidth() - infoPanelWidth, toolbar->getBottom(), infoPanelWidth, getHeight() - toolbar->getBottom());
  305. else
  306. infoPanelWidth = 0;
  307. if (tree->isVisible())
  308. {
  309. tree->setBounds (0, toolbar->getBottom(), infoPanelWidth, getHeight() - toolbar->getBottom());
  310. viewport->setBounds (infoPanelWidth, toolbar->getBottom(), getWidth() - infoPanelWidth * 2, getHeight() - toolbar->getBottom());
  311. }
  312. else
  313. {
  314. viewport->setBounds (0, toolbar->getBottom(), getWidth() - infoPanelWidth, getHeight() - toolbar->getBottom());
  315. }
  316. }
  317. void showOrHideProperties()
  318. {
  319. infoPanel->setVisible (! infoPanel->isVisible());
  320. resized();
  321. }
  322. void showOrHideTree()
  323. {
  324. tree->setVisible (! tree->isVisible());
  325. resized();
  326. }
  327. Viewport* getViewport() const { return viewport; }
  328. //==============================================================================
  329. enum DemoToolbarItemIds
  330. {
  331. createComponent = 1,
  332. showInfo = 2,
  333. showComponentTree = 3,
  334. };
  335. void getAllToolbarItemIds (Array <int>& ids)
  336. {
  337. ids.add (createComponent);
  338. ids.add (showInfo);
  339. ids.add (showComponentTree);
  340. ids.add (separatorBarId);
  341. ids.add (spacerId);
  342. ids.add (flexibleSpacerId);
  343. }
  344. void getDefaultItemSet (Array <int>& ids)
  345. {
  346. ids.add (createComponent);
  347. ids.add (flexibleSpacerId);
  348. ids.add (showInfo);
  349. ids.add (showComponentTree);
  350. }
  351. ToolbarItemComponent* createItem (int itemId)
  352. {
  353. String name;
  354. int commandId = 0;
  355. switch (itemId)
  356. {
  357. case createComponent: name = "new"; break;
  358. case showInfo: name = "info"; commandId = CommandIDs::showOrHideProperties; break;
  359. case showComponentTree: name = "tree"; commandId = CommandIDs::showOrHideTree; break;
  360. default: jassertfalse; return 0;
  361. }
  362. ToolbarButton* b = new ToolbarButton (itemId, name, new DrawablePath(), 0);
  363. b->setCommandToTrigger (commandManager, commandId, true);
  364. return b;
  365. }
  366. private:
  367. //==============================================================================
  368. class InfoPanel : public Component,
  369. public ChangeListener
  370. {
  371. public:
  372. InfoPanel (ComponentEditor& editor_)
  373. : editor (editor_)
  374. {
  375. setOpaque (true);
  376. addAndMakeVisible (props = new PropertyPanel());
  377. editor.getCanvas()->getSelection().addChangeListener (this);
  378. }
  379. ~InfoPanel()
  380. {
  381. editor.getCanvas()->getSelection().removeChangeListener (this);
  382. props->clear();
  383. deleteAllChildren();
  384. }
  385. void changeListenerCallback (void*)
  386. {
  387. Array <PropertyComponent*> newComps;
  388. editor.getCanvas()->getSelectedItemProperties (newComps);
  389. props->clear();
  390. props->addProperties (newComps);
  391. }
  392. void paint (Graphics& g)
  393. {
  394. g.fillAll (Colour::greyLevel (0.92f));
  395. }
  396. void resized()
  397. {
  398. props->setSize (getWidth(), getHeight());
  399. }
  400. private:
  401. ComponentEditor& editor;
  402. PropertyPanel* props;
  403. };
  404. Toolbar* toolbar;
  405. ComponentEditor& editor;
  406. Viewport* viewport;
  407. InfoPanel* infoPanel;
  408. TreeView* tree;
  409. };
  410. //==============================================================================
  411. class ComponentEditor::BackgroundEditorHolder : public Component
  412. {
  413. public:
  414. BackgroundEditorHolder (ComponentEditor& editor_)
  415. : editor (editor_)
  416. {
  417. }
  418. ~BackgroundEditorHolder()
  419. {
  420. }
  421. private:
  422. ComponentEditor& editor;
  423. };
  424. //==============================================================================
  425. class ComponentEditor::CodeEditorHolder : public Component
  426. {
  427. public:
  428. CodeEditorHolder (ComponentEditor& editor_)
  429. : editor (editor_)
  430. {
  431. }
  432. ~CodeEditorHolder()
  433. {
  434. }
  435. private:
  436. ComponentEditor& editor;
  437. };
  438. //==============================================================================
  439. ComponentEditor::ComponentEditor (OpenDocumentManager::Document* document,
  440. Project* project_, ComponentDocument* componentDocument_)
  441. : DocumentEditorComponent (document),
  442. project (project_),
  443. componentDocument (componentDocument_),
  444. classInfoHolder (0),
  445. layoutEditorHolder (0),
  446. backgroundEditorHolder (0),
  447. codeEditorHolder (0)
  448. {
  449. setOpaque (true);
  450. if (componentDocument != 0)
  451. {
  452. classInfoHolder = new ClassInfoHolder (*this);
  453. layoutEditorHolder = new LayoutEditorHolder (*this);
  454. backgroundEditorHolder = new BackgroundEditorHolder (*this);
  455. codeEditorHolder = new CodeEditorHolder (*this);
  456. layoutEditorHolder->createCanvas();
  457. }
  458. addAndMakeVisible (tabs = new TabbedComponent (TabbedButtonBar::TabsAtRight));
  459. tabs->setTabBarDepth (22);
  460. tabs->addTab ("Class Settings", Colour::greyLevel (0.88f), classInfoHolder, true);
  461. tabs->addTab ("Components", Colours::white, layoutEditorHolder, true);
  462. tabs->addTab ("Background", Colours::white, backgroundEditorHolder, true);
  463. tabs->addTab ("Source Code", Colours::white, codeEditorHolder, true);
  464. tabs->setCurrentTabIndex (1);
  465. }
  466. ComponentEditor::~ComponentEditor()
  467. {
  468. deleteAllChildren();
  469. }
  470. void ComponentEditor::paint (Graphics& g)
  471. {
  472. g.fillAll (Colours::white);
  473. }
  474. void ComponentEditor::resized()
  475. {
  476. tabs->setBounds (getLocalBounds());
  477. }
  478. ComponentEditorCanvas* ComponentEditor::getCanvas() const
  479. {
  480. return dynamic_cast <ComponentEditorCanvas*> (getViewport()->getViewedComponent());
  481. }
  482. Viewport* ComponentEditor::getViewport() const
  483. {
  484. return layoutEditorHolder->getViewport();
  485. }
  486. //==============================================================================
  487. class TestComponent : public ComponentEditorCanvas::ComponentHolder
  488. {
  489. public:
  490. TestComponent (ComponentDocument& document_)
  491. : document (document_)
  492. {
  493. setSize (document.getCanvasWidth().getValue(),
  494. document.getCanvasHeight().getValue());
  495. }
  496. ~TestComponent()
  497. {
  498. }
  499. void resized()
  500. {
  501. document.getCanvasWidth() = getWidth();
  502. document.getCanvasHeight() = getHeight();
  503. ComponentEditorCanvas::ComponentHolder::resized();
  504. updateComponents (document, selected);
  505. }
  506. private:
  507. ComponentDocument document;
  508. ComponentEditorCanvas::SelectedItems selected;
  509. };
  510. void ComponentEditor::test()
  511. {
  512. TestComponent testComp (getDocument());
  513. DialogWindow::showModalDialog ("Testing: " + getDocument().getClassName().toString(),
  514. &testComp, this, Colours::lightgrey, true, true);
  515. }
  516. //==============================================================================
  517. void ComponentEditor::getAllCommands (Array <CommandID>& commands)
  518. {
  519. DocumentEditorComponent::getAllCommands (commands);
  520. const CommandID ids[] = { CommandIDs::undo,
  521. CommandIDs::redo,
  522. CommandIDs::toFront,
  523. CommandIDs::toBack,
  524. CommandIDs::test,
  525. CommandIDs::showOrHideProperties,
  526. CommandIDs::showOrHideTree,
  527. StandardApplicationCommandIDs::del };
  528. commands.addArray (ids, numElementsInArray (ids));
  529. }
  530. void ComponentEditor::getCommandInfo (CommandID commandID, ApplicationCommandInfo& result)
  531. {
  532. result.setActive (document != 0);
  533. switch (commandID)
  534. {
  535. case CommandIDs::undo:
  536. result.setInfo ("Undo", "Undoes the last change", CommandCategories::general, 0);
  537. result.defaultKeypresses.add (KeyPress ('z', ModifierKeys::commandModifier, 0));
  538. break;
  539. case CommandIDs::redo:
  540. result.setInfo ("Redo", "Redoes the last change", CommandCategories::general, 0);
  541. result.defaultKeypresses.add (KeyPress ('z', ModifierKeys::shiftModifier | ModifierKeys::commandModifier, 0));
  542. result.defaultKeypresses.add (KeyPress ('y', ModifierKeys::commandModifier, 0));
  543. break;
  544. case CommandIDs::toFront:
  545. result.setInfo ("Bring to Front", "Brings the selected items to the front", CommandCategories::editing, 0);
  546. break;
  547. case CommandIDs::toBack:
  548. result.setInfo ("Send to Back", "Moves the selected items to the back", CommandCategories::editing, 0);
  549. break;
  550. case CommandIDs::test:
  551. result.setInfo ("Test", "Test the current component", CommandCategories::editing, 0);
  552. result.defaultKeypresses.add (KeyPress ('t', ModifierKeys::commandModifier, 0));
  553. break;
  554. case CommandIDs::showOrHideProperties:
  555. result.setInfo ("Show/Hide Tree", "Shows or hides the component tree view", CommandCategories::editing, 0);
  556. break;
  557. case CommandIDs::showOrHideTree:
  558. result.setInfo ("Show/Hide Properties", "Shows or hides the component properties panel", CommandCategories::editing, 0);
  559. break;
  560. case StandardApplicationCommandIDs::del:
  561. result.setInfo ("Delete", String::empty, CommandCategories::general, 0);
  562. result.defaultKeypresses.add (KeyPress (KeyPress::deleteKey, 0, 0));
  563. result.defaultKeypresses.add (KeyPress (KeyPress::backspaceKey, 0, 0));
  564. break;
  565. default:
  566. DocumentEditorComponent::getCommandInfo (commandID, result);
  567. break;
  568. }
  569. }
  570. bool ComponentEditor::perform (const InvocationInfo& info)
  571. {
  572. switch (info.commandID)
  573. {
  574. case CommandIDs::undo:
  575. getDocument().getUndoManager()->undo();
  576. return true;
  577. case CommandIDs::redo:
  578. getDocument().getUndoManager()->redo();
  579. return true;
  580. case CommandIDs::toFront:
  581. getCanvas()->selectionToFront();
  582. return true;
  583. case CommandIDs::toBack:
  584. getCanvas()->selectionToBack();
  585. return true;
  586. case CommandIDs::test:
  587. test();
  588. return true;
  589. case CommandIDs::showOrHideProperties:
  590. layoutEditorHolder->showOrHideProperties();
  591. return true;
  592. case CommandIDs::showOrHideTree:
  593. layoutEditorHolder->showOrHideTree();
  594. return true;
  595. case StandardApplicationCommandIDs::del:
  596. getCanvas()->deleteSelection();
  597. return true;
  598. default:
  599. break;
  600. }
  601. return DocumentEditorComponent::perform (info);
  602. }