From bcc7ad9a25ee8391ceec6b6ecf5b8945c7d67395 Mon Sep 17 00:00:00 2001 From: jules Date: Wed, 11 Jul 2007 16:35:57 +0000 Subject: [PATCH] --- .../juce_mac_Windowing.cpp | 6134 +++++++------- .../src/model/jucer_JucerDocument.cpp | 4 +- .../gui/components/controls/juce_Slider.cpp | 4 + .../gui/components/juce_Component.cpp | 7440 ++++++++--------- 4 files changed, 6795 insertions(+), 6787 deletions(-) diff --git a/build/macosx/platform_specific_code/juce_mac_Windowing.cpp b/build/macosx/platform_specific_code/juce_mac_Windowing.cpp index ee7a44c58f..587b27ac69 100644 --- a/build/macosx/platform_specific_code/juce_mac_Windowing.cpp +++ b/build/macosx/platform_specific_code/juce_mac_Windowing.cpp @@ -1,3065 +1,3069 @@ -/* - ============================================================================== - - 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 "../../../src/juce_core/basics/juce_StandardHeader.h" -#include -#include - -#if JUCE_OPENGL -#include -#endif - -BEGIN_JUCE_NAMESPACE - -#include "../../../src/juce_appframework/events/juce_Timer.h" -#include "../../../src/juce_appframework/application/juce_DeletedAtShutdown.h" -#include "../../../src/juce_appframework/events/juce_AsyncUpdater.h" -#include "../../../src/juce_appframework/events/juce_MessageManager.h" -#include "../../../src/juce_core/basics/juce_Singleton.h" -#include "../../../src/juce_core/basics/juce_Random.h" -#include "../../../src/juce_core/threads/juce_Process.h" -#include "../../../src/juce_appframework/application/juce_SystemClipboard.h" -#include "../../../src/juce_appframework/gui/components/keyboard/juce_KeyPress.h" -#include "../../../src/juce_appframework/gui/components/windows/juce_AlertWindow.h" -#include "../../../src/juce_appframework/gui/graphics/geometry/juce_RectangleList.h" -#include "../../../src/juce_appframework/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h" -#include "../../../src/juce_appframework/gui/components/juce_Desktop.h" -#include "../../../src/juce_appframework/gui/components/menus/juce_MenuBarModel.h" -#include "../../../src/juce_core/misc/juce_PlatformUtilities.h" -#include "../../../src/juce_appframework/application/juce_Application.h" -#include "../../../src/juce_appframework/gui/components/special/juce_OpenGLComponent.h" -#include "../../../src/juce_appframework/gui/components/mouse/juce_DragAndDropContainer.h" -#include "../../../src/juce_appframework/gui/components/keyboard/juce_KeyPressMappingSet.h" -#include "../../../src/juce_appframework/gui/graphics/imaging/juce_ImageFileFormat.h" - -#undef Point - -const WindowRegionCode windowRegionToUse = kWindowContentRgn; - -static HIObjectClassRef viewClassRef = 0; -static CFStringRef juceHiViewClassNameCFString = 0; -static ComponentPeer* juce_currentMouseTrackingPeer = 0; - - -//============================================================================== -static VoidArray keysCurrentlyDown; - -bool KeyPress::isKeyCurrentlyDown (int keyCode) -{ - if (keysCurrentlyDown.contains ((void*) keyCode)) - return true; - - if (keyCode >= 'A' && keyCode <= 'Z' - && keysCurrentlyDown.contains ((void*) (int) CharacterFunctions::toLowerCase ((tchar) keyCode))) - return true; - - if (keyCode >= 'a' && keyCode <= 'z' - && keysCurrentlyDown.contains ((void*) (int) CharacterFunctions::toUpperCase ((tchar) keyCode))) - return true; - - return false; -} - -//============================================================================== -static VoidArray minimisedWindows; - -static void setWindowMinimised (WindowRef ref, const bool isMinimised) -{ - if (isMinimised != minimisedWindows.contains (ref)) - CollapseWindow (ref, isMinimised); -} - -void juce_maximiseAllMinimisedWindows() -{ - const VoidArray minWin (minimisedWindows); - - for (int i = minWin.size(); --i >= 0;) - setWindowMinimised ((WindowRef) (minWin[i]), false); -} - -//============================================================================== -class HIViewComponentPeer; -static HIViewComponentPeer* currentlyFocusedPeer = 0; - - -//============================================================================== -static int currentModifiers = 0; - -static void updateModifiers (EventRef theEvent) -{ - currentModifiers &= ~ (ModifierKeys::shiftModifier | ModifierKeys::ctrlModifier - | ModifierKeys::altModifier | ModifierKeys::commandModifier); - - UInt32 m; - - if (theEvent != 0) - GetEventParameter (theEvent, kEventParamKeyModifiers, typeUInt32, 0, sizeof(m), 0, &m); - else - m = GetCurrentEventKeyModifiers(); - - if ((m & (shiftKey | rightShiftKey)) != 0) - currentModifiers |= ModifierKeys::shiftModifier; - - if ((m & (controlKey | rightControlKey)) != 0) - currentModifiers |= ModifierKeys::ctrlModifier; - - if ((m & (optionKey | rightOptionKey)) != 0) - currentModifiers |= ModifierKeys::altModifier; - - if ((m & cmdKey) != 0) - currentModifiers |= ModifierKeys::commandModifier; -} - -void ModifierKeys::updateCurrentModifiers() -{ - currentModifierFlags = currentModifiers; -} - -static int64 getEventTime (EventRef event) -{ - const int64 millis = (int64) (1000.0 * (event != 0 ? GetEventTime (event) - : GetCurrentEventTime())); - - static int64 offset = 0; - if (offset == 0) - offset = Time::currentTimeMillis() - millis; - - return offset + millis; -} - - -//============================================================================== -class MacBitmapImage : public Image -{ -public: - //============================================================================== - CGColorSpaceRef colourspace; - CGDataProviderRef provider; - - //============================================================================== - MacBitmapImage (const PixelFormat format_, - const int w, const int h, const bool clearImage) - : Image (format_, w, h) - { - jassert (format_ == RGB || format_ == ARGB); - - pixelStride = (format_ == RGB) ? 3 : 4; - - lineStride = (w * pixelStride + 3) & ~3; - const int imageSize = lineStride * h; - - if (clearImage) - imageData = (uint8*) juce_calloc (imageSize); - else - imageData = (uint8*) juce_malloc (imageSize); - - //colourspace = CGColorSpaceCreateWithName (kCGColorSpaceUserRGB); - - CMProfileRef prof; - CMGetSystemProfile (&prof); - colourspace = CGColorSpaceCreateWithPlatformColorSpace (prof); - - provider = CGDataProviderCreateWithData (0, imageData, h * lineStride, 0); - } - - MacBitmapImage::~MacBitmapImage() - { - CGDataProviderRelease (provider); - CGColorSpaceRelease (colourspace); - - juce_free (imageData); - imageData = 0; // to stop the base class freeing this - } - - void blitToContext (CGContextRef context, const float dx, const float dy) - { - CGImageRef tempImage = CGImageCreate (getWidth(), getHeight(), - 8, pixelStride << 3, lineStride, colourspace, -#if MACOS_10_3_OR_EARLIER || JUCE_BIG_ENDIAN - hasAlphaChannel() ? kCGImageAlphaPremultipliedFirst - : kCGImageAlphaNone, -#else - hasAlphaChannel() ? kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst - : kCGImageAlphaNone, -#endif - provider, 0, false, - kCGRenderingIntentDefault); - - HIRect r; - r.origin.x = dx; - r.origin.y = dy; - r.size.width = (float) getWidth(); - r.size.height = (float) getHeight(); - - HIViewDrawCGImage (context, &r, tempImage); - - CGImageRelease (tempImage); - } - - juce_UseDebuggingNewOperator -}; - - -//============================================================================== -class MouseCheckTimer : private Timer, - private DeletedAtShutdown -{ - HIViewComponentPeer* lastPeerUnderMouse; - int lastX, lastY; - -public: - MouseCheckTimer() - : lastX (0), - lastY (0) - { - lastPeerUnderMouse = 0; - resetMouseMoveChecker(); - } - - ~MouseCheckTimer() - { - clearSingletonInstance(); - } - - juce_DeclareSingleton_SingleThreaded_Minimal (MouseCheckTimer) - - bool hasEverHadAMouseMove; - - void moved (HIViewComponentPeer* const peer) - { - if (hasEverHadAMouseMove) - startTimer (200); - - lastPeerUnderMouse = peer; - } - - void resetMouseMoveChecker() - { - hasEverHadAMouseMove = false; - startTimer (1000 / 16); - } - - void timerCallback(); -}; - -juce_ImplementSingleton_SingleThreaded (MouseCheckTimer) - -//============================================================================== -#if JUCE_QUICKTIME -extern void OfferMouseClickToQuickTime (WindowRef window, ::Point where, long when, long modifiers, - Component* topLevelComp); -#endif - - -//============================================================================== -class HIViewComponentPeer : public ComponentPeer, - private Timer -{ -public: - //============================================================================== - HIViewComponentPeer (Component* const component, - const int windowStyleFlags, - HIViewRef viewToAttachTo) - : ComponentPeer (component, windowStyleFlags), - fullScreen (false), - isCompositingWindow (false), - windowRef (0), - viewRef (0) - { - repainter = new RepaintManager (this); - - eventHandlerRef = 0; - - if (viewToAttachTo != 0) - { - isSharedWindow = true; - } - else - { - isSharedWindow = false; - - WindowRef newWindow = createNewWindow (windowStyleFlags); - - GetRootControl (newWindow, (ControlRef*) &viewToAttachTo); - jassert (viewToAttachTo != 0); - - HIViewRef growBox = 0; - HIViewFindByID (HIViewGetRoot (newWindow), kHIViewWindowGrowBoxID, &growBox); - - if (growBox != 0) - HIGrowBoxViewSetTransparent (growBox, true); - } - - createNewHIView(); - - HIViewAddSubview (viewToAttachTo, viewRef); - HIViewSetVisible (viewRef, component->isVisible()); - - setTitle (component->getName()); - - if (component->isVisible() && ! isSharedWindow) - { - ShowWindow (windowRef); - ActivateWindow (windowRef, component->getWantsKeyboardFocus()); - } - } - - ~HIViewComponentPeer() - { - minimisedWindows.removeValue (windowRef); - - if (IsValidWindowPtr (windowRef)) - { - if (! isSharedWindow) - { - CFRelease (viewRef); - viewRef = 0; - - DisposeWindow (windowRef); - } - else - { - if (eventHandlerRef != 0) - RemoveEventHandler (eventHandlerRef); - - CFRelease (viewRef); - viewRef = 0; - } - - windowRef = 0; - } - - if (currentlyFocusedPeer == this) - currentlyFocusedPeer = 0; - - delete repainter; - } - - //============================================================================== - void* getNativeHandle() const - { - return windowRef; - } - - void setVisible (bool shouldBeVisible) - { - HIViewSetVisible (viewRef, shouldBeVisible); - - if ((! isSharedWindow) && IsValidWindowPtr (windowRef)) - { - if (shouldBeVisible) - ShowWindow (windowRef); - else - HideWindow (windowRef); - - resizeViewToFitWindow(); - - // If nothing else is focused, then grab the focus too - if (shouldBeVisible - && Component::getCurrentlyFocusedComponent() == 0 - && Process::isForegroundProcess()) - { - component->toFront (true); - } - } - } - - void setTitle (const String& title) - { - if ((! isSharedWindow) && IsValidWindowPtr (windowRef)) - { - CFStringRef t = PlatformUtilities::juceStringToCFString (title); - SetWindowTitleWithCFString (windowRef, t); - CFRelease (t); - } - } - - void setPosition (int x, int y) - { - if (isSharedWindow) - { - HIViewPlaceInSuperviewAt (viewRef, x, y); - } - else if (IsValidWindowPtr (windowRef)) - { - Rect r; - GetWindowBounds (windowRef, windowRegionToUse, &r); - r.right += x - r.left; - r.bottom += y - r.top; - r.left = x; - r.top = y; - SetWindowBounds (windowRef, windowRegionToUse, &r); - } - } - - void setSize (int w, int h) - { - w = jmax (0, w); - h = jmax (0, h); - - if (w != getComponent()->getWidth() - || h != getComponent()->getHeight()) - { - repainter->repaint (0, 0, w, h); - } - - if (isSharedWindow) - { - HIRect r; - HIViewGetFrame (viewRef, &r); - r.size.width = (float) w; - r.size.height = (float) h; - HIViewSetFrame (viewRef, &r); - } - else if (IsValidWindowPtr (windowRef)) - { - Rect r; - GetWindowBounds (windowRef, windowRegionToUse, &r); - r.right = r.left + w; - r.bottom = r.top + h; - SetWindowBounds (windowRef, windowRegionToUse, &r); - } - } - - void setBounds (int x, int y, int w, int h, const bool isNowFullScreen) - { - fullScreen = isNowFullScreen; - w = jmax (0, w); - h = jmax (0, h); - - if (w != getComponent()->getWidth() - || h != getComponent()->getHeight()) - { - repainter->repaint (0, 0, w, h); - } - - if (isSharedWindow) - { - HIRect r; - r.origin.x = (float) x; - r.origin.y = (float) y; - r.size.width = (float) w; - r.size.height = (float) h; - HIViewSetFrame (viewRef, &r); - } - else if (IsValidWindowPtr (windowRef)) - { - Rect r; - r.left = x; - r.top = y; - r.right = x + w; - r.bottom = y + h; - SetWindowBounds (windowRef, windowRegionToUse, &r); - } - } - - void getBounds (int& x, int& y, int& w, int& h, const bool global) const - { - HIRect hiViewPos; - HIViewGetFrame (viewRef, &hiViewPos); - - if (global) - { - HIViewRef content = 0; - HIViewFindByID (HIViewGetRoot (windowRef), kHIViewWindowContentID, &content); - HIPoint p = { 0.0f, 0.0f }; - HIViewConvertPoint (&p, viewRef, content); - - x = (int) p.x; - y = (int) p.y; - - if (IsValidWindowPtr (windowRef)) - { - Rect windowPos; - GetWindowBounds (windowRef, kWindowContentRgn, &windowPos); - - x += windowPos.left; - y += windowPos.top; - } - } - else - { - x = (int) hiViewPos.origin.x; - y = (int) hiViewPos.origin.y; - } - - w = (int) hiViewPos.size.width; - h = (int) hiViewPos.size.height; - } - - void getBounds (int& x, int& y, int& w, int& h) const - { - getBounds (x, y, w, h, ! isSharedWindow); - } - - int getScreenX() const - { - int x, y, w, h; - getBounds (x, y, w, h, true); - return x; - } - - int getScreenY() const - { - int x, y, w, h; - getBounds (x, y, w, h, true); - return y; - } - - void relativePositionToGlobal (int& x, int& y) - { - int wx, wy, ww, wh; - getBounds (wx, wy, ww, wh, true); - - x += wx; - y += wy; - } - - void globalPositionToRelative (int& x, int& y) - { - int wx, wy, ww, wh; - getBounds (wx, wy, ww, wh, true); - - x -= wx; - y -= wy; - } - - void setMinimised (bool shouldBeMinimised) - { - if (! isSharedWindow) - setWindowMinimised (windowRef, shouldBeMinimised); - } - - bool isMinimised() const - { - return minimisedWindows.contains (windowRef); - } - - void setFullScreen (bool shouldBeFullScreen) - { - if (! isSharedWindow) - { - setMinimised (false); - - if (fullScreen != shouldBeFullScreen) - { - Rectangle r (lastNonFullscreenBounds); - - if (shouldBeFullScreen) - r = Desktop::getInstance().getMainMonitorArea(); - - // (can't call the component's setBounds method because that'll reset our fullscreen flag) - if (r != getComponent()->getBounds() && ! r.isEmpty()) - setBounds (r.getX(), r.getY(), r.getWidth(), r.getHeight(), shouldBeFullScreen); - } - } - } - - bool isFullScreen() const - { - return fullScreen; - } - - bool contains (int x, int y, bool trueIfInAChildWindow) const - { - if (x < 0 || y < 0 - || x >= component->getWidth() || y >= component->getHeight() - || ! IsValidWindowPtr (windowRef)) - return false; - - Rect r; - GetWindowBounds (windowRef, windowRegionToUse, &r); - - ::Point p; - p.h = r.left + x; - p.v = r.top + y; - - WindowRef ref2 = 0; - FindWindow (p, &ref2); - - if (windowRef != ref2) - return false; - - if (trueIfInAChildWindow) - return true; - - HIPoint p2; - p2.x = (float) x; - p2.y = (float) y; - HIViewRef hit; - - HIViewGetSubviewHit (viewRef, &p2, true, &hit); - return hit == 0 || hit == viewRef; - } - - const BorderSize getFrameSize() const - { - return BorderSize(); - } - - bool setAlwaysOnTop (bool alwaysOnTop) - { - // can't do this so return false and let the component create a new window - return false; - } - - void toFront (bool makeActiveWindow) - { - makeActiveWindow = makeActiveWindow - && component->isValidComponent() - && (component->getWantsKeyboardFocus() - || component->isCurrentlyModal()); - - if (windowRef != FrontWindow() - || (makeActiveWindow && ! IsWindowActive (windowRef)) - || ! Process::isForegroundProcess()) - { - if (! Process::isForegroundProcess()) - { - ProcessSerialNumber psn; - GetCurrentProcess (&psn); - SetFrontProcess (&psn); - } - - if (IsValidWindowPtr (windowRef)) - { - if (makeActiveWindow) - { - SelectWindow (windowRef); - SetUserFocusWindow (windowRef); - HIViewAdvanceFocus (viewRef, 0); - } - else - { - BringToFront (windowRef); - } - - handleBroughtToFront(); - } - } - } - - void toBehind (ComponentPeer* other) - { - HIViewComponentPeer* const otherWindow = dynamic_cast (other); - - if (other != 0 && windowRef != 0 && otherWindow->windowRef != 0) - { - if (windowRef == otherWindow->windowRef) - { - HIViewSetZOrder (viewRef, kHIViewZOrderBelow, otherWindow->viewRef); - } - else - { - SendBehind (windowRef, otherWindow->windowRef); - } - } - } - - void setIcon (const Image& /*newIcon*/) - { - // to do.. - } - - //============================================================================== - void viewFocusGain() - { - const MessageManagerLock messLock; - - if (currentlyFocusedPeer != this) - { - if (ComponentPeer::isValidPeer (currentlyFocusedPeer)) - currentlyFocusedPeer->handleFocusLoss(); - - currentlyFocusedPeer = this; - - handleFocusGain(); - } - } - - void viewFocusLoss() - { - if (currentlyFocusedPeer == this) - { - currentlyFocusedPeer = 0; - handleFocusLoss(); - } - } - - bool isFocused() const - { - return windowRef == GetUserFocusWindow() - && HIViewSubtreeContainsFocus (viewRef); - } - - void grabFocus() - { - if ((! isFocused()) && IsValidWindowPtr (windowRef)) - { - SetUserFocusWindow (windowRef); - HIViewAdvanceFocus (viewRef, 0); - } - } - - //============================================================================== - void repaint (int x, int y, int w, int h) - { - if (Rectangle::intersectRectangles (x, y, w, h, - 0, 0, - getComponent()->getWidth(), - getComponent()->getHeight())) - { - if ((getStyleFlags() & windowRepaintedExplictly) == 0) - { - if (isCompositingWindow) - { -#if MACOS_10_3_OR_EARLIER - RgnHandle rgn = NewRgn(); - SetRectRgn (rgn, x, y, x + w, y + h); - HIViewSetNeedsDisplayInRegion (viewRef, rgn, true); - DisposeRgn (rgn); -#else - HIRect r; - r.origin.x = x; - r.origin.y = y; - r.size.width = w; - r.size.height = h; - - HIViewSetNeedsDisplayInRect (viewRef, &r, true); -#endif - } - else - { - if (! isTimerRunning()) - startTimer (20); - } - } - - repainter->repaint (x, y, w, h); - } - } - - void timerCallback() - { - performAnyPendingRepaintsNow(); - } - - void performAnyPendingRepaintsNow() - { - stopTimer(); - - if (component->isVisible()) - { -#if MACOS_10_2_OR_EARLIER - if (! isCompositingWindow) - { - Rect w; - GetWindowBounds (windowRef, windowRegionToUse, &w); - - RgnHandle rgn = NewRgn(); - SetRectRgn (rgn, 0, 0, w.right - w.left, w.bottom - w.top); - UpdateControls (windowRef, rgn); - DisposeRgn (rgn); - } - else - { - EventRef theEvent; - - EventTypeSpec eventTypes[1]; - eventTypes[0].eventClass = kEventClassControl; - eventTypes[0].eventKind = kEventControlDraw; - - int n = 3; - while (--n >= 0 - && ReceiveNextEvent (1, eventTypes, kEventDurationNoWait, true, &theEvent) == noErr) - { - if (GetEventClass (theEvent) == kEventClassAppleEvent) - { - EventRecord eventRec; - if (ConvertEventRefToEventRecord (theEvent, &eventRec)) - AEProcessAppleEvent (&eventRec); - } - else - { - EventTargetRef theTarget = GetEventDispatcherTarget(); - SendEventToEventTarget (theEvent, theTarget); - } - - ReleaseEvent (theEvent); - } - } -#else - HIViewRender (viewRef); -#endif - } - } - - //============================================================================== - juce_UseDebuggingNewOperator - - WindowRef windowRef; - HIViewRef viewRef; - -private: - EventHandlerRef eventHandlerRef; - bool fullScreen, isSharedWindow, isCompositingWindow; - - //============================================================================== - class RepaintManager : public Timer - { -public: - RepaintManager (HIViewComponentPeer* const peer_) - : peer (peer_), - image (0) - { - } - - ~RepaintManager() - { - delete image; - } - - void timerCallback() - { - stopTimer(); - deleteAndZero (image); - } - - void repaint (int x, int y, int w, int h) - { - regionsNeedingRepaint.add (x, y, w, h); - } - - void repaintAnyRemainingRegions() - { - // if any regions have been invaldated during the paint callback, - // we need to repaint them explicitly because the mac throws this - // stuff away - for (RectangleList::Iterator i (regionsNeedingRepaint); i.next();) - { - const Rectangle& r = *i.getRectangle(); - peer->repaint (r.getX(), r.getY(), r.getWidth(), r.getHeight()); - } - } - - void paint (CGContextRef cgContext, int x, int y, int w, int h) - { - if (w > 0 && h > 0) - { - bool refresh = false; - int imW = image != 0 ? image->getWidth() : 0; - int imH = image != 0 ? image->getHeight() : 0; - - if (imW < w || imH < h) - { - imW = jmin (peer->getComponent()->getWidth(), (w + 31) & ~31); - imH = jmin (peer->getComponent()->getHeight(), (h + 31) & ~31); - - delete image; - image = new MacBitmapImage (peer->getComponent()->isOpaque() ? Image::RGB - : Image::ARGB, - imW, imH, false); - - refresh = true; - } - else if (imageX > x || imageY > y - || imageX + imW < x + w - || imageY + imH < y + h) - { - refresh = true; - } - - if (refresh) - { - regionsNeedingRepaint.clear(); - regionsNeedingRepaint.addWithoutMerging (Rectangle (x, y, imW, imH)); - imageX = x; - imageY = y; - } - - LowLevelGraphicsSoftwareRenderer context (*image); - context.setOrigin (-imageX, -imageY); - - if (context.reduceClipRegion (regionsNeedingRepaint)) - { - regionsNeedingRepaint.clear(); - - if (! peer->getComponent()->isOpaque()) - { - for (RectangleList::Iterator i (*context.getRawClipRegion()); i.next();) - { - const Rectangle& r = *i.getRectangle(); - image->clear (r.getX(), r.getY(), r.getWidth(), r.getHeight()); - } - } - - regionsNeedingRepaint.clear(); - peer->clearMaskedRegion(); - peer->handlePaint (context); - } - else - { - regionsNeedingRepaint.clear(); - } - - if (! peer->maskedRegion.isEmpty()) - { - RectangleList total (Rectangle (x, y, w, h)); - total.subtract (peer->maskedRegion); - - CGRect* rects = (CGRect*) juce_malloc (sizeof (CGRect) * total.getNumRectangles()); - int n = 0; - - for (RectangleList::Iterator i (total); i.next();) - { - const Rectangle& r = *i.getRectangle(); - rects[n].origin.x = (int) r.getX(); - rects[n].origin.y = (int) r.getY(); - rects[n].size.width = roundFloatToInt (r.getWidth()); - rects[n++].size.height = roundFloatToInt (r.getHeight()); - } - - CGContextClipToRects (cgContext, rects, n); - juce_free (rects); - } - - if (peer->isSharedWindow) - { - CGRect clip; - clip.origin.x = x; - clip.origin.y = y; - clip.size.width = jmin (w, peer->getComponent()->getWidth() - x); - clip.size.height = jmin (h, peer->getComponent()->getHeight() - y); - - CGContextClipToRect (cgContext, clip); - } - - image->blitToContext (cgContext, imageX, imageY); - } - - startTimer (3000); - } - - private: - HIViewComponentPeer* const peer; - MacBitmapImage* image; - int imageX, imageY; - RectangleList regionsNeedingRepaint; - - RepaintManager (const RepaintManager&); - const RepaintManager& operator= (const RepaintManager&); - }; - - RepaintManager* repainter; - - friend class RepaintManager; - - //============================================================================== - static OSStatus handleFrameRepaintEvent (EventHandlerCallRef myHandler, - EventRef theEvent, - void* userData) - { - // don't draw the frame.. - return noErr; - } - - //============================================================================== - OSStatus handleWindowClassEvent (EventRef theEvent) - { - switch (GetEventKind (theEvent)) - { - case kEventWindowBoundsChanged: - resizeViewToFitWindow(); - break; // allow other handlers in the event chain to also get a look at the events - - case kEventWindowBoundsChanging: - if ((styleFlags & (windowIsResizable | windowHasTitleBar)) == (windowIsResizable | windowHasTitleBar)) - { - UInt32 atts = 0; - GetEventParameter (theEvent, kEventParamAttributes, typeUInt32, - 0, sizeof (UInt32), 0, &atts); - - if ((atts & (kWindowBoundsChangeUserDrag | kWindowBoundsChangeUserResize)) != 0) - { - if (component->isCurrentlyBlockedByAnotherModalComponent()) - { - Component* const modal = Component::getCurrentlyModalComponent(); - if (modal != 0) - modal->inputAttemptWhenModal(); - } - - if ((atts & kWindowBoundsChangeUserResize) != 0 - && constrainer != 0 && ! isSharedWindow) - { - Rect current; - GetEventParameter (theEvent, kEventParamCurrentBounds, typeQDRectangle, - 0, sizeof (Rect), 0, ¤t); - - int x = current.left; - int y = current.top; - int w = current.right - current.left; - int h = current.bottom - current.top; - - const Rectangle currentRect (getComponent()->getBounds()); - - constrainer->checkBounds (x, y, w, h, currentRect, - Desktop::getInstance().getAllMonitorDisplayAreas().getBounds(), - y != currentRect.getY() && y + h == currentRect.getBottom(), - x != currentRect.getX() && x + w == currentRect.getRight(), - y == currentRect.getY() && y + h != currentRect.getBottom(), - x == currentRect.getX() && x + w != currentRect.getRight()); - - current.left = x; - current.top = y; - current.right = x + w; - current.bottom = y + h; - - SetEventParameter (theEvent, kEventParamCurrentBounds, typeQDRectangle, - sizeof (Rect), ¤t); - - return noErr; - } - } - } - break; - - case kEventWindowFocusAcquired: - keysCurrentlyDown.clear(); - - if ((! isSharedWindow) || HIViewSubtreeContainsFocus (viewRef)) - viewFocusGain(); - - break; // allow other handlers in the event chain to also get a look at the events - - case kEventWindowFocusRelinquish: - keysCurrentlyDown.clear(); - viewFocusLoss(); - - break; // allow other handlers in the event chain to also get a look at the events - - case kEventWindowCollapsed: - minimisedWindows.addIfNotAlreadyThere (windowRef); - handleMovedOrResized(); - break; // allow other handlers in the event chain to also get a look at the events - - case kEventWindowExpanded: - minimisedWindows.removeValue (windowRef); - handleMovedOrResized(); - break; // allow other handlers in the event chain to also get a look at the events - - case kEventWindowShown: - break; // allow other handlers in the event chain to also get a look at the events - - case kEventWindowClose: - if (isSharedWindow) - break; // break to let the OS delete the window - - handleUserClosingWindow(); - return noErr; // avoids letting the OS to delete the window, we'll do that ourselves. - - default: - break; - } - - return eventNotHandledErr; - } - - OSStatus handleKeyEvent (EventRef theEvent, juce_wchar textCharacter) - { - updateModifiers (theEvent); - - UniChar unicodeChars [4]; - zeromem (unicodeChars, sizeof (unicodeChars)); - GetEventParameter (theEvent, kEventParamKeyUnicodes, typeUnicodeText, 0, sizeof (unicodeChars), 0, unicodeChars); - - int keyCode = (int) (unsigned int) unicodeChars[0]; - - UInt32 rawKey = 0; - GetEventParameter (theEvent, kEventParamKeyCode, typeUInt32, 0, sizeof (UInt32), 0, &rawKey); - - if ((currentModifiers & ModifierKeys::ctrlModifier) != 0) - { - if (keyCode >= 1 && keyCode <= 26) - keyCode += ('A' - 1); - } - - static const int keyTranslations[] = - { - 0, 's', 'd', 'f', 'h', 'g', 'z', 'x', 'c', 'v', 0xa7, 'b', - 'q', 'w', 'e', 'r', 'y', 't', '1', '2', '3', '4', '6', '5', - '=', '9', '7', '-', '8', '0', ']', 'o', 'u', '[', 'i', 'p', - KeyPress::returnKey, 'l', 'j', '\'', 'k', ';', '\\', ',', '/', - 'n', 'm', '.', 0, KeyPress::spaceKey, '`', KeyPress::backspaceKey, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, KeyPress::numberPadDecimalPoint, - 0, KeyPress::numberPadMultiply, 0, KeyPress::numberPadAdd, - 0, KeyPress::numberPadDelete, 0, 0, 0, KeyPress::numberPadDivide, KeyPress::returnKey, - 0, KeyPress::numberPadSubtract, 0, 0, KeyPress::numberPadEquals, KeyPress::numberPad0, - KeyPress::numberPad1, KeyPress::numberPad2, KeyPress::numberPad3, - KeyPress::numberPad4, KeyPress::numberPad5, KeyPress::numberPad6, - KeyPress::numberPad7, 0, KeyPress::numberPad8, KeyPress::numberPad9, - 0, 0, 0, KeyPress::F5Key, KeyPress::F6Key, KeyPress::F7Key, KeyPress::F3Key, - KeyPress::F8Key, KeyPress::F9Key, 0, KeyPress::F11Key, 0, KeyPress::F13Key, - KeyPress::F16Key, KeyPress::F14Key, 0, KeyPress::F10Key, 0, KeyPress::F12Key, - 0, KeyPress::F15Key, 0, KeyPress::homeKey, KeyPress::pageUpKey, 0, KeyPress::F4Key, - KeyPress::endKey, KeyPress::F2Key, KeyPress::pageDownKey, KeyPress::F1Key, - KeyPress::leftKey, KeyPress::rightKey, KeyPress::downKey, KeyPress::upKey, 0 - }; - - if (rawKey > 0 && rawKey < numElementsInArray (keyTranslations)) - keyCode = keyTranslations [rawKey]; - else if (rawKey == 0 && textCharacter != 0) - keyCode = 'a'; - - if ((currentModifiers & (ModifierKeys::commandModifier | ModifierKeys::ctrlModifier)) != 0) - textCharacter = 0; - - static juce_wchar lastTextCharacter = 0; - - switch (GetEventKind (theEvent)) - { - case kEventRawKeyDown: - { - keysCurrentlyDown.addIfNotAlreadyThere ((void*) keyCode); - lastTextCharacter = textCharacter; - - const bool used1 = handleKeyUpOrDown(); - const bool used2 = handleKeyPress (keyCode, textCharacter); - - return (used1 || used2) ? noErr : eventNotHandledErr; - } - - case kEventRawKeyUp: - keysCurrentlyDown.removeValue ((void*) keyCode); - lastTextCharacter = 0; - return handleKeyUpOrDown() ? noErr : eventNotHandledErr; - - case kEventRawKeyRepeat: - return handleKeyPress (keyCode, lastTextCharacter) ? noErr - : eventNotHandledErr; - - case kEventRawKeyModifiersChanged: - handleModifierKeysChange(); - break; - - default: - jassertfalse - break; - } - - return noErr; - } - - OSStatus handleTextInputEvent (EventRef theEvent) - { - UniChar uc; - GetEventParameter (theEvent, kEventParamTextInputSendText, typeUnicodeText, 0, sizeof (uc), 0, &uc); - - EventRef originalEvent; - GetEventParameter (theEvent, kEventParamTextInputSendKeyboardEvent, typeEventRef, 0, sizeof (originalEvent), 0, &originalEvent); - - return handleKeyEvent (originalEvent, (juce_wchar) uc); - } - - OSStatus handleMouseEvent (EventHandlerCallRef callRef, EventRef theEvent) - { - MouseCheckTimer::getInstance()->moved (this); - - ::Point where; - GetEventParameter (theEvent, kEventParamMouseLocation, typeQDPoint, 0, sizeof (::Point), 0, &where); - int x = where.h; - int y = where.v; - globalPositionToRelative (x, y); - - int64 time = getEventTime (theEvent); - - switch (GetEventKind (theEvent)) - { - case kEventMouseMoved: - MouseCheckTimer::getInstance()->hasEverHadAMouseMove = true; - updateModifiers (theEvent); - handleMouseMove (x, y, time); - break; - - case kEventMouseDragged: - updateModifiers (theEvent); - handleMouseDrag (x, y, time); - break; - - case kEventMouseDown: - { - if (! Process::isForegroundProcess()) - { - ProcessSerialNumber psn; - GetCurrentProcess (&psn); - SetFrontProcess (&psn); - - toFront (true); - } - -#if JUCE_QUICKTIME - { - long mods; - GetEventParameter (theEvent, kEventParamKeyModifiers, typeUInt32, 0, sizeof (mods), 0, &mods); - - ::Point where; - GetEventParameter (theEvent, kEventParamMouseLocation, typeQDPoint, 0, sizeof (::Point), 0, &where); - - OfferMouseClickToQuickTime (windowRef, where, EventTimeToTicks (GetEventTime (theEvent)), mods, component); - } -#endif - - if (component->isBroughtToFrontOnMouseClick() - && ! component->isCurrentlyBlockedByAnotherModalComponent()) - { - //ActivateWindow (windowRef, true); - SelectWindow (windowRef); - } - - EventMouseButton button; - GetEventParameter (theEvent, kEventParamMouseButton, typeMouseButton, 0, sizeof (EventMouseButton), 0, &button); - - // need to clear all these flags because sometimes the mac can swallow (right) mouse-up events and - // this makes a button get stuck down. Since there's no other way to tell what buttons are down, - // this is all I can think of doing about it.. - currentModifiers &= ~(ModifierKeys::leftButtonModifier | ModifierKeys::rightButtonModifier | ModifierKeys::middleButtonModifier); - - if (button == kEventMouseButtonPrimary) - currentModifiers |= ModifierKeys::leftButtonModifier; - else if (button == kEventMouseButtonSecondary) - currentModifiers |= ModifierKeys::rightButtonModifier; - else if (button == kEventMouseButtonTertiary) - currentModifiers |= ModifierKeys::middleButtonModifier; - - updateModifiers (theEvent); - - juce_currentMouseTrackingPeer = this; // puts the message dispatcher into mouse-tracking mode.. - handleMouseDown (x, y, time); - break; - } - - case kEventMouseUp: - { - const int oldModifiers = currentModifiers; - - EventMouseButton button; - GetEventParameter (theEvent, kEventParamMouseButton, typeMouseButton, 0, sizeof (EventMouseButton), 0, &button); - - if (button == kEventMouseButtonPrimary) - currentModifiers &= ~ModifierKeys::leftButtonModifier; - else if (button == kEventMouseButtonSecondary) - currentModifiers &= ~ModifierKeys::rightButtonModifier; - - updateModifiers (theEvent); - - handleMouseUp (oldModifiers, x, y, time); - juce_currentMouseTrackingPeer = 0; - break; - } - - case kEventMouseWheelMoved: - { - EventMouseWheelAxis axis; - GetEventParameter (theEvent, kEventParamMouseWheelAxis, typeMouseWheelAxis, 0, sizeof (axis), 0, &axis); - - SInt32 delta; - GetEventParameter (theEvent, kEventParamMouseWheelDelta, - typeLongInteger, 0, sizeof (delta), 0, &delta); - - updateModifiers (theEvent); - - handleMouseWheel (axis == kEventMouseWheelAxisX ? delta * 10 : 0, - axis == kEventMouseWheelAxisX ? 0 : delta * 10, - time); - - break; - } - } - - return noErr; - } - - OSStatus handleDragAndDrop (EventRef theEvent) - { - DragRef dragRef; - if (GetEventParameter (theEvent, kEventParamDragRef, typeDragRef, 0, sizeof (dragRef), 0, &dragRef) == noErr) - { - int mx, my; - component->getMouseXYRelative (mx, my); - - UInt16 numItems = 0; - if (CountDragItems (dragRef, &numItems) == noErr) - { - StringArray filenames; - - for (int i = 0; i < (int) numItems; ++i) - { - DragItemRef ref; - - if (GetDragItemReferenceNumber (dragRef, i + 1, &ref) == noErr) - { - const FlavorType flavorType = kDragFlavorTypeHFS; - - Size size = 0; - if (GetFlavorDataSize (dragRef, ref, flavorType, &size) == noErr) - { - void* data = juce_calloc (size); - - if (GetFlavorData (dragRef, ref, flavorType, data, &size, 0) == noErr) - { - HFSFlavor* f = (HFSFlavor*) data; - FSRef fsref; - - if (FSpMakeFSRef (&f->fileSpec, &fsref) == noErr) - { - const String path (PlatformUtilities::makePathFromFSRef (&fsref)); - - if (path.isNotEmpty()) - filenames.add (path); - } - } - - juce_free (data); - } - } - } - - filenames.trim(); - filenames.removeEmptyStrings(); - - if (filenames.size() > 0) - handleFilesDropped (mx, my, filenames); - } - } - - return noErr; - } - - void resizeViewToFitWindow() - { - HIRect r; - - if (isSharedWindow) - { - HIViewGetFrame (viewRef, &r); - r.size.width = (float) component->getWidth(); - r.size.height = (float) component->getHeight(); - } - else - { - r.origin.x = 0; - r.origin.y = 0; - - Rect w; - GetWindowBounds (windowRef, windowRegionToUse, &w); - - r.size.width = (float) (w.right - w.left); - r.size.height = (float) (w.bottom - w.top); - } - - HIViewSetFrame (viewRef, &r); - -#if MACOS_10_3_OR_EARLIER - component->repaint(); -#endif - } - - OSStatus hiViewDraw (EventRef theEvent) - { - CGContextRef context = 0; - GetEventParameter (theEvent, kEventParamCGContextRef, typeCGContextRef, 0, sizeof (CGContextRef), 0, &context); - - CGrafPtr oldPort; - CGrafPtr port = 0; - - if (context == 0) - { - GetEventParameter (theEvent, kEventParamGrafPort, typeGrafPtr, 0, sizeof (CGrafPtr), 0, &port); - - GetPort (&oldPort); - SetPort (port); - - if (port != 0) - QDBeginCGContext (port, &context); - - if (! isCompositingWindow) - { - Rect bounds; - GetWindowBounds (windowRef, windowRegionToUse, &bounds); - CGContextTranslateCTM (context, 0, bounds.bottom - bounds.top); - CGContextScaleCTM (context, 1.0, -1.0); - } - - if (isSharedWindow) - { - // NB - Had terrible problems trying to correctly get the position - // of this view relative to the window, and this seems wrong, but - // works better than any other method I've tried.. - HIRect hiViewPos; - HIViewGetFrame (viewRef, &hiViewPos); - CGContextTranslateCTM (context, hiViewPos.origin.x, hiViewPos.origin.y); - } - } - -#if MACOS_10_2_OR_EARLIER - RgnHandle rgn = 0; - GetEventParameter (theEvent, kEventParamRgnHandle, typeQDRgnHandle, 0, sizeof (RgnHandle), 0, &rgn); - - CGRect clip; - - if (rgn != 0) - { - Rect bounds; - GetRegionBounds (rgn, &bounds); - clip.origin.x = bounds.left; - clip.origin.y = bounds.top; - clip.size.width = bounds.right - bounds.left; - clip.size.height = bounds.bottom - bounds.top; - } - else - { - HIViewGetBounds (viewRef, &clip); - } -#else - CGRect clip (CGContextGetClipBoundingBox (context)); -#endif - - clip = CGRectIntegral (clip); - - if (clip.origin.x < 0) - { - clip.size.width += clip.origin.x; - clip.origin.x = 0; - } - - if (clip.origin.y < 0) - { - clip.size.height += clip.origin.y; - clip.origin.y = 0; - } - - if (! component->isOpaque()) - CGContextClearRect (context, clip); - - repainter->paint (context, - (int) clip.origin.x, (int) clip.origin.y, - (int) clip.size.width, (int) clip.size.height); - - if (port != 0) - { - CGContextFlush (context); - QDEndCGContext (port, &context); - - SetPort (oldPort); - } - - repainter->repaintAnyRemainingRegions(); - - return noErr; - } - - static pascal OSStatus handleWindowEvent (EventHandlerCallRef callRef, EventRef theEvent, void* userData) - { - MessageManager::delayWaitCursor(); - - HIViewComponentPeer* const peer = (HIViewComponentPeer*) userData; - - const MessageManagerLock messLock; - - if (ComponentPeer::isValidPeer (peer)) - return peer->handleWindowEventForPeer (callRef, theEvent); - - return eventNotHandledErr; - } - - OSStatus handleWindowEventForPeer (EventHandlerCallRef callRef, EventRef theEvent) - { - switch (GetEventClass (theEvent)) - { - case kEventClassMouse: - { - static HIViewComponentPeer* lastMouseDownPeer = 0; - - const UInt32 eventKind = GetEventKind (theEvent); - HIViewRef view = 0; - - if (eventKind == kEventMouseDragged) - { - view = viewRef; - } - else - { - HIViewGetViewForMouseEvent (HIViewGetRoot (windowRef), theEvent, &view); - - if (view != viewRef) - { - if ((eventKind == kEventMouseUp - || eventKind == kEventMouseExited) - && ComponentPeer::isValidPeer (lastMouseDownPeer)) - { - return lastMouseDownPeer->handleMouseEvent (callRef, theEvent); - } - - return eventNotHandledErr; - } - } - - if (eventKind == kEventMouseDown - || eventKind == kEventMouseDragged - || eventKind == kEventMouseEntered) - { - lastMouseDownPeer = this; - } - - return handleMouseEvent (callRef, theEvent); - } - break; - - case kEventClassWindow: - return handleWindowClassEvent (theEvent); - - case kEventClassKeyboard: - if (isFocused()) - return handleKeyEvent (theEvent, 0); - - break; - - case kEventClassTextInput: - if (isFocused()) - return handleTextInputEvent (theEvent); - - break; - - default: - break; - } - - return eventNotHandledErr; - } - - static pascal OSStatus hiViewEventHandler (EventHandlerCallRef myHandler, EventRef theEvent, void* userData) - { - MessageManager::delayWaitCursor(); - - const UInt32 eventKind = GetEventKind (theEvent); - const UInt32 eventClass = GetEventClass (theEvent); - - if (eventClass == kEventClassHIObject) - { - switch (eventKind) - { - case kEventHIObjectConstruct: - { - void* data = juce_calloc (sizeof (void*)); - SetEventParameter (theEvent, kEventParamHIObjectInstance, - typeVoidPtr, sizeof (void*), &data); - - return noErr; - } - - case kEventHIObjectInitialize: - GetEventParameter (theEvent, 'peer', typeVoidPtr, 0, sizeof (void*), 0, (void**) userData); - return noErr; - - case kEventHIObjectDestruct: - juce_free (userData); - return noErr; - - default: - break; - } - } - else if (eventClass == kEventClassControl) - { - HIViewComponentPeer* const peer = *(HIViewComponentPeer**) userData; - const MessageManagerLock messLock; - - if (! ComponentPeer::isValidPeer (peer)) - return eventNotHandledErr; - - switch (eventKind) - { - case kEventControlDraw: - return peer->hiViewDraw (theEvent); - - case kEventControlBoundsChanged: - { - HIRect bounds; - HIViewGetBounds (peer->viewRef, &bounds); - peer->repaint (0, 0, roundFloatToInt (bounds.size.width), roundFloatToInt (bounds.size.height)); - - peer->handleMovedOrResized(); - return noErr; - } - - case kEventControlHitTest: - { - HIPoint where; - GetEventParameter (theEvent, kEventParamMouseLocation, typeHIPoint, 0, sizeof (HIPoint), 0, &where); - - HIRect bounds; - HIViewGetBounds (peer->viewRef, &bounds); - - ControlPartCode part = kControlNoPart; - - if (CGRectContainsPoint (bounds, where)) - part = 1; - - SetEventParameter (theEvent, kEventParamControlPart, typeControlPartCode, sizeof (ControlPartCode), &part); - return noErr; - } - break; - - case kEventControlSetFocusPart: - { - ControlPartCode desiredFocus; - if (GetEventParameter (theEvent, kEventParamControlPart, typeControlPartCode, 0, sizeof (ControlPartCode), 0, &desiredFocus) != noErr) - break; - - if (desiredFocus == kControlNoPart) - peer->viewFocusLoss(); - else - peer->viewFocusGain(); - - return noErr; - } - break; - - case kEventControlDragEnter: - { -#if MACOS_10_2_OR_EARLIER - enum { kEventParamControlWouldAcceptDrop = 'cldg' }; -#endif - Boolean accept = true; - SetEventParameter (theEvent, kEventParamControlWouldAcceptDrop, typeBoolean, sizeof (accept), &accept); - return noErr; - } - - case kEventControlDragWithin: - return noErr; - - case kEventControlDragReceive: - return peer->handleDragAndDrop (theEvent); - - case kEventControlOwningWindowChanged: - return peer->ownerWindowChanged (theEvent); - -#if ! MACOS_10_2_OR_EARLIER - case kEventControlGetFrameMetrics: - { - CallNextEventHandler (myHandler, theEvent); - HIViewFrameMetrics metrics; - GetEventParameter (theEvent, kEventParamControlFrameMetrics, typeControlFrameMetrics, 0, sizeof (metrics), 0, &metrics); - metrics.top = metrics.bottom = 0; - SetEventParameter (theEvent, kEventParamControlFrameMetrics, typeControlFrameMetrics, sizeof (metrics), &metrics); - return noErr; - } -#endif - - case kEventControlInitialize: - { - UInt32 features = kControlSupportsDragAndDrop - | kControlSupportsFocus - | kControlHandlesTracking - | kControlSupportsEmbedding - | (1 << 8) /*kHIViewFeatureGetsFocusOnClick*/; - - SetEventParameter (theEvent, kEventParamControlFeatures, typeUInt32, sizeof (UInt32), &features); - return noErr; - } - - default: - break; - } - } - - return eventNotHandledErr; - } - - WindowRef createNewWindow (const int windowStyleFlags) - { - jassert (windowRef == 0); - - static ToolboxObjectClassRef customWindowClass = 0; - - if (customWindowClass == 0) - { - // Register our window class - const EventTypeSpec customTypes[] = { { kEventClassWindow, kEventWindowDrawFrame } }; - - UnsignedWide t; - Microseconds (&t); - const String randomString ((int) (t.lo & 0x7ffffff)); - const String juceWindowClassName (T("JUCEWindowClass_") + randomString); - CFStringRef juceWindowClassNameCFString = PlatformUtilities::juceStringToCFString (juceWindowClassName); - - RegisterToolboxObjectClass (juceWindowClassNameCFString, - 0, 1, customTypes, - NewEventHandlerUPP (handleFrameRepaintEvent), - 0, &customWindowClass); - - CFRelease (juceWindowClassNameCFString); - } - - Rect pos; - pos.left = getComponent()->getX(); - pos.top = getComponent()->getY(); - pos.right = getComponent()->getRight(); - pos.bottom = getComponent()->getBottom(); - - int attributes = kWindowStandardHandlerAttribute | kWindowCompositingAttribute; - if ((windowStyleFlags & windowHasDropShadow) == 0) - attributes |= kWindowNoShadowAttribute; - - if ((windowStyleFlags & windowIgnoresMouseClicks) != 0) - attributes |= kWindowIgnoreClicksAttribute; - -#if ! MACOS_10_3_OR_EARLIER - if ((windowStyleFlags & windowIsTemporary) != 0) - attributes |= kWindowDoesNotCycleAttribute; -#endif - - WindowRef newWindow = 0; - - if ((windowStyleFlags & windowHasTitleBar) == 0) - { - attributes |= kWindowCollapseBoxAttribute; - - WindowDefSpec customWindowSpec; - customWindowSpec.defType = kWindowDefObjectClass; - customWindowSpec.u.classRef = customWindowClass; - - CreateCustomWindow (&customWindowSpec, - ((windowStyleFlags & windowIsTemporary) != 0) ? kUtilityWindowClass : - (getComponent()->isAlwaysOnTop() ? kUtilityWindowClass - : kDocumentWindowClass), - attributes, - &pos, - &newWindow); - } - else - { - if ((windowStyleFlags & windowHasCloseButton) != 0) - attributes |= kWindowCloseBoxAttribute; - - if ((windowStyleFlags & windowHasMinimiseButton) != 0) - attributes |= kWindowCollapseBoxAttribute; - - if ((windowStyleFlags & windowHasMaximiseButton) != 0) - attributes |= kWindowFullZoomAttribute; - - if ((windowStyleFlags & windowIsResizable) != 0) - attributes |= kWindowResizableAttribute | kWindowLiveResizeAttribute; - - CreateNewWindow (kDocumentWindowClass, attributes, &pos, &newWindow); - } - - jassert (newWindow != 0); - if (newWindow != 0) - { - HideWindow (newWindow); - - SetAutomaticControlDragTrackingEnabledForWindow (newWindow, true); - - if (! getComponent()->isOpaque()) - SetWindowAlpha (newWindow, 0.9999999f); // to fool it into giving the window an alpha-channel - } - - return newWindow; - } - - OSStatus ownerWindowChanged (EventRef theEvent) - { - WindowRef newWindow = 0; - GetEventParameter (theEvent, kEventParamControlCurrentOwningWindow, typeWindowRef, 0, sizeof (newWindow), 0, &newWindow); - - if (windowRef != newWindow) - { - if (eventHandlerRef != 0) - { - RemoveEventHandler (eventHandlerRef); - eventHandlerRef = 0; - } - - windowRef = newWindow; - - if (windowRef != 0) - { - const EventTypeSpec eventTypes[] = - { - { kEventClassWindow, kEventWindowBoundsChanged }, - { kEventClassWindow, kEventWindowBoundsChanging }, - { kEventClassWindow, kEventWindowFocusAcquired }, - { kEventClassWindow, kEventWindowFocusRelinquish }, - { kEventClassWindow, kEventWindowCollapsed }, - { kEventClassWindow, kEventWindowExpanded }, - { kEventClassWindow, kEventWindowShown }, - { kEventClassWindow, kEventWindowClose }, - { kEventClassMouse, kEventMouseDown }, - { kEventClassMouse, kEventMouseUp }, - { kEventClassMouse, kEventMouseMoved }, - { kEventClassMouse, kEventMouseDragged }, - { kEventClassMouse, kEventMouseEntered }, - { kEventClassMouse, kEventMouseExited }, - { kEventClassMouse, kEventMouseWheelMoved }, - { kEventClassKeyboard, kEventRawKeyUp }, - { kEventClassKeyboard, kEventRawKeyRepeat }, - { kEventClassKeyboard, kEventRawKeyModifiersChanged }, - { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent } - }; - - static EventHandlerUPP handleWindowEventUPP = 0; - - if (handleWindowEventUPP == 0) - handleWindowEventUPP = NewEventHandlerUPP (handleWindowEvent); - - InstallWindowEventHandler (windowRef, handleWindowEventUPP, - GetEventTypeCount (eventTypes), eventTypes, - (void*) this, (EventHandlerRef*) &eventHandlerRef); - - WindowAttributes attributes; - GetWindowAttributes (windowRef, &attributes); - -#if MACOS_10_3_OR_EARLIER - isCompositingWindow = ((attributes & kWindowCompositingAttribute) != 0); -#else - isCompositingWindow = HIViewIsCompositingEnabled (viewRef); -#endif - - MouseCheckTimer::getInstance()->resetMouseMoveChecker(); - } - } - - resizeViewToFitWindow(); - return noErr; - } - - void createNewHIView() - { - jassert (viewRef == 0); - - if (viewClassRef == 0) - { - // Register our HIView class - EventTypeSpec viewEvents[] = - { - { kEventClassHIObject, kEventHIObjectConstruct }, - { kEventClassHIObject, kEventHIObjectInitialize }, - { kEventClassHIObject, kEventHIObjectDestruct }, - { kEventClassControl, kEventControlInitialize }, - { kEventClassControl, kEventControlDraw }, - { kEventClassControl, kEventControlBoundsChanged }, - { kEventClassControl, kEventControlSetFocusPart }, - { kEventClassControl, kEventControlHitTest }, - { kEventClassControl, kEventControlDragEnter }, - { kEventClassControl, kEventControlDragWithin }, - { kEventClassControl, kEventControlDragReceive }, - { kEventClassControl, kEventControlOwningWindowChanged } - }; - - UnsignedWide t; - Microseconds (&t); - const String randomString ((int) (t.lo & 0x7ffffff)); - const String juceHiViewClassName (T("JUCEHIViewClass_") + randomString); - juceHiViewClassNameCFString = PlatformUtilities::juceStringToCFString (juceHiViewClassName); - - HIObjectRegisterSubclass (juceHiViewClassNameCFString, - kHIViewClassID, 0, - NewEventHandlerUPP (hiViewEventHandler), - GetEventTypeCount (viewEvents), - viewEvents, 0, - &viewClassRef); - } - - EventRef event; - CreateEvent (0, kEventClassHIObject, kEventHIObjectInitialize, GetCurrentEventTime(), kEventAttributeNone, &event); - - void* thisPointer = this; - SetEventParameter (event, 'peer', typeVoidPtr, sizeof (void*), &thisPointer); - - HIObjectCreate (juceHiViewClassNameCFString, event, (HIObjectRef*) &viewRef); - - SetControlDragTrackingEnabled (viewRef, true); - - if (isSharedWindow) - { - setBounds (component->getX(), component->getY(), - component->getWidth(), component->getHeight(), false); - } - } -}; - -bool juce_isHIViewCreatedByJuce (HIViewRef view) -{ - return juceHiViewClassNameCFString != 0 - && HIObjectIsOfClass ((HIObjectRef) view, juceHiViewClassNameCFString); -} - -static void trackNextMouseEvent() -{ - UInt32 mods; - MouseTrackingResult result; - ::Point where; - - if (TrackMouseLocationWithOptions ((GrafPtr) -1, 0, 0.01, //kEventDurationForever, - &where, &mods, &result) != noErr - || ! ComponentPeer::isValidPeer (juce_currentMouseTrackingPeer)) - { - juce_currentMouseTrackingPeer = 0; - return; - } - - if (result == kMouseTrackingTimedOut) - return; - -#if MACOS_10_3_OR_EARLIER - const int x = where.h - juce_currentMouseTrackingPeer->getScreenX(); - const int y = where.v - juce_currentMouseTrackingPeer->getScreenY(); -#else - HIPoint p; - p.x = where.h; - p.y = where.v; - HIPointConvert (&p, kHICoordSpaceScreenPixel, 0, - kHICoordSpaceView, ((HIViewComponentPeer*) juce_currentMouseTrackingPeer)->viewRef); - const int x = p.x; - const int y = p.y; -#endif - - if (result == kMouseTrackingMouseDragged) - { - updateModifiers (0); - juce_currentMouseTrackingPeer->handleMouseDrag (x, y, getEventTime (0)); - - if (! ComponentPeer::isValidPeer (juce_currentMouseTrackingPeer)) - { - juce_currentMouseTrackingPeer = 0; - return; - } - } - else if (result == kMouseTrackingMouseUp - || result == kMouseTrackingUserCancelled - || result == kMouseTrackingMouseMoved) - { - if (ComponentPeer::isValidPeer (juce_currentMouseTrackingPeer)) - { - const int oldModifiers = currentModifiers; - currentModifiers &= ~(ModifierKeys::leftButtonModifier | ModifierKeys::rightButtonModifier | ModifierKeys::middleButtonModifier); - updateModifiers (0); - - juce_currentMouseTrackingPeer->handleMouseUp (oldModifiers, x, y, getEventTime (0)); - } - - juce_currentMouseTrackingPeer = 0; - } -} - -bool juce_dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages) -{ - if (juce_currentMouseTrackingPeer != 0) - trackNextMouseEvent(); - - EventRef theEvent; - - if (ReceiveNextEvent (0, 0, (returnIfNoPendingMessages) ? kEventDurationNoWait - : kEventDurationForever, - true, &theEvent) == noErr) - { - if (GetEventClass (theEvent) == kEventClassAppleEvent) - { - EventRecord eventRec; - if (ConvertEventRefToEventRecord (theEvent, &eventRec)) - AEProcessAppleEvent (&eventRec); - } - else - { - EventTargetRef theTarget = GetEventDispatcherTarget(); - SendEventToEventTarget (theEvent, theTarget); - } - - ReleaseEvent (theEvent); - return true; - } - - return false; -} - -//============================================================================== -ComponentPeer* Component::createNewPeer (int styleFlags, void* windowToAttachTo) -{ - return new HIViewComponentPeer (this, styleFlags, (HIViewRef) windowToAttachTo); -} - -//============================================================================== -void MouseCheckTimer::timerCallback() -{ - if (ModifierKeys::getCurrentModifiersRealtime().isAnyMouseButtonDown()) - return; - - if (Process::isForegroundProcess()) - { - bool stillOver = false; - int x = 0, y = 0, w = 0, h = 0; - int mx = 0, my = 0; - const bool validWindow = ComponentPeer::isValidPeer (lastPeerUnderMouse); - - if (validWindow) - { - lastPeerUnderMouse->getBounds (x, y, w, h, true); - Desktop::getMousePosition (mx, my); - - stillOver = (mx >= x && my >= y && mx < x + w && my < y + h); - - if (stillOver) - { - // check if it's over an embedded HIView - int rx = mx, ry = my; - lastPeerUnderMouse->globalPositionToRelative (rx, ry); - HIPoint hipoint; - hipoint.x = rx; - hipoint.y = ry; - - HIViewRef root; - GetRootControl ((WindowRef) lastPeerUnderMouse->getNativeHandle(), &root); - - HIViewRef hitview; - if (HIViewGetSubviewHit (root, &hipoint, true, &hitview) == noErr && hitview != 0) - { - stillOver = HIObjectIsOfClass ((HIObjectRef) hitview, juceHiViewClassNameCFString); - } - } - } - - if (! stillOver) - { - // mouse is outside our windows so set a normal cursor (only - // if we're running as an app, not a plugin) - if (JUCEApplication::getInstance() != 0) - SetThemeCursor (kThemeArrowCursor); - - if (validWindow) - lastPeerUnderMouse->handleMouseExit (mx - x, my - y, Time::currentTimeMillis()); - - if (hasEverHadAMouseMove) - stopTimer(); - } - - if ((! hasEverHadAMouseMove) && validWindow - && (mx != lastX || my != lastY)) - { - lastX = mx; - lastY = my; - - if (stillOver) - lastPeerUnderMouse->handleMouseMove (mx - x, my - y, Time::currentTimeMillis()); - } - } -} - -//============================================================================== -// called from juce_Messaging.cpp -void juce_HandleProcessFocusChange() -{ - keysCurrentlyDown.clear(); - - if (HIViewComponentPeer::isValidPeer (currentlyFocusedPeer)) - { - if (Process::isForegroundProcess()) - currentlyFocusedPeer->handleFocusGain(); - else - currentlyFocusedPeer->handleFocusLoss(); - } -} - -static bool performDrag (DragRef drag) -{ - EventRecord event; - event.what = mouseDown; - event.message = 0; - event.when = TickCount(); - - int x, y; - Desktop::getMousePosition (x, y); - event.where.h = x; - event.where.v = y; - - event.modifiers = GetCurrentKeyModifiers(); - - RgnHandle rgn = NewRgn(); - RgnHandle rgn2 = NewRgn(); - SetRectRgn (rgn, - event.where.h - 8, event.where.v - 8, - event.where.h + 8, event.where.v + 8); - CopyRgn (rgn, rgn2); - InsetRgn (rgn2, 1, 1); - DiffRgn (rgn, rgn2, rgn); - DisposeRgn (rgn2); - - bool result = TrackDrag (drag, &event, rgn) == noErr; - - DisposeRgn (rgn); - return result; -} - -bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, const bool canMoveFiles) -{ - for (int i = ComponentPeer::getNumPeers(); --i >= 0;) - ComponentPeer::getPeer (i)->performAnyPendingRepaintsNow(); - - DragRef drag; - bool result = false; - - if (NewDrag (&drag) == noErr) - { - for (int i = 0; i < files.size(); ++i) - { - HFSFlavor hfsData; - - if (PlatformUtilities::makeFSSpecFromPath (&hfsData.fileSpec, files[i])) - { - FInfo info; - if (FSpGetFInfo (&hfsData.fileSpec, &info) == noErr) - { - hfsData.fileType = info.fdType; - hfsData.fileCreator = info.fdCreator; - hfsData.fdFlags = info.fdFlags; - - AddDragItemFlavor (drag, i + 1, kDragFlavorTypeHFS, &hfsData, sizeof (hfsData), 0); - result = true; - } - } - } - - SetDragAllowableActions (drag, canMoveFiles ? kDragActionAll - : kDragActionCopy, false); - - if (result) - result = performDrag (drag); - - DisposeDrag (drag); - } - - return result; -} - -bool DragAndDropContainer::performExternalDragDropOfText (const String& text) -{ - jassertfalse // not implemented! - return false; -} - - -//============================================================================== -bool Process::isForegroundProcess() throw() -{ - ProcessSerialNumber psn, front; - GetCurrentProcess (&psn); - GetFrontProcess (&front); - - Boolean b; - return (SameProcess (&psn, &front, &b) == noErr) && b; -} - -//============================================================================== -bool Desktop::canUseSemiTransparentWindows() throw() -{ - return true; -} - - -//============================================================================== -void Desktop::getMousePosition (int& x, int& y) throw() -{ - CGrafPtr currentPort; - GetPort (¤tPort); - - if (! IsValidPort (currentPort)) - { - WindowRef front = FrontWindow(); - - if (front != 0) - { - SetPortWindowPort (front); - } - else - { - x = y = 0; - return; - } - } - - ::Point p; - GetMouse (&p); - LocalToGlobal (&p); - x = p.h; - y = p.v; - - SetPort (currentPort); -} - -void Desktop::setMousePosition (int x, int y) throw() -{ - CGPoint pos = { x, y }; - CGWarpMouseCursorPosition (pos); -} - -const ModifierKeys ModifierKeys::getCurrentModifiersRealtime() throw() -{ - return ModifierKeys (currentModifiers); -} - -//============================================================================== -void juce_updateMultiMonitorInfo (Array & monitorCoords, const bool clipToWorkArea) throw() -{ - int mainMonitorIndex = 0; - CGDirectDisplayID mainDisplayID = CGMainDisplayID(); - - CGDisplayCount count = 0; - CGDirectDisplayID disps [8]; - - if (CGGetActiveDisplayList (numElementsInArray (disps), disps, &count) == noErr) - { - for (int i = 0; i < count; ++i) - { - if (mainDisplayID == disps[i]) - mainMonitorIndex = monitorCoords.size(); - - GDHandle hGDevice; - - if (clipToWorkArea - && DMGetGDeviceByDisplayID ((DisplayIDType) disps[i], &hGDevice, false) == noErr) - { - Rect rect; - GetAvailableWindowPositioningBounds (hGDevice, &rect); - - monitorCoords.add (Rectangle (rect.left, - rect.top, - rect.right - rect.left, - rect.bottom - rect.top)); - } - else - { - const CGRect r (CGDisplayBounds (disps[i])); - - monitorCoords.add (Rectangle (r.origin.x, - r.origin.y, - r.size.width, - r.size.height)); - } - } - } - - // make sure the first in the list is the main monitor - if (mainMonitorIndex > 0) - monitorCoords.swap (mainMonitorIndex, 0); - - jassert (monitorCoords.size() > 0); - - //xxx need to register for display change callbacks -} - -//============================================================================== -struct CursorWrapper -{ - Cursor* cursor; - ThemeCursor themeCursor; -}; - -void* juce_createMouseCursorFromImage (const Image& image, int hotspotX, int hotspotY) throw() -{ - const int maxW = 16; - const int maxH = 16; - - const Image* im = ℑ - Image* newIm = 0; - - if (image.getWidth() > maxW || image.getHeight() > maxH) - { - im = newIm = image.createCopy (maxW, maxH); - - hotspotX = (hotspotX * maxW) / image.getWidth(); - hotspotY = (hotspotY * maxH) / image.getHeight(); - } - - Cursor* const c = new Cursor(); - c->hotSpot.h = hotspotX; - c->hotSpot.v = hotspotY; - - for (int y = 0; y < maxH; ++y) - { - c->data[y] = 0; - c->mask[y] = 0; - - for (int x = 0; x < maxW; ++x) - { - const Colour pixelColour (im->getPixelAt (15 - x, y)); - - if (pixelColour.getAlpha() > 0.5f) - { - c->mask[y] |= (1 << x); - - if (pixelColour.getBrightness() < 0.5f) - c->data[y] |= (1 << x); - } - } - - c->data[y] = CFSwapInt16BigToHost (c->data[y]); - c->mask[y] = CFSwapInt16BigToHost (c->mask[y]); - } - - if (newIm != 0) - delete newIm; - - CursorWrapper* const cw = new CursorWrapper(); - cw->cursor = c; - cw->themeCursor = kThemeArrowCursor; - return (void*) cw; -} - -static void* cursorFromData (const unsigned char* data, const int size, int hx, int hy) throw() -{ - Image* const im = ImageFileFormat::loadFrom ((const char*) data, size); - jassert (im != 0); - void* curs = juce_createMouseCursorFromImage (*im, hx, hy); - delete im; - return curs; -} - -const unsigned int kSpecialNoCursor = 'nocr'; - -void* juce_createStandardMouseCursor (MouseCursor::StandardCursorType type) throw() -{ - ThemeCursor id = kThemeArrowCursor; - - switch (type) - { - case MouseCursor::NormalCursor: - id = kThemeArrowCursor; - break; - - case MouseCursor::NoCursor: - id = kSpecialNoCursor; - break; - - case MouseCursor::DraggingHandCursor: - { - static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,2,0,0,0,0,255,255,255,0, - 0,0,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, - 16,0,0,2,52,148,47,0,200,185,16,130,90,12,74,139,107,84,123,39, - 132,117,151,116,132,146,248,60,209,138,98,22,203,114,34,236,37,52,77,217, - 247,154,191,119,110,240,193,128,193,95,163,56,60,234,98,135,2,0,59 }; - const int cursDataSize = 99; - - return cursorFromData (cursData, cursDataSize, 8, 8); - } - break; - - case MouseCursor::CopyingCursor: - id = kThemeCopyArrowCursor; - break; - - case MouseCursor::WaitCursor: - id = kThemeWatchCursor; - break; - - case MouseCursor::IBeamCursor: - id = kThemeIBeamCursor; - break; - - case MouseCursor::PointingHandCursor: - id = kThemePointingHandCursor; - break; - - case MouseCursor::LeftRightResizeCursor: - case MouseCursor::LeftEdgeResizeCursor: - case MouseCursor::RightEdgeResizeCursor: - { - static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,0,0,255,255,255,0,0,0,255, - 255,255,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, - 16,0,0,2,38,148,143,169,203,237,15,19,0,106,202,64,111,22,32,224, - 9,78,30,213,121,230,121,146,99,8,142,71,183,189,152,20,27,86,132,231, - 58,83,0,0,59 }; - const int cursDataSize = 85; - - return cursorFromData (cursData, cursDataSize, 8, 8); - } - - case MouseCursor::UpDownResizeCursor: - case MouseCursor::TopEdgeResizeCursor: - case MouseCursor::BottomEdgeResizeCursor: - { - static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,0,0,255,255,255,0,0,0,255, - 255,255,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, - 16,0,0,2,38,148,111,128,187,16,202,90,152,48,10,55,169,189,192,245, - 106,121,27,34,142,201,99,158,224,86,154,109,216,61,29,155,105,180,61,190, - 121,84,0,0,59 }; - const int cursDataSize = 85; - - return cursorFromData (cursData, cursDataSize, 8, 8); - } - - case MouseCursor::TopLeftCornerResizeCursor: - case MouseCursor::BottomRightCornerResizeCursor: - { - static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,0,0,255,255,255,0,0,0,255, - 255,255,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, - 16,0,0,2,43,132,15,162,187,16,255,18,99,14,202,217,44,158,213,221, - 237,9,225,38,94,35,73,5,31,42,170,108,106,174,112,43,195,209,91,185, - 104,174,131,208,77,66,28,10,0,59 }; - const int cursDataSize = 90; - - return cursorFromData (cursData, cursDataSize, 8, 8); - } - - case MouseCursor::TopRightCornerResizeCursor: - case MouseCursor::BottomLeftCornerResizeCursor: - { - static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,0,0,255,255,255,0,0,0,255, - 255,255,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, - 16,0,0,2,45,148,127,160,11,232,16,98,108,14,65,73,107,194,122,223, - 92,65,141,216,145,134,162,153,221,25,128,73,166,62,173,16,203,237,188,94, - 120,46,237,105,239,123,48,80,157,2,0,59 }; - const int cursDataSize = 92; - - return cursorFromData (cursData, cursDataSize, 8, 8); - } - - case MouseCursor::UpDownLeftRightResizeCursor: - { - static const unsigned char cursData[] = {71,73,70,56,57,97,15,0,15,0,145,0,0,0,0,0,255,255,255,0, - 128,128,255,255,255,33,249,4,1,0,0,3,0,44,0,0,0,0,15,0, - 15,0,0,2,46,156,63,129,139,1,202,26,152,48,186,73,109,114,65,85, - 195,37,143,88,93,29,215,101,23,198,178,30,149,158,25,56,134,97,179,61, - 158,213,126,203,234,99,220,34,56,70,1,0,59,0,0 }; - const int cursDataSize = 93; - - return cursorFromData (cursData, cursDataSize, 7, 7); - } - - case MouseCursor::CrosshairCursor: - id = kThemeCrossCursor; - break; - } - - CursorWrapper* cw = new CursorWrapper(); - cw->cursor = 0; - cw->themeCursor = id; - - return (void*) cw; -} - -void juce_deleteMouseCursor (void* const cursorHandle, const bool isStandard) throw() -{ - CursorWrapper* const cw = (CursorWrapper*) cursorHandle; - - if (cw != 0) - { - delete cw->cursor; - delete cw; - } -} - -void MouseCursor::showInAllWindows() const throw() -{ - showInWindow (0); -} - -void MouseCursor::showInWindow (ComponentPeer*) const throw() -{ - const CursorWrapper* const cw = (CursorWrapper*) getHandle(); - - if (cw != 0) - { - static bool isCursorHidden = false; - static bool showingWaitCursor = false; - const bool shouldShowWaitCursor = (cw->themeCursor == kThemeWatchCursor); - const bool shouldHideCursor = (cw->themeCursor == kSpecialNoCursor); - - if (shouldShowWaitCursor != showingWaitCursor - && Process::isForegroundProcess()) - { - showingWaitCursor = shouldShowWaitCursor; - QDDisplayWaitCursor (shouldShowWaitCursor); - } - - if (shouldHideCursor != isCursorHidden) - { - isCursorHidden = shouldHideCursor; - - if (shouldHideCursor) - HideCursor(); - else - ShowCursor(); - } - - if (cw->cursor != 0) - SetCursor (cw->cursor); - else if (! (shouldShowWaitCursor || shouldHideCursor)) - SetThemeCursor (cw->themeCursor); - } -} - -//============================================================================== -Image* juce_createIconForFile (const File& file) -{ - return 0; -} - - -//============================================================================== -class MainMenuHandler; -static MainMenuHandler* mainMenu = 0; - -class MainMenuHandler : private MenuBarModelListener, - private DeletedAtShutdown -{ -public: - MainMenuHandler() throw() - : currentModel (0) - { - } - - ~MainMenuHandler() throw() - { - setMenu (0); - - jassert (mainMenu == this); - mainMenu = 0; - } - - void setMenu (MenuBarModel* const newMenuBarModel) throw() - { - if (currentModel != newMenuBarModel) - { - if (currentModel != 0) - currentModel->removeListener (this); - - currentModel = newMenuBarModel; - - if (currentModel != 0) - currentModel->addListener (this); - - menuBarItemsChanged (0); - } - } - - void menuBarItemsChanged (MenuBarModel*) - { - ClearMenuBar(); - - if (currentModel != 0) - { - int id = 1000; - const StringArray menuNames (currentModel->getMenuBarNames()); - - for (int i = 0; i < menuNames.size(); ++i) - { - const PopupMenu menu (currentModel->getMenuForIndex (i, menuNames [i])); - - MenuRef m = createMenu (menu, menuNames [i], id, i); - - InsertMenu (m, 0); - CFRelease (m); - } - } - } - - void menuCommandInvoked (MenuBarModel*, const ApplicationCommandTarget::InvocationInfo& info) - { - MenuRef menu = 0; - MenuItemIndex index = 0; - GetIndMenuItemWithCommandID (0, info.commandID, 1, &menu, &index); - - FlashMenuBar (GetMenuID (menu)); - FlashMenuBar (GetMenuID (menu)); - } - - void invoke (const int id, ApplicationCommandManager* const commandManager, const int topLevelIndex) const - { - if (currentModel != 0) - { - if (commandManager != 0) - { - ApplicationCommandTarget::InvocationInfo info (id); - info.invocationMethod = ApplicationCommandTarget::InvocationInfo::fromMenu; - - commandManager->invoke (info, true); - } - - currentModel->menuItemSelected (id, topLevelIndex); - } - } - - MenuBarModel* currentModel; - -private: - static MenuRef createMenu (const PopupMenu menu, - const String& menuName, - int& id, - const int topLevelIndex) - { - MenuRef m = 0; - - if (CreateNewMenu (id++, kMenuAttrAutoDisable, &m) == noErr) - { - CFStringRef name = PlatformUtilities::juceStringToCFString (menuName); - SetMenuTitleWithCFString (m, name); - CFRelease (name); - - PopupMenu::MenuItemIterator iter (menu); - - while (iter.next()) - { - MenuItemIndex index = 0; - - int flags = kMenuAttrAutoDisable | kMenuItemAttrIgnoreMeta | kMenuItemAttrNotPreviousAlternate; - if (! iter.isEnabled) - flags |= kMenuItemAttrDisabled; - - CFStringRef text = PlatformUtilities::juceStringToCFString (iter.itemName.upToFirstOccurrenceOf (T(""), false, true)); - - if (iter.isSeparator) - { - AppendMenuItemTextWithCFString (m, text, kMenuItemAttrSeparator, 0, &index); - } - else if (iter.isSectionHeader) - { - AppendMenuItemTextWithCFString (m, text, kMenuItemAttrSectionHeader, 0, &index); - } - else if (iter.subMenu != 0) - { - AppendMenuItemTextWithCFString (m, text, flags, id++, &index); - - MenuRef sub = createMenu (*iter.subMenu, iter.itemName, id, topLevelIndex); - SetMenuItemHierarchicalMenu (m, index, sub); - CFRelease (sub); - } - else - { - AppendMenuItemTextWithCFString (m, text, flags, iter.itemId, &index); - - if (iter.isTicked) - CheckMenuItem (m, index, true); - - SetMenuItemProperty (m, index, 'juce', 'apcm', sizeof (void*), &iter.commandManager); - SetMenuItemProperty (m, index, 'juce', 'topi', sizeof (int), &topLevelIndex); - - if (iter.commandManager != 0) - { - const Array keyPresses (iter.commandManager->getKeyMappings() - ->getKeyPressesAssignedToCommand (iter.itemId)); - - if (keyPresses.size() > 0) - { - const KeyPress& kp = keyPresses.getUnchecked(0); - int mods = 0; - - if (kp.getModifiers().isShiftDown()) - mods |= kMenuShiftModifier; - if (kp.getModifiers().isCtrlDown()) - mods |= kMenuControlModifier; - if (kp.getModifiers().isAltDown()) - mods |= kMenuOptionModifier; - if (! kp.getModifiers().isCommandDown()) - mods |= kMenuNoCommandModifier; - - tchar keyCode = (tchar) kp.getKeyCode(); - - if (kp.getKeyCode() >= KeyPress::numberPad0 - && kp.getKeyCode() <= KeyPress::numberPad9) - { - keyCode = (tchar) ((T('0') - KeyPress::numberPad0) + kp.getKeyCode()); - } - - SetMenuItemCommandKey (m, index, true, 255); - - if (CharacterFunctions::isLetterOrDigit (keyCode) - || CharacterFunctions::indexOfChar (T(",.;/\\'[]=-+_<>?{}\":"), keyCode, false) >= 0) - { - SetMenuItemModifiers (m, index, mods); - SetMenuItemCommandKey (m, index, false, CharacterFunctions::toUpperCase (keyCode)); - } - else - { - const SInt16 glyph = getGlyphForKeyCode (kp.getKeyCode()); - - if (glyph != 0) - { - SetMenuItemModifiers (m, index, mods); - SetMenuItemKeyGlyph (m, index, glyph); - } - } - - // if we set the key glyph to be a text char, and enable virtual - // key triggering, it stops the menu automatically triggering the callback - ChangeMenuItemAttributes (m, index, kMenuItemAttrUseVirtualKey, 0); - } - } - } - - CFRelease (text); - } - } - - return m; - } - - static SInt16 getGlyphForKeyCode (const int keyCode) throw() - { - if (keyCode == KeyPress::spaceKey) - return kMenuSpaceGlyph; - else if (keyCode == KeyPress::returnKey) - return kMenuReturnGlyph; - else if (keyCode == KeyPress::escapeKey) - return kMenuEscapeGlyph; - else if (keyCode == KeyPress::backspaceKey) - return kMenuDeleteLeftGlyph; - else if (keyCode == KeyPress::leftKey) - return kMenuLeftArrowGlyph; - else if (keyCode == KeyPress::rightKey) - return kMenuRightArrowGlyph; - else if (keyCode == KeyPress::upKey) - return kMenuUpArrowGlyph; - else if (keyCode == KeyPress::downKey) - return kMenuDownArrowGlyph; - else if (keyCode == KeyPress::pageUpKey) - return kMenuPageUpGlyph; - else if (keyCode == KeyPress::pageDownKey) - return kMenuPageDownGlyph; - else if (keyCode == KeyPress::endKey) - return kMenuSoutheastArrowGlyph; - else if (keyCode == KeyPress::homeKey) - return kMenuNorthwestArrowGlyph; - else if (keyCode == KeyPress::deleteKey) - return kMenuDeleteRightGlyph; - else if (keyCode == KeyPress::tabKey) - return kMenuTabRightGlyph; - else if (keyCode == KeyPress::F1Key) - return kMenuF1Glyph; - else if (keyCode == KeyPress::F2Key) - return kMenuF2Glyph; - else if (keyCode == KeyPress::F3Key) - return kMenuF3Glyph; - else if (keyCode == KeyPress::F4Key) - return kMenuF4Glyph; - else if (keyCode == KeyPress::F5Key) - return kMenuF5Glyph; - else if (keyCode == KeyPress::F6Key) - return kMenuF6Glyph; - else if (keyCode == KeyPress::F7Key) - return kMenuF7Glyph; - else if (keyCode == KeyPress::F8Key) - return kMenuF8Glyph; - else if (keyCode == KeyPress::F9Key) - return kMenuF9Glyph; - else if (keyCode == KeyPress::F10Key) - return kMenuF10Glyph; - else if (keyCode == KeyPress::F11Key) - return kMenuF11Glyph; - else if (keyCode == KeyPress::F12Key) - return kMenuF12Glyph; - else if (keyCode == KeyPress::F13Key) - return kMenuF13Glyph; - else if (keyCode == KeyPress::F14Key) - return kMenuF14Glyph; - else if (keyCode == KeyPress::F15Key) - return kMenuF15Glyph; - - return 0; - } -}; - -void MenuBarModel::setMacMainMenu (MenuBarModel* newMenuBarModel) throw() -{ - if (getMacMainMenu() != newMenuBarModel) - { - if (newMenuBarModel == 0) - { - delete mainMenu; - jassert (mainMenu == 0); // should be zeroed in the destructor - } - else - { - if (mainMenu == 0) - mainMenu = new MainMenuHandler(); - - mainMenu->setMenu (newMenuBarModel); - } - } -} - -MenuBarModel* MenuBarModel::getMacMainMenu() throw() -{ - return mainMenu != 0 ? mainMenu->currentModel : 0; -} - -// these functions are called externally from the message handling code -void juce_MainMenuAboutToBeUsed() -{ - // force an update of the items just before the menu appears.. - if (mainMenu != 0) - mainMenu->menuBarItemsChanged (0); -} - -void juce_InvokeMainMenuCommand (const HICommand& command) -{ - if (mainMenu != 0) - { - ApplicationCommandManager* commandManager = 0; - int topLevelIndex = 0; - - if (GetMenuItemProperty (command.menu.menuRef, command.menu.menuItemIndex, - 'juce', 'apcm', sizeof (commandManager), 0, &commandManager) == noErr - && GetMenuItemProperty (command.menu.menuRef, command.menu.menuItemIndex, - 'juce', 'topi', sizeof (topLevelIndex), 0, &topLevelIndex) == noErr) - { - mainMenu->invoke (command.commandID, commandManager, topLevelIndex); - } - } -} - -//============================================================================== -void PlatformUtilities::beep() -{ - SysBeep (30); -} - -//============================================================================== -void SystemClipboard::copyTextToClipboard (const String& text) -{ - ClearCurrentScrap(); - ScrapRef ref; - GetCurrentScrap (&ref); - - const int len = text.length(); - const int numBytes = sizeof (UniChar) * len; - UniChar* const temp = (UniChar*) juce_calloc (numBytes); - - for (int i = 0; i < len; ++i) - temp[i] = (UniChar) text[i]; - - PutScrapFlavor (ref, - kScrapFlavorTypeUnicode, - kScrapFlavorMaskNone, - numBytes, - temp); - - juce_free (temp); -} - -const String SystemClipboard::getTextFromClipboard() -{ - String result; - - ScrapRef ref; - GetCurrentScrap (&ref); - Size size = 0; - - if (GetScrapFlavorSize (ref, kScrapFlavorTypeUnicode, &size) == noErr - && size > 0) - { - void* const data = juce_calloc (size + 8); - - if (GetScrapFlavorData (ref, kScrapFlavorTypeUnicode, &size, data) == noErr) - { - result = PlatformUtilities::convertUTF16ToString ((UniChar*) data); - } - - juce_free (data); - } - - return result; -} - - -//============================================================================== -bool AlertWindow::showNativeDialogBox (const String& title, - const String& bodyText, - bool isOkCancel) -{ - Str255 tit, txt; - PlatformUtilities::copyToStr255 (tit, title); - PlatformUtilities::copyToStr255 (txt, bodyText); - - AlertStdAlertParamRec ar; - ar.movable = true; - ar.helpButton = false; - ar.filterProc = 0; - ar.defaultText = (const unsigned char*)-1; - ar.cancelText = (const unsigned char*)((isOkCancel) ? -1 : 0); - ar.otherText = 0; - ar.defaultButton = kAlertStdAlertOKButton; - ar.cancelButton = 0; - ar.position = kWindowDefaultPosition; - - SInt16 result; - StandardAlert (kAlertNoteAlert, tit, txt, &ar, &result); - return result == kAlertStdAlertOKButton; -} - -//============================================================================== -const int KeyPress::spaceKey = ' '; -const int KeyPress::returnKey = kReturnCharCode; -const int KeyPress::escapeKey = kEscapeCharCode; -const int KeyPress::backspaceKey = kBackspaceCharCode; -const int KeyPress::leftKey = kLeftArrowCharCode; -const int KeyPress::rightKey = kRightArrowCharCode; -const int KeyPress::upKey = kUpArrowCharCode; -const int KeyPress::downKey = kDownArrowCharCode; -const int KeyPress::pageUpKey = kPageUpCharCode; -const int KeyPress::pageDownKey = kPageDownCharCode; -const int KeyPress::endKey = kEndCharCode; -const int KeyPress::homeKey = kHomeCharCode; -const int KeyPress::deleteKey = kDeleteCharCode; -const int KeyPress::insertKey = -1; -const int KeyPress::tabKey = kTabCharCode; -const int KeyPress::F1Key = 0x10110; -const int KeyPress::F2Key = 0x10111; -const int KeyPress::F3Key = 0x10112; -const int KeyPress::F4Key = 0x10113; -const int KeyPress::F5Key = 0x10114; -const int KeyPress::F6Key = 0x10115; -const int KeyPress::F7Key = 0x10116; -const int KeyPress::F8Key = 0x10117; -const int KeyPress::F9Key = 0x10118; -const int KeyPress::F10Key = 0x10119; -const int KeyPress::F11Key = 0x1011a; -const int KeyPress::F12Key = 0x1011b; -const int KeyPress::F13Key = 0x1011c; -const int KeyPress::F14Key = 0x1011d; -const int KeyPress::F15Key = 0x1011e; -const int KeyPress::F16Key = 0x1011f; -const int KeyPress::numberPad0 = 0x30020; -const int KeyPress::numberPad1 = 0x30021; -const int KeyPress::numberPad2 = 0x30022; -const int KeyPress::numberPad3 = 0x30023; -const int KeyPress::numberPad4 = 0x30024; -const int KeyPress::numberPad5 = 0x30025; -const int KeyPress::numberPad6 = 0x30026; -const int KeyPress::numberPad7 = 0x30027; -const int KeyPress::numberPad8 = 0x30028; -const int KeyPress::numberPad9 = 0x30029; -const int KeyPress::numberPadAdd = 0x3002a; -const int KeyPress::numberPadSubtract = 0x3002b; -const int KeyPress::numberPadMultiply = 0x3002c; -const int KeyPress::numberPadDivide = 0x3002d; -const int KeyPress::numberPadSeparator = 0x3002e; -const int KeyPress::numberPadDecimalPoint = 0x3002f; -const int KeyPress::numberPadEquals = 0x30030; -const int KeyPress::numberPadDelete = 0x30031; -const int KeyPress::playKey = 0x30000; -const int KeyPress::stopKey = 0x30001; -const int KeyPress::fastForwardKey = 0x30002; -const int KeyPress::rewindKey = 0x30003; - -//============================================================================== -#if JUCE_OPENGL - -struct OpenGLContextInfo -{ - AGLContext renderContext; -}; - -void* juce_createOpenGLContext (OpenGLComponent* component, void* sharedContext) -{ - jassert (component != 0); - - HIViewComponentPeer* const peer = dynamic_cast (component->getTopLevelComponent()->getPeer()); - - if (peer == 0) - return 0; - - OpenGLContextInfo* const oc = new OpenGLContextInfo(); - - GLint attrib[] = { AGL_RGBA, AGL_DOUBLEBUFFER, - AGL_RED_SIZE, 8, - AGL_ALPHA_SIZE, 8, - AGL_DEPTH_SIZE, 24, - AGL_CLOSEST_POLICY, AGL_NO_RECOVERY, - AGL_SAMPLE_BUFFERS_ARB, 1, - AGL_SAMPLES_ARB, 4, - AGL_NONE }; - - oc->renderContext = aglCreateContext (aglChoosePixelFormat (0, 0, attrib), - (sharedContext != 0) ? ((OpenGLContextInfo*) sharedContext)->renderContext - : 0); - - aglSetDrawable (oc->renderContext, - GetWindowPort (peer->windowRef)); - - return oc; -} - -void juce_updateOpenGLWindowPos (void* context, Component* owner, Component* topComp) -{ - jassert (context != 0); - OpenGLContextInfo* const oc = (OpenGLContextInfo*) context; - - GLint bufferRect[4]; - - bufferRect[0] = owner->getScreenX() - topComp->getScreenX(); - bufferRect[1] = topComp->getHeight() - (owner->getHeight() + owner->getScreenY() - topComp->getScreenY()); - bufferRect[2] = owner->getWidth(); - bufferRect[3] = owner->getHeight(); - - aglSetInteger (oc->renderContext, AGL_BUFFER_RECT, bufferRect); - aglEnable (oc->renderContext, AGL_BUFFER_RECT); -} - -void juce_deleteOpenGLContext (void* context) -{ - OpenGLContextInfo* const oc = (OpenGLContextInfo*) context; - - aglDestroyContext (oc->renderContext); - - delete oc; -} - -bool juce_makeOpenGLContextCurrent (void* context) -{ - OpenGLContextInfo* const oc = (OpenGLContextInfo*) context; - - return aglSetCurrentContext ((oc != 0) ? oc->renderContext : 0); -} - -void juce_swapOpenGLBuffers (void* context) -{ - OpenGLContextInfo* const oc = (OpenGLContextInfo*) context; - - if (oc != 0) - aglSwapBuffers (oc->renderContext); -} - -void juce_repaintOpenGLWindow (void* context) -{ -} - -#endif - -END_JUCE_NAMESPACE +/* + ============================================================================== + + 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 "../../../src/juce_core/basics/juce_StandardHeader.h" +#include +#include + +#if JUCE_OPENGL +#include +#endif + +BEGIN_JUCE_NAMESPACE + +#include "../../../src/juce_appframework/events/juce_Timer.h" +#include "../../../src/juce_appframework/application/juce_DeletedAtShutdown.h" +#include "../../../src/juce_appframework/events/juce_AsyncUpdater.h" +#include "../../../src/juce_appframework/events/juce_MessageManager.h" +#include "../../../src/juce_core/basics/juce_Singleton.h" +#include "../../../src/juce_core/basics/juce_Random.h" +#include "../../../src/juce_core/threads/juce_Process.h" +#include "../../../src/juce_appframework/application/juce_SystemClipboard.h" +#include "../../../src/juce_appframework/gui/components/keyboard/juce_KeyPress.h" +#include "../../../src/juce_appframework/gui/components/windows/juce_AlertWindow.h" +#include "../../../src/juce_appframework/gui/graphics/geometry/juce_RectangleList.h" +#include "../../../src/juce_appframework/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h" +#include "../../../src/juce_appframework/gui/components/juce_Desktop.h" +#include "../../../src/juce_appframework/gui/components/menus/juce_MenuBarModel.h" +#include "../../../src/juce_core/misc/juce_PlatformUtilities.h" +#include "../../../src/juce_appframework/application/juce_Application.h" +#include "../../../src/juce_appframework/gui/components/special/juce_OpenGLComponent.h" +#include "../../../src/juce_appframework/gui/components/mouse/juce_DragAndDropContainer.h" +#include "../../../src/juce_appframework/gui/components/keyboard/juce_KeyPressMappingSet.h" +#include "../../../src/juce_appframework/gui/graphics/imaging/juce_ImageFileFormat.h" + +#undef Point + +const WindowRegionCode windowRegionToUse = kWindowContentRgn; + +static HIObjectClassRef viewClassRef = 0; +static CFStringRef juceHiViewClassNameCFString = 0; +static ComponentPeer* juce_currentMouseTrackingPeer = 0; + + +//============================================================================== +static VoidArray keysCurrentlyDown; + +bool KeyPress::isKeyCurrentlyDown (int keyCode) +{ + if (keysCurrentlyDown.contains ((void*) keyCode)) + return true; + + if (keyCode >= 'A' && keyCode <= 'Z' + && keysCurrentlyDown.contains ((void*) (int) CharacterFunctions::toLowerCase ((tchar) keyCode))) + return true; + + if (keyCode >= 'a' && keyCode <= 'z' + && keysCurrentlyDown.contains ((void*) (int) CharacterFunctions::toUpperCase ((tchar) keyCode))) + return true; + + return false; +} + +//============================================================================== +static VoidArray minimisedWindows; + +static void setWindowMinimised (WindowRef ref, const bool isMinimised) +{ + if (isMinimised != minimisedWindows.contains (ref)) + CollapseWindow (ref, isMinimised); +} + +void juce_maximiseAllMinimisedWindows() +{ + const VoidArray minWin (minimisedWindows); + + for (int i = minWin.size(); --i >= 0;) + setWindowMinimised ((WindowRef) (minWin[i]), false); +} + +//============================================================================== +class HIViewComponentPeer; +static HIViewComponentPeer* currentlyFocusedPeer = 0; + + +//============================================================================== +static int currentModifiers = 0; + +static void updateModifiers (EventRef theEvent) +{ + currentModifiers &= ~ (ModifierKeys::shiftModifier | ModifierKeys::ctrlModifier + | ModifierKeys::altModifier | ModifierKeys::commandModifier); + + UInt32 m; + + if (theEvent != 0) + GetEventParameter (theEvent, kEventParamKeyModifiers, typeUInt32, 0, sizeof(m), 0, &m); + else + m = GetCurrentEventKeyModifiers(); + + if ((m & (shiftKey | rightShiftKey)) != 0) + currentModifiers |= ModifierKeys::shiftModifier; + + if ((m & (controlKey | rightControlKey)) != 0) + currentModifiers |= ModifierKeys::ctrlModifier; + + if ((m & (optionKey | rightOptionKey)) != 0) + currentModifiers |= ModifierKeys::altModifier; + + if ((m & cmdKey) != 0) + currentModifiers |= ModifierKeys::commandModifier; +} + +void ModifierKeys::updateCurrentModifiers() +{ + currentModifierFlags = currentModifiers; +} + +static int64 getEventTime (EventRef event) +{ + const int64 millis = (int64) (1000.0 * (event != 0 ? GetEventTime (event) + : GetCurrentEventTime())); + + static int64 offset = 0; + if (offset == 0) + offset = Time::currentTimeMillis() - millis; + + return offset + millis; +} + + +//============================================================================== +class MacBitmapImage : public Image +{ +public: + //============================================================================== + CGColorSpaceRef colourspace; + CGDataProviderRef provider; + + //============================================================================== + MacBitmapImage (const PixelFormat format_, + const int w, const int h, const bool clearImage) + : Image (format_, w, h) + { + jassert (format_ == RGB || format_ == ARGB); + + pixelStride = (format_ == RGB) ? 3 : 4; + + lineStride = (w * pixelStride + 3) & ~3; + const int imageSize = lineStride * h; + + if (clearImage) + imageData = (uint8*) juce_calloc (imageSize); + else + imageData = (uint8*) juce_malloc (imageSize); + + //colourspace = CGColorSpaceCreateWithName (kCGColorSpaceUserRGB); + + CMProfileRef prof; + CMGetSystemProfile (&prof); + colourspace = CGColorSpaceCreateWithPlatformColorSpace (prof); + + provider = CGDataProviderCreateWithData (0, imageData, h * lineStride, 0); + } + + MacBitmapImage::~MacBitmapImage() + { + CGDataProviderRelease (provider); + CGColorSpaceRelease (colourspace); + + juce_free (imageData); + imageData = 0; // to stop the base class freeing this + } + + void blitToContext (CGContextRef context, const float dx, const float dy) + { + CGImageRef tempImage = CGImageCreate (getWidth(), getHeight(), + 8, pixelStride << 3, lineStride, colourspace, +#if MACOS_10_3_OR_EARLIER || JUCE_BIG_ENDIAN + hasAlphaChannel() ? kCGImageAlphaPremultipliedFirst + : kCGImageAlphaNone, +#else + hasAlphaChannel() ? kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst + : kCGImageAlphaNone, +#endif + provider, 0, false, + kCGRenderingIntentDefault); + + HIRect r; + r.origin.x = dx; + r.origin.y = dy; + r.size.width = (float) getWidth(); + r.size.height = (float) getHeight(); + + HIViewDrawCGImage (context, &r, tempImage); + + CGImageRelease (tempImage); + } + + juce_UseDebuggingNewOperator +}; + + +//============================================================================== +class MouseCheckTimer : private Timer, + private DeletedAtShutdown +{ + HIViewComponentPeer* lastPeerUnderMouse; + int lastX, lastY; + +public: + MouseCheckTimer() + : lastX (0), + lastY (0) + { + lastPeerUnderMouse = 0; + resetMouseMoveChecker(); + } + + ~MouseCheckTimer() + { + clearSingletonInstance(); + } + + juce_DeclareSingleton_SingleThreaded_Minimal (MouseCheckTimer) + + bool hasEverHadAMouseMove; + + void moved (HIViewComponentPeer* const peer) + { + if (hasEverHadAMouseMove) + startTimer (200); + + lastPeerUnderMouse = peer; + } + + void resetMouseMoveChecker() + { + hasEverHadAMouseMove = false; + startTimer (1000 / 16); + } + + void timerCallback(); +}; + +juce_ImplementSingleton_SingleThreaded (MouseCheckTimer) + +//============================================================================== +#if JUCE_QUICKTIME +extern void OfferMouseClickToQuickTime (WindowRef window, ::Point where, long when, long modifiers, + Component* topLevelComp); +#endif + + +//============================================================================== +class HIViewComponentPeer : public ComponentPeer, + private Timer +{ +public: + //============================================================================== + HIViewComponentPeer (Component* const component, + const int windowStyleFlags, + HIViewRef viewToAttachTo) + : ComponentPeer (component, windowStyleFlags), + fullScreen (false), + isCompositingWindow (false), + windowRef (0), + viewRef (0) + { + repainter = new RepaintManager (this); + + eventHandlerRef = 0; + + if (viewToAttachTo != 0) + { + isSharedWindow = true; + } + else + { + isSharedWindow = false; + + WindowRef newWindow = createNewWindow (windowStyleFlags); + + GetRootControl (newWindow, (ControlRef*) &viewToAttachTo); + jassert (viewToAttachTo != 0); + + HIViewRef growBox = 0; + HIViewFindByID (HIViewGetRoot (newWindow), kHIViewWindowGrowBoxID, &growBox); + + if (growBox != 0) + HIGrowBoxViewSetTransparent (growBox, true); + } + + createNewHIView(); + + HIViewAddSubview (viewToAttachTo, viewRef); + HIViewSetVisible (viewRef, component->isVisible()); + + setTitle (component->getName()); + + if (component->isVisible() && ! isSharedWindow) + { + ShowWindow (windowRef); + ActivateWindow (windowRef, component->getWantsKeyboardFocus()); + } + } + + ~HIViewComponentPeer() + { + minimisedWindows.removeValue (windowRef); + + if (IsValidWindowPtr (windowRef)) + { + if (! isSharedWindow) + { + CFRelease (viewRef); + viewRef = 0; + + DisposeWindow (windowRef); + } + else + { + if (eventHandlerRef != 0) + RemoveEventHandler (eventHandlerRef); + + CFRelease (viewRef); + viewRef = 0; + } + + windowRef = 0; + } + + if (currentlyFocusedPeer == this) + currentlyFocusedPeer = 0; + + delete repainter; + } + + //============================================================================== + void* getNativeHandle() const + { + return windowRef; + } + + void setVisible (bool shouldBeVisible) + { + HIViewSetVisible (viewRef, shouldBeVisible); + + if ((! isSharedWindow) && IsValidWindowPtr (windowRef)) + { + if (shouldBeVisible) + ShowWindow (windowRef); + else + HideWindow (windowRef); + + resizeViewToFitWindow(); + + // If nothing else is focused, then grab the focus too + if (shouldBeVisible + && Component::getCurrentlyFocusedComponent() == 0 + && Process::isForegroundProcess()) + { + component->toFront (true); + } + } + } + + void setTitle (const String& title) + { + if ((! isSharedWindow) && IsValidWindowPtr (windowRef)) + { + CFStringRef t = PlatformUtilities::juceStringToCFString (title); + SetWindowTitleWithCFString (windowRef, t); + CFRelease (t); + } + } + + void setPosition (int x, int y) + { + if (isSharedWindow) + { + HIViewPlaceInSuperviewAt (viewRef, x, y); + } + else if (IsValidWindowPtr (windowRef)) + { + Rect r; + GetWindowBounds (windowRef, windowRegionToUse, &r); + r.right += x - r.left; + r.bottom += y - r.top; + r.left = x; + r.top = y; + SetWindowBounds (windowRef, windowRegionToUse, &r); + } + } + + void setSize (int w, int h) + { + w = jmax (0, w); + h = jmax (0, h); + + if (w != getComponent()->getWidth() + || h != getComponent()->getHeight()) + { + repainter->repaint (0, 0, w, h); + } + + if (isSharedWindow) + { + HIRect r; + HIViewGetFrame (viewRef, &r); + r.size.width = (float) w; + r.size.height = (float) h; + HIViewSetFrame (viewRef, &r); + } + else if (IsValidWindowPtr (windowRef)) + { + Rect r; + GetWindowBounds (windowRef, windowRegionToUse, &r); + r.right = r.left + w; + r.bottom = r.top + h; + SetWindowBounds (windowRef, windowRegionToUse, &r); + } + } + + void setBounds (int x, int y, int w, int h, const bool isNowFullScreen) + { + fullScreen = isNowFullScreen; + w = jmax (0, w); + h = jmax (0, h); + + if (w != getComponent()->getWidth() + || h != getComponent()->getHeight()) + { + repainter->repaint (0, 0, w, h); + } + + if (isSharedWindow) + { + HIRect r; + r.origin.x = (float) x; + r.origin.y = (float) y; + r.size.width = (float) w; + r.size.height = (float) h; + HIViewSetFrame (viewRef, &r); + } + else if (IsValidWindowPtr (windowRef)) + { + Rect r; + r.left = x; + r.top = y; + r.right = x + w; + r.bottom = y + h; + SetWindowBounds (windowRef, windowRegionToUse, &r); + } + } + + void getBounds (int& x, int& y, int& w, int& h, const bool global) const + { + HIRect hiViewPos; + HIViewGetFrame (viewRef, &hiViewPos); + + if (global) + { + HIViewRef content = 0; + HIViewFindByID (HIViewGetRoot (windowRef), kHIViewWindowContentID, &content); + HIPoint p = { 0.0f, 0.0f }; + HIViewConvertPoint (&p, viewRef, content); + + x = (int) p.x; + y = (int) p.y; + + if (IsValidWindowPtr (windowRef)) + { + Rect windowPos; + GetWindowBounds (windowRef, kWindowContentRgn, &windowPos); + + x += windowPos.left; + y += windowPos.top; + } + } + else + { + x = (int) hiViewPos.origin.x; + y = (int) hiViewPos.origin.y; + } + + w = (int) hiViewPos.size.width; + h = (int) hiViewPos.size.height; + } + + void getBounds (int& x, int& y, int& w, int& h) const + { + getBounds (x, y, w, h, ! isSharedWindow); + } + + int getScreenX() const + { + int x, y, w, h; + getBounds (x, y, w, h, true); + return x; + } + + int getScreenY() const + { + int x, y, w, h; + getBounds (x, y, w, h, true); + return y; + } + + void relativePositionToGlobal (int& x, int& y) + { + int wx, wy, ww, wh; + getBounds (wx, wy, ww, wh, true); + + x += wx; + y += wy; + } + + void globalPositionToRelative (int& x, int& y) + { + int wx, wy, ww, wh; + getBounds (wx, wy, ww, wh, true); + + x -= wx; + y -= wy; + } + + void setMinimised (bool shouldBeMinimised) + { + if (! isSharedWindow) + setWindowMinimised (windowRef, shouldBeMinimised); + } + + bool isMinimised() const + { + return minimisedWindows.contains (windowRef); + } + + void setFullScreen (bool shouldBeFullScreen) + { + if (! isSharedWindow) + { + setMinimised (false); + + if (fullScreen != shouldBeFullScreen) + { + Rectangle r (lastNonFullscreenBounds); + + if (shouldBeFullScreen) + r = Desktop::getInstance().getMainMonitorArea(); + + // (can't call the component's setBounds method because that'll reset our fullscreen flag) + if (r != getComponent()->getBounds() && ! r.isEmpty()) + setBounds (r.getX(), r.getY(), r.getWidth(), r.getHeight(), shouldBeFullScreen); + } + } + } + + bool isFullScreen() const + { + return fullScreen; + } + + bool contains (int x, int y, bool trueIfInAChildWindow) const + { + if (x < 0 || y < 0 + || x >= component->getWidth() || y >= component->getHeight() + || ! IsValidWindowPtr (windowRef)) + return false; + + Rect r; + GetWindowBounds (windowRef, windowRegionToUse, &r); + + ::Point p; + p.h = r.left + x; + p.v = r.top + y; + + WindowRef ref2 = 0; + FindWindow (p, &ref2); + + if (windowRef != ref2) + return false; + + if (trueIfInAChildWindow) + return true; + + HIPoint p2; + p2.x = (float) x; + p2.y = (float) y; + HIViewRef hit; + + HIViewGetSubviewHit (viewRef, &p2, true, &hit); + return hit == 0 || hit == viewRef; + } + + const BorderSize getFrameSize() const + { + return BorderSize(); + } + + bool setAlwaysOnTop (bool alwaysOnTop) + { + // can't do this so return false and let the component create a new window + return false; + } + + void toFront (bool makeActiveWindow) + { + makeActiveWindow = makeActiveWindow + && component->isValidComponent() + && (component->getWantsKeyboardFocus() + || component->isCurrentlyModal()); + + if (windowRef != FrontWindow() + || (makeActiveWindow && ! IsWindowActive (windowRef)) + || ! Process::isForegroundProcess()) + { + if (! Process::isForegroundProcess()) + { + ProcessSerialNumber psn; + GetCurrentProcess (&psn); + SetFrontProcess (&psn); + } + + if (IsValidWindowPtr (windowRef)) + { + if (makeActiveWindow) + { + SelectWindow (windowRef); + SetUserFocusWindow (windowRef); + HIViewAdvanceFocus (viewRef, 0); + } + else + { + BringToFront (windowRef); + } + + handleBroughtToFront(); + } + } + } + + void toBehind (ComponentPeer* other) + { + HIViewComponentPeer* const otherWindow = dynamic_cast (other); + + if (other != 0 && windowRef != 0 && otherWindow->windowRef != 0) + { + if (windowRef == otherWindow->windowRef) + { + HIViewSetZOrder (viewRef, kHIViewZOrderBelow, otherWindow->viewRef); + } + else + { + SendBehind (windowRef, otherWindow->windowRef); + } + } + } + + void setIcon (const Image& /*newIcon*/) + { + // to do.. + } + + //============================================================================== + void viewFocusGain() + { + const MessageManagerLock messLock; + + if (currentlyFocusedPeer != this) + { + if (ComponentPeer::isValidPeer (currentlyFocusedPeer)) + currentlyFocusedPeer->handleFocusLoss(); + + currentlyFocusedPeer = this; + + handleFocusGain(); + } + } + + void viewFocusLoss() + { + if (currentlyFocusedPeer == this) + { + currentlyFocusedPeer = 0; + handleFocusLoss(); + } + } + + bool isFocused() const + { + return windowRef == GetUserFocusWindow() + && HIViewSubtreeContainsFocus (viewRef); + } + + void grabFocus() + { + if ((! isFocused()) && IsValidWindowPtr (windowRef)) + { + SetUserFocusWindow (windowRef); + HIViewAdvanceFocus (viewRef, 0); + } + } + + //============================================================================== + void repaint (int x, int y, int w, int h) + { + if (Rectangle::intersectRectangles (x, y, w, h, + 0, 0, + getComponent()->getWidth(), + getComponent()->getHeight())) + { + if ((getStyleFlags() & windowRepaintedExplictly) == 0) + { + if (isCompositingWindow) + { +#if MACOS_10_3_OR_EARLIER + RgnHandle rgn = NewRgn(); + SetRectRgn (rgn, x, y, x + w, y + h); + HIViewSetNeedsDisplayInRegion (viewRef, rgn, true); + DisposeRgn (rgn); +#else + HIRect r; + r.origin.x = x; + r.origin.y = y; + r.size.width = w; + r.size.height = h; + + HIViewSetNeedsDisplayInRect (viewRef, &r, true); +#endif + } + else + { + if (! isTimerRunning()) + startTimer (20); + } + } + + repainter->repaint (x, y, w, h); + } + } + + void timerCallback() + { + performAnyPendingRepaintsNow(); + } + + void performAnyPendingRepaintsNow() + { + stopTimer(); + + if (component->isVisible()) + { +#if MACOS_10_2_OR_EARLIER + if (! isCompositingWindow) + { + Rect w; + GetWindowBounds (windowRef, windowRegionToUse, &w); + + RgnHandle rgn = NewRgn(); + SetRectRgn (rgn, 0, 0, w.right - w.left, w.bottom - w.top); + UpdateControls (windowRef, rgn); + DisposeRgn (rgn); + } + else + { + EventRef theEvent; + + EventTypeSpec eventTypes[1]; + eventTypes[0].eventClass = kEventClassControl; + eventTypes[0].eventKind = kEventControlDraw; + + int n = 3; + while (--n >= 0 + && ReceiveNextEvent (1, eventTypes, kEventDurationNoWait, true, &theEvent) == noErr) + { + if (GetEventClass (theEvent) == kEventClassAppleEvent) + { + EventRecord eventRec; + if (ConvertEventRefToEventRecord (theEvent, &eventRec)) + AEProcessAppleEvent (&eventRec); + } + else + { + EventTargetRef theTarget = GetEventDispatcherTarget(); + SendEventToEventTarget (theEvent, theTarget); + } + + ReleaseEvent (theEvent); + } + } +#else + HIViewRender (viewRef); +#endif + } + } + + //============================================================================== + juce_UseDebuggingNewOperator + + WindowRef windowRef; + HIViewRef viewRef; + +private: + EventHandlerRef eventHandlerRef; + bool fullScreen, isSharedWindow, isCompositingWindow; + + //============================================================================== + class RepaintManager : public Timer + { +public: + RepaintManager (HIViewComponentPeer* const peer_) + : peer (peer_), + image (0) + { + } + + ~RepaintManager() + { + delete image; + } + + void timerCallback() + { + stopTimer(); + deleteAndZero (image); + } + + void repaint (int x, int y, int w, int h) + { + regionsNeedingRepaint.add (x, y, w, h); + } + + void repaintAnyRemainingRegions() + { + // if any regions have been invaldated during the paint callback, + // we need to repaint them explicitly because the mac throws this + // stuff away + for (RectangleList::Iterator i (regionsNeedingRepaint); i.next();) + { + const Rectangle& r = *i.getRectangle(); + peer->repaint (r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + } + + void paint (CGContextRef cgContext, int x, int y, int w, int h) + { + if (w > 0 && h > 0) + { + bool refresh = false; + int imW = image != 0 ? image->getWidth() : 0; + int imH = image != 0 ? image->getHeight() : 0; + + if (imW < w || imH < h) + { + imW = jmin (peer->getComponent()->getWidth(), (w + 31) & ~31); + imH = jmin (peer->getComponent()->getHeight(), (h + 31) & ~31); + + delete image; + image = new MacBitmapImage (peer->getComponent()->isOpaque() ? Image::RGB + : Image::ARGB, + imW, imH, false); + + refresh = true; + } + else if (imageX > x || imageY > y + || imageX + imW < x + w + || imageY + imH < y + h) + { + refresh = true; + } + + if (refresh) + { + regionsNeedingRepaint.clear(); + regionsNeedingRepaint.addWithoutMerging (Rectangle (x, y, imW, imH)); + imageX = x; + imageY = y; + } + + LowLevelGraphicsSoftwareRenderer context (*image); + context.setOrigin (-imageX, -imageY); + + if (context.reduceClipRegion (regionsNeedingRepaint)) + { + regionsNeedingRepaint.clear(); + + if (! peer->getComponent()->isOpaque()) + { + for (RectangleList::Iterator i (*context.getRawClipRegion()); i.next();) + { + const Rectangle& r = *i.getRectangle(); + image->clear (r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + } + + regionsNeedingRepaint.clear(); + peer->clearMaskedRegion(); + peer->handlePaint (context); + } + else + { + regionsNeedingRepaint.clear(); + } + + if (! peer->maskedRegion.isEmpty()) + { + RectangleList total (Rectangle (x, y, w, h)); + total.subtract (peer->maskedRegion); + + CGRect* rects = (CGRect*) juce_malloc (sizeof (CGRect) * total.getNumRectangles()); + int n = 0; + + for (RectangleList::Iterator i (total); i.next();) + { + const Rectangle& r = *i.getRectangle(); + rects[n].origin.x = (int) r.getX(); + rects[n].origin.y = (int) r.getY(); + rects[n].size.width = roundFloatToInt (r.getWidth()); + rects[n++].size.height = roundFloatToInt (r.getHeight()); + } + + CGContextClipToRects (cgContext, rects, n); + juce_free (rects); + } + + if (peer->isSharedWindow) + { + CGRect clip; + clip.origin.x = x; + clip.origin.y = y; + clip.size.width = jmin (w, peer->getComponent()->getWidth() - x); + clip.size.height = jmin (h, peer->getComponent()->getHeight() - y); + + CGContextClipToRect (cgContext, clip); + } + + image->blitToContext (cgContext, imageX, imageY); + } + + startTimer (3000); + } + + private: + HIViewComponentPeer* const peer; + MacBitmapImage* image; + int imageX, imageY; + RectangleList regionsNeedingRepaint; + + RepaintManager (const RepaintManager&); + const RepaintManager& operator= (const RepaintManager&); + }; + + RepaintManager* repainter; + + friend class RepaintManager; + + //============================================================================== + static OSStatus handleFrameRepaintEvent (EventHandlerCallRef myHandler, + EventRef theEvent, + void* userData) + { + // don't draw the frame.. + return noErr; + } + + //============================================================================== + OSStatus handleWindowClassEvent (EventRef theEvent) + { + switch (GetEventKind (theEvent)) + { + case kEventWindowBoundsChanged: + resizeViewToFitWindow(); + break; // allow other handlers in the event chain to also get a look at the events + + case kEventWindowBoundsChanging: + if ((styleFlags & (windowIsResizable | windowHasTitleBar)) == (windowIsResizable | windowHasTitleBar)) + { + UInt32 atts = 0; + GetEventParameter (theEvent, kEventParamAttributes, typeUInt32, + 0, sizeof (UInt32), 0, &atts); + + if ((atts & (kWindowBoundsChangeUserDrag | kWindowBoundsChangeUserResize)) != 0) + { + if (component->isCurrentlyBlockedByAnotherModalComponent()) + { + Component* const modal = Component::getCurrentlyModalComponent(); + if (modal != 0) + modal->inputAttemptWhenModal(); + } + + if ((atts & kWindowBoundsChangeUserResize) != 0 + && constrainer != 0 && ! isSharedWindow) + { + Rect current; + GetEventParameter (theEvent, kEventParamCurrentBounds, typeQDRectangle, + 0, sizeof (Rect), 0, ¤t); + + int x = current.left; + int y = current.top; + int w = current.right - current.left; + int h = current.bottom - current.top; + + const Rectangle currentRect (getComponent()->getBounds()); + + constrainer->checkBounds (x, y, w, h, currentRect, + Desktop::getInstance().getAllMonitorDisplayAreas().getBounds(), + y != currentRect.getY() && y + h == currentRect.getBottom(), + x != currentRect.getX() && x + w == currentRect.getRight(), + y == currentRect.getY() && y + h != currentRect.getBottom(), + x == currentRect.getX() && x + w != currentRect.getRight()); + + current.left = x; + current.top = y; + current.right = x + w; + current.bottom = y + h; + + SetEventParameter (theEvent, kEventParamCurrentBounds, typeQDRectangle, + sizeof (Rect), ¤t); + + return noErr; + } + } + } + break; + + case kEventWindowFocusAcquired: + keysCurrentlyDown.clear(); + + if ((! isSharedWindow) || HIViewSubtreeContainsFocus (viewRef)) + viewFocusGain(); + + break; // allow other handlers in the event chain to also get a look at the events + + case kEventWindowFocusRelinquish: + keysCurrentlyDown.clear(); + viewFocusLoss(); + + break; // allow other handlers in the event chain to also get a look at the events + + case kEventWindowCollapsed: + minimisedWindows.addIfNotAlreadyThere (windowRef); + handleMovedOrResized(); + break; // allow other handlers in the event chain to also get a look at the events + + case kEventWindowExpanded: + minimisedWindows.removeValue (windowRef); + handleMovedOrResized(); + break; // allow other handlers in the event chain to also get a look at the events + + case kEventWindowShown: + break; // allow other handlers in the event chain to also get a look at the events + + case kEventWindowClose: + if (isSharedWindow) + break; // break to let the OS delete the window + + handleUserClosingWindow(); + return noErr; // avoids letting the OS to delete the window, we'll do that ourselves. + + default: + break; + } + + return eventNotHandledErr; + } + + OSStatus handleKeyEvent (EventRef theEvent, juce_wchar textCharacter) + { + updateModifiers (theEvent); + + UniChar unicodeChars [4]; + zeromem (unicodeChars, sizeof (unicodeChars)); + GetEventParameter (theEvent, kEventParamKeyUnicodes, typeUnicodeText, 0, sizeof (unicodeChars), 0, unicodeChars); + + int keyCode = (int) (unsigned int) unicodeChars[0]; + + UInt32 rawKey = 0; + GetEventParameter (theEvent, kEventParamKeyCode, typeUInt32, 0, sizeof (UInt32), 0, &rawKey); + + if ((currentModifiers & ModifierKeys::ctrlModifier) != 0) + { + if (keyCode >= 1 && keyCode <= 26) + keyCode += ('A' - 1); + } + + static const int keyTranslations[] = + { + 0, 's', 'd', 'f', 'h', 'g', 'z', 'x', 'c', 'v', 0xa7, 'b', + 'q', 'w', 'e', 'r', 'y', 't', '1', '2', '3', '4', '6', '5', + '=', '9', '7', '-', '8', '0', ']', 'o', 'u', '[', 'i', 'p', + KeyPress::returnKey, 'l', 'j', '\'', 'k', ';', '\\', ',', '/', + 'n', 'm', '.', 0, KeyPress::spaceKey, '`', KeyPress::backspaceKey, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, KeyPress::numberPadDecimalPoint, + 0, KeyPress::numberPadMultiply, 0, KeyPress::numberPadAdd, + 0, KeyPress::numberPadDelete, 0, 0, 0, KeyPress::numberPadDivide, KeyPress::returnKey, + 0, KeyPress::numberPadSubtract, 0, 0, KeyPress::numberPadEquals, KeyPress::numberPad0, + KeyPress::numberPad1, KeyPress::numberPad2, KeyPress::numberPad3, + KeyPress::numberPad4, KeyPress::numberPad5, KeyPress::numberPad6, + KeyPress::numberPad7, 0, KeyPress::numberPad8, KeyPress::numberPad9, + 0, 0, 0, KeyPress::F5Key, KeyPress::F6Key, KeyPress::F7Key, KeyPress::F3Key, + KeyPress::F8Key, KeyPress::F9Key, 0, KeyPress::F11Key, 0, KeyPress::F13Key, + KeyPress::F16Key, KeyPress::F14Key, 0, KeyPress::F10Key, 0, KeyPress::F12Key, + 0, KeyPress::F15Key, 0, KeyPress::homeKey, KeyPress::pageUpKey, 0, KeyPress::F4Key, + KeyPress::endKey, KeyPress::F2Key, KeyPress::pageDownKey, KeyPress::F1Key, + KeyPress::leftKey, KeyPress::rightKey, KeyPress::downKey, KeyPress::upKey, 0 + }; + + if (rawKey > 0 && rawKey < numElementsInArray (keyTranslations)) + keyCode = keyTranslations [rawKey]; + else if (rawKey == 0 && textCharacter != 0) + keyCode = 'a'; + + if ((currentModifiers & (ModifierKeys::commandModifier | ModifierKeys::ctrlModifier)) != 0) + textCharacter = 0; + + static juce_wchar lastTextCharacter = 0; + + switch (GetEventKind (theEvent)) + { + case kEventRawKeyDown: + { + keysCurrentlyDown.addIfNotAlreadyThere ((void*) keyCode); + lastTextCharacter = textCharacter; + + const bool used1 = handleKeyUpOrDown(); + const bool used2 = handleKeyPress (keyCode, textCharacter); + + return (used1 || used2) ? noErr : eventNotHandledErr; + } + + case kEventRawKeyUp: + keysCurrentlyDown.removeValue ((void*) keyCode); + lastTextCharacter = 0; + return handleKeyUpOrDown() ? noErr : eventNotHandledErr; + + case kEventRawKeyRepeat: + return handleKeyPress (keyCode, lastTextCharacter) ? noErr + : eventNotHandledErr; + + case kEventRawKeyModifiersChanged: + handleModifierKeysChange(); + break; + + default: + jassertfalse + break; + } + + return eventNotHandledErr; + } + + OSStatus handleTextInputEvent (EventRef theEvent) + { + UniChar uc; + GetEventParameter (theEvent, kEventParamTextInputSendText, typeUnicodeText, 0, sizeof (uc), 0, &uc); + + EventRef originalEvent; + GetEventParameter (theEvent, kEventParamTextInputSendKeyboardEvent, typeEventRef, 0, sizeof (originalEvent), 0, &originalEvent); + + return handleKeyEvent (originalEvent, (juce_wchar) uc); + } + + OSStatus handleMouseEvent (EventHandlerCallRef callRef, EventRef theEvent) + { + MouseCheckTimer::getInstance()->moved (this); + + ::Point where; + GetEventParameter (theEvent, kEventParamMouseLocation, typeQDPoint, 0, sizeof (::Point), 0, &where); + int x = where.h; + int y = where.v; + globalPositionToRelative (x, y); + + int64 time = getEventTime (theEvent); + + switch (GetEventKind (theEvent)) + { + case kEventMouseMoved: + MouseCheckTimer::getInstance()->hasEverHadAMouseMove = true; + updateModifiers (theEvent); + handleMouseMove (x, y, time); + break; + + case kEventMouseDragged: + updateModifiers (theEvent); + handleMouseDrag (x, y, time); + break; + + case kEventMouseDown: + { + if (! Process::isForegroundProcess()) + { + ProcessSerialNumber psn; + GetCurrentProcess (&psn); + SetFrontProcess (&psn); + + toFront (true); + } + +#if JUCE_QUICKTIME + { + long mods; + GetEventParameter (theEvent, kEventParamKeyModifiers, typeUInt32, 0, sizeof (mods), 0, &mods); + + ::Point where; + GetEventParameter (theEvent, kEventParamMouseLocation, typeQDPoint, 0, sizeof (::Point), 0, &where); + + OfferMouseClickToQuickTime (windowRef, where, EventTimeToTicks (GetEventTime (theEvent)), mods, component); + } +#endif + + if (component->isBroughtToFrontOnMouseClick() + && ! component->isCurrentlyBlockedByAnotherModalComponent()) + { + //ActivateWindow (windowRef, true); + SelectWindow (windowRef); + } + + EventMouseButton button; + GetEventParameter (theEvent, kEventParamMouseButton, typeMouseButton, 0, sizeof (EventMouseButton), 0, &button); + + // need to clear all these flags because sometimes the mac can swallow (right) mouse-up events and + // this makes a button get stuck down. Since there's no other way to tell what buttons are down, + // this is all I can think of doing about it.. + currentModifiers &= ~(ModifierKeys::leftButtonModifier | ModifierKeys::rightButtonModifier | ModifierKeys::middleButtonModifier); + + if (button == kEventMouseButtonPrimary) + currentModifiers |= ModifierKeys::leftButtonModifier; + else if (button == kEventMouseButtonSecondary) + currentModifiers |= ModifierKeys::rightButtonModifier; + else if (button == kEventMouseButtonTertiary) + currentModifiers |= ModifierKeys::middleButtonModifier; + + updateModifiers (theEvent); + + juce_currentMouseTrackingPeer = this; // puts the message dispatcher into mouse-tracking mode.. + handleMouseDown (x, y, time); + break; + } + + case kEventMouseUp: + { + const int oldModifiers = currentModifiers; + + EventMouseButton button; + GetEventParameter (theEvent, kEventParamMouseButton, typeMouseButton, 0, sizeof (EventMouseButton), 0, &button); + + if (button == kEventMouseButtonPrimary) + currentModifiers &= ~ModifierKeys::leftButtonModifier; + else if (button == kEventMouseButtonSecondary) + currentModifiers &= ~ModifierKeys::rightButtonModifier; + + updateModifiers (theEvent); + + juce_currentMouseTrackingPeer = 0; + handleMouseUp (oldModifiers, x, y, time); + break; + } + + case kEventMouseWheelMoved: + { + EventMouseWheelAxis axis; + GetEventParameter (theEvent, kEventParamMouseWheelAxis, typeMouseWheelAxis, 0, sizeof (axis), 0, &axis); + + SInt32 delta; + GetEventParameter (theEvent, kEventParamMouseWheelDelta, + typeLongInteger, 0, sizeof (delta), 0, &delta); + + updateModifiers (theEvent); + + handleMouseWheel (axis == kEventMouseWheelAxisX ? delta * 10 : 0, + axis == kEventMouseWheelAxisX ? 0 : delta * 10, + time); + + break; + } + } + + return noErr; + } + + OSStatus handleDragAndDrop (EventRef theEvent) + { + DragRef dragRef; + if (GetEventParameter (theEvent, kEventParamDragRef, typeDragRef, 0, sizeof (dragRef), 0, &dragRef) == noErr) + { + int mx, my; + component->getMouseXYRelative (mx, my); + + UInt16 numItems = 0; + if (CountDragItems (dragRef, &numItems) == noErr) + { + StringArray filenames; + + for (int i = 0; i < (int) numItems; ++i) + { + DragItemRef ref; + + if (GetDragItemReferenceNumber (dragRef, i + 1, &ref) == noErr) + { + const FlavorType flavorType = kDragFlavorTypeHFS; + + Size size = 0; + if (GetFlavorDataSize (dragRef, ref, flavorType, &size) == noErr) + { + void* data = juce_calloc (size); + + if (GetFlavorData (dragRef, ref, flavorType, data, &size, 0) == noErr) + { + HFSFlavor* f = (HFSFlavor*) data; + FSRef fsref; + + if (FSpMakeFSRef (&f->fileSpec, &fsref) == noErr) + { + const String path (PlatformUtilities::makePathFromFSRef (&fsref)); + + if (path.isNotEmpty()) + filenames.add (path); + } + } + + juce_free (data); + } + } + } + + filenames.trim(); + filenames.removeEmptyStrings(); + + if (filenames.size() > 0) + handleFilesDropped (mx, my, filenames); + } + } + + return noErr; + } + + void resizeViewToFitWindow() + { + HIRect r; + + if (isSharedWindow) + { + HIViewGetFrame (viewRef, &r); + r.size.width = (float) component->getWidth(); + r.size.height = (float) component->getHeight(); + } + else + { + r.origin.x = 0; + r.origin.y = 0; + + Rect w; + GetWindowBounds (windowRef, windowRegionToUse, &w); + + r.size.width = (float) (w.right - w.left); + r.size.height = (float) (w.bottom - w.top); + } + + HIViewSetFrame (viewRef, &r); + +#if MACOS_10_3_OR_EARLIER + component->repaint(); +#endif + } + + OSStatus hiViewDraw (EventRef theEvent) + { + CGContextRef context = 0; + GetEventParameter (theEvent, kEventParamCGContextRef, typeCGContextRef, 0, sizeof (CGContextRef), 0, &context); + + CGrafPtr oldPort; + CGrafPtr port = 0; + + if (context == 0) + { + GetEventParameter (theEvent, kEventParamGrafPort, typeGrafPtr, 0, sizeof (CGrafPtr), 0, &port); + + GetPort (&oldPort); + SetPort (port); + + if (port != 0) + QDBeginCGContext (port, &context); + + if (! isCompositingWindow) + { + Rect bounds; + GetWindowBounds (windowRef, windowRegionToUse, &bounds); + CGContextTranslateCTM (context, 0, bounds.bottom - bounds.top); + CGContextScaleCTM (context, 1.0, -1.0); + } + + if (isSharedWindow) + { + // NB - Had terrible problems trying to correctly get the position + // of this view relative to the window, and this seems wrong, but + // works better than any other method I've tried.. + HIRect hiViewPos; + HIViewGetFrame (viewRef, &hiViewPos); + CGContextTranslateCTM (context, hiViewPos.origin.x, hiViewPos.origin.y); + } + } + +#if MACOS_10_2_OR_EARLIER + RgnHandle rgn = 0; + GetEventParameter (theEvent, kEventParamRgnHandle, typeQDRgnHandle, 0, sizeof (RgnHandle), 0, &rgn); + + CGRect clip; + + if (rgn != 0) + { + Rect bounds; + GetRegionBounds (rgn, &bounds); + clip.origin.x = bounds.left; + clip.origin.y = bounds.top; + clip.size.width = bounds.right - bounds.left; + clip.size.height = bounds.bottom - bounds.top; + } + else + { + HIViewGetBounds (viewRef, &clip); + } +#else + CGRect clip (CGContextGetClipBoundingBox (context)); +#endif + + clip = CGRectIntegral (clip); + + if (clip.origin.x < 0) + { + clip.size.width += clip.origin.x; + clip.origin.x = 0; + } + + if (clip.origin.y < 0) + { + clip.size.height += clip.origin.y; + clip.origin.y = 0; + } + + if (! component->isOpaque()) + CGContextClearRect (context, clip); + + repainter->paint (context, + (int) clip.origin.x, (int) clip.origin.y, + (int) clip.size.width, (int) clip.size.height); + + if (port != 0) + { + CGContextFlush (context); + QDEndCGContext (port, &context); + + SetPort (oldPort); + } + + repainter->repaintAnyRemainingRegions(); + + return noErr; + } + + static pascal OSStatus handleWindowEvent (EventHandlerCallRef callRef, EventRef theEvent, void* userData) + { + MessageManager::delayWaitCursor(); + + HIViewComponentPeer* const peer = (HIViewComponentPeer*) userData; + + const MessageManagerLock messLock; + + if (ComponentPeer::isValidPeer (peer)) + return peer->handleWindowEventForPeer (callRef, theEvent); + + return eventNotHandledErr; + } + + OSStatus handleWindowEventForPeer (EventHandlerCallRef callRef, EventRef theEvent) + { + switch (GetEventClass (theEvent)) + { + case kEventClassMouse: + { + static HIViewComponentPeer* lastMouseDownPeer = 0; + + const UInt32 eventKind = GetEventKind (theEvent); + HIViewRef view = 0; + + if (eventKind == kEventMouseDragged) + { + view = viewRef; + } + else + { + HIViewGetViewForMouseEvent (HIViewGetRoot (windowRef), theEvent, &view); + + if (view != viewRef) + { + if ((eventKind == kEventMouseUp + || eventKind == kEventMouseExited) + && ComponentPeer::isValidPeer (lastMouseDownPeer)) + { + return lastMouseDownPeer->handleMouseEvent (callRef, theEvent); + } + + return eventNotHandledErr; + } + } + + if (eventKind == kEventMouseDown + || eventKind == kEventMouseDragged + || eventKind == kEventMouseEntered) + { + lastMouseDownPeer = this; + } + + return handleMouseEvent (callRef, theEvent); + } + break; + + case kEventClassWindow: + return handleWindowClassEvent (theEvent); + + case kEventClassKeyboard: + if (isFocused()) + return handleKeyEvent (theEvent, 0); + + break; + + case kEventClassTextInput: + if (isFocused()) + return handleTextInputEvent (theEvent); + + break; + + default: + break; + } + + return eventNotHandledErr; + } + + static pascal OSStatus hiViewEventHandler (EventHandlerCallRef myHandler, EventRef theEvent, void* userData) + { + MessageManager::delayWaitCursor(); + + const UInt32 eventKind = GetEventKind (theEvent); + const UInt32 eventClass = GetEventClass (theEvent); + + if (eventClass == kEventClassHIObject) + { + switch (eventKind) + { + case kEventHIObjectConstruct: + { + void* data = juce_calloc (sizeof (void*)); + SetEventParameter (theEvent, kEventParamHIObjectInstance, + typeVoidPtr, sizeof (void*), &data); + + return noErr; + } + + case kEventHIObjectInitialize: + GetEventParameter (theEvent, 'peer', typeVoidPtr, 0, sizeof (void*), 0, (void**) userData); + return noErr; + + case kEventHIObjectDestruct: + juce_free (userData); + return noErr; + + default: + break; + } + } + else if (eventClass == kEventClassControl) + { + HIViewComponentPeer* const peer = *(HIViewComponentPeer**) userData; + const MessageManagerLock messLock; + + if (! ComponentPeer::isValidPeer (peer)) + return eventNotHandledErr; + + switch (eventKind) + { + case kEventControlDraw: + return peer->hiViewDraw (theEvent); + + case kEventControlBoundsChanged: + { + HIRect bounds; + HIViewGetBounds (peer->viewRef, &bounds); + peer->repaint (0, 0, roundFloatToInt (bounds.size.width), roundFloatToInt (bounds.size.height)); + + peer->handleMovedOrResized(); + return noErr; + } + + case kEventControlHitTest: + { + HIPoint where; + GetEventParameter (theEvent, kEventParamMouseLocation, typeHIPoint, 0, sizeof (HIPoint), 0, &where); + + HIRect bounds; + HIViewGetBounds (peer->viewRef, &bounds); + + ControlPartCode part = kControlNoPart; + + if (CGRectContainsPoint (bounds, where)) + part = 1; + + SetEventParameter (theEvent, kEventParamControlPart, typeControlPartCode, sizeof (ControlPartCode), &part); + return noErr; + } + break; + + case kEventControlSetFocusPart: + { + ControlPartCode desiredFocus; + if (GetEventParameter (theEvent, kEventParamControlPart, typeControlPartCode, 0, sizeof (ControlPartCode), 0, &desiredFocus) != noErr) + break; + + if (desiredFocus == kControlNoPart) + peer->viewFocusLoss(); + else + peer->viewFocusGain(); + + return noErr; + } + break; + + case kEventControlDragEnter: + { +#if MACOS_10_2_OR_EARLIER + enum { kEventParamControlWouldAcceptDrop = 'cldg' }; +#endif + Boolean accept = true; + SetEventParameter (theEvent, kEventParamControlWouldAcceptDrop, typeBoolean, sizeof (accept), &accept); + return noErr; + } + + case kEventControlDragWithin: + return noErr; + + case kEventControlDragReceive: + return peer->handleDragAndDrop (theEvent); + + case kEventControlOwningWindowChanged: + return peer->ownerWindowChanged (theEvent); + +#if ! MACOS_10_2_OR_EARLIER + case kEventControlGetFrameMetrics: + { + CallNextEventHandler (myHandler, theEvent); + HIViewFrameMetrics metrics; + GetEventParameter (theEvent, kEventParamControlFrameMetrics, typeControlFrameMetrics, 0, sizeof (metrics), 0, &metrics); + metrics.top = metrics.bottom = 0; + SetEventParameter (theEvent, kEventParamControlFrameMetrics, typeControlFrameMetrics, sizeof (metrics), &metrics); + return noErr; + } +#endif + + case kEventControlInitialize: + { + UInt32 features = kControlSupportsDragAndDrop + | kControlSupportsFocus + | kControlHandlesTracking + | kControlSupportsEmbedding + | (1 << 8) /*kHIViewFeatureGetsFocusOnClick*/; + + SetEventParameter (theEvent, kEventParamControlFeatures, typeUInt32, sizeof (UInt32), &features); + return noErr; + } + + default: + break; + } + } + + return eventNotHandledErr; + } + + WindowRef createNewWindow (const int windowStyleFlags) + { + jassert (windowRef == 0); + + static ToolboxObjectClassRef customWindowClass = 0; + + if (customWindowClass == 0) + { + // Register our window class + const EventTypeSpec customTypes[] = { { kEventClassWindow, kEventWindowDrawFrame } }; + + UnsignedWide t; + Microseconds (&t); + const String randomString ((int) (t.lo & 0x7ffffff)); + const String juceWindowClassName (T("JUCEWindowClass_") + randomString); + CFStringRef juceWindowClassNameCFString = PlatformUtilities::juceStringToCFString (juceWindowClassName); + + RegisterToolboxObjectClass (juceWindowClassNameCFString, + 0, 1, customTypes, + NewEventHandlerUPP (handleFrameRepaintEvent), + 0, &customWindowClass); + + CFRelease (juceWindowClassNameCFString); + } + + Rect pos; + pos.left = getComponent()->getX(); + pos.top = getComponent()->getY(); + pos.right = getComponent()->getRight(); + pos.bottom = getComponent()->getBottom(); + + int attributes = kWindowStandardHandlerAttribute | kWindowCompositingAttribute; + if ((windowStyleFlags & windowHasDropShadow) == 0) + attributes |= kWindowNoShadowAttribute; + + if ((windowStyleFlags & windowIgnoresMouseClicks) != 0) + attributes |= kWindowIgnoreClicksAttribute; + +#if ! MACOS_10_3_OR_EARLIER + if ((windowStyleFlags & windowIsTemporary) != 0) + attributes |= kWindowDoesNotCycleAttribute; +#endif + + WindowRef newWindow = 0; + + if ((windowStyleFlags & windowHasTitleBar) == 0) + { + attributes |= kWindowCollapseBoxAttribute; + + WindowDefSpec customWindowSpec; + customWindowSpec.defType = kWindowDefObjectClass; + customWindowSpec.u.classRef = customWindowClass; + + CreateCustomWindow (&customWindowSpec, + ((windowStyleFlags & windowIsTemporary) != 0) ? kUtilityWindowClass : + (getComponent()->isAlwaysOnTop() ? kUtilityWindowClass + : kDocumentWindowClass), + attributes, + &pos, + &newWindow); + } + else + { + if ((windowStyleFlags & windowHasCloseButton) != 0) + attributes |= kWindowCloseBoxAttribute; + + if ((windowStyleFlags & windowHasMinimiseButton) != 0) + attributes |= kWindowCollapseBoxAttribute; + + if ((windowStyleFlags & windowHasMaximiseButton) != 0) + attributes |= kWindowFullZoomAttribute; + + if ((windowStyleFlags & windowIsResizable) != 0) + attributes |= kWindowResizableAttribute | kWindowLiveResizeAttribute; + + CreateNewWindow (kDocumentWindowClass, attributes, &pos, &newWindow); + } + + jassert (newWindow != 0); + if (newWindow != 0) + { + HideWindow (newWindow); + + SetAutomaticControlDragTrackingEnabledForWindow (newWindow, true); + + if (! getComponent()->isOpaque()) + SetWindowAlpha (newWindow, 0.9999999f); // to fool it into giving the window an alpha-channel + } + + return newWindow; + } + + OSStatus ownerWindowChanged (EventRef theEvent) + { + WindowRef newWindow = 0; + GetEventParameter (theEvent, kEventParamControlCurrentOwningWindow, typeWindowRef, 0, sizeof (newWindow), 0, &newWindow); + + if (windowRef != newWindow) + { + if (eventHandlerRef != 0) + { + RemoveEventHandler (eventHandlerRef); + eventHandlerRef = 0; + } + + windowRef = newWindow; + + if (windowRef != 0) + { + const EventTypeSpec eventTypes[] = + { + { kEventClassWindow, kEventWindowBoundsChanged }, + { kEventClassWindow, kEventWindowBoundsChanging }, + { kEventClassWindow, kEventWindowFocusAcquired }, + { kEventClassWindow, kEventWindowFocusRelinquish }, + { kEventClassWindow, kEventWindowCollapsed }, + { kEventClassWindow, kEventWindowExpanded }, + { kEventClassWindow, kEventWindowShown }, + { kEventClassWindow, kEventWindowClose }, + { kEventClassMouse, kEventMouseDown }, + { kEventClassMouse, kEventMouseUp }, + { kEventClassMouse, kEventMouseMoved }, + { kEventClassMouse, kEventMouseDragged }, + { kEventClassMouse, kEventMouseEntered }, + { kEventClassMouse, kEventMouseExited }, + { kEventClassMouse, kEventMouseWheelMoved }, + { kEventClassKeyboard, kEventRawKeyUp }, + { kEventClassKeyboard, kEventRawKeyRepeat }, + { kEventClassKeyboard, kEventRawKeyModifiersChanged }, + { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent } + }; + + static EventHandlerUPP handleWindowEventUPP = 0; + + if (handleWindowEventUPP == 0) + handleWindowEventUPP = NewEventHandlerUPP (handleWindowEvent); + + InstallWindowEventHandler (windowRef, handleWindowEventUPP, + GetEventTypeCount (eventTypes), eventTypes, + (void*) this, (EventHandlerRef*) &eventHandlerRef); + + WindowAttributes attributes; + GetWindowAttributes (windowRef, &attributes); + +#if MACOS_10_3_OR_EARLIER + isCompositingWindow = ((attributes & kWindowCompositingAttribute) != 0); +#else + isCompositingWindow = HIViewIsCompositingEnabled (viewRef); +#endif + + MouseCheckTimer::getInstance()->resetMouseMoveChecker(); + } + } + + resizeViewToFitWindow(); + return noErr; + } + + void createNewHIView() + { + jassert (viewRef == 0); + + if (viewClassRef == 0) + { + // Register our HIView class + EventTypeSpec viewEvents[] = + { + { kEventClassHIObject, kEventHIObjectConstruct }, + { kEventClassHIObject, kEventHIObjectInitialize }, + { kEventClassHIObject, kEventHIObjectDestruct }, + { kEventClassControl, kEventControlInitialize }, + { kEventClassControl, kEventControlDraw }, + { kEventClassControl, kEventControlBoundsChanged }, + { kEventClassControl, kEventControlSetFocusPart }, + { kEventClassControl, kEventControlHitTest }, + { kEventClassControl, kEventControlDragEnter }, + { kEventClassControl, kEventControlDragWithin }, + { kEventClassControl, kEventControlDragReceive }, + { kEventClassControl, kEventControlOwningWindowChanged } + }; + + UnsignedWide t; + Microseconds (&t); + const String randomString ((int) (t.lo & 0x7ffffff)); + const String juceHiViewClassName (T("JUCEHIViewClass_") + randomString); + juceHiViewClassNameCFString = PlatformUtilities::juceStringToCFString (juceHiViewClassName); + + HIObjectRegisterSubclass (juceHiViewClassNameCFString, + kHIViewClassID, 0, + NewEventHandlerUPP (hiViewEventHandler), + GetEventTypeCount (viewEvents), + viewEvents, 0, + &viewClassRef); + } + + EventRef event; + CreateEvent (0, kEventClassHIObject, kEventHIObjectInitialize, GetCurrentEventTime(), kEventAttributeNone, &event); + + void* thisPointer = this; + SetEventParameter (event, 'peer', typeVoidPtr, sizeof (void*), &thisPointer); + + HIObjectCreate (juceHiViewClassNameCFString, event, (HIObjectRef*) &viewRef); + + SetControlDragTrackingEnabled (viewRef, true); + + if (isSharedWindow) + { + setBounds (component->getX(), component->getY(), + component->getWidth(), component->getHeight(), false); + } + } +}; + +bool juce_isHIViewCreatedByJuce (HIViewRef view) +{ + return juceHiViewClassNameCFString != 0 + && HIObjectIsOfClass ((HIObjectRef) view, juceHiViewClassNameCFString); +} + +static void trackNextMouseEvent() +{ + UInt32 mods; + MouseTrackingResult result; + ::Point where; + + if (TrackMouseLocationWithOptions ((GrafPtr) -1, 0, 0.01, //kEventDurationForever, + &where, &mods, &result) != noErr + || ! ComponentPeer::isValidPeer (juce_currentMouseTrackingPeer)) + { + juce_currentMouseTrackingPeer = 0; + return; + } + + if (result == kMouseTrackingTimedOut) + return; + +#if MACOS_10_3_OR_EARLIER + const int x = where.h - juce_currentMouseTrackingPeer->getScreenX(); + const int y = where.v - juce_currentMouseTrackingPeer->getScreenY(); +#else + HIPoint p; + p.x = where.h; + p.y = where.v; + HIPointConvert (&p, kHICoordSpaceScreenPixel, 0, + kHICoordSpaceView, ((HIViewComponentPeer*) juce_currentMouseTrackingPeer)->viewRef); + const int x = p.x; + const int y = p.y; +#endif + + if (result == kMouseTrackingMouseDragged) + { + updateModifiers (0); + juce_currentMouseTrackingPeer->handleMouseDrag (x, y, getEventTime (0)); + + if (! ComponentPeer::isValidPeer (juce_currentMouseTrackingPeer)) + { + juce_currentMouseTrackingPeer = 0; + return; + } + } + else if (result == kMouseTrackingMouseUp + || result == kMouseTrackingUserCancelled + || result == kMouseTrackingMouseMoved) + { + ComponentPeer* const oldPeer = juce_currentMouseTrackingPeer; + juce_currentMouseTrackingPeer = 0; + + if (ComponentPeer::isValidPeer (oldPeer)) + { + const int oldModifiers = currentModifiers; + currentModifiers &= ~(ModifierKeys::leftButtonModifier | ModifierKeys::rightButtonModifier | ModifierKeys::middleButtonModifier); + updateModifiers (0); + + oldPeer->handleMouseUp (oldModifiers, x, y, getEventTime (0)); + } + } +} + +bool juce_dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages) +{ + if (juce_currentMouseTrackingPeer != 0) + trackNextMouseEvent(); + + EventRef theEvent; + + if (ReceiveNextEvent (0, 0, (returnIfNoPendingMessages) ? kEventDurationNoWait + : kEventDurationForever, + true, &theEvent) == noErr) + { + if (GetEventClass (theEvent) == kEventClassAppleEvent) + { + EventRecord eventRec; + if (ConvertEventRefToEventRecord (theEvent, &eventRec)) + AEProcessAppleEvent (&eventRec); + } + else + { + EventTargetRef theTarget = GetEventDispatcherTarget(); + SendEventToEventTarget (theEvent, theTarget); + } + + ReleaseEvent (theEvent); + return true; + } + + return false; +} + +//============================================================================== +ComponentPeer* Component::createNewPeer (int styleFlags, void* windowToAttachTo) +{ + return new HIViewComponentPeer (this, styleFlags, (HIViewRef) windowToAttachTo); +} + +//============================================================================== +void MouseCheckTimer::timerCallback() +{ + if (ModifierKeys::getCurrentModifiersRealtime().isAnyMouseButtonDown()) + return; + + if (Process::isForegroundProcess()) + { + bool stillOver = false; + int x = 0, y = 0, w = 0, h = 0; + int mx = 0, my = 0; + const bool validWindow = ComponentPeer::isValidPeer (lastPeerUnderMouse); + + if (validWindow) + { + lastPeerUnderMouse->getBounds (x, y, w, h, true); + Desktop::getMousePosition (mx, my); + + stillOver = (mx >= x && my >= y && mx < x + w && my < y + h); + + if (stillOver) + { + // check if it's over an embedded HIView + int rx = mx, ry = my; + lastPeerUnderMouse->globalPositionToRelative (rx, ry); + HIPoint hipoint; + hipoint.x = rx; + hipoint.y = ry; + + HIViewRef root; + GetRootControl ((WindowRef) lastPeerUnderMouse->getNativeHandle(), &root); + + HIViewRef hitview; + if (HIViewGetSubviewHit (root, &hipoint, true, &hitview) == noErr && hitview != 0) + { + stillOver = HIObjectIsOfClass ((HIObjectRef) hitview, juceHiViewClassNameCFString); + } + } + } + + if (! stillOver) + { + // mouse is outside our windows so set a normal cursor (only + // if we're running as an app, not a plugin) + if (JUCEApplication::getInstance() != 0) + SetThemeCursor (kThemeArrowCursor); + + if (validWindow) + lastPeerUnderMouse->handleMouseExit (mx - x, my - y, Time::currentTimeMillis()); + + if (hasEverHadAMouseMove) + stopTimer(); + } + + if ((! hasEverHadAMouseMove) && validWindow + && (mx != lastX || my != lastY)) + { + lastX = mx; + lastY = my; + + if (stillOver) + lastPeerUnderMouse->handleMouseMove (mx - x, my - y, Time::currentTimeMillis()); + } + } +} + +//============================================================================== +// called from juce_Messaging.cpp +void juce_HandleProcessFocusChange() +{ + keysCurrentlyDown.clear(); + + if (HIViewComponentPeer::isValidPeer (currentlyFocusedPeer)) + { + if (Process::isForegroundProcess()) + currentlyFocusedPeer->handleFocusGain(); + else + currentlyFocusedPeer->handleFocusLoss(); + } +} + +static bool performDrag (DragRef drag) +{ + EventRecord event; + event.what = mouseDown; + event.message = 0; + event.when = TickCount(); + + int x, y; + Desktop::getMousePosition (x, y); + event.where.h = x; + event.where.v = y; + + event.modifiers = GetCurrentKeyModifiers(); + + RgnHandle rgn = NewRgn(); + RgnHandle rgn2 = NewRgn(); + SetRectRgn (rgn, + event.where.h - 8, event.where.v - 8, + event.where.h + 8, event.where.v + 8); + CopyRgn (rgn, rgn2); + InsetRgn (rgn2, 1, 1); + DiffRgn (rgn, rgn2, rgn); + DisposeRgn (rgn2); + + bool result = TrackDrag (drag, &event, rgn) == noErr; + + DisposeRgn (rgn); + return result; +} + +bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, const bool canMoveFiles) +{ + for (int i = ComponentPeer::getNumPeers(); --i >= 0;) + ComponentPeer::getPeer (i)->performAnyPendingRepaintsNow(); + + DragRef drag; + bool result = false; + + if (NewDrag (&drag) == noErr) + { + for (int i = 0; i < files.size(); ++i) + { + HFSFlavor hfsData; + + if (PlatformUtilities::makeFSSpecFromPath (&hfsData.fileSpec, files[i])) + { + FInfo info; + if (FSpGetFInfo (&hfsData.fileSpec, &info) == noErr) + { + hfsData.fileType = info.fdType; + hfsData.fileCreator = info.fdCreator; + hfsData.fdFlags = info.fdFlags; + + AddDragItemFlavor (drag, i + 1, kDragFlavorTypeHFS, &hfsData, sizeof (hfsData), 0); + result = true; + } + } + } + + SetDragAllowableActions (drag, canMoveFiles ? kDragActionAll + : kDragActionCopy, false); + + if (result) + result = performDrag (drag); + + DisposeDrag (drag); + } + + return result; +} + +bool DragAndDropContainer::performExternalDragDropOfText (const String& text) +{ + jassertfalse // not implemented! + return false; +} + + +//============================================================================== +bool Process::isForegroundProcess() throw() +{ + ProcessSerialNumber psn, front; + GetCurrentProcess (&psn); + GetFrontProcess (&front); + + Boolean b; + return (SameProcess (&psn, &front, &b) == noErr) && b; +} + +//============================================================================== +bool Desktop::canUseSemiTransparentWindows() throw() +{ + return true; +} + + +//============================================================================== +void Desktop::getMousePosition (int& x, int& y) throw() +{ + CGrafPtr currentPort; + GetPort (¤tPort); + + if (! IsValidPort (currentPort)) + { + WindowRef front = FrontWindow(); + + if (front != 0) + { + SetPortWindowPort (front); + } + else + { + x = y = 0; + return; + } + } + + ::Point p; + GetMouse (&p); + LocalToGlobal (&p); + x = p.h; + y = p.v; + + SetPort (currentPort); +} + +void Desktop::setMousePosition (int x, int y) throw() +{ + CGPoint pos = { x, y }; + CGWarpMouseCursorPosition (pos); +} + +const ModifierKeys ModifierKeys::getCurrentModifiersRealtime() throw() +{ + return ModifierKeys (currentModifiers); +} + +//============================================================================== +void juce_updateMultiMonitorInfo (Array & monitorCoords, const bool clipToWorkArea) throw() +{ + int mainMonitorIndex = 0; + CGDirectDisplayID mainDisplayID = CGMainDisplayID(); + + CGDisplayCount count = 0; + CGDirectDisplayID disps [8]; + + if (CGGetActiveDisplayList (numElementsInArray (disps), disps, &count) == noErr) + { + for (int i = 0; i < count; ++i) + { + if (mainDisplayID == disps[i]) + mainMonitorIndex = monitorCoords.size(); + + GDHandle hGDevice; + + if (clipToWorkArea + && DMGetGDeviceByDisplayID ((DisplayIDType) disps[i], &hGDevice, false) == noErr) + { + Rect rect; + GetAvailableWindowPositioningBounds (hGDevice, &rect); + + monitorCoords.add (Rectangle (rect.left, + rect.top, + rect.right - rect.left, + rect.bottom - rect.top)); + } + else + { + const CGRect r (CGDisplayBounds (disps[i])); + + monitorCoords.add (Rectangle (r.origin.x, + r.origin.y, + r.size.width, + r.size.height)); + } + } + } + + // make sure the first in the list is the main monitor + if (mainMonitorIndex > 0) + monitorCoords.swap (mainMonitorIndex, 0); + + jassert (monitorCoords.size() > 0); // xxx seems like this can happen when the screen's in power-saving mode.. + + if (monitorCoords.size() == 0) + monitorCoords.add (Rectangle (0, 0, 1024, 768)); + + //xxx need to register for display change callbacks +} + +//============================================================================== +struct CursorWrapper +{ + Cursor* cursor; + ThemeCursor themeCursor; +}; + +void* juce_createMouseCursorFromImage (const Image& image, int hotspotX, int hotspotY) throw() +{ + const int maxW = 16; + const int maxH = 16; + + const Image* im = ℑ + Image* newIm = 0; + + if (image.getWidth() > maxW || image.getHeight() > maxH) + { + im = newIm = image.createCopy (maxW, maxH); + + hotspotX = (hotspotX * maxW) / image.getWidth(); + hotspotY = (hotspotY * maxH) / image.getHeight(); + } + + Cursor* const c = new Cursor(); + c->hotSpot.h = hotspotX; + c->hotSpot.v = hotspotY; + + for (int y = 0; y < maxH; ++y) + { + c->data[y] = 0; + c->mask[y] = 0; + + for (int x = 0; x < maxW; ++x) + { + const Colour pixelColour (im->getPixelAt (15 - x, y)); + + if (pixelColour.getAlpha() > 0.5f) + { + c->mask[y] |= (1 << x); + + if (pixelColour.getBrightness() < 0.5f) + c->data[y] |= (1 << x); + } + } + + c->data[y] = CFSwapInt16BigToHost (c->data[y]); + c->mask[y] = CFSwapInt16BigToHost (c->mask[y]); + } + + if (newIm != 0) + delete newIm; + + CursorWrapper* const cw = new CursorWrapper(); + cw->cursor = c; + cw->themeCursor = kThemeArrowCursor; + return (void*) cw; +} + +static void* cursorFromData (const unsigned char* data, const int size, int hx, int hy) throw() +{ + Image* const im = ImageFileFormat::loadFrom ((const char*) data, size); + jassert (im != 0); + void* curs = juce_createMouseCursorFromImage (*im, hx, hy); + delete im; + return curs; +} + +const unsigned int kSpecialNoCursor = 'nocr'; + +void* juce_createStandardMouseCursor (MouseCursor::StandardCursorType type) throw() +{ + ThemeCursor id = kThemeArrowCursor; + + switch (type) + { + case MouseCursor::NormalCursor: + id = kThemeArrowCursor; + break; + + case MouseCursor::NoCursor: + id = kSpecialNoCursor; + break; + + case MouseCursor::DraggingHandCursor: + { + static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,2,0,0,0,0,255,255,255,0, + 0,0,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, + 16,0,0,2,52,148,47,0,200,185,16,130,90,12,74,139,107,84,123,39, + 132,117,151,116,132,146,248,60,209,138,98,22,203,114,34,236,37,52,77,217, + 247,154,191,119,110,240,193,128,193,95,163,56,60,234,98,135,2,0,59 }; + const int cursDataSize = 99; + + return cursorFromData (cursData, cursDataSize, 8, 8); + } + break; + + case MouseCursor::CopyingCursor: + id = kThemeCopyArrowCursor; + break; + + case MouseCursor::WaitCursor: + id = kThemeWatchCursor; + break; + + case MouseCursor::IBeamCursor: + id = kThemeIBeamCursor; + break; + + case MouseCursor::PointingHandCursor: + id = kThemePointingHandCursor; + break; + + case MouseCursor::LeftRightResizeCursor: + case MouseCursor::LeftEdgeResizeCursor: + case MouseCursor::RightEdgeResizeCursor: + { + static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,0,0,255,255,255,0,0,0,255, + 255,255,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, + 16,0,0,2,38,148,143,169,203,237,15,19,0,106,202,64,111,22,32,224, + 9,78,30,213,121,230,121,146,99,8,142,71,183,189,152,20,27,86,132,231, + 58,83,0,0,59 }; + const int cursDataSize = 85; + + return cursorFromData (cursData, cursDataSize, 8, 8); + } + + case MouseCursor::UpDownResizeCursor: + case MouseCursor::TopEdgeResizeCursor: + case MouseCursor::BottomEdgeResizeCursor: + { + static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,0,0,255,255,255,0,0,0,255, + 255,255,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, + 16,0,0,2,38,148,111,128,187,16,202,90,152,48,10,55,169,189,192,245, + 106,121,27,34,142,201,99,158,224,86,154,109,216,61,29,155,105,180,61,190, + 121,84,0,0,59 }; + const int cursDataSize = 85; + + return cursorFromData (cursData, cursDataSize, 8, 8); + } + + case MouseCursor::TopLeftCornerResizeCursor: + case MouseCursor::BottomRightCornerResizeCursor: + { + static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,0,0,255,255,255,0,0,0,255, + 255,255,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, + 16,0,0,2,43,132,15,162,187,16,255,18,99,14,202,217,44,158,213,221, + 237,9,225,38,94,35,73,5,31,42,170,108,106,174,112,43,195,209,91,185, + 104,174,131,208,77,66,28,10,0,59 }; + const int cursDataSize = 90; + + return cursorFromData (cursData, cursDataSize, 8, 8); + } + + case MouseCursor::TopRightCornerResizeCursor: + case MouseCursor::BottomLeftCornerResizeCursor: + { + static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,0,0,255,255,255,0,0,0,255, + 255,255,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, + 16,0,0,2,45,148,127,160,11,232,16,98,108,14,65,73,107,194,122,223, + 92,65,141,216,145,134,162,153,221,25,128,73,166,62,173,16,203,237,188,94, + 120,46,237,105,239,123,48,80,157,2,0,59 }; + const int cursDataSize = 92; + + return cursorFromData (cursData, cursDataSize, 8, 8); + } + + case MouseCursor::UpDownLeftRightResizeCursor: + { + static const unsigned char cursData[] = {71,73,70,56,57,97,15,0,15,0,145,0,0,0,0,0,255,255,255,0, + 128,128,255,255,255,33,249,4,1,0,0,3,0,44,0,0,0,0,15,0, + 15,0,0,2,46,156,63,129,139,1,202,26,152,48,186,73,109,114,65,85, + 195,37,143,88,93,29,215,101,23,198,178,30,149,158,25,56,134,97,179,61, + 158,213,126,203,234,99,220,34,56,70,1,0,59,0,0 }; + const int cursDataSize = 93; + + return cursorFromData (cursData, cursDataSize, 7, 7); + } + + case MouseCursor::CrosshairCursor: + id = kThemeCrossCursor; + break; + } + + CursorWrapper* cw = new CursorWrapper(); + cw->cursor = 0; + cw->themeCursor = id; + + return (void*) cw; +} + +void juce_deleteMouseCursor (void* const cursorHandle, const bool isStandard) throw() +{ + CursorWrapper* const cw = (CursorWrapper*) cursorHandle; + + if (cw != 0) + { + delete cw->cursor; + delete cw; + } +} + +void MouseCursor::showInAllWindows() const throw() +{ + showInWindow (0); +} + +void MouseCursor::showInWindow (ComponentPeer*) const throw() +{ + const CursorWrapper* const cw = (CursorWrapper*) getHandle(); + + if (cw != 0) + { + static bool isCursorHidden = false; + static bool showingWaitCursor = false; + const bool shouldShowWaitCursor = (cw->themeCursor == kThemeWatchCursor); + const bool shouldHideCursor = (cw->themeCursor == kSpecialNoCursor); + + if (shouldShowWaitCursor != showingWaitCursor + && Process::isForegroundProcess()) + { + showingWaitCursor = shouldShowWaitCursor; + QDDisplayWaitCursor (shouldShowWaitCursor); + } + + if (shouldHideCursor != isCursorHidden) + { + isCursorHidden = shouldHideCursor; + + if (shouldHideCursor) + HideCursor(); + else + ShowCursor(); + } + + if (cw->cursor != 0) + SetCursor (cw->cursor); + else if (! (shouldShowWaitCursor || shouldHideCursor)) + SetThemeCursor (cw->themeCursor); + } +} + +//============================================================================== +Image* juce_createIconForFile (const File& file) +{ + return 0; +} + + +//============================================================================== +class MainMenuHandler; +static MainMenuHandler* mainMenu = 0; + +class MainMenuHandler : private MenuBarModelListener, + private DeletedAtShutdown +{ +public: + MainMenuHandler() throw() + : currentModel (0) + { + } + + ~MainMenuHandler() throw() + { + setMenu (0); + + jassert (mainMenu == this); + mainMenu = 0; + } + + void setMenu (MenuBarModel* const newMenuBarModel) throw() + { + if (currentModel != newMenuBarModel) + { + if (currentModel != 0) + currentModel->removeListener (this); + + currentModel = newMenuBarModel; + + if (currentModel != 0) + currentModel->addListener (this); + + menuBarItemsChanged (0); + } + } + + void menuBarItemsChanged (MenuBarModel*) + { + ClearMenuBar(); + + if (currentModel != 0) + { + int id = 1000; + const StringArray menuNames (currentModel->getMenuBarNames()); + + for (int i = 0; i < menuNames.size(); ++i) + { + const PopupMenu menu (currentModel->getMenuForIndex (i, menuNames [i])); + + MenuRef m = createMenu (menu, menuNames [i], id, i); + + InsertMenu (m, 0); + CFRelease (m); + } + } + } + + void menuCommandInvoked (MenuBarModel*, const ApplicationCommandTarget::InvocationInfo& info) + { + MenuRef menu = 0; + MenuItemIndex index = 0; + GetIndMenuItemWithCommandID (0, info.commandID, 1, &menu, &index); + + FlashMenuBar (GetMenuID (menu)); + FlashMenuBar (GetMenuID (menu)); + } + + void invoke (const int id, ApplicationCommandManager* const commandManager, const int topLevelIndex) const + { + if (currentModel != 0) + { + if (commandManager != 0) + { + ApplicationCommandTarget::InvocationInfo info (id); + info.invocationMethod = ApplicationCommandTarget::InvocationInfo::fromMenu; + + commandManager->invoke (info, true); + } + + currentModel->menuItemSelected (id, topLevelIndex); + } + } + + MenuBarModel* currentModel; + +private: + static MenuRef createMenu (const PopupMenu menu, + const String& menuName, + int& id, + const int topLevelIndex) + { + MenuRef m = 0; + + if (CreateNewMenu (id++, kMenuAttrAutoDisable, &m) == noErr) + { + CFStringRef name = PlatformUtilities::juceStringToCFString (menuName); + SetMenuTitleWithCFString (m, name); + CFRelease (name); + + PopupMenu::MenuItemIterator iter (menu); + + while (iter.next()) + { + MenuItemIndex index = 0; + + int flags = kMenuAttrAutoDisable | kMenuItemAttrIgnoreMeta | kMenuItemAttrNotPreviousAlternate; + if (! iter.isEnabled) + flags |= kMenuItemAttrDisabled; + + CFStringRef text = PlatformUtilities::juceStringToCFString (iter.itemName.upToFirstOccurrenceOf (T(""), false, true)); + + if (iter.isSeparator) + { + AppendMenuItemTextWithCFString (m, text, kMenuItemAttrSeparator, 0, &index); + } + else if (iter.isSectionHeader) + { + AppendMenuItemTextWithCFString (m, text, kMenuItemAttrSectionHeader, 0, &index); + } + else if (iter.subMenu != 0) + { + AppendMenuItemTextWithCFString (m, text, flags, id++, &index); + + MenuRef sub = createMenu (*iter.subMenu, iter.itemName, id, topLevelIndex); + SetMenuItemHierarchicalMenu (m, index, sub); + CFRelease (sub); + } + else + { + AppendMenuItemTextWithCFString (m, text, flags, iter.itemId, &index); + + if (iter.isTicked) + CheckMenuItem (m, index, true); + + SetMenuItemProperty (m, index, 'juce', 'apcm', sizeof (void*), &iter.commandManager); + SetMenuItemProperty (m, index, 'juce', 'topi', sizeof (int), &topLevelIndex); + + if (iter.commandManager != 0) + { + const Array keyPresses (iter.commandManager->getKeyMappings() + ->getKeyPressesAssignedToCommand (iter.itemId)); + + if (keyPresses.size() > 0) + { + const KeyPress& kp = keyPresses.getUnchecked(0); + int mods = 0; + + if (kp.getModifiers().isShiftDown()) + mods |= kMenuShiftModifier; + if (kp.getModifiers().isCtrlDown()) + mods |= kMenuControlModifier; + if (kp.getModifiers().isAltDown()) + mods |= kMenuOptionModifier; + if (! kp.getModifiers().isCommandDown()) + mods |= kMenuNoCommandModifier; + + tchar keyCode = (tchar) kp.getKeyCode(); + + if (kp.getKeyCode() >= KeyPress::numberPad0 + && kp.getKeyCode() <= KeyPress::numberPad9) + { + keyCode = (tchar) ((T('0') - KeyPress::numberPad0) + kp.getKeyCode()); + } + + SetMenuItemCommandKey (m, index, true, 255); + + if (CharacterFunctions::isLetterOrDigit (keyCode) + || CharacterFunctions::indexOfChar (T(",.;/\\'[]=-+_<>?{}\":"), keyCode, false) >= 0) + { + SetMenuItemModifiers (m, index, mods); + SetMenuItemCommandKey (m, index, false, CharacterFunctions::toUpperCase (keyCode)); + } + else + { + const SInt16 glyph = getGlyphForKeyCode (kp.getKeyCode()); + + if (glyph != 0) + { + SetMenuItemModifiers (m, index, mods); + SetMenuItemKeyGlyph (m, index, glyph); + } + } + + // if we set the key glyph to be a text char, and enable virtual + // key triggering, it stops the menu automatically triggering the callback + ChangeMenuItemAttributes (m, index, kMenuItemAttrUseVirtualKey, 0); + } + } + } + + CFRelease (text); + } + } + + return m; + } + + static SInt16 getGlyphForKeyCode (const int keyCode) throw() + { + if (keyCode == KeyPress::spaceKey) + return kMenuSpaceGlyph; + else if (keyCode == KeyPress::returnKey) + return kMenuReturnGlyph; + else if (keyCode == KeyPress::escapeKey) + return kMenuEscapeGlyph; + else if (keyCode == KeyPress::backspaceKey) + return kMenuDeleteLeftGlyph; + else if (keyCode == KeyPress::leftKey) + return kMenuLeftArrowGlyph; + else if (keyCode == KeyPress::rightKey) + return kMenuRightArrowGlyph; + else if (keyCode == KeyPress::upKey) + return kMenuUpArrowGlyph; + else if (keyCode == KeyPress::downKey) + return kMenuDownArrowGlyph; + else if (keyCode == KeyPress::pageUpKey) + return kMenuPageUpGlyph; + else if (keyCode == KeyPress::pageDownKey) + return kMenuPageDownGlyph; + else if (keyCode == KeyPress::endKey) + return kMenuSoutheastArrowGlyph; + else if (keyCode == KeyPress::homeKey) + return kMenuNorthwestArrowGlyph; + else if (keyCode == KeyPress::deleteKey) + return kMenuDeleteRightGlyph; + else if (keyCode == KeyPress::tabKey) + return kMenuTabRightGlyph; + else if (keyCode == KeyPress::F1Key) + return kMenuF1Glyph; + else if (keyCode == KeyPress::F2Key) + return kMenuF2Glyph; + else if (keyCode == KeyPress::F3Key) + return kMenuF3Glyph; + else if (keyCode == KeyPress::F4Key) + return kMenuF4Glyph; + else if (keyCode == KeyPress::F5Key) + return kMenuF5Glyph; + else if (keyCode == KeyPress::F6Key) + return kMenuF6Glyph; + else if (keyCode == KeyPress::F7Key) + return kMenuF7Glyph; + else if (keyCode == KeyPress::F8Key) + return kMenuF8Glyph; + else if (keyCode == KeyPress::F9Key) + return kMenuF9Glyph; + else if (keyCode == KeyPress::F10Key) + return kMenuF10Glyph; + else if (keyCode == KeyPress::F11Key) + return kMenuF11Glyph; + else if (keyCode == KeyPress::F12Key) + return kMenuF12Glyph; + else if (keyCode == KeyPress::F13Key) + return kMenuF13Glyph; + else if (keyCode == KeyPress::F14Key) + return kMenuF14Glyph; + else if (keyCode == KeyPress::F15Key) + return kMenuF15Glyph; + + return 0; + } +}; + +void MenuBarModel::setMacMainMenu (MenuBarModel* newMenuBarModel) throw() +{ + if (getMacMainMenu() != newMenuBarModel) + { + if (newMenuBarModel == 0) + { + delete mainMenu; + jassert (mainMenu == 0); // should be zeroed in the destructor + } + else + { + if (mainMenu == 0) + mainMenu = new MainMenuHandler(); + + mainMenu->setMenu (newMenuBarModel); + } + } +} + +MenuBarModel* MenuBarModel::getMacMainMenu() throw() +{ + return mainMenu != 0 ? mainMenu->currentModel : 0; +} + +// these functions are called externally from the message handling code +void juce_MainMenuAboutToBeUsed() +{ + // force an update of the items just before the menu appears.. + if (mainMenu != 0) + mainMenu->menuBarItemsChanged (0); +} + +void juce_InvokeMainMenuCommand (const HICommand& command) +{ + if (mainMenu != 0) + { + ApplicationCommandManager* commandManager = 0; + int topLevelIndex = 0; + + if (GetMenuItemProperty (command.menu.menuRef, command.menu.menuItemIndex, + 'juce', 'apcm', sizeof (commandManager), 0, &commandManager) == noErr + && GetMenuItemProperty (command.menu.menuRef, command.menu.menuItemIndex, + 'juce', 'topi', sizeof (topLevelIndex), 0, &topLevelIndex) == noErr) + { + mainMenu->invoke (command.commandID, commandManager, topLevelIndex); + } + } +} + +//============================================================================== +void PlatformUtilities::beep() +{ + SysBeep (30); +} + +//============================================================================== +void SystemClipboard::copyTextToClipboard (const String& text) +{ + ClearCurrentScrap(); + ScrapRef ref; + GetCurrentScrap (&ref); + + const int len = text.length(); + const int numBytes = sizeof (UniChar) * len; + UniChar* const temp = (UniChar*) juce_calloc (numBytes); + + for (int i = 0; i < len; ++i) + temp[i] = (UniChar) text[i]; + + PutScrapFlavor (ref, + kScrapFlavorTypeUnicode, + kScrapFlavorMaskNone, + numBytes, + temp); + + juce_free (temp); +} + +const String SystemClipboard::getTextFromClipboard() +{ + String result; + + ScrapRef ref; + GetCurrentScrap (&ref); + Size size = 0; + + if (GetScrapFlavorSize (ref, kScrapFlavorTypeUnicode, &size) == noErr + && size > 0) + { + void* const data = juce_calloc (size + 8); + + if (GetScrapFlavorData (ref, kScrapFlavorTypeUnicode, &size, data) == noErr) + { + result = PlatformUtilities::convertUTF16ToString ((UniChar*) data); + } + + juce_free (data); + } + + return result; +} + + +//============================================================================== +bool AlertWindow::showNativeDialogBox (const String& title, + const String& bodyText, + bool isOkCancel) +{ + Str255 tit, txt; + PlatformUtilities::copyToStr255 (tit, title); + PlatformUtilities::copyToStr255 (txt, bodyText); + + AlertStdAlertParamRec ar; + ar.movable = true; + ar.helpButton = false; + ar.filterProc = 0; + ar.defaultText = (const unsigned char*)-1; + ar.cancelText = (const unsigned char*)((isOkCancel) ? -1 : 0); + ar.otherText = 0; + ar.defaultButton = kAlertStdAlertOKButton; + ar.cancelButton = 0; + ar.position = kWindowDefaultPosition; + + SInt16 result; + StandardAlert (kAlertNoteAlert, tit, txt, &ar, &result); + return result == kAlertStdAlertOKButton; +} + +//============================================================================== +const int KeyPress::spaceKey = ' '; +const int KeyPress::returnKey = kReturnCharCode; +const int KeyPress::escapeKey = kEscapeCharCode; +const int KeyPress::backspaceKey = kBackspaceCharCode; +const int KeyPress::leftKey = kLeftArrowCharCode; +const int KeyPress::rightKey = kRightArrowCharCode; +const int KeyPress::upKey = kUpArrowCharCode; +const int KeyPress::downKey = kDownArrowCharCode; +const int KeyPress::pageUpKey = kPageUpCharCode; +const int KeyPress::pageDownKey = kPageDownCharCode; +const int KeyPress::endKey = kEndCharCode; +const int KeyPress::homeKey = kHomeCharCode; +const int KeyPress::deleteKey = kDeleteCharCode; +const int KeyPress::insertKey = -1; +const int KeyPress::tabKey = kTabCharCode; +const int KeyPress::F1Key = 0x10110; +const int KeyPress::F2Key = 0x10111; +const int KeyPress::F3Key = 0x10112; +const int KeyPress::F4Key = 0x10113; +const int KeyPress::F5Key = 0x10114; +const int KeyPress::F6Key = 0x10115; +const int KeyPress::F7Key = 0x10116; +const int KeyPress::F8Key = 0x10117; +const int KeyPress::F9Key = 0x10118; +const int KeyPress::F10Key = 0x10119; +const int KeyPress::F11Key = 0x1011a; +const int KeyPress::F12Key = 0x1011b; +const int KeyPress::F13Key = 0x1011c; +const int KeyPress::F14Key = 0x1011d; +const int KeyPress::F15Key = 0x1011e; +const int KeyPress::F16Key = 0x1011f; +const int KeyPress::numberPad0 = 0x30020; +const int KeyPress::numberPad1 = 0x30021; +const int KeyPress::numberPad2 = 0x30022; +const int KeyPress::numberPad3 = 0x30023; +const int KeyPress::numberPad4 = 0x30024; +const int KeyPress::numberPad5 = 0x30025; +const int KeyPress::numberPad6 = 0x30026; +const int KeyPress::numberPad7 = 0x30027; +const int KeyPress::numberPad8 = 0x30028; +const int KeyPress::numberPad9 = 0x30029; +const int KeyPress::numberPadAdd = 0x3002a; +const int KeyPress::numberPadSubtract = 0x3002b; +const int KeyPress::numberPadMultiply = 0x3002c; +const int KeyPress::numberPadDivide = 0x3002d; +const int KeyPress::numberPadSeparator = 0x3002e; +const int KeyPress::numberPadDecimalPoint = 0x3002f; +const int KeyPress::numberPadEquals = 0x30030; +const int KeyPress::numberPadDelete = 0x30031; +const int KeyPress::playKey = 0x30000; +const int KeyPress::stopKey = 0x30001; +const int KeyPress::fastForwardKey = 0x30002; +const int KeyPress::rewindKey = 0x30003; + +//============================================================================== +#if JUCE_OPENGL + +struct OpenGLContextInfo +{ + AGLContext renderContext; +}; + +void* juce_createOpenGLContext (OpenGLComponent* component, void* sharedContext) +{ + jassert (component != 0); + + HIViewComponentPeer* const peer = dynamic_cast (component->getTopLevelComponent()->getPeer()); + + if (peer == 0) + return 0; + + OpenGLContextInfo* const oc = new OpenGLContextInfo(); + + GLint attrib[] = { AGL_RGBA, AGL_DOUBLEBUFFER, + AGL_RED_SIZE, 8, + AGL_ALPHA_SIZE, 8, + AGL_DEPTH_SIZE, 24, + AGL_CLOSEST_POLICY, AGL_NO_RECOVERY, + AGL_SAMPLE_BUFFERS_ARB, 1, + AGL_SAMPLES_ARB, 4, + AGL_NONE }; + + oc->renderContext = aglCreateContext (aglChoosePixelFormat (0, 0, attrib), + (sharedContext != 0) ? ((OpenGLContextInfo*) sharedContext)->renderContext + : 0); + + aglSetDrawable (oc->renderContext, + GetWindowPort (peer->windowRef)); + + return oc; +} + +void juce_updateOpenGLWindowPos (void* context, Component* owner, Component* topComp) +{ + jassert (context != 0); + OpenGLContextInfo* const oc = (OpenGLContextInfo*) context; + + GLint bufferRect[4]; + + bufferRect[0] = owner->getScreenX() - topComp->getScreenX(); + bufferRect[1] = topComp->getHeight() - (owner->getHeight() + owner->getScreenY() - topComp->getScreenY()); + bufferRect[2] = owner->getWidth(); + bufferRect[3] = owner->getHeight(); + + aglSetInteger (oc->renderContext, AGL_BUFFER_RECT, bufferRect); + aglEnable (oc->renderContext, AGL_BUFFER_RECT); +} + +void juce_deleteOpenGLContext (void* context) +{ + OpenGLContextInfo* const oc = (OpenGLContextInfo*) context; + + aglDestroyContext (oc->renderContext); + + delete oc; +} + +bool juce_makeOpenGLContextCurrent (void* context) +{ + OpenGLContextInfo* const oc = (OpenGLContextInfo*) context; + + return aglSetCurrentContext ((oc != 0) ? oc->renderContext : 0); +} + +void juce_swapOpenGLBuffers (void* context) +{ + OpenGLContextInfo* const oc = (OpenGLContextInfo*) context; + + if (oc != 0) + aglSwapBuffers (oc->renderContext); +} + +void juce_repaintOpenGLWindow (void* context) +{ +} + +#endif + +END_JUCE_NAMESPACE diff --git a/extras/the jucer/src/model/jucer_JucerDocument.cpp b/extras/the jucer/src/model/jucer_JucerDocument.cpp index 9bd8051fce..eb74297128 100644 --- a/extras/the jucer/src/model/jucer_JucerDocument.cpp +++ b/extras/the jucer/src/model/jucer_JucerDocument.cpp @@ -445,7 +445,7 @@ const String JucerDocument::saveDocument (const File& file) String templateH, templateCpp; if (! findTemplateFiles (templateH, templateCpp)) - return TRANS("Couldn't find the required Jucer template files - make sure the template files directory is set up correctly in the preferences box."); + return TRANS("Couldn't find the required Jucer template files...\n\nMake sure the template files directory is set up correctly in the preferences box."); const bool ok = writeCodeFiles (hFile, cppFile, templateH, templateCpp); TestComponent::reloadAll(); @@ -608,7 +608,7 @@ void JucerDocument::getPreviewFiles (String& h, String& cpp) { if (! findTemplateFiles (h, cpp)) { - h = cpp = TRANS("Couldn't find the required Jucer template files - make sure the template files directory is set up correctly in the preferences box."); + h = cpp = TRANS("Couldn't find the required Jucer template files...\n\nMake sure the template files directory is set up correctly in the preferences box."); } else { diff --git a/src/juce_appframework/gui/components/controls/juce_Slider.cpp b/src/juce_appframework/gui/components/controls/juce_Slider.cpp index 2c60c175ab..08d858af9b 100644 --- a/src/juce_appframework/gui/components/controls/juce_Slider.cpp +++ b/src/juce_appframework/gui/components/controls/juce_Slider.cpp @@ -638,6 +638,10 @@ void Slider::labelTextChanged (Label* label) setValue (newValue, true, true); sendDragEnd(); } + else + { + updateText(); + } } void Slider::buttonClicked (Button* button) diff --git a/src/juce_appframework/gui/components/juce_Component.cpp b/src/juce_appframework/gui/components/juce_Component.cpp index 0df37388e6..bf7d336cfc 100644 --- a/src/juce_appframework/gui/components/juce_Component.cpp +++ b/src/juce_appframework/gui/components/juce_Component.cpp @@ -1,3720 +1,3720 @@ -/* - ============================================================================== - - 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; -} - -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 +/* + ============================================================================== + + 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; +} + +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