From 91a21c057f331343a50cf6e8eb3e847092ee942b Mon Sep 17 00:00:00 2001
From: Septen <gammerxpower@gmail.com>
Date: Sat, 9 Apr 2016 21:56:21 +0400
Subject: [PATCH] Added TiledButtonGroupManager to the ChannelSelector class

* Now ChannelSelector class contains three instances
  of TiledButtonGroupManager class (AUDIO, RECORD, PARAM).
  This managers own appropriate buttons, position it, responsible
  for all animations, fuzzy selection via dragging mouse, etc.
* Button group managers minor fixes.
---
 Source/Processors/Editors/ChannelSelector.cpp | 391 ++++++++----------
 Source/Processors/Editors/ChannelSelector.h   |   7 +-
 Source/Processors/Parameter/ParameterEditor.h |   2 +-
 Source/UI/Utils/ButtonGroupManager.cpp        |  29 +-
 Source/UI/Utils/ButtonGroupManager.h          |  10 +-
 Source/UI/Utils/LinearButtonGroupManager.cpp  |   2 +
 Source/UI/Utils/TiledButtonGroupManager.cpp   |  10 +-
 7 files changed, 229 insertions(+), 222 deletions(-)

diff --git a/Source/Processors/Editors/ChannelSelector.cpp b/Source/Processors/Editors/ChannelSelector.cpp
index 70d4cdec4..a9e29723c 100755
--- a/Source/Processors/Editors/ChannelSelector.cpp
+++ b/Source/Processors/Editors/ChannelSelector.cpp
@@ -35,7 +35,6 @@ ChannelSelector::ChannelSelector(bool createButtons, Font& titleFont_) :
     recActive(true), radioStatus(false), isNotSink(createButtons),
     moveRight(false), moveLeft(false), offsetLR(0), offsetUD(0), desiredOffset(0), titleFont(titleFont_), acquisitionIsActive(false)
 {
-
     // initialize buttons
     audioButton = new EditorButton("AUDIO", titleFont);
     audioButton->addListener(this);
@@ -55,16 +54,11 @@ ChannelSelector::ChannelSelector(bool createButtons, Font& titleFont_) :
 
     paramsButton->setToggleState(true, dontSendNotification);
 
-    audioButtons.clear();
-    recordButtons.clear();
-
     // set button layout parameters
     parameterOffset = 0;
     recordOffset = getDesiredWidth();
     audioOffset = getDesiredWidth() * 2;
 
-    parameterButtons.clear();
-
     allButton = new EditorButton("all", titleFont);
     allButton->addListener(this);
     addAndMakeVisible(allButton);
@@ -99,22 +93,50 @@ ChannelSelector::ChannelSelector(bool createButtons, Font& titleFont_) :
 
     channelSelectorRegion = new ChannelSelectorRegion(this);
     //channelSelectorRegion->setBounds(0,20,0,getHeight()-35);
-    addAndMakeVisible(channelSelectorRegion);
-    channelSelectorRegion->toBack();
+    //addAndMakeVisible(channelSelectorRegion);
+    //channelSelectorRegion->toBack();
 
-    paramBox = new ChannelSelectorBox();
-    recordBox = new ChannelSelectorBox();
-    audioBox = new ChannelSelectorBox();
+    addAndMakeVisible (paramBox  = new ChannelSelectorBox);
+    addAndMakeVisible (recordBox = new ChannelSelectorBox);
+    addAndMakeVisible (audioBox  = new ChannelSelectorBox);
 
     numColumnsLessThan100 = 8;
     numColumnsGreaterThan100 = 6;
 
+    addAndMakeVisible (audioButtonsManager);
+    addAndMakeVisible (recordButtonsManager);
+    addAndMakeVisible (parameterButtonsManager);
+
+    // Enable fast mode selection for buttons
+    audioButtonsManager.setFastSelectionModeEnabled     (true);
+    recordButtonsManager.setFastSelectionModeEnabled    (true);
+    parameterButtonsManager.setFastSelectionModeEnabled (true);
+
+    audioButtonsManager.setMinPaddingBetweenButtons     (0);
+    recordButtonsManager.setMinPaddingBetweenButtons    (0);
+    parameterButtonsManager.setMinPaddingBetweenButtons (0);
+
+    audioButtonsManager.setColour       (ButtonGroupManager::outlineColourId, Colour (0x0));
+    recordButtonsManager.setColour      (ButtonGroupManager::outlineColourId, Colour (0x0));
+    parameterButtonsManager.setColour   (ButtonGroupManager::outlineColourId, Colour (0x0));
+
+    // Register listeners for buttons
+    audioButtonsManager.setButtonListener      (this);
+    recordButtonsManager.setButtonListener     (this);
+    parameterButtonsManager.setButtonListener  (this);
 }
 
 ChannelSelector::~ChannelSelector()
 {
-    deleteAllChildren();
+    // Just a temporary workaround as we don't want to delete these buttons managers by hands.
+    // We will remove it after getting rid of the ugly calling of deleteAllChildren() method.
+    // We should really use some RAII technuiqes to avoid calling this method.
+    // TODO: refactor the code to follow RAII best principles and to avoid using raw pointers after merge with priyanjitdey94
+    removeChildComponent (&audioButtonsManager);
+    removeChildComponent (&recordButtonsManager);
+    removeChildComponent (&parameterButtonsManager);
 
+    deleteAllChildren();
 }
 
 void ChannelSelector::paint(Graphics& g)
