Audio plugin host https://kx.studio/carla
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.

435 lines
14KB

  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 "MainHostWindow.h"
  18. #include "InternalFilters.h"
  19. //==============================================================================
  20. class MainHostWindow::PluginListWindow : public DocumentWindow
  21. {
  22. public:
  23. PluginListWindow (MainHostWindow& owner_, AudioPluginFormatManager& formatManager)
  24. : DocumentWindow ("Available Plugins", Colours::white,
  25. DocumentWindow::minimiseButton | DocumentWindow::closeButton),
  26. owner (owner_)
  27. {
  28. const File deadMansPedalFile (owner.appProperties.getUserSettings()
  29. ->getFile().getSiblingFile ("RecentlyCrashedPluginsList"));
  30. setContentOwned (new PluginListComponent (formatManager,
  31. owner.knownPluginList,
  32. deadMansPedalFile,
  33. owner.appProperties.getUserSettings()), true);
  34. setOpaque (true);
  35. setResizable (true, false);
  36. setResizeLimits (300, 400, 800, 1500);
  37. setTopLeftPosition (60, 60);
  38. restoreWindowStateFromString (owner.appProperties.getUserSettings()->getValue ("listWindowPos"));
  39. setUsingNativeTitleBar (true);
  40. setVisible (true);
  41. }
  42. ~PluginListWindow()
  43. {
  44. owner.appProperties.getUserSettings()->setValue ("listWindowPos", getWindowStateAsString());
  45. clearContentComponent();
  46. }
  47. void closeButtonPressed()
  48. {
  49. owner.pluginListWindow = nullptr;
  50. }
  51. private:
  52. MainHostWindow& owner;
  53. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PluginListWindow)
  54. };
  55. //==============================================================================
  56. MainHostWindow::MainHostWindow (AudioPluginFormatManager& fm, FilterGraph& graph, ApplicationProperties& ap)
  57. : DocumentWindow ("Juce Patchbay", Colours::lightgrey, DocumentWindow::allButtons),
  58. formatManager (fm),
  59. appProperties (ap),
  60. closed (false)
  61. {
  62. LookAndFeel::setDefaultLookAndFeel (&lookAndFeel);
  63. setOpaque (true);
  64. setResizable (true, false);
  65. setResizeLimits (500, 400, 10000, 10000);
  66. centreWithSize (800, 600);
  67. setContentOwned (new GraphDocumentComponent (graph), false);
  68. setUsingNativeTitleBar (true);
  69. restoreWindowStateFromString (appProperties.getUserSettings()->getValue ("mainWindowPos"));
  70. setVisible (true);
  71. InternalPluginFormat internalFormat;
  72. internalFormat.getAllTypes (internalTypes);
  73. ScopedPointer<XmlElement> savedPluginList (appProperties.getUserSettings()->getXmlValue ("pluginList"));
  74. if (savedPluginList != nullptr)
  75. knownPluginList.recreateFromXml (*savedPluginList);
  76. pluginSortMethod = (KnownPluginList::SortMethod) appProperties.getUserSettings()
  77. ->getIntValue ("pluginSortMethod", KnownPluginList::sortByManufacturer);
  78. knownPluginList.addChangeListener (this);
  79. addKeyListener (commandManager.getKeyMappings());
  80. //Process::setPriority (Process::HighPriority);
  81. #if JUCE_MAC
  82. setMacMainMenu (this);
  83. #else
  84. setMenuBar (this);
  85. #endif
  86. commandManager.setFirstCommandTarget (this);
  87. commandManager.registerAllCommandsForTarget (this);
  88. menuItemsChanged();
  89. }
  90. MainHostWindow::~MainHostWindow()
  91. {
  92. pluginListWindow = nullptr;
  93. #if JUCE_MAC
  94. setMacMainMenu (nullptr);
  95. #else
  96. setMenuBar (nullptr);
  97. #endif
  98. knownPluginList.removeChangeListener (this);
  99. appProperties.getUserSettings()->setValue ("mainWindowPos", getWindowStateAsString());
  100. clearContentComponent();
  101. LookAndFeel::setDefaultLookAndFeel (nullptr);
  102. }
  103. void MainHostWindow::closeButtonPressed()
  104. {
  105. getGraphEditor()->closeAllCurrentlyOpenWindows();
  106. closed = true;
  107. }
  108. void MainHostWindow::changeListenerCallback (ChangeBroadcaster*)
  109. {
  110. menuItemsChanged();
  111. // save the plugin list every time it gets chnaged, so that if we're scanning
  112. // and it crashes, we've still saved the previous ones
  113. ScopedPointer<XmlElement> savedPluginList (knownPluginList.createXml());
  114. if (savedPluginList != nullptr)
  115. {
  116. appProperties.getUserSettings()->setValue ("pluginList", savedPluginList);
  117. appProperties.saveIfNeeded();
  118. }
  119. }
  120. StringArray MainHostWindow::getMenuBarNames()
  121. {
  122. const char* const names[] = { "File", "Plugins", "Options", nullptr };
  123. return StringArray (names);
  124. }
  125. PopupMenu MainHostWindow::getMenuForIndex (int topLevelMenuIndex, const String& /*menuName*/)
  126. {
  127. PopupMenu menu;
  128. if (topLevelMenuIndex == 0)
  129. {
  130. // "File" menu
  131. menu.addCommandItem (&commandManager, CommandIDs::open);
  132. RecentlyOpenedFilesList recentFiles;
  133. recentFiles.restoreFromString (appProperties.getUserSettings()
  134. ->getValue ("recentFilterGraphFiles"));
  135. PopupMenu recentFilesMenu;
  136. recentFiles.createPopupMenuItems (recentFilesMenu, 100, true, true);
  137. menu.addSubMenu ("Open recent file", recentFilesMenu);
  138. menu.addCommandItem (&commandManager, CommandIDs::save);
  139. menu.addCommandItem (&commandManager, CommandIDs::saveAs);
  140. }
  141. else if (topLevelMenuIndex == 1)
  142. {
  143. // "Plugins" menu
  144. PopupMenu pluginsMenu;
  145. addPluginsToMenu (pluginsMenu);
  146. menu.addSubMenu ("Create plugin", pluginsMenu);
  147. menu.addSeparator();
  148. menu.addItem (250, "Delete all plugins");
  149. }
  150. else if (topLevelMenuIndex == 2)
  151. {
  152. // "Options" menu
  153. menu.addCommandItem (&commandManager, CommandIDs::showPluginListEditor);
  154. PopupMenu sortTypeMenu;
  155. sortTypeMenu.addItem (200, "List plugins in default order", true, pluginSortMethod == KnownPluginList::defaultOrder);
  156. sortTypeMenu.addItem (201, "List plugins in alphabetical order", true, pluginSortMethod == KnownPluginList::sortAlphabetically);
  157. sortTypeMenu.addItem (202, "List plugins by category", true, pluginSortMethod == KnownPluginList::sortByCategory);
  158. sortTypeMenu.addItem (203, "List plugins by manufacturer", true, pluginSortMethod == KnownPluginList::sortByManufacturer);
  159. sortTypeMenu.addItem (204, "List plugins based on the directory structure", true, pluginSortMethod == KnownPluginList::sortByFileSystemLocation);
  160. menu.addSubMenu ("Plugin menu type", sortTypeMenu);
  161. menu.addSeparator();
  162. menu.addCommandItem (&commandManager, CommandIDs::aboutBox);
  163. }
  164. return menu;
  165. }
  166. void MainHostWindow::menuItemSelected (int menuItemID, int /*topLevelMenuIndex*/)
  167. {
  168. GraphDocumentComponent* const graphEditor = getGraphEditor();
  169. if (menuItemID == 250)
  170. {
  171. if (graphEditor != nullptr)
  172. graphEditor->graph.clear();
  173. }
  174. else if (menuItemID >= 100 && menuItemID < 200)
  175. {
  176. RecentlyOpenedFilesList recentFiles;
  177. recentFiles.restoreFromString (appProperties.getUserSettings()
  178. ->getValue ("recentFilterGraphFiles"));
  179. if (graphEditor != nullptr && graphEditor->graph.saveIfNeededAndUserAgrees() == FileBasedDocument::savedOk)
  180. graphEditor->graph.loadFrom (recentFiles.getFile (menuItemID - 100), true);
  181. }
  182. else if (menuItemID >= 200 && menuItemID < 210)
  183. {
  184. if (menuItemID == 200) pluginSortMethod = KnownPluginList::defaultOrder;
  185. else if (menuItemID == 201) pluginSortMethod = KnownPluginList::sortAlphabetically;
  186. else if (menuItemID == 202) pluginSortMethod = KnownPluginList::sortByCategory;
  187. else if (menuItemID == 203) pluginSortMethod = KnownPluginList::sortByManufacturer;
  188. else if (menuItemID == 204) pluginSortMethod = KnownPluginList::sortByFileSystemLocation;
  189. appProperties.getUserSettings()->setValue ("pluginSortMethod", (int) pluginSortMethod);
  190. menuItemsChanged();
  191. }
  192. else
  193. {
  194. createPlugin (getChosenType (menuItemID),
  195. proportionOfWidth (0.3f + Random::getSystemRandom().nextFloat() * 0.6f),
  196. proportionOfHeight (0.3f + Random::getSystemRandom().nextFloat() * 0.6f));
  197. }
  198. }
  199. void MainHostWindow::createPlugin (const PluginDescription* desc, int x, int y)
  200. {
  201. GraphDocumentComponent* const graphEditor = getGraphEditor();
  202. if (graphEditor != nullptr)
  203. graphEditor->createNewPlugin (desc, x, y);
  204. }
  205. void MainHostWindow::addPluginsToMenu (PopupMenu& m) const
  206. {
  207. for (int i = 0; i < internalTypes.size(); ++i)
  208. m.addItem (i + 1, internalTypes.getUnchecked(i)->name);
  209. m.addSeparator();
  210. knownPluginList.addToMenu (m, pluginSortMethod);
  211. }
  212. const PluginDescription* MainHostWindow::getChosenType (const int menuID) const
  213. {
  214. if (menuID >= 1 && menuID < 1 + internalTypes.size())
  215. return internalTypes [menuID - 1];
  216. return knownPluginList.getType (knownPluginList.getIndexChosenByMenu (menuID));
  217. }
  218. //==============================================================================
  219. ApplicationCommandTarget* MainHostWindow::getNextCommandTarget()
  220. {
  221. return findFirstTargetParentComponent();
  222. }
  223. void MainHostWindow::getAllCommands (Array <CommandID>& commands)
  224. {
  225. // this returns the set of all commands that this target can perform..
  226. const CommandID ids[] = { CommandIDs::open,
  227. CommandIDs::save,
  228. CommandIDs::saveAs,
  229. CommandIDs::showPluginListEditor,
  230. CommandIDs::aboutBox
  231. };
  232. commands.addArray (ids, numElementsInArray (ids));
  233. }
  234. void MainHostWindow::getCommandInfo (const CommandID commandID, ApplicationCommandInfo& result)
  235. {
  236. const String category ("General");
  237. switch (commandID)
  238. {
  239. case CommandIDs::open:
  240. result.setInfo ("Open...",
  241. "Opens a filter graph file",
  242. category, 0);
  243. result.defaultKeypresses.add (KeyPress ('o', ModifierKeys::commandModifier, 0));
  244. break;
  245. case CommandIDs::save:
  246. result.setInfo ("Save",
  247. "Saves the current graph to a file",
  248. category, 0);
  249. result.defaultKeypresses.add (KeyPress ('s', ModifierKeys::commandModifier, 0));
  250. break;
  251. case CommandIDs::saveAs:
  252. result.setInfo ("Save As...",
  253. "Saves a copy of the current graph to a file",
  254. category, 0);
  255. result.defaultKeypresses.add (KeyPress ('s', ModifierKeys::shiftModifier | ModifierKeys::commandModifier, 0));
  256. break;
  257. case CommandIDs::showPluginListEditor:
  258. result.setInfo ("Edit the list of available plug-Ins...", String::empty, category, 0);
  259. result.addDefaultKeypress ('p', ModifierKeys::commandModifier);
  260. break;
  261. case CommandIDs::aboutBox:
  262. result.setInfo ("About...", String::empty, category, 0);
  263. break;
  264. default:
  265. break;
  266. }
  267. }
  268. bool MainHostWindow::perform (const InvocationInfo& info)
  269. {
  270. GraphDocumentComponent* const graphEditor = getGraphEditor();
  271. switch (info.commandID)
  272. {
  273. case CommandIDs::open:
  274. if (graphEditor != nullptr && graphEditor->graph.saveIfNeededAndUserAgrees() == FileBasedDocument::savedOk)
  275. graphEditor->graph.loadFromUserSpecifiedFile (true);
  276. break;
  277. case CommandIDs::save:
  278. if (graphEditor != nullptr)
  279. graphEditor->graph.save (true, true);
  280. break;
  281. case CommandIDs::saveAs:
  282. if (graphEditor != nullptr)
  283. graphEditor->graph.saveAs (File::nonexistent, true, true, true);
  284. break;
  285. case CommandIDs::showPluginListEditor:
  286. if (pluginListWindow == nullptr)
  287. pluginListWindow = new PluginListWindow (*this, formatManager);
  288. pluginListWindow->toFront (true);
  289. break;
  290. case CommandIDs::aboutBox:
  291. // TODO
  292. break;
  293. default:
  294. return false;
  295. }
  296. return true;
  297. }
  298. bool MainHostWindow::isInterestedInFileDrag (const StringArray&)
  299. {
  300. return true;
  301. }
  302. void MainHostWindow::fileDragEnter (const StringArray&, int, int)
  303. {
  304. }
  305. void MainHostWindow::fileDragMove (const StringArray&, int, int)
  306. {
  307. }
  308. void MainHostWindow::fileDragExit (const StringArray&)
  309. {
  310. }
  311. void MainHostWindow::filesDropped (const StringArray& files, int x, int y)
  312. {
  313. GraphDocumentComponent* const graphEditor = getGraphEditor();
  314. if (graphEditor != nullptr)
  315. {
  316. if (files.size() == 1 && File (files[0]).hasFileExtension (filenameSuffix))
  317. {
  318. if (graphEditor->graph.saveIfNeededAndUserAgrees() == FileBasedDocument::savedOk)
  319. graphEditor->graph.loadFrom (File (files[0]), true);
  320. }
  321. else
  322. {
  323. OwnedArray <PluginDescription> typesFound;
  324. knownPluginList.scanAndAddDragAndDroppedFiles (formatManager, files, typesFound);
  325. Point<int> pos (graphEditor->getLocalPoint (this, Point<int> (x, y)));
  326. for (int i = 0; i < jmin (5, typesFound.size()); ++i)
  327. createPlugin (typesFound.getUnchecked(i), pos.getX(), pos.getY());
  328. }
  329. }
  330. }
  331. GraphDocumentComponent* MainHostWindow::getGraphEditor() const
  332. {
  333. return dynamic_cast <GraphDocumentComponent*> (getContentComponent());
  334. }
  335. bool MainHostWindow::wasClosedByUser() const noexcept
  336. {
  337. return closed;
  338. }