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.

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