Browse Source

Cleaned up the AsyncUpdater and ChangeBroadcaster classes internally - these now have less overhead when creating and deleting them. Removed the ActionListenerList class, as all its functionality is already provided by ActionBroadcaster.

tags/2021-05-28
Julian Storer 14 years ago
parent
commit
75ff0c5e7e
29 changed files with 490 additions and 780 deletions
  1. +0
    -6
      Builds/Linux/Makefile
  2. +0
    -6
      Builds/MacOSX/Juce.xcodeproj/project.pbxproj
  3. +0
    -2
      Builds/VisualStudio2005/Juce.vcproj
  4. +0
    -2
      Builds/VisualStudio2008/Juce.vcproj
  5. +0
    -2
      Builds/VisualStudio2008_DLL/Juce.vcproj
  6. +0
    -2
      Builds/VisualStudio2010/Juce.vcxproj
  7. +0
    -6
      Builds/VisualStudio2010/Juce.vcxproj.filters
  8. +0
    -6
      Builds/iPhone/Juce.xcodeproj/project.pbxproj
  9. +0
    -4
      Juce.jucer
  10. +0
    -1
      amalgamation/juce_amalgamated_template.cpp
  11. +128
    -168
      juce_amalgamated.cpp
  12. +166
    -226
      juce_amalgamated.h
  13. +1
    -0
      src/application/juce_ApplicationCommandTarget.h
  14. +1
    -1
      src/core/juce_StandardHeader.h
  15. +46
    -8
      src/events/juce_ActionBroadcaster.cpp
  16. +20
    -9
      src/events/juce_ActionBroadcaster.h
  17. +2
    -2
      src/events/juce_ActionListener.h
  18. +0
    -108
      src/events/juce_ActionListenerList.cpp
  19. +0
    -96
      src/events/juce_ActionListenerList.h
  20. +40
    -12
      src/events/juce_AsyncUpdater.cpp
  21. +10
    -17
      src/events/juce_AsyncUpdater.h
  22. +30
    -41
      src/events/juce_ChangeBroadcaster.cpp
  23. +12
    -5
      src/events/juce_ChangeBroadcaster.h
  24. +4
    -2
      src/events/juce_Message.cpp
  25. +27
    -39
      src/events/juce_MessageManager.cpp
  26. +2
    -4
      src/events/juce_MessageManager.h
  27. +0
    -1
      src/gui/components/windows/juce_ComponentPeer.h
  28. +0
    -3
      src/juce_app_includes.h
  29. +1
    -1
      src/text/juce_String.cpp

+ 0
- 6
Builds/Linux/Makefile View File

@@ -119,7 +119,6 @@ OBJECTS := \
$(OBJDIR)/juce_Primes_32e6603.o \
$(OBJDIR)/juce_RSAKey_b60982ae.o \
$(OBJDIR)/juce_ActionBroadcaster_7f997786.o \
$(OBJDIR)/juce_ActionListenerList_9e099ae4.o \
$(OBJDIR)/juce_AsyncUpdater_a7e1cb89.o \
$(OBJDIR)/juce_ChangeBroadcaster_3eb8fecc.o \
$(OBJDIR)/juce_InterprocessConnection_13086b6d.o \
@@ -763,11 +762,6 @@ $(OBJDIR)/juce_ActionBroadcaster_7f997786.o: ../../src/events/juce_ActionBroadca
@echo "Compiling juce_ActionBroadcaster.cpp"
@$(CXX) $(CXXFLAGS) -o "$@" -c "$<"
$(OBJDIR)/juce_ActionListenerList_9e099ae4.o: ../../src/events/juce_ActionListenerList.cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling juce_ActionListenerList.cpp"
@$(CXX) $(CXXFLAGS) -o "$@" -c "$<"
$(OBJDIR)/juce_AsyncUpdater_a7e1cb89.o: ../../src/events/juce_AsyncUpdater.cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling juce_AsyncUpdater.cpp"


+ 0
- 6
Builds/MacOSX/Juce.xcodeproj/project.pbxproj View File

@@ -88,7 +88,6 @@
12E3CC31875A202D6B30F778 = { isa = PBXBuildFile; fileRef = E9E692847C14AD33CD5FB40B; };
CF51988743ED2CD823DFE0B5 = { isa = PBXBuildFile; fileRef = 7AE9331938549244E27A5D0E; };
659D9CD58B6914EB420E6AEC = { isa = PBXBuildFile; fileRef = 31D985CB8646B78460E9D5A7; };
5BE4BAA99FDC6F1B3177096F = { isa = PBXBuildFile; fileRef = 5A46476E16BA4F9DA95E9E6A; };
55737E2F1817DE642AA7DA05 = { isa = PBXBuildFile; fileRef = 1617348BBF5D103619D76911; };
6D2C50B0A69855A7F8C062E7 = { isa = PBXBuildFile; fileRef = B80F8CD026033ACCCE11A1A4; };
70EE7A1273945B62B013DB43 = { isa = PBXBuildFile; fileRef = AE68ECB6E063BD8D4984C0B3; };
@@ -554,8 +553,6 @@
31D985CB8646B78460E9D5A7 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_ActionBroadcaster.cpp; path = ../../src/events/juce_ActionBroadcaster.cpp; sourceTree = SOURCE_ROOT; };
09F7685D1EFF472ECB1F5EF1 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_ActionBroadcaster.h; path = ../../src/events/juce_ActionBroadcaster.h; sourceTree = SOURCE_ROOT; };
4EF8BD4BF46C4BCB39F96609 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_ActionListener.h; path = ../../src/events/juce_ActionListener.h; sourceTree = SOURCE_ROOT; };
5A46476E16BA4F9DA95E9E6A = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_ActionListenerList.cpp; path = ../../src/events/juce_ActionListenerList.cpp; sourceTree = SOURCE_ROOT; };
8269D9DFAD7923EE13E7EEC7 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_ActionListenerList.h; path = ../../src/events/juce_ActionListenerList.h; sourceTree = SOURCE_ROOT; };
1617348BBF5D103619D76911 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_AsyncUpdater.cpp; path = ../../src/events/juce_AsyncUpdater.cpp; sourceTree = SOURCE_ROOT; };
44DB44953945417F76199479 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_AsyncUpdater.h; path = ../../src/events/juce_AsyncUpdater.h; sourceTree = SOURCE_ROOT; };
D04B6E43A037F985434B2F5A = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_CallbackMessage.h; path = ../../src/events/juce_CallbackMessage.h; sourceTree = SOURCE_ROOT; };
@@ -1282,8 +1279,6 @@
31D985CB8646B78460E9D5A7,
09F7685D1EFF472ECB1F5EF1,
4EF8BD4BF46C4BCB39F96609,
5A46476E16BA4F9DA95E9E6A,
8269D9DFAD7923EE13E7EEC7,
1617348BBF5D103619D76911,
44DB44953945417F76199479,
D04B6E43A037F985434B2F5A,
@@ -2008,7 +2003,6 @@
12E3CC31875A202D6B30F778,
CF51988743ED2CD823DFE0B5,
659D9CD58B6914EB420E6AEC,
5BE4BAA99FDC6F1B3177096F,
55737E2F1817DE642AA7DA05,
6D2C50B0A69855A7F8C062E7,
70EE7A1273945B62B013DB43,


+ 0
- 2
Builds/VisualStudio2005/Juce.vcproj View File

@@ -422,8 +422,6 @@
<File RelativePath="..\..\src\events\juce_ActionBroadcaster.cpp"/>
<File RelativePath="..\..\src\events\juce_ActionBroadcaster.h"/>
<File RelativePath="..\..\src\events\juce_ActionListener.h"/>
<File RelativePath="..\..\src\events\juce_ActionListenerList.cpp"/>
<File RelativePath="..\..\src\events\juce_ActionListenerList.h"/>
<File RelativePath="..\..\src\events\juce_AsyncUpdater.cpp"/>
<File RelativePath="..\..\src\events\juce_AsyncUpdater.h"/>
<File RelativePath="..\..\src\events\juce_CallbackMessage.h"/>


+ 0
- 2
Builds/VisualStudio2008/Juce.vcproj View File

@@ -422,8 +422,6 @@
<File RelativePath="..\..\src\events\juce_ActionBroadcaster.cpp"/>
<File RelativePath="..\..\src\events\juce_ActionBroadcaster.h"/>
<File RelativePath="..\..\src\events\juce_ActionListener.h"/>
<File RelativePath="..\..\src\events\juce_ActionListenerList.cpp"/>
<File RelativePath="..\..\src\events\juce_ActionListenerList.h"/>
<File RelativePath="..\..\src\events\juce_AsyncUpdater.cpp"/>
<File RelativePath="..\..\src\events\juce_AsyncUpdater.h"/>
<File RelativePath="..\..\src\events\juce_CallbackMessage.h"/>


+ 0
- 2
Builds/VisualStudio2008_DLL/Juce.vcproj View File