@@ -141,8 +163,7 @@ void ChannelSelector::paint(Graphics& g)
 
 void ChannelSelector::setNumChannels(int numChans)
 {
-
-    int difference = numChans - parameterButtons.size();
+    int difference = numChans - parameterButtonsManager.getNumButtons();
 
     // std::cout << difference << " buttons needed." << std::endl;
 
@@ -161,31 +182,31 @@ void ChannelSelector::setNumChannels(int numChans)
         }
     }
 
+    const int numButtons = parameterButtonsManager.getNumButtons();
     //Reassign numbers according to the actual channels (useful for channel mapper)
-    for (int n = 0; n < parameterButtons.size(); n++)
+    for (int n = 0; n < numButtons; ++n)
     {
-        int num = ((GenericEditor*)getParentComponent())->getChannel(n)->nodeIndex;
-        parameterButtons[n]->setChannel(n + 1, num + 1);
+        int num = ( (GenericEditor*)getParentComponent())->getChannel (n)->nodeIndex;
+        static_cast<ChannelSelectorButton*> (parameterButtonsManager.getButtonAt  (n))->setChannel (n + 1, num + 1);
+
         if (isNotSink)
         {
-            recordButtons[n]->setChannel(n + 1, num + 1);
-            audioButtons[n]->setChannel(n + 1, num + 1);
+            static_cast<ChannelSelectorButton*> (recordButtonsManager.getButtonAt (n))->setChannel (n + 1, num + 1);
+            static_cast<ChannelSelectorButton*> (audioButtonsManager.getButtonAt  (n))->setChannel (n + 1, num + 1);
         }
     }
 
     refreshButtonBoundaries();
