1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00
JUCE/src/juce_appframework/gui/components/juce_Component.cpp
2007-07-17 15:06:38 +00:00

3730 lines
109 KiB
C++

/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-7 by Raw Material Software ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the
GNU General Public License, as published by the Free Software Foundation;
either version 2 of the License, or (at your option) any later version.
JUCE 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 JUCE; if not, visit www.gnu.org/licenses or write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
------------------------------------------------------------------------------
If you'd like to release a closed-source product which uses JUCE, commercial
licenses are also available: visit www.rawmaterialsoftware.com/juce for
more information.
==============================================================================
*/
#include "../../../juce_core/basics/juce_StandardHeader.h"
BEGIN_JUCE_NAMESPACE
#include "juce_Component.h"
#include "juce_ComponentDeletionWatcher.h"
#include "juce_Desktop.h"
#include "keyboard/juce_KeyListener.h"
#include "lookandfeel/juce_LookAndFeel.h"
#include "../../application/juce_Application.h"
#include "../graphics/geometry/juce_RectangleList.h"
#include "../graphics/imaging/juce_Image.h"
#include "../graphics/contexts/juce_LowLevelGraphicsContext.h"
#include "../../events/juce_MessageManager.h"
#include "../../events/juce_Timer.h"
#include "../../../juce_core/basics/juce_Time.h"
#include "../../../juce_core/misc/juce_PlatformUtilities.h"
//==============================================================================
Component* Component::componentUnderMouse = 0;
Component* Component::currentlyFocusedComponent = 0;
static Array <Component*> modalComponentStack (4), modalComponentReturnValueKeys (4);
static Array <int> modalReturnValues (4);
static const int customCommandMessage = 0x7fff0001;
static const int exitModalStateMessage = 0x7fff0002;
//==============================================================================
// these are also used by ComponentPeer
int64 juce_recentMouseDownTimes [4] = { 0, 0, 0, 0 };
int juce_recentMouseDownX [4] = { 0, 0, 0, 0 };
int juce_recentMouseDownY [4] = { 0, 0, 0, 0 };
int juce_LastMousePosX = 0;
int juce_LastMousePosY = 0;
int juce_MouseClickCounter = 0;
bool juce_MouseHasMovedSignificantlySincePressed = false;
static int countMouseClicks() throw()
{
int numClicks = 0;
if (juce_recentMouseDownTimes[0] != 0)
{
if (! juce_MouseHasMovedSignificantlySincePressed)
++numClicks;
for (int i = 1; i < 4; ++i)
{
if (juce_recentMouseDownTimes[0] - juce_recentMouseDownTimes [i]
< (int) (MouseEvent::getDoubleClickTimeout() * (1.0 + 0.25 * (i - 1)))
&& abs (juce_recentMouseDownX[0] - juce_recentMouseDownX[i]) < 8
&& abs (juce_recentMouseDownY[0] - juce_recentMouseDownY[i]) < 8)
{
++numClicks;
}
else
{
break;
}
}
}
return numClicks;
}
static int unboundedMouseOffsetX = 0;
static int unboundedMouseOffsetY = 0;
static bool isUnboundedMouseModeOn = false;
static bool isCursorVisibleUntilOffscreen;
//==============================================================================
const int juce_windowIsSemiTransparentFlag = (1 << 31); // duplicated in native windowing code
#define checkMessageManagerIsLocked jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager());
static uint32 nextComponentUID = 0;
//==============================================================================
Component::Component() throw()
: parentComponent_ (0),
componentUID (++nextComponentUID),
numDeepMouseListeners (0),
childComponentList_ (16),
lookAndFeel_ (0),
effect_ (0),
bufferedImage_ (0),
mouseListeners_ (0),
keyListeners_ (0),
componentListeners_ (0),
propertySet_ (0),
componentFlags_ (0)
{
}
Component::Component (const String& name) throw()
: componentName_ (name),
parentComponent_ (0),
componentUID (++nextComponentUID),
numDeepMouseListeners (0),
childComponentList_ (16),
lookAndFeel_ (0),
effect_ (0),
bufferedImage_ (0),
mouseListeners_ (0),
keyListeners_ (0),
componentListeners_ (0),
propertySet_ (0),
componentFlags_ (0)
{
}
Component::~Component()
{
if (parentComponent_ != 0)
{
parentComponent_->removeChildComponent (this);
}
else if ((currentlyFocusedComponent == this)
|| isParentOf (currentlyFocusedComponent))
{
giveAwayFocus();
}
if (componentUnderMouse == this)
componentUnderMouse = 0;
if (flags.hasHeavyweightPeerFlag)
removeFromDesktop();
modalComponentStack.removeValue (this);
for (int i = childComponentList_.size(); --i >= 0;)
childComponentList_.getUnchecked(i)->parentComponent_ = 0;
delete bufferedImage_;
delete mouseListeners_;
delete keyListeners_;
delete componentListeners_;
delete propertySet_;
}
//==============================================================================
void Component::setName (const String& name)
{
// if component methods are being called from threads other than the message
// thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
checkMessageManagerIsLocked
if (componentName_ != name)
{
componentName_ = name;
if (flags.hasHeavyweightPeerFlag)
{
ComponentPeer* const peer = getPeer();
jassert (peer != 0);
if (peer != 0)
peer->setTitle (name);
}
if (componentListeners_ != 0)
{
const ComponentDeletionWatcher deletionChecker (this);
for (int i = componentListeners_->size(); --i >= 0;)
{
((ComponentListener*) componentListeners_->getUnchecked (i))
->componentNameChanged (*this);
if (deletionChecker.hasBeenDeleted())
return;
i = jmin (i, componentListeners_->size());
}
}
}
}
void Component::setVisible (bool shouldBeVisible)
{
if (flags.visibleFlag != shouldBeVisible)
{
// if component methods are being called from threads other than the message
// thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
checkMessageManagerIsLocked
const ComponentDeletionWatcher deletionChecker (this);
flags.visibleFlag = shouldBeVisible;
internalRepaint (0, 0, getWidth(), getHeight());
sendFakeMouseMove();
if (! shouldBeVisible)
{
if (currentlyFocusedComponent == this
|| isParentOf (currentlyFocusedComponent))
{
if (parentComponent_ != 0)
parentComponent_->grabKeyboardFocus();
else
giveAwayFocus();
}
}
sendVisibilityChangeMessage();
if ((! deletionChecker.hasBeenDeleted()) && flags.hasHeavyweightPeerFlag)
{
ComponentPeer* const peer = getPeer();
jassert (peer != 0);
if (peer != 0)
peer->setVisible (shouldBeVisible);
}
}
}
void Component::visibilityChanged()
{
}
void Component::sendVisibilityChangeMessage()
{
const ComponentDeletionWatcher deletionChecker (this);
visibilityChanged();
if ((! deletionChecker.hasBeenDeleted()) && componentListeners_ != 0)
{
for (int i = componentListeners_->size(); --i >= 0;)
{
((ComponentListener*) componentListeners_->getUnchecked (i))
->componentVisibilityChanged (*this);
if (deletionChecker.hasBeenDeleted())
return;
i = jmin (i, componentListeners_->size());
}
}
}
bool Component::isShowing() const throw()
{
if (flags.visibleFlag)
{
if (parentComponent_ != 0)
{
return parentComponent_->isShowing();
}
else
{
const ComponentPeer* const peer = getPeer();
return peer != 0 && ! peer->isMinimised();
}
}
return false;
}
//==============================================================================
class FadeOutProxyComponent : public Component,
public Timer
{
public:
FadeOutProxyComponent (Component* comp,
const int fadeLengthMs,
const int deltaXToMove,
const int deltaYToMove,
const float scaleFactorAtEnd)
: lastTime (0),
alpha (1.0f),
scale (1.0f)
{
image = comp->createComponentSnapshot (Rectangle (0, 0, comp->getWidth(), comp->getHeight()));
setBounds (comp->getBounds());
comp->getParentComponent()->addAndMakeVisible (this);
toBehind (comp);
alphaChangePerMs = -1.0f / (float)fadeLengthMs;
centreX = comp->getX() + comp->getWidth() * 0.5f;
xChangePerMs = deltaXToMove / (float)fadeLengthMs;
centreY = comp->getY() + comp->getHeight() * 0.5f;
yChangePerMs = deltaYToMove / (float)fadeLengthMs;
scaleChangePerMs = (scaleFactorAtEnd - 1.0f) / (float)fadeLengthMs;
setInterceptsMouseClicks (false, false);
// 30 fps is enough for a fade, but we need a higher rate if it's moving as well..
startTimer (1000 / ((deltaXToMove == 0 && deltaYToMove == 0) ? 30 : 50));
}
~FadeOutProxyComponent()
{
delete image;
}
void paint (Graphics& g)
{
g.setOpacity (alpha);
g.drawImage (image,
0, 0, getWidth(), getHeight(),
0, 0, image->getWidth(), image->getHeight());
}
void timerCallback()
{
const uint32 now = Time::getMillisecondCounter();
if (lastTime == 0)
lastTime = now;
const int msPassed = (now > lastTime) ? now - lastTime : 0;
lastTime = now;
alpha += alphaChangePerMs * msPassed;
if (alpha > 0)
{
if (xChangePerMs != 0.0f || yChangePerMs != 0.0f || scaleChangePerMs != 0.0f)
{
centreX += xChangePerMs * msPassed;
centreY += yChangePerMs * msPassed;
scale += scaleChangePerMs * msPassed;
const int w = roundFloatToInt (image->getWidth() * scale);
const int h = roundFloatToInt (image->getHeight() * scale);
setBounds (roundFloatToInt (centreX) - w / 2,
roundFloatToInt (centreY) - h / 2,
w, h);
}
repaint();
}
else
{
delete this;
}
}
juce_UseDebuggingNewOperator
private:
Image* image;
uint32 lastTime;
float alpha, alphaChangePerMs;
float centreX, xChangePerMs;
float centreY, yChangePerMs;
float scale, scaleChangePerMs;
FadeOutProxyComponent (const FadeOutProxyComponent&);
const FadeOutProxyComponent& operator= (const FadeOutProxyComponent&);
};
void Component::fadeOutComponent (const int millisecondsToFade,
const int deltaXToMove,
const int deltaYToMove,
const float scaleFactorAtEnd)
{
//xxx won't work for comps without parents
if (isShowing() && millisecondsToFade > 0)
new FadeOutProxyComponent (this, millisecondsToFade,
deltaXToMove, deltaYToMove, scaleFactorAtEnd);
setVisible (false);
}
//==============================================================================
bool Component::isValidComponent() const throw()
{
return (this != 0) && isValidMessageListener();
}
void* Component::getWindowHandle() const throw()
{
const ComponentPeer* const peer = getPeer();
if (peer != 0)
return peer->getNativeHandle();
return 0;
}
//==============================================================================
void Component::addToDesktop (int desktopWindowStyleFlags, void* nativeWindowToAttachTo)
{
// if component methods are being called from threads other than the message
// thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
checkMessageManagerIsLocked
if (! isOpaque())
desktopWindowStyleFlags |= juce_windowIsSemiTransparentFlag;
int currentStyleFlags = 0;
ComponentBoundsConstrainer* currentConstainer = 0;
if (isOnDesktop())
{
const ComponentPeer* const peer = getPeer();
if (peer != 0)
{
currentStyleFlags = peer->getStyleFlags();
currentConstainer = peer->getConstrainer();
}
}
if ((! isOnDesktop()) || desktopWindowStyleFlags != currentStyleFlags)
{
const ComponentDeletionWatcher deletionChecker (this);
#if JUCE_LINUX
// it's wise to give the component a non-zero size before
// putting it on the desktop, as X windows get confused by this, and
// a (1, 1) minimum size is enforced here.
setSize (jmax (1, getWidth()),
jmax (1, getHeight()));
#endif
int x = 0, y = 0;
relativePositionToGlobal (x, y);
ComponentPeer* peer = getPeer();
bool wasFullscreen = false;
bool wasMinimised = false;
if (peer != 0)
{
wasFullscreen = peer->isFullScreen();
wasMinimised = peer->isMinimised();
removeFromDesktop();
}
if (parentComponent_ != 0)
parentComponent_->removeChildComponent (this);
if (! deletionChecker.hasBeenDeleted())
{
flags.hasHeavyweightPeerFlag = true;
peer = createNewPeer (desktopWindowStyleFlags, nativeWindowToAttachTo);
Desktop::getInstance().addDesktopComponent (this);
bounds_.setPosition (x, y);
peer->setBounds (x, y, getWidth(), getHeight(), false);
peer->setVisible (isVisible());
if (wasFullscreen)
peer->setFullScreen (true);
if (wasMinimised)
peer->setMinimised (true);
if (isAlwaysOnTop())
peer->setAlwaysOnTop (true);
peer->setConstrainer (currentConstainer);
repaint();
}
internalHierarchyChanged();
}
}
void Component::removeFromDesktop()
{
// if component methods are being called from threads other than the message
// thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
checkMessageManagerIsLocked
if (isOnDesktop())
{
ComponentPeer* const peer = ComponentPeer::getPeerFor (this);
flags.hasHeavyweightPeerFlag = false;
jassert (peer != 0);
delete peer;
Desktop::getInstance().removeDesktopComponent (this);
}
}
bool Component::isOnDesktop() const throw()
{
return flags.hasHeavyweightPeerFlag;
}
void Component::userTriedToCloseWindow()
{
/* This means that the user's trying to get rid of your window with the 'close window' system
menu option (on windows) or possibly the task manager - you should really handle this
and delete or hide your component in an appropriate way.
If you want to ignore the event and don't want to trigger this assertion, just override
this method and do nothing.
*/
jassertfalse
}
void Component::minimisationStateChanged (bool)
{
}
//==============================================================================
void Component::setOpaque (const bool shouldBeOpaque) throw()
{
if (shouldBeOpaque != flags.opaqueFlag)
{
flags.opaqueFlag = shouldBeOpaque;
if (isOnDesktop())
{
const ComponentPeer* const peer = getPeer();
if (peer != 0)
{
// to make it recreate the heavyweight window
addToDesktop (peer->getStyleFlags());
}
}
repaint();
}
}
bool Component::isOpaque() const throw()
{
return flags.opaqueFlag;
}
//==============================================================================
void Component::setBufferedToImage (const bool shouldBeBuffered) throw()
{
if (shouldBeBuffered != flags.bufferToImageFlag)
{
deleteAndZero (bufferedImage_);
flags.bufferToImageFlag = shouldBeBuffered;
}
}
//==============================================================================
void Component::toFront (const bool setAsForeground)
{
// if component methods are being called from threads other than the message
// thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
checkMessageManagerIsLocked
if (flags.hasHeavyweightPeerFlag)
{
ComponentPeer* const peer = getPeer();
if (peer != 0)
{
peer->toFront (setAsForeground);
if (setAsForeground && ! hasKeyboardFocus (true))
grabKeyboardFocus();
}
}
else if (parentComponent_ != 0)
{
if (parentComponent_->childComponentList_.getLast() != this)
{
const int index = parentComponent_->childComponentList_.indexOf (this);
if (index >= 0)
{
int insertIndex = -1;
if (! flags.alwaysOnTopFlag)
{
insertIndex = parentComponent_->childComponentList_.size() - 1;
while (insertIndex > 0
&& parentComponent_->childComponentList_.getUnchecked (insertIndex)->isAlwaysOnTop())
{
--insertIndex;
}
}
if (index != insertIndex)
{
parentComponent_->childComponentList_.move (index, insertIndex);
sendFakeMouseMove();
repaintParent();
}
}
}
if (setAsForeground)
{
internalBroughtToFront();
grabKeyboardFocus();
}
}
}
void Component::toBehind (Component* const other)
{
if (other != 0)
{
// the two components must belong to the same parent..
jassert (parentComponent_ == other->parentComponent_);
if (parentComponent_ != 0)
{
const int index = parentComponent_->childComponentList_.indexOf (this);
int otherIndex = parentComponent_->childComponentList_.indexOf (other);
if (index >= 0
&& otherIndex >= 0
&& index != otherIndex - 1
&& other != this)
{
if (index < otherIndex)
--otherIndex;
parentComponent_->childComponentList_.move (index, otherIndex);
sendFakeMouseMove();
repaintParent();
}
}
else if (isOnDesktop())
{
jassert (other->isOnDesktop());
if (other->isOnDesktop())
{
ComponentPeer* const us = getPeer();
ComponentPeer* const them = other->getPeer();
jassert (us != 0 && them != 0);
if (us != 0 && them != 0)
us->toBehind (them);
}
}
}
}
void Component::toBack()
{
if (isOnDesktop())
{
jassertfalse //xxx need to add this to native window
}
else if (parentComponent_ != 0
&& parentComponent_->childComponentList_.getFirst() != this)
{
const int index = parentComponent_->childComponentList_.indexOf (this);
if (index > 0)
{
int insertIndex = 0;
if (flags.alwaysOnTopFlag)
{
while (insertIndex < parentComponent_->childComponentList_.size()
&& ! parentComponent_->childComponentList_.getUnchecked (insertIndex)->isAlwaysOnTop())
{
++insertIndex;
}
}
if (index != insertIndex)
{
parentComponent_->childComponentList_.move (index, insertIndex);
sendFakeMouseMove();
repaintParent();
}
}
}
}
void Component::setAlwaysOnTop (const bool shouldStayOnTop)
{
if (shouldStayOnTop != flags.alwaysOnTopFlag)
{
flags.alwaysOnTopFlag = shouldStayOnTop;
if (isOnDesktop())
{
ComponentPeer* const peer = getPeer();
jassert (peer != 0);
if (peer != 0)
{
if (! peer->setAlwaysOnTop (shouldStayOnTop))
{
// some kinds of peer can't change their always-on-top status, so
// for these, we'll need to create a new window
const int oldFlags = peer->getStyleFlags();
removeFromDesktop();
addToDesktop (oldFlags);
}
}
}
if (shouldStayOnTop)
toFront (false);
internalHierarchyChanged();
}
}
bool Component::isAlwaysOnTop() const throw()
{
return flags.alwaysOnTopFlag;
}
//==============================================================================
int Component::proportionOfWidth (const float proportion) const throw()
{
return roundDoubleToInt (proportion * bounds_.getWidth());
}
int Component::proportionOfHeight (const float proportion) const throw()
{
return roundDoubleToInt (proportion * bounds_.getHeight());
}
int Component::getParentWidth() const throw()
{
return (parentComponent_ != 0) ? parentComponent_->getWidth()
: getParentMonitorArea().getWidth();
}
int Component::getParentHeight() const throw()
{
return (parentComponent_ != 0) ? parentComponent_->getHeight()
: getParentMonitorArea().getHeight();
}
int Component::getScreenX() const throw()
{
return (parentComponent_ != 0) ? parentComponent_->getScreenX() + getX()
: (flags.hasHeavyweightPeerFlag ? getPeer()->getScreenX()
: getX());
}
int Component::getScreenY() const throw()
{
return (parentComponent_ != 0) ? parentComponent_->getScreenY() + getY()
: (flags.hasHeavyweightPeerFlag ? getPeer()->getScreenY()
: getY());
}
void Component::relativePositionToGlobal (int& x, int& y) const throw()
{
const Component* c = this;
do
{
if (c->flags.hasHeavyweightPeerFlag)
{
c->getPeer()->relativePositionToGlobal (x, y);
break;
}
x += c->getX();
y += c->getY();
c = c->parentComponent_;
}
while (c != 0);
}
void Component::globalPositionToRelative (int& x, int& y) const throw()
{
if (flags.hasHeavyweightPeerFlag)
{
getPeer()->globalPositionToRelative (x, y);
}
else
{
if (parentComponent_ != 0)
parentComponent_->globalPositionToRelative (x, y);
x -= getX();
y -= getY();
}
}
void Component::relativePositionToOtherComponent (const Component* const targetComponent, int& x, int& y) const throw()
{
if (targetComponent != 0)
{
const Component* c = this;
do
{
if (c == targetComponent)
return;
if (c->flags.hasHeavyweightPeerFlag)
{
c->getPeer()->relativePositionToGlobal (x, y);
break;
}
x += c->getX();
y += c->getY();
c = c->parentComponent_;
}
while (c != 0);
targetComponent->globalPositionToRelative (x, y);
}
}
//==============================================================================
void Component::setBounds (int x, int y, int w, int h)
{
// if component methods are being called from threads other than the message
// thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
checkMessageManagerIsLocked
if (w < 0) w = 0;
if (h < 0) h = 0;
const bool wasResized = (getWidth() != w || getHeight() != h);
const bool wasMoved = (getX() != x || getY() != y);
if (wasMoved || wasResized)
{
if (flags.visibleFlag)
{
// send a fake mouse move to trigger enter/exit messages if needed..
sendFakeMouseMove();
if (! isOnDesktop())
repaintParent();
}
bounds_.setBounds (x, y, w, h);
if (wasResized)
repaint();
else if (! isOnDesktop())
repaintParent();
if (flags.hasHeavyweightPeerFlag)
{
ComponentPeer* const peer = getPeer();
if (peer != 0)
{
if (wasMoved && wasResized)
peer->setBounds (getX(), getY(), getWidth(), getHeight(), false);
else if (wasMoved)
peer->setPosition (getX(), getY());
else if (wasResized)
peer->setSize (getWidth(), getHeight());
}
}
sendMovedResizedMessages (wasMoved, wasResized);
}
}
void Component::sendMovedResizedMessages (const bool wasMoved, const bool wasResized)
{
JUCE_TRY
{
if (wasMoved)
moved();
if (wasResized)
{
resized();
for (int i = childComponentList_.size(); --i >= 0;)
{
childComponentList_.getUnchecked(i)->parentSizeChanged();
i = jmin (i, childComponentList_.size());
}
}
if (parentComponent_ != 0)
parentComponent_->childBoundsChanged (this);
if (componentListeners_ != 0)
{
const ComponentDeletionWatcher deletionChecker (this);
for (int i = componentListeners_->size(); --i >= 0;)
{
((ComponentListener*) componentListeners_->getUnchecked (i))
->componentMovedOrResized (*this, wasMoved, wasResized);
if (deletionChecker.hasBeenDeleted())
return;
i = jmin (i, componentListeners_->size());
}
}
}
JUCE_CATCH_EXCEPTION
}
void Component::setSize (const int w, const int h)
{
setBounds (getX(), getY(), w, h);
}
void Component::setTopLeftPosition (const int x, const int y)
{
setBounds (x, y, getWidth(), getHeight());
}
void Component::setTopRightPosition (const int x, const int y)
{
setTopLeftPosition (x - getWidth(), y);
}
void Component::setBounds (const Rectangle& r)
{
setBounds (r.getX(),
r.getY(),
r.getWidth(),
r.getHeight());
}
void Component::setBoundsRelative (const float x, const float y,
const float w, const float h)
{
const int pw = getParentWidth();
const int ph = getParentHeight();
setBounds (roundFloatToInt (x * pw),
roundFloatToInt (y * ph),
roundFloatToInt (w * pw),
roundFloatToInt (h * ph));
}
void Component::setCentrePosition (const int x, const int y)
{
setTopLeftPosition (x - getWidth() / 2,
y - getHeight() / 2);
}
void Component::setCentreRelative (const float x, const float y)
{
setCentrePosition (roundFloatToInt (getParentWidth() * x),
roundFloatToInt (getParentHeight() * y));
}
void Component::centreWithSize (const int width, const int height)
{
setBounds ((getParentWidth() - width) / 2,
(getParentHeight() - height) / 2,
width,
height);
}
void Component::setBoundsInset (const BorderSize& borders)
{
setBounds (borders.getLeft(),
borders.getTop(),
getParentWidth() - (borders.getLeftAndRight()),
getParentHeight() - (borders.getTopAndBottom()));
}
void Component::setBoundsToFit (int x, int y, int width, int height,
const Justification& justification,
const bool onlyReduceInSize)
{
// it's no good calling this method unless both the component and
// target rectangle have a finite size.
jassert (getWidth() > 0 && getHeight() > 0 && width > 0 && height > 0);
if (getWidth() > 0 && getHeight() > 0
&& width > 0 && height > 0)
{
int newW, newH;
if (onlyReduceInSize && getWidth() <= width && getHeight() <= height)
{
newW = getWidth();
newH = getHeight();
}
else
{
const double imageRatio = getHeight() / (double) getWidth();
const double targetRatio = height / (double) width;
if (imageRatio <= targetRatio)
{
newW = width;
newH = jmin (height, roundDoubleToInt (newW * imageRatio));
}
else
{
newH = height;
newW = jmin (width, roundDoubleToInt (newH / imageRatio));
}
}
if (newW > 0 && newH > 0)
{
int newX, newY;
justification.applyToRectangle (newX, newY, newW, newH,
x, y, width, height);
setBounds (newX, newY, newW, newH);
}
}
}
//==============================================================================
bool Component::hitTest (int x, int y)
{
if (! flags.ignoresMouseClicksFlag)
return true;
if (flags.allowChildMouseClicksFlag)
{
for (int i = getNumChildComponents(); --i >= 0;)
{
Component* const c = getChildComponent (i);
if (c->isVisible()
&& c->bounds_.contains (x, y)
&& c->hitTest (x - c->getX(),
y - c->getY()))
{
return true;
}
}
}
return false;
}
void Component::setInterceptsMouseClicks (const bool allowClicks,
const bool allowClicksOnChildComponents) throw()
{
flags.ignoresMouseClicksFlag = ! allowClicks;
flags.allowChildMouseClicksFlag = allowClicksOnChildComponents;
}
void Component::getInterceptsMouseClicks (bool& allowsClicksOnThisComponent,
bool& allowsClicksOnChildComponents) const throw()
{
allowsClicksOnThisComponent = ! flags.ignoresMouseClicksFlag;
allowsClicksOnChildComponents = flags.allowChildMouseClicksFlag;
}
bool Component::contains (const int x, const int y)
{
if (x >= 0 && y >= 0 && x < getWidth() && y < getHeight()
&& hitTest (x, y))
{
if (parentComponent_ != 0)
{
return parentComponent_->contains (x + getX(),
y + getY());
}
else if (flags.hasHeavyweightPeerFlag)
{
const ComponentPeer* const peer = getPeer();
if (peer != 0)
return peer->contains (x, y, true);
}
}
return false;
}
bool Component::reallyContains (int x, int y, const bool returnTrueIfWithinAChild)
{
if (! contains (x, y))
return false;
Component* p = this;
while (p->parentComponent_ != 0)
{
x += p->getX();
y += p->getY();
p = p->parentComponent_;
}
const Component* const c = p->getComponentAt (x, y);
return (c == this) || (returnTrueIfWithinAChild && isParentOf (c));
}
Component* Component::getComponentAt (const int x, const int y)
{
if (flags.visibleFlag
&& x >= 0 && y >= 0 && x < getWidth() && y < getHeight()
&& hitTest (x, y))
{
for (int i = childComponentList_.size(); --i >= 0;)
{
Component* const child = childComponentList_.getUnchecked(i);
Component* const c = child->getComponentAt (x - child->getX(),
y - child->getY());
if (c != 0)
return c;
}
return this;
}
return 0;
}
//==============================================================================
void Component::addChildComponent (Component* const child, int zOrder)
{
// if component methods are being called from threads other than the message
// thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
checkMessageManagerIsLocked
if (child != 0 && child->parentComponent_ != this)
{
if (child->parentComponent_ != 0)
child->parentComponent_->removeChildComponent (child);
else
child->removeFromDesktop();
child->parentComponent_ = this;
if (child->isVisible())
child->repaintParent();
if (! child->isAlwaysOnTop())
{
if (zOrder < 0)
zOrder = childComponentList_.size();
while (zOrder > 0)
{
if (! childComponentList_.getUnchecked (zOrder - 1)->isAlwaysOnTop())
break;
--zOrder;
}
}
childComponentList_.insert (zOrder, child);
child->internalHierarchyChanged();
internalChildrenChanged();
}
}
void Component::addAndMakeVisible (Component* const child, int zOrder)
{
if (child != 0)
{
child->setVisible (true);
addChildComponent (child, zOrder);
}
}
void Component::removeChildComponent (Component* const child)
{
removeChildComponent (childComponentList_.indexOf (child));
}
Component* Component::removeChildComponent (const int index)
{
// if component methods are being called from threads other than the message
// thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
checkMessageManagerIsLocked
Component* const child = childComponentList_ [index];
if (child != 0)
{
sendFakeMouseMove();
child->repaintParent();
childComponentList_.remove (index);
child->parentComponent_ = 0;
JUCE_TRY
{
if ((currentlyFocusedComponent == child)
|| child->isParentOf (currentlyFocusedComponent))
{
// get rid first to force the grabKeyboardFocus to change to us.
giveAwayFocus();
grabKeyboardFocus();
}
}
#if JUCE_CATCH_UNHANDLED_EXCEPTIONS
catch (const std::exception& e)
{
currentlyFocusedComponent = 0;
Desktop::getInstance().triggerFocusCallback();
JUCEApplication::sendUnhandledException (&e, __FILE__, __LINE__);
}
catch (...)
{
currentlyFocusedComponent = 0;
Desktop::getInstance().triggerFocusCallback();
JUCEApplication::sendUnhandledException (0, __FILE__, __LINE__);
}
#endif
child->internalHierarchyChanged();
internalChildrenChanged();
}
return child;
}
//==============================================================================
void Component::removeAllChildren()
{
for (int i = childComponentList_.size(); --i >= 0;)
removeChildComponent (i);
}
void Component::deleteAllChildren()
{
for (int i = childComponentList_.size(); --i >= 0;)
delete (removeChildComponent (i));
}
//==============================================================================
int Component::getNumChildComponents() const throw()
{
return childComponentList_.size();
}
Component* Component::getChildComponent (const int index) const throw()
{
return childComponentList_ [index];
}
int Component::getIndexOfChildComponent (const Component* const child) const throw()
{
return childComponentList_.indexOf (const_cast <Component*> (child));
}
Component* Component::getTopLevelComponent() const throw()
{
const Component* comp = this;
while (comp->parentComponent_ != 0)
comp = comp->parentComponent_;
return (Component*) comp;
}
bool Component::isParentOf (const Component* possibleChild) const throw()
{
while (possibleChild->isValidComponent())
{
possibleChild = possibleChild->parentComponent_;
if (possibleChild == this)
return true;
}
return false;
}
//==============================================================================
void Component::parentHierarchyChanged()
{
}
void Component::childrenChanged()
{
}
void Component::internalChildrenChanged()
{
const ComponentDeletionWatcher deletionChecker (this);
const bool hasListeners = componentListeners_ != 0;
childrenChanged();
if (hasListeners)
{
if (deletionChecker.hasBeenDeleted())
return;
for (int i = componentListeners_->size(); --i >= 0;)
{
((ComponentListener*) componentListeners_->getUnchecked (i))
->componentChildrenChanged (*this);
if (deletionChecker.hasBeenDeleted())
return;
i = jmin (i, componentListeners_->size());
}
}
}
void Component::internalHierarchyChanged()
{
parentHierarchyChanged();
const ComponentDeletionWatcher deletionChecker (this);
if (componentListeners_ != 0)
{
for (int i = componentListeners_->size(); --i >= 0;)
{
((ComponentListener*) componentListeners_->getUnchecked (i))
->componentParentHierarchyChanged (*this);
if (deletionChecker.hasBeenDeleted())
return;
i = jmin (i, componentListeners_->size());
}
}
for (int i = childComponentList_.size(); --i >= 0;)
{
childComponentList_.getUnchecked (i)->internalHierarchyChanged();
// you really shouldn't delete the parent component during a callback telling you
// that it's changed..
jassert (! deletionChecker.hasBeenDeleted());
if (deletionChecker.hasBeenDeleted())
return;
i = jmin (i, childComponentList_.size());
}
}
//==============================================================================
void* Component::runModalLoopCallback (void* userData)
{
return (void*) (pointer_sized_int) ((Component*) userData)->runModalLoop();
}
int Component::runModalLoop()
{
if (! MessageManager::getInstance()->isThisTheMessageThread())
{
// use a callback so this can be called from non-gui threads
return (int) (pointer_sized_int)
MessageManager::getInstance()
->callFunctionOnMessageThread (&runModalLoopCallback, (void*) this);
}
Component* const prevFocused = getCurrentlyFocusedComponent();
ComponentDeletionWatcher* deletionChecker = 0;
if (prevFocused != 0)
deletionChecker = new ComponentDeletionWatcher (prevFocused);
if (! isCurrentlyModal())
enterModalState();
JUCE_TRY
{
while (flags.currentlyModalFlag && flags.visibleFlag)
{
if (! MessageManager::getInstance()->dispatchNextMessage())
break;
// check whether this component was deleted during the last message
if (! isValidMessageListener())
break;
}
}
#if JUCE_CATCH_UNHANDLED_EXCEPTIONS
catch (const std::exception& e)
{
JUCEApplication::sendUnhandledException (&e, __FILE__, __LINE__);
return 0;
}
catch (...)
{
JUCEApplication::sendUnhandledException (0, __FILE__, __LINE__);
return 0;
}
#endif
const int modalIndex = modalComponentReturnValueKeys.indexOf (this);
int returnValue = 0;
if (modalIndex >= 0)
{
modalComponentReturnValueKeys.remove (modalIndex);
returnValue = modalReturnValues.remove (modalIndex);
}
modalComponentStack.removeValue (this);
if (deletionChecker != 0)
{
if (! deletionChecker->hasBeenDeleted())
prevFocused->grabKeyboardFocus();
delete deletionChecker;
}
return returnValue;
}
void Component::enterModalState (const bool takeKeyboardFocus)
{
// if component methods are being called from threads other than the message
// thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
checkMessageManagerIsLocked
// Check for an attempt to make a component modal when it already is!
// This can cause nasty problems..
jassert (! flags.currentlyModalFlag);
if (! isCurrentlyModal())
{
modalComponentStack.add (this);
modalComponentReturnValueKeys.add (this);
modalReturnValues.add (0);
flags.currentlyModalFlag = true;
setVisible (true);
if (takeKeyboardFocus)
grabKeyboardFocus();
}
}
void Component::exitModalState (const int returnValue)
{
if (isCurrentlyModal())
{
if (MessageManager::getInstance()->isThisTheMessageThread())
{
const int modalIndex = modalComponentReturnValueKeys.indexOf (this);
if (modalIndex >= 0)
{
modalReturnValues.set (modalIndex, returnValue);
}
else
{
modalComponentReturnValueKeys.add (this);
modalReturnValues.add (returnValue);
}
modalComponentStack.removeValue (this);
flags.currentlyModalFlag = false;
}
else
{
postMessage (new Message (exitModalStateMessage, returnValue, 0, 0));
}
}
}
bool Component::isCurrentlyModal() const throw()
{
return flags.currentlyModalFlag
&& getCurrentlyModalComponent() == this;
}
bool Component::isCurrentlyBlockedByAnotherModalComponent() const throw()
{
Component* const mc = getCurrentlyModalComponent();
return mc != 0
&& mc != this
&& (! mc->isParentOf (this))
&& ! mc->canModalEventBeSentToComponent (this);
}
Component* Component::getCurrentlyModalComponent() throw()
{
Component* const c = (Component*) modalComponentStack.getLast();
return c->isValidComponent() ? c : 0;
}
//==============================================================================
void Component::setBroughtToFrontOnMouseClick (const bool shouldBeBroughtToFront) throw()
{
flags.bringToFrontOnClickFlag = shouldBeBroughtToFront;
}
bool Component::isBroughtToFrontOnMouseClick() const throw()
{
return flags.bringToFrontOnClickFlag;
}
//==============================================================================
void Component::setMouseCursor (const MouseCursor& cursor) throw()
{
cursor_ = cursor;
if (flags.visibleFlag)
{
int mx, my;
getMouseXYRelative (mx, my);
if (flags.draggingFlag || reallyContains (mx, my, false))
{
internalUpdateMouseCursor (false);
}
}
}
const MouseCursor Component::getMouseCursor()
{
return cursor_;
}
void Component::updateMouseCursor() const throw()
{
sendFakeMouseMove();
}
void Component::internalUpdateMouseCursor (const bool forcedUpdate) throw()
{
ComponentPeer* const peer = getPeer();
if (peer != 0)
{
MouseCursor mc (getMouseCursor());
if (isUnboundedMouseModeOn && (unboundedMouseOffsetX != 0
|| unboundedMouseOffsetY != 0
|| ! isCursorVisibleUntilOffscreen))
{
mc = MouseCursor::NoCursor;
}
static void* currentCursorHandle = 0;
if (forcedUpdate || mc.getHandle() != currentCursorHandle)
{
currentCursorHandle = mc.getHandle();
mc.showInWindow (peer);
}
}
}
//==============================================================================
void Component::setRepaintsOnMouseActivity (const bool shouldRepaint) throw()
{
flags.repaintOnMouseActivityFlag = shouldRepaint;
}
//==============================================================================
void Component::repaintParent() throw()
{
if (flags.visibleFlag)
internalRepaint (0, 0, getWidth(), getHeight());
}
void Component::repaint() throw()
{
repaint (0, 0, getWidth(), getHeight());
}
void Component::repaint (const int x, const int y,
const int w, const int h) throw()
{
deleteAndZero (bufferedImage_);
if (flags.visibleFlag)
internalRepaint (x, y, w, h);
}
void Component::internalRepaint (int x, int y, int w, int h)
{
// if component methods are being called from threads other than the message
// thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
checkMessageManagerIsLocked
if (x < 0)
{
w += x;
x = 0;
}
if (x + w > getWidth())
w = getWidth() - x;
if (w > 0)
{
if (y < 0)
{
h += y;
y = 0;
}
if (y + h > getHeight())
h = getHeight() - y;
if (h > 0)
{
if (parentComponent_ != 0)
{
x += getX();
y += getY();
if (parentComponent_->flags.visibleFlag)
parentComponent_->internalRepaint (x, y, w, h);
}
else if (flags.hasHeavyweightPeerFlag)
{
ComponentPeer* const peer = getPeer();
if (peer != 0)
peer->repaint (x, y, w, h);
}
}
}
}
//==============================================================================
void Component::paintEntireComponent (Graphics& originalContext)
{
jassert (! originalContext.isClipEmpty());
Graphics* g = &originalContext;
Image* effectImage = 0;
if (effect_ != 0)
{
effectImage = new Image (flags.opaqueFlag ? Image::RGB : Image::ARGB,
getWidth(), getHeight(),
! flags.opaqueFlag);
g = new Graphics (*effectImage);
}
g->saveState();
clipObscuredRegions (*g, g->getClipBounds(), 0, 0);
if (! g->isClipEmpty())
{
if (bufferedImage_ != 0)
{
g->setColour (Colours::black);
g->drawImageAt (bufferedImage_, 0, 0);
}
else
{
if (flags.bufferToImageFlag)
{
if (bufferedImage_ == 0)
{
bufferedImage_ = new Image (flags.opaqueFlag ? Image::RGB : Image::ARGB,
getWidth(), getHeight(), ! flags.opaqueFlag);
Graphics imG (*bufferedImage_);
paint (imG);
}
g->setColour (Colours::black);
g->drawImageAt (bufferedImage_, 0, 0);
}
else
{
paint (*g);
g->resetToDefaultState();
}
}
}
g->restoreState();
for (int i = 0; i < childComponentList_.size(); ++i)
{
Component* const child = childComponentList_.getUnchecked (i);
if (child->isVisible())
{
g->saveState();
if (g->reduceClipRegion (child->getX(), child->getY(),
child->getWidth(), child->getHeight()))
{
for (int j = i + 1; j < childComponentList_.size(); ++j)
{
const Component* const sibling = childComponentList_.getUnchecked (j);
if (sibling->flags.opaqueFlag && sibling->isVisible())
g->excludeClipRegion (sibling->getX(), sibling->getY(),
sibling->getWidth(), sibling->getHeight());
}
if (! g->isClipEmpty())
{
g->setOrigin (child->getX(), child->getY());
child->paintEntireComponent (*g);
}
}
g->restoreState();
}
}
JUCE_TRY
{
g->saveState();
paintOverChildren (*g);
g->restoreState();
}
JUCE_CATCH_EXCEPTION
if (effect_ != 0)
{
delete g;
effect_->applyEffect (*effectImage, originalContext);
delete effectImage;
}
}
//==============================================================================
Image* Component::createComponentSnapshot (const Rectangle& areaToGrab,
const bool clipImageToComponentBounds)
{
Rectangle r (areaToGrab);
if (clipImageToComponentBounds)
r = r.getIntersection (Rectangle (0, 0, getWidth(), getHeight()));
Image* const componentImage = new Image (flags.opaqueFlag ? Image::RGB : Image::ARGB,
jmax (1, r.getWidth()),
jmax (1, r.getHeight()),
true);
Graphics imageContext (*componentImage);
imageContext.setOrigin (-r.getX(),
-r.getY());
paintEntireComponent (imageContext);
return componentImage;
}
void Component::setComponentEffect (ImageEffectFilter* const effect)
{
if (effect_ != effect)
{
effect_ = effect;
repaint();
}
}
//==============================================================================
LookAndFeel& Component::getLookAndFeel() const throw()
{
const Component* c = this;
do
{
if (c->lookAndFeel_ != 0)
return *(c->lookAndFeel_);
c = c->parentComponent_;
}
while (c != 0);
return LookAndFeel::getDefaultLookAndFeel();
}
void Component::setLookAndFeel (LookAndFeel* const newLookAndFeel)
{
if (lookAndFeel_ != newLookAndFeel)
{
lookAndFeel_ = newLookAndFeel;
sendLookAndFeelChange();
}
}
void Component::lookAndFeelChanged()
{
}
void Component::sendLookAndFeelChange()
{
repaint();
lookAndFeelChanged();
// (it's not a great idea to do anything that would delete this component
// during the lookAndFeelChanged() callback)
jassert (isValidComponent());
const ComponentDeletionWatcher deletionChecker (this);
for (int i = childComponentList_.size(); --i >= 0;)
{
childComponentList_.getUnchecked (i)->sendLookAndFeelChange();
if (deletionChecker.hasBeenDeleted())
return;
i = jmin (i, childComponentList_.size());
}
}
const Colour Component::findColour (const int colourId, const bool inheritFromParent) const throw()
{
const String customColour (getComponentProperty (T("jucecol_") + String::toHexString (colourId),
inheritFromParent,
String::empty));
if (customColour.isNotEmpty())
return Colour (customColour.getIntValue());
return getLookAndFeel().findColour (colourId);
}
bool Component::isColourSpecified (const int colourId) const throw()
{
return getComponentProperty (T("jucecol_") + String::toHexString (colourId),
false,
String::empty).isNotEmpty();
}
void Component::removeColour (const int colourId)
{
if (isColourSpecified (colourId))
{
removeComponentProperty (T("jucecol_") + String::toHexString (colourId));
colourChanged();
}
}
void Component::setColour (const int colourId, const Colour& colour)
{
const String colourName (T("jucecol_") + String::toHexString (colourId));
const String customColour (getComponentProperty (colourName, false, String::empty));
if (customColour.isEmpty() || Colour (customColour.getIntValue()) != colour)
{
setComponentProperty (colourName, colour);
colourChanged();
}
}
void Component::copyAllExplicitColoursTo (Component& target) const throw()
{
if (propertySet_ != 0)
{
const StringPairArray& props = propertySet_->getAllProperties();
const StringArray& keys = props.getAllKeys();
for (int i = 0; i < keys.size(); ++i)
{
if (keys[i].startsWith (T("jucecol_")))
{
target.setComponentProperty (keys[i],
props.getAllValues() [i]);
}
}
target.colourChanged();
}
}
void Component::colourChanged()
{
}
//==============================================================================
const Rectangle Component::getUnclippedArea() const
{
int x = 0, y = 0, w = getWidth(), h = getHeight();
Component* p = parentComponent_;
int px = getX();
int py = getY();
while (p != 0)
{
if (! Rectangle::intersectRectangles (x, y, w, h, -px, -py, p->getWidth(), p->getHeight()))
return Rectangle();
px += p->getX();
py += p->getY();
p = p->parentComponent_;
}
return Rectangle (x, y, w, h);
}
void Component::clipObscuredRegions (Graphics& g, const Rectangle& clipRect,
const int deltaX, const int deltaY) const throw()
{
for (int i = childComponentList_.size(); --i >= 0;)
{
const Component* const c = childComponentList_.getUnchecked(i);
if (c->isVisible())
{
Rectangle newClip (clipRect.getIntersection (c->bounds_));
if (! newClip.isEmpty())
{
if (c->isOpaque())
{
g.excludeClipRegion (deltaX + newClip.getX(),
deltaY + newClip.getY(),
newClip.getWidth(),
newClip.getHeight());
}
else
{
newClip.translate (-c->getX(), -c->getY());
c->clipObscuredRegions (g, newClip,
c->getX() + deltaX,
c->getY() + deltaY);
}
}
}
}
}
void Component::getVisibleArea (RectangleList& result,
const bool includeSiblings) const
{
result.clear();
const Rectangle unclipped (getUnclippedArea());
if (! unclipped.isEmpty())
{
result.add (unclipped);
if (includeSiblings)
{
const Component* const c = getTopLevelComponent();
int x = 0, y = 0;
c->relativePositionToOtherComponent (this, x, y);
c->subtractObscuredRegions (result, x, y,
Rectangle (0, 0, c->getWidth(), c->getHeight()),
this);
}
subtractObscuredRegions (result, 0, 0, unclipped, 0);
result.consolidate();
}
}
void Component::subtractObscuredRegions (RectangleList& result,
const int deltaX,
const int deltaY,
const Rectangle& clipRect,
const Component* const compToAvoid) const throw()
{
for (int i = childComponentList_.size(); --i >= 0;)
{
const Component* const c = childComponentList_.getUnchecked(i);
if (c != compToAvoid && c->isVisible())
{
if (c->isOpaque())
{
Rectangle childBounds (c->bounds_.getIntersection (clipRect));
childBounds.translate (deltaX, deltaY);
result.subtract (childBounds);
}
else
{
Rectangle newClip (clipRect.getIntersection (c->bounds_));
newClip.translate (-c->getX(), -c->getY());
c->subtractObscuredRegions (result,
c->getX() + deltaX,
c->getY() + deltaY,
newClip,
compToAvoid);
}
}
}
}
//==============================================================================
void Component::mouseEnter (const MouseEvent&)
{
// base class does nothing
}
void Component::mouseExit (const MouseEvent&)
{
// base class does nothing
}
void Component::mouseDown (const MouseEvent&)
{
// base class does nothing
}
void Component::mouseUp (const MouseEvent&)
{
// base class does nothing
}
void Component::mouseDrag (const MouseEvent&)
{
// base class does nothing
}
void Component::mouseMove (const MouseEvent&)
{
// base class does nothing
}
void Component::mouseDoubleClick (const MouseEvent&)
{
// base class does nothing
}
void Component::mouseWheelMove (const MouseEvent& e, float wheelIncrementX, float wheelIncrementY)
{
// the base class just passes this event up to its parent..
if (parentComponent_ != 0)
parentComponent_->mouseWheelMove (e.getEventRelativeTo (parentComponent_),
wheelIncrementX, wheelIncrementY);
}
//==============================================================================
void Component::resized()
{
// base class does nothing
}
void Component::moved()
{
// base class does nothing
}
void Component::childBoundsChanged (Component*)
{
// base class does nothing
}
void Component::parentSizeChanged()
{
// base class does nothing
}
void Component::addComponentListener (ComponentListener* const newListener) throw()
{
if (componentListeners_ == 0)
componentListeners_ = new VoidArray (4);
componentListeners_->addIfNotAlreadyThere (newListener);
}
void Component::removeComponentListener (ComponentListener* const listenerToRemove) throw()
{
jassert (isValidComponent());
if (componentListeners_ != 0)
componentListeners_->removeValue (listenerToRemove);
}
//==============================================================================
void Component::inputAttemptWhenModal()
{
getTopLevelComponent()->toFront (true);
PlatformUtilities::beep();
}
bool Component::canModalEventBeSentToComponent (const Component*)
{
return false;
}
void Component::internalModalInputAttempt()
{
Component* const current = getCurrentlyModalComponent();
if (current != 0)
current->inputAttemptWhenModal();
}
//==============================================================================
void Component::paint (Graphics&)
{
// all painting is done in the subclasses
jassert (! isOpaque()); // if your component's opaque, you've gotta paint it!
}
void Component::paintOverChildren (Graphics&)
{
// all painting is done in the subclasses
}
//==============================================================================
void Component::handleMessage (const Message& message)
{
if (message.intParameter1 == exitModalStateMessage)
{
exitModalState (message.intParameter2);
}
else if (message.intParameter1 == customCommandMessage)
{
handleCommandMessage (message.intParameter2);
}
}
//==============================================================================
void Component::postCommandMessage (const int commandId) throw()
{
postMessage (new Message (customCommandMessage, commandId, 0, 0));
}
void Component::handleCommandMessage (int)
{
// used by subclasses
}
//==============================================================================
void Component::addMouseListener (MouseListener* const newListener,
const bool wantsEventsForAllNestedChildComponents) throw()
{
// if component methods are being called from threads other than the message
// thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
checkMessageManagerIsLocked
if (mouseListeners_ == 0)
mouseListeners_ = new VoidArray (4);
if (! mouseListeners_->contains (newListener))
{
if (wantsEventsForAllNestedChildComponents)
{
mouseListeners_->insert (0, newListener);
++numDeepMouseListeners;
}
else
{
mouseListeners_->add (newListener);
}
}
}
void Component::removeMouseListener (MouseListener* const listenerToRemove) throw()
{
// if component methods are being called from threads other than the message
// thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
checkMessageManagerIsLocked
if (mouseListeners_ != 0)
{
const int index = mouseListeners_->indexOf (listenerToRemove);
if (index >= 0)
{
if (index < numDeepMouseListeners)
--numDeepMouseListeners;
mouseListeners_->remove (index);
}
}
}
//==============================================================================
void Component::internalMouseEnter (int x, int y, int64 time)
{
if (isCurrentlyBlockedByAnotherModalComponent())
{
// if something else is modal, always just show a normal mouse cursor
if (componentUnderMouse == this)
{
ComponentPeer* const peer = getPeer();
if (peer != 0)
{
MouseCursor mc (MouseCursor::NormalCursor);
mc.showInWindow (peer);
}
}
return;
}
if (! flags.mouseInsideFlag)
{
flags.mouseInsideFlag = true;
flags.mouseOverFlag = true;
flags.draggingFlag = false;
if (isValidComponent())
{
const ComponentDeletionWatcher deletionChecker (this);
if (flags.repaintOnMouseActivityFlag)
repaint();
const MouseEvent me (x, y,
ModifierKeys::getCurrentModifiers(),
this,
Time (time),
x, y,
Time (time),
0, false);
mouseEnter (me);
if (deletionChecker.hasBeenDeleted())
return;
Desktop::getInstance().resetTimer();
for (int i = Desktop::getInstance().mouseListeners.size(); --i >= 0;)
{
((MouseListener*) Desktop::getInstance().mouseListeners[i])->mouseEnter (me);
if (deletionChecker.hasBeenDeleted())
return;
i = jmin (i, Desktop::getInstance().mouseListeners.size());
}
if (mouseListeners_ != 0)
{
for (int i = mouseListeners_->size(); --i >= 0;)
{
((MouseListener*) mouseListeners_->getUnchecked(i))->mouseEnter (me);
if (deletionChecker.hasBeenDeleted())
return;
i = jmin (i, mouseListeners_->size());
}
}
const Component* p = parentComponent_;
while (p != 0)
{
const ComponentDeletionWatcher parentDeletionChecker (p);
for (int i = p->numDeepMouseListeners; --i >= 0;)
{
((MouseListener*) (p->mouseListeners_->getUnchecked(i)))->mouseEnter (me);
if (deletionChecker.hasBeenDeleted() || parentDeletionChecker.hasBeenDeleted())
return;
i = jmin (i, p->numDeepMouseListeners);
}
p = p->parentComponent_;
}
}
}
if (componentUnderMouse == this)
internalUpdateMouseCursor (true);
}
void Component::internalMouseExit (int x, int y, int64 time)
{
const ComponentDeletionWatcher deletionChecker (this);
if (flags.draggingFlag)
{
internalMouseUp (ModifierKeys::getCurrentModifiers().getRawFlags(), x, y, time);
if (deletionChecker.hasBeenDeleted())
return;
}
enableUnboundedMouseMovement (false);
if (flags.mouseInsideFlag || flags.mouseOverFlag)
{
flags.mouseInsideFlag = false;
flags.mouseOverFlag = false;
flags.draggingFlag = false;
if (flags.repaintOnMouseActivityFlag)
repaint();
const MouseEvent me (x, y,
ModifierKeys::getCurrentModifiers(),
this,
Time (time),
x, y,
Time (time),
0, false);
mouseExit (me);
if (deletionChecker.hasBeenDeleted())
return;
Desktop::getInstance().resetTimer();
for (int i = Desktop::getInstance().mouseListeners.size(); --i >= 0;)
{
((MouseListener*) Desktop::getInstance().mouseListeners[i])->mouseExit (me);
if (deletionChecker.hasBeenDeleted())
return;
i = jmin (i, Desktop::getInstance().mouseListeners.size());
}
if (mouseListeners_ != 0)
{
for (int i = mouseListeners_->size(); --i >= 0;)
{
((MouseListener*) mouseListeners_->getUnchecked (i))->mouseExit (me);
if (deletionChecker.hasBeenDeleted())
return;
i = jmin (i, mouseListeners_->size());
}
}
const Component* p = parentComponent_;
while (p != 0)
{
const ComponentDeletionWatcher parentDeletionChecker (p);
for (int i = p->numDeepMouseListeners; --i >= 0;)
{
((MouseListener*) (p->mouseListeners_->getUnchecked (i)))->mouseExit (me);
if (deletionChecker.hasBeenDeleted() || parentDeletionChecker.hasBeenDeleted())
return;
i = jmin (i, p->numDeepMouseListeners);
}
p = p->parentComponent_;
}
}
}
//==============================================================================
class InternalDragRepeater : public Timer
{
public:
InternalDragRepeater()
{}
~InternalDragRepeater()
{}
void timerCallback()
{
Component* const c = Component::getComponentUnderMouse();
if (c != 0 && c->isMouseButtonDown())
{
int x, y;
c->getMouseXYRelative (x, y);
// the offsets have been added on, so must be taken off before calling the
// drag.. otherwise they'll be added twice
x -= unboundedMouseOffsetX;
y -= unboundedMouseOffsetY;
c->internalMouseDrag (x, y, Time::currentTimeMillis());
}
}
juce_UseDebuggingNewOperator
};
static InternalDragRepeater* dragRepeater = 0;
void Component::beginDragAutoRepeat (const int interval)
{
if (interval > 0)
{
if (dragRepeater == 0)
dragRepeater = new InternalDragRepeater();
if (dragRepeater->getTimerInterval() != interval)
dragRepeater->startTimer (interval);
}
else
{
deleteAndZero (dragRepeater);
}
}
//==============================================================================
void Component::internalMouseDown (const int x, const int y)
{
const ComponentDeletionWatcher deletionChecker (this);
if (isCurrentlyBlockedByAnotherModalComponent())
{
internalModalInputAttempt();
if (deletionChecker.hasBeenDeleted())
return;
// If processing the input attempt has exited the modal loop, we'll allow the event
// to be delivered..
if (isCurrentlyBlockedByAnotherModalComponent())
{
// allow blocked mouse-events to go to global listeners..
const MouseEvent me (x, y,
ModifierKeys::getCurrentModifiers(),
this,
Time (juce_recentMouseDownTimes[0]),
x, y,
Time (juce_recentMouseDownTimes[0]),
countMouseClicks(),
false);
Desktop::getInstance().resetTimer();
for (int i = Desktop::getInstance().mouseListeners.size(); --i >= 0;)
{
((MouseListener*) Desktop::getInstance().mouseListeners[i])->mouseDown (me);
if (deletionChecker.hasBeenDeleted())
return;
i = jmin (i, Desktop::getInstance().mouseListeners.size());
}
return;
}
}
{
Component* c = this;
while (c != 0)
{
if (c->isBroughtToFrontOnMouseClick())
{
c->toFront (true);
if (deletionChecker.hasBeenDeleted())
return;
}
c = c->parentComponent_;
}
}
if (! flags.dontFocusOnMouseClickFlag)
grabFocusInternal (focusChangedByMouseClick);
if (! deletionChecker.hasBeenDeleted())
{
flags.draggingFlag = true;
flags.mouseOverFlag = true;
if (flags.repaintOnMouseActivityFlag)
repaint();
const MouseEvent me (x, y,
ModifierKeys::getCurrentModifiers(),
this,
Time (juce_recentMouseDownTimes[0]),
x, y,
Time (juce_recentMouseDownTimes[0]),
countMouseClicks(),
false);
mouseDown (me);
if (deletionChecker.hasBeenDeleted())
return;
Desktop::getInstance().resetTimer();
for (int i = Desktop::getInstance().mouseListeners.size(); --i >= 0;)
{
((MouseListener*) Desktop::getInstance().mouseListeners[i])->mouseDown (me);
if (deletionChecker.hasBeenDeleted())
return;
i = jmin (i, Desktop::getInstance().mouseListeners.size());
}
if (mouseListeners_ != 0)
{
for (int i = mouseListeners_->size(); --i >= 0;)
{
((MouseListener*) mouseListeners_->getUnchecked (i))->mouseDown (me);
if (deletionChecker.hasBeenDeleted())
return;
i = jmin (i, mouseListeners_->size());
}
}
const Component* p = parentComponent_;
while (p != 0)
{
const ComponentDeletionWatcher parentDeletionChecker (p);
for (int i = p->numDeepMouseListeners; --i >= 0;)
{
((MouseListener*) (p->mouseListeners_->getUnchecked (i)))->mouseDown (me);
if (deletionChecker.hasBeenDeleted() || parentDeletionChecker.hasBeenDeleted())
return;
i = jmin (i, p->numDeepMouseListeners);
}
p = p->parentComponent_;
}
}
}
//==============================================================================
void Component::internalMouseUp (const int oldModifiers, int x, int y, const int64 time)
{
if (isValidComponent() && flags.draggingFlag)
{
flags.draggingFlag = false;
deleteAndZero (dragRepeater);
x += unboundedMouseOffsetX;
y += unboundedMouseOffsetY;
juce_LastMousePosX = x;
juce_LastMousePosY = y;
relativePositionToGlobal (juce_LastMousePosX, juce_LastMousePosY);
const ComponentDeletionWatcher deletionChecker (this);
if (flags.repaintOnMouseActivityFlag)
repaint();
int mdx = juce_recentMouseDownX[0];
int mdy = juce_recentMouseDownY[0];
globalPositionToRelative (mdx, mdy);
const MouseEvent me (x, y,
oldModifiers,
this,
Time (time),
mdx, mdy,
Time (juce_recentMouseDownTimes [0]),
countMouseClicks(),
juce_MouseHasMovedSignificantlySincePressed
|| juce_recentMouseDownTimes[0] + 300 < time);
mouseUp (me);
if (deletionChecker.hasBeenDeleted())
return;
Desktop::getInstance().resetTimer();
for (int i = Desktop::getInstance().mouseListeners.size(); --i >= 0;)
{
((MouseListener*) Desktop::getInstance().mouseListeners[i])->mouseUp (me);
if (deletionChecker.hasBeenDeleted())
return;
i = jmin (i, Desktop::getInstance().mouseListeners.size());
}
if (mouseListeners_ != 0)
{
for (int i = mouseListeners_->size(); --i >= 0;)
{
((MouseListener*) mouseListeners_->getUnchecked (i))->mouseUp (me);
if (deletionChecker.hasBeenDeleted())
return;
i = jmin (i, mouseListeners_->size());
}
}
{
const Component* p = parentComponent_;
while (p != 0)
{
const ComponentDeletionWatcher parentDeletionChecker (p);
for (int i = p->numDeepMouseListeners; --i >= 0;)
{
((MouseListener*) (p->mouseListeners_->getUnchecked (i)))->mouseUp (me);
if (deletionChecker.hasBeenDeleted() || parentDeletionChecker.hasBeenDeleted())
return;
i = jmin (i, p->numDeepMouseListeners);
}
p = p->parentComponent_;
}
}
// check for double-click
if (me.getNumberOfClicks() >= 2)
{
const int numListeners = (mouseListeners_ != 0) ? mouseListeners_->size() : 0;
mouseDoubleClick (me);
int i;
for (i = Desktop::getInstance().mouseListeners.size(); --i >= 0;)
{
((MouseListener*) Desktop::getInstance().mouseListeners[i])->mouseDoubleClick (me);
if (deletionChecker.hasBeenDeleted())
return;
i = jmin (i, Desktop::getInstance().mouseListeners.size());
}
for (i = numListeners; --i >= 0;)
{
if (deletionChecker.hasBeenDeleted() || mouseListeners_ == 0)
return;
MouseListener* const ml = (MouseListener*)((*mouseListeners_)[i]);
if (ml != 0)
ml->mouseDoubleClick (me);
}
if (deletionChecker.hasBeenDeleted())
return;
const Component* p = parentComponent_;
while (p != 0)
{
const ComponentDeletionWatcher parentDeletionChecker (p);
for (i = p->numDeepMouseListeners; --i >= 0;)
{
((MouseListener*) (p->mouseListeners_->getUnchecked (i)))->mouseDoubleClick (me);
if (deletionChecker.hasBeenDeleted() || parentDeletionChecker.hasBeenDeleted())
return;
i = jmin (i, p->numDeepMouseListeners);
}
p = p->parentComponent_;
}
}
}
enableUnboundedMouseMovement (false);
}
void Component::internalMouseDrag (int x, int y, const int64 time)
{
if (isValidComponent() && flags.draggingFlag)
{
flags.mouseOverFlag = reallyContains (x, y, false);
x += unboundedMouseOffsetX;
y += unboundedMouseOffsetY;
juce_LastMousePosX = x;
juce_LastMousePosY = y;
relativePositionToGlobal (juce_LastMousePosX, juce_LastMousePosY);
juce_MouseHasMovedSignificantlySincePressed
= juce_MouseHasMovedSignificantlySincePressed
|| abs (juce_recentMouseDownX[0] - juce_LastMousePosX) >= 4
|| abs (juce_recentMouseDownY[0] - juce_LastMousePosY) >= 4;
const ComponentDeletionWatcher deletionChecker (this);
int mdx = juce_recentMouseDownX[0];
int mdy = juce_recentMouseDownY[0];
globalPositionToRelative (mdx, mdy);
const MouseEvent me (x, y,
ModifierKeys::getCurrentModifiers(),
this,
Time (time),
mdx, mdy,
Time (juce_recentMouseDownTimes[0]),
countMouseClicks(),
juce_MouseHasMovedSignificantlySincePressed
|| juce_recentMouseDownTimes[0] + 300 < time);
mouseDrag (me);
if (deletionChecker.hasBeenDeleted())
return;
Desktop::getInstance().resetTimer();
for (int i = Desktop::getInstance().mouseListeners.size(); --i >= 0;)
{
((MouseListener*) Desktop::getInstance().mouseListeners[i])->mouseDrag (me);
if (deletionChecker.hasBeenDeleted())
return;
i = jmin (i, Desktop::getInstance().mouseListeners.size());
}
if (mouseListeners_ != 0)
{
for (int i = mouseListeners_->size(); --i >= 0;)
{
((MouseListener*) mouseListeners_->getUnchecked (i))->mouseDrag (me);
if (deletionChecker.hasBeenDeleted())
return;
i = jmin (i, mouseListeners_->size());
}
}
const Component* p = parentComponent_;
while (p != 0)
{
const ComponentDeletionWatcher parentDeletionChecker (p);
for (int i = p->numDeepMouseListeners; --i >= 0;)
{
((MouseListener*) (p->mouseListeners_->getUnchecked (i)))->mouseDrag (me);
if (deletionChecker.hasBeenDeleted() || parentDeletionChecker.hasBeenDeleted())
return;
i = jmin (i, p->numDeepMouseListeners);
}
p = p->parentComponent_;
}
if (this == componentUnderMouse)
{
if (isUnboundedMouseModeOn)
{
Rectangle screenArea (getParentMonitorArea().expanded (-2, -2));
int mx, my;
Desktop::getMousePosition (mx, my);
if (! screenArea.contains (mx, my))
{
int deltaX = 0, deltaY = 0;
if (mx <= screenArea.getX() || mx >= screenArea.getRight())
deltaX = getScreenX() + getWidth() / 2 - mx;
if (my <= screenArea.getY() || my >= screenArea.getBottom())
deltaY = getScreenY() + getHeight() / 2 - my;
unboundedMouseOffsetX -= deltaX;
unboundedMouseOffsetY -= deltaY;
Desktop::setMousePosition (mx + deltaX,
my + deltaY);
}
else if (isCursorVisibleUntilOffscreen
&& (unboundedMouseOffsetX != 0 || unboundedMouseOffsetY != 0)
&& screenArea.contains (mx + unboundedMouseOffsetX,
my + unboundedMouseOffsetY))
{
mx += unboundedMouseOffsetX;
my += unboundedMouseOffsetY;
unboundedMouseOffsetX = 0;
unboundedMouseOffsetY = 0;
Desktop::setMousePosition (mx, my);
}
}
internalUpdateMouseCursor (false);
}
}
}
void Component::internalMouseMove (const int x, const int y, const int64 time)
{
const ComponentDeletionWatcher deletionChecker (this);
if (isValidComponent())
{
const MouseEvent me (x, y,
ModifierKeys::getCurrentModifiers(),
this,
Time (time),
x, y,
Time (time),
0, false);
if (isCurrentlyBlockedByAnotherModalComponent())
{
// allow blocked mouse-events to go to global listeners..
Desktop::getInstance().sendMouseMove();
}
else
{
if (this == componentUnderMouse)
internalUpdateMouseCursor (false);
flags.mouseOverFlag = true;
mouseMove (me);
if (deletionChecker.hasBeenDeleted())
return;
Desktop::getInstance().resetTimer();
for (int i = Desktop::getInstance().mouseListeners.size(); --i >= 0;)
{
((MouseListener*) Desktop::getInstance().mouseListeners[i])->mouseMove (me);
if (deletionChecker.hasBeenDeleted())
return;
i = jmin (i, Desktop::getInstance().mouseListeners.size());
}
if (mouseListeners_ != 0)
{
for (int i = mouseListeners_->size(); --i >= 0;)
{
((MouseListener*) mouseListeners_->getUnchecked (i))->mouseMove (me);
if (deletionChecker.hasBeenDeleted())
return;
i = jmin (i, mouseListeners_->size());
}
}
const Component* p = parentComponent_;
while (p != 0)
{
const ComponentDeletionWatcher parentDeletionChecker (p);
for (int i = p->numDeepMouseListeners; --i >= 0;)
{
((MouseListener*) (p->mouseListeners_->getUnchecked (i)))->mouseMove (me);
if (deletionChecker.hasBeenDeleted() || parentDeletionChecker.hasBeenDeleted())
return;
i = jmin (i, p->numDeepMouseListeners);
}
p = p->parentComponent_;
}
}
}
}
void Component::internalMouseWheel (const int intAmountX, const int intAmountY, const int64 time)
{
const ComponentDeletionWatcher deletionChecker (this);
const float wheelIncrementX = intAmountX * (1.0f / 256.0f);
const float wheelIncrementY = intAmountY * (1.0f / 256.0f);
int mx, my;
getMouseXYRelative (mx, my);
const MouseEvent me (mx, my,
ModifierKeys::getCurrentModifiers(),
this,
Time (time),
mx, my,
Time (time),
0, false);
if (isCurrentlyBlockedByAnotherModalComponent())
{
// allow blocked mouse-events to go to global listeners..
for (int i = Desktop::getInstance().mouseListeners.size(); --i >= 0;)
{
((MouseListener*) Desktop::getInstance().mouseListeners[i])->mouseWheelMove (me, wheelIncrementX, wheelIncrementY);
if (deletionChecker.hasBeenDeleted())
return;
i = jmin (i, Desktop::getInstance().mouseListeners.size());
}
}
else
{
mouseWheelMove (me, wheelIncrementX, wheelIncrementY);
if (deletionChecker.hasBeenDeleted())
return;
for (int i = Desktop::getInstance().mouseListeners.size(); --i >= 0;)
{
((MouseListener*) Desktop::getInstance().mouseListeners[i])->mouseWheelMove (me, wheelIncrementX, wheelIncrementY);
if (deletionChecker.hasBeenDeleted())
return;
i = jmin (i, Desktop::getInstance().mouseListeners.size());
}
if (mouseListeners_ != 0)
{
for (int i = mouseListeners_->size(); --i >= 0;)
{
((MouseListener*) mouseListeners_->getUnchecked (i))->mouseWheelMove (me, wheelIncrementX, wheelIncrementY);
if (deletionChecker.hasBeenDeleted())
return;
i = jmin (i, mouseListeners_->size());
}
}
const Component* p = parentComponent_;
while (p != 0)
{
const ComponentDeletionWatcher parentDeletionChecker (p);
for (int i = p->numDeepMouseListeners; --i >= 0;)
{
((MouseListener*) (p->mouseListeners_->getUnchecked (i)))->mouseWheelMove (me, wheelIncrementX, wheelIncrementY);
if (deletionChecker.hasBeenDeleted() || parentDeletionChecker.hasBeenDeleted())
return;
i = jmin (i, p->numDeepMouseListeners);
}
p = p->parentComponent_;
}
sendFakeMouseMove();
}
}
void Component::sendFakeMouseMove() const
{
ComponentPeer* const peer = getPeer();
if (peer != 0)
peer->sendFakeMouseMove();
}
void Component::broughtToFront()
{
}
void Component::internalBroughtToFront()
{
if (isValidComponent())
{
if (flags.hasHeavyweightPeerFlag)
Desktop::getInstance().componentBroughtToFront (this);
const ComponentDeletionWatcher deletionChecker (this);
broughtToFront();
if (deletionChecker.hasBeenDeleted())
return;
if (componentListeners_ != 0)
{
for (int i = componentListeners_->size(); --i >= 0;)
{
((ComponentListener*) componentListeners_->getUnchecked (i))
->componentBroughtToFront (*this);
if (deletionChecker.hasBeenDeleted())
return;
i = jmin (i, componentListeners_->size());
}
}
// when brought to the front and there's a modal component blocking this one,
// we need to bring the modal one to the front instead..
Component* const cm = getCurrentlyModalComponent();
if (cm != 0 && cm->getTopLevelComponent() != getTopLevelComponent())
{
cm->getTopLevelComponent()->toFront (false);
}
}
}
void Component::focusGained (FocusChangeType)
{
// base class does nothing
}
void Component::internalFocusGain (const FocusChangeType cause)
{
const ComponentDeletionWatcher deletionChecker (this);
focusGained (cause);
if (! deletionChecker.hasBeenDeleted())
internalChildFocusChange (cause);
}
void Component::focusLost (FocusChangeType)
{
// base class does nothing
}
void Component::internalFocusLoss (const FocusChangeType cause)
{
const ComponentDeletionWatcher deletionChecker (this);
focusLost (focusChangedDirectly);
if (! deletionChecker.hasBeenDeleted())
internalChildFocusChange (cause);
}
void Component::focusOfChildComponentChanged (FocusChangeType /*cause*/)
{
// base class does nothing
}
void Component::internalChildFocusChange (FocusChangeType cause)
{
const bool childIsNowFocused = hasKeyboardFocus (true);
if (flags.childCompFocusedFlag != childIsNowFocused)
{
flags.childCompFocusedFlag = childIsNowFocused;
const ComponentDeletionWatcher deletionChecker (this);
focusOfChildComponentChanged (cause);
if (deletionChecker.hasBeenDeleted())
return;
}
if (parentComponent_ != 0)
parentComponent_->internalChildFocusChange (cause);
}
//==============================================================================
bool Component::isEnabled() const throw()
{
return (! flags.isDisabledFlag)
&& (parentComponent_ == 0 || parentComponent_->isEnabled());
}
void Component::setEnabled (const bool shouldBeEnabled)
{
if (flags.isDisabledFlag == shouldBeEnabled)
{
flags.isDisabledFlag = ! shouldBeEnabled;
// if any parent components are disabled, setting our flag won't make a difference,
// so no need to send a change message
if (parentComponent_ == 0 || parentComponent_->isEnabled())
sendEnablementChangeMessage();
}
}
void Component::sendEnablementChangeMessage()
{
const ComponentDeletionWatcher deletionChecker (this);
enablementChanged();
if (deletionChecker.hasBeenDeleted())
return;
for (int i = getNumChildComponents(); --i >= 0;)
{
Component* const c = getChildComponent (i);
if (c != 0)
{
c->sendEnablementChangeMessage();
if (deletionChecker.hasBeenDeleted())
return;
}
}
}
void Component::enablementChanged()
{
}
//==============================================================================
void Component::setWantsKeyboardFocus (const bool wantsFocus) throw()
{
flags.wantsFocusFlag = wantsFocus;
}
void Component::setMouseClickGrabsKeyboardFocus (const bool shouldGrabFocus)
{
flags.dontFocusOnMouseClickFlag = ! shouldGrabFocus;
}
bool Component::getMouseClickGrabsKeyboardFocus() const throw()
{
return ! flags.dontFocusOnMouseClickFlag;
}
bool Component::getWantsKeyboardFocus() const throw()
{
return flags.wantsFocusFlag && ! flags.isDisabledFlag;
}
void Component::setFocusContainer (const bool isFocusContainer) throw()
{
flags.isFocusContainerFlag = isFocusContainer;
}
bool Component::isFocusContainer() const throw()
{
return flags.isFocusContainerFlag;
}
int Component::getExplicitFocusOrder() const throw()
{
return getComponentPropertyInt (T("_jexfo"), false, 0);
}
void Component::setExplicitFocusOrder (const int newFocusOrderIndex) throw()
{
setComponentProperty (T("_jexfo"), newFocusOrderIndex);
}
KeyboardFocusTraverser* Component::createFocusTraverser()
{
if (flags.isFocusContainerFlag || parentComponent_ == 0)
return new KeyboardFocusTraverser();
return parentComponent_->createFocusTraverser();
}
void Component::takeKeyboardFocus (FocusChangeType cause)
{
// give the focus to this component
if (currentlyFocusedComponent != this)
{
JUCE_TRY
{
// get the focus onto our desktop window
ComponentPeer* const peer = getPeer();
if (peer != 0)
{
const ComponentDeletionWatcher deletionChecker (this);
peer->grabFocus();
if (peer->isFocused() && currentlyFocusedComponent != this)
{
Component* const componentLosingFocus = currentlyFocusedComponent;
currentlyFocusedComponent = this;
Desktop::getInstance().triggerFocusCallback();
// call this after setting currentlyFocusedComponent so that the one that's
// losing it has a chance to see where focus is going
if (componentLosingFocus->isValidComponent())
componentLosingFocus->internalFocusLoss (cause);
if (currentlyFocusedComponent == this)
{
focusGained (cause);
if (! deletionChecker.hasBeenDeleted())
internalChildFocusChange (cause);
}
}
}
}
#if JUCE_CATCH_UNHANDLED_EXCEPTIONS
catch (const std::exception& e)
{
currentlyFocusedComponent = 0;
Desktop::getInstance().triggerFocusCallback();
JUCEApplication::sendUnhandledException (&e, __FILE__, __LINE__);
}
catch (...)
{
currentlyFocusedComponent = 0;
Desktop::getInstance().triggerFocusCallback();
JUCEApplication::sendUnhandledException (0, __FILE__, __LINE__);
}
#endif
}
}
void Component::grabFocusInternal (FocusChangeType cause, const bool canTryParent)
{
if (isShowing())
{
if (flags.wantsFocusFlag && (isEnabled() || parentComponent_ == 0))
{
takeKeyboardFocus (cause);
}
else
{
if (isParentOf (currentlyFocusedComponent)
&& currentlyFocusedComponent->isShowing())
{
// do nothing if the focused component is actually a child of ours..
}
else
{
// find the default child component..
KeyboardFocusTraverser* const traverser = createFocusTraverser();
if (traverser != 0)
{
Component* const defaultComp = traverser->getDefaultComponent (this);
delete traverser;
if (defaultComp != 0)
{
defaultComp->grabFocusInternal (cause, false);
return;
}
}
if (canTryParent && parentComponent_ != 0)
{
// if no children want it and we're allowed to try our parent comp,
// then pass up to parent, which will try our siblings.
parentComponent_->grabFocusInternal (cause, true);
}
}
}
}
}
void Component::grabKeyboardFocus()
{
// if component methods are being called from threads other than the message
// thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
checkMessageManagerIsLocked
grabFocusInternal (focusChangedDirectly);
}
void Component::moveKeyboardFocusToSibling (const bool moveToNext)
{
// if component methods are being called from threads other than the message
// thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
checkMessageManagerIsLocked
if (parentComponent_ != 0)
{
KeyboardFocusTraverser* const traverser = createFocusTraverser();
if (traverser != 0)
{
Component* const nextComp = moveToNext ? traverser->getNextComponent (this)
: traverser->getPreviousComponent (this);
delete traverser;
if (nextComp != 0)
{
if (nextComp->isCurrentlyBlockedByAnotherModalComponent())
{
const ComponentDeletionWatcher deletionChecker (nextComp);
internalModalInputAttempt();
if (deletionChecker.hasBeenDeleted()
|| nextComp->isCurrentlyBlockedByAnotherModalComponent())
return;
}
nextComp->grabFocusInternal (focusChangedByTabKey);
return;
}
}
parentComponent_->moveKeyboardFocusToSibling (moveToNext);
}
}
bool Component::hasKeyboardFocus (const bool trueIfChildIsFocused) const throw()
{
return (currentlyFocusedComponent == this)
|| (trueIfChildIsFocused && isParentOf (currentlyFocusedComponent));
}
Component* Component::getCurrentlyFocusedComponent() throw()
{
return currentlyFocusedComponent;
}
void Component::giveAwayFocus()
{
// use a copy so we can clear the value before the call
Component* const componentLosingFocus = currentlyFocusedComponent;
currentlyFocusedComponent = 0;
Desktop::getInstance().triggerFocusCallback();
if (componentLosingFocus->isValidComponent())
componentLosingFocus->internalFocusLoss (focusChangedDirectly);
}
//==============================================================================
bool Component::isMouseOver() const throw()
{
return flags.mouseOverFlag;
}
bool Component::isMouseButtonDown() const throw()
{
return flags.draggingFlag;
}
bool Component::isMouseOverOrDragging() const throw()
{
return flags.mouseOverFlag || flags.draggingFlag;
}
bool Component::isMouseButtonDownAnywhere() throw()
{
return ModifierKeys::getCurrentModifiers().isAnyMouseButtonDown();
}
void Component::getMouseXYRelative (int& mx, int& my) const throw()
{
Desktop::getMousePosition (mx, my);
globalPositionToRelative (mx, my);
mx += unboundedMouseOffsetX;
my += unboundedMouseOffsetY;
}
void Component::enableUnboundedMouseMovement (bool enable,
bool keepCursorVisibleUntilOffscreen) throw()
{
enable = enable && isMouseButtonDown();
isCursorVisibleUntilOffscreen = keepCursorVisibleUntilOffscreen;
if (enable != isUnboundedMouseModeOn)
{
if ((! enable) && ((! isCursorVisibleUntilOffscreen)
|| unboundedMouseOffsetX != 0
|| unboundedMouseOffsetY != 0))
{
// when released, return the mouse to within the component's bounds
int mx, my;
getMouseXYRelative (mx, my);
mx = jlimit (0, getWidth(), mx);
my = jlimit (0, getHeight(), my);
relativePositionToGlobal (mx, my);
Desktop::setMousePosition (mx, my);
}
isUnboundedMouseModeOn = enable;
unboundedMouseOffsetX = 0;
unboundedMouseOffsetY = 0;
internalUpdateMouseCursor (true);
}
}
Component* Component::getComponentUnderMouse() throw()
{
return componentUnderMouse;
}
//==============================================================================
const Rectangle Component::getParentMonitorArea() const throw()
{
int centreX = getWidth() / 2;
int centreY = getHeight() / 2;
relativePositionToGlobal (centreX, centreY);
return Desktop::getInstance().getMonitorAreaContaining (centreX, centreY);
}
//==============================================================================
void Component::addKeyListener (KeyListener* const newListener) throw()
{
if (keyListeners_ == 0)
keyListeners_ = new VoidArray (4);
keyListeners_->addIfNotAlreadyThere (newListener);
}
void Component::removeKeyListener (KeyListener* const listenerToRemove) throw()
{
if (keyListeners_ != 0)
keyListeners_->removeValue (listenerToRemove);
}
void Component::keyPressed (const KeyPress& key)
{
const ComponentDeletionWatcher deletionChecker (this);
if (key.isKeyCode (KeyPress::tabKey)
&& getCurrentlyFocusedComponent()->isValidComponent())
{
getCurrentlyFocusedComponent()->moveKeyboardFocusToSibling (! key.getModifiers().isShiftDown());
}
else if (parentComponent_ != 0)
{
parentComponent_->keyPressed (key);
}
if ((! deletionChecker.hasBeenDeleted()) && keyListeners_ != 0)
{
for (int i = keyListeners_->size(); --i >= 0;)
{
((KeyListener*) keyListeners_->getUnchecked(i))->keyPressed (key, this);
if (deletionChecker.hasBeenDeleted())
return;
i = jmin (i, keyListeners_->size());
}
}
}
void Component::keyStateChanged()
{
const ComponentDeletionWatcher deletionChecker (this);
if (parentComponent_ != 0)
parentComponent_->keyStateChanged();
if ((! deletionChecker.hasBeenDeleted()) && keyListeners_ != 0)
{
for (int i = keyListeners_->size(); --i >= 0;)
{
((KeyListener*) keyListeners_->getUnchecked(i))->keyStateChanged (this);
if (deletionChecker.hasBeenDeleted())
return;
i = jmin (i, keyListeners_->size());
}
}
}
bool Component::internalKeyPress (const int key, const juce_wchar textCharacter)
{
const KeyPress keyPress (key,
ModifierKeys::getCurrentModifiers().getRawFlags()
& ModifierKeys::allKeyboardModifiers,
textCharacter);
if (isCurrentlyBlockedByAnotherModalComponent())
{
Component* const currentModalComp = getCurrentlyModalComponent();
if (currentModalComp != 0)
currentModalComp->keyPressed (keyPress);
}
else
{
keyPressed (keyPress);
}
return true;
}
bool Component::internalKeyStateChanged()
{
keyStateChanged();
return true;
}
void Component::modifierKeysChanged (const ModifierKeys& modifiers)
{
if (parentComponent_ != 0)
parentComponent_->modifierKeysChanged (modifiers);
}
void Component::internalModifierKeysChanged()
{
sendFakeMouseMove();
modifierKeysChanged (ModifierKeys::getCurrentModifiers());
}
//==============================================================================
bool Component::filesDropped (const StringArray&, int, int)
{
// base class returns false to let the message get passed up to its parent
return false;
}
void Component::internalFilesDropped (const int x,
const int y,
const StringArray& files)
{
if (isCurrentlyBlockedByAnotherModalComponent())
{
internalModalInputAttempt();
if (isCurrentlyBlockedByAnotherModalComponent())
return;
}
Component* c = getComponentAt (x, y);
while (c->isValidComponent())
{
int rx = x, ry = y;
relativePositionToOtherComponent (c, rx, ry);
if (c->filesDropped (files, rx, ry))
break;
c = c->getParentComponent();
}
}
ComponentPeer* Component::getPeer() const throw()
{
if (flags.hasHeavyweightPeerFlag)
return ComponentPeer::getPeerFor (this);
else if (parentComponent_ != 0)
return parentComponent_->getPeer();
else
return 0;
}
//==============================================================================
const String Component::getComponentProperty (const String& keyName,
const bool useParentComponentIfNotFound,
const String& defaultReturnValue) const throw()
{
if (propertySet_ != 0 && ((! useParentComponentIfNotFound) || propertySet_->containsKey (keyName)))
return propertySet_->getValue (keyName, defaultReturnValue);
if (useParentComponentIfNotFound && (parentComponent_ != 0))
return parentComponent_->getComponentProperty (keyName, true, defaultReturnValue);
return defaultReturnValue;
}
int Component::getComponentPropertyInt (const String& keyName,
const bool useParentComponentIfNotFound,
const int defaultReturnValue) const throw()
{
if (propertySet_ != 0 && ((! useParentComponentIfNotFound) || propertySet_->containsKey (keyName)))
return propertySet_->getIntValue (keyName, defaultReturnValue);
if (useParentComponentIfNotFound && (parentComponent_ != 0))
return parentComponent_->getComponentPropertyInt (keyName, true, defaultReturnValue);
return defaultReturnValue;
}
double Component::getComponentPropertyDouble (const String& keyName,
const bool useParentComponentIfNotFound,
const double defaultReturnValue) const throw()
{
if (propertySet_ != 0 && ((! useParentComponentIfNotFound) || propertySet_->containsKey (keyName)))
return propertySet_->getDoubleValue (keyName, defaultReturnValue);
if (useParentComponentIfNotFound && (parentComponent_ != 0))
return parentComponent_->getComponentPropertyDouble (keyName, true, defaultReturnValue);
return defaultReturnValue;
}
bool Component::getComponentPropertyBool (const String& keyName,
const bool useParentComponentIfNotFound,
const bool defaultReturnValue) const throw()
{
if (propertySet_ != 0 && ((! useParentComponentIfNotFound) || propertySet_->containsKey (keyName)))
return propertySet_->getBoolValue (keyName, defaultReturnValue);
if (useParentComponentIfNotFound && (parentComponent_ != 0))
return parentComponent_->getComponentPropertyBool (keyName, true, defaultReturnValue);
return defaultReturnValue;
}
const Colour Component::getComponentPropertyColour (const String& keyName,
const bool useParentComponentIfNotFound,
const Colour& defaultReturnValue) const throw()
{
return Colour ((uint32) getComponentPropertyInt (keyName,
useParentComponentIfNotFound,
defaultReturnValue.getARGB()));
}
void Component::setComponentProperty (const String& keyName, const String& value) throw()
{
if (propertySet_ == 0)
propertySet_ = new PropertySet();
propertySet_->setValue (keyName, value);
}
void Component::setComponentProperty (const String& keyName, const int value) throw()
{
if (propertySet_ == 0)
propertySet_ = new PropertySet();
propertySet_->setValue (keyName, value);
}
void Component::setComponentProperty (const String& keyName, const double value) throw()
{
if (propertySet_ == 0)
propertySet_ = new PropertySet();
propertySet_->setValue (keyName, value);
}
void Component::setComponentProperty (const String& keyName, const bool value) throw()
{
if (propertySet_ == 0)
propertySet_ = new PropertySet();
propertySet_->setValue (keyName, value);
}
void Component::setComponentProperty (const String& keyName, const Colour& colour) throw()
{
setComponentProperty (keyName, (int) colour.getARGB());
}
void Component::removeComponentProperty (const String& keyName) throw()
{
if (propertySet_ != 0)
propertySet_->removeValue (keyName);
}
//==============================================================================
ComponentDeletionWatcher::ComponentDeletionWatcher (const Component* const componentToWatch_) throw()
: componentToWatch (componentToWatch_),
componentUID (componentToWatch_->getComponentUID())
{
// not possible to check on an already-deleted object..
jassert (componentToWatch_->isValidComponent());
}
ComponentDeletionWatcher::~ComponentDeletionWatcher() throw() {}
bool ComponentDeletionWatcher::hasBeenDeleted() const throw()
{
return ! (componentToWatch->isValidComponent()
&& componentToWatch->getComponentUID() == componentUID);
}
const Component* ComponentDeletionWatcher::getComponent() const throw()
{
return hasBeenDeleted() ? 0 : componentToWatch;
}
END_JUCE_NAMESPACE