@@ -424,8 +424,6 @@
<File RelativePath="..\..\src\events\juce_ActionBroadcaster.cpp"/>
<File RelativePath="..\..\src\events\juce_ActionBroadcaster.h"/>
<File RelativePath="..\..\src\events\juce_ActionListener.h"/>
<File RelativePath="..\..\src\events\juce_ActionListenerList.cpp"/>
<File RelativePath="..\..\src\events\juce_ActionListenerList.h"/>
<File RelativePath="..\..\src\events\juce_AsyncUpdater.cpp"/>
<File RelativePath="..\..\src\events\juce_AsyncUpdater.h"/>
<File RelativePath="..\..\src\events\juce_CallbackMessage.h"/>


+ 0
- 2
Builds/VisualStudio2010/Juce.vcxproj View File

@@ -202,7 +202,6 @@
<ClCompile Include="..\..\src\cryptography\juce_Primes.cpp"/>
<ClCompile Include="..\..\src\cryptography\juce_RSAKey.cpp"/>
<ClCompile Include="..\..\src\events\juce_ActionBroadcaster.cpp"/>
<ClCompile Include="..\..\src\events\juce_ActionListenerList.cpp"/>
<ClCompile Include="..\..\src\events\juce_AsyncUpdater.cpp"/>
<ClCompile Include="..\..\src\events\juce_ChangeBroadcaster.cpp"/>
<ClCompile Include="..\..\src\events\juce_InterprocessConnection.cpp"/>
@@ -550,7 +549,6 @@
<ClInclude Include="..\..\src\cryptography\juce_RSAKey.h"/>
<ClInclude Include="..\..\src\events\juce_ActionBroadcaster.h"/>
<ClInclude Include="..\..\src\events\juce_ActionListener.h"/>
<ClInclude Include="..\..\src\events\juce_ActionListenerList.h"/>
<ClInclude Include="..\..\src\events\juce_AsyncUpdater.h"/>
<ClInclude Include="..\..\src\events\juce_CallbackMessage.h"/>
<ClInclude Include="..\..\src\events\juce_ChangeBroadcaster.h"/>


+ 0
- 6
Builds/VisualStudio2010/Juce.vcxproj.filters View File

@@ -454,9 +454,6 @@
<ClCompile Include="..\..\src\events\juce_ActionBroadcaster.cpp">
<Filter>Juce\Source\events</Filter>
</ClCompile>
<ClCompile Include="..\..\src\events\juce_ActionListenerList.cpp">
<Filter>Juce\Source\events</Filter>
</ClCompile>
<ClCompile Include="..\..\src\events\juce_AsyncUpdater.cpp">
<Filter>Juce\Source\events</Filter>
</ClCompile>
@@ -1572,9 +1569,6 @@
<ClInclude Include="..\..\src\events\juce_ActionListener.h">
<Filter>Juce\Source\events</Filter>
</ClInclude>
<ClInclude Include="..\..\src\events\juce_ActionListenerList.h">
<Filter>Juce\Source\events</Filter>
</ClInclude>
<ClInclude Include="..\..\src\events\juce_AsyncUpdater.h">
<Filter>Juce\Source\events</Filter>
</ClInclude>


+ 0
- 6
Builds/iPhone/Juce.xcodeproj/project.pbxproj View File

@@ -88,7 +88,6 @@
12E3CC31875A202D6B30F778 = { isa = PBXBuildFile; fileRef = E9E692847C14AD33CD5FB40B; };
CF51988743ED2CD823DFE0B5 = { isa = PBXBuildFile; fileRef = 7AE9331938549244E27A5D0E; };
659D9CD58B6914EB420E6AEC = { isa = PBXBuildFile; fileRef = 31D985CB8646B78460E9D5A7; };
5BE4BAA99FDC6F1B3177096F = { isa = PBXBuildFile; fileRef = 5A46476E16BA4F9DA95E9E6A; };
55737E2F1817DE642AA7DA05 = { isa = PBXBuildFile; fileRef = 1617348BBF5D103619D76911; };
6D2C50B0A69855A7F8C062E7 = { isa = PBXBuildFile; fileRef = B80F8CD026033ACCCE11A1A4; };
70EE7A1273945B62B013DB43 = { isa = PBXBuildFile; fileRef = AE68ECB6E063BD8D4984C0B3; };
@@ -554,8 +553,6 @@
31D985CB8646B78460E9D5A7 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_ActionBroadcaster.cpp; path = ../../src/events/juce_ActionBroadcaster.cpp; sourceTree = SOURCE_ROOT; };
09F7685D1EFF472ECB1F5EF1 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_ActionBroadcaster.h; path = ../../src/events/juce_ActionBroadcaster.h; sourceTree = SOURCE_ROOT; };
4EF8BD4BF46C4BCB39F96609 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_ActionListener.h; path = ../../src/events/juce_ActionListener.h; sourceTree = SOURCE_ROOT; };
5A46476E16BA4F9DA95E9E6A = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_ActionListenerList.cpp; path = ../../src/events/juce_ActionListenerList.cpp; sourceTree = SOURCE_ROOT; };
8269D9DFAD7923EE13E7EEC7 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_ActionListenerList.h; path = ../../src/events/juce_ActionListenerList.h; sourceTree = SOURCE_ROOT; };
1617348BBF5D103619D76911 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_AsyncUpdater.cpp; path = ../../src/events/juce_AsyncUpdater.cpp; sourceTree = SOURCE_ROOT; };
44DB44953945417F76199479 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_AsyncUpdater.h; path = ../../src/events/juce_AsyncUpdater.h; sourceTree = SOURCE_ROOT; };
D04B6E43A037F985434B2F5A = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_CallbackMessage.h; path = ../../src/events/juce_CallbackMessage.h; sourceTree = SOURCE_ROOT; };
@@ -1282,8 +1279,6 @@
31D985CB8646B78460E9D5A7,
09F7685D1EFF472ECB1F5EF1,
4EF8BD4BF46C4BCB39F96609,
5A46476E16BA4F9DA95E9E6A,
8269D9DFAD7923EE13E7EEC7,
1617348BBF5D103619D76911,
44DB44953945417F76199479,
D04B6E43A037F985434B2F5A,
@@ -2008,7 +2003,6 @@
12E3CC31875A202D6B30F778,
CF51988743ED2CD823DFE0B5,
659D9CD58B6914EB420E6AEC,
5BE4BAA99FDC6F1B3177096F,
55737E2F1817DE642AA7DA05,
6D2C50B0A69855A7F8C062E7,
70EE7A1273945B62B013DB43,


+ 0
- 4
Juce.jucer View File

@@ -491,10 +491,6 @@
file="src/events/juce_ActionBroadcaster.h"/>
<FILE id="Ux4rL61r9" name="juce_ActionListener.h" compile="0" resource="0"
file="src/events/juce_ActionListener.h"/>
<FILE id="F703c1Yid" name="juce_ActionListenerList.cpp" compile="1"
resource="0" file="src/events/juce_ActionListenerList.cpp"/>
<FILE id="OTsKfiFYq" name="juce_ActionListenerList.h" compile="0" resource="0"
file="src/events/juce_ActionListenerList.h"/>
<FILE id="LgjeCwe5N" name="juce_AsyncUpdater.cpp" compile="1" resource="0"
file="src/events/juce_AsyncUpdater.cpp"/>
<FILE id="TABmiJS44" name="juce_AsyncUpdater.h" compile="0" resource="0"


+ 0
- 1
amalgamation/juce_amalgamated_template.cpp View File

@@ -214,7 +214,6 @@
#include "../src/audio/synthesisers/juce_Sampler.cpp"
#include "../src/audio/synthesisers/juce_Synthesiser.cpp"
#include "../src/events/juce_ActionBroadcaster.cpp"
#include "../src/events/juce_ActionListenerList.cpp"
#include "../src/events/juce_AsyncUpdater.cpp"
#include "../src/events/juce_ChangeBroadcaster.cpp"
#include "../src/events/juce_InterprocessConnection.cpp"


+ 128
- 168
juce_amalgamated.cpp View File

