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.

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