From 975851d31c3545baea9990b2b56977cf76d2e860 Mon Sep 17 00:00:00 2001 From: jules Date: Tue, 3 Mar 2009 10:33:42 +0000 Subject: [PATCH] Altered the way MessageManagerLocks are applied internally; tweaked ResamplingAudioSource to avoid a DC offset condition; changed AudioUnitPluginFormat to handle null-pointers; added a method to get all the currently modal components as well as just the most recent; changed AlertWindow to allow more customisation by the look and feel; added a few handy methods to Rectangle; --- build/macosx/Juce.xcodeproj/project.pbxproj | 9 +- .../juce_mac_AppleRemote.mm | 1 + .../platform_specific_code/juce_mac_Fonts.mm | 24 +- .../juce_mac_MainMenu.mm | 5 + .../juce_mac_MessageManager.mm | 20 +- .../juce_mac_NSViewComponentPeer.mm | 34 ++ .../juce_mac_WebBrowserComponent.mm | 2 + .../juce_win32_Messaging.cpp | 2 + juce_amalgamated.cpp | 421 ++++++++++++------ juce_amalgamated.h | 92 +++- .../application/juce_Application.cpp | 19 +- .../juce_ResamplingAudioSource.cpp | 4 +- .../formats/juce_AudioUnitPluginFormat.mm | 43 +- .../events/juce_MessageManager.cpp | 3 +- .../events/juce_MessageManager.h | 5 +- .../gui/components/controls/juce_TreeView.cpp | 2 + .../gui/components/juce_Component.cpp | 9 +- .../gui/components/juce_Component.h | 19 +- .../keyboard/juce_KeyPressMappingSet.cpp | 1 + .../lookandfeel/juce_LookAndFeel.cpp | 142 +++--- .../components/lookandfeel/juce_LookAndFeel.h | 12 +- .../components/windows/juce_AlertWindow.cpp | 67 ++- .../gui/components/windows/juce_AlertWindow.h | 29 +- .../windows/juce_ThreadWithProgressWindow.cpp | 23 +- .../windows/juce_ThreadWithProgressWindow.h | 2 +- .../gui/graphics/geometry/juce_Rectangle.cpp | 24 + .../gui/graphics/geometry/juce_Rectangle.h | 22 + 27 files changed, 726 insertions(+), 310 deletions(-) diff --git a/build/macosx/Juce.xcodeproj/project.pbxproj b/build/macosx/Juce.xcodeproj/project.pbxproj index 6799936e24..37a92bc699 100644 --- a/build/macosx/Juce.xcodeproj/project.pbxproj +++ b/build/macosx/Juce.xcodeproj/project.pbxproj @@ -716,7 +716,7 @@ 84A06BAA09CAD6A3006A43BD /* juce_AudioIODevice.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = juce_AudioIODevice.cpp; sourceTree = ""; }; 84A06BAB09CAD6A3006A43BD /* juce_AudioIODeviceType.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = juce_AudioIODeviceType.cpp; sourceTree = ""; }; 84A06BAC09CAD6A3006A43BD /* juce_AudioIODeviceType.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = juce_AudioIODeviceType.h; sourceTree = ""; }; - 84A4883508A22E4900752A2B /* juce_Application.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = juce_Application.cpp; path = ../../src/juce_appframework/application/juce_Application.cpp; sourceTree = SOURCE_ROOT; }; + 84A4883508A22E4900752A2B /* juce_Application.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 2; name = juce_Application.cpp; path = ../../src/juce_appframework/application/juce_Application.cpp; sourceTree = SOURCE_ROOT; }; 84A4883608A22E4900752A2B /* juce_Application.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = juce_Application.h; path = ../../src/juce_appframework/application/juce_Application.h; sourceTree = SOURCE_ROOT; }; 84A4883708A22E4900752A2B /* juce_DeletedAtShutdown.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = juce_DeletedAtShutdown.cpp; path = ../../src/juce_appframework/application/juce_DeletedAtShutdown.cpp; sourceTree = SOURCE_ROOT; }; 84A4883808A22E4900752A2B /* juce_DeletedAtShutdown.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = juce_DeletedAtShutdown.h; path = ../../src/juce_appframework/application/juce_DeletedAtShutdown.h; sourceTree = SOURCE_ROOT; }; @@ -1080,13 +1080,13 @@ 84E024DE0E94028C003E41AF /* juce_mac_CoreMidi.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = juce_mac_CoreMidi.cpp; sourceTree = ""; }; 84E024DF0E94028C003E41AF /* juce_mac_FileChooser.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = juce_mac_FileChooser.mm; sourceTree = ""; }; 84E024E00E94028C003E41AF /* juce_mac_Files.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = juce_mac_Files.mm; sourceTree = ""; }; - 84E024E10E94028C003E41AF /* juce_mac_Fonts.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = juce_mac_Fonts.mm; sourceTree = ""; }; + 84E024E10E94028C003E41AF /* juce_mac_Fonts.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 2; path = juce_mac_Fonts.mm; sourceTree = ""; }; 84E024E20E94028C003E41AF /* juce_mac_MainMenu.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = juce_mac_MainMenu.mm; sourceTree = ""; }; - 84E024E30E94028C003E41AF /* juce_mac_MessageManager.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = juce_mac_MessageManager.mm; sourceTree = ""; }; + 84E024E30E94028C003E41AF /* juce_mac_MessageManager.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 2; path = juce_mac_MessageManager.mm; sourceTree = ""; }; 84E024E40E94028C003E41AF /* juce_mac_MouseCursor.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = juce_mac_MouseCursor.mm; sourceTree = ""; }; 84E024E50E94028C003E41AF /* juce_mac_NamedPipe.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = juce_mac_NamedPipe.cpp; sourceTree = ""; }; 84E024E60E94028C003E41AF /* juce_mac_NativeCode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = juce_mac_NativeCode.mm; sourceTree = ""; }; - 84E024E80E94028C003E41AF /* juce_mac_Network.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = juce_mac_Network.mm; sourceTree = ""; }; + 84E024E80E94028C003E41AF /* juce_mac_Network.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 2; path = juce_mac_Network.mm; sourceTree = ""; }; 84E024E90E94028C003E41AF /* juce_mac_NSViewComponent.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = juce_mac_NSViewComponent.mm; sourceTree = ""; }; 84E024EA0E94028C003E41AF /* juce_mac_NSViewComponentPeer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 2; path = juce_mac_NSViewComponentPeer.mm; sourceTree = ""; }; 84E024EB0E94028C003E41AF /* juce_mac_OpenGLComponent.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 2; path = juce_mac_OpenGLComponent.mm; sourceTree = ""; }; @@ -1210,6 +1210,7 @@ 84A4899708A22E4A00752A2B /* juce_core */, 84A4881C08A22E2400752A2B /* mac specific code */, ); + lineEnding = 2; name = Source; sourceTree = ""; }; diff --git a/build/macosx/platform_specific_code/juce_mac_AppleRemote.mm b/build/macosx/platform_specific_code/juce_mac_AppleRemote.mm index 04c3b429d4..71c892f02f 100644 --- a/build/macosx/platform_specific_code/juce_mac_AppleRemote.mm +++ b/build/macosx/platform_specific_code/juce_mac_AppleRemote.mm @@ -265,6 +265,7 @@ void AppleRemoteDevice::handleCallbackInternal() { if (strcmp (cookies, buttonPatterns + i) == 0) { + const MessageManagerLock mml; buttonPressed ((ButtonType) buttonNum, totalValues > 0); break; } diff --git a/build/macosx/platform_specific_code/juce_mac_Fonts.mm b/build/macosx/platform_specific_code/juce_mac_Fonts.mm index e8cab4d50a..b51b4eb706 100644 --- a/build/macosx/platform_specific_code/juce_mac_Fonts.mm +++ b/build/macosx/platform_specific_code/juce_mac_Fonts.mm @@ -44,6 +44,7 @@ public: bool isBold, isItalic; float fontSize, totalSize, ascent; int refCount; + NSMutableDictionary* attributes; FontHelper (const String& name_, const bool bold_, @@ -56,10 +57,23 @@ public: fontSize (size_), refCount (1) { + attributes = [[NSMutableDictionary dictionaryWithObject: [NSNumber numberWithInt: 0] + forKey: NSLigatureAttributeName] retain]; + font = [NSFont fontWithName: juceStringToNS (name_) size: size_]; if (italic_) - font = [[NSFontManager sharedFontManager] convertFont: font toHaveTrait: NSItalicFontMask]; + { + NSFont* newFont = [[NSFontManager sharedFontManager] convertFont: font toHaveTrait: NSItalicFontMask]; + + if (newFont == font) + { + // couldn't find an italic version, so fake it with obliqueness.. + [attributes setObject: [NSNumber numberWithFloat: 0.16] forKey: NSObliquenessAttributeName]; + } + + font = newFont; + } if (bold_) font = [[NSFontManager sharedFontManager] convertFont: font toHaveTrait: NSBoldFontMask]; @@ -73,6 +87,7 @@ public: ~FontHelper() { [font release]; + [attributes release]; } bool getPathAndKerning (const juce_wchar char1, @@ -90,10 +105,9 @@ public: String chars; chars << ' ' << char1 << char2; - NSTextStorage* textStorage = [[[NSTextStorage alloc] - initWithString: juceStringToNS (chars) - attributes: [NSDictionary dictionaryWithObject: [NSNumber numberWithInt: 0] - forKey: NSLigatureAttributeName]] autorelease]; + + NSTextStorage* textStorage = [[[NSTextStorage alloc] initWithString: juceStringToNS (chars) + attributes: attributes] autorelease]; NSLayoutManager* layoutManager = [[[NSLayoutManager alloc] init] autorelease]; NSTextContainer* textContainer = [[[NSTextContainer alloc] init] autorelease]; [layoutManager addTextContainer: textContainer]; diff --git a/build/macosx/platform_specific_code/juce_mac_MainMenu.mm b/build/macosx/platform_specific_code/juce_mac_MainMenu.mm index 6d17317390..dc05c1187c 100644 --- a/build/macosx/platform_specific_code/juce_mac_MainMenu.mm +++ b/build/macosx/platform_specific_code/juce_mac_MainMenu.mm @@ -212,11 +212,16 @@ public: void updateMenus() { if (Time::getMillisecondCounter() > lastUpdateTime + 500) + { + const MessageManagerLock mml; menuBarItemsChanged (0); + } } void invoke (const int commandId, ApplicationCommandManager* const commandManager, const int topLevelIndex) const { + const MessageManagerLock mml; + if (currentModel != 0) { if (commandManager != 0) diff --git a/build/macosx/platform_specific_code/juce_mac_MessageManager.mm b/build/macosx/platform_specific_code/juce_mac_MessageManager.mm index c2753aca2e..f9cab0749d 100644 --- a/build/macosx/platform_specific_code/juce_mac_MessageManager.mm +++ b/build/macosx/platform_specific_code/juce_mac_MessageManager.mm @@ -61,6 +61,7 @@ public: { if (JUCEApplication::getInstance() != 0) { + const MessageManagerLock mml; JUCEApplication::getInstance()->systemRequestedQuit(); return NSTerminateCancel; } @@ -72,6 +73,7 @@ public: { if (JUCEApplication::getInstance() != 0) { + const MessageManagerLock mml; JUCEApplication::getInstance()->anotherInstanceStarted (nsStringToJuce (filename)); return YES; } @@ -86,19 +88,31 @@ public: files.add (nsStringToJuce ((NSString*) [filenames objectAtIndex: i])); if (files.size() > 0 && JUCEApplication::getInstance() != 0) + { + const MessageManagerLock mml; JUCEApplication::getInstance()->anotherInstanceStarted (files.joinIntoString (T(" "))); + } } virtual void focusChanged() { + const MessageManagerLock mml; juce_HandleProcessFocusChange(); } virtual void deliverMessage (void* message) { + // no need for an mm lock here - deliverMessage locks it MessageManager::getInstance()->deliverMessage (message); } + virtual void performCallback (CallbackMessagePayload* pl) + { + const MessageManagerLock mml; + pl->result = (*pl->function) (pl->parameter); + pl->hasBeenExecuted = true; + } + virtual void deleteSelf() { delete this; @@ -227,10 +241,7 @@ static bool flushingMessages = false; CallbackMessagePayload* pl = (CallbackMessagePayload*) [((NSData*) info) bytes]; if (pl != 0) - { - pl->result = (*pl->function) (pl->parameter); - pl->hasBeenExecuted = true; - } + redirector->performCallback (pl); } else { @@ -249,7 +260,6 @@ static JuceAppDelegate* juceAppDelegate = 0; void MessageManager::runDispatchLoop() { const ScopedAutoReleasePool pool; - MessageManagerLock mml; // must only be called by the message thread! jassert (isThisTheMessageThread()); diff --git a/build/macosx/platform_specific_code/juce_mac_NSViewComponentPeer.mm b/build/macosx/platform_specific_code/juce_mac_NSViewComponentPeer.mm index af20354945..7003ce6498 100644 --- a/build/macosx/platform_specific_code/juce_mac_NSViewComponentPeer.mm +++ b/build/macosx/platform_specific_code/juce_mac_NSViewComponentPeer.mm @@ -100,6 +100,7 @@ END_JUCE_NAMESPACE - (void) setOwner: (NSViewComponentPeer*) owner; - (BOOL) canBecomeKeyWindow; +- (void) becomeKeyWindow; - (BOOL) windowShouldClose: (id) window; - (NSRect) constrainFrameRect: (NSRect) frameRect toScreen: (NSScreen*) screen; - (NSSize) windowWillResize: (NSWindow*) window toSize: (NSSize) proposedFrameSize; @@ -428,6 +429,14 @@ END_JUCE_NAMESPACE return owner != 0 && owner->canBecomeKeyWindow(); } +- (void) becomeKeyWindow +{ + [super becomeKeyWindow]; + + if (owner != 0) + owner->grabFocus(); +} + - (BOOL) windowShouldClose: (id) window { return owner == 0 || owner->windowShouldClose(); @@ -909,6 +918,8 @@ NSRect NSViewComponentPeer::constrainRect (NSRect r) { if (constrainer != 0) { + const MessageManagerLock mml; + NSRect current = [window frame]; current.origin.y = [[[NSScreen screens] objectAtIndex: 0] frame].size.height - current.origin.y - current.size.height; @@ -1124,6 +1135,8 @@ void NSViewComponentPeer::grabFocus() { [window makeKeyWindow]; [window makeFirstResponder: view]; + + viewFocusGain(); } } @@ -1133,6 +1146,8 @@ void NSViewComponentPeer::textInputRequired (int /*x*/, int /*y*/) bool NSViewComponentPeer::handleKeyEvent (NSEvent* ev, bool isKeyDown) { + const MessageManagerLock mml; + String unicode (nsStringToJuce ([ev characters])); String unmodified (nsStringToJuce ([ev charactersIgnoringModifiers])); int keyCode = getKeyCodeFromEvent (ev); @@ -1172,6 +1187,7 @@ bool NSViewComponentPeer::handleKeyEvent (NSEvent* ev, bool isKeyDown) bool NSViewComponentPeer::redirectKeyDown (NSEvent* ev) { + const MessageManagerLock mml; updateKeysDown (ev, true); bool used = handleKeyEvent (ev, true); @@ -1187,12 +1203,14 @@ bool NSViewComponentPeer::redirectKeyDown (NSEvent* ev) bool NSViewComponentPeer::redirectKeyUp (NSEvent* ev) { + const MessageManagerLock mml; updateKeysDown (ev, false); return handleKeyEvent (ev, false); } void NSViewComponentPeer::redirectModKeyChange (NSEvent* ev) { + const MessageManagerLock mml; updateModifiers (ev); handleModifierKeysChange(); } @@ -1200,6 +1218,7 @@ void NSViewComponentPeer::redirectModKeyChange (NSEvent* ev) #if MACOS_10_4_OR_EARLIER bool NSViewComponentPeer::redirectPerformKeyEquivalent (NSEvent* ev) { + const MessageManagerLock mml; if ([ev type] == NSKeyDown) return redirectKeyDown (ev); else if ([ev type] == NSKeyUp) @@ -1212,6 +1231,7 @@ bool NSViewComponentPeer::redirectPerformKeyEquivalent (NSEvent* ev) //============================================================================== void NSViewComponentPeer::redirectMouseDown (NSEvent* ev) { + const MessageManagerLock mml; updateModifiers (ev); currentModifiers |= getModifierForButtonNumber ([ev buttonNumber]); int x, y; @@ -1222,6 +1242,7 @@ void NSViewComponentPeer::redirectMouseDown (NSEvent* ev) void NSViewComponentPeer::redirectMouseUp (NSEvent* ev) { + const MessageManagerLock mml; const int oldMods = currentModifiers; updateModifiers (ev); currentModifiers &= ~getModifierForButtonNumber ([ev buttonNumber]); @@ -1233,6 +1254,7 @@ void NSViewComponentPeer::redirectMouseUp (NSEvent* ev) void NSViewComponentPeer::redirectMouseDrag (NSEvent* ev) { + const MessageManagerLock mml; updateModifiers (ev); currentModifiers |= getModifierForButtonNumber ([ev buttonNumber]); int x, y; @@ -1243,6 +1265,7 @@ void NSViewComponentPeer::redirectMouseDrag (NSEvent* ev) void NSViewComponentPeer::redirectMouseMove (NSEvent* ev) { + const MessageManagerLock mml; updateModifiers (ev); int x, y; getMousePos (ev, view, x, y); @@ -1252,6 +1275,7 @@ void NSViewComponentPeer::redirectMouseMove (NSEvent* ev) void NSViewComponentPeer::redirectMouseEnter (NSEvent* ev) { + const MessageManagerLock mml; updateModifiers (ev); int x, y; getMousePos (ev, view, x, y); @@ -1261,6 +1285,7 @@ void NSViewComponentPeer::redirectMouseEnter (NSEvent* ev) void NSViewComponentPeer::redirectMouseExit (NSEvent* ev) { + const MessageManagerLock mml; updateModifiers (ev); int x, y; getMousePos (ev, view, x, y); @@ -1270,6 +1295,7 @@ void NSViewComponentPeer::redirectMouseExit (NSEvent* ev) void NSViewComponentPeer::redirectMouseWheel (NSEvent* ev) { + const MessageManagerLock mml; updateModifiers (ev); handleMouseWheel (roundFloatToInt ([ev deltaX] * 10.0f), @@ -1280,6 +1306,8 @@ void NSViewComponentPeer::redirectMouseWheel (NSEvent* ev) //============================================================================== BOOL NSViewComponentPeer::sendDragCallback (int type, id sender) { + const MessageManagerLock mml; + NSString* bestType = [[sender draggingPasteboard] availableTypeFromArray: [view getSupportedDragTypes]]; @@ -1330,6 +1358,7 @@ void NSViewComponentPeer::drawRect (NSRect r) if (r.size.width < 1.0f || r.size.height < 1.0f) return; + const MessageManagerLock mml; const float y = [view frame].size.height - (r.origin.y + r.size.height); JuceNSImage temp ((int) (r.size.width + 0.5f), @@ -1363,6 +1392,8 @@ void NSViewComponentPeer::drawRect (NSRect r) bool NSViewComponentPeer::canBecomeKeyWindow() { + const MessageManagerLock mml; + // If running as a plugin, let the component decide whether it's going to allow the window to get focused. return JUCEApplication::getInstance() != 0 || (isValidPeer (this) && getComponent()->getWantsKeyboardFocus()); @@ -1370,6 +1401,8 @@ bool NSViewComponentPeer::canBecomeKeyWindow() bool NSViewComponentPeer::windowShouldClose() { + const MessageManagerLock mml; + if (! isValidPeer (this)) return YES; @@ -1379,6 +1412,7 @@ bool NSViewComponentPeer::windowShouldClose() void NSViewComponentPeer::redirectMovedOrResized() { + const MessageManagerLock mml; handleMovedOrResized(); } diff --git a/build/macosx/platform_specific_code/juce_mac_WebBrowserComponent.mm b/build/macosx/platform_specific_code/juce_mac_WebBrowserComponent.mm index 2beaedabc6..0d8f8b3847 100644 --- a/build/macosx/platform_specific_code/juce_mac_WebBrowserComponent.mm +++ b/build/macosx/platform_specific_code/juce_mac_WebBrowserComponent.mm @@ -69,6 +69,8 @@ END_JUCE_NAMESPACE { NSURL* url = [actionInformation valueForKey: @"WebActionOriginalURLKey"]; + const MessageManagerLock mml; + if (ownerComponent->pageAboutToLoad (nsStringToJuce ([url absoluteString]))) [listener use]; else diff --git a/build/win32/platform_specific_code/juce_win32_Messaging.cpp b/build/win32/platform_specific_code/juce_win32_Messaging.cpp index 6e7333b74c..c074d235c9 100644 --- a/build/win32/platform_specific_code/juce_win32_Messaging.cpp +++ b/build/win32/platform_specific_code/juce_win32_Messaging.cpp @@ -101,6 +101,8 @@ bool juce_dispatchNextMessageOnSystemQueue (const bool returnIfNoPendingMessages if (GetMessage (&m, (HWND) 0, 0, 0) > 0) { + const MessageManagerLock mml; + if (m.message == specialId && m.hwnd == juce_messageWindowHandle) { diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index 0985f2ae40..2900d01057 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -15680,8 +15680,12 @@ int JUCEApplication::main (String& commandLine, JUCEApplication* const app) { juce_setCurrentThreadName ("Juce Message Thread"); - // let the app do its setting-up.. - app->initialise (app->commandLineParameters); + { + const MessageManagerLock mml; + + // let the app do its setting-up.. + app->initialise (app->commandLineParameters); + } // register for broadcast new app messages MessageManager::getInstance()->registerBroadcastListener (app); @@ -15728,6 +15732,7 @@ int JUCEApplication::shutdownAppAndClearUp() JUCE_TRY { // give the app a chance to clean up.. + const MessageManagerLock mml; app->shutdown(); } #if JUCE_CATCH_UNHANDLED_EXCEPTIONS @@ -15824,9 +15829,15 @@ void JUCE_PUBLIC_FUNCTION shutdownJuce_GUI() #if JUCE_MAC const ScopedAutoReleasePool pool; #endif - DeletedAtShutdown::deleteAll(); + { + const MessageManagerLock mml; + DeletedAtShutdown::deleteAll(); + + LookAndFeel::clearDefaultLookAndFeel(); + } + + delete MessageManager::getInstance(); - LookAndFeel::clearDefaultLookAndFeel(); shutdownJuce_NonGUI(); juceInitialisedGUI = false; @@ -21872,7 +21883,7 @@ void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& inf input->getNextAudioBlock (readInfo); - if (ratio > 1.0) + if (ratio > 1.0001) { // for down-sampling, pre-apply the filter.. @@ -21918,7 +21929,7 @@ void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& inf } } - if (ratio < 1.0) + if (ratio < 0.9999) { // for up-sampling, apply the filter after transposing.. @@ -28855,13 +28866,19 @@ OSStatus AudioUnitPluginInstance::getBeatAndTempo (Float64* outCurrentBeat, Floa if (ph != 0 && ph->getCurrentPosition (result)) { - *outCurrentBeat = result.ppqPosition; - *outCurrentTempo = result.bpm; + if (outCurrentBeat != 0) + *outCurrentBeat = result.ppqPosition; + + if (outCurrentTempo != 0) + *outCurrentTempo = result.bpm; } else { - *outCurrentBeat = 0; - *outCurrentTempo = 120.0; + if (outCurrentBeat != 0) + *outCurrentBeat = 0; + + if (outCurrentTempo != 0) + *outCurrentTempo = 120.0; } return noErr; @@ -28877,18 +28894,31 @@ OSStatus AudioUnitPluginInstance::getMusicalTimeLocation (UInt32* outDeltaSample if (ph != 0 && ph->getCurrentPosition (result)) { - *outTimeSig_Numerator = result.timeSigNumerator; - *outTimeSig_Denominator = result.timeSigDenominator; + if (outTimeSig_Numerator != 0) + *outTimeSig_Numerator = result.timeSigNumerator; - *outDeltaSampleOffsetToNextBeat = 0; //xxx - *outCurrentMeasureDownBeat = result.ppqPositionOfLastBarStart; //xxx wrong + if (outTimeSig_Denominator != 0) + *outTimeSig_Denominator = result.timeSigDenominator; + + if (outDeltaSampleOffsetToNextBeat != 0) + *outDeltaSampleOffsetToNextBeat = 0; //xxx + + if (outCurrentMeasureDownBeat != 0) + *outCurrentMeasureDownBeat = result.ppqPositionOfLastBarStart; //xxx wrong } else { - *outDeltaSampleOffsetToNextBeat = 0; - *outTimeSig_Numerator = 4; - *outTimeSig_Denominator = 4; - *outCurrentMeasureDownBeat = 0; + if (outDeltaSampleOffsetToNextBeat != 0) + *outDeltaSampleOffsetToNextBeat = 0; + + if (outTimeSig_Numerator != 0) + *outTimeSig_Numerator = 4; + + if (outTimeSig_Denominator != 0) + *outTimeSig_Denominator = 4; + + if (outCurrentMeasureDownBeat != 0) + *outCurrentMeasureDownBeat = 0; } return noErr; @@ -36771,7 +36801,8 @@ MessageManager::MessageManager() throw() quitMessagePosted (false), quitMessageReceived (false) { - currentLockingThreadId = messageThreadId = Thread::getCurrentThreadId(); + currentLockingThreadId = 0; + messageThreadId = Thread::getCurrentThreadId(); } MessageManager::~MessageManager() throw() @@ -38879,9 +38910,14 @@ bool Component::isCurrentlyBlockedByAnotherModalComponent() const throw() && ! mc->canModalEventBeSentToComponent (this); } -Component* JUCE_CALLTYPE Component::getCurrentlyModalComponent() throw() +int JUCE_CALLTYPE Component::getNumCurrentlyModalComponents() throw() +{ + return modalComponentStack.size(); +} + +Component* JUCE_CALLTYPE Component::getCurrentlyModalComponent (int index) throw() { - Component* const c = (Component*) modalComponentStack.getLast(); + Component* const c = (Component*) (modalComponentStack [modalComponentStack.size() - index - 1]); return c->isValidComponent() ? c : 0; } @@ -51957,7 +51993,9 @@ void TreeView::paint (Graphics& g) void TreeView::resized() { viewport->setBounds (0, 0, getWidth(), getHeight()); + itemsChanged(); + handleAsyncUpdate(); } void TreeView::moveSelectedRow (int delta) @@ -56283,6 +56321,7 @@ bool KeyPressMappingSet::keyStateChanged (Component* originatingComponent) { keyPressEntryIndex = k; wasDown = true; + used = true; break; } } @@ -60278,78 +60317,104 @@ void LookAndFeel::changeToggleButtonWidthToFitText (ToggleButton& button) button.getHeight()); } +AlertWindow* LookAndFeel::createAlertWindow (const String& title, + const String& message, + const String& button1, + const String& button2, + const String& button3, + AlertWindow::AlertIconType iconType, + int numButtons, + Component* associatedComponent) +{ + AlertWindow* aw = new AlertWindow (title, message, iconType); + + if (numButtons == 1) + { + aw->addButton (button1, 0, + KeyPress (KeyPress::escapeKey, 0, 0), + KeyPress (KeyPress::returnKey, 0, 0)); + } + else + { + const KeyPress button1ShortCut (CharacterFunctions::toLowerCase (button1[0]), 0, 0); + KeyPress button2ShortCut (CharacterFunctions::toLowerCase (button2[0]), 0, 0); + if (button1ShortCut == button2ShortCut) + button2ShortCut = KeyPress(); + + if (numButtons == 2) + { + aw->addButton (button1, 1, KeyPress (KeyPress::returnKey, 0, 0), button1ShortCut); + aw->addButton (button2, 0, KeyPress (KeyPress::escapeKey, 0, 0), button2ShortCut); + } + else + { + jassert (numButtons == 3); + + aw->addButton (button1, 1, button1ShortCut); + aw->addButton (button2, 2, button2ShortCut); + aw->addButton (button3, 0, KeyPress (KeyPress::escapeKey, 0, 0)); + } + } + + return aw; +} + void LookAndFeel::drawAlertBox (Graphics& g, AlertWindow& alert, const Rectangle& textArea, TextLayout& textLayout) { - const int iconWidth = 80; - - const Colour background (alert.findColour (AlertWindow::backgroundColourId)); - - g.fillAll (background); + g.fillAll (alert.findColour (AlertWindow::backgroundColourId)); int iconSpaceUsed = 0; Justification alignment (Justification::horizontallyCentred); + const int iconWidth = 80; int iconSize = jmin (iconWidth + 50, alert.getHeight() + 20); if (alert.containsAnyExtraComponents() || alert.getNumButtons() > 2) iconSize = jmin (iconSize, textArea.getHeight() + 50); - const Rectangle iconRect (iconSize / -10, - iconSize / -10, - iconSize, - iconSize); + const Rectangle iconRect (iconSize / -10, iconSize / -10, + iconSize, iconSize); - if (alert.getAlertType() == AlertWindow::QuestionIcon - || alert.getAlertType() == AlertWindow::InfoIcon) + if (alert.getAlertType() != AlertWindow::NoIcon) { - if (alert.getAlertType() == AlertWindow::InfoIcon) - g.setColour (background.overlaidWith (Colour (0x280000ff))); + Path icon; + uint32 colour; + char character; + + if (alert.getAlertType() == AlertWindow::WarningIcon) + { + colour = 0x55ff5555; + character = '!'; + + icon.addTriangle (iconRect.getX() + iconRect.getWidth() * 0.5f, (float) iconRect.getY(), + (float) iconRect.getRight(), (float) iconRect.getBottom(), + (float) iconRect.getX(), (float) iconRect.getBottom()); + + icon = icon.createPathWithRoundedCorners (5.0f); + } else - g.setColour (background.overlaidWith (Colours::gold.darker().withAlpha (0.25f))); - - g.fillEllipse ((float) iconRect.getX(), - (float) iconRect.getY(), - (float) iconRect.getWidth(), - (float) iconRect.getHeight()); - - g.setColour (background); - g.setFont (iconRect.getHeight() * 0.9f, Font::bold); - g.drawText ((alert.getAlertType() == AlertWindow::InfoIcon) ? "i" - : "?", - iconRect.getX(), - iconRect.getY(), - iconRect.getWidth(), - iconRect.getHeight(), - Justification::centred, false); + { + colour = alert.getAlertType() == AlertWindow::InfoIcon ? 0x605555ff : 0x40b69900; + character = alert.getAlertType() == AlertWindow::InfoIcon ? 'i' : '?'; - iconSpaceUsed = iconWidth; - alignment = Justification::left; - } - else if (alert.getAlertType() == AlertWindow::WarningIcon) - { - Path p; - p.addTriangle (iconRect.getX() + iconRect.getWidth() * 0.5f, - (float) iconRect.getY(), - (float) iconRect.getRight(), - (float) iconRect.getBottom(), - (float) iconRect.getX(), - (float) iconRect.getBottom()); - - g.setColour (background.overlaidWith (Colour (0x33ff0000))); - g.fillPath (p.createPathWithRoundedCorners (5.0f)); - - g.setColour (background); - g.setFont (iconRect.getHeight() * 0.9f, Font::bold); - - g.drawText (T("!"), - iconRect.getX(), - iconRect.getY(), - iconRect.getWidth(), - iconRect.getHeight() + iconRect.getHeight() / 8, - Justification::centred, false); + icon.addEllipse ((float) iconRect.getX(), (float) iconRect.getY(), + (float) iconRect.getWidth(), (float) iconRect.getHeight()); + } + + GlyphArrangement ga; + ga.addFittedText (Font (iconRect.getHeight() * 0.9f, Font::bold), + String::charToString (character), + (float) iconRect.getX(), (float) iconRect.getY(), + (float) iconRect.getWidth(), (float) iconRect.getHeight(), + Justification::centred, false); + ga.createPath (icon); + + icon.setUsingNonZeroWinding (false); + g.setColour (Colour (colour)); + g.fillPath (icon); iconSpaceUsed = iconWidth; alignment = Justification::left; @@ -60358,10 +60423,8 @@ void LookAndFeel::drawAlertBox (Graphics& g, g.setColour (alert.findColour (AlertWindow::textColourId)); textLayout.drawWithin (g, - textArea.getX() + iconSpaceUsed, - textArea.getY(), - textArea.getWidth() - iconSpaceUsed, - textArea.getHeight(), + textArea.getX() + iconSpaceUsed, textArea.getY(), + textArea.getWidth() - iconSpaceUsed, textArea.getHeight(), alignment.getFlags() | Justification::top); g.setColour (alert.findColour (AlertWindow::outlineColourId)); @@ -71264,18 +71327,17 @@ private: AlertWindow::AlertWindow (const String& title, const String& message, - AlertIconType iconType) + AlertIconType iconType, + Component* associatedComponent_) : TopLevelWindow (title, true), - alertIconType (iconType) + alertIconType (iconType), + associatedComponent (associatedComponent_) { if (message.isEmpty()) text = T(" "); // to force an update if the message is empty setMessage (message); -#if JUCE_MAC - setAlwaysOnTop (true); -#else for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;) { Component* const c = Desktop::getInstance().getComponent (i); @@ -71286,7 +71348,6 @@ AlertWindow::AlertWindow (const String& title, break; } } -#endif lookAndFeelChanged(); @@ -71634,7 +71695,7 @@ void AlertWindow::updateLayout (const bool onlyIncreaseSize) if (! isVisible()) { - centreAroundComponent (0, w, h); + centreAroundComponent (associatedComponent, w, h); } else { @@ -71751,7 +71812,7 @@ void AlertWindow::lookAndFeelChanged() const int flags = getLookAndFeel().getAlertBoxWindowFlags(); setUsingNativeTitleBar ((flags & ComponentPeer::windowHasTitleBar) != 0); - setDropShadowEnabled ((flags & ComponentPeer::windowHasDropShadow) != 0); + setDropShadowEnabled (isOpaque() && (flags & ComponentPeer::windowHasDropShadow) != 0); } int AlertWindow::getDesktopWindowStyleFlags() const @@ -71764,6 +71825,7 @@ struct AlertWindowInfo String title, message, button1, button2, button3; AlertWindow::AlertIconType iconType; int numButtons; + Component* associatedComponent; int run() const { @@ -71774,37 +71836,19 @@ struct AlertWindowInfo private: int show() const { - AlertWindow aw (title, message, iconType); + jassert (associatedComponent == 0 || associatedComponent->isValidComponent()); // has your comp been deleted? - if (numButtons == 1) - { - aw.addButton (button1, 0, - KeyPress (KeyPress::escapeKey, 0, 0), - KeyPress (KeyPress::returnKey, 0, 0)); - } - else - { - const KeyPress button1ShortCut (CharacterFunctions::toLowerCase (button1[0]), 0, 0); - KeyPress button2ShortCut (CharacterFunctions::toLowerCase (button2[0]), 0, 0); - if (button1ShortCut == button2ShortCut) - button2ShortCut = KeyPress(); + LookAndFeel& lf = associatedComponent->isValidComponent() ? associatedComponent->getLookAndFeel() + : LookAndFeel::getDefaultLookAndFeel(); - if (numButtons == 2) - { - aw.addButton (button1, 1, KeyPress (KeyPress::returnKey, 0, 0), button1ShortCut); - aw.addButton (button2, 0, KeyPress (KeyPress::escapeKey, 0, 0), button2ShortCut); - } - else - { - jassert (numButtons == 3); + Component* const alertBox = lf.createAlertWindow (title, message, button1, button2, button3, + iconType, numButtons, associatedComponent); - aw.addButton (button1, 1, button1ShortCut); - aw.addButton (button2, 2, button2ShortCut); - aw.addButton (button3, 0, KeyPress (KeyPress::escapeKey, 0, 0)); - } - } + jassert (alertBox != 0); // you have to return one of these! - return aw.runModalLoop(); + const int result = alertBox->runModalLoop(); + delete alertBox; + return result; } static void* showCallback (void* userData) @@ -71816,7 +71860,8 @@ private: void AlertWindow::showMessageBox (AlertIconType iconType, const String& title, const String& message, - const String& buttonText) + const String& buttonText, + Component* associatedComponent) { AlertWindowInfo info; info.title = title; @@ -71824,6 +71869,7 @@ void AlertWindow::showMessageBox (AlertIconType iconType, info.button1 = buttonText.isEmpty() ? TRANS("ok") : buttonText; info.iconType = iconType; info.numButtons = 1; + info.associatedComponent = associatedComponent; info.run(); } @@ -71832,7 +71878,8 @@ bool AlertWindow::showOkCancelBox (AlertIconType iconType, const String& title, const String& message, const String& button1Text, - const String& button2Text) + const String& button2Text, + Component* associatedComponent) { AlertWindowInfo info; info.title = title; @@ -71841,6 +71888,7 @@ bool AlertWindow::showOkCancelBox (AlertIconType iconType, info.button2 = button2Text.isEmpty() ? TRANS("cancel") : button2Text; info.iconType = iconType; info.numButtons = 2; + info.associatedComponent = associatedComponent; return info.run() != 0; } @@ -71850,7 +71898,8 @@ int AlertWindow::showYesNoCancelBox (AlertIconType iconType, const String& message, const String& button1Text, const String& button2Text, - const String& button3Text) + const String& button3Text, + Component* associatedComponent) { AlertWindowInfo info; info.title = title; @@ -71860,6 +71909,7 @@ int AlertWindow::showYesNoCancelBox (AlertIconType iconType, info.button3 = button3Text.isEmpty() ? TRANS("cancel") : button3Text; info.iconType = iconType; info.numButtons = 3; + info.associatedComponent = associatedComponent; return info.run(); } @@ -73672,19 +73722,23 @@ ThreadWithProgressWindow::ThreadWithProgressWindow (const String& title, const String& cancelButtonText) : Thread ("Juce Progress Window"), progress (0.0), - alertWindow (title, String::empty, AlertWindow::NoIcon), timeOutMsWhenCancelling (timeOutMsWhenCancelling_) { + alertWindow = LookAndFeel::getDefaultLookAndFeel() + .createAlertWindow (title, String::empty, cancelButtonText, String::empty, String::empty, + AlertWindow::NoIcon, 1, 0); + if (hasProgressBar) - alertWindow.addProgressBarComponent (progress); + alertWindow->addProgressBarComponent (progress); if (hasCancelButton) - alertWindow.addButton (cancelButtonText, 1); + alertWindow->addButton (cancelButtonText, 1); } ThreadWithProgressWindow::~ThreadWithProgressWindow() { stopThread (timeOutMsWhenCancelling); + delete alertWindow; } bool ThreadWithProgressWindow::runThread (const int priority) @@ -73694,14 +73748,14 @@ bool ThreadWithProgressWindow::runThread (const int priority) { const ScopedLock sl (messageLock); - alertWindow.setMessage (message); + alertWindow->setMessage (message); } - const bool wasCancelled = alertWindow.runModalLoop() != 0; + const bool wasCancelled = alertWindow->runModalLoop() != 0; stopThread (timeOutMsWhenCancelling); - alertWindow.setVisible (false); + alertWindow->setVisible (false); return ! wasCancelled; } @@ -73722,13 +73776,13 @@ void ThreadWithProgressWindow::timerCallback() if (! isThreadRunning()) { // thread has finished normally.. - alertWindow.exitModalState (0); - alertWindow.setVisible (false); + alertWindow->exitModalState (0); + alertWindow->setVisible (false); } else { const ScopedLock sl (messageLock); - alertWindow.setMessage (message); + alertWindow->setMessage (message); } } @@ -87373,6 +87427,30 @@ void Rectangle::setSize (const int w_, h = h_; } +void Rectangle::setLeft (const int newLeft) throw() +{ + w = jmax (0, x + w - newLeft); + x = newLeft; +} + +void Rectangle::setTop (const int newTop) throw() +{ + h = jmax (0, y + h - newTop); + y = newTop; +} + +void Rectangle::setRight (const int newRight) throw() +{ + x = jmin (x, newRight); + w = newRight - x; +} + +void Rectangle::setBottom (const int newBottom) throw() +{ + y = jmin (y, newBottom); + h = newBottom - y; +} + void Rectangle::translate (const int dx, const int dy) throw() { @@ -242157,6 +242235,8 @@ bool juce_dispatchNextMessageOnSystemQueue (const bool returnIfNoPendingMessages if (GetMessage (&m, (HWND) 0, 0, 0) > 0) { + const MessageManagerLock mml; + if (m.message == specialId && m.hwnd == juce_messageWindowHandle) { @@ -265915,7 +265995,6 @@ void juce_updateMultiMonitorInfo (Array & monitorCoords, const bool c #endif #endif - /********* End of inlined file: juce_mac_MiscUtilities.mm *********/ /********* Start of inlined file: juce_mac_Debugging.mm *********/ @@ -266034,6 +266113,7 @@ END_JUCE_NAMESPACE - (void) setOwner: (NSViewComponentPeer*) owner; - (BOOL) canBecomeKeyWindow; +- (void) becomeKeyWindow; - (BOOL) windowShouldClose: (id) window; - (NSRect) constrainFrameRect: (NSRect) frameRect toScreen: (NSScreen*) screen; - (NSSize) windowWillResize: (NSWindow*) window toSize: (NSSize) proposedFrameSize; @@ -266351,6 +266431,14 @@ END_JUCE_NAMESPACE return owner != 0 && owner->canBecomeKeyWindow(); } +- (void) becomeKeyWindow +{ + [super becomeKeyWindow]; + + if (owner != 0) + owner->grabFocus(); +} + - (BOOL) windowShouldClose: (id) window { return owner == 0 || owner->windowShouldClose(); @@ -266826,6 +266914,8 @@ NSRect NSViewComponentPeer::constrainRect (NSRect r) { if (constrainer != 0) { + const MessageManagerLock mml; + NSRect current = [window frame]; current.origin.y = [[[NSScreen screens] objectAtIndex: 0] frame].size.height - current.origin.y - current.size.height; @@ -267040,6 +267130,8 @@ void NSViewComponentPeer::grabFocus() { [window makeKeyWindow]; [window makeFirstResponder: view]; + + viewFocusGain(); } } @@ -267049,6 +267141,8 @@ void NSViewComponentPeer::textInputRequired (int /*x*/, int /*y*/) bool NSViewComponentPeer::handleKeyEvent (NSEvent* ev, bool isKeyDown) { + const MessageManagerLock mml; + String unicode (nsStringToJuce ([ev characters])); String unmodified (nsStringToJuce ([ev charactersIgnoringModifiers])); int keyCode = getKeyCodeFromEvent (ev); @@ -267088,6 +267182,7 @@ bool NSViewComponentPeer::handleKeyEvent (NSEvent* ev, bool isKeyDown) bool NSViewComponentPeer::redirectKeyDown (NSEvent* ev) { + const MessageManagerLock mml; updateKeysDown (ev, true); bool used = handleKeyEvent (ev, true); @@ -267103,12 +267198,14 @@ bool NSViewComponentPeer::redirectKeyDown (NSEvent* ev) bool NSViewComponentPeer::redirectKeyUp (NSEvent* ev) { + const MessageManagerLock mml; updateKeysDown (ev, false); return handleKeyEvent (ev, false); } void NSViewComponentPeer::redirectModKeyChange (NSEvent* ev) { + const MessageManagerLock mml; updateModifiers (ev); handleModifierKeysChange(); } @@ -267116,6 +267213,7 @@ void NSViewComponentPeer::redirectModKeyChange (NSEvent* ev) #if MACOS_10_4_OR_EARLIER bool NSViewComponentPeer::redirectPerformKeyEquivalent (NSEvent* ev) { + const MessageManagerLock mml; if ([ev type] == NSKeyDown) return redirectKeyDown (ev); else if ([ev type] == NSKeyUp) @@ -267127,6 +267225,7 @@ bool NSViewComponentPeer::redirectPerformKeyEquivalent (NSEvent* ev) void NSViewComponentPeer::redirectMouseDown (NSEvent* ev) { + const MessageManagerLock mml; updateModifiers (ev); currentModifiers |= getModifierForButtonNumber ([ev buttonNumber]); int x, y; @@ -267137,6 +267236,7 @@ void NSViewComponentPeer::redirectMouseDown (NSEvent* ev) void NSViewComponentPeer::redirectMouseUp (NSEvent* ev) { + const MessageManagerLock mml; const int oldMods = currentModifiers; updateModifiers (ev); currentModifiers &= ~getModifierForButtonNumber ([ev buttonNumber]); @@ -267148,6 +267248,7 @@ void NSViewComponentPeer::redirectMouseUp (NSEvent* ev) void NSViewComponentPeer::redirectMouseDrag (NSEvent* ev) { + const MessageManagerLock mml; updateModifiers (ev); currentModifiers |= getModifierForButtonNumber ([ev buttonNumber]); int x, y; @@ -267158,6 +267259,7 @@ void NSViewComponentPeer::redirectMouseDrag (NSEvent* ev) void NSViewComponentPeer::redirectMouseMove (NSEvent* ev) { + const MessageManagerLock mml; updateModifiers (ev); int x, y; getMousePos (ev, view, x, y); @@ -267167,6 +267269,7 @@ void NSViewComponentPeer::redirectMouseMove (NSEvent* ev) void NSViewComponentPeer::redirectMouseEnter (NSEvent* ev) { + const MessageManagerLock mml; updateModifiers (ev); int x, y; getMousePos (ev, view, x, y); @@ -267176,6 +267279,7 @@ void NSViewComponentPeer::redirectMouseEnter (NSEvent* ev) void NSViewComponentPeer::redirectMouseExit (NSEvent* ev) { + const MessageManagerLock mml; updateModifiers (ev); int x, y; getMousePos (ev, view, x, y); @@ -267185,6 +267289,7 @@ void NSViewComponentPeer::redirectMouseExit (NSEvent* ev) void NSViewComponentPeer::redirectMouseWheel (NSEvent* ev) { + const MessageManagerLock mml; updateModifiers (ev); handleMouseWheel (roundFloatToInt ([ev deltaX] * 10.0f), @@ -267194,6 +267299,8 @@ void NSViewComponentPeer::redirectMouseWheel (NSEvent* ev) BOOL NSViewComponentPeer::sendDragCallback (int type, id sender) { + const MessageManagerLock mml; + NSString* bestType = [[sender draggingPasteboard] availableTypeFromArray: [view getSupportedDragTypes]]; @@ -267244,6 +267351,7 @@ void NSViewComponentPeer::drawRect (NSRect r) if (r.size.width < 1.0f || r.size.height < 1.0f) return; + const MessageManagerLock mml; const float y = [view frame].size.height - (r.origin.y + r.size.height); JuceNSImage temp ((int) (r.size.width + 0.5f), @@ -267277,6 +267385,8 @@ void NSViewComponentPeer::drawRect (NSRect r) bool NSViewComponentPeer::canBecomeKeyWindow() { + const MessageManagerLock mml; + // If running as a plugin, let the component decide whether it's going to allow the window to get focused. return JUCEApplication::getInstance() != 0 || (isValidPeer (this) && getComponent()->getWantsKeyboardFocus()); @@ -267284,6 +267394,8 @@ bool NSViewComponentPeer::canBecomeKeyWindow() bool NSViewComponentPeer::windowShouldClose() { + const MessageManagerLock mml; + if (! isValidPeer (this)) return YES; @@ -267293,6 +267405,7 @@ bool NSViewComponentPeer::windowShouldClose() void NSViewComponentPeer::redirectMovedOrResized() { + const MessageManagerLock mml; handleMovedOrResized(); } @@ -267923,6 +268036,7 @@ void AppleRemoteDevice::handleCallbackInternal() { if (strcmp (cookies, buttonPatterns + i) == 0) { + const MessageManagerLock mml; buttonPressed ((ButtonType) buttonNum, totalValues > 0); break; } @@ -268149,7 +268263,7 @@ public: juce_UseDebuggingNewOperator NSOpenGLContext* renderContext; - ThreadSafeNSOpenGLView* view; + ThreadSafeNSOpenGLView* view; private: OpenGLPixelFormat pixelFormat; @@ -268401,11 +268515,16 @@ public: void updateMenus() { if (Time::getMillisecondCounter() > lastUpdateTime + 500) + { + const MessageManagerLock mml; menuBarItemsChanged (0); + } } void invoke (const int commandId, ApplicationCommandManager* const commandManager, const int topLevelIndex) const { + const MessageManagerLock mml; + if (currentModel != 0) { if (commandManager != 0) @@ -269502,6 +269621,7 @@ public: bool isBold, isItalic; float fontSize, totalSize, ascent; int refCount; + NSMutableDictionary* attributes; FontHelper (const String& name_, const bool bold_, @@ -269514,10 +269634,23 @@ public: fontSize (size_), refCount (1) { + attributes = [[NSMutableDictionary dictionaryWithObject: [NSNumber numberWithInt: 0] + forKey: NSLigatureAttributeName] retain]; + font = [NSFont fontWithName: juceStringToNS (name_) size: size_]; if (italic_) - font = [[NSFontManager sharedFontManager] convertFont: font toHaveTrait: NSItalicFontMask]; + { + NSFont* newFont = [[NSFontManager sharedFontManager] convertFont: font toHaveTrait: NSItalicFontMask]; + + if (newFont == font) + { + // couldn't find an italic version, so fake it with obliqueness.. + [attributes setObject: [NSNumber numberWithFloat: 0.16] forKey: NSObliquenessAttributeName]; + } + + font = newFont; + } if (bold_) font = [[NSFontManager sharedFontManager] convertFont: font toHaveTrait: NSBoldFontMask]; @@ -269531,6 +269664,7 @@ public: ~FontHelper() { [font release]; + [attributes release]; } bool getPathAndKerning (const juce_wchar char1, @@ -269548,10 +269682,9 @@ public: String chars; chars << ' ' << char1 << char2; - NSTextStorage* textStorage = [[[NSTextStorage alloc] - initWithString: juceStringToNS (chars) - attributes: [NSDictionary dictionaryWithObject: [NSNumber numberWithInt: 0] - forKey: NSLigatureAttributeName]] autorelease]; + + NSTextStorage* textStorage = [[[NSTextStorage alloc] initWithString: juceStringToNS (chars) + attributes: attributes] autorelease]; NSLayoutManager* layoutManager = [[[NSLayoutManager alloc] init] autorelease]; NSTextContainer* textContainer = [[[NSTextContainer alloc] init] autorelease]; [layoutManager addTextContainer: textContainer]; @@ -269829,6 +269962,7 @@ public: { if (JUCEApplication::getInstance() != 0) { + const MessageManagerLock mml; JUCEApplication::getInstance()->systemRequestedQuit(); return NSTerminateCancel; } @@ -269840,6 +269974,7 @@ public: { if (JUCEApplication::getInstance() != 0) { + const MessageManagerLock mml; JUCEApplication::getInstance()->anotherInstanceStarted (nsStringToJuce (filename)); return YES; } @@ -269854,19 +269989,31 @@ public: files.add (nsStringToJuce ((NSString*) [filenames objectAtIndex: i])); if (files.size() > 0 && JUCEApplication::getInstance() != 0) + { + const MessageManagerLock mml; JUCEApplication::getInstance()->anotherInstanceStarted (files.joinIntoString (T(" "))); + } } virtual void focusChanged() { + const MessageManagerLock mml; juce_HandleProcessFocusChange(); } virtual void deliverMessage (void* message) { + // no need for an mm lock here - deliverMessage locks it MessageManager::getInstance()->deliverMessage (message); } + virtual void performCallback (CallbackMessagePayload* pl) + { + const MessageManagerLock mml; + pl->result = (*pl->function) (pl->parameter); + pl->hasBeenExecuted = true; + } + virtual void deleteSelf() { delete this; @@ -269995,10 +270142,7 @@ static bool flushingMessages = false; CallbackMessagePayload* pl = (CallbackMessagePayload*) [((NSData*) info) bytes]; if (pl != 0) - { - pl->result = (*pl->function) (pl->parameter); - pl->hasBeenExecuted = true; - } + redirector->performCallback (pl); } else { @@ -270017,7 +270161,6 @@ static JuceAppDelegate* juceAppDelegate = 0; void MessageManager::runDispatchLoop() { const ScopedAutoReleasePool pool; - MessageManagerLock mml; // must only be called by the message thread! jassert (isThisTheMessageThread()); @@ -270173,6 +270316,8 @@ END_JUCE_NAMESPACE { NSURL* url = [actionInformation valueForKey: @"WebActionOriginalURLKey"]; + const MessageManagerLock mml; + if (ownerComponent->pageAboutToLoad (nsStringToJuce ([url absoluteString]))) [listener use]; else diff --git a/juce_amalgamated.h b/juce_amalgamated.h index 23211948e5..3fa07effbb 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -15016,8 +15016,8 @@ private: #if ! JUCE_ONLY_BUILD_CORE_LIBRARY /********* Start of inlined file: juce_app_includes.h *********/ -#ifndef __JUCE_JUCE_APP_INCLUDES_INCLUDEFILES__ -#define __JUCE_JUCE_APP_INCLUDES_INCLUDEFILES__ +#ifndef __JUCE_APP_INCLUDES_JUCEHEADER__ +#define __JUCE_APP_INCLUDES_JUCEHEADER__ #ifndef __JUCE_APPLICATION_JUCEHEADER__ @@ -17747,6 +17747,28 @@ public: void setBounds (const int newX, const int newY, const int newWidth, const int newHeight) throw(); + /** Moves the x position, adjusting the width so that the right-hand edge remains in the same place. + If the x is moved to be on the right of the current right-hand edge, the width will be set to zero. + */ + void setLeft (const int newLeft) throw(); + + /** Moves the y position, adjusting the height so that the bottom edge remains in the same place. + If the y is moved to be below the current bottom edge, the height will be set to zero. + */ + void setTop (const int newTop) throw(); + + /** Adjusts the width so that the right-hand edge of the rectangle has this new value. + If the new right is below the current X value, the X will be pushed down to match it. + @see getRight + */ + void setRight (const int newRight) throw(); + + /** Adjusts the height so that the bottom edge of the rectangle has this new value. + If the new bottom is lower than the current Y value, the Y will be pushed down to match it. + @see getBottom + */ + void setBottom (const int newBottom) throw(); + /** Moves the rectangle's position by adding amount to its x and y co-ordinates. */ void translate (const int deltaX, const int deltaY) throw(); @@ -22451,12 +22473,23 @@ public: */ bool isCurrentlyModal() const throw(); - /** Returns the component that is currently modal. + /** Returns the number of components that are currently in a modal state. + @see getCurrentlyModalComponent + */ + static int JUCE_CALLTYPE getNumCurrentlyModalComponents() throw(); + + /** Returns one of the components that are currently modal. - @returns the modal component, or null if no components are modal - @see runModalLoop, isCurrentlyModal + The index specifies which of the possible modal components to return. The order + of the components in this list is the reverse of the order in which they became + modal - so the component at index 0 is always the active component, and the others + are progressively earlier ones that are themselves now blocked by later ones. + + @returns the modal component, or null if no components are modal (or if the + index is out of range) + @see getNumCurrentlyModalComponents, runModalLoop, isCurrentlyModal */ - static Component* JUCE_CALLTYPE getCurrentlyModalComponent() throw(); + static Component* JUCE_CALLTYPE getCurrentlyModalComponent (int index = 0) throw(); /** Checks whether there's a modal component somewhere that's stopping this one from receiving messages. @@ -37764,7 +37797,7 @@ typedef void* (MessageCallbackFunction) (void* userData); @see Message, MessageListener, MessageManagerLock, JUCEApplication */ -class JUCE_API MessageManager : private DeletedAtShutdown +class JUCE_API MessageManager { public: @@ -37871,12 +37904,13 @@ public: void deliverMessage (void*); /** @internal */ void deliverBroadcastMessage (const String&); + /** @internal */ + ~MessageManager() throw(); juce_UseDebuggingNewOperator private: MessageManager() throw(); - ~MessageManager() throw(); friend class MessageListener; friend class ChangeBroadcaster; @@ -50522,10 +50556,14 @@ public: @param message a longer, more descriptive message to show underneath the headline @param iconType the type of icon to display + @param associatedComponent if this is non-zero, it specifies the component that the + alert window should be associated with. Depending on the look + and feel, this might be used for positioning of the alert window. */ AlertWindow (const String& title, const String& message, - AlertIconType iconType); + AlertIconType iconType, + Component* associatedComponent = 0); /** Destroys the AlertWindow */ ~AlertWindow(); @@ -50670,11 +50708,15 @@ public: headline @param buttonText the text to show in the button - if this string is empty, the default string "ok" (or a localised version) will be used. + @param associatedComponent if this is non-zero, it specifies the component that the + alert window should be associated with. Depending on the look + and feel, this might be used for positioning of the alert window. */ static void JUCE_CALLTYPE showMessageBox (AlertIconType iconType, const String& title, const String& message, - const String& buttonText = String::empty); + const String& buttonText = String::empty, + Component* associatedComponent = 0); /** Shows a dialog box with two buttons. @@ -50691,13 +50733,17 @@ public: @param button2Text the text to show in the second button - if this string is empty, the default string "cancel" (or a localised version of it) will be used. - @returns true if button 1 was clicked, false if it was button 2 + @param associatedComponent if this is non-zero, it specifies the component that the + alert window should be associated with. Depending on the look + and feel, this might be used for positioning of the alert window. + @returns true if button 1 was clicked, false if it was button 2 */ static bool JUCE_CALLTYPE showOkCancelBox (AlertIconType iconType, const String& title, const String& message, const String& button1Text = String::empty, - const String& button2Text = String::empty); + const String& button2Text = String::empty, + Component* associatedComponent = 0); /** Shows a dialog box with three buttons. @@ -50715,6 +50761,9 @@ public: "no" will be used (or a localised version of it) @param button3Text the text to show in the first button - if an empty string, then "cancel" will be used (or a localised version of it) + @param associatedComponent if this is non-zero, it specifies the component that the + alert window should be associated with. Depending on the look + and feel, this might be used for positioning of the alert window. @returns one of the following values: - 0 if the third button was pressed (normally used for 'cancel') @@ -50726,7 +50775,8 @@ public: const String& message, const String& button1Text = String::empty, const String& button2Text = String::empty, - const String& button3Text = String::empty); + const String& button3Text = String::empty, + Component* associatedComponent = 0); /** Shows an operating-system native dialog box. @@ -50785,6 +50835,7 @@ private: VoidArray progressBars, customComps, textBlocks, allComps; StringArray textboxNames, comboBoxNames; Font font; + Component* associatedComponent; void updateLayout (const bool onlyIncreaseSize); @@ -51161,7 +51212,7 @@ private: void timerCallback(); double progress; - AlertWindow alertWindow; + AlertWindow* alertWindow; String message; CriticalSection messageLock; const int timeOutMsWhenCancelling; @@ -53196,8 +53247,17 @@ public: const bool isMouseOverButton, const bool isButtonDown); - /** Draws the contents of a message box. + /** AlertWindow handling.. */ + virtual AlertWindow* createAlertWindow (const String& title, + const String& message, + const String& button1, + const String& button2, + const String& button3, + AlertWindow::AlertIconType iconType, + int numButtons, + Component* associatedComponent); + virtual void drawAlertBox (Graphics& g, AlertWindow& alert, const Rectangle& textArea, @@ -54173,7 +54233,7 @@ private: #endif -#endif +#endif // __JUCE_APP_INCLUDES_JUCEHEADER__ /********* End of inlined file: juce_app_includes.h *********/ #endif diff --git a/src/juce_appframework/application/juce_Application.cpp b/src/juce_appframework/application/juce_Application.cpp index d56b961ea2..2f7522e438 100644 --- a/src/juce_appframework/application/juce_Application.cpp +++ b/src/juce_appframework/application/juce_Application.cpp @@ -194,8 +194,12 @@ int JUCEApplication::main (String& commandLine, JUCEApplication* const app) { juce_setCurrentThreadName ("Juce Message Thread"); - // let the app do its setting-up.. - app->initialise (app->commandLineParameters); + { + const MessageManagerLock mml; + + // let the app do its setting-up.. + app->initialise (app->commandLineParameters); + } // register for broadcast new app messages MessageManager::getInstance()->registerBroadcastListener (app); @@ -242,6 +246,7 @@ int JUCEApplication::shutdownAppAndClearUp() JUCE_TRY { // give the app a chance to clean up.. + const MessageManagerLock mml; app->shutdown(); } #if JUCE_CATCH_UNHANDLED_EXCEPTIONS @@ -340,9 +345,15 @@ void JUCE_PUBLIC_FUNCTION shutdownJuce_GUI() #if JUCE_MAC const ScopedAutoReleasePool pool; #endif - DeletedAtShutdown::deleteAll(); + { + const MessageManagerLock mml; + DeletedAtShutdown::deleteAll(); + + LookAndFeel::clearDefaultLookAndFeel(); + } + + delete MessageManager::getInstance(); - LookAndFeel::clearDefaultLookAndFeel(); shutdownJuce_NonGUI(); juceInitialisedGUI = false; diff --git a/src/juce_appframework/audio/audio_sources/juce_ResamplingAudioSource.cpp b/src/juce_appframework/audio/audio_sources/juce_ResamplingAudioSource.cpp index d74a3cb3da..c95b4cedb1 100644 --- a/src/juce_appframework/audio/audio_sources/juce_ResamplingAudioSource.cpp +++ b/src/juce_appframework/audio/audio_sources/juce_ResamplingAudioSource.cpp @@ -126,7 +126,7 @@ void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& inf input->getNextAudioBlock (readInfo); - if (ratio > 1.0) + if (ratio > 1.0001) { // for down-sampling, pre-apply the filter.. @@ -172,7 +172,7 @@ void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& inf } } - if (ratio < 1.0) + if (ratio < 0.9999) { // for up-sampling, apply the filter after transposing.. diff --git a/src/juce_appframework/audio/plugins/formats/juce_AudioUnitPluginFormat.mm b/src/juce_appframework/audio/plugins/formats/juce_AudioUnitPluginFormat.mm index 43d4359857..d7b0efc70d 100644 --- a/src/juce_appframework/audio/plugins/formats/juce_AudioUnitPluginFormat.mm +++ b/src/juce_appframework/audio/plugins/formats/juce_AudioUnitPluginFormat.mm @@ -730,13 +730,19 @@ OSStatus AudioUnitPluginInstance::getBeatAndTempo (Float64* outCurrentBeat, Floa if (ph != 0 && ph->getCurrentPosition (result)) { - *outCurrentBeat = result.ppqPosition; - *outCurrentTempo = result.bpm; + if (outCurrentBeat != 0) + *outCurrentBeat = result.ppqPosition; + + if (outCurrentTempo != 0) + *outCurrentTempo = result.bpm; } else { - *outCurrentBeat = 0; - *outCurrentTempo = 120.0; + if (outCurrentBeat != 0) + *outCurrentBeat = 0; + + if (outCurrentTempo != 0) + *outCurrentTempo = 120.0; } return noErr; @@ -752,18 +758,31 @@ OSStatus AudioUnitPluginInstance::getMusicalTimeLocation (UInt32* outDeltaSample if (ph != 0 && ph->getCurrentPosition (result)) { - *outTimeSig_Numerator = result.timeSigNumerator; - *outTimeSig_Denominator = result.timeSigDenominator; + if (outTimeSig_Numerator != 0) + *outTimeSig_Numerator = result.timeSigNumerator; + + if (outTimeSig_Denominator != 0) + *outTimeSig_Denominator = result.timeSigDenominator; + + if (outDeltaSampleOffsetToNextBeat != 0) + *outDeltaSampleOffsetToNextBeat = 0; //xxx - *outDeltaSampleOffsetToNextBeat = 0; //xxx - *outCurrentMeasureDownBeat = result.ppqPositionOfLastBarStart; //xxx wrong + if (outCurrentMeasureDownBeat != 0) + *outCurrentMeasureDownBeat = result.ppqPositionOfLastBarStart; //xxx wrong } else { - *outDeltaSampleOffsetToNextBeat = 0; - *outTimeSig_Numerator = 4; - *outTimeSig_Denominator = 4; - *outCurrentMeasureDownBeat = 0; + if (outDeltaSampleOffsetToNextBeat != 0) + *outDeltaSampleOffsetToNextBeat = 0; + + if (outTimeSig_Numerator != 0) + *outTimeSig_Numerator = 4; + + if (outTimeSig_Denominator != 0) + *outTimeSig_Denominator = 4; + + if (outCurrentMeasureDownBeat != 0) + *outCurrentMeasureDownBeat = 0; } return noErr; diff --git a/src/juce_appframework/events/juce_MessageManager.cpp b/src/juce_appframework/events/juce_MessageManager.cpp index 0c5bff904a..3e88b28e49 100644 --- a/src/juce_appframework/events/juce_MessageManager.cpp +++ b/src/juce_appframework/events/juce_MessageManager.cpp @@ -57,7 +57,8 @@ MessageManager::MessageManager() throw() quitMessagePosted (false), quitMessageReceived (false) { - currentLockingThreadId = messageThreadId = Thread::getCurrentThreadId(); + currentLockingThreadId = 0; + messageThreadId = Thread::getCurrentThreadId(); } MessageManager::~MessageManager() throw() diff --git a/src/juce_appframework/events/juce_MessageManager.h b/src/juce_appframework/events/juce_MessageManager.h index 088930c02b..adcbd90f1b 100644 --- a/src/juce_appframework/events/juce_MessageManager.h +++ b/src/juce_appframework/events/juce_MessageManager.h @@ -52,7 +52,7 @@ typedef void* (MessageCallbackFunction) (void* userData); @see Message, MessageListener, MessageManagerLock, JUCEApplication */ -class JUCE_API MessageManager : private DeletedAtShutdown +class JUCE_API MessageManager { public: //============================================================================== @@ -163,13 +163,14 @@ public: void deliverMessage (void*); /** @internal */ void deliverBroadcastMessage (const String&); + /** @internal */ + ~MessageManager() throw(); //============================================================================== juce_UseDebuggingNewOperator private: MessageManager() throw(); - ~MessageManager() throw(); friend class MessageListener; friend class ChangeBroadcaster; diff --git a/src/juce_appframework/gui/components/controls/juce_TreeView.cpp b/src/juce_appframework/gui/components/controls/juce_TreeView.cpp index 582457b894..9ea9c58ca9 100644 --- a/src/juce_appframework/gui/components/controls/juce_TreeView.cpp +++ b/src/juce_appframework/gui/components/controls/juce_TreeView.cpp @@ -567,7 +567,9 @@ void TreeView::paint (Graphics& g) void TreeView::resized() { viewport->setBounds (0, 0, getWidth(), getHeight()); + itemsChanged(); + handleAsyncUpdate(); } void TreeView::moveSelectedRow (int delta) diff --git a/src/juce_appframework/gui/components/juce_Component.cpp b/src/juce_appframework/gui/components/juce_Component.cpp index 76816cac92..3138b637c4 100644 --- a/src/juce_appframework/gui/components/juce_Component.cpp +++ b/src/juce_appframework/gui/components/juce_Component.cpp @@ -1539,9 +1539,14 @@ bool Component::isCurrentlyBlockedByAnotherModalComponent() const throw() && ! mc->canModalEventBeSentToComponent (this); } -Component* JUCE_CALLTYPE Component::getCurrentlyModalComponent() throw() +int JUCE_CALLTYPE Component::getNumCurrentlyModalComponents() throw() { - Component* const c = (Component*) modalComponentStack.getLast(); + return modalComponentStack.size(); +} + +Component* JUCE_CALLTYPE Component::getCurrentlyModalComponent (int index) throw() +{ + Component* const c = (Component*) (modalComponentStack [modalComponentStack.size() - index - 1]); return c->isValidComponent() ? c : 0; } diff --git a/src/juce_appframework/gui/components/juce_Component.h b/src/juce_appframework/gui/components/juce_Component.h index bcece888ad..8a18c3fa6a 100644 --- a/src/juce_appframework/gui/components/juce_Component.h +++ b/src/juce_appframework/gui/components/juce_Component.h @@ -1763,12 +1763,23 @@ public: */ bool isCurrentlyModal() const throw(); - /** Returns the component that is currently modal. + /** Returns the number of components that are currently in a modal state. + @see getCurrentlyModalComponent + */ + static int JUCE_CALLTYPE getNumCurrentlyModalComponents() throw(); + + /** Returns one of the components that are currently modal. - @returns the modal component, or null if no components are modal - @see runModalLoop, isCurrentlyModal + The index specifies which of the possible modal components to return. The order + of the components in this list is the reverse of the order in which they became + modal - so the component at index 0 is always the active component, and the others + are progressively earlier ones that are themselves now blocked by later ones. + + @returns the modal component, or null if no components are modal (or if the + index is out of range) + @see getNumCurrentlyModalComponents, runModalLoop, isCurrentlyModal */ - static Component* JUCE_CALLTYPE getCurrentlyModalComponent() throw(); + static Component* JUCE_CALLTYPE getCurrentlyModalComponent (int index = 0) throw(); /** Checks whether there's a modal component somewhere that's stopping this one from receiving messages. diff --git a/src/juce_appframework/gui/components/keyboard/juce_KeyPressMappingSet.cpp b/src/juce_appframework/gui/components/keyboard/juce_KeyPressMappingSet.cpp index e72e8f69ef..c104e359fc 100644 --- a/src/juce_appframework/gui/components/keyboard/juce_KeyPressMappingSet.cpp +++ b/src/juce_appframework/gui/components/keyboard/juce_KeyPressMappingSet.cpp @@ -393,6 +393,7 @@ bool KeyPressMappingSet::keyStateChanged (Component* originatingComponent) { keyPressEntryIndex = k; wasDown = true; + used = true; break; } } diff --git a/src/juce_appframework/gui/components/lookandfeel/juce_LookAndFeel.cpp b/src/juce_appframework/gui/components/lookandfeel/juce_LookAndFeel.cpp index 584e7ba545..8794c34ad0 100644 --- a/src/juce_appframework/gui/components/lookandfeel/juce_LookAndFeel.cpp +++ b/src/juce_appframework/gui/components/lookandfeel/juce_LookAndFeel.cpp @@ -465,78 +465,105 @@ void LookAndFeel::changeToggleButtonWidthToFitText (ToggleButton& button) button.getHeight()); } +//============================================================================== +AlertWindow* LookAndFeel::createAlertWindow (const String& title, + const String& message, + const String& button1, + const String& button2, + const String& button3, + AlertWindow::AlertIconType iconType, + int numButtons, + Component* associatedComponent) +{ + AlertWindow* aw = new AlertWindow (title, message, iconType); + + if (numButtons == 1) + { + aw->addButton (button1, 0, + KeyPress (KeyPress::escapeKey, 0, 0), + KeyPress (KeyPress::returnKey, 0, 0)); + } + else + { + const KeyPress button1ShortCut (CharacterFunctions::toLowerCase (button1[0]), 0, 0); + KeyPress button2ShortCut (CharacterFunctions::toLowerCase (button2[0]), 0, 0); + if (button1ShortCut == button2ShortCut) + button2ShortCut = KeyPress(); + + if (numButtons == 2) + { + aw->addButton (button1, 1, KeyPress (KeyPress::returnKey, 0, 0), button1ShortCut); + aw->addButton (button2, 0, KeyPress (KeyPress::escapeKey, 0, 0), button2ShortCut); + } + else + { + jassert (numButtons == 3); + + aw->addButton (button1, 1, button1ShortCut); + aw->addButton (button2, 2, button2ShortCut); + aw->addButton (button3, 0, KeyPress (KeyPress::escapeKey, 0, 0)); + } + } + + return aw; +} + void LookAndFeel::drawAlertBox (Graphics& g, AlertWindow& alert, const Rectangle& textArea, TextLayout& textLayout) { - const int iconWidth = 80; - - const Colour background (alert.findColour (AlertWindow::backgroundColourId)); - - g.fillAll (background); + g.fillAll (alert.findColour (AlertWindow::backgroundColourId)); int iconSpaceUsed = 0; Justification alignment (Justification::horizontallyCentred); + const int iconWidth = 80; int iconSize = jmin (iconWidth + 50, alert.getHeight() + 20); if (alert.containsAnyExtraComponents() || alert.getNumButtons() > 2) iconSize = jmin (iconSize, textArea.getHeight() + 50); - const Rectangle iconRect (iconSize / -10, - iconSize / -10, - iconSize, - iconSize); + const Rectangle iconRect (iconSize / -10, iconSize / -10, + iconSize, iconSize); - if (alert.getAlertType() == AlertWindow::QuestionIcon - || alert.getAlertType() == AlertWindow::InfoIcon) + if (alert.getAlertType() != AlertWindow::NoIcon) { - if (alert.getAlertType() == AlertWindow::InfoIcon) - g.setColour (background.overlaidWith (Colour (0x280000ff))); + Path icon; + uint32 colour; + char character; + + if (alert.getAlertType() == AlertWindow::WarningIcon) + { + colour = 0x55ff5555; + character = '!'; + + icon.addTriangle (iconRect.getX() + iconRect.getWidth() * 0.5f, (float) iconRect.getY(), + (float) iconRect.getRight(), (float) iconRect.getBottom(), + (float) iconRect.getX(), (float) iconRect.getBottom()); + + icon = icon.createPathWithRoundedCorners (5.0f); + } else - g.setColour (background.overlaidWith (Colours::gold.darker().withAlpha (0.25f))); - - g.fillEllipse ((float) iconRect.getX(), - (float) iconRect.getY(), - (float) iconRect.getWidth(), - (float) iconRect.getHeight()); - - g.setColour (background); - g.setFont (iconRect.getHeight() * 0.9f, Font::bold); - g.drawText ((alert.getAlertType() == AlertWindow::InfoIcon) ? "i" - : "?", - iconRect.getX(), - iconRect.getY(), - iconRect.getWidth(), - iconRect.getHeight(), - Justification::centred, false); + { + colour = alert.getAlertType() == AlertWindow::InfoIcon ? 0x605555ff : 0x40b69900; + character = alert.getAlertType() == AlertWindow::InfoIcon ? 'i' : '?'; - iconSpaceUsed = iconWidth; - alignment = Justification::left; - } - else if (alert.getAlertType() == AlertWindow::WarningIcon) - { - Path p; - p.addTriangle (iconRect.getX() + iconRect.getWidth() * 0.5f, - (float) iconRect.getY(), - (float) iconRect.getRight(), - (float) iconRect.getBottom(), - (float) iconRect.getX(), - (float) iconRect.getBottom()); - - g.setColour (background.overlaidWith (Colour (0x33ff0000))); - g.fillPath (p.createPathWithRoundedCorners (5.0f)); - - g.setColour (background); - g.setFont (iconRect.getHeight() * 0.9f, Font::bold); - - g.drawText (T("!"), - iconRect.getX(), - iconRect.getY(), - iconRect.getWidth(), - iconRect.getHeight() + iconRect.getHeight() / 8, - Justification::centred, false); + icon.addEllipse ((float) iconRect.getX(), (float) iconRect.getY(), + (float) iconRect.getWidth(), (float) iconRect.getHeight()); + } + + GlyphArrangement ga; + ga.addFittedText (Font (iconRect.getHeight() * 0.9f, Font::bold), + String::charToString (character), + (float) iconRect.getX(), (float) iconRect.getY(), + (float) iconRect.getWidth(), (float) iconRect.getHeight(), + Justification::centred, false); + ga.createPath (icon); + + icon.setUsingNonZeroWinding (false); + g.setColour (Colour (colour)); + g.fillPath (icon); iconSpaceUsed = iconWidth; alignment = Justification::left; @@ -545,10 +572,8 @@ void LookAndFeel::drawAlertBox (Graphics& g, g.setColour (alert.findColour (AlertWindow::textColourId)); textLayout.drawWithin (g, - textArea.getX() + iconSpaceUsed, - textArea.getY(), - textArea.getWidth() - iconSpaceUsed, - textArea.getHeight(), + textArea.getX() + iconSpaceUsed, textArea.getY(), + textArea.getWidth() - iconSpaceUsed, textArea.getHeight(), alignment.getFlags() | Justification::top); g.setColour (alert.findColour (AlertWindow::outlineColourId)); @@ -571,6 +596,7 @@ const Font LookAndFeel::getAlertWindowFont() return Font (12.0f); } +//============================================================================== void LookAndFeel::drawProgressBar (Graphics& g, ProgressBar& progressBar, int width, int height, double progress, const String& textToShow) diff --git a/src/juce_appframework/gui/components/lookandfeel/juce_LookAndFeel.h b/src/juce_appframework/gui/components/lookandfeel/juce_LookAndFeel.h index 8f74eb85af..1150ebfd1c 100644 --- a/src/juce_appframework/gui/components/lookandfeel/juce_LookAndFeel.h +++ b/src/juce_appframework/gui/components/lookandfeel/juce_LookAndFeel.h @@ -36,6 +36,7 @@ #include "../../graphics/effects/juce_DropShadowEffect.h" #include "../controls/juce_Slider.h" #include "../layout/juce_TabbedComponent.h" +#include "../windows/juce_AlertWindow.h" class ToggleButton; class TextButton; @@ -175,8 +176,17 @@ public: const bool isButtonDown); //============================================================================== - /** Draws the contents of a message box. + /** AlertWindow handling.. */ + virtual AlertWindow* createAlertWindow (const String& title, + const String& message, + const String& button1, + const String& button2, + const String& button3, + AlertWindow::AlertIconType iconType, + int numButtons, + Component* associatedComponent); + virtual void drawAlertBox (Graphics& g, AlertWindow& alert, const Rectangle& textArea, diff --git a/src/juce_appframework/gui/components/windows/juce_AlertWindow.cpp b/src/juce_appframework/gui/components/windows/juce_AlertWindow.cpp index b0b14af060..0a60d8646e 100644 --- a/src/juce_appframework/gui/components/windows/juce_AlertWindow.cpp +++ b/src/juce_appframework/gui/components/windows/juce_AlertWindow.cpp @@ -90,18 +90,17 @@ private: //============================================================================== AlertWindow::AlertWindow (const String& title, const String& message, - AlertIconType iconType) + AlertIconType iconType, + Component* associatedComponent_) : TopLevelWindow (title, true), - alertIconType (iconType) + alertIconType (iconType), + associatedComponent (associatedComponent_) { if (message.isEmpty()) text = T(" "); // to force an update if the message is empty setMessage (message); -#if JUCE_MAC - setAlwaysOnTop (true); -#else for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;) { Component* const c = Desktop::getInstance().getComponent (i); @@ -112,7 +111,6 @@ AlertWindow::AlertWindow (const String& title, break; } } -#endif lookAndFeelChanged(); @@ -470,7 +468,7 @@ void AlertWindow::updateLayout (const bool onlyIncreaseSize) if (! isVisible()) { - centreAroundComponent (0, w, h); + centreAroundComponent (associatedComponent, w, h); } else { @@ -588,7 +586,7 @@ void AlertWindow::lookAndFeelChanged() const int flags = getLookAndFeel().getAlertBoxWindowFlags(); setUsingNativeTitleBar ((flags & ComponentPeer::windowHasTitleBar) != 0); - setDropShadowEnabled ((flags & ComponentPeer::windowHasDropShadow) != 0); + setDropShadowEnabled (isOpaque() && (flags & ComponentPeer::windowHasDropShadow) != 0); } int AlertWindow::getDesktopWindowStyleFlags() const @@ -602,6 +600,7 @@ struct AlertWindowInfo String title, message, button1, button2, button3; AlertWindow::AlertIconType iconType; int numButtons; + Component* associatedComponent; int run() const { @@ -612,37 +611,19 @@ struct AlertWindowInfo private: int show() const { - AlertWindow aw (title, message, iconType); + jassert (associatedComponent == 0 || associatedComponent->isValidComponent()); // has your comp been deleted? - if (numButtons == 1) - { - aw.addButton (button1, 0, - KeyPress (KeyPress::escapeKey, 0, 0), - KeyPress (KeyPress::returnKey, 0, 0)); - } - else - { - const KeyPress button1ShortCut (CharacterFunctions::toLowerCase (button1[0]), 0, 0); - KeyPress button2ShortCut (CharacterFunctions::toLowerCase (button2[0]), 0, 0); - if (button1ShortCut == button2ShortCut) - button2ShortCut = KeyPress(); - - if (numButtons == 2) - { - aw.addButton (button1, 1, KeyPress (KeyPress::returnKey, 0, 0), button1ShortCut); - aw.addButton (button2, 0, KeyPress (KeyPress::escapeKey, 0, 0), button2ShortCut); - } - else - { - jassert (numButtons == 3); - - aw.addButton (button1, 1, button1ShortCut); - aw.addButton (button2, 2, button2ShortCut); - aw.addButton (button3, 0, KeyPress (KeyPress::escapeKey, 0, 0)); - } - } + LookAndFeel& lf = associatedComponent->isValidComponent() ? associatedComponent->getLookAndFeel() + : LookAndFeel::getDefaultLookAndFeel(); + + Component* const alertBox = lf.createAlertWindow (title, message, button1, button2, button3, + iconType, numButtons, associatedComponent); + + jassert (alertBox != 0); // you have to return one of these! - return aw.runModalLoop(); + const int result = alertBox->runModalLoop(); + delete alertBox; + return result; } static void* showCallback (void* userData) @@ -654,7 +635,8 @@ private: void AlertWindow::showMessageBox (AlertIconType iconType, const String& title, const String& message, - const String& buttonText) + const String& buttonText, + Component* associatedComponent) { AlertWindowInfo info; info.title = title; @@ -662,6 +644,7 @@ void AlertWindow::showMessageBox (AlertIconType iconType, info.button1 = buttonText.isEmpty() ? TRANS("ok") : buttonText; info.iconType = iconType; info.numButtons = 1; + info.associatedComponent = associatedComponent; info.run(); } @@ -670,7 +653,8 @@ bool AlertWindow::showOkCancelBox (AlertIconType iconType, const String& title, const String& message, const String& button1Text, - const String& button2Text) + const String& button2Text, + Component* associatedComponent) { AlertWindowInfo info; info.title = title; @@ -679,6 +663,7 @@ bool AlertWindow::showOkCancelBox (AlertIconType iconType, info.button2 = button2Text.isEmpty() ? TRANS("cancel") : button2Text; info.iconType = iconType; info.numButtons = 2; + info.associatedComponent = associatedComponent; return info.run() != 0; } @@ -688,7 +673,8 @@ int AlertWindow::showYesNoCancelBox (AlertIconType iconType, const String& message, const String& button1Text, const String& button2Text, - const String& button3Text) + const String& button3Text, + Component* associatedComponent) { AlertWindowInfo info; info.title = title; @@ -698,6 +684,7 @@ int AlertWindow::showYesNoCancelBox (AlertIconType iconType, info.button3 = button3Text.isEmpty() ? TRANS("cancel") : button3Text; info.iconType = iconType; info.numButtons = 3; + info.associatedComponent = associatedComponent; return info.run(); } diff --git a/src/juce_appframework/gui/components/windows/juce_AlertWindow.h b/src/juce_appframework/gui/components/windows/juce_AlertWindow.h index 10df1ee1cf..98c4182ee0 100644 --- a/src/juce_appframework/gui/components/windows/juce_AlertWindow.h +++ b/src/juce_appframework/gui/components/windows/juce_AlertWindow.h @@ -76,10 +76,14 @@ public: @param message a longer, more descriptive message to show underneath the headline @param iconType the type of icon to display + @param associatedComponent if this is non-zero, it specifies the component that the + alert window should be associated with. Depending on the look + and feel, this might be used for positioning of the alert window. */ AlertWindow (const String& title, const String& message, - AlertIconType iconType); + AlertIconType iconType, + Component* associatedComponent = 0); /** Destroys the AlertWindow */ ~AlertWindow(); @@ -234,11 +238,15 @@ public: headline @param buttonText the text to show in the button - if this string is empty, the default string "ok" (or a localised version) will be used. + @param associatedComponent if this is non-zero, it specifies the component that the + alert window should be associated with. Depending on the look + and feel, this might be used for positioning of the alert window. */ static void JUCE_CALLTYPE showMessageBox (AlertIconType iconType, const String& title, const String& message, - const String& buttonText = String::empty); + const String& buttonText = String::empty, + Component* associatedComponent = 0); /** Shows a dialog box with two buttons. @@ -255,13 +263,17 @@ public: @param button2Text the text to show in the second button - if this string is empty, the default string "cancel" (or a localised version of it) will be used. - @returns true if button 1 was clicked, false if it was button 2 + @param associatedComponent if this is non-zero, it specifies the component that the + alert window should be associated with. Depending on the look + and feel, this might be used for positioning of the alert window. + @returns true if button 1 was clicked, false if it was button 2 */ static bool JUCE_CALLTYPE showOkCancelBox (AlertIconType iconType, const String& title, const String& message, const String& button1Text = String::empty, - const String& button2Text = String::empty); + const String& button2Text = String::empty, + Component* associatedComponent = 0); /** Shows a dialog box with three buttons. @@ -279,7 +291,10 @@ public: "no" will be used (or a localised version of it) @param button3Text the text to show in the first button - if an empty string, then "cancel" will be used (or a localised version of it) - + @param associatedComponent if this is non-zero, it specifies the component that the + alert window should be associated with. Depending on the look + and feel, this might be used for positioning of the alert window. + @returns one of the following values: - 0 if the third button was pressed (normally used for 'cancel') - 1 if the first button was pressed (normally used for 'yes') @@ -290,7 +305,8 @@ public: const String& message, const String& button1Text = String::empty, const String& button2Text = String::empty, - const String& button3Text = String::empty); + const String& button3Text = String::empty, + Component* associatedComponent = 0); //============================================================================== /** Shows an operating-system native dialog box. @@ -353,6 +369,7 @@ private: VoidArray progressBars, customComps, textBlocks, allComps; StringArray textboxNames, comboBoxNames; Font font; + Component* associatedComponent; void updateLayout (const bool onlyIncreaseSize); diff --git a/src/juce_appframework/gui/components/windows/juce_ThreadWithProgressWindow.cpp b/src/juce_appframework/gui/components/windows/juce_ThreadWithProgressWindow.cpp index f8cf92dc5b..68f540a144 100644 --- a/src/juce_appframework/gui/components/windows/juce_ThreadWithProgressWindow.cpp +++ b/src/juce_appframework/gui/components/windows/juce_ThreadWithProgressWindow.cpp @@ -35,6 +35,7 @@ BEGIN_JUCE_NAMESPACE #include "juce_ThreadWithProgressWindow.h" +#include "../lookandfeel/juce_LookAndFeel.h" #include "../../../../juce_core/text/juce_LocalisedStrings.h" @@ -46,19 +47,23 @@ ThreadWithProgressWindow::ThreadWithProgressWindow (const String& title, const String& cancelButtonText) : Thread ("Juce Progress Window"), progress (0.0), - alertWindow (title, String::empty, AlertWindow::NoIcon), timeOutMsWhenCancelling (timeOutMsWhenCancelling_) { + alertWindow = LookAndFeel::getDefaultLookAndFeel() + .createAlertWindow (title, String::empty, cancelButtonText, String::empty, String::empty, + AlertWindow::NoIcon, 1, 0); + if (hasProgressBar) - alertWindow.addProgressBarComponent (progress); + alertWindow->addProgressBarComponent (progress); if (hasCancelButton) - alertWindow.addButton (cancelButtonText, 1); + alertWindow->addButton (cancelButtonText, 1); } ThreadWithProgressWindow::~ThreadWithProgressWindow() { stopThread (timeOutMsWhenCancelling); + delete alertWindow; } bool ThreadWithProgressWindow::runThread (const int priority) @@ -68,14 +73,14 @@ bool ThreadWithProgressWindow::runThread (const int priority) { const ScopedLock sl (messageLock); - alertWindow.setMessage (message); + alertWindow->setMessage (message); } - const bool wasCancelled = alertWindow.runModalLoop() != 0; + const bool wasCancelled = alertWindow->runModalLoop() != 0; stopThread (timeOutMsWhenCancelling); - alertWindow.setVisible (false); + alertWindow->setVisible (false); return ! wasCancelled; } @@ -96,13 +101,13 @@ void ThreadWithProgressWindow::timerCallback() if (! isThreadRunning()) { // thread has finished normally.. - alertWindow.exitModalState (0); - alertWindow.setVisible (false); + alertWindow->exitModalState (0); + alertWindow->setVisible (false); } else { const ScopedLock sl (messageLock); - alertWindow.setMessage (message); + alertWindow->setMessage (message); } } diff --git a/src/juce_appframework/gui/components/windows/juce_ThreadWithProgressWindow.h b/src/juce_appframework/gui/components/windows/juce_ThreadWithProgressWindow.h index 69c3ebd89c..a498a7c7ec 100644 --- a/src/juce_appframework/gui/components/windows/juce_ThreadWithProgressWindow.h +++ b/src/juce_appframework/gui/components/windows/juce_ThreadWithProgressWindow.h @@ -155,7 +155,7 @@ private: void timerCallback(); double progress; - AlertWindow alertWindow; + AlertWindow* alertWindow; String message; CriticalSection messageLock; const int timeOutMsWhenCancelling; diff --git a/src/juce_appframework/gui/graphics/geometry/juce_Rectangle.cpp b/src/juce_appframework/gui/graphics/geometry/juce_Rectangle.cpp index 7e7fa8a692..07a6e13c6b 100644 --- a/src/juce_appframework/gui/graphics/geometry/juce_Rectangle.cpp +++ b/src/juce_appframework/gui/graphics/geometry/juce_Rectangle.cpp @@ -106,6 +106,30 @@ void Rectangle::setSize (const int w_, h = h_; } +void Rectangle::setLeft (const int newLeft) throw() +{ + w = jmax (0, x + w - newLeft); + x = newLeft; +} + +void Rectangle::setTop (const int newTop) throw() +{ + h = jmax (0, y + h - newTop); + y = newTop; +} + +void Rectangle::setRight (const int newRight) throw() +{ + x = jmin (x, newRight); + w = newRight - x; +} + +void Rectangle::setBottom (const int newBottom) throw() +{ + y = jmin (y, newBottom); + h = newBottom - y; +} + void Rectangle::translate (const int dx, const int dy) throw() { diff --git a/src/juce_appframework/gui/graphics/geometry/juce_Rectangle.h b/src/juce_appframework/gui/graphics/geometry/juce_Rectangle.h index 4386d83bfd..d776e955fa 100644 --- a/src/juce_appframework/gui/graphics/geometry/juce_Rectangle.h +++ b/src/juce_appframework/gui/graphics/geometry/juce_Rectangle.h @@ -102,6 +102,28 @@ public: void setBounds (const int newX, const int newY, const int newWidth, const int newHeight) throw(); + /** Moves the x position, adjusting the width so that the right-hand edge remains in the same place. + If the x is moved to be on the right of the current right-hand edge, the width will be set to zero. + */ + void setLeft (const int newLeft) throw(); + + /** Moves the y position, adjusting the height so that the bottom edge remains in the same place. + If the y is moved to be below the current bottom edge, the height will be set to zero. + */ + void setTop (const int newTop) throw(); + + /** Adjusts the width so that the right-hand edge of the rectangle has this new value. + If the new right is below the current X value, the X will be pushed down to match it. + @see getRight + */ + void setRight (const int newRight) throw(); + + /** Adjusts the height so that the bottom edge of the rectangle has this new value. + If the new bottom is lower than the current Y value, the Y will be pushed down to match it. + @see getBottom + */ + void setBottom (const int newBottom) throw(); + /** Moves the rectangle's position by adding amount to its x and y co-ordinates. */ void translate (const int deltaX, const int deltaY) throw();