@@ -12159,7 +12159,7 @@ namespace NumberToStringConverters
else
{
#if JUCE_WINDOWS
#if JUCE_VC8_OR_EARLIER || JUCE_MINGW
#if JUCE_VC7_OR_EARLIER || JUCE_MINGW
len = _snwprintf (buffer, numChars, L"%.9g", n);
#else
len = _snwprintf_s (buffer, numChars, _TRUNCATE, L"%.9g", n);
@@ -38287,162 +38287,143 @@ END_JUCE_NAMESPACE
/*** Start of inlined file: juce_ActionBroadcaster.cpp ***/
BEGIN_JUCE_NAMESPACE

ActionBroadcaster::ActionBroadcaster() throw()
{
// are you trying to create this object before or after juce has been intialised??
jassert (MessageManager::instance != 0);
}

ActionBroadcaster::~ActionBroadcaster()
{
// all event-based objects must be deleted BEFORE juce is shut down!
jassert (MessageManager::instance != 0);
}

void ActionBroadcaster::addActionListener (ActionListener* const listener)
{
actionListenerList.addActionListener (listener);
}

void ActionBroadcaster::removeActionListener (ActionListener* const listener)
{
jassert (actionListenerList.isValidMessageListener());

if (actionListenerList.isValidMessageListener())
actionListenerList.removeActionListener (listener);
}

void ActionBroadcaster::removeAllActionListeners()
{
actionListenerList.removeAllActionListeners();
}

void ActionBroadcaster::sendActionMessage (const String& message) const
{
actionListenerList.sendActionMessage (message);
}

END_JUCE_NAMESPACE
/*** End of inlined file: juce_ActionBroadcaster.cpp ***/


/*** Start of inlined file: juce_ActionListenerList.cpp ***/
BEGIN_JUCE_NAMESPACE

// special message of our own with a string in it
class ActionMessage : public Message
{
public:
const String message;

ActionMessage (const String& messageText, void* const listener_) throw()
ActionMessage (const String& messageText, ActionListener* const listener_) throw()
: message (messageText)
{
pointerParameter = listener_;
}

~ActionMessage() throw()
{
}

private:
ActionMessage (const ActionMessage&);
ActionMessage& operator= (const ActionMessage&);
};

ActionListenerList::ActionListenerList()
{
}
ActionBroadcaster::CallbackReceiver::CallbackReceiver() {}

ActionListenerList::~ActionListenerList()
void ActionBroadcaster::CallbackReceiver::handleMessage (const Message& message)
{
const ActionMessage& am = static_cast <const ActionMessage&> (message);
ActionListener* const target = static_cast <ActionListener*> (am.pointerParameter);

if (owner->actionListeners.contains (target))
target->actionListenerCallback (am.message);
}

void ActionListenerList::addActionListener (ActionListener* const listener)
ActionBroadcaster::ActionBroadcaster()
{
const ScopedLock sl (actionListenerLock_);

jassert (listener != 0);
jassert (! actionListeners_.contains (listener)); // trying to add a listener to the list twice!
// are you trying to create this object before or after juce has been intialised??
jassert (MessageManager::instance != 0);

if (listener != 0)
actionListeners_.add (listener);
callback.owner = this;
}

void ActionListenerList::removeActionListener (ActionListener* const listener)
ActionBroadcaster::~ActionBroadcaster()
{
const ScopedLock sl (actionListenerLock_);
// all event-based objects must be deleted BEFORE juce is shut down!
jassert (MessageManager::instance != 0);
}

jassert (actionListeners_.contains (listener)); // trying to remove a listener that isn't on the list!
void ActionBroadcaster::addActionListener (ActionListener* const listener)
{
const ScopedLock sl (actionListenerLock);

actionListeners_.removeValue (listener);
if (listener != 0)
actionListeners.add (listener);
}

void ActionListenerList::removeAllActionListeners()
void ActionBroadcaster::removeActionListener (ActionListener* const listener)
{
const ScopedLock sl (actionListenerLock_);
actionListeners_.clear();
const ScopedLock sl (actionListenerLock);
actionListeners.removeValue (listener);
}

void ActionListenerList::sendActionMessage (const String& message) const
void ActionBroadcaster::removeAllActionListeners()
{
const ScopedLock sl (actionListenerLock_);

for (int i = actionListeners_.size(); --i >= 0;)
postMessage (new ActionMessage (message, static_cast <ActionListener*> (actionListeners_.getUnchecked(i))));
const ScopedLock sl (actionListenerLock);
actionListeners.clear();
}

void ActionListenerList::handleMessage (const Message& message)
void ActionBroadcaster::sendActionMessage (const String& message) const
{
const ActionMessage& am = (const ActionMessage&) message;
const ScopedLock sl (actionListenerLock);

if (actionListeners_.contains (am.pointerParameter))
static_cast <ActionListener*> (am.pointerParameter)->actionListenerCallback (am.message);
for (int i = actionListeners.size(); --i >= 0;)
callback.postMessage (new ActionMessage (message, actionListeners.getUnchecked(i)));
}

END_JUCE_NAMESPACE
/*** End of inlined file: juce_ActionListenerList.cpp ***/
/*** End of inlined file: juce_ActionBroadcaster.cpp ***/


/*** Start of inlined file: juce_AsyncUpdater.cpp ***/
BEGIN_JUCE_NAMESPACE

class AsyncUpdater::AsyncUpdaterMessage : public CallbackMessage
{
public:
AsyncUpdaterMessage (AsyncUpdater& owner_)
: owner (owner_)
{
}

void messageCallback()
{
if (owner.pendingMessage.compareAndSetBool (0, this))
owner.handleAsyncUpdate();
}

AsyncUpdater& owner;
};

AsyncUpdater::AsyncUpdater() throw()
: asyncMessagePending (false)
{
internalAsyncHandler.owner = this;
}

AsyncUpdater::~AsyncUpdater()
{
// You're deleting this object with a background thread while there's an update
// pending on the main event thread - that's pretty dodgy threading, as the callback could
// happen after this destructor has finished. You should either use a MessageManagerLock while
// deleting this object, or find some other way to avoid such a race condition.
jassert (/*(! isUpdatePending()) ||*/ MessageManager::getInstance()->currentThreadHasLockedMessageManager());

pendingMessage = 0;
}

void AsyncUpdater::triggerAsyncUpdate()
{
if (! asyncMessagePending)
if (pendingMessage.value == 0)
{
asyncMessagePending = true;
internalAsyncHandler.postMessage (new Message());
ScopedPointer<AsyncUpdaterMessage> pending (new AsyncUpdaterMessage (*this));

if (pendingMessage.compareAndSetBool (pending, 0))
pending.release()->post();
}
}

void AsyncUpdater::cancelPendingUpdate() throw()
{
asyncMessagePending = false;
pendingMessage = 0;
}

void AsyncUpdater::handleUpdateNowIfNeeded()
{
if (asyncMessagePending)
{
asyncMessagePending = false;
// This can only be called by the event thread.
jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager());

if (pendingMessage.exchange (0) != 0)
handleAsyncUpdate();
}
}

void AsyncUpdater::AsyncUpdaterInternal::handleMessage (const Message&)
bool AsyncUpdater::isUpdatePending() const throw()
{
owner->handleUpdateNowIfNeeded();
return pendingMessage.value != 0;
}

END_JUCE_NAMESPACE
@@ -38452,40 +38433,24 @@ END_JUCE_NAMESPACE
/*** Start of inlined file: juce_ChangeBroadcaster.cpp ***/
BEGIN_JUCE_NAMESPACE

class ChangeBroadcaster::ChangeBroadcasterMessage : public CallbackMessage
{
public:
ChangeBroadcasterMessage (ChangeBroadcaster* const owner_)
: owner (owner_)
{
}

void messageCallback()
{
if (owner != 0 && owner->pendingMessage.value == this)
owner->sendSynchronousChangeMessage();
}

ChangeBroadcaster* owner;
};

ChangeBroadcaster::ChangeBroadcaster() throw()
{
// are you trying to create this object before or after juce has been intialised??
jassert (MessageManager::instance != 0);

callback.owner = this;
}

ChangeBroadcaster::~ChangeBroadcaster()
{
// all event-based objects must be deleted BEFORE juce is shut down!
jassert (MessageManager::instance != 0);

invalidatePendingMessage();
}

void ChangeBroadcaster::addChangeListener (ChangeListener* const listener)
{
// Listeners can only be safely added when the event thread is locked...
// Listeners can only be safely added when the event thread is locked
// You can use a MessageManagerLock if you need to call this from another thread.
jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager());

changeListeners.add (listener);
@@ -38493,7 +38458,8 @@ void ChangeBroadcaster::addChangeListener (ChangeListener* const listener)

void ChangeBroadcaster::removeChangeListener (ChangeListener* const listener)
{
// Listeners can only be safely added when the event thread is locked...
// Listeners can only be safely added when the event thread is locked
// You can use a MessageManagerLock if you need to call this from another thread.
jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager());

changeListeners.remove (listener);
@@ -38501,28 +38467,17 @@ void ChangeBroadcaster::removeChangeListener (ChangeListener* const listener)

void ChangeBroadcaster::removeAllChangeListeners()
{
// Listeners can only be safely added when the event thread is locked...
// Listeners can only be safely added when the event thread is locked
// You can use a MessageManagerLock if you need to call this from another thread.
jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager());

changeListeners.clear();
}

void ChangeBroadcaster::invalidatePendingMessage()
{
ChangeBroadcasterMessage* const oldMessage = pendingMessage.exchange (0);
if (oldMessage != 0)
oldMessage->owner = 0;
}

void ChangeBroadcaster::sendChangeMessage()
{
if (pendingMessage.value == 0 && changeListeners.size() > 0)
{
ScopedPointer<ChangeBroadcasterMessage> pending (new ChangeBroadcasterMessage (this));

if (pendingMessage.compareAndSetBool (pending, 0))
pending.release()->post();
}
if (changeListeners.size() > 0)
callback.triggerAsyncUpdate();
}

