// // IconMenu.cpp // Light Host // // Created by Rolando Islas on 12/26/15. // // #include "../JuceLibraryCode/JuceHeader.h" #include "IconMenu.hpp" #include "PluginWindow.h" #if !JUCE_MAC class IconMenu::PluginListWindow : public DocumentWindow { public: PluginListWindow(IconMenu& owner_, AudioPluginFormatManager& pluginFormatManager) : DocumentWindow("Available Plugins", Colours::white, DocumentWindow::minimiseButton | DocumentWindow::closeButton), owner(owner_) { const File deadMansPedalFile(getAppProperties().getUserSettings() ->getFile().getSiblingFile("RecentlyCrashedPluginsList")); setContentOwned(new PluginListComponent(pluginFormatManager, owner.knownPluginList, deadMansPedalFile, getAppProperties().getUserSettings()), true); setResizable(true, false); setResizeLimits(300, 400, 800, 1500); setTopLeftPosition(60, 60); restoreWindowStateFromString(getAppProperties().getUserSettings()->getValue("listWindowPos")); setVisible(true); } ~PluginListWindow() { getAppProperties().getUserSettings()->setValue("listWindowPos", getWindowStateAsString()); clearContentComponent(); } void closeButtonPressed() { owner.pluginListWindow = nullptr; } private: IconMenu& owner; }; #endif IconMenu::IconMenu() { // Initiialization formatManager.addDefaultFormats(); // Audio device ScopedPointer savedAudioState (getAppProperties().getUserSettings()->getXmlValue("audioDeviceState")); deviceManager.initialise(256, 256, savedAudioState, true); player.setProcessor(&graph); deviceManager.addAudioCallback(&player); // Plugins - all ScopedPointer savedPluginList(getAppProperties().getUserSettings()->getXmlValue("pluginList")); if (savedPluginList != nullptr) knownPluginList.recreateFromXml(*savedPluginList); pluginSortMethod = KnownPluginList::sortByManufacturer; knownPluginList.addChangeListener(this); // Plugins - active ScopedPointer savedPluginListActive(getAppProperties().getUserSettings()->getXmlValue("pluginListActive")); if (savedPluginListActive != nullptr) activePluginList.recreateFromXml(*savedPluginListActive); loadActivePlugins(); activePluginList.addChangeListener(this); // Set menu icon #if JUCE_MAC if (exec("defaults read -g AppleInterfaceStyle").compare("Dark") == 1) setIconImage(ImageFileFormat::loadFrom(BinaryData::menu_icon_white_png, BinaryData::menu_icon_white_pngSize)); else setIconImage(ImageFileFormat::loadFrom(BinaryData::menu_icon_png, BinaryData::menu_icon_pngSize)); #else setIconImage(ImageFileFormat::loadFrom(BinaryData::menu_icon_png, BinaryData::menu_icon_pngSize)); #endif }; IconMenu::~IconMenu() { } void IconMenu::loadActivePlugins() { graph.clear(); inputNode = graph.addNode(new AudioProcessorGraph::AudioGraphIOProcessor(AudioProcessorGraph::AudioGraphIOProcessor::audioInputNode), 1); outputNode = graph.addNode(new AudioProcessorGraph::AudioGraphIOProcessor(AudioProcessorGraph::AudioGraphIOProcessor::audioOutputNode), 2); if (activePluginList.getNumTypes() == 0) { graph.addConnection(1, 0, 2, 0); graph.addConnection(1, 1, 2, 1); } for (int i = 0; i < activePluginList.getNumTypes(); i++) { PluginDescription plugin = *activePluginList.getType(i); String errorMessage; AudioPluginInstance* instance = formatManager.createPluginInstance(plugin, graph.getSampleRate(), graph.getBlockSize(), errorMessage); String pluginUid; pluginUid << "pluginState-" << i; String savedPluginState = getAppProperties().getUserSettings()->getValue(pluginUid); MemoryBlock savedPluginBinary; savedPluginBinary.fromBase64Encoding(savedPluginState); instance->setStateInformation(savedPluginBinary.getData(), savedPluginBinary.getSize()); graph.addNode(instance, i+3); // Input to plugin if (i == 0) { graph.addConnection(1, 0, i+3, 0); graph.addConnection(1, 1, i+3, 1); } // Plugin to output if (i == activePluginList.getNumTypes() - 1) { graph.addConnection(i+3, 0, 2, 0); graph.addConnection(i+3, 1, 2, 1); } // Connect previous plugin to current if (i > 0) { graph.addConnection(i+2, 0, i+3, 0); graph.addConnection(i+2, 1, i+3, 1); } } } void IconMenu::changeListenerCallback(ChangeBroadcaster* changed) { if (changed == &knownPluginList) { ScopedPointer savedPluginList (knownPluginList.createXml()); if (savedPluginList != nullptr) { getAppProperties().getUserSettings()->setValue ("pluginList", savedPluginList); getAppProperties().saveIfNeeded(); } } else if (changed == &activePluginList) { ScopedPointer savedPluginList (activePluginList.createXml()); if (savedPluginList != nullptr) { getAppProperties().getUserSettings()->setValue ("pluginListActive", savedPluginList); getAppProperties().saveIfNeeded(); loadActivePlugins(); } } } #if JUCE_MAC std::string IconMenu::exec(const char* cmd) { std::shared_ptr pipe(popen(cmd, "r"), pclose); if (!pipe) return "ERROR"; char buffer[128]; std::string result = ""; while (!feof(pipe.get())) { if (fgets(buffer, 128, pipe.get()) != NULL) result += buffer; } return result; } #endif void IconMenu::timerCallback() { stopTimer(); menu.clear(); menu.addSectionHeader(JUCEApplication::getInstance()->getApplicationName()); if (menuIconLeftClicked) { menu.addItem(1, "Preferences"); menu.addItem(2, "Reload Plugins"); menu.addSeparator(); // Active plugins for (int i = 0; i < activePluginList.getNumTypes(); i++) { PopupMenu options; options.addItem(i+3, "Edit"); options.addItem(activePluginList.getNumTypes()+i+3, "Delete"); // TODO bypass menu.addSubMenu(activePluginList.getType(i)->name, options); } menu.addSeparator(); // All plugins knownPluginList.addToMenu(menu, pluginSortMethod); } else { menu.addItem(1, "Quit"); } menu.showMenuAsync(PopupMenu::Options().withTargetComponent(this), ModalCallbackFunction::forComponent(menuInvocationCallback, this)); } void IconMenu::mouseDown(const MouseEvent& e) { #if JUCE_MAC Process::setDockIconVisible(true); #endif Process::makeForegroundProcess(); menuIconLeftClicked = e.mods.isLeftButtonDown(); startTimer(50); } void IconMenu::menuInvocationCallback(int id, IconMenu* im) { // Right click if ((!im->menuIconLeftClicked) && id == 1) { im->savePluginStates(); return JUCEApplication::getInstance()->quit(); } #if JUCE_MAC // Click elsewhere if (id == 0 && !PluginWindow::containsActiveWindows()) Process::setDockIconVisible(false); #endif // Audio settings if (id == 1) im->showAudioSettings(); // Reload if (id == 2) im->reloadPlugins(); // Plugins if (id > 2) { // Delete plugin if (id > im->activePluginList.getNumTypes() + 2 && id <= im->activePluginList.getNumTypes() * 2 + 2) { im->deletePluginStates(); im->activePluginList.removeType(id - im->activePluginList.getNumTypes() - 3); } // Add plugin else if (id > im->activePluginList.getNumTypes() + 2) { im->deletePluginStates(); im->activePluginList.addType(*im->knownPluginList.getType(im->knownPluginList.getIndexChosenByMenu(id))); } // Show active plugin GUI else { if (const AudioProcessorGraph::Node::Ptr f = im->graph.getNodeForId(id)) if (PluginWindow* const w = PluginWindow::getWindowFor(f, PluginWindow::Normal)) w->toFront(true); } // Update menu im->startTimer(50); } } void IconMenu::deletePluginStates() { for (int i = 0; i < activePluginList.getNumTypes(); i++) { String pluginUid; pluginUid << "pluginState-" << i; getAppProperties().getUserSettings()->removeValue(pluginUid); getAppProperties().saveIfNeeded(); } } void IconMenu::savePluginStates() { for (int i = 0; i < activePluginList.getNumTypes(); i++) { AudioProcessor& processor = *graph.getNodeForId(i+3)->getProcessor(); String pluginUid; pluginUid << "pluginState-" << i; MemoryBlock savedStateBinary; processor.getStateInformation(savedStateBinary); ScopedPointer savedStateXml(XmlElement::createTextElement(savedStateBinary.toBase64Encoding())); getAppProperties().getUserSettings()->setValue(pluginUid, savedStateBinary.toBase64Encoding()); getAppProperties().saveIfNeeded(); } } void IconMenu::showAudioSettings() { AudioDeviceSelectorComponent audioSettingsComp (deviceManager, 0, 256, 0, 256, false, false, true, true); audioSettingsComp.setSize(500, 450); DialogWindow::LaunchOptions o; o.content.setNonOwned(&audioSettingsComp); o.dialogTitle = "Audio Settings"; o.componentToCentreAround = this; o.dialogBackgroundColour = Colour::fromRGB(236, 236, 236); o.escapeKeyTriggersCloseButton = true; o.useNativeTitleBar = true; o.resizable = false; o.runModal(); ScopedPointer audioState(deviceManager.createStateXml()); getAppProperties().getUserSettings()->setValue("audioDeviceState", audioState); getAppProperties().getUserSettings()->saveIfNeeded(); } void IconMenu::reloadPlugins() { #if JUCE_MAC NativeMessageBox::showOkCancelBox(AlertWindow::AlertIconType::InfoIcon, "Reload Plugins?", "Confirm scan and load of any new or updated plugins.", this, ModalCallbackFunction::forComponent(doReloadWithDefaultLocations, this)); #else if (pluginListWindow == nullptr) pluginListWindow = new PluginListWindow(*this, formatManager); pluginListWindow->toFront(true); #endif } void IconMenu::doReloadWithDefaultLocations(int id, IconMenu* im) { #if JUCE_MAC // Canceled if (id == 0) return Process::setDockIconVisible(false); #endif // Scan const File deadMansPedalFile (getAppProperties().getUserSettings()->getFile().getSiblingFile("RecentlyCrashedPluginsList")); String pluginName; for (int i = 0; i < im->formatManager.getNumFormats(); i++) { im->scanner = new PluginDirectoryScanner(im->knownPluginList, *im->formatManager.getFormat(i), im->formatManager.getFormat(i)->getDefaultLocationsToSearch(), true, deadMansPedalFile); while (im->scanner->scanNextFile(true, pluginName)) { } } // Remove plugins without inputs and/or outputs std::vector removeIndex; for (int i = 0; i < im->knownPluginList.getNumTypes(); i++) { PluginDescription* plugin = im->knownPluginList.getType(i); if (plugin->numInputChannels < 2 || plugin->numOutputChannels < 2) removeIndex.push_back(i); } for (int i = 0; i < removeIndex.size(); i++) im->knownPluginList.removeType(removeIndex[i] - i); // Finish NativeMessageBox::showMessageBox(AlertWindow::AlertIconType::InfoIcon, "Completed", "Plugins have been refreshed."); #if JUCE_MAC Process::setDockIconVisible(false); #endif }