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.

745 lines
26KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2020 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 6 End-User License
  8. Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
  9. End User License Agreement: www.juce.com/juce-6-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. #include <JuceHeader.h>
  19. #include "MainHostWindow.h"
  20. #include "../Plugins/InternalPlugins.h"
  21. //==============================================================================
  22. class MainHostWindow::PluginListWindow : public DocumentWindow
  23. {
  24. public:
  25. PluginListWindow (MainHostWindow& mw, AudioPluginFormatManager& pluginFormatManager)
  26. : DocumentWindow ("Available Plugins",
  27. LookAndFeel::getDefaultLookAndFeel().findColour (ResizableWindow::backgroundColourId),
  28. DocumentWindow::minimiseButton | DocumentWindow::closeButton),
  29. owner (mw)
  30. {
  31. auto deadMansPedalFile = getAppProperties().getUserSettings()
  32. ->getFile().getSiblingFile ("RecentlyCrashedPluginsList");
  33. setContentOwned (new PluginListComponent (pluginFormatManager,
  34. owner.knownPluginList,
  35. deadMansPedalFile,
  36. getAppProperties().getUserSettings(), true), true);
  37. setResizable (true, false);
  38. setResizeLimits (300, 400, 800, 1500);
  39. setTopLeftPosition (60, 60);
  40. restoreWindowStateFromString (getAppProperties().getUserSettings()->getValue ("listWindowPos"));
  41. setVisible (true);
  42. }
  43. ~PluginListWindow() override
  44. {
  45. getAppProperties().getUserSettings()->setValue ("listWindowPos", getWindowStateAsString());
  46. clearContentComponent();
  47. }
  48. void closeButtonPressed() override
  49. {
  50. owner.pluginListWindow = nullptr;
  51. }
  52. private:
  53. MainHostWindow& owner;
  54. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PluginListWindow)
  55. };
  56. //==============================================================================
  57. MainHostWindow::MainHostWindow()
  58. : DocumentWindow (JUCEApplication::getInstance()->getApplicationName(),
  59. LookAndFeel::getDefaultLookAndFeel().findColour (ResizableWindow::backgroundColourId),
  60. DocumentWindow::allButtons)
  61. {
  62. formatManager.addDefaultFormats();
  63. formatManager.addFormat (new InternalPluginFormat());
  64. auto safeThis = SafePointer<MainHostWindow> (this);
  65. RuntimePermissions::request (RuntimePermissions::recordAudio,
  66. [safeThis] (bool granted) mutable
  67. {
  68. auto savedState = getAppProperties().getUserSettings()->getXmlValue ("audioDeviceState");
  69. safeThis->deviceManager.initialise (granted ? 256 : 0, 256, savedState.get(), true);
  70. });
  71. #if JUCE_IOS || JUCE_ANDROID
  72. setFullScreen (true);
  73. #else
  74. setResizable (true, false);
  75. setResizeLimits (500, 400, 10000, 10000);
  76. centreWithSize (800, 600);
  77. #endif
  78. graphHolder.reset (new GraphDocumentComponent (formatManager, deviceManager, knownPluginList));
  79. setContentNonOwned (graphHolder.get(), false);
  80. restoreWindowStateFromString (getAppProperties().getUserSettings()->getValue ("mainWindowPos"));
  81. setVisible (true);
  82. InternalPluginFormat internalFormat;
  83. internalTypes = internalFormat.getAllTypes();
  84. if (auto savedPluginList = getAppProperties().getUserSettings()->getXmlValue ("pluginList"))
  85. knownPluginList.recreateFromXml (*savedPluginList);
  86. for (auto& t : internalTypes)
  87. knownPluginList.addType (t);
  88. pluginSortMethod = (KnownPluginList::SortMethod) getAppProperties().getUserSettings()
  89. ->getIntValue ("pluginSortMethod", KnownPluginList::sortByManufacturer);
  90. knownPluginList.addChangeListener (this);
  91. if (auto* g = graphHolder->graph.get())
  92. g->addChangeListener (this);
  93. addKeyListener (getCommandManager().getKeyMappings());
  94. Process::setPriority (Process::HighPriority);
  95. #if JUCE_IOS || JUCE_ANDROID
  96. graphHolder->burgerMenu.setModel (this);
  97. #else
  98. #if JUCE_MAC
  99. setMacMainMenu (this);
  100. #else
  101. setMenuBar (this);
  102. #endif
  103. #endif
  104. getCommandManager().setFirstCommandTarget (this);
  105. }
  106. MainHostWindow::~MainHostWindow()
  107. {
  108. pluginListWindow = nullptr;
  109. knownPluginList.removeChangeListener (this);
  110. if (auto* g = graphHolder->graph.get())
  111. g->removeChangeListener (this);
  112. getAppProperties().getUserSettings()->setValue ("mainWindowPos", getWindowStateAsString());
  113. clearContentComponent();
  114. #if ! (JUCE_ANDROID || JUCE_IOS)
  115. #if JUCE_MAC
  116. setMacMainMenu (nullptr);
  117. #else
  118. setMenuBar (nullptr);
  119. #endif
  120. #endif
  121. graphHolder = nullptr;
  122. }
  123. void MainHostWindow::closeButtonPressed()
  124. {
  125. tryToQuitApplication();
  126. }
  127. struct AsyncQuitRetrier : private Timer
  128. {
  129. AsyncQuitRetrier() { startTimer (500); }
  130. void timerCallback() override
  131. {
  132. stopTimer();
  133. delete this;
  134. if (auto app = JUCEApplicationBase::getInstance())
  135. app->systemRequestedQuit();
  136. }
  137. };
  138. void MainHostWindow::tryToQuitApplication()
  139. {
  140. if (graphHolder->closeAnyOpenPluginWindows())
  141. {
  142. // Really important thing to note here: if the last call just deleted any plugin windows,
  143. // we won't exit immediately - instead we'll use our AsyncQuitRetrier to let the message
  144. // loop run for another brief moment, then try again. This will give any plugins a chance
  145. // to flush any GUI events that may have been in transit before the app forces them to
  146. // be unloaded
  147. new AsyncQuitRetrier();
  148. return;
  149. }
  150. if (ModalComponentManager::getInstance()->cancelAllModalComponents())
  151. {
  152. new AsyncQuitRetrier();
  153. return;
  154. }
  155. if (graphHolder != nullptr)
  156. {
  157. auto releaseAndQuit = [this]
  158. {
  159. // Some plug-ins do not want [NSApp stop] to be called
  160. // before the plug-ins are not deallocated.
  161. graphHolder->releaseGraph();
  162. JUCEApplication::quit();
  163. };
  164. #if JUCE_ANDROID || JUCE_IOS
  165. if (graphHolder->graph->saveDocument (PluginGraph::getDefaultGraphDocumentOnMobile()))
  166. releaseAndQuit();
  167. #else
  168. SafePointer<MainHostWindow> parent { this };
  169. graphHolder->graph->saveIfNeededAndUserAgreesAsync ([parent, releaseAndQuit] (FileBasedDocument::SaveResult r)
  170. {
  171. if (parent == nullptr)
  172. return;
  173. if (r == FileBasedDocument::savedOk)
  174. releaseAndQuit();
  175. });
  176. #endif
  177. return;
  178. }
  179. JUCEApplication::quit();
  180. }
  181. void MainHostWindow::changeListenerCallback (ChangeBroadcaster* changed)
  182. {
  183. if (changed == &knownPluginList)
  184. {
  185. menuItemsChanged();
  186. // save the plugin list every time it gets changed, so that if we're scanning
  187. // and it crashes, we've still saved the previous ones
  188. if (auto savedPluginList = std::unique_ptr<XmlElement> (knownPluginList.createXml()))
  189. {
  190. getAppProperties().getUserSettings()->setValue ("pluginList", savedPluginList.get());
  191. getAppProperties().saveIfNeeded();
  192. }
  193. }
  194. else if (graphHolder != nullptr && changed == graphHolder->graph.get())
  195. {
  196. auto title = JUCEApplication::getInstance()->getApplicationName();
  197. auto f = graphHolder->graph->getFile();
  198. if (f.existsAsFile())
  199. title = f.getFileName() + " - " + title;
  200. setName (title);
  201. }
  202. }
  203. StringArray MainHostWindow::getMenuBarNames()
  204. {
  205. StringArray names;
  206. names.add ("File");
  207. names.add ("Plugins");
  208. names.add ("Options");
  209. names.add ("Windows");
  210. return names;
  211. }
  212. PopupMenu MainHostWindow::getMenuForIndex (int topLevelMenuIndex, const String& /*menuName*/)
  213. {
  214. PopupMenu menu;
  215. if (topLevelMenuIndex == 0)
  216. {
  217. // "File" menu
  218. #if ! (JUCE_IOS || JUCE_ANDROID)
  219. menu.addCommandItem (&getCommandManager(), CommandIDs::newFile);
  220. menu.addCommandItem (&getCommandManager(), CommandIDs::open);
  221. #endif
  222. RecentlyOpenedFilesList recentFiles;
  223. recentFiles.restoreFromString (getAppProperties().getUserSettings()
  224. ->getValue ("recentFilterGraphFiles"));
  225. PopupMenu recentFilesMenu;
  226. recentFiles.createPopupMenuItems (recentFilesMenu, 100, true, true);
  227. menu.addSubMenu ("Open recent file", recentFilesMenu);
  228. #if ! (JUCE_IOS || JUCE_ANDROID)
  229. menu.addCommandItem (&getCommandManager(), CommandIDs::save);
  230. menu.addCommandItem (&getCommandManager(), CommandIDs::saveAs);
  231. #endif
  232. menu.addSeparator();
  233. menu.addCommandItem (&getCommandManager(), StandardApplicationCommandIDs::quit);
  234. }
  235. else if (topLevelMenuIndex == 1)
  236. {
  237. // "Plugins" menu
  238. PopupMenu pluginsMenu;
  239. addPluginsToMenu (pluginsMenu);
  240. menu.addSubMenu ("Create Plug-in", pluginsMenu);
  241. menu.addSeparator();
  242. menu.addItem (250, "Delete All Plug-ins");
  243. }
  244. else if (topLevelMenuIndex == 2)
  245. {
  246. // "Options" menu
  247. menu.addCommandItem (&getCommandManager(), CommandIDs::showPluginListEditor);
  248. PopupMenu sortTypeMenu;
  249. sortTypeMenu.addItem (200, "List Plug-ins in Default Order", true, pluginSortMethod == KnownPluginList::defaultOrder);
  250. sortTypeMenu.addItem (201, "List Plug-ins in Alphabetical Order", true, pluginSortMethod == KnownPluginList::sortAlphabetically);
  251. sortTypeMenu.addItem (202, "List Plug-ins by Category", true, pluginSortMethod == KnownPluginList::sortByCategory);
  252. sortTypeMenu.addItem (203, "List Plug-ins by Manufacturer", true, pluginSortMethod == KnownPluginList::sortByManufacturer);
  253. sortTypeMenu.addItem (204, "List Plug-ins Based on the Directory Structure", true, pluginSortMethod == KnownPluginList::sortByFileSystemLocation);
  254. menu.addSubMenu ("Plug-in Menu Type", sortTypeMenu);
  255. menu.addSeparator();
  256. menu.addCommandItem (&getCommandManager(), CommandIDs::showAudioSettings);
  257. menu.addCommandItem (&getCommandManager(), CommandIDs::toggleDoublePrecision);
  258. if (autoScaleOptionAvailable)
  259. menu.addCommandItem (&getCommandManager(), CommandIDs::autoScalePluginWindows);
  260. menu.addSeparator();
  261. menu.addCommandItem (&getCommandManager(), CommandIDs::aboutBox);
  262. }
  263. else if (topLevelMenuIndex == 3)
  264. {
  265. menu.addCommandItem (&getCommandManager(), CommandIDs::allWindowsForward);
  266. }
  267. return menu;
  268. }
  269. void MainHostWindow::menuItemSelected (int menuItemID, int /*topLevelMenuIndex*/)
  270. {
  271. if (menuItemID == 250)
  272. {
  273. if (graphHolder != nullptr)
  274. if (auto* graph = graphHolder->graph.get())
  275. graph->clear();
  276. }
  277. #if ! (JUCE_ANDROID || JUCE_IOS)
  278. else if (menuItemID >= 100 && menuItemID < 200)
  279. {
  280. RecentlyOpenedFilesList recentFiles;
  281. recentFiles.restoreFromString (getAppProperties().getUserSettings()
  282. ->getValue ("recentFilterGraphFiles"));
  283. if (graphHolder != nullptr)
  284. {
  285. if (auto* graph = graphHolder->graph.get())
  286. {
  287. SafePointer<MainHostWindow> parent { this };
  288. graph->saveIfNeededAndUserAgreesAsync ([parent, recentFiles, menuItemID] (FileBasedDocument::SaveResult r)
  289. {
  290. if (parent == nullptr)
  291. return;
  292. if (r == FileBasedDocument::savedOk)
  293. parent->graphHolder->graph->loadFrom (recentFiles.getFile (menuItemID - 100), true);
  294. });
  295. }
  296. }
  297. }
  298. #endif
  299. else if (menuItemID >= 200 && menuItemID < 210)
  300. {
  301. if (menuItemID == 200) pluginSortMethod = KnownPluginList::defaultOrder;
  302. else if (menuItemID == 201) pluginSortMethod = KnownPluginList::sortAlphabetically;
  303. else if (menuItemID == 202) pluginSortMethod = KnownPluginList::sortByCategory;
  304. else if (menuItemID == 203) pluginSortMethod = KnownPluginList::sortByManufacturer;
  305. else if (menuItemID == 204) pluginSortMethod = KnownPluginList::sortByFileSystemLocation;
  306. getAppProperties().getUserSettings()->setValue ("pluginSortMethod", (int) pluginSortMethod);
  307. menuItemsChanged();
  308. }
  309. else
  310. {
  311. if (KnownPluginList::getIndexChosenByMenu (pluginDescriptions, menuItemID) >= 0)
  312. createPlugin (getChosenType (menuItemID), { proportionOfWidth (0.3f + Random::getSystemRandom().nextFloat() * 0.6f),
  313. proportionOfHeight (0.3f + Random::getSystemRandom().nextFloat() * 0.6f) });
  314. }
  315. }
  316. void MainHostWindow::menuBarActivated (bool isActivated)
  317. {
  318. if (isActivated && graphHolder != nullptr)
  319. graphHolder->unfocusKeyboardComponent();
  320. }
  321. void MainHostWindow::createPlugin (const PluginDescription& desc, Point<int> pos)
  322. {
  323. if (graphHolder != nullptr)
  324. graphHolder->createNewPlugin (desc, pos);
  325. }
  326. void MainHostWindow::addPluginsToMenu (PopupMenu& m)
  327. {
  328. if (graphHolder != nullptr)
  329. {
  330. int i = 0;
  331. for (auto& t : internalTypes)
  332. m.addItem (++i, t.name + " (" + t.pluginFormatName + ")");
  333. }
  334. m.addSeparator();
  335. pluginDescriptions = knownPluginList.getTypes();
  336. // This avoids showing the internal types again later on in the list
  337. pluginDescriptions.removeIf ([] (PluginDescription& desc)
  338. {
  339. return desc.pluginFormatName == InternalPluginFormat::getIdentifier();
  340. });
  341. KnownPluginList::addToMenu (m, pluginDescriptions, pluginSortMethod);
  342. }
  343. PluginDescription MainHostWindow::getChosenType (const int menuID) const
  344. {
  345. if (menuID >= 1 && menuID < (int) (1 + internalTypes.size()))
  346. return internalTypes[(size_t) (menuID - 1)];
  347. return pluginDescriptions[KnownPluginList::getIndexChosenByMenu (pluginDescriptions, menuID)];
  348. }
  349. //==============================================================================
  350. ApplicationCommandTarget* MainHostWindow::getNextCommandTarget()
  351. {
  352. return findFirstTargetParentComponent();
  353. }
  354. void MainHostWindow::getAllCommands (Array<CommandID>& commands)
  355. {
  356. // this returns the set of all commands that this target can perform..
  357. const CommandID ids[] = {
  358. #if ! (JUCE_IOS || JUCE_ANDROID)
  359. CommandIDs::newFile,
  360. CommandIDs::open,
  361. CommandIDs::save,
  362. CommandIDs::saveAs,
  363. #endif
  364. CommandIDs::showPluginListEditor,
  365. CommandIDs::showAudioSettings,
  366. CommandIDs::toggleDoublePrecision,
  367. CommandIDs::aboutBox,
  368. CommandIDs::allWindowsForward,
  369. CommandIDs::autoScalePluginWindows
  370. };
  371. commands.addArray (ids, numElementsInArray (ids));
  372. }
  373. void MainHostWindow::getCommandInfo (const CommandID commandID, ApplicationCommandInfo& result)
  374. {
  375. const String category ("General");
  376. switch (commandID)
  377. {
  378. #if ! (JUCE_IOS || JUCE_ANDROID)
  379. case CommandIDs::newFile:
  380. result.setInfo ("New", "Creates a new filter graph file", category, 0);
  381. result.defaultKeypresses.add(KeyPress('n', ModifierKeys::commandModifier, 0));
  382. break;
  383. case CommandIDs::open:
  384. result.setInfo ("Open...", "Opens a filter graph file", category, 0);
  385. result.defaultKeypresses.add (KeyPress ('o', ModifierKeys::commandModifier, 0));
  386. break;
  387. case CommandIDs::save:
  388. result.setInfo ("Save", "Saves the current graph to a file", category, 0);
  389. result.defaultKeypresses.add (KeyPress ('s', ModifierKeys::commandModifier, 0));
  390. break;
  391. case CommandIDs::saveAs:
  392. result.setInfo ("Save As...",
  393. "Saves a copy of the current graph to a file",
  394. category, 0);
  395. result.defaultKeypresses.add (KeyPress ('s', ModifierKeys::shiftModifier | ModifierKeys::commandModifier, 0));
  396. break;
  397. #endif
  398. case CommandIDs::showPluginListEditor:
  399. result.setInfo ("Edit the List of Available Plug-ins...", {}, category, 0);
  400. result.addDefaultKeypress ('p', ModifierKeys::commandModifier);
  401. break;
  402. case CommandIDs::showAudioSettings:
  403. result.setInfo ("Change the Audio Device Settings", {}, category, 0);
  404. result.addDefaultKeypress ('a', ModifierKeys::commandModifier);
  405. break;
  406. case CommandIDs::toggleDoublePrecision:
  407. updatePrecisionMenuItem (result);
  408. break;
  409. case CommandIDs::aboutBox:
  410. result.setInfo ("About...", {}, category, 0);
  411. break;
  412. case CommandIDs::allWindowsForward:
  413. result.setInfo ("All Windows Forward", "Bring all plug-in windows forward", category, 0);
  414. result.addDefaultKeypress ('w', ModifierKeys::commandModifier);
  415. break;
  416. case CommandIDs::autoScalePluginWindows:
  417. updateAutoScaleMenuItem (result);
  418. break;
  419. default:
  420. break;
  421. }
  422. }
  423. bool MainHostWindow::perform (const InvocationInfo& info)
  424. {
  425. switch (info.commandID)
  426. {
  427. #if ! (JUCE_IOS || JUCE_ANDROID)
  428. case CommandIDs::newFile:
  429. if (graphHolder != nullptr && graphHolder->graph != nullptr)
  430. {
  431. SafePointer<MainHostWindow> parent { this };
  432. graphHolder->graph->saveIfNeededAndUserAgreesAsync ([parent] (FileBasedDocument::SaveResult r)
  433. {
  434. if (parent == nullptr)
  435. return;
  436. if (r == FileBasedDocument::savedOk)
  437. parent->graphHolder->graph->newDocument();
  438. });
  439. }
  440. break;
  441. case CommandIDs::open:
  442. if (graphHolder != nullptr && graphHolder->graph != nullptr)
  443. {
  444. SafePointer<MainHostWindow> parent { this };
  445. graphHolder->graph->saveIfNeededAndUserAgreesAsync ([parent] (FileBasedDocument::SaveResult r)
  446. {
  447. if (parent == nullptr)
  448. return;
  449. if (r == FileBasedDocument::savedOk)
  450. parent->graphHolder->graph->loadFromUserSpecifiedFileAsync (true, [] (Result) {});
  451. });
  452. }
  453. break;
  454. case CommandIDs::save:
  455. if (graphHolder != nullptr && graphHolder->graph != nullptr)
  456. graphHolder->graph->saveAsync (true, true, nullptr);
  457. break;
  458. case CommandIDs::saveAs:
  459. if (graphHolder != nullptr && graphHolder->graph != nullptr)
  460. graphHolder->graph->saveAsAsync ({}, true, true, true, nullptr);
  461. break;
  462. #endif
  463. case CommandIDs::showPluginListEditor:
  464. if (pluginListWindow == nullptr)
  465. pluginListWindow.reset (new PluginListWindow (*this, formatManager));
  466. pluginListWindow->toFront (true);
  467. break;
  468. case CommandIDs::showAudioSettings:
  469. showAudioSettings();
  470. break;
  471. case CommandIDs::toggleDoublePrecision:
  472. if (auto* props = getAppProperties().getUserSettings())
  473. {
  474. auto newIsDoublePrecision = ! isDoublePrecisionProcessingEnabled();
  475. props->setValue ("doublePrecisionProcessing", var (newIsDoublePrecision));
  476. ApplicationCommandInfo cmdInfo (info.commandID);
  477. updatePrecisionMenuItem (cmdInfo);
  478. menuItemsChanged();
  479. if (graphHolder != nullptr)
  480. graphHolder->setDoublePrecision (newIsDoublePrecision);
  481. }
  482. break;
  483. case CommandIDs::autoScalePluginWindows:
  484. if (auto* props = getAppProperties().getUserSettings())
  485. {
  486. auto newAutoScale = ! isAutoScalePluginWindowsEnabled();
  487. props->setValue ("autoScalePluginWindows", var (newAutoScale));
  488. ApplicationCommandInfo cmdInfo (info.commandID);
  489. updateAutoScaleMenuItem (cmdInfo);
  490. menuItemsChanged();
  491. }
  492. break;
  493. case CommandIDs::aboutBox:
  494. // TODO
  495. break;
  496. case CommandIDs::allWindowsForward:
  497. {
  498. auto& desktop = Desktop::getInstance();
  499. for (int i = 0; i < desktop.getNumComponents(); ++i)
  500. desktop.getComponent (i)->toBehind (this);
  501. break;
  502. }
  503. default:
  504. return false;
  505. }
  506. return true;
  507. }
  508. void MainHostWindow::showAudioSettings()
  509. {
  510. auto* audioSettingsComp = new AudioDeviceSelectorComponent (deviceManager,
  511. 0, 256,
  512. 0, 256,
  513. true, true,
  514. true, false);
  515. audioSettingsComp->setSize (500, 450);
  516. DialogWindow::LaunchOptions o;
  517. o.content.setOwned (audioSettingsComp);
  518. o.dialogTitle = "Audio Settings";
  519. o.componentToCentreAround = this;
  520. o.dialogBackgroundColour = getLookAndFeel().findColour (ResizableWindow::backgroundColourId);
  521. o.escapeKeyTriggersCloseButton = true;
  522. o.useNativeTitleBar = false;
  523. o.resizable = false;
  524. auto* w = o.create();
  525. auto safeThis = SafePointer<MainHostWindow> (this);
  526. w->enterModalState (true,
  527. ModalCallbackFunction::create
  528. ([safeThis] (int)
  529. {
  530. auto audioState = safeThis->deviceManager.createStateXml();
  531. getAppProperties().getUserSettings()->setValue ("audioDeviceState", audioState.get());
  532. getAppProperties().getUserSettings()->saveIfNeeded();
  533. if (safeThis->graphHolder != nullptr)
  534. if (safeThis->graphHolder->graph != nullptr)
  535. safeThis->graphHolder->graph->graph.removeIllegalConnections();
  536. }), true);
  537. }
  538. bool MainHostWindow::isInterestedInFileDrag (const StringArray&)
  539. {
  540. return true;
  541. }
  542. void MainHostWindow::fileDragEnter (const StringArray&, int, int)
  543. {
  544. }
  545. void MainHostWindow::fileDragMove (const StringArray&, int, int)
  546. {
  547. }
  548. void MainHostWindow::fileDragExit (const StringArray&)
  549. {
  550. }
  551. void MainHostWindow::filesDropped (const StringArray& files, int x, int y)
  552. {
  553. if (graphHolder != nullptr)
  554. {
  555. #if ! (JUCE_ANDROID || JUCE_IOS)
  556. File firstFile { files[0] };
  557. if (files.size() == 1 && firstFile.hasFileExtension (PluginGraph::getFilenameSuffix()))
  558. {
  559. if (auto* g = graphHolder->graph.get())
  560. {
  561. SafePointer<MainHostWindow> parent;
  562. g->saveIfNeededAndUserAgreesAsync ([parent, g, firstFile] (FileBasedDocument::SaveResult r)
  563. {
  564. if (parent == nullptr)
  565. return;
  566. if (r == FileBasedDocument::savedOk)
  567. g->loadFrom (firstFile, true);
  568. });
  569. }
  570. }
  571. else
  572. #endif
  573. {
  574. OwnedArray<PluginDescription> typesFound;
  575. knownPluginList.scanAndAddDragAndDroppedFiles (formatManager, files, typesFound);
  576. auto pos = graphHolder->getLocalPoint (this, Point<int> (x, y));
  577. for (int i = 0; i < jmin (5, typesFound.size()); ++i)
  578. if (auto* desc = typesFound.getUnchecked(i))
  579. createPlugin (*desc, pos);
  580. }
  581. }
  582. }
  583. bool MainHostWindow::isDoublePrecisionProcessingEnabled()
  584. {
  585. if (auto* props = getAppProperties().getUserSettings())
  586. return props->getBoolValue ("doublePrecisionProcessing", false);
  587. return false;
  588. }
  589. bool MainHostWindow::isAutoScalePluginWindowsEnabled()
  590. {
  591. if (auto* props = getAppProperties().getUserSettings())
  592. return props->getBoolValue ("autoScalePluginWindows", false);
  593. return false;
  594. }
  595. void MainHostWindow::updatePrecisionMenuItem (ApplicationCommandInfo& info)
  596. {
  597. info.setInfo ("Double Floating-Point Precision Rendering", {}, "General", 0);
  598. info.setTicked (isDoublePrecisionProcessingEnabled());
  599. }
  600. void MainHostWindow::updateAutoScaleMenuItem (ApplicationCommandInfo& info)
  601. {
  602. info.setInfo ("Auto-Scale Plug-in Windows", {}, "General", 0);
  603. info.setTicked (isAutoScalePluginWindowsEnabled());
  604. }