diff --git a/build/macosx/platform_specific_code/juce_mac_Network.cpp b/build/macosx/platform_specific_code/juce_mac_Network.cpp index ecc76173f1..97c231f9f7 100644 --- a/build/macosx/platform_specific_code/juce_mac_Network.cpp +++ b/build/macosx/platform_specific_code/juce_mac_Network.cpp @@ -1,191 +1,191 @@ -/* - ============================================================================== - - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-7 by Raw Material Software ltd. - - ------------------------------------------------------------------------------ - - JUCE can be redistributed and/or modified under the terms of the - GNU General Public License, as published by the Free Software Foundation; - either version 2 of the License, or (at your option) any later version. - - JUCE is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with JUCE; if not, visit www.gnu.org/licenses or write to the - Free Software Foundation, Inc., 59 Temple Place, Suite 330, - Boston, MA 02111-1307 USA - - ------------------------------------------------------------------------------ - - If you'd like to release a closed-source product which uses JUCE, commercial - licenses are also available: visit www.rawmaterialsoftware.com/juce for - more information. - - ============================================================================== -*/ - -#include "../../../src/juce_core/basics/juce_StandardHeader.h" -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -BEGIN_JUCE_NAMESPACE - -#include "../../../src/juce_core/text/juce_String.h" -#include "../../../src/juce_core/basics/juce_Time.h" -#include "../../../src/juce_core/basics/juce_SystemStats.h" -#include "../../../src/juce_core/containers/juce_MemoryBlock.h" -#include "../../../src/juce_core/text/juce_StringArray.h" -#include "../../../src/juce_core/misc/juce_PlatformUtilities.h" -#include "../../../src/juce_core/io/network/juce_URL.h" -#include "juce_mac_HTTPStream.h" - - -//============================================================================== -static bool GetEthernetIterator (io_iterator_t* matchingServices) throw() -{ - mach_port_t masterPort; - - if (IOMasterPort (MACH_PORT_NULL, &masterPort) == KERN_SUCCESS) - { - CFMutableDictionaryRef dict = IOServiceMatching (kIOEthernetInterfaceClass); - - if (dict != 0) - { - CFMutableDictionaryRef propDict = CFDictionaryCreateMutable (kCFAllocatorDefault, - 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - - if (propDict != 0) - { - CFDictionarySetValue (propDict, CFSTR (kIOPrimaryInterface), kCFBooleanTrue); - - CFDictionarySetValue (dict, CFSTR (kIOPropertyMatchKey), propDict); - CFRelease (propDict); - } - } - - return IOServiceGetMatchingServices (masterPort, dict, matchingServices) == KERN_SUCCESS; - } - - return false; -} - -int SystemStats::getMACAddresses (int64* addresses, int maxNum, const bool littleEndian) throw() -{ - int numResults = 0; - io_iterator_t it; - - if (GetEthernetIterator (&it)) - { - io_object_t i; - - while ((i = IOIteratorNext (it)) != 0) - { - io_object_t controller; - - if (IORegistryEntryGetParentEntry (i, kIOServicePlane, &controller) == KERN_SUCCESS) - { - CFTypeRef data = IORegistryEntryCreateCFProperty (controller, - CFSTR (kIOMACAddress), - kCFAllocatorDefault, - 0); - if (data != 0) - { - UInt8 addr [kIOEthernetAddressSize]; - zeromem (addr, sizeof (addr)); - - CFDataGetBytes ((CFDataRef) data, CFRangeMake (0, sizeof (addr)), addr); - CFRelease (data); - - int64 a = 0; - for (int i = 6; --i >= 0;) - a = (a << 8) | addr[i]; - - if (! littleEndian) - a = (int64) swapByteOrder ((uint64) a); - - if (numResults < maxNum) - { - *addresses++ = a; - ++numResults; - } - } - - IOObjectRelease (controller); - } - - IOObjectRelease (i); - } - - IOObjectRelease (it); - } - - return numResults; -} - - -//============================================================================== -bool PlatformUtilities::launchEmailWithAttachments (const String& targetEmailAddress, - const String& emailSubject, - const String& bodyText, - const StringArray& filesToAttach) -{ - String script; - script << "tell application \"Mail\"\r\n" - "set newMessage to make new outgoing message with properties {subject:\"" - << emailSubject - << "\", content:\"" - << bodyText - << "\" & return & return}\r\n" - "tell newMessage\r\n" - "set visible to true\r\n" - "set sender to \"sdfsdfsdfewf\"\r\n" - "make new to recipient at end of to recipients with properties {address:\"" - << targetEmailAddress - << "\"}\r\n"; - - for (int i = 0; i < filesToAttach.size(); ++i) - { - script << "tell content\r\n" - "make new attachment with properties {file name:\"" - << filesToAttach[i] - << "\"} at after the last paragraph\r\n" - "end tell\r\n"; - } - - script << "end tell\r\n" - "end tell\r\n"; - - DBG (script); - ComponentInstance comp = OpenDefaultComponent (kOSAComponentType, kOSAGenericScriptingComponentSubtype); - - OSAID resultID; - AEDesc source; - AECreateDesc (typeUTF8Text, (Ptr) script.toUTF8(), strlen (script.toUTF8()), &source); - OSAError err = OSACompileExecute (comp, &source, - kOSANullScript, kOSAModeNull, - &resultID); - AEDisposeDesc (&source); - CloseComponent (comp); - - return false; -} - - -END_JUCE_NAMESPACE +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#include "../../../src/juce_core/basics/juce_StandardHeader.h" +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +BEGIN_JUCE_NAMESPACE + +#include "../../../src/juce_core/text/juce_String.h" +#include "../../../src/juce_core/basics/juce_Time.h" +#include "../../../src/juce_core/basics/juce_SystemStats.h" +#include "../../../src/juce_core/containers/juce_MemoryBlock.h" +#include "../../../src/juce_core/text/juce_StringArray.h" +#include "../../../src/juce_core/misc/juce_PlatformUtilities.h" +#include "../../../src/juce_core/io/network/juce_URL.h" +#include "juce_mac_HTTPStream.h" + + +//============================================================================== +static bool GetEthernetIterator (io_iterator_t* matchingServices) throw() +{ + mach_port_t masterPort; + + if (IOMasterPort (MACH_PORT_NULL, &masterPort) == KERN_SUCCESS) + { + CFMutableDictionaryRef dict = IOServiceMatching (kIOEthernetInterfaceClass); + + if (dict != 0) + { + CFMutableDictionaryRef propDict = CFDictionaryCreateMutable (kCFAllocatorDefault, + 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + if (propDict != 0) + { + CFDictionarySetValue (propDict, CFSTR (kIOPrimaryInterface), kCFBooleanTrue); + + CFDictionarySetValue (dict, CFSTR (kIOPropertyMatchKey), propDict); + CFRelease (propDict); + } + } + + return IOServiceGetMatchingServices (masterPort, dict, matchingServices) == KERN_SUCCESS; + } + + return false; +} + +int SystemStats::getMACAddresses (int64* addresses, int maxNum, const bool littleEndian) throw() +{ + int numResults = 0; + io_iterator_t it; + + if (GetEthernetIterator (&it)) + { + io_object_t i; + + while ((i = IOIteratorNext (it)) != 0) + { + io_object_t controller; + + if (IORegistryEntryGetParentEntry (i, kIOServicePlane, &controller) == KERN_SUCCESS) + { + CFTypeRef data = IORegistryEntryCreateCFProperty (controller, + CFSTR (kIOMACAddress), + kCFAllocatorDefault, + 0); + if (data != 0) + { + UInt8 addr [kIOEthernetAddressSize]; + zeromem (addr, sizeof (addr)); + + CFDataGetBytes ((CFDataRef) data, CFRangeMake (0, sizeof (addr)), addr); + CFRelease (data); + + int64 a = 0; + for (int i = 6; --i >= 0;) + a = (a << 8) | addr[i]; + + if (! littleEndian) + a = (int64) swapByteOrder ((uint64) a); + + if (numResults < maxNum) + { + *addresses++ = a; + ++numResults; + } + } + + IOObjectRelease (controller); + } + + IOObjectRelease (i); + } + + IOObjectRelease (it); + } + + return numResults; +} + + +//============================================================================== +bool PlatformUtilities::launchEmailWithAttachments (const String& targetEmailAddress, + const String& emailSubject, + const String& bodyText, + const StringArray& filesToAttach) +{ + String script; + script << "tell application \"Mail\"\r\n" + "set newMessage to make new outgoing message with properties {subject:\"" + << emailSubject + << "\", content:\"" + << bodyText + << "\" & return & return}\r\n" + "tell newMessage\r\n" + "set visible to true\r\n" + "set sender to \"sdfsdfsdfewf\"\r\n" + "make new to recipient at end of to recipients with properties {address:\"" + << targetEmailAddress + << "\"}\r\n"; + + for (int i = 0; i < filesToAttach.size(); ++i) + { + script << "tell content\r\n" + "make new attachment with properties {file name:\"" + << filesToAttach[i] + << "\"} at after the last paragraph\r\n" + "end tell\r\n"; + } + + script << "end tell\r\n" + "end tell\r\n"; + + DBG (script); + ComponentInstance comp = OpenDefaultComponent (kOSAComponentType, kOSAGenericScriptingComponentSubtype); + + OSAID resultID; + AEDesc source; + AECreateDesc (typeUTF8Text, (Ptr) script.toUTF8(), strlen (script.toUTF8()), &source); + OSAError err = OSACompileExecute (comp, &source, + kOSANullScript, kOSAModeNull, + &resultID); + AEDisposeDesc (&source); + CloseComponent (comp); + + return false; +} + + +END_JUCE_NAMESPACE diff --git a/build/macosx/platform_specific_code/juce_mac_Threads.cpp b/build/macosx/platform_specific_code/juce_mac_Threads.cpp index 10cfdf84a1..2fa3b1d928 100644 --- a/build/macosx/platform_specific_code/juce_mac_Threads.cpp +++ b/build/macosx/platform_specific_code/juce_mac_Threads.cpp @@ -235,7 +235,7 @@ bool JUCE_CALLTYPE juce_isRunningUnderDebugger() throw() int m[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() }; size_t sz = sizeof (info); sysctl (m, 4, &info, &sz, 0, 0); - testResult = (info.kp_proc.p_flag & P_TRACED) == P_TRACED ? 1 : -1; + testResult = ((info.kp_proc.p_flag & P_TRACED) != 0) ? 1 : -1; } return testResult > 0; diff --git a/build/macosx/platform_specific_code/juce_mac_Windowing.cpp b/build/macosx/platform_specific_code/juce_mac_Windowing.cpp index 27b0b9633e..771f1723c3 100644 --- a/build/macosx/platform_specific_code/juce_mac_Windowing.cpp +++ b/build/macosx/platform_specific_code/juce_mac_Windowing.cpp @@ -1,3604 +1,3604 @@ -/* - ============================================================================== - - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-7 by Raw Material Software ltd. - - ------------------------------------------------------------------------------ - - JUCE can be redistributed and/or modified under the terms of the - GNU General Public License, as published by the Free Software Foundation; - either version 2 of the License, or (at your option) any later version. - - JUCE is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with JUCE; if not, visit www.gnu.org/licenses or write to the - Free Software Foundation, Inc., 59 Temple Place, Suite 330, - Boston, MA 02111-1307 USA - - ------------------------------------------------------------------------------ - - If you'd like to release a closed-source product which uses JUCE, commercial - licenses are also available: visit www.rawmaterialsoftware.com/juce for - more information. - - ============================================================================== -*/ - -#include "../../../src/juce_core/basics/juce_StandardHeader.h" -#include -#include -#include -#include -#include -#include - -#if JUCE_OPENGL -#include -#endif - -BEGIN_JUCE_NAMESPACE - -#include "../../../src/juce_appframework/events/juce_Timer.h" -#include "../../../src/juce_appframework/application/juce_DeletedAtShutdown.h" -#include "../../../src/juce_appframework/events/juce_AsyncUpdater.h" -#include "../../../src/juce_appframework/events/juce_MessageManager.h" -#include "../../../src/juce_core/basics/juce_Singleton.h" -#include "../../../src/juce_core/basics/juce_Random.h" -#include "../../../src/juce_core/threads/juce_Process.h" -#include "../../../src/juce_appframework/application/juce_SystemClipboard.h" -#include "../../../src/juce_appframework/gui/components/keyboard/juce_KeyPress.h" -#include "../../../src/juce_appframework/gui/components/windows/juce_AlertWindow.h" -#include "../../../src/juce_appframework/gui/graphics/geometry/juce_RectangleList.h" -#include "../../../src/juce_appframework/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h" -#include "../../../src/juce_appframework/gui/components/juce_Desktop.h" -#include "../../../src/juce_appframework/gui/components/menus/juce_MenuBarModel.h" -#include "../../../src/juce_core/misc/juce_PlatformUtilities.h" -#include "../../../src/juce_appframework/application/juce_Application.h" -#include "../../../src/juce_appframework/gui/components/special/juce_OpenGLComponent.h" -#include "../../../src/juce_appframework/gui/components/mouse/juce_DragAndDropContainer.h" -#include "../../../src/juce_appframework/gui/components/keyboard/juce_KeyPressMappingSet.h" -#include "../../../src/juce_appframework/gui/graphics/imaging/juce_ImageFileFormat.h" -#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); - } - - 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 (other); - - if (other != 0 && windowRef != 0 && otherWindow->windowRef != 0) - { - if (windowRef == otherWindow->windowRef) - { - HIViewSetZOrder (viewRef, kHIViewZOrderBelow, otherWindow->viewRef); - } - else - { - SendBehind (windowRef, otherWindow->windowRef); - } - } - } - - void setIcon (const Image& /*newIcon*/) - { - // to do.. - } - - //============================================================================== - void viewFocusGain() - { - const MessageManagerLock messLock; - - if (currentlyFocusedPeer != this) - { - if (ComponentPeer::isValidPeer (currentlyFocusedPeer)) - currentlyFocusedPeer->handleFocusLoss(); - - currentlyFocusedPeer = this; - - handleFocusGain(); - } - } - - void viewFocusLoss() - { - if (currentlyFocusedPeer == this) - { - currentlyFocusedPeer = 0; - handleFocusLoss(); - } - } - - bool isFocused() const - { - return windowRef == GetUserFocusWindow() - && HIViewSubtreeContainsFocus (viewRef); - } - - void grabFocus() - { - if ((! isFocused()) && IsValidWindowPtr (windowRef)) - { - SetUserFocusWindow (windowRef); - HIViewAdvanceFocus (viewRef, 0); - } - } - - //============================================================================== - void repaint (int x, int y, int w, int h) - { - if (Rectangle::intersectRectangles (x, y, w, h, - 0, 0, - getComponent()->getWidth(), - getComponent()->getHeight())) - { - if ((getStyleFlags() & windowRepaintedExplictly) == 0) - { - if (isCompositingWindow) - { -#if MACOS_10_3_OR_EARLIER - RgnHandle rgn = NewRgn(); - SetRectRgn (rgn, x, y, x + w, y + h); - HIViewSetNeedsDisplayInRegion (viewRef, rgn, true); - DisposeRgn (rgn); -#else - HIRect r; - r.origin.x = x; - r.origin.y = y; - r.size.width = w; - r.size.height = h; - - HIViewSetNeedsDisplayInRect (viewRef, &r, true); -#endif - } - else - { - if (! isTimerRunning()) - startTimer (20); - } - } - - repainter->repaint (x, y, w, h); - } - } - - void timerCallback() - { - performAnyPendingRepaintsNow(); - } - - void performAnyPendingRepaintsNow() - { - stopTimer(); - - if (component->isVisible()) - { -#if MACOS_10_2_OR_EARLIER - if (! isCompositingWindow) - { - Rect w; - GetWindowBounds (windowRef, windowRegionToUse, &w); - - RgnHandle rgn = NewRgn(); - SetRectRgn (rgn, 0, 0, w.right - w.left, w.bottom - w.top); - UpdateControls (windowRef, rgn); - DisposeRgn (rgn); - } - else - { - EventRef theEvent; - - EventTypeSpec eventTypes[1]; - eventTypes[0].eventClass = kEventClassControl; - eventTypes[0].eventKind = kEventControlDraw; - - int n = 3; - while (--n >= 0 - && ReceiveNextEvent (1, eventTypes, kEventDurationNoWait, true, &theEvent) == noErr) - { - if (GetEventClass (theEvent) == kEventClassAppleEvent) - { - EventRecord eventRec; - if (ConvertEventRefToEventRecord (theEvent, &eventRec)) - AEProcessAppleEvent (&eventRec); - } - else - { - EventTargetRef theTarget = GetEventDispatcherTarget(); - SendEventToEventTarget (theEvent, theTarget); - } - - ReleaseEvent (theEvent); - } - } -#else - HIViewRender (viewRef); -#endif - } - } - - //============================================================================== - juce_UseDebuggingNewOperator - - WindowRef windowRef; - HIViewRef viewRef; - -private: - EventHandlerRef eventHandlerRef; - bool fullScreen, isSharedWindow, isCompositingWindow; - 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); - } - - 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 - - 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 & 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 id = kThemeArrowCursor; - - switch (type) - { - case MouseCursor::NormalCursor: - id = kThemeArrowCursor; - break; - - case MouseCursor::NoCursor: - id = kSpecialNoCursor; - break; - - case MouseCursor::DraggingHandCursor: - { - static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,2,0,0,0,0,255,255,255,0, - 0,0,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, - 16,0,0,2,52,148,47,0,200,185,16,130,90,12,74,139,107,84,123,39, - 132,117,151,116,132,146,248,60,209,138,98,22,203,114,34,236,37,52,77,217, - 247,154,191,119,110,240,193,128,193,95,163,56,60,234,98,135,2,0,59 }; - const int cursDataSize = 99; - - return cursorFromData (cursData, cursDataSize, 8, 8); - } - break; - - case MouseCursor::CopyingCursor: - id = kThemeCopyArrowCursor; - break; - - case MouseCursor::WaitCursor: - id = kThemeWatchCursor; - break; - - case MouseCursor::IBeamCursor: - id = kThemeIBeamCursor; - break; - - case MouseCursor::PointingHandCursor: - id = kThemePointingHandCursor; - break; - - case MouseCursor::LeftRightResizeCursor: - case MouseCursor::LeftEdgeResizeCursor: - case MouseCursor::RightEdgeResizeCursor: - { - static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,0,0,255,255,255,0,0,0,255, - 255,255,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, - 16,0,0,2,38,148,143,169,203,237,15,19,0,106,202,64,111,22,32,224, - 9,78,30,213,121,230,121,146,99,8,142,71,183,189,152,20,27,86,132,231, - 58,83,0,0,59 }; - const int cursDataSize = 85; - - return cursorFromData (cursData, cursDataSize, 8, 8); - } - - case MouseCursor::UpDownResizeCursor: - case MouseCursor::TopEdgeResizeCursor: - case MouseCursor::BottomEdgeResizeCursor: - { - static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,0,0,255,255,255,0,0,0,255, - 255,255,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, - 16,0,0,2,38,148,111,128,187,16,202,90,152,48,10,55,169,189,192,245, - 106,121,27,34,142,201,99,158,224,86,154,109,216,61,29,155,105,180,61,190, - 121,84,0,0,59 }; - const int cursDataSize = 85; - - return cursorFromData (cursData, cursDataSize, 8, 8); - } - - case MouseCursor::TopLeftCornerResizeCursor: - case MouseCursor::BottomRightCornerResizeCursor: - { - static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,0,0,255,255,255,0,0,0,255, - 255,255,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, - 16,0,0,2,43,132,15,162,187,16,255,18,99,14,202,217,44,158,213,221, - 237,9,225,38,94,35,73,5,31,42,170,108,106,174,112,43,195,209,91,185, - 104,174,131,208,77,66,28,10,0,59 }; - const int cursDataSize = 90; - - return cursorFromData (cursData, cursDataSize, 8, 8); - } - - case MouseCursor::TopRightCornerResizeCursor: - case MouseCursor::BottomLeftCornerResizeCursor: - { - static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,0,0,255,255,255,0,0,0,255, - 255,255,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, - 16,0,0,2,45,148,127,160,11,232,16,98,108,14,65,73,107,194,122,223, - 92,65,141,216,145,134,162,153,221,25,128,73,166,62,173,16,203,237,188,94, - 120,46,237,105,239,123,48,80,157,2,0,59 }; - const int cursDataSize = 92; - - return cursorFromData (cursData, cursDataSize, 8, 8); - } - - case MouseCursor::UpDownLeftRightResizeCursor: - { - static const unsigned char cursData[] = {71,73,70,56,57,97,15,0,15,0,145,0,0,0,0,0,255,255,255,0, - 128,128,255,255,255,33,249,4,1,0,0,3,0,44,0,0,0,0,15,0, - 15,0,0,2,46,156,63,129,139,1,202,26,152,48,186,73,109,114,65,85, - 195,37,143,88,93,29,215,101,23,198,178,30,149,158,25,56,134,97,179,61, - 158,213,126,203,234,99,220,34,56,70,1,0,59,0,0 }; - const int cursDataSize = 93; - - return cursorFromData (cursData, cursDataSize, 7, 7); - } - - case MouseCursor::CrosshairCursor: - id = kThemeCrossCursor; - break; - } - - CursorWrapper* cw = new CursorWrapper(); - cw->cursor = 0; - cw->themeCursor = id; - - return (void*) cw; -} - -void juce_deleteMouseCursor (void* const cursorHandle, const bool isStandard) throw() -{ - CursorWrapper* const cw = (CursorWrapper*) cursorHandle; - - if (cw != 0) - { - delete cw->cursor; - delete cw; - } -} - -void MouseCursor::showInAllWindows() const throw() -{ - showInWindow (0); -} - -void MouseCursor::showInWindow (ComponentPeer*) const throw() -{ - const CursorWrapper* const cw = (CursorWrapper*) getHandle(); - - if (cw != 0) - { - static bool isCursorHidden = false; - static bool showingWaitCursor = false; - const bool shouldShowWaitCursor = (cw->themeCursor == kThemeWatchCursor); - const bool shouldHideCursor = (cw->themeCursor == kSpecialNoCursor); - - if (shouldShowWaitCursor != showingWaitCursor - && Process::isForegroundProcess()) - { - showingWaitCursor = shouldShowWaitCursor; - QDDisplayWaitCursor (shouldShowWaitCursor); - } - - if (shouldHideCursor != isCursorHidden) - { - isCursorHidden = shouldHideCursor; - - if (shouldHideCursor) - HideCursor(); - else - ShowCursor(); - } - - if (cw->cursor != 0) - SetCursor (cw->cursor); - else if (! (shouldShowWaitCursor || shouldHideCursor)) - SetThemeCursor (cw->themeCursor); - } -} - -//============================================================================== -Image* juce_createIconForFile (const File& file) -{ - return 0; -} - - -//============================================================================== -class MainMenuHandler; -static MainMenuHandler* mainMenu = 0; - -class MainMenuHandler : private MenuBarModelListener, - private DeletedAtShutdown -{ -public: - MainMenuHandler() throw() - : currentModel (0) - { - } - - ~MainMenuHandler() throw() - { - setMenu (0); - - jassert (mainMenu == this); - mainMenu = 0; - } - - void setMenu (MenuBarModel* const newMenuBarModel) throw() - { - if (currentModel != newMenuBarModel) - { - if (currentModel != 0) - currentModel->removeListener (this); - - currentModel = newMenuBarModel; - - if (currentModel != 0) - currentModel->addListener (this); - - menuBarItemsChanged (0); - } - } - - void menuBarItemsChanged (MenuBarModel*) - { - ClearMenuBar(); - - if (currentModel != 0) - { - int id = 1000; - const StringArray menuNames (currentModel->getMenuBarNames()); - - for (int i = 0; i < menuNames.size(); ++i) - { - const PopupMenu menu (currentModel->getMenuForIndex (i, menuNames [i])); - - MenuRef m = createMenu (menu, menuNames [i], id, i); - - InsertMenu (m, 0); - CFRelease (m); - } - } - } - - void menuCommandInvoked (MenuBarModel*, const ApplicationCommandTarget::InvocationInfo& info) - { - MenuRef menu = 0; - MenuItemIndex index = 0; - GetIndMenuItemWithCommandID (0, info.commandID, 1, &menu, &index); - - if (menu != 0) - { - FlashMenuBar (GetMenuID (menu)); - FlashMenuBar (GetMenuID (menu)); - } - } - - void invoke (const int id, ApplicationCommandManager* const commandManager, const int topLevelIndex) const - { - if (currentModel != 0) - { - if (commandManager != 0) - { - ApplicationCommandTarget::InvocationInfo info (id); - info.invocationMethod = ApplicationCommandTarget::InvocationInfo::fromMenu; - - commandManager->invoke (info, true); - } - - currentModel->menuItemSelected (id, topLevelIndex); - } - } - - MenuBarModel* currentModel; - -private: - static MenuRef createMenu (const PopupMenu menu, - const String& menuName, - int& id, - const int topLevelIndex) - { - MenuRef m = 0; - - if (CreateNewMenu (id++, kMenuAttrAutoDisable, &m) == noErr) - { - CFStringRef name = PlatformUtilities::juceStringToCFString (menuName); - SetMenuTitleWithCFString (m, name); - CFRelease (name); - - PopupMenu::MenuItemIterator iter (menu); - - while (iter.next()) - { - MenuItemIndex index = 0; - - int flags = kMenuAttrAutoDisable | kMenuItemAttrIgnoreMeta | kMenuItemAttrNotPreviousAlternate; - if (! iter.isEnabled) - flags |= kMenuItemAttrDisabled; - - CFStringRef text = PlatformUtilities::juceStringToCFString (iter.itemName.upToFirstOccurrenceOf (T(""), false, true)); - - if (iter.isSeparator) - { - AppendMenuItemTextWithCFString (m, text, kMenuItemAttrSeparator, 0, &index); - } - else if (iter.isSectionHeader) - { - AppendMenuItemTextWithCFString (m, text, kMenuItemAttrSectionHeader, 0, &index); - } - else if (iter.subMenu != 0) - { - AppendMenuItemTextWithCFString (m, text, flags, id++, &index); - - MenuRef sub = createMenu (*iter.subMenu, iter.itemName, id, topLevelIndex); - SetMenuItemHierarchicalMenu (m, index, sub); - CFRelease (sub); - } - else - { - AppendMenuItemTextWithCFString (m, text, flags, iter.itemId, &index); - - if (iter.isTicked) - CheckMenuItem (m, index, true); - - SetMenuItemProperty (m, index, 'juce', 'apcm', sizeof (void*), &iter.commandManager); - SetMenuItemProperty (m, index, 'juce', 'topi', sizeof (int), &topLevelIndex); - - if (iter.commandManager != 0) - { - const Array keyPresses (iter.commandManager->getKeyMappings() - ->getKeyPressesAssignedToCommand (iter.itemId)); - - if (keyPresses.size() > 0) - { - const KeyPress& kp = keyPresses.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 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 (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 & 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 +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#include "../../../src/juce_core/basics/juce_StandardHeader.h" +#include +#include +#include +#include +#include +#include + +#if JUCE_OPENGL +#include +#endif + +BEGIN_JUCE_NAMESPACE + +#include "../../../src/juce_appframework/events/juce_Timer.h" +#include "../../../src/juce_appframework/application/juce_DeletedAtShutdown.h" +#include "../../../src/juce_appframework/events/juce_AsyncUpdater.h" +#include "../../../src/juce_appframework/events/juce_MessageManager.h" +#include "../../../src/juce_core/basics/juce_Singleton.h" +#include "../../../src/juce_core/basics/juce_Random.h" +#include "../../../src/juce_core/threads/juce_Process.h" +#include "../../../src/juce_appframework/application/juce_SystemClipboard.h" +#include "../../../src/juce_appframework/gui/components/keyboard/juce_KeyPress.h" +#include "../../../src/juce_appframework/gui/components/windows/juce_AlertWindow.h" +#include "../../../src/juce_appframework/gui/graphics/geometry/juce_RectangleList.h" +#include "../../../src/juce_appframework/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h" +#include "../../../src/juce_appframework/gui/components/juce_Desktop.h" +#include "../../../src/juce_appframework/gui/components/menus/juce_MenuBarModel.h" +#include "../../../src/juce_core/misc/juce_PlatformUtilities.h" +#include "../../../src/juce_appframework/application/juce_Application.h" +#include "../../../src/juce_appframework/gui/components/special/juce_OpenGLComponent.h" +#include "../../../src/juce_appframework/gui/components/mouse/juce_DragAndDropContainer.h" +#include "../../../src/juce_appframework/gui/components/keyboard/juce_KeyPressMappingSet.h" +#include "../../../src/juce_appframework/gui/graphics/imaging/juce_ImageFileFormat.h" +#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); + } + + 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 (other); + + if (other != 0 && windowRef != 0 && otherWindow->windowRef != 0) + { + if (windowRef == otherWindow->windowRef) + { + HIViewSetZOrder (viewRef, kHIViewZOrderBelow, otherWindow->viewRef); + } + else + { + SendBehind (windowRef, otherWindow->windowRef); + } + } + } + + void setIcon (const Image& /*newIcon*/) + { + // to do.. + } + + //============================================================================== + void viewFocusGain() + { + const MessageManagerLock messLock; + + if (currentlyFocusedPeer != this) + { + if (ComponentPeer::isValidPeer (currentlyFocusedPeer)) + currentlyFocusedPeer->handleFocusLoss(); + + currentlyFocusedPeer = this; + + handleFocusGain(); + } + } + + void viewFocusLoss() + { + if (currentlyFocusedPeer == this) + { + currentlyFocusedPeer = 0; + handleFocusLoss(); + } + } + + bool isFocused() const + { + return windowRef == GetUserFocusWindow() + && HIViewSubtreeContainsFocus (viewRef); + } + + void grabFocus() + { + if ((! isFocused()) && IsValidWindowPtr (windowRef)) + { + SetUserFocusWindow (windowRef); + HIViewAdvanceFocus (viewRef, 0); + } + } + + //============================================================================== + void repaint (int x, int y, int w, int h) + { + if (Rectangle::intersectRectangles (x, y, w, h, + 0, 0, + getComponent()->getWidth(), + getComponent()->getHeight())) + { + if ((getStyleFlags() & windowRepaintedExplictly) == 0) + { + if (isCompositingWindow) + { +#if MACOS_10_3_OR_EARLIER + RgnHandle rgn = NewRgn(); + SetRectRgn (rgn, x, y, x + w, y + h); + HIViewSetNeedsDisplayInRegion (viewRef, rgn, true); + DisposeRgn (rgn); +#else + HIRect r; + r.origin.x = x; + r.origin.y = y; + r.size.width = w; + r.size.height = h; + + HIViewSetNeedsDisplayInRect (viewRef, &r, true); +#endif + } + else + { + if (! isTimerRunning()) + startTimer (20); + } + } + + repainter->repaint (x, y, w, h); + } + } + + void timerCallback() + { + performAnyPendingRepaintsNow(); + } + + void performAnyPendingRepaintsNow() + { + stopTimer(); + + if (component->isVisible()) + { +#if MACOS_10_2_OR_EARLIER + if (! isCompositingWindow) + { + Rect w; + GetWindowBounds (windowRef, windowRegionToUse, &w); + + RgnHandle rgn = NewRgn(); + SetRectRgn (rgn, 0, 0, w.right - w.left, w.bottom - w.top); + UpdateControls (windowRef, rgn); + DisposeRgn (rgn); + } + else + { + EventRef theEvent; + + EventTypeSpec eventTypes[1]; + eventTypes[0].eventClass = kEventClassControl; + eventTypes[0].eventKind = kEventControlDraw; + + int n = 3; + while (--n >= 0 + && ReceiveNextEvent (1, eventTypes, kEventDurationNoWait, true, &theEvent) == noErr) + { + if (GetEventClass (theEvent) == kEventClassAppleEvent) + { + EventRecord eventRec; + if (ConvertEventRefToEventRecord (theEvent, &eventRec)) + AEProcessAppleEvent (&eventRec); + } + else + { + EventTargetRef theTarget = GetEventDispatcherTarget(); + SendEventToEventTarget (theEvent, theTarget); + } + + ReleaseEvent (theEvent); + } + } +#else + HIViewRender (viewRef); +#endif + } + } + + //============================================================================== + juce_UseDebuggingNewOperator + + WindowRef windowRef; + HIViewRef viewRef; + +private: + EventHandlerRef eventHandlerRef; + bool fullScreen, isSharedWindow, isCompositingWindow; + 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); + } + + 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 + + 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 & 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 id = kThemeArrowCursor; + + switch (type) + { + case MouseCursor::NormalCursor: + id = kThemeArrowCursor; + break; + + case MouseCursor::NoCursor: + id = kSpecialNoCursor; + break; + + case MouseCursor::DraggingHandCursor: + { + static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,2,0,0,0,0,255,255,255,0, + 0,0,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, + 16,0,0,2,52,148,47,0,200,185,16,130,90,12,74,139,107,84,123,39, + 132,117,151,116,132,146,248,60,209,138,98,22,203,114,34,236,37,52,77,217, + 247,154,191,119,110,240,193,128,193,95,163,56,60,234,98,135,2,0,59 }; + const int cursDataSize = 99; + + return cursorFromData (cursData, cursDataSize, 8, 8); + } + break; + + case MouseCursor::CopyingCursor: + id = kThemeCopyArrowCursor; + break; + + case MouseCursor::WaitCursor: + id = kThemeWatchCursor; + break; + + case MouseCursor::IBeamCursor: + id = kThemeIBeamCursor; + break; + + case MouseCursor::PointingHandCursor: + id = kThemePointingHandCursor; + break; + + case MouseCursor::LeftRightResizeCursor: + case MouseCursor::LeftEdgeResizeCursor: + case MouseCursor::RightEdgeResizeCursor: + { + static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,0,0,255,255,255,0,0,0,255, + 255,255,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, + 16,0,0,2,38,148,143,169,203,237,15,19,0,106,202,64,111,22,32,224, + 9,78,30,213,121,230,121,146,99,8,142,71,183,189,152,20,27,86,132,231, + 58,83,0,0,59 }; + const int cursDataSize = 85; + + return cursorFromData (cursData, cursDataSize, 8, 8); + } + + case MouseCursor::UpDownResizeCursor: + case MouseCursor::TopEdgeResizeCursor: + case MouseCursor::BottomEdgeResizeCursor: + { + static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,0,0,255,255,255,0,0,0,255, + 255,255,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, + 16,0,0,2,38,148,111,128,187,16,202,90,152,48,10,55,169,189,192,245, + 106,121,27,34,142,201,99,158,224,86,154,109,216,61,29,155,105,180,61,190, + 121,84,0,0,59 }; + const int cursDataSize = 85; + + return cursorFromData (cursData, cursDataSize, 8, 8); + } + + case MouseCursor::TopLeftCornerResizeCursor: + case MouseCursor::BottomRightCornerResizeCursor: + { + static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,0,0,255,255,255,0,0,0,255, + 255,255,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, + 16,0,0,2,43,132,15,162,187,16,255,18,99,14,202,217,44,158,213,221, + 237,9,225,38,94,35,73,5,31,42,170,108,106,174,112,43,195,209,91,185, + 104,174,131,208,77,66,28,10,0,59 }; + const int cursDataSize = 90; + + return cursorFromData (cursData, cursDataSize, 8, 8); + } + + case MouseCursor::TopRightCornerResizeCursor: + case MouseCursor::BottomLeftCornerResizeCursor: + { + static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,0,0,255,255,255,0,0,0,255, + 255,255,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, + 16,0,0,2,45,148,127,160,11,232,16,98,108,14,65,73,107,194,122,223, + 92,65,141,216,145,134,162,153,221,25,128,73,166,62,173,16,203,237,188,94, + 120,46,237,105,239,123,48,80,157,2,0,59 }; + const int cursDataSize = 92; + + return cursorFromData (cursData, cursDataSize, 8, 8); + } + + case MouseCursor::UpDownLeftRightResizeCursor: + { + static const unsigned char cursData[] = {71,73,70,56,57,97,15,0,15,0,145,0,0,0,0,0,255,255,255,0, + 128,128,255,255,255,33,249,4,1,0,0,3,0,44,0,0,0,0,15,0, + 15,0,0,2,46,156,63,129,139,1,202,26,152,48,186,73,109,114,65,85, + 195,37,143,88,93,29,215,101,23,198,178,30,149,158,25,56,134,97,179,61, + 158,213,126,203,234,99,220,34,56,70,1,0,59,0,0 }; + const int cursDataSize = 93; + + return cursorFromData (cursData, cursDataSize, 7, 7); + } + + case MouseCursor::CrosshairCursor: + id = kThemeCrossCursor; + break; + } + + CursorWrapper* cw = new CursorWrapper(); + cw->cursor = 0; + cw->themeCursor = id; + + return (void*) cw; +} + +void juce_deleteMouseCursor (void* const cursorHandle, const bool isStandard) throw() +{ + CursorWrapper* const cw = (CursorWrapper*) cursorHandle; + + if (cw != 0) + { + delete cw->cursor; + delete cw; + } +} + +void MouseCursor::showInAllWindows() const throw() +{ + showInWindow (0); +} + +void MouseCursor::showInWindow (ComponentPeer*) const throw() +{ + const CursorWrapper* const cw = (CursorWrapper*) getHandle(); + + if (cw != 0) + { + static bool isCursorHidden = false; + static bool showingWaitCursor = false; + const bool shouldShowWaitCursor = (cw->themeCursor == kThemeWatchCursor); + const bool shouldHideCursor = (cw->themeCursor == kSpecialNoCursor); + + if (shouldShowWaitCursor != showingWaitCursor + && Process::isForegroundProcess()) + { + showingWaitCursor = shouldShowWaitCursor; + QDDisplayWaitCursor (shouldShowWaitCursor); + } + + if (shouldHideCursor != isCursorHidden) + { + isCursorHidden = shouldHideCursor; + + if (shouldHideCursor) + HideCursor(); + else + ShowCursor(); + } + + if (cw->cursor != 0) + SetCursor (cw->cursor); + else if (! (shouldShowWaitCursor || shouldHideCursor)) + SetThemeCursor (cw->themeCursor); + } +} + +//============================================================================== +Image* juce_createIconForFile (const File& file) +{ + return 0; +} + + +//============================================================================== +class MainMenuHandler; +static MainMenuHandler* mainMenu = 0; + +class MainMenuHandler : private MenuBarModelListener, + private DeletedAtShutdown +{ +public: + MainMenuHandler() throw() + : currentModel (0) + { + } + + ~MainMenuHandler() throw() + { + setMenu (0); + + jassert (mainMenu == this); + mainMenu = 0; + } + + void setMenu (MenuBarModel* const newMenuBarModel) throw() + { + if (currentModel != newMenuBarModel) + { + if (currentModel != 0) + currentModel->removeListener (this); + + currentModel = newMenuBarModel; + + if (currentModel != 0) + currentModel->addListener (this); + + menuBarItemsChanged (0); + } + } + + void menuBarItemsChanged (MenuBarModel*) + { + ClearMenuBar(); + + if (currentModel != 0) + { + int id = 1000; + const StringArray menuNames (currentModel->getMenuBarNames()); + + for (int i = 0; i < menuNames.size(); ++i) + { + const PopupMenu menu (currentModel->getMenuForIndex (i, menuNames [i])); + + MenuRef m = createMenu (menu, menuNames [i], id, i); + + InsertMenu (m, 0); + CFRelease (m); + } + } + } + + void menuCommandInvoked (MenuBarModel*, const ApplicationCommandTarget::InvocationInfo& info) + { + MenuRef menu = 0; + MenuItemIndex index = 0; + GetIndMenuItemWithCommandID (0, info.commandID, 1, &menu, &index); + + if (menu != 0) + { + FlashMenuBar (GetMenuID (menu)); + FlashMenuBar (GetMenuID (menu)); + } + } + + void invoke (const int id, ApplicationCommandManager* const commandManager, const int topLevelIndex) const + { + if (currentModel != 0) + { + if (commandManager != 0) + { + ApplicationCommandTarget::InvocationInfo info (id); + info.invocationMethod = ApplicationCommandTarget::InvocationInfo::fromMenu; + + commandManager->invoke (info, true); + } + + currentModel->menuItemSelected (id, topLevelIndex); + } + } + + MenuBarModel* currentModel; + +private: + static MenuRef createMenu (const PopupMenu menu, + const String& menuName, + int& id, + const int topLevelIndex) + { + MenuRef m = 0; + + if (CreateNewMenu (id++, kMenuAttrAutoDisable, &m) == noErr) + { + CFStringRef name = PlatformUtilities::juceStringToCFString (menuName); + SetMenuTitleWithCFString (m, name); + CFRelease (name); + + PopupMenu::MenuItemIterator iter (menu); + + while (iter.next()) + { + MenuItemIndex index = 0; + + int flags = kMenuAttrAutoDisable | kMenuItemAttrIgnoreMeta | kMenuItemAttrNotPreviousAlternate; + if (! iter.isEnabled) + flags |= kMenuItemAttrDisabled; + + CFStringRef text = PlatformUtilities::juceStringToCFString (iter.itemName.upToFirstOccurrenceOf (T(""), false, true)); + + if (iter.isSeparator) + { + AppendMenuItemTextWithCFString (m, text, kMenuItemAttrSeparator, 0, &index); + } + else if (iter.isSectionHeader) + { + AppendMenuItemTextWithCFString (m, text, kMenuItemAttrSectionHeader, 0, &index); + } + else if (iter.subMenu != 0) + { + AppendMenuItemTextWithCFString (m, text, flags, id++, &index); + + MenuRef sub = createMenu (*iter.subMenu, iter.itemName, id, topLevelIndex); + SetMenuItemHierarchicalMenu (m, index, sub); + CFRelease (sub); + } + else + { + AppendMenuItemTextWithCFString (m, text, flags, iter.itemId, &index); + + if (iter.isTicked) + CheckMenuItem (m, index, true); + + SetMenuItemProperty (m, index, 'juce', 'apcm', sizeof (void*), &iter.commandManager); + SetMenuItemProperty (m, index, 'juce', 'topi', sizeof (int), &topLevelIndex); + + if (iter.commandManager != 0) + { + const Array keyPresses (iter.commandManager->getKeyMappings() + ->getKeyPressesAssignedToCommand (iter.itemId)); + + if (keyPresses.size() > 0) + { + const KeyPress& kp = keyPresses.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 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 (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 & 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 diff --git a/build/win32/platform_specific_code/juce_win32_AudioCDReader.cpp b/build/win32/platform_specific_code/juce_win32_AudioCDReader.cpp index 89caeeb21d..fa4c98d51a 100644 --- a/build/win32/platform_specific_code/juce_win32_AudioCDReader.cpp +++ b/build/win32/platform_specific_code/juce_win32_AudioCDReader.cpp @@ -36,13 +36,18 @@ #include "win32_headers.h" #include +#include +#include + #include "../../../src/juce_core/basics/juce_StandardHeader.h" BEGIN_JUCE_NAMESPACE #include "../../../src/juce_appframework/audio/audio_file_formats/juce_AudioCDReader.h" +#include "../../../src/juce_appframework/audio/audio_file_formats/juce_AudioCDBurner.h" #include "../../../src/juce_appframework/events/juce_Timer.h" #include "../../../src/juce_appframework/application/juce_DeletedAtShutdown.h" +#include "../../../src/juce_appframework/audio/dsp/juce_AudioDataConverters.h" #ifdef _MSC_VER #pragma warning (pop) @@ -2108,4 +2113,381 @@ void AudioCDReader::ejectDisk() ((CDDeviceWrapper*) handle)->cdH->openDrawer (true); } + +//============================================================================== +static IDiscRecorder* enumCDBurners (StringArray* list, int indexToOpen, IDiscMaster** master) +{ + CoInitialize (0); + + IDiscMaster* dm; + IDiscRecorder* result = 0; + + if (SUCCEEDED (CoCreateInstance (CLSID_MSDiscMasterObj, 0, + CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, + IID_IDiscMaster, + (void**) &dm))) + { + if (SUCCEEDED (dm->Open())) + { + IEnumDiscRecorders* drEnum = 0; + + if (SUCCEEDED (dm->EnumDiscRecorders (&drEnum))) + { + IDiscRecorder* dr = 0; + DWORD dummy; + int index = 0; + + while (drEnum->Next (1, &dr, &dummy) == S_OK) + { + if (indexToOpen == index) + { + result = dr; + break; + } + else if (list != 0) + { + BSTR path; + + if (SUCCEEDED (dr->GetPath (&path))) + list->add ((const WCHAR*) path); + } + + ++index; + dr->Release(); + } + + drEnum->Release(); + } + + /*if (redbookFormat != 0) + { + IEnumDiscMasterFormats* mfEnum; + + if (SUCCEEDED (dm->EnumDiscMasterFormats (&mfEnum))) + { + IID formatIID; + DWORD dummy; + + while (mfEnum->Next (1, &formatIID, &dummy) == S_OK) + { + } + + mfEnum->Release(); + } + + redbookFormat + }*/ + + if (master == 0) + dm->Close(); + } + + if (master != 0) + *master = dm; + else + dm->Release(); + } + + return result; +} + +const StringArray AudioCDBurner::findAvailableDevices() +{ + StringArray devs; + enumCDBurners (&devs, -1, 0); + return devs; +} + +AudioCDBurner* AudioCDBurner::openDevice (const int deviceIndex) +{ + AudioCDBurner* b = new AudioCDBurner (deviceIndex); + + if (b->internal == 0) + deleteAndZero (b); + + return b; +} + +class CDBurnerInfo : public IDiscMasterProgressEvents +{ +public: + CDBurnerInfo() + : refCount (1), + progress (0), + shouldCancel (false), + listener (0) + { + } + + ~CDBurnerInfo() + { + } + + HRESULT __stdcall QueryInterface (REFIID id, void __RPC_FAR* __RPC_FAR* result) + { + if (result == 0) + return E_POINTER; + + if (id == IID_IUnknown || id == IID_IDiscMasterProgressEvents) + { + AddRef(); + *result = this; + return S_OK; + } + + *result = 0; + return E_NOINTERFACE; + } + + ULONG __stdcall AddRef() { return ++refCount; } + ULONG __stdcall Release() { jassert (refCount > 0); const int r = --refCount; if (r == 0) delete this; return r; } + + HRESULT __stdcall QueryCancel (boolean* pbCancel) + { + if (listener != 0 && ! shouldCancel) + shouldCancel = listener->audioCDBurnProgress (progress); + + *pbCancel = shouldCancel; + + return S_OK; + } + + HRESULT __stdcall NotifyBlockProgress (long nCompleted, long nTotal) + { + progress = nCompleted / (float) nTotal; + shouldCancel = listener != 0 && listener->audioCDBurnProgress (progress); + + return E_NOTIMPL; + } + + HRESULT __stdcall NotifyPnPActivity (void) { return E_NOTIMPL; } + HRESULT __stdcall NotifyAddProgress (long /*nCompletedSteps*/, long /*nTotalSteps*/) { return E_NOTIMPL; } + HRESULT __stdcall NotifyTrackProgress (long /*nCurrentTrack*/, long /*nTotalTracks*/) { return E_NOTIMPL; } + HRESULT __stdcall NotifyPreparingBurn (long /*nEstimatedSeconds*/) { return E_NOTIMPL; } + HRESULT __stdcall NotifyClosingDisc (long /*nEstimatedSeconds*/) { return E_NOTIMPL; } + HRESULT __stdcall NotifyBurnComplete (HRESULT /*status*/) { return E_NOTIMPL; } + HRESULT __stdcall NotifyEraseComplete (HRESULT /*status*/) { return E_NOTIMPL; } + + IDiscMaster* discMaster; + IDiscRecorder* discRecorder; + IRedbookDiscMaster* redbook; + AudioCDBurner::BurnProgressListener* listener; + float progress; + bool shouldCancel; + +private: + int refCount; +}; + +AudioCDBurner::AudioCDBurner (const int deviceIndex) + : internal (0) +{ + IDiscMaster* discMaster; + IDiscRecorder* dr = enumCDBurners (0, deviceIndex, &discMaster); + + if (dr != 0) + { + IRedbookDiscMaster* redbook; + HRESULT hr = discMaster->SetActiveDiscMasterFormat (IID_IRedbookDiscMaster, (void**) &redbook); + + hr = discMaster->SetActiveDiscRecorder (dr); + + CDBurnerInfo* const info = new CDBurnerInfo(); + internal = info; + + info->discMaster = discMaster; + info->discRecorder = dr; + info->redbook = redbook; + } +} + +AudioCDBurner::~AudioCDBurner() +{ + CDBurnerInfo* const info = (CDBurnerInfo*) internal; + + if (info != 0) + { + info->discRecorder->Close(); + info->redbook->Release(); + info->discRecorder->Release(); + info->discMaster->Release(); + + info->Release(); + } +} + +bool AudioCDBurner::isDiskPresent() const +{ + CDBurnerInfo* const info = (CDBurnerInfo*) internal; + + HRESULT hr = info->discRecorder->OpenExclusive(); + + long type, flags; + hr = info->discRecorder->QueryMediaType (&type, &flags); + + info->discRecorder->Close(); + return hr == S_OK && type != 0 && (flags & MEDIA_WRITABLE) != 0; +} + +int AudioCDBurner::getNumAvailableAudioBlocks() const +{ + CDBurnerInfo* const info = (CDBurnerInfo*) internal; + long blocksFree = 0; + info->redbook->GetAvailableAudioTrackBlocks (&blocksFree); + return blocksFree; +} + +const String AudioCDBurner::burn (AudioCDBurner::BurnProgressListener* listener, + const bool ejectDiscAfterwards) +{ + CDBurnerInfo* const info = (CDBurnerInfo*) internal; + + info->listener = listener; + info->progress = 0; + info->shouldCancel = false; + + UINT cookie; + HRESULT hr = info->discMaster->ProgressAdvise (info, &cookie); + + hr = info->discMaster->RecordDisc (FALSE, // set this to TRUE to make it do a fake burn, without actually writing to the disc + ejectDiscAfterwards); + + String error; + if (hr != S_OK) + { + const char* e = "Couldn't open or write to the CD device"; + + if (hr == IMAPI_E_USERABORT) + e = "User cancelled the write operation"; + else if (hr == IMAPI_E_MEDIUM_NOTPRESENT || hr == IMAPI_E_TRACKOPEN) + e = "No Disk present"; + + error = e; + } + + info->discMaster->ProgressUnadvise (cookie); + info->listener = 0; + + return error; +} + +bool AudioCDBurner::addAudioTrack (AudioFormatReader& source, int numSamples) +{ + CDBurnerInfo* const info = (CDBurnerInfo*) internal; + + long bytesPerBlock; + HRESULT hr = info->redbook->GetAudioBlockSize (&bytesPerBlock); + + const int samplesPerBlock = bytesPerBlock / 4; + bool ok = true; + + hr = info->redbook->CreateAudioTrack ((long) numSamples / (bytesPerBlock * 4)); + + byte* const buffer = (byte*) juce_malloc (bytesPerBlock); + + int* sourceBuffers[2]; + sourceBuffers[0] = (int*) juce_malloc (samplesPerBlock * 4); + sourceBuffers[1] = (int*) juce_malloc (samplesPerBlock * 4); + + int samplesDone = 0; + + for (;;) + { + zeromem (buffer, bytesPerBlock); + + if (! source.read (sourceBuffers, samplesDone, samplesPerBlock)) + { + ok = false; + break; + } + + short* destBuffer = (short*) buffer; + + for (int j = 0; j < samplesPerBlock; ++j) + { + *destBuffer++ = (short) (sourceBuffers [0][j] >> 16); + *destBuffer++ = (short) (sourceBuffers [jmin (1, source.numChannels - 1)][j] >> 16); + } + + hr = info->redbook->AddAudioTrackBlocks (buffer, bytesPerBlock); + + if (hr != S_OK) + { + ok = false; + break; + } + + samplesDone += samplesPerBlock; + + if (samplesDone >= numSamples) + break; + } + + juce_free (sourceBuffers[0]); + juce_free (sourceBuffers[1]); + juce_free (buffer); + + hr = info->redbook->CloseAudioTrack(); + + return ok && hr == S_OK; +} + +bool AudioCDBurner::addAudioTrack (AudioSource& source, int numSamples) +{ + CDBurnerInfo* const info = (CDBurnerInfo*) internal; + + long bytesPerBlock; + HRESULT hr = info->redbook->GetAudioBlockSize (&bytesPerBlock); + + const int samplesPerBlock = bytesPerBlock / 4; + bool ok = true; + + hr = info->redbook->CreateAudioTrack ((long) numSamples / (bytesPerBlock * 4)); + + byte* const buffer = (byte*) juce_malloc (bytesPerBlock); + + AudioSampleBuffer sourceBuffer (2, samplesPerBlock); + int samplesDone = 0; + + source.prepareToPlay (samplesPerBlock, 44100.0); + + while (ok) + { + { + AudioSourceChannelInfo info; + info.buffer = &sourceBuffer; + info.numSamples = samplesPerBlock; + info.startSample = 0; + sourceBuffer.clear(); + + source.getNextAudioBlock (info); + } + + zeromem (buffer, bytesPerBlock); + + AudioDataConverters::convertFloatToInt16LE (sourceBuffer.getSampleData (0, 0), + buffer, samplesPerBlock, 4); + + AudioDataConverters::convertFloatToInt16LE (sourceBuffer.getSampleData (1, 0), + buffer + 2, samplesPerBlock, 4); + + hr = info->redbook->AddAudioTrackBlocks (buffer, bytesPerBlock); + + if (hr != S_OK) + ok = false; + + samplesDone += samplesPerBlock; + + if (samplesDone >= numSamples) + break; + } + + juce_free (buffer); + + hr = info->redbook->CloseAudioTrack(); + + return ok && hr == S_OK; +} + + END_JUCE_NAMESPACE diff --git a/build/win32/platform_specific_code/juce_win32_Network.cpp b/build/win32/platform_specific_code/juce_win32_Network.cpp index 28ab5cca07..1536da421f 100644 --- a/build/win32/platform_specific_code/juce_win32_Network.cpp +++ b/build/win32/platform_specific_code/juce_win32_Network.cpp @@ -417,7 +417,7 @@ bool PlatformUtilities::launchEmailWithAttachments (const String& targetEmailAdd files[i].lpszPathName = (LPTSTR) (LPCTSTR) filesToAttach [i]; } - ok = (mapiSendMail (0, 0, &message, MAPI_DIALOG, 0) == SUCCESS_SUCCESS); + ok = (mapiSendMail (0, 0, &message, MAPI_DIALOG | MAPI_LOGON_UI, 0) == SUCCESS_SUCCESS); } FreeLibrary (h); diff --git a/build/win32/vc8/JUCE.vcproj b/build/win32/vc8/JUCE.vcproj index 95c6d3bc4e..ab4804031c 100644 --- a/build/win32/vc8/JUCE.vcproj +++ b/build/win32/vc8/JUCE.vcproj @@ -533,6 +533,10 @@ RelativePath="..\..\..\src\juce_appframework\audio\audio_file_formats\juce_AiffAudioFormat.h" > + + @@ -641,6 +645,22 @@ RelativePath="..\..\..\src\juce_appframework\audio\audio_file_formats\juce_AudioSubsectionReader.h" > + + + + + + + + @@ -5242,6 +5262,14 @@ RelativePath="..\..\..\src\juce_core\io\streams\juce_BufferedInputStream.h" > + + + + @@ -5330,6 +5358,10 @@ RelativePath="..\..\..\src\juce_core\io\streams\juce_GZIPDecompressorInputStream.h" > + + diff --git a/docs/JUCE changelist.txt b/docs/JUCE changelist.txt index ed04f73d2e..df657c82bd 100644 --- a/docs/JUCE changelist.txt +++ b/docs/JUCE changelist.txt @@ -20,6 +20,10 @@ Changelist for version 1.46 - added a parameter to DragAndDropTarget::isInterestedInDragSource(). This ma - changed the parameters to AudioIODeviceCallback::audioDeviceAboutToStart(), so that it now just supplies a pointer to the device. If you need to, you can still find out the sample rate and block size by asking the device for them. - changes to the URL class to allow file uploading +- new method: PlatformUtilities::launchEmailWithAttachments +- new classes: AudioThumbnail and AudioThumbnailCache, which allow easy rendering of low-res waveform previews +- new classes: InputSource and FileInputSource. These encapsulate some kind of resource, and also replace the XmlInputSource class. + ============================================================================== Changelist for version 1.45 diff --git a/extras/juce demo/build/macosx/jucedemo.xcodeproj/project.pbxproj b/extras/juce demo/build/macosx/jucedemo.xcodeproj/project.pbxproj index e4fbeea181..3c8d8a7933 100644 --- a/extras/juce demo/build/macosx/jucedemo.xcodeproj/project.pbxproj +++ b/extras/juce demo/build/macosx/jucedemo.xcodeproj/project.pbxproj @@ -32,7 +32,7 @@ 84EAE2F309DAAF0B00288D0A /* AGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84EAE2F109DAAF0B00288D0A /* AGL.framework */; }; 84EAE2F409DAAF0B00288D0A /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84EAE2F209DAAF0B00288D0A /* OpenGL.framework */; }; 84F8DB0B099CA8DD00E911ED /* QuickTimeDemo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F8DB0A099CA8DD00E911ED /* QuickTimeDemo.cpp */; }; - 84FDB0950C15BDDD00CD0087 /* libjuce.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 84FDB0940C15BDCE00CD0087 /* libjuce.a */; }; + 84FDB0950C15BDDD00CD0087 /* libjucedebug.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 84FDB0940C15BDCE00CD0087 /* libjucedebug.a */; }; 8D0C4E8D0486CD37000505A6 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 0867D6AAFE840B52C02AAC07 /* InfoPlist.strings */; }; 8D0C4E920486CD37000505A6 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 20286C33FDCF999611CA2CEA /* Carbon.framework */; }; /* End PBXBuildFile section */ @@ -77,7 +77,7 @@ 84EAE2F909DAAF2F00288D0A /* Juce.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Juce.xcodeproj; path = ../../../../build/macosx/Juce.xcodeproj; sourceTree = SOURCE_ROOT; }; 84F8DB0A099CA8DD00E911ED /* QuickTimeDemo.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = QuickTimeDemo.cpp; sourceTree = ""; }; 8D0C4E960486CD37000505A6 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; - 8D0C4E970486CD37000505A6 /* jucedemo.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = jucedemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 8D0C4E970486CD37000505A6 /* jucedemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = jucedemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -85,7 +85,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 84FDB0950C15BDDD00CD0087 /* libjuce.a in Frameworks */, + 84FDB0950C15BDDD00CD0087 /* libjucedebug.a in Frameworks */, 8D0C4E920486CD37000505A6 /* Carbon.framework in Frameworks */, 84C49F2606C0F3200066071B /* CoreAudio.framework in Frameworks */, 84C4A41106C0F9A00066071B /* IOKit.framework in Frameworks */, @@ -181,7 +181,7 @@ 84FDB0900C15BDCE00CD0087 /* Products */ = { isa = PBXGroup; children = ( - 84FDB0940C15BDCE00CD0087 /* libjuce.a */, + 84FDB0940C15BDCE00CD0087 /* libjucedebug.a */, ); name = Products; sourceTree = ""; @@ -228,6 +228,7 @@ 20286C28FDCF999611CA2CEA /* Project object */ = { isa = PBXProject; buildConfigurationList = 84CFBCCF085F14F000C52F85 /* Build configuration list for PBXProject "jucedemo" */; + compatibilityVersion = "Xcode 2.4"; hasScannedForEncodings = 1; mainGroup = 20286C29FDCF999611CA2CEA /* jucedemo */; projectDirPath = ""; @@ -237,6 +238,7 @@ ProjectRef = 84EAE2F909DAAF2F00288D0A /* Juce.xcodeproj */; }, ); + projectRoot = ""; targets = ( 8D0C4E890486CD37000505A6 /* jucedemo */, ); @@ -244,10 +246,10 @@ /* End PBXProject section */ /* Begin PBXReferenceProxy section */ - 84FDB0940C15BDCE00CD0087 /* libjuce.a */ = { + 84FDB0940C15BDCE00CD0087 /* libjucedebug.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; - path = libjuce.a; + path = libjucedebug.a; remoteRef = 84FDB0930C15BDCE00CD0087 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; diff --git a/src/juce_app_includes.h b/src/juce_app_includes.h index d6497d061f..a69e675630 100644 --- a/src/juce_app_includes.h +++ b/src/juce_app_includes.h @@ -197,6 +197,9 @@ #ifndef __JUCE_AIFFAUDIOFORMAT_JUCEHEADER__ #include "juce_appframework/audio/audio_file_formats/juce_AiffAudioFormat.h" #endif +#ifndef __JUCE_AUDIOCDBURNER_JUCEHEADER__ + #include "juce_appframework/audio/audio_file_formats/juce_AudioCDBurner.h" +#endif #ifndef __JUCE_AUDIOCDREADER_JUCEHEADER__ #include "juce_appframework/audio/audio_file_formats/juce_AudioCDReader.h" #endif @@ -215,6 +218,12 @@ #ifndef __JUCE_AUDIOSUBSECTIONREADER_JUCEHEADER__ #include "juce_appframework/audio/audio_file_formats/juce_AudioSubsectionReader.h" #endif +#ifndef __JUCE_AUDIOTHUMBNAIL_JUCEHEADER__ + #include "juce_appframework/audio/audio_file_formats/juce_AudioThumbnail.h" +#endif +#ifndef __JUCE_AUDIOTHUMBNAILCACHE_JUCEHEADER__ + #include "juce_appframework/audio/audio_file_formats/juce_AudioThumbnailCache.h" +#endif #ifndef __JUCE_FLACAUDIOFORMAT_JUCEHEADER__ #include "juce_appframework/audio/audio_file_formats/juce_FlacAudioFormat.h" #endif diff --git a/src/juce_appframework/audio/audio_file_formats/juce_AudioCDBurner.h b/src/juce_appframework/audio/audio_file_formats/juce_AudioCDBurner.h new file mode 100644 index 0000000000..862a5ae5f3 --- /dev/null +++ b/src/juce_appframework/audio/audio_file_formats/juce_AudioCDBurner.h @@ -0,0 +1,89 @@ +/* + ============================================================================== + + 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. + + ============================================================================== +*/ + +#ifndef __JUCE_AUDIOCDBURNER_JUCEHEADER__ +#define __JUCE_AUDIOCDBURNER_JUCEHEADER__ + +#include "juce_AudioFormatReader.h" +#include "../audio_sources/juce_AudioSource.h" + + +//============================================================================== +/** +*/ +class AudioCDBurner +{ +public: + //============================================================================== + static const StringArray findAvailableDevices(); + static AudioCDBurner* openDevice (const int deviceIndex); + + ~AudioCDBurner(); + + //============================================================================== + bool isDiskPresent() const; + + int getNumAvailableAudioBlocks() const; + + bool addAudioTrack (AudioFormatReader& source, int numSamples); + bool addAudioTrack (AudioSource& source, int numSamples); + + /** + + Return true to cancel the current burn operation + */ + class BurnProgressListener + { + public: + BurnProgressListener() throw() {} + virtual ~BurnProgressListener() {} + + /** Called at intervals to report on the progress of the AudioCDBurner. + + To cancel the burn, return true from this. + */ + virtual bool audioCDBurnProgress (float proportionComplete) = 0; + }; + + const String burn (BurnProgressListener* listener, + const bool ejectDiscAfterwards); + + //============================================================================== + juce_UseDebuggingNewOperator + +private: + AudioCDBurner (const int deviceIndex); + + void* internal; +}; + + +#endif // __JUCE_AUDIOCDBURNER_JUCEHEADER__ diff --git a/src/juce_appframework/audio/audio_file_formats/juce_AudioThumbnail.cpp b/src/juce_appframework/audio/audio_file_formats/juce_AudioThumbnail.cpp new file mode 100644 index 0000000000..68bb320d11 --- /dev/null +++ b/src/juce_appframework/audio/audio_file_formats/juce_AudioThumbnail.cpp @@ -0,0 +1,535 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#include "../../../juce_core/basics/juce_StandardHeader.h" + +BEGIN_JUCE_NAMESPACE + +#include "juce_AudioThumbnail.h" +#include "juce_AudioThumbnailCache.h" + +const int timeBeforeDeletingReader = 1000; + + +//============================================================================== +struct AudioThumbnailDataFormat +{ + char thumbnailMagic[4]; + int samplesPerThumbSample; + int64 totalSamples; // source samples + int64 numFinishedSamples; // source samples + int numThumbnailSamples; + int numChannels; + int sampleRate; + char future[16]; + char data[1]; +}; + +#if JUCE_BIG_ENDIAN + static void swap (int& n) { n = (int) swapByteOrder ((uint32) n); } + static void swap (int64& n) { n = (int64) swapByteOrder ((uint64) n); } +#endif + +static void swapEndiannessIfNeeded (AudioThumbnailDataFormat* const d) +{ + (void) d; + +#if JUCE_BIG_ENDIAN + swap (d->samplesPerThumbSample); + swap (d->totalSamples); + swap (d->numFinishedSamples); + swap (d->numThumbnailSamples); + swap (d->numChannels); + swap (d->sampleRate); +#endif +} + +//============================================================================== +AudioThumbnail::AudioThumbnail (const int orginalSamplesPerThumbnailSample_, + AudioFormatManager& formatManagerToUse_, + AudioThumbnailCache& cacheToUse) + : cache (cacheToUse), + formatManagerToUse (formatManagerToUse_), + source (0), + reader (0), + orginalSamplesPerThumbnailSample (orginalSamplesPerThumbnailSample_) +{ + clear(); +} + +AudioThumbnail::~AudioThumbnail() +{ + cache.removeThumbnail (this); + + const ScopedLock sl (readerLock); + deleteAndZero (reader); + + delete source; +} + +void AudioThumbnail::setSource (InputSource* const newSource) +{ + cache.removeThumbnail (this); + + delete source; + source = newSource; + + { + const ScopedLock sl (readerLock); + + deleteAndZero (reader); + reader = createReader(); + } + + clear(); + + if (reader != 0) + { + startTimer (timeBeforeDeletingReader); + + initialiseFromAudioFile (*reader); + + cache.loadThumb (*this, newSource->hashCode()); + + if (! isFullyLoaded()) + cache.addThumbnail (this); + } +} + +bool AudioThumbnail::useTimeSlice() +{ + const ScopedLock sl (readerLock); + + if (isFullyLoaded()) + { + if (reader != 0) + startTimer (timeBeforeDeletingReader); + + cache.removeThumbnail (this); + return false; + } + + if (reader == 0) + reader = createReader(); + + if (reader != 0) + { + readNextBlockFromAudioFile (*reader); + startTimer (timeBeforeDeletingReader); + + sendChangeMessage (this); + + const bool justFinished = isFullyLoaded(); + + if (justFinished) + cache.storeThumb (*this, source->hashCode()); + + return ! justFinished; + } + + return false; +} + +AudioFormatReader* AudioThumbnail::createReader() const +{ + if (source != 0) + { + InputStream* const audioFileStream = source->createInputStream(); + + if (audioFileStream != 0) + return formatManagerToUse.createReaderFor (audioFileStream); + } + + return 0; +} + +void AudioThumbnail::timerCallback() +{ + stopTimer(); + + const ScopedLock sl (readerLock); + deleteAndZero (reader); +} + +void AudioThumbnail::clear() +{ + data.setSize (sizeof (AudioThumbnailDataFormat) + 3); + + AudioThumbnailDataFormat* const d = (AudioThumbnailDataFormat*) data.getData(); + + d->thumbnailMagic[0] = 'j'; + d->thumbnailMagic[1] = 'a'; + d->thumbnailMagic[2] = 't'; + d->thumbnailMagic[3] = 'm'; + + d->samplesPerThumbSample = orginalSamplesPerThumbnailSample; + d->totalSamples = 0; + d->numFinishedSamples = 0; + d->numThumbnailSamples = 0; + d->numChannels = 0; + d->sampleRate = 0; + + numSamplesCached = 0; +} + +void AudioThumbnail::loadFrom (InputStream& input) +{ + data.setSize (0); + input.readIntoMemoryBlock (data); + + AudioThumbnailDataFormat* const d = (AudioThumbnailDataFormat*) data.getData(); + swapEndiannessIfNeeded (d); + + if (! (d->thumbnailMagic[0] == 'j' + && d->thumbnailMagic[1] == 'a' + && d->thumbnailMagic[2] == 't' + && d->thumbnailMagic[3] == 'm')) + { + clear(); + } + + numSamplesCached = 0; +} + +void AudioThumbnail::saveTo (OutputStream& output) const +{ + AudioThumbnailDataFormat* const d = (AudioThumbnailDataFormat*) data.getData(); + swapEndiannessIfNeeded (d); + output.write (data.getData(), data.getSize()); + swapEndiannessIfNeeded (d); +} + +bool AudioThumbnail::initialiseFromAudioFile (AudioFormatReader& reader) +{ + AudioThumbnailDataFormat* d = (AudioThumbnailDataFormat*) data.getData(); + + d->totalSamples = reader.lengthInSamples; + d->numChannels = jmin (2, reader.numChannels); + d->numFinishedSamples = 0; + d->sampleRate = roundDoubleToInt (reader.sampleRate); + d->numThumbnailSamples = (int) (d->totalSamples / d->samplesPerThumbSample) + 1; + + data.setSize (sizeof (AudioThumbnailDataFormat) + 3 + d->numThumbnailSamples * d->numChannels * 2); + + d = (AudioThumbnailDataFormat*) data.getData(); + zeromem (&(d->data[0]), d->numThumbnailSamples * d->numChannels * 2); + + return d->totalSamples > 0; +} + +bool AudioThumbnail::readNextBlockFromAudioFile (AudioFormatReader& reader) +{ + AudioThumbnailDataFormat* const d = (AudioThumbnailDataFormat*) data.getData(); + + if (d->numFinishedSamples < d->totalSamples) + { + const int numToDo = (int) jmin ((int64) 65536, d->totalSamples - d->numFinishedSamples); + + generateSection (reader, + d->numFinishedSamples, + numToDo); + + d->numFinishedSamples += numToDo; + } + + numSamplesCached = 0; // zap the cache + return (d->numFinishedSamples < d->totalSamples); +} + +int AudioThumbnail::getNumChannels() const throw() +{ + const AudioThumbnailDataFormat* const d = (const AudioThumbnailDataFormat*) data.getData(); + jassert (d != 0); + + return d->numChannels; +} + +double AudioThumbnail::getTotalLength() const throw() +{ + const AudioThumbnailDataFormat* const d = (const AudioThumbnailDataFormat*) data.getData(); + jassert (d != 0); + + if (d->sampleRate > 0) + return d->totalSamples / (double)d->sampleRate; + else + return 0.0; +} + +void AudioThumbnail::generateSection (AudioFormatReader& reader, + int64 startSample, + int numSamples) +{ + AudioThumbnailDataFormat* const d = (AudioThumbnailDataFormat*) data.getData(); + jassert (d != 0); + + int firstDataPos = (int) (startSample / d->samplesPerThumbSample); + int lastDataPos = (int) ((startSample + numSamples) / d->samplesPerThumbSample); + + char* l = getChannelData (0); + char* r = getChannelData (1); + + for (int i = firstDataPos; i < lastDataPos; ++i) + { + const int sourceStart = i * d->samplesPerThumbSample; + const int sourceEnd = sourceStart + d->samplesPerThumbSample; + + float lowestLeft, highestLeft, lowestRight, highestRight; + + reader.readMaxLevels (sourceStart, + sourceEnd - sourceStart, + lowestLeft, + highestLeft, + lowestRight, + highestRight); + + int n = i * 2; + + if (r != 0) + { + l [n] = (char) jlimit (-128.0f, 127.0f, lowestLeft * 127.0f); + r [n++] = (char) jlimit (-128.0f, 127.0f, lowestRight * 127.0f); + l [n] = (char) jlimit (-128.0f, 127.0f, highestLeft * 127.0f); + r [n++] = (char) jlimit (-128.0f, 127.0f, highestRight * 127.0f); + } + else + { + l [n++] = (char) jlimit (-128.0f, 127.0f, lowestLeft * 127.0f); + l [n++] = (char) jlimit (-128.0f, 127.0f, highestLeft * 127.0f); + } + } +} + +char* AudioThumbnail::getChannelData (int channel) const +{ + AudioThumbnailDataFormat* const d = (AudioThumbnailDataFormat*) data.getData(); + jassert (d != 0); + + if (channel >= 0 && channel < d->numChannels) + return d->data + (channel * 2 * d->numThumbnailSamples); + + return 0; +} + +bool AudioThumbnail::isFullyLoaded() const throw() +{ + const AudioThumbnailDataFormat* const d = (const AudioThumbnailDataFormat*) data.getData(); + jassert (d != 0); + + return d->numFinishedSamples >= d->totalSamples; +} + +void AudioThumbnail::refillCache (const int numSamples, + double startTime, + const double timePerPixel) +{ + const AudioThumbnailDataFormat* const d = (const AudioThumbnailDataFormat*) data.getData(); + jassert (d != 0); + + if (numSamples <= 0 + || timePerPixel <= 0.0 + || d->sampleRate <= 0) + { + numSamplesCached = 0; + return; + } + + if (numSamples == numSamplesCached + && numChannelsCached == d->numChannels + && startTime == cachedStart + && timePerPixel == cachedTimePerPixel) + { + return; + } + + numSamplesCached = numSamples; + numChannelsCached = d->numChannels; + cachedStart = startTime; + cachedTimePerPixel = timePerPixel; + + cachedLevels.ensureSize (2 * numChannelsCached * numSamples); + + const bool needExtraDetail = (timePerPixel * d->sampleRate <= d->samplesPerThumbSample); + + const ScopedLock sl (readerLock); + + if (needExtraDetail && reader == 0) + reader = createReader(); + + if (reader != 0 && timePerPixel * d->sampleRate <= d->samplesPerThumbSample) + { + startTimer (timeBeforeDeletingReader); + + char* cacheData = (char*) cachedLevels; + int sample = roundDoubleToInt (startTime * d->sampleRate); + + for (int i = numSamples; --i >= 0;) + { + const int nextSample = roundDoubleToInt ((startTime + timePerPixel) * d->sampleRate); + + if (sample >= 0) + { + if (sample >= reader->lengthInSamples) + break; + + float lmin, lmax, rmin, rmax; + + reader->readMaxLevels (sample, + jmax (1, nextSample - sample), + lmin, lmax, rmin, rmax); + + cacheData[0] = (char) jlimit (-128, 127, roundFloatToInt (lmin * 127.0f)); + cacheData[1] = (char) jlimit (-128, 127, roundFloatToInt (lmax * 127.0f)); + + if (numChannelsCached > 1) + { + cacheData[2] = (char) jlimit (-128, 127, roundFloatToInt (rmin * 127.0f)); + cacheData[3] = (char) jlimit (-128, 127, roundFloatToInt (rmax * 127.0f)); + } + + cacheData += 2 * numChannelsCached; + } + + startTime += timePerPixel; + sample = nextSample; + } + } + else + { + for (int channelNum = 0; channelNum < 2; ++channelNum) + { + char* const data = getChannelData (channelNum); + char* cacheData = ((char*) cachedLevels) + channelNum * 2; + + const double timeToThumbSampleFactor = d->sampleRate / (double) d->samplesPerThumbSample; + + startTime = cachedStart; + int sample = roundDoubleToInt (startTime * timeToThumbSampleFactor); + const int numFinished = (int) (d->numFinishedSamples / d->samplesPerThumbSample); + + for (int i = numSamples; --i >= 0;) + { + const int nextSample = roundDoubleToInt ((startTime + timePerPixel) * timeToThumbSampleFactor); + + if (sample >= 0 && data != 0) + { + char mx = -128; + char mn = 127; + + while (sample <= nextSample) + { + if (sample >= numFinished) + break; + + const int n = sample << 1; + const char sampMin = data [n]; + const char sampMax = data [n + 1]; + + if (sampMin < mn) + mn = sampMin; + + if (sampMax > mx) + mx = sampMax; + + ++sample; + } + + if (mn <= mx) + { + cacheData[0] = mn; + cacheData[1] = mx; + } + else + { + cacheData[0] = 1; + cacheData[1] = 0; + } + } + else + { + cacheData[0] = 1; + cacheData[1] = 0; + } + + cacheData += numChannelsCached * 2; + startTime += timePerPixel; + sample = nextSample; + } + } + } +} + +void AudioThumbnail::drawChannel (Graphics& g, + int x, int y, int w, int h, + double startTime, + double endTime, + int channelNum, + const float verticalZoomFactor) +{ + refillCache (w, startTime, (endTime - startTime) / w); + + if (numSamplesCached >= w + && channelNum >= 0 + && channelNum < numChannelsCached) + { + const float topY = (float) y; + const float bottomY = topY + h; + const float midY = topY + h * 0.5f; + const float vscale = verticalZoomFactor * h / 256.0f; + + const Rectangle clip (g.getClipBounds()); + const int skipLeft = clip.getX() - x; + w -= skipLeft; + x += skipLeft; + + const char* cacheData = ((const char*) cachedLevels) + + (channelNum << 1) + + skipLeft * (numChannelsCached << 1); + + while (--w >= 0) + { + const char mn = cacheData[0]; + const char mx = cacheData[1]; + cacheData += numChannelsCached << 1; + + if (mn <= mx) // if the wrong way round, signifies that the sample's not yet known + g.drawLine ((float) x, jmax (midY - mx * vscale - 0.3f, topY), + (float) x, jmin (midY - mn * vscale + 0.3f, bottomY)); + + ++x; + + if (x >= clip.getRight()) + break; + } + } +} + + +END_JUCE_NAMESPACE diff --git a/src/juce_appframework/audio/audio_file_formats/juce_AudioThumbnail.h b/src/juce_appframework/audio/audio_file_formats/juce_AudioThumbnail.h new file mode 100644 index 0000000000..10c4113c1f --- /dev/null +++ b/src/juce_appframework/audio/audio_file_formats/juce_AudioThumbnail.h @@ -0,0 +1,194 @@ +/* + ============================================================================== + + 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. + + ============================================================================== +*/ + +#ifndef __JUCE_AUDIOTHUMBNAIL_JUCEHEADER__ +#define __JUCE_AUDIOTHUMBNAIL_JUCEHEADER__ + +#include "../../../juce_core/threads/juce_TimeSliceThread.h" +#include "../../../juce_core/io/streams/juce_InputSource.h" +#include "../../../juce_core/io/juce_OutputStream.h" +#include "../../events/juce_ChangeBroadcaster.h" +#include "../../events/juce_Timer.h" +#include "../../gui/graphics/contexts/juce_Graphics.h" +#include "juce_AudioFormatReader.h" +#include "juce_AudioFormatManager.h" + +class AudioThumbnailCache; + + +//============================================================================== +/** + Makes it easy to quickly draw scaled views of the waveform shape of an + audio file. + + To use this class, just create an AudioThumbNail class for the file you want + to draw, call setSource to tell it which file or resource to use, then call + drawChannel() to draw it. + + The class will asynchronously scan the wavefile to create its scaled-down view, + so you should make your UI repaint itself as this data comes in. To do this, the + AudioThumbnail is a ChangeBroadcaster, and will broadcast a message when its + listeners should repaint themselves. + + The thumbnail stores an internal low-res version of the wave data, and this can + be loaded and saved to avoid having to scan the file again. + + @see AudioThumbnailCache +*/ +class JUCE_API AudioThumbnail : public ChangeBroadcaster, + public TimeSliceClient, + private Timer +{ +public: + //============================================================================== + /** Creates an audio thumbnail. + + @param sourceSamplesPerThumbnailSample when creating a stored, low-res version + of the audio data, this is the scale at which it should be done + @param formatManagerToUse the audio format manager that is used to open the file + @param cacheToUse an instance of an AudioThumbnailCache - this provides a background + thread and storage that is used to by the thumbnail, and the cache + object can be shared between multiple thumbnails + */ + AudioThumbnail (const int sourceSamplesPerThumbnailSample, + AudioFormatManager& formatManagerToUse, + AudioThumbnailCache& cacheToUse); + + /** Destructor. */ + ~AudioThumbnail(); + + //============================================================================== + /** Specifies the file or stream that contains the audio file. + + For a file, just call + @code + setSource (new FileInputSource (file)) + @endcode + + You can pass a zero in here to clear the thumbnail. + + The source that is passed in will be deleted by this object when it is no + longer needed + */ + void setSource (InputSource* const newSource); + + /** Reloads the low res thumbnail data from an input stream. + + The thumb will automatically attempt to reload itself from its + AudioThumbnailCache. + */ + void loadFrom (InputStream& input); + + /** Saves the low res thumbnail data to an output stream. + + The thumb will automatically attempt to save itself to its + AudioThumbnailCache after it finishes scanning the wave file. + */ + void saveTo (OutputStream& output) const; + + //============================================================================== + /** Returns the number of channels in the file. + */ + int getNumChannels() const throw(); + + /** Returns the length of the audio file. + */ + double getTotalLength() const throw(); + + /** Renders the waveform shape for a channel. + + The waveform will be drawn within the specified rectangle, where startTime + and endTime specify the times within the audio file that should be positioned + at the left and right edges of the rectangle. + + The waveform will be scaled vertically so that a full-volume sample will fill + the rectangle vertically, but you can also specify an extra vertical scale factor + with the verticalZoomFactor parameter. + */ + void drawChannel (Graphics& g, + int x, int y, int w, int h, + double startTime, + double endTime, + int channelNum, + const float verticalZoomFactor); + + /** Returns true if the low res preview is fully generated. + */ + bool isFullyLoaded() const throw(); + + //============================================================================== + /** @internal */ + bool useTimeSlice(); + /** @internal */ + void timerCallback(); + + //============================================================================== + juce_UseDebuggingNewOperator + +private: + AudioFormatManager& formatManagerToUse; + AudioThumbnailCache& cache; + InputSource* source; + + CriticalSection readerLock; + AudioFormatReader* reader; + + MemoryBlock data, cachedLevels; + int orginalSamplesPerThumbnailSample; + + int numChannelsCached, numSamplesCached; + double cachedStart, cachedTimePerPixel; + + void clear(); + + AudioFormatReader* createReader() const; + + void generateSection (AudioFormatReader& reader, + int64 startSample, + int numSamples); + + char* getChannelData (int channel) const; + + void refillCache (const int numSamples, + double startTime, + const double timePerPixel); + + friend class AudioThumbnailCache; + + // true if it needs more callbacks from the readNextBlockFromAudioFile() method + bool initialiseFromAudioFile (AudioFormatReader& reader); + + // returns true if more needs to be read + bool readNextBlockFromAudioFile (AudioFormatReader& reader); +}; + + +#endif // __JUCE_AUDIOTHUMBNAIL_JUCEHEADER__ diff --git a/src/juce_appframework/audio/audio_file_formats/juce_AudioThumbnailCache.cpp b/src/juce_appframework/audio/audio_file_formats/juce_AudioThumbnailCache.cpp new file mode 100644 index 0000000000..2d148ec45e --- /dev/null +++ b/src/juce_appframework/audio/audio_file_formats/juce_AudioThumbnailCache.cpp @@ -0,0 +1,145 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#include "../../../juce_core/basics/juce_StandardHeader.h" + +BEGIN_JUCE_NAMESPACE + +#include "juce_AudioThumbnailCache.h" +#include "../../../juce_core/io/streams/juce_MemoryInputStream.h" +#include "../../../juce_core/io/streams/juce_MemoryOutputStream.h" + + +//============================================================================== +struct ThumbnailCacheEntry +{ + int64 hash; + uint32 lastUsed; + MemoryBlock data; + + juce_UseDebuggingNewOperator +}; + +//============================================================================== +AudioThumbnailCache::AudioThumbnailCache (const int maxNumThumbsToStore_) + : TimeSliceThread (T("thumb cache")), + maxNumThumbsToStore (maxNumThumbsToStore_) +{ + startThread (2); +} + +AudioThumbnailCache::~AudioThumbnailCache() +{ +} + +bool AudioThumbnailCache::loadThumb (AudioThumbnail& thumb, const int64 hashCode) +{ + for (int i = thumbs.size(); --i >= 0;) + { + if (thumbs[i]->hash == hashCode) + { + MemoryInputStream in ((const char*) thumbs[i]->data, + thumbs[i]->data.getSize(), + false); + + thumb.loadFrom (in); + + thumbs[i]->lastUsed = Time::getMillisecondCounter(); + return true; + } + } + + return false; +} + +void AudioThumbnailCache::storeThumb (const AudioThumbnail& thumb, + const int64 hashCode) +{ + MemoryOutputStream out; + thumb.saveTo (out); + + ThumbnailCacheEntry* te = 0; + + for (int i = thumbs.size(); --i >= 0;) + { + if (thumbs[i]->hash == hashCode) + { + te = thumbs[i]; + break; + } + } + + if (te == 0) + { + te = new ThumbnailCacheEntry(); + te->hash = hashCode; + + if (thumbs.size() < maxNumThumbsToStore) + { + thumbs.add (te); + } + else + { + int oldest = 0; + unsigned int oldestTime = Time::getMillisecondCounter() + 1; + + int i; + for (i = thumbs.size(); --i >= 0;) + if (thumbs[i]->lastUsed < oldestTime) + oldest = i; + + thumbs.set (i, te); + } + } + + te->lastUsed = Time::getMillisecondCounter(); + te->data.setSize (0); + te->data.append (out.getData(), out.getDataSize()); +} + +void AudioThumbnailCache::clear() +{ + thumbs.clear(); +} + +//============================================================================== +void AudioThumbnailCache::addThumbnail (AudioThumbnail* const thumb) +{ + addTimeSliceClient (thumb); +} + +void AudioThumbnailCache::removeThumbnail (AudioThumbnail* const thumb) +{ + removeTimeSliceClient (thumb); +} + + +END_JUCE_NAMESPACE diff --git a/src/juce_appframework/audio/audio_file_formats/juce_AudioThumbnailCache.h b/src/juce_appframework/audio/audio_file_formats/juce_AudioThumbnailCache.h new file mode 100644 index 0000000000..9b940f82b9 --- /dev/null +++ b/src/juce_appframework/audio/audio_file_formats/juce_AudioThumbnailCache.h @@ -0,0 +1,98 @@ +/* + ============================================================================== + + 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. + + ============================================================================== +*/ + +#ifndef __JUCE_AUDIOTHUMBNAILCACHE_JUCEHEADER__ +#define __JUCE_AUDIOTHUMBNAILCACHE_JUCEHEADER__ + +#include "juce_AudioThumbnail.h" +struct ThumbnailCacheEntry; + + +//============================================================================== +/** + An instance of this class is used to manage multiple AudioThumbnail objects. + + The cache runs a single background thread that is shared by all the thumbnails + that need it, and it maintains a set of low-res previews in memory, to avoid + having to re-scan audio files too often. + + @see AudioThumbnail +*/ +class JUCE_API AudioThumbnailCache : public TimeSliceThread +{ +public: + //============================================================================== + /** Creates a cache object. + + The maxNumThumbsToStore parameter lets you specify how many previews should + be kept in memory at once. + */ + AudioThumbnailCache (const int maxNumThumbsToStore); + + /** Destructor. */ + ~AudioThumbnailCache(); + + //============================================================================== + /** Clears out any stored thumbnails. + */ + void clear(); + + /** Reloads the specified thumb if this cache contains the appropriate stored + data. + + This is called automatically by the AudioThumbnail class, so you shouldn't + normally need to call it directly. + */ + bool loadThumb (AudioThumbnail& thumb, const int64 hashCode); + + /** Stores the cachable data from the specified thumb in this cache. + + This is called automatically by the AudioThumbnail class, so you shouldn't + normally need to call it directly. + */ + void storeThumb (const AudioThumbnail& thumb, const int64 hashCode); + + + //============================================================================== + juce_UseDebuggingNewOperator + +private: + //============================================================================== + OwnedArray thumbs; + int maxNumThumbsToStore; + + friend class AudioThumbnail; + void addThumbnail (AudioThumbnail* const thumb); + void removeThumbnail (AudioThumbnail* const thumb); +}; + + +#endif // __JUCE_AUDIOTHUMBNAILCACHE_JUCEHEADER__ diff --git a/src/juce_appframework/gui/components/controls/juce_ListBox.cpp b/src/juce_appframework/gui/components/controls/juce_ListBox.cpp index 24eabe9f80..df9130e486 100644 --- a/src/juce_appframework/gui/components/controls/juce_ListBox.cpp +++ b/src/juce_appframework/gui/components/controls/juce_ListBox.cpp @@ -666,7 +666,7 @@ int ListBox::getRowNumberOfComponent (Component* const rowComponent) const throw return viewport->getRowNumberOfComponent (rowComponent); } -const Rectangle ListBox::getRowPosition (const int rowNumber, +const Rectangle ListBox::getRowPosition (const int rowNumber, const bool relativeToComponentTopLeft) const throw() { const int rowHeight = getRowHeight(); @@ -675,7 +675,7 @@ const Rectangle ListBox::getRowPosition (const int rowNumber, if (relativeToComponentTopLeft) y -= viewport->getViewPositionY(); - return Rectangle (viewport->getX(), y, + return Rectangle (viewport->getX(), y, viewport->getViewedComponent()->getWidth(), rowHeight); } diff --git a/src/juce_appframework/gui/components/controls/juce_ListBox.h b/src/juce_appframework/gui/components/controls/juce_ListBox.h index 3cf21027b7..f17110e675 100644 --- a/src/juce_appframework/gui/components/controls/juce_ListBox.h +++ b/src/juce_appframework/gui/components/controls/juce_ListBox.h @@ -384,8 +384,8 @@ public: /** Finds a row index that would be the most suitable place to insert a new item for a given position. - This is useful when the user is e.g. dragging and dropping onto the listbox, - because it lets you easily choose the best position to insert the item that + This is useful when the user is e.g. dragging and dropping onto the listbox, + because it lets you easily choose the best position to insert the item that they drop, based on where they drop it. If the position is out of range, this will return -1. If the position is diff --git a/src/juce_appframework/gui/components/controls/juce_TableListBox.cpp b/src/juce_appframework/gui/components/controls/juce_TableListBox.cpp index 58267af2e9..c6d4cc35ce 100644 --- a/src/juce_appframework/gui/components/controls/juce_TableListBox.cpp +++ b/src/juce_appframework/gui/components/controls/juce_TableListBox.cpp @@ -388,7 +388,7 @@ bool TableListBox::isAutoSizeMenuOptionShown() const throw() return autoSizeOptionsShown; } -const Rectangle TableListBox::getCellPosition (const int columnId, +const Rectangle TableListBox::getCellPosition (const int columnId, const int rowNumber, const bool relativeToComponentTopLeft) const { diff --git a/src/juce_appframework/gui/components/controls/juce_TableListBox.h b/src/juce_appframework/gui/components/controls/juce_TableListBox.h index ec5a0dedc2..2576305495 100644 --- a/src/juce_appframework/gui/components/controls/juce_TableListBox.h +++ b/src/juce_appframework/gui/components/controls/juce_TableListBox.h @@ -264,14 +264,14 @@ public: /** Returns the position of one of the cells in the table. - If relativeToComponentTopLeft is true, the co-ordinates are relative to - the table component's top-left. The row number isn't checked to see if it's + If relativeToComponentTopLeft is true, the co-ordinates are relative to + the table component's top-left. The row number isn't checked to see if it's in-range, but the column ID must exist or this will return an empty rectangle. - If relativeToComponentTopLeft is false, the co-ords are relative to the + If relativeToComponentTopLeft is false, the co-ords are relative to the top-left of the table's top-left cell. */ - const Rectangle getCellPosition (const int columnId, + const Rectangle getCellPosition (const int columnId, const int rowNumber, const bool relativeToComponentTopLeft) const; diff --git a/src/juce_core/basics/juce_DataConversions.h b/src/juce_core/basics/juce_DataConversions.h index 77eb5a1719..a88f22df7e 100644 --- a/src/juce_core/basics/juce_DataConversions.h +++ b/src/juce_core/basics/juce_DataConversions.h @@ -155,10 +155,10 @@ inline void bigEndian24BitToChars (const int value, char* const destBytes) throw This is faster than using the normal c++ cast to convert a double to an int, and it will round the value to the nearest integer, rather than rounding it down - like the normal cast does. - - Note that this routine gets its speed at the expense of some accuracy, and when - rounding values whose floating point component is exactly 0.5, odd numbers and + like the normal cast does. + + Note that this routine gets its speed at the expense of some accuracy, and when + rounding values whose floating point component is exactly 0.5, odd numbers and even numbers will be rounded up or down differently. For a more accurate conversion, see roundDoubleToIntAccurate(). */ @@ -190,8 +190,8 @@ inline int roundDoubleToIntAccurate (const double value) throw() it will round the value to the nearest integer, rather than rounding it down like the normal cast does. - Note that this routine gets its speed at the expense of some accuracy, and when - rounding values whose floating point component is exactly 0.5, odd numbers and + Note that this routine gets its speed at the expense of some accuracy, and when + rounding values whose floating point component is exactly 0.5, odd numbers and even numbers will be rounded up or down differently. */ inline int roundFloatToInt (const float value) throw() diff --git a/src/juce_core/io/juce_OutputStream.cpp b/src/juce_core/io/juce_OutputStream.cpp index edd2fd3500..51520ef0ec 100644 --- a/src/juce_core/io/juce_OutputStream.cpp +++ b/src/juce_core/io/juce_OutputStream.cpp @@ -46,7 +46,7 @@ static VoidArray activeStreams; void juce_CheckForDanglingStreams() { - /* + /* It's always a bad idea to leak any object, but if you're leaking output streams, then there's a good chance that you're failing to flush a file to disk properly, which could result in corrupted data and other similar diff --git a/src/juce_core/io/streams/juce_FileInputSource.cpp b/src/juce_core/io/streams/juce_FileInputSource.cpp new file mode 100644 index 0000000000..fce77c8783 --- /dev/null +++ b/src/juce_core/io/streams/juce_FileInputSource.cpp @@ -0,0 +1,66 @@ +/* + ============================================================================== + + 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 "../../basics/juce_StandardHeader.h" + +BEGIN_JUCE_NAMESPACE + +#include "juce_FileInputSource.h" +#include "../files/juce_FileInputStream.h" + + +//============================================================================== +FileInputSource::FileInputSource (const File& file_) throw() + : file (file_) +{ +} + +FileInputSource::~FileInputSource() +{ +} + +InputStream* FileInputSource::createInputStream() +{ + return file.createInputStream(); +} + +InputStream* FileInputSource::createInputStreamFor (const String& relatedItemPath) +{ + return file.getSiblingFile (relatedItemPath).createInputStream(); +} + +int64 FileInputSource::hashCode() const +{ + return file.hashCode(); +} + + +END_JUCE_NAMESPACE diff --git a/src/juce_core/io/streams/juce_FileInputSource.h b/src/juce_core/io/streams/juce_FileInputSource.h new file mode 100644 index 0000000000..76d896bab6 --- /dev/null +++ b/src/juce_core/io/streams/juce_FileInputSource.h @@ -0,0 +1,67 @@ +/* + ============================================================================== + + 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. + + ============================================================================== +*/ + +#ifndef __JUCE_FILEINPUTSOURCE_JUCEHEADER__ +#define __JUCE_FILEINPUTSOURCE_JUCEHEADER__ + +#include "juce_InputSource.h" +#include "../files/juce_File.h" + + +//============================================================================== +/** + A type of InputSource that represents a normal file. + + @see InputSource +*/ +class JUCE_API FileInputSource : public InputSource +{ +public: + //============================================================================== + FileInputSource (const File& file) throw(); + ~FileInputSource(); + + InputStream* createInputStream(); + InputStream* createInputStreamFor (const String& relatedItemPath); + int64 hashCode() const; + + //============================================================================== + juce_UseDebuggingNewOperator + +private: + const File file; + + FileInputSource (const FileInputSource&); + const FileInputSource& operator= (const FileInputSource&); +}; + + +#endif // __JUCE_FILEINPUTSOURCE_JUCEHEADER__ diff --git a/src/juce_core/io/streams/juce_InputSource.h b/src/juce_core/io/streams/juce_InputSource.h new file mode 100644 index 0000000000..461fe0cc43 --- /dev/null +++ b/src/juce_core/io/streams/juce_InputSource.h @@ -0,0 +1,80 @@ +/* + ============================================================================== + + 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. + + ============================================================================== +*/ + +#ifndef __JUCE_INPUTSOURCE_JUCEHEADER__ +#define __JUCE_INPUTSOURCE_JUCEHEADER__ + +#include "../juce_InputStream.h" + +//============================================================================== +/** + A lightweight object that can create a stream to read some kind of resource. + + This may be used to refer to a file, or some other kind of source, allowing a + caller to create an input stream that can read from it when required. + + @see FileInputSource +*/ +class JUCE_API InputSource +{ +public: + //============================================================================== + InputSource() throw() {} + + /** Destructor. */ + virtual ~InputSource() {} + + //============================================================================== + /** Returns a new InputStream to read this item. + + @returns an inputstream that the caller will delete, or 0 if + the filename isn't found. + */ + virtual InputStream* createInputStream() = 0; + + /** Returns a new InputStream to read an item, relative. + + @param relatedItemPath the relative pathname of the resource that is required + @returns an inputstream that the caller will delete, or 0 if + the item isn't found. + */ + virtual InputStream* createInputStreamFor (const String& relatedItemPath) = 0; + + /** Returns a hash code that uniquely represents this item. + */ + virtual int64 hashCode() const = 0; + + //============================================================================== + juce_UseDebuggingNewOperator +}; + + +#endif // __JUCE_INPUTSOURCE_JUCEHEADER__ diff --git a/src/juce_core/text/juce_XmlDocument.cpp b/src/juce_core/text/juce_XmlDocument.cpp index 716ded1a7c..a67c600500 100644 --- a/src/juce_core/text/juce_XmlDocument.cpp +++ b/src/juce_core/text/juce_XmlDocument.cpp @@ -34,17 +34,7 @@ BEGIN_JUCE_NAMESPACE #include "juce_XmlDocument.h" -#include "../io/files/juce_FileInputStream.h" - - -//============================================================================== -XmlInputSource::XmlInputSource() throw() -{ -} - -XmlInputSource::~XmlInputSource() -{ -} +#include "../io/streams/juce_FileInputSource.h" //============================================================================== @@ -60,34 +50,6 @@ static bool isXmlIdentifierChar_Slow (const tchar c) throw() #define isXmlIdentifierChar(c) \ ((c > 0 && c <= 127) ? identifierLookupTable [(int) c] : isXmlIdentifierChar_Slow (c)) -//============================================================================== -class FileInputSource : public XmlInputSource -{ -public: - FileInputSource (const File& file_) throw() - : file (file_) - { - } - - ~FileInputSource() - { - } - - InputStream* createInputStreamFor (const String& filename) - { - if (filename.isEmpty()) - return file.createInputStream(); - else - return file.getSiblingFile (filename).createInputStream(); - } - -private: - const File file; - - FileInputSource (const FileInputSource&); - const FileInputSource& operator= (const FileInputSource&); -}; - //============================================================================== XmlDocument::XmlDocument (const String& documentText) throw() @@ -106,7 +68,7 @@ XmlDocument::~XmlDocument() throw() delete inputSource; } -void XmlDocument::setInputSource (XmlInputSource* const newSource) throw() +void XmlDocument::setInputSource (InputSource* const newSource) throw() { if (inputSource != newSource) { @@ -121,7 +83,7 @@ XmlElement* XmlDocument::getDocumentElement (const bool onlyReadOuterDocumentEle if (textToParse.isEmpty() && inputSource != 0) { - InputStream* const in = inputSource->createInputStreamFor (String::empty); + InputStream* const in = inputSource->createInputStream(); if (in != 0) { diff --git a/src/juce_core/text/juce_XmlDocument.h b/src/juce_core/text/juce_XmlDocument.h index 967cbbe790..2589d54a34 100644 --- a/src/juce_core/text/juce_XmlDocument.h +++ b/src/juce_core/text/juce_XmlDocument.h @@ -35,39 +35,7 @@ #include "juce_XmlElement.h" #include "juce_StringArray.h" #include "../io/files/juce_File.h" -#include "../io/juce_InputStream.h" - - -//============================================================================== -/** - Used by the XmlDocument class to find a document's associated files. - - Because an XML document might need to reference other files for its - external DTDs, this class can be used to create input streams for - these files. - - @see XmlDocument -*/ -class JUCE_API XmlInputSource -{ -protected: - /** Creates a default source that can read from files. */ - XmlInputSource() throw(); - -public: - /** Destructor. */ - virtual ~XmlInputSource(); - - /** Returns a new InputStream to read a required file. - - @param filename the partial filename of a file that needs to be read, - or an empty string to open the root document that the - source refers to - @returns an inputstream that the caller will delete, or 0 if - the filename isn't found. - */ - virtual InputStream* createInputStreamFor (const String& filename) = 0; -}; +#include "../io/streams/juce_InputSource.h" //============================================================================== @@ -150,9 +118,9 @@ public: The object that is passed-in will be deleted automatically when no longer needed. - @see XmlInputSource + @see InputSource */ - void setInputSource (XmlInputSource* const newSource) throw(); + void setInputSource (InputSource* const newSource) throw(); //============================================================================== @@ -167,7 +135,7 @@ private: String lastError, dtdText; StringArray tokenisedDTD; bool needToLoadDTD; - XmlInputSource* inputSource; + InputSource* inputSource; void setLastError (const String& desc, const bool carryOn) throw(); void skipHeader() throw(); diff --git a/src/juce_core_includes.h b/src/juce_core_includes.h index 8cd90ae215..14291fc3d2 100644 --- a/src/juce_core_includes.h +++ b/src/juce_core_includes.h @@ -155,12 +155,18 @@ #ifndef __JUCE_BUFFEREDINPUTSTREAM_JUCEHEADER__ #include "juce_core/io/streams/juce_BufferedInputStream.h" #endif +#ifndef __JUCE_FILEINPUTSOURCE_JUCEHEADER__ + #include "juce_core/io/streams/juce_FileInputSource.h" +#endif #ifndef __JUCE_GZIPCOMPRESSOROUTPUTSTREAM_JUCEHEADER__ #include "juce_core/io/streams/juce_GZIPCompressorOutputStream.h" #endif #ifndef __JUCE_GZIPDECOMPRESSORINPUTSTREAM_JUCEHEADER__ #include "juce_core/io/streams/juce_GZIPDecompressorInputStream.h" #endif +#ifndef __JUCE_INPUTSOURCE_JUCEHEADER__ + #include "juce_core/io/streams/juce_InputSource.h" +#endif #ifndef __JUCE_MEMORYINPUTSTREAM_JUCEHEADER__ #include "juce_core/io/streams/juce_MemoryInputStream.h" #endif