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.

1123 lines
33KB

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