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.

485 lines
17KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-9 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. #include "../JuceLibraryCode/JuceHeader.h"
  19. #include "MainHostWindow.h"
  20. #include "InternalFilters.h"
  21. //==============================================================================
  22. class MainHostWindow::PluginListWindow : public DocumentWindow
  23. {
  24. public:
  25. PluginListWindow (MainHostWindow& owner_, AudioPluginFormatManager& formatManager)
  26. : DocumentWindow ("Available Plugins", Colours::white,
  27. DocumentWindow::minimiseButton | DocumentWindow::closeButton),
  28. owner (owner_)
  29. {
  30. const File deadMansPedalFile (appProperties->getUserSettings()
  31. ->getFile().getSiblingFile ("RecentlyCrashedPluginsList"));
  32. setContentOwned (new PluginListComponent (formatManager,
  33. owner.knownPluginList,
  34. deadMansPedalFile,
  35. appProperties->getUserSettings()), true);
  36. setResizable (true, false);
  37. setResizeLimits (300, 400, 800, 1500);
  38. setTopLeftPosition (60, 60);
  39. restoreWindowStateFromString (appProperties->getUserSettings()->getValue ("listWindowPos"));
  40. setVisible (true);
  41. }
  42. ~PluginListWindow()
  43. {
  44. 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()
  57. : DocumentWindow (JUCEApplication::getInstance()->getApplicationName(), Colours::lightgrey,
  58. DocumentWindow::allButtons)
  59. {
  60. formatManager.addDefaultFormats();
  61. formatManager.addFormat (new InternalPluginFormat());
  62. ScopedPointer<XmlElement> savedAudioState (appProperties->getUserSettings()
  63. ->getXmlValue ("audioDeviceState"));
  64. deviceManager.initialise (256, 256, savedAudioState, true);
  65. setResizable (true, false);
  66. setResizeLimits (500, 400, 10000, 10000);
  67. centreWithSize (800, 600);
  68. setContentOwned (new GraphDocumentComponent (formatManager, &deviceManager), false);
  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. }
  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. }
  100. void MainHostWindow::closeButtonPressed()
  101. {
  102. tryToQuitApplication();
  103. }
  104. bool MainHostWindow::tryToQuitApplication()
  105. {
  106. PluginWindow::closeAllCurrentlyOpenWindows();
  107. if (getGraphEditor() == nullptr
  108. || getGraphEditor()->graph.saveIfNeededAndUserAgrees() == FileBasedDocument::savedOk)
  109. {
  110. JUCEApplication::quit();
  111. return true;
  112. }
  113. return false;
  114. }
  115. void MainHostWindow::changeListenerCallback (ChangeBroadcaster*)
  116. {
  117. menuItemsChanged();
  118. // save the plugin list every time it gets chnaged, so that if we're scanning
  119. // and it crashes, we've still saved the previous ones
  120. ScopedPointer<XmlElement> savedPluginList (knownPluginList.createXml());
  121. if (savedPluginList != nullptr)
  122. {
  123. appProperties->getUserSettings()->setValue ("pluginList", savedPluginList);
  124. appProperties->saveIfNeeded();
  125. }
  126. }
  127. StringArray MainHostWindow::getMenuBarNames()
  128. {
  129. const char* const names[] = { "File", "Plugins", "Options", nullptr };
  130. return StringArray (names);
  131. }
  132. PopupMenu MainHostWindow::getMenuForIndex (int topLevelMenuIndex, const String& /*menuName*/)
  133. {
  134. PopupMenu menu;
  135. if (topLevelMenuIndex == 0)
  136. {
  137. // "File" menu
  138. menu.addCommandItem (commandManager, CommandIDs::open);
  139. RecentlyOpenedFilesList recentFiles;
  140. recentFiles.restoreFromString (appProperties->getUserSettings()
  141. ->getValue ("recentFilterGraphFiles"));
  142. PopupMenu recentFilesMenu;
  143. recentFiles.createPopupMenuItems (recentFilesMenu, 100, true, true);
  144. menu.addSubMenu ("Open recent file", recentFilesMenu);
  145. menu.addCommandItem (commandManager, CommandIDs::save);
  146. menu.addCommandItem (commandManager, CommandIDs::saveAs);
  147. menu.addSeparator();
  148. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::quit);
  149. }
  150. else if (topLevelMenuIndex == 1)
  151. {
  152. // "Plugins" menu
  153. PopupMenu pluginsMenu;
  154. addPluginsToMenu (pluginsMenu);
  155. menu.addSubMenu ("Create plugin", pluginsMenu);
  156. menu.addSeparator();
  157. menu.addItem (250, "Delete all plugins");
  158. }
  159. else if (topLevelMenuIndex == 2)
  160. {
  161. // "Options" menu
  162. menu.addCommandItem (commandManager, CommandIDs::showPluginListEditor);
  163. PopupMenu sortTypeMenu;
  164. sortTypeMenu.addItem (200, "List plugins in default order", true, pluginSortMethod == KnownPluginList::defaultOrder);
  165. sortTypeMenu.addItem (201, "List plugins in alphabetical order", true, pluginSortMethod == KnownPluginList::sortAlphabetically);
  166. sortTypeMenu.addItem (202, "List plugins by category", true, pluginSortMethod == KnownPluginList::sortByCategory);
  167. sortTypeMenu.addItem (203, "List plugins by manufacturer", true, pluginSortMethod == KnownPluginList::sortByManufacturer);
  168. sortTypeMenu.addItem (204, "List plugins based on the directory structure", true, pluginSortMethod == KnownPluginList::sortByFileSystemLocation);
  169. menu.addSubMenu ("Plugin menu type", sortTypeMenu);
  170. menu.addSeparator();
  171. menu.addCommandItem (commandManager, CommandIDs::showAudioSettings);
  172. menu.addSeparator();
  173. menu.addCommandItem (commandManager, CommandIDs::aboutBox);
  174. }
  175. return menu;
  176. }
  177. void MainHostWindow::menuItemSelected (int menuItemID, int /*topLevelMenuIndex*/)
  178. {
  179. GraphDocumentComponent* const graphEditor = getGraphEditor();
  180. if (menuItemID == 250)
  181. {
  182. if (graphEditor != nullptr)
  183. graphEditor->graph.clear();
  184. }
  185. else if (menuItemID >= 100 && menuItemID < 200)
  186. {
  187. RecentlyOpenedFilesList recentFiles;
  188. recentFiles.restoreFromString (appProperties->getUserSettings()
  189. ->getValue ("recentFilterGraphFiles"));
  190. if (graphEditor != nullptr && graphEditor->graph.saveIfNeededAndUserAgrees() == FileBasedDocument::savedOk)
  191. graphEditor->graph.loadFrom (recentFiles.getFile (menuItemID - 100), true);
  192. }
  193. else if (menuItemID >= 200 && menuItemID < 210)
  194. {
  195. if (menuItemID == 200) pluginSortMethod = KnownPluginList::defaultOrder;
  196. else if (menuItemID == 201) pluginSortMethod = KnownPluginList::sortAlphabetically;
  197. else if (menuItemID == 202) pluginSortMethod = KnownPluginList::sortByCategory;
  198. else if (menuItemID == 203) pluginSortMethod = KnownPluginList::sortByManufacturer;
  199. else if (menuItemID == 204) pluginSortMethod = KnownPluginList::sortByFileSystemLocation;
  200. appProperties->getUserSettings()->setValue ("pluginSortMethod", (int) pluginSortMethod);
  201. menuItemsChanged();
  202. }
  203. else
  204. {
  205. createPlugin (getChosenType (menuItemID),
  206. proportionOfWidth (0.3f + Random::getSystemRandom().nextFloat() * 0.6f),
  207. proportionOfHeight (0.3f + Random::getSystemRandom().nextFloat() * 0.6f));
  208. }
  209. }
  210. void MainHostWindow::createPlugin (const PluginDescription* desc, int x, int y)
  211. {
  212. GraphDocumentComponent* const graphEditor = getGraphEditor();
  213. if (graphEditor != nullptr)
  214. graphEditor->createNewPlugin (desc, x, y);
  215. }
  216. void MainHostWindow::addPluginsToMenu (PopupMenu& m) const
  217. {
  218. for (int i = 0; i < internalTypes.size(); ++i)
  219. m.addItem (i + 1, internalTypes.getUnchecked(i)->name);
  220. m.addSeparator();
  221. knownPluginList.addToMenu (m, pluginSortMethod);
  222. }
  223. const PluginDescription* MainHostWindow::getChosenType (const int menuID) const
  224. {
  225. if (menuID >= 1 && menuID < 1 + internalTypes.size())
  226. return internalTypes [menuID - 1];
  227. return knownPluginList.getType (knownPluginList.getIndexChosenByMenu (menuID));
  228. }
  229. //==============================================================================
  230. ApplicationCommandTarget* MainHostWindow::getNextCommandTarget()
  231. {
  232. return findFirstTargetParentComponent();
  233. }
  234. void MainHostWindow::getAllCommands (Array <CommandID>& commands)
  235. {
  236. // this returns the set of all commands that this target can perform..
  237. const CommandID ids[] = { CommandIDs::open,
  238. CommandIDs::save,
  239. CommandIDs::saveAs,
  240. CommandIDs::showPluginListEditor,
  241. CommandIDs::showAudioSettings,
  242. CommandIDs::aboutBox
  243. };
  244. commands.addArray (ids, numElementsInArray (ids));
  245. }
  246. void MainHostWindow::getCommandInfo (const CommandID commandID, ApplicationCommandInfo& result)
  247. {
  248. const String category ("General");
  249. switch (commandID)
  250. {
  251. case CommandIDs::open:
  252. result.setInfo ("Open...",
  253. "Opens a filter graph file",
  254. category, 0);
  255. result.defaultKeypresses.add (KeyPress ('o', ModifierKeys::commandModifier, 0));
  256. break;
  257. case CommandIDs::save:
  258. result.setInfo ("Save",
  259. "Saves the current graph to a file",
  260. category, 0);
  261. result.defaultKeypresses.add (KeyPress ('s', ModifierKeys::commandModifier, 0));
  262. break;
  263. case CommandIDs::saveAs:
  264. result.setInfo ("Save As...",
  265. "Saves a copy of the current graph to a file",
  266. category, 0);
  267. result.defaultKeypresses.add (KeyPress ('s', ModifierKeys::shiftModifier | ModifierKeys::commandModifier, 0));
  268. break;
  269. case CommandIDs::showPluginListEditor:
  270. result.setInfo ("Edit the list of available plug-Ins...", String::empty, category, 0);
  271. result.addDefaultKeypress ('p', ModifierKeys::commandModifier);
  272. break;
  273. case CommandIDs::showAudioSettings:
  274. result.setInfo ("Change the audio device settings", String::empty, category, 0);
  275. result.addDefaultKeypress ('a', ModifierKeys::commandModifier);
  276. break;
  277. case CommandIDs::aboutBox:
  278. result.setInfo ("About...", String::empty, category, 0);
  279. break;
  280. default:
  281. break;
  282. }
  283. }
  284. bool MainHostWindow::perform (const InvocationInfo& info)
  285. {
  286. GraphDocumentComponent* const graphEditor = getGraphEditor();
  287. switch (info.commandID)
  288. {
  289. case CommandIDs::open:
  290. if (graphEditor != nullptr && graphEditor->graph.saveIfNeededAndUserAgrees() == FileBasedDocument::savedOk)
  291. graphEditor->graph.loadFromUserSpecifiedFile (true);
  292. break;
  293. case CommandIDs::save:
  294. if (graphEditor != nullptr)
  295. graphEditor->graph.save (true, true);
  296. break;
  297. case CommandIDs::saveAs:
  298. if (graphEditor != nullptr)
  299. graphEditor->graph.saveAs (File::nonexistent, true, true, true);
  300. break;
  301. case CommandIDs::showPluginListEditor:
  302. if (pluginListWindow == nullptr)
  303. pluginListWindow = new PluginListWindow (*this, formatManager);
  304. pluginListWindow->toFront (true);
  305. break;
  306. case CommandIDs::showAudioSettings:
  307. showAudioSettings();
  308. break;
  309. case CommandIDs::aboutBox:
  310. // TODO
  311. break;
  312. default:
  313. return false;
  314. }
  315. return true;
  316. }
  317. void MainHostWindow::showAudioSettings()
  318. {
  319. AudioDeviceSelectorComponent audioSettingsComp (deviceManager,
  320. 0, 256,
  321. 0, 256,
  322. true, true, true, false);
  323. audioSettingsComp.setSize (500, 450);
  324. DialogWindow::LaunchOptions o;
  325. o.content.setNonOwned (&audioSettingsComp);
  326. o.dialogTitle = "Audio Settings";
  327. o.componentToCentreAround = this;
  328. o.dialogBackgroundColour = Colours::azure;
  329. o.escapeKeyTriggersCloseButton = true;
  330. o.useNativeTitleBar = false;
  331. o.resizable = false;
  332. o.runModal();
  333. ScopedPointer<XmlElement> audioState (deviceManager.createStateXml());
  334. appProperties->getUserSettings()->setValue ("audioDeviceState", audioState);
  335. appProperties->getUserSettings()->saveIfNeeded();
  336. GraphDocumentComponent* const graphEditor = getGraphEditor();
  337. if (graphEditor != nullptr)
  338. graphEditor->graph.removeIllegalConnections();
  339. }
  340. bool MainHostWindow::isInterestedInFileDrag (const StringArray&)
  341. {
  342. return true;
  343. }
  344. void MainHostWindow::fileDragEnter (const StringArray&, int, int)
  345. {
  346. }
  347. void MainHostWindow::fileDragMove (const StringArray&, int, int)
  348. {
  349. }
  350. void MainHostWindow::fileDragExit (const StringArray&)
  351. {
  352. }
  353. void MainHostWindow::filesDropped (const StringArray& files, int x, int y)
  354. {
  355. GraphDocumentComponent* const graphEditor = getGraphEditor();
  356. if (graphEditor != nullptr)
  357. {
  358. if (files.size() == 1 && File (files[0]).hasFileExtension (filenameSuffix))
  359. {
  360. if (graphEditor->graph.saveIfNeededAndUserAgrees() == FileBasedDocument::savedOk)
  361. graphEditor->graph.loadFrom (File (files[0]), true);
  362. }
  363. else
  364. {
  365. OwnedArray <PluginDescription> typesFound;
  366. knownPluginList.scanAndAddDragAndDroppedFiles (formatManager, files, typesFound);
  367. Point<int> pos (graphEditor->getLocalPoint (this, Point<int> (x, y)));
  368. for (int i = 0; i < jmin (5, typesFound.size()); ++i)
  369. createPlugin (typesFound.getUnchecked(i), pos.getX(), pos.getY());
  370. }
  371. }
  372. }
  373. GraphDocumentComponent* MainHostWindow::getGraphEditor() const
  374. {
  375. return dynamic_cast <GraphDocumentComponent*> (getContentComponent());
  376. }