|  | /*
  ==============================================================================
   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 <Carbon/Carbon.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/IOCFPlugIn.h>
#include <IOKit/hid/IOHIDLib.h>
#include <IOKit/hid/IOHIDKeys.h>
#include <fnmatch.h>
#if JUCE_OPENGL
#include <AGL/agl.h>
#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"
#include "../../../src/juce_core/containers/juce_MemoryBlock.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 (const int keyCode) throw()
{
    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() throw()
{
    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);
        CMCloseProfile (prof);
    }
    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
{
public:
    MouseCheckTimer()
        : lastX (0),
          lastY (0)
    {
        lastPeerUnderMouse = 0;
        resetMouseMoveChecker();
#if ! MACOS_10_2_OR_EARLIER
        // Just putting this in here because it's a convenient object that'll get deleted at shutdown
        CGDisplayRegisterReconfigurationCallback (&displayChangeCallback, 0);
#endif
    }
    ~MouseCheckTimer()
    {
#if ! MACOS_10_2_OR_EARLIER
        CGDisplayRemoveReconfigurationCallback (&displayChangeCallback, 0);
#endif
        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();
private:
    HIViewComponentPeer* lastPeerUnderMouse;
    int lastX, lastY;
#if ! MACOS_10_2_OR_EARLIER
    static void displayChangeCallback (CGDirectDisplayID, CGDisplayChangeSummaryFlags flags, void*)
    {
        Desktop::getInstance().refreshMonitorSizes();
    }
#endif
};
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)
        {
            Rectangle r (lastNonFullscreenBounds);
            setMinimised (false);
            if (fullScreen != shouldBeFullScreen)
            {
                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 (((unsigned int) x) >= (unsigned int) component->getWidth()
            || ((unsigned int) y) >= (unsigned int) 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);
                SetFrontProcessWithOptions (&psn, kSetFrontProcessFrontWindowOnly);
            }
            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 <HIViewComponentPeer*> (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);
                const int offsetInWindowX = component->getScreenX() - getScreenX();
                const int offsetInWindowY = component->getScreenY() - getScreenY();
                for (RectangleList::Iterator i (repainter->getRegionsNeedingRepaint()); i.next();)
                {
                    const Rectangle& r = *i.getRectangle();
                    w.left = offsetInWindowX + r.getX();
                    w.top = offsetInWindowY + r.getY();
                    w.right = offsetInWindowX + r.getRight();
                    w.bottom = offsetInWindowY + r.getBottom();
                    InvalWindowRect (windowRef, &w);
                }
            }
            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
            if (HIViewGetNeedsDisplay (viewRef) || repainter->isRepaintNeeded())
                HIViewRender (viewRef);
#endif
        }
    }
    //==============================================================================
    juce_UseDebuggingNewOperator
    WindowRef windowRef;
    HIViewRef viewRef;
private:
    EventHandlerRef eventHandlerRef;
    bool fullScreen, isSharedWindow, isCompositingWindow;
    StringArray dragAndDropFiles;
    //==============================================================================
    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);
        }
        bool isRepaintNeeded() const throw()
        {
            return ! regionsNeedingRepaint.isEmpty();
        }
        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 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
             && keyCode >= 1 && keyCode <= 26)
        {
            keyCode += ('A' - 1);
        }
        else
        {
            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 (((unsigned int) rawKey) < (unsigned int) numElementsInArray (keyTranslations)
                 && keyTranslations [rawKey] != 0)
            {
                keyCode = keyTranslations [rawKey];
            }
            if ((rawKey == 0 && textCharacter != 0)
                || (CharacterFunctions::isLetterOrDigit ((juce_wchar) keyCode)
                     && CharacterFunctions::isLetterOrDigit (textCharacter))) // correction for azerty-type layouts..
            {
                keyCode = CharacterFunctions::toLowerCase (textCharacter);
            }
        }
        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);
                if (used1 || used2)
                    return noErr;
                break;
            }
        case kEventRawKeyUp:
            keysCurrentlyDown.removeValue ((void*) keyCode);
            lastTextCharacter = 0;
            if (handleKeyUpOrDown())
                return noErr;
            break;
        case kEventRawKeyRepeat:
            if (handleKeyPress (keyCode, lastTextCharacter))
                return noErr;
            break;
        case kEventRawKeyModifiersChanged:
            handleModifierKeysChange();
            break;
        default:
            jassertfalse
            break;
        }
        return eventNotHandledErr;
    }
    OSStatus handleTextInputEvent (EventRef theEvent)
    {
        UInt32 numBytesRequired = 0;
        GetEventParameter (theEvent, kEventParamTextInputSendText, typeUnicodeText, 0, 0, &numBytesRequired, 0);
        MemoryBlock buffer (numBytesRequired, true);
        UniChar* const uc = (UniChar*) buffer.getData();
        GetEventParameter (theEvent, kEventParamTextInputSendText, typeUnicodeText, 0, numBytesRequired, &numBytesRequired, uc);
        EventRef originalEvent;
        GetEventParameter (theEvent, kEventParamTextInputSendKeyboardEvent, typeEventRef, 0, sizeof (originalEvent), 0, &originalEvent);
        OSStatus res = noErr;
        for (int i = 0; i < numBytesRequired / sizeof (UniChar); ++i)
            res = handleKeyEvent (originalEvent, (juce_wchar) uc[i]);
        return res;
    }
    //==============================================================================
    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);
                    SetFrontProcessWithOptions (&psn, kSetFrontProcessFrontWindowOnly);
                    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;
    }
    //==============================================================================
    void doDragDropEnter (EventRef theEvent)
    {
        updateDragAndDropFileList (theEvent);
        if (dragAndDropFiles.size() > 0)
        {
            int x, y;
            component->getMouseXYRelative (x, y);
            handleFileDragMove (dragAndDropFiles, x, y);
        }
    }
    void doDragDropMove (EventRef theEvent)
    {
        if (dragAndDropFiles.size() > 0)
        {
            int x, y;
            component->getMouseXYRelative (x, y);
            handleFileDragMove (dragAndDropFiles, x, y);
        }
    }
    void doDragDropExit (EventRef theEvent)
    {
        if (dragAndDropFiles.size() > 0)
            handleFileDragExit (dragAndDropFiles);
    }
    void doDragDrop (EventRef theEvent)
    {
        updateDragAndDropFileList (theEvent);
        if (dragAndDropFiles.size() > 0)
        {
            int x, y;
            component->getMouseXYRelative (x, y);
            handleFileDragDrop (dragAndDropFiles, x, y);
        }
    }
    void updateDragAndDropFileList (EventRef theEvent)
    {
        dragAndDropFiles.clear();
        DragRef dragRef;
        if (GetEventParameter (theEvent, kEventParamDragRef, typeDragRef, 0, sizeof (dragRef), 0, &dragRef) == noErr)
        {
            UInt16 numItems = 0;
            if (CountDragItems (dragRef, &numItems) == noErr)
            {
                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())
                                        dragAndDropFiles.add (path);
                                }
                            }
                            juce_free (data);
                        }
                    }
                }
                dragAndDropFiles.trim();
                dragAndDropFiles.removeEmptyStrings();
            }
        }
    }
    //==============================================================================
    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;
        // (avoid doing this in plugins because of some strange redraw bugs..)
        if (rgn != 0 && JUCEApplication::getInstance() != 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);
            clip.origin.x = 0;
            clip.origin.y = 0;
        }
#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;
    }
    //==============================================================================
    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)
                        {
                            static uint32 lastDragTime = 0;
                            const uint32 now = Time::currentTimeMillis();
                            if (now > lastDragTime + 1000)
                            {
                                lastDragTime = now;
                                modal->inputAttemptWhenModal();
                            }
                            const Rectangle currentRect (getComponent()->getBounds());
                            Rect current;
                            current.left = currentRect.getX();
                            current.top = currentRect.getY();
                            current.right = currentRect.getRight();
                            current.bottom = currentRect.getBottom();
                            // stop the window getting dragged..
                            SetEventParameter (theEvent, kEventParamCurrentBounds, typeQDRectangle,
                                               sizeof (Rect), ¤t);
                            return noErr;
                        }
                    }
                    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 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 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;
    }
    //==============================================================================
    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);
                    peer->doDragDropEnter (theEvent);
                    return noErr;
                }
                case kEventControlDragWithin:
                    peer->doDragDropMove (theEvent);
                    return noErr;
                case kEventControlDragLeave:
                    peer->doDragDropExit (theEvent);
                    return noErr;
                case kEventControlDragReceive:
                    peer->doDragDrop (theEvent);
                    return noErr;
                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
                SetAutomaticControlDragTrackingEnabledForWindow (newWindow, true);
                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, kEventControlDragLeave },
                { 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);
}
bool juce_isWindowCreatedByJuce (WindowRef window)
{
    for (int i = ComponentPeer::getNumPeers(); --i >= 0;)
        if (ComponentPeer::getPeer(i)->getNativeHandle() == window)
            return true;
    return false;
}
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()
{
    // this rubbish needs to be done around the warp call, to avoid causing a
    // bizarre glitch..
    CGAssociateMouseAndMouseCursorPosition (false);
    CGSetLocalEventsSuppressionInterval (0);
    CGPoint pos = { x, y };
    CGWarpMouseCursorPosition (pos);
    CGAssociateMouseAndMouseCursorPosition (true);
}
const ModifierKeys ModifierKeys::getCurrentModifiersRealtime() throw()
{
    return ModifierKeys (currentModifiers);
}
//==============================================================================
class ScreenSaverDefeater   : public Timer,
                              public DeletedAtShutdown
{
public:
    ScreenSaverDefeater() throw()
    {
        startTimer (10000);
        timerCallback();
    }
    ~ScreenSaverDefeater()
    {
    }
    void timerCallback()
    {
        if (Process::isForegroundProcess())
            UpdateSystemActivity (UsrActivity);
    }
};
static ScreenSaverDefeater* screenSaverDefeater = 0;
void Desktop::setScreenSaverEnabled (const bool isEnabled) throw()
{
    if (screenSaverDefeater == 0)
        screenSaverDefeater = new ScreenSaverDefeater();
}
bool Desktop::isScreenSaverEnabled() throw()
{
    return screenSaverDefeater == 0;
}
//==============================================================================
void juce_updateMultiMonitorInfo (Array <Rectangle>& monitorCoords, const bool clipToWorkArea) throw()
{
    int mainMonitorIndex = 0;
    CGDirectDisplayID mainDisplayID = CGMainDisplayID();
    CGDisplayCount count = 0;
    CGDirectDisplayID disps [8];
    if (CGGetOnlineDisplayList (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 ((int) r.origin.x,
                                              (int) r.origin.y,
                                              (int) r.size.width,
                                              (int) 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);
    if (monitorCoords.size() == 0)
        monitorCoords.add (Rectangle (0, 0, 1024, 768));
}
//==============================================================================
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 cursorId = kThemeArrowCursor;
    switch (type)
    {
    case MouseCursor::NormalCursor:
        cursorId = kThemeArrowCursor;
        break;
    case MouseCursor::NoCursor:
        cursorId = 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:
        cursorId = kThemeCopyArrowCursor;
        break;
    case MouseCursor::WaitCursor:
        cursorId = kThemeWatchCursor;
        break;
    case MouseCursor::IBeamCursor:
        cursorId = kThemeIBeamCursor;
        break;
    case MouseCursor::PointingHandCursor:
        cursorId = 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:
        cursorId = kThemeCrossCursor;
        break;
    }
    CursorWrapper* cw = new CursorWrapper();
    cw->cursor = 0;
    cw->themeCursor = cursorId;
    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 menuId = 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], menuId, 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);
        if (menu != 0)
        {
            FlashMenuBar (GetMenuID (menu));
            FlashMenuBar (GetMenuID (menu));
        }
    }
    void invoke (const int commandId, ApplicationCommandManager* const commandManager, const int topLevelIndex) const
    {
        if (currentModel != 0)
        {
            if (commandManager != 0)
            {
                ApplicationCommandTarget::InvocationInfo info (commandId);
                info.invocationMethod = ApplicationCommandTarget::InvocationInfo::fromMenu;
                commandManager->invoke (info, true);
            }
            currentModel->menuItemSelected (commandId, 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("<end>"), 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 <KeyPress> keyPresses (iter.commandManager->getKeyMappings()
                                                            ->getKeyPressesAssignedToCommand (iter.itemId));
                        if (keyPresses.size() > 0)
                        {
                            const KeyPress& kp = keyPresses.getReference(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) throw()
{
    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() throw()
{
    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;
//==============================================================================
AppleRemoteDevice::AppleRemoteDevice()
    : device (0),
      queue (0),
      remoteId (0)
{
}
AppleRemoteDevice::~AppleRemoteDevice()
{
    stop();
}
static io_object_t getAppleRemoteDevice() throw()
{
    CFMutableDictionaryRef dict = IOServiceMatching ("AppleIRController");
    io_iterator_t iter = 0;
    io_object_t iod = 0;
    if (IOServiceGetMatchingServices (kIOMasterPortDefault, dict, &iter) == kIOReturnSuccess
         && iter != 0)
    {
        iod = IOIteratorNext (iter);
    }
    IOObjectRelease (iter);
    return iod;
}
static bool createAppleRemoteInterface (io_object_t iod, void** device) throw()
{
    jassert (*device == 0);
    io_name_t classname;
    if (IOObjectGetClass (iod, classname) == kIOReturnSuccess)
    {
        IOCFPlugInInterface** cfPlugInInterface = 0;
        SInt32 score = 0;
        if (IOCreatePlugInInterfaceForService (iod,
                                               kIOHIDDeviceUserClientTypeID,
                                               kIOCFPlugInInterfaceID,
                                               &cfPlugInInterface,
                                               &score) == kIOReturnSuccess)
        {
            HRESULT hr = (*cfPlugInInterface)->QueryInterface (cfPlugInInterface,
                                                               CFUUIDGetUUIDBytes (kIOHIDDeviceInterfaceID),
                                                               device);
            (void) hr;
            (*cfPlugInInterface)->Release (cfPlugInInterface);
        }
    }
    return *device != 0;
}
bool AppleRemoteDevice::start (const bool inExclusiveMode) throw()
{
    if (queue != 0)
        return true;
    stop();
    bool result = false;
    io_object_t iod = getAppleRemoteDevice();
    if (iod != 0)
    {
        if (createAppleRemoteInterface (iod, &device) && open (inExclusiveMode))
            result = true;
        else
            stop();
        IOObjectRelease (iod);
    }
    return result;
}
void AppleRemoteDevice::stop() throw()
{
    if (queue != 0)
    {
        (*(IOHIDQueueInterface**) queue)->stop ((IOHIDQueueInterface**) queue);
        (*(IOHIDQueueInterface**) queue)->dispose ((IOHIDQueueInterface**) queue);
        (*(IOHIDQueueInterface**) queue)->Release ((IOHIDQueueInterface**) queue);
        queue = 0;
    }
    if (device != 0)
    {
        (*(IOHIDDeviceInterface**) device)->close ((IOHIDDeviceInterface**) device);
        (*(IOHIDDeviceInterface**) device)->Release ((IOHIDDeviceInterface**) device);
        device = 0;
    }
}
bool AppleRemoteDevice::isActive() const throw()
{
    return queue != 0;
}
static void appleRemoteQueueCallback (void* const target, const IOReturn result, void*, void*)
{
    if (result == kIOReturnSuccess)
        ((AppleRemoteDevice*) target)->handleCallbackInternal();
}
bool AppleRemoteDevice::open (const bool openInExclusiveMode) throw()
{
#if ! MACOS_10_2_OR_EARLIER
    Array <int> cookies;
    CFArrayRef elements;
    IOHIDDeviceInterface122** const device122 = (IOHIDDeviceInterface122**) device;
    if ((*device122)->copyMatchingElements (device122, 0, &elements) != kIOReturnSuccess)
        return false;
    for (int i = 0; i < CFArrayGetCount (elements); ++i)
    {
        CFDictionaryRef element = (CFDictionaryRef) CFArrayGetValueAtIndex (elements, i);
        // get the cookie
        CFTypeRef object = CFDictionaryGetValue (element, CFSTR (kIOHIDElementCookieKey));
        if (object == 0 || CFGetTypeID (object) != CFNumberGetTypeID())
            continue;
        long number;
        if (! CFNumberGetValue ((CFNumberRef) object, kCFNumberLongType, &number))
            continue;
        cookies.add ((int) number);
    }
    CFRelease (elements);
    if ((*(IOHIDDeviceInterface**) device)
            ->open ((IOHIDDeviceInterface**) device,
                    openInExclusiveMode ? kIOHIDOptionsTypeSeizeDevice
                                        : kIOHIDOptionsTypeNone) == KERN_SUCCESS)
    {
        queue = (*(IOHIDDeviceInterface**) device)->allocQueue ((IOHIDDeviceInterface**) device);
        if (queue != 0)
        {
            (*(IOHIDQueueInterface**) queue)->create ((IOHIDQueueInterface**) queue, 0, 12);
            for (int i = 0; i < cookies.size(); ++i)
            {
                IOHIDElementCookie cookie = (IOHIDElementCookie) cookies.getUnchecked(i);
                (*(IOHIDQueueInterface**) queue)->addElement ((IOHIDQueueInterface**) queue, cookie, 0);
            }
            CFRunLoopSourceRef eventSource;
            if ((*(IOHIDQueueInterface**) queue)
                    ->createAsyncEventSource ((IOHIDQueueInterface**) queue, &eventSource) == KERN_SUCCESS)
            {
                if ((*(IOHIDQueueInterface**) queue)->setEventCallout ((IOHIDQueueInterface**) queue,
                                                                       appleRemoteQueueCallback, this, 0) == KERN_SUCCESS)
                {
                    CFRunLoopAddSource (CFRunLoopGetCurrent(), eventSource, kCFRunLoopDefaultMode);
                    (*(IOHIDQueueInterface**) queue)->start ((IOHIDQueueInterface**) queue);
                    return true;
                }
            }
        }
    }
#endif
    return false;
}
void AppleRemoteDevice::handleCallbackInternal()
{
    int totalValues = 0;
    AbsoluteTime nullTime = { 0, 0 };
    char cookies [12];
    int numCookies = 0;
    while (numCookies < numElementsInArray (cookies))
    {
        IOHIDEventStruct e;
        if ((*(IOHIDQueueInterface**) queue)->getNextEvent ((IOHIDQueueInterface**) queue, &e, nullTime, 0) != kIOReturnSuccess)
            break;
        if ((int) e.elementCookie == 19)
        {
            remoteId = e.value;
            buttonPressed (switched, false);
        }
        else
        {
            totalValues += e.value;
            cookies [numCookies++] = (char) (pointer_sized_int) e.elementCookie;
        }
    }
    cookies [numCookies++] = 0;
    static const char buttonPatterns[] =
    {
        14, 7, 6, 5, 14, 7, 6, 5, 0,
        14, 8, 6, 5, 14, 8, 6, 5, 0,
        14, 12, 11, 6, 5, 0,
        14, 13, 11, 6, 5, 0,
        14, 9, 6, 5, 14, 9, 6, 5, 0,
        14, 10, 6, 5, 14, 10, 6, 5, 0,
        14, 6, 5, 4, 2, 0,
        14, 6, 5, 3, 2, 0,
        14, 6, 5, 14, 6, 5, 0,
        18, 14, 6, 5, 18, 14, 6, 5, 0,
        19, 0
    };
    int buttonNum = (int) menuButton;
    int i = 0;
    while (i < numElementsInArray (buttonPatterns))
    {
        if (strcmp (cookies, buttonPatterns + i) == 0)
        {
            buttonPressed ((ButtonType) buttonNum, totalValues > 0);
            break;
        }
        i += strlen (buttonPatterns + i) + 1;
        ++buttonNum;
    }
}
//==============================================================================
#if JUCE_OPENGL
//==============================================================================
class WindowedGLContext     : public OpenGLContext
{
public:
    WindowedGLContext (Component* const component,
                       const OpenGLPixelFormat& pixelFormat_,
                       AGLContext sharedContext)
        : renderContext (0),
          pixelFormat (pixelFormat_)
    {
        jassert (component != 0);
        HIViewComponentPeer* const peer = dynamic_cast <HIViewComponentPeer*> (component->getTopLevelComponent()->getPeer());
        if (peer == 0)
            return;
        GLint attribs [64];
        int n = 0;
        attribs[n++] = AGL_RGBA;
        attribs[n++] = AGL_DOUBLEBUFFER;
        attribs[n++] = AGL_ACCELERATED;
        attribs[n++] = AGL_RED_SIZE;
        attribs[n++] = pixelFormat.redBits;
        attribs[n++] = AGL_GREEN_SIZE;
        attribs[n++] = pixelFormat.greenBits;
        attribs[n++] = AGL_BLUE_SIZE;
        attribs[n++] = pixelFormat.blueBits;
        attribs[n++] = AGL_ALPHA_SIZE;
        attribs[n++] = pixelFormat.alphaBits;
        attribs[n++] = AGL_DEPTH_SIZE;
        attribs[n++] = pixelFormat.depthBufferBits;
        attribs[n++] = AGL_STENCIL_SIZE;
        attribs[n++] = pixelFormat.stencilBufferBits;
        attribs[n++] = AGL_ACCUM_RED_SIZE;
        attribs[n++] = pixelFormat.accumulationBufferRedBits;
        attribs[n++] = AGL_ACCUM_GREEN_SIZE;
        attribs[n++] = pixelFormat.accumulationBufferGreenBits;
        attribs[n++] = AGL_ACCUM_BLUE_SIZE;
        attribs[n++] = pixelFormat.accumulationBufferBlueBits;
        attribs[n++] = AGL_ACCUM_ALPHA_SIZE;
        attribs[n++] = pixelFormat.accumulationBufferAlphaBits;
        // xxx not sure how to do fullSceneAntiAliasingNumSamples..
        attribs[n++] = AGL_SAMPLE_BUFFERS_ARB;
        attribs[n++] = 1;
        attribs[n++] = AGL_SAMPLES_ARB;
        attribs[n++] = 4;
        attribs[n++] = AGL_CLOSEST_POLICY;
        attribs[n++] = AGL_NO_RECOVERY;
        attribs[n++] = AGL_NONE;
        renderContext = aglCreateContext (aglChoosePixelFormat (0, 0, attribs),
                                          sharedContext);
        aglSetDrawable (renderContext, GetWindowPort (peer->windowRef));
    }
    ~WindowedGLContext()
    {
        makeInactive();
        aglSetDrawable (renderContext, 0);
        aglDestroyContext (renderContext);
    }
    bool makeActive() const throw()
    {
        jassert (renderContext != 0);
        return aglSetCurrentContext (renderContext);
    }
    bool makeInactive() const throw()
    {
        return (! isActive()) || aglSetCurrentContext (0);
    }
    bool isActive() const throw()
    {
        return aglGetCurrentContext() == renderContext;
    }
    const OpenGLPixelFormat getPixelFormat() const
    {
        return pixelFormat;
    }
    void* getRawContext() const throw()
    {
        return renderContext;
    }
    void updateWindowPosition (int x, int y, int w, int h, int outerWindowHeight)
    {
        GLint bufferRect[4];
        bufferRect[0] = x;
        bufferRect[1] = outerWindowHeight - (y + h);
        bufferRect[2] = w;
        bufferRect[3] = h;
        aglSetInteger (renderContext, AGL_BUFFER_RECT, bufferRect);
        aglEnable (renderContext, AGL_BUFFER_RECT);
    }
    void swapBuffers()
    {
        aglSwapBuffers (renderContext);
    }
    bool setSwapInterval (const int numFramesPerSwap)
    {
        return aglSetInteger (renderContext, AGL_SWAP_INTERVAL, (const GLint*) &numFramesPerSwap);
    }
    int getSwapInterval() const
    {
        GLint numFrames = 0;
        aglGetInteger (renderContext, AGL_SWAP_INTERVAL, &numFrames);
        return numFrames;
    }
    void repaint()
    {
    }
    //==============================================================================
    juce_UseDebuggingNewOperator
    AGLContext renderContext;
private:
    OpenGLPixelFormat pixelFormat;
    //==============================================================================
    WindowedGLContext (const WindowedGLContext&);
    const WindowedGLContext& operator= (const WindowedGLContext&);
};
//==============================================================================
OpenGLContext* OpenGLContext::createContextForWindow (Component* const component,
                                                      const OpenGLPixelFormat& pixelFormat,
                                                      const OpenGLContext* const contextToShareWith)
{
    WindowedGLContext* c = new WindowedGLContext (component, pixelFormat,
                                                  contextToShareWith != 0 ? (AGLContext) contextToShareWith->getRawContext() : 0);
    if (c->renderContext == 0)
        deleteAndZero (c);
    return c;
}
void juce_glViewport (const int w, const int h)
{
    glViewport (0, 0, w, h);
}
static int getAGLAttribute (AGLPixelFormat p, const GLint attrib)
{
    GLint result = 0;
    aglDescribePixelFormat (p, attrib, &result);
    return result;
}
void OpenGLPixelFormat::getAvailablePixelFormats (Component* /*component*/,
                                                  OwnedArray <OpenGLPixelFormat>& results)
{
    GLint attribs [64];
    int n = 0;
    attribs[n++] = AGL_RGBA;
    attribs[n++] = AGL_DOUBLEBUFFER;
    attribs[n++] = AGL_ACCELERATED;
    attribs[n++] = AGL_NO_RECOVERY;
    attribs[n++] = AGL_NONE;
    AGLPixelFormat p = aglChoosePixelFormat (0, 0, attribs);
    while (p != 0)
    {
        OpenGLPixelFormat* const pf = new OpenGLPixelFormat();
        pf->redBits = getAGLAttribute (p, AGL_RED_SIZE);
        pf->greenBits = getAGLAttribute (p, AGL_GREEN_SIZE);
        pf->blueBits = getAGLAttribute (p, AGL_BLUE_SIZE);
        pf->alphaBits = getAGLAttribute (p, AGL_ALPHA_SIZE);
        pf->depthBufferBits = getAGLAttribute (p, AGL_DEPTH_SIZE);
        pf->stencilBufferBits = getAGLAttribute (p, AGL_STENCIL_SIZE);
        pf->accumulationBufferRedBits = getAGLAttribute (p, AGL_ACCUM_RED_SIZE);
        pf->accumulationBufferGreenBits = getAGLAttribute (p, AGL_ACCUM_GREEN_SIZE);
        pf->accumulationBufferBlueBits = getAGLAttribute (p, AGL_ACCUM_BLUE_SIZE);
        pf->accumulationBufferAlphaBits = getAGLAttribute (p, AGL_ACCUM_ALPHA_SIZE);
        results.add (pf);
        p = aglNextPixelFormat (p);
    }
}
#endif
END_JUCE_NAMESPACE
 |