From 6217fdd9eb3316d437af47cbf8862ccea7c4822c Mon Sep 17 00:00:00 2001
From: Septen <gammerxpower@gmail.com>
Date: Sun, 27 Mar 2016 03:10:41 +0300
Subject: [PATCH] Implemented TiledButtonGroupManager.

* This class position buttons inside itself
  as tiles and has feature which gives
  us fuzzy button selection possibility via
  mouse drag (or SHIFT+mouse drag for deselection).
---
 Source/UI/Utils/ButtonGroupManager.cpp      |   1 -
 Source/UI/Utils/TiledButtonGroupManager.cpp | 194 ++++++++++++++++++++
 Source/UI/Utils/TiledButtonGroupManager.h   |  96 ++++++++++
 3 files changed, 290 insertions(+), 1 deletion(-)
 create mode 100644 Source/UI/Utils/TiledButtonGroupManager.cpp
 create mode 100644 Source/UI/Utils/TiledButtonGroupManager.h

diff --git a/Source/UI/Utils/ButtonGroupManager.cpp b/Source/UI/Utils/ButtonGroupManager.cpp
index 3b64682a6..5e8f931db 100644
--- a/Source/UI/Utils/ButtonGroupManager.cpp
+++ b/Source/UI/Utils/ButtonGroupManager.cpp
@@ -91,7 +91,6 @@ void ButtonGroupManager::addButton (Button* newButton)
 
     if (m_isRadioButtonMode)
     {
-        newButton->setClickingTogglesState (true);
         newButton->setRadioGroupId (1);
     }
 
