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.

1062 lines
31KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-9 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 "../JuceLibraryCode/JuceHeader.h"
  19. #include "GraphEditorPanel.h"
  20. #include "InternalFilters.h"
  21. #include "MainHostWindow.h"
  22. //==============================================================================
  23. class PluginWindow;
  24. static Array <PluginWindow*> activePluginWindows;
  25. PluginWindow::PluginWindow (Component* const uiComp,
  26. AudioProcessorGraph::Node* owner_,
  27. const bool isGeneric_)
  28. : DocumentWindow (uiComp->getName(), Colours::lightblue,
  29. DocumentWindow::minimiseButton | DocumentWindow::closeButton),
  30. owner (owner_),
  31. isGeneric (isGeneric_)
  32. {
  33. setSize (400, 300);
  34. setContentComponent (uiComp, true, true);
  35. setTopLeftPosition (owner->properties.getWithDefault ("uiLastX", Random::getSystemRandom().nextInt (500)),
  36. owner->properties.getWithDefault ("uiLastY", Random::getSystemRandom().nextInt (500)));
  37. setVisible (true);
  38. activePluginWindows.add (this);
  39. }
  40. void PluginWindow::closeCurrentlyOpenWindowsFor (const uint32 nodeId)
  41. {
  42. for (int i = activePluginWindows.size(); --i >= 0;)
  43. if (activePluginWindows.getUnchecked(i)->owner->id == nodeId)
  44. delete activePluginWindows.getUnchecked(i);
  45. }
  46. void PluginWindow::closeAllCurrentlyOpenWindows()
  47. {
  48. for (int i = activePluginWindows.size(); --i >= 0;)
  49. delete activePluginWindows.getUnchecked(i);
  50. }
  51. PluginWindow* PluginWindow::getWindowFor (AudioProcessorGraph::Node* node,
  52. bool useGenericView)
  53. {
  54. for (int i = activePluginWindows.size(); --i >= 0;)
  55. if (activePluginWindows.getUnchecked(i)->owner == node
  56. && activePluginWindows.getUnchecked(i)->isGeneric == useGenericView)
  57. return activePluginWindows.getUnchecked(i);
  58. AudioProcessorEditor* ui = 0;
  59. if (! useGenericView)
  60. {
  61. ui = node->getProcessor()->createEditorIfNeeded();
  62. if (ui == 0)
  63. useGenericView = true;
  64. }
  65. if (useGenericView)
  66. {
  67. ui = new GenericAudioProcessorEditor (node->getProcessor());
  68. }
  69. if (ui != 0)
  70. {
  71. AudioPluginInstance* const plugin = dynamic_cast <AudioPluginInstance*> (node->getProcessor());
  72. if (plugin != 0)
  73. ui->setName (plugin->getName());
  74. return new PluginWindow (ui, node, useGenericView);
  75. }
  76. return 0;
  77. }
  78. PluginWindow::~PluginWindow()
  79. {
  80. activePluginWindows.removeValue (this);
  81. setContentComponent (0);
  82. }
  83. void PluginWindow::moved()
  84. {
  85. owner->properties.set ("uiLastX", getX());
  86. owner->properties.set ("uiLastY", getY());
  87. }
  88. void PluginWindow::closeButtonPressed()
  89. {
  90. delete this;
  91. }
  92. //==============================================================================
  93. class PinComponent : public Component,
  94. public SettableTooltipClient
  95. {
  96. public:
  97. PinComponent (FilterGraph& graph_,
  98. const uint32 filterID_, const int index_, const bool isInput_)
  99. : graph (graph_),
  100. filterID (filterID_),
  101. index (index_),
  102. isInput (isInput_)
  103. {
  104. const AudioProcessorGraph::Node::Ptr node (graph.getNodeForId (filterID_));
  105. if (node != 0)
  106. {
  107. String tip;
  108. if (isInput)
  109. tip = node->getProcessor()->getInputChannelName (index_);
  110. else
  111. tip = node->getProcessor()->getOutputChannelName (index_);
  112. if (tip.isEmpty())
  113. {
  114. if (index_ == FilterGraph::midiChannelNumber)
  115. tip = isInput ? "Midi Input" : "Midi Output";
  116. else
  117. tip = (isInput ? "Input " : "Output ") + String (index_ + 1);
  118. }
  119. setTooltip (tip);
  120. }
  121. setSize (16, 16);
  122. }
  123. ~PinComponent()
  124. {
  125. }
  126. void paint (Graphics& g)
  127. {
  128. const float w = (float) getWidth();
  129. const float h = (float) getHeight();
  130. Path p;
  131. p.addEllipse (w * 0.25f, h * 0.25f, w * 0.5f, h * 0.5f);
  132. p.addRectangle (w * 0.4f, isInput ? (0.5f * h) : 0.0f, w * 0.2f, h * 0.5f);
  133. g.setColour (index == FilterGraph::midiChannelNumber ? Colours::red : Colours::green);
  134. g.fillPath (p);
  135. }
  136. void mouseDown (const MouseEvent& e)
  137. {
  138. getGraphPanel()->beginConnectorDrag (isInput ? 0 : filterID,
  139. index,
  140. isInput ? filterID : 0,
  141. index,
  142. e);
  143. }
  144. void mouseDrag (const MouseEvent& e)
  145. {
  146. getGraphPanel()->dragConnector (e);
  147. }
  148. void mouseUp (const MouseEvent& e)
  149. {
  150. getGraphPanel()->endDraggingConnector (e);
  151. }
  152. const uint32 filterID;
  153. const int index;
  154. const bool isInput;
  155. private:
  156. FilterGraph& graph;
  157. GraphEditorPanel* getGraphPanel() const throw()
  158. {
  159. return findParentComponentOfClass ((GraphEditorPanel*) 0);
  160. }
  161. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PinComponent);
  162. };
  163. //==============================================================================
  164. class FilterComponent : public Component
  165. {
  166. public:
  167. FilterComponent (FilterGraph& graph_,
  168. const uint32 filterID_)
  169. : graph (graph_),
  170. filterID (filterID_),
  171. numInputs (0),
  172. numOutputs (0),
  173. pinSize (16),
  174. font (13.0f, Font::bold),
  175. numIns (0),
  176. numOuts (0)
  177. {
  178. shadow.setShadowProperties (2.5f, 0.5f, -1, 0);
  179. setComponentEffect (&shadow);
  180. setSize (150, 60);
  181. }
  182. ~FilterComponent()
  183. {
  184. deleteAllChildren();
  185. }
  186. void mouseDown (const MouseEvent& e)
  187. {
  188. originalPos = relativePositionToGlobal (Point<int>());
  189. toFront (true);
  190. if (e.mods.isPopupMenu())
  191. {
  192. PopupMenu m;
  193. m.addItem (1, "Delete this filter");
  194. m.addItem (2, "Disconnect all pins");
  195. m.addSeparator();
  196. m.addItem (3, "Show plugin UI");
  197. m.addItem (4, "Show all parameters");
  198. const int r = m.show();
  199. if (r == 1)
  200. {
  201. graph.removeFilter (filterID);
  202. return;
  203. }
  204. else if (r == 2)
  205. {
  206. graph.disconnectFilter (filterID);
  207. }
  208. else if (r == 3 || r == 4)
  209. {
  210. AudioProcessorGraph::Node::Ptr f (graph.getNodeForId (filterID));
  211. if (f != 0)
  212. {
  213. PluginWindow* const w = PluginWindow::getWindowFor (f, r == 4);
  214. if (w != 0)
  215. w->toFront (true);
  216. }
  217. }
  218. }
  219. }
  220. void mouseDrag (const MouseEvent& e)
  221. {
  222. if (! e.mods.isPopupMenu())
  223. {
  224. Point<int> pos (originalPos + Point<int> (e.getDistanceFromDragStartX(), e.getDistanceFromDragStartY()));
  225. if (getParentComponent() != 0)
  226. pos = getParentComponent()->globalPositionToRelative (pos);
  227. graph.setNodePosition (filterID,
  228. (pos.getX() + getWidth() / 2) / (double) getParentWidth(),
  229. (pos.getY() + getHeight() / 2) / (double) getParentHeight());
  230. getGraphPanel()->updateComponents();
  231. }
  232. }
  233. void mouseUp (const MouseEvent& e)
  234. {
  235. if (e.mouseWasClicked() && e.getNumberOfClicks() == 2)
  236. {
  237. const AudioProcessorGraph::Node::Ptr f (graph.getNodeForId (filterID));
  238. if (f != 0)
  239. {
  240. PluginWindow* const w = PluginWindow::getWindowFor (f, false);
  241. if (w != 0)
  242. w->toFront (true);
  243. }
  244. }
  245. else if (! e.mouseWasClicked())
  246. {
  247. graph.setChangedFlag (true);
  248. }
  249. }
  250. bool hitTest (int x, int y)
  251. {
  252. for (int i = getNumChildComponents(); --i >= 0;)
  253. if (getChildComponent(i)->getBounds().contains (x, y))
  254. return true;
  255. return x >= 3 && x < getWidth() - 6 && y >= pinSize && y < getHeight() - pinSize;
  256. }
  257. void paint (Graphics& g)
  258. {
  259. g.setColour (Colours::lightgrey);
  260. const int x = 4;
  261. const int y = pinSize;
  262. const int w = getWidth() - x * 2;
  263. const int h = getHeight() - pinSize * 2;
  264. g.fillRect (x, y, w, h);
  265. g.setColour (Colours::black);
  266. g.setFont (font);
  267. g.drawFittedText (getName(),
  268. x + 4, y + 2, w - 8, h - 4,
  269. Justification::centred, 2);
  270. g.setColour (Colours::grey);
  271. g.drawRect (x, y, w, h);
  272. }
  273. void resized()
  274. {
  275. for (int i = 0; i < getNumChildComponents(); ++i)
  276. {
  277. PinComponent* const pc = dynamic_cast <PinComponent*> (getChildComponent(i));
  278. if (pc != 0)
  279. {
  280. const int total = pc->isInput ? numIns : numOuts;
  281. const int index = pc->index == FilterGraph::midiChannelNumber ? (total - 1) : pc->index;
  282. pc->setBounds (proportionOfWidth ((1 + index) / (total + 1.0f)) - pinSize / 2,
  283. pc->isInput ? 0 : (getHeight() - pinSize),
  284. pinSize, pinSize);
  285. }
  286. }
  287. }
  288. void getPinPos (const int index, const bool isInput, float& x, float& y)
  289. {
  290. for (int i = 0; i < getNumChildComponents(); ++i)
  291. {
  292. PinComponent* const pc = dynamic_cast <PinComponent*> (getChildComponent(i));
  293. if (pc != 0 && pc->index == index && isInput == pc->isInput)
  294. {
  295. x = getX() + pc->getX() + pc->getWidth() * 0.5f;
  296. y = getY() + pc->getY() + pc->getHeight() * 0.5f;
  297. break;
  298. }
  299. }
  300. }
  301. void update()
  302. {
  303. const AudioProcessorGraph::Node::Ptr f (graph.getNodeForId (filterID));
  304. if (f == 0)
  305. {
  306. delete this;
  307. return;
  308. }
  309. numIns = f->getProcessor()->getNumInputChannels();
  310. if (f->getProcessor()->acceptsMidi())
  311. ++numIns;
  312. numOuts = f->getProcessor()->getNumOutputChannels();
  313. if (f->getProcessor()->producesMidi())
  314. ++numOuts;
  315. int w = 100;
  316. int h = 60;
  317. w = jmax (w, (jmax (numIns, numOuts) + 1) * 20);
  318. const int textWidth = font.getStringWidth (f->getProcessor()->getName());
  319. w = jmax (w, 16 + jmin (textWidth, 300));
  320. if (textWidth > 300)
  321. h = 100;
  322. setSize (w, h);
  323. setName (f->getProcessor()->getName());
  324. {
  325. double x, y;
  326. graph.getNodePosition (filterID, x, y);
  327. setCentreRelative ((float) x, (float) y);
  328. }
  329. if (numIns != numInputs || numOuts != numOutputs)
  330. {
  331. numInputs = numIns;
  332. numOutputs = numOuts;
  333. deleteAllChildren();
  334. int i;
  335. for (i = 0; i < f->getProcessor()->getNumInputChannels(); ++i)
  336. addAndMakeVisible (new PinComponent (graph, filterID, i, true));
  337. if (f->getProcessor()->acceptsMidi())
  338. addAndMakeVisible (new PinComponent (graph, filterID, FilterGraph::midiChannelNumber, true));
  339. for (i = 0; i < f->getProcessor()->getNumOutputChannels(); ++i)
  340. addAndMakeVisible (new PinComponent (graph, filterID, i, false));
  341. if (f->getProcessor()->producesMidi())
  342. addAndMakeVisible (new PinComponent (graph, filterID, FilterGraph::midiChannelNumber, false));
  343. resized();
  344. }
  345. }
  346. FilterGraph& graph;
  347. const uint32 filterID;
  348. int numInputs, numOutputs;
  349. private:
  350. int pinSize;
  351. Point<int> originalPos;
  352. int numIns, numOuts;
  353. DropShadowEffect shadow;
  354. Font font;
  355. GraphEditorPanel* getGraphPanel() const throw()
  356. {
  357. return findParentComponentOfClass ((GraphEditorPanel*) 0);
  358. }
  359. FilterComponent (const FilterComponent&);
  360. FilterComponent& operator= (const FilterComponent&);
  361. };
  362. //==============================================================================
  363. class ConnectorComponent : public Component,
  364. public SettableTooltipClient
  365. {
  366. public:
  367. ConnectorComponent (FilterGraph& graph_)
  368. : graph (graph_),
  369. sourceFilterID (0),
  370. destFilterID (0),
  371. sourceFilterChannel (0),
  372. destFilterChannel (0),
  373. lastInputX (0),
  374. lastInputY (0),
  375. lastOutputX (0),
  376. lastOutputY (0)
  377. {
  378. setAlwaysOnTop (true);
  379. }
  380. ~ConnectorComponent()
  381. {
  382. }
  383. void setInput (const uint32 sourceFilterID_, const int sourceFilterChannel_)
  384. {
  385. if (sourceFilterID != sourceFilterID_ || sourceFilterChannel != sourceFilterChannel_)
  386. {
  387. sourceFilterID = sourceFilterID_;
  388. sourceFilterChannel = sourceFilterChannel_;
  389. update();
  390. }
  391. }
  392. void setOutput (const uint32 destFilterID_, const int destFilterChannel_)
  393. {
  394. if (destFilterID != destFilterID_ || destFilterChannel != destFilterChannel_)
  395. {
  396. destFilterID = destFilterID_;
  397. destFilterChannel = destFilterChannel_;
  398. update();
  399. }
  400. }
  401. void dragStart (int x, int y)
  402. {
  403. lastInputX = (float) x;
  404. lastInputY = (float) y;
  405. resizeToFit();
  406. }
  407. void dragEnd (int x, int y)
  408. {
  409. lastOutputX = (float) x;
  410. lastOutputY = (float) y;
  411. resizeToFit();
  412. }
  413. void update()
  414. {
  415. float x1, y1, x2, y2;
  416. getPoints (x1, y1, x2, y2);
  417. if (lastInputX != x1
  418. || lastInputY != y1
  419. || lastOutputX != x2
  420. || lastOutputY != y2)
  421. {
  422. resizeToFit();
  423. }
  424. }
  425. void resizeToFit()
  426. {
  427. float x1, y1, x2, y2;
  428. getPoints (x1, y1, x2, y2);
  429. const Rectangle<int> newBounds ((int) jmin (x1, x2) - 4,
  430. (int) jmin (y1, y2) - 4,
  431. (int) fabsf (x1 - x2) + 8,
  432. (int) fabsf (y1 - y2) + 8);
  433. if (newBounds != getBounds())
  434. setBounds (newBounds);
  435. else
  436. resized();
  437. repaint();
  438. }
  439. void getPoints (float& x1, float& y1, float& x2, float& y2) const
  440. {
  441. x1 = lastInputX;
  442. y1 = lastInputY;
  443. x2 = lastOutputX;
  444. y2 = lastOutputY;
  445. GraphEditorPanel* const hostPanel = getGraphPanel();
  446. if (hostPanel != 0)
  447. {
  448. FilterComponent* srcFilterComp = hostPanel->getComponentForFilter (sourceFilterID);
  449. if (srcFilterComp != 0)
  450. srcFilterComp->getPinPos (sourceFilterChannel, false, x1, y1);
  451. FilterComponent* dstFilterComp = hostPanel->getComponentForFilter (destFilterID);
  452. if (dstFilterComp != 0)
  453. dstFilterComp->getPinPos (destFilterChannel, true, x2, y2);
  454. }
  455. }
  456. void paint (Graphics& g)
  457. {
  458. if (sourceFilterChannel == FilterGraph::midiChannelNumber
  459. || destFilterChannel == FilterGraph::midiChannelNumber)
  460. {
  461. g.setColour (Colours::red);
  462. }
  463. else
  464. {
  465. g.setColour (Colours::green);
  466. }
  467. g.fillPath (linePath);
  468. }
  469. bool hitTest (int x, int y)
  470. {
  471. if (hitPath.contains ((float) x, (float) y))
  472. {
  473. double distanceFromStart, distanceFromEnd;
  474. getDistancesFromEnds (x, y, distanceFromStart, distanceFromEnd);
  475. // avoid clicking the connector when over a pin
  476. return distanceFromStart > 7.0 && distanceFromEnd > 7.0;
  477. }
  478. return false;
  479. }
  480. void mouseDown (const MouseEvent&)
  481. {
  482. dragging = false;
  483. }
  484. void mouseDrag (const MouseEvent& e)
  485. {
  486. if ((! dragging) && ! e.mouseWasClicked())
  487. {
  488. dragging = true;
  489. graph.removeConnection (sourceFilterID, sourceFilterChannel, destFilterID, destFilterChannel);
  490. double distanceFromStart, distanceFromEnd;
  491. getDistancesFromEnds (e.x, e.y, distanceFromStart, distanceFromEnd);
  492. const bool isNearerSource = (distanceFromStart < distanceFromEnd);
  493. getGraphPanel()->beginConnectorDrag (isNearerSource ? 0 : sourceFilterID,
  494. sourceFilterChannel,
  495. isNearerSource ? destFilterID : 0,
  496. destFilterChannel,
  497. e);
  498. }
  499. else if (dragging)
  500. {
  501. getGraphPanel()->dragConnector (e);
  502. }
  503. }
  504. void mouseUp (const MouseEvent& e)
  505. {
  506. if (dragging)
  507. getGraphPanel()->endDraggingConnector (e);
  508. }
  509. void resized()
  510. {
  511. float x1, y1, x2, y2;
  512. getPoints (x1, y1, x2, y2);
  513. lastInputX = x1;
  514. lastInputY = y1;
  515. lastOutputX = x2;
  516. lastOutputY = y2;
  517. x1 -= getX();
  518. y1 -= getY();
  519. x2 -= getX();
  520. y2 -= getY();
  521. linePath.clear();
  522. linePath.startNewSubPath (x1, y1);
  523. linePath.cubicTo (x1, y1 + (y2 - y1) * 0.33f,
  524. x2, y1 + (y2 - y1) * 0.66f,
  525. x2, y2);
  526. PathStrokeType wideStroke (8.0f);
  527. wideStroke.createStrokedPath (hitPath, linePath);
  528. PathStrokeType stroke (2.5f);
  529. stroke.createStrokedPath (linePath, linePath);
  530. const float arrowW = 5.0f;
  531. const float arrowL = 4.0f;
  532. Path arrow;
  533. arrow.addTriangle (-arrowL, arrowW,
  534. -arrowL, -arrowW,
  535. arrowL, 0.0f);
  536. arrow.applyTransform (AffineTransform::identity
  537. .rotated (float_Pi * 0.5f - (float) atan2 (x2 - x1, y2 - y1))
  538. .translated ((x1 + x2) * 0.5f,
  539. (y1 + y2) * 0.5f));
  540. linePath.addPath (arrow);
  541. linePath.setUsingNonZeroWinding (true);
  542. }
  543. uint32 sourceFilterID, destFilterID;
  544. int sourceFilterChannel, destFilterChannel;
  545. private:
  546. FilterGraph& graph;
  547. float lastInputX, lastInputY, lastOutputX, lastOutputY;
  548. Path linePath, hitPath;
  549. bool dragging;
  550. GraphEditorPanel* getGraphPanel() const throw()
  551. {
  552. return findParentComponentOfClass ((GraphEditorPanel*) 0);
  553. }
  554. void getDistancesFromEnds (int x, int y, double& distanceFromStart, double& distanceFromEnd) const
  555. {
  556. float x1, y1, x2, y2;
  557. getPoints (x1, y1, x2, y2);
  558. distanceFromStart = juce_hypot (x - (x1 - getX()), y - (y1 - getY()));
  559. distanceFromEnd = juce_hypot (x - (x2 - getX()), y - (y2 - getY()));
  560. }
  561. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConnectorComponent);
  562. };
  563. //==============================================================================
  564. GraphEditorPanel::GraphEditorPanel (FilterGraph& graph_)
  565. : graph (graph_),
  566. draggingConnector (0)
  567. {
  568. graph.addChangeListener (this);
  569. setOpaque (true);
  570. }
  571. GraphEditorPanel::~GraphEditorPanel()
  572. {
  573. graph.removeChangeListener (this);
  574. deleteAndZero (draggingConnector);
  575. deleteAllChildren();
  576. }
  577. void GraphEditorPanel::paint (Graphics& g)
  578. {
  579. g.fillAll (Colours::white);
  580. }
  581. void GraphEditorPanel::mouseDown (const MouseEvent& e)
  582. {
  583. if (e.mods.isPopupMenu())
  584. {
  585. PopupMenu m;
  586. MainHostWindow* const mainWindow = findParentComponentOfClass ((MainHostWindow*) 0);
  587. if (mainWindow != 0)
  588. {
  589. mainWindow->addPluginsToMenu (m);
  590. const int r = m.show();
  591. createNewPlugin (mainWindow->getChosenType (r), e.x, e.y);
  592. }
  593. }
  594. }
  595. void GraphEditorPanel::createNewPlugin (const PluginDescription* desc, int x, int y)
  596. {
  597. graph.addFilter (desc, x / (double) getWidth(), y / (double) getHeight());
  598. }
  599. FilterComponent* GraphEditorPanel::getComponentForFilter (const uint32 filterID) const
  600. {
  601. for (int i = getNumChildComponents(); --i >= 0;)
  602. {
  603. FilterComponent* const fc = dynamic_cast <FilterComponent*> (getChildComponent (i));
  604. if (fc != 0 && fc->filterID == filterID)
  605. return fc;
  606. }
  607. return 0;
  608. }
  609. ConnectorComponent* GraphEditorPanel::getComponentForConnection (const AudioProcessorGraph::Connection& conn) const
  610. {
  611. for (int i = getNumChildComponents(); --i >= 0;)
  612. {
  613. ConnectorComponent* const c = dynamic_cast <ConnectorComponent*> (getChildComponent (i));
  614. if (c != 0
  615. && c->sourceFilterID == conn.sourceNodeId
  616. && c->destFilterID == conn.destNodeId
  617. && c->sourceFilterChannel == conn.sourceChannelIndex
  618. && c->destFilterChannel == conn.destChannelIndex)
  619. {
  620. return c;
  621. }
  622. }
  623. return 0;
  624. }
  625. PinComponent* GraphEditorPanel::findPinAt (const int x, const int y) const
  626. {
  627. for (int i = getNumChildComponents(); --i >= 0;)
  628. {
  629. FilterComponent* const fc = dynamic_cast <FilterComponent*> (getChildComponent (i));
  630. if (fc != 0)
  631. {
  632. PinComponent* const pin
  633. = dynamic_cast <PinComponent*> (fc->getComponentAt (x - fc->getX(),
  634. y - fc->getY()));
  635. if (pin != 0)
  636. return pin;
  637. }
  638. }
  639. return 0;
  640. }
  641. void GraphEditorPanel::resized()
  642. {
  643. updateComponents();
  644. }
  645. void GraphEditorPanel::changeListenerCallback (ChangeBroadcaster*)
  646. {
  647. updateComponents();
  648. }
  649. void GraphEditorPanel::updateComponents()
  650. {
  651. int i;
  652. for (i = getNumChildComponents(); --i >= 0;)
  653. {
  654. FilterComponent* const fc = dynamic_cast <FilterComponent*> (getChildComponent (i));
  655. if (fc != 0)
  656. fc->update();
  657. }
  658. for (i = getNumChildComponents(); --i >= 0;)
  659. {
  660. ConnectorComponent* const cc = dynamic_cast <ConnectorComponent*> (getChildComponent (i));
  661. if (cc != 0 && cc != draggingConnector)
  662. {
  663. if (graph.getConnectionBetween (cc->sourceFilterID, cc->sourceFilterChannel,
  664. cc->destFilterID, cc->destFilterChannel) == 0)
  665. {
  666. delete cc;
  667. }
  668. else
  669. {
  670. cc->update();
  671. }
  672. }
  673. }
  674. for (i = graph.getNumFilters(); --i >= 0;)
  675. {
  676. const AudioProcessorGraph::Node::Ptr f (graph.getNode (i));
  677. if (getComponentForFilter (f->id) == 0)
  678. {
  679. FilterComponent* const comp = new FilterComponent (graph, f->id);
  680. addAndMakeVisible (comp);
  681. comp->update();
  682. }
  683. }
  684. for (i = graph.getNumConnections(); --i >= 0;)
  685. {
  686. const AudioProcessorGraph::Connection* const c = graph.getConnection (i);
  687. if (getComponentForConnection (*c) == 0)
  688. {
  689. ConnectorComponent* const comp = new ConnectorComponent (graph);
  690. addAndMakeVisible (comp);
  691. comp->setInput (c->sourceNodeId, c->sourceChannelIndex);
  692. comp->setOutput (c->destNodeId, c->destChannelIndex);
  693. }
  694. }
  695. }
  696. void GraphEditorPanel::beginConnectorDrag (const uint32 sourceFilterID, const int sourceFilterChannel,
  697. const uint32 destFilterID, const int destFilterChannel,
  698. const MouseEvent& e)
  699. {
  700. delete draggingConnector;
  701. draggingConnector = dynamic_cast <ConnectorComponent*> (e.originalComponent);
  702. if (draggingConnector == 0)
  703. draggingConnector = new ConnectorComponent (graph);
  704. draggingConnector->setInput (sourceFilterID, sourceFilterChannel);
  705. draggingConnector->setOutput (destFilterID, destFilterChannel);
  706. addAndMakeVisible (draggingConnector);
  707. draggingConnector->toFront (false);
  708. dragConnector (e);
  709. }
  710. void GraphEditorPanel::dragConnector (const MouseEvent& e)
  711. {
  712. const MouseEvent e2 (e.getEventRelativeTo (this));
  713. if (draggingConnector != 0)
  714. {
  715. draggingConnector->setTooltip (String::empty);
  716. int x = e2.x;
  717. int y = e2.y;
  718. PinComponent* const pin = findPinAt (x, y);
  719. if (pin != 0)
  720. {
  721. uint32 srcFilter = draggingConnector->sourceFilterID;
  722. int srcChannel = draggingConnector->sourceFilterChannel;
  723. uint32 dstFilter = draggingConnector->destFilterID;
  724. int dstChannel = draggingConnector->destFilterChannel;
  725. if (srcFilter == 0 && ! pin->isInput)
  726. {
  727. srcFilter = pin->filterID;
  728. srcChannel = pin->index;
  729. }
  730. else if (dstFilter == 0 && pin->isInput)
  731. {
  732. dstFilter = pin->filterID;
  733. dstChannel = pin->index;
  734. }
  735. if (graph.canConnect (srcFilter, srcChannel, dstFilter, dstChannel))
  736. {
  737. x = pin->getParentComponent()->getX() + pin->getX() + pin->getWidth() / 2;
  738. y = pin->getParentComponent()->getY() + pin->getY() + pin->getHeight() / 2;
  739. draggingConnector->setTooltip (pin->getTooltip());
  740. }
  741. }
  742. if (draggingConnector->sourceFilterID == 0)
  743. draggingConnector->dragStart (x, y);
  744. else
  745. draggingConnector->dragEnd (x, y);
  746. }
  747. }
  748. void GraphEditorPanel::endDraggingConnector (const MouseEvent& e)
  749. {
  750. if (draggingConnector == 0)
  751. return;
  752. draggingConnector->setTooltip (String::empty);
  753. const MouseEvent e2 (e.getEventRelativeTo (this));
  754. uint32 srcFilter = draggingConnector->sourceFilterID;
  755. int srcChannel = draggingConnector->sourceFilterChannel;
  756. uint32 dstFilter = draggingConnector->destFilterID;
  757. int dstChannel = draggingConnector->destFilterChannel;
  758. deleteAndZero (draggingConnector);
  759. PinComponent* const pin = findPinAt (e2.x, e2.y);
  760. if (pin != 0)
  761. {
  762. if (srcFilter == 0)
  763. {
  764. if (pin->isInput)
  765. return;
  766. srcFilter = pin->filterID;
  767. srcChannel = pin->index;
  768. }
  769. else
  770. {
  771. if (! pin->isInput)
  772. return;
  773. dstFilter = pin->filterID;
  774. dstChannel = pin->index;
  775. }
  776. graph.addConnection (srcFilter, srcChannel, dstFilter, dstChannel);
  777. }
  778. }
  779. //==============================================================================
  780. class TooltipBar : public Component,
  781. private Timer
  782. {
  783. public:
  784. TooltipBar()
  785. {
  786. startTimer (100);
  787. }
  788. void paint (Graphics& g)
  789. {
  790. g.setFont (getHeight() * 0.7f, Font::bold);
  791. g.setColour (Colours::black);
  792. g.drawFittedText (tip, 10, 0, getWidth() - 12, getHeight(), Justification::centredLeft, 1);
  793. }
  794. void timerCallback()
  795. {
  796. Component* const underMouse = Desktop::getInstance().getMainMouseSource().getComponentUnderMouse();
  797. TooltipClient* const ttc = dynamic_cast <TooltipClient*> (underMouse);
  798. String newTip;
  799. if (ttc != 0 && ! (underMouse->isMouseButtonDown() || underMouse->isCurrentlyBlockedByAnotherModalComponent()))
  800. newTip = ttc->getTooltip();
  801. if (newTip != tip)
  802. {
  803. tip = newTip;
  804. repaint();
  805. }
  806. }
  807. private:
  808. String tip;
  809. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TooltipBar);
  810. };
  811. //==============================================================================
  812. GraphDocumentComponent::GraphDocumentComponent (AudioDeviceManager* deviceManager_)
  813. : deviceManager (deviceManager_)
  814. {
  815. addAndMakeVisible (graphPanel = new GraphEditorPanel (graph));
  816. graphPlayer.setProcessor (&graph.getGraph());
  817. keyState.addListener (&graphPlayer.getMidiMessageCollector());
  818. addAndMakeVisible (keyboardComp = new MidiKeyboardComponent (keyState,
  819. MidiKeyboardComponent::horizontalKeyboard));
  820. addAndMakeVisible (statusBar = new TooltipBar());
  821. deviceManager->addAudioCallback (&graphPlayer);
  822. graphPanel->updateComponents();
  823. }
  824. GraphDocumentComponent::~GraphDocumentComponent()
  825. {
  826. deviceManager->removeAudioCallback (&graphPlayer);
  827. deleteAllChildren();
  828. graphPlayer.setProcessor (0);
  829. keyState.removeListener (&graphPlayer.getMidiMessageCollector());
  830. graph.clear();
  831. }
  832. void GraphDocumentComponent::resized()
  833. {
  834. const int keysHeight = 60;
  835. const int statusHeight = 20;
  836. graphPanel->setBounds (0, 0, getWidth(), getHeight() - keysHeight);
  837. statusBar->setBounds (0, getHeight() - keysHeight - statusHeight, getWidth(), statusHeight);
  838. keyboardComp->setBounds (0, getHeight() - keysHeight, getWidth(), keysHeight);
  839. }
  840. void GraphDocumentComponent::createNewPlugin (const PluginDescription* desc, int x, int y)
  841. {
  842. graphPanel->createNewPlugin (desc, x, y);
  843. }