diff --git a/src/juce_appframework/audio/devices/juce_MidiInput.h b/src/juce_appframework/audio/devices/juce_MidiInput.h index 28eb8f1ce7..d5be9356fb 100644 --- a/src/juce_appframework/audio/devices/juce_MidiInput.h +++ b/src/juce_appframework/audio/devices/juce_MidiInput.h @@ -149,6 +149,11 @@ public: */ const String getName() const throw() { return name; } + /** Allows you to set a custom name for the device, in case you don't like the name + it was given when created. + */ + void setName (const String& newName) throw() { name = newName; } + //============================================================================== /** Starts the device running. diff --git a/src/juce_appframework/audio/midi/juce_MidiFile.cpp b/src/juce_appframework/audio/midi/juce_MidiFile.cpp index 789b9f186f..94b2feb5df 100644 --- a/src/juce_appframework/audio/midi/juce_MidiFile.cpp +++ b/src/juce_appframework/audio/midi/juce_MidiFile.cpp @@ -272,7 +272,7 @@ void MidiFile::readNextTrack (const char* data, int size) while (size > 0) { int bytesUsed; - const int delay = MidiMessage::readVariableLengthVal ((const unsigned char*)data, bytesUsed); + const int delay = MidiMessage::readVariableLengthVal ((const uint8*) data, bytesUsed); data += bytesUsed; size -= bytesUsed; time += delay; diff --git a/src/juce_appframework/gui/components/juce_Component.cpp b/src/juce_appframework/gui/components/juce_Component.cpp index f3f5bfaa4e..cc1f9be3c1 100644 --- a/src/juce_appframework/gui/components/juce_Component.cpp +++ b/src/juce_appframework/gui/components/juce_Component.cpp @@ -1,3193 +1,3193 @@ -/* - ============================================================================== - - 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 modalComponentStack (4), modalComponentReturnValueKeys (4); -static Array 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 (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) -{ - if (componentListeners_ == 0) - componentListeners_ = new VoidArray (4); - - componentListeners_->addIfNotAlreadyThere (newListener); -} - -void Component::removeComponentListener (ComponentListener* const listenerToRemove) -{ - 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; -} - +/* + ============================================================================== + + 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 modalComponentStack (4), modalComponentReturnValueKeys (4); +static Array 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 (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) +{ + if (componentListeners_ == 0) + componentListeners_ = new VoidArray (4); + + componentListeners_->addIfNotAlreadyThere (newListener); +} + +void Component::removeComponentListener (ComponentListener* const listenerToRemove) +{ + 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); @@ -3197,534 +3197,534 @@ 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 + +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 diff --git a/src/juce_appframework/gui/graphics/imaging/image_file_formats/juce_GIFLoader.cpp b/src/juce_appframework/gui/graphics/imaging/image_file_formats/juce_GIFLoader.cpp index c6fb4c67b9..85df34a037 100644 --- a/src/juce_appframework/gui/graphics/imaging/image_file_formats/juce_GIFLoader.cpp +++ b/src/juce_appframework/gui/graphics/imaging/image_file_formats/juce_GIFLoader.cpp @@ -57,23 +57,22 @@ GIFLoader::GIFLoader (InputStream& in) clearCode = end_code = 0; int imageWidth, imageHeight; - unsigned char colourMap[256][4]; int transparent = -1; - if (!getSizeFromHeader (imageWidth, imageHeight)) + if (! getSizeFromHeader (imageWidth, imageHeight)) return; if ((imageWidth <= 0) || (imageHeight <= 0)) return; - unsigned char buf [64]; + unsigned char buf [16]; if (in.read (buf, 3) != 3) return; int numColours = 2 << (buf[0] & 7); if ((buf[0] & 0x80) != 0) - readColourMap (numColours, colourMap); + readPalette (numColours); for (;;) { @@ -81,15 +80,15 @@ GIFLoader::GIFLoader (InputStream& in) break; if (buf[0] == ';') - return; + break; if (buf[0] == '!') { if (input.read (buf, 1) != 1) - return; + break; if (processExtension (buf[0], transparent) < 0) - return; + break; continue; } @@ -98,22 +97,21 @@ GIFLoader::GIFLoader (InputStream& in) continue; if (input.read (buf, 9) != 9) - return; + break; - imageWidth = makeWord (buf[4], buf[5]); + imageWidth = makeWord (buf[4], buf[5]); imageHeight = makeWord (buf[6], buf[7]); numColours = 2 << (buf[8] & 7); if ((buf[8] & 0x80) != 0) - if (! readColourMap (numColours, colourMap)) - return; + if (! readPalette (numColours)) + break; image = new Image ((transparent >= 0) ? Image::ARGB : Image::RGB, imageWidth, imageHeight, (transparent >= 0)); - readImage (colourMap, - imageWidth, imageHeight, + readImage (imageWidth, imageHeight, (buf[8] & 0x40) != 0, transparent); @@ -121,7 +119,7 @@ GIFLoader::GIFLoader (InputStream& in) } } -GIFLoader::~GIFLoader() +GIFLoader::~GIFLoader() throw() { } @@ -131,8 +129,8 @@ bool GIFLoader::getSizeFromHeader (int& w, int& h) if (input.read (b, 6) == 6) { - if ((strncmp ("GIF87a", (char*)b, 6) == 0) - || (strncmp ("GIF89a", (char*)b, 6) == 0)) + if ((strncmp ("GIF87a", (char*) b, 6) == 0) + || (strncmp ("GIF89a", (char*) b, 6) == 0)) { if (input.read (b, 4) == 4) { @@ -146,7 +144,7 @@ bool GIFLoader::getSizeFromHeader (int& w, int& h) return false; } -bool GIFLoader::readColourMap (const int numCols, unsigned char colourBuffer[256][4]) +bool GIFLoader::readPalette (const int numCols) { unsigned char rgb[4]; @@ -154,19 +152,16 @@ bool GIFLoader::readColourMap (const int numCols, unsigned char colourBuffer[256 { input.read (rgb, 3); - if (colourBuffer) - { - colourBuffer [i][0] = rgb[0]; - colourBuffer [i][1] = rgb[1]; - colourBuffer [i][2] = rgb[2]; - colourBuffer [i][3] = 0xff; - } + palette [i][0] = rgb[0]; + palette [i][1] = rgb[1]; + palette [i][2] = rgb[2]; + palette [i][3] = 0xff; } return true; } -int GIFLoader::readDataBlock (unsigned char* dest) +int GIFLoader::readDataBlock (unsigned char* const dest) { unsigned char n; @@ -181,7 +176,7 @@ int GIFLoader::readDataBlock (unsigned char* dest) return -1; } -int GIFLoader::processExtension (int type, int& transparent) +int GIFLoader::processExtension (const int type, int& transparent) { unsigned char b [300]; int n = 0; @@ -205,7 +200,7 @@ int GIFLoader::processExtension (int type, int& transparent) return n; } -int GIFLoader::getCode (int codeSize, bool initialise) +int GIFLoader::getCode (const int codeSize, const bool initialise) { if (initialise) { @@ -247,7 +242,7 @@ int GIFLoader::getCode (int codeSize, bool initialise) return result; } -int GIFLoader::readLZWByte (bool initialise, int inputCodeSize) +int GIFLoader::readLZWByte (const bool initialise, const int inputCodeSize) { int code, incode, i; @@ -272,7 +267,8 @@ int GIFLoader::readLZWByte (bool initialise, int inputCodeSize) for (; i < maxGifCode; ++i) { - table[0][i] = table[1][0] = 0; + table[0][i] = 0; + table[1][i] = 0; } sp = stack; @@ -377,19 +373,16 @@ int GIFLoader::readLZWByte (bool initialise, int inputCodeSize) return code; } -bool GIFLoader::readImage (unsigned char palette[256][4], - int width, int height, - int interlace, int transparent) +bool GIFLoader::readImage (const int width, const int height, + const int interlace, const int transparent) { unsigned char c; - if (input.read (&c, 1) != 1) + if (input.read (&c, 1) != 1 + || readLZWByte (true, c) < 0) return false; - if (readLZWByte (true, c) < 0) - return false; - - if (transparent != -1) + if (transparent >= 0) { palette [transparent][0] = 0; palette [transparent][1] = 0; @@ -403,7 +396,7 @@ bool GIFLoader::readImage (unsigned char palette[256][4], int stride, pixelStride; uint8* const pixels = image->lockPixelDataReadWrite (0, 0, width, height, stride, pixelStride); uint8* p = pixels; - bool hasAlpha = image->hasAlphaChannel(); + const bool hasAlpha = image->hasAlphaChannel(); while ((index = readLZWByte (false, c)) >= 0) { @@ -488,9 +481,5 @@ bool GIFLoader::readImage (unsigned char palette[256][4], return true; } -Image* GIFLoader::getImage() -{ - return image; -} END_JUCE_NAMESPACE diff --git a/src/juce_appframework/gui/graphics/imaging/image_file_formats/juce_GIFLoader.h b/src/juce_appframework/gui/graphics/imaging/image_file_formats/juce_GIFLoader.h index afd1fd5be5..25d525818c 100644 --- a/src/juce_appframework/gui/graphics/imaging/image_file_formats/juce_GIFLoader.h +++ b/src/juce_appframework/gui/graphics/imaging/image_file_formats/juce_GIFLoader.h @@ -51,14 +51,15 @@ class GIFLoader { public: GIFLoader (InputStream& in); - ~GIFLoader(); + ~GIFLoader() throw(); - Image* getImage(); + Image* getImage() const throw() { return image; } private: Image* image; InputStream& input; - unsigned char buffer [300]; + uint8 buffer [300]; + uint8 palette [256][4]; bool dataBlockIsZero, fresh, finished; int currentBit, lastBit, lastByteIndex; int codeSize, setCodeSize; @@ -70,13 +71,12 @@ private: int *sp; bool getSizeFromHeader (int& width, int& height); - bool readColourMap (const int numCols, unsigned char buffer[256][4]); + bool readPalette (const int numCols); int readDataBlock (unsigned char* dest); int processExtension (int type, int& transparent); int readLZWByte (bool initialise, int input_code_size); int getCode (int code_size, bool initialise); - bool readImage (unsigned char palette[256][4], - int width, int height, + bool readImage (int width, int height, int interlace, int transparent); GIFLoader (const GIFLoader&);