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.

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