-
 }
 
 int ChannelSelector::getNumChannels()
 {
-    return parameterButtons.size();
+    return parameterButtonsManager.getNumButtons();
 }
 
 void ChannelSelector::shiftChannelsVertical(float amount)
 {
-
-    if (parameterButtons.size() > 16)
+    if (parameterButtonsManager.getNumButtons() > 16)
     {
         offsetUD -= amount * 10;
         offsetUD = jmin(offsetUD, 0.0f);
@@ -195,97 +216,73 @@ void ChannelSelector::shiftChannelsVertical(float amount)
     //std::cout << "offsetUD = " << offsetUD << std::endl;
 
     refreshButtonBoundaries();
-
 }
 
 void ChannelSelector::refreshButtonBoundaries()
 {
-
     channelSelectorRegion->setBounds(0, 20, getWidth(), getHeight() - 35);
 
-    int rowHeight = 14,px,py,rx,ry,ax,ay;
     int column = 0;
     int row = 0;
     int nColumns;
 
-    for (int i = 0; i < parameterButtons.size(); i++)
-    {
-
-        if (i < 96)
-            nColumns = numColumnsLessThan100;
-        else
-            nColumns = numColumnsGreaterThan100;
-
-        int columnWidth = getDesiredWidth() / (nColumns + 1) + 1;
-
-        int xLoc = columnWidth / 2 + offsetLR + columnWidth*column;
-        int yLoc = row * rowHeight + offsetUD + 25;
-        if (i == 0)
-        {
-            px = xLoc;
-            py = yLoc - 25;
-            rx = xLoc - getDesiredWidth();
-            ry = yLoc - 25;
-            ax = xLoc - getDesiredWidth() * 2;
-            ay = yLoc - 25;
-        }
-        parameterButtons[i]->setBounds(xLoc, yLoc, columnWidth, rowHeight);
-
-        if (isNotSink)
-        {
-            recordButtons[i]->setBounds(xLoc - getDesiredWidth(), yLoc, columnWidth, rowHeight);
-            audioButtons[i]->setBounds(xLoc - getDesiredWidth() * 2, yLoc, columnWidth, rowHeight);
-        }
-
-        column++;
-
-        if (column >= nColumns)
-        {
-            column = 0;
-            row++;
-            overallHeight = row * rowHeight;
-        }
-
-    }
-
-    int w = getWidth() / 3;
-    int h = 15;
-
-
-    /*
-       definition of textbox
-    */
-    paramBox->setBounds(px, py+20, 90, 20);
-    addAndMakeVisible(paramBox);
-    recordBox->setBounds(rx, ry+20, 90, 20);
-    addAndMakeVisible(recordBox);
-    audioBox->setBounds(ax, ay+20, 90, 20);
-    addAndMakeVisible(audioBox);
+    const int numButtons    = parameterButtonsManager.getNumButtons();
+    const int columnWidth   = getDesiredWidth() / (numColumnsGreaterThan100 + 1) + 1;
+    const int rowHeight     = 14;
+
+    audioButtonsManager.setButtonSize      (columnWidth, rowHeight);
+    recordButtonsManager.setButtonSize     (columnWidth, rowHeight);
+    parameterButtonsManager.setButtonSize  (columnWidth, rowHeight);
+
+    const int headerHeight = 25;
+    //const int xLoc = columnWidth / 2 + offsetLR;
+    //const int yLoc = offsetUD + headerHeight;
+    const int xLoc = offsetLR + 3;
+    const int yLoc = offsetUD + headerHeight * 2 - 5;
+
+    const int tabButtonHeight = 15;
+    juce::Rectangle<int> buttonManagerBounds (xLoc, yLoc, getDesiredWidth() - 6, getHeight() - yLoc - tabButtonHeight);
+    parameterButtonsManager.setBounds (buttonManagerBounds);
+    buttonManagerBounds.translate (- getDesiredWidth(), 0);
+    recordButtonsManager.setBounds    (buttonManagerBounds);
+    buttonManagerBounds.translate (- getDesiredWidth(), 0);
+    audioButtonsManager.setBounds    (buttonManagerBounds);
+
+    juce::Rectangle<int> textEditBounds (xLoc, yLoc - headerHeight, 90, 20);
+    paramBox->setBounds     (textEditBounds);
+    textEditBounds.translate (- getDesiredWidth(), 0);
+    recordBox->setBounds    (textEditBounds);
+    textEditBounds.translate (- getDesiredWidth(), 0);
+    audioBox->setBounds     (textEditBounds);
 
     /*
       audio,record and param tabs
     */
-    audioButton->setBounds(0, 0, w, h);
-    recordButton->setBounds(w, 0, w, h);
-    paramsButton->setBounds(w * 2, 0, w, h);
+    const int tabButtonWidth = getWidth() / 3;
+    audioButton->setBounds  (0, 0, tabButtonWidth, tabButtonHeight);
+    recordButton->setBounds (tabButtonWidth, 0, tabButtonWidth, tabButtonHeight);
+    paramsButton->setBounds (tabButtonWidth * 2, 0, tabButtonWidth, tabButtonHeight);
 
     /*
       select and deselect button under each tab
     */
-    selectButtonParam->setBounds(px + 95, py + 20, 20, 20);
-    deselectButtonParam->setBounds(px + 117, py + 20, 20, 20);
+    juce::Rectangle<int> selectionControlBounds (xLoc + 95, 20, 20, 20);
+    selectButtonParam->setBounds    (selectionControlBounds);
+    deselectButtonParam->setBounds  (selectionControlBounds.translated (22, 0));
+
+    selectionControlBounds.translate (- getDesiredWidth(), 0);
+    selectButtonRecord->setBounds    (selectionControlBounds);
+    deselectButtonRecord->setBounds  (selectionControlBounds.translated (22, 0));
 
-    selectButtonRecord->setBounds(rx + 95, ry + 20, 20, 20);
-    deselectButtonRecord->setBounds(rx + 117, ry + 20, 20, 20);
+    selectionControlBounds.translate (- getDesiredWidth(), 0);
+    selectButtonAudio->setBounds    (selectionControlBounds);
+    deselectButtonAudio->setBounds  (selectionControlBounds.translated (22, 0));
 
-    selectButtonAudio->setBounds(ax + 95, ay + 20, 20, 20);
-    deselectButtonAudio->setBounds(ax + 117, ay + 20, 20, 20);
     /*
       All and None buttons
     */
-    allButton->setBounds(0, getHeight() - 15, getWidth() / 2, 15);
-    noneButton->setBounds(getWidth() / 2, getHeight() - 15, getWidth() / 2, 15);
-
+    allButton->setBounds (0, getHeight() - 15, getWidth() / 2, tabButtonHeight);
+    noneButton->setBounds (getWidth() / 2, getHeight() - 15, getWidth() / 2, tabButtonHeight);
 }
 
 void ChannelSelector::resized()
@@ -295,7 +292,6 @@ void ChannelSelector::resized()
 
 void ChannelSelector::timerCallback()
 {
-
     //std::cout << desiredOffset - offsetLR << std::endl;
 
     if (offsetLR != desiredOffset)
@@ -310,7 +306,6 @@ void ChannelSelector::timerCallback()
         {
             offsetLR -= 25;
         }
-
     }
     else
     {
@@ -318,17 +313,17 @@ void ChannelSelector::timerCallback()
     }
 
     refreshButtonBoundaries();
-
 }
 
 void ChannelSelector::addButton()
 {
+    const int size = parameterButtonsManager.getNumButtons();
 
-    int size = parameterButtons.size();
+    ChannelSelectorButton* b = new ChannelSelectorButton (size + 1, PARAMETER, titleFont);
+    parameterButtonsManager.addButton (b);
 
-    ChannelSelectorButton* b = new ChannelSelectorButton(size + 1, PARAMETER, titleFont);
-    parameterButtons.add(b);
-    channelSelectorRegion->addAndMakeVisible(b);
+    // TODO
+    //channelSelectorRegion->addAndMakeVisible(b);
 
     if (paramsToggled)
         b->setToggleState(true, dontSendNotification);
@@ -338,39 +333,35 @@ void ChannelSelector::addButton()
     if (!paramsActive)
         b->setActive(false);
 
-    b->addListener(this);
-
     if (isNotSink)
     {
         ChannelSelectorButton* br = new ChannelSelectorButton(size + 1, RECORD, titleFont);
-        recordButtons.add(br);
-        channelSelectorRegion->addAndMakeVisible(br);
-        br->addListener(this);
+        recordButtonsManager.addButton (br);
+        //channelSelectorRegion->addAndMakeVisible(br);
+        //br->addListener(this);
 
         ChannelSelectorButton* ba = new ChannelSelectorButton(size + 1, AUDIO, titleFont);
-        audioButtons.add(ba);
-        channelSelectorRegion->addAndMakeVisible(ba);
-        ba->addListener(this);
+        audioButtonsManager.addButton (ba);
+        //channelSelectorRegion->addAndMakeVisible(ba);
+        //ba->addListener(this);
     }
 }
 
 void ChannelSelector::removeButton()
 {
-    int size = parameterButtons.size();
+    int size = parameterButtonsManager.getNumButtons();
 
-    ChannelSelectorButton* b = parameterButtons.remove(size - 1);
-    channelSelectorRegion->removeChildComponent(b);
-    deleteAndZero(b);
+    parameterButtonsManager.removeButton (size - 1);
+    // TODO
+    // channelSelectorRegion->removeChildComponent(b);
 
     if (isNotSink)
     {
-        ChannelSelectorButton* br = recordButtons.remove(size - 1);
-        channelSelectorRegion->removeChildComponent(br);
-        deleteAndZero(br);
+        recordButtonsManager.removeButton (size - 1);
+        //channelSelectorRegion->removeChildComponent(br);
 
-        ChannelSelectorButton* ba = audioButtons.remove(size - 1);
-        channelSelectorRegion->removeChildComponent(ba);
-        deleteAndZero(ba);
+        audioButtonsManager.removeButton (size - 1);
+        //channelSelectorRegion->removeChildComponent(ba);
     }
 }
 
