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.

496 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 PluginListWindow : public DocumentWindow
  23. {
  24. public:
  25. PluginListWindow (KnownPluginList& knownPluginList)
  26. : DocumentWindow ("Available Plugins", Colours::white,
  27. DocumentWindow::minimiseButton | DocumentWindow::closeButton)
  28. {
  29. currentPluginListWindow = this;
  30. const File deadMansPedalFile (appProperties->getUserSettings()
  31. ->getFile().getSiblingFile ("RecentlyCrashedPluginsList"));
  32. setContentOwned (new PluginListComponent (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. jassert (currentPluginListWindow == this);
  46. currentPluginListWindow = 0;
  47. }
  48. void closeButtonPressed()
  49. {
  50. delete this;
  51. }
  52. static PluginListWindow* currentPluginListWindow;
  53. };
  54. PluginListWindow* PluginListWindow::currentPluginListWindow = 0;
  55. //==============================================================================
  56. MainHostWindow::MainHostWindow()
  57. : DocumentWindow (JUCEApplication::getInstance()->getApplicationName(), Colours::lightgrey,
  58. DocumentWindow::allButtons)
  59. {
  60. ScopedPointer<XmlElement> savedAudioState (appProperties->getUserSettings()
  61. ->getXmlValue ("audioDeviceState"));
  62. deviceManager.initialise (256, 256, savedAudioState, true);
  63. setResizable (true, false);
  64. setResizeLimits (500, 400, 10000, 10000);
  65. centreWithSize (800, 600);
  66. setContentOwned (new GraphDocumentComponent (&deviceManager), false);
  67. restoreWindowStateFromString (appProperties->getUserSettings()->getValue ("mainWindowPos"));
  68. setVisible (true);
  69. InternalPluginFormat internalFormat;
  70. internalFormat.getAllTypes (internalTypes);
  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. }
  85. MainHostWindow::~MainHostWindow()
  86. {
  87. delete PluginListWindow::currentPluginListWindow;
  88. #if JUCE_MAC
  89. setMacMainMenu (0);
  90. #else
  91. setMenuBar (0);
  92. #endif
  93. knownPluginList.removeChangeListener (this);
  94. appProperties->getUserSettings()->setValue ("mainWindowPos", getWindowStateAsString());
  95. clearContentComponent();
  96. }
  97. void MainHostWindow::closeButtonPressed()
  98. {
  99. tryToQuitApplication();
  100. }
  101. bool MainHostWindow::tryToQuitApplication()
  102. {
  103. if (getGraphEditor() != 0
  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 != 0)
  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", 0 };
  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 != 0)
  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 != 0 && 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)
  192. pluginSortMethod = KnownPluginList::defaultOrder;
  193. else if (menuItemID == 201)
  194. pluginSortMethod = KnownPluginList::sortAlphabetically;
  195. else if (menuItemID == 202)
  196. pluginSortMethod = KnownPluginList::sortByCategory;
  197. else if (menuItemID == 203)
  198. pluginSortMethod = KnownPluginList::sortByManufacturer;
  199. else if (menuItemID == 204)
  200. pluginSortMethod = KnownPluginList::sortByFileSystemLocation;
  201. appProperties->getUserSettings()->setValue ("pluginSortMethod", (int) pluginSortMethod);
  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 != 0)
  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. {
  227. return internalTypes [menuID - 1];
  228. }
  229. else
  230. {
  231. return knownPluginList.getType (knownPluginList.getIndexChosenByMenu (menuID));
  232. }
  233. }
  234. //==============================================================================
  235. ApplicationCommandTarget* MainHostWindow::getNextCommandTarget()
  236. {
  237. return findFirstTargetParentComponent();
  238. }
  239. void MainHostWindow::getAllCommands (Array <CommandID>& commands)
  240. {
  241. // this returns the set of all commands that this target can perform..
  242. const CommandID ids[] = { CommandIDs::open,
  243. CommandIDs::save,
  244. CommandIDs::saveAs,
  245. CommandIDs::showPluginListEditor,
  246. CommandIDs::showAudioSettings,
  247. CommandIDs::aboutBox
  248. };
  249. commands.addArray (ids, numElementsInArray (ids));
  250. }
  251. void MainHostWindow::getCommandInfo (const CommandID commandID, ApplicationCommandInfo& result)
  252. {
  253. const String category ("General");
  254. switch (commandID)
  255. {
  256. case CommandIDs::open:
  257. result.setInfo ("Open...",
  258. "Opens a filter graph file",
  259. category, 0);
  260. result.defaultKeypresses.add (KeyPress ('o', ModifierKeys::commandModifier, 0));
  261. break;
  262. case CommandIDs::save:
  263. result.setInfo ("Save",
  264. "Saves the current graph to a file",
  265. category, 0);
  266. result.defaultKeypresses.add (KeyPress ('s', ModifierKeys::commandModifier, 0));
  267. break;
  268. case CommandIDs::saveAs:
  269. result.setInfo ("Save As...",
  270. "Saves a copy of the current graph to a file",
  271. category, 0);
  272. result.defaultKeypresses.add (KeyPress ('s', ModifierKeys::shiftModifier | ModifierKeys::commandModifier, 0));
  273. break;
  274. case CommandIDs::showPluginListEditor:
  275. result.setInfo ("Edit the list of available plug-Ins...", String::empty, category, 0);
  276. result.addDefaultKeypress ('p', ModifierKeys::commandModifier);
  277. break;
  278. case CommandIDs::showAudioSettings:
  279. result.setInfo ("Change the audio device settings", String::empty, category, 0);
  280. result.addDefaultKeypress ('a', ModifierKeys::commandModifier);
  281. break;
  282. case CommandIDs::aboutBox:
  283. result.setInfo ("About...", String::empty, category, 0);
  284. break;
  285. default:
  286. break;
  287. }
  288. }
  289. bool MainHostWindow::perform (const InvocationInfo& info)
  290. {
  291. GraphDocumentComponent* const graphEditor = getGraphEditor();
  292. switch (info.commandID)
  293. {
  294. case CommandIDs::open:
  295. if (graphEditor != 0 && graphEditor->graph.saveIfNeededAndUserAgrees() == FileBasedDocument::savedOk)
  296. graphEditor->graph.loadFromUserSpecifiedFile (true);
  297. break;
  298. case CommandIDs::save:
  299. if (graphEditor != 0)
  300. graphEditor->graph.save (true, true);
  301. break;
  302. case CommandIDs::saveAs:
  303. if (graphEditor != 0)
  304. graphEditor->graph.saveAs (File::nonexistent, true, true, true);
  305. break;
  306. case CommandIDs::showPluginListEditor:
  307. if (PluginListWindow::currentPluginListWindow == 0)
  308. PluginListWindow::currentPluginListWindow = new PluginListWindow (knownPluginList);
  309. PluginListWindow::currentPluginListWindow->toFront (true);
  310. break;
  311. case CommandIDs::showAudioSettings:
  312. showAudioSettings();
  313. break;
  314. case CommandIDs::aboutBox:
  315. {
  316. /* AboutBoxComponent aboutComp;
  317. DialogWindow::showModalDialog ("About",
  318. &aboutComp,
  319. this, Colours::white,
  320. true, false, false);
  321. */ }
  322. break;
  323. default:
  324. return false;
  325. }
  326. return true;
  327. }
  328. void MainHostWindow::showAudioSettings()
  329. {
  330. AudioDeviceSelectorComponent audioSettingsComp (deviceManager,
  331. 0, 256,
  332. 0, 256,
  333. true, true, true, false);
  334. audioSettingsComp.setSize (500, 450);
  335. DialogWindow::showModalDialog ("Audio Settings",
  336. &audioSettingsComp,
  337. this,
  338. Colours::azure,
  339. true);
  340. ScopedPointer<XmlElement> audioState (deviceManager.createStateXml());
  341. appProperties->getUserSettings()->setValue ("audioDeviceState", audioState);
  342. appProperties->getUserSettings()->saveIfNeeded();
  343. GraphDocumentComponent* const graphEditor = getGraphEditor();
  344. if (graphEditor != 0)
  345. graphEditor->graph.removeIllegalConnections();
  346. }
  347. bool MainHostWindow::isInterestedInFileDrag (const StringArray&)
  348. {
  349. return true;
  350. }
  351. void MainHostWindow::fileDragEnter (const StringArray&, int, int)
  352. {
  353. }
  354. void MainHostWindow::fileDragMove (const StringArray&, int, int)
  355. {
  356. }
  357. void MainHostWindow::fileDragExit (const StringArray&)
  358. {
  359. }
  360. void MainHostWindow::filesDropped (const StringArray& files, int x, int y)
  361. {
  362. if (files.size() == 1 && File (files[0]).hasFileExtension (filenameSuffix))
  363. {
  364. GraphDocumentComponent* const graphEditor = getGraphEditor();
  365. if (graphEditor != 0
  366. && graphEditor->graph.saveIfNeededAndUserAgrees() == FileBasedDocument::savedOk)
  367. {
  368. graphEditor->graph.loadFrom (File (files[0]), true);
  369. }
  370. }
  371. else
  372. {
  373. OwnedArray <PluginDescription> typesFound;
  374. knownPluginList.scanAndAddDragAndDroppedFiles (files, typesFound);
  375. GraphDocumentComponent* const graphEditor = getGraphEditor();
  376. Point<int> pos (x, y);
  377. if (graphEditor != 0)
  378. pos = graphEditor->getLocalPoint (this, pos);
  379. for (int i = 0; i < jmin (5, typesFound.size()); ++i)
  380. createPlugin (typesFound.getUnchecked(i), pos.getX(), pos.getY());
  381. }
  382. }
  383. GraphDocumentComponent* MainHostWindow::getGraphEditor() const
  384. {
  385. return dynamic_cast <GraphDocumentComponent*> (getContentComponent());
  386. }