diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index dbac461e49..9617bfa2c6 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -646,6 +646,10 @@ #include #endif +#ifndef WM_APPCOMMAND + #define WM_APPCOMMAND 0x0319 +#endif + /** A simple COM smart pointer. Avoids having to include ATL just to get one of these. */ @@ -1716,20 +1720,6 @@ const String SystemStats::getJUCEVersion() static JuceVersionPrinter juceVersionPrinter; #endif -#ifdef JUCE_DLL - void* juce_Malloc (int size) { return malloc (size); } - void* juce_Calloc (int size) { return calloc (1, size); } - void* juce_Realloc (void* block, int size) { return realloc (block, size); } - void juce_Free (void* block) { free (block); } - - #if JUCE_MSVC && JUCE_CHECK_MEMORY_LEAKS - void* juce_DebugMalloc (int size, const char* file, int line) { return _malloc_dbg (size, _NORMAL_BLOCK, file, line); } - void* juce_DebugCalloc (int size, const char* file, int line) { return _calloc_dbg (1, size, _NORMAL_BLOCK, file, line); } - void* juce_DebugRealloc (void* block, int size, const char* file, int line) { return _realloc_dbg (block, size, _NORMAL_BLOCK, file, line); } - void juce_DebugFree (void* block) { _free_dbg (block, _NORMAL_BLOCK); } - #endif -#endif - END_JUCE_NAMESPACE /*** End of inlined file: juce_SystemStats.cpp ***/ @@ -2032,7 +2022,9 @@ uint32 Time::getMillisecondCounter() noexcept uint32 Time::getApproximateMillisecondCounter() noexcept { - jassert (TimeHelpers::lastMSCounterValue != 0); + if (TimeHelpers::lastMSCounterValue == 0) + getMillisecondCounter(); + return TimeHelpers::lastMSCounterValue; } @@ -4164,7 +4156,7 @@ Identifier::Identifier (const String& name_) { /* An Identifier string must be suitable for use as a script variable or XML attribute, so it can only contain this limited set of characters.. */ - jassert (name_.containsOnly ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_") && name_.isNotEmpty()); + jassert (isValidIdentifier (name_)); } Identifier::Identifier (const char* const name_) @@ -4172,13 +4164,19 @@ Identifier::Identifier (const char* const name_) { /* An Identifier string must be suitable for use as a script variable or XML attribute, so it can only contain this limited set of characters.. */ - jassert (toString().containsOnly ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_") && toString().isNotEmpty()); + jassert (isValidIdentifier (toString())); } Identifier::~Identifier() { } +bool Identifier::isValidIdentifier (const String& possibleIdentifier) noexcept +{ + return possibleIdentifier.isNotEmpty() + && possibleIdentifier.containsOnly ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"); +} + END_JUCE_NAMESPACE /*** End of inlined file: juce_Identifier.cpp ***/ @@ -4740,7 +4738,7 @@ const var NamedValueSet::getWithDefault (const Identifier& name, const var& defa return v != nullptr ? *v : defaultReturnValue; } -var* NamedValueSet::getVarPointer (const Identifier& name) const +var* NamedValueSet::getVarPointer (const Identifier& name) const noexcept { for (NamedValue* i = values; i != nullptr; i = i->nextListItem) if (i->name == name) @@ -13550,7 +13548,7 @@ struct StringCopier jassert (maxBufferSizeBytes >= 0); // keep this value positive, or no characters will be copied! if (buffer == nullptr) - return (int) CharPointerType_Dest::getBytesRequiredFor (source); + return (int) (CharPointerType_Dest::getBytesRequiredFor (source) + sizeof (typename CharPointerType_Dest::CharType)); return CharPointerType_Dest (buffer).writeWithDestByteLimit (source, maxBufferSizeBytes); } @@ -24596,7 +24594,7 @@ void AudioTransportSource::setSource (PositionableAudioSource* const newSource, if (readAheadBufferSize_ > 0) newPositionableSource = newBufferingSource - = new BufferingAudioSource (newPositionableSource, false, readAheadBufferSize_); + = new BufferingAudioSource (newPositionableSource, false, readAheadBufferSize_, maxNumChannels); newPositionableSource->setNextReadPosition (0); @@ -24897,11 +24895,13 @@ juce_ImplementSingleton (SharedBufferingAudioSourceThread) BufferingAudioSource::BufferingAudioSource (PositionableAudioSource* source_, const bool deleteSourceWhenDeleted_, - int numberOfSamplesToBuffer_) + const int numberOfSamplesToBuffer_, + const int numberOfChannels_) : source (source_), deleteSourceWhenDeleted (deleteSourceWhenDeleted_), numberOfSamplesToBuffer (jmax (1024, numberOfSamplesToBuffer_)), - buffer (2, 0), + numberOfChannels (numberOfChannels_), + buffer (numberOfChannels_, 0), bufferValidStart (0), bufferValidEnd (0), nextPlayPos (0), @@ -24930,7 +24930,7 @@ void BufferingAudioSource::prepareToPlay (int samplesPerBlockExpected, double sa sampleRate = sampleRate_; - buffer.setSize (2, jmax (samplesPerBlockExpected * 2, numberOfSamplesToBuffer)); + buffer.setSize (numberOfChannels, jmax (samplesPerBlockExpected * 2, numberOfSamplesToBuffer)); buffer.clear(); bufferValidStart = 0; @@ -24953,7 +24953,7 @@ void BufferingAudioSource::releaseResources() if (thread != nullptr) thread->removeSource (this); - buffer.setSize (2, 0); + buffer.setSize (numberOfChannels, 0); source->releaseResources(); } @@ -24980,7 +24980,7 @@ void BufferingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info if (validStart < validEnd) { - for (int chan = jmin (2, info.buffer->getNumChannels()); --chan >= 0;) + for (int chan = jmin (numberOfChannels, info.buffer->getNumChannels()); --chan >= 0;) { const int startBufferIndex = (validStart + nextPlayPos) % buffer.getNumSamples(); const int endBufferIndex = (validEnd + nextPlayPos) % buffer.getNumSamples(); @@ -40070,13 +40070,14 @@ public: { const uint32 now = Time::getMillisecondCounter(); - if (now <= lastTime) + if (now == lastTime) { - wait (2); + wait (1); continue; } - const int elapsed = now - lastTime; + const int elapsed = now >= lastTime ? (now - lastTime) + : (std::numeric_limits::max() - (lastTime - now)); lastTime = now; const int timeUntilFirstTimer = getTimeUntilFirstTimer (elapsed); @@ -70513,20 +70514,22 @@ public: { if (isVisible()) { + WeakReference deletionChecker (this); + activeSubMenu = nullptr; currentChild = nullptr; - exitModalState (item != nullptr ? item->itemId : 0); - - if (makeInvisible) - setVisible (false); - if (item != nullptr && item->commandManager != nullptr && item->itemId != 0) { *managerOfChosenCommand = item->commandManager; } + + exitModalState (item != nullptr ? item->itemId : 0); + + if (makeInvisible && (deletionChecker != nullptr)) + setVisible (false); } } @@ -79046,9 +79049,7 @@ void ComponentPeer::addMaskedRegion (int x, int y, int w, int h) const StringArray ComponentPeer::getAvailableRenderingEngines() { - StringArray s; - s.add ("Software Renderer"); - return s; + return StringArray ("Software Renderer"); } int ComponentPeer::getCurrentRenderingEngine() const @@ -79701,12 +79702,12 @@ void ResizableWindow::resized() { if (resizableBorder != nullptr) { - #if JUCE_WINDOWS || JUCE_LINUX + #if JUCE_WINDOWS || JUCE_LINUX // hide the resizable border if the OS already provides one.. resizableBorder->setVisible (! (isFullScreen() || isUsingNativeTitleBar())); - #else + #else resizableBorder->setVisible (! isFullScreen()); - #endif + #endif resizableBorder->setBorderThickness (getBorderThickness()); resizableBorder->setSize (getWidth(), getHeight()); @@ -79715,12 +79716,12 @@ void ResizableWindow::resized() if (resizableCorner != nullptr) { - #if JUCE_MAC + #if JUCE_MAC // hide the resizable border if the OS already provides one.. resizableCorner->setVisible (! (isFullScreen() || isUsingNativeTitleBar())); - #else + #else resizableCorner->setVisible (! isFullScreen()); - #endif + #endif const int resizerSize = 18; resizableCorner->setBounds (getWidth() - resizerSize, @@ -79733,9 +79734,9 @@ void ResizableWindow::resized() updateLastPos(); - #if JUCE_DEBUG + #if JUCE_DEBUG hasBeenResized = true; - #endif + #endif } void ResizableWindow::childBoundsChanged (Component* child) @@ -79862,7 +79863,7 @@ void ResizableWindow::paint (Graphics& g) getBorderThickness(), *this); } -#if JUCE_DEBUG + #if JUCE_DEBUG /* If this fails, then you've probably written a subclass with a resized() callback but forgotten to make it call its parent class's resized() method. @@ -79875,7 +79876,7 @@ void ResizableWindow::paint (Graphics& g) layout. */ jassert (hasBeenResized || (getWidth() == 0 && getHeight() == 0)); -#endif + #endif } void ResizableWindow::lookAndFeelChanged() @@ -243769,6 +243770,11 @@ void Logger::outputDebugString (const String& text) OutputDebugString ((text + "\n").toWideCharPointer()); } +#ifdef JUCE_DLL + JUCE_API void* juceDLL_malloc (size_t sz) { return ::malloc (sz); } + JUCE_API void juceDLL_free (void* block) { ::free (block); } +#endif + #if JUCE_USE_INTRINSICS || JUCE_64BIT // CPU info functions using intrinsics... @@ -244191,10 +244197,10 @@ void JUCE_API juce_threadEntryPoint (void*); static unsigned int __stdcall threadEntryProc (void* userData) { - #if ! JUCE_ONLY_BUILD_CORE_LIBRARY + #if ! JUCE_ONLY_BUILD_CORE_LIBRARY AttachThreadInput (GetWindowThreadProcessId (juce_messageWindowHandle, 0), GetCurrentThreadId(), TRUE); - #endif + #endif juce_threadEntryPoint (userData); @@ -244220,16 +244226,16 @@ void Thread::killThread() { if (threadHandle_ != 0) { - #if JUCE_DEBUG + #if JUCE_DEBUG OutputDebugString (_T("** Warning - Forced thread termination **\n")); - #endif + #endif TerminateThread (threadHandle_, 0); } } void Thread::setCurrentThreadName (const String& name) { - #if JUCE_DEBUG && JUCE_MSVC + #if JUCE_DEBUG && JUCE_MSVC struct { DWORD dwType; @@ -244249,9 +244255,9 @@ void Thread::setCurrentThreadName (const String& name) } __except (EXCEPTION_CONTINUE_EXECUTION) {} - #else + #else (void) name; - #endif + #endif } Thread::ThreadID Thread::getCurrentThreadId() @@ -244397,7 +244403,7 @@ void PlatformUtilities::freeDynamicLibrary (void* h) { JUCE_TRY { - if (h != 0) + if (h != nullptr) FreeLibrary ((HMODULE) h); } JUCE_CATCH_ALL @@ -244405,7 +244411,7 @@ void PlatformUtilities::freeDynamicLibrary (void* h) void* PlatformUtilities::getProcedureEntryPoint (void* h, const String& name) { - return (h != 0) ? (void*) GetProcAddress ((HMODULE) h, name.toUTF8()) : nullptr; // (void* cast is required for mingw) + return (h != nullptr) ? (void*) GetProcAddress ((HMODULE) h, name.toUTF8()) : nullptr; // (void* cast is required for mingw) } class InterProcessLock::Pimpl @@ -245366,11 +245372,11 @@ void NamedPipe::cancelPendingReads() #if JUCE_INCLUDED_FILE #ifndef INTERNET_FLAG_NEED_FILE - #define INTERNET_FLAG_NEED_FILE 0x00000010 + #define INTERNET_FLAG_NEED_FILE 0x00000010 #endif #ifndef INTERNET_OPTION_DISABLE_AUTODIAL - #define INTERNET_OPTION_DISABLE_AUTODIAL 70 + #define INTERNET_OPTION_DISABLE_AUTODIAL 70 #endif #ifndef WORKAROUND_TIMEOUT_BUG @@ -246040,10 +246046,6 @@ HWND juce_messageWindowHandle = 0; extern long improbableWindowNumber; // defined in windowing.cpp -#ifndef WM_APPCOMMAND - #define WM_APPCOMMAND 0x0319 -#endif - static LRESULT CALLBACK juce_MessageWndProc (HWND h, const UINT message, const WPARAM wParam, @@ -247570,7 +247572,6 @@ private: #define APPCOMMAND_MEDIA_PREVIOUSTRACK 12 #define APPCOMMAND_MEDIA_STOP 13 #define APPCOMMAND_MEDIA_PLAY_PAUSE 14 - #define WM_APPCOMMAND 0x0319 #endif extern void juce_repeatLastProcessPriority(); // in juce_win32_Threads.cpp @@ -252279,29 +252280,32 @@ typedef BOOL (WINAPI * PFNWGLCHOOSEPIXELFORMATARBPROC) (HDC hdc, const int* piAt typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC) (int interval); typedef int (WINAPI * PFNWGLGETSWAPINTERVALEXTPROC) (void); -#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 -#define WGL_DRAW_TO_WINDOW_ARB 0x2001 -#define WGL_ACCELERATION_ARB 0x2003 -#define WGL_SWAP_METHOD_ARB 0x2007 -#define WGL_SUPPORT_OPENGL_ARB 0x2010 -#define WGL_PIXEL_TYPE_ARB 0x2013 -#define WGL_DOUBLE_BUFFER_ARB 0x2011 -#define WGL_COLOR_BITS_ARB 0x2014 -#define WGL_RED_BITS_ARB 0x2015 -#define WGL_GREEN_BITS_ARB 0x2017 -#define WGL_BLUE_BITS_ARB 0x2019 -#define WGL_ALPHA_BITS_ARB 0x201B -#define WGL_DEPTH_BITS_ARB 0x2022 -#define WGL_STENCIL_BITS_ARB 0x2023 -#define WGL_FULL_ACCELERATION_ARB 0x2027 -#define WGL_ACCUM_RED_BITS_ARB 0x201E -#define WGL_ACCUM_GREEN_BITS_ARB 0x201F -#define WGL_ACCUM_BLUE_BITS_ARB 0x2020 -#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021 -#define WGL_STEREO_ARB 0x2012 -#define WGL_SAMPLE_BUFFERS_ARB 0x2041 -#define WGL_SAMPLES_ARB 0x2042 -#define WGL_TYPE_RGBA_ARB 0x202B +enum +{ + WGL_NUMBER_PIXEL_FORMATS_ARB = 0x2000, + WGL_DRAW_TO_WINDOW_ARB = 0x2001, + WGL_ACCELERATION_ARB = 0x2003, + WGL_SWAP_METHOD_ARB = 0x2007, + WGL_SUPPORT_OPENGL_ARB = 0x2010, + WGL_PIXEL_TYPE_ARB = 0x2013, + WGL_DOUBLE_BUFFER_ARB = 0x2011, + WGL_COLOR_BITS_ARB = 0x2014, + WGL_RED_BITS_ARB = 0x2015, + WGL_GREEN_BITS_ARB = 0x2017, + WGL_BLUE_BITS_ARB = 0x2019, + WGL_ALPHA_BITS_ARB = 0x201B, + WGL_DEPTH_BITS_ARB = 0x2022, + WGL_STENCIL_BITS_ARB = 0x2023, + WGL_FULL_ACCELERATION_ARB = 0x2027, + WGL_ACCUM_RED_BITS_ARB = 0x201E, + WGL_ACCUM_GREEN_BITS_ARB = 0x201F, + WGL_ACCUM_BLUE_BITS_ARB = 0x2020, + WGL_ACCUM_ALPHA_BITS_ARB = 0x2021, + WGL_STEREO_ARB = 0x2012, + WGL_SAMPLE_BUFFERS_ARB = 0x2041, + WGL_SAMPLES_ARB = 0x2042, + WGL_TYPE_RGBA_ARB = 0x202B +}; static void getWglExtensions (HDC dc, StringArray& result) noexcept { @@ -261987,7 +261991,7 @@ int64 Time::getHighResolutionTicks() noexcept timespec t; clock_gettime (CLOCK_MONOTONIC, &t); - return (t.tv_sec * (int64) 1000000) + (t.tv_nsec / (int64) 1000); + return (t.tv_sec * (int64) 1000000) + (t.tv_nsec / 1000); } int64 Time::getHighResolutionTicksPerSecond() noexcept @@ -269340,13 +269344,15 @@ public: { mach_timebase_info_data_t timebase; (void) mach_timebase_info (&timebase); - highResTimerFrequency = (int64) (1.0e9 * timebase.denom / timebase.numer); - highResTimerToMillisecRatio = timebase.numer / (1.0e6 * timebase.denom); + highResTimerFrequency = (timebase.denom * (int64) 1000000000) / timebase.numer; + numerator = timebase.numer; + denominator = timebase.denom * (int64) 1000000; + highResTimerToMillisecRatio = numerator / (double) denominator; } inline uint32 millisecondsSinceStartup() const noexcept { - return (uint32) (mach_absolute_time() * highResTimerToMillisecRatio); + return (uint32) ((mach_absolute_time() * numerator) / denominator); } inline double getMillisecondCounterHiRes() const noexcept @@ -269355,6 +269361,9 @@ public: } int64 highResTimerFrequency; + +private: + int64 numerator, denominator; double highResTimerToMillisecRatio; }; @@ -271813,8 +271822,6 @@ public: timerCallback(); } - ~ScreenSaverDefeater() {} - void timerCallback() { if (Process::isForegroundProcess()) @@ -286929,7 +286936,7 @@ int64 Time::getHighResolutionTicks() noexcept timespec t; clock_gettime (CLOCK_MONOTONIC, &t); - return (t.tv_sec * (int64) 1000000) + (t.tv_nsec / (int64) 1000); + return (t.tv_sec * (int64) 1000000) + (t.tv_nsec / 1000); } int64 Time::getHighResolutionTicksPerSecond() noexcept diff --git a/juce_amalgamated.h b/juce_amalgamated.h index 6e98f5410f..26e656aa5d 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -73,7 +73,7 @@ namespace JuceDummyNamespace {} */ #define JUCE_MAJOR_VERSION 1 #define JUCE_MINOR_VERSION 53 -#define JUCE_BUILDNUMBER 85 +#define JUCE_BUILDNUMBER 86 /** Current Juce version number. @@ -895,102 +895,6 @@ extern JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger(); #ifndef __JUCE_MEMORY_JUCEHEADER__ #define __JUCE_MEMORY_JUCEHEADER__ -/* - This file defines the various juce_malloc(), juce_free() macros that can be used in - preference to the standard calls. - - None of this stuff is actually used in the library itself, and will probably be - deprecated at some point in the future, to force everyone to use HeapBlock and other - safer allocation methods. -*/ - -#if JUCE_MSVC && JUCE_CHECK_MEMORY_LEAKS && ! DOXYGEN - #ifndef JUCE_DLL - - // Win32 debug non-DLL versions.. - - #define juce_malloc(numBytes) _malloc_dbg (numBytes, _NORMAL_BLOCK, __FILE__, __LINE__) - #define juce_calloc(numBytes) _calloc_dbg (1, numBytes, _NORMAL_BLOCK, __FILE__, __LINE__) - #define juce_realloc(location, numBytes) _realloc_dbg (location, numBytes, _NORMAL_BLOCK, __FILE__, __LINE__) - #define juce_free(location) _free_dbg (location, _NORMAL_BLOCK) - - #else - - // Win32 debug DLL versions.. - - // For the DLL, we'll define some functions in the DLL that will be used for allocation - that - // way all juce calls in the DLL and in the host API will all use the same allocator. - extern JUCE_API void* juce_DebugMalloc (int size, const char* file, int line); - extern JUCE_API void* juce_DebugCalloc (int size, const char* file, int line); - extern JUCE_API void* juce_DebugRealloc (void* block, int size, const char* file, int line); - extern JUCE_API void juce_DebugFree (void* block); - - #define juce_malloc(numBytes) JUCE_NAMESPACE::juce_DebugMalloc (numBytes, __FILE__, __LINE__) - #define juce_calloc(numBytes) JUCE_NAMESPACE::juce_DebugCalloc (numBytes, __FILE__, __LINE__) - #define juce_realloc(location, numBytes) JUCE_NAMESPACE::juce_DebugRealloc (location, numBytes, __FILE__, __LINE__) - #define juce_free(location) JUCE_NAMESPACE::juce_DebugFree (location) - - #define JUCE_LEAK_DETECTOR(OwnerClass) public:\ - static void* operator new (size_t sz) { void* const p = juce_malloc ((int) sz); return (p != 0) ? p : ::operator new (sz); } \ - static void* operator new (size_t, void* p) { return p; } \ - static void operator delete (void* p) { juce_free (p); } \ - static void operator delete (void*, void*) {} - #endif - -#elif defined (JUCE_DLL) && ! DOXYGEN - - // Win32 DLL (release) versions.. - - // For the DLL, we'll define some functions in the DLL that will be used for allocation - that - // way all juce calls in the DLL and in the host API will all use the same allocator. - extern JUCE_API void* juce_Malloc (int size); - extern JUCE_API void* juce_Calloc (int size); - extern JUCE_API void* juce_Realloc (void* block, int size); - extern JUCE_API void juce_Free (void* block); - - #define juce_malloc(numBytes) JUCE_NAMESPACE::juce_Malloc (numBytes) - #define juce_calloc(numBytes) JUCE_NAMESPACE::juce_Calloc (numBytes) - #define juce_realloc(location, numBytes) JUCE_NAMESPACE::juce_Realloc (location, numBytes) - #define juce_free(location) JUCE_NAMESPACE::juce_Free (location) - - #define JUCE_LEAK_DETECTOR(OwnerClass) public:\ - static void* operator new (size_t sz) { void* const p = juce_malloc ((int) sz); return (p != 0) ? p : ::operator new (sz); } \ - static void* operator new (size_t, void* p) { return p; } \ - static void operator delete (void* p) { juce_free (p); } \ - static void operator delete (void*, void*) {} -#else - - // Mac, Linux and Win32 (release) versions.. - - /** This can be used instead of calling malloc directly. - Only use direct memory allocation if there's really no way to use a HeapBlock object instead! - */ - #define juce_malloc(numBytes) malloc (numBytes) - - /** This can be used instead of calling calloc directly. - Only use direct memory allocation if there's really no way to use a HeapBlock object instead! - */ - #define juce_calloc(numBytes) calloc (1, numBytes) - - /** This can be used instead of calling realloc directly. - Only use direct memory allocation if there's really no way to use a HeapBlock object instead! - */ - #define juce_realloc(location, numBytes) realloc (location, numBytes) - - /** This can be used instead of calling free directly. - Only use direct memory allocation if there's really no way to use a HeapBlock object instead! - */ - #define juce_free(location) free (location) - -#endif - -/** (Deprecated) This was a win32-specific way of checking for object leaks - now please - use the JUCE_LEAK_DETECTOR instead. -*/ -#ifndef juce_UseDebuggingNewOperator - #define juce_UseDebuggingNewOperator -#endif - #if JUCE_MSVC || DOXYGEN /** This is a compiler-independent way of declaring a variable as being thread-local. @@ -1031,6 +935,35 @@ inline void deleteAndZero (Type& pointer) { delete pointer; pointer = nullp template inline Type* addBytesToPointer (Type* pointer, int bytes) noexcept { return (Type*) (((char*) pointer) + bytes); } +/** A handy function which returns the difference between any two pointers, in bytes. + The address of the second pointer is subtracted from the first, and the difference in bytes is returned. +*/ +template +inline int getAddressDifference (Type1* pointer1, Type2* pointer2) noexcept { return (int) (((const char*) pointer1) - (const char*) pointer2); } + +/* In a win32 DLL build, we'll expose some malloc/free functions that live inside the DLL, and use these for + allocating all the objects - that way all juce objects in the DLL and in the host will live in the same heap, + avoiding problems when an object is created in one module and passed across to another where it is deleted. + By piggy-backing on the JUCE_LEAK_DETECTOR macro, these allocators can be injected into most juce classes. +*/ +#if JUCE_MSVC && defined (JUCE_DLL) && ! DOXYGEN + extern JUCE_API void* juceDLL_malloc (size_t); + extern JUCE_API void juceDLL_free (void*); + + #define JUCE_LEAK_DETECTOR(OwnerClass) public:\ + static void* operator new (size_t sz) { return JUCE_NAMESPACE::juceDLL_malloc ((int) sz); } \ + static void* operator new (size_t, void* p) { return p; } \ + static void operator delete (void* p) { JUCE_NAMESPACE::juceDLL_free (p); } \ + static void operator delete (void*, void*) {} +#endif + +/** (Deprecated) This was a win32-specific way of checking for object leaks - now please + use the JUCE_LEAK_DETECTOR instead. +*/ +#ifndef juce_UseDebuggingNewOperator + #define juce_UseDebuggingNewOperator +#endif + #endif // __JUCE_MEMORY_JUCEHEADER__ /*** End of inlined file: juce_Memory.h ***/ @@ -1912,7 +1845,7 @@ public: template static int copyWithDestByteLimit (DestCharPointerType& dest, SrcCharPointerType src, int maxBytes) noexcept { - int numBytesDone = 0; + typename DestCharPointerType::CharType const* const startAddress = dest.getAddress(); maxBytes -= sizeof (typename DestCharPointerType::CharType); // (allow for a terminating null) for (;;) @@ -1924,12 +1857,12 @@ public: if (c == 0 || maxBytes < 0) break; - numBytesDone += bytesNeeded; dest.write (c); } dest.writeNull(); - return numBytesDone; + + return getAddressDifference (dest.getAddress(), startAddress); } template @@ -8008,6 +7941,12 @@ public: /** Returns this identifier's raw string pointer. */ operator const String::CharPointerType() const noexcept { return name; } + /** Checks a given string for characters that might not be valid in an Identifier. + Since Identifiers are used as a script variables and XML attributes, they should only contain + alphanumeric characters and underscores. + */ + static bool isValidIdentifier (const String& possibleIdentifier) noexcept; + private: String::CharPointerType name; @@ -8053,7 +7992,7 @@ public: operator const String() const { return getDefault(); } }; -/** An predefined object representing a new-line, which can be written to a string or stream. +/** A predefined object representing a new-line, which can be written to a string or stream. To write a new-line to a stream, you can use the predefined 'newLine' variable like this: @code @@ -9317,7 +9256,7 @@ public: Do not use this method unless you really need access to the internal var object for some reason - for normal reading and writing always prefer operator[]() and set(). */ - var* getVarPointer (const Identifier& name) const; + var* getVarPointer (const Identifier& name) const noexcept; /** Sets properties to the values of all of an XML element's attributes. */ void setFromXmlAttributes (const XmlElement& xml); @@ -10791,7 +10730,7 @@ public: { const ScopedLockType sl (getLock()); - for (const HashEntry* entry = slots [generateHashFor (keyToLookFor)]; entry != nullptr; entry = entry->nextEntry) + for (const HashEntry* entry = slots.getUnchecked (generateHashFor (keyToLookFor)); entry != nullptr; entry = entry->nextEntry) if (entry->key == keyToLookFor) return entry->value; @@ -10803,7 +10742,7 @@ public: { const ScopedLockType sl (getLock()); - for (const HashEntry* entry = slots [generateHashFor (keyToLookFor)]; entry != nullptr; entry = entry->nextEntry) + for (const HashEntry* entry = slots.getUnchecked (generateHashFor (keyToLookFor)); entry != nullptr; entry = entry->nextEntry) if (entry->key == keyToLookFor) return true; @@ -10832,25 +10771,22 @@ public: const ScopedLockType sl (getLock()); const int hashIndex = generateHashFor (newKey); - if (isPositiveAndBelow (hashIndex, getNumSlots())) - { - HashEntry* const firstEntry = slots.getUnchecked (hashIndex); + HashEntry* const firstEntry = slots.getUnchecked (hashIndex); - for (HashEntry* entry = firstEntry; entry != nullptr; entry = entry->nextEntry) + for (HashEntry* entry = firstEntry; entry != nullptr; entry = entry->nextEntry) + { + if (entry->key == newKey) { - if (entry->key == newKey) - { - entry->value = newValue; - return; - } + entry->value = newValue; + return; } + } - slots.set (hashIndex, new HashEntry (newKey, newValue, firstEntry)); - ++totalNumItems; + slots.set (hashIndex, new HashEntry (newKey, newValue, firstEntry)); + ++totalNumItems; - if (totalNumItems > (getNumSlots() * 3) / 2) - remapTable (getNumSlots() * 2); - } + if (totalNumItems > (getNumSlots() * 3) / 2) + remapTable (getNumSlots() * 2); } /** Removes an item with the given key. */ @@ -10858,7 +10794,7 @@ public: { const ScopedLockType sl (getLock()); const int hashIndex = generateHashFor (keyToRemove); - HashEntry* entry = slots [hashIndex]; + HashEntry* entry = slots.getUnchecked (hashIndex); HashEntry* previous = nullptr; while (entry != nullptr) @@ -11953,6 +11889,10 @@ public: system clock. It should be accurate to within a few millisecs, depending on platform, hardware, etc. + Being a 32-bit return value, it will of course wrap back to 0 after 2^32 seconds of + uptime, so be careful to take that into account. If you need a 64-bit time, you can + use currentTimeMillis() instead. + @see getApproximateMillisecondCounter */ static uint32 getMillisecondCounter() noexcept; @@ -38449,10 +38389,12 @@ public: @param deleteSourceWhenDeleted if true, then the input source object will be deleted when this object is deleted @param numberOfSamplesToBuffer the size of buffer to use for reading ahead + @param numberOfChannels the number of channels that will be played */ BufferingAudioSource (PositionableAudioSource* source, bool deleteSourceWhenDeleted, - int numberOfSamplesToBuffer); + int numberOfSamplesToBuffer, + int numberOfChannels = 2); /** Destructor. @@ -38486,7 +38428,7 @@ private: PositionableAudioSource* source; bool deleteSourceWhenDeleted; - int numberOfSamplesToBuffer; + int numberOfSamplesToBuffer, numberOfChannels; AudioSampleBuffer buffer; CriticalSection bufferStartPosLock; int64 volatile bufferValidStart, bufferValidEnd, nextPlayPos; @@ -43908,6 +43850,9 @@ public: This stores the current device, its samplerate, block size, etc, and can be restored later with initialise(). + + Note that this can return a null pointer if no settings have been explicitly changed + (i.e. if the device manager has just been left in its default state). */ XmlElement* createStateXml() const; @@ -47730,7 +47675,7 @@ private: ReferenceCountedArray nodes; OwnedArray connections; - int lastNodeId; + uint32 lastNodeId; AudioSampleBuffer renderingBuffers; OwnedArray midiBuffers; @@ -57819,22 +57764,24 @@ private: @code { - WildcardFileFilter wildcardFilter ("*.foo", "Foo files"); + WildcardFileFilter wildcardFilter ("*.foo", String::empty, "Foo files"); - FileBrowserComponent browser (FileBrowserComponent::loadFileMode, + FileBrowserComponent browser (FileBrowserComponent::canSelectFiles, File::nonexistent, &wildcardFilter, - 0); + nullptr); FileChooserDialogBox dialogBox ("Open some kind of file", "Please choose some kind of file that you want to open...", browser, - getLookAndFeel().alertWindowBackground); + false, + Colours::lightgrey); if (dialogBox.show()) { - File selectedFile = browser.getCurrentFile(); - ... + File selectedFile = browser.getSelectedFile (0); + + ...etc.. } } @endcode @@ -58447,6 +58394,10 @@ public: patterns, e.g. "*.wav;*.aiff" would look for files ending in either .wav or .aiff. + Passing an empty string as a pattern will fail to match anything, so by leaving + either the file or directory pattern parameter empty means you can control + whether files or directories are found. + The description is a name to show the user in a list of possible patterns, so for the wav/aiff example, your description might be "audio files". */ diff --git a/src/audio/audio_sources/juce_AudioTransportSource.cpp b/src/audio/audio_sources/juce_AudioTransportSource.cpp index 4db43a10a2..526802a2a7 100644 --- a/src/audio/audio_sources/juce_AudioTransportSource.cpp +++ b/src/audio/audio_sources/juce_AudioTransportSource.cpp @@ -89,7 +89,7 @@ void AudioTransportSource::setSource (PositionableAudioSource* const newSource, if (readAheadBufferSize_ > 0) newPositionableSource = newBufferingSource - = new BufferingAudioSource (newPositionableSource, false, readAheadBufferSize_); + = new BufferingAudioSource (newPositionableSource, false, readAheadBufferSize_, maxNumChannels); newPositionableSource->setNextReadPosition (0); diff --git a/src/audio/audio_sources/juce_BufferingAudioSource.cpp b/src/audio/audio_sources/juce_BufferingAudioSource.cpp index 2f0392b3b2..844963c6eb 100644 --- a/src/audio/audio_sources/juce_BufferingAudioSource.cpp +++ b/src/audio/audio_sources/juce_BufferingAudioSource.cpp @@ -121,11 +121,13 @@ juce_ImplementSingleton (SharedBufferingAudioSourceThread) //============================================================================== BufferingAudioSource::BufferingAudioSource (PositionableAudioSource* source_, const bool deleteSourceWhenDeleted_, - int numberOfSamplesToBuffer_) + const int numberOfSamplesToBuffer_, + const int numberOfChannels_) : source (source_), deleteSourceWhenDeleted (deleteSourceWhenDeleted_), numberOfSamplesToBuffer (jmax (1024, numberOfSamplesToBuffer_)), - buffer (2, 0), + numberOfChannels (numberOfChannels_), + buffer (numberOfChannels_, 0), bufferValidStart (0), bufferValidEnd (0), nextPlayPos (0), @@ -155,7 +157,7 @@ void BufferingAudioSource::prepareToPlay (int samplesPerBlockExpected, double sa sampleRate = sampleRate_; - buffer.setSize (2, jmax (samplesPerBlockExpected * 2, numberOfSamplesToBuffer)); + buffer.setSize (numberOfChannels, jmax (samplesPerBlockExpected * 2, numberOfSamplesToBuffer)); buffer.clear(); bufferValidStart = 0; @@ -178,7 +180,7 @@ void BufferingAudioSource::releaseResources() if (thread != nullptr) thread->removeSource (this); - buffer.setSize (2, 0); + buffer.setSize (numberOfChannels, 0); source->releaseResources(); } @@ -205,7 +207,7 @@ void BufferingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info if (validStart < validEnd) { - for (int chan = jmin (2, info.buffer->getNumChannels()); --chan >= 0;) + for (int chan = jmin (numberOfChannels, info.buffer->getNumChannels()); --chan >= 0;) { const int startBufferIndex = (validStart + nextPlayPos) % buffer.getNumSamples(); const int endBufferIndex = (validEnd + nextPlayPos) % buffer.getNumSamples(); diff --git a/src/audio/audio_sources/juce_BufferingAudioSource.h b/src/audio/audio_sources/juce_BufferingAudioSource.h index c4a85b3409..61412e7d1a 100644 --- a/src/audio/audio_sources/juce_BufferingAudioSource.h +++ b/src/audio/audio_sources/juce_BufferingAudioSource.h @@ -51,10 +51,12 @@ public: @param deleteSourceWhenDeleted if true, then the input source object will be deleted when this object is deleted @param numberOfSamplesToBuffer the size of buffer to use for reading ahead + @param numberOfChannels the number of channels that will be played */ BufferingAudioSource (PositionableAudioSource* source, bool deleteSourceWhenDeleted, - int numberOfSamplesToBuffer); + int numberOfSamplesToBuffer, + int numberOfChannels = 2); /** Destructor. @@ -90,7 +92,7 @@ private: //============================================================================== PositionableAudioSource* source; bool deleteSourceWhenDeleted; - int numberOfSamplesToBuffer; + int numberOfSamplesToBuffer, numberOfChannels; AudioSampleBuffer buffer; CriticalSection bufferStartPosLock; int64 volatile bufferValidStart, bufferValidEnd, nextPlayPos; diff --git a/src/audio/devices/juce_AudioDeviceManager.h b/src/audio/devices/juce_AudioDeviceManager.h index 791f262bb7..4df042c9a0 100644 --- a/src/audio/devices/juce_AudioDeviceManager.h +++ b/src/audio/devices/juce_AudioDeviceManager.h @@ -201,6 +201,9 @@ public: This stores the current device, its samplerate, block size, etc, and can be restored later with initialise(). + + Note that this can return a null pointer if no settings have been explicitly changed + (i.e. if the device manager has just been left in its default state). */ XmlElement* createStateXml() const; diff --git a/src/containers/juce_HashMap.h b/src/containers/juce_HashMap.h index 83a7dcd5b3..b9d9eba3d3 100644 --- a/src/containers/juce_HashMap.h +++ b/src/containers/juce_HashMap.h @@ -160,7 +160,7 @@ public: { const ScopedLockType sl (getLock()); - for (const HashEntry* entry = slots [generateHashFor (keyToLookFor)]; entry != nullptr; entry = entry->nextEntry) + for (const HashEntry* entry = slots.getUnchecked (generateHashFor (keyToLookFor)); entry != nullptr; entry = entry->nextEntry) if (entry->key == keyToLookFor) return entry->value; @@ -173,7 +173,7 @@ public: { const ScopedLockType sl (getLock()); - for (const HashEntry* entry = slots [generateHashFor (keyToLookFor)]; entry != nullptr; entry = entry->nextEntry) + for (const HashEntry* entry = slots.getUnchecked (generateHashFor (keyToLookFor)); entry != nullptr; entry = entry->nextEntry) if (entry->key == keyToLookFor) return true; @@ -203,25 +203,22 @@ public: const ScopedLockType sl (getLock()); const int hashIndex = generateHashFor (newKey); - if (isPositiveAndBelow (hashIndex, getNumSlots())) - { - HashEntry* const firstEntry = slots.getUnchecked (hashIndex); + HashEntry* const firstEntry = slots.getUnchecked (hashIndex); - for (HashEntry* entry = firstEntry; entry != nullptr; entry = entry->nextEntry) + for (HashEntry* entry = firstEntry; entry != nullptr; entry = entry->nextEntry) + { + if (entry->key == newKey) { - if (entry->key == newKey) - { - entry->value = newValue; - return; - } + entry->value = newValue; + return; } + } - slots.set (hashIndex, new HashEntry (newKey, newValue, firstEntry)); - ++totalNumItems; + slots.set (hashIndex, new HashEntry (newKey, newValue, firstEntry)); + ++totalNumItems; - if (totalNumItems > (getNumSlots() * 3) / 2) - remapTable (getNumSlots() * 2); - } + if (totalNumItems > (getNumSlots() * 3) / 2) + remapTable (getNumSlots() * 2); } /** Removes an item with the given key. */ @@ -229,7 +226,7 @@ public: { const ScopedLockType sl (getLock()); const int hashIndex = generateHashFor (keyToRemove); - HashEntry* entry = slots [hashIndex]; + HashEntry* entry = slots.getUnchecked (hashIndex); HashEntry* previous = nullptr; while (entry != nullptr) diff --git a/src/core/juce_StandardHeader.h b/src/core/juce_StandardHeader.h index 2cf018da5c..ad8e76a9d3 100644 --- a/src/core/juce_StandardHeader.h +++ b/src/core/juce_StandardHeader.h @@ -33,7 +33,7 @@ */ #define JUCE_MAJOR_VERSION 1 #define JUCE_MINOR_VERSION 53 -#define JUCE_BUILDNUMBER 85 +#define JUCE_BUILDNUMBER 86 /** Current Juce version number. diff --git a/src/core/juce_Time.cpp b/src/core/juce_Time.cpp index c556e98d4e..ecfb330128 100644 --- a/src/core/juce_Time.cpp +++ b/src/core/juce_Time.cpp @@ -276,7 +276,9 @@ uint32 Time::getMillisecondCounter() noexcept uint32 Time::getApproximateMillisecondCounter() noexcept { - jassert (TimeHelpers::lastMSCounterValue != 0); + if (TimeHelpers::lastMSCounterValue == 0) + getMillisecondCounter(); + return TimeHelpers::lastMSCounterValue; } diff --git a/src/core/juce_Time.h b/src/core/juce_Time.h index b428ee55bc..a56325210b 100644 --- a/src/core/juce_Time.h +++ b/src/core/juce_Time.h @@ -298,6 +298,10 @@ public: system clock. It should be accurate to within a few millisecs, depending on platform, hardware, etc. + Being a 32-bit return value, it will of course wrap back to 0 after 2^32 seconds of + uptime, so be careful to take that into account. If you need a 64-bit time, you can + use currentTimeMillis() instead. + @see getApproximateMillisecondCounter */ static uint32 getMillisecondCounter() noexcept; diff --git a/src/events/juce_Timer.cpp b/src/events/juce_Timer.cpp index eb76385fe4..e946cd5fe4 100644 --- a/src/events/juce_Timer.cpp +++ b/src/events/juce_Timer.cpp @@ -72,13 +72,14 @@ public: { const uint32 now = Time::getMillisecondCounter(); - if (now <= lastTime) + if (now == lastTime) { - wait (2); + wait (1); continue; } - const int elapsed = now - lastTime; + const int elapsed = now >= lastTime ? (now - lastTime) + : (std::numeric_limits::max() - (lastTime - now)); lastTime = now; const int timeUntilFirstTimer = getTimeUntilFirstTimer (elapsed); diff --git a/src/gui/components/menus/juce_PopupMenu.cpp b/src/gui/components/menus/juce_PopupMenu.cpp index 03c411563f..c5385e5669 100644 --- a/src/gui/components/menus/juce_PopupMenu.cpp +++ b/src/gui/components/menus/juce_PopupMenu.cpp @@ -373,20 +373,22 @@ public: { if (isVisible()) { + WeakReference deletionChecker (this); + activeSubMenu = nullptr; currentChild = nullptr; - exitModalState (item != nullptr ? item->itemId : 0); - - if (makeInvisible) - setVisible (false); - if (item != nullptr && item->commandManager != nullptr && item->itemId != 0) { *managerOfChosenCommand = item->commandManager; } + + exitModalState (item != nullptr ? item->itemId : 0); + + if (makeInvisible && (deletionChecker != nullptr)) + setVisible (false); } } diff --git a/src/memory/juce_Memory.h b/src/memory/juce_Memory.h index 3e80a4b60c..148c69362f 100644 --- a/src/memory/juce_Memory.h +++ b/src/memory/juce_Memory.h @@ -69,6 +69,13 @@ inline void deleteAndZero (Type& pointer) { delete poi template inline Type* addBytesToPointer (Type* pointer, int bytes) noexcept { return (Type*) (((char*) pointer) + bytes); } +/** A handy function which returns the difference between any two pointers, in bytes. + The address of the second pointer is subtracted from the first, and the difference in bytes is returned. +*/ +template +inline int getAddressDifference (Type1* pointer1, Type2* pointer2) noexcept { return (int) (((const char*) pointer1) - (const char*) pointer2); } + + //============================================================================== /* In a win32 DLL build, we'll expose some malloc/free functions that live inside the DLL, and use these for allocating all the objects - that way all juce objects in the DLL and in the host will live in the same heap, diff --git a/src/native/android/juce_android_SystemStats.cpp b/src/native/android/juce_android_SystemStats.cpp index ad809a01b3..4a6d48f16f 100644 --- a/src/native/android/juce_android_SystemStats.cpp +++ b/src/native/android/juce_android_SystemStats.cpp @@ -142,7 +142,7 @@ int64 Time::getHighResolutionTicks() noexcept timespec t; clock_gettime (CLOCK_MONOTONIC, &t); - return (t.tv_sec * (int64) 1000000) + (t.tv_nsec / (int64) 1000); + return (t.tv_sec * (int64) 1000000) + (t.tv_nsec / 1000); } int64 Time::getHighResolutionTicksPerSecond() noexcept diff --git a/src/native/linux/juce_linux_SystemStats.cpp b/src/native/linux/juce_linux_SystemStats.cpp index aca5a54824..7f9222dc76 100644 --- a/src/native/linux/juce_linux_SystemStats.cpp +++ b/src/native/linux/juce_linux_SystemStats.cpp @@ -155,7 +155,7 @@ int64 Time::getHighResolutionTicks() noexcept timespec t; clock_gettime (CLOCK_MONOTONIC, &t); - return (t.tv_sec * (int64) 1000000) + (t.tv_nsec / (int64) 1000); + return (t.tv_sec * (int64) 1000000) + (t.tv_nsec / 1000); } int64 Time::getHighResolutionTicksPerSecond() noexcept diff --git a/src/native/mac/juce_mac_MiscUtilities.mm b/src/native/mac/juce_mac_MiscUtilities.mm index ab6986810e..2e9ada5d81 100644 --- a/src/native/mac/juce_mac_MiscUtilities.mm +++ b/src/native/mac/juce_mac_MiscUtilities.mm @@ -274,8 +274,6 @@ public: timerCallback(); } - ~ScreenSaverDefeater() {} - void timerCallback() { if (Process::isForegroundProcess()) diff --git a/src/native/mac/juce_mac_SystemStats.mm b/src/native/mac/juce_mac_SystemStats.mm index 6646339872..b4e6f7aa1d 100644 --- a/src/native/mac/juce_mac_SystemStats.mm +++ b/src/native/mac/juce_mac_SystemStats.mm @@ -190,13 +190,15 @@ public: { mach_timebase_info_data_t timebase; (void) mach_timebase_info (&timebase); - highResTimerFrequency = (int64) (1.0e9 * timebase.denom / timebase.numer); - highResTimerToMillisecRatio = timebase.numer / (1.0e6 * timebase.denom); + highResTimerFrequency = (timebase.denom * (int64) 1000000000) / timebase.numer; + numerator = timebase.numer; + denominator = timebase.denom * (int64) 1000000; + highResTimerToMillisecRatio = numerator / (double) denominator; } inline uint32 millisecondsSinceStartup() const noexcept { - return (uint32) (mach_absolute_time() * highResTimerToMillisecRatio); + return (uint32) ((mach_absolute_time() * numerator) / denominator); } inline double getMillisecondCounterHiRes() const noexcept @@ -205,6 +207,9 @@ public: } int64 highResTimerFrequency; + +private: + int64 numerator, denominator; double highResTimerToMillisecRatio; }; diff --git a/src/text/juce_CharacterFunctions.h b/src/text/juce_CharacterFunctions.h index ff0040617a..09d791fa2d 100644 --- a/src/text/juce_CharacterFunctions.h +++ b/src/text/juce_CharacterFunctions.h @@ -308,7 +308,7 @@ public: template static int copyWithDestByteLimit (DestCharPointerType& dest, SrcCharPointerType src, int maxBytes) noexcept { - int numBytesDone = 0; + typename DestCharPointerType::CharType const* const startAddress = dest.getAddress(); maxBytes -= sizeof (typename DestCharPointerType::CharType); // (allow for a terminating null) for (;;) @@ -320,12 +320,12 @@ public: if (c == 0 || maxBytes < 0) break; - numBytesDone += bytesNeeded; dest.write (c); } dest.writeNull(); - return numBytesDone; + + return getAddressDifference (dest.getAddress(), startAddress); } template diff --git a/src/text/juce_Identifier.cpp b/src/text/juce_Identifier.cpp index 66dc281a42..837c74adcf 100644 --- a/src/text/juce_Identifier.cpp +++ b/src/text/juce_Identifier.cpp @@ -58,7 +58,7 @@ Identifier::Identifier (const String& name_) { /* An Identifier string must be suitable for use as a script variable or XML attribute, so it can only contain this limited set of characters.. */ - jassert (name_.containsOnly ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_") && name_.isNotEmpty()); + jassert (isValidIdentifier (name_)); } Identifier::Identifier (const char* const name_) @@ -66,11 +66,17 @@ Identifier::Identifier (const char* const name_) { /* An Identifier string must be suitable for use as a script variable or XML attribute, so it can only contain this limited set of characters.. */ - jassert (toString().containsOnly ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_") && toString().isNotEmpty()); + jassert (isValidIdentifier (toString())); } Identifier::~Identifier() { } +bool Identifier::isValidIdentifier (const String& possibleIdentifier) noexcept +{ + return possibleIdentifier.isNotEmpty() + && possibleIdentifier.containsOnly ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"); +} + END_JUCE_NAMESPACE diff --git a/src/text/juce_Identifier.h b/src/text/juce_Identifier.h index 99b9412c4a..65b17ffe37 100644 --- a/src/text/juce_Identifier.h +++ b/src/text/juce_Identifier.h @@ -78,6 +78,13 @@ public: /** Returns this identifier's raw string pointer. */ operator const String::CharPointerType() const noexcept { return name; } + /** Checks a given string for characters that might not be valid in an Identifier. + Since Identifiers are used as a script variables and XML attributes, they should only contain + alphanumeric characters and underscores. + */ + static bool isValidIdentifier (const String& possibleIdentifier) noexcept; + + private: //============================================================================== String::CharPointerType name; diff --git a/src/text/juce_String.cpp b/src/text/juce_String.cpp index 7ba38e3429..0994664dce 100644 --- a/src/text/juce_String.cpp +++ b/src/text/juce_String.cpp @@ -2052,7 +2052,7 @@ struct StringCopier jassert (maxBufferSizeBytes >= 0); // keep this value positive, or no characters will be copied! if (buffer == nullptr) - return (int) CharPointerType_Dest::getBytesRequiredFor (source); + return (int) (CharPointerType_Dest::getBytesRequiredFor (source) + sizeof (typename CharPointerType_Dest::CharType)); return CharPointerType_Dest (buffer).writeWithDestByteLimit (source, maxBufferSizeBytes); }