void ChangeBroadcaster::sendSynchronousChangeMessage()
@@ -38530,14 +38485,29 @@ void ChangeBroadcaster::sendSynchronousChangeMessage()
// This can only be called by the event thread.
jassert (MessageManager::getInstance()->isThisTheMessageThread());

invalidatePendingMessage();
changeListeners.call (&ChangeListener::changeListenerCallback, this);
callback.cancelPendingUpdate();
callListeners();
}

void ChangeBroadcaster::dispatchPendingMessages()
{
if (pendingMessage.get() != 0)
sendSynchronousChangeMessage();
callback.handleUpdateNowIfNeeded();
}

void ChangeBroadcaster::callListeners()
{
changeListeners.call (&ChangeListener::changeListenerCallback, this);
}

ChangeBroadcaster::ChangeBroadcasterCallback::ChangeBroadcasterCallback()
: owner (0)
{
}

void ChangeBroadcaster::ChangeBroadcasterCallback::handleAsyncUpdate()
{
jassert (owner != 0);
owner->callListeners();
}

END_JUCE_NAMESPACE
@@ -38955,7 +38925,8 @@ Message::Message() throw()
: intParameter1 (0),
intParameter2 (0),
intParameter3 (0),
pointerParameter (0)
pointerParameter (0),
messageRecipient (0)
{
}

@@ -38966,7 +38937,8 @@ Message::Message (const int intParameter1_,
: intParameter1 (intParameter1_),
intParameter2 (intParameter2_),
intParameter3 (intParameter3_),
pointerParameter (pointerParameter_)
pointerParameter (pointerParameter_),
messageRecipient (0)
{
}

@@ -39037,7 +39009,7 @@ MessageManager::MessageManager() throw()

MessageManager::~MessageManager() throw()
{
broadcastListeners = 0;
broadcaster = 0;

doPlatformSpecificShutdown();

@@ -39068,47 +39040,40 @@ void MessageManager::postMessageToQueue (Message* const message)
delete message;
}

CallbackMessage::CallbackMessage() throw() {}
CallbackMessage::~CallbackMessage() {}
CallbackMessage::CallbackMessage() throw() {}
CallbackMessage::~CallbackMessage() {}

void CallbackMessage::post()
{
if (MessageManager::instance != 0)
MessageManager::instance->postCallbackMessage (this);
}

void MessageManager::postCallbackMessage (Message* const message)
{
message->messageRecipient = 0;
postMessageToQueue (message);
MessageManager::instance->postMessageToQueue (this);
}

// not for public use..
void MessageManager::deliverMessage (Message* const message)
{
const ScopedPointer <Message> messageDeleter (message);
MessageListener* const recipient = message->messageRecipient;

JUCE_TRY
{
if (messageListeners.contains (recipient))
{
recipient->handleMessage (*message);
}
else if (recipient == 0)
const ScopedPointer <Message> messageDeleter (message);
MessageListener* const recipient = message->messageRecipient;

if (recipient == 0)
{
if (message->intParameter1 == quitMessageId)
CallbackMessage* const callbackMessage = dynamic_cast <CallbackMessage*> (message);

if (callbackMessage != 0)
{
quitMessageReceived = true;
callbackMessage->messageCallback();
}
else
else if (message->intParameter1 == quitMessageId)
{
CallbackMessage* const cm = dynamic_cast <CallbackMessage*> (message);

if (cm != 0)
cm->messageCallback();
quitMessageReceived = true;
}
}
else if (messageListeners.contains (recipient))
{
recipient->handleMessage (*message);
}
}
JUCE_CATCH_EXCEPTION
}
@@ -39123,10 +39088,7 @@ void MessageManager::runDispatchLoop()

void MessageManager::stopDispatchLoop()
{
Message* const m = new Message (quitMessageId, 0, 0, 0);
m->messageRecipient = 0;
postMessageToQueue (m);

postMessageToQueue (new Message (quitMessageId, 0, 0, 0));
quitMessagePosted = true;
}

@@ -39159,22 +39121,22 @@ bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor)

void MessageManager::deliverBroadcastMessage (const String& value)
{
if (broadcastListeners != 0)
broadcastListeners->sendActionMessage (value);
if (broadcaster != 0)
broadcaster->sendActionMessage (value);
}

void MessageManager::registerBroadcastListener (ActionListener* const listener)
{
if (broadcastListeners == 0)
broadcastListeners = new ActionListenerList();
if (broadcaster == 0)
broadcaster = new ActionBroadcaster();

broadcastListeners->addActionListener (listener);
broadcaster->addActionListener (listener);
}

void MessageManager::deregisterBroadcastListener (ActionListener* const listener)
{
if (broadcastListeners != 0)
broadcastListeners->removeActionListener (listener);
if (broadcaster != 0)
broadcaster->removeActionListener (listener);
}