@@ -378,17 +369,18 @@ Array<int> ChannelSelector::getActiveChannels()
 {
     Array<int> a;
 
-    if (!eventsOnly)
+    if (! eventsOnly)
     {
-        for (int i = 0; i < parameterButtons.size(); i++)
+        const int numButtons = parameterButtonsManager.getNumButtons();
+        for (int i = 0; i < numButtons; ++i)
         {
-            if (parameterButtons[i]->getToggleState())
-                a.add(i);
+            if (parameterButtonsManager.getButtonAt (i)->getToggleState())
+                a.add (i);
         }
     }
     else
     {
-        a.add(0);
+        a.add (0);
     }
 
     return a;
@@ -396,71 +388,73 @@ Array<int> ChannelSelector::getActiveChannels()
 
 void ChannelSelector::setActiveChannels(Array<int> a)
 {
-
     //std::cout << "Setting active channels!" << std::endl;
 
-    for (int i = 0; i < parameterButtons.size(); i++)
+    const int numButtons = parameterButtonsManager.getNumButtons();
+    for (int i = 0; i < numButtons; ++i)
     {
-        parameterButtons[i]->setToggleState(false, dontSendNotification);
+        parameterButtonsManager.getButtonAt (i)->setToggleState (false, dontSendNotification);
     }
 
     for (int i = 0; i < a.size(); i++)
     {
-        if (a[i] < parameterButtons.size())
+        if (a[i] < numButtons)
         {
-            parameterButtons[a[i]]->setToggleState(true, dontSendNotification);
+            parameterButtonsManager.getButtonAt (a[i])->setToggleState (true, dontSendNotification);
         }
     }
 }
 
 void ChannelSelector::inactivateButtons()
 {
-
     paramsActive = false;
 
-    for (int i = 0; i < parameterButtons.size(); i++)
+    const int numButtons = parameterButtonsManager.getNumButtons();
+    for (int i = 0; i < numButtons; ++i)
     {
-        parameterButtons[i]->setActive(false);
-        parameterButtons[i]->repaint();
+        const auto& button = static_cast<ChannelSelectorButton*> (parameterButtonsManager.getButtonAt (i));
+        button->setActive (false);
+        button->repaint();
     }
 }
 
 void ChannelSelector::activateButtons()
 {
-
     paramsActive = true;
 
-    for (int i = 0; i < parameterButtons.size(); i++)
+    const int numButtons = parameterButtonsManager.getNumButtons();
+    for (int i = 0; i < numButtons; ++i)
     {
-        parameterButtons[i]->setActive(true);
-        parameterButtons[i]->repaint();
+        const auto& button = static_cast<ChannelSelectorButton*> (parameterButtonsManager.getButtonAt (i));
+        button->setActive (true);
+        button->repaint();
     }
-
 }
 
 void ChannelSelector::inactivateRecButtons()
 {
-
     recActive = false;
 
-    for (int i = 0; i < recordButtons.size(); i++)
+    const int numButtons = recordButtonsManager.getNumButtons();
+    for (int i = 0; i < numButtons; ++i)
     {
-        recordButtons[i]->setActive(false);
-        recordButtons[i]->repaint();
+        const auto& button = static_cast<ChannelSelectorButton*> (recordButtonsManager.getButtonAt (i));
+        button->setActive (false);
+        button->repaint();
     }
 }
 
 void ChannelSelector::activateRecButtons()
 {
-
     recActive = true;
 
-    for (int i = 0; i < recordButtons.size(); i++)
+    const int numButtons = recordButtonsManager.getNumButtons();
+    for (int i = 0; i < numButtons; ++i)
     {
-        recordButtons[i]->setActive(true);
-        recordButtons[i]->repaint();
+        const auto& button = static_cast<ChannelSelectorButton*> (recordButtonsManager.getButtonAt (i));
+        button->setActive (true);
+        button->repaint();
     }
-
 }
 
 void ChannelSelector::refreshParameterColors()
@@ -486,90 +480,67 @@ void ChannelSelector::stopAcquisition()
 
 void ChannelSelector::setRadioStatus(bool radioOn)
 {
-
     if (radioStatus != radioOn)
     {
-
         radioStatus = radioOn;
 
-        for (int i = 0; i < parameterButtons.size(); i++)
+        const int numButtons = parameterButtonsManager.getNumButtons();
+        for (int i = 0; i < numButtons; ++i)
         {
-            if (radioOn)
-            {
-                parameterButtons[i]->setToggleState(false, dontSendNotification);
-                parameterButtons[i]->setRadioGroupId(999);
-            }
-            else
-            {
-                parameterButtons[i]->setToggleState(false, dontSendNotification);
-                parameterButtons[i]->setRadioGroupId(0);
-            }
+            parameterButtonsManager.getButtonAt (i)->setToggleState (false, dontSendNotification);
         }
 
+        parameterButtonsManager.setRadioButtonMode (radioStatus);
     }
-
-
-
 }
 
 bool ChannelSelector::getParamStatus(int chan)
 {
-
-    if (chan >= 0 && chan < parameterButtons.size())
-        return parameterButtons[chan]->getToggleState();
+    if (chan >= 0 && chan < parameterButtonsManager.getNumButtons())
+        return parameterButtonsManager.getButtonAt (chan)->getToggleState();
     else
         return false;
-
 }
 
 bool ChannelSelector::getRecordStatus(int chan)
 {
-
-    if (chan >= 0 && chan < recordButtons.size())
-        return recordButtons[chan]->getToggleState();
+    if (chan >= 0 && chan < recordButtonsManager.getNumButtons())
+        return recordButtonsManager.getButtonAt (chan)->getToggleState();
     else
         return false;
-
 }
 
 bool ChannelSelector::getAudioStatus(int chan)
 {
-
-    if (chan >= 0 && chan < audioButtons.size())
-        return audioButtons[chan]->getToggleState();
+    if (chan >= 0 && chan < audioButtonsManager.getNumButtons())
+        return audioButtonsManager.getButtonAt (chan)->getToggleState();
     else
         return false;
-
 }
 
 void ChannelSelector::setParamStatus(int chan, bool b)
 {
-
-    if (chan >= 0 && chan < parameterButtons.size())
-        parameterButtons[chan]->setToggleState(b, sendNotification);
-
+    if (chan >= 0 && chan < parameterButtonsManager.getNumButtons())
+        parameterButtonsManager.getButtonAt (chan)->setToggleState(b, sendNotification);
 }
 
 void ChannelSelector::setRecordStatus(int chan, bool b)
 {
-
-    if (chan >= 0 && chan < recordButtons.size())
-        recordButtons[chan]->setToggleState(b, sendNotification);
-
+    if (chan >= 0 && chan < recordButtonsManager.getNumButtons())
+        recordButtonsManager.getButtonAt (chan)->setToggleState(b, sendNotification);
 }
 
 void ChannelSelector::setAudioStatus(int chan, bool b)
 {
-
-    if (chan >= 0 && chan < audioButtons.size())
-        audioButtons[chan]->setToggleState(b, sendNotification);
-
+    if (chan >= 0 && chan < audioButtonsManager.getNumButtons())
+        audioButtonsManager.getButtonAt (chan)->setToggleState (b, sendNotification);
 }
 
 void ChannelSelector::clearAudio()
 {
-    for (int chan = 0; chan < audioButtons.size(); chan++)
-        audioButtons[chan]->setToggleState(false, sendNotification);
+    const int numButtons = audioButtonsManager.getNumButtons();
+    for (int chan = 0; chan < numButtons; ++chan)
+        audioButtonsManager.getButtonAt (chan)->setToggleState (false, sendNotification);
 }
 
 int ChannelSelector::getDesiredWidth()
@@ -625,21 +596,17 @@ void ChannelSelector::buttonClicked(Button* button)
         // select all active buttons
         if (offsetLR == recordOffset)
         {
-
-
-            for (int i = 0; i < recordButtons.size(); i++)
+            for (int i = 0; i < recordButtonsManager.getNumButtons(); ++i)
             {
-                recordButtons[i]->setToggleState(true, sendNotification);
+                recordButtonsManager.getButtonAt (i)->setToggleState (true, sendNotification);
             }
 
         }
         else if (offsetLR == parameterOffset)
         {
-
-
-            for (int i = 0; i < parameterButtons.size(); i++)
+            for (int i = 0; i < parameterButtonsManager.getNumButtons(); ++i)
             {
-                parameterButtons[i]->setToggleState(true, sendNotification);
+                parameterButtonsManager.getButtonAt (i)->setToggleState (true, sendNotification);
             }
         }
         else if (offsetLR == audioOffset)
@@ -652,23 +619,23 @@ void ChannelSelector::buttonClicked(Button* button)
         // deselect all active buttons
         if (offsetLR == recordOffset)
         {
-            for (int i = 0; i < recordButtons.size(); i++)
+            for (int i = 0; i < recordButtonsManager.getNumButtons(); ++i)
             {
-                recordButtons[i]->setToggleState(false, sendNotification);
+                recordButtonsManager.getButtonAt (i)->setToggleState (false, sendNotification);
             }
         }
         else if (offsetLR == parameterOffset)
         {
-            for (int i = 0; i < parameterButtons.size(); i++)
+            for (int i = 0; i < parameterButtonsManager.getNumButtons(); ++i)
             {
-                parameterButtons[i]->setToggleState(false, sendNotification);
+                parameterButtonsManager.getButtonAt (i)->setToggleState (false, sendNotification);
             }
         }
         else if (offsetLR == audioOffset)
         {
-            for (int i = 0; i < audioButtons.size(); i++)
+            for (int i = 0; i < audioButtonsManager.getNumButtons(); ++i)
             {
-                audioButtons[i]->setToggleState(false, sendNotification);
+                audioButtonsManager.getButtonAt (i)->setToggleState (false, sendNotification);
             }
         }
 
@@ -685,7 +652,7 @@ void ChannelSelector::buttonClicked(Button* button)
         deselectButtonParam->removeListener(this);
         std::vector<int> getBoxList;
         int fa, lim, comd, i;
-        getBoxList = paramBox->getBoxInfo(parameterButtons.size());
+        getBoxList = paramBox->getBoxInfo (parameterButtonsManager.getNumButtons());
         if (getBoxList.size() < 3)
         {
             selectButtonParam->addListener(this);
@@ -700,7 +667,7 @@ void ChannelSelector::buttonClicked(Button* button)
             comd = getBoxList[i + 2];
             for (; fa <= lim; fa += comd)
             {
-                parameterButtons[fa]->setToggleState(true, sendNotification);
+                parameterButtonsManager.getButtonAt (fa)->setToggleState (true, sendNotification);
             }
             i += 3;
         }
@@ -713,7 +680,7 @@ void ChannelSelector::buttonClicked(Button* button)
         deselectButtonRecord->removeListener(this);
         std::vector<int> getBoxList;
         int fa, lim, comd, i;
-        getBoxList = recordBox->getBoxInfo(recordButtons.size());
+        getBoxList = recordBox->getBoxInfo (recordButtonsManager.getNumButtons());
         if (getBoxList.size() < 3)
         {
             selectButtonRecord->addListener(this);
@@ -728,7 +695,7 @@ void ChannelSelector::buttonClicked(Button* button)
             comd = getBoxList[i + 2];
             for (; fa <= lim; fa += comd)
             {
-                recordButtons[fa]->setToggleState(true, sendNotification);
+                recordButtonsManager.getButtonAt (fa)->setToggleState (true, sendNotification);
             }
             i += 3;
         }
@@ -741,7 +708,7 @@ void ChannelSelector::buttonClicked(Button* button)
         deselectButtonAudio->removeListener(this);
         std::vector<int> getBoxList;
         int fa, lim, comd, i;
-        getBoxList = audioBox->getBoxInfo(audioButtons.size());
+        getBoxList = audioBox->getBoxInfo (audioButtonsManager.getNumButtons());
         if (getBoxList.size() < 3)
         {
             selectButtonAudio->addListener(this);
@@ -756,7 +723,7 @@ void ChannelSelector::buttonClicked(Button* button)
             comd = getBoxList[i + 2];
             for (; fa <= lim; fa += comd)
             {
-                audioButtons[fa]->setToggleState(true, sendNotification);
+                audioButtonsManager.getButtonAt (fa)->setToggleState(true, sendNotification);
             }
             i += 3;
         }
@@ -769,7 +736,7 @@ void ChannelSelector::buttonClicked(Button* button)
         deselectButtonParam->removeListener(this);
         std::vector<int> getBoxList;
         int fa, lim, comd, i;
-        getBoxList = paramBox->getBoxInfo(parameterButtons.size());
+        getBoxList = paramBox->getBoxInfo (parameterButtonsManager.getNumButtons());
         if (getBoxList.size() < 3)
         {
             selectButtonParam->addListener(this);
@@ -784,7 +751,7 @@ void ChannelSelector::buttonClicked(Button* button)
             comd = getBoxList[i + 2];
             for (; fa <= lim; fa += comd)
             {
-                parameterButtons[fa]->setToggleState(false, sendNotification);
+                parameterButtonsManager.getButtonAt (fa)->setToggleState (false, sendNotification);
             }
             i += 3;
         }
@@ -797,7 +764,7 @@ void ChannelSelector::buttonClicked(Button* button)
         deselectButtonRecord->removeListener(this);
         std::vector<int> getBoxList;
         int fa, lim, comd, i;
-        getBoxList = recordBox->getBoxInfo(recordButtons.size());
+        getBoxList = recordBox->getBoxInfo (recordButtonsManager.getNumButtons());
         if (getBoxList.size() < 3)
         {
             selectButtonRecord->addListener(this);
@@ -812,7 +779,7 @@ void ChannelSelector::buttonClicked(Button* button)
             comd = getBoxList[i + 2];
             for (; fa <= lim; fa += comd)
             {
-                recordButtons[fa]->setToggleState(false, sendNotification);
+                recordButtonsManager.getButtonAt (fa)->setToggleState (false, sendNotification);
             }
             i += 3;
         }
@@ -825,7 +792,7 @@ void ChannelSelector::buttonClicked(Button* button)
         deselectButtonAudio->removeListener(this);
         std::vector<int> getBoxList;
         int fa, lim, comd, i;
-        getBoxList = audioBox->getBoxInfo(audioButtons.size());
+        getBoxList = audioBox->getBoxInfo (audioButtonsManager.getNumButtons());
         if (getBoxList.size() < 3)
         {
             selectButtonAudio->addListener(this);
@@ -840,7 +807,7 @@ void ChannelSelector::buttonClicked(Button* button)
             comd = getBoxList[i + 2];
             for (; fa <= lim; fa += comd)
             {
-                audioButtons[fa]->setToggleState(false, sendNotification);
+                audioButtonsManager.getButtonAt (fa)->setToggleState(false, sendNotification);
             }
             i += 3;
         }
diff --git a/Source/Processors/Editors/ChannelSelector.h b/Source/Processors/Editors/ChannelSelector.h
index e4372826c..d829dfd35 100644
--- a/Source/Processors/Editors/ChannelSelector.h
+++ b/Source/Processors/Editors/ChannelSelector.h
@@ -26,6 +26,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 #include "../../../JuceLibraryCode/JuceHeader.h"
 #include "../Editors/GenericEditor.h"
+#include "../../UI/Utils/TiledButtonGroupManager.h"
 
 #include "../Channel/Channel.h"
 
@@ -151,21 +152,21 @@ private:
     will be updated when a parameter is changed.
     paramBox: TextBox where user input is taken for param tab.
     */
-    Array<ChannelSelectorButton*> parameterButtons;
+    TiledButtonGroupManager parameterButtonsManager;
     ChannelSelectorBox* paramBox;
 
     /** An array of ChannelSelectorButtons used to select the channels that
     are sent to the audio monitor.
     audioBox: TextBox where user input is taken for audio tab
     */
-    Array<ChannelSelectorButton*> audioButtons;
+    TiledButtonGroupManager audioButtonsManager;
     ChannelSelectorBox* audioBox;
 
     /** An array of ChannelSelectorButtons used to select the channels that
     will be written to disk when the record button is pressed.
     recordBox: TextBox where user input is taken for record tab
     */
-    Array<ChannelSelectorButton*> recordButtons;
+    TiledButtonGroupManager recordButtonsManager;
     ChannelSelectorBox* recordBox;
 
     bool paramsToggled;
diff --git a/Source/Processors/Parameter/ParameterEditor.h b/Source/Processors/Parameter/ParameterEditor.h
index 02faf68d8..65ab696c0 100755
--- a/Source/Processors/Parameter/ParameterEditor.h
+++ b/Source/Processors/Parameter/ParameterEditor.h
@@ -80,7 +80,7 @@ private:
 
     Parameter* parameter;
     GenericProcessor* processor;
-    ChannelSelector* channelSelector;
+    ScopedPointer<ChannelSelector> channelSelector;
 
     enum
     {
diff --git a/Source/UI/Utils/ButtonGroupManager.cpp b/Source/UI/Utils/ButtonGroupManager.cpp
index e654f6c47..55ddb82de 100644
--- a/Source/UI/Utils/ButtonGroupManager.cpp
+++ b/Source/UI/Utils/ButtonGroupManager.cpp
@@ -32,12 +32,18 @@ static const Colour COLOUR_PRIMARY (Colours::black.withAlpha (0.87f));
 
 
 ButtonGroupManager::ButtonGroupManager()
-    : m_isRadioButtonMode   (true)
-    , m_buttonListener      (nullptr)
-    , m_buttonsLookAndFeel  (nullptr)
+    : m_isRadioButtonMode       (true)
+    , m_buttonListener          (nullptr)
+    , m_buttonsLookAndFeel      (nullptr)
+    , m_componentProxyHandler   (new Component)
 {
     setColour (backgroundColourId,  Colour (0x0));
     setColour (outlineColourId,     COLOUR_BORDER);
+
+
+    addAndMakeVisible (m_buttonsViewport);
+    m_buttonsViewport.setViewedComponent (m_componentProxyHandler, false);
+    m_buttonsViewport.setScrollBarsShown (false, false, true, false);
 }
 
 
@@ -62,6 +68,21 @@ void ButtonGroupManager::paint (Graphics& g)
 }
 
 
+void ButtonGroupManager::resized()
+{
+    const int width  = getWidth();
+    const int height = getHeight();
+
+    if (m_buttons.size())
+        m_componentProxyHandler->setBounds (0, 0, width, jmax (height,
+                                                               m_buttons[m_buttons.size() - 1]->getBounds().getBottom()));
+    else
+        m_componentProxyHandler->setBounds (0, 0, width, height);
+
+    m_buttonsViewport.setBounds (0, 0, getWidth(), getHeight());
+}
+
+
 void ButtonGroupManager::colourChanged()
 {
     repaint();
@@ -100,7 +121,7 @@ void ButtonGroupManager::addButton (Button* newButton)
     newButton->addListener (this);
     newButton->setLookAndFeel (m_buttonsLookAndFeel);
 
-    addAndMakeVisible (newButton);
+    m_componentProxyHandler->addAndMakeVisible (newButton);
     m_buttons.add (newButton);
 
     if (m_isRadioButtonMode)
diff --git a/Source/UI/Utils/ButtonGroupManager.h b/Source/UI/Utils/ButtonGroupManager.h
index 4b9bcc104..4c3f1431d 100644
--- a/Source/UI/Utils/ButtonGroupManager.h
+++ b/Source/UI/Utils/ButtonGroupManager.h
@@ -62,7 +62,7 @@ public:
 
     /** All component, that inherit ButtonGroupManager should override the resized() method
         in order to control buttons positioning inside it */
-    virtual void resized() = 0;
+    virtual void resized();
     // ===========================================================
 
     // Button::Listener methods
@@ -109,6 +109,14 @@ protected:
     /** Pointer to the LookAndFeel which will be used for each button */
     LookAndFeel* m_buttonsLookAndFeel;
 
+    /** Viewport whick will show appropriate buttons */
+    Viewport m_buttonsViewport;
+
+    /** We will instead all of our buttons to this component instead of current object
+        to be sure that we can use it with viewport support - it will be easy to show
+        a very big quantity of buttons with scrolling feature */
+    ScopedPointer<Component> m_componentProxyHandler;
+
     /** An array which stores buttons that will be managed by this class */
     OwnedArray<Button> m_buttons;
 
diff --git a/Source/UI/Utils/LinearButtonGroupManager.cpp b/Source/UI/Utils/LinearButtonGroupManager.cpp
index fed66a932..8c4488b3d 100644
--- a/Source/UI/Utils/LinearButtonGroupManager.cpp
+++ b/Source/UI/Utils/LinearButtonGroupManager.cpp
@@ -82,6 +82,8 @@ void LinearButtonGroupManager::paintOverChildren (Graphics& g)
 
 void LinearButtonGroupManager::resized()
 {
+    ButtonGroupManager::resized();
+
     const int width     = getWidth();
     const int height    = getHeight();
 
diff --git a/Source/UI/Utils/TiledButtonGroupManager.cpp b/Source/UI/Utils/TiledButtonGroupManager.cpp
index 3538ba2d9..02d980561 100644
--- a/Source/UI/Utils/TiledButtonGroupManager.cpp
+++ b/Source/UI/Utils/TiledButtonGroupManager.cpp
@@ -45,6 +45,8 @@ TiledButtonGroupManager::TiledButtonGroupManager()
 
 void TiledButtonGroupManager::resized()
 {
+    ButtonGroupManager::resized();
+
     const int width = getWidth();
 
     if (! width)
@@ -162,10 +164,16 @@ void TiledButtonGroupManager::addButton (Button* newButton)
 
 int TiledButtonGroupManager::getIndexOfButtonAtPosition (Point<int> position) const
 {
+
     const int numButtons = m_buttons.size();
+    const int viewPositionX = m_buttonsViewport.getViewPositionX();
+    const int viewPositionY = m_buttonsViewport.getViewPositionY();
+
+    //DBG ("Down Y position: " + String (position.translated (viewPositionX, viewPositionY).getY()));
+
     for (int i = 0; i < numButtons; ++i)
     {
-        if (m_buttons[i]->getBounds().contains (position))
+        if (m_buttons[i]->getBounds().contains (position.translated (viewPositionX, viewPositionY)))
             return i;
     }
 
-- 
GitLab