| @@ -35,94 +35,92 @@ package.linkflags = { "static-runtime" } | |||
| package.files = { matchfiles ( | |||
| "../../src/*.h", | |||
| "../../src/juce_core/basics/*.cpp", | |||
| "../../src/juce_core/basics/*.h", | |||
| "../../src/juce_core/cryptography/*.cpp", | |||
| "../../src/juce_core/cryptography/*.h", | |||
| "../../src/juce_core/containers/*.cpp", | |||
| "../../src/juce_core/containers/*.h", | |||
| "../../src/juce_core/io/*.cpp", | |||
| "../../src/juce_core/io/*.h", | |||
| "../../src/juce_core/io/files/*.cpp", | |||
| "../../src/juce_core/io/files/*.h", | |||
| "../../src/juce_core/io/network/*.cpp", | |||
| "../../src/juce_core/io/network/*.h", | |||
| "../../src/juce_core/io/streams/*.cpp", | |||
| "../../src/juce_core/io/streams/*.h", | |||
| "../../src/juce_core/misc/*.cpp", | |||
| "../../src/juce_core/misc/*.h", | |||
| "../../src/juce_core/text/*.cpp", | |||
| "../../src/juce_core/text/*.h", | |||
| "../../src/juce_core/threads/*.cpp", | |||
| "../../src/juce_core/threads/*.h", | |||
| "../../src/juce_appframework/application/*.cpp", | |||
| "../../src/juce_appframework/application/*.h", | |||
| "../../src/juce_appframework/audio/*.cpp", | |||
| "../../src/juce_appframework/audio/*.h", | |||
| "../../src/juce_appframework/audio/dsp/*.cpp", | |||
| "../../src/juce_appframework/audio/dsp/*.h", | |||
| "../../src/juce_appframework/audio/midi/*.cpp", | |||
| "../../src/juce_appframework/audio/midi/*.h", | |||
| "../../src/juce_appframework/audio/processors/*.cpp", | |||
| "../../src/juce_appframework/audio/processors/*.h", | |||
| "../../src/juce_appframework/audio/plugins/*.cpp", | |||
| "../../src/juce_appframework/audio/plugins/*.h", | |||
| "../../src/juce_appframework/audio/plugins/formats/*.cpp", | |||
| "../../src/juce_appframework/audio/plugins/formats/*.h", | |||
| "../../src/juce_appframework/audio/audio_file_formats/*.cpp", | |||
| "../../src/juce_appframework/audio/audio_file_formats/*.h", | |||
| "../../src/juce_appframework/audio/audio_sources/*.cpp", | |||
| "../../src/juce_appframework/audio/audio_sources/*.h", | |||
| "../../src/juce_appframework/audio/devices/*.cpp", | |||
| "../../src/juce_appframework/audio/devices/*.h", | |||
| "../../src/juce_appframework/audio/synthesisers/*.cpp", | |||
| "../../src/juce_appframework/audio/synthesisers/*.h", | |||
| "../../src/juce_appframework/documents/*.cpp", | |||
| "../../src/juce_appframework/documents/*.h", | |||
| "../../src/juce_appframework/events/*.cpp", | |||
| "../../src/juce_appframework/events/*.h", | |||
| "../../src/juce_appframework/gui/graphics/brushes/*.cpp", | |||
| "../../src/juce_appframework/gui/graphics/brushes/*.h", | |||
| "../../src/juce_appframework/gui/graphics/colour/*.cpp", | |||
| "../../src/juce_appframework/gui/graphics/colour/*.h", | |||
| "../../src/juce_appframework/gui/graphics/contexts/*.cpp", | |||
| "../../src/juce_appframework/gui/graphics/contexts/*.h", | |||
| "../../src/juce_appframework/gui/graphics/drawables/*.cpp", | |||
| "../../src/juce_appframework/gui/graphics/drawables/*.h", | |||
| "../../src/juce_appframework/gui/graphics/effects/*.cpp", | |||
| "../../src/juce_appframework/gui/graphics/effects/*.h", | |||
| "../../src/juce_appframework/gui/graphics/fonts/*.cpp", | |||
| "../../src/juce_appframework/gui/graphics/fonts/*.h", | |||
| "../../src/juce_appframework/gui/graphics/geometry/*.cpp", | |||
| "../../src/juce_appframework/gui/graphics/geometry/*.h", | |||
| "../../src/juce_appframework/gui/graphics/imaging/*.cpp", | |||
| "../../src/juce_appframework/gui/graphics/imaging/*.h", | |||
| "../../src/juce_appframework/gui/graphics/imaging/image_file_formats/*.cpp", | |||
| "../../src/juce_appframework/gui/graphics/imaging/image_file_formats/*.h", | |||
| "../../src/juce_appframework/gui/components/*.cpp", | |||
| "../../src/juce_appframework/gui/components/*.h", | |||
| "../../src/juce_appframework/gui/components/buttons/*.cpp", | |||
| "../../src/juce_appframework/gui/components/buttons/*.h", | |||
| "../../src/juce_appframework/gui/components/controls/*.cpp", | |||
| "../../src/juce_appframework/gui/components/controls/*.h", | |||
| "../../src/juce_appframework/gui/components/filebrowser/*.cpp", | |||
| "../../src/juce_appframework/gui/components/filebrowser/*.h", | |||
| "../../src/juce_appframework/gui/components/keyboard/*.cpp", | |||
| "../../src/juce_appframework/gui/components/keyboard/*.h", | |||
| "../../src/juce_appframework/gui/components/layout/*.cpp", | |||
| "../../src/juce_appframework/gui/components/layout/*.h", | |||
| "../../src/juce_appframework/gui/components/lookandfeel/*.cpp", | |||
| "../../src/juce_appframework/gui/components/lookandfeel/*.h", | |||
| "../../src/juce_appframework/gui/components/menus/*.cpp", | |||
| "../../src/juce_appframework/gui/components/menus/*.h", | |||
| "../../src/juce_appframework/gui/components/mouse/*.cpp", | |||
| "../../src/juce_appframework/gui/components/mouse/*.h", | |||
| "../../src/juce_appframework/gui/components/properties/*.cpp", | |||
| "../../src/juce_appframework/gui/components/properties/*.h", | |||
| "../../src/juce_appframework/gui/components/special/*.cpp", | |||
| "../../src/juce_appframework/gui/components/special/*.h", | |||
| "../../src/juce_appframework/gui/components/windows/*.cpp", | |||
| "../../src/juce_appframework/gui/components/windows/*.h", | |||
| "../../src/core/*.cpp", | |||
| "../../src/core/*.h", | |||
| "../../src/cryptography/*.cpp", | |||
| "../../src/cryptography/*.h", | |||
| "../../src/containers/*.cpp", | |||
| "../../src/containers/*.h", | |||
| "../../src/io/*.cpp", | |||
| "../../src/io/*.h", | |||
| "../../src/io/files/*.cpp", | |||
| "../../src/io/files/*.h", | |||
| "../../src/io/network/*.cpp", | |||
| "../../src/io/network/*.h", | |||
| "../../src/io/streams/*.cpp", | |||
| "../../src/io/streams/*.h", | |||
| "../../src/text/*.cpp", | |||
| "../../src/text/*.h", | |||
| "../../src/threads/*.cpp", | |||
| "../../src/threads/*.h", | |||
| "../../src/application/*.cpp", | |||
| "../../src/application/*.h", | |||
| "../../src/audio/*.cpp", | |||
| "../../src/audio/*.h", | |||
| "../../src/audio/dsp/*.cpp", | |||
| "../../src/audio/dsp/*.h", | |||
| "../../src/audio/midi/*.cpp", | |||
| "../../src/audio/midi/*.h", | |||
| "../../src/audio/processors/*.cpp", | |||
| "../../src/audio/processors/*.h", | |||
| "../../src/audio/plugins/*.cpp", | |||
| "../../src/audio/plugins/*.h", | |||
| "../../src/audio/plugins/formats/*.cpp", | |||
| "../../src/audio/plugins/formats/*.h", | |||
| "../../src/audio/audio_file_formats/*.cpp", | |||
| "../../src/audio/audio_file_formats/*.h", | |||
| "../../src/audio/audio_sources/*.cpp", | |||
| "../../src/audio/audio_sources/*.h", | |||
| "../../src/audio/devices/*.cpp", | |||
| "../../src/audio/devices/*.h", | |||
| "../../src/audio/synthesisers/*.cpp", | |||
| "../../src/audio/synthesisers/*.h", | |||
| "../../src/events/*.cpp", | |||
| "../../src/events/*.h", | |||
| "../../src/utilities/*.cpp", | |||
| "../../src/utilities/*.h", | |||
| "../../src/gui/graphics/brushes/*.cpp", | |||
| "../../src/gui/graphics/brushes/*.h", | |||
| "../../src/gui/graphics/colour/*.cpp", | |||
| "../../src/gui/graphics/colour/*.h", | |||
| "../../src/gui/graphics/contexts/*.cpp", | |||
| "../../src/gui/graphics/contexts/*.h", | |||
| "../../src/gui/graphics/drawables/*.cpp", | |||
| "../../src/gui/graphics/drawables/*.h", | |||
| "../../src/gui/graphics/effects/*.cpp", | |||
| "../../src/gui/graphics/effects/*.h", | |||
| "../../src/gui/graphics/fonts/*.cpp", | |||
| "../../src/gui/graphics/fonts/*.h", | |||
| "../../src/gui/graphics/geometry/*.cpp", | |||
| "../../src/gui/graphics/geometry/*.h", | |||
| "../../src/gui/graphics/imaging/*.cpp", | |||
| "../../src/gui/graphics/imaging/*.h", | |||
| "../../src/gui/graphics/imaging/image_file_formats/*.cpp", | |||
| "../../src/gui/graphics/imaging/image_file_formats/*.h", | |||
| "../../src/gui/components/*.cpp", | |||
| "../../src/gui/components/*.h", | |||
| "../../src/gui/components/buttons/*.cpp", | |||
| "../../src/gui/components/buttons/*.h", | |||
| "../../src/gui/components/controls/*.cpp", | |||
| "../../src/gui/components/controls/*.h", | |||
| "../../src/gui/components/filebrowser/*.cpp", | |||
| "../../src/gui/components/filebrowser/*.h", | |||
| "../../src/gui/components/keyboard/*.cpp", | |||
| "../../src/gui/components/keyboard/*.h", | |||
| "../../src/gui/components/layout/*.cpp", | |||
| "../../src/gui/components/layout/*.h", | |||
| "../../src/gui/components/lookandfeel/*.cpp", | |||
| "../../src/gui/components/lookandfeel/*.h", | |||
| "../../src/gui/components/menus/*.cpp", | |||
| "../../src/gui/components/menus/*.h", | |||
| "../../src/gui/components/mouse/*.cpp", | |||
| "../../src/gui/components/mouse/*.h", | |||
| "../../src/gui/components/properties/*.cpp", | |||
| "../../src/gui/components/properties/*.h", | |||
| "../../src/gui/components/special/*.cpp", | |||
| "../../src/gui/components/special/*.h", | |||
| "../../src/gui/components/windows/*.cpp", | |||
| "../../src/gui/components/windows/*.h", | |||
| "../../src/native/linux/*.h", | |||
| "../../src/native/linux/*.cpp", | |||
| "../../src/native/juce_linux_NativeCode.cpp" | |||
| @@ -40,22 +40,22 @@ endif | |||
| OBJECTS := \ | |||
| $(OBJDIR)/ApplicationStartup.o \ | |||
| $(OBJDIR)/BinaryData.o \ | |||
| $(OBJDIR)/juce_LibrarySource.o \ | |||
| $(OBJDIR)/MainDemoWindow.o \ | |||
| $(OBJDIR)/BinaryData.o \ | |||
| $(OBJDIR)/AudioDemo.o \ | |||
| $(OBJDIR)/CameraDemo.o \ | |||
| $(OBJDIR)/DragAndDropDemo.o \ | |||
| $(OBJDIR)/FontsAndTextDemo.o \ | |||
| $(OBJDIR)/InterprocessCommsDemo.o \ | |||
| $(OBJDIR)/OpenGLDemo.o \ | |||
| $(OBJDIR)/PathsAndTransformsDemo.o \ | |||
| $(OBJDIR)/WidgetsDemo.o \ | |||
| $(OBJDIR)/ThreadingDemo.o \ | |||
| $(OBJDIR)/TreeViewDemo.o \ | |||
| $(OBJDIR)/QuickTimeDemo.o \ | |||
| $(OBJDIR)/TableDemo.o \ | |||
| $(OBJDIR)/OpenGLDemo.o \ | |||
| $(OBJDIR)/FontsAndTextDemo.o \ | |||
| $(OBJDIR)/InterprocessCommsDemo.o \ | |||
| $(OBJDIR)/DragAndDropDemo.o \ | |||
| $(OBJDIR)/CameraDemo.o \ | |||
| $(OBJDIR)/AudioDemo.o \ | |||
| $(OBJDIR)/ThreadingDemo.o \ | |||
| $(OBJDIR)/TreeViewDemo.o \ | |||
| $(OBJDIR)/WebBrowserDemo.o \ | |||
| $(OBJDIR)/WidgetsDemo.o \ | |||
| MKDIR_TYPE := msdos | |||
| CMD := $(subst \,\\,$(ComSpec)$(COMSPEC)) | |||
| @@ -102,11 +102,6 @@ $(OBJDIR)/ApplicationStartup.o: ../../src/ApplicationStartup.cpp | |||
| @echo $(notdir $<) | |||
| @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" | |||
| $(OBJDIR)/BinaryData.o: ../../src/BinaryData.cpp | |||
| -@$(CMD_MKOBJDIR) | |||
| @echo $(notdir $<) | |||
| @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" | |||
| $(OBJDIR)/juce_LibrarySource.o: ../../src/juce_LibrarySource.cpp | |||
| -@$(CMD_MKOBJDIR) | |||
| @echo $(notdir $<) | |||
| @@ -117,32 +112,32 @@ $(OBJDIR)/MainDemoWindow.o: ../../src/MainDemoWindow.cpp | |||
| @echo $(notdir $<) | |||
| @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" | |||
| $(OBJDIR)/PathsAndTransformsDemo.o: ../../src/demos/PathsAndTransformsDemo.cpp | |||
| $(OBJDIR)/BinaryData.o: ../../src/BinaryData.cpp | |||
| -@$(CMD_MKOBJDIR) | |||
| @echo $(notdir $<) | |||
| @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" | |||
| $(OBJDIR)/WidgetsDemo.o: ../../src/demos/WidgetsDemo.cpp | |||
| $(OBJDIR)/AudioDemo.o: ../../src/demos/AudioDemo.cpp | |||
| -@$(CMD_MKOBJDIR) | |||
| @echo $(notdir $<) | |||
| @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" | |||
| $(OBJDIR)/ThreadingDemo.o: ../../src/demos/ThreadingDemo.cpp | |||
| $(OBJDIR)/CameraDemo.o: ../../src/demos/CameraDemo.cpp | |||
| -@$(CMD_MKOBJDIR) | |||
| @echo $(notdir $<) | |||
| @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" | |||
| $(OBJDIR)/TreeViewDemo.o: ../../src/demos/TreeViewDemo.cpp | |||
| $(OBJDIR)/DragAndDropDemo.o: ../../src/demos/DragAndDropDemo.cpp | |||
| -@$(CMD_MKOBJDIR) | |||
| @echo $(notdir $<) | |||
| @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" | |||
| $(OBJDIR)/QuickTimeDemo.o: ../../src/demos/QuickTimeDemo.cpp | |||
| $(OBJDIR)/FontsAndTextDemo.o: ../../src/demos/FontsAndTextDemo.cpp | |||
| -@$(CMD_MKOBJDIR) | |||
| @echo $(notdir $<) | |||
| @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" | |||
| $(OBJDIR)/TableDemo.o: ../../src/demos/TableDemo.cpp | |||
| $(OBJDIR)/InterprocessCommsDemo.o: ../../src/demos/InterprocessCommsDemo.cpp | |||
| -@$(CMD_MKOBJDIR) | |||
| @echo $(notdir $<) | |||
| @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" | |||
| @@ -152,27 +147,27 @@ $(OBJDIR)/OpenGLDemo.o: ../../src/demos/OpenGLDemo.cpp | |||
| @echo $(notdir $<) | |||
| @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" | |||
| $(OBJDIR)/FontsAndTextDemo.o: ../../src/demos/FontsAndTextDemo.cpp | |||
| $(OBJDIR)/PathsAndTransformsDemo.o: ../../src/demos/PathsAndTransformsDemo.cpp | |||
| -@$(CMD_MKOBJDIR) | |||
| @echo $(notdir $<) | |||
| @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" | |||
| $(OBJDIR)/InterprocessCommsDemo.o: ../../src/demos/InterprocessCommsDemo.cpp | |||
| $(OBJDIR)/QuickTimeDemo.o: ../../src/demos/QuickTimeDemo.cpp | |||
| -@$(CMD_MKOBJDIR) | |||
| @echo $(notdir $<) | |||
| @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" | |||
| $(OBJDIR)/DragAndDropDemo.o: ../../src/demos/DragAndDropDemo.cpp | |||
| $(OBJDIR)/TableDemo.o: ../../src/demos/TableDemo.cpp | |||
| -@$(CMD_MKOBJDIR) | |||
| @echo $(notdir $<) | |||
| @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" | |||
| $(OBJDIR)/CameraDemo.o: ../../src/demos/CameraDemo.cpp | |||
| $(OBJDIR)/ThreadingDemo.o: ../../src/demos/ThreadingDemo.cpp | |||
| -@$(CMD_MKOBJDIR) | |||
| @echo $(notdir $<) | |||
| @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" | |||
| $(OBJDIR)/AudioDemo.o: ../../src/demos/AudioDemo.cpp | |||
| $(OBJDIR)/TreeViewDemo.o: ../../src/demos/TreeViewDemo.cpp | |||
| -@$(CMD_MKOBJDIR) | |||
| @echo $(notdir $<) | |||
| @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" | |||
| @@ -182,5 +177,10 @@ $(OBJDIR)/WebBrowserDemo.o: ../../src/demos/WebBrowserDemo.cpp | |||
| @echo $(notdir $<) | |||
| @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" | |||
| $(OBJDIR)/WidgetsDemo.o: ../../src/demos/WidgetsDemo.cpp | |||
| -@$(CMD_MKOBJDIR) | |||
| @echo $(notdir $<) | |||
| @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" | |||
| -include $(OBJECTS:%.o=%.d) | |||
| @@ -34,7 +34,7 @@ | |||
| //============================================================================== | |||
| // (this includes things that need defining outside of the JUCE namespace) | |||
| #include "src/juce_core/basics/juce_StandardHeader.h" | |||
| #include "src/core/juce_StandardHeader.h" | |||
| BEGIN_JUCE_NAMESPACE | |||
| @@ -0,0 +1,348 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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" | |||
| #if JUCE_MSVC | |||
| #pragma warning (push) | |||
| #pragma warning (disable: 4245 4514 4100) | |||
| #include <crtdbg.h> | |||
| #pragma warning (pop) | |||
| #endif | |||
| BEGIN_JUCE_NAMESPACE | |||
| #include "juce_Application.h" | |||
| #include "../utilities/juce_DeletedAtShutdown.h" | |||
| #include "../events/juce_MessageManager.h" | |||
| #include "../gui/graphics/contexts/juce_Graphics.h" | |||
| #include "../gui/components/windows/juce_AlertWindow.h" | |||
| #include "../gui/components/buttons/juce_TextButton.h" | |||
| #include "../gui/components/lookandfeel/juce_LookAndFeel.h" | |||
| #include "../core/juce_Time.h" | |||
| #include "../core/juce_Initialisation.h" | |||
| #include "../threads/juce_Process.h" | |||
| #include "../threads/juce_InterProcessLock.h" | |||
| #include "../core/juce_PlatformUtilities.h" | |||
| void juce_setCurrentThreadName (const String& name) throw(); | |||
| static JUCEApplication* appInstance = 0; | |||
| //============================================================================== | |||
| JUCEApplication::JUCEApplication() | |||
| : appReturnValue (0), | |||
| stillInitialising (true) | |||
| { | |||
| } | |||
| JUCEApplication::~JUCEApplication() | |||
| { | |||
| } | |||
| JUCEApplication* JUCEApplication::getInstance() throw() | |||
| { | |||
| return appInstance; | |||
| } | |||
| bool JUCEApplication::isInitialising() const throw() | |||
| { | |||
| return stillInitialising; | |||
| } | |||
| //============================================================================== | |||
| const String JUCEApplication::getApplicationVersion() | |||
| { | |||
| return String::empty; | |||
| } | |||
| bool JUCEApplication::moreThanOneInstanceAllowed() | |||
| { | |||
| return true; | |||
| } | |||
| void JUCEApplication::anotherInstanceStarted (const String&) | |||
| { | |||
| } | |||
| void JUCEApplication::systemRequestedQuit() | |||
| { | |||
| quit(); | |||
| } | |||
| void JUCEApplication::quit() | |||
| { | |||
| MessageManager::getInstance()->stopDispatchLoop(); | |||
| } | |||
| void JUCEApplication::setApplicationReturnValue (const int newReturnValue) throw() | |||
| { | |||
| appReturnValue = newReturnValue; | |||
| } | |||
| //============================================================================== | |||
| void JUCEApplication::unhandledException (const std::exception*, | |||
| const String&, | |||
| const int) | |||
| { | |||
| jassertfalse | |||
| } | |||
| void JUCEApplication::sendUnhandledException (const std::exception* const e, | |||
| const char* const sourceFile, | |||
| const int lineNumber) | |||
| { | |||
| if (appInstance != 0) | |||
| appInstance->unhandledException (e, sourceFile, lineNumber); | |||
| } | |||
| //============================================================================== | |||
| ApplicationCommandTarget* JUCEApplication::getNextCommandTarget() | |||
| { | |||
| return 0; | |||
| } | |||
| void JUCEApplication::getAllCommands (Array <CommandID>& commands) | |||
| { | |||
| commands.add (StandardApplicationCommandIDs::quit); | |||
| } | |||
| void JUCEApplication::getCommandInfo (const CommandID commandID, ApplicationCommandInfo& result) | |||
| { | |||
| if (commandID == StandardApplicationCommandIDs::quit) | |||
| { | |||
| result.setInfo ("Quit", | |||
| "Quits the application", | |||
| "Application", | |||
| 0); | |||
| result.defaultKeypresses.add (KeyPress (T('q'), ModifierKeys::commandModifier, 0)); | |||
| } | |||
| } | |||
| bool JUCEApplication::perform (const InvocationInfo& info) | |||
| { | |||
| if (info.commandID == StandardApplicationCommandIDs::quit) | |||
| { | |||
| systemRequestedQuit(); | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| //============================================================================== | |||
| int JUCEApplication::main (String& commandLine, JUCEApplication* const app) | |||
| { | |||
| jassert (appInstance == 0); | |||
| appInstance = app; | |||
| app->commandLineParameters = commandLine.trim(); | |||
| commandLine = String::empty; | |||
| initialiseJuce_GUI(); | |||
| InterProcessLock* appLock = 0; | |||
| if (! app->moreThanOneInstanceAllowed()) | |||
| { | |||
| appLock = new InterProcessLock ("juceAppLock_" + app->getApplicationName()); | |||
| if (! appLock->enter(0)) | |||
| { | |||
| MessageManager::broadcastMessage (app->getApplicationName() + "/" + app->commandLineParameters); | |||
| delete appInstance; | |||
| appInstance = 0; | |||
| DBG ("Another instance is running - quitting..."); | |||
| return 0; | |||
| } | |||
| } | |||
| JUCE_TRY | |||
| { | |||
| juce_setCurrentThreadName ("Juce Message Thread"); | |||
| // let the app do its setting-up.. | |||
| app->initialise (app->commandLineParameters); | |||
| // register for broadcast new app messages | |||
| MessageManager::getInstance()->registerBroadcastListener (app); | |||
| app->stillInitialising = false; | |||
| // now loop until a quit message is received.. | |||
| MessageManager::getInstance()->runDispatchLoop(); | |||
| MessageManager::getInstance()->deregisterBroadcastListener (app); | |||
| if (appLock != 0) | |||
| { | |||
| appLock->exit(); | |||
| delete appLock; | |||
| } | |||
| } | |||
| #if JUCE_CATCH_UNHANDLED_EXCEPTIONS | |||
| catch (const std::exception& e) | |||
| { | |||
| app->unhandledException (&e, __FILE__, __LINE__); | |||
| } | |||
| catch (...) | |||
| { | |||
| app->unhandledException (0, __FILE__, __LINE__); | |||
| } | |||
| #endif | |||
| return shutdownAppAndClearUp(); | |||
| } | |||
| int JUCEApplication::shutdownAppAndClearUp() | |||
| { | |||
| jassert (appInstance != 0); | |||
| JUCEApplication* const app = appInstance; | |||
| int returnValue = 0; | |||
| static bool reentrancyCheck = false; | |||
| if (! reentrancyCheck) | |||
| { | |||
| reentrancyCheck = true; | |||
| JUCE_TRY | |||
| { | |||
| // give the app a chance to clean up.. | |||
| app->shutdown(); | |||
| } | |||
| #if JUCE_CATCH_UNHANDLED_EXCEPTIONS | |||
| catch (const std::exception& e) | |||
| { | |||
| app->unhandledException (&e, __FILE__, __LINE__); | |||
| } | |||
| catch (...) | |||
| { | |||
| app->unhandledException (0, __FILE__, __LINE__); | |||
| } | |||
| #endif | |||
| JUCE_TRY | |||
| { | |||
| shutdownJuce_GUI(); | |||
| returnValue = app->getApplicationReturnValue(); | |||
| appInstance = 0; | |||
| delete app; | |||
| } | |||
| JUCE_CATCH_ALL_ASSERT | |||
| reentrancyCheck = false; | |||
| } | |||
| return returnValue; | |||
| } | |||
| int JUCEApplication::main (int argc, char* argv[], | |||
| JUCEApplication* const newApp) | |||
| { | |||
| #if JUCE_MAC | |||
| const ScopedAutoReleasePool pool; | |||
| #endif | |||
| String cmd; | |||
| for (int i = 1; i < argc; ++i) | |||
| cmd << String::fromUTF8 ((const uint8*) argv[i]) << T(' '); | |||
| return JUCEApplication::main (cmd, newApp); | |||
| } | |||
| void JUCEApplication::actionListenerCallback (const String& message) | |||
| { | |||
| if (message.startsWith (getApplicationName() + "/")) | |||
| anotherInstanceStarted (message.substring (getApplicationName().length() + 1)); | |||
| } | |||
| //============================================================================== | |||
| static bool juceInitialisedGUI = false; | |||
| void JUCE_PUBLIC_FUNCTION initialiseJuce_GUI() | |||
| { | |||
| if (! juceInitialisedGUI) | |||
| { | |||
| #if JUCE_MAC | |||
| const ScopedAutoReleasePool pool; | |||
| #endif | |||
| juceInitialisedGUI = true; | |||
| initialiseJuce_NonGUI(); | |||
| MessageManager::getInstance(); | |||
| LookAndFeel::setDefaultLookAndFeel (0); | |||
| #if JUCE_WIN32 && JUCE_DEBUG | |||
| // This section is just for catching people who mess up their project settings and | |||
| // turn RTTI off.. | |||
| try | |||
| { | |||
| TextButton tb (String::empty); | |||
| Component* c = &tb; | |||
| // Got an exception here? Then TURN ON RTTI in your compiler settings!! | |||
| c = dynamic_cast <Button*> (c); | |||
| } | |||
| catch (...) | |||
| { | |||
| // Ended up here? If so, TURN ON RTTI in your compiler settings!! And if you | |||
| // got as far as this catch statement, then why haven't you got exception catching | |||
| // turned on in the debugger??? | |||
| jassertfalse | |||
| } | |||
| #endif | |||
| } | |||
| } | |||
| void JUCE_PUBLIC_FUNCTION shutdownJuce_GUI() | |||
| { | |||
| if (juceInitialisedGUI) | |||
| { | |||
| #if JUCE_MAC | |||
| const ScopedAutoReleasePool pool; | |||
| #endif | |||
| { | |||
| DeletedAtShutdown::deleteAll(); | |||
| LookAndFeel::clearDefaultLookAndFeel(); | |||
| } | |||
| delete MessageManager::getInstance(); | |||
| shutdownJuce_NonGUI(); | |||
| juceInitialisedGUI = false; | |||
| } | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,73 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_ApplicationCommandInfo.h" | |||
| //============================================================================== | |||
| ApplicationCommandInfo::ApplicationCommandInfo (const CommandID commandID_) throw() | |||
| : commandID (commandID_), | |||
| flags (0) | |||
| { | |||
| } | |||
| void ApplicationCommandInfo::setInfo (const String& shortName_, | |||
| const String& description_, | |||
| const String& categoryName_, | |||
| const int flags_) throw() | |||
| { | |||
| shortName = shortName_; | |||
| description = description_; | |||
| categoryName = categoryName_; | |||
| flags = flags_; | |||
| } | |||
| void ApplicationCommandInfo::setActive (const bool b) throw() | |||
| { | |||
| if (b) | |||
| flags &= ~isDisabled; | |||
| else | |||
| flags |= isDisabled; | |||
| } | |||
| void ApplicationCommandInfo::setTicked (const bool b) throw() | |||
| { | |||
| if (b) | |||
| flags |= isTicked; | |||
| else | |||
| flags &= ~isTicked; | |||
| } | |||
| void ApplicationCommandInfo::addDefaultKeypress (const int keyCode, const ModifierKeys& modifiers) throw() | |||
| { | |||
| defaultKeypresses.add (KeyPress (keyCode, modifiers, 0)); | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,196 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_APPLICATIONCOMMANDINFO_JUCEHEADER__ | |||
| #define __JUCE_APPLICATIONCOMMANDINFO_JUCEHEADER__ | |||
| #include "../text/juce_String.h" | |||
| #include "../containers/juce_Array.h" | |||
| #include "../gui/components/keyboard/juce_KeyPress.h" | |||
| #include "juce_ApplicationCommandID.h" | |||
| //============================================================================== | |||
| /** | |||
| Holds information describing an application command. | |||
| This object is used to pass information about a particular command, such as its | |||
| name, description and other usage flags. | |||
| When an ApplicationCommandTarget is asked to provide information about the commands | |||
| it can perform, this is the structure gets filled-in to describe each one. | |||
| @see ApplicationCommandTarget, ApplicationCommandTarget::getCommandInfo(), | |||
| ApplicationCommandManager | |||
| */ | |||
| struct JUCE_API ApplicationCommandInfo | |||
| { | |||
| //============================================================================== | |||
| ApplicationCommandInfo (const CommandID commandID) throw(); | |||
| //============================================================================== | |||
| /** Sets a number of the structures values at once. | |||
| The meanings of each of the parameters is described below, in the appropriate | |||
| member variable's description. | |||
| */ | |||
| void setInfo (const String& shortName, | |||
| const String& description, | |||
| const String& categoryName, | |||
| const int flags) throw(); | |||
| /** An easy way to set or remove the isDisabled bit in the structure's flags field. | |||
| If isActive is true, the flags member has the isDisabled bit cleared; if isActive | |||
| is false, the bit is set. | |||
| */ | |||
| void setActive (const bool isActive) throw(); | |||
| /** An easy way to set or remove the isTicked bit in the structure's flags field. | |||
| */ | |||
| void setTicked (const bool isTicked) throw(); | |||
| /** Handy method for adding a keypress to the defaultKeypresses array. | |||
| This is just so you can write things like: | |||
| @code | |||
| myinfo.addDefaultKeypress (T('s'), ModifierKeys::commandModifier); | |||
| @endcode | |||
| instead of | |||
| @code | |||
| myinfo.defaultKeypresses.add (KeyPress (T('s'), ModifierKeys::commandModifier)); | |||
| @endcode | |||
| */ | |||
| void addDefaultKeypress (const int keyCode, | |||
| const ModifierKeys& modifiers) throw(); | |||
| //============================================================================== | |||
| /** The command's unique ID number. | |||
| */ | |||
| CommandID commandID; | |||
| /** A short name to describe the command. | |||
| This should be suitable for use in menus, on buttons that trigger the command, etc. | |||
| You can use the setInfo() method to quickly set this and some of the command's | |||
| other properties. | |||
| */ | |||
| String shortName; | |||
| /** A longer description of the command. | |||
| This should be suitable for use in contexts such as a KeyMappingEditorComponent or | |||
| pop-up tooltip describing what the command does. | |||
| You can use the setInfo() method to quickly set this and some of the command's | |||
| other properties. | |||
| */ | |||
| String description; | |||
| /** A named category that the command fits into. | |||
| You can give your commands any category you like, and these will be displayed in | |||
| contexts such as the KeyMappingEditorComponent, where the category is used to group | |||
| commands together. | |||
| You can use the setInfo() method to quickly set this and some of the command's | |||
| other properties. | |||
| */ | |||
| String categoryName; | |||
| /** A list of zero or more keypresses that should be used as the default keys for | |||
| this command. | |||
| Methods such as KeyPressMappingSet::resetToDefaultMappings() will use the keypresses in | |||
| this list to initialise the default set of key-to-command mappings. | |||
| @see addDefaultKeypress | |||
| */ | |||
| Array <KeyPress> defaultKeypresses; | |||
| //============================================================================== | |||
| /** Flags describing the ways in which this command should be used. | |||
| A bitwise-OR of these values is stored in the ApplicationCommandInfo::flags | |||
| variable. | |||
| */ | |||
| enum CommandFlags | |||
| { | |||
| /** Indicates that the command can't currently be performed. | |||
| The ApplicationCommandTarget::getCommandInfo() method must set this flag if it's | |||
| not currently permissable to perform the command. If the flag is set, then | |||
| components that trigger the command, e.g. PopupMenu, may choose to grey-out the | |||
| command or show themselves as not being enabled. | |||
| @see ApplicationCommandInfo::setActive | |||
| */ | |||
| isDisabled = 1 << 0, | |||
| /** Indicates that the command should have a tick next to it on a menu. | |||
| If your command is shown on a menu and this is set, it'll show a tick next to | |||
| it. Other components such as buttons may also use this flag to indicate that it | |||
| is a value that can be toggled, and is currently in the 'on' state. | |||
| @see ApplicationCommandInfo::setTicked | |||
| */ | |||
| isTicked = 1 << 1, | |||
| /** If this flag is present, then when a KeyPressMappingSet invokes the command, | |||
| it will call the command twice, once on key-down and again on key-up. | |||
| @see ApplicationCommandTarget::InvocationInfo | |||
| */ | |||
| wantsKeyUpDownCallbacks = 1 << 2, | |||
| /** If this flag is present, then a KeyMappingEditorComponent will not display the | |||
| command in its list. | |||
| */ | |||
| hiddenFromKeyEditor = 1 << 3, | |||
| /** If this flag is present, then a KeyMappingEditorComponent will display the | |||
| command in its list, but won't allow the assigned keypress to be changed. | |||
| */ | |||
| readOnlyInKeyEditor = 1 << 4, | |||
| /** If this flag is present and the command is invoked from a keypress, then any | |||
| buttons or menus that are also connected to the command will not flash to | |||
| indicate that they've been triggered. | |||
| */ | |||
| dontTriggerVisualFeedback = 1 << 5 | |||
| }; | |||
| /** A bitwise-OR of the values specified in the CommandFlags enum. | |||
| You can use the setInfo() method to quickly set this and some of the command's | |||
| other properties. | |||
| */ | |||
| int flags; | |||
| }; | |||
| #endif // __JUCE_APPLICATIONCOMMANDINFO_JUCEHEADER__ | |||
| @@ -0,0 +1,342 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_ApplicationCommandManager.h" | |||
| #include "juce_Application.h" | |||
| #include "../gui/components/keyboard/juce_KeyPressMappingSet.h" | |||
| #include "../gui/components/windows/juce_ResizableWindow.h" | |||
| #include "../gui/components/juce_Desktop.h" | |||
| #include "../events/juce_MessageManager.h" | |||
| #include "../threads/juce_Process.h" | |||
| //============================================================================== | |||
| ApplicationCommandManager::ApplicationCommandManager() | |||
| : listeners (8), | |||
| firstTarget (0) | |||
| { | |||
| keyMappings = new KeyPressMappingSet (this); | |||
| Desktop::getInstance().addFocusChangeListener (this); | |||
| } | |||
| ApplicationCommandManager::~ApplicationCommandManager() | |||
| { | |||
| Desktop::getInstance().removeFocusChangeListener (this); | |||
| deleteAndZero (keyMappings); | |||
| } | |||
| //============================================================================== | |||
| void ApplicationCommandManager::clearCommands() | |||
| { | |||
| commands.clear(); | |||
| keyMappings->clearAllKeyPresses(); | |||
| triggerAsyncUpdate(); | |||
| } | |||
| void ApplicationCommandManager::registerCommand (const ApplicationCommandInfo& newCommand) | |||
| { | |||
| // zero isn't a valid command ID! | |||
| jassert (newCommand.commandID != 0); | |||
| // the name isn't optional! | |||
| jassert (newCommand.shortName.isNotEmpty()); | |||
| if (getCommandForID (newCommand.commandID) == 0) | |||
| { | |||
| ApplicationCommandInfo* const newInfo = new ApplicationCommandInfo (newCommand); | |||
| newInfo->flags &= ~ApplicationCommandInfo::isTicked; | |||
| commands.add (newInfo); | |||
| keyMappings->resetToDefaultMapping (newCommand.commandID); | |||
| triggerAsyncUpdate(); | |||
| } | |||
| else | |||
| { | |||
| // trying to re-register the same command with different parameters? | |||
| jassert (newCommand.shortName == getCommandForID (newCommand.commandID)->shortName | |||
| && (newCommand.description == getCommandForID (newCommand.commandID)->description || newCommand.description.isEmpty()) | |||
| && newCommand.categoryName == getCommandForID (newCommand.commandID)->categoryName | |||
| && newCommand.defaultKeypresses == getCommandForID (newCommand.commandID)->defaultKeypresses | |||
| && (newCommand.flags & (ApplicationCommandInfo::wantsKeyUpDownCallbacks | ApplicationCommandInfo::hiddenFromKeyEditor | ApplicationCommandInfo::readOnlyInKeyEditor)) | |||
| == (getCommandForID (newCommand.commandID)->flags & (ApplicationCommandInfo::wantsKeyUpDownCallbacks | ApplicationCommandInfo::hiddenFromKeyEditor | ApplicationCommandInfo::readOnlyInKeyEditor))); | |||
| } | |||
| } | |||
| void ApplicationCommandManager::registerAllCommandsForTarget (ApplicationCommandTarget* target) | |||
| { | |||
| if (target != 0) | |||
| { | |||
| Array <CommandID> commandIDs; | |||
| target->getAllCommands (commandIDs); | |||
| for (int i = 0; i < commandIDs.size(); ++i) | |||
| { | |||
| ApplicationCommandInfo info (commandIDs.getUnchecked(i)); | |||
| target->getCommandInfo (info.commandID, info); | |||
| registerCommand (info); | |||
| } | |||
| } | |||
| } | |||
| void ApplicationCommandManager::removeCommand (const CommandID commandID) | |||
| { | |||
| for (int i = commands.size(); --i >= 0;) | |||
| { | |||
| if (commands.getUnchecked (i)->commandID == commandID) | |||
| { | |||
| commands.remove (i); | |||
| triggerAsyncUpdate(); | |||
| const Array <KeyPress> keys (keyMappings->getKeyPressesAssignedToCommand (commandID)); | |||
| for (int j = keys.size(); --j >= 0;) | |||
| keyMappings->removeKeyPress (keys.getReference (j)); | |||
| } | |||
| } | |||
| } | |||
| void ApplicationCommandManager::commandStatusChanged() | |||
| { | |||
| triggerAsyncUpdate(); | |||
| } | |||
| //============================================================================== | |||
| const ApplicationCommandInfo* ApplicationCommandManager::getCommandForID (const CommandID commandID) const throw() | |||
| { | |||
| for (int i = commands.size(); --i >= 0;) | |||
| if (commands.getUnchecked(i)->commandID == commandID) | |||
| return commands.getUnchecked(i); | |||
| return 0; | |||
| } | |||
| const String ApplicationCommandManager::getNameOfCommand (const CommandID commandID) const throw() | |||
| { | |||
| const ApplicationCommandInfo* const ci = getCommandForID (commandID); | |||
| return (ci != 0) ? ci->shortName : String::empty; | |||
| } | |||
| const String ApplicationCommandManager::getDescriptionOfCommand (const CommandID commandID) const throw() | |||
| { | |||
| const ApplicationCommandInfo* const ci = getCommandForID (commandID); | |||
| return (ci != 0) ? (ci->description.isNotEmpty() ? ci->description : ci->shortName) | |||
| : String::empty; | |||
| } | |||
| const StringArray ApplicationCommandManager::getCommandCategories() const throw() | |||
| { | |||
| StringArray s; | |||
| for (int i = 0; i < commands.size(); ++i) | |||
| s.addIfNotAlreadyThere (commands.getUnchecked(i)->categoryName, false); | |||
| return s; | |||
| } | |||
| const Array <CommandID> ApplicationCommandManager::getCommandsInCategory (const String& categoryName) const throw() | |||
| { | |||
| Array <CommandID> results (4); | |||
| for (int i = 0; i < commands.size(); ++i) | |||
| if (commands.getUnchecked(i)->categoryName == categoryName) | |||
| results.add (commands.getUnchecked(i)->commandID); | |||
| return results; | |||
| } | |||
| //============================================================================== | |||
| bool ApplicationCommandManager::invokeDirectly (const CommandID commandID, const bool asynchronously) | |||
| { | |||
| ApplicationCommandTarget::InvocationInfo info (commandID); | |||
| info.invocationMethod = ApplicationCommandTarget::InvocationInfo::direct; | |||
| return invoke (info, asynchronously); | |||
| } | |||
| bool ApplicationCommandManager::invoke (const ApplicationCommandTarget::InvocationInfo& info_, const bool asynchronously) | |||
| { | |||
| // This call isn't thread-safe for use from a non-UI thread without locking the message | |||
| // manager first.. | |||
| jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager()); | |||
| ApplicationCommandTarget* const target = getFirstCommandTarget (info_.commandID); | |||
| if (target == 0) | |||
| return false; | |||
| ApplicationCommandInfo commandInfo (0); | |||
| target->getCommandInfo (info_.commandID, commandInfo); | |||
| ApplicationCommandTarget::InvocationInfo info (info_); | |||
| info.commandFlags = commandInfo.flags; | |||
| sendListenerInvokeCallback (info); | |||
| const bool ok = target->invoke (info, asynchronously); | |||
| commandStatusChanged(); | |||
| return ok; | |||
| } | |||
| //============================================================================== | |||
| ApplicationCommandTarget* ApplicationCommandManager::getFirstCommandTarget (const CommandID) | |||
| { | |||
| return firstTarget != 0 ? firstTarget | |||
| : findDefaultComponentTarget(); | |||
| } | |||
| void ApplicationCommandManager::setFirstCommandTarget (ApplicationCommandTarget* const newTarget) throw() | |||
| { | |||
| firstTarget = newTarget; | |||
| } | |||
| ApplicationCommandTarget* ApplicationCommandManager::getTargetForCommand (const CommandID commandID, | |||
| ApplicationCommandInfo& upToDateInfo) | |||
| { | |||
| ApplicationCommandTarget* target = getFirstCommandTarget (commandID); | |||
| if (target == 0) | |||
| target = JUCEApplication::getInstance(); | |||
| if (target != 0) | |||
| target = target->getTargetForCommand (commandID); | |||
| if (target != 0) | |||
| target->getCommandInfo (commandID, upToDateInfo); | |||
| return target; | |||
| } | |||
| //============================================================================== | |||
| ApplicationCommandTarget* ApplicationCommandManager::findTargetForComponent (Component* c) | |||
| { | |||
| ApplicationCommandTarget* target = dynamic_cast <ApplicationCommandTarget*> (c); | |||
| if (target == 0 && c != 0) | |||
| // (unable to use the syntax findParentComponentOfClass <ApplicationCommandTarget> () because of a VC6 compiler bug) | |||
| target = c->findParentComponentOfClass ((ApplicationCommandTarget*) 0); | |||
| return target; | |||
| } | |||
| ApplicationCommandTarget* ApplicationCommandManager::findDefaultComponentTarget() | |||
| { | |||
| Component* c = Component::getCurrentlyFocusedComponent(); | |||
| if (c == 0) | |||
| { | |||
| TopLevelWindow* const activeWindow = TopLevelWindow::getActiveTopLevelWindow(); | |||
| if (activeWindow != 0) | |||
| { | |||
| c = activeWindow->getPeer()->getLastFocusedSubcomponent(); | |||
| if (c == 0) | |||
| c = activeWindow; | |||
| } | |||
| } | |||
| if (c == 0 && Process::isForegroundProcess()) | |||
| { | |||
| // getting a bit desperate now - try all desktop comps.. | |||
| for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;) | |||
| { | |||
| ApplicationCommandTarget* const target | |||
| = findTargetForComponent (Desktop::getInstance().getComponent (i) | |||
| ->getPeer()->getLastFocusedSubcomponent()); | |||
| if (target != 0) | |||
| return target; | |||
| } | |||
| } | |||
| if (c != 0) | |||
| { | |||
| ResizableWindow* const resizableWindow = dynamic_cast <ResizableWindow*> (c); | |||
| // if we're focused on a ResizableWindow, chances are that it's the content | |||
| // component that really should get the event. And if not, the event will | |||
| // still be passed up to the top level window anyway, so let's send it to the | |||
| // content comp. | |||
| if (resizableWindow != 0 && resizableWindow->getContentComponent() != 0) | |||
| c = resizableWindow->getContentComponent(); | |||
| ApplicationCommandTarget* const target = findTargetForComponent (c); | |||
| if (target != 0) | |||
| return target; | |||
| } | |||
| return JUCEApplication::getInstance(); | |||
| } | |||
| //============================================================================== | |||
| void ApplicationCommandManager::addListener (ApplicationCommandManagerListener* const listener) throw() | |||
| { | |||
| jassert (listener != 0); | |||
| if (listener != 0) | |||
| listeners.add (listener); | |||
| } | |||
| void ApplicationCommandManager::removeListener (ApplicationCommandManagerListener* const listener) throw() | |||
| { | |||
| listeners.removeValue (listener); | |||
| } | |||
| void ApplicationCommandManager::sendListenerInvokeCallback (const ApplicationCommandTarget::InvocationInfo& info) const | |||
| { | |||
| for (int i = listeners.size(); --i >= 0;) | |||
| { | |||
| ((ApplicationCommandManagerListener*) listeners.getUnchecked (i))->applicationCommandInvoked (info); | |||
| i = jmin (i, listeners.size()); | |||
| } | |||
| } | |||
| void ApplicationCommandManager::handleAsyncUpdate() | |||
| { | |||
| for (int i = listeners.size(); --i >= 0;) | |||
| { | |||
| ((ApplicationCommandManagerListener*) listeners.getUnchecked (i))->applicationCommandListChanged(); | |||
| i = jmin (i, listeners.size()); | |||
| } | |||
| } | |||
| void ApplicationCommandManager::globalFocusChanged (Component*) | |||
| { | |||
| commandStatusChanged(); | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,367 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_APPLICATIONCOMMANDMANAGER_JUCEHEADER__ | |||
| #define __JUCE_APPLICATIONCOMMANDMANAGER_JUCEHEADER__ | |||
| #include "juce_ApplicationCommandTarget.h" | |||
| #include "../events/juce_AsyncUpdater.h" | |||
| #include "../gui/components/juce_Desktop.h" | |||
| #include "../containers/juce_SortedSet.h" | |||
| class KeyPressMappingSet; | |||
| class ApplicationCommandManagerListener; | |||
| //============================================================================== | |||
| /** | |||
| One of these objects holds a list of all the commands your app can perform, | |||
| and despatches these commands when needed. | |||
| Application commands are a good way to trigger actions in your app, e.g. "Quit", | |||
| "Copy", "Paste", etc. Menus, buttons and keypresses can all be given commands | |||
| to invoke automatically, which means you don't have to handle the result of a menu | |||
| or button click manually. Commands are despatched to ApplicationCommandTarget objects | |||
| which can choose which events they want to handle. | |||
| This architecture also allows for nested ApplicationCommandTargets, so that for example | |||
| you could have two different objects, one inside the other, both of which can respond to | |||
| a "delete" command. Depending on which one has focus, the command will be sent to the | |||
| appropriate place, regardless of whether it was triggered by a menu, keypress or some other | |||
| method. | |||
| To set up your app to use commands, you'll need to do the following: | |||
| - Create a global ApplicationCommandManager to hold the list of all possible | |||
| commands. (This will also manage a set of key-mappings for them). | |||
| - Make some of your UI components (or other objects) inherit from ApplicationCommandTarget. | |||
| This allows the object to provide a list of commands that it can perform, and | |||
| to handle them. | |||
| - Register each type of command using ApplicationCommandManager::registerAllCommandsForTarget(), | |||
| or ApplicationCommandManager::registerCommand(). | |||
| - If you want key-presses to trigger your commands, use the ApplicationCommandManager::getKeyMappings() | |||
| method to access the key-mapper object, which you will need to register as a key-listener | |||
| in whatever top-level component you're using. See the KeyPressMappingSet class for more help | |||
| about setting this up. | |||
| - Use methods such as PopupMenu::addCommandItem() or Button::setCommandToTrigger() to | |||
| cause these commands to be invoked automatically. | |||
| - Commands can be invoked directly by your code using ApplicationCommandManager::invokeDirectly(). | |||
| When a command is invoked, the ApplicationCommandManager will try to choose the best | |||
| ApplicationCommandTarget to receive the specified command. To do this it will use the | |||
| current keyboard focus to see which component might be interested, and will search the | |||
| component hierarchy for those that also implement the ApplicationCommandTarget interface. | |||
| If an ApplicationCommandTarget isn't interested in the command that is being invoked, then | |||
| the next one in line will be tried (see the ApplicationCommandTarget::getNextCommandTarget() | |||
| method), and so on until ApplicationCommandTarget::getNextCommandTarget() returns 0. At this | |||
| point if the command still hasn't been performed, it will be passed to the current | |||
| JUCEApplication object (which is itself an ApplicationCommandTarget). | |||
| To exert some custom control over which ApplicationCommandTarget is chosen to invoke a command, | |||
| you can override the ApplicationCommandManager::getFirstCommandTarget() method and choose | |||
| the object yourself. | |||
| @see ApplicationCommandTarget, ApplicationCommandInfo | |||
| */ | |||
| class JUCE_API ApplicationCommandManager : private AsyncUpdater, | |||
| private FocusChangeListener | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Creates an ApplicationCommandManager. | |||
| Once created, you'll need to register all your app's commands with it, using | |||
| ApplicationCommandManager::registerAllCommandsForTarget() or | |||
| ApplicationCommandManager::registerCommand(). | |||
| */ | |||
| ApplicationCommandManager(); | |||
| /** Destructor. | |||
| Make sure that you don't delete this if pointers to it are still being used by | |||
| objects such as PopupMenus or Buttons. | |||
| */ | |||
| virtual ~ApplicationCommandManager(); | |||
| //============================================================================== | |||
| /** Clears the current list of all commands. | |||
| Note that this will also clear the contents of the KeyPressMappingSet. | |||
| */ | |||
| void clearCommands(); | |||
| /** Adds a command to the list of registered commands. | |||
| @see registerAllCommandsForTarget | |||
| */ | |||
| void registerCommand (const ApplicationCommandInfo& newCommand); | |||
| /** Adds all the commands that this target publishes to the manager's list. | |||
| This will use ApplicationCommandTarget::getAllCommands() and ApplicationCommandTarget::getCommandInfo() | |||
| to get details about all the commands that this target can do, and will call | |||
| registerCommand() to add each one to the manger's list. | |||
| @see registerCommand | |||
| */ | |||
| void registerAllCommandsForTarget (ApplicationCommandTarget* target); | |||
| /** Removes the command with a specified ID. | |||
| Note that this will also remove any key mappings that are mapped to the command. | |||
| */ | |||
| void removeCommand (const CommandID commandID); | |||
| /** This should be called to tell the manager that one of its registered commands may have changed | |||
| its active status. | |||
| Because the command manager only finds out whether a command is active or inactive by querying | |||
| the current ApplicationCommandTarget, this is used to tell it that things may have changed. It | |||
| allows things like buttons to update their enablement, etc. | |||
| This method will cause an asynchronous call to ApplicationCommandManagerListener::applicationCommandListChanged() | |||
| for any registered listeners. | |||
| */ | |||
| void commandStatusChanged(); | |||
| //============================================================================== | |||
| /** Returns the number of commands that have been registered. | |||
| @see registerCommand | |||
| */ | |||
| int getNumCommands() const throw() { return commands.size(); } | |||
| /** Returns the details about one of the registered commands. | |||
| The index is between 0 and (getNumCommands() - 1). | |||
| */ | |||
| const ApplicationCommandInfo* getCommandForIndex (const int index) const throw() { return commands [index]; } | |||
| /** Returns the details about a given command ID. | |||
| This will search the list of registered commands for one with the given command | |||
| ID number, and return its associated info. If no matching command is found, this | |||
| will return 0. | |||
| */ | |||
| const ApplicationCommandInfo* getCommandForID (const CommandID commandID) const throw(); | |||
| /** Returns the name field for a command. | |||
| An empty string is returned if no command with this ID has been registered. | |||
| @see getDescriptionOfCommand | |||
| */ | |||
| const String getNameOfCommand (const CommandID commandID) const throw(); | |||
| /** Returns the description field for a command. | |||
| An empty string is returned if no command with this ID has been registered. If the | |||
| command has no description, this will return its short name field instead. | |||
| @see getNameOfCommand | |||
| */ | |||
| const String getDescriptionOfCommand (const CommandID commandID) const throw(); | |||
| /** Returns the list of categories. | |||
| This will go through all registered commands, and return a list of all the distict | |||
| categoryName values from their ApplicationCommandInfo structure. | |||
| @see getCommandsInCategory() | |||
| */ | |||
| const StringArray getCommandCategories() const throw(); | |||
| /** Returns a list of all the command UIDs in a particular category. | |||
| @see getCommandCategories() | |||
| */ | |||
| const Array <CommandID> getCommandsInCategory (const String& categoryName) const throw(); | |||
| //============================================================================== | |||
| /** Returns the manager's internal set of key mappings. | |||
| This object can be used to edit the keypresses. To actually link this object up | |||
| to invoke commands when a key is pressed, see the comments for the KeyPressMappingSet | |||
| class. | |||
| @see KeyPressMappingSet | |||
| */ | |||
| KeyPressMappingSet* getKeyMappings() const throw() { return keyMappings; } | |||
| //============================================================================== | |||
| /** Invokes the given command directly, sending it to the default target. | |||
| This is just an easy way to call invoke() without having to fill out the InvocationInfo | |||
| structure. | |||
| */ | |||
| bool invokeDirectly (const CommandID commandID, | |||
| const bool asynchronously); | |||
| /** Sends a command to the default target. | |||
| This will choose a target using getFirstCommandTarget(), and send the specified command | |||
| to it using the ApplicationCommandTarget::invoke() method. This means that if the | |||
| first target can't handle the command, it will be passed on to targets further down the | |||
| chain (see ApplicationCommandTarget::invoke() for more info). | |||
| @param invocationInfo this must be correctly filled-in, describing the context for | |||
| the invocation. | |||
| @param asynchronously if false, the command will be performed before this method returns. | |||
| If true, a message will be posted so that the command will be performed | |||
| later on the message thread, and this method will return immediately. | |||
| @see ApplicationCommandTarget::invoke | |||
| */ | |||
| bool invoke (const ApplicationCommandTarget::InvocationInfo& invocationInfo, | |||
| const bool asynchronously); | |||
| //============================================================================== | |||
| /** Chooses the ApplicationCommandTarget to which a command should be sent. | |||
| Whenever the manager needs to know which target a command should be sent to, it calls | |||
| this method to determine the first one to try. | |||
| By default, this method will return the target that was set by calling setFirstCommandTarget(). | |||
| If no target is set, it will return the result of findDefaultComponentTarget(). | |||
| If you need to make sure all commands go via your own custom target, then you can | |||
| either use setFirstCommandTarget() to specify a single target, or override this method | |||
| if you need more complex logic to choose one. | |||
| It may return 0 if no targets are available. | |||
| @see getTargetForCommand, invoke, invokeDirectly | |||
| */ | |||
| virtual ApplicationCommandTarget* getFirstCommandTarget (const CommandID commandID); | |||
| /** Sets a target to be returned by getFirstCommandTarget(). | |||
| If this is set to 0, then getFirstCommandTarget() will by default return the | |||
| result of findDefaultComponentTarget(). | |||
| If you use this to set a target, make sure you call setFirstCommandTarget (0) before | |||
| deleting the target object. | |||
| */ | |||
| void setFirstCommandTarget (ApplicationCommandTarget* const newTarget) throw(); | |||
| /** Tries to find the best target to use to perform a given command. | |||
| This will call getFirstCommandTarget() to find the preferred target, and will | |||
| check whether that target can handle the given command. If it can't, then it'll use | |||
| ApplicationCommandTarget::getNextCommandTarget() to find the next one to try, and | |||
| so on until no more are available. | |||
| If no targets are found that can perform the command, this method will return 0. | |||
| If a target is found, then it will get the target to fill-in the upToDateInfo | |||
| structure with the latest info about that command, so that the caller can see | |||
| whether the command is disabled, ticked, etc. | |||
| */ | |||
| ApplicationCommandTarget* getTargetForCommand (const CommandID commandID, | |||
| ApplicationCommandInfo& upToDateInfo); | |||
| //============================================================================== | |||
| /** Registers a listener that will be called when various events occur. */ | |||
| void addListener (ApplicationCommandManagerListener* const listener) throw(); | |||
| /** Deregisters a previously-added listener. */ | |||
| void removeListener (ApplicationCommandManagerListener* const listener) throw(); | |||
| //============================================================================== | |||
| /** Looks for a suitable command target based on which Components have the keyboard focus. | |||
| This is used by the default implementation of ApplicationCommandTarget::getFirstCommandTarget(), | |||
| but is exposed here in case it's useful. | |||
| It tries to pick the best ApplicationCommandTarget by looking at focused components, top level | |||
| windows, etc., and using the findTargetForComponent() method. | |||
| */ | |||
| static ApplicationCommandTarget* findDefaultComponentTarget(); | |||
| /** Examines this component and all its parents in turn, looking for the first one | |||
| which is a ApplicationCommandTarget. | |||
| Returns the first ApplicationCommandTarget that it finds, or 0 if none of them implement | |||
| that class. | |||
| */ | |||
| static ApplicationCommandTarget* findTargetForComponent (Component* component); | |||
| //============================================================================== | |||
| juce_UseDebuggingNewOperator | |||
| private: | |||
| //============================================================================== | |||
| OwnedArray <ApplicationCommandInfo> commands; | |||
| SortedSet <void*> listeners; | |||
| KeyPressMappingSet* keyMappings; | |||
| ApplicationCommandTarget* firstTarget; | |||
| void sendListenerInvokeCallback (const ApplicationCommandTarget::InvocationInfo& info) const; | |||
| void handleAsyncUpdate(); | |||
| void globalFocusChanged (Component*); | |||
| // xxx this is just here to cause a compile error in old code that hasn't been changed to use the new | |||
| // version of this method. | |||
| virtual short getFirstCommandTarget() { return 0; } | |||
| }; | |||
| //============================================================================== | |||
| /** | |||
| A listener that receives callbacks from an ApplicationCommandManager when | |||
| commands are invoked or the command list is changed. | |||
| @see ApplicationCommandManager::addListener, ApplicationCommandManager::removeListener | |||
| */ | |||
| class JUCE_API ApplicationCommandManagerListener | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Destructor. */ | |||
| virtual ~ApplicationCommandManagerListener() {} | |||
| /** Called when an app command is about to be invoked. */ | |||
| virtual void applicationCommandInvoked (const ApplicationCommandTarget::InvocationInfo& info) = 0; | |||
| /** Called when commands are registered or deregistered from the | |||
| command manager, or when commands are made active or inactive. | |||
| Note that if you're using this to watch for changes to whether a command is disabled, | |||
| you'll need to make sure that ApplicationCommandManager::commandStatusChanged() is called | |||
| whenever the status of your command might have changed. | |||
| */ | |||
| virtual void applicationCommandListChanged() = 0; | |||
| }; | |||
| #endif // __JUCE_APPLICATIONCOMMANDMANAGER_JUCEHEADER__ | |||
| @@ -0,0 +1,202 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_ApplicationCommandTarget.h" | |||
| #include "juce_Application.h" | |||
| //============================================================================== | |||
| ApplicationCommandTarget::ApplicationCommandTarget() | |||
| : messageInvoker (0) | |||
| { | |||
| } | |||
| ApplicationCommandTarget::~ApplicationCommandTarget() | |||
| { | |||
| deleteAndZero (messageInvoker); | |||
| } | |||
| //============================================================================== | |||
| bool ApplicationCommandTarget::tryToInvoke (const InvocationInfo& info, const bool async) | |||
| { | |||
| if (isCommandActive (info.commandID)) | |||
| { | |||
| if (async) | |||
| { | |||
| if (messageInvoker == 0) | |||
| messageInvoker = new CommandTargetMessageInvoker (this); | |||
| messageInvoker->postMessage (new Message (0, 0, 0, new ApplicationCommandTarget::InvocationInfo (info))); | |||
| return true; | |||
| } | |||
| else | |||
| { | |||
| const bool success = perform (info); | |||
| jassert (success); // hmm - your target should have been able to perform this command. If it can't | |||
| // do it at the moment for some reason, it should clear the 'isActive' flag when it | |||
| // returns the command's info. | |||
| return success; | |||
| } | |||
| } | |||
| return false; | |||
| } | |||
| ApplicationCommandTarget* ApplicationCommandTarget::findFirstTargetParentComponent() | |||
| { | |||
| Component* c = dynamic_cast <Component*> (this); | |||
| if (c != 0) | |||
| // (unable to use the syntax findParentComponentOfClass <ApplicationCommandTarget> () because of a VC6 compiler bug) | |||
| return c->findParentComponentOfClass ((ApplicationCommandTarget*) 0); | |||
| return 0; | |||
| } | |||
| ApplicationCommandTarget* ApplicationCommandTarget::getTargetForCommand (const CommandID commandID) | |||
| { | |||
| ApplicationCommandTarget* target = this; | |||
| int depth = 0; | |||
| while (target != 0) | |||
| { | |||
| Array <CommandID> commandIDs; | |||
| target->getAllCommands (commandIDs); | |||
| if (commandIDs.contains (commandID)) | |||
| return target; | |||
| target = target->getNextCommandTarget(); | |||
| ++depth; | |||
| jassert (depth < 100); // could be a recursive command chain?? | |||
| jassert (target != this); // definitely a recursive command chain! | |||
| if (depth > 100 || target == this) | |||
| break; | |||
| } | |||
| if (target == 0) | |||
| { | |||
| target = JUCEApplication::getInstance(); | |||
| if (target != 0) | |||
| { | |||
| Array <CommandID> commandIDs; | |||
| target->getAllCommands (commandIDs); | |||
| if (commandIDs.contains (commandID)) | |||
| return target; | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| bool ApplicationCommandTarget::isCommandActive (const CommandID commandID) | |||
| { | |||
| ApplicationCommandInfo info (commandID); | |||
| info.flags = ApplicationCommandInfo::isDisabled; | |||
| getCommandInfo (commandID, info); | |||
| return (info.flags & ApplicationCommandInfo::isDisabled) == 0; | |||
| } | |||
| //============================================================================== | |||
| bool ApplicationCommandTarget::invoke (const InvocationInfo& info, const bool async) | |||
| { | |||
| ApplicationCommandTarget* target = this; | |||
| int depth = 0; | |||
| while (target != 0) | |||
| { | |||
| if (target->tryToInvoke (info, async)) | |||
| return true; | |||
| target = target->getNextCommandTarget(); | |||
| ++depth; | |||
| jassert (depth < 100); // could be a recursive command chain?? | |||
| jassert (target != this); // definitely a recursive command chain! | |||
| if (depth > 100 || target == this) | |||
| break; | |||
| } | |||
| if (target == 0) | |||
| { | |||
| target = JUCEApplication::getInstance(); | |||
| if (target != 0) | |||
| return target->tryToInvoke (info, async); | |||
| } | |||
| return false; | |||
| } | |||
| bool ApplicationCommandTarget::invokeDirectly (const CommandID commandID, const bool asynchronously) | |||
| { | |||
| ApplicationCommandTarget::InvocationInfo info (commandID); | |||
| info.invocationMethod = ApplicationCommandTarget::InvocationInfo::direct; | |||
| return invoke (info, asynchronously); | |||
| } | |||
| //============================================================================== | |||
| ApplicationCommandTarget::InvocationInfo::InvocationInfo (const CommandID commandID_) throw() | |||
| : commandID (commandID_), | |||
| commandFlags (0), | |||
| invocationMethod (direct), | |||
| originatingComponent (0), | |||
| isKeyDown (false), | |||
| millisecsSinceKeyPressed (0) | |||
| { | |||
| } | |||
| //============================================================================== | |||
| ApplicationCommandTarget::CommandTargetMessageInvoker::CommandTargetMessageInvoker (ApplicationCommandTarget* const owner_) | |||
| : owner (owner_) | |||
| { | |||
| } | |||
| ApplicationCommandTarget::CommandTargetMessageInvoker::~CommandTargetMessageInvoker() | |||
| { | |||
| } | |||
| void ApplicationCommandTarget::CommandTargetMessageInvoker::handleMessage (const Message& message) | |||
| { | |||
| InvocationInfo* const info = (InvocationInfo*) message.pointerParameter; | |||
| owner->tryToInvoke (*info, false); | |||
| delete info; | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,161 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_ApplicationProperties.h" | |||
| #include "../gui/components/windows/juce_AlertWindow.h" | |||
| #include "../text/juce_LocalisedStrings.h" | |||
| //============================================================================== | |||
| juce_ImplementSingleton (ApplicationProperties) | |||
| //============================================================================== | |||
| ApplicationProperties::ApplicationProperties() throw() | |||
| : userProps (0), | |||
| commonProps (0), | |||
| msBeforeSaving (3000), | |||
| options (PropertiesFile::storeAsBinary), | |||
| commonSettingsAreReadOnly (0) | |||
| { | |||
| } | |||
| ApplicationProperties::~ApplicationProperties() | |||
| { | |||
| closeFiles(); | |||
| clearSingletonInstance(); | |||
| } | |||
| //============================================================================== | |||
| void ApplicationProperties::setStorageParameters (const String& applicationName, | |||
| const String& fileNameSuffix, | |||
| const String& folderName_, | |||
| const int millisecondsBeforeSaving, | |||
| const int propertiesFileOptions) throw() | |||
| { | |||
| appName = applicationName; | |||
| fileSuffix = fileNameSuffix; | |||
| folderName = folderName_; | |||
| msBeforeSaving = millisecondsBeforeSaving; | |||
| options = propertiesFileOptions; | |||
| } | |||
| bool ApplicationProperties::testWriteAccess (const bool testUserSettings, | |||
| const bool testCommonSettings, | |||
| const bool showWarningDialogOnFailure) | |||
| { | |||
| const bool userOk = (! testUserSettings) || getUserSettings()->save(); | |||
| const bool commonOk = (! testCommonSettings) || getCommonSettings (false)->save(); | |||
| if (! (userOk && commonOk)) | |||
| { | |||
| if (showWarningDialogOnFailure) | |||
| { | |||
| String filenames; | |||
| if (userProps != 0 && ! userOk) | |||
| filenames << '\n' << userProps->getFile().getFullPathName(); | |||
| if (commonProps != 0 && ! commonOk) | |||
| filenames << '\n' << commonProps->getFile().getFullPathName(); | |||
| AlertWindow::showMessageBox (AlertWindow::WarningIcon, | |||
| appName + TRANS(" - Unable to save settings"), | |||
| TRANS("An error occurred when trying to save the application's settings file...\n\nIn order to save and restore its settings, ") | |||
| + appName + TRANS(" needs to be able to write to the following files:\n") | |||
| + filenames | |||
| + TRANS("\n\nMake sure that these files aren't read-only, and that the disk isn't full.")); | |||
| } | |||
| return false; | |||
| } | |||
| return true; | |||
| } | |||
| //============================================================================== | |||
| void ApplicationProperties::openFiles() throw() | |||
| { | |||
| // You need to call setStorageParameters() before trying to get hold of the | |||
| // properties! | |||
| jassert (appName.isNotEmpty()); | |||
| if (appName.isNotEmpty()) | |||
| { | |||
| if (userProps == 0) | |||
| userProps = PropertiesFile::createDefaultAppPropertiesFile (appName, fileSuffix, folderName, | |||
| false, msBeforeSaving, options); | |||
| if (commonProps == 0) | |||
| commonProps = PropertiesFile::createDefaultAppPropertiesFile (appName, fileSuffix, folderName, | |||
| true, msBeforeSaving, options); | |||
| userProps->setFallbackPropertySet (commonProps); | |||
| } | |||
| } | |||
| PropertiesFile* ApplicationProperties::getUserSettings() throw() | |||
| { | |||
| if (userProps == 0) | |||
| openFiles(); | |||
| return userProps; | |||
| } | |||
| PropertiesFile* ApplicationProperties::getCommonSettings (const bool returnUserPropsIfReadOnly) throw() | |||
| { | |||
| if (commonProps == 0) | |||
| openFiles(); | |||
| if (returnUserPropsIfReadOnly) | |||
| { | |||
| if (commonSettingsAreReadOnly == 0) | |||
| commonSettingsAreReadOnly = commonProps->save() ? -1 : 1; | |||
| if (commonSettingsAreReadOnly > 0) | |||
| return userProps; | |||
| } | |||
| return commonProps; | |||
| } | |||
| bool ApplicationProperties::saveIfNeeded() | |||
| { | |||
| return (userProps == 0 || userProps->saveIfNeeded()) | |||
| && (commonProps == 0 || commonProps->saveIfNeeded()); | |||
| } | |||
| void ApplicationProperties::closeFiles() | |||
| { | |||
| deleteAndZero (userProps); | |||
| deleteAndZero (commonProps); | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,165 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_APPLICATIONPROPERTIES_JUCEHEADER__ | |||
| #define __JUCE_APPLICATIONPROPERTIES_JUCEHEADER__ | |||
| #include "../utilities/juce_PropertiesFile.h" | |||
| #include "../utilities/juce_DeletedAtShutdown.h" | |||
| #include "../core/juce_Singleton.h" | |||
| //============================================================================== | |||
| /** | |||
| Manages a collection of properties. | |||
| This is a slightly higher-level wrapper for PropertiesFile, which can be used | |||
| as a singleton. | |||
| It holds two different PropertiesFile objects internally, one for user-specific | |||
| settings (stored in your user directory), and one for settings that are common to | |||
| all users (stored in a folder accessible to all users). | |||
| The class manages the creation of these files on-demand, allowing access via the | |||
| getUserSettings() and getCommonSettings() methods. It also has a few handy | |||
| methods like testWriteAccess() to check that the files can be saved. | |||
| If you're using one of these as a singleton, then your app's start-up code should | |||
| first of all call setStorageParameters() to tell it the parameters to use to create | |||
| the properties files. | |||
| @see PropertiesFile | |||
| */ | |||
| class JUCE_API ApplicationProperties : public DeletedAtShutdown | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** | |||
| Creates an ApplicationProperties object. | |||
| Before using it, you must call setStorageParameters() to give it the info | |||
| it needs to create the property files. | |||
| */ | |||
| ApplicationProperties() throw(); | |||
| /** Destructor. | |||
| */ | |||
| ~ApplicationProperties(); | |||
| //============================================================================== | |||
| juce_DeclareSingleton (ApplicationProperties, false) | |||
| //============================================================================== | |||
| /** Gives the object the information it needs to create the appropriate properties files. | |||
| See the comments for PropertiesFile::createDefaultAppPropertiesFile() for more | |||
| info about how these parameters are used. | |||
| */ | |||
| void setStorageParameters (const String& applicationName, | |||
| const String& fileNameSuffix, | |||
| const String& folderName, | |||
| const int millisecondsBeforeSaving, | |||
| const int propertiesFileOptions) throw(); | |||
| /** Tests whether the files can be successfully written to, and can show | |||
| an error message if not. | |||
| Returns true if none of the tests fail. | |||
| @param testUserSettings if true, the user settings file will be tested | |||
| @param testCommonSettings if true, the common settings file will be tested | |||
| @param showWarningDialogOnFailure if true, the method will show a helpful error | |||
| message box if either of the tests fail | |||
| */ | |||
| bool testWriteAccess (const bool testUserSettings, | |||
| const bool testCommonSettings, | |||
| const bool showWarningDialogOnFailure); | |||
| //============================================================================== | |||
| /** Returns the user settings file. | |||
| The first time this is called, it will create and load the properties file. | |||
| Note that when you search the user PropertiesFile for a value that it doesn't contain, | |||
| the common settings are used as a second-chance place to look. This is done via the | |||
| PropertySet::setFallbackPropertySet() method - by default the common settings are set | |||
| to the fallback for the user settings. | |||
| @see getCommonSettings | |||
| */ | |||
| PropertiesFile* getUserSettings() throw(); | |||
| /** Returns the common settings file. | |||
| The first time this is called, it will create and load the properties file. | |||
| @param returnUserPropsIfReadOnly if this is true, and the common properties file is | |||
| read-only (e.g. because the user doesn't have permission to write | |||
| to shared files), then this will return the user settings instead, | |||
| (like getUserSettings() would do). This is handy if you'd like to | |||
| write a value to the common settings, but if that's no possible, | |||
| then you'd rather write to the user settings than none at all. | |||
| If returnUserPropsIfReadOnly is false, this method will always return | |||
| the common settings, even if any changes to them can't be saved. | |||
| @see getUserSettings | |||
| */ | |||
| PropertiesFile* getCommonSettings (const bool returnUserPropsIfReadOnly) throw(); | |||
| //============================================================================== | |||
| /** Saves both files if they need to be saved. | |||
| @see PropertiesFile::saveIfNeeded | |||
| */ | |||
| bool saveIfNeeded(); | |||
| /** Flushes and closes both files if they are open. | |||
| This flushes any pending changes to disk with PropertiesFile::saveIfNeeded() | |||
| and closes both files. They will then be re-opened the next time getUserSettings() | |||
| or getCommonSettings() is called. | |||
| */ | |||
| void closeFiles(); | |||
| //============================================================================== | |||
| juce_UseDebuggingNewOperator | |||
| private: | |||
| //============================================================================== | |||
| PropertiesFile* userProps; | |||
| PropertiesFile* commonProps; | |||
| String appName, fileSuffix, folderName; | |||
| int msBeforeSaving, options; | |||
| int commonSettingsAreReadOnly; | |||
| ApplicationProperties (const ApplicationProperties&); | |||
| const ApplicationProperties& operator= (const ApplicationProperties&); | |||
| void openFiles() throw(); | |||
| }; | |||
| #endif // __JUCE_APPLICATIONPROPERTIES_JUCEHEADER__ | |||
| @@ -0,0 +1,951 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_BitArray.h" | |||
| #include "juce_MemoryBlock.h" | |||
| #include "../core/juce_Random.h" | |||
| //============================================================================== | |||
| BitArray::BitArray() throw() | |||
| : numValues (4), | |||
| highestBit (-1), | |||
| negative (false) | |||
| { | |||
| values = (unsigned int*) juce_calloc (sizeof (unsigned int) * (numValues + 1)); | |||
| } | |||
| BitArray::BitArray (const int value) throw() | |||
| : numValues (4), | |||
| highestBit (31), | |||
| negative (value < 0) | |||
| { | |||
| values = (unsigned int*) juce_calloc (sizeof (unsigned int) * (numValues + 1)); | |||
| values[0] = abs (value); | |||
| highestBit = getHighestBit(); | |||
| } | |||
| BitArray::BitArray (int64 value) throw() | |||
| : numValues (4), | |||
| highestBit (63), | |||
| negative (value < 0) | |||
| { | |||
| values = (unsigned int*) juce_calloc (sizeof (unsigned int) * (numValues + 1)); | |||
| if (value < 0) | |||
| value = -value; | |||
| values[0] = (unsigned int) value; | |||
| values[1] = (unsigned int) (value >> 32); | |||
| highestBit = getHighestBit(); | |||
| } | |||
| BitArray::BitArray (const unsigned int value) throw() | |||
| : numValues (4), | |||
| highestBit (31), | |||
| negative (false) | |||
| { | |||
| values = (unsigned int*) juce_calloc (sizeof (unsigned int) * (numValues + 1)); | |||
| values[0] = value; | |||
| highestBit = getHighestBit(); | |||
| } | |||
| BitArray::BitArray (const BitArray& other) throw() | |||
| : numValues (jmax (4, (other.highestBit >> 5) + 1)), | |||
| highestBit (other.getHighestBit()), | |||
| negative (other.negative) | |||
| { | |||
| const int bytes = sizeof (unsigned int) * (numValues + 1); | |||
| values = (unsigned int*) juce_malloc (bytes); | |||
| memcpy (values, other.values, bytes); | |||
| } | |||
| BitArray::~BitArray() throw() | |||
| { | |||
| juce_free (values); | |||
| } | |||
| const BitArray& BitArray::operator= (const BitArray& other) throw() | |||
| { | |||
| if (this != &other) | |||
| { | |||
| juce_free (values); | |||
| highestBit = other.getHighestBit(); | |||
| numValues = jmax (4, (highestBit >> 5) + 1); | |||
| negative = other.negative; | |||
| const int memSize = sizeof (unsigned int) * (numValues + 1); | |||
| values = (unsigned int*)juce_malloc (memSize); | |||
| memcpy (values, other.values, memSize); | |||
| } | |||
| return *this; | |||
| } | |||
| // result == 0 = the same | |||
| // result < 0 = this number is smaller | |||
| // result > 0 = this number is bigger | |||
| int BitArray::compare (const BitArray& other) const throw() | |||
| { | |||
| if (isNegative() == other.isNegative()) | |||
| { | |||
| const int absComp = compareAbsolute (other); | |||
| return isNegative() ? -absComp : absComp; | |||
| } | |||
| else | |||
| { | |||
| return isNegative() ? -1 : 1; | |||
| } | |||
| } | |||
| int BitArray::compareAbsolute (const BitArray& other) const throw() | |||
| { | |||
| const int h1 = getHighestBit(); | |||
| const int h2 = other.getHighestBit(); | |||
| if (h1 > h2) | |||
| return 1; | |||
| else if (h1 < h2) | |||
| return -1; | |||
| for (int i = (h1 >> 5) + 1; --i >= 0;) | |||
| if (values[i] != other.values[i]) | |||
| return (values[i] > other.values[i]) ? 1 : -1; | |||
| return 0; | |||
| } | |||
| bool BitArray::operator== (const BitArray& other) const throw() | |||
| { | |||
| return compare (other) == 0; | |||
| } | |||
| bool BitArray::operator!= (const BitArray& other) const throw() | |||
| { | |||
| return compare (other) != 0; | |||
| } | |||
| bool BitArray::operator[] (const int bit) const throw() | |||
| { | |||
| return bit >= 0 && bit <= highestBit | |||
| && ((values [bit >> 5] & (1 << (bit & 31))) != 0); | |||
| } | |||
| bool BitArray::isEmpty() const throw() | |||
| { | |||
| return getHighestBit() < 0; | |||
| } | |||
| void BitArray::clear() throw() | |||
| { | |||
| if (numValues > 16) | |||
| { | |||
| juce_free (values); | |||
| numValues = 4; | |||
| values = (unsigned int*) juce_calloc (sizeof (unsigned int) * (numValues + 1)); | |||
| } | |||
| else | |||
| { | |||
| zeromem (values, sizeof (unsigned int) * (numValues + 1)); | |||
| } | |||
| highestBit = -1; | |||
| negative = false; | |||
| } | |||
| void BitArray::setBit (const int bit) throw() | |||
| { | |||
| if (bit >= 0) | |||
| { | |||
| if (bit > highestBit) | |||
| { | |||
| ensureSize (bit >> 5); | |||
| highestBit = bit; | |||
| } | |||
| values [bit >> 5] |= (1 << (bit & 31)); | |||
| } | |||
| } | |||
| void BitArray::setBit (const int bit, | |||
| const bool shouldBeSet) throw() | |||
| { | |||
| if (shouldBeSet) | |||
| setBit (bit); | |||
| else | |||
| clearBit (bit); | |||
| } | |||
| void BitArray::clearBit (const int bit) throw() | |||
| { | |||
| if (bit >= 0 && bit <= highestBit) | |||
| values [bit >> 5] &= ~(1 << (bit & 31)); | |||
| } | |||
| void BitArray::setRange (int startBit, | |||
| int numBits, | |||
| const bool shouldBeSet) throw() | |||
| { | |||
| while (--numBits >= 0) | |||
| setBit (startBit++, shouldBeSet); | |||
| } | |||
| void BitArray::insertBit (const int bit, | |||
| const bool shouldBeSet) throw() | |||
| { | |||
| if (bit >= 0) | |||
| shiftBits (1, bit); | |||
| setBit (bit, shouldBeSet); | |||
| } | |||
| //============================================================================== | |||
| void BitArray::andWith (const BitArray& other) throw() | |||
| { | |||
| // this operation will only work with the absolute values | |||
| jassert (isNegative() == other.isNegative()); | |||
| int n = numValues; | |||
| while (n > other.numValues) | |||
| values[--n] = 0; | |||
| while (--n >= 0) | |||
| values[n] &= other.values[n]; | |||
| if (other.highestBit < highestBit) | |||
| highestBit = other.highestBit; | |||
| highestBit = getHighestBit(); | |||
| } | |||
| void BitArray::orWith (const BitArray& other) throw() | |||
| { | |||
| if (other.highestBit < 0) | |||
| return; | |||
| // this operation will only work with the absolute values | |||
| jassert (isNegative() == other.isNegative()); | |||
| ensureSize (other.highestBit >> 5); | |||
| int n = (other.highestBit >> 5) + 1; | |||
| while (--n >= 0) | |||
| values[n] |= other.values[n]; | |||
| if (other.highestBit > highestBit) | |||
| highestBit = other.highestBit; | |||
| highestBit = getHighestBit(); | |||
| } | |||
| void BitArray::xorWith (const BitArray& other) throw() | |||
| { | |||
| if (other.highestBit < 0) | |||
| return; | |||
| // this operation will only work with the absolute values | |||
| jassert (isNegative() == other.isNegative()); | |||
| ensureSize (other.highestBit >> 5); | |||
| int n = (other.highestBit >> 5) + 1; | |||
| while (--n >= 0) | |||
| values[n] ^= other.values[n]; | |||
| if (other.highestBit > highestBit) | |||
| highestBit = other.highestBit; | |||
| highestBit = getHighestBit(); | |||
| } | |||
| //============================================================================== | |||
| void BitArray::add (const BitArray& other) throw() | |||
| { | |||
| if (other.isNegative()) | |||
| { | |||
| BitArray o (other); | |||
| o.negate(); | |||
| subtract (o); | |||
| return; | |||
| } | |||
| if (isNegative()) | |||
| { | |||
| if (compareAbsolute (other) < 0) | |||
| { | |||
| BitArray temp (*this); | |||
| temp.negate(); | |||
| *this = other; | |||
| subtract (temp); | |||
| } | |||
| else | |||
| { | |||
| negate(); | |||
| subtract (other); | |||
| negate(); | |||
| } | |||
| return; | |||
| } | |||
| if (other.highestBit > highestBit) | |||
| highestBit = other.highestBit; | |||
| ++highestBit; | |||
| const int numInts = (highestBit >> 5) + 1; | |||
| ensureSize (numInts); | |||
| int64 remainder = 0; | |||
| for (int i = 0; i <= numInts; ++i) | |||
| { | |||
| if (i < numValues) | |||
| remainder += values[i]; | |||
| if (i < other.numValues) | |||
| remainder += other.values[i]; | |||
| values[i] = (unsigned int) remainder; | |||
| remainder >>= 32; | |||
| } | |||
| jassert (remainder == 0); | |||
| highestBit = getHighestBit(); | |||
| } | |||
| void BitArray::subtract (const BitArray& other) throw() | |||
| { | |||
| if (other.isNegative()) | |||
| { | |||
| BitArray o (other); | |||
| o.negate(); | |||
| add (o); | |||
| return; | |||
| } | |||
| if (! isNegative()) | |||
| { | |||
| if (compareAbsolute (other) < 0) | |||
| { | |||
| BitArray temp (*this); | |||
| *this = other; | |||
| subtract (temp); | |||
| negate(); | |||
| return; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| negate(); | |||
| add (other); | |||
| negate(); | |||
| return; | |||
| } | |||
| const int numInts = (highestBit >> 5) + 1; | |||
| const int maxOtherInts = (other.highestBit >> 5) + 1; | |||
| int64 amountToSubtract = 0; | |||
| for (int i = 0; i <= numInts; ++i) | |||
| { | |||
| if (i <= maxOtherInts) | |||
| amountToSubtract += (int64)other.values[i]; | |||
| if (values[i] >= amountToSubtract) | |||
| { | |||
| values[i] = (unsigned int) (values[i] - amountToSubtract); | |||
| amountToSubtract = 0; | |||
| } | |||
| else | |||
| { | |||
| const int64 n = ((int64) values[i] + (((int64) 1) << 32)) - amountToSubtract; | |||
| values[i] = (unsigned int) n; | |||
| amountToSubtract = 1; | |||
| } | |||
| } | |||
| } | |||
| void BitArray::multiplyBy (const BitArray& other) throw() | |||
| { | |||
| BitArray total; | |||
| highestBit = getHighestBit(); | |||
| const bool wasNegative = isNegative(); | |||
| setNegative (false); | |||
| for (int i = 0; i <= highestBit; ++i) | |||
| { | |||
| if (operator[](i)) | |||
| { | |||
| BitArray n (other); | |||
| n.setNegative (false); | |||
| n.shiftBits (i); | |||
| total.add (n); | |||
| } | |||
| } | |||
| *this = total; | |||
| negative = wasNegative ^ other.isNegative(); | |||
| } | |||
| void BitArray::divideBy (const BitArray& divisor, BitArray& remainder) throw() | |||
| { | |||
| jassert (this != &remainder); // (can't handle passing itself in to get the remainder) | |||
| const int divHB = divisor.getHighestBit(); | |||
| const int ourHB = getHighestBit(); | |||
| if (divHB < 0 || ourHB < 0) | |||
| { | |||
| // division by zero | |||
| remainder.clear(); | |||
| clear(); | |||
| } | |||
| else | |||
| { | |||
| remainder = *this; | |||
| remainder.setNegative (false); | |||
| const bool wasNegative = isNegative(); | |||
| clear(); | |||
| BitArray temp (divisor); | |||
| temp.setNegative (false); | |||
| int leftShift = ourHB - divHB; | |||
| temp.shiftBits (leftShift); | |||
| while (leftShift >= 0) | |||
| { | |||
| if (remainder.compareAbsolute (temp) >= 0) | |||
| { | |||
| remainder.subtract (temp); | |||
| setBit (leftShift); | |||
| } | |||
| if (--leftShift >= 0) | |||
| temp.shiftBits (-1); | |||
| } | |||
| negative = wasNegative ^ divisor.isNegative(); | |||
| remainder.setNegative (wasNegative); | |||
| } | |||
| } | |||
| void BitArray::modulo (const BitArray& divisor) throw() | |||
| { | |||
| BitArray remainder; | |||
| divideBy (divisor, remainder); | |||
| *this = remainder; | |||
| } | |||
| static const BitArray simpleGCD (BitArray* m, BitArray* n) throw() | |||
| { | |||
| while (! m->isEmpty()) | |||
| { | |||
| if (n->compareAbsolute (*m) > 0) | |||
| swapVariables (m, n); | |||
| m->subtract (*n); | |||
| } | |||
| return *n; | |||
| } | |||
| const BitArray BitArray::findGreatestCommonDivisor (BitArray n) const throw() | |||
| { | |||
| BitArray m (*this); | |||
| while (! n.isEmpty()) | |||
| { | |||
| if (abs (m.getHighestBit() - n.getHighestBit()) <= 16) | |||
| return simpleGCD (&m, &n); | |||
| BitArray temp1 (m), temp2; | |||
| temp1.divideBy (n, temp2); | |||
| m = n; | |||
| n = temp2; | |||
| } | |||
| return m; | |||
| } | |||
| void BitArray::exponentModulo (const BitArray& exponent, | |||
| const BitArray& modulus) throw() | |||
| { | |||
| BitArray exp (exponent); | |||
| exp.modulo (modulus); | |||
| BitArray value (*this); | |||
| value.modulo (modulus); | |||
| clear(); | |||
| setBit (0); | |||
| while (! exp.isEmpty()) | |||
| { | |||
| if (exp [0]) | |||
| { | |||
| multiplyBy (value); | |||
| this->modulo (modulus); | |||
| } | |||
| value.multiplyBy (value); | |||
| value.modulo (modulus); | |||
| exp.shiftBits (-1); | |||
| } | |||
| } | |||
| void BitArray::inverseModulo (const BitArray& modulus) throw() | |||
| { | |||
| const BitArray one (1); | |||
| if (modulus == one || modulus.isNegative()) | |||
| { | |||
| clear(); | |||
| return; | |||
| } | |||
| if (isNegative() || compareAbsolute (modulus) >= 0) | |||
| this->modulo (modulus); | |||
| if (*this == one) | |||
| return; | |||
| if (! (*this)[0]) | |||
| { | |||
| // not invertible | |||
| clear(); | |||
| return; | |||
| } | |||
| BitArray a1 (modulus); | |||
| BitArray a2 (*this); | |||
| BitArray b1 (modulus); | |||
| BitArray b2 (1); | |||
| while (a2 != one) | |||
| { | |||
| BitArray temp1, temp2, multiplier (a1); | |||
| multiplier.divideBy (a2, temp1); | |||
| temp1 = a2; | |||
| temp1.multiplyBy (multiplier); | |||
| temp2 = a1; | |||
| temp2.subtract (temp1); | |||
| a1 = a2; | |||
| a2 = temp2; | |||
| temp1 = b2; | |||
| temp1.multiplyBy (multiplier); | |||
| temp2 = b1; | |||
| temp2.subtract (temp1); | |||
| b1 = b2; | |||
| b2 = temp2; | |||
| } | |||
| while (b2.isNegative()) | |||
| b2.add (modulus); | |||
| b2.modulo (modulus); | |||
| *this = b2; | |||
| } | |||
| //============================================================================== | |||
| void BitArray::shiftBits (int bits, const int startBit) throw() | |||
| { | |||
| if (highestBit < 0) | |||
| return; | |||
| if (startBit > 0) | |||
| { | |||
| if (bits < 0) | |||
| { | |||
| // right shift | |||
| for (int i = startBit; i <= highestBit; ++i) | |||
| setBit (i, operator[] (i - bits)); | |||
| highestBit = getHighestBit(); | |||
| } | |||
| else if (bits > 0) | |||
| { | |||
| // left shift | |||
| for (int i = highestBit + 1; --i >= startBit;) | |||
| setBit (i + bits, operator[] (i)); | |||
| while (--bits >= 0) | |||
| clearBit (bits + startBit); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| if (bits < 0) | |||
| { | |||
| // right shift | |||
| bits = -bits; | |||
| if (bits > highestBit) | |||
| { | |||
| clear(); | |||
| } | |||
| else | |||
| { | |||
| const int wordsToMove = bits >> 5; | |||
| int top = 1 + (highestBit >> 5) - wordsToMove; | |||
| highestBit -= bits; | |||
| if (wordsToMove > 0) | |||
| { | |||
| int i; | |||
| for (i = 0; i < top; ++i) | |||
| values [i] = values [i + wordsToMove]; | |||
| for (i = 0; i < wordsToMove; ++i) | |||
| values [top + i] = 0; | |||
| bits &= 31; | |||
| } | |||
| if (bits != 0) | |||
| { | |||
| const int invBits = 32 - bits; | |||
| --top; | |||
| for (int i = 0; i < top; ++i) | |||
| values[i] = (values[i] >> bits) | (values [i + 1] << invBits); | |||
| values[top] = (values[top] >> bits); | |||
| } | |||
| highestBit = getHighestBit(); | |||
| } | |||
| } | |||
| else if (bits > 0) | |||
| { | |||
| // left shift | |||
| ensureSize (((highestBit + bits) >> 5) + 1); | |||
| const int wordsToMove = bits >> 5; | |||
| int top = 1 + (highestBit >> 5); | |||
| highestBit += bits; | |||
| if (wordsToMove > 0) | |||
| { | |||
| int i; | |||
| for (i = top; --i >= 0;) | |||
| values [i + wordsToMove] = values [i]; | |||
| for (i = 0; i < wordsToMove; ++i) | |||
| values [i] = 0; | |||
| bits &= 31; | |||
| } | |||
| if (bits != 0) | |||
| { | |||
| const int invBits = 32 - bits; | |||
| for (int i = top + 1 + wordsToMove; --i > wordsToMove;) | |||
| values[i] = (values[i] << bits) | (values [i - 1] >> invBits); | |||
| values [wordsToMove] = values [wordsToMove] << bits; | |||
| } | |||
| highestBit = getHighestBit(); | |||
| } | |||
| } | |||
| } | |||
| const BitArray BitArray::getBitRange (int startBit, int numBits) const throw() | |||
| { | |||
| BitArray r; | |||
| numBits = jmin (numBits, getHighestBit() + 1 - startBit); | |||
| r.ensureSize (numBits >> 5); | |||
| r.highestBit = numBits; | |||
| int i = 0; | |||
| while (numBits > 0) | |||
| { | |||
| r.values[i++] = getBitRangeAsInt (startBit, jmin (32, numBits)); | |||
| numBits -= 32; | |||
| startBit += 32; | |||
| } | |||
| r.highestBit = r.getHighestBit(); | |||
| return r; | |||
| } | |||
| int BitArray::getBitRangeAsInt (const int startBit, int numBits) const throw() | |||
| { | |||
| if (numBits > 32) | |||
| { | |||
| jassertfalse // use getBitRange() if you need more than 32 bits.. | |||
| numBits = 32; | |||
| } | |||
| numBits = jmin (numBits, highestBit + 1 - startBit); | |||
| if (numBits <= 0) | |||
| return 0; | |||
| const int pos = startBit >> 5; | |||
| const int offset = startBit & 31; | |||
| const int endSpace = 32 - numBits; | |||
| uint32 n = ((uint32) values [pos]) >> offset; | |||
| if (offset > endSpace) | |||
| n |= ((uint32) values [pos + 1]) << (32 - offset); | |||
| return (int) (n & (((uint32) 0xffffffff) >> endSpace)); | |||
| } | |||
| void BitArray::setBitRangeAsInt (const int startBit, int numBits, unsigned int valueToSet) throw() | |||
| { | |||
| if (numBits > 32) | |||
| { | |||
| jassertfalse | |||
| numBits = 32; | |||
| } | |||
| for (int i = 0; i < numBits; ++i) | |||
| { | |||
| setBit (startBit + i, (valueToSet & 1) != 0); | |||
| valueToSet >>= 1; | |||
| } | |||
| } | |||
| //============================================================================== | |||
| bool BitArray::isNegative() const throw() | |||
| { | |||
| return negative && ! isEmpty(); | |||
| } | |||
| void BitArray::setNegative (const bool neg) throw() | |||
| { | |||
| negative = neg; | |||
| } | |||
| void BitArray::negate() throw() | |||
| { | |||
| negative = (! negative) && ! isEmpty(); | |||
| } | |||
| int BitArray::countNumberOfSetBits() const throw() | |||
| { | |||
| int total = 0; | |||
| for (int i = (highestBit >> 5) + 1; --i >= 0;) | |||
| { | |||
| unsigned int n = values[i]; | |||
| if (n == 0xffffffff) | |||
| { | |||
| total += 32; | |||
| } | |||
| else | |||
| { | |||
| while (n != 0) | |||
| { | |||
| total += (n & 1); | |||
| n >>= 1; | |||
| } | |||
| } | |||
| } | |||
| return total; | |||
| } | |||
| int BitArray::getHighestBit() const throw() | |||
| { | |||
| for (int i = highestBit + 1; --i >= 0;) | |||
| if ((values [i >> 5] & (1 << (i & 31))) != 0) | |||
| return i; | |||
| return -1; | |||
| } | |||
| int BitArray::findNextSetBit (int i) const throw() | |||
| { | |||
| for (; i <= highestBit; ++i) | |||
| if ((values [i >> 5] & (1 << (i & 31))) != 0) | |||
| return i; | |||
| return -1; | |||
| } | |||
| int BitArray::findNextClearBit (int i) const throw() | |||
| { | |||
| for (; i <= highestBit; ++i) | |||
| if ((values [i >> 5] & (1 << (i & 31))) == 0) | |||
| break; | |||
| return i; | |||
| } | |||
| void BitArray::ensureSize (const int numVals) throw() | |||
| { | |||
| if (numVals + 2 >= numValues) | |||
| { | |||
| int oldSize = numValues; | |||
| numValues = ((numVals + 2) * 3) / 2; | |||
| values = (unsigned int*) juce_realloc (values, sizeof (unsigned int) * numValues + 4); | |||
| while (oldSize < numValues) | |||
| values [oldSize++] = 0; | |||
| } | |||
| } | |||
| //============================================================================== | |||
| const String BitArray::toString (const int base, const int minimumNumCharacters) const throw() | |||
| { | |||
| String s; | |||
| BitArray v (*this); | |||
| if (base == 2 || base == 8 || base == 16) | |||
| { | |||
| const int bits = (base == 2) ? 1 : (base == 8 ? 3 : 4); | |||
| static const tchar* const hexDigits = T("0123456789abcdef"); | |||
| for (;;) | |||
| { | |||
| const int remainder = v.getBitRangeAsInt (0, bits); | |||
| v.shiftBits (-bits); | |||
| if (remainder == 0 && v.isEmpty()) | |||
| break; | |||
| s = String::charToString (hexDigits [remainder]) + s; | |||
| } | |||
| } | |||
| else if (base == 10) | |||
| { | |||
| const BitArray ten (10); | |||
| BitArray remainder; | |||
| for (;;) | |||
| { | |||
| v.divideBy (ten, remainder); | |||
| if (remainder.isEmpty() && v.isEmpty()) | |||
| break; | |||
| s = String (remainder.getBitRangeAsInt (0, 8)) + s; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| jassertfalse // can't do the specified base | |||
| return String::empty; | |||
| } | |||
| const int length = s.length(); | |||
| if (length < minimumNumCharacters) | |||
| s = String::repeatedString (T("0"), minimumNumCharacters - length); | |||
| return isNegative() ? T("-") + s : s; | |||
| } | |||
| void BitArray::parseString (const String& text, | |||
| const int base) throw() | |||
| { | |||
| clear(); | |||
| const tchar* t = (const tchar*) text; | |||
| if (base == 2 || base == 8 || base == 16) | |||
| { | |||
| const int bits = (base == 2) ? 1 : (base == 8 ? 3 : 4); | |||
| for (;;) | |||
| { | |||
| const tchar c = *t++; | |||
| const int digit = CharacterFunctions::getHexDigitValue (c); | |||
| if (((unsigned int) digit) < (unsigned int) base) | |||
| { | |||
| shiftBits (bits); | |||
| add (digit); | |||
| } | |||
| else if (c == 0) | |||
| { | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| else if (base == 10) | |||
| { | |||
| const BitArray ten ((unsigned int) 10); | |||
| for (;;) | |||
| { | |||
| const tchar c = *t++; | |||
| if (c >= T('0') && c <= T('9')) | |||
| { | |||
| multiplyBy (ten); | |||
| add ((int) (c - T('0'))); | |||
| } | |||
| else if (c == 0) | |||
| { | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| setNegative (text.trimStart().startsWithChar (T('-'))); | |||
| } | |||
| const MemoryBlock BitArray::toMemoryBlock() const throw() | |||
| { | |||
| const int numBytes = (getHighestBit() + 8) >> 3; | |||
| MemoryBlock mb (numBytes); | |||
| for (int i = 0; i < numBytes; ++i) | |||
| mb[i] = (uint8) getBitRangeAsInt (i << 3, 8); | |||
| return mb; | |||
| } | |||
| void BitArray::loadFromMemoryBlock (const MemoryBlock& data) throw() | |||
| { | |||
| clear(); | |||
| for (int i = data.getSize(); --i >= 0;) | |||
| this->setBitRangeAsInt (i << 3, 8, data [i]); | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,397 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_MemoryBlock.h" | |||
| //============================================================================== | |||
| MemoryBlock::MemoryBlock() throw() | |||
| : data (0), | |||
| size (0) | |||
| { | |||
| } | |||
| MemoryBlock::MemoryBlock (const int initialSize, | |||
| const bool initialiseToZero) throw() | |||
| { | |||
| if (initialSize > 0) | |||
| { | |||
| size = initialSize; | |||
| if (initialiseToZero) | |||
| data = (char*) juce_calloc (initialSize); | |||
| else | |||
| data = (char*) juce_malloc (initialSize); | |||
| } | |||
| else | |||
| { | |||
| data = 0; | |||
| size = 0; | |||
| } | |||
| } | |||
| MemoryBlock::MemoryBlock (const MemoryBlock& other) throw() | |||
| : data (0), | |||
| size (other.size) | |||
| { | |||
| if (size > 0) | |||
| { | |||
| jassert (other.data != 0); | |||
| data = (char*) juce_malloc (size); | |||
| memcpy (data, other.data, size); | |||
| } | |||
| } | |||
| MemoryBlock::MemoryBlock (const void* const dataToInitialiseFrom, | |||
| const int sizeInBytes) throw() | |||
| : data (0), | |||
| size (jmax (0, sizeInBytes)) | |||
| { | |||
| jassert (sizeInBytes >= 0); | |||
| if (size > 0) | |||
| { | |||
| jassert (dataToInitialiseFrom != 0); // non-zero size, but a zero pointer passed-in? | |||
| data = (char*) juce_malloc (size); | |||
| if (dataToInitialiseFrom != 0) | |||
| memcpy (data, dataToInitialiseFrom, size); | |||
| } | |||
| } | |||
| MemoryBlock::~MemoryBlock() throw() | |||
| { | |||
| jassert (size >= 0); // should never happen | |||
| jassert (size == 0 || data != 0); // non-zero size but no data allocated? | |||
| juce_free (data); | |||
| } | |||
| const MemoryBlock& MemoryBlock::operator= (const MemoryBlock& other) throw() | |||
| { | |||
| if (this != &other) | |||
| { | |||
| setSize (other.size, false); | |||
| memcpy (data, other.data, size); | |||
| } | |||
| return *this; | |||
| } | |||
| //============================================================================== | |||
| bool MemoryBlock::operator== (const MemoryBlock& other) const throw() | |||
| { | |||
| return (size == other.size) | |||
| && (memcmp (data, other.data, size) == 0); | |||
| } | |||
| bool MemoryBlock::operator!= (const MemoryBlock& other) const throw() | |||
| { | |||
| return ! operator== (other); | |||
| } | |||
| //============================================================================== | |||
| // this will resize the block to this size | |||
| void MemoryBlock::setSize (const int newSize, | |||
| const bool initialiseToZero) throw() | |||
| { | |||
| if (size != newSize) | |||
| { | |||
| if (newSize <= 0) | |||
| { | |||
| juce_free (data); | |||
| data = 0; | |||
| size = 0; | |||
| } | |||
| else | |||
| { | |||
| if (data != 0) | |||
| { | |||
| data = (char*) juce_realloc (data, newSize); | |||
| if (initialiseToZero && (newSize > size)) | |||
| zeromem (data + size, newSize - size); | |||
| } | |||
| else | |||
| { | |||
| if (initialiseToZero) | |||
| data = (char*) juce_calloc (newSize); | |||
| else | |||
| data = (char*) juce_malloc (newSize); | |||
| } | |||
| size = newSize; | |||
| } | |||
| } | |||
| } | |||
| void MemoryBlock::ensureSize (const int minimumSize, | |||
| const bool initialiseToZero) throw() | |||
| { | |||
| if (size < minimumSize) | |||
| setSize (minimumSize, initialiseToZero); | |||
| } | |||
| //============================================================================== | |||
| void MemoryBlock::fillWith (const uint8 value) throw() | |||
| { | |||
| memset (data, (int) value, size); | |||
| } | |||
| void MemoryBlock::append (const void* const srcData, | |||
| const int numBytes) throw() | |||
| { | |||
| if (numBytes > 0) | |||
| { | |||
| const int oldSize = size; | |||
| setSize (size + numBytes); | |||
| memcpy (data + oldSize, srcData, numBytes); | |||
| } | |||
| } | |||
| void MemoryBlock::copyFrom (const void* const src, int offset, int num) throw() | |||
| { | |||
| const char* d = (const char*) src; | |||
| if (offset < 0) | |||
| { | |||
| d -= offset; | |||
| num -= offset; | |||
| offset = 0; | |||
| } | |||
| if (offset + num > size) | |||
| num = size - offset; | |||
| if (num > 0) | |||
| memcpy (data + offset, d, num); | |||
| } | |||
| void MemoryBlock::copyTo (void* const dst, int offset, int num) const throw() | |||
| { | |||
| char* d = (char*) dst; | |||
| if (offset < 0) | |||
| { | |||
| zeromem (d, -offset); | |||
| d -= offset; | |||
| num += offset; | |||
| offset = 0; | |||
| } | |||
| if (offset + num > size) | |||
| { | |||
| const int newNum = size - offset; | |||
| zeromem (d + newNum, num - newNum); | |||
| num = newNum; | |||
| } | |||
| if (num > 0) | |||
| memcpy (d, data + offset, num); | |||
| } | |||
| void MemoryBlock::removeSection (int startByte, int numBytesToRemove) throw() | |||
| { | |||
| if (startByte < 0) | |||
| { | |||
| numBytesToRemove += startByte; | |||
| startByte = 0; | |||
| } | |||
| if (startByte + numBytesToRemove >= size) | |||
| { | |||
| setSize (startByte); | |||
| } | |||
| else if (numBytesToRemove > 0) | |||
| { | |||
| memmove (data + startByte, | |||
| data + startByte + numBytesToRemove, | |||
| size - (startByte + numBytesToRemove)); | |||
| setSize (size - numBytesToRemove); | |||
| } | |||
| } | |||
| const String MemoryBlock::toString() const throw() | |||
| { | |||
| return String (data, size); | |||
| } | |||
| //============================================================================== | |||
| int MemoryBlock::getBitRange (const int bitRangeStart, int numBits) const throw() | |||
| { | |||
| int res = 0; | |||
| int byte = bitRangeStart >> 3; | |||
| int offsetInByte = bitRangeStart & 7; | |||
| int bitsSoFar = 0; | |||
| while (numBits > 0 && byte < size) | |||
| { | |||
| const int bitsThisTime = jmin (numBits, 8 - offsetInByte); | |||
| const int mask = (0xff >> (8 - bitsThisTime)) << offsetInByte; | |||
| res |= (((data[byte] & mask) >> offsetInByte) << bitsSoFar); | |||
| bitsSoFar += bitsThisTime; | |||
| numBits -= bitsThisTime; | |||
| ++byte; | |||
| offsetInByte = 0; | |||
| } | |||
| return res; | |||
| } | |||
| void MemoryBlock::setBitRange (const int bitRangeStart, int numBits, int bitsToSet) throw() | |||
| { | |||
| int byte = bitRangeStart >> 3; | |||
| int offsetInByte = bitRangeStart & 7; | |||
| unsigned int mask = ~((((unsigned int)0xffffffff) << (32 - numBits)) >> (32 - numBits)); | |||
| while (numBits > 0 && byte < size) | |||
| { | |||
| const int bitsThisTime = jmin (numBits, 8 - offsetInByte); | |||
| const unsigned int tempMask = (mask << offsetInByte) | ~((((unsigned int)0xffffffff) >> offsetInByte) << offsetInByte); | |||
| const unsigned int tempBits = bitsToSet << offsetInByte; | |||
| data[byte] = (char)((data[byte] & tempMask) | tempBits); | |||
| ++byte; | |||
| numBits -= bitsThisTime; | |||
| bitsToSet >>= bitsThisTime; | |||
| mask >>= bitsThisTime; | |||
| offsetInByte = 0; | |||
| } | |||
| } | |||
| //============================================================================== | |||
| void MemoryBlock::loadFromHexString (const String& hex) throw() | |||
| { | |||
| ensureSize (hex.length() >> 1); | |||
| char* dest = data; | |||
| int i = 0; | |||
| for (;;) | |||
| { | |||
| int byte = 0; | |||
| for (int loop = 2; --loop >= 0;) | |||
| { | |||
| byte <<= 4; | |||
| for (;;) | |||
| { | |||
| const tchar c = hex [i++]; | |||
| if (c >= T('0') && c <= T('9')) | |||
| { | |||
| byte |= c - T('0'); | |||
| break; | |||
| } | |||
| else if (c >= T('a') && c <= T('z')) | |||
| { | |||
| byte |= c - (T('a') - 10); | |||
| break; | |||
| } | |||
| else if (c >= T('A') && c <= T('Z')) | |||
| { | |||
| byte |= c - (T('A') - 10); | |||
| break; | |||
| } | |||
| else if (c == 0) | |||
| { | |||
| setSize ((int) (dest - data)); | |||
| return; | |||
| } | |||
| } | |||
| } | |||
| *dest++ = (char) byte; | |||
| } | |||
| } | |||
| //============================================================================== | |||
| static const char* const encodingTable | |||
| = ".ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+"; | |||
| const String MemoryBlock::toBase64Encoding() const throw() | |||
| { | |||
| const int numChars = ((size << 3) + 5) / 6; | |||
| String destString (size); // store the length, followed by a '.', and then the data. | |||
| const int initialLen = destString.length(); | |||
| destString.preallocateStorage (initialLen + 2 + numChars); | |||
| tchar* d = const_cast <tchar*> (((const tchar*) destString) + initialLen); | |||
| *d++ = T('.'); | |||
| for (int i = 0; i < numChars; ++i) | |||
| *d++ = encodingTable [getBitRange (i * 6, 6)]; | |||
| *d++ = 0; | |||
| return destString; | |||
| } | |||
| bool MemoryBlock::fromBase64Encoding (const String& s) throw() | |||
| { | |||
| const int startPos = s.indexOfChar (T('.')) + 1; | |||
| if (startPos <= 0) | |||
| return false; | |||
| const int numBytesNeeded = s.substring (0, startPos - 1).getIntValue(); | |||
| setSize (numBytesNeeded, true); | |||
| const int numChars = s.length() - startPos; | |||
| const tchar* const srcChars = ((const tchar*) s) + startPos; | |||
| for (int i = 0; i < numChars; ++i) | |||
| { | |||
| const char c = (char) srcChars[i]; | |||
| for (int j = 0; j < 64; ++j) | |||
| { | |||
| if (encodingTable[j] == c) | |||
| { | |||
| setBitRange (i * 6, 6, j); | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| return true; | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,249 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_PropertySet.h" | |||
| #include "../threads/juce_ScopedLock.h" | |||
| #include "../text/juce_XmlDocument.h" | |||
| //============================================================================== | |||
| PropertySet::PropertySet (const bool ignoreCaseOfKeyNames) throw() | |||
| : properties (ignoreCaseOfKeyNames), | |||
| fallbackProperties (0), | |||
| ignoreCaseOfKeys (ignoreCaseOfKeyNames) | |||
| { | |||
| } | |||
| PropertySet::PropertySet (const PropertySet& other) throw() | |||
| : properties (other.properties), | |||
| fallbackProperties (other.fallbackProperties), | |||
| ignoreCaseOfKeys (other.ignoreCaseOfKeys) | |||
| { | |||
| } | |||
| const PropertySet& PropertySet::operator= (const PropertySet& other) throw() | |||
| { | |||
| properties = other.properties; | |||
| fallbackProperties = other.fallbackProperties; | |||
| ignoreCaseOfKeys = other.ignoreCaseOfKeys; | |||
| propertyChanged(); | |||
| return *this; | |||
| } | |||
| PropertySet::~PropertySet() | |||
| { | |||
| } | |||
| void PropertySet::clear() | |||
| { | |||
| const ScopedLock sl (lock); | |||
| if (properties.size() > 0) | |||
| { | |||
| properties.clear(); | |||
| propertyChanged(); | |||
| } | |||
| } | |||
| const String PropertySet::getValue (const String& keyName, | |||
| const String& defaultValue) const throw() | |||
| { | |||
| const ScopedLock sl (lock); | |||
| const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); | |||
| if (index >= 0) | |||
| return properties.getAllValues() [index]; | |||
| return fallbackProperties != 0 ? fallbackProperties->getValue (keyName, defaultValue) | |||
| : defaultValue; | |||
| } | |||
| int PropertySet::getIntValue (const String& keyName, | |||
| const int defaultValue) const throw() | |||
| { | |||
| const ScopedLock sl (lock); | |||
| const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); | |||
| if (index >= 0) | |||
| return properties.getAllValues() [index].getIntValue(); | |||
| return fallbackProperties != 0 ? fallbackProperties->getIntValue (keyName, defaultValue) | |||
| : defaultValue; | |||
| } | |||
| double PropertySet::getDoubleValue (const String& keyName, | |||
| const double defaultValue) const throw() | |||
| { | |||
| const ScopedLock sl (lock); | |||
| const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); | |||
| if (index >= 0) | |||
| return properties.getAllValues()[index].getDoubleValue(); | |||
| return fallbackProperties != 0 ? fallbackProperties->getDoubleValue (keyName, defaultValue) | |||
| : defaultValue; | |||
| } | |||
| bool PropertySet::getBoolValue (const String& keyName, | |||
| const bool defaultValue) const throw() | |||
| { | |||
| const ScopedLock sl (lock); | |||
| const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); | |||
| if (index >= 0) | |||
| return properties.getAllValues() [index].getIntValue() != 0; | |||
| return fallbackProperties != 0 ? fallbackProperties->getBoolValue (keyName, defaultValue) | |||
| : defaultValue; | |||
| } | |||
| XmlElement* PropertySet::getXmlValue (const String& keyName) const | |||
| { | |||
| XmlDocument doc (getValue (keyName)); | |||
| return doc.getDocumentElement(); | |||
| } | |||
| void PropertySet::setValue (const String& keyName, | |||
| const String& value) throw() | |||
| { | |||
| jassert (keyName.isNotEmpty()); // shouldn't use an empty key name! | |||
| if (keyName.isNotEmpty()) | |||
| { | |||
| const ScopedLock sl (lock); | |||
| const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); | |||
| if (index < 0 || properties.getAllValues() [index] != value) | |||
| { | |||
| properties.set (keyName, value); | |||
| propertyChanged(); | |||
| } | |||
| } | |||
| } | |||
| void PropertySet::removeValue (const String& keyName) throw() | |||
| { | |||
| if (keyName.isNotEmpty()) | |||
| { | |||
| const ScopedLock sl (lock); | |||
| const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); | |||
| if (index >= 0) | |||
| { | |||
| properties.remove (keyName); | |||
| propertyChanged(); | |||
| } | |||
| } | |||
| } | |||
| void PropertySet::setValue (const String& keyName, const tchar* const value) throw() | |||
| { | |||
| setValue (keyName, String (value)); | |||
| } | |||
| void PropertySet::setValue (const String& keyName, const int value) throw() | |||
| { | |||
| setValue (keyName, String (value)); | |||
| } | |||
| void PropertySet::setValue (const String& keyName, const double value) throw() | |||
| { | |||
| setValue (keyName, String (value)); | |||
| } | |||
| void PropertySet::setValue (const String& keyName, const bool value) throw() | |||
| { | |||
| setValue (keyName, String ((value) ? T("1") : T("0"))); | |||
| } | |||
| void PropertySet::setValue (const String& keyName, const XmlElement* const xml) | |||
| { | |||
| setValue (keyName, (xml == 0) ? String::empty | |||
| : xml->createDocument (String::empty, true)); | |||
| } | |||
| bool PropertySet::containsKey (const String& keyName) const throw() | |||
| { | |||
| const ScopedLock sl (lock); | |||
| return properties.getAllKeys().contains (keyName, ignoreCaseOfKeys); | |||
| } | |||
| void PropertySet::setFallbackPropertySet (PropertySet* fallbackProperties_) throw() | |||
| { | |||
| const ScopedLock sl (lock); | |||
| fallbackProperties = fallbackProperties_; | |||
| } | |||
| XmlElement* PropertySet::createXml (const String& nodeName) const throw() | |||
| { | |||
| const ScopedLock sl (lock); | |||
| XmlElement* const xml = new XmlElement (nodeName); | |||
| for (int i = 0; i < properties.getAllKeys().size(); ++i) | |||
| { | |||
| XmlElement* const e = new XmlElement (T("VALUE")); | |||
| e->setAttribute (T("name"), properties.getAllKeys()[i]); | |||
| e->setAttribute (T("val"), properties.getAllValues()[i]); | |||
| xml->addChildElement (e); | |||
| } | |||
| return xml; | |||
| } | |||
| void PropertySet::restoreFromXml (const XmlElement& xml) throw() | |||
| { | |||
| const ScopedLock sl (lock); | |||
| clear(); | |||
| forEachXmlChildElementWithTagName (xml, e, T("VALUE")) | |||
| { | |||
| if (e->hasAttribute (T("name")) | |||
| && e->hasAttribute (T("val"))) | |||
| { | |||
| properties.set (e->getStringAttribute (T("name")), | |||
| e->getStringAttribute (T("val"))); | |||
| } | |||
| } | |||
| if (properties.size() > 0) | |||
| propertyChanged(); | |||
| } | |||
| void PropertySet::propertyChanged() | |||
| { | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,792 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_REFERENCECOUNTEDARRAY_JUCEHEADER__ | |||
| #define __JUCE_REFERENCECOUNTEDARRAY_JUCEHEADER__ | |||
| #include "juce_ReferenceCountedObject.h" | |||
| #include "juce_ArrayAllocationBase.h" | |||
| #include "juce_ElementComparator.h" | |||
| #include "../threads/juce_CriticalSection.h" | |||
| //============================================================================== | |||
| /** | |||
| Holds a list of objects derived from ReferenceCountedObject. | |||
| A ReferenceCountedArray holds objects derived from ReferenceCountedObject, | |||
| and takes care of incrementing and decrementing their ref counts when they | |||
| are added and removed from the array. | |||
| To make all the array's methods thread-safe, pass in "CriticalSection" as the templated | |||
| TypeOfCriticalSectionToUse parameter, instead of the default DummyCriticalSection. | |||
| @see Array, OwnedArray, StringArray | |||
| */ | |||
| template <class ObjectClass, class TypeOfCriticalSectionToUse = DummyCriticalSection> | |||
| class ReferenceCountedArray : private ArrayAllocationBase <ObjectClass*> | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Creates an empty array. | |||
| @param granularity this is the size of increment by which the internal storage | |||
| used by the array will grow. Only change it from the default if you know the | |||
| array is going to be very big and needs to be able to grow efficiently. | |||
| @see ReferenceCountedObject, ArrayAllocationBase, Array, OwnedArray | |||
| */ | |||
| ReferenceCountedArray (const int granularity = juceDefaultArrayGranularity) throw() | |||
| : ArrayAllocationBase <ObjectClass*> (granularity), | |||
| numUsed (0) | |||
| { | |||
| } | |||
| /** Creates a copy of another array */ | |||
| ReferenceCountedArray (const ReferenceCountedArray<ObjectClass, TypeOfCriticalSectionToUse>& other) throw() | |||
| : ArrayAllocationBase <ObjectClass*> (other.granularity) | |||
| { | |||
| other.lockArray(); | |||
| numUsed = other.numUsed; | |||
| this->setAllocatedSize (numUsed); | |||
| memcpy (this->elements, other.elements, numUsed * sizeof (ObjectClass*)); | |||
| for (int i = numUsed; --i >= 0;) | |||
| if (this->elements[i] != 0) | |||
| this->elements[i]->incReferenceCount(); | |||
| other.unlockArray(); | |||
| } | |||
| /** Copies another array into this one. | |||
| Any existing objects in this array will first be released. | |||
| */ | |||
| const ReferenceCountedArray<ObjectClass, TypeOfCriticalSectionToUse>& operator= (const ReferenceCountedArray<ObjectClass, TypeOfCriticalSectionToUse>& other) throw() | |||
| { | |||
| if (this != &other) | |||
| { | |||
| other.lockArray(); | |||
| lock.enter(); | |||
| clear(); | |||
| this->granularity = other.granularity; | |||
| this->ensureAllocatedSize (other.numUsed); | |||
| numUsed = other.numUsed; | |||
| memcpy (this->elements, other.elements, numUsed * sizeof (ObjectClass*)); | |||
| minimiseStorageOverheads(); | |||
| for (int i = numUsed; --i >= 0;) | |||
| if (this->elements[i] != 0) | |||
| this->elements[i]->incReferenceCount(); | |||
| lock.exit(); | |||
| other.unlockArray(); | |||
| } | |||
| return *this; | |||
| } | |||
| /** Destructor. | |||
| Any objects in the array will be released, and may be deleted if not referenced from elsewhere. | |||
| */ | |||
| ~ReferenceCountedArray() | |||
| { | |||
| clear(); | |||
| } | |||
| //============================================================================== | |||
| /** Removes all objects from the array. | |||
| Any objects in the array that are not referenced from elsewhere will be deleted. | |||
| */ | |||
| void clear() | |||
| { | |||
| lock.enter(); | |||
| while (numUsed > 0) | |||
| if (this->elements [--numUsed] != 0) | |||
| this->elements [numUsed]->decReferenceCount(); | |||
| jassert (numUsed == 0); | |||
| this->setAllocatedSize (0); | |||
| lock.exit(); | |||
| } | |||
| /** Returns the current number of objects in the array. */ | |||
| inline int size() const throw() | |||
| { | |||
| return numUsed; | |||
| } | |||
| /** Returns a pointer to the object at this index in the array. | |||
| If the index is out-of-range, this will return a null pointer, (and | |||
| it could be null anyway, because it's ok for the array to hold null | |||
| pointers as well as objects). | |||
| @see getUnchecked | |||
| */ | |||
| inline const ReferenceCountedObjectPtr<ObjectClass> operator[] (const int index) const throw() | |||
| { | |||
| lock.enter(); | |||
| const ReferenceCountedObjectPtr<ObjectClass> result ((((unsigned int) index) < (unsigned int) numUsed) | |||
| ? this->elements [index] | |||
| : (ObjectClass*) 0); | |||
| lock.exit(); | |||
| return result; | |||
| } | |||
| /** Returns a pointer to the object at this index in the array, without checking whether the index is in-range. | |||
| This is a faster and less safe version of operator[] which doesn't check the index passed in, so | |||
| it can be used when you're sure the index if always going to be legal. | |||
| */ | |||
| inline const ReferenceCountedObjectPtr<ObjectClass> getUnchecked (const int index) const throw() | |||
| { | |||
| lock.enter(); | |||
| jassert (((unsigned int) index) < (unsigned int) numUsed); | |||
| const ReferenceCountedObjectPtr<ObjectClass> result (this->elements [index]); | |||
| lock.exit(); | |||
| return result; | |||
| } | |||
| /** Returns a pointer to the first object in the array. | |||
| This will return a null pointer if the array's empty. | |||
| @see getLast | |||
| */ | |||
| inline const ReferenceCountedObjectPtr<ObjectClass> getFirst() const throw() | |||
| { | |||
| lock.enter(); | |||
| const ReferenceCountedObjectPtr<ObjectClass> result ((numUsed > 0) ? this->elements [0] | |||
| : (ObjectClass*) 0); | |||
| lock.exit(); | |||
| return result; | |||
| } | |||
| /** Returns a pointer to the last object in the array. | |||
| This will return a null pointer if the array's empty. | |||
| @see getFirst | |||
| */ | |||
| inline const ReferenceCountedObjectPtr<ObjectClass> getLast() const throw() | |||
| { | |||
| lock.enter(); | |||
| const ReferenceCountedObjectPtr<ObjectClass> result ((numUsed > 0) ? this->elements [numUsed - 1] | |||
| : (ObjectClass*) 0); | |||
| lock.exit(); | |||
| return result; | |||
| } | |||
| //============================================================================== | |||
| /** Finds the index of the first occurrence of an object in the array. | |||
| @param objectToLookFor the object to look for | |||
| @returns the index at which the object was found, or -1 if it's not found | |||
| */ | |||
| int indexOf (const ObjectClass* const objectToLookFor) const throw() | |||
| { | |||
| int result = -1; | |||
| lock.enter(); | |||
| ObjectClass** e = this->elements; | |||
| for (int i = numUsed; --i >= 0;) | |||
| { | |||
| if (objectToLookFor == *e) | |||
| { | |||
| result = (int) (e - this->elements); | |||
| break; | |||
| } | |||
| ++e; | |||
| } | |||
| lock.exit(); | |||
| return result; | |||
| } | |||
| /** Returns true if the array contains a specified object. | |||
| @param objectToLookFor the object to look for | |||
| @returns true if the object is in the array | |||
| */ | |||
| bool contains (const ObjectClass* const objectToLookFor) const throw() | |||
| { | |||
| lock.enter(); | |||
| ObjectClass** e = this->elements; | |||
| for (int i = numUsed; --i >= 0;) | |||
| { | |||
| if (objectToLookFor == *e) | |||
| { | |||
| lock.exit(); | |||
| return true; | |||
| } | |||
| ++e; | |||
| } | |||
| lock.exit(); | |||
| return false; | |||
| } | |||
| /** Appends a new object to the end of the array. | |||
| This will increase the new object's reference count. | |||
| @param newObject the new object to add to the array | |||
| @see set, insert, addIfNotAlreadyThere, addSorted, addArray | |||
| */ | |||
| void add (ObjectClass* const newObject) throw() | |||
| { | |||
| lock.enter(); | |||
| this->ensureAllocatedSize (numUsed + 1); | |||
| this->elements [numUsed++] = newObject; | |||
| if (newObject != 0) | |||
| newObject->incReferenceCount(); | |||
| lock.exit(); | |||
| } | |||
| /** Inserts a new object into the array at the given index. | |||
| If the index is less than 0 or greater than the size of the array, the | |||
| element will be added to the end of the array. | |||
| Otherwise, it will be inserted into the array, moving all the later elements | |||
| along to make room. | |||
| This will increase the new object's reference count. | |||
| @param indexToInsertAt the index at which the new element should be inserted | |||
| @param newObject the new object to add to the array | |||
| @see add, addSorted, addIfNotAlreadyThere, set | |||
| */ | |||
| void insert (int indexToInsertAt, | |||
| ObjectClass* const newObject) throw() | |||
| { | |||
| if (indexToInsertAt >= 0) | |||
| { | |||
| lock.enter(); | |||
| if (indexToInsertAt > numUsed) | |||
| indexToInsertAt = numUsed; | |||
| this->ensureAllocatedSize (numUsed + 1); | |||
| ObjectClass** const e = this->elements + indexToInsertAt; | |||
| const int numToMove = numUsed - indexToInsertAt; | |||
| if (numToMove > 0) | |||
| memmove (e + 1, e, numToMove * sizeof (ObjectClass*)); | |||
| *e = newObject; | |||
| if (newObject != 0) | |||
| newObject->incReferenceCount(); | |||
| ++numUsed; | |||
| lock.exit(); | |||
| } | |||
| else | |||
| { | |||
| add (newObject); | |||
| } | |||
| } | |||
| /** Appends a new object at the end of the array as long as the array doesn't | |||
| already contain it. | |||
| If the array already contains a matching object, nothing will be done. | |||
| @param newObject the new object to add to the array | |||
| */ | |||
| void addIfNotAlreadyThere (ObjectClass* const newObject) throw() | |||
| { | |||
| lock.enter(); | |||
| if (! contains (newObject)) | |||
| add (newObject); | |||
| lock.exit(); | |||
| } | |||
| /** Replaces an object in the array with a different one. | |||
| If the index is less than zero, this method does nothing. | |||
| If the index is beyond the end of the array, the new object is added to the end of the array. | |||
| The object being added has its reference count increased, and if it's replacing | |||
| another object, then that one has its reference count decreased, and may be deleted. | |||
| @param indexToChange the index whose value you want to change | |||
| @param newObject the new value to set for this index. | |||
| @see add, insert, remove | |||
| */ | |||
| void set (const int indexToChange, | |||
| ObjectClass* const newObject) | |||
| { | |||
| if (indexToChange >= 0) | |||
| { | |||
| lock.enter(); | |||
| if (newObject != 0) | |||
| newObject->incReferenceCount(); | |||
| if (indexToChange < numUsed) | |||
| { | |||
| if (this->elements [indexToChange] != 0) | |||
| this->elements [indexToChange]->decReferenceCount(); | |||
| this->elements [indexToChange] = newObject; | |||
| } | |||
| else | |||
| { | |||
| this->ensureAllocatedSize (numUsed + 1); | |||
| this->elements [numUsed++] = newObject; | |||
| } | |||
| lock.exit(); | |||
| } | |||
| } | |||
| /** Adds elements from another array to the end of this array. | |||
| @param arrayToAddFrom the array from which to copy the elements | |||
| @param startIndex the first element of the other array to start copying from | |||
| @param numElementsToAdd how many elements to add from the other array. If this | |||
| value is negative or greater than the number of available elements, | |||
| all available elements will be copied. | |||
| @see add | |||
| */ | |||
| void addArray (const ReferenceCountedArray<ObjectClass, TypeOfCriticalSectionToUse>& arrayToAddFrom, | |||
| int startIndex = 0, | |||
| int numElementsToAdd = -1) throw() | |||
| { | |||
| arrayToAddFrom.lockArray(); | |||
| lock.enter(); | |||
| if (startIndex < 0) | |||
| { | |||
| jassertfalse | |||
| startIndex = 0; | |||
| } | |||
| if (numElementsToAdd < 0 || startIndex + numElementsToAdd > arrayToAddFrom.size()) | |||
| numElementsToAdd = arrayToAddFrom.size() - startIndex; | |||
| if (numElementsToAdd > 0) | |||
| { | |||
| this->ensureAllocatedSize (numUsed + numElementsToAdd); | |||
| while (--numElementsToAdd >= 0) | |||
| add (arrayToAddFrom.getUnchecked (startIndex++)); | |||
| } | |||
| lock.exit(); | |||
| arrayToAddFrom.unlockArray(); | |||
| } | |||
| /** Inserts a new object into the array assuming that the array is sorted. | |||
| This will use a comparator to find the position at which the new object | |||
| should go. If the array isn't sorted, the behaviour of this | |||
| method will be unpredictable. | |||
| @param comparator the comparator object to use to compare the elements - see the | |||
| sort() method for details about this object's form | |||
| @param newObject the new object to insert to the array | |||
| @see add, sort | |||
| */ | |||
| template <class ElementComparator> | |||
| void addSorted (ElementComparator& comparator, | |||
| ObjectClass* newObject) throw() | |||
| { | |||
| lock.enter(); | |||
| insert (findInsertIndexInSortedArray (comparator, this->elements, newObject, 0, numUsed), newObject); | |||
| lock.exit(); | |||
| } | |||
| /** Inserts or replaces an object in the array, assuming it is sorted. | |||
| This is similar to addSorted, but if a matching element already exists, then it will be | |||
| replaced by the new one, rather than the new one being added as well. | |||
| */ | |||
| template <class ElementComparator> | |||
| void addOrReplaceSorted (ElementComparator& comparator, | |||
| ObjectClass* newObject) throw() | |||
| { | |||
| lock.enter(); | |||
| const int index = findInsertIndexInSortedArray (comparator, this->elements, newObject, 0, numUsed); | |||
| if (index > 0 && comparator.compareElements (newObject, this->elements [index - 1]) == 0) | |||
| set (index - 1, newObject); // replace an existing object that matches | |||
| else | |||
| insert (index, newObject); // no match, so insert the new one | |||
| lock.exit(); | |||
| } | |||
| //============================================================================== | |||
| /** Removes an object from the array. | |||
| This will remove the object at a given index and move back all the | |||
| subsequent objects to close the gap. | |||
| If the index passed in is out-of-range, nothing will happen. | |||
| The object that is removed will have its reference count decreased, | |||
| and may be deleted if not referenced from elsewhere. | |||
| @param indexToRemove the index of the element to remove | |||
| @see removeObject, removeRange | |||
| */ | |||
| void remove (const int indexToRemove) | |||
| { | |||
| lock.enter(); | |||
| if (((unsigned int) indexToRemove) < (unsigned int) numUsed) | |||
| { | |||
| ObjectClass** const e = this->elements + indexToRemove; | |||
| if (*e != 0) | |||
| (*e)->decReferenceCount(); | |||
| --numUsed; | |||
| const int numberToShift = numUsed - indexToRemove; | |||
| if (numberToShift > 0) | |||
| memmove (e, e + 1, numberToShift * sizeof (ObjectClass*)); | |||
| if ((numUsed << 1) < this->numAllocated) | |||
| minimiseStorageOverheads(); | |||
| } | |||
| lock.exit(); | |||
| } | |||
| /** Removes the first occurrence of a specified object from the array. | |||
| If the item isn't found, no action is taken. If it is found, it is | |||
| removed and has its reference count decreased. | |||
| @param objectToRemove the object to try to remove | |||
| @see remove, removeRange | |||
| */ | |||
| void removeObject (ObjectClass* const objectToRemove) | |||
| { | |||
| lock.enter(); | |||
| remove (indexOf (objectToRemove)); | |||
| lock.exit(); | |||
| } | |||
| /** Removes a range of objects from the array. | |||
| This will remove a set of objects, starting from the given index, | |||
| and move any subsequent elements down to close the gap. | |||
| If the range extends beyond the bounds of the array, it will | |||
| be safely clipped to the size of the array. | |||
| The objects that are removed will have their reference counts decreased, | |||
| and may be deleted if not referenced from elsewhere. | |||
| @param startIndex the index of the first object to remove | |||
| @param numberToRemove how many objects should be removed | |||
| @see remove, removeObject | |||
| */ | |||
| void removeRange (const int startIndex, | |||
| const int numberToRemove) | |||
| { | |||
| lock.enter(); | |||
| const int start = jlimit (0, numUsed, startIndex); | |||
| const int end = jlimit (0, numUsed, startIndex + numberToRemove); | |||
| if (end > start) | |||
| { | |||
| int i; | |||
| for (i = start; i < end; ++i) | |||
| { | |||
| if (this->elements[i] != 0) | |||
| { | |||
| this->elements[i]->decReferenceCount(); | |||
| this->elements[i] = 0; // (in case one of the destructors accesses this array and hits a dangling pointer) | |||
| } | |||
| } | |||
| const int rangeSize = end - start; | |||
| ObjectClass** e = this->elements + start; | |||
| i = numUsed - end; | |||
| numUsed -= rangeSize; | |||
| while (--i >= 0) | |||
| { | |||
| *e = e [rangeSize]; | |||
| ++e; | |||
| } | |||
| if ((numUsed << 1) < this->numAllocated) | |||
| minimiseStorageOverheads(); | |||
| } | |||
| lock.exit(); | |||
| } | |||
| /** Removes the last n objects from the array. | |||
| The objects that are removed will have their reference counts decreased, | |||
| and may be deleted if not referenced from elsewhere. | |||
| @param howManyToRemove how many objects to remove from the end of the array | |||
| @see remove, removeObject, removeRange | |||
| */ | |||
| void removeLast (int howManyToRemove = 1) | |||
| { | |||
| lock.enter(); | |||
| if (howManyToRemove > numUsed) | |||
| howManyToRemove = numUsed; | |||
| while (--howManyToRemove >= 0) | |||
| remove (numUsed - 1); | |||
| lock.exit(); | |||
| } | |||
| /** Swaps a pair of objects in the array. | |||
| If either of the indexes passed in is out-of-range, nothing will happen, | |||
| otherwise the two objects at these positions will be exchanged. | |||
| */ | |||
| void swap (const int index1, | |||
| const int index2) throw() | |||
| { | |||
| lock.enter(); | |||
| if (((unsigned int) index1) < (unsigned int) numUsed | |||
| && ((unsigned int) index2) < (unsigned int) numUsed) | |||
| { | |||
| swapVariables (this->elements [index1], | |||
| this->elements [index2]); | |||
| } | |||
| lock.exit(); | |||
| } | |||
| /** Moves one of the objects to a different position. | |||
| This will move the object to a specified index, shuffling along | |||
| any intervening elements as required. | |||
| So for example, if you have the array { 0, 1, 2, 3, 4, 5 } then calling | |||
| move (2, 4) would result in { 0, 1, 3, 4, 2, 5 }. | |||
| @param currentIndex the index of the object to be moved. If this isn't a | |||
| valid index, then nothing will be done | |||
| @param newIndex the index at which you'd like this object to end up. If this | |||
| is less than zero, it will be moved to the end of the array | |||
| */ | |||
| void move (const int currentIndex, | |||
| int newIndex) throw() | |||
| { | |||
| if (currentIndex != newIndex) | |||
| { | |||
| lock.enter(); | |||
| if (((unsigned int) currentIndex) < (unsigned int) numUsed) | |||
| { | |||
| if (((unsigned int) newIndex) >= (unsigned int) numUsed) | |||
| newIndex = numUsed - 1; | |||
| ObjectClass* const value = this->elements [currentIndex]; | |||
| if (newIndex > currentIndex) | |||
| { | |||
| memmove (this->elements + currentIndex, | |||
| this->elements + currentIndex + 1, | |||
| (newIndex - currentIndex) * sizeof (ObjectClass*)); | |||
| } | |||
| else | |||
| { | |||
| memmove (this->elements + newIndex + 1, | |||
| this->elements + newIndex, | |||
| (currentIndex - newIndex) * sizeof (ObjectClass*)); | |||
| } | |||
| this->elements [newIndex] = value; | |||
| } | |||
| lock.exit(); | |||
| } | |||
| } | |||
| //============================================================================== | |||
| /** Compares this array to another one. | |||
| @returns true only if the other array contains the same objects in the same order | |||
| */ | |||
| bool operator== (const ReferenceCountedArray<ObjectClass, TypeOfCriticalSectionToUse>& other) const throw() | |||
| { | |||
| other.lockArray(); | |||
| lock.enter(); | |||
| bool result = numUsed == other.numUsed; | |||
| if (result) | |||
| { | |||
| for (int i = numUsed; --i >= 0;) | |||
| { | |||
| if (this->elements [i] != other.elements [i]) | |||
| { | |||
| result = false; | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| lock.exit(); | |||
| other.unlockArray(); | |||
| return result; | |||
| } | |||
| /** Compares this array to another one. | |||
| @see operator== | |||
| */ | |||
| bool operator!= (const ReferenceCountedArray<ObjectClass, TypeOfCriticalSectionToUse>& other) const throw() | |||
| { | |||
| return ! operator== (other); | |||
| } | |||
| //============================================================================== | |||
| /** Sorts the elements in the array. | |||
| This will use a comparator object to sort the elements into order. The object | |||
| passed must have a method of the form: | |||
| @code | |||
| int compareElements (ElementType first, ElementType second); | |||
| @endcode | |||
| ..and this method must return: | |||
| - a value of < 0 if the first comes before the second | |||
| - a value of 0 if the two objects are equivalent | |||
| - a value of > 0 if the second comes before the first | |||
| To improve performance, the compareElements() method can be declared as static or const. | |||
| @param comparator the comparator to use for comparing elements. | |||
| @param retainOrderOfEquivalentItems if this is true, then items | |||
| which the comparator says are equivalent will be | |||
| kept in the order in which they currently appear | |||
| in the array. This is slower to perform, but may | |||
| be important in some cases. If it's false, a faster | |||
| algorithm is used, but equivalent elements may be | |||
| rearranged. | |||
| @see sortArray | |||
| */ | |||
| template <class ElementComparator> | |||
| void sort (ElementComparator& comparator, | |||
| const bool retainOrderOfEquivalentItems = false) const throw() | |||
| { | |||
| (void) comparator; // if you pass in an object with a static compareElements() method, this | |||
| // avoids getting warning messages about the parameter being unused | |||
| lock.enter(); | |||
| sortArray (comparator, this->elements, 0, size() - 1, retainOrderOfEquivalentItems); | |||
| lock.exit(); | |||
| } | |||
| //============================================================================== | |||
| /** Reduces the amount of storage being used by the array. | |||
| Arrays typically allocate slightly more storage than they need, and after | |||
| removing elements, they may have quite a lot of unused space allocated. | |||
| This method will reduce the amount of allocated storage to a minimum. | |||
| */ | |||
| void minimiseStorageOverheads() throw() | |||
| { | |||
| lock.enter(); | |||
| if (numUsed == 0) | |||
| { | |||
| this->setAllocatedSize (0); | |||
| } | |||
| else | |||
| { | |||
| const int newAllocation = this->granularity * (numUsed / this->granularity + 1); | |||
| if (newAllocation < this->numAllocated) | |||
| this->setAllocatedSize (newAllocation); | |||
| } | |||
| lock.exit(); | |||
| } | |||
| //============================================================================== | |||
| /** Locks the array's CriticalSection. | |||
| Of course if the type of section used is a DummyCriticalSection, this won't | |||
| have any effect. | |||
| @see unlockArray | |||
| */ | |||
| void lockArray() const throw() | |||
| { | |||
| lock.enter(); | |||
| } | |||
| /** Unlocks the array's CriticalSection. | |||
| Of course if the type of section used is a DummyCriticalSection, this won't | |||
| have any effect. | |||
| @see lockArray | |||
| */ | |||
| void unlockArray() const throw() | |||
| { | |||
| lock.exit(); | |||
| } | |||
| //============================================================================== | |||
| juce_UseDebuggingNewOperator | |||
| private: | |||
| int numUsed; | |||
| TypeOfCriticalSectionToUse lock; | |||
| }; | |||
| #endif // __JUCE_REFERENCECOUNTEDARRAY_JUCEHEADER__ | |||
| @@ -0,0 +1,252 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_REFERENCECOUNTEDOBJECT_JUCEHEADER__ | |||
| #define __JUCE_REFERENCECOUNTEDOBJECT_JUCEHEADER__ | |||
| #include "../core/juce_Atomic.h" | |||
| //============================================================================== | |||
| /** | |||
| Adds reference-counting to an object. | |||
| To add reference-counting to a class, derive it from this class, and | |||
| use the ReferenceCountedObjectPtr class to point to it. | |||
| e.g. @code | |||
| class MyClass : public ReferenceCountedObject | |||
| { | |||
| void foo(); | |||
| // This is a neat way of declaring a typedef for a pointer class, | |||
| // rather than typing out the full templated name each time.. | |||
| typedef ReferenceCountedObjectPtr<MyClass> Ptr; | |||
| }; | |||
| MyClass::Ptr p = new MyClass(); | |||
| MyClass::Ptr p2 = p; | |||
| p = 0; | |||
| p2->foo(); | |||
| @endcode | |||
| Once a new ReferenceCountedObject has been assigned to a pointer, be | |||
| careful not to delete the object manually. | |||
| @see ReferenceCountedObjectPtr, ReferenceCountedArray | |||
| */ | |||
| class JUCE_API ReferenceCountedObject | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Increments the object's reference count. | |||
| This is done automatically by the smart pointer, but is public just | |||
| in case it's needed for nefarious purposes. | |||
| */ | |||
| inline void incReferenceCount() throw() | |||
| { | |||
| atomicIncrement (refCounts); | |||
| jassert (refCounts > 0); | |||
| } | |||
| /** Decreases the object's reference count. | |||
| If the count gets to zero, the object will be deleted. | |||
| */ | |||
| inline void decReferenceCount() throw() | |||
| { | |||
| jassert (refCounts > 0); | |||
| if (atomicDecrementAndReturn (refCounts) == 0) | |||
| delete this; | |||
| } | |||
| /** Returns the object's current reference count. */ | |||
| inline int getReferenceCount() const throw() | |||
| { | |||
| return refCounts; | |||
| } | |||
| protected: | |||
| //============================================================================== | |||
| /** Creates the reference-counted object (with an initial ref count of zero). */ | |||
| ReferenceCountedObject() | |||
| : refCounts (0) | |||
| { | |||
| } | |||
| /** Destructor. */ | |||
| virtual ~ReferenceCountedObject() | |||
| { | |||
| // it's dangerous to delete an object that's still referenced by something else! | |||
| jassert (refCounts == 0); | |||
| } | |||
| private: | |||
| //============================================================================== | |||
| int refCounts; | |||
| }; | |||
| //============================================================================== | |||
| /** | |||
| Used to point to an object of type ReferenceCountedObject. | |||
| It's wise to use a typedef instead of typing out the templated name | |||
| each time - e.g. | |||
| typedef ReferenceCountedObjectPtr<MyClass> MyClassPtr; | |||
| @see ReferenceCountedObject, ReferenceCountedObjectArray | |||
| */ | |||
| template <class ReferenceCountedObjectClass> | |||
| class ReferenceCountedObjectPtr | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Creates a pointer to a null object. */ | |||
| inline ReferenceCountedObjectPtr() throw() | |||
| : referencedObject (0) | |||
| { | |||
| } | |||
| /** Creates a pointer to an object. | |||
| This will increment the object's reference-count if it is non-null. | |||
| */ | |||
| inline ReferenceCountedObjectPtr (ReferenceCountedObjectClass* const refCountedObject) throw() | |||
| : referencedObject (refCountedObject) | |||
| { | |||
| if (refCountedObject != 0) | |||
| refCountedObject->incReferenceCount(); | |||
| } | |||
| /** Copies another pointer. | |||
| This will increment the object's reference-count (if it is non-null). | |||
| */ | |||
| inline ReferenceCountedObjectPtr (const ReferenceCountedObjectPtr<ReferenceCountedObjectClass>& other) throw() | |||
| : referencedObject (other.referencedObject) | |||
| { | |||
| if (referencedObject != 0) | |||
| referencedObject->incReferenceCount(); | |||
| } | |||
| /** Changes this pointer to point at a different object. | |||
| The reference count of the old object is decremented, and it might be | |||
| deleted if it hits zero. The new object's count is incremented. | |||
| */ | |||
| const ReferenceCountedObjectPtr<ReferenceCountedObjectClass>& operator= (const ReferenceCountedObjectPtr<ReferenceCountedObjectClass>& other) | |||
| { | |||
| ReferenceCountedObjectClass* const newObject = other.referencedObject; | |||
| if (newObject != referencedObject) | |||
| { | |||
| if (newObject != 0) | |||
| newObject->incReferenceCount(); | |||
| ReferenceCountedObjectClass* const oldObject = referencedObject; | |||
| referencedObject = newObject; | |||
| if (oldObject != 0) | |||
| oldObject->decReferenceCount(); | |||
| } | |||
| return *this; | |||
| } | |||
| /** Changes this pointer to point at a different object. | |||
| The reference count of the old object is decremented, and it might be | |||
| deleted if it hits zero. The new object's count is incremented. | |||
| */ | |||
| const ReferenceCountedObjectPtr<ReferenceCountedObjectClass>& operator= (ReferenceCountedObjectClass* const newObject) | |||
| { | |||
| if (referencedObject != newObject) | |||
| { | |||
| if (newObject != 0) | |||
| newObject->incReferenceCount(); | |||
| ReferenceCountedObjectClass* const oldObject = referencedObject; | |||
| referencedObject = newObject; | |||
| if (oldObject != 0) | |||
| oldObject->decReferenceCount(); | |||
| } | |||
| return *this; | |||
| } | |||
| /** Destructor. | |||
| This will decrement the object's reference-count, and may delete it if it | |||
| gets to zero. | |||
| */ | |||
| inline ~ReferenceCountedObjectPtr() | |||
| { | |||
| if (referencedObject != 0) | |||
| referencedObject->decReferenceCount(); | |||
| } | |||
| /** Returns the object that this pointer references. | |||
| The pointer returned may be zero, of course. | |||
| */ | |||
| inline operator ReferenceCountedObjectClass*() const throw() | |||
| { | |||
| return referencedObject; | |||
| } | |||
| /** Returns true if this pointer refers to the given object. */ | |||
| inline bool operator== (ReferenceCountedObjectClass* const object) const throw() | |||
| { | |||
| return referencedObject == object; | |||
| } | |||
| /** Returns true if this pointer doesn't refer to the given object. */ | |||
| inline bool operator!= (ReferenceCountedObjectClass* const object) const throw() | |||
| { | |||
| return referencedObject != object; | |||
| } | |||
| // the -> operator is called on the referenced object | |||
| inline ReferenceCountedObjectClass* operator->() const throw() | |||
| { | |||
| return referencedObject; | |||
| } | |||
| private: | |||
| //============================================================================== | |||
| ReferenceCountedObjectClass* referencedObject; | |||
| }; | |||
| #endif // __JUCE_REFERENCECOUNTEDOBJECT_JUCEHEADER__ | |||
| @@ -0,0 +1,438 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_Variant.h" | |||
| //============================================================================== | |||
| var::var() throw() | |||
| : type (voidType) | |||
| { | |||
| value.doubleValue = 0; | |||
| } | |||
| void var::releaseValue() throw() | |||
| { | |||
| if (type == stringType) | |||
| delete value.stringValue; | |||
| else if (type == objectType && value.objectValue != 0) | |||
| value.objectValue->decReferenceCount(); | |||
| } | |||
| var::~var() | |||
| { | |||
| releaseValue(); | |||
| } | |||
| //============================================================================== | |||
| var::var (const var& valueToCopy) throw() | |||
| : type (valueToCopy.type), | |||
| value (valueToCopy.value) | |||
| { | |||
| if (type == stringType) | |||
| value.stringValue = new String (*(value.stringValue)); | |||
| else if (type == objectType && value.objectValue != 0) | |||
| value.objectValue->incReferenceCount(); | |||
| } | |||
| var::var (const int value_) throw() | |||
| : type (intType) | |||
| { | |||
| value.intValue = value_; | |||
| } | |||
| var::var (const bool value_) throw() | |||
| : type (boolType) | |||
| { | |||
| value.boolValue = value_; | |||
| } | |||
| var::var (const double value_) throw() | |||
| : type (doubleType) | |||
| { | |||
| value.doubleValue = value_; | |||
| } | |||
| var::var (const String& value_) throw() | |||
| : type (stringType) | |||
| { | |||
| value.stringValue = new String (value_); | |||
| } | |||
| var::var (const char* const value_) throw() | |||
| : type (stringType) | |||
| { | |||
| value.stringValue = new String (value_); | |||
| } | |||
| var::var (const juce_wchar* const value_) throw() | |||
| : type (stringType) | |||
| { | |||
| value.stringValue = new String (value_); | |||
| } | |||
| var::var (DynamicObject* const object) throw() | |||
| : type (objectType) | |||
| { | |||
| value.objectValue = object; | |||
| if (object != 0) | |||
| object->incReferenceCount(); | |||
| } | |||
| var::var (MethodFunction method_) throw() | |||
| : type (methodType) | |||
| { | |||
| value.methodValue = method_; | |||
| } | |||
| //============================================================================== | |||
| const var& var::operator= (const var& valueToCopy) throw() | |||
| { | |||
| if (this != &valueToCopy) | |||
| { | |||
| if (type == stringType) | |||
| delete value.stringValue; | |||
| DynamicObject* const oldObject = getObject(); | |||
| type = valueToCopy.type; | |||
| value = valueToCopy.value; | |||
| if (type == stringType) | |||
| value.stringValue = new String (*(value.stringValue)); | |||
| else if (type == objectType && value.objectValue != 0) | |||
| value.objectValue->incReferenceCount(); | |||
| if (oldObject != 0) | |||
| oldObject->decReferenceCount(); | |||
| } | |||
| return *this; | |||
| } | |||
| const var& var::operator= (const int value_) throw() | |||
| { | |||
| releaseValue(); | |||
| type = intType; | |||
| value.intValue = value_; | |||
| return *this; | |||
| } | |||
| const var& var::operator= (const bool value_) throw() | |||
| { | |||
| releaseValue(); | |||
| type = boolType; | |||
| value.boolValue = value_; | |||
| return *this; | |||
| } | |||
| const var& var::operator= (const double value_) throw() | |||
| { | |||
| releaseValue(); | |||
| type = doubleType; | |||
| value.doubleValue = value_; | |||
| return *this; | |||
| } | |||
| const var& var::operator= (const char* const value_) throw() | |||
| { | |||
| releaseValue(); | |||
| type = stringType; | |||
| value.stringValue = new String (value_); | |||
| return *this; | |||
| } | |||
| const var& var::operator= (const juce_wchar* const value_) throw() | |||
| { | |||
| releaseValue(); | |||
| type = stringType; | |||
| value.stringValue = new String (value_); | |||
| return *this; | |||
| } | |||
| const var& var::operator= (const String& value_) throw() | |||
| { | |||
| releaseValue(); | |||
| type = stringType; | |||
| value.stringValue = new String (value_); | |||
| return *this; | |||
| } | |||
| const var& var::operator= (DynamicObject* const value_) throw() | |||
| { | |||
| value_->incReferenceCount(); | |||
| releaseValue(); | |||
| type = objectType; | |||
| value.objectValue = value_; | |||
| return *this; | |||
| } | |||
| const var& var::operator= (MethodFunction method_) throw() | |||
| { | |||
| releaseValue(); | |||
| type = doubleType; | |||
| value.methodValue = method_; | |||
| return *this; | |||
| } | |||
| //============================================================================== | |||
| var::operator int() const throw() | |||
| { | |||
| switch (type) | |||
| { | |||
| case voidType: | |||
| case objectType: break; | |||
| case intType: return value.intValue; | |||
| case boolType: return value.boolValue ? 1 : 0; | |||
| case doubleType: return (int) value.doubleValue; | |||
| case stringType: return value.stringValue->getIntValue(); | |||
| default: jassertfalse; break; | |||
| } | |||
| return 0; | |||
| } | |||
| var::operator bool() const throw() | |||
| { | |||
| switch (type) | |||
| { | |||
| case voidType: break; | |||
| case objectType: return value.objectValue != 0; | |||
| case intType: return value.intValue != 0; | |||
| case boolType: return value.boolValue; | |||
| case doubleType: return value.doubleValue != 0; | |||
| case stringType: return value.stringValue->getIntValue() != 0 | |||
| || value.stringValue->trim().equalsIgnoreCase (T("true")) | |||
| || value.stringValue->trim().equalsIgnoreCase (T("yes")); | |||
| default: jassertfalse; break; | |||
| } | |||
| return false; | |||
| } | |||
| var::operator double() const throw() | |||
| { | |||
| switch (type) | |||
| { | |||
| case voidType: | |||
| case objectType: break; | |||
| case intType: return value.intValue; | |||
| case boolType: return value.boolValue ? 1.0 : 0.0; | |||
| case doubleType: return value.doubleValue; | |||
| case stringType: return value.stringValue->getDoubleValue(); | |||
| default: jassertfalse; break; | |||
| } | |||
| return 0; | |||
| } | |||
| const String var::toString() const throw() | |||
| { | |||
| switch (type) | |||
| { | |||
| case voidType: | |||
| case objectType: return "Object 0x" + String::toHexString ((int) (pointer_sized_int) value.objectValue); | |||
| case intType: return String (value.intValue); | |||
| case boolType: return value.boolValue ? T("1") : T("0"); | |||
| case doubleType: return String (value.doubleValue); | |||
| case stringType: return *(value.stringValue); | |||
| default: jassertfalse; break; | |||
| } | |||
| return String::empty; | |||
| } | |||
| var::operator const String() const throw() | |||
| { | |||
| return toString(); | |||
| } | |||
| DynamicObject* var::getObject() const throw() | |||
| { | |||
| return type == objectType ? value.objectValue : 0; | |||
| } | |||
| const var var::operator[] (const var::identifier& propertyName) const throw() | |||
| { | |||
| if (type == objectType && value.objectValue != 0) | |||
| return value.objectValue->getProperty (propertyName); | |||
| return var(); | |||
| } | |||
| const var var::invoke (const var::identifier& method, const var* arguments, int numArguments) const | |||
| { | |||
| if (type == objectType && value.objectValue != 0) | |||
| return value.objectValue->invokeMethod (method, arguments, numArguments); | |||
| return var(); | |||
| } | |||
| const var var::invoke (const var& targetObject, const var* arguments, int numArguments) const | |||
| { | |||
| if (isMethod()) | |||
| { | |||
| DynamicObject* const target = targetObject.getObject(); | |||
| if (target != 0) | |||
| return (target->*(value.methodValue)) (arguments, numArguments); | |||
| } | |||
| return var(); | |||
| } | |||
| const var var::call (const var::identifier& method) const | |||
| { | |||
| return invoke (method, 0, 0); | |||
| } | |||
| const var var::call (const var::identifier& method, const var& arg1) const | |||
| { | |||
| return invoke (method, &arg1, 1); | |||
| } | |||
| const var var::call (const var::identifier& method, const var& arg1, const var& arg2) const | |||
| { | |||
| var args[] = { arg1, arg2 }; | |||
| return invoke (method, args, 2); | |||
| } | |||
| const var var::call (const var::identifier& method, const var& arg1, const var& arg2, const var& arg3) | |||
| { | |||
| var args[] = { arg1, arg2, arg3 }; | |||
| return invoke (method, args, 3); | |||
| } | |||
| const var var::call (const var::identifier& method, const var& arg1, const var& arg2, const var& arg3, const var& arg4) const | |||
| { | |||
| var args[] = { arg1, arg2, arg3, arg4 }; | |||
| return invoke (method, args, 4); | |||
| } | |||
| const var var::call (const var::identifier& method, const var& arg1, const var& arg2, const var& arg3, const var& arg4, const var& arg5) const | |||
| { | |||
| var args[] = { arg1, arg2, arg3, arg4, arg5 }; | |||
| return invoke (method, args, 5); | |||
| } | |||
| //============================================================================== | |||
| var::identifier::identifier (const String& name_) throw() | |||
| : name (name_), | |||
| hashCode (name_.hashCode()) | |||
| { | |||
| } | |||
| var::identifier::identifier (const char* const name_) throw() | |||
| : name (name_), | |||
| hashCode (name.hashCode()) | |||
| { | |||
| } | |||
| var::identifier::~identifier() throw() | |||
| { | |||
| } | |||
| //============================================================================== | |||
| //============================================================================== | |||
| DynamicObject::DynamicObject() | |||
| { | |||
| } | |||
| DynamicObject::~DynamicObject() | |||
| { | |||
| } | |||
| bool DynamicObject::hasProperty (const var::identifier& propertyName) const | |||
| { | |||
| const int index = propertyIds.indexOf (propertyName.hashCode); | |||
| return index >= 0 && ! propertyValues.getUnchecked (index)->isMethod(); | |||
| } | |||
| const var DynamicObject::getProperty (const var::identifier& propertyName) const | |||
| { | |||
| const int index = propertyIds.indexOf (propertyName.hashCode); | |||
| if (index >= 0) | |||
| return *propertyValues.getUnchecked (index); | |||
| return var(); | |||
| } | |||
| void DynamicObject::setProperty (const var::identifier& propertyName, const var& newValue) | |||
| { | |||
| const int index = propertyIds.indexOf (propertyName.hashCode); | |||
| if (index >= 0) | |||
| { | |||
| propertyValues.set (index, new var (newValue)); | |||
| } | |||
| else | |||
| { | |||
| propertyIds.add (propertyName.hashCode); | |||
| propertyValues.add (new var (newValue)); | |||
| } | |||
| } | |||
| void DynamicObject::removeProperty (const var::identifier& propertyName) | |||
| { | |||
| const int index = propertyIds.indexOf (propertyName.hashCode); | |||
| if (index >= 0) | |||
| { | |||
| propertyIds.remove (index); | |||
| propertyValues.remove (index); | |||
| } | |||
| } | |||
| bool DynamicObject::hasMethod (const var::identifier& methodName) const | |||
| { | |||
| return getProperty (methodName).isMethod(); | |||
| } | |||
| const var DynamicObject::invokeMethod (const var::identifier& methodName, | |||
| const var* parameters, | |||
| int numParameters) | |||
| { | |||
| return getProperty (methodName).invoke (this, parameters, numParameters); | |||
| } | |||
| void DynamicObject::setMethod (const var::identifier& name, | |||
| var::MethodFunction methodFunction) | |||
| { | |||
| setProperty (name, methodFunction); | |||
| } | |||
| void DynamicObject::clear() | |||
| { | |||
| propertyIds.clear(); | |||
| propertyValues.clear(); | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,453 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_BlowFish.h" | |||
| //============================================================================== | |||
| static const uint32 initialPValues [18] = | |||
| { | |||
| 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, | |||
| 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, | |||
| 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, | |||
| 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, | |||
| 0x9216d5d9, 0x8979fb1b | |||
| }; | |||
| static const uint32 initialSValues [4 * 256] = | |||
| { | |||
| 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, | |||
| 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, | |||
| 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, | |||
| 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, | |||
| 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, | |||
| 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, | |||
| 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, | |||
| 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, | |||
| 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, | |||
| 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, | |||
| 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, | |||
| 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, | |||
| 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, | |||
| 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, | |||
| 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, | |||
| 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, | |||
| 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, | |||
| 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, | |||
| 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, | |||
| 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, | |||
| 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, | |||
| 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, | |||
| 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, | |||
| 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, | |||
| 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, | |||
| 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, | |||
| 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, | |||
| 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, | |||
| 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, | |||
| 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, | |||
| 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, | |||
| 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, | |||
| 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, | |||
| 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, | |||
| 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, | |||
| 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, | |||
| 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, | |||
| 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, | |||
| 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, | |||
| 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, | |||
| 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, | |||
| 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, | |||
| 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, | |||
| 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, | |||
| 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, | |||
| 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, | |||
| 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, | |||
| 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, | |||
| 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, | |||
| 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, | |||
| 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, | |||
| 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, | |||
| 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, | |||
| 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, | |||
| 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, | |||
| 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, | |||
| 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, | |||
| 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, | |||
| 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, | |||
| 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, | |||
| 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, | |||
| 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, | |||
| 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, | |||
| 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a, | |||
| 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, | |||
| 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, | |||
| 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, | |||
| 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, | |||
| 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, | |||
| 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, | |||
| 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, | |||
| 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, | |||
| 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, | |||
| 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, | |||
| 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, | |||
| 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, | |||
| 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, | |||
| 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, | |||
| 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, | |||
| 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, | |||
| 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, | |||
| 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, | |||
| 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, | |||
| 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, | |||
| 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, | |||
| 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, | |||
| 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, | |||
| 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, | |||
| 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, | |||
| 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, | |||
| 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, | |||
| 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, | |||
| 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, | |||
| 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, | |||
| 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, | |||
| 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, | |||
| 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, | |||
| 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, | |||
| 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, | |||
| 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, | |||
| 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, | |||
| 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, | |||
| 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, | |||
| 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, | |||
| 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, | |||
| 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, | |||
| 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, | |||
| 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, | |||
| 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, | |||
| 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, | |||
| 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, | |||
| 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, | |||
| 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, | |||
| 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, | |||
| 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, | |||
| 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, | |||
| 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, | |||
| 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, | |||
| 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, | |||
| 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, | |||
| 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, | |||
| 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, | |||
| 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, | |||
| 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, | |||
| 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, | |||
| 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, | |||
| 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, | |||
| 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7, | |||
| 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, | |||
| 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, | |||
| 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, | |||
| 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, | |||
| 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, | |||
| 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, | |||
| 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, | |||
| 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, | |||
| 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, | |||
| 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, | |||
| 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, | |||
| 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, | |||
| 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, | |||
| 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, | |||
| 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, | |||
| 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, | |||
| 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, | |||
| 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, | |||
| 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, | |||
| 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, | |||
| 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, | |||
| 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, | |||
| 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, | |||
| 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, | |||
| 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, | |||
| 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, | |||
| 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, | |||
| 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, | |||
| 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, | |||
| 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, | |||
| 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, | |||
| 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, | |||
| 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, | |||
| 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, | |||
| 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, | |||
| 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, | |||
| 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, | |||
| 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, | |||
| 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, | |||
| 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, | |||
| 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, | |||
| 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, | |||
| 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, | |||
| 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, | |||
| 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, | |||
| 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, | |||
| 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, | |||
| 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, | |||
| 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, | |||
| 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, | |||
| 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, | |||
| 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, | |||
| 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, | |||
| 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, | |||
| 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, | |||
| 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, | |||
| 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, | |||
| 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, | |||
| 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, | |||
| 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, | |||
| 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, | |||
| 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, | |||
| 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, | |||
| 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, | |||
| 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, | |||
| 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, | |||
| 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, | |||
| 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, | |||
| 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, | |||
| 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, | |||
| 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, | |||
| 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, | |||
| 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, | |||
| 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, | |||
| 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, | |||
| 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, | |||
| 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, | |||
| 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, | |||
| 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, | |||
| 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, | |||
| 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, | |||
| 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, | |||
| 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, | |||
| 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, | |||
| 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, | |||
| 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, | |||
| 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, | |||
| 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, | |||
| 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, | |||
| 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, | |||
| 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, | |||
| 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, | |||
| 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, | |||
| 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, | |||
| 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, | |||
| 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, | |||
| 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, | |||
| 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, | |||
| 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, | |||
| 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, | |||
| 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, | |||
| 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, | |||
| 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, | |||
| 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, | |||
| 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, | |||
| 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, | |||
| 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, | |||
| 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, | |||
| 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, | |||
| 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, | |||
| 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, | |||
| 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, | |||
| 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, | |||
| 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, | |||
| 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, | |||
| 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, | |||
| 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, | |||
| 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, | |||
| 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, | |||
| 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, | |||
| 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, | |||
| 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, | |||
| 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, | |||
| 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, | |||
| 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, | |||
| 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, | |||
| 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, | |||
| 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6 | |||
| }; | |||
| //============================================================================== | |||
| BlowFish::BlowFish (const uint8* keyData, int keyBytes) | |||
| { | |||
| memcpy (p, initialPValues, sizeof (p)); | |||
| int i, j; | |||
| for (i = 4; --i >= 0;) | |||
| { | |||
| s[i] = (uint32*) juce_malloc (256 * sizeof (uint32)); | |||
| memcpy (s[i], initialSValues + i * 256, 256 * sizeof (uint32)); | |||
| } | |||
| j = 0; | |||
| for (i = 0; i < 18; ++i) | |||
| { | |||
| uint32 d = 0; | |||
| for (int k = 0; k < 4; ++k) | |||
| { | |||
| d = (d << 8) | keyData[j]; | |||
| if (++j >= keyBytes) | |||
| j = 0; | |||
| } | |||
| p[i] = initialPValues[i] ^ d; | |||
| } | |||
| uint32 l = 0, r = 0; | |||
| for (i = 0; i < 18; i += 2) | |||
| { | |||
| encrypt (l, r); | |||
| p[i] = l; | |||
| p[i + 1] = r; | |||
| } | |||
| for (i = 0; i < 4; ++i) | |||
| { | |||
| for (j = 0; j < 256; j += 2) | |||
| { | |||
| encrypt (l, r); | |||
| s[i][j] = l; | |||
| s[i][j + 1] = r; | |||
| } | |||
| } | |||
| } | |||
| BlowFish::BlowFish (const BlowFish& other) | |||
| { | |||
| for (int i = 4; --i >= 0;) | |||
| s[i] = (uint32*) juce_malloc (256 * sizeof (uint32)); | |||
| operator= (other); | |||
| } | |||
| const BlowFish& BlowFish::operator= (const BlowFish& other) | |||
| { | |||
| memcpy (p, other.p, sizeof (p)); | |||
| for (int i = 4; --i >= 0;) | |||
| memcpy (s[i], other.s[i], 256 * sizeof (uint32)); | |||
| return *this; | |||
| } | |||
| BlowFish::~BlowFish() | |||
| { | |||
| for (int i = 4; --i >= 0;) | |||
| juce_free (s[i]); | |||
| } | |||
| uint32 BlowFish::F (uint32 x) const | |||
| { | |||
| uint16 a, b, c, d; | |||
| uint32 y; | |||
| d = (uint16) (x & 0xff); | |||
| x >>= 8; | |||
| c = (uint16) (x & 0xff); | |||
| x >>= 8; | |||
| b = (uint16) (x & 0xff); | |||
| x >>= 8; | |||
| a = (uint16) (x & 0xff); | |||
| y = s[0][a] + s[1][b]; | |||
| y = y ^ s[2][c]; | |||
| y = y + s[3][d]; | |||
| return y; | |||
| } | |||
| void BlowFish::encrypt (uint32& data1, | |||
| uint32& data2) const | |||
| { | |||
| uint32 l = data1; | |||
| uint32 r = data2; | |||
| for (int i = 0; i < 16; ++i) | |||
| { | |||
| l = l ^ p[i]; | |||
| r = F (l) ^ r; | |||
| const uint32 temp = l; | |||
| l = r; | |||
| r = temp; | |||
| } | |||
| const uint32 temp = l; | |||
| l = r; | |||
| r = temp; | |||
| r = r ^ p[16]; | |||
| l = l ^ p[17]; | |||
| data1 = l; | |||
| data2 = r; | |||
| } | |||
| void BlowFish::decrypt (uint32& data1, | |||
| uint32& data2) const | |||
| { | |||
| uint32 l = data1; | |||
| uint32 r = data2; | |||
| for (int i = 17; i > 1; --i) | |||
| { | |||
| l =l ^ p[i]; | |||
| r = F (l) ^ r; | |||
| const uint32 temp = l; | |||
| l = r; | |||
| r = temp; | |||
| } | |||
| const uint32 temp = l; | |||
| l = r; | |||
| r = temp; | |||
| r = r ^ p[1]; | |||
| l = l ^ p[0]; | |||
| data1 = l; | |||
| data2 = r; | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,382 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_MD5.h" | |||
| #include "../io/files/juce_FileInputStream.h" | |||
| //============================================================================== | |||
| MD5::MD5() | |||
| { | |||
| zeromem (result, sizeof (result)); | |||
| } | |||
| MD5::MD5 (const MD5& other) | |||
| { | |||
| memcpy (result, other.result, sizeof (result)); | |||
| } | |||
| const MD5& MD5::operator= (const MD5& other) | |||
| { | |||
| memcpy (result, other.result, sizeof (result)); | |||
| return *this; | |||
| } | |||
| //============================================================================== | |||
| MD5::MD5 (const MemoryBlock& data) | |||
| { | |||
| ProcessContext context; | |||
| context.processBlock ((const uint8*) data.getData(), data.getSize()); | |||
| context.finish (result); | |||
| } | |||
| MD5::MD5 (const char* data, const int numBytes) | |||
| { | |||
| ProcessContext context; | |||
| context.processBlock ((const uint8*) data, numBytes); | |||
| context.finish (result); | |||
| } | |||
| MD5::MD5 (const String& text) | |||
| { | |||
| ProcessContext context; | |||
| const int len = text.length(); | |||
| const juce_wchar* const t = text; | |||
| for (int i = 0; i < len; ++i) | |||
| { | |||
| // force the string into integer-sized unicode characters, to try to make it | |||
| // get the same results on all platforms + compilers. | |||
| uint32 unicodeChar = (uint32) t[i]; | |||
| swapIfBigEndian (unicodeChar); | |||
| context.processBlock ((const uint8*) &unicodeChar, | |||
| sizeof (unicodeChar)); | |||
| } | |||
| context.finish (result); | |||
| } | |||
| void MD5::processStream (InputStream& input, int numBytesToRead) | |||
| { | |||
| ProcessContext context; | |||
| if (numBytesToRead < 0) | |||
| numBytesToRead = INT_MAX; | |||
| while (numBytesToRead > 0) | |||
| { | |||
| char tempBuffer [512]; | |||
| const int bytesRead = input.read (tempBuffer, jmin (numBytesToRead, sizeof (tempBuffer))); | |||
| if (bytesRead <= 0) | |||
| break; | |||
| numBytesToRead -= bytesRead; | |||
| context.processBlock ((const uint8*) tempBuffer, bytesRead); | |||
| } | |||
| context.finish (result); | |||
| } | |||
| MD5::MD5 (InputStream& input, int numBytesToRead) | |||
| { | |||
| processStream (input, numBytesToRead); | |||
| } | |||
| MD5::MD5 (const File& file) | |||
| { | |||
| FileInputStream* const fin = file.createInputStream(); | |||
| if (fin != 0) | |||
| { | |||
| processStream (*fin, -1); | |||
| delete fin; | |||
| } | |||
| else | |||
| { | |||
| zeromem (result, sizeof (result)); | |||
| } | |||
| } | |||
| MD5::~MD5() | |||
| { | |||
| } | |||
| //============================================================================== | |||
| MD5::ProcessContext::ProcessContext() | |||
| { | |||
| state[0] = 0x67452301; | |||
| state[1] = 0xefcdab89; | |||
| state[2] = 0x98badcfe; | |||
| state[3] = 0x10325476; | |||
| count[0] = 0; | |||
| count[1] = 0; | |||
| } | |||
| void MD5::ProcessContext::processBlock (const uint8* const data, int dataSize) | |||
| { | |||
| int bufferPos = ((count[0] >> 3) & 0x3F); | |||
| count[0] += (dataSize << 3); | |||
| if (count[0] < ((uint32) dataSize << 3)) | |||
| count[1]++; | |||
| count[1] += (dataSize >> 29); | |||
| const int spaceLeft = 64 - bufferPos; | |||
| int i = 0; | |||
| if (dataSize >= spaceLeft) | |||
| { | |||
| memcpy (buffer + bufferPos, data, spaceLeft); | |||
| transform (buffer); | |||
| i = spaceLeft; | |||
| while (i < dataSize - 63) | |||
| { | |||
| transform (data + i); | |||
| i += 64; | |||
| } | |||
| bufferPos = 0; | |||
| } | |||
| memcpy (buffer + bufferPos, data + i, dataSize - i); | |||
| } | |||
| //============================================================================== | |||
| static void encode (uint8* const output, | |||
| const uint32* const input, | |||
| const int numBytes) | |||
| { | |||
| uint32* const o = (uint32*) output; | |||
| for (int i = 0; i < (numBytes >> 2); ++i) | |||
| o[i] = swapIfBigEndian (input [i]); | |||
| } | |||
| static void decode (uint32* const output, | |||
| const uint8* const input, | |||
| const int numBytes) | |||
| { | |||
| for (int i = 0; i < (numBytes >> 2); ++i) | |||
| output[i] = littleEndianInt ((const char*) input + (i << 2)); | |||
| } | |||
| //============================================================================== | |||
| void MD5::ProcessContext::finish (uint8* const result) | |||
| { | |||
| unsigned char encodedLength[8]; | |||
| encode (encodedLength, count, 8); | |||
| // Pad out to 56 mod 64. | |||
| const int index = (uint32) ((count[0] >> 3) & 0x3f); | |||
| const int paddingLength = (index < 56) ? (56 - index) | |||
| : (120 - index); | |||
| uint8 paddingBuffer [64]; | |||
| zeromem (paddingBuffer, paddingLength); | |||
| paddingBuffer [0] = 0x80; | |||
| processBlock (paddingBuffer, paddingLength); | |||
| processBlock (encodedLength, 8); | |||
| encode (result, state, 16); | |||
| zeromem (buffer, sizeof (buffer)); | |||
| } | |||
| //============================================================================== | |||
| #define S11 7 | |||
| #define S12 12 | |||
| #define S13 17 | |||
| #define S14 22 | |||
| #define S21 5 | |||
| #define S22 9 | |||
| #define S23 14 | |||
| #define S24 20 | |||
| #define S31 4 | |||
| #define S32 11 | |||
| #define S33 16 | |||
| #define S34 23 | |||
| #define S41 6 | |||
| #define S42 10 | |||
| #define S43 15 | |||
| #define S44 21 | |||
| static inline uint32 F (const uint32 x, const uint32 y, const uint32 z) { return (x & y) | (~x & z); } | |||
| static inline uint32 G (const uint32 x, const uint32 y, const uint32 z) { return (x & z) | (y & ~z); } | |||
| static inline uint32 H (const uint32 x, const uint32 y, const uint32 z) { return x ^ y ^ z; } | |||
| static inline uint32 I (const uint32 x, const uint32 y, const uint32 z) { return y ^ (x | ~z); } | |||
| static inline uint32 rotateLeft (const uint32 x, const uint32 n) { return (x << n) | (x >> (32 - n)); } | |||
| static inline void FF (uint32& a, const uint32 b, const uint32 c, const uint32 d, const uint32 x, const uint32 s, const uint32 ac) | |||
| { | |||
| a += F (b, c, d) + x + ac; | |||
| a = rotateLeft (a, s) + b; | |||
| } | |||
| static inline void GG (uint32& a, const uint32 b, const uint32 c, const uint32 d, const uint32 x, const uint32 s, const uint32 ac) | |||
| { | |||
| a += G (b, c, d) + x + ac; | |||
| a = rotateLeft (a, s) + b; | |||
| } | |||
| static inline void HH (uint32& a, const uint32 b, const uint32 c, const uint32 d, const uint32 x, const uint32 s, const uint32 ac) | |||
| { | |||
| a += H (b, c, d) + x + ac; | |||
| a = rotateLeft (a, s) + b; | |||
| } | |||
| static inline void II (uint32& a, const uint32 b, const uint32 c, const uint32 d, const uint32 x, const uint32 s, const uint32 ac) | |||
| { | |||
| a += I (b, c, d) + x + ac; | |||
| a = rotateLeft (a, s) + b; | |||
| } | |||
| void MD5::ProcessContext::transform (const uint8* const buffer) | |||
| { | |||
| uint32 a = state[0]; | |||
| uint32 b = state[1]; | |||
| uint32 c = state[2]; | |||
| uint32 d = state[3]; | |||
| uint32 x[16]; | |||
| decode (x, buffer, 64); | |||
| FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ | |||
| FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ | |||
| FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ | |||
| FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ | |||
| FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ | |||
| FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ | |||
| FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ | |||
| FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ | |||
| FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ | |||
| FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ | |||
| FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ | |||
| FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ | |||
| FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ | |||
| FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ | |||
| FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ | |||
| FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ | |||
| GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ | |||
| GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ | |||
| GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ | |||
| GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ | |||
| GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ | |||
| GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ | |||
| GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ | |||
| GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ | |||
| GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ | |||
| GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ | |||
| GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ | |||
| GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ | |||
| GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ | |||
| GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ | |||
| GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ | |||
| GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ | |||
| HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ | |||
| HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ | |||
| HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ | |||
| HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ | |||
| HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ | |||
| HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ | |||
| HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ | |||
| HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ | |||
| HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ | |||
| HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ | |||
| HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ | |||
| HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ | |||
| HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ | |||
| HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ | |||
| HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ | |||
| HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ | |||
| II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ | |||
| II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ | |||
| II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ | |||
| II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ | |||
| II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ | |||
| II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ | |||
| II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ | |||
| II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ | |||
| II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ | |||
| II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ | |||
| II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ | |||
| II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ | |||
| II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ | |||
| II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ | |||
| II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ | |||
| II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ | |||
| state[0] += a; | |||
| state[1] += b; | |||
| state[2] += c; | |||
| state[3] += d; | |||
| zeromem (x, sizeof (x)); | |||
| } | |||
| //============================================================================== | |||
| const MemoryBlock MD5::getRawChecksumData() const | |||
| { | |||
| return MemoryBlock (result, 16); | |||
| } | |||
| const String MD5::toHexString() const | |||
| { | |||
| return String::toHexString (result, 16, 0); | |||
| } | |||
| //============================================================================== | |||
| bool MD5::operator== (const MD5& other) const | |||
| { | |||
| return memcmp (result, other.result, 16) == 0; | |||
| } | |||
| bool MD5::operator!= (const MD5& other) const | |||
| { | |||
| return ! operator== (other); | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,126 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_MD5_JUCEHEADER__ | |||
| #define __JUCE_MD5_JUCEHEADER__ | |||
| #include "../containers/juce_MemoryBlock.h" | |||
| #include "../io/streams/juce_InputStream.h" | |||
| #include "../io/files/juce_File.h" | |||
| //============================================================================== | |||
| /** | |||
| MD5 checksum class. | |||
| Create one of these with a block of source data or a string, and it calculates the | |||
| MD5 checksum of that data. | |||
| You can then retrieve this checksum as a 16-byte block, or as a hex string. | |||
| */ | |||
| class JUCE_API MD5 | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Creates a null MD5 object. */ | |||
| MD5(); | |||
| /** Creates a copy of another MD5. */ | |||
| MD5 (const MD5& other); | |||
| /** Copies another MD5. */ | |||
| const MD5& operator= (const MD5& other); | |||
| //============================================================================== | |||
| /** Creates a checksum for a block of binary data. */ | |||
| MD5 (const MemoryBlock& data); | |||
| /** Creates a checksum for a block of binary data. */ | |||
| MD5 (const char* data, const int numBytes); | |||
| /** Creates a checksum for a string. | |||
| Note that this operates on the string as a block of unicode characters, so the | |||
| result you get will differ from the value you'd get if the string was treated | |||
| as a block of utf8 or ascii. Bear this in mind if you're comparing the result | |||
| of this method with a checksum created by a different framework, which may have | |||
| used a different encoding. | |||
| */ | |||
| MD5 (const String& text); | |||
| /** Creates a checksum for the input from a stream. | |||
| This will read up to the given number of bytes from the stream, and produce the | |||
| checksum of that. If the number of bytes to read is negative, it'll read | |||
| until the stream is exhausted. | |||
| */ | |||
| MD5 (InputStream& input, int numBytesToRead = -1); | |||
| /** Creates a checksum for a file. */ | |||
| MD5 (const File& file); | |||
| /** Destructor. */ | |||
| ~MD5(); | |||
| //============================================================================== | |||
| /** Returns the checksum as a 16-byte block of data. */ | |||
| const MemoryBlock getRawChecksumData() const; | |||
| /** Returns the checksum as a 32-digit hex string. */ | |||
| const String toHexString() const; | |||
| //============================================================================== | |||
| /** Compares this to another MD5. */ | |||
| bool operator== (const MD5& other) const; | |||
| /** Compares this to another MD5. */ | |||
| bool operator!= (const MD5& other) const; | |||
| //============================================================================== | |||
| juce_UseDebuggingNewOperator | |||
| private: | |||
| uint8 result [16]; | |||
| struct ProcessContext | |||
| { | |||
| uint8 buffer [64]; | |||
| uint32 state [4]; | |||
| uint32 count [2]; | |||
| ProcessContext(); | |||
| void processBlock (const uint8* const data, int dataSize); | |||
| void transform (const uint8* const buffer); | |||
| void finish (uint8* const result); | |||
| }; | |||
| void processStream (InputStream& input, int numBytesToRead); | |||
| }; | |||
| #endif // __JUCE_MD5_JUCEHEADER__ | |||
| @@ -0,0 +1,260 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_Primes.h" | |||
| #include "../core/juce_Random.h" | |||
| //============================================================================== | |||
| static void createSmallSieve (const int numBits, BitArray& result) throw() | |||
| { | |||
| result.setBit (numBits); | |||
| result.clearBit (numBits); // to enlarge the array | |||
| result.setBit (0); | |||
| int n = 2; | |||
| do | |||
| { | |||
| for (int i = n + n; i < numBits; i += n) | |||
| result.setBit (i); | |||
| n = result.findNextClearBit (n + 1); | |||
| } | |||
| while (n <= (numBits >> 1)); | |||
| } | |||
| static void bigSieve (const BitArray& base, | |||
| const int numBits, | |||
| BitArray& result, | |||
| const BitArray& smallSieve, | |||
| const int smallSieveSize) throw() | |||
| { | |||
| jassert (! base[0]); // must be even! | |||
| result.setBit (numBits); | |||
| result.clearBit (numBits); // to enlarge the array | |||
| int index = smallSieve.findNextClearBit (0); | |||
| do | |||
| { | |||
| const int prime = (index << 1) + 1; | |||
| BitArray r (base); | |||
| BitArray remainder; | |||
| r.divideBy (prime, remainder); | |||
| int i = prime - remainder.getBitRangeAsInt (0, 32); | |||
| if (r.isEmpty()) | |||
| i += prime; | |||
| if ((i & 1) == 0) | |||
| i += prime; | |||
| i = (i - 1) >> 1; | |||
| while (i < numBits) | |||
| { | |||
| result.setBit (i); | |||
| i += prime; | |||
| } | |||
| index = smallSieve.findNextClearBit (index + 1); | |||
| } | |||
| while (index < smallSieveSize); | |||
| } | |||
| static bool findCandidate (const BitArray& base, | |||
| const BitArray& sieve, | |||
| const int numBits, | |||
| BitArray& result, | |||
| const int certainty) throw() | |||
| { | |||
| for (int i = 0; i < numBits; ++i) | |||
| { | |||
| if (! sieve[i]) | |||
| { | |||
| result = base; | |||
| result.add (BitArray ((unsigned int) ((i << 1) + 1))); | |||
| if (Primes::isProbablyPrime (result, certainty)) | |||
| return true; | |||
| } | |||
| } | |||
| return false; | |||
| } | |||
| //============================================================================== | |||
| const BitArray Primes::createProbablePrime (const int bitLength, | |||
| const int certainty, | |||
| const int* randomSeeds, | |||
| int numRandomSeeds) throw() | |||
| { | |||
| int defaultSeeds [16]; | |||
| if (numRandomSeeds <= 0) | |||
| { | |||
| randomSeeds = defaultSeeds; | |||
| numRandomSeeds = numElementsInArray (defaultSeeds); | |||
| Random r (0); | |||
| for (int j = 10; --j >= 0;) | |||
| { | |||
| r.setSeedRandomly(); | |||
| for (int i = numRandomSeeds; --i >= 0;) | |||
| defaultSeeds[i] ^= r.nextInt() ^ Random::getSystemRandom().nextInt(); | |||
| } | |||
| } | |||
| BitArray smallSieve; | |||
| const int smallSieveSize = 15000; | |||
| createSmallSieve (smallSieveSize, smallSieve); | |||
| BitArray p; | |||
| for (int i = numRandomSeeds; --i >= 0;) | |||
| { | |||
| BitArray p2; | |||
| Random r (randomSeeds[i]); | |||
| r.fillBitsRandomly (p2, 0, bitLength); | |||
| p.xorWith (p2); | |||
| } | |||
| p.setBit (bitLength - 1); | |||
| p.clearBit (0); | |||
| const int searchLen = jmax (1024, (bitLength / 20) * 64); | |||
| while (p.getHighestBit() < bitLength) | |||
| { | |||
| p.add (2 * searchLen); | |||
| BitArray sieve; | |||
| bigSieve (p, searchLen, sieve, | |||
| smallSieve, smallSieveSize); | |||
| BitArray candidate; | |||
| if (findCandidate (p, sieve, searchLen, candidate, certainty)) | |||
| return candidate; | |||
| } | |||
| jassertfalse | |||
| return BitArray(); | |||
| } | |||
| static bool passesMillerRabin (const BitArray& n, int iterations) throw() | |||
| { | |||
| const BitArray one (1); | |||
| const BitArray two (2); | |||
| BitArray nMinusOne (n); | |||
| nMinusOne.subtract (one); | |||
| BitArray d (nMinusOne); | |||
| const int s = d.findNextSetBit (0); | |||
| d.shiftBits (-s); | |||
| BitArray smallPrimes; | |||
| int numBitsInSmallPrimes = 0; | |||
| for (;;) | |||
| { | |||
| numBitsInSmallPrimes += 256; | |||
| createSmallSieve (numBitsInSmallPrimes, smallPrimes); | |||
| const int numPrimesFound = numBitsInSmallPrimes - smallPrimes.countNumberOfSetBits(); | |||
| if (numPrimesFound > iterations + 1) | |||
| break; | |||
| } | |||
| int smallPrime = 2; | |||
| while (--iterations >= 0) | |||
| { | |||
| smallPrime = smallPrimes.findNextClearBit (smallPrime + 1); | |||
| BitArray r (smallPrime); | |||
| //r.createRandomNumber (nMinusOne); | |||
| r.exponentModulo (d, n); | |||
| if (! (r == one || r == nMinusOne)) | |||
| { | |||
| for (int j = 0; j < s; ++j) | |||
| { | |||
| r.exponentModulo (two, n); | |||
| if (r == nMinusOne) | |||
| break; | |||
| } | |||
| if (r != nMinusOne) | |||
| return false; | |||
| } | |||
| } | |||
| return true; | |||
| } | |||
| bool Primes::isProbablyPrime (const BitArray& number, | |||
| const int certainty) throw() | |||
| { | |||
| if (! number[0]) | |||
| return false; | |||
| if (number.getHighestBit() <= 10) | |||
| { | |||
| const int num = number.getBitRangeAsInt (0, 10); | |||
| for (int i = num / 2; --i > 1;) | |||
| if (num % i == 0) | |||
| return false; | |||
| return true; | |||
| } | |||
| else | |||
| { | |||
| const BitArray screen (2 * 3 * 5 * 7 * 11 * 13 * 17 * 19 * 23); | |||
| if (number.findGreatestCommonDivisor (screen) != BitArray (1)) | |||
| return false; | |||
| return passesMillerRabin (number, certainty); | |||
| } | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,155 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_RSAKey.h" | |||
| #include "juce_Primes.h" | |||
| //============================================================================== | |||
| RSAKey::RSAKey() throw() | |||
| { | |||
| } | |||
| RSAKey::RSAKey (const String& s) throw() | |||
| { | |||
| if (s.containsChar (T(','))) | |||
| { | |||
| part1.parseString (s.upToFirstOccurrenceOf (T(","), false, false), 16); | |||
| part2.parseString (s.fromFirstOccurrenceOf (T(","), false, false), 16); | |||
| } | |||
| else | |||
| { | |||
| // the string needs to be two hex numbers, comma-separated.. | |||
| jassertfalse; | |||
| } | |||
| } | |||
| RSAKey::~RSAKey() throw() | |||
| { | |||
| } | |||
| const String RSAKey::toString() const throw() | |||
| { | |||
| return part1.toString (16) + T(",") + part2.toString (16); | |||
| } | |||
| bool RSAKey::applyToValue (BitArray& value) const throw() | |||
| { | |||
| if (part1.isEmpty() || part2.isEmpty() | |||
| || value.compare (0) <= 0) | |||
| { | |||
| jassertfalse // using an uninitialised key | |||
| value.clear(); | |||
| return false; | |||
| } | |||
| BitArray result; | |||
| while (! value.isEmpty()) | |||
| { | |||
| result.multiplyBy (part2); | |||
| BitArray remainder; | |||
| value.divideBy (part2, remainder); | |||
| remainder.exponentModulo (part1, part2); | |||
| result.add (remainder); | |||
| } | |||
| value = result; | |||
| return true; | |||
| } | |||
| static const BitArray findBestCommonDivisor (const BitArray& p, | |||
| const BitArray& q) throw() | |||
| { | |||
| const BitArray one (1); | |||
| // try 3, 5, 9, 17, etc first because these only contain 2 bits and so | |||
| // are fast to divide + multiply | |||
| for (int i = 2; i <= 65536; i *= 2) | |||
| { | |||
| const BitArray e (1 + i); | |||
| if (e.findGreatestCommonDivisor (p) == one | |||
| && e.findGreatestCommonDivisor (q) == one) | |||
| { | |||
| return e; | |||
| } | |||
| } | |||
| BitArray e (4); | |||
| while (! (e.findGreatestCommonDivisor (p) == one | |||
| && e.findGreatestCommonDivisor (q) == one)) | |||
| { | |||
| e.add (one); | |||
| } | |||
| return e; | |||
| } | |||
| void RSAKey::createKeyPair (RSAKey& publicKey, | |||
| RSAKey& privateKey, | |||
| const int numBits, | |||
| const int* randomSeeds, | |||
| const int numRandomSeeds) throw() | |||
| { | |||
| jassert (numBits > 16); // not much point using less than this.. | |||
| BitArray p (Primes::createProbablePrime (numBits / 2, 30, randomSeeds, numRandomSeeds)); | |||
| BitArray q (Primes::createProbablePrime (numBits - numBits / 2, 30, randomSeeds, numRandomSeeds)); | |||
| BitArray n (p); | |||
| n.multiplyBy (q); // n = pq | |||
| const BitArray one (1); | |||
| p.subtract (one); | |||
| q.subtract (one); | |||
| BitArray m (p); | |||
| m.multiplyBy (q); // m = (p - 1)(q - 1) | |||
| const BitArray e (findBestCommonDivisor (p, q)); | |||
| BitArray d (e); | |||
| d.inverseModulo (m); | |||
| publicKey.part1 = e; | |||
| publicKey.part2 = n; | |||
| privateKey.part1 = d; | |||
| privateKey.part2 = n; | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,70 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_ActionBroadcaster.h" | |||
| #include "juce_MessageManager.h" | |||
| //============================================================================== | |||
| 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 | |||
| @@ -0,0 +1,56 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_ACTIONLISTENER_JUCEHEADER__ | |||
| #define __JUCE_ACTIONLISTENER_JUCEHEADER__ | |||
| #include "../text/juce_String.h" | |||
| //============================================================================== | |||
| /** | |||
| Receives callbacks to indicate that some kind of event has occurred. | |||
| Used by various classes, e.g. buttons when they are pressed, to tell listeners | |||
| about something that's happened. | |||
| @see ActionListenerList, ActionBroadcaster, ChangeListener | |||
| */ | |||
| class JUCE_API ActionListener | |||
| { | |||
| public: | |||
| /** Destructor. */ | |||
| virtual ~ActionListener() {} | |||
| /** 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() | |||
| */ | |||
| virtual void actionListenerCallback (const String& message) = 0; | |||
| }; | |||
| #endif // __JUCE_ACTIONLISTENER_JUCEHEADER__ | |||
| @@ -0,0 +1,112 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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&); | |||
| const ActionMessage& operator= (const ActionMessage&); | |||
| }; | |||
| //============================================================================== | |||
| ActionListenerList::ActionListenerList() throw() | |||
| { | |||
| } | |||
| ActionListenerList::~ActionListenerList() throw() | |||
| { | |||
| } | |||
| void ActionListenerList::addActionListener (ActionListener* const listener) throw() | |||
| { | |||
| 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) throw() | |||
| { | |||
| 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() throw() | |||
| { | |||
| 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, | |||
| (ActionListener*) actionListeners_.getUnchecked(i))); | |||
| } | |||
| } | |||
| void ActionListenerList::handleMessage (const Message& message) | |||
| { | |||
| const ActionMessage& am = (const ActionMessage&) message; | |||
| if (actionListeners_.contains (am.pointerParameter)) | |||
| ((ActionListener*) am.pointerParameter)->actionListenerCallback (am.message); | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,96 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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() throw(); | |||
| /** Destructor. */ | |||
| ~ActionListenerList() throw(); | |||
| //============================================================================== | |||
| /** Adds a listener to the list. | |||
| (Trying to add a listener that's already on the list will have no effect). | |||
| */ | |||
| void addActionListener (ActionListener* const listener) throw(); | |||
| /** Removes a listener from the list. | |||
| If the listener isn't on the list, this won't have any effect. | |||
| */ | |||
| void removeActionListener (ActionListener* const listener) throw(); | |||
| /** Removes all listeners from the list. */ | |||
| void removeAllActionListeners() throw(); | |||
| /** 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&); | |||
| const ActionListenerList& operator= (const ActionListenerList&); | |||
| }; | |||
| #endif // __JUCE_ACTIONLISTENERLIST_JUCEHEADER__ | |||
| @@ -0,0 +1,73 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_AsyncUpdater.h" | |||
| //============================================================================== | |||
| AsyncUpdater::AsyncUpdater() throw() | |||
| : asyncMessagePending (false) | |||
| { | |||
| internalAsyncHandler.owner = this; | |||
| } | |||
| AsyncUpdater::~AsyncUpdater() | |||
| { | |||
| } | |||
| void AsyncUpdater::triggerAsyncUpdate() throw() | |||
| { | |||
| if (! asyncMessagePending) | |||
| { | |||
| asyncMessagePending = true; | |||
| internalAsyncHandler.postMessage (new Message()); | |||
| } | |||
| } | |||
| void AsyncUpdater::cancelPendingUpdate() throw() | |||
| { | |||
| asyncMessagePending = false; | |||
| } | |||
| void AsyncUpdater::handleUpdateNowIfNeeded() | |||
| { | |||
| if (asyncMessagePending) | |||
| { | |||
| asyncMessagePending = false; | |||
| handleAsyncUpdate(); | |||
| } | |||
| } | |||
| void AsyncUpdater::AsyncUpdaterInternal::handleMessage (const Message&) | |||
| { | |||
| owner->handleUpdateNowIfNeeded(); | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,81 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_ChangeBroadcaster.h" | |||
| #include "juce_MessageManager.h" | |||
| //============================================================================== | |||
| ChangeBroadcaster::ChangeBroadcaster() throw() | |||
| { | |||
| // are you trying to create this object before or after juce has been intialised?? | |||
| jassert (MessageManager::instance != 0); | |||
| } | |||
| ChangeBroadcaster::~ChangeBroadcaster() | |||
| { | |||
| // all event-based objects must be deleted BEFORE juce is shut down! | |||
| jassert (MessageManager::instance != 0); | |||
| } | |||
| void ChangeBroadcaster::addChangeListener (ChangeListener* const listener) throw() | |||
| { | |||
| changeListenerList.addChangeListener (listener); | |||
| } | |||
| void ChangeBroadcaster::removeChangeListener (ChangeListener* const listener) throw() | |||
| { | |||
| jassert (changeListenerList.isValidMessageListener()); | |||
| if (changeListenerList.isValidMessageListener()) | |||
| changeListenerList.removeChangeListener (listener); | |||
| } | |||
| void ChangeBroadcaster::removeAllChangeListeners() throw() | |||
| { | |||
| changeListenerList.removeAllChangeListeners(); | |||
| } | |||
| void ChangeBroadcaster::sendChangeMessage (void* objectThatHasChanged) throw() | |||
| { | |||
| changeListenerList.sendChangeMessage (objectThatHasChanged); | |||
| } | |||
| void ChangeBroadcaster::sendSynchronousChangeMessage (void* objectThatHasChanged) | |||
| { | |||
| changeListenerList.sendSynchronousChangeMessage (objectThatHasChanged); | |||
| } | |||
| void ChangeBroadcaster::dispatchPendingMessages() | |||
| { | |||
| changeListenerList.dispatchPendingMessages(); | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,107 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_ChangeListenerList.h" | |||
| //============================================================================== | |||
| ChangeListenerList::ChangeListenerList() throw() | |||
| : lastChangedObject (0), | |||
| messagePending (false) | |||
| { | |||
| } | |||
| ChangeListenerList::~ChangeListenerList() throw() | |||
| { | |||
| } | |||
| void ChangeListenerList::addChangeListener (ChangeListener* const listener) throw() | |||
| { | |||
| const ScopedLock sl (lock); | |||
| jassert (listener != 0); | |||
| if (listener != 0) | |||
| listeners.add (listener); | |||
| } | |||
| void ChangeListenerList::removeChangeListener (ChangeListener* const listener) throw() | |||
| { | |||
| const ScopedLock sl (lock); | |||
| listeners.removeValue (listener); | |||
| } | |||
| void ChangeListenerList::removeAllChangeListeners() throw() | |||
| { | |||
| const ScopedLock sl (lock); | |||
| listeners.clear(); | |||
| } | |||
| void ChangeListenerList::sendChangeMessage (void* const objectThatHasChanged) throw() | |||
| { | |||
| const ScopedLock sl (lock); | |||
| if ((! messagePending) && (listeners.size() > 0)) | |||
| { | |||
| lastChangedObject = objectThatHasChanged; | |||
| postMessage (new Message (0, 0, 0, objectThatHasChanged)); | |||
| messagePending = true; | |||
| } | |||
| } | |||
| void ChangeListenerList::handleMessage (const Message& message) | |||
| { | |||
| sendSynchronousChangeMessage (message.pointerParameter); | |||
| } | |||
| void ChangeListenerList::sendSynchronousChangeMessage (void* const objectThatHasChanged) | |||
| { | |||
| const ScopedLock sl (lock); | |||
| messagePending = false; | |||
| for (int i = listeners.size(); --i >= 0;) | |||
| { | |||
| ChangeListener* const l = (ChangeListener*) listeners.getUnchecked (i); | |||
| { | |||
| const ScopedUnlock tempUnlocker (lock); | |||
| l->changeListenerCallback (objectThatHasChanged); | |||
| } | |||
| i = jmin (i, listeners.size()); | |||
| } | |||
| } | |||
| void ChangeListenerList::dispatchPendingMessages() | |||
| { | |||
| if (messagePending) | |||
| sendSynchronousChangeMessage (lastChangedObject); | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,119 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_CHANGELISTENERLIST_JUCEHEADER__ | |||
| #define __JUCE_CHANGELISTENERLIST_JUCEHEADER__ | |||
| #include "juce_ChangeListener.h" | |||
| #include "juce_MessageListener.h" | |||
| #include "../containers/juce_SortedSet.h" | |||
| #include "../threads/juce_ScopedLock.h" | |||
| //============================================================================== | |||
| /** | |||
| A set of ChangeListeners. | |||
| Listeners can be added and removed from the list, and change messages can be | |||
| broadcast to all the listeners. | |||
| @see ChangeListener, ChangeBroadcaster | |||
| */ | |||
| class JUCE_API ChangeListenerList : public MessageListener | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Creates an empty list. */ | |||
| ChangeListenerList() throw(); | |||
| /** Destructor. */ | |||
| ~ChangeListenerList() throw(); | |||
| //============================================================================== | |||
| /** Adds a listener to the list. | |||
| (Trying to add a listener that's already on the list will have no effect). | |||
| */ | |||
| void addChangeListener (ChangeListener* const listener) throw(); | |||
| /** Removes a listener from the list. | |||
| If the listener isn't on the list, this won't have any effect. | |||
| */ | |||
| void removeChangeListener (ChangeListener* const listener) throw(); | |||
| /** Removes all listeners from the list. */ | |||
| void removeAllChangeListeners() throw(); | |||
| //============================================================================== | |||
| /** Posts an asynchronous change message to all the listeners. | |||
| If a message has already been sent and hasn't yet been delivered, this | |||
| method won't send another - in this way it coalesces multiple frequent | |||
| changes into fewer actual callbacks to the ChangeListeners. Contrast this | |||
| with the ActionListener, which posts a new event for every call to its | |||
| sendActionMessage() method. | |||
| Only listeners which are on the list when the change event is delivered | |||
| will receive the event - and this may include listeners that weren't on | |||
| the list when the change message was sent. | |||
| @param objectThatHasChanged this pointer is passed to the | |||
| ChangeListener::changeListenerCallback() method, | |||
| and can be any value the application needs | |||
| @see sendSynchronousChangeMessage | |||
| */ | |||
| void sendChangeMessage (void* objectThatHasChanged) throw(); | |||
| /** This will synchronously callback all the ChangeListeners. | |||
| Use this if you need to synchronously force a call to all the | |||
| listeners' ChangeListener::changeListenerCallback() methods. | |||
| */ | |||
| void sendSynchronousChangeMessage (void* objectThatHasChanged); | |||
| /** If a change message has been sent but not yet dispatched, this will | |||
| use sendSynchronousChangeMessage() to make the callback immediately. | |||
| */ | |||
| void dispatchPendingMessages(); | |||
| //============================================================================== | |||
| /** @internal */ | |||
| void handleMessage (const Message&); | |||
| juce_UseDebuggingNewOperator | |||
| private: | |||
| SortedSet <void*> listeners; | |||
| CriticalSection lock; | |||
| void* lastChangedObject; | |||
| bool messagePending; | |||
| ChangeListenerList (const ChangeListenerList&); | |||
| const ChangeListenerList& operator= (const ChangeListenerList&); | |||
| }; | |||
| #endif // __JUCE_CHANGELISTENERLIST_JUCEHEADER__ | |||
| @@ -0,0 +1,387 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_InterprocessConnection.h" | |||
| #include "../threads/juce_ScopedLock.h" | |||
| //============================================================================== | |||
| InterprocessConnection::InterprocessConnection (const bool callbacksOnMessageThread, | |||
| const uint32 magicMessageHeaderNumber) | |||
| : Thread ("Juce IPC connection"), | |||
| socket (0), | |||
| pipe (0), | |||
| callbackConnectionState (false), | |||
| useMessageThread (callbacksOnMessageThread), | |||
| magicMessageHeader (magicMessageHeaderNumber), | |||
| pipeReceiveMessageTimeout (-1) | |||
| { | |||
| } | |||
| InterprocessConnection::~InterprocessConnection() | |||
| { | |||
| callbackConnectionState = false; | |||
| disconnect(); | |||
| } | |||
| //============================================================================== | |||
| bool InterprocessConnection::connectToSocket (const String& hostName, | |||
| const int portNumber, | |||
| const int timeOutMillisecs) | |||
| { | |||
| disconnect(); | |||
| const ScopedLock sl (pipeAndSocketLock); | |||
| socket = new StreamingSocket(); | |||
| if (socket->connect (hostName, portNumber, timeOutMillisecs)) | |||
| { | |||
| connectionMadeInt(); | |||
| startThread(); | |||
| return true; | |||
| } | |||
| else | |||
| { | |||
| deleteAndZero (socket); | |||
| return false; | |||
| } | |||
| } | |||
| bool InterprocessConnection::connectToPipe (const String& pipeName, | |||
| const int pipeReceiveMessageTimeoutMs) | |||
| { | |||
| disconnect(); | |||
| NamedPipe* const newPipe = new NamedPipe(); | |||
| if (newPipe->openExisting (pipeName)) | |||
| { | |||
| const ScopedLock sl (pipeAndSocketLock); | |||
| pipeReceiveMessageTimeout = pipeReceiveMessageTimeoutMs; | |||
| initialiseWithPipe (newPipe); | |||
| return true; | |||
| } | |||
| else | |||
| { | |||
| delete newPipe; | |||
| return false; | |||
| } | |||
| } | |||
| bool InterprocessConnection::createPipe (const String& pipeName, | |||
| const int pipeReceiveMessageTimeoutMs) | |||
| { | |||
| disconnect(); | |||
| NamedPipe* const newPipe = new NamedPipe(); | |||
| if (newPipe->createNewPipe (pipeName)) | |||
| { | |||
| const ScopedLock sl (pipeAndSocketLock); | |||
| pipeReceiveMessageTimeout = pipeReceiveMessageTimeoutMs; | |||
| initialiseWithPipe (newPipe); | |||
| return true; | |||
| } | |||
| else | |||
| { | |||
| delete newPipe; | |||
| return false; | |||
| } | |||
| } | |||
| void InterprocessConnection::disconnect() | |||
| { | |||
| if (socket != 0) | |||
| socket->close(); | |||
| if (pipe != 0) | |||
| { | |||
| pipe->cancelPendingReads(); | |||
| pipe->close(); | |||
| } | |||
| stopThread (4000); | |||
| { | |||
| const ScopedLock sl (pipeAndSocketLock); | |||
| deleteAndZero (socket); | |||
| deleteAndZero (pipe); | |||
| } | |||
| connectionLostInt(); | |||
| } | |||
| bool InterprocessConnection::isConnected() const | |||
| { | |||
| const ScopedLock sl (pipeAndSocketLock); | |||
| return ((socket != 0 && socket->isConnected()) | |||
| || (pipe != 0 && pipe->isOpen())) | |||
| && isThreadRunning(); | |||
| } | |||
| const String InterprocessConnection::getConnectedHostName() const | |||
| { | |||
| if (pipe != 0) | |||
| { | |||
| return "localhost"; | |||
| } | |||
| else if (socket != 0) | |||
| { | |||
| if (! socket->isLocal()) | |||
| return socket->getHostName(); | |||
| return "localhost"; | |||
| } | |||
| return String::empty; | |||
| } | |||
| //============================================================================== | |||
| bool InterprocessConnection::sendMessage (const MemoryBlock& message) | |||
| { | |||
| uint32 messageHeader[2]; | |||
| messageHeader [0] = swapIfBigEndian (magicMessageHeader); | |||
| messageHeader [1] = swapIfBigEndian ((uint32) message.getSize()); | |||
| MemoryBlock messageData (sizeof (messageHeader) + message.getSize()); | |||
| messageData.copyFrom (messageHeader, 0, sizeof (messageHeader)); | |||
| messageData.copyFrom (message.getData(), sizeof (messageHeader), message.getSize()); | |||
| int bytesWritten = 0; | |||
| const ScopedLock sl (pipeAndSocketLock); | |||
| if (socket != 0) | |||
| { | |||
| bytesWritten = socket->write (messageData.getData(), messageData.getSize()); | |||
| } | |||
| else if (pipe != 0) | |||
| { | |||
| bytesWritten = pipe->write (messageData.getData(), messageData.getSize()); | |||
| } | |||
| if (bytesWritten < 0) | |||
| { | |||
| // error.. | |||
| return false; | |||
| } | |||
| return (bytesWritten == messageData.getSize()); | |||
| } | |||
| //============================================================================== | |||
| void InterprocessConnection::initialiseWithSocket (StreamingSocket* const socket_) | |||
| { | |||
| jassert (socket == 0); | |||
| socket = socket_; | |||
| connectionMadeInt(); | |||
| startThread(); | |||
| } | |||
| void InterprocessConnection::initialiseWithPipe (NamedPipe* const pipe_) | |||
| { | |||
| jassert (pipe == 0); | |||
| pipe = pipe_; | |||
| connectionMadeInt(); | |||
| startThread(); | |||
| } | |||
| const int messageMagicNumber = 0xb734128b; | |||
| void InterprocessConnection::handleMessage (const Message& message) | |||
| { | |||
| if (message.intParameter1 == messageMagicNumber) | |||
| { | |||
| switch (message.intParameter2) | |||
| { | |||
| case 0: | |||
| { | |||
| MemoryBlock* const data = (MemoryBlock*) message.pointerParameter; | |||
| messageReceived (*data); | |||
| delete data; | |||
| break; | |||
| } | |||
| case 1: | |||
| connectionMade(); | |||
| break; | |||
| case 2: | |||
| connectionLost(); | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| void InterprocessConnection::connectionMadeInt() | |||
| { | |||
| if (! callbackConnectionState) | |||
| { | |||
| callbackConnectionState = true; | |||
| if (useMessageThread) | |||
| postMessage (new Message (messageMagicNumber, 1, 0, 0)); | |||
| else | |||
| connectionMade(); | |||
| } | |||
| } | |||
| void InterprocessConnection::connectionLostInt() | |||
| { | |||
| if (callbackConnectionState) | |||
| { | |||
| callbackConnectionState = false; | |||
| if (useMessageThread) | |||
| postMessage (new Message (messageMagicNumber, 2, 0, 0)); | |||
| else | |||
| connectionLost(); | |||
| } | |||
| } | |||
| void InterprocessConnection::deliverDataInt (const MemoryBlock& data) | |||
| { | |||
| jassert (callbackConnectionState); | |||
| if (useMessageThread) | |||
| postMessage (new Message (messageMagicNumber, 0, 0, new MemoryBlock (data))); | |||
| else | |||
| messageReceived (data); | |||
| } | |||
| //============================================================================== | |||
| bool InterprocessConnection::readNextMessageInt() | |||
| { | |||
| const int maximumMessageSize = 1024 * 1024 * 10; // sanity check | |||
| uint32 messageHeader[2]; | |||
| const int bytes = (socket != 0) ? socket->read (messageHeader, sizeof (messageHeader), true) | |||
| : pipe->read (messageHeader, sizeof (messageHeader), pipeReceiveMessageTimeout); | |||
| if (bytes == sizeof (messageHeader) | |||
| && swapIfBigEndian (messageHeader[0]) == magicMessageHeader) | |||
| { | |||
| const int bytesInMessage = (int) swapIfBigEndian (messageHeader[1]); | |||
| if (bytesInMessage > 0 && bytesInMessage < maximumMessageSize) | |||
| { | |||
| MemoryBlock messageData (bytesInMessage, true); | |||
| int bytesRead = 0; | |||
| while (bytesRead < bytesInMessage) | |||
| { | |||
| if (threadShouldExit()) | |||
| return false; | |||
| const int numThisTime = jmin (bytesInMessage, 65536); | |||
| const int bytesIn = (socket != 0) ? socket->read (((char*) messageData.getData()) + bytesRead, numThisTime, true) | |||
| : pipe->read (((char*) messageData.getData()) + bytesRead, numThisTime, | |||
| pipeReceiveMessageTimeout); | |||
| if (bytesIn <= 0) | |||
| break; | |||
| bytesRead += bytesIn; | |||
| } | |||
| if (bytesRead >= 0) | |||
| deliverDataInt (messageData); | |||
| } | |||
| } | |||
| else if (bytes < 0) | |||
| { | |||
| { | |||
| const ScopedLock sl (pipeAndSocketLock); | |||
| deleteAndZero (socket); | |||
| } | |||
| connectionLostInt(); | |||
| return false; | |||
| } | |||
| return true; | |||
| } | |||
| void InterprocessConnection::run() | |||
| { | |||
| while (! threadShouldExit()) | |||
| { | |||
| if (socket != 0) | |||
| { | |||
| const int ready = socket->waitUntilReady (true, 0); | |||
| if (ready < 0) | |||
| { | |||
| { | |||
| const ScopedLock sl (pipeAndSocketLock); | |||
| deleteAndZero (socket); | |||
| } | |||
| connectionLostInt(); | |||
| break; | |||
| } | |||
| else if (ready > 0) | |||
| { | |||
| if (! readNextMessageInt()) | |||
| break; | |||
| } | |||
| else | |||
| { | |||
| Thread::sleep (2); | |||
| } | |||
| } | |||
| else if (pipe != 0) | |||
| { | |||
| if (! pipe->isOpen()) | |||
| { | |||
| { | |||
| const ScopedLock sl (pipeAndSocketLock); | |||
| deleteAndZero (pipe); | |||
| } | |||
| connectionLostInt(); | |||
| break; | |||
| } | |||
| else | |||
| { | |||
| if (! readNextMessageInt()) | |||
| break; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,215 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_INTERPROCESSCONNECTION_JUCEHEADER__ | |||
| #define __JUCE_INTERPROCESSCONNECTION_JUCEHEADER__ | |||
| #include "juce_MessageListener.h" | |||
| #include "../threads/juce_Thread.h" | |||
| #include "../io/network/juce_Socket.h" | |||
| #include "../io/files/juce_NamedPipe.h" | |||
| class InterprocessConnectionServer; | |||
| //============================================================================== | |||
| /** | |||
| Manages a simple two-way messaging connection to another process, using either | |||
| a socket or a named pipe as the transport medium. | |||
| To connect to a waiting socket or an open pipe, use the connectToSocket() or | |||
| connectToPipe() methods. If this succeeds, messages can be sent to the other end, | |||
| and incoming messages will result in a callback via the messageReceived() | |||
| method. | |||
| To open a pipe and wait for another client to connect to it, use the createPipe() | |||
| method. | |||
| To act as a socket server and create connections for one or more client, see the | |||
| InterprocessConnectionServer class. | |||
| @see InterprocessConnectionServer, Socket, NamedPipe | |||
| */ | |||
| class JUCE_API InterprocessConnection : public Thread, | |||
| private MessageListener | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Creates a connection. | |||
| Connections are created manually, connecting them with the connectToSocket() | |||
| or connectToPipe() methods, or they are created automatically by a InterprocessConnectionServer | |||
| when a client wants to connect. | |||
| @param callbacksOnMessageThread if true, callbacks to the connectionMade(), | |||
| connectionLost() and messageReceived() methods will | |||
| always be made using the message thread; if false, | |||
| these will be called immediately on the connection's | |||
| own thread. | |||
| @param magicMessageHeaderNumber a magic number to use in the header to check the | |||
| validity of the data blocks being sent and received. This | |||
| can be any number, but the sender and receiver must obviously | |||
| use matching values or they won't recognise each other. | |||
| */ | |||
| InterprocessConnection (const bool callbacksOnMessageThread = true, | |||
| const uint32 magicMessageHeaderNumber = 0xf2b49e2c); | |||
| /** Destructor. */ | |||
| ~InterprocessConnection(); | |||
| //============================================================================== | |||
| /** Tries to connect this object to a socket. | |||
| For this to work, the machine on the other end needs to have a InterprocessConnectionServer | |||
| object waiting to receive client connections on this port number. | |||
| @param hostName the host computer, either a network address or name | |||
| @param portNumber the socket port number to try to connect to | |||
| @param timeOutMillisecs how long to keep trying before giving up | |||
| @returns true if the connection is established successfully | |||
| @see Socket | |||
| */ | |||
| bool connectToSocket (const String& hostName, | |||
| const int portNumber, | |||
| const int timeOutMillisecs); | |||
| /** Tries to connect the object to an existing named pipe. | |||
| For this to work, another process on the same computer must already have opened | |||
| an InterprocessConnection object and used createPipe() to create a pipe for this | |||
| to connect to. | |||
| You can optionally specify a timeout length to be passed to the NamedPipe::read() method. | |||
| @returns true if it connects successfully. | |||
| @see createPipe, NamedPipe | |||
| */ | |||
| bool connectToPipe (const String& pipeName, | |||
| const int pipeReceiveMessageTimeoutMs = -1); | |||
| /** Tries to create a new pipe for other processes to connect to. | |||
| This creates a pipe with the given name, so that other processes can use | |||
| connectToPipe() to connect to the other end. | |||
| You can optionally specify a timeout length to be passed to the NamedPipe::read() method. | |||
| If another process is already using this pipe, this will fail and return false. | |||
| */ | |||
| bool createPipe (const String& pipeName, | |||
| const int pipeReceiveMessageTimeoutMs = -1); | |||
| /** Disconnects and closes any currently-open sockets or pipes. */ | |||
| void disconnect(); | |||
| /** True if a socket or pipe is currently active. */ | |||
| bool isConnected() const; | |||
| /** Returns the socket that this connection is using (or null if it uses a pipe). */ | |||
| StreamingSocket* getSocket() const throw() { return socket; } | |||
| /** Returns the pipe that this connection is using (or null if it uses a socket). */ | |||
| NamedPipe* getPipe() const throw() { return pipe; } | |||
| /** Returns the name of the machine at the other end of this connection. | |||
| This will return an empty string if the other machine isn't known for | |||
| some reason. | |||
| */ | |||
| const String getConnectedHostName() const; | |||
| //============================================================================== | |||
| /** Tries to send a message to the other end of this connection. | |||
| This will fail if it's not connected, or if there's some kind of write error. If | |||
| it succeeds, the connection object at the other end will receive the message by | |||
| a callback to its messageReceived() method. | |||
| @see messageReceived | |||
| */ | |||
| bool sendMessage (const MemoryBlock& message); | |||
| //============================================================================== | |||
| /** Called when the connection is first connected. | |||
| If the connection was created with the callbacksOnMessageThread flag set, then | |||
| this will be called on the message thread; otherwise it will be called on a server | |||
| thread. | |||
| */ | |||
| virtual void connectionMade() = 0; | |||
| /** Called when the connection is broken. | |||
| If the connection was created with the callbacksOnMessageThread flag set, then | |||
| this will be called on the message thread; otherwise it will be called on a server | |||
| thread. | |||
| */ | |||
| virtual void connectionLost() = 0; | |||
| /** Called when a message arrives. | |||
| When the object at the other end of this connection sends us a message with sendMessage(), | |||
| this callback is used to deliver it to us. | |||
| If the connection was created with the callbacksOnMessageThread flag set, then | |||
| this will be called on the message thread; otherwise it will be called on a server | |||
| thread. | |||
| @see sendMessage | |||
| */ | |||
| virtual void messageReceived (const MemoryBlock& message) = 0; | |||
| //============================================================================== | |||
| juce_UseDebuggingNewOperator | |||
| private: | |||
| CriticalSection pipeAndSocketLock; | |||
| StreamingSocket* socket; | |||
| NamedPipe* pipe; | |||
| bool callbackConnectionState; | |||
| const bool useMessageThread; | |||
| const uint32 magicMessageHeader; | |||
| int pipeReceiveMessageTimeout; | |||
| //============================================================================== | |||
| friend class InterprocessConnectionServer; | |||
| void initialiseWithSocket (StreamingSocket* const socket_); | |||
| void initialiseWithPipe (NamedPipe* const pipe_); | |||
| void handleMessage (const Message& message); | |||
| void connectionMadeInt(); | |||
| void connectionLostInt(); | |||
| void deliverDataInt (const MemoryBlock& data); | |||
| bool readNextMessageInt(); | |||
| void run(); | |||
| InterprocessConnection (const InterprocessConnection&); | |||
| const InterprocessConnection& operator= (const InterprocessConnection&); | |||
| }; | |||
| #endif // __JUCE_INTERPROCESSCONNECTION_JUCEHEADER__ | |||
| @@ -0,0 +1,97 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_InterprocessConnectionServer.h" | |||
| //============================================================================== | |||
| InterprocessConnectionServer::InterprocessConnectionServer() | |||
| : Thread ("Juce IPC server"), | |||
| socket (0) | |||
| { | |||
| } | |||
| InterprocessConnectionServer::~InterprocessConnectionServer() | |||
| { | |||
| stop(); | |||
| } | |||
| //============================================================================== | |||
| bool InterprocessConnectionServer::beginWaitingForSocket (const int portNumber) | |||
| { | |||
| stop(); | |||
| socket = new StreamingSocket(); | |||
| if (socket->createListener (portNumber)) | |||
| { | |||
| startThread(); | |||
| return true; | |||
| } | |||
| deleteAndZero (socket); | |||
| return false; | |||
| } | |||
| void InterprocessConnectionServer::stop() | |||
| { | |||
| signalThreadShouldExit(); | |||
| if (socket != 0) | |||
| socket->close(); | |||
| stopThread (4000); | |||
| deleteAndZero (socket); | |||
| } | |||
| void InterprocessConnectionServer::run() | |||
| { | |||
| while ((! threadShouldExit()) && socket != 0) | |||
| { | |||
| StreamingSocket* const clientSocket = socket->waitForNextConnection(); | |||
| if (clientSocket != 0) | |||
| { | |||
| InterprocessConnection* newConnection = createConnectionObject(); | |||
| if (newConnection != 0) | |||
| { | |||
| newConnection->initialiseWithSocket (clientSocket); | |||
| } | |||
| else | |||
| { | |||
| delete clientSocket; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,53 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_MessageListener.h" | |||
| //============================================================================== | |||
| Message::Message() throw() | |||
| { | |||
| } | |||
| Message::~Message() throw() | |||
| { | |||
| } | |||
| Message::Message (const int intParameter1_, | |||
| const int intParameter2_, | |||
| const int intParameter3_, | |||
| void* const pointerParameter_) throw() | |||
| : intParameter1 (intParameter1_), | |||
| intParameter2 (intParameter2_), | |||
| intParameter3 (intParameter3_), | |||
| pointerParameter (pointerParameter_) | |||
| { | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,65 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_MessageManager.h" | |||
| //============================================================================== | |||
| MessageListener::MessageListener() throw() | |||
| { | |||
| // are you trying to create a messagelistener before or after juce has been intialised?? | |||
| jassert (MessageManager::instance != 0); | |||
| if (MessageManager::instance != 0) | |||
| MessageManager::instance->messageListeners.add (this); | |||
| } | |||
| MessageListener::~MessageListener() | |||
| { | |||
| if (MessageManager::instance != 0) | |||
| MessageManager::instance->messageListeners.removeValue (this); | |||
| } | |||
| void MessageListener::postMessage (Message* const message) const throw() | |||
| { | |||
| message->messageRecipient = const_cast <MessageListener*> (this); | |||
| if (MessageManager::instance == 0) | |||
| MessageManager::getInstance(); | |||
| MessageManager::instance->postMessageToQueue (message); | |||
| } | |||
| bool MessageListener::isValidMessageListener() const throw() | |||
| { | |||
| return (MessageManager::instance != 0) | |||
| && MessageManager::instance->messageListeners.contains (this); | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,343 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_MessageManager.h" | |||
| #include "juce_ActionListenerList.h" | |||
| #include "../application/juce_Application.h" | |||
| #include "../gui/components/juce_Component.h" | |||
| #include "../threads/juce_Thread.h" | |||
| #include "../threads/juce_ScopedLock.h" | |||
| #include "../core/juce_Time.h" | |||
| //============================================================================== | |||
| // platform-specific functions.. | |||
| bool juce_dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages); | |||
| bool juce_postMessageToSystemQueue (void* message); | |||
| //============================================================================== | |||
| MessageManager* MessageManager::instance = 0; | |||
| static const int quitMessageId = 0xfffff321; | |||
| MessageManager::MessageManager() throw() | |||
| : broadcastListeners (0), | |||
| quitMessagePosted (false), | |||
| quitMessageReceived (false), | |||
| threadWithLock (0) | |||
| { | |||
| messageThreadId = Thread::getCurrentThreadId(); | |||
| } | |||
| MessageManager::~MessageManager() throw() | |||
| { | |||
| deleteAndZero (broadcastListeners); | |||
| doPlatformSpecificShutdown(); | |||
| jassert (instance == this); | |||
| instance = 0; // do this last in case this instance is still needed by doPlatformSpecificShutdown() | |||
| } | |||
| MessageManager* MessageManager::getInstance() throw() | |||
| { | |||
| if (instance == 0) | |||
| { | |||
| instance = new MessageManager(); | |||
| doPlatformSpecificInitialisation(); | |||
| } | |||
| return instance; | |||
| } | |||
| void MessageManager::postMessageToQueue (Message* const message) | |||
| { | |||
| if (quitMessagePosted || ! juce_postMessageToSystemQueue (message)) | |||
| delete message; | |||
| } | |||
| //============================================================================== | |||
| CallbackMessage::CallbackMessage() throw() {} | |||
| CallbackMessage::~CallbackMessage() throw() {} | |||
| void CallbackMessage::post() | |||
| { | |||
| if (MessageManager::instance != 0) | |||
| MessageManager::instance->postCallbackMessage (this); | |||
| } | |||
| void MessageManager::postCallbackMessage (Message* const message) | |||
| { | |||
| message->messageRecipient = 0; | |||
| postMessageToQueue (message); | |||
| } | |||
| //============================================================================== | |||
| // not for public use.. | |||
| void MessageManager::deliverMessage (void* message) | |||
| { | |||
| Message* const m = (Message*) message; | |||
| MessageListener* const recipient = m->messageRecipient; | |||
| JUCE_TRY | |||
| { | |||
| if (messageListeners.contains (recipient)) | |||
| { | |||
| recipient->handleMessage (*m); | |||
| } | |||
| else if (recipient == 0) | |||
| { | |||
| if (m->intParameter1 == quitMessageId) | |||
| { | |||
| quitMessageReceived = true; | |||
| } | |||
| else if (dynamic_cast <CallbackMessage*> (m) != 0) | |||
| { | |||
| (dynamic_cast <CallbackMessage*> (m))->messageCallback(); | |||
| } | |||
| } | |||
| } | |||
| JUCE_CATCH_EXCEPTION | |||
| delete m; | |||
| } | |||
| //============================================================================== | |||
| #if ! JUCE_MAC | |||
| void MessageManager::runDispatchLoop() | |||
| { | |||
| jassert (isThisTheMessageThread()); // must only be called by the message thread | |||
| runDispatchLoopUntil (-1); | |||
| } | |||
| void MessageManager::stopDispatchLoop() | |||
| { | |||
| Message* const m = new Message (quitMessageId, 0, 0, 0); | |||
| m->messageRecipient = 0; | |||
| postMessageToQueue (m); | |||
| quitMessagePosted = true; | |||
| } | |||
| bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) | |||
| { | |||
| jassert (isThisTheMessageThread()); // must only be called by the message thread | |||
| const int64 endTime = Time::currentTimeMillis() + millisecondsToRunFor; | |||
| while ((millisecondsToRunFor < 0 || endTime > Time::currentTimeMillis()) | |||
| && ! quitMessageReceived) | |||
| { | |||
| JUCE_TRY | |||
| { | |||
| if (! juce_dispatchNextMessageOnSystemQueue (millisecondsToRunFor >= 0)) | |||
| { | |||
| const int msToWait = (int) (endTime - Time::currentTimeMillis()); | |||
| if (msToWait > 0) | |||
| Thread::sleep (jmin (5, msToWait)); | |||
| } | |||
| } | |||
| JUCE_CATCH_EXCEPTION | |||
| } | |||
| return ! quitMessageReceived; | |||
| } | |||
| #endif | |||
| //============================================================================== | |||
| void MessageManager::deliverBroadcastMessage (const String& value) | |||
| { | |||
| if (broadcastListeners != 0) | |||
| broadcastListeners->sendActionMessage (value); | |||
| } | |||
| void MessageManager::registerBroadcastListener (ActionListener* const listener) throw() | |||
| { | |||
| if (broadcastListeners == 0) | |||
| broadcastListeners = new ActionListenerList(); | |||
| broadcastListeners->addActionListener (listener); | |||
| } | |||
| void MessageManager::deregisterBroadcastListener (ActionListener* const listener) throw() | |||
| { | |||
| if (broadcastListeners != 0) | |||
| broadcastListeners->removeActionListener (listener); | |||
| } | |||
| //============================================================================== | |||
| bool MessageManager::isThisTheMessageThread() const throw() | |||
| { | |||
| return Thread::getCurrentThreadId() == messageThreadId; | |||
| } | |||
| void MessageManager::setCurrentMessageThread (const Thread::ThreadID threadId) throw() | |||
| { | |||
| messageThreadId = threadId; | |||
| } | |||
| bool MessageManager::currentThreadHasLockedMessageManager() const throw() | |||
| { | |||
| const Thread::ThreadID thisThread = Thread::getCurrentThreadId(); | |||
| return thisThread == messageThreadId || thisThread == threadWithLock; | |||
| } | |||
| //============================================================================== | |||
| //============================================================================== | |||
| /* The only safe way to lock the message thread while another thread does | |||
| some work is by posting a special message, whose purpose is to tie up the event | |||
| loop until the other thread has finished its business. | |||
| Any other approach can get horribly deadlocked if the OS uses its own hidden locks which | |||
| get locked before making an event callback, because if the same OS lock gets indirectly | |||
| accessed from another thread inside a MM lock, you're screwed. (this is exactly what happens | |||
| in Cocoa). | |||
| */ | |||
| class SharedLockingEvents : public ReferenceCountedObject | |||
| { | |||
| public: | |||
| SharedLockingEvents() throw() {} | |||
| ~SharedLockingEvents() {} | |||
| /* This class just holds a couple of events to communicate between the MMLockMessage | |||
| and the MessageManagerLock. Because both of these objects may be deleted at any time, | |||
| this shared data must be kept in a separate, ref-counted container. */ | |||
| WaitableEvent lockedEvent, releaseEvent; | |||
| }; | |||
| class MMLockMessage : public CallbackMessage | |||
| { | |||
| public: | |||
| MMLockMessage (SharedLockingEvents* const events_) throw() | |||
| : events (events_) | |||
| {} | |||
| ~MMLockMessage() throw() {} | |||
| ReferenceCountedObjectPtr <SharedLockingEvents> events; | |||
| void messageCallback() | |||
| { | |||
| events->lockedEvent.signal(); | |||
| events->releaseEvent.wait(); | |||
| } | |||
| juce_UseDebuggingNewOperator | |||
| MMLockMessage (const MMLockMessage&); | |||
| const MMLockMessage& operator= (const MMLockMessage&); | |||
| }; | |||
| //============================================================================== | |||
| MessageManagerLock::MessageManagerLock (Thread* const threadToCheck) throw() | |||
| : locked (false), | |||
| needsUnlocking (false) | |||
| { | |||
| init (threadToCheck, 0); | |||
| } | |||
| MessageManagerLock::MessageManagerLock (ThreadPoolJob* const jobToCheckForExitSignal) throw() | |||
| : locked (false), | |||
| needsUnlocking (false) | |||
| { | |||
| init (0, jobToCheckForExitSignal); | |||
| } | |||
| void MessageManagerLock::init (Thread* const threadToCheck, ThreadPoolJob* const job) throw() | |||
| { | |||
| if (MessageManager::instance != 0) | |||
| { | |||
| if (MessageManager::instance->currentThreadHasLockedMessageManager()) | |||
| { | |||
| locked = true; // either we're on the message thread, or this is a re-entrant call. | |||
| } | |||
| else | |||
| { | |||
| if (threadToCheck == 0 && job == 0) | |||
| { | |||
| MessageManager::instance->lockingLock.enter(); | |||
| } | |||
| else | |||
| { | |||
| while (! MessageManager::instance->lockingLock.tryEnter()) | |||
| { | |||
| if ((threadToCheck != 0 && threadToCheck->threadShouldExit()) | |||
| || (job != 0 && job->shouldExit())) | |||
| return; | |||
| Thread::sleep (1); | |||
| } | |||
| } | |||
| SharedLockingEvents* const events = new SharedLockingEvents(); | |||
| sharedEvents = events; | |||
| events->incReferenceCount(); | |||
| (new MMLockMessage (events))->post(); | |||
| while (! events->lockedEvent.wait (50)) | |||
| { | |||
| if ((threadToCheck != 0 && threadToCheck->threadShouldExit()) | |||
| || (job != 0 && job->shouldExit())) | |||
| { | |||
| events->releaseEvent.signal(); | |||
| events->decReferenceCount(); | |||
| MessageManager::instance->lockingLock.exit(); | |||
| return; | |||
| } | |||
| } | |||
| jassert (MessageManager::instance->threadWithLock == 0); | |||
| MessageManager::instance->threadWithLock = Thread::getCurrentThreadId(); | |||
| locked = true; | |||
| needsUnlocking = true; | |||
| } | |||
| } | |||
| } | |||
| MessageManagerLock::~MessageManagerLock() throw() | |||
| { | |||
| if (needsUnlocking && MessageManager::instance != 0) | |||
| { | |||
| jassert (MessageManager::instance->currentThreadHasLockedMessageManager()); | |||
| ((SharedLockingEvents*) sharedEvents)->releaseEvent.signal(); | |||
| ((SharedLockingEvents*) sharedEvents)->decReferenceCount(); | |||
| MessageManager::instance->threadWithLock = 0; | |||
| MessageManager::instance->lockingLock.exit(); | |||
| } | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,315 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_MESSAGEMANAGER_JUCEHEADER__ | |||
| #define __JUCE_MESSAGEMANAGER_JUCEHEADER__ | |||
| #include "../utilities/juce_DeletedAtShutdown.h" | |||
| #include "../containers/juce_SortedSet.h" | |||
| #include "../containers/juce_VoidArray.h" | |||
| #include "../threads/juce_Thread.h" | |||
| #include "../threads/juce_ThreadPool.h" | |||
| #include "juce_ActionListenerList.h" | |||
| #include "juce_CallbackMessage.h" | |||
| class Component; | |||
| class MessageManagerLock; | |||
| //============================================================================== | |||
| /** See MessageManager::callFunctionOnMessageThread() for use of this function type | |||
| */ | |||
| typedef void* (MessageCallbackFunction) (void* userData); | |||
| //============================================================================== | |||
| /** Delivers Message objects to MessageListeners, and handles the event-dispatch loop. | |||
| @see Message, MessageListener, MessageManagerLock, JUCEApplication | |||
| */ | |||
| class JUCE_API MessageManager | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Returns the global instance of the MessageManager. */ | |||
| static MessageManager* getInstance() throw(); | |||
| //============================================================================== | |||
| /** Runs the event dispatch loop until a stop message is posted. | |||
| This method is only intended to be run by the application's startup routine, | |||
| as it blocks, and will only return after the stopDispatchLoop() method has been used. | |||
| @see stopDispatchLoop | |||
| */ | |||
| void runDispatchLoop(); | |||
| /** Sends a signal that the dispatch loop should terminate. | |||
| After this is called, the runDispatchLoop() or runDispatchLoopUntil() methods | |||
| will be interrupted and will return. | |||
| @see runDispatchLoop | |||
| */ | |||
| void stopDispatchLoop(); | |||
| /** Returns true if the stopDispatchLoop() method has been called. | |||
| */ | |||
| bool hasStopMessageBeenSent() const throw() { return quitMessagePosted; } | |||
| /** Synchronously dispatches messages until a given time has elapsed. | |||
| Returns false if a quit message has been posted by a call to stopDispatchLoop(), | |||
| otherwise returns true. | |||
| */ | |||
| bool runDispatchLoopUntil (int millisecondsToRunFor); | |||
| //============================================================================== | |||
| /** Calls a function using the message-thread. | |||
| This can be used by any thread to cause this function to be called-back | |||
| by the message thread. If it's the message-thread that's calling this method, | |||
| then the function will just be called; if another thread is calling, a message | |||
| will be posted to the queue, and this method will block until that message | |||
| is delivered, the function is called, and the result is returned. | |||
| Be careful not to cause any deadlocks with this! It's easy to do - e.g. if the caller | |||
| thread has a critical section locked, which an unrelated message callback then tries to lock | |||
| before the message thread gets round to processing this callback. | |||
| @param callback the function to call - its signature must be @code | |||
| void* myCallbackFunction (void*) @endcode | |||
| @param userData a user-defined pointer that will be passed to the function that gets called | |||
| @returns the value that the callback function returns. | |||
| @see MessageManagerLock | |||
| */ | |||
| void* callFunctionOnMessageThread (MessageCallbackFunction* callback, | |||
| void* userData); | |||
| /** Returns true if the caller-thread is the message thread. */ | |||
| bool isThisTheMessageThread() const throw(); | |||
| /** Called to tell the manager which thread is the one that's running the dispatch loop. | |||
| (Best to ignore this method unless you really know what you're doing..) | |||
| @see getCurrentMessageThread | |||
| */ | |||
| void setCurrentMessageThread (const Thread::ThreadID threadId) throw(); | |||
| /** Returns the ID of the current message thread, as set by setCurrentMessageThread(). | |||
| (Best to ignore this method unless you really know what you're doing..) | |||
| @see setCurrentMessageThread | |||
| */ | |||
| Thread::ThreadID getCurrentMessageThread() const throw() { return messageThreadId; } | |||
| /** Returns true if the caller thread has currenltly got the message manager locked. | |||
| see the MessageManagerLock class for more info about this. | |||
| This will be true if the caller is the message thread, because that automatically | |||
| gains a lock while a message is being dispatched. | |||
| */ | |||
| bool currentThreadHasLockedMessageManager() const throw(); | |||
| //============================================================================== | |||
| /** Sends a message to all other JUCE applications that are running. | |||
| @param messageText the string that will be passed to the actionListenerCallback() | |||
| method of the broadcast listeners in the other app. | |||
| @see registerBroadcastListener, ActionListener | |||
| */ | |||
| static void broadcastMessage (const String& messageText) throw(); | |||
| /** Registers a listener to get told about broadcast messages. | |||
| The actionListenerCallback() callback's string parameter | |||
| is the message passed into broadcastMessage(). | |||
| @see broadcastMessage | |||
| */ | |||
| void registerBroadcastListener (ActionListener* listener) throw(); | |||
| /** Deregisters a broadcast listener. */ | |||
| void deregisterBroadcastListener (ActionListener* listener) throw(); | |||
| //============================================================================== | |||
| /** @internal */ | |||
| void deliverMessage (void*); | |||
| /** @internal */ | |||
| void deliverBroadcastMessage (const String&); | |||
| /** @internal */ | |||
| ~MessageManager() throw(); | |||
| //============================================================================== | |||
| juce_UseDebuggingNewOperator | |||
| private: | |||
| MessageManager() throw(); | |||
| friend class MessageListener; | |||
| friend class ChangeBroadcaster; | |||
| friend class ActionBroadcaster; | |||
| friend class CallbackMessage; | |||
| static MessageManager* instance; | |||
| SortedSet<const MessageListener*> messageListeners; | |||
| ActionListenerList* broadcastListeners; | |||
| friend class JUCEApplication; | |||
| bool quitMessagePosted, quitMessageReceived; | |||
| Thread::ThreadID messageThreadId; | |||
| VoidArray modalComponents; | |||
| static void* exitModalLoopCallback (void*); | |||
| void postMessageToQueue (Message* const message); | |||
| void postCallbackMessage (Message* const message); | |||
| static void doPlatformSpecificInitialisation(); | |||
| static void doPlatformSpecificShutdown(); | |||
| friend class MessageManagerLock; | |||
| Thread::ThreadID volatile threadWithLock; | |||
| CriticalSection lockingLock; | |||
| MessageManager (const MessageManager&); | |||
| const MessageManager& operator= (const MessageManager&); | |||
| }; | |||
| //============================================================================== | |||
| /** Used to make sure that the calling thread has exclusive access to the message loop. | |||
| Because it's not thread-safe to call any of the Component or other UI classes | |||
| from threads other than the message thread, one of these objects can be used to | |||
| lock the message loop and allow this to be done. The message thread will be | |||
| suspended for the lifetime of the MessageManagerLock object, so create one on | |||
| the stack like this: @code | |||
| void MyThread::run() | |||
| { | |||
| someData = 1234; | |||
| const MessageManagerLock mmLock; | |||
| // the event loop will now be locked so it's safe to make a few calls.. | |||
| myComponent->setBounds (newBounds); | |||
| myComponent->repaint(); | |||
| // ..the event loop will now be unlocked as the MessageManagerLock goes out of scope | |||
| } | |||
| @endcode | |||
| Obviously be careful not to create one of these and leave it lying around, or | |||
| your app will grind to a halt! | |||
| Another caveat is that using this in conjunction with other CriticalSections | |||
| can create lots of interesting ways of producing a deadlock! In particular, if | |||
| your message thread calls stopThread() for a thread that uses these locks, | |||
| you'll get an (occasional) deadlock.. | |||
| @see MessageManager, MessageManager::currentThreadHasLockedMessageManager | |||
| */ | |||
| class JUCE_API MessageManagerLock | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Tries to acquire a lock on the message manager. | |||
| The constructor attempts to gain a lock on the message loop, and the lock will be | |||
| kept for the lifetime of this object. | |||
| Optionally, you can pass a thread object here, and while waiting to obtain the lock, | |||
| this method will keep checking whether the thread has been given the | |||
| Thread::signalThreadShouldExit() signal. If this happens, then it will return | |||
| without gaining the lock. If you pass a thread, you must check whether the lock was | |||
| successful by calling lockWasGained(). If this is false, your thread is being told to | |||
| die, so you should take evasive action. | |||
| If you pass zero for the thread object, it will wait indefinitely for the lock - be | |||
| careful when doing this, because it's very easy to deadlock if your message thread | |||
| attempts to call stopThread() on a thread just as that thread attempts to get the | |||
| message lock. | |||
| If the calling thread already has the lock, nothing will be done, so it's safe and | |||
| quick to use these locks recursively. | |||
| E.g. | |||
| @code | |||
| void run() | |||
| { | |||
| ... | |||
| while (! threadShouldExit()) | |||
| { | |||
| MessageManagerLock mml (Thread::getCurrentThread()); | |||
| if (! mml.lockWasGained()) | |||
| return; // another thread is trying to kill us! | |||
| ..do some locked stuff here.. | |||
| } | |||
| ..and now the MM is now unlocked.. | |||
| } | |||
| @endcode | |||
| */ | |||
| MessageManagerLock (Thread* const threadToCheckForExitSignal = 0) throw(); | |||
| //============================================================================== | |||
| /** This has the same behaviour as the other constructor, but takes a ThreadPoolJob | |||
| instead of a thread. | |||
| See the MessageManagerLock (Thread*) constructor for details on how this works. | |||
| */ | |||
| MessageManagerLock (ThreadPoolJob* const jobToCheckForExitSignal) throw(); | |||
| //============================================================================== | |||
| /** Releases the current thread's lock on the message manager. | |||
| Make sure this object is created and deleted by the same thread, | |||
| otherwise there are no guarantees what will happen! | |||
| */ | |||
| ~MessageManagerLock() throw(); | |||
| //============================================================================== | |||
| /** Returns true if the lock was successfully acquired. | |||
| (See the constructor that takes a Thread for more info). | |||
| */ | |||
| bool lockWasGained() const throw() { return locked; } | |||
| private: | |||
| bool locked, needsUnlocking; | |||
| void* sharedEvents; | |||
| void init (Thread* const thread, ThreadPoolJob* const job) throw(); | |||
| }; | |||
| #endif // __JUCE_MESSAGEMANAGER_JUCEHEADER__ | |||
| @@ -0,0 +1,142 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_MultiTimer.h" | |||
| #include "juce_Timer.h" | |||
| #include "../threads/juce_ScopedLock.h" | |||
| //============================================================================== | |||
| class InternalMultiTimerCallback : public Timer | |||
| { | |||
| public: | |||
| InternalMultiTimerCallback (const int timerId_, MultiTimer& owner_) | |||
| : timerId (timerId_), | |||
| owner (owner_) | |||
| { | |||
| } | |||
| ~InternalMultiTimerCallback() | |||
| { | |||
| } | |||
| void timerCallback() | |||
| { | |||
| owner.timerCallback (timerId); | |||
| } | |||
| const int timerId; | |||
| private: | |||
| MultiTimer& owner; | |||
| }; | |||
| //============================================================================== | |||
| MultiTimer::MultiTimer() throw() | |||
| { | |||
| } | |||
| MultiTimer::MultiTimer (const MultiTimer&) throw() | |||
| { | |||
| } | |||
| MultiTimer::~MultiTimer() | |||
| { | |||
| const ScopedLock sl (timerListLock); | |||
| for (int i = timers.size(); --i >= 0;) | |||
| delete (InternalMultiTimerCallback*) timers.getUnchecked(i); | |||
| timers.clear(); | |||
| } | |||
| //============================================================================== | |||
| void MultiTimer::startTimer (const int timerId, const int intervalInMilliseconds) throw() | |||
| { | |||
| const ScopedLock sl (timerListLock); | |||
| for (int i = timers.size(); --i >= 0;) | |||
| { | |||
| InternalMultiTimerCallback* const t = (InternalMultiTimerCallback*) timers.getUnchecked(i); | |||
| if (t->timerId == timerId) | |||
| { | |||
| t->startTimer (intervalInMilliseconds); | |||
| return; | |||
| } | |||
| } | |||
| InternalMultiTimerCallback* const newTimer = new InternalMultiTimerCallback (timerId, *this); | |||
| timers.add (newTimer); | |||
| newTimer->startTimer (intervalInMilliseconds); | |||
| } | |||
| void MultiTimer::stopTimer (const int timerId) throw() | |||
| { | |||
| const ScopedLock sl (timerListLock); | |||
| for (int i = timers.size(); --i >= 0;) | |||
| { | |||
| InternalMultiTimerCallback* const t = (InternalMultiTimerCallback*) timers.getUnchecked(i); | |||
| if (t->timerId == timerId) | |||
| t->stopTimer(); | |||
| } | |||
| } | |||
| bool MultiTimer::isTimerRunning (const int timerId) const throw() | |||
| { | |||
| const ScopedLock sl (timerListLock); | |||
| for (int i = timers.size(); --i >= 0;) | |||
| { | |||
| const InternalMultiTimerCallback* const t = (InternalMultiTimerCallback*) timers.getUnchecked(i); | |||
| if (t->timerId == timerId) | |||
| return t->isTimerRunning(); | |||
| } | |||
| return false; | |||
| } | |||
| int MultiTimer::getTimerInterval (const int timerId) const throw() | |||
| { | |||
| const ScopedLock sl (timerListLock); | |||
| for (int i = timers.size(); --i >= 0;) | |||
| { | |||
| const InternalMultiTimerCallback* const t = (InternalMultiTimerCallback*) timers.getUnchecked(i); | |||
| if (t->timerId == timerId) | |||
| return t->getTimerInterval(); | |||
| } | |||
| return 0; | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,131 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_MULTITIMER_JUCEHEADER__ | |||
| #define __JUCE_MULTITIMER_JUCEHEADER__ | |||
| #include "juce_Timer.h" | |||
| #include "../containers/juce_VoidArray.h" | |||
| //============================================================================== | |||
| /** | |||
| A type of timer class that can run multiple timers with different frequencies, | |||
| all of which share a single callback. | |||
| This class is very similar to the Timer class, but allows you run multiple | |||
| separate timers, where each one has a unique ID number. The methods in this | |||
| class are exactly equivalent to those in Timer, but with the addition of | |||
| this ID number. | |||
| To use it, you need to create a subclass of MultiTimer, implementing the | |||
| timerCallback() method. Then you can start timers with startTimer(), and | |||
| each time the callback is triggered, it passes in the ID of the timer that | |||
| caused it. | |||
| @see Timer | |||
| */ | |||
| class JUCE_API MultiTimer | |||
| { | |||
| protected: | |||
| //============================================================================== | |||
| /** Creates a MultiTimer. | |||
| When created, no timers are running, so use startTimer() to start things off. | |||
| */ | |||
| MultiTimer() throw(); | |||
| /** Creates a copy of another timer. | |||
| Note that this timer will not contain any running timers, even if the one you're | |||
| copying from was running. | |||
| */ | |||
| MultiTimer (const MultiTimer& other) throw(); | |||
| public: | |||
| //============================================================================== | |||
| /** Destructor. */ | |||
| virtual ~MultiTimer(); | |||
| //============================================================================== | |||
| /** The user-defined callback routine that actually gets called by each of the | |||
| timers that are running. | |||
| It's perfectly ok to call startTimer() or stopTimer() from within this | |||
| callback to change the subsequent intervals. | |||
| */ | |||
| virtual void timerCallback (const int timerId) = 0; | |||
| //============================================================================== | |||
| /** Starts a timer and sets the length of interval required. | |||
| If the timer is already started, this will reset it, so the | |||
| time between calling this method and the next timer callback | |||
| will not be less than the interval length passed in. | |||
| @param timerId a unique Id number that identifies the timer to | |||
| start. This is the id that will be passed back | |||
| to the timerCallback() method when this timer is | |||
| triggered | |||
| @param intervalInMilliseconds the interval to use (any values less than 1 will be | |||
| rounded up to 1) | |||
| */ | |||
| void startTimer (const int timerId, const int intervalInMilliseconds) throw(); | |||
| /** Stops a timer. | |||
| If a timer has been started with the given ID number, it will be cancelled. | |||
| No more callbacks will be made for the specified timer after this method returns. | |||
| If this is called from a different thread, any callbacks that may | |||
| be currently executing may be allowed to finish before the method | |||
| returns. | |||
| */ | |||
| void stopTimer (const int timerId) throw(); | |||
| //============================================================================== | |||
| /** Checks whether a timer has been started for a specified ID. | |||
| @returns true if a timer with the given ID is running. | |||
| */ | |||
| bool isTimerRunning (const int timerId) const throw(); | |||
| /** Returns the interval for a specified timer ID. | |||
| @returns the timer's interval in milliseconds if it's running, or 0 if it's no timer | |||
| is running for the ID number specified. | |||
| */ | |||
| int getTimerInterval (const int timerId) const throw(); | |||
| //============================================================================== | |||
| private: | |||
| CriticalSection timerListLock; | |||
| VoidArray timers; | |||
| const MultiTimer& operator= (const MultiTimer&); | |||
| }; | |||
| #endif // __JUCE_MULTITIMER_JUCEHEADER__ | |||
| @@ -0,0 +1,382 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_Timer.h" | |||
| #include "juce_MessageManager.h" | |||
| #include "juce_AsyncUpdater.h" | |||
| #include "../application/juce_Application.h" | |||
| #include "../utilities/juce_DeletedAtShutdown.h" | |||
| #include "../core/juce_Time.h" | |||
| #include "../threads/juce_Thread.h" | |||
| #include "../threads/juce_ScopedLock.h" | |||
| #include "../containers/juce_VoidArray.h" | |||
| //============================================================================== | |||
| class InternalTimerThread : private Thread, | |||
| private MessageListener, | |||
| private DeletedAtShutdown, | |||
| private AsyncUpdater | |||
| { | |||
| private: | |||
| friend class Timer; | |||
| static InternalTimerThread* instance; | |||
| static CriticalSection lock; | |||
| Timer* volatile firstTimer; | |||
| bool volatile callbackNeeded; | |||
| InternalTimerThread (const InternalTimerThread&); | |||
| const InternalTimerThread& operator= (const InternalTimerThread&); | |||
| void addTimer (Timer* const t) throw() | |||
| { | |||
| #ifdef JUCE_DEBUG | |||
| Timer* tt = firstTimer; | |||
| while (tt != 0) | |||
| { | |||
| // trying to add a timer that's already here - shouldn't get to this point, | |||
| // so if you get this assertion, let me know! | |||
| jassert (tt != t); | |||
| tt = tt->next; | |||
| } | |||
| jassert (t->previous == 0 && t->next == 0); | |||
| #endif | |||
| Timer* i = firstTimer; | |||
| if (i == 0 || i->countdownMs > t->countdownMs) | |||
| { | |||
| t->next = firstTimer; | |||
| firstTimer = t; | |||
| } | |||
| else | |||
| { | |||
| while (i->next != 0 && i->next->countdownMs <= t->countdownMs) | |||
| i = i->next; | |||
| jassert (i != 0); | |||
| t->next = i->next; | |||
| t->previous = i; | |||
| i->next = t; | |||
| } | |||
| if (t->next != 0) | |||
| t->next->previous = t; | |||
| jassert ((t->next == 0 || t->next->countdownMs >= t->countdownMs) | |||
| && (t->previous == 0 || t->previous->countdownMs <= t->countdownMs)); | |||
| notify(); | |||
| } | |||
| void removeTimer (Timer* const t) throw() | |||
| { | |||
| #ifdef JUCE_DEBUG | |||
| Timer* tt = firstTimer; | |||
| bool found = false; | |||
| while (tt != 0) | |||
| { | |||
| if (tt == t) | |||
| { | |||
| found = true; | |||
| break; | |||
| } | |||
| tt = tt->next; | |||
| } | |||
| // trying to remove a timer that's not here - shouldn't get to this point, | |||
| // so if you get this assertion, let me know! | |||
| jassert (found); | |||
| #endif | |||
| if (t->previous != 0) | |||
| { | |||
| jassert (firstTimer != t); | |||
| t->previous->next = t->next; | |||
| } | |||
| else | |||
| { | |||
| jassert (firstTimer == t); | |||
| firstTimer = t->next; | |||
| } | |||
| if (t->next != 0) | |||
| t->next->previous = t->previous; | |||
| t->next = 0; | |||
| t->previous = 0; | |||
| } | |||
| void decrementAllCounters (const int numMillisecs) const | |||
| { | |||
| Timer* t = firstTimer; | |||
| while (t != 0) | |||
| { | |||
| t->countdownMs -= numMillisecs; | |||
| t = t->next; | |||
| } | |||
| } | |||
| void handleAsyncUpdate() | |||
| { | |||
| startThread (7); | |||
| } | |||
| public: | |||
| InternalTimerThread() | |||
| : Thread ("Juce Timer"), | |||
| firstTimer (0), | |||
| callbackNeeded (false) | |||
| { | |||
| triggerAsyncUpdate(); | |||
| } | |||
| ~InternalTimerThread() throw() | |||
| { | |||
| stopThread (4000); | |||
| jassert (instance == this || instance == 0); | |||
| if (instance == this) | |||
| instance = 0; | |||
| } | |||
| void run() | |||
| { | |||
| uint32 lastTime = Time::getMillisecondCounter(); | |||
| while (! threadShouldExit()) | |||
| { | |||
| uint32 now = Time::getMillisecondCounter(); | |||
| if (now <= lastTime) | |||
| { | |||
| wait (2); | |||
| continue; | |||
| } | |||
| const int elapsed = now - lastTime; | |||
| lastTime = now; | |||
| lock.enter(); | |||
| decrementAllCounters (elapsed); | |||
| const int timeUntilFirstTimer = (firstTimer != 0) ? firstTimer->countdownMs | |||
| : 1000; | |||
| lock.exit(); | |||
| if (timeUntilFirstTimer <= 0) | |||
| { | |||
| callbackNeeded = true; | |||
| postMessage (new Message()); | |||
| // sometimes, our message could get discarded by the OS (particularly when running as an RTAS when the app has a modal loop), | |||
| // so this is how long to wait before assuming the message has been lost and trying again. | |||
| const uint32 messageDeliveryTimeout = now + 2000; | |||
| while (callbackNeeded) | |||
| { | |||
| wait (4); | |||
| if (threadShouldExit()) | |||
| return; | |||
| now = Time::getMillisecondCounter(); | |||
| if (now > messageDeliveryTimeout) | |||
| break; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| // don't wait for too long because running this loop also helps keep the | |||
| // Time::getApproximateMillisecondTimer value stay up-to-date | |||
| wait (jlimit (1, 50, timeUntilFirstTimer)); | |||
| } | |||
| } | |||
| } | |||
| void handleMessage (const Message&) | |||
| { | |||
| const ScopedLock sl (lock); | |||
| while (firstTimer != 0 && firstTimer->countdownMs <= 0) | |||
| { | |||
| Timer* const t = firstTimer; | |||
| t->countdownMs = t->periodMs; | |||
| removeTimer (t); | |||
| addTimer (t); | |||
| const ScopedUnlock ul (lock); | |||
| JUCE_TRY | |||
| { | |||
| t->timerCallback(); | |||
| } | |||
| JUCE_CATCH_EXCEPTION | |||
| } | |||
| callbackNeeded = false; | |||
| } | |||
| static void callAnyTimersSynchronously() | |||
| { | |||
| if (InternalTimerThread::instance != 0) | |||
| { | |||
| const Message m; | |||
| InternalTimerThread::instance->handleMessage (m); | |||
| } | |||
| } | |||
| static inline void add (Timer* const tim) throw() | |||
| { | |||
| if (instance == 0) | |||
| instance = new InternalTimerThread(); | |||
| const ScopedLock sl (instance->lock); | |||
| instance->addTimer (tim); | |||
| } | |||
| static inline void remove (Timer* const tim) throw() | |||
| { | |||
| if (instance != 0) | |||
| { | |||
| const ScopedLock sl (instance->lock); | |||
| instance->removeTimer (tim); | |||
| } | |||
| } | |||
| static inline void resetCounter (Timer* const tim, | |||
| const int newCounter) throw() | |||
| { | |||
| if (instance != 0) | |||
| { | |||
| tim->countdownMs = newCounter; | |||
| tim->periodMs = newCounter; | |||
| if ((tim->next != 0 && tim->next->countdownMs < tim->countdownMs) | |||
| || (tim->previous != 0 && tim->previous->countdownMs > tim->countdownMs)) | |||
| { | |||
| const ScopedLock sl (instance->lock); | |||
| instance->removeTimer (tim); | |||
| instance->addTimer (tim); | |||
| } | |||
| } | |||
| } | |||
| }; | |||
| InternalTimerThread* InternalTimerThread::instance = 0; | |||
| CriticalSection InternalTimerThread::lock; | |||
| void juce_callAnyTimersSynchronously() | |||
| { | |||
| InternalTimerThread::callAnyTimersSynchronously(); | |||
| } | |||
| //============================================================================== | |||
| #ifdef JUCE_DEBUG | |||
| static SortedSet <Timer*> activeTimers; | |||
| #endif | |||
| Timer::Timer() throw() | |||
| : countdownMs (0), | |||
| periodMs (0), | |||
| previous (0), | |||
| next (0) | |||
| { | |||
| #ifdef JUCE_DEBUG | |||
| activeTimers.add (this); | |||
| #endif | |||
| } | |||
| Timer::Timer (const Timer&) throw() | |||
| : countdownMs (0), | |||
| periodMs (0), | |||
| previous (0), | |||
| next (0) | |||
| { | |||
| #ifdef JUCE_DEBUG | |||
| activeTimers.add (this); | |||
| #endif | |||
| } | |||
| Timer::~Timer() | |||
| { | |||
| stopTimer(); | |||
| #ifdef JUCE_DEBUG | |||
| activeTimers.removeValue (this); | |||
| #endif | |||
| } | |||
| void Timer::startTimer (const int interval) throw() | |||
| { | |||
| const ScopedLock sl (InternalTimerThread::lock); | |||
| #ifdef JUCE_DEBUG | |||
| // this isn't a valid object! Your timer might be a dangling pointer or something.. | |||
| jassert (activeTimers.contains (this)); | |||
| #endif | |||
| if (periodMs == 0) | |||
| { | |||
| countdownMs = interval; | |||
| periodMs = jmax (1, interval); | |||
| InternalTimerThread::add (this); | |||
| } | |||
| else | |||
| { | |||
| InternalTimerThread::resetCounter (this, interval); | |||
| } | |||
| } | |||
| void Timer::stopTimer() throw() | |||
| { | |||
| const ScopedLock sl (InternalTimerThread::lock); | |||
| #ifdef JUCE_DEBUG | |||
| // this isn't a valid object! Your timer might be a dangling pointer or something.. | |||
| jassert (activeTimers.contains (this)); | |||
| #endif | |||
| if (periodMs > 0) | |||
| { | |||
| InternalTimerThread::remove (this); | |||
| periodMs = 0; | |||
| } | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,76 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_ArrowButton.h" | |||
| //============================================================================== | |||
| ArrowButton::ArrowButton (const String& name, | |||
| float arrowDirectionInRadians, | |||
| const Colour& arrowColour) | |||
| : Button (name), | |||
| colour (arrowColour) | |||
| { | |||
| path.lineTo (0.0f, 1.0f); | |||
| path.lineTo (1.0f, 0.5f); | |||
| path.closeSubPath(); | |||
| path.applyTransform (AffineTransform::rotation (float_Pi * 2.0f * arrowDirectionInRadians, | |||
| 0.5f, 0.5f)); | |||
| setComponentEffect (&shadow); | |||
| buttonStateChanged(); | |||
| } | |||
| ArrowButton::~ArrowButton() | |||
| { | |||
| } | |||
| void ArrowButton::paintButton (Graphics& g, | |||
| bool /*isMouseOverButton*/, | |||
| bool /*isButtonDown*/) | |||
| { | |||
| g.setColour (colour); | |||
| g.fillPath (path, path.getTransformToScaleToFit ((float) offset, | |||
| (float) offset, | |||
| (float) (getWidth() - 3), | |||
| (float) (getHeight() - 3), | |||
| false)); | |||
| } | |||
| void ArrowButton::buttonStateChanged() | |||
| { | |||
| offset = (isDown()) ? 1 : 0; | |||
| shadow.setShadowProperties ((isDown()) ? 1.2f : 3.0f, | |||
| 0.3f, -1, 0); | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,725 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_Button.h" | |||
| #include "../juce_ComponentDeletionWatcher.h" | |||
| #include "../keyboard/juce_KeyPressMappingSet.h" | |||
| //============================================================================== | |||
| Button::Button (const String& name) | |||
| : Component (name), | |||
| shortcuts (2), | |||
| keySource (0), | |||
| text (name), | |||
| buttonListeners (2), | |||
| repeatTimer (0), | |||
| buttonPressTime (0), | |||
| lastTimeCallbackTime (0), | |||
| commandManagerToUse (0), | |||
| autoRepeatDelay (-1), | |||
| autoRepeatSpeed (0), | |||
| autoRepeatMinimumDelay (-1), | |||
| radioGroupId (0), | |||
| commandID (0), | |||
| connectedEdgeFlags (0), | |||
| buttonState (buttonNormal), | |||
| isOn (false), | |||
| clickTogglesState (false), | |||
| needsToRelease (false), | |||
| needsRepainting (false), | |||
| isKeyDown (false), | |||
| triggerOnMouseDown (false), | |||
| generateTooltip (false) | |||
| { | |||
| setWantsKeyboardFocus (true); | |||
| } | |||
| Button::~Button() | |||
| { | |||
| if (commandManagerToUse != 0) | |||
| commandManagerToUse->removeListener (this); | |||
| delete repeatTimer; | |||
| clearShortcuts(); | |||
| } | |||
| //============================================================================== | |||
| void Button::setButtonText (const String& newText) throw() | |||
| { | |||
| if (text != newText) | |||
| { | |||
| text = newText; | |||
| repaint(); | |||
| } | |||
| } | |||
| void Button::setTooltip (const String& newTooltip) | |||
| { | |||
| SettableTooltipClient::setTooltip (newTooltip); | |||
| generateTooltip = false; | |||
| } | |||
| const String Button::getTooltip() | |||
| { | |||
| if (generateTooltip && commandManagerToUse != 0 && commandID != 0) | |||
| { | |||
| String tt (commandManagerToUse->getDescriptionOfCommand (commandID)); | |||
| Array <KeyPress> keyPresses (commandManagerToUse->getKeyMappings()->getKeyPressesAssignedToCommand (commandID)); | |||
| for (int i = 0; i < keyPresses.size(); ++i) | |||
| { | |||
| const String key (keyPresses.getReference(i).getTextDescription()); | |||
| if (key.length() == 1) | |||
| tt << " [shortcut: '" << key << "']"; | |||
| else | |||
| tt << " [" << key << ']'; | |||
| } | |||
| return tt; | |||
| } | |||
| return SettableTooltipClient::getTooltip(); | |||
| } | |||
| void Button::setConnectedEdges (const int connectedEdgeFlags_) throw() | |||
| { | |||
| if (connectedEdgeFlags != connectedEdgeFlags_) | |||
| { | |||
| connectedEdgeFlags = connectedEdgeFlags_; | |||
| repaint(); | |||
| } | |||
| } | |||
| //============================================================================== | |||
| void Button::setToggleState (const bool shouldBeOn, | |||
| const bool sendChangeNotification) | |||
| { | |||
| if (shouldBeOn != isOn) | |||
| { | |||
| const ComponentDeletionWatcher deletionWatcher (this); | |||
| isOn = shouldBeOn; | |||
| repaint(); | |||
| if (sendChangeNotification) | |||
| sendClickMessage (ModifierKeys()); | |||
| if ((! deletionWatcher.hasBeenDeleted()) && isOn) | |||
| turnOffOtherButtonsInGroup (sendChangeNotification); | |||
| } | |||
| } | |||
| void Button::setClickingTogglesState (const bool shouldToggle) throw() | |||
| { | |||
| clickTogglesState = shouldToggle; | |||
| // if you've got clickTogglesState turned on, you shouldn't also connect the button | |||
| // up to be a command invoker. Instead, your command handler must flip the state of whatever | |||
| // it is that this button represents, and the button will update its state to reflect this | |||
| // in the applicationCommandListChanged() method. | |||
| jassert (commandManagerToUse == 0 || ! clickTogglesState); | |||
| } | |||
| bool Button::getClickingTogglesState() const throw() | |||
| { | |||
| return clickTogglesState; | |||
| } | |||
| void Button::setRadioGroupId (const int newGroupId) | |||
| { | |||
| if (radioGroupId != newGroupId) | |||
| { | |||
| radioGroupId = newGroupId; | |||
| if (isOn) | |||
| turnOffOtherButtonsInGroup (true); | |||
| } | |||
| } | |||
| void Button::turnOffOtherButtonsInGroup (const bool sendChangeNotification) | |||
| { | |||
| Component* const p = getParentComponent(); | |||
| if (p != 0 && radioGroupId != 0) | |||
| { | |||
| const ComponentDeletionWatcher deletionWatcher (this); | |||
| for (int i = p->getNumChildComponents(); --i >= 0;) | |||
| { | |||
| Component* const c = p->getChildComponent (i); | |||
| if (c != this) | |||
| { | |||
| Button* const b = dynamic_cast <Button*> (c); | |||
| if (b != 0 && b->getRadioGroupId() == radioGroupId) | |||
| { | |||
| b->setToggleState (false, sendChangeNotification); | |||
| if (deletionWatcher.hasBeenDeleted()) | |||
| return; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| //============================================================================== | |||
| void Button::enablementChanged() | |||
| { | |||
| updateState (0); | |||
| repaint(); | |||
| } | |||
| Button::ButtonState Button::updateState (const MouseEvent* const e) throw() | |||
| { | |||
| ButtonState state = buttonNormal; | |||
| if (isEnabled() && isVisible() && ! isCurrentlyBlockedByAnotherModalComponent()) | |||
| { | |||
| int mx, my; | |||
| if (e == 0) | |||
| { | |||
| getMouseXYRelative (mx, my); | |||
| } | |||
| else | |||
| { | |||
| const MouseEvent e2 (e->getEventRelativeTo (this)); | |||
| mx = e2.x; | |||
| my = e2.y; | |||
| } | |||
| const bool over = reallyContains (mx, my, true); | |||
| const bool down = isMouseButtonDown(); | |||
| if ((down && (over || (triggerOnMouseDown && buttonState == buttonDown))) || isKeyDown) | |||
| state = buttonDown; | |||
| else if (over) | |||
| state = buttonOver; | |||
| } | |||
| setState (state); | |||
| return state; | |||
| } | |||
| void Button::setState (const ButtonState newState) | |||
| { | |||
| if (buttonState != newState) | |||
| { | |||
| buttonState = newState; | |||
| repaint(); | |||
| if (buttonState == buttonDown) | |||
| { | |||
| buttonPressTime = Time::getApproximateMillisecondCounter(); | |||
| lastTimeCallbackTime = buttonPressTime; | |||
| } | |||
| sendStateMessage(); | |||
| } | |||
| } | |||
| bool Button::isDown() const throw() | |||
| { | |||
| return buttonState == buttonDown; | |||
| } | |||
| bool Button::isOver() const throw() | |||
| { | |||
| return buttonState != buttonNormal; | |||
| } | |||
| void Button::buttonStateChanged() | |||
| { | |||
| } | |||
| uint32 Button::getMillisecondsSinceButtonDown() const throw() | |||
| { | |||
| const uint32 now = Time::getApproximateMillisecondCounter(); | |||
| return now > buttonPressTime ? now - buttonPressTime : 0; | |||
| } | |||
| void Button::setTriggeredOnMouseDown (const bool isTriggeredOnMouseDown) throw() | |||
| { | |||
| triggerOnMouseDown = isTriggeredOnMouseDown; | |||
| } | |||
| //============================================================================== | |||
| void Button::clicked() | |||
| { | |||
| } | |||
| void Button::clicked (const ModifierKeys& /*modifiers*/) | |||
| { | |||
| clicked(); | |||
| } | |||
| static const int clickMessageId = 0x2f3f4f99; | |||
| void Button::triggerClick() | |||
| { | |||
| postCommandMessage (clickMessageId); | |||
| } | |||
| void Button::internalClickCallback (const ModifierKeys& modifiers) | |||
| { | |||
| if (clickTogglesState) | |||
| setToggleState ((radioGroupId != 0) || ! isOn, false); | |||
| sendClickMessage (modifiers); | |||
| } | |||
| void Button::flashButtonState() throw() | |||
| { | |||
| if (isEnabled()) | |||
| { | |||
| needsToRelease = true; | |||
| setState (buttonDown); | |||
| getRepeatTimer().startTimer (100); | |||
| } | |||
| } | |||
| void Button::handleCommandMessage (int commandId) | |||
| { | |||
| if (commandId == clickMessageId) | |||
| { | |||
| if (isEnabled()) | |||
| { | |||
| flashButtonState(); | |||
| internalClickCallback (ModifierKeys::getCurrentModifiers()); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| Component::handleCommandMessage (commandId); | |||
| } | |||
| } | |||
| //============================================================================== | |||
| void Button::addButtonListener (ButtonListener* const newListener) throw() | |||
| { | |||
| jassert (newListener != 0); | |||
| jassert (! buttonListeners.contains (newListener)); // trying to add a listener to the list twice! | |||
| if (newListener != 0) | |||
| buttonListeners.add (newListener); | |||
| } | |||
| void Button::removeButtonListener (ButtonListener* const listener) throw() | |||
| { | |||
| jassert (buttonListeners.contains (listener)); // trying to remove a listener that isn't on the list! | |||
| buttonListeners.removeValue (listener); | |||
| } | |||
| void Button::sendClickMessage (const ModifierKeys& modifiers) | |||
| { | |||
| const ComponentDeletionWatcher cdw (this); | |||
| if (commandManagerToUse != 0 && commandID != 0) | |||
| { | |||
| ApplicationCommandTarget::InvocationInfo info (commandID); | |||
| info.invocationMethod = ApplicationCommandTarget::InvocationInfo::fromButton; | |||
| info.originatingComponent = this; | |||
| commandManagerToUse->invoke (info, true); | |||
| } | |||
| clicked (modifiers); | |||
| if (! cdw.hasBeenDeleted()) | |||
| { | |||
| for (int i = buttonListeners.size(); --i >= 0;) | |||
| { | |||
| ButtonListener* const bl = (ButtonListener*) buttonListeners[i]; | |||
| if (bl != 0) | |||
| { | |||
| bl->buttonClicked (this); | |||
| if (cdw.hasBeenDeleted()) | |||
| return; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| void Button::sendStateMessage() | |||
| { | |||
| const ComponentDeletionWatcher cdw (this); | |||
| buttonStateChanged(); | |||
| if (cdw.hasBeenDeleted()) | |||
| return; | |||
| for (int i = buttonListeners.size(); --i >= 0;) | |||
| { | |||
| ButtonListener* const bl = (ButtonListener*) buttonListeners[i]; | |||
| if (bl != 0) | |||
| { | |||
| bl->buttonStateChanged (this); | |||
| if (cdw.hasBeenDeleted()) | |||
| return; | |||
| } | |||
| } | |||
| } | |||
| //============================================================================== | |||
| void Button::paint (Graphics& g) | |||
| { | |||
| if (needsToRelease && isEnabled()) | |||
| { | |||
| needsToRelease = false; | |||
| needsRepainting = true; | |||
| } | |||
| paintButton (g, isOver(), isDown()); | |||
| } | |||
| //============================================================================== | |||
| void Button::mouseEnter (const MouseEvent& e) | |||
| { | |||
| updateState (&e); | |||
| } | |||
| void Button::mouseExit (const MouseEvent& e) | |||
| { | |||
| updateState (&e); | |||
| } | |||
| void Button::mouseDown (const MouseEvent& e) | |||
| { | |||
| updateState (&e); | |||
| if (isDown()) | |||
| { | |||
| if (autoRepeatDelay >= 0) | |||
| getRepeatTimer().startTimer (autoRepeatDelay); | |||
| if (triggerOnMouseDown) | |||
| internalClickCallback (e.mods); | |||
| } | |||
| } | |||
| void Button::mouseUp (const MouseEvent& e) | |||
| { | |||
| const bool wasDown = isDown(); | |||
| updateState (&e); | |||
| if (wasDown && isOver() && ! triggerOnMouseDown) | |||
| internalClickCallback (e.mods); | |||
| } | |||
| void Button::mouseDrag (const MouseEvent& e) | |||
| { | |||
| const ButtonState oldState = buttonState; | |||
| updateState (&e); | |||
| if (autoRepeatDelay >= 0 && buttonState != oldState && isDown()) | |||
| getRepeatTimer().startTimer (autoRepeatSpeed); | |||
| } | |||
| void Button::focusGained (FocusChangeType) | |||
| { | |||
| updateState (0); | |||
| repaint(); | |||
| } | |||
| void Button::focusLost (FocusChangeType) | |||
| { | |||
| updateState (0); | |||
| repaint(); | |||
| } | |||
| //============================================================================== | |||
| void Button::setVisible (bool shouldBeVisible) | |||
| { | |||
| if (shouldBeVisible != isVisible()) | |||
| { | |||
| Component::setVisible (shouldBeVisible); | |||
| if (! shouldBeVisible) | |||
| needsToRelease = false; | |||
| updateState (0); | |||
| } | |||
| else | |||
| { | |||
| Component::setVisible (shouldBeVisible); | |||
| } | |||
| } | |||
| void Button::parentHierarchyChanged() | |||
| { | |||
| Component* const newKeySource = (shortcuts.size() == 0) ? 0 : getTopLevelComponent(); | |||
| if (newKeySource != keySource) | |||
| { | |||
| if (keySource->isValidComponent()) | |||
| keySource->removeKeyListener (this); | |||
| keySource = newKeySource; | |||
| if (keySource->isValidComponent()) | |||
| keySource->addKeyListener (this); | |||
| } | |||
| } | |||
| //============================================================================== | |||
| void Button::setCommandToTrigger (ApplicationCommandManager* const commandManagerToUse_, | |||
| const int commandID_, | |||
| const bool generateTooltip_) | |||
| { | |||
| commandID = commandID_; | |||
| generateTooltip = generateTooltip_; | |||
| if (commandManagerToUse != commandManagerToUse_) | |||
| { | |||
| if (commandManagerToUse != 0) | |||
| commandManagerToUse->removeListener (this); | |||
| commandManagerToUse = commandManagerToUse_; | |||
| if (commandManagerToUse != 0) | |||
| commandManagerToUse->addListener (this); | |||
| // if you've got clickTogglesState turned on, you shouldn't also connect the button | |||
| // up to be a command invoker. Instead, your command handler must flip the state of whatever | |||
| // it is that this button represents, and the button will update its state to reflect this | |||
| // in the applicationCommandListChanged() method. | |||
| jassert (commandManagerToUse == 0 || ! clickTogglesState); | |||
| } | |||
| if (commandManagerToUse != 0) | |||
| applicationCommandListChanged(); | |||
| else | |||
| setEnabled (true); | |||
| } | |||
| void Button::applicationCommandInvoked (const ApplicationCommandTarget::InvocationInfo& info) | |||
| { | |||
| if (info.commandID == commandID | |||
| && (info.commandFlags & ApplicationCommandInfo::dontTriggerVisualFeedback) == 0) | |||
| { | |||
| flashButtonState(); | |||
| } | |||
| } | |||
| void Button::applicationCommandListChanged() | |||
| { | |||
| if (commandManagerToUse != 0) | |||
| { | |||
| ApplicationCommandInfo info (0); | |||
| ApplicationCommandTarget* const target = commandManagerToUse->getTargetForCommand (commandID, info); | |||
| setEnabled (target != 0 && (info.flags & ApplicationCommandInfo::isDisabled) == 0); | |||
| if (target != 0) | |||
| setToggleState ((info.flags & ApplicationCommandInfo::isTicked) != 0, false); | |||
| } | |||
| } | |||
| //============================================================================== | |||
| void Button::addShortcut (const KeyPress& key) | |||
| { | |||
| if (key.isValid()) | |||
| { | |||
| jassert (! isRegisteredForShortcut (key)); // already registered! | |||
| shortcuts.add (key); | |||
| parentHierarchyChanged(); | |||
| } | |||
| } | |||
| void Button::clearShortcuts() | |||
| { | |||
| shortcuts.clear(); | |||
| parentHierarchyChanged(); | |||
| } | |||
| bool Button::isShortcutPressed() const throw() | |||
| { | |||
| if (! isCurrentlyBlockedByAnotherModalComponent()) | |||
| { | |||
| for (int i = shortcuts.size(); --i >= 0;) | |||
| if (shortcuts.getReference(i).isCurrentlyDown()) | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| bool Button::isRegisteredForShortcut (const KeyPress& key) const throw() | |||
| { | |||
| for (int i = shortcuts.size(); --i >= 0;) | |||
| if (key == shortcuts.getReference(i)) | |||
| return true; | |||
| return false; | |||
| } | |||
| bool Button::keyStateChanged (const bool, Component*) | |||
| { | |||
| if (! isEnabled()) | |||
| return false; | |||
| const bool wasDown = isKeyDown; | |||
| isKeyDown = isShortcutPressed(); | |||
| if (autoRepeatDelay >= 0 && (isKeyDown && ! wasDown)) | |||
| getRepeatTimer().startTimer (autoRepeatDelay); | |||
| updateState (0); | |||
| if (isEnabled() && wasDown && ! isKeyDown) | |||
| { | |||
| internalClickCallback (ModifierKeys::getCurrentModifiers()); | |||
| // (return immediately - this button may now have been deleted) | |||
| return true; | |||
| } | |||
| return wasDown || isKeyDown; | |||
| } | |||
| bool Button::keyPressed (const KeyPress&, Component*) | |||
| { | |||
| // returning true will avoid forwarding events for keys that we're using as shortcuts | |||
| return isShortcutPressed(); | |||
| } | |||
| bool Button::keyPressed (const KeyPress& key) | |||
| { | |||
| if (isEnabled() && key.isKeyCode (KeyPress::returnKey)) | |||
| { | |||
| triggerClick(); | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| //============================================================================== | |||
| void Button::setRepeatSpeed (const int initialDelayMillisecs, | |||
| const int repeatMillisecs, | |||
| const int minimumDelayInMillisecs) throw() | |||
| { | |||
| autoRepeatDelay = initialDelayMillisecs; | |||
| autoRepeatSpeed = repeatMillisecs; | |||
| autoRepeatMinimumDelay = jmin (autoRepeatSpeed, minimumDelayInMillisecs); | |||
| } | |||
| void Button::repeatTimerCallback() throw() | |||
| { | |||
| if (needsRepainting) | |||
| { | |||
| getRepeatTimer().stopTimer(); | |||
| updateState (0); | |||
| needsRepainting = false; | |||
| } | |||
| else if (autoRepeatSpeed > 0 && (isKeyDown || (updateState (0) == buttonDown))) | |||
| { | |||
| int repeatSpeed = autoRepeatSpeed; | |||
| if (autoRepeatMinimumDelay >= 0) | |||
| { | |||
| double timeHeldDown = jmin (1.0, getMillisecondsSinceButtonDown() / 4000.0); | |||
| timeHeldDown *= timeHeldDown; | |||
| repeatSpeed = repeatSpeed + (int) (timeHeldDown * (autoRepeatMinimumDelay - repeatSpeed)); | |||
| } | |||
| repeatSpeed = jmax (1, repeatSpeed); | |||
| getRepeatTimer().startTimer (repeatSpeed); | |||
| const uint32 now = Time::getApproximateMillisecondCounter(); | |||
| const int numTimesToCallback | |||
| = (now > lastTimeCallbackTime) ? jmax (1, (now - lastTimeCallbackTime) / repeatSpeed) : 1; | |||
| lastTimeCallbackTime = now; | |||
| const ComponentDeletionWatcher cdw (this); | |||
| for (int i = numTimesToCallback; --i >= 0;) | |||
| { | |||
| internalClickCallback (ModifierKeys::getCurrentModifiers()); | |||
| if (cdw.hasBeenDeleted() || ! isDown()) | |||
| return; | |||
| } | |||
| } | |||
| else if (! needsToRelease) | |||
| { | |||
| getRepeatTimer().stopTimer(); | |||
| } | |||
| } | |||
| class InternalButtonRepeatTimer : public Timer | |||
| { | |||
| public: | |||
| InternalButtonRepeatTimer (Button& owner_) throw() | |||
| : owner (owner_) | |||
| { | |||
| } | |||
| ~InternalButtonRepeatTimer() | |||
| { | |||
| } | |||
| void timerCallback() | |||
| { | |||
| owner.repeatTimerCallback(); | |||
| } | |||
| private: | |||
| Button& owner; | |||
| InternalButtonRepeatTimer (const InternalButtonRepeatTimer&); | |||
| const InternalButtonRepeatTimer& operator= (const InternalButtonRepeatTimer&); | |||
| }; | |||
| Timer& Button::getRepeatTimer() throw() | |||
| { | |||
| if (repeatTimer == 0) | |||
| repeatTimer = new InternalButtonRepeatTimer (*this); | |||
| return *repeatTimer; | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,500 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_BUTTON_JUCEHEADER__ | |||
| #define __JUCE_BUTTON_JUCEHEADER__ | |||
| #include "../juce_Component.h" | |||
| #include "../keyboard/juce_KeyListener.h" | |||
| #include "../../../application/juce_ApplicationCommandManager.h" | |||
| #include "../../../containers/juce_SortedSet.h" | |||
| #include "../windows/juce_TooltipWindow.h" | |||
| #include "../../../events/juce_Timer.h" | |||
| class Button; | |||
| //============================================================================== | |||
| /** | |||
| Used to receive callbacks when a button is clicked. | |||
| @see Button::addButtonListener, Button::removeButtonListener | |||
| */ | |||
| class JUCE_API ButtonListener | |||
| { | |||
| public: | |||
| /** Destructor. */ | |||
| virtual ~ButtonListener() {} | |||
| /** Called when the button is clicked. */ | |||
| virtual void buttonClicked (Button* button) = 0; | |||
| /** Called when the button's state changes. */ | |||
| virtual void buttonStateChanged (Button*) {} | |||
| }; | |||
| //============================================================================== | |||
| /** | |||
| A base class for buttons. | |||
| This contains all the logic for button behaviours such as enabling/disabling, | |||
| responding to shortcut keystrokes, auto-repeating when held down, toggle-buttons | |||
| and radio groups, etc. | |||
| @see TextButton, DrawableButton, ToggleButton | |||
| */ | |||
| class JUCE_API Button : public Component, | |||
| public SettableTooltipClient, | |||
| public ApplicationCommandManagerListener, | |||
| private KeyListener | |||
| { | |||
| protected: | |||
| //============================================================================== | |||
| /** Creates a button. | |||
| @param buttonName the text to put in the button (the component's name is also | |||
| initially set to this string, but these can be changed later | |||
| using the setName() and setButtonText() methods) | |||
| */ | |||
| Button (const String& buttonName); | |||
| public: | |||
| /** Destructor. */ | |||
| virtual ~Button(); | |||
| //============================================================================== | |||
| /** Changes the button's text. | |||
| @see getButtonText | |||
| */ | |||
| void setButtonText (const String& newText) throw(); | |||
| /** Returns the text displayed in the button. | |||
| @see setButtonText | |||
| */ | |||
| const String getButtonText() const throw() { return text; } | |||
| //============================================================================== | |||
| /** Returns true if the button is currently being held down by the mouse. | |||
| @see isOver | |||
| */ | |||
| bool isDown() const throw(); | |||
| /** Returns true if the mouse is currently over the button. | |||
| This will be also be true if the mouse is being held down. | |||
| @see isDown | |||
| */ | |||
| bool isOver() const throw(); | |||
| //============================================================================== | |||
| /** A button has an on/off state associated with it, and this changes that. | |||
| By default buttons are 'off' and for simple buttons that you click to perform | |||
| an action you won't change this. Toggle buttons, however will want to | |||
| change their state when turned on or off. | |||
| @param shouldBeOn whether to set the button's toggle state to be on or | |||
| off. If it's a member of a button group, this will | |||
| always try to turn it on, and to turn off any other | |||
| buttons in the group | |||
| @param sendChangeNotification if true, a callback will be made to clicked(); if false | |||
| the button will be repainted but no notification will | |||
| be sent | |||
| @see getToggleState, setRadioGroupId | |||
| */ | |||
| void setToggleState (const bool shouldBeOn, | |||
| const bool sendChangeNotification); | |||
| /** Returns true if the button in 'on'. | |||
| By default buttons are 'off' and for simple buttons that you click to perform | |||
| an action you won't change this. Toggle buttons, however will want to | |||
| change their state when turned on or off. | |||
| @see setToggleState | |||
| */ | |||
| bool getToggleState() const throw() { return isOn; } | |||
| /** This tells the button to automatically flip the toggle state when | |||
| the button is clicked. | |||
| If set to true, then before the clicked() callback occurs, the toggle-state | |||
| of the button is flipped. | |||
| */ | |||
| void setClickingTogglesState (const bool shouldToggle) throw(); | |||
| /** Returns true if this button is set to be an automatic toggle-button. | |||
| This returns the last value that was passed to setClickingTogglesState(). | |||
| */ | |||
| bool getClickingTogglesState() const throw(); | |||
| //============================================================================== | |||
| /** Enables the button to act as a member of a mutually-exclusive group | |||
| of 'radio buttons'. | |||
| If the group ID is set to a non-zero number, then this button will | |||
| act as part of a group of buttons with the same ID, only one of | |||
| which can be 'on' at the same time. Note that when it's part of | |||
| a group, clicking a toggle-button that's 'on' won't turn it off. | |||
| To find other buttons with the same ID, this button will search through | |||
| its sibling components for ToggleButtons, so all the buttons for a | |||
| particular group must be placed inside the same parent component. | |||
| Set the group ID back to zero if you want it to act as a normal toggle | |||
| button again. | |||
| @see getRadioGroupId | |||
| */ | |||
| void setRadioGroupId (const int newGroupId); | |||
| /** Returns the ID of the group to which this button belongs. | |||
| (See setRadioGroupId() for an explanation of this). | |||
| */ | |||
| int getRadioGroupId() const throw() { return radioGroupId; } | |||
| //============================================================================== | |||
| /** Registers a listener to receive events when this button's state changes. | |||
| If the listener is already registered, this will not register it again. | |||
| @see removeButtonListener | |||
| */ | |||
| void addButtonListener (ButtonListener* const newListener) throw(); | |||
| /** Removes a previously-registered button listener | |||
| @see addButtonListener | |||
| */ | |||
| void removeButtonListener (ButtonListener* const listener) throw(); | |||
| //============================================================================== | |||
| /** Causes the button to act as if it's been clicked. | |||
| This will asynchronously make the button draw itself going down and up, and | |||
| will then call back the clicked() method as if mouse was clicked on it. | |||
| @see clicked | |||
| */ | |||
| virtual void triggerClick(); | |||
| //============================================================================== | |||
| /** Sets a command ID for this button to automatically invoke when it's clicked. | |||
| When the button is pressed, it will use the given manager to trigger the | |||
| command ID. | |||
| Obviously be careful that the ApplicationCommandManager doesn't get deleted | |||
| before this button is. To disable the command triggering, call this method and | |||
| pass 0 for the parameters. | |||
| If generateTooltip is true, then the button's tooltip will be automatically | |||
| generated based on the name of this command and its current shortcut key. | |||
| @see addShortcut, getCommandID | |||
| */ | |||
| void setCommandToTrigger (ApplicationCommandManager* commandManagerToUse, | |||
| const int commandID, | |||
| const bool generateTooltip); | |||
| /** Returns the command ID that was set by setCommandToTrigger(). | |||
| */ | |||
| int getCommandID() const throw() { return commandID; } | |||
| //============================================================================== | |||
| /** Assigns a shortcut key to trigger the button. | |||
| The button registers itself with its top-level parent component for keypresses. | |||
| Note that a different way of linking buttons to keypresses is by using the | |||
| setKeyPressToTrigger() method to invoke a command - the difference being that | |||
| setting a shortcut allows the button to be temporarily linked to a keypress | |||
| only while it's on-screen. | |||
| @see clearShortcuts | |||
| */ | |||
| void addShortcut (const KeyPress& key); | |||
| /** Removes all key shortcuts that had been set for this button. | |||
| @see addShortcut | |||
| */ | |||
| void clearShortcuts(); | |||
| /** Returns true if the given keypress is a shortcut for this button. | |||
| @see addShortcut | |||
| */ | |||
| bool isRegisteredForShortcut (const KeyPress& key) const throw(); | |||
| //============================================================================== | |||
| /** Sets an auto-repeat speed for the button when it is held down. | |||
| (Auto-repeat is disabled by default). | |||
| @param initialDelayInMillisecs how long to wait after the mouse is pressed before | |||
| triggering the next click. If this is zero, auto-repeat | |||
| is disabled | |||
| @param repeatDelayInMillisecs the frequently subsequent repeated clicks should be | |||
| triggered | |||
| @param minimumDelayInMillisecs if this is greater than 0, the auto-repeat speed will | |||
| get faster, the longer the button is held down, up to the | |||
| minimum interval specified here | |||
| */ | |||
| void setRepeatSpeed (const int initialDelayInMillisecs, | |||
| const int repeatDelayInMillisecs, | |||
| const int minimumDelayInMillisecs = -1) throw(); | |||
| /** Sets whether the button click should happen when the mouse is pressed or released. | |||
| By default the button is only considered to have been clicked when the mouse is | |||
| released, but setting this to true will make it call the clicked() method as soon | |||
| as the button is pressed. | |||
| This is useful if the button is being used to show a pop-up menu, as it allows | |||
| the click to be used as a drag onto the menu. | |||
| */ | |||
| void setTriggeredOnMouseDown (const bool isTriggeredOnMouseDown) throw(); | |||
| /** Returns the number of milliseconds since the last time the button | |||
| went into the 'down' state. | |||
| */ | |||
| uint32 getMillisecondsSinceButtonDown() const throw(); | |||
| //============================================================================== | |||
| /** (overridden from Component to do special stuff). */ | |||
| void setVisible (bool shouldBeVisible); | |||
| //============================================================================== | |||
| /** Sets the tooltip for this button. | |||
| @see TooltipClient, TooltipWindow | |||
| */ | |||
| void setTooltip (const String& newTooltip); | |||
| // (implementation of the TooltipClient method) | |||
| const String getTooltip(); | |||
| //============================================================================== | |||
| /** A combination of these flags are used by setConnectedEdges(). | |||
| */ | |||
| enum ConnectedEdgeFlags | |||
| { | |||
| ConnectedOnLeft = 1, | |||
| ConnectedOnRight = 2, | |||
| ConnectedOnTop = 4, | |||
| ConnectedOnBottom = 8 | |||
| }; | |||
| /** Hints about which edges of the button might be connected to adjoining buttons. | |||
| The value passed in is a bitwise combination of any of the values in the | |||
| ConnectedEdgeFlags enum. | |||
| E.g. if you are placing two buttons adjacent to each other, you could use this to | |||
| indicate which edges are touching, and the LookAndFeel might choose to drawn them | |||
| without rounded corners on the edges that connect. It's only a hint, so the | |||
| LookAndFeel can choose to ignore it if it's not relevent for this type of | |||
| button. | |||
| */ | |||
| void setConnectedEdges (const int connectedEdgeFlags) throw(); | |||
| /** Returns the set of flags passed into setConnectedEdges(). */ | |||
| int getConnectedEdgeFlags() const throw() { return connectedEdgeFlags; } | |||
| /** Indicates whether the button adjoins another one on its left edge. | |||
| @see setConnectedEdges | |||
| */ | |||
| bool isConnectedOnLeft() const throw() { return (connectedEdgeFlags & ConnectedOnLeft) != 0; } | |||
| /** Indicates whether the button adjoins another one on its right edge. | |||
| @see setConnectedEdges | |||
| */ | |||
| bool isConnectedOnRight() const throw() { return (connectedEdgeFlags & ConnectedOnRight) != 0; } | |||
| /** Indicates whether the button adjoins another one on its top edge. | |||
| @see setConnectedEdges | |||
| */ | |||
| bool isConnectedOnTop() const throw() { return (connectedEdgeFlags & ConnectedOnTop) != 0; } | |||
| /** Indicates whether the button adjoins another one on its bottom edge. | |||
| @see setConnectedEdges | |||
| */ | |||
| bool isConnectedOnBottom() const throw() { return (connectedEdgeFlags & ConnectedOnBottom) != 0; } | |||
| //============================================================================== | |||
| /** Used by setState(). */ | |||
| enum ButtonState | |||
| { | |||
| buttonNormal, | |||
| buttonOver, | |||
| buttonDown | |||
| }; | |||
| /** Can be used to force the button into a particular state. | |||
| This only changes the button's appearance, it won't trigger a click, or stop any mouse-clicks | |||
| from happening. | |||
| The state that you set here will only last until it is automatically changed when the mouse | |||
| enters or exits the button, or the mouse-button is pressed or released. | |||
| */ | |||
| void setState (const ButtonState newState); | |||
| //============================================================================== | |||
| juce_UseDebuggingNewOperator | |||
| protected: | |||
| //============================================================================== | |||
| /** This method is called when the button has been clicked. | |||
| Subclasses can override this to perform whatever they actions they need | |||
| to do. | |||
| Alternatively, a ButtonListener can be added to the button, and these listeners | |||
| will be called when the click occurs. | |||
| @see triggerClick | |||
| */ | |||
| virtual void clicked(); | |||
| /** This method is called when the button has been clicked. | |||
| By default it just calls clicked(), but you might want to override it to handle | |||
| things like clicking when a modifier key is pressed, etc. | |||
| @see ModifierKeys | |||
| */ | |||
| virtual void clicked (const ModifierKeys& modifiers); | |||
| /** Subclasses should override this to actually paint the button's contents. | |||
| It's better to use this than the paint method, because it gives you information | |||
| about the over/down state of the button. | |||
| @param g the graphics context to use | |||
| @param isMouseOverButton true if the button is either in the 'over' or | |||
| 'down' state | |||
| @param isButtonDown true if the button should be drawn in the 'down' position | |||
| */ | |||
| virtual void paintButton (Graphics& g, | |||
| bool isMouseOverButton, | |||
| bool isButtonDown) = 0; | |||
| /** Called when the button's up/down/over state changes. | |||
| Subclasses can override this if they need to do something special when the button | |||
| goes up or down. | |||
| @see isDown, isOver | |||
| */ | |||
| virtual void buttonStateChanged(); | |||
| //============================================================================== | |||
| /** @internal */ | |||
| virtual void internalClickCallback (const ModifierKeys& modifiers); | |||
| /** @internal */ | |||
| void handleCommandMessage (int commandId); | |||
| /** @internal */ | |||
| void mouseEnter (const MouseEvent& e); | |||
| /** @internal */ | |||
| void mouseExit (const MouseEvent& e); | |||
| /** @internal */ | |||
| void mouseDown (const MouseEvent& e); | |||
| /** @internal */ | |||
| void mouseDrag (const MouseEvent& e); | |||
| /** @internal */ | |||
| void mouseUp (const MouseEvent& e); | |||
| /** @internal */ | |||
| bool keyPressed (const KeyPress& key); | |||
| /** @internal */ | |||
| bool keyPressed (const KeyPress& key, Component* originatingComponent); | |||
| /** @internal */ | |||
| bool keyStateChanged (const bool isKeyDown, Component* originatingComponent); | |||
| /** @internal */ | |||
| void paint (Graphics& g); | |||
| /** @internal */ | |||
| void parentHierarchyChanged(); | |||
| /** @internal */ | |||
| void focusGained (FocusChangeType cause); | |||
| /** @internal */ | |||
| void focusLost (FocusChangeType cause); | |||
| /** @internal */ | |||
| void enablementChanged(); | |||
| /** @internal */ | |||
| void applicationCommandInvoked (const ApplicationCommandTarget::InvocationInfo&); | |||
| /** @internal */ | |||
| void applicationCommandListChanged(); | |||
| private: | |||
| //============================================================================== | |||
| Array <KeyPress> shortcuts; | |||
| Component* keySource; | |||
| String text; | |||
| SortedSet <void*> buttonListeners; | |||
| friend class InternalButtonRepeatTimer; | |||
| Timer* repeatTimer; | |||
| uint32 buttonPressTime, lastTimeCallbackTime; | |||
| ApplicationCommandManager* commandManagerToUse; | |||
| int autoRepeatDelay, autoRepeatSpeed, autoRepeatMinimumDelay; | |||
| int radioGroupId, commandID, connectedEdgeFlags; | |||
| ButtonState buttonState; | |||
| bool isOn : 1; | |||
| bool clickTogglesState : 1; | |||
| bool needsToRelease : 1; | |||
| bool needsRepainting : 1; | |||
| bool isKeyDown : 1; | |||
| bool triggerOnMouseDown : 1; | |||
| bool generateTooltip : 1; | |||
| void repeatTimerCallback() throw(); | |||
| Timer& getRepeatTimer() throw(); | |||
| ButtonState updateState (const MouseEvent* const e) throw(); | |||
| bool isShortcutPressed() const throw(); | |||
| void turnOffOtherButtonsInGroup (const bool sendChangeNotification); | |||
| void flashButtonState() throw(); | |||
| void sendClickMessage (const ModifierKeys& modifiers); | |||
| void sendStateMessage(); | |||
| Button (const Button&); | |||
| const Button& operator= (const Button&); | |||
| }; | |||
| #endif // __JUCE_BUTTON_JUCEHEADER__ | |||
| @@ -0,0 +1,307 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_DrawableButton.h" | |||
| #include "../lookandfeel/juce_LookAndFeel.h" | |||
| //============================================================================== | |||
| DrawableButton::DrawableButton (const String& name, | |||
| const DrawableButton::ButtonStyle buttonStyle) | |||
| : Button (name), | |||
| style (buttonStyle), | |||
| normalImage (0), | |||
| overImage (0), | |||
| downImage (0), | |||
| disabledImage (0), | |||
| normalImageOn (0), | |||
| overImageOn (0), | |||
| downImageOn (0), | |||
| disabledImageOn (0), | |||
| edgeIndent (3) | |||
| { | |||
| if (buttonStyle == ImageOnButtonBackground) | |||
| { | |||
| backgroundOff = Colour (0xffbbbbff); | |||
| backgroundOn = Colour (0xff3333ff); | |||
| } | |||
| else | |||
| { | |||
| backgroundOff = Colours::transparentBlack; | |||
| backgroundOn = Colour (0xaabbbbff); | |||
| } | |||
| } | |||
| DrawableButton::~DrawableButton() | |||
| { | |||
| deleteImages(); | |||
| } | |||
| //============================================================================== | |||
| void DrawableButton::deleteImages() | |||
| { | |||
| deleteAndZero (normalImage); | |||
| deleteAndZero (overImage); | |||
| deleteAndZero (downImage); | |||
| deleteAndZero (disabledImage); | |||
| deleteAndZero (normalImageOn); | |||
| deleteAndZero (overImageOn); | |||
| deleteAndZero (downImageOn); | |||
| deleteAndZero (disabledImageOn); | |||
| } | |||
| void DrawableButton::setImages (const Drawable* normal, | |||
| const Drawable* over, | |||
| const Drawable* down, | |||
| const Drawable* disabled, | |||
| const Drawable* normalOn, | |||
| const Drawable* overOn, | |||
| const Drawable* downOn, | |||
| const Drawable* disabledOn) | |||
| { | |||
| deleteImages(); | |||
| jassert (normal != 0); // you really need to give it at least a normal image.. | |||
| if (normal != 0) | |||
| normalImage = normal->createCopy(); | |||
| if (over != 0) | |||
| overImage = over->createCopy(); | |||
| if (down != 0) | |||
| downImage = down->createCopy(); | |||
| if (disabled != 0) | |||
| disabledImage = disabled->createCopy(); | |||
| if (normalOn != 0) | |||
| normalImageOn = normalOn->createCopy(); | |||
| if (overOn != 0) | |||
| overImageOn = overOn->createCopy(); | |||
| if (downOn != 0) | |||
| downImageOn = downOn->createCopy(); | |||
| if (disabledOn != 0) | |||
| disabledImageOn = disabledOn->createCopy(); | |||
| repaint(); | |||
| } | |||
| //============================================================================== | |||
| void DrawableButton::setButtonStyle (const DrawableButton::ButtonStyle newStyle) | |||
| { | |||
| if (style != newStyle) | |||
| { | |||
| style = newStyle; | |||
| repaint(); | |||
| } | |||
| } | |||
| void DrawableButton::setBackgroundColours (const Colour& toggledOffColour, | |||
| const Colour& toggledOnColour) | |||
| { | |||
| if (backgroundOff != toggledOffColour | |||
| || backgroundOn != toggledOnColour) | |||
| { | |||
| backgroundOff = toggledOffColour; | |||
| backgroundOn = toggledOnColour; | |||
| repaint(); | |||
| } | |||
| } | |||
| const Colour& DrawableButton::getBackgroundColour() const throw() | |||
| { | |||
| return getToggleState() ? backgroundOn | |||
| : backgroundOff; | |||
| } | |||
| void DrawableButton::setEdgeIndent (const int numPixelsIndent) | |||
| { | |||
| edgeIndent = numPixelsIndent; | |||
| repaint(); | |||
| } | |||
| void DrawableButton::paintButton (Graphics& g, | |||
| bool isMouseOverButton, | |||
| bool isButtonDown) | |||
| { | |||
| Rectangle imageSpace; | |||
| if (style == ImageOnButtonBackground) | |||
| { | |||
| const int insetX = getWidth() / 4; | |||
| const int insetY = getHeight() / 4; | |||
| imageSpace.setBounds (insetX, insetY, getWidth() - insetX * 2, getHeight() - insetY * 2); | |||
| getLookAndFeel().drawButtonBackground (g, *this, | |||
| getBackgroundColour(), | |||
| isMouseOverButton, | |||
| isButtonDown); | |||
| } | |||
| else | |||
| { | |||
| g.fillAll (getBackgroundColour()); | |||
| const int textH = (style == ImageAboveTextLabel) | |||
| ? jmin (16, proportionOfHeight (0.25f)) | |||
| : 0; | |||
| const int indentX = jmin (edgeIndent, proportionOfWidth (0.3f)); | |||
| const int indentY = jmin (edgeIndent, proportionOfHeight (0.3f)); | |||
| imageSpace.setBounds (indentX, indentY, | |||
| getWidth() - indentX * 2, | |||
| getHeight() - indentY * 2 - textH); | |||
| if (textH > 0) | |||
| { | |||
| g.setFont ((float) textH); | |||
| g.setColour (Colours::black.withAlpha (isEnabled() ? 1.0f : 0.4f)); | |||
| g.drawFittedText (getButtonText(), | |||
| 2, getHeight() - textH - 1, | |||
| getWidth() - 4, textH, | |||
| Justification::centred, 1); | |||
| } | |||
| } | |||
| g.setImageResamplingQuality (Graphics::mediumResamplingQuality); | |||
| g.setOpacity (1.0f); | |||
| const Drawable* imageToDraw = 0; | |||
| if (isEnabled()) | |||
| { | |||
| imageToDraw = getCurrentImage(); | |||
| } | |||
| else | |||
| { | |||
| imageToDraw = getToggleState() ? disabledImageOn | |||
| : disabledImage; | |||
| if (imageToDraw == 0) | |||
| { | |||
| g.setOpacity (0.4f); | |||
| imageToDraw = getNormalImage(); | |||
| } | |||
| } | |||
| if (imageToDraw != 0) | |||
| { | |||
| if (style == ImageRaw) | |||
| { | |||
| imageToDraw->draw (g); | |||
| } | |||
| else | |||
| { | |||
| imageToDraw->drawWithin (g, | |||
| imageSpace.getX(), | |||
| imageSpace.getY(), | |||
| imageSpace.getWidth(), | |||
| imageSpace.getHeight(), | |||
| RectanglePlacement::centred); | |||
| } | |||
| } | |||
| } | |||
| //============================================================================== | |||
| const Drawable* DrawableButton::getCurrentImage() const throw() | |||
| { | |||
| if (isDown()) | |||
| return getDownImage(); | |||
| if (isOver()) | |||
| return getOverImage(); | |||
| return getNormalImage(); | |||
| } | |||
| const Drawable* DrawableButton::getNormalImage() const throw() | |||
| { | |||
| return (getToggleState() && normalImageOn != 0) ? normalImageOn | |||
| : normalImage; | |||
| } | |||
| const Drawable* DrawableButton::getOverImage() const throw() | |||
| { | |||
| const Drawable* d = normalImage; | |||
| if (getToggleState()) | |||
| { | |||
| if (overImageOn != 0) | |||
| d = overImageOn; | |||
| else if (normalImageOn != 0) | |||
| d = normalImageOn; | |||
| else if (overImage != 0) | |||
| d = overImage; | |||
| } | |||
| else | |||
| { | |||
| if (overImage != 0) | |||
| d = overImage; | |||
| } | |||
| return d; | |||
| } | |||
| const Drawable* DrawableButton::getDownImage() const throw() | |||
| { | |||
| const Drawable* d = normalImage; | |||
| if (getToggleState()) | |||
| { | |||
| if (downImageOn != 0) | |||
| d = downImageOn; | |||
| else if (overImageOn != 0) | |||
| d = overImageOn; | |||
| else if (normalImageOn != 0) | |||
| d = normalImageOn; | |||
| else if (downImage != 0) | |||
| d = downImage; | |||
| else | |||
| d = getOverImage(); | |||
| } | |||
| else | |||
| { | |||
| if (downImage != 0) | |||
| d = downImage; | |||
| else | |||
| d = getOverImage(); | |||
| } | |||
| return d; | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,115 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_HyperlinkButton.h" | |||
| //============================================================================== | |||
| HyperlinkButton::HyperlinkButton (const String& linkText, | |||
| const URL& linkURL) | |||
| : Button (linkText), | |||
| url (linkURL), | |||
| font (14.0f, Font::underlined), | |||
| resizeFont (true), | |||
| justification (Justification::centred) | |||
| { | |||
| setMouseCursor (MouseCursor::PointingHandCursor); | |||
| setTooltip (linkURL.toString (false)); | |||
| } | |||
| HyperlinkButton::~HyperlinkButton() | |||
| { | |||
| } | |||
| //============================================================================== | |||
| void HyperlinkButton::setFont (const Font& newFont, | |||
| const bool resizeToMatchComponentHeight, | |||
| const Justification& justificationType) | |||
| { | |||
| font = newFont; | |||
| resizeFont = resizeToMatchComponentHeight; | |||
| justification = justificationType; | |||
| repaint(); | |||
| } | |||
| void HyperlinkButton::setURL (const URL& newURL) throw() | |||
| { | |||
| url = newURL; | |||
| setTooltip (newURL.toString (false)); | |||
| } | |||
| const Font HyperlinkButton::getFontToUse() const | |||
| { | |||
| Font f (font); | |||
| if (resizeFont) | |||
| f.setHeight (getHeight() * 0.7f); | |||
| return f; | |||
| } | |||
| void HyperlinkButton::changeWidthToFitText() | |||
| { | |||
| setSize (getFontToUse().getStringWidth (getName()) + 6, getHeight()); | |||
| } | |||
| void HyperlinkButton::colourChanged() | |||
| { | |||
| repaint(); | |||
| } | |||
| //============================================================================== | |||
| void HyperlinkButton::clicked() | |||
| { | |||
| if (url.isWellFormed()) | |||
| url.launchInDefaultBrowser(); | |||
| } | |||
| void HyperlinkButton::paintButton (Graphics& g, | |||
| bool isMouseOverButton, | |||
| bool isButtonDown) | |||
| { | |||
| const Colour textColour (findColour (textColourId)); | |||
| if (isEnabled()) | |||
| g.setColour ((isMouseOverButton) ? textColour.darker ((isButtonDown) ? 1.3f : 0.4f) | |||
| : textColour); | |||
| else | |||
| g.setColour (textColour.withMultipliedAlpha (0.4f)); | |||
| g.setFont (getFontToUse()); | |||
| g.drawText (getButtonText(), | |||
| 2, 0, getWidth() - 2, getHeight(), | |||
| justification.getOnlyHorizontalFlags() | Justification::verticallyCentred, | |||
| true); | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,120 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_HYPERLINKBUTTON_JUCEHEADER__ | |||
| #define __JUCE_HYPERLINKBUTTON_JUCEHEADER__ | |||
| #include "juce_Button.h" | |||
| #include "../../../io/network/juce_URL.h" | |||
| //============================================================================== | |||
| /** | |||
| A button showing an underlined weblink, that will launch the link | |||
| when it's clicked. | |||
| @see Button | |||
| */ | |||
| class JUCE_API HyperlinkButton : public Button | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Creates a HyperlinkButton. | |||
| @param linkText the text that will be displayed in the button - this is | |||
| also set as the Component's name, but the text can be | |||
| changed later with the Button::getButtonText() method | |||
| @param linkURL the URL to launch when the user clicks the button | |||
| */ | |||
| HyperlinkButton (const String& linkText, | |||
| const URL& linkURL); | |||
| /** Destructor. */ | |||
| ~HyperlinkButton(); | |||
| //============================================================================== | |||
| /** Changes the font to use for the text. | |||
| If resizeToMatchComponentHeight is true, the font's height will be adjusted | |||
| to match the size of the component. | |||
| */ | |||
| void setFont (const Font& newFont, | |||
| const bool resizeToMatchComponentHeight, | |||
| const Justification& justificationType = Justification::horizontallyCentred); | |||
| //============================================================================== | |||
| /** A set of colour IDs to use to change the colour of various aspects of the link. | |||
| These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() | |||
| methods. | |||
| @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour | |||
| */ | |||
| enum ColourIds | |||
| { | |||
| textColourId = 0x1001f00, /**< The colour to use for the URL text. */ | |||
| }; | |||
| //============================================================================== | |||
| /** Changes the URL that the button will trigger. */ | |||
| void setURL (const URL& newURL) throw(); | |||
| /** Returns the URL that the button will trigger. */ | |||
| const URL& getURL() const throw() { return url; } | |||
| //============================================================================== | |||
| /** Resizes the button horizontally to fit snugly around the text. | |||
| This won't affect the button's height. | |||
| */ | |||
| void changeWidthToFitText(); | |||
| //============================================================================== | |||
| juce_UseDebuggingNewOperator | |||
| protected: | |||
| /** @internal */ | |||
| void clicked(); | |||
| /** @internal */ | |||
| void colourChanged(); | |||
| /** @internal */ | |||
| void paintButton (Graphics& g, | |||
| bool isMouseOverButton, | |||
| bool isButtonDown); | |||
| private: | |||
| URL url; | |||
| Font font; | |||
| bool resizeFont; | |||
| Justification justification; | |||
| const Font getFontToUse() const; | |||
| HyperlinkButton (const HyperlinkButton&); | |||
| const HyperlinkButton& operator= (const HyperlinkButton&); | |||
| }; | |||
| #endif // __JUCE_HYPERLINKBUTTON_JUCEHEADER__ | |||
| @@ -0,0 +1,236 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_ImageButton.h" | |||
| #include "../../graphics/imaging/juce_ImageCache.h" | |||
| #include "../lookandfeel/juce_LookAndFeel.h" | |||
| //============================================================================== | |||
| ImageButton::ImageButton (const String& text_) | |||
| : Button (text_), | |||
| scaleImageToFit (true), | |||
| preserveProportions (true), | |||
| alphaThreshold (0), | |||
| imageX (0), | |||
| imageY (0), | |||
| imageW (0), | |||
| imageH (0), | |||
| normalImage (0), | |||
| overImage (0), | |||
| downImage (0) | |||
| { | |||
| } | |||
| ImageButton::~ImageButton() | |||
| { | |||
| deleteImages(); | |||
| } | |||
| void ImageButton::deleteImages() | |||
| { | |||
| if (normalImage != 0) | |||
| { | |||
| if (ImageCache::isImageInCache (normalImage)) | |||
| ImageCache::release (normalImage); | |||
| else | |||
| delete normalImage; | |||
| } | |||
| if (overImage != 0) | |||
| { | |||
| if (ImageCache::isImageInCache (overImage)) | |||
| ImageCache::release (overImage); | |||
| else | |||
| delete overImage; | |||
| } | |||
| if (downImage != 0) | |||
| { | |||
| if (ImageCache::isImageInCache (downImage)) | |||
| ImageCache::release (downImage); | |||
| else | |||
| delete downImage; | |||
| } | |||
| } | |||
| void ImageButton::setImages (const bool resizeButtonNowToFitThisImage, | |||
| const bool rescaleImagesWhenButtonSizeChanges, | |||
| const bool preserveImageProportions, | |||
| Image* const normalImage_, | |||
| const float imageOpacityWhenNormal, | |||
| const Colour& overlayColourWhenNormal, | |||
| Image* const overImage_, | |||
| const float imageOpacityWhenOver, | |||
| const Colour& overlayColourWhenOver, | |||
| Image* const downImage_, | |||
| const float imageOpacityWhenDown, | |||
| const Colour& overlayColourWhenDown, | |||
| const float hitTestAlphaThreshold) | |||
| { | |||
| deleteImages(); | |||
| normalImage = normalImage_; | |||
| overImage = overImage_; | |||
| downImage = downImage_; | |||
| if (resizeButtonNowToFitThisImage && normalImage != 0) | |||
| { | |||
| imageW = normalImage->getWidth(); | |||
| imageH = normalImage->getHeight(); | |||
| setSize (imageW, imageH); | |||
| } | |||
| scaleImageToFit = rescaleImagesWhenButtonSizeChanges; | |||
| preserveProportions = preserveImageProportions; | |||
| normalOpacity = imageOpacityWhenNormal; | |||
| normalOverlay = overlayColourWhenNormal; | |||
| overOpacity = imageOpacityWhenOver; | |||
| overOverlay = overlayColourWhenOver; | |||
| downOpacity = imageOpacityWhenDown; | |||
| downOverlay = overlayColourWhenDown; | |||
| alphaThreshold = (unsigned char) jlimit (0, 0xff, roundFloatToInt (255.0f * hitTestAlphaThreshold)); | |||
| repaint(); | |||
| } | |||
| Image* ImageButton::getCurrentImage() const | |||
| { | |||
| if (isDown() || getToggleState()) | |||
| return getDownImage(); | |||
| if (isOver()) | |||
| return getOverImage(); | |||
| return getNormalImage(); | |||
| } | |||
| Image* ImageButton::getNormalImage() const throw() | |||
| { | |||
| return normalImage; | |||
| } | |||
| Image* ImageButton::getOverImage() const throw() | |||
| { | |||
| return (overImage != 0) ? overImage | |||
| : normalImage; | |||
| } | |||
| Image* ImageButton::getDownImage() const throw() | |||
| { | |||
| return (downImage != 0) ? downImage | |||
| : getOverImage(); | |||
| } | |||
| void ImageButton::paintButton (Graphics& g, | |||
| bool isMouseOverButton, | |||
| bool isButtonDown) | |||
| { | |||
| if (! isEnabled()) | |||
| { | |||
| isMouseOverButton = false; | |||
| isButtonDown = false; | |||
| } | |||
| Image* const im = getCurrentImage(); | |||
| if (im != 0) | |||
| { | |||
| const int iw = im->getWidth(); | |||
| const int ih = im->getHeight(); | |||
| imageW = getWidth(); | |||
| imageH = getHeight(); | |||
| imageX = (imageW - iw) >> 1; | |||
| imageY = (imageH - ih) >> 1; | |||
| if (scaleImageToFit) | |||
| { | |||
| if (preserveProportions) | |||
| { | |||
| int newW, newH; | |||
| const float imRatio = ih / (float)iw; | |||
| const float destRatio = imageH / (float)imageW; | |||
| if (imRatio > destRatio) | |||
| { | |||
| newW = roundFloatToInt (imageH / imRatio); | |||
| newH = imageH; | |||
| } | |||
| else | |||
| { | |||
| newW = imageW; | |||
| newH = roundFloatToInt (imageW * imRatio); | |||
| } | |||
| imageX = (imageW - newW) / 2; | |||
| imageY = (imageH - newH) / 2; | |||
| imageW = newW; | |||
| imageH = newH; | |||
| } | |||
| else | |||
| { | |||
| imageX = 0; | |||
| imageY = 0; | |||
| } | |||
| } | |||
| if (! scaleImageToFit) | |||
| { | |||
| imageW = iw; | |||
| imageH = ih; | |||
| } | |||
| getLookAndFeel().drawImageButton (g, im, imageX, imageY, imageW, imageH, | |||
| isButtonDown ? downOverlay | |||
| : (isMouseOverButton ? overOverlay | |||
| : normalOverlay), | |||
| isButtonDown ? downOpacity | |||
| : (isMouseOverButton ? overOpacity | |||
| : normalOpacity), | |||
| *this); | |||
| } | |||
| } | |||
| bool ImageButton::hitTest (int x, int y) | |||
| { | |||
| if (alphaThreshold == 0) | |||
| return true; | |||
| Image* const im = getCurrentImage(); | |||
| return im == 0 | |||
| || (imageW > 0 && imageH > 0 | |||
| && alphaThreshold < im->getPixelAt (((x - imageX) * im->getWidth()) / imageW, | |||
| ((y - imageY) * im->getHeight()) / imageH).getAlpha()); | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,132 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_ShapeButton.h" | |||
| //============================================================================== | |||
| ShapeButton::ShapeButton (const String& text, | |||
| const Colour& normalColour_, | |||
| const Colour& overColour_, | |||
| const Colour& downColour_) | |||
| : Button (text), | |||
| normalColour (normalColour_), | |||
| overColour (overColour_), | |||
| downColour (downColour_), | |||
| maintainShapeProportions (false), | |||
| outlineWidth (0.0f) | |||
| { | |||
| } | |||
| ShapeButton::~ShapeButton() | |||
| { | |||
| } | |||
| void ShapeButton::setColours (const Colour& newNormalColour, | |||
| const Colour& newOverColour, | |||
| const Colour& newDownColour) | |||
| { | |||
| normalColour = newNormalColour; | |||
| overColour = newOverColour; | |||
| downColour = newDownColour; | |||
| } | |||
| void ShapeButton::setOutline (const Colour& newOutlineColour, | |||
| const float newOutlineWidth) | |||
| { | |||
| outlineColour = newOutlineColour; | |||
| outlineWidth = newOutlineWidth; | |||
| } | |||
| void ShapeButton::setShape (const Path& newShape, | |||
| const bool resizeNowToFitThisShape, | |||
| const bool maintainShapeProportions_, | |||
| const bool hasShadow) | |||
| { | |||
| shape = newShape; | |||
| maintainShapeProportions = maintainShapeProportions_; | |||
| shadow.setShadowProperties (3.0f, 0.5f, 0, 0); | |||
| setComponentEffect ((hasShadow) ? &shadow : 0); | |||
| if (resizeNowToFitThisShape) | |||
| { | |||
| float x, y, w, h; | |||
| shape.getBounds (x, y, w, h); | |||
| shape.applyTransform (AffineTransform::translation (-x, -y)); | |||
| if (hasShadow) | |||
| { | |||
| w += 4.0f; | |||
| h += 4.0f; | |||
| shape.applyTransform (AffineTransform::translation (2.0f, 2.0f)); | |||
| } | |||
| setSize (1 + (int) (w + outlineWidth), | |||
| 1 + (int) (h + outlineWidth)); | |||
| } | |||
| } | |||
| void ShapeButton::paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown) | |||
| { | |||
| if (! isEnabled()) | |||
| { | |||
| isMouseOverButton = false; | |||
| isButtonDown = false; | |||
| } | |||
| g.setColour ((isButtonDown) ? downColour | |||
| : (isMouseOverButton) ? overColour | |||
| : normalColour); | |||
| int w = getWidth(); | |||
| int h = getHeight(); | |||
| if (getComponentEffect() != 0) | |||
| { | |||
| w -= 4; | |||
| h -= 4; | |||
| } | |||
| const float offset = (outlineWidth * 0.5f) + (isButtonDown ? 1.5f : 0.0f); | |||
| const AffineTransform trans (shape.getTransformToScaleToFit (offset, offset, | |||
| w - offset - outlineWidth, | |||
| h - offset - outlineWidth, | |||
| maintainShapeProportions)); | |||
| g.fillPath (shape, trans); | |||
| if (outlineWidth > 0.0f) | |||
| { | |||
| g.setColour (outlineColour); | |||
| g.strokePath (shape, PathStrokeType (outlineWidth), trans); | |||
| } | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,81 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_TextButton.h" | |||
| #include "../lookandfeel/juce_LookAndFeel.h" | |||
| //============================================================================== | |||
| TextButton::TextButton (const String& name, | |||
| const String& toolTip) | |||
| : Button (name) | |||
| { | |||
| setTooltip (toolTip); | |||
| } | |||
| TextButton::~TextButton() | |||
| { | |||
| } | |||
| void TextButton::paintButton (Graphics& g, | |||
| bool isMouseOverButton, | |||
| bool isButtonDown) | |||
| { | |||
| getLookAndFeel().drawButtonBackground (g, *this, | |||
| findColour (getToggleState() ? buttonOnColourId | |||
| : buttonColourId), | |||
| isMouseOverButton, | |||
| isButtonDown); | |||
| getLookAndFeel().drawButtonText (g, *this, | |||
| isMouseOverButton, | |||
| isButtonDown); | |||
| } | |||
| void TextButton::colourChanged() | |||
| { | |||
| repaint(); | |||
| } | |||
| const Font TextButton::getFont() | |||
| { | |||
| return Font (jmin (15.0f, getHeight() * 0.6f)); | |||
| } | |||
| void TextButton::changeWidthToFitText (const int newHeight) | |||
| { | |||
| if (newHeight >= 0) | |||
| setSize (jmax (1, getWidth()), newHeight); | |||
| setSize (getFont().getStringWidth (getButtonText()) + getHeight(), | |||
| getHeight()); | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,65 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_ToggleButton.h" | |||
| #include "../lookandfeel/juce_LookAndFeel.h" | |||
| #include "../juce_ComponentDeletionWatcher.h" | |||
| //============================================================================== | |||
| ToggleButton::ToggleButton (const String& buttonText) | |||
| : Button (buttonText) | |||
| { | |||
| setClickingTogglesState (true); | |||
| } | |||
| ToggleButton::~ToggleButton() | |||
| { | |||
| } | |||
| void ToggleButton::paintButton (Graphics& g, | |||
| bool isMouseOverButton, | |||
| bool isButtonDown) | |||
| { | |||
| getLookAndFeel().drawToggleButton (g, *this, | |||
| isMouseOverButton, | |||
| isButtonDown); | |||
| } | |||
| void ToggleButton::changeWidthToFitText() | |||
| { | |||
| getLookAndFeel().changeToggleButtonWidthToFitText (*this); | |||
| } | |||
| void ToggleButton::colourChanged() | |||
| { | |||
| repaint(); | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,93 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_ToolbarButton.h" | |||
| #include "../controls/juce_ToolbarItemFactory.h" | |||
| #include "../../graphics/imaging/juce_Image.h" | |||
| #include "../lookandfeel/juce_LookAndFeel.h" | |||
| //============================================================================== | |||
| ToolbarButton::ToolbarButton (const int itemId_, | |||
| const String& buttonText, | |||
| Drawable* const normalImage_, | |||
| Drawable* const toggledOnImage_) | |||
| : ToolbarItemComponent (itemId_, buttonText, true), | |||
| normalImage (normalImage_), | |||
| toggledOnImage (toggledOnImage_) | |||
| { | |||
| } | |||
| ToolbarButton::~ToolbarButton() | |||
| { | |||
| delete normalImage; | |||
| delete toggledOnImage; | |||
| } | |||
| //============================================================================== | |||
| bool ToolbarButton::getToolbarItemSizes (int toolbarDepth, | |||
| bool /*isToolbarVertical*/, | |||
| int& preferredSize, | |||
| int& minSize, int& maxSize) | |||
| { | |||
| preferredSize = minSize = maxSize = toolbarDepth; | |||
| return true; | |||
| } | |||
| void ToolbarButton::paintButtonArea (Graphics& g, | |||
| int width, int height, | |||
| bool /*isMouseOver*/, | |||
| bool /*isMouseDown*/) | |||
| { | |||
| Drawable* d = normalImage; | |||
| if (getToggleState() && toggledOnImage != 0) | |||
| d = toggledOnImage; | |||
| if (! isEnabled()) | |||
| { | |||
| Image im (Image::ARGB, width, height, true); | |||
| Graphics g2 (im); | |||
| d->drawWithin (g2, 0, 0, width, height, RectanglePlacement::centred); | |||
| im.desaturate(); | |||
| g.drawImageAt (&im, 0, 0); | |||
| } | |||
| else | |||
| { | |||
| d->drawWithin (g, 0, 0, width, height, RectanglePlacement::centred); | |||
| } | |||
| } | |||
| void ToolbarButton::contentAreaChanged (const Rectangle&) | |||
| { | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,610 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_ComboBox.h" | |||
| #include "../menus/juce_PopupMenu.h" | |||
| #include "../lookandfeel/juce_LookAndFeel.h" | |||
| #include "../../../text/juce_LocalisedStrings.h" | |||
| //============================================================================== | |||
| ComboBox::ComboBox (const String& name) | |||
| : Component (name), | |||
| items (4), | |||
| currentIndex (-1), | |||
| isButtonDown (false), | |||
| separatorPending (false), | |||
| menuActive (false), | |||
| listeners (2), | |||
| label (0) | |||
| { | |||
| noChoicesMessage = TRANS("(no choices)"); | |||
| setRepaintsOnMouseActivity (true); | |||
| lookAndFeelChanged(); | |||
| } | |||
| ComboBox::~ComboBox() | |||
| { | |||
| if (menuActive) | |||
| PopupMenu::dismissAllActiveMenus(); | |||
| deleteAllChildren(); | |||
| } | |||
| //============================================================================== | |||
| void ComboBox::setEditableText (const bool isEditable) | |||
| { | |||
| label->setEditable (isEditable, isEditable, false); | |||
| setWantsKeyboardFocus (! isEditable); | |||
| resized(); | |||
| } | |||
| bool ComboBox::isTextEditable() const throw() | |||
| { | |||
| return label->isEditable(); | |||
| } | |||
| void ComboBox::setJustificationType (const Justification& justification) throw() | |||
| { | |||
| label->setJustificationType (justification); | |||
| } | |||
| const Justification ComboBox::getJustificationType() const throw() | |||
| { | |||
| return label->getJustificationType(); | |||
| } | |||
| void ComboBox::setTooltip (const String& newTooltip) | |||
| { | |||
| SettableTooltipClient::setTooltip (newTooltip); | |||
| label->setTooltip (newTooltip); | |||
| } | |||
| //============================================================================== | |||
| void ComboBox::addItem (const String& newItemText, | |||
| const int newItemId) throw() | |||
| { | |||
| // you can't add empty strings to the list.. | |||
| jassert (newItemText.isNotEmpty()); | |||
| // IDs must be non-zero, as zero is used to indicate a lack of selecion. | |||
| jassert (newItemId != 0); | |||
| // you shouldn't use duplicate item IDs! | |||
| jassert (getItemForId (newItemId) == 0); | |||
| if (newItemText.isNotEmpty() && newItemId != 0) | |||
| { | |||
| if (separatorPending) | |||
| { | |||
| separatorPending = false; | |||
| ItemInfo* const item = new ItemInfo(); | |||
| item->itemId = 0; | |||
| item->isEnabled = false; | |||
| item->isHeading = false; | |||
| items.add (item); | |||
| } | |||
| ItemInfo* const item = new ItemInfo(); | |||
| item->name = newItemText; | |||
| item->itemId = newItemId; | |||
| item->isEnabled = true; | |||
| item->isHeading = false; | |||
| items.add (item); | |||
| } | |||
| } | |||
| void ComboBox::addSeparator() throw() | |||
| { | |||
| separatorPending = (items.size() > 0); | |||
| } | |||
| void ComboBox::addSectionHeading (const String& headingName) throw() | |||
| { | |||
| // you can't add empty strings to the list.. | |||
| jassert (headingName.isNotEmpty()); | |||
| if (headingName.isNotEmpty()) | |||
| { | |||
| if (separatorPending) | |||
| { | |||
| separatorPending = false; | |||
| ItemInfo* const item = new ItemInfo(); | |||
| item->itemId = 0; | |||
| item->isEnabled = false; | |||
| item->isHeading = false; | |||
| items.add (item); | |||
| } | |||
| ItemInfo* const item = new ItemInfo(); | |||
| item->name = headingName; | |||
| item->itemId = 0; | |||
| item->isEnabled = true; | |||
| item->isHeading = true; | |||
| items.add (item); | |||
| } | |||
| } | |||
| void ComboBox::setItemEnabled (const int itemId, | |||
| const bool isEnabled) throw() | |||
| { | |||
| ItemInfo* const item = getItemForId (itemId); | |||
| if (item != 0) | |||
| item->isEnabled = isEnabled; | |||
| } | |||
| void ComboBox::changeItemText (const int itemId, | |||
| const String& newText) throw() | |||
| { | |||
| ItemInfo* const item = getItemForId (itemId); | |||
| jassert (item != 0); | |||
| if (item != 0) | |||
| item->name = newText; | |||
| } | |||
| void ComboBox::clear (const bool dontSendChangeMessage) | |||
| { | |||
| items.clear(); | |||
| separatorPending = false; | |||
| if (! label->isEditable()) | |||
| setSelectedItemIndex (-1, dontSendChangeMessage); | |||
| } | |||
| //============================================================================== | |||
| ComboBox::ItemInfo* ComboBox::getItemForId (const int itemId) const throw() | |||
| { | |||
| jassert (itemId != 0); | |||
| if (itemId != 0) | |||
| { | |||
| for (int i = items.size(); --i >= 0;) | |||
| if (items.getUnchecked(i)->itemId == itemId) | |||
| return items.getUnchecked(i); | |||
| } | |||
| return 0; | |||
| } | |||
| ComboBox::ItemInfo* ComboBox::getItemForIndex (const int index) const throw() | |||
| { | |||
| int n = 0; | |||
| for (int i = 0; i < items.size(); ++i) | |||
| { | |||
| ItemInfo* const item = items.getUnchecked(i); | |||
| if (item->isRealItem()) | |||
| { | |||
| if (n++ == index) | |||
| return item; | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| int ComboBox::getNumItems() const throw() | |||
| { | |||
| int n = 0; | |||
| for (int i = items.size(); --i >= 0;) | |||
| { | |||
| ItemInfo* const item = items.getUnchecked(i); | |||
| if (item->isRealItem()) | |||
| ++n; | |||
| } | |||
| return n; | |||
| } | |||
| const String ComboBox::getItemText (const int index) const throw() | |||
| { | |||
| ItemInfo* const item = getItemForIndex (index); | |||
| if (item != 0) | |||
| return item->name; | |||
| return String::empty; | |||
| } | |||
| int ComboBox::getItemId (const int index) const throw() | |||
| { | |||
| ItemInfo* const item = getItemForIndex (index); | |||
| return (item != 0) ? item->itemId : 0; | |||
| } | |||
| //============================================================================== | |||
| bool ComboBox::ItemInfo::isSeparator() const throw() | |||
| { | |||
| return name.isEmpty(); | |||
| } | |||
| bool ComboBox::ItemInfo::isRealItem() const throw() | |||
| { | |||
| return ! (isHeading || name.isEmpty()); | |||
| } | |||
| //============================================================================== | |||
| int ComboBox::getSelectedItemIndex() const throw() | |||
| { | |||
| return (currentIndex >= 0 && getText() == getItemText (currentIndex)) | |||
| ? currentIndex | |||
| : -1; | |||
| } | |||
| void ComboBox::setSelectedItemIndex (const int index, | |||
| const bool dontSendChangeMessage) throw() | |||
| { | |||
| if (currentIndex != index || label->getText() != getItemText (currentIndex)) | |||
| { | |||
| if (((unsigned int) index) < (unsigned int) getNumItems()) | |||
| currentIndex = index; | |||
| else | |||
| currentIndex = -1; | |||
| label->setText (getItemText (currentIndex), false); | |||
| if (! dontSendChangeMessage) | |||
| triggerAsyncUpdate(); | |||
| } | |||
| } | |||
| void ComboBox::setSelectedId (const int newItemId, | |||
| const bool dontSendChangeMessage) throw() | |||
| { | |||
| for (int i = getNumItems(); --i >= 0;) | |||
| { | |||
| if (getItemId(i) == newItemId) | |||
| { | |||
| setSelectedItemIndex (i, dontSendChangeMessage); | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| int ComboBox::getSelectedId() const throw() | |||
| { | |||
| const ItemInfo* const item = getItemForIndex (currentIndex); | |||
| return (item != 0 && getText() == item->name) | |||
| ? item->itemId | |||
| : 0; | |||
| } | |||
| //============================================================================== | |||
| void ComboBox::addListener (ComboBoxListener* const listener) throw() | |||
| { | |||
| jassert (listener != 0); | |||
| if (listener != 0) | |||
| listeners.add (listener); | |||
| } | |||
| void ComboBox::removeListener (ComboBoxListener* const listener) throw() | |||
| { | |||
| listeners.removeValue (listener); | |||
| } | |||
| void ComboBox::handleAsyncUpdate() | |||
| { | |||
| for (int i = listeners.size(); --i >= 0;) | |||
| { | |||
| ((ComboBoxListener*) listeners.getUnchecked (i))->comboBoxChanged (this); | |||
| i = jmin (i, listeners.size()); | |||
| } | |||
| } | |||
| //============================================================================== | |||
| const String ComboBox::getText() const throw() | |||
| { | |||
| return label->getText(); | |||
| } | |||
| void ComboBox::setText (const String& newText, | |||
| const bool dontSendChangeMessage) throw() | |||
| { | |||
| for (int i = items.size(); --i >= 0;) | |||
| { | |||
| ItemInfo* const item = items.getUnchecked(i); | |||
| if (item->isRealItem() | |||
| && item->name == newText) | |||
| { | |||
| setSelectedId (item->itemId, dontSendChangeMessage); | |||
| return; | |||
| } | |||
| } | |||
| currentIndex = -1; | |||
| if (label->getText() != newText) | |||
| { | |||
| label->setText (newText, false); | |||
| if (! dontSendChangeMessage) | |||
| triggerAsyncUpdate(); | |||
| } | |||
| repaint(); | |||
| } | |||
| void ComboBox::showEditor() | |||
| { | |||
| jassert (isTextEditable()); // you probably shouldn't do this to a non-editable combo box? | |||
| label->showEditor(); | |||
| } | |||
| //============================================================================== | |||
| void ComboBox::setTextWhenNothingSelected (const String& newMessage) throw() | |||
| { | |||
| textWhenNothingSelected = newMessage; | |||
| repaint(); | |||
| } | |||
| const String ComboBox::getTextWhenNothingSelected() const throw() | |||
| { | |||
| return textWhenNothingSelected; | |||
| } | |||
| void ComboBox::setTextWhenNoChoicesAvailable (const String& newMessage) throw() | |||
| { | |||
| noChoicesMessage = newMessage; | |||
| } | |||
| const String ComboBox::getTextWhenNoChoicesAvailable() const throw() | |||
| { | |||
| return noChoicesMessage; | |||
| } | |||
| //============================================================================== | |||
| void ComboBox::paint (Graphics& g) | |||
| { | |||
| getLookAndFeel().drawComboBox (g, | |||
| getWidth(), | |||
| getHeight(), | |||
| isButtonDown, | |||
| label->getRight(), | |||
| 0, | |||
| getWidth() - label->getRight(), | |||
| getHeight(), | |||
| *this); | |||
| if (textWhenNothingSelected.isNotEmpty() | |||
| && label->getText().isEmpty() | |||
| && ! label->isBeingEdited()) | |||
| { | |||
| g.setColour (findColour (textColourId).withMultipliedAlpha (0.5f)); | |||
| g.setFont (label->getFont()); | |||
| g.drawFittedText (textWhenNothingSelected, | |||
| label->getX() + 2, label->getY() + 1, | |||
| label->getWidth() - 4, label->getHeight() - 2, | |||
| label->getJustificationType(), | |||
| jmax (1, (int) (label->getHeight() / label->getFont().getHeight()))); | |||
| } | |||
| } | |||
| void ComboBox::resized() | |||
| { | |||
| if (getHeight() > 0 && getWidth() > 0) | |||
| getLookAndFeel().positionComboBoxText (*this, *label); | |||
| } | |||
| void ComboBox::enablementChanged() | |||
| { | |||
| repaint(); | |||
| } | |||
| void ComboBox::lookAndFeelChanged() | |||
| { | |||
| repaint(); | |||
| Label* const newLabel = getLookAndFeel().createComboBoxTextBox (*this); | |||
| if (label != 0) | |||
| { | |||
| newLabel->setEditable (label->isEditable()); | |||
| newLabel->setJustificationType (label->getJustificationType()); | |||
| newLabel->setTooltip (label->getTooltip()); | |||
| newLabel->setText (label->getText(), false); | |||
| } | |||
| delete label; | |||
| label = newLabel; | |||
| addAndMakeVisible (newLabel); | |||
| newLabel->addListener (this); | |||
| newLabel->addMouseListener (this, false); | |||
| newLabel->setColour (Label::backgroundColourId, Colours::transparentBlack); | |||
| newLabel->setColour (Label::textColourId, findColour (ComboBox::textColourId)); | |||
| newLabel->setColour (TextEditor::textColourId, findColour (ComboBox::textColourId)); | |||
| newLabel->setColour (TextEditor::backgroundColourId, Colours::transparentBlack); | |||
| newLabel->setColour (TextEditor::highlightColourId, findColour (TextEditor::highlightColourId)); | |||
| newLabel->setColour (TextEditor::outlineColourId, Colours::transparentBlack); | |||
| resized(); | |||
| } | |||
| void ComboBox::colourChanged() | |||
| { | |||
| lookAndFeelChanged(); | |||
| } | |||
| //============================================================================== | |||
| bool ComboBox::keyPressed (const KeyPress& key) | |||
| { | |||
| bool used = false; | |||
| if (key.isKeyCode (KeyPress::upKey) | |||
| || key.isKeyCode (KeyPress::leftKey)) | |||
| { | |||
| setSelectedItemIndex (jmax (0, currentIndex - 1)); | |||
| used = true; | |||
| } | |||
| else if (key.isKeyCode (KeyPress::downKey) | |||
| || key.isKeyCode (KeyPress::rightKey)) | |||
| { | |||
| setSelectedItemIndex (jmin (currentIndex + 1, getNumItems() - 1)); | |||
| used = true; | |||
| } | |||
| else if (key.isKeyCode (KeyPress::returnKey)) | |||
| { | |||
| showPopup(); | |||
| used = true; | |||
| } | |||
| return used; | |||
| } | |||
| bool ComboBox::keyStateChanged (const bool isKeyDown) | |||
| { | |||
| // only forward key events that aren't used by this component | |||
| return isKeyDown | |||
| && (KeyPress::isKeyCurrentlyDown (KeyPress::upKey) | |||
| || KeyPress::isKeyCurrentlyDown (KeyPress::leftKey) | |||
| || KeyPress::isKeyCurrentlyDown (KeyPress::downKey) | |||
| || KeyPress::isKeyCurrentlyDown (KeyPress::rightKey)); | |||
| } | |||
| //============================================================================== | |||
| void ComboBox::focusGained (FocusChangeType) | |||
| { | |||
| repaint(); | |||
| } | |||
| void ComboBox::focusLost (FocusChangeType) | |||
| { | |||
| repaint(); | |||
| } | |||
| //============================================================================== | |||
| void ComboBox::labelTextChanged (Label*) | |||
| { | |||
| triggerAsyncUpdate(); | |||
| } | |||
| //============================================================================== | |||
| void ComboBox::showPopup() | |||
| { | |||
| if (! menuActive) | |||
| { | |||
| const int currentId = getSelectedId(); | |||
| ComponentDeletionWatcher deletionWatcher (this); | |||
| PopupMenu menu; | |||
| menu.setLookAndFeel (&getLookAndFeel()); | |||
| for (int i = 0; i < items.size(); ++i) | |||
| { | |||
| const ItemInfo* const item = items.getUnchecked(i); | |||
| if (item->isSeparator()) | |||
| menu.addSeparator(); | |||
| else if (item->isHeading) | |||
| menu.addSectionHeader (item->name); | |||
| else | |||
| menu.addItem (item->itemId, item->name, | |||
| item->isEnabled, item->itemId == currentId); | |||
| } | |||
| if (items.size() == 0) | |||
| menu.addItem (1, noChoicesMessage, false); | |||
| const int itemHeight = jlimit (12, 24, getHeight()); | |||
| menuActive = true; | |||
| const int resultId = menu.showAt (this, currentId, | |||
| getWidth(), 1, itemHeight); | |||
| if (deletionWatcher.hasBeenDeleted()) | |||
| return; | |||
| menuActive = false; | |||
| if (resultId != 0) | |||
| setSelectedId (resultId); | |||
| } | |||
| } | |||
| //============================================================================== | |||
| void ComboBox::mouseDown (const MouseEvent& e) | |||
| { | |||
| beginDragAutoRepeat (300); | |||
| isButtonDown = isEnabled(); | |||
| if (isButtonDown | |||
| && (e.eventComponent == this || ! label->isEditable())) | |||
| { | |||
| showPopup(); | |||
| } | |||
| } | |||
| void ComboBox::mouseDrag (const MouseEvent& e) | |||
| { | |||
| beginDragAutoRepeat (50); | |||
| if (isButtonDown && ! e.mouseWasClicked()) | |||
| showPopup(); | |||
| } | |||
| void ComboBox::mouseUp (const MouseEvent& e2) | |||
| { | |||
| if (isButtonDown) | |||
| { | |||
| isButtonDown = false; | |||
| repaint(); | |||
| const MouseEvent e (e2.getEventRelativeTo (this)); | |||
| if (reallyContains (e.x, e.y, true) | |||
| && (e2.eventComponent == this || ! label->isEditable())) | |||
| { | |||
| showPopup(); | |||
| } | |||
| } | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,393 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_COMBOBOX_JUCEHEADER__ | |||
| #define __JUCE_COMBOBOX_JUCEHEADER__ | |||
| #include "juce_Label.h" | |||
| #include "../../../text/juce_StringArray.h" | |||
| class ComboBox; | |||
| //============================================================================== | |||
| /** | |||
| A class for receiving events from a ComboBox. | |||
| You can register a ComboBoxListener with a ComboBox using the ComboBox::addListener() | |||
| method, and it will be called when the selected item in the box changes. | |||
| @see ComboBox::addListener, ComboBox::removeListener | |||
| */ | |||
| class JUCE_API ComboBoxListener | |||
| { | |||
| public: | |||
| /** Destructor. */ | |||
| virtual ~ComboBoxListener() {} | |||
| /** Called when a ComboBox has its selected item changed. | |||
| */ | |||
| virtual void comboBoxChanged (ComboBox* comboBoxThatHasChanged) = 0; | |||
| }; | |||
| //============================================================================== | |||
| /** | |||
| A component that lets the user choose from a drop-down list of choices. | |||
| The combo-box has a list of text strings, each with an associated id number, | |||
| that will be shown in the drop-down list when the user clicks on the component. | |||
| The currently selected choice is displayed in the combo-box, and this can | |||
| either be read-only text, or editable. | |||
| To find out when the user selects a different item or edits the text, you | |||
| can register a ComboBoxListener to receive callbacks. | |||
| @see ComboBoxListener | |||
| */ | |||
| class JUCE_API ComboBox : public Component, | |||
| public SettableTooltipClient, | |||
| private LabelListener, | |||
| private AsyncUpdater | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Creates a combo-box. | |||
| On construction, the text field will be empty, so you should call the | |||
| setSelectedId() or setText() method to choose the initial value before | |||
| displaying it. | |||
| @param componentName the name to set for the component (see Component::setName()) | |||
| */ | |||
| ComboBox (const String& componentName); | |||
| /** Destructor. */ | |||
| ~ComboBox(); | |||
| //============================================================================== | |||
| /** Sets whether the test in the combo-box is editable. | |||
| The default state for a new ComboBox is non-editable, and can only be changed | |||
| by choosing from the drop-down list. | |||
| */ | |||
| void setEditableText (const bool isEditable); | |||
| /** Returns true if the text is directly editable. | |||
| @see setEditableText | |||
| */ | |||
| bool isTextEditable() const throw(); | |||
| /** Sets the style of justification to be used for positioning the text. | |||
| The default is Justification::centredLeft. The text is displayed using a | |||
| Label component inside the ComboBox. | |||
| */ | |||
| void setJustificationType (const Justification& justification) throw(); | |||
| /** Returns the current justification for the text box. | |||
| @see setJustificationType | |||
| */ | |||
| const Justification getJustificationType() const throw(); | |||
| //============================================================================== | |||
| /** Adds an item to be shown in the drop-down list. | |||
| @param newItemText the text of the item to show in the list | |||
| @param newItemId an associated ID number that can be set or retrieved - see | |||
| getSelectedId() and setSelectedId() | |||
| @see setItemEnabled, addSeparator, addSectionHeading, removeItem, getNumItems, getItemText, getItemId | |||
| */ | |||
| void addItem (const String& newItemText, | |||
| const int newItemId) throw(); | |||
| /** Adds a separator line to the drop-down list. | |||
| This is like adding a separator to a popup menu. See PopupMenu::addSeparator(). | |||
| */ | |||
| void addSeparator() throw(); | |||
| /** Adds a heading to the drop-down list, so that you can group the items into | |||
| different sections. | |||
| The headings are indented slightly differently to set them apart from the | |||
| items on the list, and obviously can't be selected. You might want to add | |||
| separators between your sections too. | |||
| @see addItem, addSeparator | |||
| */ | |||
| void addSectionHeading (const String& headingName) throw(); | |||
| /** This allows items in the drop-down list to be selectively disabled. | |||
| When you add an item, it's enabled by default, but you can call this | |||
| method to change its status. | |||
| If you disable an item which is already selected, this won't change the | |||
| current selection - it just stops the user choosing that item from the list. | |||
| */ | |||
| void setItemEnabled (const int itemId, | |||
| const bool isEnabled) throw(); | |||
| /** Changes the text for an existing item. | |||
| */ | |||
| void changeItemText (const int itemId, | |||
| const String& newText) throw(); | |||
| /** Removes all the items from the drop-down list. | |||
| If this call causes the content to be cleared, then a change-message | |||
| will be broadcast unless dontSendChangeMessage is true. | |||
| @see addItem, removeItem, getNumItems | |||
| */ | |||
| void clear (const bool dontSendChangeMessage = false); | |||
| /** Returns the number of items that have been added to the list. | |||
| Note that this doesn't include headers or separators. | |||
| */ | |||
| int getNumItems() const throw(); | |||
| /** Returns the text for one of the items in the list. | |||
| Note that this doesn't include headers or separators. | |||
| @param index the item's index from 0 to (getNumItems() - 1) | |||
| */ | |||
| const String getItemText (const int index) const throw(); | |||
| /** Returns the ID for one of the items in the list. | |||
| Note that this doesn't include headers or separators. | |||
| @param index the item's index from 0 to (getNumItems() - 1) | |||
| */ | |||
| int getItemId (const int index) const throw(); | |||
| //============================================================================== | |||
| /** Returns the ID of the item that's currently shown in the box. | |||
| If no item is selected, or if the text is editable and the user | |||
| has entered something which isn't one of the items in the list, then | |||
| this will return 0. | |||
| @see setSelectedId, getSelectedItemIndex, getText | |||
| */ | |||
| int getSelectedId() const throw(); | |||
| /** Sets one of the items to be the current selection. | |||
| This will set the ComboBox's text to that of the item that matches | |||
| this ID. | |||
| @param newItemId the new item to select | |||
| @param dontSendChangeMessage if set to true, this method won't trigger a | |||
| change notification | |||
| @see getSelectedId, setSelectedItemIndex, setText | |||
| */ | |||
| void setSelectedId (const int newItemId, | |||
| const bool dontSendChangeMessage = false) throw(); | |||
| //============================================================================== | |||
| /** Returns the index of the item that's currently shown in the box. | |||
| If no item is selected, or if the text is editable and the user | |||
| has entered something which isn't one of the items in the list, then | |||
| this will return -1. | |||
| @see setSelectedItemIndex, getSelectedId, getText | |||
| */ | |||
| int getSelectedItemIndex() const throw(); | |||
| /** Sets one of the items to be the current selection. | |||
| This will set the ComboBox's text to that of the item at the given | |||
| index in the list. | |||
| @param newItemIndex the new item to select | |||
| @param dontSendChangeMessage if set to true, this method won't trigger a | |||
| change notification | |||
| @see getSelectedItemIndex, setSelectedId, setText | |||
| */ | |||
| void setSelectedItemIndex (const int newItemIndex, | |||
| const bool dontSendChangeMessage = false) throw(); | |||
| //============================================================================== | |||
| /** Returns the text that is currently shown in the combo-box's text field. | |||
| If the ComboBox has editable text, then this text may have been edited | |||
| by the user; otherwise it will be one of the items from the list, or | |||
| possibly an empty string if nothing was selected. | |||
| @see setText, getSelectedId, getSelectedItemIndex | |||
| */ | |||
| const String getText() const throw(); | |||
| /** Sets the contents of the combo-box's text field. | |||
| The text passed-in will be set as the current text regardless of whether | |||
| it is one of the items in the list. If the current text isn't one of the | |||
| items, then getSelectedId() will return -1, otherwise it wil return | |||
| the approriate ID. | |||
| @param newText the text to select | |||
| @param dontSendChangeMessage if set to true, this method won't trigger a | |||
| change notification | |||
| @see getText | |||
| */ | |||
| void setText (const String& newText, | |||
| const bool dontSendChangeMessage = false) throw(); | |||
| /** Programmatically opens the text editor to allow the user to edit the current item. | |||
| This is the same effect as when the box is clicked-on. | |||
| @see Label::showEditor(); | |||
| */ | |||
| void showEditor(); | |||
| //============================================================================== | |||
| /** Registers a listener that will be called when the box's content changes. */ | |||
| void addListener (ComboBoxListener* const listener) throw(); | |||
| /** Deregisters a previously-registered listener. */ | |||
| void removeListener (ComboBoxListener* const listener) throw(); | |||
| //============================================================================== | |||
| /** Sets a message to display when there is no item currently selected. | |||
| @see getTextWhenNothingSelected | |||
| */ | |||
| void setTextWhenNothingSelected (const String& newMessage) throw(); | |||
| /** Returns the text that is shown when no item is selected. | |||
| @see setTextWhenNothingSelected | |||
| */ | |||
| const String getTextWhenNothingSelected() const throw(); | |||
| /** Sets the message to show when there are no items in the list, and the user clicks | |||
| on the drop-down box. | |||
| By default it just says "no choices", but this lets you change it to something more | |||
| meaningful. | |||
| */ | |||
| void setTextWhenNoChoicesAvailable (const String& newMessage) throw(); | |||
| /** Returns the text shown when no items have been added to the list. | |||
| @see setTextWhenNoChoicesAvailable | |||
| */ | |||
| const String getTextWhenNoChoicesAvailable() const throw(); | |||
| //============================================================================== | |||
| /** Gives the ComboBox a tooltip. */ | |||
| void setTooltip (const String& newTooltip); | |||
| //============================================================================== | |||
| /** A set of colour IDs to use to change the colour of various aspects of the combo box. | |||
| These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() | |||
| methods. | |||
| To change the colours of the menu that pops up | |||
| @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour | |||
| */ | |||
| enum ColourIds | |||
| { | |||
| backgroundColourId = 0x1000b00, /**< The background colour to fill the box with. */ | |||
| textColourId = 0x1000a00, /**< The colour for the text in the box. */ | |||
| outlineColourId = 0x1000c00, /**< The colour for an outline around the box. */ | |||
| buttonColourId = 0x1000d00, /**< The base colour for the button (a LookAndFeel class will probably use variations on this). */ | |||
| arrowColourId = 0x1000e00, /**< The colour for the arrow shape that pops up the menu */ | |||
| }; | |||
| //============================================================================== | |||
| /** @internal */ | |||
| void labelTextChanged (Label*); | |||
| /** @internal */ | |||
| void enablementChanged(); | |||
| /** @internal */ | |||
| void colourChanged(); | |||
| /** @internal */ | |||
| void focusGained (Component::FocusChangeType cause); | |||
| /** @internal */ | |||
| void focusLost (Component::FocusChangeType cause); | |||
| /** @internal */ | |||
| void handleAsyncUpdate(); | |||
| /** @internal */ | |||
| const String getTooltip() { return label->getTooltip(); } | |||
| /** @internal */ | |||
| void mouseDown (const MouseEvent&); | |||
| /** @internal */ | |||
| void mouseDrag (const MouseEvent&); | |||
| /** @internal */ | |||
| void mouseUp (const MouseEvent&); | |||
| /** @internal */ | |||
| void lookAndFeelChanged(); | |||
| /** @internal */ | |||
| void paint (Graphics&); | |||
| /** @internal */ | |||
| void resized(); | |||
| /** @internal */ | |||
| bool keyStateChanged (const bool isKeyDown); | |||
| /** @internal */ | |||
| bool keyPressed (const KeyPress&); | |||
| //============================================================================== | |||
| juce_UseDebuggingNewOperator | |||
| private: | |||
| struct ItemInfo | |||
| { | |||
| String name; | |||
| int itemId; | |||
| bool isEnabled : 1, isHeading : 1; | |||
| bool isSeparator() const throw(); | |||
| bool isRealItem() const throw(); | |||
| }; | |||
| OwnedArray <ItemInfo> items; | |||
| int currentIndex; | |||
| bool isButtonDown; | |||
| bool separatorPending; | |||
| bool menuActive; | |||
| SortedSet <void*> listeners; | |||
| Label* label; | |||
| String textWhenNothingSelected, noChoicesMessage; | |||
| void showPopup(); | |||
| ItemInfo* getItemForId (const int itemId) const throw(); | |||
| ItemInfo* getItemForIndex (const int index) const throw(); | |||
| ComboBox (const ComboBox&); | |||
| const ComboBox& operator= (const ComboBox&); | |||
| }; | |||
| #endif // __JUCE_COMBOBOX_JUCEHEADER__ | |||
| @@ -0,0 +1,461 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_Label.h" | |||
| #include "../lookandfeel/juce_LookAndFeel.h" | |||
| //============================================================================== | |||
| Label::Label (const String& componentName, | |||
| const String& labelText) | |||
| : Component (componentName), | |||
| text (labelText), | |||
| font (15.0f), | |||
| justification (Justification::centredLeft), | |||
| editor (0), | |||
| listeners (2), | |||
| ownerComponent (0), | |||
| deletionWatcher (0), | |||
| horizontalBorderSize (3), | |||
| verticalBorderSize (1), | |||
| minimumHorizontalScale (0.7f), | |||
| editSingleClick (false), | |||
| editDoubleClick (false), | |||
| lossOfFocusDiscardsChanges (false) | |||
| { | |||
| setColour (TextEditor::textColourId, Colours::black); | |||
| setColour (TextEditor::backgroundColourId, Colours::transparentBlack); | |||
| setColour (TextEditor::outlineColourId, Colours::transparentBlack); | |||
| } | |||
| Label::~Label() | |||
| { | |||
| if (ownerComponent != 0 && ! deletionWatcher->hasBeenDeleted()) | |||
| ownerComponent->removeComponentListener (this); | |||
| deleteAndZero (deletionWatcher); | |||
| if (editor != 0) | |||
| delete editor; | |||
| } | |||
| //============================================================================== | |||
| void Label::setText (const String& newText, | |||
| const bool broadcastChangeMessage) | |||
| { | |||
| hideEditor (true); | |||
| if (text != newText) | |||
| { | |||
| text = newText; | |||
| repaint(); | |||
| textWasChanged(); | |||
| if (ownerComponent != 0 && ! deletionWatcher->hasBeenDeleted()) | |||
| componentMovedOrResized (*ownerComponent, true, true); | |||
| if (broadcastChangeMessage) | |||
| callChangeListeners(); | |||
| } | |||
| } | |||
| const String Label::getText (const bool returnActiveEditorContents) const throw() | |||
| { | |||
| return (returnActiveEditorContents && isBeingEdited()) | |||
| ? editor->getText() | |||
| : text; | |||
| } | |||
| void Label::setFont (const Font& newFont) throw() | |||
| { | |||
| font = newFont; | |||
| repaint(); | |||
| } | |||
| const Font& Label::getFont() const throw() | |||
| { | |||
| return font; | |||
| } | |||
| void Label::setEditable (const bool editOnSingleClick, | |||
| const bool editOnDoubleClick, | |||
| const bool lossOfFocusDiscardsChanges_) throw() | |||
| { | |||
| editSingleClick = editOnSingleClick; | |||
| editDoubleClick = editOnDoubleClick; | |||
| lossOfFocusDiscardsChanges = lossOfFocusDiscardsChanges_; | |||
| setWantsKeyboardFocus (editOnSingleClick || editOnDoubleClick); | |||
| setFocusContainer (editOnSingleClick || editOnDoubleClick); | |||
| } | |||
| void Label::setJustificationType (const Justification& justification_) throw() | |||
| { | |||
| justification = justification_; | |||
| repaint(); | |||
| } | |||
| void Label::setBorderSize (int h, int v) | |||
| { | |||
| horizontalBorderSize = h; | |||
| verticalBorderSize = v; | |||
| repaint(); | |||
| } | |||
| //============================================================================== | |||
| void Label::attachToComponent (Component* owner, | |||
| const bool onLeft) | |||
| { | |||
| if (ownerComponent != 0 && ! deletionWatcher->hasBeenDeleted()) | |||
| ownerComponent->removeComponentListener (this); | |||
| deleteAndZero (deletionWatcher); | |||
| ownerComponent = owner; | |||
| leftOfOwnerComp = onLeft; | |||
| if (ownerComponent != 0) | |||
| { | |||
| deletionWatcher = new ComponentDeletionWatcher (owner); | |||
| setVisible (owner->isVisible()); | |||
| ownerComponent->addComponentListener (this); | |||
| componentParentHierarchyChanged (*ownerComponent); | |||
| componentMovedOrResized (*ownerComponent, true, true); | |||
| } | |||
| } | |||
| void Label::componentMovedOrResized (Component& component, | |||
| bool /*wasMoved*/, | |||
| bool /*wasResized*/) | |||
| { | |||
| if (leftOfOwnerComp) | |||
| { | |||
| setSize (jmin (getFont().getStringWidth (text) + 8, component.getX()), | |||
| component.getHeight()); | |||
| setTopRightPosition (component.getX(), component.getY()); | |||
| } | |||
| else | |||
| { | |||
| setSize (component.getWidth(), | |||
| 8 + roundFloatToInt (getFont().getHeight())); | |||
| setTopLeftPosition (component.getX(), component.getY() - getHeight()); | |||
| } | |||
| } | |||
| void Label::componentParentHierarchyChanged (Component& component) | |||
| { | |||
| if (component.getParentComponent() != 0) | |||
| component.getParentComponent()->addChildComponent (this); | |||
| } | |||
| void Label::componentVisibilityChanged (Component& component) | |||
| { | |||
| setVisible (component.isVisible()); | |||
| } | |||
| //============================================================================== | |||
| void Label::textWasEdited() | |||
| { | |||
| } | |||
| void Label::textWasChanged() | |||
| { | |||
| } | |||
| void Label::showEditor() | |||
| { | |||
| if (editor == 0) | |||
| { | |||
| addAndMakeVisible (editor = createEditorComponent()); | |||
| editor->setText (getText(), false); | |||
| editor->addListener (this); | |||
| editor->grabKeyboardFocus(); | |||
| editor->setHighlightedRegion (0, text.length()); | |||
| editor->addListener (this); | |||
| resized(); | |||
| repaint(); | |||
| editorShown (editor); | |||
| enterModalState(); | |||
| editor->grabKeyboardFocus(); | |||
| } | |||
| } | |||
| void Label::editorShown (TextEditor* editorComponent) | |||
| { | |||
| } | |||
| void Label::editorAboutToBeHidden (TextEditor* editorComponent) | |||
| { | |||
| } | |||
| bool Label::updateFromTextEditorContents() | |||
| { | |||
| jassert (editor != 0); | |||
| const String newText (editor->getText()); | |||
| if (text != newText) | |||
| { | |||
| text = newText; | |||
| repaint(); | |||
| textWasChanged(); | |||
| if (ownerComponent != 0 && ! deletionWatcher->hasBeenDeleted()) | |||
| componentMovedOrResized (*ownerComponent, true, true); | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| void Label::hideEditor (const bool discardCurrentEditorContents) | |||
| { | |||
| if (editor != 0) | |||
| { | |||
| editorAboutToBeHidden (editor); | |||
| const bool changed = (! discardCurrentEditorContents) | |||
| && updateFromTextEditorContents(); | |||
| deleteAndZero (editor); | |||
| repaint(); | |||
| if (changed) | |||
| textWasEdited(); | |||
| exitModalState (0); | |||
| if (changed && isValidComponent()) | |||
| callChangeListeners(); | |||
| } | |||
| } | |||
| void Label::inputAttemptWhenModal() | |||
| { | |||
| if (editor != 0) | |||
| { | |||
| if (lossOfFocusDiscardsChanges) | |||
| textEditorEscapeKeyPressed (*editor); | |||
| else | |||
| textEditorReturnKeyPressed (*editor); | |||
| } | |||
| } | |||
| bool Label::isBeingEdited() const throw() | |||
| { | |||
| return editor != 0; | |||
| } | |||
| TextEditor* Label::createEditorComponent() | |||
| { | |||
| TextEditor* const ed = new TextEditor (getName()); | |||
| ed->setFont (font); | |||
| // copy these colours from our own settings.. | |||
| const int cols[] = { TextEditor::backgroundColourId, | |||
| TextEditor::textColourId, | |||
| TextEditor::highlightColourId, | |||
| TextEditor::highlightedTextColourId, | |||
| TextEditor::caretColourId, | |||
| TextEditor::outlineColourId, | |||
| TextEditor::focusedOutlineColourId, | |||
| TextEditor::shadowColourId }; | |||
| for (int i = 0; i < numElementsInArray (cols); ++i) | |||
| ed->setColour (cols[i], findColour (cols[i])); | |||
| return ed; | |||
| } | |||
| //============================================================================== | |||
| void Label::paint (Graphics& g) | |||
| { | |||
| getLookAndFeel().drawLabel (g, *this); | |||
| } | |||
| void Label::mouseUp (const MouseEvent& e) | |||
| { | |||
| if (editSingleClick | |||
| && e.mouseWasClicked() | |||
| && contains (e.x, e.y) | |||
| && ! e.mods.isPopupMenu()) | |||
| { | |||
| showEditor(); | |||
| } | |||
| } | |||
| void Label::mouseDoubleClick (const MouseEvent& e) | |||
| { | |||
| if (editDoubleClick && ! e.mods.isPopupMenu()) | |||
| showEditor(); | |||
| } | |||
| void Label::resized() | |||
| { | |||
| if (editor != 0) | |||
| editor->setBoundsInset (BorderSize (0)); | |||
| } | |||
| void Label::focusGained (FocusChangeType cause) | |||
| { | |||
| if (editSingleClick && cause == focusChangedByTabKey) | |||
| showEditor(); | |||
| } | |||
| void Label::enablementChanged() | |||
| { | |||
| repaint(); | |||
| } | |||
| void Label::colourChanged() | |||
| { | |||
| repaint(); | |||
| } | |||
| void Label::setMinimumHorizontalScale (const float newScale) | |||
| { | |||
| if (minimumHorizontalScale != newScale) | |||
| { | |||
| minimumHorizontalScale = newScale; | |||
| repaint(); | |||
| } | |||
| } | |||
| //============================================================================== | |||
| // We'll use a custom focus traverser here to make sure focus goes from the | |||
| // text editor to another component rather than back to the label itself. | |||
| class LabelKeyboardFocusTraverser : public KeyboardFocusTraverser | |||
| { | |||
| public: | |||
| LabelKeyboardFocusTraverser() {} | |||
| Component* getNextComponent (Component* current) | |||
| { | |||
| return KeyboardFocusTraverser::getNextComponent (dynamic_cast <TextEditor*> (current) != 0 | |||
| ? current->getParentComponent() : current); | |||
| } | |||
| Component* getPreviousComponent (Component* current) | |||
| { | |||
| return KeyboardFocusTraverser::getPreviousComponent (dynamic_cast <TextEditor*> (current) != 0 | |||
| ? current->getParentComponent() : current); | |||
| } | |||
| }; | |||
| KeyboardFocusTraverser* Label::createFocusTraverser() | |||
| { | |||
| return new LabelKeyboardFocusTraverser(); | |||
| } | |||
| //============================================================================== | |||
| void Label::addListener (LabelListener* const listener) throw() | |||
| { | |||
| jassert (listener != 0); | |||
| if (listener != 0) | |||
| listeners.add (listener); | |||
| } | |||
| void Label::removeListener (LabelListener* const listener) throw() | |||
| { | |||
| listeners.removeValue (listener); | |||
| } | |||
| void Label::callChangeListeners() | |||
| { | |||
| for (int i = listeners.size(); --i >= 0;) | |||
| { | |||
| ((LabelListener*) listeners.getUnchecked (i))->labelTextChanged (this); | |||
| i = jmin (i, listeners.size()); | |||
| } | |||
| } | |||
| //============================================================================== | |||
| void Label::textEditorTextChanged (TextEditor& ed) | |||
| { | |||
| if (editor != 0) | |||
| { | |||
| jassert (&ed == editor); | |||
| if (! (hasKeyboardFocus (true) || isCurrentlyBlockedByAnotherModalComponent())) | |||
| { | |||
| if (lossOfFocusDiscardsChanges) | |||
| textEditorEscapeKeyPressed (ed); | |||
| else | |||
| textEditorReturnKeyPressed (ed); | |||
| } | |||
| } | |||
| } | |||
| void Label::textEditorReturnKeyPressed (TextEditor& ed) | |||
| { | |||
| if (editor != 0) | |||
| { | |||
| jassert (&ed == editor); | |||
| (void) ed; | |||
| const bool changed = updateFromTextEditorContents(); | |||
| hideEditor (true); | |||
| if (changed) | |||
| { | |||
| textWasEdited(); | |||
| if (isValidComponent()) | |||
| callChangeListeners(); | |||
| } | |||
| } | |||
| } | |||
| void Label::textEditorEscapeKeyPressed (TextEditor& ed) | |||
| { | |||
| if (editor != 0) | |||
| { | |||
| jassert (&ed == editor); | |||
| (void) ed; | |||
| editor->setText (text, false); | |||
| hideEditor (true); | |||
| } | |||
| } | |||
| void Label::textEditorFocusLost (TextEditor& ed) | |||
| { | |||
| textEditorTextChanged (ed); | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,970 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_ListBox.h" | |||
| #include "../../graphics/geometry/juce_RectangleList.h" | |||
| #include "../../graphics/imaging/juce_Image.h" | |||
| #include "../mouse/juce_DragAndDropContainer.h" | |||
| //============================================================================== | |||
| class ListBoxRowComponent : public Component | |||
| { | |||
| public: | |||
| ListBoxRowComponent (ListBox& owner_) | |||
| : owner (owner_), | |||
| row (-1), | |||
| selected (false), | |||
| isDragging (false) | |||
| { | |||
| } | |||
| ~ListBoxRowComponent() | |||
| { | |||
| deleteAllChildren(); | |||
| } | |||
| void paint (Graphics& g) | |||
| { | |||
| if (owner.getModel() != 0) | |||
| owner.getModel()->paintListBoxItem (row, g, getWidth(), getHeight(), selected); | |||
| } | |||
| void update (const int row_, const bool selected_) | |||
| { | |||
| if (row != row_ || selected != selected_) | |||
| { | |||
| repaint(); | |||
| row = row_; | |||
| selected = selected_; | |||
| } | |||
| if (owner.getModel() != 0) | |||
| { | |||
| Component* const customComp = owner.getModel()->refreshComponentForRow (row_, selected_, getChildComponent (0)); | |||
| if (customComp != 0) | |||
| { | |||
| addAndMakeVisible (customComp); | |||
| customComp->setBounds (0, 0, getWidth(), getHeight()); | |||
| for (int i = getNumChildComponents(); --i >= 0;) | |||
| if (getChildComponent (i) != customComp) | |||
| delete getChildComponent (i); | |||
| } | |||
| else | |||
| { | |||
| deleteAllChildren(); | |||
| } | |||
| } | |||
| } | |||
| void mouseDown (const MouseEvent& e) | |||
| { | |||
| isDragging = false; | |||
| selectRowOnMouseUp = false; | |||
| if (isEnabled()) | |||
| { | |||
| if (! selected) | |||
| { | |||
| owner.selectRowsBasedOnModifierKeys (row, e.mods); | |||
| if (owner.getModel() != 0) | |||
| owner.getModel()->listBoxItemClicked (row, e); | |||
| } | |||
| else | |||
| { | |||
| selectRowOnMouseUp = true; | |||
| } | |||
| } | |||
| } | |||
| void mouseUp (const MouseEvent& e) | |||
| { | |||
| if (isEnabled() && selectRowOnMouseUp && ! isDragging) | |||
| { | |||
| owner.selectRowsBasedOnModifierKeys (row, e.mods); | |||
| if (owner.getModel() != 0) | |||
| owner.getModel()->listBoxItemClicked (row, e); | |||
| } | |||
| } | |||
| void mouseDoubleClick (const MouseEvent& e) | |||
| { | |||
| if (owner.getModel() != 0 && isEnabled()) | |||
| owner.getModel()->listBoxItemDoubleClicked (row, e); | |||
| } | |||
| void mouseDrag (const MouseEvent& e) | |||
| { | |||
| if (isEnabled() && owner.getModel() != 0 && ! (e.mouseWasClicked() || isDragging)) | |||
| { | |||
| const SparseSet <int> selectedRows (owner.getSelectedRows()); | |||
| if (selectedRows.size() > 0) | |||
| { | |||
| const String dragDescription (owner.getModel()->getDragSourceDescription (selectedRows)); | |||
| if (dragDescription.isNotEmpty()) | |||
| { | |||
| isDragging = true; | |||
| DragAndDropContainer* const dragContainer | |||
| = DragAndDropContainer::findParentDragContainerFor (this); | |||
| if (dragContainer != 0) | |||
| { | |||
| Image* dragImage = owner.createSnapshotOfSelectedRows(); | |||
| dragImage->multiplyAllAlphas (0.6f); | |||
| dragContainer->startDragging (dragDescription, &owner, dragImage, true); | |||
| } | |||
| else | |||
| { | |||
| // to be able to do a drag-and-drop operation, the listbox needs to | |||
| // be inside a component which is also a DragAndDropContainer. | |||
| jassertfalse | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| void resized() | |||
| { | |||
| if (getNumChildComponents() > 0) | |||
| getChildComponent(0)->setBounds (0, 0, getWidth(), getHeight()); | |||
| } | |||
| juce_UseDebuggingNewOperator | |||
| bool neededFlag; | |||
| private: | |||
| ListBox& owner; | |||
| int row; | |||
| bool selected, isDragging, selectRowOnMouseUp; | |||
| ListBoxRowComponent (const ListBoxRowComponent&); | |||
| const ListBoxRowComponent& operator= (const ListBoxRowComponent&); | |||
| }; | |||
| //============================================================================== | |||
| class ListViewport : public Viewport | |||
| { | |||
| public: | |||
| int firstIndex, firstWholeIndex, lastWholeIndex; | |||
| bool hasUpdated; | |||
| //============================================================================== | |||
| ListViewport (ListBox& owner_) | |||
| : owner (owner_) | |||
| { | |||
| setWantsKeyboardFocus (false); | |||
| setViewedComponent (new Component()); | |||
| getViewedComponent()->addMouseListener (this, false); | |||
| getViewedComponent()->setWantsKeyboardFocus (false); | |||
| } | |||
| ~ListViewport() | |||
| { | |||
| getViewedComponent()->removeMouseListener (this); | |||
| getViewedComponent()->deleteAllChildren(); | |||
| } | |||
| ListBoxRowComponent* getComponentForRow (const int row) const throw() | |||
| { | |||
| return (ListBoxRowComponent*) getViewedComponent() | |||
| ->getChildComponent (row % jmax (1, getViewedComponent()->getNumChildComponents())); | |||
| } | |||
| int getRowNumberOfComponent (Component* const rowComponent) const throw() | |||
| { | |||
| const int index = getIndexOfChildComponent (rowComponent); | |||
| const int num = getViewedComponent()->getNumChildComponents(); | |||
| for (int i = num; --i >= 0;) | |||
| if (((firstIndex + i) % jmax (1, num)) == index) | |||
| return firstIndex + i; | |||
| return -1; | |||
| } | |||
| Component* getComponentForRowIfOnscreen (const int row) const throw() | |||
| { | |||
| return (row >= firstIndex && row < firstIndex + getViewedComponent()->getNumChildComponents()) | |||
| ? getComponentForRow (row) : 0; | |||
| } | |||
| void visibleAreaChanged (int, int, int, int) | |||
| { | |||
| updateVisibleArea (true); | |||
| if (owner.getModel() != 0) | |||
| owner.getModel()->listWasScrolled(); | |||
| } | |||
| void updateVisibleArea (const bool makeSureItUpdatesContent) | |||
| { | |||
| hasUpdated = false; | |||
| const int newX = getViewedComponent()->getX(); | |||
| int newY = getViewedComponent()->getY(); | |||
| const int newW = jmax (owner.minimumRowWidth, getMaximumVisibleWidth()); | |||
| const int newH = owner.totalItems * owner.getRowHeight(); | |||
| if (newY + newH < getMaximumVisibleHeight() && newH > getMaximumVisibleHeight()) | |||
| newY = getMaximumVisibleHeight() - newH; | |||
| getViewedComponent()->setBounds (newX, newY, newW, newH); | |||
| if (makeSureItUpdatesContent && ! hasUpdated) | |||
| updateContents(); | |||
| } | |||
| void updateContents() | |||
| { | |||
| hasUpdated = true; | |||
| const int rowHeight = owner.getRowHeight(); | |||
| if (rowHeight > 0) | |||
| { | |||
| const int y = getViewPositionY(); | |||
| const int w = getViewedComponent()->getWidth(); | |||
| const int numNeeded = 2 + getMaximumVisibleHeight() / rowHeight; | |||
| while (numNeeded > getViewedComponent()->getNumChildComponents()) | |||
| getViewedComponent()->addAndMakeVisible (new ListBoxRowComponent (owner)); | |||
| jassert (numNeeded >= 0); | |||
| while (numNeeded < getViewedComponent()->getNumChildComponents()) | |||
| { | |||
| Component* const rowToRemove | |||
| = getViewedComponent()->getChildComponent (getViewedComponent()->getNumChildComponents() - 1); | |||
| delete rowToRemove; | |||
| } | |||
| firstIndex = y / rowHeight; | |||
| firstWholeIndex = (y + rowHeight - 1) / rowHeight; | |||
| lastWholeIndex = (y + getMaximumVisibleHeight() - 1) / rowHeight; | |||
| for (int i = 0; i < numNeeded; ++i) | |||
| { | |||
| const int row = i + firstIndex; | |||
| ListBoxRowComponent* const rowComp = getComponentForRow (row); | |||
| if (rowComp != 0) | |||
| { | |||
| rowComp->setBounds (0, row * rowHeight, w, rowHeight); | |||
| rowComp->update (row, owner.isRowSelected (row)); | |||
| } | |||
| } | |||
| } | |||
| if (owner.headerComponent != 0) | |||
| owner.headerComponent->setBounds (owner.outlineThickness + getViewedComponent()->getX(), | |||
| owner.outlineThickness, | |||
| jmax (owner.getWidth() - owner.outlineThickness * 2, | |||
| getViewedComponent()->getWidth()), | |||
| owner.headerComponent->getHeight()); | |||
| } | |||
| void paint (Graphics& g) | |||
| { | |||
| if (isOpaque()) | |||
| g.fillAll (owner.findColour (ListBox::backgroundColourId)); | |||
| } | |||
| bool keyPressed (const KeyPress& key) | |||
| { | |||
| if (key.isKeyCode (KeyPress::upKey) | |||
| || key.isKeyCode (KeyPress::downKey) | |||
| || key.isKeyCode (KeyPress::pageUpKey) | |||
| || key.isKeyCode (KeyPress::pageDownKey) | |||
| || key.isKeyCode (KeyPress::homeKey) | |||
| || key.isKeyCode (KeyPress::endKey)) | |||
| { | |||
| // we want to avoid these keypresses going to the viewport, and instead allow | |||
| // them to pass up to our listbox.. | |||
| return false; | |||
| } | |||
| return Viewport::keyPressed (key); | |||
| } | |||
| //============================================================================== | |||
| juce_UseDebuggingNewOperator | |||
| private: | |||
| ListBox& owner; | |||
| ListViewport (const ListViewport&); | |||
| const ListViewport& operator= (const ListViewport&); | |||
| }; | |||
| //============================================================================== | |||
| ListBox::ListBox (const String& name, ListBoxModel* const model_) | |||
| : Component (name), | |||
| model (model_), | |||
| headerComponent (0), | |||
| totalItems (0), | |||
| rowHeight (22), | |||
| minimumRowWidth (0), | |||
| outlineThickness (0), | |||
| lastRowSelected (-1), | |||
| mouseMoveSelects (false), | |||
| multipleSelection (false), | |||
| hasDoneInitialUpdate (false) | |||
| { | |||
| addAndMakeVisible (viewport = new ListViewport (*this)); | |||
| setWantsKeyboardFocus (true); | |||
| } | |||
| ListBox::~ListBox() | |||
| { | |||
| deleteAllChildren(); | |||
| } | |||
| void ListBox::setModel (ListBoxModel* const newModel) | |||
| { | |||
| if (model != newModel) | |||
| { | |||
| model = newModel; | |||
| updateContent(); | |||
| } | |||
| } | |||
| void ListBox::setMultipleSelectionEnabled (bool b) | |||
| { | |||
| multipleSelection = b; | |||
| } | |||
| void ListBox::setMouseMoveSelectsRows (bool b) | |||
| { | |||
| mouseMoveSelects = b; | |||
| if (b) | |||
| addMouseListener (this, true); | |||
| } | |||
| //============================================================================== | |||
| void ListBox::paint (Graphics& g) | |||
| { | |||
| if (! hasDoneInitialUpdate) | |||
| updateContent(); | |||
| g.fillAll (findColour (backgroundColourId)); | |||
| } | |||
| void ListBox::paintOverChildren (Graphics& g) | |||
| { | |||
| if (outlineThickness > 0) | |||
| { | |||
| g.setColour (findColour (outlineColourId)); | |||
| g.drawRect (0, 0, getWidth(), getHeight(), outlineThickness); | |||
| } | |||
| } | |||
| void ListBox::resized() | |||
| { | |||
| viewport->setBoundsInset (BorderSize (outlineThickness + ((headerComponent != 0) ? headerComponent->getHeight() : 0), | |||
| outlineThickness, | |||
| outlineThickness, | |||
| outlineThickness)); | |||
| viewport->setSingleStepSizes (20, getRowHeight()); | |||
| viewport->updateVisibleArea (false); | |||
| } | |||
| void ListBox::visibilityChanged() | |||
| { | |||
| viewport->updateVisibleArea (true); | |||
| } | |||
| Viewport* ListBox::getViewport() const throw() | |||
| { | |||
| return viewport; | |||
| } | |||
| //============================================================================== | |||
| void ListBox::updateContent() | |||
| { | |||
| hasDoneInitialUpdate = true; | |||
| totalItems = (model != 0) ? model->getNumRows() : 0; | |||
| bool selectionChanged = false; | |||
| if (selected [selected.size() - 1] >= totalItems) | |||
| { | |||
| selected.removeRange (totalItems, INT_MAX - totalItems); | |||
| lastRowSelected = getSelectedRow (0); | |||
| selectionChanged = true; | |||
| } | |||
| viewport->updateVisibleArea (isVisible()); | |||
| viewport->resized(); | |||
| if (selectionChanged && model != 0) | |||
| model->selectedRowsChanged (lastRowSelected); | |||
| } | |||
| //============================================================================== | |||
| void ListBox::selectRow (const int row, | |||
| bool dontScroll, | |||
| bool deselectOthersFirst) | |||
| { | |||
| selectRowInternal (row, dontScroll, deselectOthersFirst, false); | |||
| } | |||
| void ListBox::selectRowInternal (const int row, | |||
| bool dontScroll, | |||
| bool deselectOthersFirst, | |||
| bool isMouseClick) | |||
| { | |||
| if (! multipleSelection) | |||
| deselectOthersFirst = true; | |||
| if ((! isRowSelected (row)) | |||
| || (deselectOthersFirst && getNumSelectedRows() > 1)) | |||
| { | |||
| if (((unsigned int) row) < (unsigned int) totalItems) | |||
| { | |||
| if (deselectOthersFirst) | |||
| selected.clear(); | |||
| selected.addRange (row, 1); | |||
| if (getHeight() == 0 || getWidth() == 0) | |||
| dontScroll = true; | |||
| viewport->hasUpdated = false; | |||
| if (row < viewport->firstWholeIndex && ! dontScroll) | |||
| { | |||
| viewport->setViewPosition (viewport->getViewPositionX(), | |||
| row * getRowHeight()); | |||
| } | |||
| else if (row >= viewport->lastWholeIndex && ! dontScroll) | |||
| { | |||
| const int rowsOnScreen = viewport->lastWholeIndex - viewport->firstWholeIndex; | |||
| if (row >= lastRowSelected + rowsOnScreen | |||
| && rowsOnScreen < totalItems - 1 | |||
| && ! isMouseClick) | |||
| { | |||
| viewport->setViewPosition (viewport->getViewPositionX(), | |||
| jlimit (0, jmax (0, totalItems - rowsOnScreen), row) | |||
| * getRowHeight()); | |||
| } | |||
| else | |||
| { | |||
| viewport->setViewPosition (viewport->getViewPositionX(), | |||
| jmax (0, (row + 1) * getRowHeight() - viewport->getMaximumVisibleHeight())); | |||
| } | |||
| } | |||
| if (! viewport->hasUpdated) | |||
| viewport->updateContents(); | |||
| lastRowSelected = row; | |||
| model->selectedRowsChanged (row); | |||
| } | |||
| else | |||
| { | |||
| if (deselectOthersFirst) | |||
| deselectAllRows(); | |||
| } | |||
| } | |||
| } | |||
| void ListBox::deselectRow (const int row) | |||
| { | |||
| if (selected.contains (row)) | |||
| { | |||
| selected.removeRange (row, 1); | |||
| if (row == lastRowSelected) | |||
| lastRowSelected = getSelectedRow (0); | |||
| viewport->updateContents(); | |||
| model->selectedRowsChanged (lastRowSelected); | |||
| } | |||
| } | |||
| void ListBox::setSelectedRows (const SparseSet<int>& setOfRowsToBeSelected, | |||
| const bool sendNotificationEventToModel) | |||
| { | |||
| selected = setOfRowsToBeSelected; | |||
| selected.removeRange (totalItems, INT_MAX - totalItems); | |||
| if (! isRowSelected (lastRowSelected)) | |||
| lastRowSelected = getSelectedRow (0); | |||
| viewport->updateContents(); | |||
| if ((model != 0) && sendNotificationEventToModel) | |||
| model->selectedRowsChanged (lastRowSelected); | |||
| } | |||
| const SparseSet<int> ListBox::getSelectedRows() const | |||
| { | |||
| return selected; | |||
| } | |||
| void ListBox::selectRangeOfRows (int firstRow, int lastRow) | |||
| { | |||
| if (multipleSelection && (firstRow != lastRow)) | |||
| { | |||
| const int numRows = totalItems - 1; | |||
| firstRow = jlimit (0, jmax (0, numRows), firstRow); | |||
| lastRow = jlimit (0, jmax (0, numRows), lastRow); | |||
| selected.addRange (jmin (firstRow, lastRow), | |||
| abs (firstRow - lastRow) + 1); | |||
| selected.removeRange (lastRow, 1); | |||
| } | |||
| selectRowInternal (lastRow, false, false, true); | |||
| } | |||
| void ListBox::flipRowSelection (const int row) | |||
| { | |||
| if (isRowSelected (row)) | |||
| deselectRow (row); | |||
| else | |||
| selectRowInternal (row, false, false, true); | |||
| } | |||
| void ListBox::deselectAllRows() | |||
| { | |||
| if (! selected.isEmpty()) | |||
| { | |||
| selected.clear(); | |||
| lastRowSelected = -1; | |||
| viewport->updateContents(); | |||
| if (model != 0) | |||
| model->selectedRowsChanged (lastRowSelected); | |||
| } | |||
| } | |||
| void ListBox::selectRowsBasedOnModifierKeys (const int row, | |||
| const ModifierKeys& mods) | |||
| { | |||
| if (multipleSelection && mods.isCommandDown()) | |||
| { | |||
| flipRowSelection (row); | |||
| } | |||
| else if (multipleSelection && mods.isShiftDown() && lastRowSelected >= 0) | |||
| { | |||
| selectRangeOfRows (lastRowSelected, row); | |||
| } | |||
| else if ((! mods.isPopupMenu()) || ! isRowSelected (row)) | |||
| { | |||
| selectRowInternal (row, false, true, true); | |||
| } | |||
| } | |||
| int ListBox::getNumSelectedRows() const | |||
| { | |||
| return selected.size(); | |||
| } | |||
| int ListBox::getSelectedRow (const int index) const | |||
| { | |||
| return (((unsigned int) index) < (unsigned int) selected.size()) | |||
| ? selected [index] : -1; | |||
| } | |||
| bool ListBox::isRowSelected (const int row) const | |||
| { | |||
| return selected.contains (row); | |||
| } | |||
| int ListBox::getLastRowSelected() const | |||
| { | |||
| return (isRowSelected (lastRowSelected)) ? lastRowSelected : -1; | |||
| } | |||
| //============================================================================== | |||
| int ListBox::getRowContainingPosition (const int x, const int y) const throw() | |||
| { | |||
| if (((unsigned int) x) < (unsigned int) getWidth()) | |||
| { | |||
| const int row = (viewport->getViewPositionY() + y - viewport->getY()) / rowHeight; | |||
| if (((unsigned int) row) < (unsigned int) totalItems) | |||
| return row; | |||
| } | |||
| return -1; | |||
| } | |||
| int ListBox::getInsertionIndexForPosition (const int x, const int y) const throw() | |||
| { | |||
| if (((unsigned int) x) < (unsigned int) getWidth()) | |||
| { | |||
| const int row = (viewport->getViewPositionY() + y + rowHeight / 2 - viewport->getY()) / rowHeight; | |||
| return jlimit (0, totalItems, row); | |||
| } | |||
| return -1; | |||
| } | |||
| Component* ListBox::getComponentForRowNumber (const int row) const throw() | |||
| { | |||
| Component* const listRowComp = viewport->getComponentForRowIfOnscreen (row); | |||
| return listRowComp != 0 ? listRowComp->getChildComponent (0) : 0; | |||
| } | |||
| int ListBox::getRowNumberOfComponent (Component* const rowComponent) const throw() | |||
| { | |||
| return viewport->getRowNumberOfComponent (rowComponent); | |||
| } | |||
| const Rectangle ListBox::getRowPosition (const int rowNumber, | |||
| const bool relativeToComponentTopLeft) const throw() | |||
| { | |||
| const int rowHeight = getRowHeight(); | |||
| int y = viewport->getY() + rowHeight * rowNumber; | |||
| if (relativeToComponentTopLeft) | |||
| y -= viewport->getViewPositionY(); | |||
| return Rectangle (viewport->getX(), y, | |||
| viewport->getViewedComponent()->getWidth(), rowHeight); | |||
| } | |||
| void ListBox::setVerticalPosition (const double proportion) | |||
| { | |||
| const int offscreen = viewport->getViewedComponent()->getHeight() - viewport->getHeight(); | |||
| viewport->setViewPosition (viewport->getViewPositionX(), | |||
| jmax (0, roundDoubleToInt (proportion * offscreen))); | |||
| } | |||
| double ListBox::getVerticalPosition() const | |||
| { | |||
| const int offscreen = viewport->getViewedComponent()->getHeight() - viewport->getHeight(); | |||
| return (offscreen > 0) ? viewport->getViewPositionY() / (double) offscreen | |||
| : 0; | |||
| } | |||
| int ListBox::getVisibleRowWidth() const throw() | |||
| { | |||
| return viewport->getViewWidth(); | |||
| } | |||
| void ListBox::scrollToEnsureRowIsOnscreen (const int row) | |||
| { | |||
| if (row < viewport->firstWholeIndex) | |||
| { | |||
| viewport->setViewPosition (viewport->getViewPositionX(), | |||
| row * getRowHeight()); | |||
| } | |||
| else if (row >= viewport->lastWholeIndex) | |||
| { | |||
| viewport->setViewPosition (viewport->getViewPositionX(), | |||
| jmax (0, (row + 1) * getRowHeight() - viewport->getMaximumVisibleHeight())); | |||
| } | |||
| } | |||
| //============================================================================== | |||
| bool ListBox::keyPressed (const KeyPress& key) | |||
| { | |||
| const int numVisibleRows = viewport->getHeight() / getRowHeight(); | |||
| const bool multiple = multipleSelection | |||
| && (lastRowSelected >= 0) | |||
| && (key.getModifiers().isShiftDown() | |||
| || key.getModifiers().isCtrlDown() | |||
| || key.getModifiers().isCommandDown()); | |||
| if (key.isKeyCode (KeyPress::upKey)) | |||
| { | |||
| if (multiple) | |||
| selectRangeOfRows (lastRowSelected, lastRowSelected - 1); | |||
| else | |||
| selectRow (jmax (0, lastRowSelected - 1)); | |||
| } | |||
| else if (key.isKeyCode (KeyPress::returnKey) | |||
| && isRowSelected (lastRowSelected)) | |||
| { | |||
| if (model != 0) | |||
| model->returnKeyPressed (lastRowSelected); | |||
| } | |||
| else if (key.isKeyCode (KeyPress::pageUpKey)) | |||
| { | |||
| if (multiple) | |||
| selectRangeOfRows (lastRowSelected, lastRowSelected - numVisibleRows); | |||
| else | |||
| selectRow (jmax (0, jmax (0, lastRowSelected) - numVisibleRows)); | |||
| } | |||
| else if (key.isKeyCode (KeyPress::pageDownKey)) | |||
| { | |||
| if (multiple) | |||
| selectRangeOfRows (lastRowSelected, lastRowSelected + numVisibleRows); | |||
| else | |||
| selectRow (jmin (totalItems - 1, jmax (0, lastRowSelected) + numVisibleRows)); | |||
| } | |||
| else if (key.isKeyCode (KeyPress::homeKey)) | |||
| { | |||
| if (multiple && key.getModifiers().isShiftDown()) | |||
| selectRangeOfRows (lastRowSelected, 0); | |||
| else | |||
| selectRow (0); | |||
| } | |||
| else if (key.isKeyCode (KeyPress::endKey)) | |||
| { | |||
| if (multiple && key.getModifiers().isShiftDown()) | |||
| selectRangeOfRows (lastRowSelected, totalItems - 1); | |||
| else | |||
| selectRow (totalItems - 1); | |||
| } | |||
| else if (key.isKeyCode (KeyPress::downKey)) | |||
| { | |||
| if (multiple) | |||
| selectRangeOfRows (lastRowSelected, lastRowSelected + 1); | |||
| else | |||
| selectRow (jmin (totalItems - 1, jmax (0, lastRowSelected) + 1)); | |||
| } | |||
| else if ((key.isKeyCode (KeyPress::deleteKey) || key.isKeyCode (KeyPress::backspaceKey)) | |||
| && isRowSelected (lastRowSelected)) | |||
| { | |||
| if (model != 0) | |||
| model->deleteKeyPressed (lastRowSelected); | |||
| } | |||
| else if (multiple && key == KeyPress (T('a'), ModifierKeys::commandModifier, 0)) | |||
| { | |||
| selectRangeOfRows (0, INT_MAX); | |||
| } | |||
| else | |||
| { | |||
| return false; | |||
| } | |||
| return true; | |||
| } | |||
| bool ListBox::keyStateChanged (const bool isKeyDown) | |||
| { | |||
| return isKeyDown | |||
| && (KeyPress::isKeyCurrentlyDown (KeyPress::upKey) | |||
| || KeyPress::isKeyCurrentlyDown (KeyPress::pageUpKey) | |||
| || KeyPress::isKeyCurrentlyDown (KeyPress::downKey) | |||
| || KeyPress::isKeyCurrentlyDown (KeyPress::pageDownKey) | |||
| || KeyPress::isKeyCurrentlyDown (KeyPress::homeKey) | |||
| || KeyPress::isKeyCurrentlyDown (KeyPress::endKey) | |||
| || KeyPress::isKeyCurrentlyDown (KeyPress::returnKey)); | |||
| } | |||
| void ListBox::mouseWheelMove (const MouseEvent& e, float wheelIncrementX, float wheelIncrementY) | |||
| { | |||
| getHorizontalScrollBar()->mouseWheelMove (e, wheelIncrementX, 0); | |||
| getVerticalScrollBar()->mouseWheelMove (e, 0, wheelIncrementY); | |||
| } | |||
| void ListBox::mouseMove (const MouseEvent& e) | |||
| { | |||
| if (mouseMoveSelects) | |||
| { | |||
| const MouseEvent e2 (e.getEventRelativeTo (this)); | |||
| selectRow (getRowContainingPosition (e2.x, e2.y), true); | |||
| lastMouseX = e2.x; | |||
| lastMouseY = e2.y; | |||
| } | |||
| } | |||
| void ListBox::mouseExit (const MouseEvent& e) | |||
| { | |||
| mouseMove (e); | |||
| } | |||
| void ListBox::mouseUp (const MouseEvent& e) | |||
| { | |||
| if (e.mouseWasClicked() && model != 0) | |||
| model->backgroundClicked(); | |||
| } | |||
| //============================================================================== | |||
| void ListBox::setRowHeight (const int newHeight) | |||
| { | |||
| rowHeight = jmax (1, newHeight); | |||
| viewport->setSingleStepSizes (20, rowHeight); | |||
| updateContent(); | |||
| } | |||
| int ListBox::getNumRowsOnScreen() const throw() | |||
| { | |||
| return viewport->getMaximumVisibleHeight() / rowHeight; | |||
| } | |||
| void ListBox::setMinimumContentWidth (const int newMinimumWidth) | |||
| { | |||
| minimumRowWidth = newMinimumWidth; | |||
| updateContent(); | |||
| } | |||
| int ListBox::getVisibleContentWidth() const throw() | |||
| { | |||
| return viewport->getMaximumVisibleWidth(); | |||
| } | |||
| ScrollBar* ListBox::getVerticalScrollBar() const throw() | |||
| { | |||
| return viewport->getVerticalScrollBar(); | |||
| } | |||
| ScrollBar* ListBox::getHorizontalScrollBar() const throw() | |||
| { | |||
| return viewport->getHorizontalScrollBar(); | |||
| } | |||
| void ListBox::colourChanged() | |||
| { | |||
| setOpaque (findColour (backgroundColourId).isOpaque()); | |||
| viewport->setOpaque (isOpaque()); | |||
| repaint(); | |||
| } | |||
| void ListBox::setOutlineThickness (const int outlineThickness_) | |||
| { | |||
| outlineThickness = outlineThickness_; | |||
| resized(); | |||
| } | |||
| void ListBox::setHeaderComponent (Component* const newHeaderComponent) | |||
| { | |||
| if (headerComponent != newHeaderComponent) | |||
| { | |||
| if (headerComponent != 0) | |||
| delete headerComponent; | |||
| headerComponent = newHeaderComponent; | |||
| addAndMakeVisible (newHeaderComponent); | |||
| ListBox::resized(); | |||
| } | |||
| } | |||
| void ListBox::repaintRow (const int rowNumber) throw() | |||
| { | |||
| const Rectangle r (getRowPosition (rowNumber, true)); | |||
| repaint (r.getX(), r.getY(), r.getWidth(), r.getHeight()); | |||
| } | |||
| Image* ListBox::createSnapshotOfSelectedRows() | |||
| { | |||
| Image* snapshot = new Image (Image::ARGB, getWidth(), getHeight(), true); | |||
| Graphics g (*snapshot); | |||
| const int firstRow = getRowContainingPosition (0, 0); | |||
| for (int i = getNumRowsOnScreen() + 2; --i >= 0;) | |||
| { | |||
| Component* rowComp = viewport->getComponentForRowIfOnscreen (firstRow + i); | |||
| if (rowComp != 0 && isRowSelected (firstRow + i)) | |||
| { | |||
| g.saveState(); | |||
| int x = 0, y = 0; | |||
| rowComp->relativePositionToOtherComponent (this, x, y); | |||
| g.setOrigin (x, y); | |||
| g.reduceClipRegion (0, 0, rowComp->getWidth(), rowComp->getHeight()); | |||
| rowComp->paintEntireComponent (g); | |||
| g.restoreState(); | |||
| } | |||
| } | |||
| return snapshot; | |||
| } | |||
| //============================================================================== | |||
| Component* ListBoxModel::refreshComponentForRow (int, bool, Component* existingComponentToUpdate) | |||
| { | |||
| (void) existingComponentToUpdate; | |||
| jassert (existingComponentToUpdate == 0); // indicates a failure in the code the recycles the components | |||
| return 0; | |||
| } | |||
| void ListBoxModel::listBoxItemClicked (int, const MouseEvent&) | |||
| { | |||
| } | |||
| void ListBoxModel::listBoxItemDoubleClicked (int, const MouseEvent&) | |||
| { | |||
| } | |||
| void ListBoxModel::backgroundClicked() | |||
| { | |||
| } | |||
| void ListBoxModel::selectedRowsChanged (int) | |||
| { | |||
| } | |||
| void ListBoxModel::deleteKeyPressed (int) | |||
| { | |||
| } | |||
| void ListBoxModel::returnKeyPressed (int) | |||
| { | |||
| } | |||
| void ListBoxModel::listWasScrolled() | |||
| { | |||
| } | |||
| const String ListBoxModel::getDragSourceDescription (const SparseSet<int>&) | |||
| { | |||
| return String::empty; | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,582 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_LISTBOX_JUCEHEADER__ | |||
| #define __JUCE_LISTBOX_JUCEHEADER__ | |||
| #include "../layout/juce_Viewport.h" | |||
| #include "../../../containers/juce_SparseSet.h" | |||
| class ListViewport; | |||
| //============================================================================== | |||
| /** | |||
| A subclass of this is used to drive a ListBox. | |||
| @see ListBox | |||
| */ | |||
| class JUCE_API ListBoxModel | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Destructor. */ | |||
| virtual ~ListBoxModel() {} | |||
| //============================================================================== | |||
| /** This has to return the number of items in the list. | |||
| @see ListBox::getNumRows() | |||
| */ | |||
| virtual int getNumRows() = 0; | |||
| /** This method must be implemented to draw a row of the list. | |||
| */ | |||
| virtual void paintListBoxItem (int rowNumber, | |||
| Graphics& g, | |||
| int width, int height, | |||
| bool rowIsSelected) = 0; | |||
| /** This is used to create or update a custom component to go in a row of the list. | |||
| Any row may contain a custom component, or can just be drawn with the paintListBoxItem() method | |||
| and handle mouse clicks with listBoxItemClicked(). | |||
| This method will be called whenever a custom component might need to be updated - e.g. | |||
| when the table is changed, or TableListBox::updateContent() is called. | |||
| If you don't need a custom component for the specified row, then return 0. | |||
| If you do want a custom component, and the existingComponentToUpdate is null, then | |||
| this method must create a suitable new component and return it. | |||
| If the existingComponentToUpdate is non-null, it will be a pointer to a component previously created | |||
| by this method. In this case, the method must either update it to make sure it's correctly representing | |||
| the given row (which may be different from the one that the component was created for), or it can | |||
| delete this component and return a new one. | |||
| The component that your method returns will be deleted by the ListBox when it is no longer needed. | |||
| */ | |||
| virtual Component* refreshComponentForRow (int rowNumber, bool isRowSelected, | |||
| Component* existingComponentToUpdate); | |||
| /** This can be overridden to react to the user clicking on a row. | |||
| @see listBoxItemDoubleClicked | |||
| */ | |||
| virtual void listBoxItemClicked (int row, const MouseEvent& e); | |||
| /** This can be overridden to react to the user double-clicking on a row. | |||
| @see listBoxItemClicked | |||
| */ | |||
| virtual void listBoxItemDoubleClicked (int row, const MouseEvent& e); | |||
| /** This can be overridden to react to the user double-clicking on a part of the list where | |||
| there are no rows. | |||
| @see listBoxItemClicked | |||
| */ | |||
| virtual void backgroundClicked(); | |||
| /** Override this to be informed when rows are selected or deselected. | |||
| This will be called whenever a row is selected or deselected. If a range of | |||
| rows is selected all at once, this will just be called once for that event. | |||
| @param lastRowSelected the last row that the user selected. If no | |||
| rows are currently selected, this may be -1. | |||
| */ | |||
| virtual void selectedRowsChanged (int lastRowSelected); | |||
| /** Override this to be informed when the delete key is pressed. | |||
| If no rows are selected when they press the key, this won't be called. | |||
| @param lastRowSelected the last row that had been selected when they pressed the | |||
| key - if there are multiple selections, this might not be | |||
| very useful | |||
| */ | |||
| virtual void deleteKeyPressed (int lastRowSelected); | |||
| /** Override this to be informed when the return key is pressed. | |||
| If no rows are selected when they press the key, this won't be called. | |||
| @param lastRowSelected the last row that had been selected when they pressed the | |||
| key - if there are multiple selections, this might not be | |||
| very useful | |||
| */ | |||
| virtual void returnKeyPressed (int lastRowSelected); | |||
| /** Override this to be informed when the list is scrolled. | |||
| This might be caused by the user moving the scrollbar, or by programmatic changes | |||
| to the list position. | |||
| */ | |||
| virtual void listWasScrolled(); | |||
| /** To allow rows from your list to be dragged-and-dropped, implement this method. | |||
| If this returns a non-empty name then when the user drags a row, the listbox will | |||
| try to find a DragAndDropContainer in its parent hierarchy, and will use it to trigger | |||
| a drag-and-drop operation, using this string as the source description, with the listbox | |||
| itself as the source component. | |||
| @see DragAndDropContainer::startDragging | |||
| */ | |||
| virtual const String getDragSourceDescription (const SparseSet<int>& currentlySelectedRows); | |||
| }; | |||
| //============================================================================== | |||
| /** | |||
| A list of items that can be scrolled vertically. | |||
| To create a list, you'll need to create a subclass of ListBoxModel. This can | |||
| either paint each row of the list and respond to events via callbacks, or for | |||
| more specialised tasks, it can supply a custom component to fill each row. | |||
| @see ComboBox, TableListBox | |||
| */ | |||
| class JUCE_API ListBox : public Component, | |||
| public SettableTooltipClient | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Creates a ListBox. | |||
| The model pointer passed-in can be null, in which case you can set it later | |||
| with setModel(). | |||
| */ | |||
| ListBox (const String& componentName, | |||
| ListBoxModel* const model); | |||
| /** Destructor. */ | |||
| ~ListBox(); | |||
| //============================================================================== | |||
| /** Changes the current data model to display. */ | |||
| void setModel (ListBoxModel* const newModel); | |||
| /** Returns the current list model. */ | |||
| ListBoxModel* getModel() const throw() { return model; } | |||
| //============================================================================== | |||
| /** Causes the list to refresh its content. | |||
| Call this when the number of rows in the list changes, or if you want it | |||
| to call refreshComponentForRow() on all the row components. | |||
| Be careful not to call it from a different thread, though, as it's not | |||
| thread-safe. | |||
| */ | |||
| void updateContent(); | |||
| //============================================================================== | |||
| /** Turns on multiple-selection of rows. | |||
| By default this is disabled. | |||
| When your row component gets clicked you'll need to call the | |||
| selectRowsBasedOnModifierKeys() method to tell the list that it's been | |||
| clicked and to get it to do the appropriate selection based on whether | |||
| the ctrl/shift keys are held down. | |||
| */ | |||
| void setMultipleSelectionEnabled (bool shouldBeEnabled); | |||
| /** Makes the list react to mouse moves by selecting the row that the mouse if over. | |||
| This function is here primarily for the ComboBox class to use, but might be | |||
| useful for some other purpose too. | |||
| */ | |||
| void setMouseMoveSelectsRows (bool shouldSelect); | |||
| //============================================================================== | |||
| /** Selects a row. | |||
| If the row is already selected, this won't do anything. | |||
| @param rowNumber the row to select | |||
| @param dontScrollToShowThisRow if true, the list's position won't change; if false and | |||
| the selected row is off-screen, it'll scroll to make | |||
| sure that row is on-screen | |||
| @param deselectOthersFirst if true and there are multiple selections, these will | |||
| first be deselected before this item is selected | |||
| @see isRowSelected, selectRowsBasedOnModifierKeys, flipRowSelection, deselectRow, | |||
| deselectAllRows, selectRangeOfRows | |||
| */ | |||
| void selectRow (const int rowNumber, | |||
| bool dontScrollToShowThisRow = false, | |||
| bool deselectOthersFirst = true); | |||
| /** Selects a set of rows. | |||
| This will add these rows to the current selection, so you might need to | |||
| clear the current selection first with deselectAllRows() | |||
| @param firstRow the first row to select (inclusive) | |||
| @param lastRow the last row to select (inclusive) | |||
| */ | |||
| void selectRangeOfRows (int firstRow, | |||
| int lastRow); | |||
| /** Deselects a row. | |||
| If it's not currently selected, this will do nothing. | |||
| @see selectRow, deselectAllRows | |||
| */ | |||
| void deselectRow (const int rowNumber); | |||
| /** Deselects any currently selected rows. | |||
| @see deselectRow | |||
| */ | |||
| void deselectAllRows(); | |||
| /** Selects or deselects a row. | |||
| If the row's currently selected, this deselects it, and vice-versa. | |||
| */ | |||
| void flipRowSelection (const int rowNumber); | |||
| /** Returns a sparse set indicating the rows that are currently selected. | |||
| @see setSelectedRows | |||
| */ | |||
| const SparseSet<int> getSelectedRows() const; | |||
| /** Sets the rows that should be selected, based on an explicit set of ranges. | |||
| If sendNotificationEventToModel is true, the ListBoxModel::selectedRowsChanged() | |||
| method will be called. If it's false, no notification will be sent to the model. | |||
| @see getSelectedRows | |||
| */ | |||
| void setSelectedRows (const SparseSet<int>& setOfRowsToBeSelected, | |||
| const bool sendNotificationEventToModel = true); | |||
| /** Checks whether a row is selected. | |||
| */ | |||
| bool isRowSelected (const int rowNumber) const; | |||
| /** Returns the number of rows that are currently selected. | |||
| @see getSelectedRow, isRowSelected, getLastRowSelected | |||
| */ | |||
| int getNumSelectedRows() const; | |||
| /** Returns the row number of a selected row. | |||
| This will return the row number of the Nth selected row. The row numbers returned will | |||
| be sorted in order from low to high. | |||
| @param index the index of the selected row to return, (from 0 to getNumSelectedRows() - 1) | |||
| @returns the row number, or -1 if the index was out of range or if there aren't any rows | |||
| selected | |||
| @see getNumSelectedRows, isRowSelected, getLastRowSelected | |||
| */ | |||
| int getSelectedRow (const int index = 0) const; | |||
| /** Returns the last row that the user selected. | |||
| This isn't the same as the highest row number that is currently selected - if the user | |||
| had multiply-selected rows 10, 5 and then 6 in that order, this would return 6. | |||
| If nothing is selected, it will return -1. | |||
| */ | |||
| int getLastRowSelected() const; | |||
| /** Multiply-selects rows based on the modifier keys. | |||
| If no modifier keys are down, this will select the given row and | |||
| deselect any others. | |||
| If the ctrl (or command on the Mac) key is down, it'll flip the | |||
| state of the selected row. | |||
| If the shift key is down, it'll select up to the given row from the | |||
| last row selected. | |||
| @see selectRow | |||
| */ | |||
| void selectRowsBasedOnModifierKeys (const int rowThatWasClickedOn, | |||
| const ModifierKeys& modifiers); | |||
| //============================================================================== | |||
| /** Scrolls the list to a particular position. | |||
| The proportion is between 0 and 1.0, so 0 scrolls to the top of the list, | |||
| 1.0 scrolls to the bottom. | |||
| If the total number of rows all fit onto the screen at once, then this | |||
| method won't do anything. | |||
| @see getVerticalPosition | |||
| */ | |||
| void setVerticalPosition (const double newProportion); | |||
| /** Returns the current vertical position as a proportion of the total. | |||
| This can be used in conjunction with setVerticalPosition() to save and restore | |||
| the list's position. It returns a value in the range 0 to 1. | |||
| @see setVerticalPosition | |||
| */ | |||
| double getVerticalPosition() const; | |||
| /** Scrolls if necessary to make sure that a particular row is visible. | |||
| */ | |||
| void scrollToEnsureRowIsOnscreen (const int row); | |||
| /** Returns a pointer to the scrollbar. | |||
| (Unlikely to be useful for most people). | |||
| */ | |||
| ScrollBar* getVerticalScrollBar() const throw(); | |||
| /** Returns a pointer to the scrollbar. | |||
| (Unlikely to be useful for most people). | |||
| */ | |||
| ScrollBar* getHorizontalScrollBar() const throw(); | |||
| /** Finds the row index that contains a given x,y position. | |||
| The position is relative to the ListBox's top-left. | |||
| If no row exists at this position, the method will return -1. | |||
| @see getComponentForRowNumber | |||
| */ | |||
| int getRowContainingPosition (const int x, const int y) const throw(); | |||
| /** Finds a row index that would be the most suitable place to insert a new | |||
| item for a given position. | |||
| This is useful when the user is e.g. dragging and dropping onto the listbox, | |||
| because it lets you easily choose the best position to insert the item that | |||
| they drop, based on where they drop it. | |||
| If the position is out of range, this will return -1. If the position is | |||
| beyond the end of the list, it will return getNumRows() to indicate the end | |||
| of the list. | |||
| @see getComponentForRowNumber | |||
| */ | |||
| int getInsertionIndexForPosition (const int x, const int y) const throw(); | |||
| /** Returns the position of one of the rows, relative to the top-left of | |||
| the listbox. | |||
| This may be off-screen, and the range of the row number that is passed-in is | |||
| not checked to see if it's a valid row. | |||
| */ | |||
| const Rectangle getRowPosition (const int rowNumber, | |||
| const bool relativeToComponentTopLeft) const throw(); | |||
| /** Finds the row component for a given row in the list. | |||
| The component returned will have been created using createRowComponent(). | |||
| If the component for this row is off-screen or if the row is out-of-range, | |||
| this will return 0. | |||
| @see getRowContainingPosition | |||
| */ | |||
| Component* getComponentForRowNumber (const int rowNumber) const throw(); | |||
| /** Returns the row number that the given component represents. | |||
| If the component isn't one of the list's rows, this will return -1. | |||
| */ | |||
| int getRowNumberOfComponent (Component* const rowComponent) const throw(); | |||
| /** Returns the width of a row (which may be less than the width of this component | |||
| if there's a scrollbar). | |||
| */ | |||
| int getVisibleRowWidth() const throw(); | |||
| //============================================================================== | |||
| /** Sets the height of each row in the list. | |||
| The default height is 22 pixels. | |||
| @see getRowHeight | |||
| */ | |||
| void setRowHeight (const int newHeight); | |||
| /** Returns the height of a row in the list. | |||
| @see setRowHeight | |||
| */ | |||
| int getRowHeight() const throw() { return rowHeight; } | |||
| /** Returns the number of rows actually visible. | |||
| This is the number of whole rows which will fit on-screen, so the value might | |||
| be more than the actual number of rows in the list. | |||
| */ | |||
| int getNumRowsOnScreen() const throw(); | |||
| //============================================================================== | |||
| /** A set of colour IDs to use to change the colour of various aspects of the label. | |||
| These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() | |||
| methods. | |||
| @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour | |||
| */ | |||
| enum ColourIds | |||
| { | |||
| backgroundColourId = 0x1002800, /**< The background colour to fill the list with. | |||
| Make this transparent if you don't want the background to be filled. */ | |||
| outlineColourId = 0x1002810, /**< An optional colour to use to draw a border around the list. | |||
| Make this transparent to not have an outline. */ | |||
| textColourId = 0x1002820 /**< The preferred colour to use for drawing text in the listbox. */ | |||
| }; | |||
| /** Sets the thickness of a border that will be drawn around the box. | |||
| To set the colour of the outline, use @code setColour (ListBox::outlineColourId, colourXYZ); @endcode | |||
| @see outlineColourId | |||
| */ | |||
| void setOutlineThickness (const int outlineThickness); | |||
| /** Returns the thickness of outline that will be drawn around the listbox. | |||
| @see setOutlineColour | |||
| */ | |||
| int getOutlineThickness() const throw() { return outlineThickness; } | |||
| /** Sets a component that the list should use as a header. | |||
| This will position the given component at the top of the list, maintaining the | |||
| height of the component passed-in, but rescaling it horizontally to match the | |||
| width of the items in the listbox. | |||
| The component will be deleted when setHeaderComponent() is called with a | |||
| different component, or when the listbox is deleted. | |||
| */ | |||
| void setHeaderComponent (Component* const newHeaderComponent); | |||
| /** Changes the width of the rows in the list. | |||
| This can be used to make the list's row components wider than the list itself - the | |||
| width of the rows will be either the width of the list or this value, whichever is | |||
| greater, and if the rows become wider than the list, a horizontal scrollbar will | |||
| appear. | |||
| The default value for this is 0, which means that the rows will always | |||
| be the same width as the list. | |||
| */ | |||
| void setMinimumContentWidth (const int newMinimumWidth); | |||
| /** Returns the space currently available for the row items, taking into account | |||
| borders, scrollbars, etc. | |||
| */ | |||
| int getVisibleContentWidth() const throw(); | |||
| /** Repaints one of the rows. | |||
| This is a lightweight alternative to calling updateContent, and just causes a | |||
| repaint of the row's area. | |||
| */ | |||
| void repaintRow (const int rowNumber) throw(); | |||
| /** This fairly obscure method creates an image that just shows the currently | |||
| selected row components. | |||
| It's a handy method for doing drag-and-drop, as it can be passed to the | |||
| DragAndDropContainer for use as the drag image. | |||
| Note that it will make the row components temporarily invisible, so if you're | |||
| using custom components this could affect them if they're sensitive to that | |||
| sort of thing. | |||
| @see Component::createComponentSnapshot | |||
| */ | |||
| Image* createSnapshotOfSelectedRows(); | |||
| /** Returns the viewport that this ListBox uses. | |||
| You may need to use this to change parameters such as whether scrollbars | |||
| are shown, etc. | |||
| */ | |||
| Viewport* getViewport() const throw(); | |||
| //============================================================================== | |||
| /** @internal */ | |||
| bool keyPressed (const KeyPress& key); | |||
| /** @internal */ | |||
| bool keyStateChanged (const bool isKeyDown); | |||
| /** @internal */ | |||
| void paint (Graphics& g); | |||
| /** @internal */ | |||
| void paintOverChildren (Graphics& g); | |||
| /** @internal */ | |||
| void resized(); | |||
| /** @internal */ | |||
| void visibilityChanged(); | |||
| /** @internal */ | |||
| void mouseWheelMove (const MouseEvent& e, float wheelIncrementX, float wheelIncrementY); | |||
| /** @internal */ | |||
| void mouseMove (const MouseEvent&); | |||
| /** @internal */ | |||
| void mouseExit (const MouseEvent&); | |||
| /** @internal */ | |||
| void mouseUp (const MouseEvent&); | |||
| /** @internal */ | |||
| void colourChanged(); | |||
| juce_UseDebuggingNewOperator | |||
| private: | |||
| //============================================================================== | |||
| friend class ListViewport; | |||
| friend class TableListBox; | |||
| ListBoxModel* model; | |||
| ListViewport* viewport; | |||
| Component* headerComponent; | |||
| int totalItems, rowHeight, minimumRowWidth; | |||
| int outlineThickness; | |||
| int lastMouseX, lastMouseY, lastRowSelected; | |||
| bool mouseMoveSelects, multipleSelection, hasDoneInitialUpdate; | |||
| SparseSet <int> selected; | |||
| void selectRowInternal (const int rowNumber, | |||
| bool dontScrollToShowThisRow, | |||
| bool deselectOthersFirst, | |||
| bool isMouseClick); | |||
| ListBox (const ListBox&); | |||
| const ListBox& operator= (const ListBox&); | |||
| }; | |||
| #endif // __JUCE_LISTBOX_JUCEHEADER__ | |||
| @@ -0,0 +1,123 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_ProgressBar.h" | |||
| #include "../lookandfeel/juce_LookAndFeel.h" | |||
| //============================================================================== | |||
| ProgressBar::ProgressBar (double& progress_) | |||
| : progress (progress_), | |||
| displayPercentage (true), | |||
| lastCallbackTime (0) | |||
| { | |||
| currentValue = jlimit (0.0, 1.0, progress); | |||
| } | |||
| ProgressBar::~ProgressBar() | |||
| { | |||
| } | |||
| //============================================================================== | |||
| void ProgressBar::setPercentageDisplay (const bool shouldDisplayPercentage) | |||
| { | |||
| displayPercentage = shouldDisplayPercentage; | |||
| repaint(); | |||
| } | |||
| void ProgressBar::setTextToDisplay (const String& text) | |||
| { | |||
| displayPercentage = false; | |||
| displayedMessage = text; | |||
| } | |||
| void ProgressBar::lookAndFeelChanged() | |||
| { | |||
| setOpaque (findColour (backgroundColourId).isOpaque()); | |||
| } | |||
| void ProgressBar::colourChanged() | |||
| { | |||
| lookAndFeelChanged(); | |||
| } | |||
| void ProgressBar::paint (Graphics& g) | |||
| { | |||
| String text; | |||
| if (displayPercentage) | |||
| { | |||
| if (currentValue >= 0 && currentValue <= 1.0) | |||
| text << roundDoubleToInt (currentValue * 100.0) << T("%"); | |||
| } | |||
| else | |||
| { | |||
| text = displayedMessage; | |||
| } | |||
| getLookAndFeel().drawProgressBar (g, *this, | |||
| getWidth(), getHeight(), | |||
| currentValue, text); | |||
| } | |||
| void ProgressBar::visibilityChanged() | |||
| { | |||
| if (isVisible()) | |||
| startTimer (30); | |||
| else | |||
| stopTimer(); | |||
| } | |||
| void ProgressBar::timerCallback() | |||
| { | |||
| double newProgress = progress; | |||
| if (currentValue != newProgress | |||
| || newProgress < 0 || newProgress >= 1.0 | |||
| || currentMessage != displayedMessage) | |||
| { | |||
| if (currentValue < newProgress | |||
| && newProgress >= 0 && newProgress < 1.0 | |||
| && currentValue >= 0 && currentValue < 1.0) | |||
| { | |||
| const uint32 now = Time::getMillisecondCounter(); | |||
| const int timeSinceLastCallback = (int) (now - lastCallbackTime); | |||
| lastCallbackTime = now; | |||
| newProgress = jmin (currentValue + 0.00018 * timeSinceLastCallback, | |||
| newProgress); | |||
| } | |||
| currentValue = newProgress; | |||
| currentMessage = displayedMessage; | |||
| repaint(); | |||
| } | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,745 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-9 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_SLIDER_JUCEHEADER__ | |||
| #define __JUCE_SLIDER_JUCEHEADER__ | |||
| #include "juce_SliderListener.h" | |||
| #include "juce_Label.h" | |||
| #include "../buttons/juce_Button.h" | |||
| #include "../../../events/juce_AsyncUpdater.h" | |||
| #include "../../../containers/juce_SortedSet.h" | |||
| //============================================================================== | |||
| /** | |||
| A slider control for changing a value. | |||
| The slider can be horizontal, vertical, or rotary, and can optionally have | |||
| a text-box inside it to show an editable display of the current value. | |||
| To use it, create a Slider object and use the setSliderStyle() method | |||
| to set up the type you want. To set up the text-entry box, use setTextBoxStyle(). | |||
| To define the values that it can be set to, see the setRange() and setValue() methods. | |||
| There are also lots of custom tweaks you can do by subclassing and overriding | |||
| some of the virtual methods, such as changing the scaling, changing the format of | |||
| the text display, custom ways of limiting the values, etc. | |||
| You can register SliderListeners with a slider, which will be informed when the value | |||
| changes, or a subclass can override valueChanged() to be informed synchronously. | |||
| @see SliderListener | |||
| */ | |||
| class JUCE_API Slider : public Component, | |||
| public SettableTooltipClient, | |||
| private AsyncUpdater, | |||
| private ButtonListener, | |||
| private LabelListener | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Creates a slider. | |||
| When created, you'll need to set up the slider's style and range with setSliderStyle(), | |||
| setRange(), etc. | |||
| */ | |||
| Slider (const String& componentName); | |||
| /** Destructor. */ | |||
| ~Slider(); | |||
| //============================================================================== | |||
| /** The types of slider available. | |||
| @see setSliderStyle, setRotaryParameters | |||
| */ | |||
| enum SliderStyle | |||
| { | |||
| LinearHorizontal, /**< A traditional horizontal slider. */ | |||
| LinearVertical, /**< A traditional vertical slider. */ | |||
| LinearBar, /**< A horizontal bar slider with the text label drawn on top of it. */ | |||
| Rotary, /**< A rotary control that you move by dragging the mouse in a circular motion, like a knob. | |||
| @see setRotaryParameters */ | |||
| RotaryHorizontalDrag, /**< A rotary control that you move by dragging the mouse left-to-right. | |||
| @see setRotaryParameters */ | |||
| RotaryVerticalDrag, /**< A rotary control that you move by dragging the mouse up-and-down. | |||
| @see setRotaryParameters */ | |||
| IncDecButtons, /**< A pair of buttons that increment or decrement the slider's value by the increment set in setRange(). */ | |||
| TwoValueHorizontal, /**< A horizontal slider that has two thumbs instead of one, so it can show a minimum and maximum value. | |||
| @see setMinValue, setMaxValue */ | |||
| TwoValueVertical, /**< A vertical slider that has two thumbs instead of one, so it can show a minimum and maximum value. | |||
| @see setMinValue, setMaxValue */ | |||
| ThreeValueHorizontal, /**< A horizontal slider that has three thumbs instead of one, so it can show a minimum and maximum | |||
| value, with the current value being somewhere between them. | |||
| @see setMinValue, setMaxValue */ | |||
| ThreeValueVertical, /**< A vertical slider that has three thumbs instead of one, so it can show a minimum and maximum | |||
| value, with the current value being somewhere between them. | |||
| @see setMinValue, setMaxValue */ | |||
| }; | |||
| /** Changes the type of slider interface being used. | |||
| @param newStyle the type of interface | |||
| @see setRotaryParameters, setVelocityBasedMode, | |||
| */ | |||
| void setSliderStyle (const SliderStyle newStyle); | |||
| /** Returns the slider's current style. | |||
| @see setSliderStyle | |||
| */ | |||
| SliderStyle getSliderStyle() const throw() { return style; } | |||
| //============================================================================== | |||
| /** Changes the properties of a rotary slider. | |||
| @param startAngleRadians the angle (in radians, clockwise from the top) at which | |||
| the slider's minimum value is represented | |||
| @param endAngleRadians the angle (in radians, clockwise from the top) at which | |||
| the slider's maximum value is represented. This must be | |||
| greater than startAngleRadians | |||
| @param stopAtEnd if true, then when the slider is dragged around past the | |||
| minimum or maximum, it'll stop there; if false, it'll wrap | |||
| back to the opposite value | |||
| */ | |||
| void setRotaryParameters (const float startAngleRadians, | |||
| const float endAngleRadians, | |||
| const bool stopAtEnd); | |||
| /** Sets the distance the mouse has to move to drag the slider across | |||
| the full extent of its range. | |||
| This only applies when in modes like RotaryHorizontalDrag, where it's using | |||
| relative mouse movements to adjust the slider. | |||
| */ | |||
| void setMouseDragSensitivity (const int distanceForFullScaleDrag); | |||
| //============================================================================== | |||
| /** Changes the way the the mouse is used when dragging the slider. | |||
| If true, this will turn on velocity-sensitive dragging, so that | |||
| the faster the mouse moves, the bigger the movement to the slider. This | |||
| helps when making accurate adjustments if the slider's range is quite large. | |||
| If false, the slider will just try to snap to wherever the mouse is. | |||
| */ | |||
| void setVelocityBasedMode (const bool isVelocityBased) throw(); | |||
| /** Changes aspects of the scaling used when in velocity-sensitive mode. | |||
| These apply when you've used setVelocityBasedMode() to turn on velocity mode, | |||
| or if you're holding down ctrl. | |||
| @param sensitivity higher values than 1.0 increase the range of acceleration used | |||
| @param threshold the minimum number of pixels that the mouse needs to move for it | |||
| to be treated as a movement | |||
| @param offset values greater than 0.0 increase the minimum speed that will be used when | |||
| the threshold is reached | |||
| @param userCanPressKeyToSwapMode if true, then the user can hold down the ctrl or command | |||
| key to toggle velocity-sensitive mode | |||
| */ | |||
| void setVelocityModeParameters (const double sensitivity = 1.0, | |||
| const int threshold = 1, | |||
| const double offset = 0.0, | |||
| const bool userCanPressKeyToSwapMode = true) throw(); | |||
| //============================================================================== | |||
| /** Sets up a skew factor to alter the way values are distributed. | |||
| You may want to use a range of values on the slider where more accuracy | |||
| is required towards one end of the range, so this will logarithmically | |||
| spread the values across the length of the slider. | |||
| If the factor is < 1.0, the lower end of the range will fill more of the | |||
| slider's length; if the factor is > 1.0, the upper end of the range | |||
| will be expanded instead. A factor of 1.0 doesn't skew it at all. | |||
| To set the skew position by using a mid-point, use the setSkewFactorFromMidPoint() | |||
| method instead. | |||
| @see getSkewFactor, setSkewFactorFromMidPoint | |||
| */ | |||
| void setSkewFactor (const double factor) throw(); | |||
| /** Sets up a skew factor to alter the way values are distributed. | |||
| This allows you to specify the slider value that should appear in the | |||
| centre of the slider's visible range. | |||
| @see setSkewFactor, getSkewFactor | |||
| */ | |||
| void setSkewFactorFromMidPoint (const double sliderValueToShowAtMidPoint) throw(); | |||
| /** Returns the current skew factor. | |||
| See setSkewFactor for more info. | |||
| @see setSkewFactor, setSkewFactorFromMidPoint | |||
| */ | |||
| double getSkewFactor() const throw() { return skewFactor; } | |||
| //============================================================================== | |||
| /** Used by setIncDecButtonsMode(). | |||
| */ | |||
| enum IncDecButtonMode | |||
| { | |||
| incDecButtonsNotDraggable, | |||
| incDecButtonsDraggable_AutoDirection, | |||
| incDecButtonsDraggable_Horizontal, | |||
| incDecButtonsDraggable_Vertical | |||
| }; | |||
| /** When the style is IncDecButtons, this lets you turn on a mode where the mouse | |||
| can be dragged on the buttons to drag the values. | |||
| By default this is turned off. When enabled, clicking on the buttons still works | |||
| them as normal, but by holding down the mouse on a button and dragging it a little | |||
| distance, it flips into a mode where the value can be dragged. The drag direction can | |||
| either be set explicitly to be vertical or horizontal, or can be set to | |||
| incDecButtonsDraggable_AutoDirection so that it depends on whether the buttons | |||
| are side-by-side or above each other. | |||
| */ | |||
| void setIncDecButtonsMode (const IncDecButtonMode mode); | |||
| //============================================================================== | |||
| /** The position of the slider's text-entry box. | |||
| @see setTextBoxStyle | |||
| */ | |||
| enum TextEntryBoxPosition | |||
| { | |||
| NoTextBox, /**< Doesn't display a text box. */ | |||
| TextBoxLeft, /**< Puts the text box to the left of the slider, vertically centred. */ | |||
| TextBoxRight, /**< Puts the text box to the right of the slider, vertically centred. */ | |||
| TextBoxAbove, /**< Puts the text box above the slider, horizontally centred. */ | |||
| TextBoxBelow /**< Puts the text box below the slider, horizontally centred. */ | |||
| }; | |||
| /** Changes the location and properties of the text-entry box. | |||
| @param newPosition where it should go (or NoTextBox to not have one at all) | |||
| @param isReadOnly if true, it's a read-only display | |||
| @param textEntryBoxWidth the width of the text-box in pixels. Make sure this leaves enough | |||
| room for the slider as well! | |||
| @param textEntryBoxHeight the height of the text-box in pixels. Make sure this leaves enough | |||
| room for the slider as well! | |||
| @see setTextBoxIsEditable, getValueFromText, getTextFromValue | |||
| */ | |||
| void setTextBoxStyle (const TextEntryBoxPosition newPosition, | |||
| const bool isReadOnly, | |||
| const int textEntryBoxWidth, | |||
| const int textEntryBoxHeight); | |||
| /** Returns the status of the text-box. | |||
| @see setTextBoxStyle | |||
| */ | |||
| const TextEntryBoxPosition getTextBoxPosition() const throw() { return textBoxPos; } | |||
| /** Returns the width used for the text-box. | |||
| @see setTextBoxStyle | |||
| */ | |||
| int getTextBoxWidth() const throw() { return textBoxWidth; } | |||
| /** Returns the height used for the text-box. | |||
| @see setTextBoxStyle | |||
| */ | |||
| int getTextBoxHeight() const throw() { return textBoxHeight; } | |||
| /** Makes the text-box editable. | |||
| By default this is true, and the user can enter values into the textbox, | |||
| but it can be turned off if that's not suitable. | |||
| @see setTextBoxStyle, getValueFromText, getTextFromValue | |||
| */ | |||
| void setTextBoxIsEditable (const bool shouldBeEditable) throw(); | |||
| /** Returns true if the text-box is read-only. | |||
| @see setTextBoxStyle | |||
| */ | |||
| bool isTextBoxEditable() const throw() { return editableText; } | |||
| /** If the text-box is editable, this will give it the focus so that the user can | |||
| type directly into it. | |||
| This is basically the effect as the user clicking on it. | |||
| */ | |||
| void showTextBox(); | |||
| /** If the text-box currently has focus and is being edited, this resets it and takes keyboard | |||
| focus away from it. | |||
| @param discardCurrentEditorContents if true, the slider's value will be left | |||
| unchanged; if false, the current contents of the | |||
| text editor will be used to set the slider position | |||
| before it is hidden. | |||
| */ | |||
| void hideTextBox (const bool discardCurrentEditorContents); | |||
| //============================================================================== | |||
| /** Changes the slider's current value. | |||
| This will trigger a callback to SliderListener::sliderValueChanged() for any listeners | |||
| that are registered, and will synchronously call the valueChanged() method in case subclasses | |||
| want to handle it. | |||
| @param newValue the new value to set - this will be restricted by the | |||
| minimum and maximum range, and will be snapped to the | |||
| nearest interval if one has been set | |||
| @param sendUpdateMessage if false, a change to the value will not trigger a call to | |||
| any SliderListeners or the valueChanged() method | |||
| @param sendMessageSynchronously if true, then a call to the SliderListeners will be made | |||
| synchronously; if false, it will be asynchronous | |||
| */ | |||
| void setValue (double newValue, | |||
| const bool sendUpdateMessage = true, | |||
| const bool sendMessageSynchronously = false); | |||
| /** Returns the slider's current value. */ | |||
| double getValue() const throw(); | |||
| //============================================================================== | |||
| /** Sets the limits that the slider's value can take. | |||
| @param newMinimum the lowest value allowed | |||
| @param newMaximum the highest value allowed | |||
| @param newInterval the steps in which the value is allowed to increase - if this | |||
| is not zero, the value will always be (newMinimum + (newInterval * an integer)). | |||
| */ | |||
| void setRange (const double newMinimum, | |||
| const double newMaximum, | |||
| const double newInterval = 0); | |||
| /** Returns the current maximum value. | |||
| @see setRange | |||
| */ | |||
| double getMaximum() const throw() { return maximum; } | |||
| /** Returns the current minimum value. | |||
| @see setRange | |||
| */ | |||
| double getMinimum() const throw() { return minimum; } | |||
| /** Returns the current step-size for values. | |||
| @see setRange | |||
| */ | |||
| double getInterval() const throw() { return interval; } | |||
| //============================================================================== | |||
| /** For a slider with two or three thumbs, this returns the lower of its values. | |||
| For a two-value slider, the values are controlled with getMinValue() and getMaxValue(). | |||
| A slider with three values also uses the normal getValue() and setValue() methods to | |||
| control the middle value. | |||
| @see setMinValue, getMaxValue, TwoValueHorizontal, TwoValueVertical, ThreeValueHorizontal, ThreeValueVertical | |||
| */ | |||
| double getMinValue() const throw(); | |||
| /** For a slider with two or three thumbs, this sets the lower of its values. | |||
| This will trigger a callback to SliderListener::sliderValueChanged() for any listeners | |||
| that are registered, and will synchronously call the valueChanged() method in case subclasses | |||
| want to handle it. | |||
| @param newValue the new value to set - this will be restricted by the | |||
| minimum and maximum range, and will be snapped to the nearest | |||
| interval if one has been set. | |||
| @param sendUpdateMessage if false, a change to the value will not trigger a call to | |||
| any SliderListeners or the valueChanged() method | |||
| @param sendMessageSynchronously if true, then a call to the SliderListeners will be made | |||
| synchronously; if false, it will be asynchronous | |||
| @param allowNudgingOfOtherValues if false, this value will be restricted to being below the | |||
| max value (in a two-value slider) or the mid value (in a three-value | |||
| slider). If false, then if this value goes beyond those values, | |||
| it will push them along with it. | |||
| @see getMinValue, setMaxValue, setValue | |||
| */ | |||
| void setMinValue (double newValue, | |||
| const bool sendUpdateMessage = true, | |||
| const bool sendMessageSynchronously = false, | |||
| const bool allowNudgingOfOtherValues = false); | |||
| /** For a slider with two or three thumbs, this returns the higher of its values. | |||
| For a two-value slider, the values are controlled with getMinValue() and getMaxValue(). | |||
| A slider with three values also uses the normal getValue() and setValue() methods to | |||
| control the middle value. | |||
| @see getMinValue, TwoValueHorizontal, TwoValueVertical, ThreeValueHorizontal, ThreeValueVertical | |||
| */ | |||
| double getMaxValue() const throw(); | |||
| /** For a slider with two or three thumbs, this sets the lower of its values. | |||
| This will trigger a callback to SliderListener::sliderValueChanged() for any listeners | |||
| that are registered, and will synchronously call the valueChanged() method in case subclasses | |||
| want to handle it. | |||
| @param newValue the new value to set - this will be restricted by the | |||
| minimum and maximum range, and will be snapped to the nearest | |||
| interval if one has been set. | |||
| @param sendUpdateMessage if false, a change to the value will not trigger a call to | |||
| any SliderListeners or the valueChanged() method | |||
| @param sendMessageSynchronously if true, then a call to the SliderListeners will be made | |||
| synchronously; if false, it will be asynchronous | |||
| @param allowNudgingOfOtherValues if false, this value will be restricted to being above the | |||
| min value (in a two-value slider) or the mid value (in a three-value | |||
| slider). If false, then if this value goes beyond those values, | |||
| it will push them along with it. | |||
| @see getMaxValue, setMinValue, setValue | |||
| */ | |||
| void setMaxValue (double newValue, | |||
| const bool sendUpdateMessage = true, | |||
| const bool sendMessageSynchronously = false, | |||
| const bool allowNudgingOfOtherValues = false); | |||
| //============================================================================== | |||
| /** Adds a listener to be called when this slider's value changes. */ | |||
| void addListener (SliderListener* const listener) throw(); | |||
| /** Removes a previously-registered listener. */ | |||
| void removeListener (SliderListener* const listener) throw(); | |||
| //============================================================================== | |||
| /** This lets you choose whether double-clicking moves the slider to a given position. | |||
| By default this is turned off, but it's handy if you want a double-click to act | |||
| as a quick way of resetting a slider. Just pass in the value you want it to | |||
| go to when double-clicked. | |||
| @see getDoubleClickReturnValue | |||
| */ | |||
| void setDoubleClickReturnValue (const bool isDoubleClickEnabled, | |||
| const double valueToSetOnDoubleClick) throw(); | |||
| /** Returns the values last set by setDoubleClickReturnValue() method. | |||
| Sets isEnabled to true if double-click is enabled, and returns the value | |||
| that was set. | |||
| @see setDoubleClickReturnValue | |||
| */ | |||
| double getDoubleClickReturnValue (bool& isEnabled) const throw(); | |||
| //============================================================================== | |||
| /** Tells the slider whether to keep sending change messages while the user | |||
| is dragging the slider. | |||
| If set to true, a change message will only be sent when the user has | |||
| dragged the slider and let go. If set to false (the default), then messages | |||
| will be continuously sent as they drag it while the mouse button is still | |||
| held down. | |||
| */ | |||
| void setChangeNotificationOnlyOnRelease (const bool onlyNotifyOnRelease) throw(); | |||
| /** This lets you change whether the slider thumb jumps to the mouse position | |||
| when you click. | |||
| By default, this is true. If it's false, then the slider moves with relative | |||
| motion when you drag it. | |||
| This only applies to linear bars, and won't affect two- or three- value | |||
| sliders. | |||
| */ | |||
| void setSliderSnapsToMousePosition (const bool shouldSnapToMouse) throw(); | |||
| /** If enabled, this gives the slider a pop-up bubble which appears while the | |||
| slider is being dragged. | |||
| This can be handy if your slider doesn't have a text-box, so that users can | |||
| see the value just when they're changing it. | |||
| If you pass a component as the parentComponentToUse parameter, the pop-up | |||
| bubble will be added as a child of that component when it's needed. If you | |||
| pass 0, the pop-up will be placed on the desktop instead (note that it's a | |||
| transparent window, so if you're using an OS that can't do transparent windows | |||
| you'll have to add it to a parent component instead). | |||
| */ | |||
| void setPopupDisplayEnabled (const bool isEnabled, | |||
| Component* const parentComponentToUse) throw(); | |||
| /** If this is set to true, then right-clicking on the slider will pop-up | |||
| a menu to let the user change the way it works. | |||
| By default this is turned off, but when turned on, the menu will include | |||
| things like velocity sensitivity, and for rotary sliders, whether they | |||
| use a linear or rotary mouse-drag to move them. | |||
| */ | |||
| void setPopupMenuEnabled (const bool menuEnabled) throw(); | |||
| /** This can be used to stop the mouse scroll-wheel from moving the slider. | |||
| By default it's enabled. | |||
| */ | |||
| void setScrollWheelEnabled (const bool enabled) throw(); | |||
| /** Returns a number to indicate which thumb is currently being dragged by the | |||
| mouse. | |||
| This will return 0 for the main thumb, 1 for the minimum-value thumb, 2 for | |||
| the maximum-value thumb, or -1 if none is currently down. | |||
| */ | |||
| int getThumbBeingDragged() const throw() { return sliderBeingDragged; } | |||
| //============================================================================== | |||
| /** Callback to indicate that the user is about to start dragging the slider. | |||
| @see SliderListener::sliderDragStarted | |||
| */ | |||
| virtual void startedDragging(); | |||
| /** Callback to indicate that the user has just stopped dragging the slider. | |||
| @see SliderListener::sliderDragEnded | |||
| */ | |||
| virtual void stoppedDragging(); | |||
| /** Callback to indicate that the user has just moved the slider. | |||
| @see SliderListener::sliderValueChanged | |||
| */ | |||
| virtual void valueChanged(); | |||
| /** Callback to indicate that the user has just moved the slider. | |||
| Note - the valueChanged() method has changed its format and now no longer has | |||
| any parameters. Update your code to use the new version. | |||
| This version has been left here with an int as its return value to cause | |||
| a syntax error if you've got existing code that uses the old version. | |||
| */ | |||
| virtual int valueChanged (double) { jassertfalse; return 0; } | |||
| //============================================================================== | |||
| /** Subclasses can override this to convert a text string to a value. | |||
| When the user enters something into the text-entry box, this method is | |||
| called to convert it to a value. | |||
| The default routine just tries to convert it to a double. | |||
| @see getTextFromValue | |||
| */ | |||
| virtual double getValueFromText (const String& text); | |||
| /** Turns the slider's current value into a text string. | |||
| Subclasses can override this to customise the formatting of the text-entry box. | |||
| The default implementation just turns the value into a string, using | |||
| a number of decimal places based on the range interval. If a suffix string | |||
| has been set using setTextValueSuffix(), this will be appended to the text. | |||
| @see getValueFromText | |||
| */ | |||
| virtual const String getTextFromValue (double value); | |||
| /** Sets a suffix to append to the end of the numeric value when it's displayed as | |||
| a string. | |||
| This is used by the default implementation of getTextFromValue(), and is just | |||
| appended to the numeric value. For more advanced formatting, you can override | |||
| getTextFromValue() and do something else. | |||
| */ | |||
| void setTextValueSuffix (const String& suffix); | |||
| //============================================================================== | |||
| /** Allows a user-defined mapping of distance along the slider to its value. | |||
| The default implementation for this performs the skewing operation that | |||
| can be set up in the setSkewFactor() method. Override it if you need | |||
| some kind of custom mapping instead, but make sure you also implement the | |||
| inverse function in valueToProportionOfLength(). | |||
| @param proportion a value 0 to 1.0, indicating a distance along the slider | |||
| @returns the slider value that is represented by this position | |||
| @see valueToProportionOfLength | |||
| */ | |||
| virtual double proportionOfLengthToValue (double proportion); | |||
| /** Allows a user-defined mapping of value to the position of the slider along its length. | |||
| The default implementation for this performs the skewing operation that | |||
| can be set up in the setSkewFactor() method. Override it if you need | |||
| some kind of custom mapping instead, but make sure you also implement the | |||
| inverse function in proportionOfLengthToValue(). | |||
| @param value a valid slider value, between the range of values specified in | |||
| setRange() | |||
| @returns a value 0 to 1.0 indicating the distance along the slider that | |||
| represents this value | |||
| @see proportionOfLengthToValue | |||
| */ | |||
| virtual double valueToProportionOfLength (double value); | |||
| /** Returns the X or Y coordinate of a value along the slider's length. | |||
| If the slider is horizontal, this will be the X coordinate of the given | |||
| value, relative to the left of the slider. If it's vertical, then this will | |||
| be the Y coordinate, relative to the top of the slider. | |||
| If the slider is rotary, this will throw an assertion and return 0. If the | |||
| value is out-of-range, it will be constrained to the length of the slider. | |||
| */ | |||
| float getPositionOfValue (const double value); | |||
| //============================================================================== | |||
| /** This can be overridden to allow the slider to snap to user-definable values. | |||
| If overridden, it will be called when the user tries to move the slider to | |||
| a given position, and allows a subclass to sanity-check this value, possibly | |||
| returning a different value to use instead. | |||
| @param attemptedValue the value the user is trying to enter | |||
| @param userIsDragging true if the user is dragging with the mouse; false if | |||
| they are entering the value using the text box | |||
| @returns the value to use instead | |||
| */ | |||
| virtual double snapValue (double attemptedValue, const bool userIsDragging); | |||
| //============================================================================== | |||
| /** This can be called to force the text box to update its contents. | |||
| (Not normally needed, as this is done automatically). | |||
| */ | |||
| void updateText(); | |||
| /** True if the slider moves horizontally. */ | |||
| bool isHorizontal() const throw(); | |||
| /** True if the slider moves vertically. */ | |||
| bool isVertical() const throw(); | |||
| //============================================================================== | |||
| /** A set of colour IDs to use to change the colour of various aspects of the slider. | |||
| These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() | |||
| methods. | |||
| @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour | |||
| */ | |||
| enum ColourIds | |||
| { | |||
| backgroundColourId = 0x1001200, /**< A colour to use to fill the slider's background. */ | |||
| thumbColourId = 0x1001300, /**< The colour to draw the thumb with. It's up to the look | |||
| and feel class how this is used. */ | |||
| trackColourId = 0x1001310, /**< The colour to draw the groove that the thumb moves along. */ | |||
| rotarySliderFillColourId = 0x1001311, /**< For rotary sliders, this colour fills the outer curve. */ | |||
| rotarySliderOutlineColourId = 0x1001312, /**< For rotary sliders, this colour is used to draw the outer curve's outline. */ | |||
| textBoxTextColourId = 0x1001400, /**< The colour for the text in the text-editor box used for editing the value. */ | |||
| textBoxBackgroundColourId = 0x1001500, /**< The background colour for the text-editor box. */ | |||
| textBoxHighlightColourId = 0x1001600, /**< The text highlight colour for the text-editor box. */ | |||
| textBoxOutlineColourId = 0x1001700 /**< The colour to use for a border around the text-editor box. */ | |||
| }; | |||
| //============================================================================== | |||
| juce_UseDebuggingNewOperator | |||
| protected: | |||
| /** @internal */ | |||
| void labelTextChanged (Label*); | |||
| /** @internal */ | |||
| void paint (Graphics& g); | |||
| /** @internal */ | |||
| void resized(); | |||
| /** @internal */ | |||
| void mouseDown (const MouseEvent& e); | |||
| /** @internal */ | |||
| void mouseUp (const MouseEvent& e); | |||
| /** @internal */ | |||
| void mouseDrag (const MouseEvent& e); | |||
| /** @internal */ | |||
| void mouseDoubleClick (const MouseEvent& e); | |||
| /** @internal */ | |||
| void mouseWheelMove (const MouseEvent& e, float wheelIncrementX, float wheelIncrementY); | |||
| /** @internal */ | |||
| void modifierKeysChanged (const ModifierKeys& modifiers); | |||
| /** @internal */ | |||
| void buttonClicked (Button* button); | |||
| /** @internal */ | |||
| void lookAndFeelChanged(); | |||
| /** @internal */ | |||
| void enablementChanged(); | |||
| /** @internal */ | |||
| void focusOfChildComponentChanged (FocusChangeType cause); | |||
| /** @internal */ | |||
| void handleAsyncUpdate(); | |||
| /** @internal */ | |||
| void colourChanged(); | |||
| private: | |||
| SortedSet <void*> listeners; | |||
| double currentValue, valueMin, valueMax; | |||
| double minimum, maximum, interval, doubleClickReturnValue; | |||
| double valueWhenLastDragged, valueOnMouseDown, skewFactor, lastAngle; | |||
| double velocityModeSensitivity, velocityModeOffset, minMaxDiff; | |||
| int velocityModeThreshold; | |||
| float rotaryStart, rotaryEnd; | |||
| int numDecimalPlaces, mouseXWhenLastDragged, mouseYWhenLastDragged; | |||
| int mouseDragStartX, mouseDragStartY; | |||
| int sliderRegionStart, sliderRegionSize; | |||
| int sliderBeingDragged; | |||
| int pixelsForFullDragExtent; | |||
| Rectangle sliderRect; | |||
| String textSuffix; | |||
| SliderStyle style; | |||
| TextEntryBoxPosition textBoxPos; | |||
| int textBoxWidth, textBoxHeight; | |||
| IncDecButtonMode incDecButtonMode; | |||
| bool editableText : 1, doubleClickToValue : 1; | |||
| bool isVelocityBased : 1, userKeyOverridesVelocity : 1, rotaryStop : 1; | |||
| bool incDecButtonsSideBySide : 1, sendChangeOnlyOnRelease : 1, popupDisplayEnabled : 1; | |||
| bool menuEnabled : 1, menuShown : 1, mouseWasHidden : 1, incDecDragged : 1; | |||
| bool scrollWheelEnabled : 1, snapsToMousePos : 1; | |||
| Font font; | |||
| Label* valueBox; | |||
| Button* incButton; | |||
| Button* decButton; | |||
| Component* popupDisplay; | |||
| Component* parentForPopupDisplay; | |||
| float getLinearSliderPos (const double value); | |||
| void restoreMouseIfHidden(); | |||
| void sendDragStart(); | |||
| void sendDragEnd(); | |||
| double constrainedValue (double value) const throw(); | |||
| void triggerChangeMessage (const bool synchronous); | |||
| bool incDecDragDirectionIsHorizontal() const throw(); | |||
| Slider (const Slider&); | |||
| const Slider& operator= (const Slider&); | |||
| }; | |||
| #endif // __JUCE_SLIDER_JUCEHEADER__ | |||