bool MessageManager::isThisTheMessageThread() const throw()
@@ -39213,7 +39175,6 @@ class MessageManagerLock::SharedEvents : public ReferenceCountedObject
{
public:
SharedEvents() {}
~SharedEvents() {}

/* This class just holds a couple of events to communicate between the BlockingMessage
and the MessageManagerLock. Because both of these objects may be deleted at any time,
@@ -39229,7 +39190,6 @@ class MessageManagerLock::BlockingMessage : public CallbackMessage
{
public:
BlockingMessage (MessageManagerLock::SharedEvents* const events_) : events (events_) {}
~BlockingMessage() throw() {}

void messageCallback()
{


+ 166
- 226
juce_amalgamated.h View File

@@ -64,7 +64,7 @@
*/
#define JUCE_MAJOR_VERSION 1
#define JUCE_MINOR_VERSION 52
#define JUCE_BUILDNUMBER 93
#define JUCE_BUILDNUMBER 94

/** Current Juce version number.

@@ -12281,131 +12281,6 @@ private:
#ifndef __JUCE_ASYNCUPDATER_JUCEHEADER__
#define __JUCE_ASYNCUPDATER_JUCEHEADER__


/*** Start of inlined file: juce_MessageListener.h ***/
#ifndef __JUCE_MESSAGELISTENER_JUCEHEADER__
#define __JUCE_MESSAGELISTENER_JUCEHEADER__


/*** Start of inlined file: juce_Message.h ***/
#ifndef __JUCE_MESSAGE_JUCEHEADER__
#define __JUCE_MESSAGE_JUCEHEADER__

class MessageListener;
class MessageManager;

/** The base class for objects that can be delivered to a MessageListener.

The simplest Message object contains a few integer and pointer parameters
that the user can set, and this is enough for a lot of purposes. For passing more
complex data, subclasses of Message can also be used.

@see MessageListener, MessageManager, ActionListener, ChangeListener
*/
class JUCE_API Message
{
public:

/** Creates an uninitialised message.

The class's variables will also be left uninitialised.
*/
Message() throw();

/** Creates a message object, filling in the member variables.

The corresponding public member variables will be set from the parameters
passed in.
*/
Message (int intParameter1,
int intParameter2,
int intParameter3,
void* pointerParameter) throw();

/** Destructor. */
virtual ~Message();

// These values can be used for carrying simple data that the application needs to
// pass around. For more complex messages, just create a subclass.

int intParameter1; /**< user-defined integer value. */
int intParameter2; /**< user-defined integer value. */
int intParameter3; /**< user-defined integer value. */
void* pointerParameter; /**< user-defined pointer value. */

juce_UseDebuggingNewOperator

private:
friend class MessageListener;
friend class MessageManager;
MessageListener* messageRecipient;

Message (const Message&);
Message& operator= (const Message&);
};

#endif // __JUCE_MESSAGE_JUCEHEADER__
/*** End of inlined file: juce_Message.h ***/

/**
MessageListener subclasses can post and receive Message objects.

@see Message, MessageManager, ActionListener, ChangeListener
*/
class JUCE_API MessageListener
{
protected:

/** Creates a MessageListener. */
MessageListener() throw();

public:

/** Destructor.

When a MessageListener is deleted, it removes itself from a global list
of registered listeners, so that the isValidMessageListener() method
will no longer return true.
*/
virtual ~MessageListener();

/** This is the callback method that receives incoming messages.

This is called by the MessageManager from its dispatch loop.

@see postMessage
*/
virtual void handleMessage (const Message& message) = 0;

/** Sends a message to the message queue, for asynchronous delivery to this listener
later on.

This method can be called safely by any thread.

@param message the message object to send - this will be deleted
automatically by the message queue, so don't keep any
references to it after calling this method.
@see handleMessage
*/
void postMessage (Message* message) const throw();

/** Checks whether this MessageListener has been deleted.

Although not foolproof, this method is safe to call on dangling or null
pointers. A list of active MessageListeners is kept internally, so this
checks whether the object is on this list or not.

Note that it's possible to get a false-positive here, if an object is
deleted and another is subsequently created that happens to be at the
exact same memory location, but I can't think of a good way of avoiding
this.
*/
bool isValidMessageListener() const throw();
};

#endif // __JUCE_MESSAGELISTENER_JUCEHEADER__
/*** End of inlined file: juce_MessageListener.h ***/

/**
Has a callback method that is triggered asynchronously.

@@ -12454,9 +12329,15 @@ public:
Use this as a kind of "flush" operation - if an update is pending, the
handleAsyncUpdate() method will be called immediately; if no update is
pending, then nothing will be done.

Because this may invoke the callback, this method must only be called on
the main event thread.
*/
void handleUpdateNowIfNeeded();

/** Returns true if there's an update callback in the pipeline. */
bool isUpdatePending() const throw();

/** Called back to do whatever your class needs to do.

This method is called by the message thread at the next convenient time
@@ -12466,23 +12347,10 @@ public:

private:

class AsyncUpdaterInternal : public MessageListener
{
public:
AsyncUpdaterInternal() {}
~AsyncUpdaterInternal() {}

void handleMessage (const Message&);

AsyncUpdater* owner;

private:
AsyncUpdaterInternal (const AsyncUpdaterInternal&);
AsyncUpdaterInternal& operator= (const AsyncUpdaterInternal&);
};
class AsyncUpdaterMessage;
friend class AsyncUpdaterMessage;

AsyncUpdaterInternal internalAsyncHandler;
bool asyncMessagePending;
Atomic<AsyncUpdaterMessage*> pendingMessage;
};

#endif // __JUCE_ASYNCUPDATER_JUCEHEADER__
@@ -13099,13 +12967,20 @@ public:

private:

class ChangeBroadcasterMessage;
friend class ChangeBroadcasterMessage;
class ChangeBroadcasterCallback : public AsyncUpdater
{
public:
ChangeBroadcasterCallback();
void handleAsyncUpdate();

ChangeBroadcaster* owner;
};

Atomic<ChangeBroadcasterMessage*> pendingMessage;
friend class ChangeBroadcasterCallback;
ChangeBroadcasterCallback callback;
ListenerList <ChangeListener> changeListeners;

void invalidatePendingMessage();
void callListeners();

ChangeBroadcaster (const ChangeBroadcaster&);
ChangeBroadcaster& operator= (const ChangeBroadcaster&);
@@ -28302,6 +28177,131 @@ struct JUCE_API ApplicationCommandInfo
#endif // __JUCE_APPLICATIONCOMMANDINFO_JUCEHEADER__
/*** End of inlined file: juce_ApplicationCommandInfo.h ***/


/*** Start of inlined file: juce_MessageListener.h ***/
#ifndef __JUCE_MESSAGELISTENER_JUCEHEADER__
#define __JUCE_MESSAGELISTENER_JUCEHEADER__


/*** Start of inlined file: juce_Message.h ***/
#ifndef __JUCE_MESSAGE_JUCEHEADER__
#define __JUCE_MESSAGE_JUCEHEADER__

class MessageListener;
class MessageManager;

/** The base class for objects that can be delivered to a MessageListener.

The simplest Message object contains a few integer and pointer parameters
that the user can set, and this is enough for a lot of purposes. For passing more
complex data, subclasses of Message can also be used.

@see MessageListener, MessageManager, ActionListener, ChangeListener
*/
class JUCE_API Message
{
public:

/** Creates an uninitialised message.

The class's variables will also be left uninitialised.
*/
Message() throw();

/** Creates a message object, filling in the member variables.

The corresponding public member variables will be set from the parameters
passed in.
*/
Message (int intParameter1,
int intParameter2,
int intParameter3,
void* pointerParameter) throw();

/** Destructor. */
virtual ~Message();

// These values can be used for carrying simple data that the application needs to
// pass around. For more complex messages, just create a subclass.

int intParameter1; /**< user-defined integer value. */
int intParameter2; /**< user-defined integer value. */
int intParameter3; /**< user-defined integer value. */
void* pointerParameter; /**< user-defined pointer value. */

juce_UseDebuggingNewOperator

private:
friend class MessageListener;
friend class MessageManager;
MessageListener* messageRecipient;

Message (const Message&);
Message& operator= (const Message&);
};

#endif // __JUCE_MESSAGE_JUCEHEADER__
/*** End of inlined file: juce_Message.h ***/

/**
MessageListener subclasses can post and receive Message objects.

@see Message, MessageManager, ActionListener, ChangeListener
*/
class JUCE_API MessageListener
{
protected:

/** Creates a MessageListener. */
MessageListener() throw();

public:

/** Destructor.

When a MessageListener is deleted, it removes itself from a global list
of registered listeners, so that the isValidMessageListener() method
will no longer return true.
*/
virtual ~MessageListener();

/** This is the callback method that receives incoming messages.

This is called by the MessageManager from its dispatch loop.

@see postMessage
*/
virtual void handleMessage (const Message& message) = 0;

/** Sends a message to the message queue, for asynchronous delivery to this listener
later on.

This method can be called safely by any thread.

@param message the message object to send - this will be deleted
automatically by the message queue, so don't keep any
references to it after calling this method.
@see handleMessage
*/
void postMessage (Message* message) const throw();

/** Checks whether this MessageListener has been deleted.

Although not foolproof, this method is safe to call on dangling or null
pointers. A list of active MessageListeners is kept internally, so this
checks whether the object is on this list or not.

Note that it's possible to get a false-positive here, if an object is
deleted and another is subsequently created that happens to be at the
exact same memory location, but I can't think of a good way of avoiding
this.
*/
bool isValidMessageListener() const throw();
};

#endif // __JUCE_MESSAGELISTENER_JUCEHEADER__
/*** End of inlined file: juce_MessageListener.h ***/

/**
A command target publishes a list of command IDs that it can perform.

@@ -28537,7 +28537,7 @@ private:
Used by various classes, e.g. buttons when they are pressed, to tell listeners
about something that's happened.

@see ActionListenerList, ActionBroadcaster, ChangeListener
@see ActionBroadcaster, ChangeListener
*/
class JUCE_API ActionListener
{
@@ -28548,7 +28548,7 @@ public:
/** Overridden by your subclass to receive the callback.

@param message the string that was specified when the event was triggered
by a call to ActionListenerList::sendActionMessage()
by a call to ActionBroadcaster::sendActionMessage()
*/
virtual void actionListenerCallback (const String& message) = 0;
};
@@ -43380,96 +43380,29 @@ private:
#ifndef __JUCE_ACTIONBROADCASTER_JUCEHEADER__
#define __JUCE_ACTIONBROADCASTER_JUCEHEADER__


/*** Start of inlined file: juce_ActionListenerList.h ***/
#ifndef __JUCE_ACTIONLISTENERLIST_JUCEHEADER__
#define __JUCE_ACTIONLISTENERLIST_JUCEHEADER__

/**
A set of ActionListeners.

Listeners can be added and removed from the list, and messages can be
broadcast to all the listeners.

@see ActionListener, ActionBroadcaster
*/
class JUCE_API ActionListenerList : public MessageListener
{
public:

/** Creates an empty list. */
ActionListenerList();

/** Destructor. */
~ActionListenerList();

/** Adds a listener to the list.

(Trying to add a listener that's already on the list will have no effect).
*/
void addActionListener (ActionListener* listener);

/** Removes a listener from the list.

If the listener isn't on the list, this won't have any effect.
*/
void removeActionListener (ActionListener* listener);

/** Removes all listeners from the list. */
void removeAllActionListeners();

/** Broadcasts a message to all the registered listeners.

This sends the message asynchronously.

If a listener is on the list when this method is called but is removed from
the list before the message arrives, it won't receive the message. Similarly
listeners that are added to the list after the message is sent but before it
arrives won't get the message either.
*/
void sendActionMessage (const String& message) const;

/** @internal */
void handleMessage (const Message&);

juce_UseDebuggingNewOperator

private:
SortedSet <void*> actionListeners_;
CriticalSection actionListenerLock_;

ActionListenerList (const ActionListenerList&);
ActionListenerList& operator= (const ActionListenerList&);
};

#endif // __JUCE_ACTIONLISTENERLIST_JUCEHEADER__
/*** End of inlined file: juce_ActionListenerList.h ***/

/** Manages a list of ActionListeners, and can send them messages.

To quickly add methods to your class that can add/remove action
listeners and broadcast to them, you can derive from this.

@see ActionListenerList, ActionListener
@see ActionListener, ChangeListener
*/
class JUCE_API ActionBroadcaster
{
public:

/** Creates an ActionBroadcaster. */
ActionBroadcaster() throw();
ActionBroadcaster();

/** Destructor. */
virtual ~ActionBroadcaster();

/** Adds a listener to the list.

(Trying to add a listener that's already on the list will have no effect).
Trying to add a listener that's already on the list will have no effect.
*/
void addActionListener (ActionListener* listener);

/** Removes a listener from the list.

If the listener isn't on the list, this won't have any effect.
*/
void removeActionListener (ActionListener* listener);
@@ -43478,14 +43411,25 @@ public:
void removeAllActionListeners();

/** Broadcasts a message to all the registered listeners.

@see ActionListenerList::sendActionMessage
@see ActionListener::actionListenerCallback
*/
void sendActionMessage (const String& message) const;

private:

ActionListenerList actionListenerList;
class CallbackReceiver : public MessageListener
{
public:
CallbackReceiver();
void handleMessage (const Message&);

ActionBroadcaster* owner;
};

friend class CallbackReceiver;
CallbackReceiver callback;
SortedSet <ActionListener*> actionListeners;
CriticalSection actionListenerLock;

ActionBroadcaster (const ActionBroadcaster&);
ActionBroadcaster& operator= (const ActionBroadcaster&);
@@ -43498,9 +43442,6 @@ private:
#endif
#ifndef __JUCE_ACTIONLISTENER_JUCEHEADER__

#endif
#ifndef __JUCE_ACTIONLISTENERLIST_JUCEHEADER__

#endif
#ifndef __JUCE_ASYNCUPDATER_JUCEHEADER__

@@ -43980,7 +43921,7 @@ private:
static MessageManager* instance;

SortedSet <const MessageListener*> messageListeners;
ScopedPointer <ActionListenerList> broadcastListeners;
ScopedPointer <ActionBroadcaster> broadcaster;

friend class JUCEApplication;
bool quitMessagePosted, quitMessageReceived;
@@ -43989,7 +43930,6 @@ private:
static void* exitModalLoopCallback (void*);

void postMessageToQueue (Message* message);
void postCallbackMessage (Message* message);

static void doPlatformSpecificInitialisation();
static void doPlatformSpecificShutdown();


+ 1
- 0
src/application/juce_ApplicationCommandTarget.h View File

@@ -28,6 +28,7 @@
#include "../gui/components/juce_Component.h"
#include "juce_ApplicationCommandInfo.h"
#include "../events/juce_MessageListener.h"
//==============================================================================


+ 1
- 1
src/core/juce_StandardHeader.h View File

@@ -33,7 +33,7 @@
*/
#define JUCE_MAJOR_VERSION 1
#define JUCE_MINOR_VERSION 52
#define JUCE_BUILDNUMBER 93
#define JUCE_BUILDNUMBER 94
/** Current Juce version number.


+ 46
- 8
src/events/juce_ActionBroadcaster.cpp View File

@@ -29,13 +29,45 @@ BEGIN_JUCE_NAMESPACE
#include "juce_ActionBroadcaster.h"
#include "juce_MessageManager.h"
#include "../threads/juce_ScopedLock.h"
//==============================================================================
ActionBroadcaster::ActionBroadcaster() throw()
// special message of our own with a string in it
class ActionMessage : public Message
{
public:
const String message;
ActionMessage (const String& messageText, ActionListener* const listener_) throw()
: message (messageText)
{
pointerParameter = listener_;
}
private:
ActionMessage (const ActionMessage&);
ActionMessage& operator= (const ActionMessage&);
};
ActionBroadcaster::CallbackReceiver::CallbackReceiver() {}
void ActionBroadcaster::CallbackReceiver::handleMessage (const Message& message)
{
const ActionMessage& am = static_cast <const ActionMessage&> (message);
ActionListener* const target = static_cast <ActionListener*> (am.pointerParameter);
if (owner->actionListeners.contains (target))
target->actionListenerCallback (am.message);
}
//==============================================================================
ActionBroadcaster::ActionBroadcaster()
{
// are you trying to create this object before or after juce has been intialised??
jassert (MessageManager::instance != 0);
callback.owner = this;
}
ActionBroadcaster::~ActionBroadcaster()
@@ -46,25 +78,31 @@ ActionBroadcaster::~ActionBroadcaster()
void ActionBroadcaster::addActionListener (ActionListener* const listener)
{
actionListenerList.addActionListener (listener);
const ScopedLock sl (actionListenerLock);
if (listener != 0)
actionListeners.add (listener);
}
void ActionBroadcaster::removeActionListener (ActionListener* const listener)
{
jassert (actionListenerList.isValidMessageListener());
if (actionListenerList.isValidMessageListener())
actionListenerList.removeActionListener (listener);
const ScopedLock sl (actionListenerLock);
actionListeners.removeValue (listener);
}
void ActionBroadcaster::removeAllActionListeners()
{
actionListenerList.removeAllActionListeners();
const ScopedLock sl (actionListenerLock);
actionListeners.clear();
}
void ActionBroadcaster::sendActionMessage (const String& message) const
{
actionListenerList.sendActionMessage (message);
const ScopedLock sl (actionListenerLock);
for (int i = actionListeners.size(); --i >= 0;)
callback.postMessage (new ActionMessage (message, actionListeners.getUnchecked(i)));
}
END_JUCE_NAMESPACE

+ 20
- 9
src/events/juce_ActionBroadcaster.h View File

@@ -26,7 +26,9 @@
#ifndef __JUCE_ACTIONBROADCASTER_JUCEHEADER__
#define __JUCE_ACTIONBROADCASTER_JUCEHEADER__
#include "juce_ActionListenerList.h"
#include "juce_ActionListener.h"
#include "juce_MessageListener.h"
#include "../containers/juce_SortedSet.h"
//==============================================================================
@@ -35,27 +37,25 @@
To quickly add methods to your class that can add/remove action
listeners and broadcast to them, you can derive from this.
@see ActionListenerList, ActionListener
@see ActionListener, ChangeListener
*/
class JUCE_API ActionBroadcaster
{
public:
//==============================================================================
/** Creates an ActionBroadcaster. */
ActionBroadcaster() throw();
ActionBroadcaster();
/** Destructor. */
virtual ~ActionBroadcaster();
//==============================================================================
/** Adds a listener to the list.
(Trying to add a listener that's already on the list will have no effect).
Trying to add a listener that's already on the list will have no effect.
*/
void addActionListener (ActionListener* listener);
/** Removes a listener from the list.
If the listener isn't on the list, this won't have any effect.
*/
void removeActionListener (ActionListener* listener);
@@ -65,15 +65,26 @@ public:
//==============================================================================
/** Broadcasts a message to all the registered listeners.
@see ActionListenerList::sendActionMessage
@see ActionListener::actionListenerCallback
*/
void sendActionMessage (const String& message) const;
private:
//==============================================================================
ActionListenerList actionListenerList;
class CallbackReceiver : public MessageListener
{
public:
CallbackReceiver();
void handleMessage (const Message&);
ActionBroadcaster* owner;
};
friend class CallbackReceiver;
CallbackReceiver callback;
SortedSet <ActionListener*> actionListeners;
CriticalSection actionListenerLock;
ActionBroadcaster (const ActionBroadcaster&);
ActionBroadcaster& operator= (const ActionBroadcaster&);


+ 2
- 2
src/events/juce_ActionListener.h View File

@@ -36,7 +36,7 @@
Used by various classes, e.g. buttons when they are pressed, to tell listeners
about something that's happened.
@see ActionListenerList, ActionBroadcaster, ChangeListener
@see ActionBroadcaster, ChangeListener
*/
class JUCE_API ActionListener
{
@@ -47,7 +47,7 @@ public:
/** Overridden by your subclass to receive the callback.
@param message the string that was specified when the event was triggered
by a call to ActionListenerList::sendActionMessage()
by a call to ActionBroadcaster::sendActionMessage()
*/
virtual void actionListenerCallback (const String& message) = 0;
};


+ 0
- 108
src/events/juce_ActionListenerList.cpp View File

@@ -1,108 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-10 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online 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.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../core/juce_StandardHeader.h"
BEGIN_JUCE_NAMESPACE
#include "juce_ActionListenerList.h"
#include "../threads/juce_ScopedLock.h"
//==============================================================================
// special message of our own with a string in it
class ActionMessage : public Message
{
public:
const String message;
ActionMessage (const String& messageText, void* const listener_) throw()
: message (messageText)
{
pointerParameter = listener_;
}
~ActionMessage() throw()
{
}
private:
ActionMessage (const ActionMessage&);
ActionMessage& operator= (const ActionMessage&);
};
//==============================================================================
ActionListenerList::ActionListenerList()
{
}
ActionListenerList::~ActionListenerList()
{
}
void ActionListenerList::addActionListener (ActionListener* const listener)
{
const ScopedLock sl (actionListenerLock_);
jassert (listener != 0);
jassert (! actionListeners_.contains (listener)); // trying to add a listener to the list twice!
if (listener != 0)
actionListeners_.add (listener);
}
void ActionListenerList::removeActionListener (ActionListener* const listener)
{
const ScopedLock sl (actionListenerLock_);
jassert (actionListeners_.contains (listener)); // trying to remove a listener that isn't on the list!
actionListeners_.removeValue (listener);
}
void ActionListenerList::removeAllActionListeners()
{
const ScopedLock sl (actionListenerLock_);
actionListeners_.clear();
}
void ActionListenerList::sendActionMessage (const String& message) const
{
const ScopedLock sl (actionListenerLock_);
for (int i = actionListeners_.size(); --i >= 0;)
postMessage (new ActionMessage (message, static_cast <ActionListener*> (actionListeners_.getUnchecked(i))));
}
void ActionListenerList::handleMessage (const Message& message)
{
const ActionMessage& am = (const ActionMessage&) message;
if (actionListeners_.contains (am.pointerParameter))
static_cast <ActionListener*> (am.pointerParameter)->actionListenerCallback (am.message);
}
END_JUCE_NAMESPACE

+ 0
- 96
src/events/juce_ActionListenerList.h View File

@@ -1,96 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-10 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online 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.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#ifndef __JUCE_ACTIONLISTENERLIST_JUCEHEADER__
#define __JUCE_ACTIONLISTENERLIST_JUCEHEADER__
#include "juce_ActionListener.h"
#include "juce_MessageListener.h"
#include "../containers/juce_SortedSet.h"
#include "../threads/juce_CriticalSection.h"
//==============================================================================
/**
A set of ActionListeners.
Listeners can be added and removed from the list, and messages can be
broadcast to all the listeners.
@see ActionListener, ActionBroadcaster
*/
class JUCE_API ActionListenerList : public MessageListener
{
public:
//==============================================================================
/** Creates an empty list. */
ActionListenerList();
/** Destructor. */
~ActionListenerList();
//==============================================================================
/** Adds a listener to the list.
(Trying to add a listener that's already on the list will have no effect).
*/
void addActionListener (ActionListener* listener);
/** Removes a listener from the list.
If the listener isn't on the list, this won't have any effect.
*/
void removeActionListener (ActionListener* listener);
/** Removes all listeners from the list. */
void removeAllActionListeners();
/** Broadcasts a message to all the registered listeners.
This sends the message asynchronously.
If a listener is on the list when this method is called but is removed from
the list before the message arrives, it won't receive the message. Similarly
listeners that are added to the list after the message is sent but before it
arrives won't get the message either.
*/
void sendActionMessage (const String& message) const;
//==============================================================================
/** @internal */
void handleMessage (const Message&);
juce_UseDebuggingNewOperator
private:
SortedSet <void*> actionListeners_;
CriticalSection actionListenerLock_;
ActionListenerList (const ActionListenerList&);
ActionListenerList& operator= (const ActionListenerList&);
};
#endif // __JUCE_ACTIONLISTENERLIST_JUCEHEADER__

+ 40
- 12
src/events/juce_AsyncUpdater.cpp View File

@@ -28,45 +28,73 @@
BEGIN_JUCE_NAMESPACE
#include "juce_AsyncUpdater.h"
#include "juce_CallbackMessage.h"
#include "../containers/juce_ScopedPointer.h"
#include "juce_MessageManager.h"
//==============================================================================
class AsyncUpdater::AsyncUpdaterMessage : public CallbackMessage
{
public:
AsyncUpdaterMessage (AsyncUpdater& owner_)
: owner (owner_)
{
}
void messageCallback()
{
if (owner.pendingMessage.compareAndSetBool (0, this))
owner.handleAsyncUpdate();
}
AsyncUpdater& owner;
};
//==============================================================================
AsyncUpdater::AsyncUpdater() throw()
: asyncMessagePending (false)
{
internalAsyncHandler.owner = this;
}
AsyncUpdater::~AsyncUpdater()
{
// You're deleting this object with a background thread while there's an update
// pending on the main event thread - that's pretty dodgy threading, as the callback could
// happen after this destructor has finished. You should either use a MessageManagerLock while
// deleting this object, or find some other way to avoid such a race condition.
jassert (/*(! isUpdatePending()) ||*/ MessageManager::getInstance()->currentThreadHasLockedMessageManager());
pendingMessage = 0;
}
void AsyncUpdater::triggerAsyncUpdate()
{
if (! asyncMessagePending)
if (pendingMessage.value == 0)
{
asyncMessagePending = true;
internalAsyncHandler.postMessage (new Message());
ScopedPointer<AsyncUpdaterMessage> pending (new AsyncUpdaterMessage (*this));
if (pendingMessage.compareAndSetBool (pending, 0))
pending.release()->post();
}
}
void AsyncUpdater::cancelPendingUpdate() throw()
{
asyncMessagePending = false;
pendingMessage = 0;
}
void AsyncUpdater::handleUpdateNowIfNeeded()
{
if (asyncMessagePending)
{
asyncMessagePending = false;
// This can only be called by the event thread.
jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager());
if (pendingMessage.exchange (0) != 0)
handleAsyncUpdate();
}
}
void AsyncUpdater::AsyncUpdaterInternal::handleMessage (const Message&)
bool AsyncUpdater::isUpdatePending() const throw()
{
owner->handleUpdateNowIfNeeded();
return pendingMessage.value != 0;
}


+ 10
- 17
src/events/juce_AsyncUpdater.h View File

@@ -26,7 +26,7 @@
#ifndef __JUCE_ASYNCUPDATER_JUCEHEADER__
#define __JUCE_ASYNCUPDATER_JUCEHEADER__
#include "juce_MessageListener.h"
#include "../core/juce_Atomic.h"
//==============================================================================
@@ -79,9 +79,15 @@ public:
Use this as a kind of "flush" operation - if an update is pending, the
handleAsyncUpdate() method will be called immediately; if no update is
pending, then nothing will be done.
Because this may invoke the callback, this method must only be called on
the main event thread.
*/
void handleUpdateNowIfNeeded();
/** Returns true if there's an update callback in the pipeline. */
bool isUpdatePending() const throw();
//==============================================================================
/** Called back to do whatever your class needs to do.
@@ -93,23 +99,10 @@ public:
private:
//==============================================================================
class AsyncUpdaterInternal : public MessageListener
{
public:
AsyncUpdaterInternal() {}
~AsyncUpdaterInternal() {}
void handleMessage (const Message&);
AsyncUpdater* owner;
private:
AsyncUpdaterInternal (const AsyncUpdaterInternal&);
AsyncUpdaterInternal& operator= (const AsyncUpdaterInternal&);
};
class AsyncUpdaterMessage;
friend class AsyncUpdaterMessage;
AsyncUpdaterInternal internalAsyncHandler;
bool asyncMessagePending;
Atomic<AsyncUpdaterMessage*> pendingMessage;
};


+ 30
- 41
src/events/juce_ChangeBroadcaster.cpp View File

@@ -31,42 +31,25 @@ BEGIN_JUCE_NAMESPACE
#include "juce_MessageManager.h"
//==============================================================================
class ChangeBroadcaster::ChangeBroadcasterMessage : public CallbackMessage
{
public:
ChangeBroadcasterMessage (ChangeBroadcaster* const owner_)
: owner (owner_)
{
}
void messageCallback()
{
if (owner != 0 && owner->pendingMessage.value == this)
owner->sendSynchronousChangeMessage();
}
ChangeBroadcaster* owner;
};
//==============================================================================
ChangeBroadcaster::ChangeBroadcaster() throw()
{
// are you trying to create this object before or after juce has been intialised??
jassert (MessageManager::instance != 0);
callback.owner = this;
}
ChangeBroadcaster::~ChangeBroadcaster()
{
// all event-based objects must be deleted BEFORE juce is shut down!
jassert (MessageManager::instance != 0);
invalidatePendingMessage();
}
void ChangeBroadcaster::addChangeListener (ChangeListener* const listener)
{
// Listeners can only be safely added when the event thread is locked...
// Listeners can only be safely added when the event thread is locked
// You can use a MessageManagerLock if you need to call this from another thread.
jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager());
changeListeners.add (listener);
@@ -74,7 +57,8 @@ void ChangeBroadcaster::addChangeListener (ChangeListener* const listener)
void ChangeBroadcaster::removeChangeListener (ChangeListener* const listener)
{
// Listeners can only be safely added when the event thread is locked...
// Listeners can only be safely added when the event thread is locked
// You can use a MessageManagerLock if you need to call this from another thread.
jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager());
changeListeners.remove (listener);
@@ -82,28 +66,17 @@ void ChangeBroadcaster::removeChangeListener (ChangeListener* const listener)
void ChangeBroadcaster::removeAllChangeListeners()
{
// Listeners can only be safely added when the event thread is locked...
// Listeners can only be safely added when the event thread is locked
// You can use a MessageManagerLock if you need to call this from another thread.
jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager());
changeListeners.clear();
}
void ChangeBroadcaster::invalidatePendingMessage()
{
ChangeBroadcasterMessage* const oldMessage = pendingMessage.exchange (0);
if (oldMessage != 0)
oldMessage->owner = 0;
}
void ChangeBroadcaster::sendChangeMessage()
{
if (pendingMessage.value == 0 && changeListeners.size() > 0)
{
ScopedPointer<ChangeBroadcasterMessage> pending (new ChangeBroadcasterMessage (this));
if (pendingMessage.compareAndSetBool (pending, 0))
pending.release()->post();
}
if (changeListeners.size() > 0)
callback.triggerAsyncUpdate();
}
void ChangeBroadcaster::sendSynchronousChangeMessage()
@@ -111,14 +84,30 @@ void ChangeBroadcaster::sendSynchronousChangeMessage()
// This can only be called by the event thread.
jassert (MessageManager::getInstance()->isThisTheMessageThread());
invalidatePendingMessage();
changeListeners.call (&ChangeListener::changeListenerCallback, this);
callback.cancelPendingUpdate();
callListeners();
}
void ChangeBroadcaster::dispatchPendingMessages()
{
if (pendingMessage.get() != 0)
sendSynchronousChangeMessage();
callback.handleUpdateNowIfNeeded();
}
void ChangeBroadcaster::callListeners()
{
changeListeners.call (&ChangeListener::changeListenerCallback, this);
}
//==============================================================================
ChangeBroadcaster::ChangeBroadcasterCallback::ChangeBroadcasterCallback()
: owner (0)
{
}
void ChangeBroadcaster::ChangeBroadcasterCallback::handleAsyncUpdate()
{
jassert (owner != 0);
owner->callListeners();
}


+ 12
- 5
src/events/juce_ChangeBroadcaster.h View File

@@ -28,7 +28,7 @@
#include "juce_ChangeListener.h"
#include "juce_ListenerList.h"
#include "../core/juce_Atomic.h"
#include "juce_AsyncUpdater.h"
//==============================================================================
@@ -88,13 +88,20 @@ public:
private:
//==============================================================================
class ChangeBroadcasterMessage;
friend class ChangeBroadcasterMessage;
class ChangeBroadcasterCallback : public AsyncUpdater
{
public:
ChangeBroadcasterCallback();
void handleAsyncUpdate();
Atomic<ChangeBroadcasterMessage*> pendingMessage;
ChangeBroadcaster* owner;
};
friend class ChangeBroadcasterCallback;
ChangeBroadcasterCallback callback;
ListenerList <ChangeListener> changeListeners;
void invalidatePendingMessage();
void callListeners();
ChangeBroadcaster (const ChangeBroadcaster&);
ChangeBroadcaster& operator= (const ChangeBroadcaster&);


+ 4
- 2
src/events/juce_Message.cpp View File

@@ -35,7 +35,8 @@ Message::Message() throw()
: intParameter1 (0),
intParameter2 (0),
intParameter3 (0),
pointerParameter (0)
pointerParameter (0),
messageRecipient (0)
{
}
@@ -46,7 +47,8 @@ Message::Message (const int intParameter1_,
: intParameter1 (intParameter1_),
intParameter2 (intParameter2_),
intParameter3 (intParameter3_),
pointerParameter (pointerParameter_)
pointerParameter (pointerParameter_),
messageRecipient (0)
{
}


+ 27
- 39
src/events/juce_MessageManager.cpp View File

@@ -28,7 +28,7 @@
BEGIN_JUCE_NAMESPACE
#include "juce_MessageManager.h"
#include "juce_ActionListenerList.h"
#include "juce_ActionBroadcaster.h"
#include "../application/juce_Application.h"
#include "../gui/components/juce_Component.h"
#include "../threads/juce_Thread.h"
@@ -56,7 +56,7 @@ MessageManager::MessageManager() throw()
MessageManager::~MessageManager() throw()
{
broadcastListeners = 0;
broadcaster = 0;
doPlatformSpecificShutdown();
@@ -88,48 +88,41 @@ void MessageManager::postMessageToQueue (Message* const message)
}
//==============================================================================
CallbackMessage::CallbackMessage() throw() {}
CallbackMessage::~CallbackMessage() {}
CallbackMessage::CallbackMessage() throw() {}
CallbackMessage::~CallbackMessage() {}
void CallbackMessage::post()
{
if (MessageManager::instance != 0)
MessageManager::instance->postCallbackMessage (this);
}
void MessageManager::postCallbackMessage (Message* const message)
{
message->messageRecipient = 0;
postMessageToQueue (message);
MessageManager::instance->postMessageToQueue (this);
}
//==============================================================================
// not for public use..
void MessageManager::deliverMessage (Message* const message)
{
const ScopedPointer <Message> messageDeleter (message);
MessageListener* const recipient = message->messageRecipient;
JUCE_TRY
{
if (messageListeners.contains (recipient))
{
recipient->handleMessage (*message);
}
else if (recipient == 0)
const ScopedPointer <Message> messageDeleter (message);
MessageListener* const recipient = message->messageRecipient;
if (recipient == 0)
{
if (message->intParameter1 == quitMessageId)
CallbackMessage* const callbackMessage = dynamic_cast <CallbackMessage*> (message);
if (callbackMessage != 0)
{
quitMessageReceived = true;
callbackMessage->messageCallback();
}
else
else if (message->intParameter1 == quitMessageId)
{
CallbackMessage* const cm = dynamic_cast <CallbackMessage*> (message);
if (cm != 0)
cm->messageCallback();
quitMessageReceived = true;
}
}
else if (messageListeners.contains (recipient))
{
recipient->handleMessage (*message);
}
}
JUCE_CATCH_EXCEPTION
}
@@ -145,10 +138,7 @@ void MessageManager::runDispatchLoop()
void MessageManager::stopDispatchLoop()
{
Message* const m = new Message (quitMessageId, 0, 0, 0);
m->messageRecipient = 0;
postMessageToQueue (m);
postMessageToQueue (new Message (quitMessageId, 0, 0, 0));
quitMessagePosted = true;
}
@@ -182,22 +172,22 @@ bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor)
//==============================================================================
void MessageManager::deliverBroadcastMessage (const String& value)
{
if (broadcastListeners != 0)
broadcastListeners->sendActionMessage (value);
if (broadcaster != 0)
broadcaster->sendActionMessage (value);
}
void MessageManager::registerBroadcastListener (ActionListener* const listener)
{
if (broadcastListeners == 0)
broadcastListeners = new ActionListenerList();
if (broadcaster == 0)
broadcaster = new ActionBroadcaster();
broadcastListeners->addActionListener (listener);
broadcaster->addActionListener (listener);
}
void MessageManager::deregisterBroadcastListener (ActionListener* const listener)
{
if (broadcastListeners != 0)
broadcastListeners->removeActionListener (listener);
if (broadcaster != 0)
broadcaster->removeActionListener (listener);
}
//==============================================================================
@@ -239,7 +229,6 @@ class MessageManagerLock::SharedEvents : public ReferenceCountedObject
{
public:
SharedEvents() {}
~SharedEvents() {}
/* This class just holds a couple of events to communicate between the BlockingMessage
and the MessageManagerLock. Because both of these objects may be deleted at any time,
@@ -255,7 +244,6 @@ class MessageManagerLock::BlockingMessage : public CallbackMessage
{
public:
BlockingMessage (MessageManagerLock::SharedEvents* const events_) : events (events_) {}
~BlockingMessage() throw() {}
void messageCallback()
{


+ 2
- 4
src/events/juce_MessageManager.h View File

@@ -31,7 +31,7 @@
#include "../containers/juce_ScopedPointer.h"
#include "../threads/juce_Thread.h"
#include "../threads/juce_ThreadPool.h"
#include "juce_ActionListenerList.h"
#include "juce_ActionBroadcaster.h"
#include "juce_CallbackMessage.h"
class Component;
class MessageManagerLock;
@@ -175,7 +175,7 @@ private:
static MessageManager* instance;
SortedSet <const MessageListener*> messageListeners;
ScopedPointer <ActionListenerList> broadcastListeners;
ScopedPointer <ActionBroadcaster> broadcaster;
friend class JUCEApplication;
bool quitMessagePosted, quitMessageReceived;
@@ -184,7 +184,6 @@ private:
static void* exitModalLoopCallback (void*);
void postMessageToQueue (Message* message);
void postCallbackMessage (Message* message);
static void doPlatformSpecificInitialisation();
static void doPlatformSpecificShutdown();
@@ -317,5 +316,4 @@ private:
};
#endif // __JUCE_MESSAGEMANAGER_JUCEHEADER__

+ 0
- 1
src/gui/components/windows/juce_ComponentPeer.h View File

@@ -29,7 +29,6 @@
#include "../juce_Component.h"
#include "../mouse/juce_MouseCursor.h"
#include "../keyboard/juce_TextInputTarget.h"
#include "../../../events/juce_MessageListener.h"
#include "../../../text/juce_StringArray.h"
#include "../../graphics/geometry/juce_RectangleList.h"


+ 0
- 3
src/juce_app_includes.h View File

@@ -230,9 +230,6 @@
#ifndef __JUCE_ACTIONLISTENER_JUCEHEADER__
#include "events/juce_ActionListener.h"
#endif
#ifndef __JUCE_ACTIONLISTENERLIST_JUCEHEADER__
#include "events/juce_ActionListenerList.h"
#endif
#ifndef __JUCE_ASYNCUPDATER_JUCEHEADER__
#include "events/juce_AsyncUpdater.h"
#endif


+ 1
- 1
src/text/juce_String.cpp View File

@@ -388,7 +388,7 @@ namespace NumberToStringConverters
else
{
#if JUCE_WINDOWS
#if JUCE_VC8_OR_EARLIER || JUCE_MINGW
#if JUCE_VC7_OR_EARLIER || JUCE_MINGW
len = _snwprintf (buffer, numChars, L"%.9g", n);
#else
len = _snwprintf_s (buffer, numChars, _TRUNCATE, L"%.9g", n);


Loading…
Cancel
Save