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.

472 lines
16KB

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