diff --git a/build/linux/platform_specific_code/juce_linux_Threads.cpp b/build/linux/platform_specific_code/juce_linux_Threads.cpp index 70f1bfa989..255dcfe277 100644 --- a/build/linux/platform_specific_code/juce_linux_Threads.cpp +++ b/build/linux/platform_specific_code/juce_linux_Threads.cpp @@ -133,7 +133,7 @@ void juce_setThreadPriority (void* handle, int priority) throw() } } -void Thread::setCurrentThreadAffinityMask (const uint32 affinityMask) +void Thread::setCurrentThreadAffinityMask (const uint32 affinityMask) throw() { #if SUPPORT_AFFINITIES cpu_set_t affinity; diff --git a/build/linux/platform_specific_code/juce_linux_Windowing.cpp b/build/linux/platform_specific_code/juce_linux_Windowing.cpp index 5ce578d47d..e9db7dab93 100644 --- a/build/linux/platform_specific_code/juce_linux_Windowing.cpp +++ b/build/linux/platform_specific_code/juce_linux_Windowing.cpp @@ -302,7 +302,7 @@ void ModifierKeys::updateCurrentModifiers() currentModifierFlags = currentModifiers; } -const ModifierKeys ModifierKeys::getCurrentModifiersRealtime() +const ModifierKeys ModifierKeys::getCurrentModifiersRealtime() throw() { int x, y, mouseMods; getMousePos (x, y, mouseMods); @@ -3090,6 +3090,8 @@ const int KeyPress::numberPadMultiply = (XK_KP_Multiply & 0xff)| extendedK const int KeyPress::numberPadDivide = (XK_KP_Divide & 0xff)| extendedKeyModifier; const int KeyPress::numberPadSeparator = (XK_KP_Separator & 0xff)| extendedKeyModifier; const int KeyPress::numberPadDecimalPoint = (XK_KP_Decimal & 0xff)| extendedKeyModifier; +const int KeyPress::numberPadEquals = (XK_KP_Equal & 0xff)| extendedKeyModifier; +const int KeyPress::numberPadDelete = (XK_KP_Delete & 0xff)| extendedKeyModifier; const int KeyPress::playKey = (0xffeeff00) | extendedKeyModifier; const int KeyPress::stopKey = (0xffeeff01) | extendedKeyModifier; const int KeyPress::fastForwardKey = (0xffeeff02) | extendedKeyModifier; diff --git a/build/macosx/platform_specific_code/juce_mac_Threads.cpp b/build/macosx/platform_specific_code/juce_mac_Threads.cpp index ea018cc777..f69495150d 100644 --- a/build/macosx/platform_specific_code/juce_mac_Threads.cpp +++ b/build/macosx/platform_specific_code/juce_mac_Threads.cpp @@ -207,7 +207,7 @@ void Thread::yield() throw() sched_yield(); } -void Thread::setCurrentThreadAffinityMask (const uint32 affinityMask) +void Thread::setCurrentThreadAffinityMask (const uint32 affinityMask) throw() { // xxx jassertfalse diff --git a/build/macosx/platform_specific_code/juce_mac_Windowing.cpp b/build/macosx/platform_specific_code/juce_mac_Windowing.cpp index ecc2fc1621..8f03a79a78 100644 --- a/build/macosx/platform_specific_code/juce_mac_Windowing.cpp +++ b/build/macosx/platform_specific_code/juce_mac_Windowing.cpp @@ -1,3068 +1,3065 @@ -/* - ============================================================================== - - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-7 by Raw Material Software ltd. - - ------------------------------------------------------------------------------ - - JUCE can be redistributed and/or modified under the terms of the - GNU General Public License, as published by the Free Software Foundation; - either version 2 of the License, or (at your option) any later version. - - JUCE is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with JUCE; if not, visit www.gnu.org/licenses or write to the - Free Software Foundation, Inc., 59 Temple Place, Suite 330, - Boston, MA 02111-1307 USA - - ------------------------------------------------------------------------------ - - If you'd like to release a closed-source product which uses JUCE, commercial - licenses are also available: visit www.rawmaterialsoftware.com/juce for - more information. - - ============================================================================== -*/ - -#include "../../../src/juce_core/basics/juce_StandardHeader.h" -#include -#include - -#if JUCE_OPENGL -#include -#endif - -BEGIN_JUCE_NAMESPACE - -#include "../../../src/juce_appframework/events/juce_Timer.h" -#include "../../../src/juce_appframework/application/juce_DeletedAtShutdown.h" -#include "../../../src/juce_appframework/events/juce_AsyncUpdater.h" -#include "../../../src/juce_appframework/events/juce_MessageManager.h" -#include "../../../src/juce_core/basics/juce_Singleton.h" -#include "../../../src/juce_core/basics/juce_Random.h" -#include "../../../src/juce_core/threads/juce_Process.h" -#include "../../../src/juce_appframework/application/juce_SystemClipboard.h" -#include "../../../src/juce_appframework/gui/components/keyboard/juce_KeyPress.h" -#include "../../../src/juce_appframework/gui/components/windows/juce_AlertWindow.h" -#include "../../../src/juce_appframework/gui/graphics/geometry/juce_RectangleList.h" -#include "../../../src/juce_appframework/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h" -#include "../../../src/juce_appframework/gui/components/juce_Desktop.h" -#include "../../../src/juce_appframework/gui/components/menus/juce_MenuBarModel.h" -#include "../../../src/juce_core/misc/juce_PlatformUtilities.h" -#include "../../../src/juce_appframework/application/juce_Application.h" -#include "../../../src/juce_appframework/gui/components/special/juce_OpenGLComponent.h" -#include "../../../src/juce_appframework/gui/components/mouse/juce_DragAndDropContainer.h" -#include "../../../src/juce_appframework/gui/components/keyboard/juce_KeyPressMappingSet.h" -#include "../../../src/juce_appframework/gui/graphics/imaging/juce_ImageFileFormat.h" - -#undef Point - -const WindowRegionCode windowRegionToUse = kWindowContentRgn; - -static HIObjectClassRef viewClassRef = 0; -static CFStringRef juceHiViewClassNameCFString = 0; -static ComponentPeer* juce_currentMouseTrackingPeer = 0; - - -//============================================================================== -static VoidArray keysCurrentlyDown; - -bool KeyPress::isKeyCurrentlyDown (int keyCode) -{ - if (keysCurrentlyDown.contains ((void*) keyCode)) - return true; - - if (keyCode >= 'A' && keyCode <= 'Z' - && keysCurrentlyDown.contains ((void*) (int) CharacterFunctions::toLowerCase ((tchar) keyCode))) - return true; - - if (keyCode >= 'a' && keyCode <= 'z' - && keysCurrentlyDown.contains ((void*) (int) CharacterFunctions::toUpperCase ((tchar) keyCode))) - return true; - - return false; -} - -//============================================================================== -static VoidArray minimisedWindows; - -static void setWindowMinimised (WindowRef ref, const bool isMinimised) -{ - if (isMinimised != minimisedWindows.contains (ref)) - CollapseWindow (ref, isMinimised); -} - -void juce_maximiseAllMinimisedWindows() -{ - const VoidArray minWin (minimisedWindows); - - for (int i = minWin.size(); --i >= 0;) - setWindowMinimised ((WindowRef) (minWin[i]), false); -} - -//============================================================================== -class HIViewComponentPeer; -static HIViewComponentPeer* currentlyFocusedPeer = 0; - - -//============================================================================== -static int currentModifiers = 0; - -static void updateModifiers (EventRef theEvent) -{ - currentModifiers &= ~ (ModifierKeys::shiftModifier | ModifierKeys::ctrlModifier - | ModifierKeys::altModifier | ModifierKeys::commandModifier); - - UInt32 m; - - if (theEvent != 0) - GetEventParameter (theEvent, kEventParamKeyModifiers, typeUInt32, 0, sizeof(m), 0, &m); - else - m = GetCurrentEventKeyModifiers(); - - if ((m & (shiftKey | rightShiftKey)) != 0) - currentModifiers |= ModifierKeys::shiftModifier; - - if ((m & (controlKey | rightControlKey)) != 0) - currentModifiers |= ModifierKeys::ctrlModifier; - - if ((m & (optionKey | rightOptionKey)) != 0) - currentModifiers |= ModifierKeys::altModifier; - - if ((m & cmdKey) != 0) - currentModifiers |= ModifierKeys::commandModifier; -} - -void ModifierKeys::updateCurrentModifiers() -{ - currentModifierFlags = currentModifiers; -} - -static int64 getEventTime (EventRef event) -{ - const int64 millis = (int64) (1000.0 * (event != 0 ? GetEventTime (event) - : GetCurrentEventTime())); - - static int64 offset = 0; - if (offset == 0) - offset = Time::currentTimeMillis() - millis; - - return offset + millis; -} - - -//============================================================================== -class MacBitmapImage : public Image -{ -public: - //============================================================================== - CGColorSpaceRef colourspace; - CGDataProviderRef provider; - - //============================================================================== - MacBitmapImage (const PixelFormat format_, - const int w, const int h, const bool clearImage) - : Image (format_, w, h) - { - jassert (format_ == RGB || format_ == ARGB); - - pixelStride = (format_ == RGB) ? 3 : 4; - - lineStride = (w * pixelStride + 3) & ~3; - const int imageSize = lineStride * h; - - if (clearImage) - imageData = (uint8*) juce_calloc (imageSize); - else - imageData = (uint8*) juce_malloc (imageSize); - - //colourspace = CGColorSpaceCreateWithName (kCGColorSpaceUserRGB); - - CMProfileRef prof; - CMGetSystemProfile (&prof); - colourspace = CGColorSpaceCreateWithPlatformColorSpace (prof); - - provider = CGDataProviderCreateWithData (0, imageData, h * lineStride, 0); - } - - MacBitmapImage::~MacBitmapImage() - { - CGDataProviderRelease (provider); - CGColorSpaceRelease (colourspace); - - juce_free (imageData); - imageData = 0; // to stop the base class freeing this - } - - void blitToContext (CGContextRef context, const float dx, const float dy) - { - CGImageRef tempImage = CGImageCreate (getWidth(), getHeight(), - 8, pixelStride << 3, lineStride, colourspace, -#if MACOS_10_3_OR_EARLIER || JUCE_BIG_ENDIAN - hasAlphaChannel() ? kCGImageAlphaPremultipliedFirst - : kCGImageAlphaNone, -#else - hasAlphaChannel() ? kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst - : kCGImageAlphaNone, -#endif - provider, 0, false, - kCGRenderingIntentDefault); - - HIRect r; - r.origin.x = dx; - r.origin.y = dy; - r.size.width = (float) getWidth(); - r.size.height = (float) getHeight(); - - HIViewDrawCGImage (context, &r, tempImage); - - CGImageRelease (tempImage); - } - - juce_UseDebuggingNewOperator -}; - - -//============================================================================== -class MouseCheckTimer : private Timer, - private DeletedAtShutdown -{ - HIViewComponentPeer* lastPeerUnderMouse; - int lastX, lastY; - -public: - MouseCheckTimer() - : lastX (0), - lastY (0) - { - lastPeerUnderMouse = 0; - resetMouseMoveChecker(); - } - - ~MouseCheckTimer() - { - clearSingletonInstance(); - } - - juce_DeclareSingleton_SingleThreaded_Minimal (MouseCheckTimer) - - bool hasEverHadAMouseMove; - - void moved (HIViewComponentPeer* const peer) - { - if (hasEverHadAMouseMove) - startTimer (200); - - lastPeerUnderMouse = peer; - } - - void resetMouseMoveChecker() - { - hasEverHadAMouseMove = false; - startTimer (1000 / 16); - } - - void timerCallback(); -}; - -juce_ImplementSingleton_SingleThreaded (MouseCheckTimer) - -//============================================================================== -#if JUCE_QUICKTIME -extern void OfferMouseClickToQuickTime (WindowRef window, ::Point where, long when, long modifiers, - Component* topLevelComp); -#endif - - -//============================================================================== -class HIViewComponentPeer : public ComponentPeer, - private Timer -{ -public: - //============================================================================== - HIViewComponentPeer (Component* const component, - const int windowStyleFlags, - HIViewRef viewToAttachTo) - : ComponentPeer (component, windowStyleFlags), - fullScreen (false), - isCompositingWindow (false), - windowRef (0), - viewRef (0) - { - repainter = new RepaintManager (this); - - eventHandlerRef = 0; - - if (viewToAttachTo != 0) - { - isSharedWindow = true; - } - else - { - isSharedWindow = false; - - WindowRef newWindow = createNewWindow (windowStyleFlags); - - GetRootControl (newWindow, (ControlRef*) &viewToAttachTo); - jassert (viewToAttachTo != 0); - - HIViewRef growBox = 0; - HIViewFindByID (HIViewGetRoot (newWindow), kHIViewWindowGrowBoxID, &growBox); - - if (growBox != 0) - HIGrowBoxViewSetTransparent (growBox, true); - } - - createNewHIView(); - - HIViewAddSubview (viewToAttachTo, viewRef); - HIViewSetVisible (viewRef, component->isVisible()); - - setTitle (component->getName()); - - if (component->isVisible() && ! isSharedWindow) - { - ShowWindow (windowRef); - ActivateWindow (windowRef, component->getWantsKeyboardFocus()); - } - } - - ~HIViewComponentPeer() - { - minimisedWindows.removeValue (windowRef); - - if (IsValidWindowPtr (windowRef)) - { - if (! isSharedWindow) - { - CFRelease (viewRef); - viewRef = 0; - - DisposeWindow (windowRef); - } - else - { - if (eventHandlerRef != 0) - RemoveEventHandler (eventHandlerRef); - - CFRelease (viewRef); - viewRef = 0; - } - - windowRef = 0; - } - - if (currentlyFocusedPeer == this) - currentlyFocusedPeer = 0; - - delete repainter; - } - - //============================================================================== - void* getNativeHandle() const - { - return windowRef; - } - - void setVisible (bool shouldBeVisible) - { - HIViewSetVisible (viewRef, shouldBeVisible); - - if ((! isSharedWindow) && IsValidWindowPtr (windowRef)) - { - if (shouldBeVisible) - ShowWindow (windowRef); - else - HideWindow (windowRef); - - resizeViewToFitWindow(); - - // If nothing else is focused, then grab the focus too - if (shouldBeVisible - && Component::getCurrentlyFocusedComponent() == 0 - && Process::isForegroundProcess()) - { - component->toFront (true); - } - } - } - - void setTitle (const String& title) - { - if ((! isSharedWindow) && IsValidWindowPtr (windowRef)) - { - CFStringRef t = PlatformUtilities::juceStringToCFString (title); - SetWindowTitleWithCFString (windowRef, t); - CFRelease (t); - } - } - - void setPosition (int x, int y) - { - if (isSharedWindow) - { - HIViewPlaceInSuperviewAt (viewRef, x, y); - } - else if (IsValidWindowPtr (windowRef)) - { - Rect r; - GetWindowBounds (windowRef, windowRegionToUse, &r); - r.right += x - r.left; - r.bottom += y - r.top; - r.left = x; - r.top = y; - SetWindowBounds (windowRef, windowRegionToUse, &r); - } - } - - void setSize (int w, int h) - { - w = jmax (0, w); - h = jmax (0, h); - - if (w != getComponent()->getWidth() - || h != getComponent()->getHeight()) - { - repainter->repaint (0, 0, w, h); - } - - if (isSharedWindow) - { - HIRect r; - HIViewGetFrame (viewRef, &r); - r.size.width = (float) w; - r.size.height = (float) h; - HIViewSetFrame (viewRef, &r); - } - else if (IsValidWindowPtr (windowRef)) - { - Rect r; - GetWindowBounds (windowRef, windowRegionToUse, &r); - r.right = r.left + w; - r.bottom = r.top + h; - SetWindowBounds (windowRef, windowRegionToUse, &r); - } - } - - void setBounds (int x, int y, int w, int h, const bool isNowFullScreen) - { - fullScreen = isNowFullScreen; - w = jmax (0, w); - h = jmax (0, h); - - if (w != getComponent()->getWidth() - || h != getComponent()->getHeight()) - { - repainter->repaint (0, 0, w, h); - } - - if (isSharedWindow) - { - HIRect r; - r.origin.x = (float) x; - r.origin.y = (float) y; - r.size.width = (float) w; - r.size.height = (float) h; - HIViewSetFrame (viewRef, &r); - } - else if (IsValidWindowPtr (windowRef)) - { - Rect r; - r.left = x; - r.top = y; - r.right = x + w; - r.bottom = y + h; - SetWindowBounds (windowRef, windowRegionToUse, &r); - } - } - - void getBounds (int& x, int& y, int& w, int& h, const bool global) const - { - HIRect hiViewPos; - HIViewGetFrame (viewRef, &hiViewPos); - - if (global) - { - HIViewRef content = 0; - HIViewFindByID (HIViewGetRoot (windowRef), kHIViewWindowContentID, &content); - HIPoint p = { 0.0f, 0.0f }; - HIViewConvertPoint (&p, viewRef, content); - - x = (int) p.x; - y = (int) p.y; - - if (IsValidWindowPtr (windowRef)) - { - Rect windowPos; - GetWindowBounds (windowRef, kWindowContentRgn, &windowPos); - - x += windowPos.left; - y += windowPos.top; - } - } - else - { - x = (int) hiViewPos.origin.x; - y = (int) hiViewPos.origin.y; - } - - w = (int) hiViewPos.size.width; - h = (int) hiViewPos.size.height; - } - - void getBounds (int& x, int& y, int& w, int& h) const - { - getBounds (x, y, w, h, ! isSharedWindow); - } - - int getScreenX() const - { - int x, y, w, h; - getBounds (x, y, w, h, true); - return x; - } - - int getScreenY() const - { - int x, y, w, h; - getBounds (x, y, w, h, true); - return y; - } - - void relativePositionToGlobal (int& x, int& y) - { - int wx, wy, ww, wh; - getBounds (wx, wy, ww, wh, true); - - x += wx; - y += wy; - } - - void globalPositionToRelative (int& x, int& y) - { - int wx, wy, ww, wh; - getBounds (wx, wy, ww, wh, true); - - x -= wx; - y -= wy; - } - - void setMinimised (bool shouldBeMinimised) - { - if (! isSharedWindow) - setWindowMinimised (windowRef, shouldBeMinimised); - } - - bool isMinimised() const - { - return minimisedWindows.contains (windowRef); - } - - void setFullScreen (bool shouldBeFullScreen) - { - if (! isSharedWindow) - { - setMinimised (false); - - if (fullScreen != shouldBeFullScreen) - { - Rectangle r (lastNonFullscreenBounds); - - if (shouldBeFullScreen) - r = Desktop::getInstance().getMainMonitorArea(); - - // (can't call the component's setBounds method because that'll reset our fullscreen flag) - if (r != getComponent()->getBounds() && ! r.isEmpty()) - setBounds (r.getX(), r.getY(), r.getWidth(), r.getHeight(), shouldBeFullScreen); - } - } - } - - bool isFullScreen() const - { - return fullScreen; - } - - bool contains (int x, int y, bool trueIfInAChildWindow) const - { - if (x < 0 || y < 0 - || x >= component->getWidth() || y >= component->getHeight() - || ! IsValidWindowPtr (windowRef)) - return false; - - Rect r; - GetWindowBounds (windowRef, windowRegionToUse, &r); - - ::Point p; - p.h = r.left + x; - p.v = r.top + y; - - WindowRef ref2 = 0; - FindWindow (p, &ref2); - - if (windowRef != ref2) - return false; - - if (trueIfInAChildWindow) - return true; - - HIPoint p2; - p2.x = (float) x; - p2.y = (float) y; - HIViewRef hit; - - HIViewGetSubviewHit (viewRef, &p2, true, &hit); - return hit == 0 || hit == viewRef; - } - - const BorderSize getFrameSize() const - { - return BorderSize(); - } - - bool setAlwaysOnTop (bool alwaysOnTop) - { - // can't do this so return false and let the component create a new window - return false; - } - - void toFront (bool makeActiveWindow) - { - makeActiveWindow = makeActiveWindow - && component->isValidComponent() - && (component->getWantsKeyboardFocus() - || component->isCurrentlyModal()); - - if (windowRef != FrontWindow() - || (makeActiveWindow && ! IsWindowActive (windowRef)) - || ! Process::isForegroundProcess()) - { - if (! Process::isForegroundProcess()) - { - ProcessSerialNumber psn; - GetCurrentProcess (&psn); - SetFrontProcess (&psn); - } - - if (IsValidWindowPtr (windowRef)) - { - if (makeActiveWindow) - { - SelectWindow (windowRef); - SetUserFocusWindow (windowRef); - HIViewAdvanceFocus (viewRef, 0); - } - else - { - BringToFront (windowRef); - } - - handleBroughtToFront(); - } - } - } - - void toBehind (ComponentPeer* other) - { - HIViewComponentPeer* const otherWindow = dynamic_cast (other); - - if (other != 0 && windowRef != 0 && otherWindow->windowRef != 0) - { - if (windowRef == otherWindow->windowRef) - { - HIViewSetZOrder (viewRef, kHIViewZOrderBelow, otherWindow->viewRef); - } - else - { - SendBehind (windowRef, otherWindow->windowRef); - } - } - } - - void setIcon (const Image& /*newIcon*/) - { - // to do.. - } - - //============================================================================== - void viewFocusGain() - { - const MessageManagerLock messLock; - - if (currentlyFocusedPeer != this) - { - if (ComponentPeer::isValidPeer (currentlyFocusedPeer)) - currentlyFocusedPeer->handleFocusLoss(); - - currentlyFocusedPeer = this; - - handleFocusGain(); - } - } - - void viewFocusLoss() - { - if (currentlyFocusedPeer == this) - { - currentlyFocusedPeer = 0; - handleFocusLoss(); - } - } - - bool isFocused() const - { - return windowRef == GetUserFocusWindow() - && HIViewSubtreeContainsFocus (viewRef); - } - - void grabFocus() - { - if ((! isFocused()) && IsValidWindowPtr (windowRef)) - { - SetUserFocusWindow (windowRef); - HIViewAdvanceFocus (viewRef, 0); - } - } - - //============================================================================== - void repaint (int x, int y, int w, int h) - { - if (Rectangle::intersectRectangles (x, y, w, h, - 0, 0, - getComponent()->getWidth(), - getComponent()->getHeight())) - { - if ((getStyleFlags() & windowRepaintedExplictly) == 0) - { - if (isCompositingWindow) - { -#if MACOS_10_3_OR_EARLIER - RgnHandle rgn = NewRgn(); - SetRectRgn (rgn, x, y, x + w, y + h); - HIViewSetNeedsDisplayInRegion (viewRef, rgn, true); - DisposeRgn (rgn); -#else - HIRect r; - r.origin.x = x; - r.origin.y = y; - r.size.width = w; - r.size.height = h; - - HIViewSetNeedsDisplayInRect (viewRef, &r, true); -#endif - } - else - { - if (! isTimerRunning()) - startTimer (20); - } - } - - repainter->repaint (x, y, w, h); - } - } - - void timerCallback() - { - performAnyPendingRepaintsNow(); - } - - void performAnyPendingRepaintsNow() - { - stopTimer(); - - if (component->isVisible()) - { -#if MACOS_10_2_OR_EARLIER - if (! isCompositingWindow) - { - Rect w; - GetWindowBounds (windowRef, windowRegionToUse, &w); - - RgnHandle rgn = NewRgn(); - SetRectRgn (rgn, 0, 0, w.right - w.left, w.bottom - w.top); - UpdateControls (windowRef, rgn); - DisposeRgn (rgn); - } - else - { - EventRef theEvent; - - EventTypeSpec eventTypes[1]; - eventTypes[0].eventClass = kEventClassControl; - eventTypes[0].eventKind = kEventControlDraw; - - int n = 3; - while (--n >= 0 - && ReceiveNextEvent (1, eventTypes, kEventDurationNoWait, true, &theEvent) == noErr) - { - if (GetEventClass (theEvent) == kEventClassAppleEvent) - { - EventRecord eventRec; - if (ConvertEventRefToEventRecord (theEvent, &eventRec)) - AEProcessAppleEvent (&eventRec); - } - else - { - EventTargetRef theTarget = GetEventDispatcherTarget(); - SendEventToEventTarget (theEvent, theTarget); - } - - ReleaseEvent (theEvent); - } - } -#else - HIViewRender (viewRef); -#endif - } - } - - //============================================================================== - juce_UseDebuggingNewOperator - - WindowRef windowRef; - HIViewRef viewRef; - -private: - EventHandlerRef eventHandlerRef; - bool fullScreen, isSharedWindow, isCompositingWindow; - - //============================================================================== - class RepaintManager : public Timer - { -public: - RepaintManager (HIViewComponentPeer* const peer_) - : peer (peer_), - image (0) - { - } - - ~RepaintManager() - { - delete image; - } - - void timerCallback() - { - stopTimer(); - deleteAndZero (image); - } - - void repaint (int x, int y, int w, int h) - { - regionsNeedingRepaint.add (x, y, w, h); - } - - void repaintAnyRemainingRegions() - { - // if any regions have been invaldated during the paint callback, - // we need to repaint them explicitly because the mac throws this - // stuff away - for (RectangleList::Iterator i (regionsNeedingRepaint); i.next();) - { - const Rectangle& r = *i.getRectangle(); - peer->repaint (r.getX(), r.getY(), r.getWidth(), r.getHeight()); - } - } - - void paint (CGContextRef cgContext, int x, int y, int w, int h) - { - if (w > 0 && h > 0) - { - bool refresh = false; - int imW = image != 0 ? image->getWidth() : 0; - int imH = image != 0 ? image->getHeight() : 0; - - if (imW < w || imH < h) - { - imW = jmin (peer->getComponent()->getWidth(), (w + 31) & ~31); - imH = jmin (peer->getComponent()->getHeight(), (h + 31) & ~31); - - delete image; - image = new MacBitmapImage (peer->getComponent()->isOpaque() ? Image::RGB - : Image::ARGB, - imW, imH, false); - - refresh = true; - } - else if (imageX > x || imageY > y - || imageX + imW < x + w - || imageY + imH < y + h) - { - refresh = true; - } - - if (refresh) - { - regionsNeedingRepaint.clear(); - regionsNeedingRepaint.addWithoutMerging (Rectangle (x, y, imW, imH)); - imageX = x; - imageY = y; - } - - LowLevelGraphicsSoftwareRenderer context (*image); - context.setOrigin (-imageX, -imageY); - - if (context.reduceClipRegion (regionsNeedingRepaint)) - { - regionsNeedingRepaint.clear(); - - if (! peer->getComponent()->isOpaque()) - { - for (RectangleList::Iterator i (*context.getRawClipRegion()); i.next();) - { - const Rectangle& r = *i.getRectangle(); - image->clear (r.getX(), r.getY(), r.getWidth(), r.getHeight()); - } - } - - regionsNeedingRepaint.clear(); - peer->clearMaskedRegion(); - peer->handlePaint (context); - } - else - { - regionsNeedingRepaint.clear(); - } - - if (! peer->maskedRegion.isEmpty()) - { - RectangleList total (Rectangle (x, y, w, h)); - total.subtract (peer->maskedRegion); - - CGRect* rects = (CGRect*) juce_malloc (sizeof (CGRect) * total.getNumRectangles()); - int n = 0; - - for (RectangleList::Iterator i (total); i.next();) - { - const Rectangle& r = *i.getRectangle(); - rects[n].origin.x = (int) r.getX(); - rects[n].origin.y = (int) r.getY(); - rects[n].size.width = roundFloatToInt (r.getWidth()); - rects[n++].size.height = roundFloatToInt (r.getHeight()); - } - - CGContextClipToRects (cgContext, rects, n); - juce_free (rects); - } - - if (peer->isSharedWindow) - { - CGRect clip; - clip.origin.x = x; - clip.origin.y = y; - clip.size.width = jmin (w, peer->getComponent()->getWidth() - x); - clip.size.height = jmin (h, peer->getComponent()->getHeight() - y); - - CGContextClipToRect (cgContext, clip); - } - - image->blitToContext (cgContext, imageX, imageY); - } - - startTimer (3000); - } - - private: - HIViewComponentPeer* const peer; - MacBitmapImage* image; - int imageX, imageY; - RectangleList regionsNeedingRepaint; - - RepaintManager (const RepaintManager&); - const RepaintManager& operator= (const RepaintManager&); - }; - - RepaintManager* repainter; - - friend class RepaintManager; - - //============================================================================== - static OSStatus handleFrameRepaintEvent (EventHandlerCallRef myHandler, - EventRef theEvent, - void* userData) - { - // don't draw the frame.. - return noErr; - } - - //============================================================================== - OSStatus handleWindowClassEvent (EventRef theEvent) - { - switch (GetEventKind (theEvent)) - { - case kEventWindowBoundsChanged: - resizeViewToFitWindow(); - break; // allow other handlers in the event chain to also get a look at the events - - case kEventWindowBoundsChanging: - if ((styleFlags & (windowIsResizable | windowHasTitleBar)) == (windowIsResizable | windowHasTitleBar)) - { - UInt32 atts = 0; - GetEventParameter (theEvent, kEventParamAttributes, typeUInt32, - 0, sizeof (UInt32), 0, &atts); - - if ((atts & (kWindowBoundsChangeUserDrag | kWindowBoundsChangeUserResize)) != 0) - { - if (component->isCurrentlyBlockedByAnotherModalComponent()) - { - Component* const modal = Component::getCurrentlyModalComponent(); - if (modal != 0) - modal->inputAttemptWhenModal(); - } - - if ((atts & kWindowBoundsChangeUserResize) != 0 - && constrainer != 0 && ! isSharedWindow) - { - Rect current; - GetEventParameter (theEvent, kEventParamCurrentBounds, typeQDRectangle, - 0, sizeof (Rect), 0, ¤t); - - int x = current.left; - int y = current.top; - int w = current.right - current.left; - int h = current.bottom - current.top; - - const Rectangle currentRect (getComponent()->getBounds()); - - constrainer->checkBounds (x, y, w, h, currentRect, - Desktop::getInstance().getAllMonitorDisplayAreas().getBounds(), - y != currentRect.getY() && y + h == currentRect.getBottom(), - x != currentRect.getX() && x + w == currentRect.getRight(), - y == currentRect.getY() && y + h != currentRect.getBottom(), - x == currentRect.getX() && x + w != currentRect.getRight()); - - current.left = x; - current.top = y; - current.right = x + w; - current.bottom = y + h; - - SetEventParameter (theEvent, kEventParamCurrentBounds, typeQDRectangle, - sizeof (Rect), ¤t); - - return noErr; - } - } - } - break; - - case kEventWindowFocusAcquired: - keysCurrentlyDown.clear(); - - if ((! isSharedWindow) || HIViewSubtreeContainsFocus (viewRef)) - viewFocusGain(); - - break; // allow other handlers in the event chain to also get a look at the events - - case kEventWindowFocusRelinquish: - keysCurrentlyDown.clear(); - viewFocusLoss(); - - break; // allow other handlers in the event chain to also get a look at the events - - case kEventWindowCollapsed: - minimisedWindows.addIfNotAlreadyThere (windowRef); - handleMovedOrResized(); - break; // allow other handlers in the event chain to also get a look at the events - - case kEventWindowExpanded: - minimisedWindows.removeValue (windowRef); - handleMovedOrResized(); - break; // allow other handlers in the event chain to also get a look at the events - - case kEventWindowShown: - break; // allow other handlers in the event chain to also get a look at the events - - case kEventWindowClose: - if (isSharedWindow) - break; // break to let the OS delete the window - - handleUserClosingWindow(); - return noErr; // avoids letting the OS to delete the window, we'll do that ourselves. - - default: - break; - } - - return eventNotHandledErr; - } - - OSStatus handleKeyEvent (EventRef theEvent, juce_wchar textCharacter) - { - updateModifiers (theEvent); - - UniChar unicodeChars [4]; - zeromem (unicodeChars, sizeof (unicodeChars)); - GetEventParameter (theEvent, kEventParamKeyUnicodes, typeUnicodeText, 0, sizeof (unicodeChars), 0, unicodeChars); - - int keyCode = (int) (unsigned int) unicodeChars[0]; - - UInt32 rawKey = 0; - GetEventParameter (theEvent, kEventParamKeyCode, typeUInt32, 0, sizeof (UInt32), 0, &rawKey); - - if ((currentModifiers & ModifierKeys::ctrlModifier) != 0) - { - if (keyCode >= 1 && keyCode <= 26) - keyCode += ('A' - 1); - } - - static const int keyTranslations[] = - { - 0, 's', 'd', 'f', 'h', 'g', 'z', 'x', 'c', 'v', 0xa7, 'b', - 'q', 'w', 'e', 'r', 'y', 't', '1', '2', '3', '4', '6', '5', - '=', '9', '7', '-', '8', '0', ']', 'o', 'u', '[', 'i', 'p', - KeyPress::returnKey, 'l', 'j', '\'', 'k', ';', '\\', ',', '/', - 'n', 'm', '.', 0, 0, '`', KeyPress::backspaceKey, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, KeyPress::numberPadDecimalPoint, - 0, KeyPress::numberPadMultiply, 0, KeyPress::numberPadAdd, - 0, 0, 0, 0, 0, KeyPress::numberPadDivide, KeyPress::returnKey, - 0, KeyPress::numberPadSubtract, 0, 0, 0, KeyPress::numberPad0, - KeyPress::numberPad1, KeyPress::numberPad2, KeyPress::numberPad3, - KeyPress::numberPad4, KeyPress::numberPad5, KeyPress::numberPad6, - KeyPress::numberPad7, 0, KeyPress::numberPad8, KeyPress::numberPad9, - 0, 0, 0, KeyPress::F5Key, KeyPress::F6Key, KeyPress::F7Key, KeyPress::F3Key, - KeyPress::F8Key, KeyPress::F9Key, 0, KeyPress::F11Key, 0, KeyPress::F13Key, - KeyPress::F16Key, KeyPress::F14Key, 0, KeyPress::F10Key, 0, KeyPress::F12Key, - 0, KeyPress::F15Key, 0, KeyPress::homeKey, KeyPress::pageUpKey, 0, KeyPress::F4Key, - KeyPress::endKey, KeyPress::F2Key, KeyPress::pageDownKey, KeyPress::F1Key, - KeyPress::leftKey, KeyPress::rightKey, KeyPress::downKey, KeyPress::upKey, 0 - }; - - if (rawKey > 0 && rawKey < numElementsInArray (keyTranslations)) - { - keyCode = keyTranslations [rawKey]; - } - else if (rawKey == 0 && textCharacter != 0) - { - keyCode = 'a'; - } - - if ((currentModifiers & (ModifierKeys::commandModifier | ModifierKeys::ctrlModifier)) != 0) - textCharacter = 0; - - static juce_wchar lastTextCharacter = 0; - - switch (GetEventKind (theEvent)) - { - case kEventRawKeyDown: - keysCurrentlyDown.addIfNotAlreadyThere ((void*) keyCode); - handleKeyUpOrDown(); - lastTextCharacter = textCharacter; - handleKeyPress (keyCode, textCharacter); - break; - - case kEventRawKeyUp: - keysCurrentlyDown.removeValue ((void*) keyCode); - handleKeyUpOrDown(); - lastTextCharacter = 0; - break; - - case kEventRawKeyRepeat: - handleKeyPress (keyCode, lastTextCharacter); - break; - - case kEventRawKeyModifiersChanged: - handleModifierKeysChange(); - break; - - default: - jassertfalse - break; - } - - return noErr; - } - - OSStatus handleTextInputEvent (EventRef theEvent) - { - UniChar uc; - GetEventParameter (theEvent, kEventParamTextInputSendText, typeUnicodeText, 0, sizeof (uc), 0, &uc); - - EventRef originalEvent; - GetEventParameter (theEvent, kEventParamTextInputSendKeyboardEvent, typeEventRef, 0, sizeof (originalEvent), 0, &originalEvent); - - handleKeyEvent (originalEvent, (juce_wchar) uc); - - return noErr; - } - - OSStatus handleMouseEvent (EventHandlerCallRef callRef, EventRef theEvent) - { - MouseCheckTimer::getInstance()->moved (this); - - ::Point where; - GetEventParameter (theEvent, kEventParamMouseLocation, typeQDPoint, 0, sizeof (::Point), 0, &where); - int x = where.h; - int y = where.v; - globalPositionToRelative (x, y); - - int64 time = getEventTime (theEvent); - - switch (GetEventKind (theEvent)) - { - case kEventMouseMoved: - MouseCheckTimer::getInstance()->hasEverHadAMouseMove = true; - updateModifiers (theEvent); - handleMouseMove (x, y, time); - break; - - case kEventMouseDragged: - updateModifiers (theEvent); - handleMouseDrag (x, y, time); - break; - - case kEventMouseDown: - { - if (! Process::isForegroundProcess()) - { - ProcessSerialNumber psn; - GetCurrentProcess (&psn); - SetFrontProcess (&psn); - - toFront (true); - } - -#if JUCE_QUICKTIME - { - long mods; - GetEventParameter (theEvent, kEventParamKeyModifiers, typeUInt32, 0, sizeof (mods), 0, &mods); - - ::Point where; - GetEventParameter (theEvent, kEventParamMouseLocation, typeQDPoint, 0, sizeof (::Point), 0, &where); - - OfferMouseClickToQuickTime (windowRef, where, EventTimeToTicks (GetEventTime (theEvent)), mods, component); - } -#endif - - if (component->isBroughtToFrontOnMouseClick() - && ! component->isCurrentlyBlockedByAnotherModalComponent()) - { - //ActivateWindow (windowRef, true); - SelectWindow (windowRef); - } - - EventMouseButton button; - GetEventParameter (theEvent, kEventParamMouseButton, typeMouseButton, 0, sizeof (EventMouseButton), 0, &button); - - // need to clear all these flags because sometimes the mac can swallow (right) mouse-up events and - // this makes a button get stuck down. Since there's no other way to tell what buttons are down, - // this is all I can think of doing about it.. - currentModifiers &= ~(ModifierKeys::leftButtonModifier | ModifierKeys::rightButtonModifier | ModifierKeys::middleButtonModifier); - - if (button == kEventMouseButtonPrimary) - currentModifiers |= ModifierKeys::leftButtonModifier; - else if (button == kEventMouseButtonSecondary) - currentModifiers |= ModifierKeys::rightButtonModifier; - else if (button == kEventMouseButtonTertiary) - currentModifiers |= ModifierKeys::middleButtonModifier; - - updateModifiers (theEvent); - - juce_currentMouseTrackingPeer = this; // puts the message dispatcher into mouse-tracking mode.. - handleMouseDown (x, y, time); - break; - } - - case kEventMouseUp: - { - const int oldModifiers = currentModifiers; - - EventMouseButton button; - GetEventParameter (theEvent, kEventParamMouseButton, typeMouseButton, 0, sizeof (EventMouseButton), 0, &button); - - if (button == kEventMouseButtonPrimary) - currentModifiers &= ~ModifierKeys::leftButtonModifier; - else if (button == kEventMouseButtonSecondary) - currentModifiers &= ~ModifierKeys::rightButtonModifier; - - updateModifiers (theEvent); - - handleMouseUp (oldModifiers, x, y, time); - juce_currentMouseTrackingPeer = 0; - break; - } - - case kEventMouseWheelMoved: - { - EventMouseWheelAxis axis; - GetEventParameter (theEvent, kEventParamMouseWheelAxis, typeMouseWheelAxis, 0, sizeof (axis), 0, &axis); - - SInt32 delta; - GetEventParameter (theEvent, kEventParamMouseWheelDelta, - typeLongInteger, 0, sizeof (delta), 0, &delta); - - updateModifiers (theEvent); - - handleMouseWheel (axis == kEventMouseWheelAxisX ? delta * 10 : 0, - axis == kEventMouseWheelAxisX ? 0 : delta * 10, - time); - - break; - } - } - - return noErr; - } - - OSStatus handleDragAndDrop (EventRef theEvent) - { - DragRef dragRef; - if (GetEventParameter (theEvent, kEventParamDragRef, typeDragRef, 0, sizeof (dragRef), 0, &dragRef) == noErr) - { - int mx, my; - component->getMouseXYRelative (mx, my); - - UInt16 numItems = 0; - if (CountDragItems (dragRef, &numItems) == noErr) - { - StringArray filenames; - - for (int i = 0; i < (int) numItems; ++i) - { - DragItemRef ref; - - if (GetDragItemReferenceNumber (dragRef, i + 1, &ref) == noErr) - { - const FlavorType flavorType = kDragFlavorTypeHFS; - - Size size = 0; - if (GetFlavorDataSize (dragRef, ref, flavorType, &size) == noErr) - { - void* data = juce_calloc (size); - - if (GetFlavorData (dragRef, ref, flavorType, data, &size, 0) == noErr) - { - HFSFlavor* f = (HFSFlavor*) data; - FSRef fsref; - - if (FSpMakeFSRef (&f->fileSpec, &fsref) == noErr) - { - const String path (PlatformUtilities::makePathFromFSRef (&fsref)); - - if (path.isNotEmpty()) - filenames.add (path); - } - } - - juce_free (data); - } - } - } - - filenames.trim(); - filenames.removeEmptyStrings(); - - if (filenames.size() > 0) - handleFilesDropped (mx, my, filenames); - } - } - - return noErr; - } - - void resizeViewToFitWindow() - { - HIRect r; - - if (isSharedWindow) - { - HIViewGetFrame (viewRef, &r); - r.size.width = (float) component->getWidth(); - r.size.height = (float) component->getHeight(); - } - else - { - r.origin.x = 0; - r.origin.y = 0; - - Rect w; - GetWindowBounds (windowRef, windowRegionToUse, &w); - - r.size.width = (float) (w.right - w.left); - r.size.height = (float) (w.bottom - w.top); - } - - HIViewSetFrame (viewRef, &r); - -#if MACOS_10_3_OR_EARLIER - component->repaint(); -#endif - } - - OSStatus hiViewDraw (EventRef theEvent) - { - CGContextRef context = 0; - GetEventParameter (theEvent, kEventParamCGContextRef, typeCGContextRef, 0, sizeof (CGContextRef), 0, &context); - - CGrafPtr oldPort; - CGrafPtr port = 0; - - if (context == 0) - { - GetEventParameter (theEvent, kEventParamGrafPort, typeGrafPtr, 0, sizeof (CGrafPtr), 0, &port); - - GetPort (&oldPort); - SetPort (port); - - if (port != 0) - QDBeginCGContext (port, &context); - - if (! isCompositingWindow) - { - Rect bounds; - GetWindowBounds (windowRef, windowRegionToUse, &bounds); - CGContextTranslateCTM (context, 0, bounds.bottom - bounds.top); - CGContextScaleCTM (context, 1.0, -1.0); - } - - if (isSharedWindow) - { - // NB - Had terrible problems trying to correctly get the position - // of this view relative to the window, and this seems wrong, but - // works better than any other method I've tried.. - HIRect hiViewPos; - HIViewGetFrame (viewRef, &hiViewPos); - CGContextTranslateCTM (context, hiViewPos.origin.x, hiViewPos.origin.y); - } - } - -#if MACOS_10_2_OR_EARLIER - RgnHandle rgn = 0; - GetEventParameter (theEvent, kEventParamRgnHandle, typeQDRgnHandle, 0, sizeof (RgnHandle), 0, &rgn); - - CGRect clip; - - if (rgn != 0) - { - Rect bounds; - GetRegionBounds (rgn, &bounds); - clip.origin.x = bounds.left; - clip.origin.y = bounds.top; - clip.size.width = bounds.right - bounds.left; - clip.size.height = bounds.bottom - bounds.top; - } - else - { - HIViewGetBounds (viewRef, &clip); - } -#else - CGRect clip (CGContextGetClipBoundingBox (context)); -#endif - - clip = CGRectIntegral (clip); - - if (clip.origin.x < 0) - { - clip.size.width += clip.origin.x; - clip.origin.x = 0; - } - - if (clip.origin.y < 0) - { - clip.size.height += clip.origin.y; - clip.origin.y = 0; - } - - if (! component->isOpaque()) - CGContextClearRect (context, clip); - - repainter->paint (context, - (int) clip.origin.x, (int) clip.origin.y, - (int) clip.size.width, (int) clip.size.height); - - if (port != 0) - { - CGContextFlush (context); - QDEndCGContext (port, &context); - - SetPort (oldPort); - } - - repainter->repaintAnyRemainingRegions(); - - return noErr; - } - - static pascal OSStatus handleWindowEvent (EventHandlerCallRef callRef, EventRef theEvent, void* userData) - { - MessageManager::delayWaitCursor(); - - HIViewComponentPeer* const peer = (HIViewComponentPeer*) userData; - - const MessageManagerLock messLock; - - if (ComponentPeer::isValidPeer (peer)) - return peer->handleWindowEventForPeer (callRef, theEvent); - - return eventNotHandledErr; - } - - OSStatus handleWindowEventForPeer (EventHandlerCallRef callRef, EventRef theEvent) - { - switch (GetEventClass (theEvent)) - { - case kEventClassMouse: - { - static HIViewComponentPeer* lastMouseDownPeer = 0; - - const UInt32 eventKind = GetEventKind (theEvent); - HIViewRef view = 0; - - if (eventKind == kEventMouseDragged) - { - view = viewRef; - } - else - { - HIViewGetViewForMouseEvent (HIViewGetRoot (windowRef), theEvent, &view); - - if (view != viewRef) - { - if ((eventKind == kEventMouseUp - || eventKind == kEventMouseExited) - && ComponentPeer::isValidPeer (lastMouseDownPeer)) - { - return lastMouseDownPeer->handleMouseEvent (callRef, theEvent); - } - - return eventNotHandledErr; - } - } - - if (eventKind == kEventMouseDown - || eventKind == kEventMouseDragged - || eventKind == kEventMouseEntered) - { - lastMouseDownPeer = this; - } - - return handleMouseEvent (callRef, theEvent); - } - break; - - case kEventClassWindow: - return handleWindowClassEvent (theEvent); - - case kEventClassKeyboard: - if (isFocused()) - return handleKeyEvent (theEvent, 0); - - break; - - case kEventClassTextInput: - if (isFocused()) - return handleTextInputEvent (theEvent); - - break; - - default: - break; - } - - return eventNotHandledErr; - } - - static pascal OSStatus hiViewEventHandler (EventHandlerCallRef myHandler, EventRef theEvent, void* userData) - { - MessageManager::delayWaitCursor(); - - const UInt32 eventKind = GetEventKind (theEvent); - const UInt32 eventClass = GetEventClass (theEvent); - - if (eventClass == kEventClassHIObject) - { - switch (eventKind) - { - case kEventHIObjectConstruct: - { - void* data = juce_calloc (sizeof (void*)); - SetEventParameter (theEvent, kEventParamHIObjectInstance, - typeVoidPtr, sizeof (void*), &data); - - return noErr; - } - - case kEventHIObjectInitialize: - GetEventParameter (theEvent, 'peer', typeVoidPtr, 0, sizeof (void*), 0, (void**) userData); - return noErr; - - case kEventHIObjectDestruct: - juce_free (userData); - return noErr; - - default: - break; - } - } - else if (eventClass == kEventClassControl) - { - HIViewComponentPeer* const peer = *(HIViewComponentPeer**) userData; - const MessageManagerLock messLock; - - if (! ComponentPeer::isValidPeer (peer)) - return eventNotHandledErr; - - switch (eventKind) - { - case kEventControlDraw: - return peer->hiViewDraw (theEvent); - - case kEventControlBoundsChanged: - { - HIRect bounds; - HIViewGetBounds (peer->viewRef, &bounds); - peer->repaint (0, 0, roundFloatToInt (bounds.size.width), roundFloatToInt (bounds.size.height)); - - peer->handleMovedOrResized(); - return noErr; - } - - case kEventControlHitTest: - { - HIPoint where; - GetEventParameter (theEvent, kEventParamMouseLocation, typeHIPoint, 0, sizeof (HIPoint), 0, &where); - - HIRect bounds; - HIViewGetBounds (peer->viewRef, &bounds); - - ControlPartCode part = kControlNoPart; - - if (CGRectContainsPoint (bounds, where)) - part = 1; - - SetEventParameter (theEvent, kEventParamControlPart, typeControlPartCode, sizeof (ControlPartCode), &part); - return noErr; - } - break; - - case kEventControlSetFocusPart: - { - ControlPartCode desiredFocus; - if (GetEventParameter (theEvent, kEventParamControlPart, typeControlPartCode, 0, sizeof (ControlPartCode), 0, &desiredFocus) != noErr) - break; - - if (desiredFocus == kControlNoPart) - peer->viewFocusLoss(); - else - peer->viewFocusGain(); - - return noErr; - } - break; - - case kEventControlDragEnter: - { -#if MACOS_10_2_OR_EARLIER - enum { kEventParamControlWouldAcceptDrop = 'cldg' }; -#endif - Boolean accept = true; - SetEventParameter (theEvent, kEventParamControlWouldAcceptDrop, typeBoolean, sizeof (accept), &accept); - return noErr; - } - - case kEventControlDragWithin: - return noErr; - - case kEventControlDragReceive: - return peer->handleDragAndDrop (theEvent); - - case kEventControlOwningWindowChanged: - return peer->ownerWindowChanged (theEvent); - -#if ! MACOS_10_2_OR_EARLIER - case kEventControlGetFrameMetrics: - { - CallNextEventHandler (myHandler, theEvent); - HIViewFrameMetrics metrics; - GetEventParameter (theEvent, kEventParamControlFrameMetrics, typeControlFrameMetrics, 0, sizeof (metrics), 0, &metrics); - metrics.top = metrics.bottom = 0; - SetEventParameter (theEvent, kEventParamControlFrameMetrics, typeControlFrameMetrics, sizeof (metrics), &metrics); - return noErr; - } -#endif - - case kEventControlInitialize: - { - UInt32 features = kControlSupportsDragAndDrop - | kControlSupportsFocus - | kControlHandlesTracking - | kControlSupportsEmbedding - | (1 << 8) /*kHIViewFeatureGetsFocusOnClick*/; - - SetEventParameter (theEvent, kEventParamControlFeatures, typeUInt32, sizeof (UInt32), &features); - return noErr; - } - - default: - break; - } - } - - return eventNotHandledErr; - } - - WindowRef createNewWindow (const int windowStyleFlags) - { - jassert (windowRef == 0); - - static ToolboxObjectClassRef customWindowClass = 0; - - if (customWindowClass == 0) - { - // Register our window class - const EventTypeSpec customTypes[] = { { kEventClassWindow, kEventWindowDrawFrame } }; - - UnsignedWide t; - Microseconds (&t); - const String randomString ((int) (t.lo & 0x7ffffff)); - const String juceWindowClassName (T("JUCEWindowClass_") + randomString); - CFStringRef juceWindowClassNameCFString = PlatformUtilities::juceStringToCFString (juceWindowClassName); - - RegisterToolboxObjectClass (juceWindowClassNameCFString, - 0, 1, customTypes, - NewEventHandlerUPP (handleFrameRepaintEvent), - 0, &customWindowClass); - - CFRelease (juceWindowClassNameCFString); - } - - Rect pos; - pos.left = getComponent()->getX(); - pos.top = getComponent()->getY(); - pos.right = getComponent()->getRight(); - pos.bottom = getComponent()->getBottom(); - - int attributes = kWindowStandardHandlerAttribute | kWindowCompositingAttribute; - if ((windowStyleFlags & windowHasDropShadow) == 0) - attributes |= kWindowNoShadowAttribute; - - if ((windowStyleFlags & windowIgnoresMouseClicks) != 0) - attributes |= kWindowIgnoreClicksAttribute; - -#if ! MACOS_10_3_OR_EARLIER - if ((windowStyleFlags & windowIsTemporary) != 0) - attributes |= kWindowDoesNotCycleAttribute; -#endif - - WindowRef newWindow = 0; - - if ((windowStyleFlags & windowHasTitleBar) == 0) - { - attributes |= kWindowCollapseBoxAttribute; - - WindowDefSpec customWindowSpec; - customWindowSpec.defType = kWindowDefObjectClass; - customWindowSpec.u.classRef = customWindowClass; - - CreateCustomWindow (&customWindowSpec, - ((windowStyleFlags & windowIsTemporary) != 0) ? kUtilityWindowClass : - (getComponent()->isAlwaysOnTop() ? kUtilityWindowClass - : kDocumentWindowClass), - attributes, - &pos, - &newWindow); - } - else - { - if ((windowStyleFlags & windowHasCloseButton) != 0) - attributes |= kWindowCloseBoxAttribute; - - if ((windowStyleFlags & windowHasMinimiseButton) != 0) - attributes |= kWindowCollapseBoxAttribute; - - if ((windowStyleFlags & windowHasMaximiseButton) != 0) - attributes |= kWindowFullZoomAttribute; - - if ((windowStyleFlags & windowIsResizable) != 0) - attributes |= kWindowResizableAttribute | kWindowLiveResizeAttribute; - - CreateNewWindow (kDocumentWindowClass, attributes, &pos, &newWindow); - } - - jassert (newWindow != 0); - if (newWindow != 0) - { - HideWindow (newWindow); - - SetAutomaticControlDragTrackingEnabledForWindow (newWindow, true); - - if (! getComponent()->isOpaque()) - SetWindowAlpha (newWindow, 0.9999999f); // to fool it into giving the window an alpha-channel - } - - return newWindow; - } - - OSStatus ownerWindowChanged (EventRef theEvent) - { - WindowRef newWindow = 0; - GetEventParameter (theEvent, kEventParamControlCurrentOwningWindow, typeWindowRef, 0, sizeof (newWindow), 0, &newWindow); - - if (windowRef != newWindow) - { - if (eventHandlerRef != 0) - { - RemoveEventHandler (eventHandlerRef); - eventHandlerRef = 0; - } - - windowRef = newWindow; - - if (windowRef != 0) - { - const EventTypeSpec eventTypes[] = - { - { kEventClassWindow, kEventWindowBoundsChanged }, - { kEventClassWindow, kEventWindowBoundsChanging }, - { kEventClassWindow, kEventWindowFocusAcquired }, - { kEventClassWindow, kEventWindowFocusRelinquish }, - { kEventClassWindow, kEventWindowCollapsed }, - { kEventClassWindow, kEventWindowExpanded }, - { kEventClassWindow, kEventWindowShown }, - { kEventClassWindow, kEventWindowClose }, - { kEventClassMouse, kEventMouseDown }, - { kEventClassMouse, kEventMouseUp }, - { kEventClassMouse, kEventMouseMoved }, - { kEventClassMouse, kEventMouseDragged }, - { kEventClassMouse, kEventMouseEntered }, - { kEventClassMouse, kEventMouseExited }, - { kEventClassMouse, kEventMouseWheelMoved }, - //{ kEventClassKeyboard, kEventRawKeyDown }, - { kEventClassKeyboard, kEventRawKeyUp }, - { kEventClassKeyboard, kEventRawKeyRepeat }, - { kEventClassKeyboard, kEventRawKeyModifiersChanged }, - { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent } - }; - - static EventHandlerUPP handleWindowEventUPP = 0; - - if (handleWindowEventUPP == 0) - handleWindowEventUPP = NewEventHandlerUPP (handleWindowEvent); - - InstallWindowEventHandler (windowRef, handleWindowEventUPP, - GetEventTypeCount (eventTypes), eventTypes, - (void*) this, (EventHandlerRef*) &eventHandlerRef); - - WindowAttributes attributes; - GetWindowAttributes (windowRef, &attributes); - -#if MACOS_10_3_OR_EARLIER - isCompositingWindow = ((attributes & kWindowCompositingAttribute) != 0); -#else - isCompositingWindow = HIViewIsCompositingEnabled (viewRef); -#endif - - MouseCheckTimer::getInstance()->resetMouseMoveChecker(); - } - } - - resizeViewToFitWindow(); - return noErr; - } - - void createNewHIView() - { - jassert (viewRef == 0); - - if (viewClassRef == 0) - { - // Register our HIView class - EventTypeSpec viewEvents[] = - { - { kEventClassHIObject, kEventHIObjectConstruct }, - { kEventClassHIObject, kEventHIObjectInitialize }, - { kEventClassHIObject, kEventHIObjectDestruct }, - { kEventClassControl, kEventControlInitialize }, - { kEventClassControl, kEventControlDraw }, - { kEventClassControl, kEventControlBoundsChanged }, - { kEventClassControl, kEventControlSetFocusPart }, - { kEventClassControl, kEventControlHitTest }, - { kEventClassControl, kEventControlDragEnter }, - { kEventClassControl, kEventControlDragWithin }, - { kEventClassControl, kEventControlDragReceive }, - { kEventClassControl, kEventControlOwningWindowChanged } - }; - - UnsignedWide t; - Microseconds (&t); - const String randomString ((int) (t.lo & 0x7ffffff)); - const String juceHiViewClassName (T("JUCEHIViewClass_") + randomString); - juceHiViewClassNameCFString = PlatformUtilities::juceStringToCFString (juceHiViewClassName); - - HIObjectRegisterSubclass (juceHiViewClassNameCFString, - kHIViewClassID, 0, - NewEventHandlerUPP (hiViewEventHandler), - GetEventTypeCount (viewEvents), - viewEvents, 0, - &viewClassRef); - } - - EventRef event; - CreateEvent (0, kEventClassHIObject, kEventHIObjectInitialize, GetCurrentEventTime(), kEventAttributeNone, &event); - - void* thisPointer = this; - SetEventParameter (event, 'peer', typeVoidPtr, sizeof (void*), &thisPointer); - - HIObjectCreate (juceHiViewClassNameCFString, event, (HIObjectRef*) &viewRef); - - SetControlDragTrackingEnabled (viewRef, true); - - if (isSharedWindow) - { - setBounds (component->getX(), component->getY(), - component->getWidth(), component->getHeight(), false); - } - } -}; - -bool juce_isHIViewCreatedByJuce (HIViewRef view) -{ - return juceHiViewClassNameCFString != 0 - && HIObjectIsOfClass ((HIObjectRef) view, juceHiViewClassNameCFString); -} - -static void trackNextMouseEvent() -{ - UInt32 mods; - MouseTrackingResult result; - ::Point where; - - if (TrackMouseLocationWithOptions ((GrafPtr) -1, 0, 0.01, //kEventDurationForever, - &where, &mods, &result) != noErr - || ! ComponentPeer::isValidPeer (juce_currentMouseTrackingPeer)) - { - juce_currentMouseTrackingPeer = 0; - return; - } - - if (result == kMouseTrackingTimedOut) - return; - -#if MACOS_10_3_OR_EARLIER - const int x = where.h - juce_currentMouseTrackingPeer->getScreenX(); - const int y = where.v - juce_currentMouseTrackingPeer->getScreenY(); -#else - HIPoint p; - p.x = where.h; - p.y = where.v; - HIPointConvert (&p, kHICoordSpaceScreenPixel, 0, - kHICoordSpaceView, ((HIViewComponentPeer*) juce_currentMouseTrackingPeer)->viewRef); - const int x = p.x; - const int y = p.y; -#endif - - if (result == kMouseTrackingMouseDragged) - { - updateModifiers (0); - juce_currentMouseTrackingPeer->handleMouseDrag (x, y, getEventTime (0)); - - if (! ComponentPeer::isValidPeer (juce_currentMouseTrackingPeer)) - { - juce_currentMouseTrackingPeer = 0; - return; - } - } - else if (result == kMouseTrackingMouseUp - || result == kMouseTrackingUserCancelled - || result == kMouseTrackingMouseMoved) - { - if (ComponentPeer::isValidPeer (juce_currentMouseTrackingPeer)) - { - const int oldModifiers = currentModifiers; - currentModifiers &= ~(ModifierKeys::leftButtonModifier | ModifierKeys::rightButtonModifier | ModifierKeys::middleButtonModifier); - updateModifiers (0); - - juce_currentMouseTrackingPeer->handleMouseUp (oldModifiers, x, y, getEventTime (0)); - } - - juce_currentMouseTrackingPeer = 0; - } -} - -bool juce_dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages) -{ - if (juce_currentMouseTrackingPeer != 0) - trackNextMouseEvent(); - - EventRef theEvent; - - if (ReceiveNextEvent (0, 0, (returnIfNoPendingMessages) ? kEventDurationNoWait - : kEventDurationForever, - true, &theEvent) == noErr) - { - if (GetEventClass (theEvent) == kEventClassAppleEvent) - { - EventRecord eventRec; - if (ConvertEventRefToEventRecord (theEvent, &eventRec)) - AEProcessAppleEvent (&eventRec); - } - else - { - EventTargetRef theTarget = GetEventDispatcherTarget(); - SendEventToEventTarget (theEvent, theTarget); - } - - ReleaseEvent (theEvent); - return true; - } - - return false; -} - -//============================================================================== -ComponentPeer* Component::createNewPeer (int styleFlags, void* windowToAttachTo) -{ - return new HIViewComponentPeer (this, styleFlags, (HIViewRef) windowToAttachTo); -} - -//============================================================================== -void MouseCheckTimer::timerCallback() -{ - if (ModifierKeys::getCurrentModifiersRealtime().isAnyMouseButtonDown()) - return; - - if (Process::isForegroundProcess()) - { - bool stillOver = false; - int x = 0, y = 0, w = 0, h = 0; - int mx = 0, my = 0; - const bool validWindow = ComponentPeer::isValidPeer (lastPeerUnderMouse); - - if (validWindow) - { - lastPeerUnderMouse->getBounds (x, y, w, h, true); - Desktop::getMousePosition (mx, my); - - stillOver = (mx >= x && my >= y && mx < x + w && my < y + h); - - if (stillOver) - { - // check if it's over an embedded HIView - int rx = mx, ry = my; - lastPeerUnderMouse->globalPositionToRelative (rx, ry); - HIPoint hipoint; - hipoint.x = rx; - hipoint.y = ry; - - HIViewRef root; - GetRootControl ((WindowRef) lastPeerUnderMouse->getNativeHandle(), &root); - - HIViewRef hitview; - if (HIViewGetSubviewHit (root, &hipoint, true, &hitview) == noErr && hitview != 0) - { - stillOver = HIObjectIsOfClass ((HIObjectRef) hitview, juceHiViewClassNameCFString); - } - } - } - - if (! stillOver) - { - // mouse is outside our windows so set a normal cursor (only - // if we're running as an app, not a plugin) - if (JUCEApplication::getInstance() != 0) - SetThemeCursor (kThemeArrowCursor); - - if (validWindow) - lastPeerUnderMouse->handleMouseExit (mx - x, my - y, Time::currentTimeMillis()); - - if (hasEverHadAMouseMove) - stopTimer(); - } - - if ((! hasEverHadAMouseMove) && validWindow - && (mx != lastX || my != lastY)) - { - lastX = mx; - lastY = my; - - if (stillOver) - lastPeerUnderMouse->handleMouseMove (mx - x, my - y, Time::currentTimeMillis()); - } - } -} - -//============================================================================== -// called from juce_Messaging.cpp -void juce_HandleProcessFocusChange() -{ - keysCurrentlyDown.clear(); - - if (HIViewComponentPeer::isValidPeer (currentlyFocusedPeer)) - { - if (Process::isForegroundProcess()) - currentlyFocusedPeer->handleFocusGain(); - else - currentlyFocusedPeer->handleFocusLoss(); - } -} - -static bool performDrag (DragRef drag) -{ - EventRecord event; - event.what = mouseDown; - event.message = 0; - event.when = TickCount(); - - int x, y; - Desktop::getMousePosition (x, y); - event.where.h = x; - event.where.v = y; - - event.modifiers = GetCurrentKeyModifiers(); - - RgnHandle rgn = NewRgn(); - RgnHandle rgn2 = NewRgn(); - SetRectRgn (rgn, - event.where.h - 8, event.where.v - 8, - event.where.h + 8, event.where.v + 8); - CopyRgn (rgn, rgn2); - InsetRgn (rgn2, 1, 1); - DiffRgn (rgn, rgn2, rgn); - DisposeRgn (rgn2); - - bool result = TrackDrag (drag, &event, rgn) == noErr; - - DisposeRgn (rgn); - return result; -} - -bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, const bool canMoveFiles) -{ - for (int i = ComponentPeer::getNumPeers(); --i >= 0;) - ComponentPeer::getPeer (i)->performAnyPendingRepaintsNow(); - - DragRef drag; - bool result = false; - - if (NewDrag (&drag) == noErr) - { - for (int i = 0; i < files.size(); ++i) - { - HFSFlavor hfsData; - - if (PlatformUtilities::makeFSSpecFromPath (&hfsData.fileSpec, files[i])) - { - FInfo info; - if (FSpGetFInfo (&hfsData.fileSpec, &info) == noErr) - { - hfsData.fileType = info.fdType; - hfsData.fileCreator = info.fdCreator; - hfsData.fdFlags = info.fdFlags; - - AddDragItemFlavor (drag, i + 1, kDragFlavorTypeHFS, &hfsData, sizeof (hfsData), 0); - result = true; - } - } - } - - SetDragAllowableActions (drag, canMoveFiles ? kDragActionAll - : kDragActionCopy, false); - - if (result) - result = performDrag (drag); - - DisposeDrag (drag); - } - - return result; -} - -bool DragAndDropContainer::performExternalDragDropOfText (const String& text) -{ - jassertfalse // not implemented! - return false; -} - - -//============================================================================== -bool Process::isForegroundProcess() throw() -{ - ProcessSerialNumber psn, front; - GetCurrentProcess (&psn); - GetFrontProcess (&front); - - Boolean b; - return (SameProcess (&psn, &front, &b) == noErr) && b; -} - -//============================================================================== -bool Desktop::canUseSemiTransparentWindows() -{ - return true; -} - - -//============================================================================== -void Desktop::getMousePosition (int& x, int& y) -{ - 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) -{ - CGPoint pos = { x, y }; - CGWarpMouseCursorPosition (pos); -} - -const ModifierKeys ModifierKeys::getCurrentModifiersRealtime() -{ - return ModifierKeys (currentModifiers); -} - -//============================================================================== -void juce_updateMultiMonitorInfo (Array & monitorCoords, const bool clipToWorkArea) throw() -{ - int mainMonitorIndex = 0; - CGDirectDisplayID mainDisplayID = CGMainDisplayID(); - - CGDisplayCount count = 0; - CGDirectDisplayID disps [8]; - - if (CGGetActiveDisplayList (numElementsInArray (disps), disps, &count) == noErr) - { - for (int i = 0; i < count; ++i) - { - if (mainDisplayID == disps[i]) - mainMonitorIndex = monitorCoords.size(); - - GDHandle hGDevice; - - if (clipToWorkArea - && DMGetGDeviceByDisplayID ((DisplayIDType) disps[i], &hGDevice, false) == noErr) - { - Rect rect; - GetAvailableWindowPositioningBounds (hGDevice, &rect); - - monitorCoords.add (Rectangle (rect.left, - rect.top, - rect.right - rect.left, - rect.bottom - rect.top)); - } - else - { - const CGRect r (CGDisplayBounds (disps[i])); - - monitorCoords.add (Rectangle (r.origin.x, - r.origin.y, - r.size.width, - r.size.height)); - } - } - } - - // make sure the first in the list is the main monitor - if (mainMonitorIndex > 0) - monitorCoords.swap (mainMonitorIndex, 0); - - jassert (monitorCoords.size() > 0); - - //xxx need to register for display change callbacks -} - -//============================================================================== -struct CursorWrapper -{ - Cursor* cursor; - ThemeCursor themeCursor; -}; - -void* juce_createMouseCursorFromImage (const Image& image, int hotspotX, int hotspotY) throw() -{ - const int maxW = 16; - const int maxH = 16; - - const Image* im = ℑ - Image* newIm = 0; - - if (image.getWidth() > maxW || image.getHeight() > maxH) - { - im = newIm = image.createCopy (maxW, maxH); - - hotspotX = (hotspotX * maxW) / image.getWidth(); - hotspotY = (hotspotY * maxH) / image.getHeight(); - } - - Cursor* const c = new Cursor(); - c->hotSpot.h = hotspotX; - c->hotSpot.v = hotspotY; - - for (int y = 0; y < maxH; ++y) - { - c->data[y] = 0; - c->mask[y] = 0; - - for (int x = 0; x < maxW; ++x) - { - const Colour pixelColour (im->getPixelAt (15 - x, y)); - - if (pixelColour.getAlpha() > 0.5f) - { - c->mask[y] |= (1 << x); - - if (pixelColour.getBrightness() < 0.5f) - c->data[y] |= (1 << x); - } - } - - c->data[y] = CFSwapInt16BigToHost (c->data[y]); - c->mask[y] = CFSwapInt16BigToHost (c->mask[y]); - } - - if (newIm != 0) - delete newIm; - - CursorWrapper* const cw = new CursorWrapper(); - cw->cursor = c; - cw->themeCursor = kThemeArrowCursor; - return (void*) cw; -} - -static void* cursorFromData (const unsigned char* data, const int size, int hx, int hy) throw() -{ - Image* const im = ImageFileFormat::loadFrom ((const char*) data, size); - jassert (im != 0); - void* curs = juce_createMouseCursorFromImage (*im, hx, hy); - delete im; - return curs; -} - -const unsigned int kSpecialNoCursor = 'nocr'; - -void* juce_createStandardMouseCursor (MouseCursor::StandardCursorType type) throw() -{ - ThemeCursor id = kThemeArrowCursor; - - switch (type) - { - case MouseCursor::NormalCursor: - id = kThemeArrowCursor; - break; - - case MouseCursor::NoCursor: - id = kSpecialNoCursor; - break; - - case MouseCursor::DraggingHandCursor: - { - static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,2,0,0,0,0,255,255,255,0, - 0,0,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, - 16,0,0,2,52,148,47,0,200,185,16,130,90,12,74,139,107,84,123,39, - 132,117,151,116,132,146,248,60,209,138,98,22,203,114,34,236,37,52,77,217, - 247,154,191,119,110,240,193,128,193,95,163,56,60,234,98,135,2,0,59 }; - const int cursDataSize = 99; - - return cursorFromData (cursData, cursDataSize, 8, 8); - } - break; - - case MouseCursor::CopyingCursor: - id = kThemeCopyArrowCursor; - break; - - case MouseCursor::WaitCursor: - id = kThemeWatchCursor; - break; - - case MouseCursor::IBeamCursor: - id = kThemeIBeamCursor; - break; - - case MouseCursor::PointingHandCursor: - id = kThemePointingHandCursor; - break; - - case MouseCursor::LeftRightResizeCursor: - case MouseCursor::LeftEdgeResizeCursor: - case MouseCursor::RightEdgeResizeCursor: - { - static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,0,0,255,255,255,0,0,0,255, - 255,255,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, - 16,0,0,2,38,148,143,169,203,237,15,19,0,106,202,64,111,22,32,224, - 9,78,30,213,121,230,121,146,99,8,142,71,183,189,152,20,27,86,132,231, - 58,83,0,0,59 }; - const int cursDataSize = 85; - - return cursorFromData (cursData, cursDataSize, 8, 8); - } - - case MouseCursor::UpDownResizeCursor: - case MouseCursor::TopEdgeResizeCursor: - case MouseCursor::BottomEdgeResizeCursor: - { - static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,0,0,255,255,255,0,0,0,255, - 255,255,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, - 16,0,0,2,38,148,111,128,187,16,202,90,152,48,10,55,169,189,192,245, - 106,121,27,34,142,201,99,158,224,86,154,109,216,61,29,155,105,180,61,190, - 121,84,0,0,59 }; - const int cursDataSize = 85; - - return cursorFromData (cursData, cursDataSize, 8, 8); - } - - case MouseCursor::TopLeftCornerResizeCursor: - case MouseCursor::BottomRightCornerResizeCursor: - { - static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,0,0,255,255,255,0,0,0,255, - 255,255,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, - 16,0,0,2,43,132,15,162,187,16,255,18,99,14,202,217,44,158,213,221, - 237,9,225,38,94,35,73,5,31,42,170,108,106,174,112,43,195,209,91,185, - 104,174,131,208,77,66,28,10,0,59 }; - const int cursDataSize = 90; - - return cursorFromData (cursData, cursDataSize, 8, 8); - } - - case MouseCursor::TopRightCornerResizeCursor: - case MouseCursor::BottomLeftCornerResizeCursor: - { - static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,0,0,255,255,255,0,0,0,255, - 255,255,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, - 16,0,0,2,45,148,127,160,11,232,16,98,108,14,65,73,107,194,122,223, - 92,65,141,216,145,134,162,153,221,25,128,73,166,62,173,16,203,237,188,94, - 120,46,237,105,239,123,48,80,157,2,0,59 }; - const int cursDataSize = 92; - - return cursorFromData (cursData, cursDataSize, 8, 8); - } - - case MouseCursor::UpDownLeftRightResizeCursor: - { - static const unsigned char cursData[] = {71,73,70,56,57,97,15,0,15,0,145,0,0,0,0,0,255,255,255,0, - 128,128,255,255,255,33,249,4,1,0,0,3,0,44,0,0,0,0,15,0, - 15,0,0,2,46,156,63,129,139,1,202,26,152,48,186,73,109,114,65,85, - 195,37,143,88,93,29,215,101,23,198,178,30,149,158,25,56,134,97,179,61, - 158,213,126,203,234,99,220,34,56,70,1,0,59,0,0 }; - const int cursDataSize = 93; - - return cursorFromData (cursData, cursDataSize, 7, 7); - } - - case MouseCursor::CrosshairCursor: - id = kThemeCrossCursor; - break; - } - - CursorWrapper* cw = new CursorWrapper(); - cw->cursor = 0; - cw->themeCursor = id; - - return (void*) cw; -} - -void juce_deleteMouseCursor (void* const cursorHandle, const bool isStandard) throw() -{ - CursorWrapper* const cw = (CursorWrapper*) cursorHandle; - - if (cw != 0) - { - delete cw->cursor; - delete cw; - } -} - -void MouseCursor::showInAllWindows() const throw() -{ - showInWindow (0); -} - -void MouseCursor::showInWindow (ComponentPeer*) const throw() -{ - const CursorWrapper* const cw = (CursorWrapper*) getHandle(); - - if (cw != 0) - { - static bool isCursorHidden = false; - static bool showingWaitCursor = false; - const bool shouldShowWaitCursor = (cw->themeCursor == kThemeWatchCursor); - const bool shouldHideCursor = (cw->themeCursor == kSpecialNoCursor); - - if (shouldShowWaitCursor != showingWaitCursor - && Process::isForegroundProcess()) - { - showingWaitCursor = shouldShowWaitCursor; - QDDisplayWaitCursor (shouldShowWaitCursor); - } - - if (shouldHideCursor != isCursorHidden) - { - isCursorHidden = shouldHideCursor; - - if (shouldHideCursor) - HideCursor(); - else - ShowCursor(); - } - - if (cw->cursor != 0) - SetCursor (cw->cursor); - else if (! (shouldShowWaitCursor || shouldHideCursor)) - SetThemeCursor (cw->themeCursor); - } -} - -//============================================================================== -Image* juce_createIconForFile (const File& file) -{ - return 0; -} - - -//============================================================================== -class MainMenuHandler; -static MainMenuHandler* mainMenu = 0; - -class MainMenuHandler : private MenuBarModelListener, - private DeletedAtShutdown -{ -public: - MainMenuHandler() throw() - : currentModel (0) - { - } - - ~MainMenuHandler() throw() - { - setMenu (0); - - jassert (mainMenu == this); - mainMenu = 0; - } - - void setMenu (MenuBarModel* const newMenuBarModel) throw() - { - if (currentModel != newMenuBarModel) - { - if (currentModel != 0) - currentModel->removeListener (this); - - currentModel = newMenuBarModel; - - if (currentModel != 0) - currentModel->addListener (this); - - menuBarItemsChanged (0); - } - } - - void menuBarItemsChanged (MenuBarModel*) - { - ClearMenuBar(); - - if (currentModel != 0) - { - int id = 1000; - const StringArray menuNames (currentModel->getMenuBarNames()); - - for (int i = 0; i < menuNames.size(); ++i) - { - const PopupMenu menu (currentModel->getMenuForIndex (i, menuNames [i])); - - MenuRef m = createMenu (menu, menuNames [i], id); - - InsertMenu (m, 0); - CFRelease (m); - } - } - } - - void menuCommandInvoked (MenuBarModel*, const ApplicationCommandTarget::InvocationInfo& info) - { - MenuRef menu = 0; - MenuItemIndex index = 0; - GetIndMenuItemWithCommandID (0, info.commandID, 1, &menu, &index); - - FlashMenuBar (GetMenuID (menu)); - FlashMenuBar (GetMenuID (menu)); - } - - void invoke (const int id, ApplicationCommandManager* 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) - { - MenuRef m = 0; - - if (CreateNewMenu (id++, kMenuAttrAutoDisable, &m) == noErr) - { - CFStringRef name = PlatformUtilities::juceStringToCFString (menuName); - SetMenuTitleWithCFString (m, name); - CFRelease (name); - - PopupMenu::MenuItemIterator iter (menu); - int topLevelIndex = 0; - - 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); - SetMenuItemHierarchicalMenu (m, index, sub); - CFRelease (sub); - } - else - { - AppendMenuItemTextWithCFString (m, text, flags, iter.itemId, &index); - - if (iter.isTicked) - CheckMenuItem (m, index, true); - - SetMenuItemProperty (m, index, 'juce', 'apcm', sizeof (void*), &iter.commandManager); - SetMenuItemProperty (m, index, 'juce', 'topi', sizeof (int), &topLevelIndex); - - if (iter.commandManager != 0) - { - const Array keyPresses (iter.commandManager->getKeyMappings() - ->getKeyPressesAssignedToCommand (iter.itemId)); - - if (keyPresses.size() > 0) - { - const KeyPress& kp = keyPresses.getUnchecked(0); - int mods = 0; - - if (kp.getModifiers().isShiftDown()) - mods |= kMenuShiftModifier; - if (kp.getModifiers().isCtrlDown()) - mods |= kMenuControlModifier; - if (kp.getModifiers().isAltDown()) - mods |= kMenuOptionModifier; - if (! kp.getModifiers().isCommandDown()) - mods |= kMenuNoCommandModifier; - - tchar keyCode = (tchar) kp.getKeyCode(); - - if (kp.getKeyCode() >= KeyPress::numberPad0 - && kp.getKeyCode() <= KeyPress::numberPad9) - { - keyCode = (tchar) ((T('0') - KeyPress::numberPad0) + kp.getKeyCode()); - } - - SetMenuItemCommandKey (m, index, true, 255); - - if (CharacterFunctions::isLetterOrDigit (keyCode) - || CharacterFunctions::indexOfChar (T(",.;/\\'[]=-+_<>?{}\":"), keyCode, false) >= 0) - { - SetMenuItemModifiers (m, index, mods); - SetMenuItemCommandKey (m, index, false, CharacterFunctions::toUpperCase (keyCode)); - } - else - { - const SInt16 glyph = getGlyphForKeyCode (kp.getKeyCode()); - - if (glyph != 0) - { - SetMenuItemModifiers (m, index, mods); - SetMenuItemKeyGlyph (m, index, glyph); - } - } - - // if we set the key glyph to be a text char, and enable virtual - // key triggering, it stops the menu automatically triggering the callback - ChangeMenuItemAttributes (m, index, kMenuItemAttrUseVirtualKey, 0); - } - } - } - - CFRelease (text); - ++topLevelIndex; - } - } - - return m; - } - - static SInt16 getGlyphForKeyCode (const int keyCode) throw() - { - if (keyCode == KeyPress::spaceKey) - return kMenuSpaceGlyph; - else if (keyCode == KeyPress::returnKey) - return kMenuReturnGlyph; - else if (keyCode == KeyPress::escapeKey) - return kMenuEscapeGlyph; - else if (keyCode == KeyPress::backspaceKey) - return kMenuDeleteLeftGlyph; - else if (keyCode == KeyPress::leftKey) - return kMenuLeftArrowGlyph; - else if (keyCode == KeyPress::rightKey) - return kMenuRightArrowGlyph; - else if (keyCode == KeyPress::upKey) - return kMenuUpArrowGlyph; - else if (keyCode == KeyPress::downKey) - return kMenuDownArrowGlyph; - else if (keyCode == KeyPress::pageUpKey) - return kMenuPageUpGlyph; - else if (keyCode == KeyPress::pageDownKey) - return kMenuPageDownGlyph; - else if (keyCode == KeyPress::endKey) - return kMenuSoutheastArrowGlyph; - else if (keyCode == KeyPress::homeKey) - return kMenuNorthwestArrowGlyph; - else if (keyCode == KeyPress::deleteKey) - return kMenuDeleteRightGlyph; - else if (keyCode == KeyPress::tabKey) - return kMenuTabRightGlyph; - else if (keyCode == KeyPress::F1Key) - return kMenuF1Glyph; - else if (keyCode == KeyPress::F2Key) - return kMenuF2Glyph; - else if (keyCode == KeyPress::F3Key) - return kMenuF3Glyph; - else if (keyCode == KeyPress::F4Key) - return kMenuF4Glyph; - else if (keyCode == KeyPress::F5Key) - return kMenuF5Glyph; - else if (keyCode == KeyPress::F6Key) - return kMenuF6Glyph; - else if (keyCode == KeyPress::F7Key) - return kMenuF7Glyph; - else if (keyCode == KeyPress::F8Key) - return kMenuF8Glyph; - else if (keyCode == KeyPress::F9Key) - return kMenuF9Glyph; - else if (keyCode == KeyPress::F10Key) - return kMenuF10Glyph; - else if (keyCode == KeyPress::F11Key) - return kMenuF11Glyph; - else if (keyCode == KeyPress::F12Key) - return kMenuF12Glyph; - else if (keyCode == KeyPress::F13Key) - return kMenuF13Glyph; - else if (keyCode == KeyPress::F14Key) - return kMenuF14Glyph; - else if (keyCode == KeyPress::F15Key) - return kMenuF15Glyph; - - return 0; - } -}; - -void MenuBarModel::setMacMainMenu (MenuBarModel* newMenuBarModel) throw() -{ - if (getMacMainMenu() != newMenuBarModel) - { - if (newMenuBarModel == 0) - { - delete mainMenu; - jassert (mainMenu == 0); // should be zeroed in the destructor - } - else - { - if (mainMenu == 0) - mainMenu = new MainMenuHandler(); - - mainMenu->setMenu (newMenuBarModel); - } - } -} - -MenuBarModel* MenuBarModel::getMacMainMenu() throw() -{ - return mainMenu != 0 ? mainMenu->currentModel : 0; -} - -// these functions are called externally from the message handling code -void juce_MainMenuAboutToBeUsed() -{ - // force an update of the items just before the menu appears.. - if (mainMenu != 0) - mainMenu->menuBarItemsChanged (0); -} - -void juce_InvokeMainMenuCommand (const HICommand& command) -{ - if (mainMenu != 0) - { - ApplicationCommandManager* commandManager = 0; - int topLevelIndex = 0; - - if (GetMenuItemProperty (command.menu.menuRef, command.menu.menuItemIndex, - 'juce', 'apcm', sizeof (commandManager), 0, &commandManager) == noErr - && GetMenuItemProperty (command.menu.menuRef, command.menu.menuItemIndex, - 'juce', 'topi', sizeof (topLevelIndex), 0, &topLevelIndex) == noErr) - { - mainMenu->invoke (command.commandID, commandManager, topLevelIndex); - } - } -} - -//============================================================================== -void PlatformUtilities::beep() -{ - SysBeep (30); -} - -//============================================================================== -void SystemClipboard::copyTextToClipboard (const String& text) -{ - ClearCurrentScrap(); - ScrapRef ref; - GetCurrentScrap (&ref); - - const int len = text.length(); - const int numBytes = sizeof (UniChar) * len; - UniChar* const temp = (UniChar*) juce_calloc (numBytes); - - for (int i = 0; i < len; ++i) - temp[i] = (UniChar) text[i]; - - PutScrapFlavor (ref, - kScrapFlavorTypeUnicode, - kScrapFlavorMaskNone, - numBytes, - temp); - - juce_free (temp); -} - -const String SystemClipboard::getTextFromClipboard() -{ - String result; - - ScrapRef ref; - GetCurrentScrap (&ref); - Size size = 0; - - if (GetScrapFlavorSize (ref, kScrapFlavorTypeUnicode, &size) == noErr - && size > 0) - { - void* const data = juce_calloc (size + 8); - - if (GetScrapFlavorData (ref, kScrapFlavorTypeUnicode, &size, data) == noErr) - { - result = PlatformUtilities::convertUTF16ToString ((UniChar*) data); - } - - juce_free (data); - } - - return result; -} - - -//============================================================================== -bool AlertWindow::showNativeDialogBox (const String& title, - const String& bodyText, - bool isOkCancel) -{ - Str255 tit, txt; - PlatformUtilities::copyToStr255 (tit, title); - PlatformUtilities::copyToStr255 (txt, bodyText); - - AlertStdAlertParamRec ar; - ar.movable = true; - ar.helpButton = false; - ar.filterProc = 0; - ar.defaultText = (const unsigned char*)-1; - ar.cancelText = (const unsigned char*)((isOkCancel) ? -1 : 0); - ar.otherText = 0; - ar.defaultButton = kAlertStdAlertOKButton; - ar.cancelButton = 0; - ar.position = kWindowDefaultPosition; - - SInt16 result; - StandardAlert (kAlertNoteAlert, tit, txt, &ar, &result); - return result == kAlertStdAlertOKButton; -} - -//============================================================================== -const int KeyPress::spaceKey = ' '; -const int KeyPress::returnKey = kReturnCharCode; -const int KeyPress::escapeKey = kEscapeCharCode; -const int KeyPress::backspaceKey = kBackspaceCharCode; -const int KeyPress::leftKey = kLeftArrowCharCode; -const int KeyPress::rightKey = kRightArrowCharCode; -const int KeyPress::upKey = kUpArrowCharCode; -const int KeyPress::downKey = kDownArrowCharCode; -const int KeyPress::pageUpKey = kPageUpCharCode; -const int KeyPress::pageDownKey = kPageDownCharCode; -const int KeyPress::endKey = kEndCharCode; -const int KeyPress::homeKey = kHomeCharCode; -const int KeyPress::deleteKey = kDeleteCharCode; -const int KeyPress::insertKey = -1; -const int KeyPress::tabKey = kTabCharCode; -const int KeyPress::F1Key = 0x10110; -const int KeyPress::F2Key = 0x10111; -const int KeyPress::F3Key = 0x10112; -const int KeyPress::F4Key = 0x10113; -const int KeyPress::F5Key = 0x10114; -const int KeyPress::F6Key = 0x10115; -const int KeyPress::F7Key = 0x10116; -const int KeyPress::F8Key = 0x10117; -const int KeyPress::F9Key = 0x10118; -const int KeyPress::F10Key = 0x10119; -const int KeyPress::F11Key = 0x1011a; -const int KeyPress::F12Key = 0x1011b; -const int KeyPress::F13Key = 0x1011c; -const int KeyPress::F14Key = 0x1011d; -const int KeyPress::F15Key = 0x1011e; -const int KeyPress::F16Key = 0x1011f; -const int KeyPress::numberPad0 = 0x30020; -const int KeyPress::numberPad1 = 0x30021; -const int KeyPress::numberPad2 = 0x30022; -const int KeyPress::numberPad3 = 0x30023; -const int KeyPress::numberPad4 = 0x30024; -const int KeyPress::numberPad5 = 0x30025; -const int KeyPress::numberPad6 = 0x30026; -const int KeyPress::numberPad7 = 0x30027; -const int KeyPress::numberPad8 = 0x30028; -const int KeyPress::numberPad9 = 0x30029; -const int KeyPress::numberPadAdd = 0x3002a; -const int KeyPress::numberPadSubtract = 0x3002b; -const int KeyPress::numberPadMultiply = 0x3002c; -const int KeyPress::numberPadDivide = 0x3002d; -const int KeyPress::numberPadSeparator = 0x3002e; -const int KeyPress::numberPadDecimalPoint = 0x3002f; -const int KeyPress::playKey = 0x30000; -const int KeyPress::stopKey = 0x30001; -const int KeyPress::fastForwardKey = 0x30002; -const int KeyPress::rewindKey = 0x30003; - -//============================================================================== -#if JUCE_OPENGL - -struct OpenGLContextInfo -{ - AGLContext renderContext; -}; - -void* juce_createOpenGLContext (OpenGLComponent* component, void* sharedContext) -{ - jassert (component != 0); - - HIViewComponentPeer* const peer = dynamic_cast (component->getTopLevelComponent()->getPeer()); - - if (peer == 0) - return 0; - - OpenGLContextInfo* const oc = new OpenGLContextInfo(); - - GLint attrib[] = { AGL_RGBA, AGL_DOUBLEBUFFER, - AGL_RED_SIZE, 8, - AGL_ALPHA_SIZE, 8, - AGL_DEPTH_SIZE, 24, - AGL_CLOSEST_POLICY, AGL_NO_RECOVERY, - AGL_SAMPLE_BUFFERS_ARB, 1, - AGL_SAMPLES_ARB, 4, - AGL_NONE }; - - oc->renderContext = aglCreateContext (aglChoosePixelFormat (0, 0, attrib), - (sharedContext != 0) ? ((OpenGLContextInfo*) sharedContext)->renderContext - : 0); - - aglSetDrawable (oc->renderContext, - GetWindowPort (peer->windowRef)); - - return oc; -} - -void juce_updateOpenGLWindowPos (void* context, Component* owner, Component* topComp) -{ - jassert (context != 0); - OpenGLContextInfo* const oc = (OpenGLContextInfo*) context; - - GLint bufferRect[4]; - - bufferRect[0] = owner->getScreenX() - topComp->getScreenX(); - bufferRect[1] = topComp->getHeight() - (owner->getHeight() + owner->getScreenY() - topComp->getScreenY()); - bufferRect[2] = owner->getWidth(); - bufferRect[3] = owner->getHeight(); - - aglSetInteger (oc->renderContext, AGL_BUFFER_RECT, bufferRect); - aglEnable (oc->renderContext, AGL_BUFFER_RECT); -} - -void juce_deleteOpenGLContext (void* context) -{ - OpenGLContextInfo* const oc = (OpenGLContextInfo*) context; - - aglDestroyContext (oc->renderContext); - - delete oc; -} - -bool juce_makeOpenGLContextCurrent (void* context) -{ - OpenGLContextInfo* const oc = (OpenGLContextInfo*) context; - - return aglSetCurrentContext ((oc != 0) ? oc->renderContext : 0); -} - -void juce_swapOpenGLBuffers (void* context) -{ - OpenGLContextInfo* const oc = (OpenGLContextInfo*) context; - - if (oc != 0) - aglSwapBuffers (oc->renderContext); -} - -void juce_repaintOpenGLWindow (void* context) -{ -} - -#endif - -END_JUCE_NAMESPACE +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#include "../../../src/juce_core/basics/juce_StandardHeader.h" +#include +#include + +#if JUCE_OPENGL +#include +#endif + +BEGIN_JUCE_NAMESPACE + +#include "../../../src/juce_appframework/events/juce_Timer.h" +#include "../../../src/juce_appframework/application/juce_DeletedAtShutdown.h" +#include "../../../src/juce_appframework/events/juce_AsyncUpdater.h" +#include "../../../src/juce_appframework/events/juce_MessageManager.h" +#include "../../../src/juce_core/basics/juce_Singleton.h" +#include "../../../src/juce_core/basics/juce_Random.h" +#include "../../../src/juce_core/threads/juce_Process.h" +#include "../../../src/juce_appframework/application/juce_SystemClipboard.h" +#include "../../../src/juce_appframework/gui/components/keyboard/juce_KeyPress.h" +#include "../../../src/juce_appframework/gui/components/windows/juce_AlertWindow.h" +#include "../../../src/juce_appframework/gui/graphics/geometry/juce_RectangleList.h" +#include "../../../src/juce_appframework/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h" +#include "../../../src/juce_appframework/gui/components/juce_Desktop.h" +#include "../../../src/juce_appframework/gui/components/menus/juce_MenuBarModel.h" +#include "../../../src/juce_core/misc/juce_PlatformUtilities.h" +#include "../../../src/juce_appframework/application/juce_Application.h" +#include "../../../src/juce_appframework/gui/components/special/juce_OpenGLComponent.h" +#include "../../../src/juce_appframework/gui/components/mouse/juce_DragAndDropContainer.h" +#include "../../../src/juce_appframework/gui/components/keyboard/juce_KeyPressMappingSet.h" +#include "../../../src/juce_appframework/gui/graphics/imaging/juce_ImageFileFormat.h" + +#undef Point + +const WindowRegionCode windowRegionToUse = kWindowContentRgn; + +static HIObjectClassRef viewClassRef = 0; +static CFStringRef juceHiViewClassNameCFString = 0; +static ComponentPeer* juce_currentMouseTrackingPeer = 0; + + +//============================================================================== +static VoidArray keysCurrentlyDown; + +bool KeyPress::isKeyCurrentlyDown (int keyCode) +{ + if (keysCurrentlyDown.contains ((void*) keyCode)) + return true; + + if (keyCode >= 'A' && keyCode <= 'Z' + && keysCurrentlyDown.contains ((void*) (int) CharacterFunctions::toLowerCase ((tchar) keyCode))) + return true; + + if (keyCode >= 'a' && keyCode <= 'z' + && keysCurrentlyDown.contains ((void*) (int) CharacterFunctions::toUpperCase ((tchar) keyCode))) + return true; + + return false; +} + +//============================================================================== +static VoidArray minimisedWindows; + +static void setWindowMinimised (WindowRef ref, const bool isMinimised) +{ + if (isMinimised != minimisedWindows.contains (ref)) + CollapseWindow (ref, isMinimised); +} + +void juce_maximiseAllMinimisedWindows() +{ + const VoidArray minWin (minimisedWindows); + + for (int i = minWin.size(); --i >= 0;) + setWindowMinimised ((WindowRef) (minWin[i]), false); +} + +//============================================================================== +class HIViewComponentPeer; +static HIViewComponentPeer* currentlyFocusedPeer = 0; + + +//============================================================================== +static int currentModifiers = 0; + +static void updateModifiers (EventRef theEvent) +{ + currentModifiers &= ~ (ModifierKeys::shiftModifier | ModifierKeys::ctrlModifier + | ModifierKeys::altModifier | ModifierKeys::commandModifier); + + UInt32 m; + + if (theEvent != 0) + GetEventParameter (theEvent, kEventParamKeyModifiers, typeUInt32, 0, sizeof(m), 0, &m); + else + m = GetCurrentEventKeyModifiers(); + + if ((m & (shiftKey | rightShiftKey)) != 0) + currentModifiers |= ModifierKeys::shiftModifier; + + if ((m & (controlKey | rightControlKey)) != 0) + currentModifiers |= ModifierKeys::ctrlModifier; + + if ((m & (optionKey | rightOptionKey)) != 0) + currentModifiers |= ModifierKeys::altModifier; + + if ((m & cmdKey) != 0) + currentModifiers |= ModifierKeys::commandModifier; +} + +void ModifierKeys::updateCurrentModifiers() +{ + currentModifierFlags = currentModifiers; +} + +static int64 getEventTime (EventRef event) +{ + const int64 millis = (int64) (1000.0 * (event != 0 ? GetEventTime (event) + : GetCurrentEventTime())); + + static int64 offset = 0; + if (offset == 0) + offset = Time::currentTimeMillis() - millis; + + return offset + millis; +} + + +//============================================================================== +class MacBitmapImage : public Image +{ +public: + //============================================================================== + CGColorSpaceRef colourspace; + CGDataProviderRef provider; + + //============================================================================== + MacBitmapImage (const PixelFormat format_, + const int w, const int h, const bool clearImage) + : Image (format_, w, h) + { + jassert (format_ == RGB || format_ == ARGB); + + pixelStride = (format_ == RGB) ? 3 : 4; + + lineStride = (w * pixelStride + 3) & ~3; + const int imageSize = lineStride * h; + + if (clearImage) + imageData = (uint8*) juce_calloc (imageSize); + else + imageData = (uint8*) juce_malloc (imageSize); + + //colourspace = CGColorSpaceCreateWithName (kCGColorSpaceUserRGB); + + CMProfileRef prof; + CMGetSystemProfile (&prof); + colourspace = CGColorSpaceCreateWithPlatformColorSpace (prof); + + provider = CGDataProviderCreateWithData (0, imageData, h * lineStride, 0); + } + + MacBitmapImage::~MacBitmapImage() + { + CGDataProviderRelease (provider); + CGColorSpaceRelease (colourspace); + + juce_free (imageData); + imageData = 0; // to stop the base class freeing this + } + + void blitToContext (CGContextRef context, const float dx, const float dy) + { + CGImageRef tempImage = CGImageCreate (getWidth(), getHeight(), + 8, pixelStride << 3, lineStride, colourspace, +#if MACOS_10_3_OR_EARLIER || JUCE_BIG_ENDIAN + hasAlphaChannel() ? kCGImageAlphaPremultipliedFirst + : kCGImageAlphaNone, +#else + hasAlphaChannel() ? kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst + : kCGImageAlphaNone, +#endif + provider, 0, false, + kCGRenderingIntentDefault); + + HIRect r; + r.origin.x = dx; + r.origin.y = dy; + r.size.width = (float) getWidth(); + r.size.height = (float) getHeight(); + + HIViewDrawCGImage (context, &r, tempImage); + + CGImageRelease (tempImage); + } + + juce_UseDebuggingNewOperator +}; + + +//============================================================================== +class MouseCheckTimer : private Timer, + private DeletedAtShutdown +{ + HIViewComponentPeer* lastPeerUnderMouse; + int lastX, lastY; + +public: + MouseCheckTimer() + : lastX (0), + lastY (0) + { + lastPeerUnderMouse = 0; + resetMouseMoveChecker(); + } + + ~MouseCheckTimer() + { + clearSingletonInstance(); + } + + juce_DeclareSingleton_SingleThreaded_Minimal (MouseCheckTimer) + + bool hasEverHadAMouseMove; + + void moved (HIViewComponentPeer* const peer) + { + if (hasEverHadAMouseMove) + startTimer (200); + + lastPeerUnderMouse = peer; + } + + void resetMouseMoveChecker() + { + hasEverHadAMouseMove = false; + startTimer (1000 / 16); + } + + void timerCallback(); +}; + +juce_ImplementSingleton_SingleThreaded (MouseCheckTimer) + +//============================================================================== +#if JUCE_QUICKTIME +extern void OfferMouseClickToQuickTime (WindowRef window, ::Point where, long when, long modifiers, + Component* topLevelComp); +#endif + + +//============================================================================== +class HIViewComponentPeer : public ComponentPeer, + private Timer +{ +public: + //============================================================================== + HIViewComponentPeer (Component* const component, + const int windowStyleFlags, + HIViewRef viewToAttachTo) + : ComponentPeer (component, windowStyleFlags), + fullScreen (false), + isCompositingWindow (false), + windowRef (0), + viewRef (0) + { + repainter = new RepaintManager (this); + + eventHandlerRef = 0; + + if (viewToAttachTo != 0) + { + isSharedWindow = true; + } + else + { + isSharedWindow = false; + + WindowRef newWindow = createNewWindow (windowStyleFlags); + + GetRootControl (newWindow, (ControlRef*) &viewToAttachTo); + jassert (viewToAttachTo != 0); + + HIViewRef growBox = 0; + HIViewFindByID (HIViewGetRoot (newWindow), kHIViewWindowGrowBoxID, &growBox); + + if (growBox != 0) + HIGrowBoxViewSetTransparent (growBox, true); + } + + createNewHIView(); + + HIViewAddSubview (viewToAttachTo, viewRef); + HIViewSetVisible (viewRef, component->isVisible()); + + setTitle (component->getName()); + + if (component->isVisible() && ! isSharedWindow) + { + ShowWindow (windowRef); + ActivateWindow (windowRef, component->getWantsKeyboardFocus()); + } + } + + ~HIViewComponentPeer() + { + minimisedWindows.removeValue (windowRef); + + if (IsValidWindowPtr (windowRef)) + { + if (! isSharedWindow) + { + CFRelease (viewRef); + viewRef = 0; + + DisposeWindow (windowRef); + } + else + { + if (eventHandlerRef != 0) + RemoveEventHandler (eventHandlerRef); + + CFRelease (viewRef); + viewRef = 0; + } + + windowRef = 0; + } + + if (currentlyFocusedPeer == this) + currentlyFocusedPeer = 0; + + delete repainter; + } + + //============================================================================== + void* getNativeHandle() const + { + return windowRef; + } + + void setVisible (bool shouldBeVisible) + { + HIViewSetVisible (viewRef, shouldBeVisible); + + if ((! isSharedWindow) && IsValidWindowPtr (windowRef)) + { + if (shouldBeVisible) + ShowWindow (windowRef); + else + HideWindow (windowRef); + + resizeViewToFitWindow(); + + // If nothing else is focused, then grab the focus too + if (shouldBeVisible + && Component::getCurrentlyFocusedComponent() == 0 + && Process::isForegroundProcess()) + { + component->toFront (true); + } + } + } + + void setTitle (const String& title) + { + if ((! isSharedWindow) && IsValidWindowPtr (windowRef)) + { + CFStringRef t = PlatformUtilities::juceStringToCFString (title); + SetWindowTitleWithCFString (windowRef, t); + CFRelease (t); + } + } + + void setPosition (int x, int y) + { + if (isSharedWindow) + { + HIViewPlaceInSuperviewAt (viewRef, x, y); + } + else if (IsValidWindowPtr (windowRef)) + { + Rect r; + GetWindowBounds (windowRef, windowRegionToUse, &r); + r.right += x - r.left; + r.bottom += y - r.top; + r.left = x; + r.top = y; + SetWindowBounds (windowRef, windowRegionToUse, &r); + } + } + + void setSize (int w, int h) + { + w = jmax (0, w); + h = jmax (0, h); + + if (w != getComponent()->getWidth() + || h != getComponent()->getHeight()) + { + repainter->repaint (0, 0, w, h); + } + + if (isSharedWindow) + { + HIRect r; + HIViewGetFrame (viewRef, &r); + r.size.width = (float) w; + r.size.height = (float) h; + HIViewSetFrame (viewRef, &r); + } + else if (IsValidWindowPtr (windowRef)) + { + Rect r; + GetWindowBounds (windowRef, windowRegionToUse, &r); + r.right = r.left + w; + r.bottom = r.top + h; + SetWindowBounds (windowRef, windowRegionToUse, &r); + } + } + + void setBounds (int x, int y, int w, int h, const bool isNowFullScreen) + { + fullScreen = isNowFullScreen; + w = jmax (0, w); + h = jmax (0, h); + + if (w != getComponent()->getWidth() + || h != getComponent()->getHeight()) + { + repainter->repaint (0, 0, w, h); + } + + if (isSharedWindow) + { + HIRect r; + r.origin.x = (float) x; + r.origin.y = (float) y; + r.size.width = (float) w; + r.size.height = (float) h; + HIViewSetFrame (viewRef, &r); + } + else if (IsValidWindowPtr (windowRef)) + { + Rect r; + r.left = x; + r.top = y; + r.right = x + w; + r.bottom = y + h; + SetWindowBounds (windowRef, windowRegionToUse, &r); + } + } + + void getBounds (int& x, int& y, int& w, int& h, const bool global) const + { + HIRect hiViewPos; + HIViewGetFrame (viewRef, &hiViewPos); + + if (global) + { + HIViewRef content = 0; + HIViewFindByID (HIViewGetRoot (windowRef), kHIViewWindowContentID, &content); + HIPoint p = { 0.0f, 0.0f }; + HIViewConvertPoint (&p, viewRef, content); + + x = (int) p.x; + y = (int) p.y; + + if (IsValidWindowPtr (windowRef)) + { + Rect windowPos; + GetWindowBounds (windowRef, kWindowContentRgn, &windowPos); + + x += windowPos.left; + y += windowPos.top; + } + } + else + { + x = (int) hiViewPos.origin.x; + y = (int) hiViewPos.origin.y; + } + + w = (int) hiViewPos.size.width; + h = (int) hiViewPos.size.height; + } + + void getBounds (int& x, int& y, int& w, int& h) const + { + getBounds (x, y, w, h, ! isSharedWindow); + } + + int getScreenX() const + { + int x, y, w, h; + getBounds (x, y, w, h, true); + return x; + } + + int getScreenY() const + { + int x, y, w, h; + getBounds (x, y, w, h, true); + return y; + } + + void relativePositionToGlobal (int& x, int& y) + { + int wx, wy, ww, wh; + getBounds (wx, wy, ww, wh, true); + + x += wx; + y += wy; + } + + void globalPositionToRelative (int& x, int& y) + { + int wx, wy, ww, wh; + getBounds (wx, wy, ww, wh, true); + + x -= wx; + y -= wy; + } + + void setMinimised (bool shouldBeMinimised) + { + if (! isSharedWindow) + setWindowMinimised (windowRef, shouldBeMinimised); + } + + bool isMinimised() const + { + return minimisedWindows.contains (windowRef); + } + + void setFullScreen (bool shouldBeFullScreen) + { + if (! isSharedWindow) + { + setMinimised (false); + + if (fullScreen != shouldBeFullScreen) + { + Rectangle r (lastNonFullscreenBounds); + + if (shouldBeFullScreen) + r = Desktop::getInstance().getMainMonitorArea(); + + // (can't call the component's setBounds method because that'll reset our fullscreen flag) + if (r != getComponent()->getBounds() && ! r.isEmpty()) + setBounds (r.getX(), r.getY(), r.getWidth(), r.getHeight(), shouldBeFullScreen); + } + } + } + + bool isFullScreen() const + { + return fullScreen; + } + + bool contains (int x, int y, bool trueIfInAChildWindow) const + { + if (x < 0 || y < 0 + || x >= component->getWidth() || y >= component->getHeight() + || ! IsValidWindowPtr (windowRef)) + return false; + + Rect r; + GetWindowBounds (windowRef, windowRegionToUse, &r); + + ::Point p; + p.h = r.left + x; + p.v = r.top + y; + + WindowRef ref2 = 0; + FindWindow (p, &ref2); + + if (windowRef != ref2) + return false; + + if (trueIfInAChildWindow) + return true; + + HIPoint p2; + p2.x = (float) x; + p2.y = (float) y; + HIViewRef hit; + + HIViewGetSubviewHit (viewRef, &p2, true, &hit); + return hit == 0 || hit == viewRef; + } + + const BorderSize getFrameSize() const + { + return BorderSize(); + } + + bool setAlwaysOnTop (bool alwaysOnTop) + { + // can't do this so return false and let the component create a new window + return false; + } + + void toFront (bool makeActiveWindow) + { + makeActiveWindow = makeActiveWindow + && component->isValidComponent() + && (component->getWantsKeyboardFocus() + || component->isCurrentlyModal()); + + if (windowRef != FrontWindow() + || (makeActiveWindow && ! IsWindowActive (windowRef)) + || ! Process::isForegroundProcess()) + { + if (! Process::isForegroundProcess()) + { + ProcessSerialNumber psn; + GetCurrentProcess (&psn); + SetFrontProcess (&psn); + } + + if (IsValidWindowPtr (windowRef)) + { + if (makeActiveWindow) + { + SelectWindow (windowRef); + SetUserFocusWindow (windowRef); + HIViewAdvanceFocus (viewRef, 0); + } + else + { + BringToFront (windowRef); + } + + handleBroughtToFront(); + } + } + } + + void toBehind (ComponentPeer* other) + { + HIViewComponentPeer* const otherWindow = dynamic_cast (other); + + if (other != 0 && windowRef != 0 && otherWindow->windowRef != 0) + { + if (windowRef == otherWindow->windowRef) + { + HIViewSetZOrder (viewRef, kHIViewZOrderBelow, otherWindow->viewRef); + } + else + { + SendBehind (windowRef, otherWindow->windowRef); + } + } + } + + void setIcon (const Image& /*newIcon*/) + { + // to do.. + } + + //============================================================================== + void viewFocusGain() + { + const MessageManagerLock messLock; + + if (currentlyFocusedPeer != this) + { + if (ComponentPeer::isValidPeer (currentlyFocusedPeer)) + currentlyFocusedPeer->handleFocusLoss(); + + currentlyFocusedPeer = this; + + handleFocusGain(); + } + } + + void viewFocusLoss() + { + if (currentlyFocusedPeer == this) + { + currentlyFocusedPeer = 0; + handleFocusLoss(); + } + } + + bool isFocused() const + { + return windowRef == GetUserFocusWindow() + && HIViewSubtreeContainsFocus (viewRef); + } + + void grabFocus() + { + if ((! isFocused()) && IsValidWindowPtr (windowRef)) + { + SetUserFocusWindow (windowRef); + HIViewAdvanceFocus (viewRef, 0); + } + } + + //============================================================================== + void repaint (int x, int y, int w, int h) + { + if (Rectangle::intersectRectangles (x, y, w, h, + 0, 0, + getComponent()->getWidth(), + getComponent()->getHeight())) + { + if ((getStyleFlags() & windowRepaintedExplictly) == 0) + { + if (isCompositingWindow) + { +#if MACOS_10_3_OR_EARLIER + RgnHandle rgn = NewRgn(); + SetRectRgn (rgn, x, y, x + w, y + h); + HIViewSetNeedsDisplayInRegion (viewRef, rgn, true); + DisposeRgn (rgn); +#else + HIRect r; + r.origin.x = x; + r.origin.y = y; + r.size.width = w; + r.size.height = h; + + HIViewSetNeedsDisplayInRect (viewRef, &r, true); +#endif + } + else + { + if (! isTimerRunning()) + startTimer (20); + } + } + + repainter->repaint (x, y, w, h); + } + } + + void timerCallback() + { + performAnyPendingRepaintsNow(); + } + + void performAnyPendingRepaintsNow() + { + stopTimer(); + + if (component->isVisible()) + { +#if MACOS_10_2_OR_EARLIER + if (! isCompositingWindow) + { + Rect w; + GetWindowBounds (windowRef, windowRegionToUse, &w); + + RgnHandle rgn = NewRgn(); + SetRectRgn (rgn, 0, 0, w.right - w.left, w.bottom - w.top); + UpdateControls (windowRef, rgn); + DisposeRgn (rgn); + } + else + { + EventRef theEvent; + + EventTypeSpec eventTypes[1]; + eventTypes[0].eventClass = kEventClassControl; + eventTypes[0].eventKind = kEventControlDraw; + + int n = 3; + while (--n >= 0 + && ReceiveNextEvent (1, eventTypes, kEventDurationNoWait, true, &theEvent) == noErr) + { + if (GetEventClass (theEvent) == kEventClassAppleEvent) + { + EventRecord eventRec; + if (ConvertEventRefToEventRecord (theEvent, &eventRec)) + AEProcessAppleEvent (&eventRec); + } + else + { + EventTargetRef theTarget = GetEventDispatcherTarget(); + SendEventToEventTarget (theEvent, theTarget); + } + + ReleaseEvent (theEvent); + } + } +#else + HIViewRender (viewRef); +#endif + } + } + + //============================================================================== + juce_UseDebuggingNewOperator + + WindowRef windowRef; + HIViewRef viewRef; + +private: + EventHandlerRef eventHandlerRef; + bool fullScreen, isSharedWindow, isCompositingWindow; + + //============================================================================== + class RepaintManager : public Timer + { +public: + RepaintManager (HIViewComponentPeer* const peer_) + : peer (peer_), + image (0) + { + } + + ~RepaintManager() + { + delete image; + } + + void timerCallback() + { + stopTimer(); + deleteAndZero (image); + } + + void repaint (int x, int y, int w, int h) + { + regionsNeedingRepaint.add (x, y, w, h); + } + + void repaintAnyRemainingRegions() + { + // if any regions have been invaldated during the paint callback, + // we need to repaint them explicitly because the mac throws this + // stuff away + for (RectangleList::Iterator i (regionsNeedingRepaint); i.next();) + { + const Rectangle& r = *i.getRectangle(); + peer->repaint (r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + } + + void paint (CGContextRef cgContext, int x, int y, int w, int h) + { + if (w > 0 && h > 0) + { + bool refresh = false; + int imW = image != 0 ? image->getWidth() : 0; + int imH = image != 0 ? image->getHeight() : 0; + + if (imW < w || imH < h) + { + imW = jmin (peer->getComponent()->getWidth(), (w + 31) & ~31); + imH = jmin (peer->getComponent()->getHeight(), (h + 31) & ~31); + + delete image; + image = new MacBitmapImage (peer->getComponent()->isOpaque() ? Image::RGB + : Image::ARGB, + imW, imH, false); + + refresh = true; + } + else if (imageX > x || imageY > y + || imageX + imW < x + w + || imageY + imH < y + h) + { + refresh = true; + } + + if (refresh) + { + regionsNeedingRepaint.clear(); + regionsNeedingRepaint.addWithoutMerging (Rectangle (x, y, imW, imH)); + imageX = x; + imageY = y; + } + + LowLevelGraphicsSoftwareRenderer context (*image); + context.setOrigin (-imageX, -imageY); + + if (context.reduceClipRegion (regionsNeedingRepaint)) + { + regionsNeedingRepaint.clear(); + + if (! peer->getComponent()->isOpaque()) + { + for (RectangleList::Iterator i (*context.getRawClipRegion()); i.next();) + { + const Rectangle& r = *i.getRectangle(); + image->clear (r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + } + + regionsNeedingRepaint.clear(); + peer->clearMaskedRegion(); + peer->handlePaint (context); + } + else + { + regionsNeedingRepaint.clear(); + } + + if (! peer->maskedRegion.isEmpty()) + { + RectangleList total (Rectangle (x, y, w, h)); + total.subtract (peer->maskedRegion); + + CGRect* rects = (CGRect*) juce_malloc (sizeof (CGRect) * total.getNumRectangles()); + int n = 0; + + for (RectangleList::Iterator i (total); i.next();) + { + const Rectangle& r = *i.getRectangle(); + rects[n].origin.x = (int) r.getX(); + rects[n].origin.y = (int) r.getY(); + rects[n].size.width = roundFloatToInt (r.getWidth()); + rects[n++].size.height = roundFloatToInt (r.getHeight()); + } + + CGContextClipToRects (cgContext, rects, n); + juce_free (rects); + } + + if (peer->isSharedWindow) + { + CGRect clip; + clip.origin.x = x; + clip.origin.y = y; + clip.size.width = jmin (w, peer->getComponent()->getWidth() - x); + clip.size.height = jmin (h, peer->getComponent()->getHeight() - y); + + CGContextClipToRect (cgContext, clip); + } + + image->blitToContext (cgContext, imageX, imageY); + } + + startTimer (3000); + } + + private: + HIViewComponentPeer* const peer; + MacBitmapImage* image; + int imageX, imageY; + RectangleList regionsNeedingRepaint; + + RepaintManager (const RepaintManager&); + const RepaintManager& operator= (const RepaintManager&); + }; + + RepaintManager* repainter; + + friend class RepaintManager; + + //============================================================================== + static OSStatus handleFrameRepaintEvent (EventHandlerCallRef myHandler, + EventRef theEvent, + void* userData) + { + // don't draw the frame.. + return noErr; + } + + //============================================================================== + OSStatus handleWindowClassEvent (EventRef theEvent) + { + switch (GetEventKind (theEvent)) + { + case kEventWindowBoundsChanged: + resizeViewToFitWindow(); + break; // allow other handlers in the event chain to also get a look at the events + + case kEventWindowBoundsChanging: + if ((styleFlags & (windowIsResizable | windowHasTitleBar)) == (windowIsResizable | windowHasTitleBar)) + { + UInt32 atts = 0; + GetEventParameter (theEvent, kEventParamAttributes, typeUInt32, + 0, sizeof (UInt32), 0, &atts); + + if ((atts & (kWindowBoundsChangeUserDrag | kWindowBoundsChangeUserResize)) != 0) + { + if (component->isCurrentlyBlockedByAnotherModalComponent()) + { + Component* const modal = Component::getCurrentlyModalComponent(); + if (modal != 0) + modal->inputAttemptWhenModal(); + } + + if ((atts & kWindowBoundsChangeUserResize) != 0 + && constrainer != 0 && ! isSharedWindow) + { + Rect current; + GetEventParameter (theEvent, kEventParamCurrentBounds, typeQDRectangle, + 0, sizeof (Rect), 0, ¤t); + + int x = current.left; + int y = current.top; + int w = current.right - current.left; + int h = current.bottom - current.top; + + const Rectangle currentRect (getComponent()->getBounds()); + + constrainer->checkBounds (x, y, w, h, currentRect, + Desktop::getInstance().getAllMonitorDisplayAreas().getBounds(), + y != currentRect.getY() && y + h == currentRect.getBottom(), + x != currentRect.getX() && x + w == currentRect.getRight(), + y == currentRect.getY() && y + h != currentRect.getBottom(), + x == currentRect.getX() && x + w != currentRect.getRight()); + + current.left = x; + current.top = y; + current.right = x + w; + current.bottom = y + h; + + SetEventParameter (theEvent, kEventParamCurrentBounds, typeQDRectangle, + sizeof (Rect), ¤t); + + return noErr; + } + } + } + break; + + case kEventWindowFocusAcquired: + keysCurrentlyDown.clear(); + + if ((! isSharedWindow) || HIViewSubtreeContainsFocus (viewRef)) + viewFocusGain(); + + break; // allow other handlers in the event chain to also get a look at the events + + case kEventWindowFocusRelinquish: + keysCurrentlyDown.clear(); + viewFocusLoss(); + + break; // allow other handlers in the event chain to also get a look at the events + + case kEventWindowCollapsed: + minimisedWindows.addIfNotAlreadyThere (windowRef); + handleMovedOrResized(); + break; // allow other handlers in the event chain to also get a look at the events + + case kEventWindowExpanded: + minimisedWindows.removeValue (windowRef); + handleMovedOrResized(); + break; // allow other handlers in the event chain to also get a look at the events + + case kEventWindowShown: + break; // allow other handlers in the event chain to also get a look at the events + + case kEventWindowClose: + if (isSharedWindow) + break; // break to let the OS delete the window + + handleUserClosingWindow(); + return noErr; // avoids letting the OS to delete the window, we'll do that ourselves. + + default: + break; + } + + return eventNotHandledErr; + } + + OSStatus handleKeyEvent (EventRef theEvent, juce_wchar textCharacter) + { + updateModifiers (theEvent); + + UniChar unicodeChars [4]; + zeromem (unicodeChars, sizeof (unicodeChars)); + GetEventParameter (theEvent, kEventParamKeyUnicodes, typeUnicodeText, 0, sizeof (unicodeChars), 0, unicodeChars); + + int keyCode = (int) (unsigned int) unicodeChars[0]; + + UInt32 rawKey = 0; + GetEventParameter (theEvent, kEventParamKeyCode, typeUInt32, 0, sizeof (UInt32), 0, &rawKey); + + if ((currentModifiers & ModifierKeys::ctrlModifier) != 0) + { + if (keyCode >= 1 && keyCode <= 26) + keyCode += ('A' - 1); + } + + static const int keyTranslations[] = + { + 0, 's', 'd', 'f', 'h', 'g', 'z', 'x', 'c', 'v', 0xa7, 'b', + 'q', 'w', 'e', 'r', 'y', 't', '1', '2', '3', '4', '6', '5', + '=', '9', '7', '-', '8', '0', ']', 'o', 'u', '[', 'i', 'p', + KeyPress::returnKey, 'l', 'j', '\'', 'k', ';', '\\', ',', '/', + 'n', 'm', '.', 0, KeyPress::spaceKey, '`', KeyPress::backspaceKey, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, KeyPress::numberPadDecimalPoint, + 0, KeyPress::numberPadMultiply, 0, KeyPress::numberPadAdd, + 0, KeyPress::numberPadDelete, 0, 0, 0, KeyPress::numberPadDivide, KeyPress::returnKey, + 0, KeyPress::numberPadSubtract, 0, 0, KeyPress::numberPadEquals, KeyPress::numberPad0, + KeyPress::numberPad1, KeyPress::numberPad2, KeyPress::numberPad3, + KeyPress::numberPad4, KeyPress::numberPad5, KeyPress::numberPad6, + KeyPress::numberPad7, 0, KeyPress::numberPad8, KeyPress::numberPad9, + 0, 0, 0, KeyPress::F5Key, KeyPress::F6Key, KeyPress::F7Key, KeyPress::F3Key, + KeyPress::F8Key, KeyPress::F9Key, 0, KeyPress::F11Key, 0, KeyPress::F13Key, + KeyPress::F16Key, KeyPress::F14Key, 0, KeyPress::F10Key, 0, KeyPress::F12Key, + 0, KeyPress::F15Key, 0, KeyPress::homeKey, KeyPress::pageUpKey, 0, KeyPress::F4Key, + KeyPress::endKey, KeyPress::F2Key, KeyPress::pageDownKey, KeyPress::F1Key, + KeyPress::leftKey, KeyPress::rightKey, KeyPress::downKey, KeyPress::upKey, 0 + }; + + if (rawKey > 0 && rawKey < numElementsInArray (keyTranslations)) + keyCode = keyTranslations [rawKey]; + else if (rawKey == 0 && textCharacter != 0) + keyCode = 'a'; + + if ((currentModifiers & (ModifierKeys::commandModifier | ModifierKeys::ctrlModifier)) != 0) + textCharacter = 0; + + static juce_wchar lastTextCharacter = 0; + + switch (GetEventKind (theEvent)) + { + case kEventRawKeyDown: + keysCurrentlyDown.addIfNotAlreadyThere ((void*) keyCode); + handleKeyUpOrDown(); + lastTextCharacter = textCharacter; + handleKeyPress (keyCode, textCharacter); + break; + + case kEventRawKeyUp: + keysCurrentlyDown.removeValue ((void*) keyCode); + handleKeyUpOrDown(); + lastTextCharacter = 0; + break; + + case kEventRawKeyRepeat: + handleKeyPress (keyCode, lastTextCharacter); + break; + + case kEventRawKeyModifiersChanged: + handleModifierKeysChange(); + break; + + default: + jassertfalse + break; + } + + return noErr; + } + + OSStatus handleTextInputEvent (EventRef theEvent) + { + UniChar uc; + GetEventParameter (theEvent, kEventParamTextInputSendText, typeUnicodeText, 0, sizeof (uc), 0, &uc); + + EventRef originalEvent; + GetEventParameter (theEvent, kEventParamTextInputSendKeyboardEvent, typeEventRef, 0, sizeof (originalEvent), 0, &originalEvent); + + handleKeyEvent (originalEvent, (juce_wchar) uc); + + return noErr; + } + + OSStatus handleMouseEvent (EventHandlerCallRef callRef, EventRef theEvent) + { + MouseCheckTimer::getInstance()->moved (this); + + ::Point where; + GetEventParameter (theEvent, kEventParamMouseLocation, typeQDPoint, 0, sizeof (::Point), 0, &where); + int x = where.h; + int y = where.v; + globalPositionToRelative (x, y); + + int64 time = getEventTime (theEvent); + + switch (GetEventKind (theEvent)) + { + case kEventMouseMoved: + MouseCheckTimer::getInstance()->hasEverHadAMouseMove = true; + updateModifiers (theEvent); + handleMouseMove (x, y, time); + break; + + case kEventMouseDragged: + updateModifiers (theEvent); + handleMouseDrag (x, y, time); + break; + + case kEventMouseDown: + { + if (! Process::isForegroundProcess()) + { + ProcessSerialNumber psn; + GetCurrentProcess (&psn); + SetFrontProcess (&psn); + + toFront (true); + } + +#if JUCE_QUICKTIME + { + long mods; + GetEventParameter (theEvent, kEventParamKeyModifiers, typeUInt32, 0, sizeof (mods), 0, &mods); + + ::Point where; + GetEventParameter (theEvent, kEventParamMouseLocation, typeQDPoint, 0, sizeof (::Point), 0, &where); + + OfferMouseClickToQuickTime (windowRef, where, EventTimeToTicks (GetEventTime (theEvent)), mods, component); + } +#endif + + if (component->isBroughtToFrontOnMouseClick() + && ! component->isCurrentlyBlockedByAnotherModalComponent()) + { + //ActivateWindow (windowRef, true); + SelectWindow (windowRef); + } + + EventMouseButton button; + GetEventParameter (theEvent, kEventParamMouseButton, typeMouseButton, 0, sizeof (EventMouseButton), 0, &button); + + // need to clear all these flags because sometimes the mac can swallow (right) mouse-up events and + // this makes a button get stuck down. Since there's no other way to tell what buttons are down, + // this is all I can think of doing about it.. + currentModifiers &= ~(ModifierKeys::leftButtonModifier | ModifierKeys::rightButtonModifier | ModifierKeys::middleButtonModifier); + + if (button == kEventMouseButtonPrimary) + currentModifiers |= ModifierKeys::leftButtonModifier; + else if (button == kEventMouseButtonSecondary) + currentModifiers |= ModifierKeys::rightButtonModifier; + else if (button == kEventMouseButtonTertiary) + currentModifiers |= ModifierKeys::middleButtonModifier; + + updateModifiers (theEvent); + + juce_currentMouseTrackingPeer = this; // puts the message dispatcher into mouse-tracking mode.. + handleMouseDown (x, y, time); + break; + } + + case kEventMouseUp: + { + const int oldModifiers = currentModifiers; + + EventMouseButton button; + GetEventParameter (theEvent, kEventParamMouseButton, typeMouseButton, 0, sizeof (EventMouseButton), 0, &button); + + if (button == kEventMouseButtonPrimary) + currentModifiers &= ~ModifierKeys::leftButtonModifier; + else if (button == kEventMouseButtonSecondary) + currentModifiers &= ~ModifierKeys::rightButtonModifier; + + updateModifiers (theEvent); + + handleMouseUp (oldModifiers, x, y, time); + juce_currentMouseTrackingPeer = 0; + break; + } + + case kEventMouseWheelMoved: + { + EventMouseWheelAxis axis; + GetEventParameter (theEvent, kEventParamMouseWheelAxis, typeMouseWheelAxis, 0, sizeof (axis), 0, &axis); + + SInt32 delta; + GetEventParameter (theEvent, kEventParamMouseWheelDelta, + typeLongInteger, 0, sizeof (delta), 0, &delta); + + updateModifiers (theEvent); + + handleMouseWheel (axis == kEventMouseWheelAxisX ? delta * 10 : 0, + axis == kEventMouseWheelAxisX ? 0 : delta * 10, + time); + + break; + } + } + + return noErr; + } + + OSStatus handleDragAndDrop (EventRef theEvent) + { + DragRef dragRef; + if (GetEventParameter (theEvent, kEventParamDragRef, typeDragRef, 0, sizeof (dragRef), 0, &dragRef) == noErr) + { + int mx, my; + component->getMouseXYRelative (mx, my); + + UInt16 numItems = 0; + if (CountDragItems (dragRef, &numItems) == noErr) + { + StringArray filenames; + + for (int i = 0; i < (int) numItems; ++i) + { + DragItemRef ref; + + if (GetDragItemReferenceNumber (dragRef, i + 1, &ref) == noErr) + { + const FlavorType flavorType = kDragFlavorTypeHFS; + + Size size = 0; + if (GetFlavorDataSize (dragRef, ref, flavorType, &size) == noErr) + { + void* data = juce_calloc (size); + + if (GetFlavorData (dragRef, ref, flavorType, data, &size, 0) == noErr) + { + HFSFlavor* f = (HFSFlavor*) data; + FSRef fsref; + + if (FSpMakeFSRef (&f->fileSpec, &fsref) == noErr) + { + const String path (PlatformUtilities::makePathFromFSRef (&fsref)); + + if (path.isNotEmpty()) + filenames.add (path); + } + } + + juce_free (data); + } + } + } + + filenames.trim(); + filenames.removeEmptyStrings(); + + if (filenames.size() > 0) + handleFilesDropped (mx, my, filenames); + } + } + + return noErr; + } + + void resizeViewToFitWindow() + { + HIRect r; + + if (isSharedWindow) + { + HIViewGetFrame (viewRef, &r); + r.size.width = (float) component->getWidth(); + r.size.height = (float) component->getHeight(); + } + else + { + r.origin.x = 0; + r.origin.y = 0; + + Rect w; + GetWindowBounds (windowRef, windowRegionToUse, &w); + + r.size.width = (float) (w.right - w.left); + r.size.height = (float) (w.bottom - w.top); + } + + HIViewSetFrame (viewRef, &r); + +#if MACOS_10_3_OR_EARLIER + component->repaint(); +#endif + } + + OSStatus hiViewDraw (EventRef theEvent) + { + CGContextRef context = 0; + GetEventParameter (theEvent, kEventParamCGContextRef, typeCGContextRef, 0, sizeof (CGContextRef), 0, &context); + + CGrafPtr oldPort; + CGrafPtr port = 0; + + if (context == 0) + { + GetEventParameter (theEvent, kEventParamGrafPort, typeGrafPtr, 0, sizeof (CGrafPtr), 0, &port); + + GetPort (&oldPort); + SetPort (port); + + if (port != 0) + QDBeginCGContext (port, &context); + + if (! isCompositingWindow) + { + Rect bounds; + GetWindowBounds (windowRef, windowRegionToUse, &bounds); + CGContextTranslateCTM (context, 0, bounds.bottom - bounds.top); + CGContextScaleCTM (context, 1.0, -1.0); + } + + if (isSharedWindow) + { + // NB - Had terrible problems trying to correctly get the position + // of this view relative to the window, and this seems wrong, but + // works better than any other method I've tried.. + HIRect hiViewPos; + HIViewGetFrame (viewRef, &hiViewPos); + CGContextTranslateCTM (context, hiViewPos.origin.x, hiViewPos.origin.y); + } + } + +#if MACOS_10_2_OR_EARLIER + RgnHandle rgn = 0; + GetEventParameter (theEvent, kEventParamRgnHandle, typeQDRgnHandle, 0, sizeof (RgnHandle), 0, &rgn); + + CGRect clip; + + if (rgn != 0) + { + Rect bounds; + GetRegionBounds (rgn, &bounds); + clip.origin.x = bounds.left; + clip.origin.y = bounds.top; + clip.size.width = bounds.right - bounds.left; + clip.size.height = bounds.bottom - bounds.top; + } + else + { + HIViewGetBounds (viewRef, &clip); + } +#else + CGRect clip (CGContextGetClipBoundingBox (context)); +#endif + + clip = CGRectIntegral (clip); + + if (clip.origin.x < 0) + { + clip.size.width += clip.origin.x; + clip.origin.x = 0; + } + + if (clip.origin.y < 0) + { + clip.size.height += clip.origin.y; + clip.origin.y = 0; + } + + if (! component->isOpaque()) + CGContextClearRect (context, clip); + + repainter->paint (context, + (int) clip.origin.x, (int) clip.origin.y, + (int) clip.size.width, (int) clip.size.height); + + if (port != 0) + { + CGContextFlush (context); + QDEndCGContext (port, &context); + + SetPort (oldPort); + } + + repainter->repaintAnyRemainingRegions(); + + return noErr; + } + + static pascal OSStatus handleWindowEvent (EventHandlerCallRef callRef, EventRef theEvent, void* userData) + { + MessageManager::delayWaitCursor(); + + HIViewComponentPeer* const peer = (HIViewComponentPeer*) userData; + + const MessageManagerLock messLock; + + if (ComponentPeer::isValidPeer (peer)) + return peer->handleWindowEventForPeer (callRef, theEvent); + + return eventNotHandledErr; + } + + OSStatus handleWindowEventForPeer (EventHandlerCallRef callRef, EventRef theEvent) + { + switch (GetEventClass (theEvent)) + { + case kEventClassMouse: + { + static HIViewComponentPeer* lastMouseDownPeer = 0; + + const UInt32 eventKind = GetEventKind (theEvent); + HIViewRef view = 0; + + if (eventKind == kEventMouseDragged) + { + view = viewRef; + } + else + { + HIViewGetViewForMouseEvent (HIViewGetRoot (windowRef), theEvent, &view); + + if (view != viewRef) + { + if ((eventKind == kEventMouseUp + || eventKind == kEventMouseExited) + && ComponentPeer::isValidPeer (lastMouseDownPeer)) + { + return lastMouseDownPeer->handleMouseEvent (callRef, theEvent); + } + + return eventNotHandledErr; + } + } + + if (eventKind == kEventMouseDown + || eventKind == kEventMouseDragged + || eventKind == kEventMouseEntered) + { + lastMouseDownPeer = this; + } + + return handleMouseEvent (callRef, theEvent); + } + break; + + case kEventClassWindow: + return handleWindowClassEvent (theEvent); + + case kEventClassKeyboard: + if (isFocused()) + return handleKeyEvent (theEvent, 0); + + break; + + case kEventClassTextInput: + if (isFocused()) + return handleTextInputEvent (theEvent); + + break; + + default: + break; + } + + return eventNotHandledErr; + } + + static pascal OSStatus hiViewEventHandler (EventHandlerCallRef myHandler, EventRef theEvent, void* userData) + { + MessageManager::delayWaitCursor(); + + const UInt32 eventKind = GetEventKind (theEvent); + const UInt32 eventClass = GetEventClass (theEvent); + + if (eventClass == kEventClassHIObject) + { + switch (eventKind) + { + case kEventHIObjectConstruct: + { + void* data = juce_calloc (sizeof (void*)); + SetEventParameter (theEvent, kEventParamHIObjectInstance, + typeVoidPtr, sizeof (void*), &data); + + return noErr; + } + + case kEventHIObjectInitialize: + GetEventParameter (theEvent, 'peer', typeVoidPtr, 0, sizeof (void*), 0, (void**) userData); + return noErr; + + case kEventHIObjectDestruct: + juce_free (userData); + return noErr; + + default: + break; + } + } + else if (eventClass == kEventClassControl) + { + HIViewComponentPeer* const peer = *(HIViewComponentPeer**) userData; + const MessageManagerLock messLock; + + if (! ComponentPeer::isValidPeer (peer)) + return eventNotHandledErr; + + switch (eventKind) + { + case kEventControlDraw: + return peer->hiViewDraw (theEvent); + + case kEventControlBoundsChanged: + { + HIRect bounds; + HIViewGetBounds (peer->viewRef, &bounds); + peer->repaint (0, 0, roundFloatToInt (bounds.size.width), roundFloatToInt (bounds.size.height)); + + peer->handleMovedOrResized(); + return noErr; + } + + case kEventControlHitTest: + { + HIPoint where; + GetEventParameter (theEvent, kEventParamMouseLocation, typeHIPoint, 0, sizeof (HIPoint), 0, &where); + + HIRect bounds; + HIViewGetBounds (peer->viewRef, &bounds); + + ControlPartCode part = kControlNoPart; + + if (CGRectContainsPoint (bounds, where)) + part = 1; + + SetEventParameter (theEvent, kEventParamControlPart, typeControlPartCode, sizeof (ControlPartCode), &part); + return noErr; + } + break; + + case kEventControlSetFocusPart: + { + ControlPartCode desiredFocus; + if (GetEventParameter (theEvent, kEventParamControlPart, typeControlPartCode, 0, sizeof (ControlPartCode), 0, &desiredFocus) != noErr) + break; + + if (desiredFocus == kControlNoPart) + peer->viewFocusLoss(); + else + peer->viewFocusGain(); + + return noErr; + } + break; + + case kEventControlDragEnter: + { +#if MACOS_10_2_OR_EARLIER + enum { kEventParamControlWouldAcceptDrop = 'cldg' }; +#endif + Boolean accept = true; + SetEventParameter (theEvent, kEventParamControlWouldAcceptDrop, typeBoolean, sizeof (accept), &accept); + return noErr; + } + + case kEventControlDragWithin: + return noErr; + + case kEventControlDragReceive: + return peer->handleDragAndDrop (theEvent); + + case kEventControlOwningWindowChanged: + return peer->ownerWindowChanged (theEvent); + +#if ! MACOS_10_2_OR_EARLIER + case kEventControlGetFrameMetrics: + { + CallNextEventHandler (myHandler, theEvent); + HIViewFrameMetrics metrics; + GetEventParameter (theEvent, kEventParamControlFrameMetrics, typeControlFrameMetrics, 0, sizeof (metrics), 0, &metrics); + metrics.top = metrics.bottom = 0; + SetEventParameter (theEvent, kEventParamControlFrameMetrics, typeControlFrameMetrics, sizeof (metrics), &metrics); + return noErr; + } +#endif + + case kEventControlInitialize: + { + UInt32 features = kControlSupportsDragAndDrop + | kControlSupportsFocus + | kControlHandlesTracking + | kControlSupportsEmbedding + | (1 << 8) /*kHIViewFeatureGetsFocusOnClick*/; + + SetEventParameter (theEvent, kEventParamControlFeatures, typeUInt32, sizeof (UInt32), &features); + return noErr; + } + + default: + break; + } + } + + return eventNotHandledErr; + } + + WindowRef createNewWindow (const int windowStyleFlags) + { + jassert (windowRef == 0); + + static ToolboxObjectClassRef customWindowClass = 0; + + if (customWindowClass == 0) + { + // Register our window class + const EventTypeSpec customTypes[] = { { kEventClassWindow, kEventWindowDrawFrame } }; + + UnsignedWide t; + Microseconds (&t); + const String randomString ((int) (t.lo & 0x7ffffff)); + const String juceWindowClassName (T("JUCEWindowClass_") + randomString); + CFStringRef juceWindowClassNameCFString = PlatformUtilities::juceStringToCFString (juceWindowClassName); + + RegisterToolboxObjectClass (juceWindowClassNameCFString, + 0, 1, customTypes, + NewEventHandlerUPP (handleFrameRepaintEvent), + 0, &customWindowClass); + + CFRelease (juceWindowClassNameCFString); + } + + Rect pos; + pos.left = getComponent()->getX(); + pos.top = getComponent()->getY(); + pos.right = getComponent()->getRight(); + pos.bottom = getComponent()->getBottom(); + + int attributes = kWindowStandardHandlerAttribute | kWindowCompositingAttribute; + if ((windowStyleFlags & windowHasDropShadow) == 0) + attributes |= kWindowNoShadowAttribute; + + if ((windowStyleFlags & windowIgnoresMouseClicks) != 0) + attributes |= kWindowIgnoreClicksAttribute; + +#if ! MACOS_10_3_OR_EARLIER + if ((windowStyleFlags & windowIsTemporary) != 0) + attributes |= kWindowDoesNotCycleAttribute; +#endif + + WindowRef newWindow = 0; + + if ((windowStyleFlags & windowHasTitleBar) == 0) + { + attributes |= kWindowCollapseBoxAttribute; + + WindowDefSpec customWindowSpec; + customWindowSpec.defType = kWindowDefObjectClass; + customWindowSpec.u.classRef = customWindowClass; + + CreateCustomWindow (&customWindowSpec, + ((windowStyleFlags & windowIsTemporary) != 0) ? kUtilityWindowClass : + (getComponent()->isAlwaysOnTop() ? kUtilityWindowClass + : kDocumentWindowClass), + attributes, + &pos, + &newWindow); + } + else + { + if ((windowStyleFlags & windowHasCloseButton) != 0) + attributes |= kWindowCloseBoxAttribute; + + if ((windowStyleFlags & windowHasMinimiseButton) != 0) + attributes |= kWindowCollapseBoxAttribute; + + if ((windowStyleFlags & windowHasMaximiseButton) != 0) + attributes |= kWindowFullZoomAttribute; + + if ((windowStyleFlags & windowIsResizable) != 0) + attributes |= kWindowResizableAttribute | kWindowLiveResizeAttribute; + + CreateNewWindow (kDocumentWindowClass, attributes, &pos, &newWindow); + } + + jassert (newWindow != 0); + if (newWindow != 0) + { + HideWindow (newWindow); + + SetAutomaticControlDragTrackingEnabledForWindow (newWindow, true); + + if (! getComponent()->isOpaque()) + SetWindowAlpha (newWindow, 0.9999999f); // to fool it into giving the window an alpha-channel + } + + return newWindow; + } + + OSStatus ownerWindowChanged (EventRef theEvent) + { + WindowRef newWindow = 0; + GetEventParameter (theEvent, kEventParamControlCurrentOwningWindow, typeWindowRef, 0, sizeof (newWindow), 0, &newWindow); + + if (windowRef != newWindow) + { + if (eventHandlerRef != 0) + { + RemoveEventHandler (eventHandlerRef); + eventHandlerRef = 0; + } + + windowRef = newWindow; + + if (windowRef != 0) + { + const EventTypeSpec eventTypes[] = + { + { kEventClassWindow, kEventWindowBoundsChanged }, + { kEventClassWindow, kEventWindowBoundsChanging }, + { kEventClassWindow, kEventWindowFocusAcquired }, + { kEventClassWindow, kEventWindowFocusRelinquish }, + { kEventClassWindow, kEventWindowCollapsed }, + { kEventClassWindow, kEventWindowExpanded }, + { kEventClassWindow, kEventWindowShown }, + { kEventClassWindow, kEventWindowClose }, + { kEventClassMouse, kEventMouseDown }, + { kEventClassMouse, kEventMouseUp }, + { kEventClassMouse, kEventMouseMoved }, + { kEventClassMouse, kEventMouseDragged }, + { kEventClassMouse, kEventMouseEntered }, + { kEventClassMouse, kEventMouseExited }, + { kEventClassMouse, kEventMouseWheelMoved }, + { kEventClassKeyboard, kEventRawKeyUp }, + { kEventClassKeyboard, kEventRawKeyRepeat }, + { kEventClassKeyboard, kEventRawKeyModifiersChanged }, + { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent } + }; + + static EventHandlerUPP handleWindowEventUPP = 0; + + if (handleWindowEventUPP == 0) + handleWindowEventUPP = NewEventHandlerUPP (handleWindowEvent); + + InstallWindowEventHandler (windowRef, handleWindowEventUPP, + GetEventTypeCount (eventTypes), eventTypes, + (void*) this, (EventHandlerRef*) &eventHandlerRef); + + WindowAttributes attributes; + GetWindowAttributes (windowRef, &attributes); + +#if MACOS_10_3_OR_EARLIER + isCompositingWindow = ((attributes & kWindowCompositingAttribute) != 0); +#else + isCompositingWindow = HIViewIsCompositingEnabled (viewRef); +#endif + + MouseCheckTimer::getInstance()->resetMouseMoveChecker(); + } + } + + resizeViewToFitWindow(); + return noErr; + } + + void createNewHIView() + { + jassert (viewRef == 0); + + if (viewClassRef == 0) + { + // Register our HIView class + EventTypeSpec viewEvents[] = + { + { kEventClassHIObject, kEventHIObjectConstruct }, + { kEventClassHIObject, kEventHIObjectInitialize }, + { kEventClassHIObject, kEventHIObjectDestruct }, + { kEventClassControl, kEventControlInitialize }, + { kEventClassControl, kEventControlDraw }, + { kEventClassControl, kEventControlBoundsChanged }, + { kEventClassControl, kEventControlSetFocusPart }, + { kEventClassControl, kEventControlHitTest }, + { kEventClassControl, kEventControlDragEnter }, + { kEventClassControl, kEventControlDragWithin }, + { kEventClassControl, kEventControlDragReceive }, + { kEventClassControl, kEventControlOwningWindowChanged } + }; + + UnsignedWide t; + Microseconds (&t); + const String randomString ((int) (t.lo & 0x7ffffff)); + const String juceHiViewClassName (T("JUCEHIViewClass_") + randomString); + juceHiViewClassNameCFString = PlatformUtilities::juceStringToCFString (juceHiViewClassName); + + HIObjectRegisterSubclass (juceHiViewClassNameCFString, + kHIViewClassID, 0, + NewEventHandlerUPP (hiViewEventHandler), + GetEventTypeCount (viewEvents), + viewEvents, 0, + &viewClassRef); + } + + EventRef event; + CreateEvent (0, kEventClassHIObject, kEventHIObjectInitialize, GetCurrentEventTime(), kEventAttributeNone, &event); + + void* thisPointer = this; + SetEventParameter (event, 'peer', typeVoidPtr, sizeof (void*), &thisPointer); + + HIObjectCreate (juceHiViewClassNameCFString, event, (HIObjectRef*) &viewRef); + + SetControlDragTrackingEnabled (viewRef, true); + + if (isSharedWindow) + { + setBounds (component->getX(), component->getY(), + component->getWidth(), component->getHeight(), false); + } + } +}; + +bool juce_isHIViewCreatedByJuce (HIViewRef view) +{ + return juceHiViewClassNameCFString != 0 + && HIObjectIsOfClass ((HIObjectRef) view, juceHiViewClassNameCFString); +} + +static void trackNextMouseEvent() +{ + UInt32 mods; + MouseTrackingResult result; + ::Point where; + + if (TrackMouseLocationWithOptions ((GrafPtr) -1, 0, 0.01, //kEventDurationForever, + &where, &mods, &result) != noErr + || ! ComponentPeer::isValidPeer (juce_currentMouseTrackingPeer)) + { + juce_currentMouseTrackingPeer = 0; + return; + } + + if (result == kMouseTrackingTimedOut) + return; + +#if MACOS_10_3_OR_EARLIER + const int x = where.h - juce_currentMouseTrackingPeer->getScreenX(); + const int y = where.v - juce_currentMouseTrackingPeer->getScreenY(); +#else + HIPoint p; + p.x = where.h; + p.y = where.v; + HIPointConvert (&p, kHICoordSpaceScreenPixel, 0, + kHICoordSpaceView, ((HIViewComponentPeer*) juce_currentMouseTrackingPeer)->viewRef); + const int x = p.x; + const int y = p.y; +#endif + + if (result == kMouseTrackingMouseDragged) + { + updateModifiers (0); + juce_currentMouseTrackingPeer->handleMouseDrag (x, y, getEventTime (0)); + + if (! ComponentPeer::isValidPeer (juce_currentMouseTrackingPeer)) + { + juce_currentMouseTrackingPeer = 0; + return; + } + } + else if (result == kMouseTrackingMouseUp + || result == kMouseTrackingUserCancelled + || result == kMouseTrackingMouseMoved) + { + if (ComponentPeer::isValidPeer (juce_currentMouseTrackingPeer)) + { + const int oldModifiers = currentModifiers; + currentModifiers &= ~(ModifierKeys::leftButtonModifier | ModifierKeys::rightButtonModifier | ModifierKeys::middleButtonModifier); + updateModifiers (0); + + juce_currentMouseTrackingPeer->handleMouseUp (oldModifiers, x, y, getEventTime (0)); + } + + juce_currentMouseTrackingPeer = 0; + } +} + +bool juce_dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages) +{ + if (juce_currentMouseTrackingPeer != 0) + trackNextMouseEvent(); + + EventRef theEvent; + + if (ReceiveNextEvent (0, 0, (returnIfNoPendingMessages) ? kEventDurationNoWait + : kEventDurationForever, + true, &theEvent) == noErr) + { + if (GetEventClass (theEvent) == kEventClassAppleEvent) + { + EventRecord eventRec; + if (ConvertEventRefToEventRecord (theEvent, &eventRec)) + AEProcessAppleEvent (&eventRec); + } + else + { + EventTargetRef theTarget = GetEventDispatcherTarget(); + SendEventToEventTarget (theEvent, theTarget); + } + + ReleaseEvent (theEvent); + return true; + } + + return false; +} + +//============================================================================== +ComponentPeer* Component::createNewPeer (int styleFlags, void* windowToAttachTo) +{ + return new HIViewComponentPeer (this, styleFlags, (HIViewRef) windowToAttachTo); +} + +//============================================================================== +void MouseCheckTimer::timerCallback() +{ + if (ModifierKeys::getCurrentModifiersRealtime().isAnyMouseButtonDown()) + return; + + if (Process::isForegroundProcess()) + { + bool stillOver = false; + int x = 0, y = 0, w = 0, h = 0; + int mx = 0, my = 0; + const bool validWindow = ComponentPeer::isValidPeer (lastPeerUnderMouse); + + if (validWindow) + { + lastPeerUnderMouse->getBounds (x, y, w, h, true); + Desktop::getMousePosition (mx, my); + + stillOver = (mx >= x && my >= y && mx < x + w && my < y + h); + + if (stillOver) + { + // check if it's over an embedded HIView + int rx = mx, ry = my; + lastPeerUnderMouse->globalPositionToRelative (rx, ry); + HIPoint hipoint; + hipoint.x = rx; + hipoint.y = ry; + + HIViewRef root; + GetRootControl ((WindowRef) lastPeerUnderMouse->getNativeHandle(), &root); + + HIViewRef hitview; + if (HIViewGetSubviewHit (root, &hipoint, true, &hitview) == noErr && hitview != 0) + { + stillOver = HIObjectIsOfClass ((HIObjectRef) hitview, juceHiViewClassNameCFString); + } + } + } + + if (! stillOver) + { + // mouse is outside our windows so set a normal cursor (only + // if we're running as an app, not a plugin) + if (JUCEApplication::getInstance() != 0) + SetThemeCursor (kThemeArrowCursor); + + if (validWindow) + lastPeerUnderMouse->handleMouseExit (mx - x, my - y, Time::currentTimeMillis()); + + if (hasEverHadAMouseMove) + stopTimer(); + } + + if ((! hasEverHadAMouseMove) && validWindow + && (mx != lastX || my != lastY)) + { + lastX = mx; + lastY = my; + + if (stillOver) + lastPeerUnderMouse->handleMouseMove (mx - x, my - y, Time::currentTimeMillis()); + } + } +} + +//============================================================================== +// called from juce_Messaging.cpp +void juce_HandleProcessFocusChange() +{ + keysCurrentlyDown.clear(); + + if (HIViewComponentPeer::isValidPeer (currentlyFocusedPeer)) + { + if (Process::isForegroundProcess()) + currentlyFocusedPeer->handleFocusGain(); + else + currentlyFocusedPeer->handleFocusLoss(); + } +} + +static bool performDrag (DragRef drag) +{ + EventRecord event; + event.what = mouseDown; + event.message = 0; + event.when = TickCount(); + + int x, y; + Desktop::getMousePosition (x, y); + event.where.h = x; + event.where.v = y; + + event.modifiers = GetCurrentKeyModifiers(); + + RgnHandle rgn = NewRgn(); + RgnHandle rgn2 = NewRgn(); + SetRectRgn (rgn, + event.where.h - 8, event.where.v - 8, + event.where.h + 8, event.where.v + 8); + CopyRgn (rgn, rgn2); + InsetRgn (rgn2, 1, 1); + DiffRgn (rgn, rgn2, rgn); + DisposeRgn (rgn2); + + bool result = TrackDrag (drag, &event, rgn) == noErr; + + DisposeRgn (rgn); + return result; +} + +bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, const bool canMoveFiles) +{ + for (int i = ComponentPeer::getNumPeers(); --i >= 0;) + ComponentPeer::getPeer (i)->performAnyPendingRepaintsNow(); + + DragRef drag; + bool result = false; + + if (NewDrag (&drag) == noErr) + { + for (int i = 0; i < files.size(); ++i) + { + HFSFlavor hfsData; + + if (PlatformUtilities::makeFSSpecFromPath (&hfsData.fileSpec, files[i])) + { + FInfo info; + if (FSpGetFInfo (&hfsData.fileSpec, &info) == noErr) + { + hfsData.fileType = info.fdType; + hfsData.fileCreator = info.fdCreator; + hfsData.fdFlags = info.fdFlags; + + AddDragItemFlavor (drag, i + 1, kDragFlavorTypeHFS, &hfsData, sizeof (hfsData), 0); + result = true; + } + } + } + + SetDragAllowableActions (drag, canMoveFiles ? kDragActionAll + : kDragActionCopy, false); + + if (result) + result = performDrag (drag); + + DisposeDrag (drag); + } + + return result; +} + +bool DragAndDropContainer::performExternalDragDropOfText (const String& text) +{ + jassertfalse // not implemented! + return false; +} + + +//============================================================================== +bool Process::isForegroundProcess() throw() +{ + ProcessSerialNumber psn, front; + GetCurrentProcess (&psn); + GetFrontProcess (&front); + + Boolean b; + return (SameProcess (&psn, &front, &b) == noErr) && b; +} + +//============================================================================== +bool Desktop::canUseSemiTransparentWindows() +{ + return true; +} + + +//============================================================================== +void Desktop::getMousePosition (int& x, int& y) +{ + 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) +{ + CGPoint pos = { x, y }; + CGWarpMouseCursorPosition (pos); +} + +const ModifierKeys ModifierKeys::getCurrentModifiersRealtime() throw() +{ + return ModifierKeys (currentModifiers); +} + +//============================================================================== +void juce_updateMultiMonitorInfo (Array & monitorCoords, const bool clipToWorkArea) throw() +{ + int mainMonitorIndex = 0; + CGDirectDisplayID mainDisplayID = CGMainDisplayID(); + + CGDisplayCount count = 0; + CGDirectDisplayID disps [8]; + + if (CGGetActiveDisplayList (numElementsInArray (disps), disps, &count) == noErr) + { + for (int i = 0; i < count; ++i) + { + if (mainDisplayID == disps[i]) + mainMonitorIndex = monitorCoords.size(); + + GDHandle hGDevice; + + if (clipToWorkArea + && DMGetGDeviceByDisplayID ((DisplayIDType) disps[i], &hGDevice, false) == noErr) + { + Rect rect; + GetAvailableWindowPositioningBounds (hGDevice, &rect); + + monitorCoords.add (Rectangle (rect.left, + rect.top, + rect.right - rect.left, + rect.bottom - rect.top)); + } + else + { + const CGRect r (CGDisplayBounds (disps[i])); + + monitorCoords.add (Rectangle (r.origin.x, + r.origin.y, + r.size.width, + r.size.height)); + } + } + } + + // make sure the first in the list is the main monitor + if (mainMonitorIndex > 0) + monitorCoords.swap (mainMonitorIndex, 0); + + jassert (monitorCoords.size() > 0); + + //xxx need to register for display change callbacks +} + +//============================================================================== +struct CursorWrapper +{ + Cursor* cursor; + ThemeCursor themeCursor; +}; + +void* juce_createMouseCursorFromImage (const Image& image, int hotspotX, int hotspotY) throw() +{ + const int maxW = 16; + const int maxH = 16; + + const Image* im = ℑ + Image* newIm = 0; + + if (image.getWidth() > maxW || image.getHeight() > maxH) + { + im = newIm = image.createCopy (maxW, maxH); + + hotspotX = (hotspotX * maxW) / image.getWidth(); + hotspotY = (hotspotY * maxH) / image.getHeight(); + } + + Cursor* const c = new Cursor(); + c->hotSpot.h = hotspotX; + c->hotSpot.v = hotspotY; + + for (int y = 0; y < maxH; ++y) + { + c->data[y] = 0; + c->mask[y] = 0; + + for (int x = 0; x < maxW; ++x) + { + const Colour pixelColour (im->getPixelAt (15 - x, y)); + + if (pixelColour.getAlpha() > 0.5f) + { + c->mask[y] |= (1 << x); + + if (pixelColour.getBrightness() < 0.5f) + c->data[y] |= (1 << x); + } + } + + c->data[y] = CFSwapInt16BigToHost (c->data[y]); + c->mask[y] = CFSwapInt16BigToHost (c->mask[y]); + } + + if (newIm != 0) + delete newIm; + + CursorWrapper* const cw = new CursorWrapper(); + cw->cursor = c; + cw->themeCursor = kThemeArrowCursor; + return (void*) cw; +} + +static void* cursorFromData (const unsigned char* data, const int size, int hx, int hy) throw() +{ + Image* const im = ImageFileFormat::loadFrom ((const char*) data, size); + jassert (im != 0); + void* curs = juce_createMouseCursorFromImage (*im, hx, hy); + delete im; + return curs; +} + +const unsigned int kSpecialNoCursor = 'nocr'; + +void* juce_createStandardMouseCursor (MouseCursor::StandardCursorType type) throw() +{ + ThemeCursor id = kThemeArrowCursor; + + switch (type) + { + case MouseCursor::NormalCursor: + id = kThemeArrowCursor; + break; + + case MouseCursor::NoCursor: + id = kSpecialNoCursor; + break; + + case MouseCursor::DraggingHandCursor: + { + static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,2,0,0,0,0,255,255,255,0, + 0,0,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, + 16,0,0,2,52,148,47,0,200,185,16,130,90,12,74,139,107,84,123,39, + 132,117,151,116,132,146,248,60,209,138,98,22,203,114,34,236,37,52,77,217, + 247,154,191,119,110,240,193,128,193,95,163,56,60,234,98,135,2,0,59 }; + const int cursDataSize = 99; + + return cursorFromData (cursData, cursDataSize, 8, 8); + } + break; + + case MouseCursor::CopyingCursor: + id = kThemeCopyArrowCursor; + break; + + case MouseCursor::WaitCursor: + id = kThemeWatchCursor; + break; + + case MouseCursor::IBeamCursor: + id = kThemeIBeamCursor; + break; + + case MouseCursor::PointingHandCursor: + id = kThemePointingHandCursor; + break; + + case MouseCursor::LeftRightResizeCursor: + case MouseCursor::LeftEdgeResizeCursor: + case MouseCursor::RightEdgeResizeCursor: + { + static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,0,0,255,255,255,0,0,0,255, + 255,255,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, + 16,0,0,2,38,148,143,169,203,237,15,19,0,106,202,64,111,22,32,224, + 9,78,30,213,121,230,121,146,99,8,142,71,183,189,152,20,27,86,132,231, + 58,83,0,0,59 }; + const int cursDataSize = 85; + + return cursorFromData (cursData, cursDataSize, 8, 8); + } + + case MouseCursor::UpDownResizeCursor: + case MouseCursor::TopEdgeResizeCursor: + case MouseCursor::BottomEdgeResizeCursor: + { + static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,0,0,255,255,255,0,0,0,255, + 255,255,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, + 16,0,0,2,38,148,111,128,187,16,202,90,152,48,10,55,169,189,192,245, + 106,121,27,34,142,201,99,158,224,86,154,109,216,61,29,155,105,180,61,190, + 121,84,0,0,59 }; + const int cursDataSize = 85; + + return cursorFromData (cursData, cursDataSize, 8, 8); + } + + case MouseCursor::TopLeftCornerResizeCursor: + case MouseCursor::BottomRightCornerResizeCursor: + { + static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,0,0,255,255,255,0,0,0,255, + 255,255,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, + 16,0,0,2,43,132,15,162,187,16,255,18,99,14,202,217,44,158,213,221, + 237,9,225,38,94,35,73,5,31,42,170,108,106,174,112,43,195,209,91,185, + 104,174,131,208,77,66,28,10,0,59 }; + const int cursDataSize = 90; + + return cursorFromData (cursData, cursDataSize, 8, 8); + } + + case MouseCursor::TopRightCornerResizeCursor: + case MouseCursor::BottomLeftCornerResizeCursor: + { + static const unsigned char cursData[] = {71,73,70,56,57,97,16,0,16,0,145,0,0,255,255,255,0,0,0,255, + 255,255,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, + 16,0,0,2,45,148,127,160,11,232,16,98,108,14,65,73,107,194,122,223, + 92,65,141,216,145,134,162,153,221,25,128,73,166,62,173,16,203,237,188,94, + 120,46,237,105,239,123,48,80,157,2,0,59 }; + const int cursDataSize = 92; + + return cursorFromData (cursData, cursDataSize, 8, 8); + } + + case MouseCursor::UpDownLeftRightResizeCursor: + { + static const unsigned char cursData[] = {71,73,70,56,57,97,15,0,15,0,145,0,0,0,0,0,255,255,255,0, + 128,128,255,255,255,33,249,4,1,0,0,3,0,44,0,0,0,0,15,0, + 15,0,0,2,46,156,63,129,139,1,202,26,152,48,186,73,109,114,65,85, + 195,37,143,88,93,29,215,101,23,198,178,30,149,158,25,56,134,97,179,61, + 158,213,126,203,234,99,220,34,56,70,1,0,59,0,0 }; + const int cursDataSize = 93; + + return cursorFromData (cursData, cursDataSize, 7, 7); + } + + case MouseCursor::CrosshairCursor: + id = kThemeCrossCursor; + break; + } + + CursorWrapper* cw = new CursorWrapper(); + cw->cursor = 0; + cw->themeCursor = id; + + return (void*) cw; +} + +void juce_deleteMouseCursor (void* const cursorHandle, const bool isStandard) throw() +{ + CursorWrapper* const cw = (CursorWrapper*) cursorHandle; + + if (cw != 0) + { + delete cw->cursor; + delete cw; + } +} + +void MouseCursor::showInAllWindows() const throw() +{ + showInWindow (0); +} + +void MouseCursor::showInWindow (ComponentPeer*) const throw() +{ + const CursorWrapper* const cw = (CursorWrapper*) getHandle(); + + if (cw != 0) + { + static bool isCursorHidden = false; + static bool showingWaitCursor = false; + const bool shouldShowWaitCursor = (cw->themeCursor == kThemeWatchCursor); + const bool shouldHideCursor = (cw->themeCursor == kSpecialNoCursor); + + if (shouldShowWaitCursor != showingWaitCursor + && Process::isForegroundProcess()) + { + showingWaitCursor = shouldShowWaitCursor; + QDDisplayWaitCursor (shouldShowWaitCursor); + } + + if (shouldHideCursor != isCursorHidden) + { + isCursorHidden = shouldHideCursor; + + if (shouldHideCursor) + HideCursor(); + else + ShowCursor(); + } + + if (cw->cursor != 0) + SetCursor (cw->cursor); + else if (! (shouldShowWaitCursor || shouldHideCursor)) + SetThemeCursor (cw->themeCursor); + } +} + +//============================================================================== +Image* juce_createIconForFile (const File& file) +{ + return 0; +} + + +//============================================================================== +class MainMenuHandler; +static MainMenuHandler* mainMenu = 0; + +class MainMenuHandler : private MenuBarModelListener, + private DeletedAtShutdown +{ +public: + MainMenuHandler() throw() + : currentModel (0) + { + } + + ~MainMenuHandler() throw() + { + setMenu (0); + + jassert (mainMenu == this); + mainMenu = 0; + } + + void setMenu (MenuBarModel* const newMenuBarModel) throw() + { + if (currentModel != newMenuBarModel) + { + if (currentModel != 0) + currentModel->removeListener (this); + + currentModel = newMenuBarModel; + + if (currentModel != 0) + currentModel->addListener (this); + + menuBarItemsChanged (0); + } + } + + void menuBarItemsChanged (MenuBarModel*) + { + ClearMenuBar(); + + if (currentModel != 0) + { + int id = 1000; + const StringArray menuNames (currentModel->getMenuBarNames()); + + for (int i = 0; i < menuNames.size(); ++i) + { + const PopupMenu menu (currentModel->getMenuForIndex (i, menuNames [i])); + + MenuRef m = createMenu (menu, menuNames [i], id); + + InsertMenu (m, 0); + CFRelease (m); + } + } + } + + void menuCommandInvoked (MenuBarModel*, const ApplicationCommandTarget::InvocationInfo& info) + { + MenuRef menu = 0; + MenuItemIndex index = 0; + GetIndMenuItemWithCommandID (0, info.commandID, 1, &menu, &index); + + FlashMenuBar (GetMenuID (menu)); + FlashMenuBar (GetMenuID (menu)); + } + + void invoke (const int id, ApplicationCommandManager* 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) + { + MenuRef m = 0; + + if (CreateNewMenu (id++, kMenuAttrAutoDisable, &m) == noErr) + { + CFStringRef name = PlatformUtilities::juceStringToCFString (menuName); + SetMenuTitleWithCFString (m, name); + CFRelease (name); + + PopupMenu::MenuItemIterator iter (menu); + int topLevelIndex = 0; + + 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); + SetMenuItemHierarchicalMenu (m, index, sub); + CFRelease (sub); + } + else + { + AppendMenuItemTextWithCFString (m, text, flags, iter.itemId, &index); + + if (iter.isTicked) + CheckMenuItem (m, index, true); + + SetMenuItemProperty (m, index, 'juce', 'apcm', sizeof (void*), &iter.commandManager); + SetMenuItemProperty (m, index, 'juce', 'topi', sizeof (int), &topLevelIndex); + + if (iter.commandManager != 0) + { + const Array keyPresses (iter.commandManager->getKeyMappings() + ->getKeyPressesAssignedToCommand (iter.itemId)); + + if (keyPresses.size() > 0) + { + const KeyPress& kp = keyPresses.getUnchecked(0); + int mods = 0; + + if (kp.getModifiers().isShiftDown()) + mods |= kMenuShiftModifier; + if (kp.getModifiers().isCtrlDown()) + mods |= kMenuControlModifier; + if (kp.getModifiers().isAltDown()) + mods |= kMenuOptionModifier; + if (! kp.getModifiers().isCommandDown()) + mods |= kMenuNoCommandModifier; + + tchar keyCode = (tchar) kp.getKeyCode(); + + if (kp.getKeyCode() >= KeyPress::numberPad0 + && kp.getKeyCode() <= KeyPress::numberPad9) + { + keyCode = (tchar) ((T('0') - KeyPress::numberPad0) + kp.getKeyCode()); + } + + SetMenuItemCommandKey (m, index, true, 255); + + if (CharacterFunctions::isLetterOrDigit (keyCode) + || CharacterFunctions::indexOfChar (T(",.;/\\'[]=-+_<>?{}\":"), keyCode, false) >= 0) + { + SetMenuItemModifiers (m, index, mods); + SetMenuItemCommandKey (m, index, false, CharacterFunctions::toUpperCase (keyCode)); + } + else + { + const SInt16 glyph = getGlyphForKeyCode (kp.getKeyCode()); + + if (glyph != 0) + { + SetMenuItemModifiers (m, index, mods); + SetMenuItemKeyGlyph (m, index, glyph); + } + } + + // if we set the key glyph to be a text char, and enable virtual + // key triggering, it stops the menu automatically triggering the callback + ChangeMenuItemAttributes (m, index, kMenuItemAttrUseVirtualKey, 0); + } + } + } + + CFRelease (text); + ++topLevelIndex; + } + } + + return m; + } + + static SInt16 getGlyphForKeyCode (const int keyCode) throw() + { + if (keyCode == KeyPress::spaceKey) + return kMenuSpaceGlyph; + else if (keyCode == KeyPress::returnKey) + return kMenuReturnGlyph; + else if (keyCode == KeyPress::escapeKey) + return kMenuEscapeGlyph; + else if (keyCode == KeyPress::backspaceKey) + return kMenuDeleteLeftGlyph; + else if (keyCode == KeyPress::leftKey) + return kMenuLeftArrowGlyph; + else if (keyCode == KeyPress::rightKey) + return kMenuRightArrowGlyph; + else if (keyCode == KeyPress::upKey) + return kMenuUpArrowGlyph; + else if (keyCode == KeyPress::downKey) + return kMenuDownArrowGlyph; + else if (keyCode == KeyPress::pageUpKey) + return kMenuPageUpGlyph; + else if (keyCode == KeyPress::pageDownKey) + return kMenuPageDownGlyph; + else if (keyCode == KeyPress::endKey) + return kMenuSoutheastArrowGlyph; + else if (keyCode == KeyPress::homeKey) + return kMenuNorthwestArrowGlyph; + else if (keyCode == KeyPress::deleteKey) + return kMenuDeleteRightGlyph; + else if (keyCode == KeyPress::tabKey) + return kMenuTabRightGlyph; + else if (keyCode == KeyPress::F1Key) + return kMenuF1Glyph; + else if (keyCode == KeyPress::F2Key) + return kMenuF2Glyph; + else if (keyCode == KeyPress::F3Key) + return kMenuF3Glyph; + else if (keyCode == KeyPress::F4Key) + return kMenuF4Glyph; + else if (keyCode == KeyPress::F5Key) + return kMenuF5Glyph; + else if (keyCode == KeyPress::F6Key) + return kMenuF6Glyph; + else if (keyCode == KeyPress::F7Key) + return kMenuF7Glyph; + else if (keyCode == KeyPress::F8Key) + return kMenuF8Glyph; + else if (keyCode == KeyPress::F9Key) + return kMenuF9Glyph; + else if (keyCode == KeyPress::F10Key) + return kMenuF10Glyph; + else if (keyCode == KeyPress::F11Key) + return kMenuF11Glyph; + else if (keyCode == KeyPress::F12Key) + return kMenuF12Glyph; + else if (keyCode == KeyPress::F13Key) + return kMenuF13Glyph; + else if (keyCode == KeyPress::F14Key) + return kMenuF14Glyph; + else if (keyCode == KeyPress::F15Key) + return kMenuF15Glyph; + + return 0; + } +}; + +void MenuBarModel::setMacMainMenu (MenuBarModel* newMenuBarModel) throw() +{ + if (getMacMainMenu() != newMenuBarModel) + { + if (newMenuBarModel == 0) + { + delete mainMenu; + jassert (mainMenu == 0); // should be zeroed in the destructor + } + else + { + if (mainMenu == 0) + mainMenu = new MainMenuHandler(); + + mainMenu->setMenu (newMenuBarModel); + } + } +} + +MenuBarModel* MenuBarModel::getMacMainMenu() throw() +{ + return mainMenu != 0 ? mainMenu->currentModel : 0; +} + +// these functions are called externally from the message handling code +void juce_MainMenuAboutToBeUsed() +{ + // force an update of the items just before the menu appears.. + if (mainMenu != 0) + mainMenu->menuBarItemsChanged (0); +} + +void juce_InvokeMainMenuCommand (const HICommand& command) +{ + if (mainMenu != 0) + { + ApplicationCommandManager* commandManager = 0; + int topLevelIndex = 0; + + if (GetMenuItemProperty (command.menu.menuRef, command.menu.menuItemIndex, + 'juce', 'apcm', sizeof (commandManager), 0, &commandManager) == noErr + && GetMenuItemProperty (command.menu.menuRef, command.menu.menuItemIndex, + 'juce', 'topi', sizeof (topLevelIndex), 0, &topLevelIndex) == noErr) + { + mainMenu->invoke (command.commandID, commandManager, topLevelIndex); + } + } +} + +//============================================================================== +void PlatformUtilities::beep() +{ + SysBeep (30); +} + +//============================================================================== +void SystemClipboard::copyTextToClipboard (const String& text) +{ + ClearCurrentScrap(); + ScrapRef ref; + GetCurrentScrap (&ref); + + const int len = text.length(); + const int numBytes = sizeof (UniChar) * len; + UniChar* const temp = (UniChar*) juce_calloc (numBytes); + + for (int i = 0; i < len; ++i) + temp[i] = (UniChar) text[i]; + + PutScrapFlavor (ref, + kScrapFlavorTypeUnicode, + kScrapFlavorMaskNone, + numBytes, + temp); + + juce_free (temp); +} + +const String SystemClipboard::getTextFromClipboard() +{ + String result; + + ScrapRef ref; + GetCurrentScrap (&ref); + Size size = 0; + + if (GetScrapFlavorSize (ref, kScrapFlavorTypeUnicode, &size) == noErr + && size > 0) + { + void* const data = juce_calloc (size + 8); + + if (GetScrapFlavorData (ref, kScrapFlavorTypeUnicode, &size, data) == noErr) + { + result = PlatformUtilities::convertUTF16ToString ((UniChar*) data); + } + + juce_free (data); + } + + return result; +} + + +//============================================================================== +bool AlertWindow::showNativeDialogBox (const String& title, + const String& bodyText, + bool isOkCancel) +{ + Str255 tit, txt; + PlatformUtilities::copyToStr255 (tit, title); + PlatformUtilities::copyToStr255 (txt, bodyText); + + AlertStdAlertParamRec ar; + ar.movable = true; + ar.helpButton = false; + ar.filterProc = 0; + ar.defaultText = (const unsigned char*)-1; + ar.cancelText = (const unsigned char*)((isOkCancel) ? -1 : 0); + ar.otherText = 0; + ar.defaultButton = kAlertStdAlertOKButton; + ar.cancelButton = 0; + ar.position = kWindowDefaultPosition; + + SInt16 result; + StandardAlert (kAlertNoteAlert, tit, txt, &ar, &result); + return result == kAlertStdAlertOKButton; +} + +//============================================================================== +const int KeyPress::spaceKey = ' '; +const int KeyPress::returnKey = kReturnCharCode; +const int KeyPress::escapeKey = kEscapeCharCode; +const int KeyPress::backspaceKey = kBackspaceCharCode; +const int KeyPress::leftKey = kLeftArrowCharCode; +const int KeyPress::rightKey = kRightArrowCharCode; +const int KeyPress::upKey = kUpArrowCharCode; +const int KeyPress::downKey = kDownArrowCharCode; +const int KeyPress::pageUpKey = kPageUpCharCode; +const int KeyPress::pageDownKey = kPageDownCharCode; +const int KeyPress::endKey = kEndCharCode; +const int KeyPress::homeKey = kHomeCharCode; +const int KeyPress::deleteKey = kDeleteCharCode; +const int KeyPress::insertKey = -1; +const int KeyPress::tabKey = kTabCharCode; +const int KeyPress::F1Key = 0x10110; +const int KeyPress::F2Key = 0x10111; +const int KeyPress::F3Key = 0x10112; +const int KeyPress::F4Key = 0x10113; +const int KeyPress::F5Key = 0x10114; +const int KeyPress::F6Key = 0x10115; +const int KeyPress::F7Key = 0x10116; +const int KeyPress::F8Key = 0x10117; +const int KeyPress::F9Key = 0x10118; +const int KeyPress::F10Key = 0x10119; +const int KeyPress::F11Key = 0x1011a; +const int KeyPress::F12Key = 0x1011b; +const int KeyPress::F13Key = 0x1011c; +const int KeyPress::F14Key = 0x1011d; +const int KeyPress::F15Key = 0x1011e; +const int KeyPress::F16Key = 0x1011f; +const int KeyPress::numberPad0 = 0x30020; +const int KeyPress::numberPad1 = 0x30021; +const int KeyPress::numberPad2 = 0x30022; +const int KeyPress::numberPad3 = 0x30023; +const int KeyPress::numberPad4 = 0x30024; +const int KeyPress::numberPad5 = 0x30025; +const int KeyPress::numberPad6 = 0x30026; +const int KeyPress::numberPad7 = 0x30027; +const int KeyPress::numberPad8 = 0x30028; +const int KeyPress::numberPad9 = 0x30029; +const int KeyPress::numberPadAdd = 0x3002a; +const int KeyPress::numberPadSubtract = 0x3002b; +const int KeyPress::numberPadMultiply = 0x3002c; +const int KeyPress::numberPadDivide = 0x3002d; +const int KeyPress::numberPadSeparator = 0x3002e; +const int KeyPress::numberPadDecimalPoint = 0x3002f; +const int KeyPress::numberPadEquals = 0x30030; +const int KeyPress::numberPadDelete = 0x30031; +const int KeyPress::playKey = 0x30000; +const int KeyPress::stopKey = 0x30001; +const int KeyPress::fastForwardKey = 0x30002; +const int KeyPress::rewindKey = 0x30003; + +//============================================================================== +#if JUCE_OPENGL + +struct OpenGLContextInfo +{ + AGLContext renderContext; +}; + +void* juce_createOpenGLContext (OpenGLComponent* component, void* sharedContext) +{ + jassert (component != 0); + + HIViewComponentPeer* const peer = dynamic_cast (component->getTopLevelComponent()->getPeer()); + + if (peer == 0) + return 0; + + OpenGLContextInfo* const oc = new OpenGLContextInfo(); + + GLint attrib[] = { AGL_RGBA, AGL_DOUBLEBUFFER, + AGL_RED_SIZE, 8, + AGL_ALPHA_SIZE, 8, + AGL_DEPTH_SIZE, 24, + AGL_CLOSEST_POLICY, AGL_NO_RECOVERY, + AGL_SAMPLE_BUFFERS_ARB, 1, + AGL_SAMPLES_ARB, 4, + AGL_NONE }; + + oc->renderContext = aglCreateContext (aglChoosePixelFormat (0, 0, attrib), + (sharedContext != 0) ? ((OpenGLContextInfo*) sharedContext)->renderContext + : 0); + + aglSetDrawable (oc->renderContext, + GetWindowPort (peer->windowRef)); + + return oc; +} + +void juce_updateOpenGLWindowPos (void* context, Component* owner, Component* topComp) +{ + jassert (context != 0); + OpenGLContextInfo* const oc = (OpenGLContextInfo*) context; + + GLint bufferRect[4]; + + bufferRect[0] = owner->getScreenX() - topComp->getScreenX(); + bufferRect[1] = topComp->getHeight() - (owner->getHeight() + owner->getScreenY() - topComp->getScreenY()); + bufferRect[2] = owner->getWidth(); + bufferRect[3] = owner->getHeight(); + + aglSetInteger (oc->renderContext, AGL_BUFFER_RECT, bufferRect); + aglEnable (oc->renderContext, AGL_BUFFER_RECT); +} + +void juce_deleteOpenGLContext (void* context) +{ + OpenGLContextInfo* const oc = (OpenGLContextInfo*) context; + + aglDestroyContext (oc->renderContext); + + delete oc; +} + +bool juce_makeOpenGLContextCurrent (void* context) +{ + OpenGLContextInfo* const oc = (OpenGLContextInfo*) context; + + return aglSetCurrentContext ((oc != 0) ? oc->renderContext : 0); +} + +void juce_swapOpenGLBuffers (void* context) +{ + OpenGLContextInfo* const oc = (OpenGLContextInfo*) context; + + if (oc != 0) + aglSwapBuffers (oc->renderContext); +} + +void juce_repaintOpenGLWindow (void* context) +{ +} + +#endif + +END_JUCE_NAMESPACE diff --git a/build/win32/platform_specific_code/juce_win32_SystemStats.cpp b/build/win32/platform_specific_code/juce_win32_SystemStats.cpp index e47bc59076..df662cc567 100644 --- a/build/win32/platform_specific_code/juce_win32_SystemStats.cpp +++ b/build/win32/platform_specific_code/juce_win32_SystemStats.cpp @@ -61,7 +61,6 @@ BEGIN_JUCE_NAMESPACE #include "../../../src/juce_core/io/files/juce_File.h" #include "../../../src/juce_core/basics/juce_SystemStats.h" -#include "juce_win32_DynamicLibraryLoader.h" extern void juce_updateMultiMonitorInfo() throw(); diff --git a/build/win32/platform_specific_code/juce_win32_Threads.cpp b/build/win32/platform_specific_code/juce_win32_Threads.cpp index 1d159f4e10..1e84fff9f2 100644 --- a/build/win32/platform_specific_code/juce_win32_Threads.cpp +++ b/build/win32/platform_specific_code/juce_win32_Threads.cpp @@ -208,7 +208,7 @@ void juce_setThreadPriority (void* threadHandle, int priority) throw() SetThreadPriority (threadHandle, pri); } -void Thread::setCurrentThreadAffinityMask (const uint32 affinityMask) +void Thread::setCurrentThreadAffinityMask (const uint32 affinityMask) throw() { SetThreadAffinityMask (GetCurrentThread(), affinityMask); } diff --git a/extras/audio plugins/demo/src/DemoEditorComponent.cpp b/extras/audio plugins/demo/src/DemoEditorComponent.cpp index 0334a2035e..903847c451 100644 --- a/extras/audio plugins/demo/src/DemoEditorComponent.cpp +++ b/extras/audio plugins/demo/src/DemoEditorComponent.cpp @@ -1,194 +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. - - ============================================================================== -*/ - -#include "DemoEditorComponent.h" - -//============================================================================== -// quick-and-dirty function to format a timecode string -static const String timeToTimecodeString (const double seconds) -{ - const double absSecs = fabs (seconds); - const tchar* const sign = (seconds < 0) ? T("-") : T(""); - - const int hours = (int) (absSecs / (60.0 * 60.0)); - const int mins = ((int) (absSecs / 60.0)) % 60; - const int secs = ((int) absSecs) % 60; - - return String::formatted (T("%s%02d:%02d:%02d:%03d"), - sign, hours, mins, secs, - roundDoubleToInt (absSecs * 1000) % 1000); -} - -// quick-and-dirty function to format a bars/beats string -static const String ppqToBarsBeatsString (const double ppq, - const double lastBarPPQ, - const int numerator, - const int denominator) -{ - if (numerator == 0 || denominator == 0) - return T("1|1|0"); - - const int ppqPerBar = (numerator * 4 / denominator); - const double beats = (fmod (ppq, ppqPerBar) / ppqPerBar) * numerator; - - const int bar = ((int) ppq) / ppqPerBar + 1; - const int beat = ((int) beats) + 1; - const int ticks = ((int) (fmod (beats, 1.0) * 960.0)); - - String s; - s << bar << T('|') << beat << T('|') << ticks; - return s; -} - - -//============================================================================== -DemoEditorComponent::DemoEditorComponent (DemoJuceFilter* const ownerFilter) - : AudioFilterEditor (ownerFilter) -{ - // create our gain slider.. - addAndMakeVisible (gainSlider = new Slider (T("gain"))); - gainSlider->addListener (this); - gainSlider->setRange (0.0, 1.0, 0.01); - gainSlider->setTooltip (T("changes the volume of the audio that runs through the plugin..")); - - // get the gain parameter from the filter and use it to set up our slider - gainSlider->setValue (ownerFilter->getParameter (0), false); - - // create and add the midi keyboard component.. - addAndMakeVisible (midiKeyboard - = new MidiKeyboardComponent (ownerFilter->keyboardState, - MidiKeyboardComponent::horizontalKeyboard)); - - // add a label that will display the current timecode and status.. - addAndMakeVisible (infoLabel = new Label (String::empty, String::empty)); - - // add the triangular resizer component for the bottom-right of the UI - addAndMakeVisible (resizer = new ResizableCornerComponent (this, &resizeLimits)); - resizeLimits.setSizeLimits (150, 150, 800, 300); - - // set our component's initial size to be the last one that was stored in the filter's settings - setSize (ownerFilter->lastUIWidth, - ownerFilter->lastUIHeight); - - // register ourselves with the filter - it will use its ChangeBroadcaster base - // class to tell us when something has changed, and this will call our changeListenerCallback() - // method. - ownerFilter->addChangeListener (this); -} - -DemoEditorComponent::~DemoEditorComponent() -{ - getFilter()->removeChangeListener (this); - - deleteAllChildren(); -} - -//============================================================================== -void DemoEditorComponent::paint (Graphics& g) -{ - // just clear the window - g.fillAll (Colour::greyLevel (0.9f)); -} - -void DemoEditorComponent::resized() -{ - gainSlider->setBounds (10, 10, 200, 22); - infoLabel->setBounds (10, 35, 450, 20); - - const int keyboardHeight = 70; - midiKeyboard->setBounds (4, getHeight() - keyboardHeight - 4, - getWidth() - 8, keyboardHeight); - - resizer->setBounds (getWidth() - 16, getHeight() - 16, 16, 16); - - // if we've been resized, tell the filter so that it can store the new size - // in its settings - getFilter()->lastUIWidth = getWidth(); - getFilter()->lastUIHeight = getHeight(); -} - -//============================================================================== -void DemoEditorComponent::changeListenerCallback (void* source) -{ - // this is the filter telling us that it's changed, so we'll update our - // display of the time, midi message, etc. - updateParametersFromFilter(); -} - -void DemoEditorComponent::sliderValueChanged (Slider*) -{ - getFilter()->setParameterNotifyingHost (0, (float) gainSlider->getValue()); -} - -//============================================================================== -void DemoEditorComponent::updateParametersFromFilter() -{ - DemoJuceFilter* const filter = getFilter(); - - // we use this lock to make sure the processBlock() method isn't writing to the - // lastMidiMessage variable while we're trying to read it, but be extra-careful to - // only hold the lock for a minimum amount of time.. - filter->getCallbackLock().enter(); - - // take a local copy of the info we need while we've got the lock.. - const AudioFilterBase::CurrentPositionInfo positionInfo (filter->lastPosInfo); - const float newGain = filter->getParameter (0); - - // ..release the lock ASAP - filter->getCallbackLock().exit(); - - - // ..and after releasing the lock, we're free to do the time-consuming UI stuff.. - String infoText; - infoText << String (positionInfo.bpm, 2) << T(" bpm, ") - << positionInfo.timeSigNumerator << T("/") << positionInfo.timeSigDenominator - << T(" - ") << timeToTimecodeString (positionInfo.timeInSeconds) - << T(" - ") << ppqToBarsBeatsString (positionInfo.ppqPosition, - positionInfo.ppqPositionOfLastBarStart, - positionInfo.timeSigNumerator, - positionInfo.timeSigDenominator); - - if (positionInfo.isPlaying) - infoText << T(" (playing)"); - - infoLabel->setText (infoText, false); - - /* Update our slider. - - (note that it's important here to tell the slider not to send a change - message, because that would cause it to call the filter with a parameter - change message again, and the values would drift out. - */ - gainSlider->setValue (newGain, false); - - setSize (filter->lastUIWidth, - filter->lastUIHeight); -} +/* + ============================================================================== + + 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 "DemoEditorComponent.h" + +//============================================================================== +// quick-and-dirty function to format a timecode string +static const String timeToTimecodeString (const double seconds) +{ + const double absSecs = fabs (seconds); + const tchar* const sign = (seconds < 0) ? T("-") : T(""); + + const int hours = (int) (absSecs / (60.0 * 60.0)); + const int mins = ((int) (absSecs / 60.0)) % 60; + const int secs = ((int) absSecs) % 60; + + return String::formatted (T("%s%02d:%02d:%02d:%03d"), + sign, hours, mins, secs, + roundDoubleToInt (absSecs * 1000) % 1000); +} + +// quick-and-dirty function to format a bars/beats string +static const String ppqToBarsBeatsString (const double ppq, + const double lastBarPPQ, + const int numerator, + const int denominator) +{ + if (numerator == 0 || denominator == 0) + return T("1|1|0"); + + const int ppqPerBar = (numerator * 4 / denominator); + const double beats = (fmod (ppq, ppqPerBar) / ppqPerBar) * numerator; + + const int bar = ((int) ppq) / ppqPerBar + 1; + const int beat = ((int) beats) + 1; + const int ticks = ((int) (fmod (beats, 1.0) * 960.0)); + + String s; + s << bar << T('|') << beat << T('|') << ticks; + return s; +} + + +//============================================================================== +DemoEditorComponent::DemoEditorComponent (DemoJuceFilter* const ownerFilter) + : AudioFilterEditor (ownerFilter) +{ + // create our gain slider.. + addAndMakeVisible (gainSlider = new Slider (T("gain"))); + gainSlider->addListener (this); + gainSlider->setRange (0.0, 1.0, 0.01); + gainSlider->setTooltip (T("changes the volume of the audio that runs through the plugin..")); + + // get the gain parameter from the filter and use it to set up our slider + gainSlider->setValue (ownerFilter->getParameter (0), false); + + // create and add the midi keyboard component.. + addAndMakeVisible (midiKeyboard + = new MidiKeyboardComponent (ownerFilter->keyboardState, + MidiKeyboardComponent::horizontalKeyboard)); + + // add a label that will display the current timecode and status.. + addAndMakeVisible (infoLabel = new Label (String::empty, String::empty)); + + // add the triangular resizer component for the bottom-right of the UI + addAndMakeVisible (resizer = new ResizableCornerComponent (this, &resizeLimits)); + resizeLimits.setSizeLimits (150, 150, 800, 300); + + // set our component's initial size to be the last one that was stored in the filter's settings + setSize (ownerFilter->lastUIWidth, + ownerFilter->lastUIHeight); + + // register ourselves with the filter - it will use its ChangeBroadcaster base + // class to tell us when something has changed, and this will call our changeListenerCallback() + // method. + ownerFilter->addChangeListener (this); +} + +DemoEditorComponent::~DemoEditorComponent() +{ + getFilter()->removeChangeListener (this); + + deleteAllChildren(); +} + +//============================================================================== +void DemoEditorComponent::paint (Graphics& g) +{ + // just clear the window + g.fillAll (Colour::greyLevel (0.9f)); +} + +void DemoEditorComponent::resized() +{ + gainSlider->setBounds (10, 10, 200, 22); + infoLabel->setBounds (10, 35, 450, 20); + + const int keyboardHeight = 70; + midiKeyboard->setBounds (4, getHeight() - keyboardHeight - 4, + getWidth() - 8, keyboardHeight); + + resizer->setBounds (getWidth() - 16, getHeight() - 16, 16, 16); + + // if we've been resized, tell the filter so that it can store the new size + // in its settings + getFilter()->lastUIWidth = getWidth(); + getFilter()->lastUIHeight = getHeight(); +} + +//============================================================================== +void DemoEditorComponent::changeListenerCallback (void* source) +{ + // this is the filter telling us that it's changed, so we'll update our + // display of the time, midi message, etc. + updateParametersFromFilter(); +} + +void DemoEditorComponent::sliderValueChanged (Slider*) +{ + getFilter()->setParameterNotifyingHost (0, (float) gainSlider->getValue()); +} + +//============================================================================== +void DemoEditorComponent::updateParametersFromFilter() +{ + DemoJuceFilter* const filter = getFilter(); + + // we use this lock to make sure the processBlock() method isn't writing to the + // lastMidiMessage variable while we're trying to read it, but be extra-careful to + // only hold the lock for a minimum amount of time.. + filter->getCallbackLock().enter(); + + // take a local copy of the info we need while we've got the lock.. + const AudioFilterBase::CurrentPositionInfo positionInfo (filter->lastPosInfo); + const float newGain = filter->getParameter (0); + + // ..release the lock ASAP + filter->getCallbackLock().exit(); + + + // ..and after releasing the lock, we're free to do the time-consuming UI stuff.. + String infoText; + infoText << String (positionInfo.bpm, 2) << T(" bpm, ") + << positionInfo.timeSigNumerator << T("/") << positionInfo.timeSigDenominator + << T(" - ") << timeToTimecodeString (positionInfo.timeInSeconds) + << T(" - ") << ppqToBarsBeatsString (positionInfo.ppqPosition, + positionInfo.ppqPositionOfLastBarStart, + positionInfo.timeSigNumerator, + positionInfo.timeSigDenominator); + + if (positionInfo.isPlaying) + infoText << T(" (playing)"); + + infoLabel->setText (infoText, false); + + /* Update our slider. + + (note that it's important here to tell the slider not to send a change + message, because that would cause it to call the filter with a parameter + change message again, and the values would drift out. + */ + gainSlider->setValue (newGain, false); + + setSize (filter->lastUIWidth, + filter->lastUIHeight); +} diff --git a/extras/audio plugins/wrapper/formats/AudioUnit/juce_AudioUnitWrapper.cpp b/extras/audio plugins/wrapper/formats/AudioUnit/juce_AudioUnitWrapper.cpp index 047b5cb770..cb7f9cc0e6 100644 --- a/extras/audio plugins/wrapper/formats/AudioUnit/juce_AudioUnitWrapper.cpp +++ b/extras/audio plugins/wrapper/formats/AudioUnit/juce_AudioUnitWrapper.cpp @@ -1,827 +1,827 @@ -/* - ============================================================================== - - 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 -#include "AUMIDIEffectBase.h" -#include "AUCarbonViewBase.h" -#include "../../juce_AudioFilterBase.h" -#include "../../juce_IncludeCharacteristics.h" - - -//============================================================================== -#define juceFilterObjectPropertyID 0x1a45ffe9 -static VoidArray activePlugins; - -static const short channelConfigs[][2] = { JucePlugin_PreferredChannelConfigurations }; -static const int numChannelConfigs = numElementsInArray (channelConfigs); - -BEGIN_JUCE_NAMESPACE -extern void juce_setCurrentExecutableFileNameFromBundleId (const String& bundleId) throw(); -END_JUCE_NAMESPACE - - -//============================================================================== -class JuceAU : public AUMIDIEffectBase, - public AudioFilterBase::FilterNativeCallbacks -{ -public: - //============================================================================== - JuceAU (AudioUnit component) - : AUMIDIEffectBase (component), - juceFilter (0), - bufferSpace (2, 16), - prepared (false) - { - CreateElements(); - - if (activePlugins.size() == 0) - { - initialiseJuce_GUI(); - -#ifdef JucePlugin_CFBundleIdentifier - juce_setCurrentExecutableFileNameFromBundleId (JucePlugin_CFBundleIdentifier); -#endif - - MessageManager::getInstance()->setTimeBeforeShowingWaitCursor (0); - } - - juceFilter = createPluginFilter(); - juceFilter->initialiseInternal (this); - - jassert (juceFilter != 0); - Globals()->UseIndexedParameters (juceFilter->getNumParameters()); - - activePlugins.add (this); - } - - ~JuceAU() - { - delete juceFilter; - juceFilter = 0; - - jassert (activePlugins.contains (this)); - activePlugins.removeValue (this); - - if (activePlugins.size() == 0) - shutdownJuce_GUI(); - } - - //============================================================================== - ComponentResult GetPropertyInfo (AudioUnitPropertyID inID, - AudioUnitScope inScope, - AudioUnitElement inElement, - UInt32& outDataSize, - Boolean& outWritable) - { - if (inScope == kAudioUnitScope_Global) - { - if (inID == juceFilterObjectPropertyID) - { - outWritable = false; - outDataSize = sizeof (void*); - return noErr; - } - } - - return AUMIDIEffectBase::GetPropertyInfo (inID, inScope, inElement, outDataSize, outWritable); - } - - ComponentResult GetProperty (AudioUnitPropertyID inID, - AudioUnitScope inScope, - AudioUnitElement inElement, - void* outData) - { - if (inScope == kAudioUnitScope_Global) - { - if (inID == juceFilterObjectPropertyID) - { - *((void**) outData) = (void*) juceFilter; - return noErr; - } - } - - return AUMIDIEffectBase::GetProperty (inID, inScope, inElement, outData); - } - - ComponentResult SaveState (CFPropertyListRef* outData) - { - ComponentResult err = AUMIDIEffectBase::SaveState (outData); - - if (err != noErr) - return err; - - jassert (CFGetTypeID (*outData) == CFDictionaryGetTypeID()); - - CFMutableDictionaryRef dict = (CFMutableDictionaryRef) *outData; - - if (juceFilter != 0) - { - JUCE_NAMESPACE::MemoryBlock state; - juceFilter->getStateInformation (state); - - if (state.getSize() > 0) - { - CFDataRef ourState = CFDataCreate (kCFAllocatorDefault, (const uint8*) state, state.getSize()); - CFDictionarySetValue (dict, CFSTR("jucePluginState"), ourState); - CFRelease (ourState); - } - } - - return noErr; - } - - ComponentResult RestoreState (CFPropertyListRef inData) - { - ComponentResult err = AUMIDIEffectBase::RestoreState (inData); - - if (err != noErr) - return err; - - if (juceFilter != 0) - { - CFDictionaryRef dict = (CFDictionaryRef) inData; - CFDataRef data = 0; - - if (CFDictionaryGetValueIfPresent (dict, CFSTR("jucePluginState"), - (const void**) &data)) - { - if (data != 0) - { - const int numBytes = (int) CFDataGetLength (data); - const uint8* const rawBytes = CFDataGetBytePtr (data); - - if (numBytes > 0) - juceFilter->setStateInformation (rawBytes, numBytes); - } - } - } - - return noErr; - } - - UInt32 SupportedNumChannels (const AUChannelInfo** outInfo) - { - if (juceFilter == 0) - return 0; - - // You need to actually add some configurations to the JucePlugin_PreferredChannelConfigurations - // value in your JucePluginCharacteristics.h file.. - jassert (numChannelConfigs > 0); - - if (outInfo != 0) - { - for (int i = 0; i < numChannelConfigs; ++i) - { - channelInfo[i].inChannels = channelConfigs[i][0]; - channelInfo[i].outChannels = channelConfigs[i][1]; - - outInfo[i] = channelInfo + i; - } - } - - return numChannelConfigs; - } - - //============================================================================== - ComponentResult GetParameterInfo (AudioUnitScope inScope, - AudioUnitParameterID inParameterID, - AudioUnitParameterInfo& outParameterInfo) - { - if (inScope == kAudioUnitScope_Global && juceFilter != 0) - { - outParameterInfo.flags = kAudioUnitParameterFlag_IsWritable - | kAudioUnitParameterFlag_IsReadable - | kAudioUnitParameterFlag_HasCFNameString; - - outParameterInfo.name[0] = 0; - outParameterInfo.cfNameString = PlatformUtilities::juceStringToCFString (juceFilter->getParameterName ((int) inParameterID)); - outParameterInfo.minValue = 0.0f; - outParameterInfo.maxValue = 1.0f; - outParameterInfo.defaultValue = 0.0f; - outParameterInfo.unit = kAudioUnitParameterUnit_Generic; - - return noErr; - } - else - { - return kAudioUnitErr_InvalidParameter; - } - } - - ComponentResult GetParameter (AudioUnitParameterID inID, - AudioUnitScope inScope, - AudioUnitElement inElement, - Float32& outValue) - { - if (inScope == kAudioUnitScope_Global && juceFilter != 0) - { - outValue = juceFilter->getParameter ((int) inID); - return noErr; - } - - return AUBase::GetParameter (inID, inScope, inElement, outValue); - } - - ComponentResult SetParameter (AudioUnitParameterID inID, - AudioUnitScope inScope, - AudioUnitElement inElement, - Float32 inValue, - UInt32 inBufferOffsetInFrames) - { - if (inScope == kAudioUnitScope_Global && juceFilter != 0) - { - juceFilter->setParameter ((int) inID, inValue); - return noErr; - } - - return AUBase::SetParameter (inID, inScope, inElement, inValue, inBufferOffsetInFrames); - } - - //============================================================================== - ComponentResult Version() { return JucePlugin_VersionCode; } - - bool SupportsTail() { return true; } - Float64 GetTailTime() { return 0; } - - Float64 GetLatency() - { - jassert (GetSampleRate() > 0); - - if (GetSampleRate() <= 0) - return 0.0; - - return (JucePlugin_Latency) / GetSampleRate(); - } - - //============================================================================== - int GetNumCustomUIComponents() { return 1; } - - void GetUIComponentDescs (ComponentDescription* inDescArray) - { - inDescArray[0].componentType = kAudioUnitCarbonViewComponentType; - inDescArray[0].componentSubType = JucePlugin_AUSubType; - inDescArray[0].componentManufacturer = JucePlugin_AUManufacturerCode; - inDescArray[0].componentFlags = 0; - inDescArray[0].componentFlagsMask = 0; - } - - //============================================================================== - bool getCurrentPositionInfo (AudioFilterBase::CurrentPositionInfo& info) - { - info.timeSigNumerator = 0; - info.timeSigDenominator = 0; - info.timeInSeconds = 0; - info.editOriginTime = 0; - info.ppqPositionOfLastBarStart = 0; - info.isPlaying = false; - info.isRecording = false; - - switch (lastSMPTETime.mType) - { - case kSMPTETimeType24: - info.frameRate = AudioFilterBase::CurrentPositionInfo::fps24; - break; - - case kSMPTETimeType25: - info.frameRate = AudioFilterBase::CurrentPositionInfo::fps25; - break; - - case kSMPTETimeType30Drop: - info.frameRate = AudioFilterBase::CurrentPositionInfo::fps30drop; - break; - - case kSMPTETimeType30: - info.frameRate = AudioFilterBase::CurrentPositionInfo::fps30; - break; - - case kSMPTETimeType2997: - info.frameRate = AudioFilterBase::CurrentPositionInfo::fps2997; - break; - - case kSMPTETimeType2997Drop: - info.frameRate = AudioFilterBase::CurrentPositionInfo::fps2997drop; - break; - - //case kSMPTETimeType60: - //case kSMPTETimeType5994: - default: - info.frameRate = AudioFilterBase::CurrentPositionInfo::fpsUnknown; - break; - } - - if (CallHostBeatAndTempo (&info.ppqPosition, &info.bpm) != noErr) - { - info.ppqPosition = 0; - info.bpm = 0; - } - - UInt32 outDeltaSampleOffsetToNextBeat; - double outCurrentMeasureDownBeat; - float num; - UInt32 den; - - if (CallHostMusicalTimeLocation (&outDeltaSampleOffsetToNextBeat, &num, &den, - &outCurrentMeasureDownBeat) == noErr) - { - info.timeSigNumerator = (int) num; - info.timeSigDenominator = den; - info.ppqPositionOfLastBarStart = outCurrentMeasureDownBeat; - } - - double outCurrentSampleInTimeLine, outCycleStartBeat, outCycleEndBeat; - Boolean playing, playchanged, looping; - - if (CallHostTransportState (&playing, - &playchanged, - &outCurrentSampleInTimeLine, - &looping, - &outCycleStartBeat, - &outCycleEndBeat) == noErr) - { - info.isPlaying = playing; - info.timeInSeconds = outCurrentSampleInTimeLine / GetSampleRate(); - } - - return true; - } - - void informHostOfParameterChange (int index, float newValue) - { - if (juceFilter != 0) - { - juceFilter->setParameter (index, newValue); - - if (AUEventListenerNotify != 0) - { - AudioUnitEvent e; - e.mEventType = kAudioUnitEvent_ParameterValueChange; - e.mArgument.mParameter.mAudioUnit = GetComponentInstance(); - e.mArgument.mParameter.mParameterID = (AudioUnitParameterID) index; - e.mArgument.mParameter.mScope = kAudioUnitScope_Global; - e.mArgument.mParameter.mElement = 0; - AUEventListenerNotify (0, 0, &e); - } - } - } - - //============================================================================== - ComponentResult Initialize() - { - AUMIDIEffectBase::Initialize(); - prepareToPlay(); - return noErr; - } - - void Cleanup() - { - AUMIDIEffectBase::Cleanup(); - - if (juceFilter != 0) - juceFilter->releaseResources(); - - bufferSpace.setSize (2, 16); - midiEvents.clear(); - prepared = false; - } - - ComponentResult Reset (AudioUnitScope inScope, AudioUnitElement inElement) - { - if (! prepared) - prepareToPlay(); - - return AUMIDIEffectBase::Reset (inScope, inElement); - } - - void prepareToPlay() - { - if (juceFilter != 0) - { - juceFilter->numInputChannels = GetInput(0)->GetStreamFormat().mChannelsPerFrame; - juceFilter->numOutputChannels = GetOutput(0)->GetStreamFormat().mChannelsPerFrame; - - bufferSpace.setSize (juceFilter->numInputChannels + juceFilter->numOutputChannels, - GetMaxFramesPerSlice() + 32); - - juceFilter->prepareToPlay (GetSampleRate(), - GetMaxFramesPerSlice()); - - midiEvents.clear(); - - prepared = true; - } - } - - ComponentResult Render (AudioUnitRenderActionFlags &ioActionFlags, - const AudioTimeStamp& inTimeStamp, - UInt32 nFrames) - { - lastSMPTETime = inTimeStamp.mSMPTETime; - - return AUMIDIEffectBase::Render (ioActionFlags, inTimeStamp, nFrames); - } - - - OSStatus ProcessBufferLists (AudioUnitRenderActionFlags& ioActionFlags, - const AudioBufferList& inBuffer, - AudioBufferList& outBuffer, - UInt32 inFramesToProcess) - { - if (juceFilter != 0) - { - jassert (prepared); - - float* inChans [64]; - int numInChans = 0; - float* outChans [64]; - int numOutChans = 0; - int nextSpareBufferChan = 0; - bool needToReinterleave = false; - - unsigned int i; - for (i = 0; i < inBuffer.mNumberBuffers; ++i) - { - const AudioBuffer& buf = inBuffer.mBuffers[i]; - - if (buf.mNumberChannels == 1) - { - inChans [numInChans++] = (float*) buf.mData; - } - else - { - // need to de-interleave.. - for (unsigned int subChan = 0; subChan < buf.mNumberChannels; ++subChan) - { - float* dest = bufferSpace.getSampleData (nextSpareBufferChan++); - inChans [numInChans++] = dest; - - const float* src = ((const float*) buf.mData) + subChan; - - for (int j = inFramesToProcess; --j >= 0;) - { - *dest++ = *src; - src += buf.mNumberChannels; - } - } - } - - if (numInChans >= juceFilter->numInputChannels) - break; - } - - const int firstBufferedOutChan = nextSpareBufferChan; - - for (i = 0; i < outBuffer.mNumberBuffers; ++i) - { - AudioBuffer& buf = outBuffer.mBuffers[i]; - - if (buf.mNumberChannels == 1) - { - outChans [numOutChans++] = (float*) buf.mData; - } - else - { - needToReinterleave = true; - - for (unsigned int subChan = 0; subChan < buf.mNumberChannels; ++subChan) - { - float* dest = bufferSpace.getSampleData (nextSpareBufferChan++); - outChans [numOutChans++] = dest; - } - } - - if (numOutChans >= juceFilter->numOutputChannels) - break; - } - - const bool accumulate = false; - - { - const AudioSampleBuffer input (inChans, numInChans, inFramesToProcess); - AudioSampleBuffer output (outChans, numOutChans, inFramesToProcess); - - const ScopedLock sl (juceFilter->getCallbackLock()); - - if (juceFilter->suspended) - output.clear(); - else - juceFilter->processBlock (input, output, accumulate, midiEvents); - } - - if (! midiEvents.isEmpty()) - { -#if JucePlugin_ProducesMidiOutput - const uint8* midiEventData; - int midiEventSize, midiEventPosition; - MidiBuffer::Iterator i (midiEvents); - - while (i.getNextEvent (midiEventData, midiEventSize, midiEventPosition)) - { - jassert (midiEventPosition >= 0 && midiEventPosition < (int) inFramesToProcess); - - - - //xxx - } -#else - // if your plugin creates midi messages, you'll need to set - // the JucePlugin_ProducesMidiOutput macro to 1 in your - // JucePluginCharacteristics.h file - //jassert (midiEvents.getNumEvents() <= numMidiEventsComingIn); -#endif - midiEvents.clear(); - } - - if (needToReinterleave) - { - nextSpareBufferChan = firstBufferedOutChan; - - for (i = 0; i < outBuffer.mNumberBuffers; ++i) - { - AudioBuffer& buf = outBuffer.mBuffers[i]; - - if (buf.mNumberChannels > 1) - { - for (unsigned int subChan = 0; subChan < buf.mNumberChannels; ++subChan) - { - const float* src = bufferSpace.getSampleData (nextSpareBufferChan++); - float* dest = ((float*) buf.mData) + subChan; - - for (int j = inFramesToProcess; --j >= 0;) - { - *dest = *src++; - dest += buf.mNumberChannels; - } - } - } - } - } - -#if ! JucePlugin_SilenceInProducesSilenceOut - ioActionFlags &= ~kAudioUnitRenderAction_OutputIsSilence; -#endif - } - - return noErr; - } - -protected: - OSStatus HandleMidiEvent (UInt8 nStatus, - UInt8 inChannel, - UInt8 inData1, - UInt8 inData2, - long inStartFrame) - { -#if JucePlugin_WantsMidiInput - uint8 data [4]; - data[0] = nStatus | inChannel; - data[1] = inData1; - data[2] = inData2; - - midiEvents.addEvent (data, 3, inStartFrame); -#endif - - return noErr; - } - - //============================================================================== -private: - AudioFilterBase* juceFilter; - AudioSampleBuffer bufferSpace; - MidiBuffer midiEvents; - bool prepared; - SMPTETime lastSMPTETime; - AUChannelInfo channelInfo [numChannelConfigs]; -}; - - -//============================================================================== -class JuceAUComponentHolder : public Component -{ -public: - JuceAUComponentHolder (Component* const editorComp) - { - addAndMakeVisible (editorComp); - setOpaque (true); - setVisible (true); - setBroughtToFrontOnMouseClick (true); - -#if ! JucePlugin_EditorRequiresKeyboardFocus - setWantsKeyboardFocus (false); -#endif - } - - ~JuceAUComponentHolder() - { - } - - void resized() - { - if (getNumChildComponents() > 0) - getChildComponent (0)->setBounds (0, 0, getWidth(), getHeight()); - } - - void paint (Graphics& g) - { - } -}; - -//============================================================================== -class JuceAUView : public AUCarbonViewBase, - public ComponentListener, - public MouseListener, - public Timer -{ - AudioFilterBase* juceFilter; - AudioFilterEditor* editorComp; - Component* windowComp; - bool recursive; - int mx, my; - -public: - JuceAUView (AudioUnitCarbonView auview) - : AUCarbonViewBase (auview), - juceFilter (0), - editorComp (0), - windowComp (0), - recursive (false), - mx (0), - my (0) - { - } - - ~JuceAUView() - { - deleteUI(); - } - - ComponentResult CreateUI (Float32 inXOffset, Float32 inYOffset) - { - if (juceFilter == 0) - { - UInt32 propertySize = sizeof (&juceFilter); - - AudioUnitGetProperty (GetEditAudioUnit(), - juceFilterObjectPropertyID, - kAudioUnitScope_Global, - 0, - &juceFilter, - &propertySize); - } - - if (juceFilter != 0) - { - deleteUI(); - - editorComp = juceFilter->createEditorIfNeeded(); - - const int w = editorComp->getWidth(); - const int h = editorComp->getHeight(); - - editorComp->setOpaque (true); - editorComp->setVisible (true); - - windowComp = new JuceAUComponentHolder (editorComp); - windowComp->setBounds ((int) inXOffset, (int) inYOffset, w, h); - - windowComp->addToDesktop (0, (void*) mCarbonPane); - SizeControl (mCarbonPane, w, h); - - editorComp->addComponentListener (this); - windowComp->addMouseListener (this, true); - - startTimer (20); - } - else - { - jassertfalse // can't get a pointer to our effect - } - - return noErr; - } - - void componentMovedOrResized (Component& component, - bool wasMoved, - bool wasResized) - { - if (! recursive) - { - recursive = true; - - if (editorComp != 0 && wasResized) - { - const int w = jmax (32, editorComp->getWidth()); - const int h = jmax (32, editorComp->getHeight()); - - SizeControl (mCarbonPane, w, h); - - if (windowComp->getWidth() != w - || windowComp->getHeight() != h) - { - windowComp->setSize (w, h); - } - - editorComp->repaint(); - } - - recursive = false; - } - } - - void timerCallback() - { - // for some stupid Apple-related reason, mouse move events just don't seem to get sent - // to the windows in an AU, so we have to bodge it here and simulate them with a - // timer.. - if (false && editorComp != 0) - { - int x, y; - Desktop::getInstance().getMousePosition (x, y); - - if (x != mx || y != my) - { - mx = x; - my = y; - - if (! ModifierKeys::getCurrentModifiers().isAnyMouseButtonDown()) - { - for (int i = ComponentPeer::getNumPeers(); --i >= 0;) - { - ComponentPeer* const peer = ComponentPeer::getPeer (i); - - const int rx = x - peer->getComponent()->getX(); - const int ry = y - peer->getComponent()->getY(); - - if (peer->contains (rx, ry, false) && peer->getComponent()->isShowing()) - { - peer->handleMouseMove (rx, ry, Time::currentTimeMillis()); - break; - } - } - } - } - } - } - - void mouseMove (const MouseEvent& e) - { - Desktop::getInstance().getMousePosition (mx, my); - startTimer (20); - } - -private: - void deleteUI() - { - PopupMenu::dismissAllActiveMenus(); - - // there's some kind of component currently modal, but the host - // is trying to delete our plugin.. - jassert (Component::getCurrentlyModalComponent() == 0); - - if (editorComp != 0) - juceFilter->editorBeingDeleted (editorComp); - - deleteAndZero (editorComp); - deleteAndZero (windowComp); - } -}; - -//============================================================================== -#define JUCE_COMPONENT_ENTRYX(Class, Name, Suffix) \ -extern "C" __attribute__((visibility("default"))) ComponentResult Name ## Suffix (ComponentParameters* params, Class* obj); \ -extern "C" __attribute__((visibility("default"))) ComponentResult Name ## Suffix (ComponentParameters* params, Class* obj) \ -{ \ - return ComponentEntryPoint::Dispatch(params, obj); \ -} - -#define JUCE_COMPONENT_ENTRY(Class, Name, Suffix) JUCE_COMPONENT_ENTRYX(Class, Name, Suffix) - -JUCE_COMPONENT_ENTRY (JuceAU, JucePlugin_AUExportPrefix, Entry) -JUCE_COMPONENT_ENTRY (JuceAUView, JucePlugin_AUExportPrefix, ViewEntry) +/* + ============================================================================== + + 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 +#include "AUMIDIEffectBase.h" +#include "AUCarbonViewBase.h" +#include "../../juce_AudioFilterBase.h" +#include "../../juce_IncludeCharacteristics.h" + + +//============================================================================== +#define juceFilterObjectPropertyID 0x1a45ffe9 +static VoidArray activePlugins; + +static const short channelConfigs[][2] = { JucePlugin_PreferredChannelConfigurations }; +static const int numChannelConfigs = numElementsInArray (channelConfigs); + +BEGIN_JUCE_NAMESPACE +extern void juce_setCurrentExecutableFileNameFromBundleId (const String& bundleId) throw(); +END_JUCE_NAMESPACE + + +//============================================================================== +class JuceAU : public AUMIDIEffectBase, + public AudioFilterBase::FilterNativeCallbacks +{ +public: + //============================================================================== + JuceAU (AudioUnit component) + : AUMIDIEffectBase (component), + juceFilter (0), + bufferSpace (2, 16), + prepared (false) + { + CreateElements(); + + if (activePlugins.size() == 0) + { + initialiseJuce_GUI(); + +#ifdef JucePlugin_CFBundleIdentifier + juce_setCurrentExecutableFileNameFromBundleId (JucePlugin_CFBundleIdentifier); +#endif + + MessageManager::getInstance()->setTimeBeforeShowingWaitCursor (0); + } + + juceFilter = createPluginFilter(); + juceFilter->initialiseInternal (this); + + jassert (juceFilter != 0); + Globals()->UseIndexedParameters (juceFilter->getNumParameters()); + + activePlugins.add (this); + } + + ~JuceAU() + { + delete juceFilter; + juceFilter = 0; + + jassert (activePlugins.contains (this)); + activePlugins.removeValue (this); + + if (activePlugins.size() == 0) + shutdownJuce_GUI(); + } + + //============================================================================== + ComponentResult GetPropertyInfo (AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + UInt32& outDataSize, + Boolean& outWritable) + { + if (inScope == kAudioUnitScope_Global) + { + if (inID == juceFilterObjectPropertyID) + { + outWritable = false; + outDataSize = sizeof (void*); + return noErr; + } + } + + return AUMIDIEffectBase::GetPropertyInfo (inID, inScope, inElement, outDataSize, outWritable); + } + + ComponentResult GetProperty (AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + void* outData) + { + if (inScope == kAudioUnitScope_Global) + { + if (inID == juceFilterObjectPropertyID) + { + *((void**) outData) = (void*) juceFilter; + return noErr; + } + } + + return AUMIDIEffectBase::GetProperty (inID, inScope, inElement, outData); + } + + ComponentResult SaveState (CFPropertyListRef* outData) + { + ComponentResult err = AUMIDIEffectBase::SaveState (outData); + + if (err != noErr) + return err; + + jassert (CFGetTypeID (*outData) == CFDictionaryGetTypeID()); + + CFMutableDictionaryRef dict = (CFMutableDictionaryRef) *outData; + + if (juceFilter != 0) + { + JUCE_NAMESPACE::MemoryBlock state; + juceFilter->getStateInformation (state); + + if (state.getSize() > 0) + { + CFDataRef ourState = CFDataCreate (kCFAllocatorDefault, (const uint8*) state, state.getSize()); + CFDictionarySetValue (dict, CFSTR("jucePluginState"), ourState); + CFRelease (ourState); + } + } + + return noErr; + } + + ComponentResult RestoreState (CFPropertyListRef inData) + { + ComponentResult err = AUMIDIEffectBase::RestoreState (inData); + + if (err != noErr) + return err; + + if (juceFilter != 0) + { + CFDictionaryRef dict = (CFDictionaryRef) inData; + CFDataRef data = 0; + + if (CFDictionaryGetValueIfPresent (dict, CFSTR("jucePluginState"), + (const void**) &data)) + { + if (data != 0) + { + const int numBytes = (int) CFDataGetLength (data); + const uint8* const rawBytes = CFDataGetBytePtr (data); + + if (numBytes > 0) + juceFilter->setStateInformation (rawBytes, numBytes); + } + } + } + + return noErr; + } + + UInt32 SupportedNumChannels (const AUChannelInfo** outInfo) + { + if (juceFilter == 0) + return 0; + + // You need to actually add some configurations to the JucePlugin_PreferredChannelConfigurations + // value in your JucePluginCharacteristics.h file.. + jassert (numChannelConfigs > 0); + + if (outInfo != 0) + { + for (int i = 0; i < numChannelConfigs; ++i) + { + channelInfo[i].inChannels = channelConfigs[i][0]; + channelInfo[i].outChannels = channelConfigs[i][1]; + + outInfo[i] = channelInfo + i; + } + } + + return numChannelConfigs; + } + + //============================================================================== + ComponentResult GetParameterInfo (AudioUnitScope inScope, + AudioUnitParameterID inParameterID, + AudioUnitParameterInfo& outParameterInfo) + { + if (inScope == kAudioUnitScope_Global && juceFilter != 0) + { + outParameterInfo.flags = kAudioUnitParameterFlag_IsWritable + | kAudioUnitParameterFlag_IsReadable + | kAudioUnitParameterFlag_HasCFNameString; + + outParameterInfo.name[0] = 0; + outParameterInfo.cfNameString = PlatformUtilities::juceStringToCFString (juceFilter->getParameterName ((int) inParameterID)); + outParameterInfo.minValue = 0.0f; + outParameterInfo.maxValue = 1.0f; + outParameterInfo.defaultValue = 0.0f; + outParameterInfo.unit = kAudioUnitParameterUnit_Generic; + + return noErr; + } + else + { + return kAudioUnitErr_InvalidParameter; + } + } + + ComponentResult GetParameter (AudioUnitParameterID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + Float32& outValue) + { + if (inScope == kAudioUnitScope_Global && juceFilter != 0) + { + outValue = juceFilter->getParameter ((int) inID); + return noErr; + } + + return AUBase::GetParameter (inID, inScope, inElement, outValue); + } + + ComponentResult SetParameter (AudioUnitParameterID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + Float32 inValue, + UInt32 inBufferOffsetInFrames) + { + if (inScope == kAudioUnitScope_Global && juceFilter != 0) + { + juceFilter->setParameter ((int) inID, inValue); + return noErr; + } + + return AUBase::SetParameter (inID, inScope, inElement, inValue, inBufferOffsetInFrames); + } + + //============================================================================== + ComponentResult Version() { return JucePlugin_VersionCode; } + + bool SupportsTail() { return true; } + Float64 GetTailTime() { return 0; } + + Float64 GetLatency() + { + jassert (GetSampleRate() > 0); + + if (GetSampleRate() <= 0) + return 0.0; + + return (JucePlugin_Latency) / GetSampleRate(); + } + + //============================================================================== + int GetNumCustomUIComponents() { return 1; } + + void GetUIComponentDescs (ComponentDescription* inDescArray) + { + inDescArray[0].componentType = kAudioUnitCarbonViewComponentType; + inDescArray[0].componentSubType = JucePlugin_AUSubType; + inDescArray[0].componentManufacturer = JucePlugin_AUManufacturerCode; + inDescArray[0].componentFlags = 0; + inDescArray[0].componentFlagsMask = 0; + } + + //============================================================================== + bool getCurrentPositionInfo (AudioFilterBase::CurrentPositionInfo& info) + { + info.timeSigNumerator = 0; + info.timeSigDenominator = 0; + info.timeInSeconds = 0; + info.editOriginTime = 0; + info.ppqPositionOfLastBarStart = 0; + info.isPlaying = false; + info.isRecording = false; + + switch (lastSMPTETime.mType) + { + case kSMPTETimeType24: + info.frameRate = AudioFilterBase::CurrentPositionInfo::fps24; + break; + + case kSMPTETimeType25: + info.frameRate = AudioFilterBase::CurrentPositionInfo::fps25; + break; + + case kSMPTETimeType30Drop: + info.frameRate = AudioFilterBase::CurrentPositionInfo::fps30drop; + break; + + case kSMPTETimeType30: + info.frameRate = AudioFilterBase::CurrentPositionInfo::fps30; + break; + + case kSMPTETimeType2997: + info.frameRate = AudioFilterBase::CurrentPositionInfo::fps2997; + break; + + case kSMPTETimeType2997Drop: + info.frameRate = AudioFilterBase::CurrentPositionInfo::fps2997drop; + break; + + //case kSMPTETimeType60: + //case kSMPTETimeType5994: + default: + info.frameRate = AudioFilterBase::CurrentPositionInfo::fpsUnknown; + break; + } + + if (CallHostBeatAndTempo (&info.ppqPosition, &info.bpm) != noErr) + { + info.ppqPosition = 0; + info.bpm = 0; + } + + UInt32 outDeltaSampleOffsetToNextBeat; + double outCurrentMeasureDownBeat; + float num; + UInt32 den; + + if (CallHostMusicalTimeLocation (&outDeltaSampleOffsetToNextBeat, &num, &den, + &outCurrentMeasureDownBeat) == noErr) + { + info.timeSigNumerator = (int) num; + info.timeSigDenominator = den; + info.ppqPositionOfLastBarStart = outCurrentMeasureDownBeat; + } + + double outCurrentSampleInTimeLine, outCycleStartBeat, outCycleEndBeat; + Boolean playing, playchanged, looping; + + if (CallHostTransportState (&playing, + &playchanged, + &outCurrentSampleInTimeLine, + &looping, + &outCycleStartBeat, + &outCycleEndBeat) == noErr) + { + info.isPlaying = playing; + info.timeInSeconds = outCurrentSampleInTimeLine / GetSampleRate(); + } + + return true; + } + + void informHostOfParameterChange (int index, float newValue) + { + if (juceFilter != 0) + { + juceFilter->setParameter (index, newValue); + + if (AUEventListenerNotify != 0) + { + AudioUnitEvent e; + e.mEventType = kAudioUnitEvent_ParameterValueChange; + e.mArgument.mParameter.mAudioUnit = GetComponentInstance(); + e.mArgument.mParameter.mParameterID = (AudioUnitParameterID) index; + e.mArgument.mParameter.mScope = kAudioUnitScope_Global; + e.mArgument.mParameter.mElement = 0; + AUEventListenerNotify (0, 0, &e); + } + } + } + + //============================================================================== + ComponentResult Initialize() + { + AUMIDIEffectBase::Initialize(); + prepareToPlay(); + return noErr; + } + + void Cleanup() + { + AUMIDIEffectBase::Cleanup(); + + if (juceFilter != 0) + juceFilter->releaseResources(); + + bufferSpace.setSize (2, 16); + midiEvents.clear(); + prepared = false; + } + + ComponentResult Reset (AudioUnitScope inScope, AudioUnitElement inElement) + { + if (! prepared) + prepareToPlay(); + + return AUMIDIEffectBase::Reset (inScope, inElement); + } + + void prepareToPlay() + { + if (juceFilter != 0) + { + juceFilter->numInputChannels = GetInput(0)->GetStreamFormat().mChannelsPerFrame; + juceFilter->numOutputChannels = GetOutput(0)->GetStreamFormat().mChannelsPerFrame; + + bufferSpace.setSize (juceFilter->numInputChannels + juceFilter->numOutputChannels, + GetMaxFramesPerSlice() + 32); + + juceFilter->prepareToPlay (GetSampleRate(), + GetMaxFramesPerSlice()); + + midiEvents.clear(); + + prepared = true; + } + } + + ComponentResult Render (AudioUnitRenderActionFlags &ioActionFlags, + const AudioTimeStamp& inTimeStamp, + UInt32 nFrames) + { + lastSMPTETime = inTimeStamp.mSMPTETime; + + return AUMIDIEffectBase::Render (ioActionFlags, inTimeStamp, nFrames); + } + + + OSStatus ProcessBufferLists (AudioUnitRenderActionFlags& ioActionFlags, + const AudioBufferList& inBuffer, + AudioBufferList& outBuffer, + UInt32 inFramesToProcess) + { + if (juceFilter != 0) + { + jassert (prepared); + + float* inChans [64]; + int numInChans = 0; + float* outChans [64]; + int numOutChans = 0; + int nextSpareBufferChan = 0; + bool needToReinterleave = false; + + unsigned int i; + for (i = 0; i < inBuffer.mNumberBuffers; ++i) + { + const AudioBuffer& buf = inBuffer.mBuffers[i]; + + if (buf.mNumberChannels == 1) + { + inChans [numInChans++] = (float*) buf.mData; + } + else + { + // need to de-interleave.. + for (unsigned int subChan = 0; subChan < buf.mNumberChannels; ++subChan) + { + float* dest = bufferSpace.getSampleData (nextSpareBufferChan++); + inChans [numInChans++] = dest; + + const float* src = ((const float*) buf.mData) + subChan; + + for (int j = inFramesToProcess; --j >= 0;) + { + *dest++ = *src; + src += buf.mNumberChannels; + } + } + } + + if (numInChans >= juceFilter->numInputChannels) + break; + } + + const int firstBufferedOutChan = nextSpareBufferChan; + + for (i = 0; i < outBuffer.mNumberBuffers; ++i) + { + AudioBuffer& buf = outBuffer.mBuffers[i]; + + if (buf.mNumberChannels == 1) + { + outChans [numOutChans++] = (float*) buf.mData; + } + else + { + needToReinterleave = true; + + for (unsigned int subChan = 0; subChan < buf.mNumberChannels; ++subChan) + { + float* dest = bufferSpace.getSampleData (nextSpareBufferChan++); + outChans [numOutChans++] = dest; + } + } + + if (numOutChans >= juceFilter->numOutputChannels) + break; + } + + const bool accumulate = false; + + { + const AudioSampleBuffer input (inChans, numInChans, inFramesToProcess); + AudioSampleBuffer output (outChans, numOutChans, inFramesToProcess); + + const ScopedLock sl (juceFilter->getCallbackLock()); + + if (juceFilter->suspended) + output.clear(); + else + juceFilter->processBlock (input, output, accumulate, midiEvents); + } + + if (! midiEvents.isEmpty()) + { +#if JucePlugin_ProducesMidiOutput + const uint8* midiEventData; + int midiEventSize, midiEventPosition; + MidiBuffer::Iterator i (midiEvents); + + while (i.getNextEvent (midiEventData, midiEventSize, midiEventPosition)) + { + jassert (midiEventPosition >= 0 && midiEventPosition < (int) inFramesToProcess); + + + + //xxx + } +#else + // if your plugin creates midi messages, you'll need to set + // the JucePlugin_ProducesMidiOutput macro to 1 in your + // JucePluginCharacteristics.h file + //jassert (midiEvents.getNumEvents() <= numMidiEventsComingIn); +#endif + midiEvents.clear(); + } + + if (needToReinterleave) + { + nextSpareBufferChan = firstBufferedOutChan; + + for (i = 0; i < outBuffer.mNumberBuffers; ++i) + { + AudioBuffer& buf = outBuffer.mBuffers[i]; + + if (buf.mNumberChannels > 1) + { + for (unsigned int subChan = 0; subChan < buf.mNumberChannels; ++subChan) + { + const float* src = bufferSpace.getSampleData (nextSpareBufferChan++); + float* dest = ((float*) buf.mData) + subChan; + + for (int j = inFramesToProcess; --j >= 0;) + { + *dest = *src++; + dest += buf.mNumberChannels; + } + } + } + } + } + +#if ! JucePlugin_SilenceInProducesSilenceOut + ioActionFlags &= ~kAudioUnitRenderAction_OutputIsSilence; +#endif + } + + return noErr; + } + +protected: + OSStatus HandleMidiEvent (UInt8 nStatus, + UInt8 inChannel, + UInt8 inData1, + UInt8 inData2, + long inStartFrame) + { +#if JucePlugin_WantsMidiInput + uint8 data [4]; + data[0] = nStatus | inChannel; + data[1] = inData1; + data[2] = inData2; + + midiEvents.addEvent (data, 3, inStartFrame); +#endif + + return noErr; + } + + //============================================================================== +private: + AudioFilterBase* juceFilter; + AudioSampleBuffer bufferSpace; + MidiBuffer midiEvents; + bool prepared; + SMPTETime lastSMPTETime; + AUChannelInfo channelInfo [numChannelConfigs]; +}; + + +//============================================================================== +class JuceAUComponentHolder : public Component +{ +public: + JuceAUComponentHolder (Component* const editorComp) + { + addAndMakeVisible (editorComp); + setOpaque (true); + setVisible (true); + setBroughtToFrontOnMouseClick (true); + +#if ! JucePlugin_EditorRequiresKeyboardFocus + setWantsKeyboardFocus (false); +#endif + } + + ~JuceAUComponentHolder() + { + } + + void resized() + { + if (getNumChildComponents() > 0) + getChildComponent (0)->setBounds (0, 0, getWidth(), getHeight()); + } + + void paint (Graphics& g) + { + } +}; + +//============================================================================== +class JuceAUView : public AUCarbonViewBase, + public ComponentListener, + public MouseListener, + public Timer +{ + AudioFilterBase* juceFilter; + AudioFilterEditor* editorComp; + Component* windowComp; + bool recursive; + int mx, my; + +public: + JuceAUView (AudioUnitCarbonView auview) + : AUCarbonViewBase (auview), + juceFilter (0), + editorComp (0), + windowComp (0), + recursive (false), + mx (0), + my (0) + { + } + + ~JuceAUView() + { + deleteUI(); + } + + ComponentResult CreateUI (Float32 inXOffset, Float32 inYOffset) + { + if (juceFilter == 0) + { + UInt32 propertySize = sizeof (&juceFilter); + + AudioUnitGetProperty (GetEditAudioUnit(), + juceFilterObjectPropertyID, + kAudioUnitScope_Global, + 0, + &juceFilter, + &propertySize); + } + + if (juceFilter != 0) + { + deleteUI(); + + editorComp = juceFilter->createEditorIfNeeded(); + + const int w = editorComp->getWidth(); + const int h = editorComp->getHeight(); + + editorComp->setOpaque (true); + editorComp->setVisible (true); + + windowComp = new JuceAUComponentHolder (editorComp); + windowComp->setBounds ((int) inXOffset, (int) inYOffset, w, h); + + windowComp->addToDesktop (0, (void*) mCarbonPane); + SizeControl (mCarbonPane, w, h); + + editorComp->addComponentListener (this); + windowComp->addMouseListener (this, true); + + startTimer (20); + } + else + { + jassertfalse // can't get a pointer to our effect + } + + return noErr; + } + + void componentMovedOrResized (Component& component, + bool wasMoved, + bool wasResized) + { + if (! recursive) + { + recursive = true; + + if (editorComp != 0 && wasResized) + { + const int w = jmax (32, editorComp->getWidth()); + const int h = jmax (32, editorComp->getHeight()); + + SizeControl (mCarbonPane, w, h); + + if (windowComp->getWidth() != w + || windowComp->getHeight() != h) + { + windowComp->setSize (w, h); + } + + editorComp->repaint(); + } + + recursive = false; + } + } + + void timerCallback() + { + // for some stupid Apple-related reason, mouse move events just don't seem to get sent + // to the windows in an AU, so we have to bodge it here and simulate them with a + // timer.. + if (editorComp != 0) + { + int x, y; + Desktop::getInstance().getMousePosition (x, y); + + if (x != mx || y != my) + { + mx = x; + my = y; + + if (! ModifierKeys::getCurrentModifiers().isAnyMouseButtonDown()) + { + for (int i = ComponentPeer::getNumPeers(); --i >= 0;) + { + ComponentPeer* const peer = ComponentPeer::getPeer (i); + + const int rx = x - peer->getComponent()->getX(); + const int ry = y - peer->getComponent()->getY(); + + if (peer->contains (rx, ry, false) && peer->getComponent()->isShowing()) + { + peer->handleMouseMove (rx, ry, Time::currentTimeMillis()); + break; + } + } + } + } + } + } + + void mouseMove (const MouseEvent& e) + { + Desktop::getInstance().getMousePosition (mx, my); + startTimer (20); + } + +private: + void deleteUI() + { + PopupMenu::dismissAllActiveMenus(); + + // there's some kind of component currently modal, but the host + // is trying to delete our plugin.. + jassert (Component::getCurrentlyModalComponent() == 0); + + if (editorComp != 0) + juceFilter->editorBeingDeleted (editorComp); + + deleteAndZero (editorComp); + deleteAndZero (windowComp); + } +}; + +//============================================================================== +#define JUCE_COMPONENT_ENTRYX(Class, Name, Suffix) \ +extern "C" __attribute__((visibility("default"))) ComponentResult Name ## Suffix (ComponentParameters* params, Class* obj); \ +extern "C" __attribute__((visibility("default"))) ComponentResult Name ## Suffix (ComponentParameters* params, Class* obj) \ +{ \ + return ComponentEntryPoint::Dispatch(params, obj); \ +} + +#define JUCE_COMPONENT_ENTRY(Class, Name, Suffix) JUCE_COMPONENT_ENTRYX(Class, Name, Suffix) + +JUCE_COMPONENT_ENTRY (JuceAU, JucePlugin_AUExportPrefix, Entry) +JUCE_COMPONENT_ENTRY (JuceAUView, JucePlugin_AUExportPrefix, ViewEntry) diff --git a/src/juce_appframework/audio/midi/juce_MidiKeyboardState.cpp b/src/juce_appframework/audio/midi/juce_MidiKeyboardState.cpp index 80f52bf1c6..1345a99089 100644 --- a/src/juce_appframework/audio/midi/juce_MidiKeyboardState.cpp +++ b/src/juce_appframework/audio/midi/juce_MidiKeyboardState.cpp @@ -164,7 +164,7 @@ void MidiKeyboardState::processNextMidiBuffer (MidiBuffer& buffer, const bool injectIndirectEvents) { MidiBuffer::Iterator i (buffer); - MidiMessage message (0, 0.0); + MidiMessage message (0xf4, 0.0); int time; const ScopedLock sl (lock); diff --git a/src/juce_appframework/audio/midi/juce_MidiMessage.cpp b/src/juce_appframework/audio/midi/juce_MidiMessage.cpp index a6c1d32a7b..68c7c3be53 100644 --- a/src/juce_appframework/audio/midi/juce_MidiMessage.cpp +++ b/src/juce_appframework/audio/midi/juce_MidiMessage.cpp @@ -1,1130 +1,1130 @@ -/* - ============================================================================== - - 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_MidiMessage.h" -#include "../../../juce_core/containers/juce_MemoryBlock.h" - - -//============================================================================== -int MidiMessage::readVariableLengthVal (const uint8* data, - int& numBytesUsed) throw() -{ - numBytesUsed = 0; - int v = 0; - int i; - - do - { - i = (int) *data++; - - if (++numBytesUsed > 6) - break; - - v = (v << 7) + (i & 0x7f); - - } while (i & 0x80); - - return v; -} - -int MidiMessage::getMessageLengthFromFirstByte (const uint8 firstByte) throw() -{ - // this method only works for valid starting bytes of a short midi message - jassert (firstByte >= 0x80 - && firstByte != 0xff - && firstByte != 0xf0 - && firstByte != 0xf7); - - static const char messageLengths[] = - { - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 1, 2, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 - }; - - return messageLengths [firstByte & 0x7f]; -} - -//============================================================================== -MidiMessage::MidiMessage (const uint8* const d, - const int dataSize, - const double t) throw() - : timeStamp (t), - message (0), - size (dataSize) -{ - jassert (dataSize > 0); - - if (dataSize <= 4) - data = (uint8*) &message; - else - data = (uint8*) juce_malloc (dataSize); - - memcpy (data, d, dataSize); - - // check that the length matches the data.. - jassert (size > 3 || getMessageLengthFromFirstByte (*d) == size); -} - -MidiMessage::MidiMessage (const int byte1, - const double t) throw() - : timeStamp (t), - data ((uint8*) &message), - size (1) -{ - data[0] = (uint8) byte1; - - // check that the length matches the data.. - jassert (getMessageLengthFromFirstByte ((uint8) byte1) == 1); -} - -MidiMessage::MidiMessage (const int byte1, - const int byte2, - const double t) throw() - : timeStamp (t), - data ((uint8*) &message), - size (2) -{ - data[0] = (uint8) byte1; - data[1] = (uint8) byte2; - - // check that the length matches the data.. - jassert (getMessageLengthFromFirstByte ((uint8) byte1) == 2); -} - -MidiMessage::MidiMessage (const int byte1, - const int byte2, - const int byte3, - const double t) throw() - : timeStamp (t), - data ((uint8*) &message), - size (3) -{ - data[0] = (uint8) byte1; - data[1] = (uint8) byte2; - data[2] = (uint8) byte3; - - // check that the length matches the data.. - jassert (getMessageLengthFromFirstByte ((uint8) byte1) == 3); -} - -MidiMessage::MidiMessage (const MidiMessage& other) throw() - : timeStamp (other.timeStamp), - message (other.message), - size (other.size) -{ - if (other.data != (uint8*) &other.message) - { - data = (uint8*) juce_malloc (size); - memcpy (data, other.data, size); - } - else - { - data = (uint8*) &message; - } -} - -MidiMessage::MidiMessage (const MidiMessage& other, - const double newTimeStamp) throw() - : timeStamp (newTimeStamp), - message (other.message), - size (other.size) -{ - if (other.data != (uint8*) &other.message) - { - data = (uint8*) juce_malloc (size); - memcpy (data, other.data, size); - } - else - { - data = (uint8*) &message; - } -} - -MidiMessage::MidiMessage (const uint8* src, - int sz, - int& numBytesUsed, - const uint8 lastStatusByte, - double t) throw() - : timeStamp (t), - data ((uint8*) &message), - message (0) -{ - unsigned int byte = (unsigned int) *src; - - if (byte < 0x80) - { - byte = (unsigned int) (uint8) lastStatusByte; - numBytesUsed = -1; - } - else - { - numBytesUsed = 0; - --sz; - ++src; - } - - if (byte >= 0x80) - { - if (byte == 0xf0) - { - const uint8* d = (const uint8*) src; - - while (d < src + sz) - { - if (*d >= 0x80) // stop if we hit a status byte, and don't include it in this message - { - if (*d == 0xf7) // include an 0xf7 if we hit one - ++d; - - break; - } - - ++d; - } - - size = 1 + (int) (d - src); - - data = (uint8*) juce_malloc (size); - *data = (uint8) byte; - memcpy (data + 1, src, size - 1); - } - else if (byte == 0xff) - { - int n; - const int bytesLeft = readVariableLengthVal (src + 1, n); - size = jmin (sz + 1, n + 2 + bytesLeft); - - data = (uint8*) juce_malloc (size); - *data = (uint8) byte; - memcpy (data + 1, src, size - 1); - } - else - { - size = getMessageLengthFromFirstByte ((uint8) byte); - *data = (uint8) byte; - - if (size > 1) - { - data[1] = src[0]; - - if (size > 2) - data[2] = src[1]; - } - } - - numBytesUsed += size; - } - else - { - message = 0; - size = 0; - } -} - -const MidiMessage& MidiMessage::operator= (const MidiMessage& other) throw() -{ - if (this == &other) - return *this; - - timeStamp = other.timeStamp; - size = other.size; - message = other.message; - - if (data != (uint8*) &message) - juce_free (data); - - if (other.data != (uint8*) &other.message) - { - data = (uint8*) juce_malloc (size); - memcpy (data, other.data, size); - } - else - { - data = (uint8*) &message; - } - - return *this; -} - -MidiMessage::~MidiMessage() throw() -{ - if (data != (uint8*) &message) - juce_free (data); -} - -int MidiMessage::getChannel() const throw() -{ - if ((data[0] & 0xf0) != 0xf0) - return (data[0] & 0xf) + 1; - else - return 0; -} - -bool MidiMessage::isForChannel (const int channel) const throw() -{ - return ((data[0] & 0xf) == channel - 1) - && ((data[0] & 0xf0) != 0xf0); -} - -void MidiMessage::setChannel (const int channel) throw() -{ - if ((data[0] & 0xf0) != (uint8) 0xf0) - data[0] = (uint8) ((data[0] & (uint8)0xf0) - | (uint8)(channel - 1)); -} - -bool MidiMessage::isNoteOn() const throw() -{ - return ((data[0] & 0xf0) == 0x90) - && (data[2] != 0); -} - -bool MidiMessage::isNoteOff() const throw() -{ - return ((data[0] & 0xf0) == 0x80) - || ((data[2] == 0) && ((data[0] & 0xf0) == 0x90)); -} - -bool MidiMessage::isNoteOnOrOff() const throw() -{ - const int d = data[0] & 0xf0; - return (d == 0x90) || (d == 0x80); -} - -int MidiMessage::getNoteNumber() const throw() -{ - return data[1]; -} - -void MidiMessage::setNoteNumber (const int newNoteNumber) throw() -{ - if (isNoteOnOrOff()) - data[1] = (uint8) jlimit (0, 127, newNoteNumber); -} - -uint8 MidiMessage::getVelocity() const throw() -{ - if (isNoteOnOrOff()) - return data[2]; - else - return 0; -} - -float MidiMessage::getFloatVelocity() const throw() -{ - return getVelocity() * (1.0f / 127.0f); -} - -void MidiMessage::setVelocity (const float newVelocity) throw() -{ - if (isNoteOnOrOff()) - data[2] = (uint8) jlimit (0, 0x7f, roundFloatToInt (newVelocity * 127.0f)); -} - -void MidiMessage::multiplyVelocity (const float scaleFactor) throw() -{ - if (isNoteOnOrOff()) - data[2] = (uint8) jlimit (0, 0x7f, roundFloatToInt (scaleFactor * data[2])); -} - -bool MidiMessage::isAftertouch() const throw() -{ - return (data[0] & 0xf0) == 0xa0; -} - -int MidiMessage::getAfterTouchValue() const throw() -{ - return data[2]; -} - -const MidiMessage MidiMessage::aftertouchChange (const int channel, - const int noteNum, - const int aftertouchValue) throw() -{ - jassert (channel > 0 && channel <= 16); - jassert (noteNum >= 0 && noteNum <= 127); - jassert (aftertouchValue >= 0 && aftertouchValue <= 127); - - return MidiMessage (0xa0 | jlimit (0, 15, channel - 1), - noteNum & 0x7f, - aftertouchValue & 0x7f); -} - -bool MidiMessage::isChannelPressure() const throw() -{ - return (data[0] & 0xf0) == 0xd0; -} - -int MidiMessage::getChannelPressureValue() const throw() -{ - jassert (isChannelPressure()); - - return data[1]; -} - -const MidiMessage MidiMessage::channelPressureChange (const int channel, - const int pressure) throw() -{ - jassert (channel > 0 && channel <= 16); - jassert (pressure >= 0 && pressure <= 127); - - return MidiMessage (0xd0 | jlimit (0, 15, channel - 1), - pressure & 0x7f); -} - -bool MidiMessage::isProgramChange() const throw() -{ - return (data[0] & 0xf0) == 0xc0; -} - -int MidiMessage::getProgramChangeNumber() const throw() -{ - return data[1]; -} - -const MidiMessage MidiMessage::programChange (const int channel, - const int programNumber) throw() -{ - jassert (channel > 0 && channel <= 16); - - return MidiMessage (0xc0 | jlimit (0, 15, channel - 1), - programNumber & 0x7f); -} - -bool MidiMessage::isPitchWheel() const throw() -{ - return (data[0] & 0xf0) == 0xe0; -} - -int MidiMessage::getPitchWheelValue() const throw() -{ - return data[1] | (data[2] << 7); -} - -const MidiMessage MidiMessage::pitchWheel (const int channel, - const int position) throw() -{ - jassert (channel > 0 && channel <= 16); - jassert (position >= 0 && position <= 0x3fff); - - return MidiMessage (0xe0 | jlimit (0, 15, channel - 1), - position & 127, - (position >> 7) & 127); -} - -bool MidiMessage::isController() const throw() -{ - return (data[0] & 0xf0) == 0xb0; -} - -int MidiMessage::getControllerNumber() const throw() -{ - jassert (isController()); - - return data[1]; -} - -int MidiMessage::getControllerValue() const throw() -{ - jassert (isController()); - - return data[2]; -} - -const MidiMessage MidiMessage::controllerEvent (const int channel, - const int controllerType, - const int value) throw() -{ - jassert (channel > 0 && channel <= 16); - - return MidiMessage (0xb0 | jlimit (0, 15, channel - 1), - controllerType & 127, - value & 127); -} - -const MidiMessage MidiMessage::noteOn (const int channel, - const int noteNumber, - const float velocity) throw() -{ - return noteOn (channel, noteNumber, (uint8)(velocity * 127.0f)); -} - -const MidiMessage MidiMessage::noteOn (const int channel, - const int noteNumber, - const uint8 velocity) throw() -{ - jassert (channel > 0 && channel <= 16); - jassert (noteNumber >= 0 && noteNumber <= 127); - - return MidiMessage (0x90 | jlimit (0, 15, channel - 1), - noteNumber & 127, - jlimit (0, 127, roundFloatToInt (velocity))); -} - -const MidiMessage MidiMessage::noteOff (const int channel, - const int noteNumber) throw() -{ - jassert (channel > 0 && channel <= 16); - jassert (noteNumber >= 0 && noteNumber <= 127); - - return MidiMessage (0x80 | jlimit (0, 15, channel - 1), noteNumber & 127, 0); -} - -const MidiMessage MidiMessage::allNotesOff (const int channel) throw() -{ - jassert (channel > 0 && channel <= 16); - - return controllerEvent (channel, 123, 0); -} - -bool MidiMessage::isAllNotesOff() const throw() -{ - return (data[0] & 0xf0) == 0xb0 - && data[1] == 123; -} - -const MidiMessage MidiMessage::allSoundOff (const int channel) throw() -{ - return controllerEvent (channel, 120, 0); -} - -bool MidiMessage::isAllSoundOff() const throw() -{ - return (data[0] & 0xf0) == 0xb0 - && data[1] == 120; -} - -const MidiMessage MidiMessage::allControllersOff (const int channel) throw() -{ - return controllerEvent (channel, 121, 0); -} - -const MidiMessage MidiMessage::masterVolume (const float volume) throw() -{ - const int vol = jlimit (0, 0x3fff, roundFloatToInt (volume * 0x4000)); - - uint8 buf[8]; - buf[0] = 0xf0; - buf[1] = 0x7f; - buf[2] = 0x7f; - buf[3] = 0x04; - buf[4] = 0x01; - buf[5] = (uint8) (vol & 0x7f); - buf[6] = (uint8) (vol >> 7); - buf[7] = 0xf7; - - return MidiMessage (buf, 8); -} - -//============================================================================== -bool MidiMessage::isSysEx() const throw() -{ - return *data == 0xf0; -} - -const MidiMessage MidiMessage::createSysExMessage (const uint8* sysexData, - const int dataSize) throw() -{ - MemoryBlock mm (dataSize + 2); - uint8* const m = (uint8*) mm.getData(); - - m[0] = 0xf0; - memcpy (m + 1, sysexData, dataSize); - m[dataSize + 1] = 0xf7; - - return MidiMessage (m, dataSize + 2); -} - -const uint8* MidiMessage::getSysExData() const throw() -{ - return (isSysEx()) ? getRawData() + 1 - : 0; -} - -int MidiMessage::getSysExDataSize() const throw() -{ - return (isSysEx()) ? size - 2 - : 0; -} - -bool MidiMessage::isMetaEvent() const throw() -{ - return *data == 0xff; -} - -bool MidiMessage::isActiveSense() const throw() -{ - return *data == 0xfe; -} - -//============================================================================== -int MidiMessage::getMetaEventType() const throw() -{ - if (*data != 0xff) - return -1; - else - return data[1]; -} - -int MidiMessage::getMetaEventLength() const throw() -{ - if (*data == 0xff) - { - int n; - return jmin (size - 2, readVariableLengthVal (data + 2, n)); - } - - return 0; -} - -const uint8* MidiMessage::getMetaEventData() const throw() -{ - int n; - const uint8* d = data + 2; - readVariableLengthVal (d, n); - return d + n; -} - -bool MidiMessage::isTrackMetaEvent() const throw() -{ - return getMetaEventType() == 0; -} - -bool MidiMessage::isEndOfTrackMetaEvent() const throw() -{ - return getMetaEventType() == 47; -} - -bool MidiMessage::isTextMetaEvent() const throw() -{ - const int t = getMetaEventType(); - - return t > 0 && t < 16; -} - -const String MidiMessage::getTextFromTextMetaEvent() const throw() -{ - return String ((const char*) getMetaEventData(), - getMetaEventLength()); -} - -bool MidiMessage::isTrackNameEvent() const throw() -{ - return (data[1] == 3) - && (*data == 0xff); -} - -bool MidiMessage::isTempoMetaEvent() const throw() -{ - return (data[1] == 81) - && (*data == 0xff); -} - -bool MidiMessage::isMidiChannelMetaEvent() const throw() -{ - return (data[1] == 0x20) - && (*data == 0xff) - && (data[2] == 1); -} - -int MidiMessage::getMidiChannelMetaEventChannel() const throw() -{ - return data[3] + 1; -} - -double MidiMessage::getTempoSecondsPerQuarterNote() const throw() -{ - if (! isTempoMetaEvent()) - return 0.0; - - const uint8* const d = getMetaEventData(); - - return (((unsigned int) d[0] << 16) - | ((unsigned int) d[1] << 8) - | d[2]) - / 1000000.0; -} - -double MidiMessage::getTempoMetaEventTickLength (const short timeFormat) const throw() -{ - if (timeFormat > 0) - { - if (! isTempoMetaEvent()) - return 0.5 / timeFormat; - - return getTempoSecondsPerQuarterNote() / timeFormat; - } - else - { - const int frameCode = (-timeFormat) >> 8; - double framesPerSecond; - - switch (frameCode) - { - case 24: framesPerSecond = 24.0; break; - case 25: framesPerSecond = 25.0; break; - case 29: framesPerSecond = 29.97; break; - case 30: framesPerSecond = 30.0; break; - default: framesPerSecond = 30.0; break; - } - - return (1.0 / framesPerSecond) / (timeFormat & 0xff); - } -} - -const MidiMessage MidiMessage::tempoMetaEvent (int microsecondsPerQuarterNote) throw() -{ - uint8 d[8]; - d[0] = 0xff; - d[1] = 81; - d[2] = 3; - d[3] = (uint8) (microsecondsPerQuarterNote >> 16); - d[4] = (uint8) ((microsecondsPerQuarterNote >> 8) & 0xff); - d[5] = (uint8) (microsecondsPerQuarterNote & 0xff); - - return MidiMessage (d, 6, 0.0); -} - -bool MidiMessage::isTimeSignatureMetaEvent() const throw() -{ - return (data[1] == 0x58) - && (*data == (uint8) 0xff); -} - -void MidiMessage::getTimeSignatureInfo (int& numerator, - int& denominator) const throw() -{ - if (isTimeSignatureMetaEvent()) - { - const uint8* const d = getMetaEventData(); - numerator = d[0]; - denominator = 1 << d[1]; - } - else - { - numerator = 4; - denominator = 4; - } -} - -const MidiMessage MidiMessage::timeSignatureMetaEvent (const int numerator, - const int denominator) throw() -{ - uint8 d[8]; - d[0] = 0xff; - d[1] = 0x58; - d[2] = 0x04; - d[3] = (uint8) numerator; - - int n = 1; - int powerOfTwo = 0; - - while (n < denominator) - { - n <<= 1; - ++powerOfTwo; - } - - d[4] = (uint8) powerOfTwo; - d[5] = 0x01; - d[6] = 96; - - return MidiMessage (d, 7, 0.0); -} - -const MidiMessage MidiMessage::midiChannelMetaEvent (const int channel) throw() -{ - uint8 d[8]; - d[0] = 0xff; - d[1] = 0x20; - d[2] = 0x01; - d[3] = (uint8) jlimit (0, 0xff, channel - 1); - - return MidiMessage (d, 4, 0.0); -} - -bool MidiMessage::isKeySignatureMetaEvent() const throw() -{ - return getMetaEventType() == 89; -} - -int MidiMessage::getKeySignatureNumberOfSharpsOrFlats() const throw() -{ - return (int)(*getMetaEventData()); -} - -const MidiMessage MidiMessage::endOfTrack() throw() -{ - return MidiMessage (0xff, 0x2f, 0, 0.0); -} - -//============================================================================== -bool MidiMessage::isSongPositionPointer() const throw() -{ - return *data == 0xf2; -} - -int MidiMessage::getSongPositionPointerMidiBeat() const throw() -{ - return data[1] | (data[2] << 7); -} - -const MidiMessage MidiMessage::songPositionPointer (const int positionInMidiBeats) throw() -{ - return MidiMessage (0xf2, - positionInMidiBeats & 127, - (positionInMidiBeats >> 7) & 127); -} - -bool MidiMessage::isMidiStart() const throw() -{ - return *data == 0xfa; -} - -const MidiMessage MidiMessage::midiStart() throw() -{ - return MidiMessage (0xfa); -} - -bool MidiMessage::isMidiContinue() const throw() -{ - return *data == 0xfb; -} - -const MidiMessage MidiMessage::midiContinue() throw() -{ - return MidiMessage (0xfb); -} - -bool MidiMessage::isMidiStop() const throw() -{ - return *data == 0xfc; -} - -const MidiMessage MidiMessage::midiStop() throw() -{ - return MidiMessage (0xfc); -} - -bool MidiMessage::isMidiClock() const throw() -{ - return *data == 0xf8; -} - -const MidiMessage MidiMessage::midiClock() throw() -{ - return MidiMessage (0xf8); -} - -bool MidiMessage::isQuarterFrame() const throw() -{ - return *data == 0xf1; -} - -int MidiMessage::getQuarterFrameSequenceNumber() const throw() -{ - return ((int) data[1]) >> 4; -} - -int MidiMessage::getQuarterFrameValue() const throw() -{ - return ((int) data[1]) & 0x0f; -} - -const MidiMessage MidiMessage::quarterFrame (const int sequenceNumber, - const int value) throw() -{ - return MidiMessage (0xf1, (sequenceNumber << 4) | value); -} - -bool MidiMessage::isFullFrame() const throw() -{ - return data[0] == 0xf0 - && data[1] == 0x7f - && size >= 10 - && data[3] == 0x01 - && data[4] == 0x01; -} - -void MidiMessage::getFullFrameParameters (int& hours, - int& minutes, - int& seconds, - int& frames, - MidiMessage::SmpteTimecodeType& timecodeType) const throw() -{ - jassert (isFullFrame()); - - timecodeType = (SmpteTimecodeType) (data[5] >> 5); - hours = data[5] & 0x1f; - minutes = data[6]; - seconds = data[7]; - frames = data[8]; -} - -const MidiMessage MidiMessage::fullFrame (const int hours, - const int minutes, - const int seconds, - const int frames, - MidiMessage::SmpteTimecodeType timecodeType) -{ - uint8 d[10]; - d[0] = 0xf0; - d[1] = 0x7f; - d[2] = 0x7f; - d[3] = 0x01; - d[4] = 0x01; - d[5] = (uint8) ((hours & 0x01f) | (timecodeType << 5)); - d[6] = (uint8) minutes; - d[7] = (uint8) seconds; - d[8] = (uint8) frames; - d[9] = 0xf7; - - return MidiMessage (d, 10, 0.0); -} - -bool MidiMessage::isMidiMachineControlMessage() const throw() -{ - return data[0] == 0xf0 - && data[1] == 0x7f - && data[3] == 0x06 - && size > 5; -} - -MidiMessage::MidiMachineControlCommand MidiMessage::getMidiMachineControlCommand() const throw() -{ - jassert (isMidiMachineControlMessage()); - - return (MidiMachineControlCommand) data[4]; -} - -const MidiMessage MidiMessage::midiMachineControlCommand (MidiMessage::MidiMachineControlCommand command) -{ - uint8 d[6]; - d[0] = 0xf0; - d[1] = 0x7f; - d[2] = 0x00; - d[3] = 0x06; - d[4] = (uint8) command; - d[5] = 0xf7; - - return MidiMessage (d, 6, 0.0); -} - -//============================================================================== -bool MidiMessage::isMidiMachineControlGoto (int& hours, - int& minutes, - int& seconds, - int& frames) const throw() -{ - if (size >= 12 - && data[0] == 0xf0 - && data[1] == 0x7f - && data[3] == 0x06 - && data[4] == 0x44 - && data[5] == 0x06 - && data[6] == 0x01) - { - hours = data[7] % 24; // (that some machines send out hours > 24) - minutes = data[8]; - seconds = data[9]; - frames = data[10]; - - return true; - } - - return false; -} - -const MidiMessage MidiMessage::midiMachineControlGoto (int hours, - int minutes, - int seconds, - int frames) -{ - uint8 d[12]; - d[0] = 0xf0; - d[1] = 0x7f; - d[2] = 0x00; - d[3] = 0x06; - d[4] = 0x44; - d[5] = 0x06; - d[6] = 0x01; - d[7] = (uint8) hours; - d[8] = (uint8) minutes; - d[9] = (uint8) seconds; - d[10] = (uint8) frames; - d[11] = 0xf7; - - return MidiMessage (d, 12, 0.0); -} - -//============================================================================== -const String MidiMessage::getMidiNoteName (int note, - bool useSharps, - bool includeOctaveNumber, - int octaveNumForMiddleC) throw() -{ - static const char* const sharpNoteNames[] = { "C", "C#", "D", "D#", "E", - "F", "F#", "G", "G#", "A", - "A#", "B" }; - - static const char* const flatNoteNames[] = { "C", "Db", "D", "Eb", "E", - "F", "Gb", "G", "Ab", "A", - "Bb", "B" }; - - if (note >= 0 && note < 128) - { - const String s ((useSharps) ? sharpNoteNames [note % 12] - : flatNoteNames [note % 12]); - - if (includeOctaveNumber) - return s + String (note / 12 + (octaveNumForMiddleC - 5)); - else - return s; - } - - return String::empty; -} - -const double MidiMessage::getMidiNoteInHertz (int noteNumber) throw() -{ - noteNumber -= 12 * 6 + 9; // now 0 = A440 - return 440.0 * pow (2.0, noteNumber / 12.0); -} - -const String MidiMessage::getGMInstrumentName (int n) throw() -{ - const char *names[] = - { - "Acoustic Grand Piano", "Bright Acoustic Piano", "Electric Grand Piano", "Honky-tonk Piano", - "Electric Piano 1", "Electric Piano 2", "Harpsichord", "Clavinet", "Celesta", "Glockenspiel", - "Music Box", "Vibraphone", "Marimba", "Xylophone", "Tubular Bells", "Dulcimer", "Drawbar Organ", - "Percussive Organ", "Rock Organ", "Church Organ", "Reed Organ", "Accordion", "Harmonica", - "Tango Accordion", "Acoustic Guitar (nylon)", "Acoustic Guitar (steel)", "Electric Guitar (jazz)", - "Electric Guitar (clean)", "Electric Guitar (mute)", "Overdriven Guitar", "Distortion Guitar", - "Guitar Harmonics", "Acoustic Bass", "Electric Bass (finger)", "Electric Bass (pick)", - "Fretless Bass", "Slap Bass 1", "Slap Bass 2", "Synth Bass 1", "Synth Bass 2", "Violin", - "Viola", "Cello", "Contrabass", "Tremolo Strings", "Pizzicato Strings", "Orchestral Harp", - "Timpani", "String Ensemble 1", "String Ensemble 2", "SynthStrings 1", "SynthStrings 2", - "Choir Aahs", "Voice Oohs", "Synth Voice", "Orchestra Hit", "Trumpet", "Trombone", "Tuba", - "Muted Trumpet", "French Horn", "Brass Section", "SynthBrass 1", "SynthBrass 2", "Soprano Sax", - "Alto Sax", "Tenor Sax", "Baritone Sax", "Oboe", "English Horn", "Bassoon", "Clarinet", - "Piccolo", "Flute", "Recorder", "Pan Flute", "Blown Bottle", "Shakuhachi", "Whistle", - "Ocarina", "Lead 1 (square)", "Lead 2 (sawtooth)", "Lead 3 (calliope)", "Lead 4 (chiff)", - "Lead 5 (charang)", "Lead 6 (voice)", "Lead 7 (fifths)", "Lead 8 (bass+lead)", "Pad 1 (new age)", - "Pad 2 (warm)", "Pad 3 (polysynth)", "Pad 4 (choir)", "Pad 5 (bowed)", "Pad 6 (metallic)", - "Pad 7 (halo)", "Pad 8 (sweep)", "FX 1 (rain)", "FX 2 (soundtrack)", "FX 3 (crystal)", - "FX 4 (atmosphere)", "FX 5 (brightness)", "FX 6 (goblins)", "FX 7 (echoes)", "FX 8 (sci-fi)", - "Sitar", "Banjo", "Shamisen", "Koto", "Kalimba", "Bag pipe", "Fiddle", "Shanai", "Tinkle Bell", - "Agogo", "Steel Drums", "Woodblock", "Taiko Drum", "Melodic Tom", "Synth Drum", "Reverse Cymbal", - "Guitar Fret Noise", "Breath Noise", "Seashore", "Bird Tweet", "Telephone Ring", "Helicopter", - "Applause", "Gunshot" - }; - - return (n >= 0 && n < 128) ? names[n] - : (const char*)0; -} - -const String MidiMessage::getGMInstrumentBankName (int n) throw() -{ - const char* names[] = - { - "Piano", "Chromatic Percussion", "Organ", "Guitar", - "Bass", "Strings", "Ensemble", "Brass", - "Reed", "Pipe", "Synth Lead", "Synth Pad", - "Synth Effects", "Ethnic", "Percussive", "Sound Effects" - }; - - return (n >= 0 && n <= 15) ? names[n] - : (const char*)0; -} - -const String MidiMessage::getRhythmInstrumentName (int n) throw() -{ - const char* names[] = - { - "Acoustic Bass Drum", "Bass Drum 1", "Side Stick", "Acoustic Snare", - "Hand Clap", "Electric Snare", "Low Floor Tom", "Closed Hi-Hat", "High Floor Tom", - "Pedal Hi-Hat", "Low Tom", "Open Hi-Hat", "Low-Mid Tom", "Hi-Mid Tom", "Crash Cymbal 1", - "High Tom", "Ride Cymbal 1", "Chinese Cymbal", "Ride Bell", "Tambourine", "Splash Cymbal", - "Cowbell", "Crash Cymbal 2", "Vibraslap", "Ride Cymbal 2", "Hi Bongo", "Low Bongo", - "Mute Hi Conga", "Open Hi Conga", "Low Conga", "High Timbale", "Low Timbale", "High Agogo", - "Low Agogo", "Cabasa", "Maracas", "Short Whistle", "Long Whistle", "Short Guiro", - "Long Guiro", "Claves", "Hi Wood Block", "Low Wood Block", "Mute Cuica", "Open Cuica", - "Mute Triangle", "Open Triangle" - }; - - return (n >= 35 && n <= 81) ? names [n - 35] - : (const char*)0; -} - -const String MidiMessage::getControllerName (int n) throw() -{ - const char* names[] = - { - "Bank Select", "Modulation Wheel (coarse)", "Breath controller (coarse)", - 0, "Foot Pedal (coarse)", "Portamento Time (coarse)", - "Data Entry (coarse)", "Volume (coarse)", "Balance (coarse)", - 0, "Pan position (coarse)", "Expression (coarse)", "Effect Control 1 (coarse)", - "Effect Control 2 (coarse)", 0, 0, "General Purpose Slider 1", "General Purpose Slider 2", - "General Purpose Slider 3", "General Purpose Slider 4", 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, "Bank Select (fine)", "Modulation Wheel (fine)", "Breath controller (fine)", - 0, "Foot Pedal (fine)", "Portamento Time (fine)", "Data Entry (fine)", "Volume (fine)", - "Balance (fine)", 0, "Pan position (fine)", "Expression (fine)", "Effect Control 1 (fine)", - "Effect Control 2 (fine)", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - "Hold Pedal (on/off)", "Portamento (on/off)", "Sustenuto Pedal (on/off)", "Soft Pedal (on/off)", - "Legato Pedal (on/off)", "Hold 2 Pedal (on/off)", "Sound Variation", "Sound Timbre", - "Sound Release Time", "Sound Attack Time", "Sound Brightness", "Sound Control 6", - "Sound Control 7", "Sound Control 8", "Sound Control 9", "Sound Control 10", - "General Purpose Button 1 (on/off)", "General Purpose Button 2 (on/off)", - "General Purpose Button 3 (on/off)", "General Purpose Button 4 (on/off)", - 0, 0, 0, 0, 0, 0, 0, "Reverb Level", "Tremolo Level", "Chorus Level", "Celeste Level", - "Phaser Level", "Data Button increment", "Data Button decrement", "Non-registered Parameter (fine)", - "Non-registered Parameter (coarse)", "Registered Parameter (fine)", "Registered Parameter (coarse)", - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "All Sound Off", "All Controllers Off", - "Local Keyboard (on/off)", "All Notes Off", "Omni Mode Off", "Omni Mode On", "Mono Operation", - "Poly Operation" - }; - - return (n >= 0 && n < 128) ? names[n] - : (const char*)0; -} - -END_JUCE_NAMESPACE +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#include "../../../juce_core/basics/juce_StandardHeader.h" + +BEGIN_JUCE_NAMESPACE + +#include "juce_MidiMessage.h" +#include "../../../juce_core/containers/juce_MemoryBlock.h" + + +//============================================================================== +int MidiMessage::readVariableLengthVal (const uint8* data, + int& numBytesUsed) throw() +{ + numBytesUsed = 0; + int v = 0; + int i; + + do + { + i = (int) *data++; + + if (++numBytesUsed > 6) + break; + + v = (v << 7) + (i & 0x7f); + + } while (i & 0x80); + + return v; +} + +int MidiMessage::getMessageLengthFromFirstByte (const uint8 firstByte) throw() +{ + // this method only works for valid starting bytes of a short midi message + jassert (firstByte >= 0x80 + && firstByte != 0xff + && firstByte != 0xf0 + && firstByte != 0xf7); + + static const char messageLengths[] = + { + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 1, 2, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + + return messageLengths [firstByte & 0x7f]; +} + +//============================================================================== +MidiMessage::MidiMessage (const uint8* const d, + const int dataSize, + const double t) throw() + : timeStamp (t), + message (0), + size (dataSize) +{ + jassert (dataSize > 0); + + if (dataSize <= 4) + data = (uint8*) &message; + else + data = (uint8*) juce_malloc (dataSize); + + memcpy (data, d, dataSize); + + // check that the length matches the data.. + jassert (size > 3 || getMessageLengthFromFirstByte (*d) == size); +} + +MidiMessage::MidiMessage (const int byte1, + const double t) throw() + : timeStamp (t), + data ((uint8*) &message), + size (1) +{ + data[0] = (uint8) byte1; + + // check that the length matches the data.. + jassert (getMessageLengthFromFirstByte ((uint8) byte1) == 1); +} + +MidiMessage::MidiMessage (const int byte1, + const int byte2, + const double t) throw() + : timeStamp (t), + data ((uint8*) &message), + size (2) +{ + data[0] = (uint8) byte1; + data[1] = (uint8) byte2; + + // check that the length matches the data.. + jassert (getMessageLengthFromFirstByte ((uint8) byte1) == 2); +} + +MidiMessage::MidiMessage (const int byte1, + const int byte2, + const int byte3, + const double t) throw() + : timeStamp (t), + data ((uint8*) &message), + size (3) +{ + data[0] = (uint8) byte1; + data[1] = (uint8) byte2; + data[2] = (uint8) byte3; + + // check that the length matches the data.. + jassert (getMessageLengthFromFirstByte ((uint8) byte1) == 3); +} + +MidiMessage::MidiMessage (const MidiMessage& other) throw() + : timeStamp (other.timeStamp), + message (other.message), + size (other.size) +{ + if (other.data != (uint8*) &other.message) + { + data = (uint8*) juce_malloc (size); + memcpy (data, other.data, size); + } + else + { + data = (uint8*) &message; + } +} + +MidiMessage::MidiMessage (const MidiMessage& other, + const double newTimeStamp) throw() + : timeStamp (newTimeStamp), + message (other.message), + size (other.size) +{ + if (other.data != (uint8*) &other.message) + { + data = (uint8*) juce_malloc (size); + memcpy (data, other.data, size); + } + else + { + data = (uint8*) &message; + } +} + +MidiMessage::MidiMessage (const uint8* src, + int sz, + int& numBytesUsed, + const uint8 lastStatusByte, + double t) throw() + : timeStamp (t), + data ((uint8*) &message), + message (0) +{ + unsigned int byte = (unsigned int) *src; + + if (byte < 0x80) + { + byte = (unsigned int) (uint8) lastStatusByte; + numBytesUsed = -1; + } + else + { + numBytesUsed = 0; + --sz; + ++src; + } + + if (byte >= 0x80) + { + if (byte == 0xf0) + { + const uint8* d = (const uint8*) src; + + while (d < src + sz) + { + if (*d >= 0x80) // stop if we hit a status byte, and don't include it in this message + { + if (*d == 0xf7) // include an 0xf7 if we hit one + ++d; + + break; + } + + ++d; + } + + size = 1 + (int) (d - src); + + data = (uint8*) juce_malloc (size); + *data = (uint8) byte; + memcpy (data + 1, src, size - 1); + } + else if (byte == 0xff) + { + int n; + const int bytesLeft = readVariableLengthVal (src + 1, n); + size = jmin (sz + 1, n + 2 + bytesLeft); + + data = (uint8*) juce_malloc (size); + *data = (uint8) byte; + memcpy (data + 1, src, size - 1); + } + else + { + size = getMessageLengthFromFirstByte ((uint8) byte); + *data = (uint8) byte; + + if (size > 1) + { + data[1] = src[0]; + + if (size > 2) + data[2] = src[1]; + } + } + + numBytesUsed += size; + } + else + { + message = 0; + size = 0; + } +} + +const MidiMessage& MidiMessage::operator= (const MidiMessage& other) throw() +{ + if (this == &other) + return *this; + + timeStamp = other.timeStamp; + size = other.size; + message = other.message; + + if (data != (uint8*) &message) + juce_free (data); + + if (other.data != (uint8*) &other.message) + { + data = (uint8*) juce_malloc (size); + memcpy (data, other.data, size); + } + else + { + data = (uint8*) &message; + } + + return *this; +} + +MidiMessage::~MidiMessage() throw() +{ + if (data != (uint8*) &message) + juce_free (data); +} + +int MidiMessage::getChannel() const throw() +{ + if ((data[0] & 0xf0) != 0xf0) + return (data[0] & 0xf) + 1; + else + return 0; +} + +bool MidiMessage::isForChannel (const int channel) const throw() +{ + return ((data[0] & 0xf) == channel - 1) + && ((data[0] & 0xf0) != 0xf0); +} + +void MidiMessage::setChannel (const int channel) throw() +{ + if ((data[0] & 0xf0) != (uint8) 0xf0) + data[0] = (uint8) ((data[0] & (uint8)0xf0) + | (uint8)(channel - 1)); +} + +bool MidiMessage::isNoteOn() const throw() +{ + return ((data[0] & 0xf0) == 0x90) + && (data[2] != 0); +} + +bool MidiMessage::isNoteOff() const throw() +{ + return ((data[0] & 0xf0) == 0x80) + || ((data[2] == 0) && ((data[0] & 0xf0) == 0x90)); +} + +bool MidiMessage::isNoteOnOrOff() const throw() +{ + const int d = data[0] & 0xf0; + return (d == 0x90) || (d == 0x80); +} + +int MidiMessage::getNoteNumber() const throw() +{ + return data[1]; +} + +void MidiMessage::setNoteNumber (const int newNoteNumber) throw() +{ + if (isNoteOnOrOff()) + data[1] = (uint8) jlimit (0, 127, newNoteNumber); +} + +uint8 MidiMessage::getVelocity() const throw() +{ + if (isNoteOnOrOff()) + return data[2]; + else + return 0; +} + +float MidiMessage::getFloatVelocity() const throw() +{ + return getVelocity() * (1.0f / 127.0f); +} + +void MidiMessage::setVelocity (const float newVelocity) throw() +{ + if (isNoteOnOrOff()) + data[2] = (uint8) jlimit (0, 0x7f, roundFloatToInt (newVelocity * 127.0f)); +} + +void MidiMessage::multiplyVelocity (const float scaleFactor) throw() +{ + if (isNoteOnOrOff()) + data[2] = (uint8) jlimit (0, 0x7f, roundFloatToInt (scaleFactor * data[2])); +} + +bool MidiMessage::isAftertouch() const throw() +{ + return (data[0] & 0xf0) == 0xa0; +} + +int MidiMessage::getAfterTouchValue() const throw() +{ + return data[2]; +} + +const MidiMessage MidiMessage::aftertouchChange (const int channel, + const int noteNum, + const int aftertouchValue) throw() +{ + jassert (channel > 0 && channel <= 16); + jassert (noteNum >= 0 && noteNum <= 127); + jassert (aftertouchValue >= 0 && aftertouchValue <= 127); + + return MidiMessage (0xa0 | jlimit (0, 15, channel - 1), + noteNum & 0x7f, + aftertouchValue & 0x7f); +} + +bool MidiMessage::isChannelPressure() const throw() +{ + return (data[0] & 0xf0) == 0xd0; +} + +int MidiMessage::getChannelPressureValue() const throw() +{ + jassert (isChannelPressure()); + + return data[1]; +} + +const MidiMessage MidiMessage::channelPressureChange (const int channel, + const int pressure) throw() +{ + jassert (channel > 0 && channel <= 16); + jassert (pressure >= 0 && pressure <= 127); + + return MidiMessage (0xd0 | jlimit (0, 15, channel - 1), + pressure & 0x7f); +} + +bool MidiMessage::isProgramChange() const throw() +{ + return (data[0] & 0xf0) == 0xc0; +} + +int MidiMessage::getProgramChangeNumber() const throw() +{ + return data[1]; +} + +const MidiMessage MidiMessage::programChange (const int channel, + const int programNumber) throw() +{ + jassert (channel > 0 && channel <= 16); + + return MidiMessage (0xc0 | jlimit (0, 15, channel - 1), + programNumber & 0x7f); +} + +bool MidiMessage::isPitchWheel() const throw() +{ + return (data[0] & 0xf0) == 0xe0; +} + +int MidiMessage::getPitchWheelValue() const throw() +{ + return data[1] | (data[2] << 7); +} + +const MidiMessage MidiMessage::pitchWheel (const int channel, + const int position) throw() +{ + jassert (channel > 0 && channel <= 16); + jassert (position >= 0 && position <= 0x3fff); + + return MidiMessage (0xe0 | jlimit (0, 15, channel - 1), + position & 127, + (position >> 7) & 127); +} + +bool MidiMessage::isController() const throw() +{ + return (data[0] & 0xf0) == 0xb0; +} + +int MidiMessage::getControllerNumber() const throw() +{ + jassert (isController()); + + return data[1]; +} + +int MidiMessage::getControllerValue() const throw() +{ + jassert (isController()); + + return data[2]; +} + +const MidiMessage MidiMessage::controllerEvent (const int channel, + const int controllerType, + const int value) throw() +{ + jassert (channel > 0 && channel <= 16); + + return MidiMessage (0xb0 | jlimit (0, 15, channel - 1), + controllerType & 127, + value & 127); +} + +const MidiMessage MidiMessage::noteOn (const int channel, + const int noteNumber, + const float velocity) throw() +{ + return noteOn (channel, noteNumber, (uint8)(velocity * 127.0f)); +} + +const MidiMessage MidiMessage::noteOn (const int channel, + const int noteNumber, + const uint8 velocity) throw() +{ + jassert (channel > 0 && channel <= 16); + jassert (noteNumber >= 0 && noteNumber <= 127); + + return MidiMessage (0x90 | jlimit (0, 15, channel - 1), + noteNumber & 127, + jlimit (0, 127, roundFloatToInt (velocity))); +} + +const MidiMessage MidiMessage::noteOff (const int channel, + const int noteNumber) throw() +{ + jassert (channel > 0 && channel <= 16); + jassert (noteNumber >= 0 && noteNumber <= 127); + + return MidiMessage (0x80 | jlimit (0, 15, channel - 1), noteNumber & 127, 0); +} + +const MidiMessage MidiMessage::allNotesOff (const int channel) throw() +{ + jassert (channel > 0 && channel <= 16); + + return controllerEvent (channel, 123, 0); +} + +bool MidiMessage::isAllNotesOff() const throw() +{ + return (data[0] & 0xf0) == 0xb0 + && data[1] == 123; +} + +const MidiMessage MidiMessage::allSoundOff (const int channel) throw() +{ + return controllerEvent (channel, 120, 0); +} + +bool MidiMessage::isAllSoundOff() const throw() +{ + return (data[0] & 0xf0) == 0xb0 + && data[1] == 120; +} + +const MidiMessage MidiMessage::allControllersOff (const int channel) throw() +{ + return controllerEvent (channel, 121, 0); +} + +const MidiMessage MidiMessage::masterVolume (const float volume) throw() +{ + const int vol = jlimit (0, 0x3fff, roundFloatToInt (volume * 0x4000)); + + uint8 buf[8]; + buf[0] = 0xf0; + buf[1] = 0x7f; + buf[2] = 0x7f; + buf[3] = 0x04; + buf[4] = 0x01; + buf[5] = (uint8) (vol & 0x7f); + buf[6] = (uint8) (vol >> 7); + buf[7] = 0xf7; + + return MidiMessage (buf, 8); +} + +//============================================================================== +bool MidiMessage::isSysEx() const throw() +{ + return *data == 0xf0; +} + +const MidiMessage MidiMessage::createSysExMessage (const uint8* sysexData, + const int dataSize) throw() +{ + MemoryBlock mm (dataSize + 2); + uint8* const m = (uint8*) mm.getData(); + + m[0] = 0xf0; + memcpy (m + 1, sysexData, dataSize); + m[dataSize + 1] = 0xf7; + + return MidiMessage (m, dataSize + 2); +} + +const uint8* MidiMessage::getSysExData() const throw() +{ + return (isSysEx()) ? getRawData() + 1 + : 0; +} + +int MidiMessage::getSysExDataSize() const throw() +{ + return (isSysEx()) ? size - 2 + : 0; +} + +bool MidiMessage::isMetaEvent() const throw() +{ + return *data == 0xff; +} + +bool MidiMessage::isActiveSense() const throw() +{ + return *data == 0xfe; +} + +//============================================================================== +int MidiMessage::getMetaEventType() const throw() +{ + if (*data != 0xff) + return -1; + else + return data[1]; +} + +int MidiMessage::getMetaEventLength() const throw() +{ + if (*data == 0xff) + { + int n; + return jmin (size - 2, readVariableLengthVal (data + 2, n)); + } + + return 0; +} + +const uint8* MidiMessage::getMetaEventData() const throw() +{ + int n; + const uint8* d = data + 2; + readVariableLengthVal (d, n); + return d + n; +} + +bool MidiMessage::isTrackMetaEvent() const throw() +{ + return getMetaEventType() == 0; +} + +bool MidiMessage::isEndOfTrackMetaEvent() const throw() +{ + return getMetaEventType() == 47; +} + +bool MidiMessage::isTextMetaEvent() const throw() +{ + const int t = getMetaEventType(); + + return t > 0 && t < 16; +} + +const String MidiMessage::getTextFromTextMetaEvent() const throw() +{ + return String ((const char*) getMetaEventData(), + getMetaEventLength()); +} + +bool MidiMessage::isTrackNameEvent() const throw() +{ + return (data[1] == 3) + && (*data == 0xff); +} + +bool MidiMessage::isTempoMetaEvent() const throw() +{ + return (data[1] == 81) + && (*data == 0xff); +} + +bool MidiMessage::isMidiChannelMetaEvent() const throw() +{ + return (data[1] == 0x20) + && (*data == 0xff) + && (data[2] == 1); +} + +int MidiMessage::getMidiChannelMetaEventChannel() const throw() +{ + return data[3] + 1; +} + +double MidiMessage::getTempoSecondsPerQuarterNote() const throw() +{ + if (! isTempoMetaEvent()) + return 0.0; + + const uint8* const d = getMetaEventData(); + + return (((unsigned int) d[0] << 16) + | ((unsigned int) d[1] << 8) + | d[2]) + / 1000000.0; +} + +double MidiMessage::getTempoMetaEventTickLength (const short timeFormat) const throw() +{ + if (timeFormat > 0) + { + if (! isTempoMetaEvent()) + return 0.5 / timeFormat; + + return getTempoSecondsPerQuarterNote() / timeFormat; + } + else + { + const int frameCode = (-timeFormat) >> 8; + double framesPerSecond; + + switch (frameCode) + { + case 24: framesPerSecond = 24.0; break; + case 25: framesPerSecond = 25.0; break; + case 29: framesPerSecond = 29.97; break; + case 30: framesPerSecond = 30.0; break; + default: framesPerSecond = 30.0; break; + } + + return (1.0 / framesPerSecond) / (timeFormat & 0xff); + } +} + +const MidiMessage MidiMessage::tempoMetaEvent (int microsecondsPerQuarterNote) throw() +{ + uint8 d[8]; + d[0] = 0xff; + d[1] = 81; + d[2] = 3; + d[3] = (uint8) (microsecondsPerQuarterNote >> 16); + d[4] = (uint8) ((microsecondsPerQuarterNote >> 8) & 0xff); + d[5] = (uint8) (microsecondsPerQuarterNote & 0xff); + + return MidiMessage (d, 6, 0.0); +} + +bool MidiMessage::isTimeSignatureMetaEvent() const throw() +{ + return (data[1] == 0x58) + && (*data == (uint8) 0xff); +} + +void MidiMessage::getTimeSignatureInfo (int& numerator, + int& denominator) const throw() +{ + if (isTimeSignatureMetaEvent()) + { + const uint8* const d = getMetaEventData(); + numerator = d[0]; + denominator = 1 << d[1]; + } + else + { + numerator = 4; + denominator = 4; + } +} + +const MidiMessage MidiMessage::timeSignatureMetaEvent (const int numerator, + const int denominator) throw() +{ + uint8 d[8]; + d[0] = 0xff; + d[1] = 0x58; + d[2] = 0x04; + d[3] = (uint8) numerator; + + int n = 1; + int powerOfTwo = 0; + + while (n < denominator) + { + n <<= 1; + ++powerOfTwo; + } + + d[4] = (uint8) powerOfTwo; + d[5] = 0x01; + d[6] = 96; + + return MidiMessage (d, 7, 0.0); +} + +const MidiMessage MidiMessage::midiChannelMetaEvent (const int channel) throw() +{ + uint8 d[8]; + d[0] = 0xff; + d[1] = 0x20; + d[2] = 0x01; + d[3] = (uint8) jlimit (0, 0xff, channel - 1); + + return MidiMessage (d, 4, 0.0); +} + +bool MidiMessage::isKeySignatureMetaEvent() const throw() +{ + return getMetaEventType() == 89; +} + +int MidiMessage::getKeySignatureNumberOfSharpsOrFlats() const throw() +{ + return (int)(*getMetaEventData()); +} + +const MidiMessage MidiMessage::endOfTrack() throw() +{ + return MidiMessage (0xff, 0x2f, 0, 0.0); +} + +//============================================================================== +bool MidiMessage::isSongPositionPointer() const throw() +{ + return *data == 0xf2; +} + +int MidiMessage::getSongPositionPointerMidiBeat() const throw() +{ + return data[1] | (data[2] << 7); +} + +const MidiMessage MidiMessage::songPositionPointer (const int positionInMidiBeats) throw() +{ + return MidiMessage (0xf2, + positionInMidiBeats & 127, + (positionInMidiBeats >> 7) & 127); +} + +bool MidiMessage::isMidiStart() const throw() +{ + return *data == 0xfa; +} + +const MidiMessage MidiMessage::midiStart() throw() +{ + return MidiMessage (0xfa); +} + +bool MidiMessage::isMidiContinue() const throw() +{ + return *data == 0xfb; +} + +const MidiMessage MidiMessage::midiContinue() throw() +{ + return MidiMessage (0xfb); +} + +bool MidiMessage::isMidiStop() const throw() +{ + return *data == 0xfc; +} + +const MidiMessage MidiMessage::midiStop() throw() +{ + return MidiMessage (0xfc); +} + +bool MidiMessage::isMidiClock() const throw() +{ + return *data == 0xf8; +} + +const MidiMessage MidiMessage::midiClock() throw() +{ + return MidiMessage (0xf8); +} + +bool MidiMessage::isQuarterFrame() const throw() +{ + return *data == 0xf1; +} + +int MidiMessage::getQuarterFrameSequenceNumber() const throw() +{ + return ((int) data[1]) >> 4; +} + +int MidiMessage::getQuarterFrameValue() const throw() +{ + return ((int) data[1]) & 0x0f; +} + +const MidiMessage MidiMessage::quarterFrame (const int sequenceNumber, + const int value) throw() +{ + return MidiMessage (0xf1, (sequenceNumber << 4) | value); +} + +bool MidiMessage::isFullFrame() const throw() +{ + return data[0] == 0xf0 + && data[1] == 0x7f + && size >= 10 + && data[3] == 0x01 + && data[4] == 0x01; +} + +void MidiMessage::getFullFrameParameters (int& hours, + int& minutes, + int& seconds, + int& frames, + MidiMessage::SmpteTimecodeType& timecodeType) const throw() +{ + jassert (isFullFrame()); + + timecodeType = (SmpteTimecodeType) (data[5] >> 5); + hours = data[5] & 0x1f; + minutes = data[6]; + seconds = data[7]; + frames = data[8]; +} + +const MidiMessage MidiMessage::fullFrame (const int hours, + const int minutes, + const int seconds, + const int frames, + MidiMessage::SmpteTimecodeType timecodeType) +{ + uint8 d[10]; + d[0] = 0xf0; + d[1] = 0x7f; + d[2] = 0x7f; + d[3] = 0x01; + d[4] = 0x01; + d[5] = (uint8) ((hours & 0x01f) | (timecodeType << 5)); + d[6] = (uint8) minutes; + d[7] = (uint8) seconds; + d[8] = (uint8) frames; + d[9] = 0xf7; + + return MidiMessage (d, 10, 0.0); +} + +bool MidiMessage::isMidiMachineControlMessage() const throw() +{ + return data[0] == 0xf0 + && data[1] == 0x7f + && data[3] == 0x06 + && size > 5; +} + +MidiMessage::MidiMachineControlCommand MidiMessage::getMidiMachineControlCommand() const throw() +{ + jassert (isMidiMachineControlMessage()); + + return (MidiMachineControlCommand) data[4]; +} + +const MidiMessage MidiMessage::midiMachineControlCommand (MidiMessage::MidiMachineControlCommand command) +{ + uint8 d[6]; + d[0] = 0xf0; + d[1] = 0x7f; + d[2] = 0x00; + d[3] = 0x06; + d[4] = (uint8) command; + d[5] = 0xf7; + + return MidiMessage (d, 6, 0.0); +} + +//============================================================================== +bool MidiMessage::isMidiMachineControlGoto (int& hours, + int& minutes, + int& seconds, + int& frames) const throw() +{ + if (size >= 12 + && data[0] == 0xf0 + && data[1] == 0x7f + && data[3] == 0x06 + && data[4] == 0x44 + && data[5] == 0x06 + && data[6] == 0x01) + { + hours = data[7] % 24; // (that some machines send out hours > 24) + minutes = data[8]; + seconds = data[9]; + frames = data[10]; + + return true; + } + + return false; +} + +const MidiMessage MidiMessage::midiMachineControlGoto (int hours, + int minutes, + int seconds, + int frames) +{ + uint8 d[12]; + d[0] = 0xf0; + d[1] = 0x7f; + d[2] = 0x00; + d[3] = 0x06; + d[4] = 0x44; + d[5] = 0x06; + d[6] = 0x01; + d[7] = (uint8) hours; + d[8] = (uint8) minutes; + d[9] = (uint8) seconds; + d[10] = (uint8) frames; + d[11] = 0xf7; + + return MidiMessage (d, 12, 0.0); +} + +//============================================================================== +const String MidiMessage::getMidiNoteName (int note, + bool useSharps, + bool includeOctaveNumber, + int octaveNumForMiddleC) throw() +{ + static const char* const sharpNoteNames[] = { "C", "C#", "D", "D#", "E", + "F", "F#", "G", "G#", "A", + "A#", "B" }; + + static const char* const flatNoteNames[] = { "C", "Db", "D", "Eb", "E", + "F", "Gb", "G", "Ab", "A", + "Bb", "B" }; + + if (note >= 0 && note < 128) + { + const String s ((useSharps) ? sharpNoteNames [note % 12] + : flatNoteNames [note % 12]); + + if (includeOctaveNumber) + return s + String (note / 12 + (octaveNumForMiddleC - 5)); + else + return s; + } + + return String::empty; +} + +const double MidiMessage::getMidiNoteInHertz (int noteNumber) throw() +{ + noteNumber -= 12 * 6 + 9; // now 0 = A440 + return 440.0 * pow (2.0, noteNumber / 12.0); +} + +const String MidiMessage::getGMInstrumentName (int n) throw() +{ + const char *names[] = + { + "Acoustic Grand Piano", "Bright Acoustic Piano", "Electric Grand Piano", "Honky-tonk Piano", + "Electric Piano 1", "Electric Piano 2", "Harpsichord", "Clavinet", "Celesta", "Glockenspiel", + "Music Box", "Vibraphone", "Marimba", "Xylophone", "Tubular Bells", "Dulcimer", "Drawbar Organ", + "Percussive Organ", "Rock Organ", "Church Organ", "Reed Organ", "Accordion", "Harmonica", + "Tango Accordion", "Acoustic Guitar (nylon)", "Acoustic Guitar (steel)", "Electric Guitar (jazz)", + "Electric Guitar (clean)", "Electric Guitar (mute)", "Overdriven Guitar", "Distortion Guitar", + "Guitar Harmonics", "Acoustic Bass", "Electric Bass (finger)", "Electric Bass (pick)", + "Fretless Bass", "Slap Bass 1", "Slap Bass 2", "Synth Bass 1", "Synth Bass 2", "Violin", + "Viola", "Cello", "Contrabass", "Tremolo Strings", "Pizzicato Strings", "Orchestral Harp", + "Timpani", "String Ensemble 1", "String Ensemble 2", "SynthStrings 1", "SynthStrings 2", + "Choir Aahs", "Voice Oohs", "Synth Voice", "Orchestra Hit", "Trumpet", "Trombone", "Tuba", + "Muted Trumpet", "French Horn", "Brass Section", "SynthBrass 1", "SynthBrass 2", "Soprano Sax", + "Alto Sax", "Tenor Sax", "Baritone Sax", "Oboe", "English Horn", "Bassoon", "Clarinet", + "Piccolo", "Flute", "Recorder", "Pan Flute", "Blown Bottle", "Shakuhachi", "Whistle", + "Ocarina", "Lead 1 (square)", "Lead 2 (sawtooth)", "Lead 3 (calliope)", "Lead 4 (chiff)", + "Lead 5 (charang)", "Lead 6 (voice)", "Lead 7 (fifths)", "Lead 8 (bass+lead)", "Pad 1 (new age)", + "Pad 2 (warm)", "Pad 3 (polysynth)", "Pad 4 (choir)", "Pad 5 (bowed)", "Pad 6 (metallic)", + "Pad 7 (halo)", "Pad 8 (sweep)", "FX 1 (rain)", "FX 2 (soundtrack)", "FX 3 (crystal)", + "FX 4 (atmosphere)", "FX 5 (brightness)", "FX 6 (goblins)", "FX 7 (echoes)", "FX 8 (sci-fi)", + "Sitar", "Banjo", "Shamisen", "Koto", "Kalimba", "Bag pipe", "Fiddle", "Shanai", "Tinkle Bell", + "Agogo", "Steel Drums", "Woodblock", "Taiko Drum", "Melodic Tom", "Synth Drum", "Reverse Cymbal", + "Guitar Fret Noise", "Breath Noise", "Seashore", "Bird Tweet", "Telephone Ring", "Helicopter", + "Applause", "Gunshot" + }; + + return (n >= 0 && n < 128) ? names[n] + : (const char*)0; +} + +const String MidiMessage::getGMInstrumentBankName (int n) throw() +{ + const char* names[] = + { + "Piano", "Chromatic Percussion", "Organ", "Guitar", + "Bass", "Strings", "Ensemble", "Brass", + "Reed", "Pipe", "Synth Lead", "Synth Pad", + "Synth Effects", "Ethnic", "Percussive", "Sound Effects" + }; + + return (n >= 0 && n <= 15) ? names[n] + : (const char*)0; +} + +const String MidiMessage::getRhythmInstrumentName (int n) throw() +{ + const char* names[] = + { + "Acoustic Bass Drum", "Bass Drum 1", "Side Stick", "Acoustic Snare", + "Hand Clap", "Electric Snare", "Low Floor Tom", "Closed Hi-Hat", "High Floor Tom", + "Pedal Hi-Hat", "Low Tom", "Open Hi-Hat", "Low-Mid Tom", "Hi-Mid Tom", "Crash Cymbal 1", + "High Tom", "Ride Cymbal 1", "Chinese Cymbal", "Ride Bell", "Tambourine", "Splash Cymbal", + "Cowbell", "Crash Cymbal 2", "Vibraslap", "Ride Cymbal 2", "Hi Bongo", "Low Bongo", + "Mute Hi Conga", "Open Hi Conga", "Low Conga", "High Timbale", "Low Timbale", "High Agogo", + "Low Agogo", "Cabasa", "Maracas", "Short Whistle", "Long Whistle", "Short Guiro", + "Long Guiro", "Claves", "Hi Wood Block", "Low Wood Block", "Mute Cuica", "Open Cuica", + "Mute Triangle", "Open Triangle" + }; + + return (n >= 35 && n <= 81) ? names [n - 35] + : (const char*)0; +} + +const String MidiMessage::getControllerName (int n) throw() +{ + const char* names[] = + { + "Bank Select", "Modulation Wheel (coarse)", "Breath controller (coarse)", + 0, "Foot Pedal (coarse)", "Portamento Time (coarse)", + "Data Entry (coarse)", "Volume (coarse)", "Balance (coarse)", + 0, "Pan position (coarse)", "Expression (coarse)", "Effect Control 1 (coarse)", + "Effect Control 2 (coarse)", 0, 0, "General Purpose Slider 1", "General Purpose Slider 2", + "General Purpose Slider 3", "General Purpose Slider 4", 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, "Bank Select (fine)", "Modulation Wheel (fine)", "Breath controller (fine)", + 0, "Foot Pedal (fine)", "Portamento Time (fine)", "Data Entry (fine)", "Volume (fine)", + "Balance (fine)", 0, "Pan position (fine)", "Expression (fine)", "Effect Control 1 (fine)", + "Effect Control 2 (fine)", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "Hold Pedal (on/off)", "Portamento (on/off)", "Sustenuto Pedal (on/off)", "Soft Pedal (on/off)", + "Legato Pedal (on/off)", "Hold 2 Pedal (on/off)", "Sound Variation", "Sound Timbre", + "Sound Release Time", "Sound Attack Time", "Sound Brightness", "Sound Control 6", + "Sound Control 7", "Sound Control 8", "Sound Control 9", "Sound Control 10", + "General Purpose Button 1 (on/off)", "General Purpose Button 2 (on/off)", + "General Purpose Button 3 (on/off)", "General Purpose Button 4 (on/off)", + 0, 0, 0, 0, 0, 0, 0, "Reverb Level", "Tremolo Level", "Chorus Level", "Celeste Level", + "Phaser Level", "Data Button increment", "Data Button decrement", "Non-registered Parameter (fine)", + "Non-registered Parameter (coarse)", "Registered Parameter (fine)", "Registered Parameter (coarse)", + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "All Sound Off", "All Controllers Off", + "Local Keyboard (on/off)", "All Notes Off", "Omni Mode Off", "Omni Mode On", "Mono Operation", + "Poly Operation" + }; + + return (n >= 0 && n < 128) ? names[n] + : (const char*)0; +} + +END_JUCE_NAMESPACE diff --git a/src/juce_appframework/gui/components/keyboard/juce_KeyPress.cpp b/src/juce_appframework/gui/components/keyboard/juce_KeyPress.cpp index 1993b52f40..a0d091941e 100644 --- a/src/juce_appframework/gui/components/keyboard/juce_KeyPress.cpp +++ b/src/juce_appframework/gui/components/keyboard/juce_KeyPress.cpp @@ -188,9 +188,29 @@ const KeyPress KeyPress::createFromDescription (const String& desc) if (key == 0) { // see if it's a numpad key.. - for (int i = 0; i < 10; ++i) - if (desc.containsWholeWordIgnoreCase (numberPadPrefix + String (i))) - key = numberPad0 + i; + if (desc.containsIgnoreCase (numberPadPrefix)) + { + const tchar lastChar = desc.trimEnd().getLastCharacter(); + + if (lastChar >= T('0') && lastChar <= T('9')) + key = numberPad0 + lastChar - T('0'); + else if (lastChar == T('+')) + key = numberPadAdd; + else if (lastChar == T('-')) + key = numberPadSubtract; + else if (lastChar == T('*')) + key = numberPadMultiply; + else if (lastChar == T('/')) + key = numberPadDivide; + else if (lastChar == T('.')) + key = numberPadDecimalPoint; + else if (lastChar == T('=')) + key = numberPadEquals; + else if (desc.endsWith (T("separator"))) + key = numberPadSeparator; + else if (desc.endsWith (T("delete"))) + key = numberPadDelete; + } if (key == 0) { @@ -208,13 +228,9 @@ const KeyPress KeyPress::createFromDescription (const String& desc) .getHexValue32(); if (hexCode > 0) - { key = hexCode; - } else - { key = CharacterFunctions::toUpperCase (desc.getLastCharacter()); - } } } } @@ -257,6 +273,20 @@ const String KeyPress::getTextDescription() const desc << numberPadPrefix << (keyCode - numberPad0); else if (keyCode >= 33 && keyCode < 176) desc += CharacterFunctions::toUpperCase ((tchar) keyCode); + else if (keyCode == numberPadAdd) + desc << numberPadPrefix << '+'; + else if (keyCode == numberPadSubtract) + desc << numberPadPrefix << '-'; + else if (keyCode == numberPadMultiply) + desc << numberPadPrefix << '*'; + else if (keyCode == numberPadDivide) + desc << numberPadPrefix << '/'; + else if (keyCode == numberPadSeparator) + desc << numberPadPrefix << "separator"; + else if (keyCode == numberPadDecimalPoint) + desc << numberPadPrefix << '.'; + else if (keyCode == numberPadDelete) + desc << numberPadPrefix << "delete"; else desc << '#' << String::toHexString (keyCode); } diff --git a/src/juce_appframework/gui/components/keyboard/juce_KeyPress.h b/src/juce_appframework/gui/components/keyboard/juce_KeyPress.h index 84782fc0f7..97b5e9b08f 100644 --- a/src/juce_appframework/gui/components/keyboard/juce_KeyPress.h +++ b/src/juce_appframework/gui/components/keyboard/juce_KeyPress.h @@ -231,6 +231,8 @@ public: static const int numberPadDivide; /**< key-code for the divide sign on the numeric keypad. */ static const int numberPadSeparator; /**< key-code for the comma on the numeric keypad. */ static const int numberPadDecimalPoint; /**< key-code for the decimal point sign on the numeric keypad. */ + static const int numberPadEquals; /**< key-code for the equals key on the numeric keypad. */ + static const int numberPadDelete; /**< key-code for the delete key on the numeric keypad. */ static const int playKey; /**< key-code for a multimedia 'play' key, (not all keyboards will have one) */ static const int stopKey; /**< key-code for a multimedia 'stop' key, (not all keyboards will have one) */ diff --git a/src/juce_appframework/gui/components/keyboard/juce_ModifierKeys.cpp b/src/juce_appframework/gui/components/keyboard/juce_ModifierKeys.cpp index c774e6c9be..d9378e0cbd 100644 --- a/src/juce_appframework/gui/components/keyboard/juce_ModifierKeys.cpp +++ b/src/juce_appframework/gui/components/keyboard/juce_ModifierKeys.cpp @@ -36,32 +36,6 @@ BEGIN_JUCE_NAMESPACE #include "juce_ModifierKeys.h" -//============================================================================== -const int ModifierKeys::shiftModifier = 1; -const int ModifierKeys::ctrlModifier = 2; -const int ModifierKeys::altModifier = 4; -const int ModifierKeys::leftButtonModifier = 16; -const int ModifierKeys::rightButtonModifier = 32; -const int ModifierKeys::middleButtonModifier = 64; - -#if JUCE_MAC - const int ModifierKeys::commandModifier = 8; - const int ModifierKeys::popupMenuClickModifier = ModifierKeys::rightButtonModifier - | ModifierKeys::ctrlModifier; -#else - const int ModifierKeys::commandModifier = ModifierKeys::ctrlModifier; - const int ModifierKeys::popupMenuClickModifier = ModifierKeys::rightButtonModifier; -#endif - -const int ModifierKeys::allKeyboardModifiers = ModifierKeys::shiftModifier - | ModifierKeys::ctrlModifier - | ModifierKeys::altModifier - | ModifierKeys::commandModifier; - -const int ModifierKeys::allMouseButtonModifiers = ModifierKeys::leftButtonModifier - | ModifierKeys::rightButtonModifier - | ModifierKeys::middleButtonModifier; - //============================================================================== ModifierKeys::ModifierKeys (const int flags_) throw() : flags (flags_) diff --git a/src/juce_appframework/gui/components/keyboard/juce_ModifierKeys.h b/src/juce_appframework/gui/components/keyboard/juce_ModifierKeys.h index b216f8e7a9..0bf1a75625 100644 --- a/src/juce_appframework/gui/components/keyboard/juce_ModifierKeys.h +++ b/src/juce_appframework/gui/components/keyboard/juce_ModifierKeys.h @@ -113,39 +113,57 @@ public: inline bool isAltDown() const throw() { return (flags & altModifier) != 0; } //============================================================================== - // modifier bitmasks - - /** Shift key flag. */ - static const int shiftModifier; - /** CTRL key flag. */ - static const int ctrlModifier; - /** ALT key flag. */ - static const int altModifier; - /** Left mouse button flag. */ - static const int leftButtonModifier; - /** Right mouse button flag. */ - static const int rightButtonModifier; - /** Middle mouse button flag. */ - static const int middleButtonModifier; - /** Command key flag - on windows this is the same as the CTRL key flag. */ - static const int commandModifier; - - /** Popup menu flag - on windows this is the same as rightButtonModifier, on the - Mac it's the same as (rightButtonModifier | ctrlModifier). */ - static const int popupMenuClickModifier; - - /** Represents a combination of all the shift, alt, ctrl and command key modifiers. */ - static const int allKeyboardModifiers; - - /** Represents a combination of all the mouse buttons at once. */ - static const int allMouseButtonModifiers; + /** Flags that represent the different keys. */ + enum Flags + { + /** Shift key flag. */ + shiftModifier = 1, + + /** CTRL key flag. */ + ctrlModifier = 2, + + /** ALT key flag. */ + altModifier = 4, + + /** Left mouse button flag. */ + leftButtonModifier = 16, + + /** Right mouse button flag. */ + rightButtonModifier = 32, + + /** Middle mouse button flag. */ + middleButtonModifier = 64, + +#if JUCE_MAC + /** Command key flag - on windows this is the same as the CTRL key flag. */ + commandModifier = 8, + + /** Popup menu flag - on windows this is the same as rightButtonModifier, on the + Mac it's the same as (rightButtonModifier | ctrlModifier). */ + popupMenuClickModifier = rightButtonModifier | ctrlModifier, +#else + /** Command key flag - on windows this is the same as the CTRL key flag. */ + commandModifier = ctrlModifier, + + /** Popup menu flag - on windows this is the same as rightButtonModifier, on the + Mac it's the same as (rightButtonModifier | ctrlModifier). */ + popupMenuClickModifier = rightButtonModifier, +#endif + + /** Represents a combination of all the shift, alt, ctrl and command key modifiers. */ + allKeyboardModifiers = shiftModifier | ctrlModifier | altModifier | commandModifier, + + /** Represents a combination of all the mouse buttons at once. */ + allMouseButtonModifiers = leftButtonModifier | rightButtonModifier | middleButtonModifier, + }; + //============================================================================== /** Returns the raw flags for direct testing. */ - inline int getRawFlags() const throw() { return flags; } + inline int getRawFlags() const throw() { return flags; } /** Tests a combination of flags and returns true if any of them are set. */ - inline bool testFlags (const int flagsToTest) const throw() { return (flags & flagsToTest) != 0; } + inline bool testFlags (const int flagsToTest) const throw() { return (flags & flagsToTest) != 0; } //============================================================================== /** Creates a ModifierKeys object to represent the last-known state of the @@ -165,7 +183,7 @@ public: This is only needed in special circumstances for up-to-date modifier information at times when the app's event loop isn't running normally. */ - static const ModifierKeys getCurrentModifiersRealtime(); + static const ModifierKeys getCurrentModifiersRealtime() throw(); private: diff --git a/src/juce_core/text/juce_XmlDocument.cpp b/src/juce_core/text/juce_XmlDocument.cpp index 661256b3f8..24db693497 100644 --- a/src/juce_core/text/juce_XmlDocument.cpp +++ b/src/juce_core/text/juce_XmlDocument.cpp @@ -48,7 +48,7 @@ XmlInputSource::~XmlInputSource() //============================================================================== -static bool isXmlIdentifierChar_Slow (const tchar c) +static bool isXmlIdentifierChar_Slow (const tchar c) throw() { return CharacterFunctions::isLetterOrDigit (c) || c == T('_') diff --git a/src/juce_core/text/juce_XmlElement.cpp b/src/juce_core/text/juce_XmlElement.cpp index 177e2ea778..9491b583e2 100644 --- a/src/juce_core/text/juce_XmlElement.cpp +++ b/src/juce_core/text/juce_XmlElement.cpp @@ -40,7 +40,7 @@ BEGIN_JUCE_NAMESPACE //============================================================================== -XmlElement::XmlAttributeNode::XmlAttributeNode (const XmlAttributeNode& other) +XmlElement::XmlAttributeNode::XmlAttributeNode (const XmlAttributeNode& other) throw() : name (other.name), value (other.value), next (0) @@ -48,7 +48,7 @@ XmlElement::XmlAttributeNode::XmlAttributeNode (const XmlAttributeNode& other) } XmlElement::XmlAttributeNode::XmlAttributeNode (const String& name_, - const String& value_) + const String& value_) throw() : name (name_), value (value_), next (0) @@ -1057,7 +1057,7 @@ XmlElement* XmlElement::findParentElementOf (const XmlElement* const elementToLo return 0; } -XmlElement** XmlElement::getChildElementsAsArray (const int num) const +XmlElement** XmlElement::getChildElementsAsArray (const int num) const throw() { XmlElement** const elems = new XmlElement* [num]; @@ -1073,7 +1073,7 @@ XmlElement** XmlElement::getChildElementsAsArray (const int num) const return elems; } -void XmlElement::reorderChildElements (XmlElement** const elems, const int num) +void XmlElement::reorderChildElements (XmlElement** const elems, const int num) throw() { XmlElement* e = firstChildElement = elems[0]; diff --git a/src/juce_core/text/juce_XmlElement.h b/src/juce_core/text/juce_XmlElement.h index 6eaaf554fe..a694d0b6a5 100644 --- a/src/juce_core/text/juce_XmlElement.h +++ b/src/juce_core/text/juce_XmlElement.h @@ -657,8 +657,8 @@ private: struct XmlAttributeNode { - XmlAttributeNode (const XmlAttributeNode& other); - XmlAttributeNode (const String& name, const String& value); + XmlAttributeNode (const XmlAttributeNode& other) throw(); + XmlAttributeNode (const String& name, const String& value) throw(); String name, value; XmlAttributeNode* next; @@ -673,8 +673,8 @@ private: void writeElementAsText (OutputStream& out, const int indentationLevel) const throw(); - XmlElement** getChildElementsAsArray (const int) const; - void reorderChildElements (XmlElement** const, const int); + XmlElement** getChildElementsAsArray (const int) const throw(); + void reorderChildElements (XmlElement** const, const int) throw(); }; diff --git a/src/juce_core/threads/juce_Thread.cpp b/src/juce_core/threads/juce_Thread.cpp index 67bd1f601a..29d6613bcd 100644 --- a/src/juce_core/threads/juce_Thread.cpp +++ b/src/juce_core/threads/juce_Thread.cpp @@ -50,7 +50,7 @@ static VoidArray runningThreads (4); static CriticalSection runningThreadsLock; //============================================================================== -void Thread::threadEntryPoint (Thread* thread) +void Thread::threadEntryPoint (Thread* const thread) throw() { runningThreadsLock.enter(); runningThreads.add (thread); @@ -106,7 +106,7 @@ Thread::~Thread() } //============================================================================== -void Thread::startThread() +void Thread::startThread() throw() { const ScopedLock sl (startStopLock); @@ -120,7 +120,7 @@ void Thread::startThread() } } -void Thread::startThread (const int priority) +void Thread::startThread (const int priority) throw() { const ScopedLock sl (startStopLock); @@ -146,7 +146,7 @@ void Thread::signalThreadShouldExit() throw() threadShouldExit_ = true; } -bool Thread::waitForThreadToExit (const int timeOutMilliseconds) const +bool Thread::waitForThreadToExit (const int timeOutMilliseconds) const throw() { // Doh! So how exactly do you expect this thread to wait for itself to stop?? jassert (getThreadId() != getCurrentThreadId()); @@ -165,7 +165,7 @@ bool Thread::waitForThreadToExit (const int timeOutMilliseconds) const return true; } -void Thread::stopThread (const int timeOutMilliseconds) +void Thread::stopThread (const int timeOutMilliseconds) throw() { const ScopedLock sl (startStopLock); @@ -196,7 +196,7 @@ void Thread::stopThread (const int timeOutMilliseconds) } //============================================================================== -void Thread::setPriority (const int priority) +void Thread::setPriority (const int priority) throw() { const ScopedLock sl (startStopLock); @@ -204,12 +204,12 @@ void Thread::setPriority (const int priority) juce_setThreadPriority (threadHandle_, priority); } -void Thread::setCurrentThreadPriority (const int priority) +void Thread::setCurrentThreadPriority (const int priority) throw() { juce_setThreadPriority (0, priority); } -void Thread::setAffinityMask (const uint32 affinityMask) +void Thread::setAffinityMask (const uint32 affinityMask) throw() { affinityMask_ = affinityMask; } @@ -231,12 +231,12 @@ void Thread::notify() const throw() } //============================================================================== -int Thread::getNumRunningThreads() +int Thread::getNumRunningThreads() throw() { return runningThreads.size(); } -void Thread::stopAllThreads (const int timeoutInMillisecs) +void Thread::stopAllThreads (const int timeoutInMillisecs) throw() { while (getNumRunningThreads() > 0) { diff --git a/src/juce_core/threads/juce_Thread.h b/src/juce_core/threads/juce_Thread.h index 57757ec317..91f9d65894 100644 --- a/src/juce_core/threads/juce_Thread.h +++ b/src/juce_core/threads/juce_Thread.h @@ -92,7 +92,7 @@ public: @see stopThread */ - void startThread(); + void startThread() throw(); /** Starts the thread with a given priority. @@ -101,7 +101,7 @@ public: @see startThread, setPriority */ - void startThread (const int priority); + void startThread (const int priority) throw(); /** Attempts to stop the thread running. @@ -121,7 +121,7 @@ public: value in here will wait forever. @see signalThreadShouldExit, threadShouldExit, waitForThreadToExit, isThreadRunning */ - void stopThread (const int timeOutMilliseconds); + void stopThread (const int timeOutMilliseconds) throw(); //============================================================================== /** Returns true if the thread is currently active */ @@ -154,7 +154,7 @@ public: is less than zero, it will wait forever. @returns true if the thread exits, or false if the timeout expires first. */ - bool waitForThreadToExit (const int timeOutMilliseconds) const; + bool waitForThreadToExit (const int timeOutMilliseconds) const throw(); //============================================================================== /** Changes the thread's priority. @@ -162,7 +162,7 @@ public: @param priority the new priority, in the range 0 (lowest) to 10 (highest). A priority of 5 is normal. */ - void setPriority (const int priority); + void setPriority (const int priority) throw(); /** Changes the priority of the caller thread. @@ -170,7 +170,7 @@ public: @see setPriority */ - static void setCurrentThreadPriority (const int priority); + static void setCurrentThreadPriority (const int priority) throw(); //============================================================================== /** Sets the affinity mask for the thread. @@ -180,7 +180,7 @@ public: @see setCurrentThreadAffinityMask */ - void setAffinityMask (const uint32 affinityMask); + void setAffinityMask (const uint32 affinityMask) throw(); /** Changes the affinity mask for the caller thread. @@ -188,7 +188,7 @@ public: @see setAffinityMask */ - static void setCurrentThreadAffinityMask (const uint32 affinityMask); + static void setCurrentThreadAffinityMask (const uint32 affinityMask) throw(); //============================================================================== // this can be called from any thread that needs to pause.. @@ -248,13 +248,13 @@ public: @returns the number of Thread objects known to be currently running. @see stopAllThreads */ - static int getNumRunningThreads(); + static int getNumRunningThreads() throw(); /** Tries to stop all currently-running threads. This will attempt to stop all the threads known to be running at the moment. */ - static void stopAllThreads (const int timeoutInMillisecs); + static void stopAllThreads (const int timeoutInMillisecs) throw(); //============================================================================== @@ -271,7 +271,7 @@ private: bool volatile threadShouldExit_; friend void JUCE_API juce_threadEntryPoint (void*); - static void threadEntryPoint (Thread* thread); + static void threadEntryPoint (Thread* thread) throw(); Thread (const Thread&); const Thread& operator= (const Thread&);