diff --git a/Source/UI/Utils/TiledButtonGroupManager.cpp b/Source/UI/Utils/TiledButtonGroupManager.cpp
new file mode 100644
index 000000000..4b011c8c6
--- /dev/null
+++ b/Source/UI/Utils/TiledButtonGroupManager.cpp
@@ -0,0 +1,194 @@
+/*
+    ------------------------------------------------------------------
+
+    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 "TiledButtonGroupManager.h"
+#include "../MaterialButtonLookAndFeel.h"
+
+#include <algorithm>
+
+
+TiledButtonGroupManager::TiledButtonGroupManager()
+    : m_firstSelectedButtonIdx    (-1)
+    , m_lastSelectedButtonIdx     (-1)
+    , m_isToggleOnMode            (true)
+    , m_isDraggingMouseNow        (false)
+    , m_isSelectButtonsByDragging (false)
+    , m_buttonWidth               (0)
+    , m_buttonHeight              (0)
+{
+    setRadioButtonMode (false);
+    setButtonsLookAndFeel (new MaterialButtonLookAndFeel);
+}
+
+
+void TiledButtonGroupManager::resized()
+{
+    const int width = getWidth();
+
+    if (! width)
+        return;
+
+    const int minPadding = 5;
+    const int numButtonsInTheRow = width / (m_buttonWidth + minPadding);
+    const int padding = jmax (minPadding, (width - numButtonsInTheRow * m_buttonWidth) / (numButtonsInTheRow - 1));
+
+    juce::Rectangle<int> buttonBounds (0, 0, m_buttonWidth, m_buttonHeight);
+    const int numButtons = m_buttons.size();
+    for (int i = 0; i < numButtons; ++i)
+    {
+        m_buttons[i]->setBounds (buttonBounds);
+
+        // Go to the next row
+        if ( (i + 1) % numButtonsInTheRow == 0)
+        {
+            buttonBounds.setX (0);
+            buttonBounds.translate (0, m_buttonHeight + padding);
+        }
+        // Go to the next column
+        else
+        {
+            buttonBounds.translate (m_buttonWidth + padding, 0);
+        }
+    }
+}
+
+
+void TiledButtonGroupManager::mouseDown (const MouseEvent& e)
+{
+}
+
+
+void TiledButtonGroupManager::mouseUp (const MouseEvent& e)
+{
+    m_isDraggingMouseNow = false;
+
+    m_firstSelectedButtonIdx = -1;
+    m_lastSelectedButtonIdx  = -1;
+}
+
+
+void TiledButtonGroupManager::mouseDrag (const MouseEvent& e)
+{
+    if (! m_isSelectButtonsByDragging)
+        return;
+
+    m_isDraggingMouseNow = true;
+
+    // Is shift + drag mouse action occurs, then we should toggle on buttons;
+    // Otherwise - toggle off.
+    m_isToggleOnMode = ! e.mods.isShiftDown();
+
+    const int currentButtonIdx = getIndexOfButtonAtPosition (e.getEventRelativeTo (this).getPosition());
+
+    // Remember the first button on which we started dragging
+    if (m_firstSelectedButtonIdx == -1
+        && m_lastSelectedButtonIdx == -1
+        && currentButtonIdx >= 0)
+    {
+        m_firstSelectedButtonIdx = currentButtonIdx;
+    }
+
+
+    if (currentButtonIdx != -1
+        && currentButtonIdx != m_lastSelectedButtonIdx)
+    {
+        m_lastSelectedButtonIdx = currentButtonIdx;
+
+        // Get the indices of range which we should to select (get FROM index and TO index)
+        const int fromIndex = jmin (m_firstSelectedButtonIdx, m_lastSelectedButtonIdx);
+        const int toIndex   = jmax (m_firstSelectedButtonIdx, m_lastSelectedButtonIdx);
+
+        for (int i = fromIndex; i <= toIndex; ++i)
+        {
+            // Trigger click only if the button still isn't selected/deselected (according to the SHIFT holds)
+            if (m_buttons[i]->getToggleState() != m_isToggleOnMode)
+                // Trigger click in the button, this will call buttonClicked() method, where we will
+                m_buttons[i]->triggerClick();
+        }
+    }
+}
+
+
+void TiledButtonGroupManager::buttonClicked (Button* buttonThatWasClicked)
+{
+    // Fast selection enabled and we dragging mouse now to select
+    if (m_isSelectButtonsByDragging
+        && m_isDraggingMouseNow)
+    {
+        buttonThatWasClicked->setToggleState (m_isToggleOnMode, dontSendNotification);
+    }
+    // Default selection
+    else
+    {
+        buttonThatWasClicked->setToggleState (! buttonThatWasClicked->getToggleState(), dontSendNotification);
+    }
+
+    // Notify the listener
+    ButtonGroupManager::buttonClicked (buttonThatWasClicked);
+}
+
+
+void TiledButtonGroupManager::addButton (Button* newButton)
+{
+    ButtonGroupManager::addButton (newButton);
+
+    // Disable default clicking on button
+    newButton->addMouseListener (this, false);
+}
+
+
+int TiledButtonGroupManager::getIndexOfButtonAtPosition (Point<int> position)
+{
+    const int numButtons = m_buttons.size();
+    for (int i = 0; i < numButtons; ++i)
+    {
+        if (m_buttons[i]->getBounds().contains (position))
+            return i;
+    }
+
+    return -1;
+}
+
+
+bool TiledButtonGroupManager::isFastSelectionModeEnabled()
+{
+    return m_isSelectButtonsByDragging;
+}
+
+
+void TiledButtonGroupManager::setFastSelectionModeEnabled (bool isFastSelectionMode)
+{
+    m_isSelectButtonsByDragging = isFastSelectionMode;
+}
+
+
+void TiledButtonGroupManager::setButtonSize (int buttonWidth, int buttonHeight)
+{
+    m_buttonWidth  = buttonWidth;
+    m_buttonHeight = buttonHeight;
+
+    std::for_each (m_buttons.begin(), m_buttons.end(),
+                   [=] (Button* button) { button->setSize (buttonWidth, buttonHeight); });
+
+    resized();
+}
diff --git a/Source/UI/Utils/TiledButtonGroupManager.h b/Source/UI/Utils/TiledButtonGroupManager.h
new file mode 100644
index 000000000..7de5e1dbc
--- /dev/null
+++ b/Source/UI/Utils/TiledButtonGroupManager.h
@@ -0,0 +1,96 @@
+/*
+    ------------------------------------------------------------------
+
+    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/>.
+
+*/
+
+#ifndef TILEDBUTTONGROUPMANAGER_H_INCLUDED
+#define TILEDBUTTONGROUPMANAGER_H_INCLUDED
+
+#include "../../../JuceLibraryCode/JuceHeader.h"
+#include "../../Processors/PluginManager/OpenEphysPlugin.h"
+#include "ButtonGroupManager.h"
+
+
+/**
+
+    This class provides possibility to store buttons in several rows having tile-structure.
+    It is responsible for positioning of buttons, any animations during resizing.
+    It also have "Fast select" feature which allows as to quickly select many buttons
+    using mouse drag selection. (mouse drag - to select, shift+mouse drag to deselect)
+
+*/
+
+
+class PLUGIN_API TiledButtonGroupManager : public ButtonGroupManager
+{
+public:
+    TiledButtonGroupManager();
+
+    // Component methods
+    // ===========================================================
+    void resized() override;
+
+    void mouseDown (const MouseEvent& e) override;
+    void mouseUp   (const MouseEvent& e) override;
+    void mouseDrag (const MouseEvent& e) override;
+    // ===========================================================
+
+    // Button::Listener methods
+    // ===========================================================
+    void buttonClicked (Button* buttonThatWasClicked);
+
+    /** Returns whether fast selection mode (toggle on or off buttons by dragging mouse) is enabled or not */
+    bool isFastSelectionModeEnabled();
+
+    /** Sets whether fast selection mode (toggle on or off buttons by dragging mouse) will be enabled */
+    void setFastSelectionModeEnabled (bool isFastSelectionMode);
+
+    /** Sets the size of each button */
+    void setButtonSize (int buttonWidth, int buttonHeight);
+
+    /** Add button to the array of buttons to manage it.
+
+        This class controls ownership of buttons.
+    */
+    void addButton (Button* newButton);
+
+
+private:
+    // Returns the index of button at given position.
+    // If nothing found at this position - returns -1.
+    int getIndexOfButtonAtPosition (Point<int> position);
+
+    int m_buttonWidth;
+    int m_buttonHeight;
+
+    int m_firstSelectedButtonIdx;
+    int m_lastSelectedButtonIdx;
+    bool m_isToggleOnMode;
+    bool m_isDraggingMouseNow;
+    bool m_isSelectButtonsByDragging;
+
+    // ===========================================================
+    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TiledButtonGroupManager)
+};
+
+
+
+#endif //TILEDBUTTONGROUPMANAGER_H_INCLUDED
-- 
GitLab