@@ -397,11 +397,9 @@ public: | |||
if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &isAlive)) && isAlive == 0) | |||
return; | |||
Float64 sr; | |||
size = sizeof (sr); | |||
pa.mSelector = kAudioDevicePropertyNominalSampleRate; | |||
if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &sr))) | |||
sampleRate = sr; | |||
const double currentRate = getNominalSampleRate(); | |||
if (currentRate > 0) | |||
sampleRate = currentRate; | |||
UInt32 framesPerBuf = (UInt32) bufferSize; | |||
size = sizeof (framesPerBuf); | |||
@@ -525,6 +523,30 @@ public: | |||
} | |||
} | |||
double getNominalSampleRate() const | |||
{ | |||
AudioObjectPropertyAddress pa; | |||
pa.mSelector = kAudioDevicePropertyNominalSampleRate; | |||
pa.mScope = kAudioObjectPropertyScopeGlobal; | |||
pa.mElement = kAudioObjectPropertyElementMaster; | |||
Float64 sr = 0; | |||
UInt32 size = (UInt32) sizeof (sr); | |||
return OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &sr)) ? (double) sr : 0.0; | |||
} | |||
bool setNominalSampleRate (double newSampleRate) const | |||
{ | |||
if (std::abs (getNominalSampleRate() - newSampleRate) < 1.0) | |||
return true; | |||
AudioObjectPropertyAddress pa; | |||
pa.mSelector = kAudioDevicePropertyNominalSampleRate; | |||
pa.mScope = kAudioObjectPropertyScopeGlobal; | |||
pa.mElement = kAudioObjectPropertyElementMaster; | |||
Float64 sr = newSampleRate; | |||
return OK (AudioObjectSetPropertyData (deviceID, &pa, 0, 0, sizeof (sr), &sr)); | |||
} | |||
//============================================================================== | |||
String reopen (const BigInteger& inputChannels, | |||
const BigInteger& outputChannels, | |||
@@ -549,25 +571,23 @@ public: | |||
numInputChans = activeInputChans.countNumberOfSetBits(); | |||
numOutputChans = activeOutputChans.countNumberOfSetBits(); | |||
// set sample rate | |||
AudioObjectPropertyAddress pa; | |||
pa.mSelector = kAudioDevicePropertyNominalSampleRate; | |||
pa.mScope = kAudioObjectPropertyScopeGlobal; | |||
pa.mElement = kAudioObjectPropertyElementMaster; | |||
Float64 sr = newSampleRate; | |||
if (! OK (AudioObjectSetPropertyData (deviceID, &pa, 0, 0, sizeof (sr), &sr))) | |||
if (! setNominalSampleRate (newSampleRate)) | |||
{ | |||
updateDetailsFromDevice(); | |||
error = "Couldn't change sample rate"; | |||
} | |||
else | |||
{ | |||
// change buffer size | |||
UInt32 framesPerBuf = (UInt32) bufferSizeSamples; | |||
AudioObjectPropertyAddress pa; | |||
pa.mSelector = kAudioDevicePropertyBufferFrameSize; | |||
pa.mScope = kAudioObjectPropertyScopeGlobal; | |||
pa.mElement = kAudioObjectPropertyElementMaster; | |||
UInt32 framesPerBuf = (UInt32) bufferSizeSamples; | |||
if (! OK (AudioObjectSetPropertyData (deviceID, &pa, 0, 0, sizeof (framesPerBuf), &framesPerBuf))) | |||
{ | |||
updateDetailsFromDevice(); | |||
error = "Couldn't change buffer size"; | |||
} | |||
else | |||
@@ -643,7 +643,7 @@ public: | |||
if (metadataValues.size() > 0) | |||
{ | |||
// The meta data should have been santised for the AIFF format. | |||
// The meta data should have been sanitised for the AIFF format. | |||
// If it was originally sourced from a WAV file the MetaDataSource | |||
// key should be removed (or set to "AIFF") once this has been done | |||
jassert (metadataValues.getValue ("MetaDataSource", "None") != "WAV"); | |||
@@ -1024,7 +1024,7 @@ public: | |||
if (metadataValues.size() > 0) | |||
{ | |||
// The meta data should have been santised for the WAV format. | |||
// The meta data should have been sanitised for the WAV format. | |||
// If it was originally sourced from an AIFF file the MetaDataSource | |||
// key should be removed (or set to "WAV") once this has been done | |||
jassert (metadataValues.getValue ("MetaDataSource", "None") != "AIFF"); | |||
@@ -37,6 +37,9 @@ | |||
#include "../utility/juce_FakeMouseMoveGenerator.h" | |||
#include "../utility/juce_CarbonVisibility.h" | |||
#undef Component | |||
#undef Point | |||
//============================================================================== | |||
namespace juce | |||
{ | |||
@@ -103,6 +106,7 @@ void* attachComponentToWindowRefVST (Component* comp, void* parentWindowOrView, | |||
WindowAttributes attributes; | |||
GetWindowAttributes ((WindowRef) parentWindowOrView, &attributes); | |||
if ((attributes & kWindowCompositingAttribute) != 0) | |||
{ | |||
HIViewRef root = HIViewGetRoot ((WindowRef) parentWindowOrView); | |||
@@ -160,7 +164,7 @@ void* attachComponentToWindowRefVST (Component* comp, void* parentWindowOrView, | |||
} | |||
#endif | |||
(void) isNSView; | |||
ignoreUnused (isNSView); | |||
NSView* parentView = [(NSView*) parentWindowOrView retain]; | |||
#if JucePlugin_EditorRequiresKeyboardFocus | |||
@@ -233,7 +237,7 @@ void detachComponentFromWindowRefVST (Component* comp, void* window, bool isNSVi | |||
} | |||
#endif | |||
(void) isNSView; | |||
ignoreUnused (isNSView); | |||
comp->removeFromDesktop(); | |||
[(id) window release]; | |||
} | |||
@@ -261,7 +265,7 @@ void setNativeHostWindowSizeVST (void* window, Component* component, int newWidt | |||
} | |||
#endif | |||
(void) isNSView; | |||
ignoreUnused (isNSView); | |||
if (NSView* hostView = (NSView*) window) | |||
{ | |||
@@ -280,7 +284,7 @@ void setNativeHostWindowSizeVST (void* window, Component* component, int newWidt | |||
void checkWindowVisibilityVST (void* window, Component* comp, bool isNSView); | |||
void checkWindowVisibilityVST (void* window, Component* comp, bool isNSView) | |||
{ | |||
(void) window; (void) comp; (void) isNSView; | |||
ignoreUnused (window, comp, isNSView); | |||
#if ! JUCE_64BIT | |||
if (! isNSView) | |||
@@ -301,7 +305,7 @@ bool forwardCurrentKeyEventToHostVST (Component* comp, bool isNSView) | |||
} | |||
#endif | |||
(void) comp; (void) isNSView; | |||
ignoreUnused (comp, isNSView); | |||
return false; | |||
} | |||
@@ -62,10 +62,15 @@ using namespace Steinberg; | |||
//============================================================================== | |||
#if JUCE_MAC | |||
extern void initialiseMacVST3(); | |||
extern void* attachComponentToWindowRefVST3 (Component*, void* parent, bool isNSView); | |||
extern void detachComponentFromWindowRefVST3 (Component*, void* window, bool isNSView); | |||
extern void setNativeHostWindowSizeVST3 (void* window, Component*, int newWidth, int newHeight, bool isNSView); | |||
extern void initialiseMacVST(); | |||
#if ! JUCE_64BIT | |||
extern void updateEditorCompBoundsVST (Component*); | |||
#endif | |||
extern void* attachComponentToWindowRefVST (Component*, void* parentWindowOrView, bool isNSView); | |||
extern void detachComponentFromWindowRefVST (Component*, void* nsWindow, bool isNSView); | |||
extern void setNativeHostWindowSizeVST (void* window, Component*, int newWidth, int newHeight, bool isNSView); | |||
#endif | |||
//============================================================================== | |||
@@ -250,7 +255,7 @@ public: | |||
toString128 (info.title, "Bypass"); | |||
toString128 (info.shortTitle, "Bypass"); | |||
toString128 (info.units, ""); | |||
info.stepCount = 2; | |||
info.stepCount = 1; | |||
info.defaultNormalizedValue = 0.0f; | |||
info.unitId = Vst::kRootUnitId; | |||
info.flags = Vst::ParameterInfo::kIsBypass; | |||
@@ -561,7 +566,7 @@ private: | |||
component->setVisible (true); | |||
#else | |||
isNSView = (strcmp (type, kPlatformTypeNSView) == 0); | |||
macHostWindow = juce::attachComponentToWindowRefVST3 (component, parent, isNSView); | |||
macHostWindow = juce::attachComponentToWindowRefVST (component, parent, isNSView); | |||
#endif | |||
component->resizeHostWindow(); | |||
@@ -580,7 +585,7 @@ private: | |||
#else | |||
if (macHostWindow != nullptr) | |||
{ | |||
juce::detachComponentFromWindowRefVST3 (component, macHostWindow, isNSView); | |||
juce::detachComponentFromWindowRefVST (component, macHostWindow, isNSView); | |||
macHostWindow = nullptr; | |||
} | |||
#endif | |||
@@ -692,7 +697,7 @@ private: | |||
setSize (w, h); | |||
#else | |||
if (owner.macHostWindow != nullptr && ! getHostType().isWavelab()) | |||
juce::setNativeHostWindowSizeVST3 (owner.macHostWindow, this, w, h, owner.isNSView); | |||
juce::setNativeHostWindowSizeVST (owner.macHostWindow, this, w, h, owner.isNSView); | |||
#endif | |||
if (owner.plugFrame != nullptr) | |||
@@ -1007,13 +1012,13 @@ public: | |||
void setStateInformation (const void* data, int sizeAsInt) | |||
{ | |||
size_t size = static_cast<size_t> (sizeAsInt); | |||
int64 size = sizeAsInt; | |||
// Check if this data was written with a newer JUCE version | |||
// and if it has the JUCE private data magic code at the end | |||
const size_t jucePrivDataIdentifierSize = std::strlen (kJucePrivateDataIdentifier); | |||
if (size >= (jucePrivDataIdentifierSize + sizeof (int64))) | |||
if (size >= jucePrivDataIdentifierSize + sizeof (int64)) | |||
{ | |||
const char* buffer = static_cast<const char*> (data); | |||
@@ -1039,7 +1044,8 @@ public: | |||
} | |||
} | |||
pluginInstance->setStateInformation (data, static_cast<int> (size)); | |||
if (size >= 0) | |||
pluginInstance->setStateInformation (data, static_cast<int> (size)); | |||
} | |||
//============================================================================== | |||
@@ -1778,7 +1784,7 @@ DEF_CLASS_IID (JuceAudioProcessor) | |||
bool initModule() | |||
{ | |||
#if JUCE_MAC | |||
initialiseMacVST3(); | |||
initialiseMacVST(); | |||
#endif | |||
return true; | |||
@@ -1,273 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2015 - ROLI Ltd. | |||
Permission is granted to use this software under the terms of either: | |||
a) the GPL v2 (or any later version) | |||
b) the Affero GPL v3 | |||
Details of these licenses can be found at: www.gnu.org/licenses | |||
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. | |||
------------------------------------------------------------------------------ | |||
To release a closed-source product which uses JUCE, commercial licenses are | |||
available: visit www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
// Your project must contain an AppConfig.h file with your project-specific settings in it, | |||
// and your header search path must make it accessible to the module's files. | |||
#include "AppConfig.h" | |||
#include "../utility/juce_CheckSettingMacros.h" | |||
#if JucePlugin_Build_VST3 | |||
#define JUCE_MAC_WINDOW_VISIBITY_BODGE 1 | |||
#include "../utility/juce_IncludeSystemHeaders.h" | |||
#include "../utility/juce_IncludeModuleHeaders.h" | |||
#include "../utility/juce_FakeMouseMoveGenerator.h" | |||
#include "../utility/juce_CarbonVisibility.h" | |||
#undef Component | |||
#undef Point | |||
//============================================================================== | |||
namespace juce | |||
{ | |||
void initialiseMacVST3() | |||
{ | |||
#if ! JUCE_64BIT | |||
NSApplicationLoad(); | |||
#endif | |||
} | |||
#if ! JUCE_64BIT | |||
static void updateComponentPosVST3 (Component* const comp) | |||
{ | |||
DBG ("updateComponentPos()"); | |||
HIViewRef dummyView = (HIViewRef) (void*) (pointer_sized_int) | |||
comp->getProperties() ["dummyViewRef"].toString().getHexValue64(); | |||
HIRect r; | |||
HIViewGetFrame (dummyView, &r); | |||
HIViewRef root; | |||
HIViewFindByID (HIViewGetRoot (HIViewGetWindow (dummyView)), kHIViewWindowContentID, &root); | |||
HIViewConvertRect (&r, HIViewGetSuperview (dummyView), root); | |||
Rect windowPos; | |||
GetWindowBounds (HIViewGetWindow (dummyView), kWindowContentRgn, &windowPos); | |||
comp->setTopLeftPosition ((int) (windowPos.left + r.origin.x), | |||
(int) (windowPos.top + r.origin.y)); | |||
} | |||
static pascal OSStatus viewBoundsChangedEventVST3 (EventHandlerCallRef, EventRef, void* user) | |||
{ | |||
updateComponentPosVST3 ((Component*) user); | |||
return noErr; | |||
} | |||
#endif | |||
void* attachComponentToWindowRefVST3 (Component* comp, void* windowRef, bool isHIView) | |||
{ | |||
DBG ("attachComponentToWindowRef()"); | |||
JUCE_AUTORELEASEPOOL | |||
{ | |||
#if JUCE_64BIT | |||
(void) isHIView; | |||
NSView* parentView = (NSView*) windowRef; | |||
#if JucePlugin_EditorRequiresKeyboardFocus | |||
comp->addToDesktop (0, parentView); | |||
#else | |||
comp->addToDesktop (ComponentPeer::windowIgnoresKeyPresses, parentView); | |||
#endif | |||
// (this workaround is because Wavelab provides a zero-size parent view..) | |||
if ([parentView frame].size.height == 0) | |||
[((NSView*) comp->getWindowHandle()) setFrameOrigin: NSZeroPoint]; | |||
comp->setVisible (true); | |||
comp->toFront (false); | |||
[[parentView window] setAcceptsMouseMovedEvents: YES]; | |||
return parentView; | |||
#else | |||
//treat NSView like 64bit | |||
if (! isHIView) | |||
{ | |||
NSView* parentView = (NSView*) windowRef; | |||
#if JucePlugin_EditorRequiresKeyboardFocus | |||
comp->addToDesktop (0, parentView); | |||
#else | |||
comp->addToDesktop (ComponentPeer::windowIgnoresKeyPresses, parentView); | |||
#endif | |||
// (this workaround is because Wavelab provides a zero-size parent view..) | |||
if ([parentView frame].size.height == 0) | |||
[((NSView*) comp->getWindowHandle()) setFrameOrigin: NSZeroPoint]; | |||
comp->setVisible (true); | |||
comp->toFront (false); | |||
[[parentView window] setAcceptsMouseMovedEvents: YES]; | |||
return parentView; | |||
} | |||
NSWindow* hostWindow = [[NSWindow alloc] initWithWindowRef: windowRef]; | |||
[hostWindow retain]; | |||
[hostWindow setCanHide: YES]; | |||
[hostWindow setReleasedWhenClosed: YES]; | |||
HIViewRef parentView = nullptr; | |||
WindowAttributes attributes; | |||
GetWindowAttributes ((WindowRef) windowRef, &attributes); | |||
if ((attributes & kWindowCompositingAttribute) != 0) | |||
{ | |||
HIViewRef root = HIViewGetRoot ((WindowRef) windowRef); | |||
HIViewFindByID (root, kHIViewWindowContentID, &parentView); | |||
if (parentView == nullptr) | |||
parentView = root; | |||
} | |||
else | |||
{ | |||
GetRootControl ((WindowRef) windowRef, (ControlRef*) &parentView); | |||
if (parentView == nullptr) | |||
CreateRootControl ((WindowRef) windowRef, (ControlRef*) &parentView); | |||
} | |||
// It seems that the only way to successfully position our overlaid window is by putting a dummy | |||
// HIView into the host's carbon window, and then catching events to see when it gets repositioned | |||
HIViewRef dummyView = 0; | |||
HIImageViewCreate (0, &dummyView); | |||
HIRect r = { {0, 0}, { (float) comp->getWidth(), (float) comp->getHeight()} }; | |||
HIViewSetFrame (dummyView, &r); | |||
HIViewAddSubview (parentView, dummyView); | |||
comp->getProperties().set ("dummyViewRef", String::toHexString ((pointer_sized_int) (void*) dummyView)); | |||
EventHandlerRef ref; | |||
const EventTypeSpec kControlBoundsChangedEvent = { kEventClassControl, kEventControlBoundsChanged }; | |||
InstallEventHandler (GetControlEventTarget (dummyView), NewEventHandlerUPP (viewBoundsChangedEventVST3), 1, &kControlBoundsChangedEvent, (void*) comp, &ref); | |||
comp->getProperties().set ("boundsEventRef", String::toHexString ((pointer_sized_int) (void*) ref)); | |||
updateComponentPosVST3 (comp); | |||
#if ! JucePlugin_EditorRequiresKeyboardFocus | |||
comp->addToDesktop (ComponentPeer::windowIsTemporary | ComponentPeer::windowIgnoresKeyPresses); | |||
#else | |||
comp->addToDesktop (ComponentPeer::windowIsTemporary); | |||
#endif | |||
comp->setVisible (true); | |||
comp->toFront (false); | |||
NSView* pluginView = (NSView*) comp->getWindowHandle(); | |||
NSWindow* pluginWindow = [pluginView window]; | |||
[pluginWindow setExcludedFromWindowsMenu: YES]; | |||
[pluginWindow setCanHide: YES]; | |||
[hostWindow addChildWindow: pluginWindow | |||
ordered: NSWindowAbove]; | |||
[hostWindow orderFront: nil]; | |||
[pluginWindow orderFront: nil]; | |||
attachWindowHidingHooks (comp, (WindowRef) windowRef, hostWindow); | |||
return hostWindow; | |||
#endif | |||
} | |||
} | |||
void detachComponentFromWindowRefVST3 (Component* comp, void* nsWindow, bool isHIView) | |||
{ | |||
JUCE_AUTORELEASEPOOL | |||
{ | |||
#if ! JUCE_64BIT | |||
if (isHIView) | |||
{ | |||
JUCE_AUTORELEASEPOOL | |||
{ | |||
EventHandlerRef ref = (EventHandlerRef) (void*) (pointer_sized_int) | |||
comp->getProperties() ["boundsEventRef"].toString().getHexValue64(); | |||
RemoveEventHandler (ref); | |||
removeWindowHidingHooks (comp); | |||
HIViewRef dummyView = (HIViewRef) (void*) (pointer_sized_int) | |||
comp->getProperties() ["dummyViewRef"].toString().getHexValue64(); | |||
if (HIViewIsValid (dummyView)) | |||
CFRelease (dummyView); | |||
NSWindow* hostWindow = (NSWindow*) nsWindow; | |||
NSView* pluginView = (NSView*) comp->getWindowHandle(); | |||
NSWindow* pluginWindow = [pluginView window]; | |||
[hostWindow removeChildWindow: pluginWindow]; | |||
comp->removeFromDesktop(); | |||
[hostWindow release]; | |||
} | |||
// The event loop needs to be run between closing the window and deleting the plugin, | |||
// presumably to let the cocoa objects get tidied up. Leaving out this line causes crashes | |||
// in Live and Reaper when you delete the plugin with its window open. | |||
// (Doing it this way rather than using a single longer timout means that we can guarantee | |||
// how many messages will be dispatched, which seems to be vital in Reaper) | |||
for (int i = 20; --i >= 0;) | |||
MessageManager::getInstance()->runDispatchLoopUntil (1); | |||
return; | |||
} | |||
#endif | |||
(void) nsWindow; (void) isHIView; | |||
comp->removeFromDesktop(); | |||
} | |||
} | |||
void setNativeHostWindowSizeVST3 (void* nsWindow, Component* component, int newWidth, int newHeight, bool isHIView) | |||
{ | |||
JUCE_AUTORELEASEPOOL | |||
{ | |||
#if ! JUCE_64BIT | |||
if (isHIView) | |||
{ | |||
if (HIViewRef dummyView = (HIViewRef) (void*) (pointer_sized_int) | |||
component->getProperties() ["dummyViewRef"].toString().getHexValue64()) | |||
{ | |||
HIRect frameRect; | |||
HIViewGetFrame (dummyView, &frameRect); | |||
frameRect.size.width = newWidth; | |||
frameRect.size.height = newHeight; | |||
HIViewSetFrame (dummyView, &frameRect); | |||
} | |||
return; | |||
} | |||
#endif | |||
(void) nsWindow; (void) isHIView; | |||
component->setSize (newWidth, newHeight); | |||
} | |||
} | |||
} // (juce namespace) | |||
#endif |
@@ -42,7 +42,7 @@ public: | |||
bool fileMightContainThisPluginType (const String& fileOrIdentifier) override; | |||
String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) override; | |||
bool pluginNeedsRescanning (const PluginDescription&) override; | |||
StringArray searchPathsForPlugins (const FileSearchPath&, bool recursive); | |||
StringArray searchPathsForPlugins (const FileSearchPath&, bool recursive) override; | |||
bool doesPluginStillExist (const PluginDescription&) override; | |||
FileSearchPath getDefaultLocationsToSearch() override; | |||
bool canScanForPlugins() const override { return true; } | |||
@@ -840,9 +840,9 @@ public: | |||
if (audioUnit != nullptr) | |||
{ | |||
UInt32 paramListSize = 0; | |||
UInt32 dummy = 0, paramListSize = 0; | |||
AudioUnitGetProperty (audioUnit, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, | |||
0, 0, ¶mListSize); | |||
0, &dummy, ¶mListSize); | |||
if (paramListSize > 0) | |||
{ | |||
@@ -1145,7 +1145,7 @@ public: | |||
} | |||
//============================================================================== | |||
int getNumParameters() { return effect != nullptr ? effect->numParams : 0; } | |||
int getNumParameters() override { return effect != nullptr ? effect->numParams : 0; } | |||
float getParameter (int index) override | |||
{ | |||
@@ -78,7 +78,7 @@ public: | |||
setSource (new FileInputSource (file)) | |||
@endcode | |||
You can pass a zero in here to clear the thumbnail. | |||
You can pass a nullptr in here to clear the thumbnail. | |||
The source that is passed in will be deleted by this object when it is no longer needed. | |||
@returns true if the source could be opened as a valid audio file, false if this failed for | |||
some reason. | |||
@@ -56,7 +56,7 @@ public: | |||
setSource (new FileInputSource (file)) | |||
@endcode | |||
You can pass a zero in here to clear the thumbnail. | |||
You can pass a nullptr in here to clear the thumbnail. | |||
The source that is passed in will be deleted by this object when it is no longer needed. | |||
@returns true if the source could be opened as a valid audio file, false if this failed for | |||
some reason. | |||
@@ -230,21 +230,35 @@ void MidiKeyboardComponent::getKeyPos (int midiNoteNumber, int& x, int& w) const | |||
x -= xOffset + rx; | |||
} | |||
Rectangle<int> MidiKeyboardComponent::getWhiteNotePos (int noteNum) const | |||
Rectangle<int> MidiKeyboardComponent::getRectangleForKey (const int note) const | |||
{ | |||
jassert (note >= rangeStart && note <= rangeEnd); | |||
int x, w; | |||
getKeyPos (noteNum, x, w); | |||
Rectangle<int> pos; | |||
getKeyPos (note, x, w); | |||
switch (orientation) | |||
if (MidiMessage::isMidiNoteBlack (note)) | |||
{ | |||
case horizontalKeyboard: pos.setBounds (x, 0, w, getHeight()); break; | |||
case verticalKeyboardFacingLeft: pos.setBounds (0, x, getWidth(), w); break; | |||
case verticalKeyboardFacingRight: pos.setBounds (0, getHeight() - x - w, getWidth(), w); break; | |||
default: break; | |||
switch (orientation) | |||
{ | |||
case horizontalKeyboard: return Rectangle<int> (x, 0, w, blackNoteLength); | |||
case verticalKeyboardFacingLeft: return Rectangle<int> (getWidth() - blackNoteLength, x, blackNoteLength, w); | |||
case verticalKeyboardFacingRight: return Rectangle<int> (0, getHeight() - x - w, blackNoteLength, w); | |||
default: jassertfalse; break; | |||
} | |||
} | |||
else | |||
{ | |||
switch (orientation) | |||
{ | |||
case horizontalKeyboard: return Rectangle<int> (x, 0, w, getHeight()); | |||
case verticalKeyboardFacingLeft: return Rectangle<int> (0, x, getWidth(), w); | |||
case verticalKeyboardFacingRight: return Rectangle<int> (0, getHeight() - x - w, getWidth(), w); | |||
default: jassertfalse; break; | |||
} | |||
} | |||
return pos; | |||
return Rectangle<int>(); | |||
} | |||
int MidiKeyboardComponent::getKeyStartPosition (const int midiNoteNumber) const | |||
@@ -339,7 +353,7 @@ int MidiKeyboardComponent::remappedXYToNote (Point<int> pos, float& mousePositio | |||
void MidiKeyboardComponent::repaintNote (const int noteNum) | |||
{ | |||
if (noteNum >= rangeStart && noteNum <= rangeEnd) | |||
repaint (getWhiteNotePos (noteNum)); | |||
repaint (getRectangleForKey (noteNum)); | |||
} | |||
void MidiKeyboardComponent::paint (Graphics& g) | |||
@@ -349,9 +363,7 @@ void MidiKeyboardComponent::paint (Graphics& g) | |||
const Colour lineColour (findColour (keySeparatorLineColourId)); | |||
const Colour textColour (findColour (textLabelColourId)); | |||
int octave; | |||
for (octave = 0; octave < 128; octave += 12) | |||
for (int octave = 0; octave < 128; octave += 12) | |||
{ | |||
for (int white = 0; white < 7; ++white) | |||
{ | |||
@@ -359,7 +371,7 @@ void MidiKeyboardComponent::paint (Graphics& g) | |||
if (noteNum >= rangeStart && noteNum <= rangeEnd) | |||
{ | |||
const Rectangle<int> pos (getWhiteNotePos (noteNum)); | |||
Rectangle<int> pos = getRectangleForKey (noteNum); | |||
drawWhiteNote (noteNum, g, pos.getX(), pos.getY(), pos.getWidth(), pos.getHeight(), | |||
state.isNoteOnForChannels (midiInChannelMask, noteNum), | |||
@@ -416,7 +428,7 @@ void MidiKeyboardComponent::paint (Graphics& g) | |||
const Colour blackNoteColour (findColour (blackNoteColourId)); | |||
for (octave = 0; octave < 128; octave += 12) | |||
for (int octave = 0; octave < 128; octave += 12) | |||
{ | |||
for (int black = 0; black < 5; ++black) | |||
{ | |||
@@ -424,16 +436,7 @@ void MidiKeyboardComponent::paint (Graphics& g) | |||
if (noteNum >= rangeStart && noteNum <= rangeEnd) | |||
{ | |||
getKeyPos (noteNum, x, w); | |||
Rectangle<int> pos; | |||
switch (orientation) | |||
{ | |||
case horizontalKeyboard: pos.setBounds (x, 0, w, blackNoteLength); break; | |||
case verticalKeyboardFacingLeft: pos.setBounds (width - blackNoteLength, x, blackNoteLength, w); break; | |||
case verticalKeyboardFacingRight: pos.setBounds (0, height - x - w, blackNoteLength, w); break; | |||
default: break; | |||
} | |||
Rectangle<int> pos = getRectangleForKey (noteNum); | |||
drawBlackNote (noteNum, g, pos.getX(), pos.getY(), pos.getWidth(), pos.getHeight(), | |||
state.isNoteOnForChannels (midiInChannelMask, noteNum), | |||
@@ -366,6 +366,10 @@ protected: | |||
virtual void getKeyPosition (int midiNoteNumber, float keyWidth, | |||
int& x, int& w) const; | |||
/** Returns the rectangle for a given key if within the displayable range */ | |||
Rectangle<int> getRectangleForKey (int midiNoteNumber) const; | |||
private: | |||
//============================================================================== | |||
friend class MidiKeyboardUpDownButton; | |||
@@ -400,9 +404,8 @@ private: | |||
void resetAnyKeysInUse(); | |||
void updateNoteUnderMouse (Point<int>, bool isDown, int fingerNum); | |||
void updateNoteUnderMouse (const MouseEvent&, bool isDown); | |||
void repaintNote (const int midiNoteNumber); | |||
void repaintNote (int midiNoteNumber); | |||
void setLowestVisibleKeyFloat (float noteNumber); | |||
Rectangle<int> getWhiteNotePos (int noteNumber) const; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiKeyboardComponent) | |||
}; | |||
@@ -49,7 +49,7 @@ public: | |||
virtual int toInt (const ValueUnion&) const noexcept { return 0; } | |||
virtual int64 toInt64 (const ValueUnion&) const noexcept { return 0; } | |||
virtual double toDouble (const ValueUnion&) const noexcept { return 0; } | |||
virtual String toString (const ValueUnion&) const { return String::empty; } | |||
virtual String toString (const ValueUnion&) const { return String(); } | |||
virtual bool toBool (const ValueUnion&) const noexcept { return false; } | |||
virtual ReferenceCountedObject* toObject (const ValueUnion&) const noexcept { return nullptr; } | |||
virtual Array<var>* toArray (const ValueUnion&) const noexcept { return nullptr; } | |||
@@ -274,7 +274,7 @@ public: | |||
} | |||
String toString (const ValueUnion& data) const override { return "Object 0x" + String::toHexString ((int) (pointer_sized_int) data.objectValue); } | |||
bool toBool (const ValueUnion& data) const noexcept override { return data.objectValue != 0; } | |||
bool toBool (const ValueUnion& data) const noexcept override { return data.objectValue != nullptr; } | |||
ReferenceCountedObject* toObject (const ValueUnion& data) const noexcept override { return data.objectValue; } | |||
bool isObject() const noexcept override { return true; } | |||
@@ -992,7 +992,8 @@ double Expression::evaluate() const | |||
double Expression::evaluate (const Expression::Scope& scope) const | |||
{ | |||
return term->resolve (scope, 0)->toDouble(); | |||
String err; | |||
return evaluate (scope, err); | |||
} | |||
double Expression::evaluate (const Scope& scope, String& evaluationError) const | |||
@@ -1038,20 +1039,16 @@ Expression Expression::adjustedToGiveNewResult (const double targetValue, const | |||
jassert (termToAdjust != nullptr); | |||
const Term* const parent = Helpers::findDestinationFor (newTerm, termToAdjust); | |||
if (parent == nullptr) | |||
if (const Term* parent = Helpers::findDestinationFor (newTerm, termToAdjust)) | |||
{ | |||
termToAdjust->value = targetValue; | |||
if (const Helpers::TermPtr reverseTerm = parent->createTermToEvaluateInput (scope, termToAdjust, targetValue, newTerm)) | |||
termToAdjust->value = Expression (reverseTerm).evaluate (scope); | |||
else | |||
return Expression (targetValue); | |||
} | |||
else | |||
{ | |||
const Helpers::TermPtr reverseTerm (parent->createTermToEvaluateInput (scope, termToAdjust, targetValue, newTerm)); | |||
if (reverseTerm == nullptr) | |||
return Expression (targetValue); | |||
termToAdjust->value = reverseTerm->resolve (scope, 0)->toDouble(); | |||
termToAdjust->value = targetValue; | |||
} | |||
return Expression (newTerm.release()); | |||
@@ -207,7 +207,7 @@ private: | |||
#endif | |||
//============================================================================== | |||
#elif (JUCE_GCC || JUCE_CLANG) && ! JUCE_WINDOWS | |||
#elif (JUCE_GCC || JUCE_CLANG) && ! JUCE_MSVC | |||
#define JUCE_ATOMICS_GCC 1 // GCC with intrinsics | |||
#if JUCE_IOS || JUCE_ANDROID // (64-bit ops will compile but not link on these mobile OSes) | |||
@@ -429,7 +429,7 @@ String SystemStats::getFullUserName() | |||
String SystemStats::getComputerName() | |||
{ | |||
TCHAR text [MAX_COMPUTERNAME_LENGTH + 1] = { 0 }; | |||
TCHAR text[128] = { 0 }; | |||
DWORD len = (DWORD) numElementsInArray (text) - 1; | |||
GetComputerName (text, &len); | |||
return String (text, len); | |||
@@ -26,28 +26,44 @@ | |||
============================================================================== | |||
*/ | |||
MACAddress::MACAddress() | |||
MACAddress::MACAddress() noexcept | |||
{ | |||
zeromem (address, sizeof (address)); | |||
} | |||
MACAddress::MACAddress (const MACAddress& other) | |||
MACAddress::MACAddress (const MACAddress& other) noexcept | |||
{ | |||
memcpy (address, other.address, sizeof (address)); | |||
} | |||
MACAddress& MACAddress::operator= (const MACAddress& other) | |||
MACAddress& MACAddress::operator= (const MACAddress& other) noexcept | |||
{ | |||
memcpy (address, other.address, sizeof (address)); | |||
return *this; | |||
} | |||
MACAddress::MACAddress (const uint8 bytes[6]) | |||
MACAddress::MACAddress (const uint8 bytes[6]) noexcept | |||
{ | |||
memcpy (address, bytes, sizeof (address)); | |||
} | |||
MACAddress::MACAddress (StringRef addressString) | |||
{ | |||
MemoryBlock hex; | |||
hex.loadFromHexString (addressString); | |||
if (hex.getSize() == sizeof (address)) | |||
memcpy (address, hex.getData(), sizeof (address)); | |||
else | |||
zeromem (address, sizeof (address)); | |||
} | |||
String MACAddress::toString() const | |||
{ | |||
return toString ("-"); | |||
} | |||
String MACAddress::toString (StringRef separator) const | |||
{ | |||
String s; | |||
@@ -56,7 +72,7 @@ String MACAddress::toString() const | |||
s << String::toHexString ((int) address[i]).paddedLeft ('0', 2); | |||
if (i < sizeof (address) - 1) | |||
s << '-'; | |||
s << separator; | |||
} | |||
return s; | |||
@@ -43,16 +43,22 @@ public: | |||
//============================================================================== | |||
/** Creates a null address (00-00-00-00-00-00). */ | |||
MACAddress(); | |||
MACAddress() noexcept; | |||
/** Creates a copy of another address. */ | |||
MACAddress (const MACAddress&); | |||
MACAddress (const MACAddress&) noexcept; | |||
/** Creates a copy of another address. */ | |||
MACAddress& operator= (const MACAddress&); | |||
MACAddress& operator= (const MACAddress&) noexcept; | |||
/** Creates an address from 6 bytes. */ | |||
explicit MACAddress (const uint8 bytes[6]); | |||
explicit MACAddress (const uint8 bytes[6]) noexcept; | |||
/** Creates an address from a hex string. | |||
If the string isn't a 6-byte hex value, this will just default-initialise | |||
the object. | |||
*/ | |||
explicit MACAddress (StringRef address); | |||
/** Returns a pointer to the 6 bytes that make up this address. */ | |||
const uint8* getBytes() const noexcept { return address; } | |||
@@ -60,6 +66,9 @@ public: | |||
/** Returns a dash-separated string in the form "11-22-33-44-55-66" */ | |||
String toString() const; | |||
/** Returns a hex string of this address, using a custom separator between each byte. */ | |||
String toString (StringRef separator) const; | |||
/** Returns the address in the lower 6 bytes of an int64. | |||
This uses a little-endian arrangement, with the first byte of the address being | |||
@@ -67,13 +67,18 @@ int MemoryInputStream::read (void* const buffer, const int howMany) | |||
{ | |||
jassert (buffer != nullptr && howMany >= 0); | |||
const int num = jmin (howMany, (int) (dataSize - position)); | |||
if (num <= 0) | |||
if (howMany <= 0 || position >= dataSize) | |||
return 0; | |||
memcpy (buffer, addBytesToPointer (data, position), (size_t) num); | |||
position += (unsigned int) num; | |||
return num; | |||
const size_t num = jmin ((size_t) howMany, dataSize - position); | |||
if (num > 0) | |||
{ | |||
memcpy (buffer, addBytesToPointer (data, position), num); | |||
position += num; | |||
} | |||
return (int) num; | |||
} | |||
bool MemoryInputStream::isExhausted() | |||
@@ -40,6 +40,10 @@ | |||
When you create a Value with its default constructor, it acts as a wrapper around a | |||
simple var object, but by creating a Value that refers to a custom subclass of ValueSource, | |||
you can map the Value onto any kind of underlying data. | |||
Important note! The Value class is not thread-safe! If you're accessing one from | |||
multiple threads, then you'll need to use your own synchronisation around any code | |||
that accesses it. | |||
*/ | |||
class JUCE_API Value | |||
{ | |||
@@ -222,20 +222,21 @@ namespace CoreTextTypeLayout | |||
CFStringRef cfText = text.getText().toCFString(); | |||
CFMutableAttributedStringRef attribString = CFAttributedStringCreateMutable (kCFAllocatorDefault, 0); | |||
CFAttributedStringReplaceString (attribString, CFRangeMake(0, 0), cfText); | |||
CFAttributedStringReplaceString (attribString, CFRangeMake (0, 0), cfText); | |||
CFRelease (cfText); | |||
const int numCharacterAttributes = text.getNumAttributes(); | |||
const CFIndex attribStringLen = CFAttributedStringGetLength (attribString); | |||
for (int i = 0; i < numCharacterAttributes; ++i) | |||
{ | |||
const AttributedString::Attribute& attr = *text.getAttribute (i); | |||
const int rangeStart = attr.range.getStart(); | |||
if (attr.range.getStart() > CFAttributedStringGetLength (attribString)) | |||
if (rangeStart >= attribStringLen) | |||
continue; | |||
Range<int> range (attr.range); | |||
range.setEnd (jmin (range.getEnd(), (int) CFAttributedStringGetLength (attribString))); | |||
CFRange range = CFRangeMake (rangeStart, jmin (attr.range.getEnd(), (int) attribStringLen) - rangeStart); | |||
if (const Font* const f = attr.getFont()) | |||
{ | |||
@@ -243,8 +244,19 @@ namespace CoreTextTypeLayout | |||
{ | |||
ctFontRef = getFontWithPointSize (ctFontRef, f->getHeight() * getHeightToPointsFactor (ctFontRef)); | |||
CFAttributedStringSetAttribute (attribString, CFRangeMake (range.getStart(), range.getLength()), | |||
kCTFontAttributeName, ctFontRef); | |||
CFAttributedStringSetAttribute (attribString, range, kCTFontAttributeName, ctFontRef); | |||
float extraKerning = f->getExtraKerningFactor(); | |||
if (extraKerning != 0.0f) | |||
{ | |||
extraKerning *= f->getHeight(); | |||
CFNumberRef numberRef = CFNumberCreate (0, kCFNumberFloatType, &extraKerning); | |||
CFAttributedStringSetAttribute (attribString, range, kCTKernAttributeName, numberRef); | |||
CFRelease (numberRef); | |||
} | |||
CFRelease (ctFontRef); | |||
} | |||
} | |||
@@ -264,9 +276,7 @@ namespace CoreTextTypeLayout | |||
col->getFloatAlpha()); | |||
#endif | |||
CFAttributedStringSetAttribute (attribString, | |||
CFRangeMake (range.getStart(), range.getLength()), | |||
kCTForegroundColorAttributeName, colour); | |||
CFAttributedStringSetAttribute (attribString, range, kCTForegroundColorAttributeName, colour); | |||
CGColorRelease (colour); | |||
} | |||
} | |||
@@ -51,10 +51,10 @@ public: | |||
Remember that when the target component is resized, it'll need to move and | |||
resize this component to keep it in place, as this won't happen automatically. | |||
If the constrainer parameter is non-zero, then this object will be used to enforce | |||
If a constrainer object is provided, then this object will be used to enforce | |||
limits on the size and position that the component can be stretched to. Make sure | |||
that the constrainer isn't deleted while still in use by this object. If you | |||
pass a zero in here, no limits will be put on the sizes it can be stretched to. | |||
pass a nullptr in here, no limits will be put on the sizes it can be stretched to. | |||
@see ComponentBoundsConstrainer | |||
*/ | |||
@@ -131,7 +131,7 @@ public: | |||
/** OSX ONLY - Sets the model that is currently being shown as the main | |||
menu bar at the top of the screen on the Mac. | |||
You can pass 0 to stop the current model being displayed. Be careful | |||
You can pass nullptr to stop the current model being displayed. Be careful | |||
not to delete a model while it is being used. | |||
An optional extra menu can be specified, containing items to add to the top of | |||
@@ -39,6 +39,14 @@ | |||
#define WM_APPCOMMAND 0x0319 | |||
#endif | |||
#ifndef MI_WP_SIGNATURE | |||
#define MI_WP_SIGNATURE 0xFF515700 | |||
#endif | |||
#ifndef SIGNATURE_MASK | |||
#define SIGNATURE_MASK 0xFFFFFF00 | |||
#endif | |||
extern void juce_repeatLastProcessPriority(); | |||
extern void juce_checkCurrentlyFocusedTopLevelWindow(); // in juce_TopLevelWindow.cpp | |||
extern bool juce_isRunningInWine(); | |||
@@ -1706,8 +1714,22 @@ private: | |||
return 1000 / 60; // Throttling the incoming mouse-events seems to still be needed in XP.. | |||
} | |||
bool isTouchEvent() noexcept | |||
{ | |||
if (registerTouchWindow == nullptr) | |||
return false; | |||
LPARAM dw = GetMessageExtraInfo(); | |||
// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms703320(v=vs.85).aspx | |||
return (dw & SIGNATURE_MASK) == MI_WP_SIGNATURE; | |||
} | |||
void doMouseMove (Point<float> position) | |||
{ | |||
// this will be handled by WM_TOUCH | |||
if (isTouchEvent()) | |||
return; | |||
if (! isMouseOver) | |||
{ | |||
isMouseOver = true; | |||
@@ -1744,6 +1766,10 @@ private: | |||
void doMouseDown (Point<float> position, const WPARAM wParam) | |||
{ | |||
// this will be handled by WM_TOUCH | |||
if (isTouchEvent()) | |||
return; | |||
if (GetCapture() != hwnd) | |||
SetCapture (hwnd); | |||
@@ -1760,6 +1786,10 @@ private: | |||
void doMouseUp (Point<float> position, const WPARAM wParam) | |||
{ | |||
// this will be handled by WM_TOUCH | |||
if (isTouchEvent()) | |||
return; | |||
updateModifiersFromWParam (wParam); | |||
const bool wasDragging = isDragging; | |||
isDragging = false; | |||
@@ -1879,8 +1909,7 @@ private: | |||
const DWORD flags = inputInfo[i].dwFlags; | |||
if ((flags & (TOUCHEVENTF_DOWN | TOUCHEVENTF_MOVE | TOUCHEVENTF_UP)) != 0) | |||
if (! handleTouchInput (inputInfo[i], (flags & TOUCHEVENTF_PRIMARY) != 0, | |||
(flags & TOUCHEVENTF_DOWN) != 0, (flags & TOUCHEVENTF_UP) != 0)) | |||
if (! handleTouchInput (inputInfo[i], (flags & TOUCHEVENTF_DOWN) != 0, (flags & TOUCHEVENTF_UP) != 0)) | |||
return 0; // abandon method if this window was deleted by the callback | |||
} | |||
} | |||
@@ -1889,7 +1918,7 @@ private: | |||
return 0; | |||
} | |||
bool handleTouchInput (const TOUCHINPUT& touch, const bool isPrimary, const bool isDown, const bool isUp) | |||
bool handleTouchInput (const TOUCHINPUT& touch, const bool isDown, const bool isUp) | |||
{ | |||
bool isCancel = false; | |||
const int touchIndex = currentTouches.getIndexOfTouch (touch.dwID); | |||
@@ -1903,13 +1932,10 @@ private: | |||
currentModifiers = currentModifiers.withoutMouseButtons().withFlags (ModifierKeys::leftButtonModifier); | |||
modsToSend = currentModifiers; | |||
if (! isPrimary) | |||
{ | |||
// this forces a mouse-enter/up event, in case for some reason we didn't get a mouse-up before. | |||
handleMouseEvent (touchIndex, pos.toFloat(), modsToSend.withoutMouseButtons(), time); | |||
if (! isValidPeer (this)) // (in case this component was deleted by the event) | |||
return false; | |||
} | |||
// this forces a mouse-enter/up event, in case for some reason we didn't get a mouse-up before. | |||
handleMouseEvent (touchIndex, pos.toFloat(), modsToSend.withoutMouseButtons(), time); | |||
if (! isValidPeer (this)) // (in case this component was deleted by the event) | |||
return false; | |||
} | |||
else if (isUp) | |||
{ | |||
@@ -1930,14 +1956,11 @@ private: | |||
currentModifiers = currentModifiers.withoutMouseButtons(); | |||
} | |||
if (! isPrimary) | |||
{ | |||
handleMouseEvent (touchIndex, pos.toFloat(), modsToSend, time); | |||
if (! isValidPeer (this)) // (in case this component was deleted by the event) | |||
return false; | |||
} | |||
handleMouseEvent (touchIndex, pos.toFloat(), modsToSend, time); | |||
if (! isValidPeer (this)) // (in case this component was deleted by the event) | |||
return false; | |||
if ((isUp || isCancel) && ! isPrimary) | |||
if (isUp || isCancel) | |||
{ | |||
handleMouseEvent (touchIndex, Point<float> (-10.0f, -10.0f), currentModifiers, time); | |||
if (! isValidPeer (this)) | |||
@@ -309,7 +309,7 @@ TextEditor* Label::createEditorComponent() | |||
copyColourIfSpecified (*this, *ed, textWhenEditingColourId, TextEditor::textColourId); | |||
copyColourIfSpecified (*this, *ed, backgroundWhenEditingColourId, TextEditor::backgroundColourId); | |||
copyColourIfSpecified (*this, *ed, outlineWhenEditingColourId, TextEditor::outlineColourId); | |||
copyColourIfSpecified (*this, *ed, outlineWhenEditingColourId, TextEditor::focusedOutlineColourId); | |||
return ed; | |||
} | |||
@@ -89,7 +89,7 @@ void AlertWindow::addButton (const String& name, | |||
const KeyPress& shortcutKey1, | |||
const KeyPress& shortcutKey2) | |||
{ | |||
TextButton* const b = new TextButton (name, String::empty); | |||
TextButton* const b = new TextButton (name, String()); | |||
buttons.add (b); | |||
b->setWantsKeyboardFocus (true); | |||
@@ -143,9 +143,9 @@ void AlertWindow::addTextEditor (const String& name, | |||
ed->setColour (TextEditor::outlineColourId, findColour (ComboBox::outlineColourId)); | |||
ed->setFont (getLookAndFeel().getAlertWindowMessageFont()); | |||
addAndMakeVisible (ed); | |||
ed->setText (initialContents); | |||
ed->setCaretPosition (initialContents.length()); | |||
addAndMakeVisible (ed); | |||
textboxNames.add (onScreenLabel); | |||
updateLayout (false); | |||
@@ -165,7 +165,7 @@ String AlertWindow::getTextEditorContents (const String& nameOfTextEditor) const | |||
if (TextEditor* const t = getTextEditor (nameOfTextEditor)) | |||
return t->getText(); | |||
return String::empty; | |||
return String(); | |||
} | |||
@@ -66,14 +66,14 @@ struct CppTokeniserFunctions | |||
static const char* const keywords7Char[] = | |||
{ "nullptr", "alignas", "alignof", "default", "mutable", "private", | |||
"typedef", "virtual", "wchar_t", nullptr }; | |||
"typedef", "virtual", "wchar_t", "__cdecl", "_Pragma", "uint8_t", nullptr }; | |||
static const char* const keywordsOther[] = | |||
{ "char16_t", "char32_t", "const_cast", "constexpr", "continue", "decltype", "dynamic_cast", | |||
"explicit", "namespace", "noexcept", "operator", "protected", "register", "reinterpret_cast", | |||
"static_assert", "static_cast", "template", "thread_local", "typename", "unsigned", "volatile", | |||
"@class", "@dynamic", "@end", "@implementation", "@interface", "@public", "@private", | |||
"@protected", "@property", "@synthesize", nullptr }; | |||
"@protected", "@property", "@synthesize", "__fastcall", "__stdcall", nullptr }; | |||
const char* const* k; | |||
@@ -234,7 +234,7 @@ void OnlineUnlockForm::showBubbleMessage (const String& text, Component& target) | |||
addChildComponent (bubble); | |||
AttributedString attString; | |||
attString.append (text, Font (15.0f)); | |||
attString.append (text, Font (16.0f)); | |||
bubble->showAt (getLocalArea (&target, target.getLocalBounds()), | |||
attString, 500, // numMillisecondsBeforeRemoving | |||