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.

440 lines
15KB

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