/* ------------------------------------------------------------------ This file is part of the Open Ephys GUI Copyright (C) 2016 Open Ephys ------------------------------------------------------------------ This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "openEphys_PluginTemplatesPageComponent.h" #include "openEphys_EditorTemplatesFactory.h" #include "openEphys_EditorTemplateComponent.h" #include "../../../Source/Plugins/Headers/AllLookAndFeels.h" #include "../../../Source/UI/Utils/LinearButtonGroupManager.h" #include "../../../Source/UI/Utils/TiledButtonGroupManager.h" const char* PluginTemplatesPageComponent::TEMPLATES_PAGE_CREATE_PROJECT_BUTTON_ID = "templatesPageComponent_createProjectButton"; PluginTemplatesPageComponent::PluginTemplatesPageComponent() : m_createProjectButton (new TextButton ("Create project...", "Create new project")) , m_pluginTypeComboBox (new ComboBox ("Plugin type")) , m_processorTypeComboBox (new ComboBox ("Processor type")) , m_lookAndFeelsComboBox (new ComboBox ("LookAndFeel selector")) , m_pluginTypeLabel (new Label (String::empty, TRANS("Plugin type") + ":")) , m_processorTypeLabel (new Label (String::empty, TRANS("Processor type") + ":")) , m_shouldUseVisualizerEditorButton (new ToggleButton ("Use visualizer")) , m_shouldUseDataThreadButton (new ToggleButton ("Use separate thread")) , m_genericEditorTemplatesManager (new TiledButtonGroupManager) , m_visualizerEditorTemplatesManager (new TiledButtonGroupManager) , m_tabsButtonManager (new LinearButtonGroupManager) { // Plugin type combo box const String pluginTypeOptions[] = { TRANS("Processor"), TRANS("Record engine"), TRANS("File source") //TRANS("Data thread"), }; m_pluginTypeComboBox->addItemList (StringArray (pluginTypeOptions, numElementsInArray (pluginTypeOptions)), 1); m_pluginTypeComboBox->setSelectedId (1, dontSendNotification); m_pluginTypeComboBox->addListener (this); addAndMakeVisible (m_pluginTypeComboBox); m_pluginTypeLabel->setColour (Label::textColourId, Colours::white); m_pluginTypeLabel->attachToComponent (m_pluginTypeComboBox, true); // Processor type combo box const String processorTypeOptions[] = { TRANS("Filter"), TRANS("Source"), TRANS("Sink"), //TRANS("Splitter"), //TRANS("Merger"), //TRANS("Utility"), }; m_processorTypeComboBox->addItemList (StringArray (processorTypeOptions, numElementsInArray (pluginTypeOptions)), 1); m_processorTypeComboBox->setSelectedId (1, dontSendNotification); m_processorTypeComboBox->addListener (this); addAndMakeVisible (m_processorTypeComboBox); m_processorTypeLabel->setColour (Label::textColourId, Colours::white); m_processorTypeLabel->attachToComponent (m_processorTypeComboBox, true); // Toggle buttons m_shouldUseVisualizerEditorButton->addListener (this); m_shouldUseVisualizerEditorButton->setColour (ToggleButton::textColourId, Colours::white); addAndMakeVisible (m_shouldUseVisualizerEditorButton); m_shouldUseDataThreadButton->addListener (this); m_shouldUseDataThreadButton->setColour (ToggleButton::textColourId, Colours::white); addChildComponent (m_shouldUseDataThreadButton); // Create project button m_createProjectButton->setComponentID (TEMPLATES_PAGE_CREATE_PROJECT_BUTTON_ID); addAndMakeVisible (m_createProjectButton); // Tab buttons static const Colour TAB_BUTTON_COLOUR_PRIMARY (Colours::black.withAlpha (0.87f)); static const Colour TAB_BUTTON_COLOUR_ACCENT (Colour::fromRGB (3, 169, 244)); TextButton* genericEditorTabButton = new TextButton ("Editor templates", "Choose template for main plugin editor"); genericEditorTabButton->setComponentID (GENERIC_EDITOR_TEMPLATES_TAB_BUTTON_ID); genericEditorTabButton->setClickingTogglesState (true); genericEditorTabButton->setToggleState (true, dontSendNotification); genericEditorTabButton->setColour (TextButton::buttonColourId, Colour (0x0)); genericEditorTabButton->setColour (TextButton::buttonOnColourId, Colour (0x0)); genericEditorTabButton->setColour (TextButton::textColourOffId, TAB_BUTTON_COLOUR_PRIMARY); genericEditorTabButton->setColour (TextButton::textColourOnId, TAB_BUTTON_COLOUR_ACCENT); TextButton* visualizerEditorTabButton = new TextButton ("Visualizer templates", "Choose template for visualizer editor"); visualizerEditorTabButton->setComponentID (VISUALIZER_EDITOR_TEMPLATES_TAB_BUTTON_ID); visualizerEditorTabButton->setClickingTogglesState (true); visualizerEditorTabButton->setColour (TextButton::buttonColourId, Colour (0x0)); visualizerEditorTabButton->setColour (TextButton::buttonOnColourId, Colour (0x0)); visualizerEditorTabButton->setColour (TextButton::textColourOffId, TAB_BUTTON_COLOUR_PRIMARY); visualizerEditorTabButton->setColour (TextButton::textColourOnId, TAB_BUTTON_COLOUR_ACCENT); m_tabsButtonManager->addButton (genericEditorTabButton); m_tabsButtonManager->addButton (visualizerEditorTabButton); m_tabsButtonManager->setRadioButtonMode (true); m_tabsButtonManager->setButtonListener (this); m_tabsButtonManager->setButtonsLookAndFeel (m_materialButtonLookAndFeel); m_tabsButtonManager->setColour (ButtonGroupManager::backgroundColourId, Colours::white); m_tabsButtonManager->setColour (ButtonGroupManager::outlineColourId, Colour (0x0)); m_tabsButtonManager->setColour (LinearButtonGroupManager::accentColourId, TAB_BUTTON_COLOUR_ACCENT); addAndMakeVisible (m_tabsButtonManager); // Templates managers const int buttonWidth = 200; const int buttonHeight = 150; m_genericEditorTemplatesManager->setButtonSize (buttonWidth, buttonHeight); m_genericEditorTemplatesManager->setColour (TiledButtonGroupManager::outlineColourId, Colour (0x0)); addAndMakeVisible (m_genericEditorTemplatesManager); m_visualizerEditorTemplatesManager->setButtonSize (buttonWidth, buttonHeight); m_visualizerEditorTemplatesManager->setColour (TiledButtonGroupManager::outlineColourId, Colour (0x0)); addChildComponent (m_visualizerEditorTemplatesManager); // LookAndFeel comboBox fillLookAndFeels(); m_lookAndFeelsComboBox->addListener (this); m_lookAndFeelsComboBox->setSelectedId (1, dontSendNotification); addAndMakeVisible (m_lookAndFeelsComboBox); // Fill in templates managers with templates fillGenericEditorTemplates(); fillVisualizerEditorTemplates(); } void PluginTemplatesPageComponent::paint (Graphics& g) { if (isProcessorPlugin()) { const auto managerBorderBounds = m_genericEditorTemplatesManager->getBounds().expanded (10, 30).translated (0, 20); g.setColour (Colours::orange); g.drawRoundedRectangle (managerBorderBounds.toFloat(), 5.f, 2.f); } } void PluginTemplatesPageComponent::resized() { auto localBounds = getLocalBounds().reduced (10, 0); // Top panel // ======================================================================== localBounds.removeFromTop (10); auto configComponentsBounds = localBounds.removeFromTop (22); configComponentsBounds.removeFromLeft (85); const int comboBoxWidth = localBounds.getWidth() * 0.2; m_pluginTypeComboBox->setBounds (configComponentsBounds.removeFromLeft (comboBoxWidth)); const int processorComboBoxWidth = m_processorTypeComboBox->isVisible() ? comboBoxWidth : 0; configComponentsBounds.removeFromLeft ((int) processorComboBoxWidth * 0.8); m_processorTypeComboBox->setBounds (configComponentsBounds.removeFromLeft (processorComboBoxWidth)); configComponentsBounds.removeFromLeft (20); if (m_shouldUseDataThreadButton->isVisible()) m_shouldUseDataThreadButton->setBounds (configComponentsBounds.removeFromLeft (160)); m_shouldUseVisualizerEditorButton->setBounds (configComponentsBounds); localBounds.removeFromTop (10); // ======================================================================== m_createProjectButton->setBounds ("right - 125, bottom - 32, parent.width - 20, parent.height - 35"); localBounds.removeFromBottom (m_createProjectButton->getHeight() + 15); auto tabSelectorsRow = localBounds.removeFromTop (36); m_tabsButtonManager->setBounds (tabSelectorsRow.removeFromLeft (400)); tabSelectorsRow.removeFromLeft (20); m_lookAndFeelsComboBox->setBounds (tabSelectorsRow.withHeight (22)); m_genericEditorTemplatesManager->setBounds (localBounds.reduced (10, 20)); m_visualizerEditorTemplatesManager->setBounds (localBounds.reduced (10, 20)); } void PluginTemplatesPageComponent::buttonClicked (Button* buttonThatWasClicked) { const auto& buttonId = buttonThatWasClicked->getComponentID(); if (buttonId == GENERIC_EDITOR_TEMPLATES_TAB_BUTTON_ID) { showGenericEditorTemplates(); } else if (buttonId == VISUALIZER_EDITOR_TEMPLATES_TAB_BUTTON_ID) { showVisualizerEditorTemplates(); } else if (buttonThatWasClicked == m_shouldUseVisualizerEditorButton) { // Uncomment if we want to disable "VisualizerEditor templates" tab when no need to use visualizer editor //setVisualizerTemplatesAvailable (m_shouldUseVisualizerEditor->getToggleState()); } else if (buttonThatWasClicked == m_shouldUseDataThreadButton) { updateComponentsVisibility(); } else if (auto selectedTemplateComponent = dynamic_cast<EditorTemplateComponent*> (buttonThatWasClicked)) { // The page with templates for generic editor is active if (m_genericEditorTemplatesManager->isVisible()) m_selectedTemplateName = selectedTemplateComponent->getTemplateName(); // The page with templates for visualizer editor canvas is active else m_selectedVisualizerTemplateName = selectedTemplateComponent->getTemplateName(); } } void PluginTemplatesPageComponent::comboBoxChanged (ComboBox* comboBoxThatHasChanged) { if (comboBoxThatHasChanged == m_pluginTypeComboBox || comboBoxThatHasChanged == m_processorTypeComboBox) { updateComponentsVisibility(); } else if (comboBoxThatHasChanged == m_lookAndFeelsComboBox) { const int selectedLookAndFeelIdx = m_lookAndFeelsComboBox->getSelectedItemIndex(); if (selectedLookAndFeelIdx < 0) return; // "Default" lookAndFeel option was selected if (selectedLookAndFeelIdx == 0) { // TODO <Kirill A>: Switch back to each template's default lookAndFeel return; } applyLookAndFeel (m_lookAndFeelsList[selectedLookAndFeelIdx]); } } void PluginTemplatesPageComponent::updateComponentsVisibility() { const int selectedPluginType = getSelectedPluginType(); const bool isProcessorPlugin = selectedPluginType == (int)Plugin::PLUGIN_TYPE_PROCESSOR; const bool isDataThreadPlugin = selectedPluginType == (int)Plugin::PLUGIN_TYPE_DATA_THREAD; const int selectedProcessorType = getSelectedProcessorType(); const bool isSourcePlugin = selectedProcessorType == (int)Plugin::PROCESSOR_TYPE_SOURCE; // These components should be visible only if the Processor plugin type was selected m_processorTypeComboBox->setVisible (isProcessorPlugin || isDataThreadPlugin); m_shouldUseVisualizerEditorButton->setVisible (isProcessorPlugin); m_lookAndFeelsComboBox->setVisible (isProcessorPlugin); m_tabsButtonManager->setVisible (isProcessorPlugin); const bool isGenericTemplatesActive = m_tabsButtonManager->getButtonAt (0)->getToggleState(); m_genericEditorTemplatesManager->setVisible (isProcessorPlugin && isGenericTemplatesActive); m_visualizerEditorTemplatesManager->setVisible (isProcessorPlugin && ! isGenericTemplatesActive); // These components should be visible only if the File Source plugin type was selected m_shouldUseDataThreadButton->setVisible (isDataThreadPlugin || isSourcePlugin); resized(); repaint(); } void PluginTemplatesPageComponent::setButtonListenerForCreateProjectButton (Button::Listener* buttonListener) { m_createProjectButton->addListener (buttonListener); } void PluginTemplatesPageComponent::showGenericEditorTemplates() { m_visualizerEditorTemplatesManager->setVisible (false); m_genericEditorTemplatesManager->setVisible (true); } void PluginTemplatesPageComponent::showVisualizerEditorTemplates() { m_genericEditorTemplatesManager->setVisible (false); m_visualizerEditorTemplatesManager->setVisible (true); } void PluginTemplatesPageComponent::fillGenericEditorTemplates() { m_genericEditorTemplatesManager->clear(); ScopedPointer<Array<EditorTemplateComponent*>> templateComponents = EditorTemplatesFactory::createGenericEditorTemplates(); for (auto templateComponent : *templateComponents) { templateComponent->setClickingTogglesState (true); templateComponent->addListener (this); templateComponent->setRadioGroupId (1, dontSendNotification); m_genericEditorTemplatesManager->addButton (templateComponent, false); } } void PluginTemplatesPageComponent::fillVisualizerEditorTemplates() { m_visualizerEditorTemplatesManager->clear(); ScopedPointer<Array<EditorTemplateComponent*>> templateComponents = EditorTemplatesFactory::createVisualizerEditorTemplates(); for (auto templateComponent : *templateComponents) { templateComponent->setClickingTogglesState (true); templateComponent->addListener (this); templateComponent->setRadioGroupId (1, dontSendNotification); m_visualizerEditorTemplatesManager->addButton (templateComponent, false); } } void PluginTemplatesPageComponent::setVisualizerTemplatesAvailable (bool areAvailable) { // Force to show generic editor templates if (! areAvailable) m_tabsButtonManager->getButtonAt (1)->setToggleState (false, sendNotification); m_tabsButtonManager->getButtonAt (1)->setEnabled (areAvailable); } bool PluginTemplatesPageComponent::isProcessorSourcePlugin() const noexcept { const int selectedPluginType = m_pluginTypeComboBox->getSelectedItemIndex() + 1; const int selectedProcessorType = m_processorTypeComboBox->getSelectedItemIndex() + 1; const bool isSourcePlugin = (selectedPluginType == (int)Plugin::PLUGIN_TYPE_PROCESSOR) && (selectedProcessorType == (int)Plugin::PROCESSOR_TYPE_SOURCE); return isSourcePlugin; } bool PluginTemplatesPageComponent::isProcessorPlugin() const noexcept { return getSelectedPluginType() == (int)Plugin::PLUGIN_TYPE_PROCESSOR; } Plugin::PluginType PluginTemplatesPageComponent::getSelectedPluginType() const noexcept { // If selected plugin type is Processor type and "Use thread type" is selected // Then it means, that we should create DataThread plugin. if (isProcessorSourcePlugin() && m_shouldUseDataThreadButton->getToggleState()) return Plugin::PLUGIN_TYPE_DATA_THREAD; return Plugin::PluginType (m_pluginTypeComboBox->getSelectedItemIndex() + 1); } Plugin::PluginProcessorType PluginTemplatesPageComponent::getSelectedProcessorType() const noexcept { if (getSelectedPluginType() != Plugin::PLUGIN_TYPE_PROCESSOR) return Plugin::PROCESSOR_TYPE_INVALID; return Plugin::PluginProcessorType (m_processorTypeComboBox->getSelectedItemIndex() + 1); } bool PluginTemplatesPageComponent::shouldUseVisualizerEditor() const noexcept { const bool isProcessorPlugin = getSelectedPluginType() == Plugin::PLUGIN_TYPE_PROCESSOR; return isProcessorPlugin && m_shouldUseVisualizerEditorButton->getToggleState(); } bool PluginTemplatesPageComponent::shouldUseDataThreadSource() const noexcept { return isProcessorSourcePlugin() && m_shouldUseDataThreadButton->getToggleState(); } String PluginTemplatesPageComponent::getSelectedTemplateName() const noexcept { return m_selectedTemplateName; } String PluginTemplatesPageComponent::getSelectedVisualizerTemplateName() const noexcept { return m_selectedVisualizerTemplateName; } String PluginTemplatesPageComponent::getSelectedLookAndFeelClassName() const noexcept { const String defaultValue = "DEFAULT"; const int selectedLnfIndex = m_lookAndFeelsComboBox->getSelectedItemIndex(); return m_lookAndFeelsNames.getValue (m_lookAndFeelsComboBox->getItemText (selectedLnfIndex), defaultValue); } void PluginTemplatesPageComponent::addLookAndFeel (LookAndFeel* newLookAndFeel, const String& name, const String& className) { m_lookAndFeelsNames.set (name, className); m_lookAndFeelsList.add (newLookAndFeel); m_lookAndFeelsComboBox->addItem (name, m_lookAndFeelsComboBox->getNumItems() + 1); } void PluginTemplatesPageComponent::applyLookAndFeel (LookAndFeel* lookAndFeel) { const int numEditorTempaltes = m_genericEditorTemplatesManager->getNumButtons(); for (int i = 0; i < numEditorTempaltes; ++i) { auto editorTemplateComponent = dynamic_cast<EditorTemplateComponent*> (m_genericEditorTemplatesManager->getButtonAt (i)); editorTemplateComponent->setContentLookAndFeel (lookAndFeel); } } void PluginTemplatesPageComponent::fillLookAndFeels() { addLookAndFeel (new LookAndFeel_V2(), "Default", "DEFAULT"); addLookAndFeel (new LookAndFeel_V1(), "LookAndFeel_V1", "LookAndFeel_V1"); addLookAndFeel (new LookAndFeel_V2(), "LookAndFeel_V2", "LookAndFeel_V2"); addLookAndFeel (new LookAndFeel_V3(), "LookAndFeel_V3", "LookAndFeel_V3"); addLookAndFeel (new FlatRoundLookAndFeel(), "Flat round LookAndFeel", "FlatRoundLookAndFeel"); addLookAndFeel (new SquareLookAndFeel(), "Square LookAndFeel", "SquareLookAndFeel"); }