/*
  ==============================================================================

   This file is part of the JUCE library - "Jules' Utility Class Extensions"
   Copyright 2004-6 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 <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/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/components/juce_Desktop.h"
#include "../../../src/juce_appframework/gui/components/juce_RepaintManager.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/graphics/imaging/juce_ImageFileFormat.h"

#undef Point

//#define DONT_USE_QUICKDRAW

const WindowRegionCode windowRegionToUse = kWindowContentRgn;

//==============================================================================
static VoidArray keysCurrentlyDown;

bool KeyPress::isKeyCurrentlyDown (int keyCode)
{
    if (keysCurrentlyDown.contains ((void*) keyCode))
        return true;

    if (keyCode >= 'A' && keyCode <= 'Z'
        && keysCurrentlyDown.contains ((void*) (int) String::toLowerCase ((tchar) keyCode)))
        return true;

    if (keyCode >= 'a' && keyCode <= 'z'
        && keysCurrentlyDown.contains ((void*) (int) String::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 MacWindowComponentPeer;
static MacWindowComponentPeer* lastFocusedWindow = 0;

static void getWindowPosition (WindowRef ref, int& x, int& y, int& w, int& h)
{
    if (ref != 0)
    {
        Rect r;
        GetWindowBounds (ref, windowRegionToUse, &r);
        x = r.left;
        y = r.top;
        h = r.bottom - r.top;
        w = r.right - r.left;
    }
}

//==============================================================================
static int currentModifiers = 0;

static void updateModifiers (EventRef theEvent)
{
    currentModifiers &= ~ (ModifierKeys::shiftModifier | ModifierKeys::ctrlModifier
                           | ModifierKeys::altModifier | ModifierKeys::commandModifier);

    UInt32 m;
    GetEventParameter (theEvent, kEventParamKeyModifiers, typeUInt32, 0, sizeof(m), 0, &m);

    if ((m & (shiftKey | rightShiftKey)) != 0)
        currentModifiers |= ModifierKeys::shiftModifier;

    if ((m & (controlKey | rightControlKey)) != 0)
        currentModifiers |= ModifierKeys::ctrlModifier;

    if ((m & (optionKey | rightOptionKey)) != 0)
        currentModifiers |= ModifierKeys::altModifier;

    if ((m & cmdKey) != 0)
        currentModifiers |= ModifierKeys::commandModifier;
}

void ModifierKeys::updateCurrentModifiers()
{
    currentModifierFlags = currentModifiers;
}


//==============================================================================
class MacBitmapImage  : public Image
{
public:
    //==============================================================================
    CGColorSpaceRef colourspace;
    CGDataProviderRef provider;
    CGImageRef image;

    //==============================================================================
    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);

        provider = CGDataProviderCreateWithData (0, imageData, h * lineStride, 0);

        image = CGImageCreate (w, h, 8, pixelStride * 8, lineStride,
                               colourspace,
                               format_ == RGB ? kCGImageAlphaPremultipliedFirst
                                              : kCGImageAlphaNone,
                               provider,
                               0, false,
                               kCGRenderingIntentDefault);
    }

    MacBitmapImage::~MacBitmapImage()
    {
        CGImageRelease (image);
        CGDataProviderRelease (provider);
        CGColorSpaceRelease (colourspace);
        juce_free (imageData);

        imageData = 0; // to stop the base class freeing this
    }

    void blitToWindow (WindowRef windowRef, const RectangleList& areas, int x, int y, const bool transparent)
    {
#ifndef DONT_USE_QUICKDRAW
        if (! transparent)
        {
            int lineStride, pixelStride;
            uint8* const imageData = lockPixelDataReadWrite (0, 0, getWidth(), getHeight(), lineStride, pixelStride);

#ifdef JUCE_LITTLE_ENDIAN
            {
                const int w = getWidth();

                for (int y = 0; y < getHeight(); ++y)
                {
                    uint32* const lineData = (uint32*) (imageData + lineStride * y);

                    for (int x = 0; x < w; ++x)
                        lineData[x] = CFSwapInt32BigToHost (lineData[x]);
                }
            }
#endif
            PixMap b;
            zerostruct (b);

            b.baseAddr = (char*) imageData;
            b.rowBytes = (short) (0x8000 | lineStride);
            b.bounds.right = (short) getWidth();
            b.bounds.bottom = (short) getHeight();
            b.pmVersion = 4;
            b.packType = 0;
            b.packSize = 0;
            b.hRes = b.vRes = 0x480000;
            b.pixelType = RGBDirect;
            b.pixelSize = 32;
            b.cmpCount = 4;
            b.cmpSize = 8;
            b.pmTable = 0;

            PixMapHandle pm = GetGWorldPixMap ((GWorldPtr) GetWindowPort (windowRef));

            if (pm != 0 && LockPixels (pm))
            {
                BackColor (whiteColor);
                ForeColor (blackColor);

                for (RectangleList::Iterator i (areas); i.next();)
                {
                    const Rectangle& r = i.getRectangle();

                    const int dx = r.getX();
                    const int dy = r.getY();
                    const int dw = r.getWidth();
                    const int dh = r.getHeight();
                    const int sx = dx - x;
                    const int sy = dy - y;

                    Rect srcRect = { (short) sy, (short) sx, (short) (sy + dh), (short) (sx + dw) };
                    Rect dstRect = { (short) dy, (short) dx, (short) (dy + dh), (short) (dx + dw) };

                    CopyBits ((BitMap*) &b, (BitMapPtr) *pm, &srcRect, &dstRect, srcCopy, 0);
                }

                UnlockPixels (pm);
            }

            releasePixelDataReadWrite (imageData);
        }
        else
#endif
        {
            Rect r;
            GetWindowBounds (windowRef, windowRegionToUse, &r);
            const int windowH = (r.bottom - r.top);

            CGrafPtr port = GetWindowPort (windowRef);
            CGContextRef context;

            if (QDBeginCGContext (port, &context) == noErr)
            {
                // temporary bodge - since 10.4 the image gets cached somewhere, probably video memory,
                // so to force it to reload from the latest contents of the image data, create a temporary
                // image. Annoyingly the only way around this is with functions specific to 10.4

                CGImageRef tempImage = CGImageCreate (getWidth(),
                                                      getHeight(),
                                                      8, pixelStride * 8, lineStride,
                                                      colourspace,
                                                      hasAlphaChannel() ? kCGImageAlphaPremultipliedFirst
                                                                        : kCGImageAlphaNone,
                                                      provider,
                                                      0, false,
                                                      kCGRenderingIntentDefault);

                for (RectangleList::Iterator i (areas); i.next();)
                {
                    const Rectangle& r = i.getRectangle();

                    const int dx = r.getX();
                    const int dy = r.getY();
                    const int dw = r.getWidth();
                    const int dh = r.getHeight();
                    const int sx = dx - x;
                    const int sy = dy - y;

                    CGRect destRect;
                    destRect.origin.x = (float) dx;
                    destRect.origin.y = (float) (windowH - dy - dh);
                    destRect.size.width = (float) dw;
                    destRect.size.height = (float) dh;

                    CGRect srcRect;
                    srcRect.origin.x = (float) (dx - sx);
                    srcRect.origin.y = (float) (windowH - (dy - sy) - getHeight());
                    srcRect.size.width = (float) getWidth();
                    srcRect.size.height = (float) getHeight();

                    if (transparent)
                        CGContextClearRect (context, destRect); //xxx needed?

                    CGContextSaveGState (context);
                    CGContextClipToRect (context, destRect);
                    CGContextDrawImage (context, srcRect, tempImage);
                    CGContextRestoreGState (context);
                }

                CGImageRelease (tempImage);

                CGContextFlush (context);
                QDEndCGContext (port, &context);
            }
        }
    }

    juce_UseDebuggingNewOperator
};


//==============================================================================
class MouseCheckTimer  : private Timer,
private DeletedAtShutdown
{
    WindowRef lastWindowUnderMouse;

public:
    MouseCheckTimer()
    {
        lastWindowUnderMouse = 0;
    }

    ~MouseCheckTimer()
    {
        clearSingletonInstance();
    }

    juce_DeclareSingleton (MouseCheckTimer, false)

    void moved (WindowRef w)
    {
        startTimer (200);
        lastWindowUnderMouse = w;
    }

    void timerCallback();
};

juce_ImplementSingleton (MouseCheckTimer)


static EventHandlerUPP handleRepaintEventUPP = 0;
static EventHandlerUPP handleBoundsEventUPP = 0;
static EventHandlerUPP handleKeyEventUPP = 0;
static EventHandlerUPP handleMouseEventUPP = 0;

static void initialiseDragDropHandler();

#if JUCE_QUICKTIME
extern void OfferMouseClickToQuickTime (WindowRef window, ::Point where, long when, long modifiers,
                                        Component* topLevelComp);
#endif


//==============================================================================
class MacWindowComponentPeer  : public ComponentPeer
{
public:
    bool dontRepaint;

    //==============================================================================
    MacWindowComponentPeer (Component* const component,
                            const int windowStyleFlags,
                            WindowRef windowToAttachTo)
        : ComponentPeer (component, windowStyleFlags),
          dontRepaint (false),
          fullScreen (false)
    {
        zeromem (eventHandlerRefs, sizeof (eventHandlerRefs));

        repainter = new MacRepaintManager (component, 3000, this);

        initialiseDragDropHandler();

        static ToolboxObjectClassRef customWindow;
        static WindowDefSpec customWindowSpec;
        static bool needToSetUpClass = true;

        if (needToSetUpClass)
        {
            needToSetUpClass = false;

            const EventTypeSpec customTypes[] = { { kEventClassWindow, kEventWindowDrawFrame } };

            UnsignedWide t;
            Microseconds (&t);
            String juceWindowClassName (T("JUCEWindowClass_") + String ((int) (t.lo & 0x7ffffff)));
            CFStringRef juceWindowClassNameCFString = PlatformUtilities::juceStringToCFString (juceWindowClassName);

            RegisterToolboxObjectClass (juceWindowClassNameCFString,
                                        0, 1, customTypes,
                                        NewEventHandlerUPP (handleFrameRepaintEvent),
                                        0, &customWindow);

            customWindowSpec.defType = kWindowDefObjectClass;
            customWindowSpec.u.classRef = customWindow;

            CFRelease (juceWindowClassNameCFString);
        }

        if (windowToAttachTo != 0)
        {
            isSharedWindow = true;
            windowRef = windowToAttachTo;
        }
        else
        {
            isSharedWindow = false;

            Rect pos;
            pos.left = getComponent()->getX();
            pos.top = getComponent()->getY();
            pos.right = getComponent()->getRight();
            pos.bottom = getComponent()->getBottom();

            int attributes = kWindowStandardHandlerAttribute;
            if ((windowStyleFlags & windowHasDropShadow) == 0)
                attributes |= kWindowNoShadowAttribute;

            if ((windowStyleFlags & windowIgnoresMouseClicks) != 0)
                attributes |= kWindowIgnoreClicksAttribute;

            if ((windowStyleFlags & windowIsTemporary) != 0)
                attributes |= kWindowDoesNotCycleAttribute;

            if ((windowStyleFlags & windowHasTitleBar) == 0)
            {
                attributes |= kWindowCollapseBoxAttribute;

                CreateCustomWindow (&customWindowSpec,
                                    getComponent()->isAlwaysOnTop() ? kUtilityWindowClass
                                                                    : kDocumentWindowClass,
                                    attributes,
                                    &pos,
                                    &windowRef);
            }
            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, &windowRef);
            }
        }

        if (! component->isVisible())
            HideWindow (windowRef);

        InstallStandardEventHandler (GetWindowEventTarget (windowRef));

        void* thisPointer = this;
        SetWindowProperty (windowRef, 'juce', 'ownc', sizeof (void*), &thisPointer);

        const EventTypeSpec types1[] = { { kEventClassWindow, kEventWindowDrawContent } };

        if (handleRepaintEventUPP == 0)
            handleRepaintEventUPP = NewEventHandlerUPP (handleRepaintEvent);

        InstallWindowEventHandler (windowRef, handleRepaintEventUPP, 1, types1, (void*) this, (EventHandlerRef*) &eventHandlerRefs[0]);

        const EventTypeSpec types2[] =
        {
            { kEventClassWindow, kEventWindowBoundsChanged },
            { kEventClassWindow, kEventWindowBoundsChanging },
            { kEventClassWindow, kEventWindowFocusAcquired },
            { kEventClassWindow, kEventWindowFocusRelinquish },
            { kEventClassWindow, kEventWindowCollapsed },
            { kEventClassWindow, kEventWindowExpanded },
            { kEventClassWindow, kEventWindowShown },
            { kEventClassWindow, kEventWindowClose }
        };

        if (handleBoundsEventUPP == 0)
            handleBoundsEventUPP = NewEventHandlerUPP (handleBoundsEvent);

        InstallWindowEventHandler (windowRef, handleBoundsEventUPP, 8, types2, (void*) this, (EventHandlerRef*) &eventHandlerRefs[1]);

        const EventTypeSpec types3[] =
        {
            { kEventClassMouse, kEventMouseDown },
            { kEventClassMouse, kEventMouseUp },
            { kEventClassMouse, kEventMouseMoved },
            { kEventClassMouse, kEventMouseDragged },
            { kEventClassMouse, kEventMouseEntered },
            { kEventClassMouse, kEventMouseExited },
            { kEventClassMouse, kEventMouseWheelMoved }
        };

        if (handleMouseEventUPP == 0)
            handleMouseEventUPP = NewEventHandlerUPP (handleMouseEvent);

        InstallWindowEventHandler (windowRef, handleMouseEventUPP, 7, types3, (void*) this, (EventHandlerRef*) &eventHandlerRefs[2]);

        const EventTypeSpec types4[] =
        {
            { kEventClassKeyboard, kEventRawKeyDown },
            { kEventClassKeyboard, kEventRawKeyUp },
            { kEventClassKeyboard, kEventRawKeyRepeat },
            { kEventClassKeyboard, kEventRawKeyModifiersChanged }
        };

        if (handleKeyEventUPP == 0)
            handleKeyEventUPP = NewEventHandlerUPP (handleKeyEvent);

        InstallWindowEventHandler (windowRef, handleKeyEventUPP, 4, types4, (void*) this, (EventHandlerRef*) &eventHandlerRefs[3]);

        if (component != 0 && ! component->isOpaque())
            SetWindowAlpha (windowRef, 0.9999999f); // to fool it into giving the window an alpha-channel

        if (component != 0 && component->isVisible())
            ActivateWindow (windowRef, component->getWantsKeyboardFocus());

        setTitle (component->getName());
    }

    ~MacWindowComponentPeer()
    {
        minimisedWindows.removeValue (windowRef);

        if (IsValidWindowPtr (windowRef))
        {
            void* nullPointer = 0;
            SetWindowProperty (windowRef, 'juce', 'ownc', sizeof (void*), &nullPointer);

            if (! isSharedWindow)
            {
                DisposeWindow (windowRef);
            }
            else
            {
                for (int i = 0; i < 4; ++i)
                    if (eventHandlerRefs[i] != 0)
                        RemoveEventHandler ((EventHandlerRef) eventHandlerRefs[i]);
            }

            windowRef = 0;
        }

        delete repainter;

        if (lastFocusedWindow == this)
            lastFocusedWindow = 0;
    }

    static int64 getEventTime (EventRef event)
    {
        const int64 millis = (int64) (1000.0 * GetEventTime (event));

        static int64 offset = 0;
        if (offset == 0)
            offset = Time::currentTimeMillis() - millis;

        return offset + millis;
    }

    //==============================================================================
    void* getNativeHandle() const
    {
        return windowRef;
    }

    void setVisible (bool shouldBeVisible)
    {
        if (IsValidWindowPtr (windowRef))
        {
            if (shouldBeVisible)
                ShowWindow (windowRef);
            else
                HideWindow (windowRef);
        }
    }

    void setTitle (const String& title)
    {
        if (IsValidWindowPtr (windowRef))
        {
            Str255 t;
            PlatformUtilities::copyToStr255 (t, title);
            SetWTitle (windowRef, t);
        }
    }

    void setPosition (int x, int y)
    {
        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)
    {
        if (IsValidWindowPtr (windowRef))
        {
            if (w != getComponent()->getWidth()
                || h != getComponent()->getHeight())
            {
                repaint (0, 0, w, h);
            }

            Rect r;
            GetWindowBounds (windowRef, windowRegionToUse, &r);
            r.bottom = r.top + jmax (0, h);
            r.right = r.left + jmax (0, w);
            SetWindowBounds (windowRef, windowRegionToUse, &r);
        }
    }

    void setBounds (int x, int y, int w, int h, const bool isNowFullScreen)
    {
        fullScreen = isNowFullScreen;

        if (IsValidWindowPtr (windowRef))
        {
            if (w != getComponent()->getWidth()
                || h != getComponent()->getHeight())
            {
                repaint (0, 0, w, h);
            }

            Rect r;
            r.left = x;
            r.right = x + jmax (0, w);
            r.top = y;
            r.bottom = y + jmax (0, h);
            SetWindowBounds (windowRef, windowRegionToUse, &r);
        }
    }

    void getBounds (int& x, int& y, int& w, int& h) const
    {
        getWindowPosition (windowRef, x, y, w, h);
    }

    int getScreenX() const
    {
        if (windowRef != 0)
        {
            Rect r;
            GetWindowBounds (windowRef, windowRegionToUse, &r);
            return r.left;
        }

        return 0;
    }

    int getScreenY() const
    {
        if (windowRef != 0)
        {
            Rect r;
            GetWindowBounds (windowRef, windowRegionToUse, &r);
            return r.top;
        }

        return 0;
    }

    void setMinimised (bool shouldBeMinimised)
    {
        setWindowMinimised (windowRef, shouldBeMinimised);
    }

    bool isMinimised() const
    {
        return minimisedWindows.contains (windowRef);
    }

    void setFullScreen (bool shouldBeFullScreen)
    {
        setMinimised (false);

        if (fullScreen != shouldBeFullScreen)
        {
            Rectangle r (lastNonFullscreenBounds);

            if (shouldBeFullScreen)
                r = Desktop::getInstance().getMainMonitorArea();

            if (! r.isEmpty())
                getComponent()->setBounds (r.getX(), r.getY(), r.getWidth(), r.getHeight());

            fullScreen = shouldBeFullScreen;
        }
    }

    bool isFullScreen() const
    {
        return fullScreen;
    }

    bool contains (int x, int y, bool trueIfInAChildWindow) const
    {
        if (x < 0 || y < 0 || x >= component->getWidth() || y >= component->getHeight())
            return false;

        if (! 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);

        return windowRef == ref2;
    }

    const BorderSize getFrameSize() const
    {
        return BorderSize();
    }

    bool setAlwaysOnTop (bool alwaysOnTop)
    {
        // can't do this so return false and let the component create a new window
        return false;
    }

    void toFront (bool makeActiveWindow)
    {
        makeActiveWindow = makeActiveWindow
                            && component->isValidComponent()
                            && (component->getWantsKeyboardFocus()
                                || component->isCurrentlyModal());

        if (windowRef != FrontWindow()
             || (makeActiveWindow && ! IsWindowActive (windowRef))
             || ! Process::isForegroundProcess())
        {
            if (! Process::isForegroundProcess())
            {
                ProcessSerialNumber psn;
                GetCurrentProcess (&psn);
                SetFrontProcess (&psn);
            }

            if (IsValidWindowPtr (windowRef))
            {
                if (makeActiveWindow)
                {
                    SelectWindow (windowRef);
                    SetUserFocusWindow (windowRef);
                    lastFocusedWindow = this;
                }
                else
                {
                    BringToFront (windowRef);
                }

                handleBroughtToFront();
            }
        }
    }

    void toBehind (ComponentPeer* other)
    {
        MacWindowComponentPeer* const otherWindow = dynamic_cast <MacWindowComponentPeer*> (other);

        if (other != 0)
        {
            if (windowRef != 0 && otherWindow->windowRef != 0)
                SendBehind (windowRef, otherWindow->windowRef);
        }
    }

    bool isFocused() const
    {
        return windowRef == GetUserFocusWindow();
    }

    void grabFocus()
    {
        if (IsValidWindowPtr (windowRef))
        {
            SetUserFocusWindow (windowRef);
            lastFocusedWindow = this;
        }
    }

    void repaint (int x, int y, int w, int h)
    {
        repainter->invalidateCache (x, y, w, h);
        repainter->repaint (x, y, w, h);
    }

    void performPendingRepaints()
    {
        repainter->performPendingRepaints();
    }

    //==============================================================================
    static MacWindowComponentPeer* getOwnerOfWindow (WindowRef ref)
    {
        MacWindowComponentPeer* owner = 0;

        if (GetWindowProperty (ref, 'juce', 'ownc', sizeof (void*), 0, &owner) == noErr)
        {
            if (owner->isValidMessageListener())
                return owner;
        }

        return 0;
    }

    //==============================================================================
    juce_UseDebuggingNewOperator

private:
    WindowRef windowRef;
    void* eventHandlerRefs[4];
    bool fullScreen, isSharedWindow;

    //==============================================================================
    class MacRepaintManager  : public RepaintManager
    {
        MacWindowComponentPeer* const peer;

    public:
        MacRepaintManager (Component* const component, const int timeBeforeReleasingImage, MacWindowComponentPeer* const peer_)
            : RepaintManager (component, timeBeforeReleasingImage),
              peer (peer_)
        {
        }

        Image* createNewImage (int w, int h)
        {
            return new MacBitmapImage (Image::ARGB, w, h, false);
        }

        void repaintNow (const RectangleList& areasToPaint)
        {
            const bool transparent = ! peer->getComponent()->isOpaque();

            WindowRef windowRef = peer->windowRef;

#ifndef DONT_USE_QUICKDRAW
            if (! transparent)
            {
                // tragically this QuickDraw implementation seems to be massively faster than the
                // CoreGraphics version, so we'll use it unless the window's transparent.
                // Would be nice to get rid of this stuff and just use CG, so keep an eye on
                // performance in future...
                SetPortWindowPort (windowRef);
            }
#endif

            peer->clearMaskedRegion();

            renderCacheAreasNeedingRepaint();

            int x, y;
            MacBitmapImage* const paintingBuffer = (MacBitmapImage*) getImage (x, y);

            if (paintingBuffer != 0)
            {
                const RectangleList* repaintRegion = &areasToPaint;
                RectangleList temp;

                if (! peer->maskedRegion.isEmpty())
                {
                    temp = areasToPaint;
                    temp.subtract (peer->maskedRegion);
                    repaintRegion = &temp;
                }

                paintingBuffer->blitToWindow (windowRef,
                                              *repaintRegion,
                                              x, y,
                                              transparent);

                for (RectangleList::Iterator i (*repaintRegion); i.next();)
                {
                    const Rectangle& r = i.getRectangle();

                    Rect rect;
                    rect.left = r.getX();
                    rect.top = r.getY();
                    rect.right = r.getRight();
                    rect.bottom = r.getBottom();

                    ValidWindowRect (windowRef, &rect);
                }
            }
        }
    };

    MacRepaintManager* repainter;

    //==============================================================================
    static pascal OSStatus handleRepaintEvent (EventHandlerCallRef, EventRef theEvent, void* userData)
    {
        MacWindowComponentPeer* const peer = (MacWindowComponentPeer*) userData;
        MessageManagerLock messLock;

        RgnHandle clip = NewRgn();
        GetPortClipRegion (GetWindowPort (peer->windowRef), clip);
        Rect bounds;
        GetRegionBounds (clip, &bounds);
        DisposeRgn (clip);

        int x = 0, y = 0, w = 0, h = 0;
        getWindowPosition (peer->windowRef, x, y, w, h);

        bounds.left = jmax (0, bounds.left);
        bounds.top = jmax (0, bounds.top);
        bounds.right = jmin (bounds.right, w);
        bounds.bottom = jmin (bounds.bottom, h);

        peer->repaint (bounds.left,
                       bounds.top,
                       bounds.right - bounds.left,
                       bounds.bottom - bounds.top);

        if (! peer->isSharedWindow)
            peer->performPendingRepaints();

        return noErr;
    }

    static pascal OSStatus handleBoundsEvent (EventHandlerCallRef callRef, EventRef theEvent, void* userData)
    {
        const UInt32 what = GetEventKind (theEvent);

        MacWindowComponentPeer* const peer = (MacWindowComponentPeer*) userData;
        MessageManagerLock messLock;

        if (peer != 0)
        {
            if (what == kEventWindowBoundsChanged)
            {
                Rect r;
                GetWindowBounds (peer->windowRef, windowRegionToUse, &r);
                r.right -= r.left;
                r.left = 0;
                r.bottom -= r.top;
                r.top = 0;
                InvalWindowRect (peer->windowRef, &r);

                peer->handleMovedOrResized();
            }
            else if (what == kEventWindowBoundsChanging)
            {
                if ((peer->styleFlags & (windowIsResizable | windowHasTitleBar)) == (windowIsResizable | windowHasTitleBar)
                     && peer->constrainer != 0)
                {
                    UInt32 atts = 0;
                    GetEventParameter (theEvent, kEventParamAttributes, typeUInt32,
                                       0, sizeof (UInt32), 0, &atts);

                    if ((atts & kWindowBoundsChangeUserResize) != 0)
                    {
                        Rect current;
                        GetEventParameter (theEvent, kEventParamCurrentBounds, typeQDRectangle,
                                           0, sizeof (Rect), 0, &current);

                        int x = current.left;
                        int y = current.top;
                        int w = current.right - current.left;
                        int h = current.bottom - current.top;

                        const Rectangle currentRect (peer->getComponent()->getBounds());

                        peer->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), &current);

                        return noErr;
                    }
                }
            }
            else if (what == kEventWindowFocusAcquired)
            {
                keysCurrentlyDown.clear();
                lastFocusedWindow = peer;
                peer->handleFocusGain();
            }
            else if (what == kEventWindowFocusRelinquish)
            {
                keysCurrentlyDown.clear();
                peer->handleFocusLoss();
            }
            else if (what == kEventWindowCollapsed)
            {
                minimisedWindows.addIfNotAlreadyThere (peer->getNativeHandle());
                peer->handleMovedOrResized();
            }
            else if (what == kEventWindowExpanded)
            {
                minimisedWindows.removeValue (peer->getNativeHandle());
                peer->handleMovedOrResized();
            }
            else if (what == kEventWindowShown)
            {
                if (peer->getComponent() != 0)
                    peer->getComponent()->repaint();
            }
            else if (what == kEventWindowClose)
            {
                peer->handleUserClosingWindow();
            }
        }

        // returning this allows other handlers in the event chain to also get a look at the events
        return eventNotHandledErr;
    }

    static pascal OSStatus handleFrameRepaintEvent (EventHandlerCallRef myHandler,
                                                    EventRef theEvent,
                                                    void* userData)
    {
        // don't draw the frame..
        return noErr;
    }

    static pascal OSStatus handleKeyEvent (EventHandlerCallRef, EventRef theEvent, void* userData)
    {
        MessageManagerLock messLock;
        updateModifiers (theEvent);

        char kc = 0;
        GetEventParameter (theEvent, kEventParamKeyMacCharCodes, typeChar, 0, sizeof (char), 0, &kc);
        int keyCode = (int) (unsigned char) kc;

        UInt32 rawKey = 0;
        GetEventParameter (theEvent, kEventParamKeyCode, typeUInt32, 0, sizeof (UInt32), 0, &rawKey);

        if ((currentModifiers & ModifierKeys::ctrlModifier) != 0)
        {
            if (keyCode >= 1 && keyCode <= 26)
                keyCode += ('A' - 1);
        }

        static const int keyTranslations[] = {
            122, KeyPress::F1Key, 120, KeyPress::F2Key,
            99, KeyPress::F3Key, 118, KeyPress::F4Key,
            96, KeyPress::F5Key, 97, KeyPress::F6Key,
            98, KeyPress::F7Key, 100, KeyPress::F8Key,
            101, KeyPress::F9Key, 109, KeyPress::F10Key,
            103, KeyPress::F11Key, 111, KeyPress::F12Key,
            36, KeyPress::returnKey, 51, KeyPress::backspaceKey,
            123, KeyPress::leftKey, 124, KeyPress::rightKey,
            126, KeyPress::upKey, 125, KeyPress::downKey,
            115, KeyPress::homeKey, 119, KeyPress::endKey,
            116, KeyPress::pageUpKey, 121, KeyPress::pageDownKey,
            76, KeyPress::returnKey,
            0 };

        const int* kt = keyTranslations;

        while (*kt != 0)
        {
            if ((int) rawKey == *kt)
            {
                keyCode = *++kt;
                break;
            }

            kt += 2;
        }

        MacWindowComponentPeer* const peer = (MacWindowComponentPeer*) userData;

        const UInt32 what = GetEventKind (theEvent);

        if (what == kEventRawKeyDown)
        {
            keysCurrentlyDown.addIfNotAlreadyThere ((void*) keyCode);

            peer->handleKeyUpOrDown();
            peer->handleKeyPress (keyCode);
        }
        else if (what == kEventRawKeyUp)
        {
            keysCurrentlyDown.removeValue ((void*)keyCode);
            peer->handleKeyUpOrDown();
        }
        else if (what == kEventRawKeyRepeat)
        {
            peer->handleKeyPress (keyCode);
        }
        else if (what == kEventRawKeyModifiersChanged)
        {
            peer->handleModifierKeysChange();
        }

        return noErr;
    }

    static pascal OSStatus handleMouseEvent (EventHandlerCallRef, EventRef theEvent, void* userData)
    {
        MessageManagerLock messLock;

        MacWindowComponentPeer* const peer = (MacWindowComponentPeer*) userData;

        if (peer == 0)
            return eventNotHandledErr;

        MouseCheckTimer::getInstance()->moved (peer->windowRef);

        ::Point where;
        GetEventParameter (theEvent, kEventParamMouseLocation, typeQDPoint, 0, sizeof(::Point), 0, &where);

        Rect r;
        GetWindowBounds (peer->windowRef, windowRegionToUse, &r);

        int64 time = getEventTime (theEvent);

        switch (GetEventKind (theEvent))
        {
            case kEventMouseMoved:
                updateModifiers (theEvent);
                peer->handleMouseMove (where.h - r.left,
                                       where.v - r.top,
                                       time);
                break;

            case kEventMouseDragged:
                updateModifiers (theEvent);
                peer->handleMouseDrag (where.h - r.left,
                                       where.v - r.top,
                                       time);
                break;

            case kEventMouseDown:
            {
                if (! Process::isForegroundProcess())
                {
                    ProcessSerialNumber psn;
                    GetCurrentProcess (&psn);
                    SetFrontProcess (&psn);

                    peer->toFront (true);
                }

#if JUCE_QUICKTIME
                long mods;
                GetEventParameter (theEvent, kEventParamKeyModifiers, typeUInt32, 0, sizeof (mods), 0, &mods);

                OfferMouseClickToQuickTime (peer->windowRef, where,
                                            EventTimeToTicks (GetEventTime (theEvent)),
                                            mods,
                                            peer->getComponent());
#endif

                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);

                peer->handleMouseDown (where.h - r.left,
                                       where.v - r.top,
                                       time);

                if (peer->getComponent()->isCurrentlyBlockedByAnotherModalComponent()
                     || ! peer->getComponent()->isBroughtToFrontOnMouseClick())
                    return noErr;   // this stops the window getting automatically brought to the front by
                                    // the default handler

                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);

                peer->handleMouseUp (oldModifiers,
                                     where.h - r.left,
                                     where.v - r.top,
                                     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);
                peer->handleMouseWheel (delta * 10, time);

                break;
            }
        }

        return eventNotHandledErr;
    }
};

ComponentPeer* Component::createNewPeer (int styleFlags, void* windowToAttachTo)
{
    return new MacWindowComponentPeer (this, styleFlags, (WindowRef) windowToAttachTo);
}

void juce_macDoPendingRepaintsNow()
{
    for (int i = ComponentPeer::getNumPeers(); --i >= 0;)
    {
        MacWindowComponentPeer* const peer = dynamic_cast<MacWindowComponentPeer*> (ComponentPeer::getPeer (i));

        if (peer != 0)
            peer->performPendingRepaints();
    }
}

//==============================================================================
void MouseCheckTimer::timerCallback()
{
    if (ModifierKeys::getCurrentModifiersRealtime().isAnyMouseButtonDown())
        return;

    stopTimer();

    if (Process::isForegroundProcess())
    {
        bool stillOver = false;
        int x = 0, y = 0, w = 0, h = 0;
        int mx = 0, my = 0;
        const bool validWindow = IsValidWindowPtr (lastWindowUnderMouse);

        if (validWindow)
        {
            getWindowPosition (lastWindowUnderMouse, x, y, w, h);
            Desktop::getMousePosition (mx, my);

            stillOver = (mx >= x && my >= y && mx < x + w && my < y + h);
        }

        if (! stillOver)
        {
            // mouse is outside our windows so set a normal cursor
            SetThemeCursor (kThemeArrowCursor);

            if (validWindow)
            {
                MacWindowComponentPeer* const peer = MacWindowComponentPeer::getOwnerOfWindow (lastWindowUnderMouse);

                if (peer != 0)
                    peer->handleMouseExit (mx - x,
                                           my - y,
                                           Time::currentTimeMillis());
            }
        }
    }
}

//==============================================================================
// called from juce_Messaging.cpp
void juce_HandleProcessFocusChange()
{
    keysCurrentlyDown.clear();

    MacWindowComponentPeer* const nowFocused = MacWindowComponentPeer::getOwnerOfWindow (GetUserFocusWindow());

    if (Process::isForegroundProcess())
    {
        if (lastFocusedWindow == 0)
            lastFocusedWindow = nowFocused;

        if (lastFocusedWindow != 0)
            lastFocusedWindow->handleFocusGain();
    }
    else
    {
        lastFocusedWindow = nowFocused;

        if (lastFocusedWindow != 0)
            lastFocusedWindow->handleFocusLoss();
    }
}


//==============================================================================
OSErr pascal JUCEDragReceiveHandler (WindowRef theWindow,
                                     void* handlerRefCon,
                                     DragRef theDrag)
{
    MessageManagerLock messLock;
    MacWindowComponentPeer* const peer = MacWindowComponentPeer::getOwnerOfWindow (theWindow);

    if (peer != 0)
    {
        const int mx = peer->getComponent()->getMouseXRelative();
        const int my = peer->getComponent()->getMouseYRelative();

        UInt16 numItems = 0;
        if (CountDragItems (theDrag, &numItems) == noErr)
        {
            StringArray filenames;

            for (int i = 0; i < (int) numItems; ++i)
            {
                DragItemRef ref;

                if (GetDragItemReferenceNumber (theDrag, i + 1, &ref) == noErr)
                {
                    const FlavorType flavorType = kDragFlavorTypeHFS;

                    Size size = 0;
                    if (GetFlavorDataSize (theDrag, ref, flavorType, &size) == noErr)
                    {
                        void* data = juce_calloc (size);

                        if (GetFlavorData (theDrag, ref, flavorType, data, &size, 0) == noErr)
                        {
                            HFSFlavor* f = (HFSFlavor*) data;
                            FSRef fsref;

                            if (FSpMakeFSRef (&f->fileSpec, &fsref) == noErr)
                            {
                                char path[2048];
                                zeromem (path, sizeof (path));
                                if (FSRefMakePath (&fsref, (UInt8*) path, sizeof (path) - 1) == noErr)
                                {
                                    filenames.add (path);
                                }
                            }
                        }

                        juce_free (data);
                    }
                }
            }

            filenames.trim();
            filenames.removeEmptyStrings();

            if (filenames.size() > 0)
                peer->handleFilesDropped (mx, my, filenames);
        }
    }

    return noErr;
}

static void initialiseDragDropHandler()
{
    static bool initialised = false;

    if (! initialised)
    {
        initialised = true;
        InstallReceiveHandler (NewDragReceiveHandlerUPP (JUCEDragReceiveHandler), 0, 0);
    }
}

static bool performDrag (DragRef drag)
{
    EventRecord event;
    event.what = mouseDown;
    event.message = 0;
    event.when = TickCount();
    event.where.h = Component::getMouseX();
    event.where.v = Component::getMouseY();
    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)
{
    juce_macDoPendingRepaintsNow();

    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()
{
    ProcessSerialNumber psn, front;
    GetCurrentProcess (&psn);
    GetFrontProcess (&front);

    Boolean b;
    return (SameProcess (&psn, &front, &b) == noErr) && b;
}

//==============================================================================
bool Desktop::canUseSemiTransparentWindows()
{
    return true;
}


//==============================================================================
void Desktop::getMousePosition (int& x, int& y)
{
    CGrafPtr currentPort;
    GetPort (&currentPort);

    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)
{
    CGDirectDisplayID mainDisplayID = CGMainDisplayID();
    CGPoint pos = { x, y };
    CGDisplayMoveCursorToPoint (mainDisplayID, pos);
}

const ModifierKeys ModifierKeys::getCurrentModifiersRealtime()
{
    return ModifierKeys (currentModifiers);
}

//==============================================================================
void juce_updateMultiMonitorInfo (Array <Rectangle>& monitorCoords, const bool clipToWorkArea)
{
    int mainMon = 0;
    int distFrom00 = 0x7fffff;

    GDHandle h = DMGetFirstScreenDevice (true);

    while (h != 0)
    {
        Rect rect;

        jassert (clipToWorkArea); // xxx need to be able to get whole monitor coords too..
        GetAvailableWindowPositioningBounds (h, &rect);

        const int dist = abs (rect.left) + abs (rect.top);
        if (distFrom00 > dist)
        {
            distFrom00 = dist;
            mainMon = monitorCoords.size();
        }

        monitorCoords.add (Rectangle (rect.left,
                                      rect.top,
                                      rect.right - rect.left,
                                      rect.bottom - rect.top));

        h = DMGetNextScreenDevice (h, true);
    }

    // make sure the first in the list is the main monitor
    if (mainMon > 0)
        monitorCoords.swap (mainMon, 0);

    if (monitorCoords.size() == 0)
    {
        BitMap screenBits;
        Rect r = GetQDGlobalsScreenBits (&screenBits)->bounds;

        monitorCoords.add (Rectangle (r.left,
                                      r.top + GetMBarHeight(),
                                      r.right - r.left,
                                      r.bottom - r.top));
    }

    //xxx need to register for display change callbacks
}

//==============================================================================
struct CursorWrapper
{
    Cursor* cursor;
    ThemeCursor themeCursor;
};

void* juce_createMouseCursorFromImage (const Image& image, int hotspotX, int hotspotY)
{
    const int maxW = 16;
    const int maxH = 16;

    const Image* im = &image;
    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* 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* 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)
{
    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)
{
    ThemeCursor id = kThemeArrowCursor;

    switch (type)
    {
    case MouseCursor::NormalCursor:
        id = kThemeArrowCursor;
        break;

    case MouseCursor::NoCursor:
        id = kSpecialNoCursor;
        break;

    case MouseCursor::DraggingHandCursor:
        {
            static unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,2,0,0,0,0,255,255,255,0,
              0,0,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0,
              16,0,0,2,52,148,47,0,200,185,16,130,90,12,74,139,107,84,123,39,
              132,117,151,116,132,146,248,60,209,138,98,22,203,114,34,236,37,52,77,217,
              247,154,191,119,110,240,193,128,193,95,163,56,60,234,98,135,2,0,59 };
            const int cursDataSize = 99;

            return cursorFromData (cursData, cursDataSize, 8, 8);
        }
        break;

    case MouseCursor::CopyingCursor:
        id = kThemeCopyArrowCursor;
        break;

    case MouseCursor::WaitCursor:
        id = kThemeWatchCursor;
        break;

    case MouseCursor::IBeamCursor:
        id = kThemeIBeamCursor;
        break;

    case MouseCursor::PointingHandCursor:
        id = kThemePointingHandCursor;
        break;

    case MouseCursor::LeftRightResizeCursor:
    case MouseCursor::LeftEdgeResizeCursor:
    case MouseCursor::RightEdgeResizeCursor:
        {
            static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,0,0,255,255,255,0,0,0,255,
              255,255,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0,
              16,0,0,2,38,148,143,169,203,237,15,19,0,106,202,64,111,22,32,224,
              9,78,30,213,121,230,121,146,99,8,142,71,183,189,152,20,27,86,132,231,
              58,83,0,0,59 };
            const int cursDataSize = 85;

            return cursorFromData (cursData, cursDataSize, 8, 8);
        }

    case MouseCursor::UpDownResizeCursor:
    case MouseCursor::TopEdgeResizeCursor:
    case MouseCursor::BottomEdgeResizeCursor:
        {
            static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,0,0,255,255,255,0,0,0,255,
              255,255,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0,
              16,0,0,2,38,148,111,128,187,16,202,90,152,48,10,55,169,189,192,245,
              106,121,27,34,142,201,99,158,224,86,154,109,216,61,29,155,105,180,61,190,
              121,84,0,0,59 };
            const int cursDataSize = 85;

            return cursorFromData (cursData, cursDataSize, 8, 8);
        }

    case MouseCursor::TopLeftCornerResizeCursor:
    case MouseCursor::BottomRightCornerResizeCursor:
        {
            static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,0,0,255,255,255,0,0,0,255,
                255,255,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0,
                16,0,0,2,43,132,15,162,187,16,255,18,99,14,202,217,44,158,213,221,
                237,9,225,38,94,35,73,5,31,42,170,108,106,174,112,43,195,209,91,185,
                104,174,131,208,77,66,28,10,0,59 };
            const int cursDataSize = 90;

            return cursorFromData (cursData, cursDataSize, 8, 8);
        }

    case MouseCursor::TopRightCornerResizeCursor:
    case MouseCursor::BottomLeftCornerResizeCursor:
        {
            static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,0,0,255,255,255,0,0,0,255,
                255,255,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0,
                16,0,0,2,45,148,127,160,11,232,16,98,108,14,65,73,107,194,122,223,
                92,65,141,216,145,134,162,153,221,25,128,73,166,62,173,16,203,237,188,94,
                120,46,237,105,239,123,48,80,157,2,0,59 };
            const int cursDataSize = 92;

            return cursorFromData (cursData, cursDataSize, 8, 8);
        }

    case MouseCursor::UpDownLeftRightResizeCursor:
        {
            static const unsigned char cursData[] = {71,73,70,56,57,97,15,0,15,0,145,0,0,0,0,0,255,255,255,0,
                128,128,255,255,255,33,249,4,1,0,0,3,0,44,0,0,0,0,15,0,
                15,0,0,2,46,156,63,129,139,1,202,26,152,48,186,73,109,114,65,85,
                195,37,143,88,93,29,215,101,23,198,178,30,149,158,25,56,134,97,179,61,
                158,213,126,203,234,99,220,34,56,70,1,0,59,0,0 };
            const int cursDataSize = 93;

            return cursorFromData (cursData, cursDataSize, 7, 7);
        }

    case MouseCursor::CrosshairCursor:
        id = kThemeCrossCursor;
        break;
    }

    CursorWrapper* cw = new CursorWrapper();
    cw->cursor = 0;
    cw->themeCursor = id;

    return (void*)cw;
}

void juce_deleteMouseCursor (void* cursorHandle, bool isStandard)
{
    CursorWrapper* cw = (CursorWrapper*)cursorHandle;

    if (cw != 0)
    {
        if (cw->cursor != 0)
            delete cw->cursor;

        delete cw;
    }
}

void MouseCursor::showInAllWindows() const
{
    showInWindow (0);
}

void MouseCursor::showInWindow (ComponentPeer*) const
{
    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;
}


//==============================================================================
void PlatformUtilities::beep()
{
    SysBeep (30);
}

//==============================================================================
void SystemClipboard::copyTextToClipboard (const String& text)
{
    ClearCurrentScrap();
    ScrapRef ref;
    GetCurrentScrap (&ref);

    const int len = text.length();
    const int numBytes = sizeof (UniChar) * len;
    UniChar* const temp = (UniChar*) juce_calloc (numBytes);

    for (int i = 0; i < len; ++i)
        temp[i] = (UniChar) text[i];

    PutScrapFlavor (ref,
                    kScrapFlavorTypeUnicode,
                    kScrapFlavorMaskNone,
                    numBytes,
                    temp);

    juce_free (temp);
}

const String SystemClipboard::getTextFromClipboard()
{
    String result;

    ScrapRef ref;
    GetCurrentScrap (&ref);
    Size size = 0;

    if (GetScrapFlavorSize (ref, kScrapFlavorTypeUnicode, &size) == noErr
         && size > 0)
    {
        void* const data = juce_calloc (size + 8);

        if (GetScrapFlavorData (ref, kScrapFlavorTypeUnicode, &size, data) == noErr)
        {
            result = PlatformUtilities::convertUTF16ToString ((UniChar*) data);
        }

        juce_free (data);
    }

    return result;
}


//==============================================================================
bool AlertWindow::showNativeDialogBox (const String& title,
                                       const String& bodyText,
                                       bool isOkCancel)
{
    Str255 tit, txt;
    PlatformUtilities::copyToStr255 (tit, title);
    PlatformUtilities::copyToStr255 (txt, bodyText);

    AlertStdAlertParamRec ar;
    ar.movable = true;
    ar.helpButton = false;
    ar.filterProc = 0;
    ar.defaultText = (const unsigned char*)-1;
    ar.cancelText = (const unsigned char*)((isOkCancel) ? -1 : 0);
    ar.otherText = 0;
    ar.defaultButton = kAlertStdAlertOKButton;
    ar.cancelButton = 0;
    ar.position = kWindowDefaultPosition;

    SInt16 result;
    StandardAlert (kAlertNoteAlert, tit, txt, &ar, &result);
    return result == kAlertStdAlertOKButton;
}

//==============================================================================
const int KeyPress::spaceKey = ' ';
const int KeyPress::returnKey = kReturnCharCode;
const int KeyPress::escapeKey = kEscapeCharCode;
const int KeyPress::backspaceKey = kBackspaceCharCode;
const int KeyPress::leftKey = kLeftArrowCharCode;
const int KeyPress::rightKey = kRightArrowCharCode;
const int KeyPress::upKey = kUpArrowCharCode;
const int KeyPress::downKey = kDownArrowCharCode;
const int KeyPress::pageUpKey = kPageUpCharCode;
const int KeyPress::pageDownKey = kPageDownCharCode;
const int KeyPress::endKey = kEndCharCode;
const int KeyPress::homeKey = kHomeCharCode;
const int KeyPress::deleteKey = kDeleteCharCode;
const int KeyPress::insertKey = -1;
const int KeyPress::tabKey = kTabCharCode;
const int KeyPress::F1Key = 0x110;
const int KeyPress::F2Key = 0x111;
const int KeyPress::F3Key = 0x112;
const int KeyPress::F4Key = 0x113;
const int KeyPress::F5Key = 0x114;
const int KeyPress::F6Key = 0x115;
const int KeyPress::F7Key = 0x116;
const int KeyPress::F8Key = 0x117;
const int KeyPress::F9Key = 0x118;
const int KeyPress::F10Key = 0x119;
const int KeyPress::F11Key = 0x11a;
const int KeyPress::F12Key = 0x11b;
const int KeyPress::playKey = 0x30000;
const int KeyPress::stopKey = 0x30001;
const int KeyPress::fastForwardKey = 0x30002;
const int KeyPress::rewindKey = 0x30003;

//==============================================================================
#if JUCE_OPENGL

struct OpenGLContextInfo
{
    AGLContext renderContext;
};

void* juce_createOpenGLContext (OpenGLComponent* component, void* sharedContext)
{
    jassert (component != 0);

    MacWindowComponentPeer* const peer = dynamic_cast <MacWindowComponentPeer*> (component->getTopLevelComponent()->getPeer());

    if (peer == 0)
        return 0;

    OpenGLContextInfo* const oc = new OpenGLContextInfo();

    GLint attrib[] = {  AGL_RGBA, AGL_DOUBLEBUFFER,
                        AGL_RED_SIZE, 8,
                        AGL_ALPHA_SIZE, 8,
                        AGL_DEPTH_SIZE, 24,
                        AGL_CLOSEST_POLICY, AGL_NO_RECOVERY,
                        AGL_SAMPLE_BUFFERS_ARB, 1,
                        AGL_SAMPLES_ARB, 4,
                        AGL_NONE };

    oc->renderContext = aglCreateContext (aglChoosePixelFormat (0, 0, attrib),
                                          (sharedContext != 0) ? ((OpenGLContextInfo*) sharedContext)->renderContext
                                                               : 0);

    aglSetDrawable (oc->renderContext,
                    GetWindowPort ((WindowRef) peer->getNativeHandle()));

    return oc;
}

void juce_updateOpenGLWindowPos (void* context, Component* owner, Component* topComp)
{
    jassert (context != 0);
    OpenGLContextInfo* const oc = (OpenGLContextInfo*) context;

    GLint bufferRect[4];

    bufferRect[0] = owner->getScreenX() - topComp->getScreenX();
    bufferRect[1] = topComp->getHeight() - (owner->getHeight() + owner->getScreenY() - topComp->getScreenY());
    bufferRect[2] = owner->getWidth();
    bufferRect[3] = owner->getHeight();

    aglSetInteger (oc->renderContext, AGL_BUFFER_RECT, bufferRect);
    aglEnable (oc->renderContext, AGL_BUFFER_RECT);
}

void juce_deleteOpenGLContext (void* context)
{
    OpenGLContextInfo* const oc = (OpenGLContextInfo*) context;

    aglDestroyContext (oc->renderContext);

    delete oc;
}

bool juce_makeOpenGLContextCurrent (void* context)
{
    OpenGLContextInfo* const oc = (OpenGLContextInfo*) context;

    return aglSetCurrentContext ((oc != 0) ? oc->renderContext : 0);
}

void juce_swapOpenGLBuffers (void* context)
{
    OpenGLContextInfo* const oc = (OpenGLContextInfo*) context;

    if (oc != 0)
        aglSwapBuffers (oc->renderContext);
}

void juce_repaintOpenGLWindow (void* context)
{
}

#endif

END_JUCE_NAMESPACE
