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.

MainHostWindow.cpp 14KB

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