Add TAL-Reverb-II plugin to test
[juce-lv2.git] / juce / source / src / gui / components / layout / juce_TabbedButtonBar.cpp
blob729b279afac67cbe287a14dc8a83a14a1f0c8052
1 /*
2 ==============================================================================
4 This file is part of the JUCE library - "Jules' Utility Class Extensions"
5 Copyright 2004-11 by Raw Material Software Ltd.
7 ------------------------------------------------------------------------------
9 JUCE can be redistributed and/or modified under the terms of the GNU General
10 Public License (Version 2), as published by the Free Software Foundation.
11 A copy of the license is included in the JUCE distribution, or can be found
12 online at www.gnu.org/licenses.
14 JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 ------------------------------------------------------------------------------
20 To release a closed-source product which uses JUCE, commercial licenses are
21 available: visit www.rawmaterialsoftware.com/juce for more information.
23 ==============================================================================
26 #include "../../../core/juce_StandardHeader.h"
28 BEGIN_JUCE_NAMESPACE
30 #include "juce_TabbedComponent.h"
31 #include "../menus/juce_PopupMenu.h"
32 #include "../lookandfeel/juce_LookAndFeel.h"
35 //==============================================================================
36 TabBarButton::TabBarButton (const String& name, TabbedButtonBar& owner_)
37 : Button (name),
38 owner (owner_),
39 overlapPixels (0)
41 shadow.setShadowProperties (2.2f, 0.7f, 0, 0);
42 setComponentEffect (&shadow);
43 setWantsKeyboardFocus (false);
46 TabBarButton::~TabBarButton()
50 int TabBarButton::getIndex() const
52 return owner.indexOfTabButton (this);
55 void TabBarButton::paintButton (Graphics& g,
56 bool isMouseOverButton,
57 bool isButtonDown)
59 const Rectangle<int> area (getActiveArea());
60 g.setOrigin (area.getX(), area.getY());
62 getLookAndFeel()
63 .drawTabButton (g, area.getWidth(), area.getHeight(),
64 owner.getTabBackgroundColour (getIndex()),
65 getIndex(), getButtonText(), *this,
66 owner.getOrientation(),
67 isMouseOverButton, isButtonDown,
68 getToggleState());
71 void TabBarButton::clicked (const ModifierKeys& mods)
73 if (mods.isPopupMenu())
74 owner.popupMenuClickOnTab (getIndex(), getButtonText());
75 else
76 owner.setCurrentTabIndex (getIndex());
79 bool TabBarButton::hitTest (int mx, int my)
81 const Rectangle<int> area (getActiveArea());
83 if (owner.getOrientation() == TabbedButtonBar::TabsAtLeft
84 || owner.getOrientation() == TabbedButtonBar::TabsAtRight)
86 if (isPositiveAndBelow (mx, getWidth())
87 && my >= area.getY() + overlapPixels
88 && my < area.getBottom() - overlapPixels)
89 return true;
91 else
93 if (mx >= area.getX() + overlapPixels && mx < area.getRight() - overlapPixels
94 && isPositiveAndBelow (my, getHeight()))
95 return true;
98 Path p;
99 getLookAndFeel()
100 .createTabButtonShape (p, area.getWidth(), area.getHeight(), getIndex(), getButtonText(), *this,
101 owner.getOrientation(), false, false, getToggleState());
103 return p.contains ((float) (mx - area.getX()),
104 (float) (my - area.getY()));
107 int TabBarButton::getBestTabLength (const int depth)
109 return jlimit (depth * 2,
110 depth * 7,
111 getLookAndFeel().getTabButtonBestWidth (getIndex(), getButtonText(), depth, *this));
114 const Rectangle<int> TabBarButton::getActiveArea()
116 Rectangle<int> r (getLocalBounds());
117 const int spaceAroundImage = getLookAndFeel().getTabButtonSpaceAroundImage();
119 if (owner.getOrientation() != TabbedButtonBar::TabsAtLeft) r.removeFromRight (spaceAroundImage);
120 if (owner.getOrientation() != TabbedButtonBar::TabsAtRight) r.removeFromLeft (spaceAroundImage);
121 if (owner.getOrientation() != TabbedButtonBar::TabsAtBottom) r.removeFromTop (spaceAroundImage);
122 if (owner.getOrientation() != TabbedButtonBar::TabsAtTop) r.removeFromBottom (spaceAroundImage);
124 return r;
128 //==============================================================================
129 class TabbedButtonBar::BehindFrontTabComp : public Component,
130 public ButtonListener // (can't use Button::Listener due to idiotic VC2005 bug)
132 public:
133 BehindFrontTabComp (TabbedButtonBar& owner_)
134 : owner (owner_)
136 setInterceptsMouseClicks (false, false);
139 void paint (Graphics& g)
141 getLookAndFeel().drawTabAreaBehindFrontButton (g, getWidth(), getHeight(),
142 owner, owner.getOrientation());
145 void enablementChanged()
147 repaint();
150 void buttonClicked (Button*)
152 owner.showExtraItemsMenu();
155 private:
156 TabbedButtonBar& owner;
158 JUCE_DECLARE_NON_COPYABLE (BehindFrontTabComp);
162 //==============================================================================
163 TabbedButtonBar::TabbedButtonBar (const Orientation orientation_)
164 : orientation (orientation_),
165 minimumScale (0.7),
166 currentTabIndex (-1)
168 setInterceptsMouseClicks (false, true);
169 addAndMakeVisible (behindFrontTab = new BehindFrontTabComp (*this));
170 setFocusContainer (true);
173 TabbedButtonBar::~TabbedButtonBar()
175 tabs.clear();
176 extraTabsButton = nullptr;
179 //==============================================================================
180 void TabbedButtonBar::setOrientation (const Orientation newOrientation)
182 orientation = newOrientation;
184 for (int i = getNumChildComponents(); --i >= 0;)
185 getChildComponent (i)->resized();
187 resized();
190 TabBarButton* TabbedButtonBar::createTabButton (const String& name, const int /*index*/)
192 return new TabBarButton (name, *this);
195 void TabbedButtonBar::setMinimumTabScaleFactor (double newMinimumScale)
197 minimumScale = newMinimumScale;
198 resized();
201 //==============================================================================
202 void TabbedButtonBar::clearTabs()
204 tabs.clear();
205 extraTabsButton = nullptr;
206 setCurrentTabIndex (-1);
209 void TabbedButtonBar::addTab (const String& tabName,
210 const Colour& tabBackgroundColour,
211 int insertIndex)
213 jassert (tabName.isNotEmpty()); // you have to give them all a name..
215 if (tabName.isNotEmpty())
217 if (! isPositiveAndBelow (insertIndex, tabs.size()))
218 insertIndex = tabs.size();
220 TabInfo* newTab = new TabInfo();
221 newTab->name = tabName;
222 newTab->colour = tabBackgroundColour;
223 newTab->component = createTabButton (tabName, insertIndex);
225 jassert (newTab->component != nullptr);
227 tabs.insert (insertIndex, newTab);
228 addAndMakeVisible (newTab->component, insertIndex);
230 resized();
232 if (currentTabIndex < 0)
233 setCurrentTabIndex (0);
237 void TabbedButtonBar::setTabName (const int tabIndex, const String& newName)
239 TabInfo* const tab = tabs [tabIndex];
241 if (tab != nullptr && tab->name != newName)
243 tab->name = newName;
244 tab->component->setButtonText (newName);
245 resized();
249 void TabbedButtonBar::removeTab (const int tabIndex)
251 if (tabs [tabIndex] != nullptr)
253 const int oldTabIndex = currentTabIndex;
254 if (currentTabIndex == tabIndex)
255 currentTabIndex = -1;
257 tabs.remove (tabIndex);
258 resized();
260 setCurrentTabIndex (jlimit (0, jmax (0, tabs.size() - 1), oldTabIndex));
264 void TabbedButtonBar::moveTab (const int currentIndex, const int newIndex)
266 tabs.move (currentIndex, newIndex);
267 resized();
270 int TabbedButtonBar::getNumTabs() const
272 return tabs.size();
275 String TabbedButtonBar::getCurrentTabName() const
277 TabInfo* tab = tabs [currentTabIndex];
278 return tab == nullptr ? String::empty : tab->name;
281 StringArray TabbedButtonBar::getTabNames() const
283 StringArray names;
285 for (int i = 0; i < tabs.size(); ++i)
286 names.add (tabs.getUnchecked(i)->name);
288 return names;
291 void TabbedButtonBar::setCurrentTabIndex (int newIndex, const bool sendChangeMessage_)
293 if (currentTabIndex != newIndex)
295 if (! isPositiveAndBelow (newIndex, tabs.size()))
296 newIndex = -1;
298 currentTabIndex = newIndex;
300 for (int i = 0; i < tabs.size(); ++i)
302 TabBarButton* tb = tabs.getUnchecked(i)->component;
303 tb->setToggleState (i == newIndex, false);
306 resized();
308 if (sendChangeMessage_)
309 sendChangeMessage();
311 currentTabChanged (newIndex, getCurrentTabName());
315 TabBarButton* TabbedButtonBar::getTabButton (const int index) const
317 TabInfo* const tab = tabs[index];
318 return tab == nullptr ? nullptr : static_cast <TabBarButton*> (tab->component);
321 int TabbedButtonBar::indexOfTabButton (const TabBarButton* button) const
323 for (int i = tabs.size(); --i >= 0;)
324 if (tabs.getUnchecked(i)->component == button)
325 return i;
327 return -1;
330 void TabbedButtonBar::lookAndFeelChanged()
332 extraTabsButton = nullptr;
333 resized();
336 void TabbedButtonBar::resized()
338 int depth = getWidth();
339 int length = getHeight();
341 if (orientation == TabsAtTop || orientation == TabsAtBottom)
342 std::swap (depth, length);
344 const int overlap = getLookAndFeel().getTabButtonOverlap (depth)
345 + getLookAndFeel().getTabButtonSpaceAroundImage() * 2;
347 int i, totalLength = overlap;
348 int numVisibleButtons = tabs.size();
350 for (i = 0; i < tabs.size(); ++i)
352 TabBarButton* const tb = tabs.getUnchecked(i)->component;
354 totalLength += tb->getBestTabLength (depth) - overlap;
355 tb->overlapPixels = overlap / 2;
358 double scale = 1.0;
360 if (totalLength > length)
361 scale = jmax (minimumScale, length / (double) totalLength);
363 const bool isTooBig = totalLength * scale > length;
364 int tabsButtonPos = 0;
366 if (isTooBig)
368 if (extraTabsButton == nullptr)
370 addAndMakeVisible (extraTabsButton = getLookAndFeel().createTabBarExtrasButton());
371 extraTabsButton->addListener (behindFrontTab);
372 extraTabsButton->setAlwaysOnTop (true);
373 extraTabsButton->setTriggeredOnMouseDown (true);
376 const int buttonSize = jmin (proportionOfWidth (0.7f), proportionOfHeight (0.7f));
377 extraTabsButton->setSize (buttonSize, buttonSize);
379 if (orientation == TabsAtTop || orientation == TabsAtBottom)
381 tabsButtonPos = getWidth() - buttonSize / 2 - 1;
382 extraTabsButton->setCentrePosition (tabsButtonPos, getHeight() / 2);
384 else
386 tabsButtonPos = getHeight() - buttonSize / 2 - 1;
387 extraTabsButton->setCentrePosition (getWidth() / 2, tabsButtonPos);
390 totalLength = 0;
392 for (i = 0; i < tabs.size(); ++i)
394 TabBarButton* const tb = tabs.getUnchecked(i)->component;
396 const int newLength = totalLength + tb->getBestTabLength (depth);
398 if (i > 0 && newLength * minimumScale > tabsButtonPos)
400 totalLength += overlap;
401 break;
404 numVisibleButtons = i + 1;
405 totalLength = newLength - overlap;
408 scale = jmax (minimumScale, tabsButtonPos / (double) totalLength);
410 else
412 extraTabsButton = nullptr;
415 int pos = 0;
417 TabBarButton* frontTab = nullptr;
419 for (i = 0; i < tabs.size(); ++i)
421 TabBarButton* const tb = getTabButton (i);
423 if (tb != nullptr)
425 const int bestLength = roundToInt (scale * tb->getBestTabLength (depth));
427 if (i < numVisibleButtons)
429 if (orientation == TabsAtTop || orientation == TabsAtBottom)
430 tb->setBounds (pos, 0, bestLength, getHeight());
431 else
432 tb->setBounds (0, pos, getWidth(), bestLength);
434 tb->toBack();
436 if (i == currentTabIndex)
437 frontTab = tb;
439 tb->setVisible (true);
441 else
443 tb->setVisible (false);
446 pos += bestLength - overlap;
450 behindFrontTab->setBounds (getLocalBounds());
452 if (frontTab != nullptr)
454 frontTab->toFront (false);
455 behindFrontTab->toBehind (frontTab);
459 //==============================================================================
460 const Colour TabbedButtonBar::getTabBackgroundColour (const int tabIndex)
462 TabInfo* const tab = tabs [tabIndex];
463 return tab == nullptr ? Colours::white : tab->colour;
466 void TabbedButtonBar::setTabBackgroundColour (const int tabIndex, const Colour& newColour)
468 TabInfo* const tab = tabs [tabIndex];
470 if (tab != nullptr && tab->colour != newColour)
472 tab->colour = newColour;
473 repaint();
477 void TabbedButtonBar::extraItemsMenuCallback (int result, TabbedButtonBar* bar)
479 if (bar != nullptr && result > 0)
480 bar->setCurrentTabIndex (result - 1);
483 void TabbedButtonBar::showExtraItemsMenu()
485 PopupMenu m;
487 for (int i = 0; i < tabs.size(); ++i)
489 const TabInfo* const tab = tabs.getUnchecked(i);
491 if (! tab->component->isVisible())
492 m.addItem (i + 1, tab->name, true, i == currentTabIndex);
495 m.showMenuAsync (PopupMenu::Options().withTargetComponent (extraTabsButton),
496 ModalCallbackFunction::forComponent (extraItemsMenuCallback, this));
499 //==============================================================================
500 void TabbedButtonBar::currentTabChanged (const int, const String&)
504 void TabbedButtonBar::popupMenuClickOnTab (const int, const String&)
508 END_JUCE_NAMESPACE