Browse Source

Rearranged the layout of the source tree, giving it a slightly flatter directory structure. Also fixed a small bug with DragAndDropComponents that was stopping the toolbar customisation working properly.

tags/2021-05-28
jules 16 years ago
parent
commit
4d16424d9c
100 changed files with 26434 additions and 12446 deletions
  1. +328
    -328
      build/linux/JUCE.make
  2. +86
    -88
      build/linux/juce_premake.lua
  3. +2455
    -2462
      build/macosx/Juce.xcodeproj/project.pbxproj
  4. +2019
    -4928
      build/win32/vc8/JUCE.vcproj
  5. +26
    -26
      extras/juce demo/build/linux/JuceDemo.make
  6. +1
    -1
      juce.h
  7. +613
    -607
      juce_amalgamated.cpp
  8. +4006
    -4006
      juce_amalgamated.h
  9. +348
    -0
      src/application/juce_Application.cpp
  10. +0
    -0
      src/application/juce_Application.h
  11. +0
    -0
      src/application/juce_ApplicationCommandID.h
  12. +73
    -0
      src/application/juce_ApplicationCommandInfo.cpp
  13. +196
    -0
      src/application/juce_ApplicationCommandInfo.h
  14. +342
    -0
      src/application/juce_ApplicationCommandManager.cpp
  15. +367
    -0
      src/application/juce_ApplicationCommandManager.h
  16. +202
    -0
      src/application/juce_ApplicationCommandTarget.cpp
  17. +0
    -0
      src/application/juce_ApplicationCommandTarget.h
  18. +161
    -0
      src/application/juce_ApplicationProperties.cpp
  19. +165
    -0
      src/application/juce_ApplicationProperties.h
  20. +0
    -0
      src/containers/juce_Array.h
  21. +0
    -0
      src/containers/juce_ArrayAllocationBase.h
  22. +951
    -0
      src/containers/juce_BitArray.cpp
  23. +0
    -0
      src/containers/juce_BitArray.h
  24. +0
    -0
      src/containers/juce_ElementComparator.h
  25. +397
    -0
      src/containers/juce_MemoryBlock.cpp
  26. +0
    -0
      src/containers/juce_MemoryBlock.h
  27. +0
    -0
      src/containers/juce_OwnedArray.h
  28. +249
    -0
      src/containers/juce_PropertySet.cpp
  29. +0
    -0
      src/containers/juce_PropertySet.h
  30. +792
    -0
      src/containers/juce_ReferenceCountedArray.h
  31. +252
    -0
      src/containers/juce_ReferenceCountedObject.h
  32. +0
    -0
      src/containers/juce_SortedSet.h
  33. +0
    -0
      src/containers/juce_SparseSet.h
  34. +438
    -0
      src/containers/juce_Variant.cpp
  35. +0
    -0
      src/containers/juce_Variant.h
  36. +0
    -0
      src/containers/juce_VoidArray.h
  37. +453
    -0
      src/cryptography/juce_BlowFish.cpp
  38. +0
    -0
      src/cryptography/juce_BlowFish.h
  39. +382
    -0
      src/cryptography/juce_MD5.cpp
  40. +126
    -0
      src/cryptography/juce_MD5.h
  41. +260
    -0
      src/cryptography/juce_Primes.cpp
  42. +0
    -0
      src/cryptography/juce_Primes.h
  43. +155
    -0
      src/cryptography/juce_RSAKey.cpp
  44. +0
    -0
      src/cryptography/juce_RSAKey.h
  45. +70
    -0
      src/events/juce_ActionBroadcaster.cpp
  46. +0
    -0
      src/events/juce_ActionBroadcaster.h
  47. +56
    -0
      src/events/juce_ActionListener.h
  48. +112
    -0
      src/events/juce_ActionListenerList.cpp
  49. +96
    -0
      src/events/juce_ActionListenerList.h
  50. +73
    -0
      src/events/juce_AsyncUpdater.cpp
  51. +0
    -0
      src/events/juce_AsyncUpdater.h
  52. +0
    -0
      src/events/juce_CallbackMessage.h
  53. +81
    -0
      src/events/juce_ChangeBroadcaster.cpp
  54. +0
    -0
      src/events/juce_ChangeBroadcaster.h
  55. +0
    -0
      src/events/juce_ChangeListener.h
  56. +107
    -0
      src/events/juce_ChangeListenerList.cpp
  57. +119
    -0
      src/events/juce_ChangeListenerList.h
  58. +387
    -0
      src/events/juce_InterprocessConnection.cpp
  59. +215
    -0
      src/events/juce_InterprocessConnection.h
  60. +97
    -0
      src/events/juce_InterprocessConnectionServer.cpp
  61. +0
    -0
      src/events/juce_InterprocessConnectionServer.h
  62. +53
    -0
      src/events/juce_Message.cpp
  63. +0
    -0
      src/events/juce_Message.h
  64. +65
    -0
      src/events/juce_MessageListener.cpp
  65. +0
    -0
      src/events/juce_MessageListener.h
  66. +343
    -0
      src/events/juce_MessageManager.cpp
  67. +315
    -0
      src/events/juce_MessageManager.h
  68. +142
    -0
      src/events/juce_MultiTimer.cpp
  69. +131
    -0
      src/events/juce_MultiTimer.h
  70. +382
    -0
      src/events/juce_Timer.cpp
  71. +0
    -0
      src/events/juce_Timer.h
  72. +76
    -0
      src/gui/components/buttons/juce_ArrowButton.cpp
  73. +0
    -0
      src/gui/components/buttons/juce_ArrowButton.h
  74. +725
    -0
      src/gui/components/buttons/juce_Button.cpp
  75. +500
    -0
      src/gui/components/buttons/juce_Button.h
  76. +307
    -0
      src/gui/components/buttons/juce_DrawableButton.cpp
  77. +0
    -0
      src/gui/components/buttons/juce_DrawableButton.h
  78. +115
    -0
      src/gui/components/buttons/juce_HyperlinkButton.cpp
  79. +120
    -0
      src/gui/components/buttons/juce_HyperlinkButton.h
  80. +236
    -0
      src/gui/components/buttons/juce_ImageButton.cpp
  81. +0
    -0
      src/gui/components/buttons/juce_ImageButton.h
  82. +132
    -0
      src/gui/components/buttons/juce_ShapeButton.cpp
  83. +0
    -0
      src/gui/components/buttons/juce_ShapeButton.h
  84. +81
    -0
      src/gui/components/buttons/juce_TextButton.cpp
  85. +0
    -0
      src/gui/components/buttons/juce_TextButton.h
  86. +65
    -0
      src/gui/components/buttons/juce_ToggleButton.cpp
  87. +0
    -0
      src/gui/components/buttons/juce_ToggleButton.h
  88. +93
    -0
      src/gui/components/buttons/juce_ToolbarButton.cpp
  89. +0
    -0
      src/gui/components/buttons/juce_ToolbarButton.h
  90. +610
    -0
      src/gui/components/controls/juce_ComboBox.cpp
  91. +393
    -0
      src/gui/components/controls/juce_ComboBox.h
  92. +461
    -0
      src/gui/components/controls/juce_Label.cpp
  93. +0
    -0
      src/gui/components/controls/juce_Label.h
  94. +970
    -0
      src/gui/components/controls/juce_ListBox.cpp
  95. +582
    -0
      src/gui/components/controls/juce_ListBox.h
  96. +123
    -0
      src/gui/components/controls/juce_ProgressBar.cpp
  97. +0
    -0
      src/gui/components/controls/juce_ProgressBar.h
  98. +1413
    -0
      src/gui/components/controls/juce_Slider.cpp
  99. +745
    -0
      src/gui/components/controls/juce_Slider.h
  100. +0
    -0
      src/gui/components/controls/juce_SliderListener.h

+ 328
- 328
build/linux/JUCE.make
File diff suppressed because it is too large
View File


+ 86
- 88
build/linux/juce_premake.lua View File

@@ -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"


+ 2455
- 2462
build/macosx/Juce.xcodeproj/project.pbxproj
File diff suppressed because it is too large
View File


+ 2019
- 4928
build/win32/vc8/JUCE.vcproj
File diff suppressed because it is too large
View File


+ 26
- 26
extras/juce demo/build/linux/JuceDemo.make View File

@@ -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)


+ 1
- 1
juce.h View File

@@ -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


+ 613
- 607
juce_amalgamated.cpp
File diff suppressed because it is too large
View File


+ 4006
- 4006
juce_amalgamated.h
File diff suppressed because it is too large
View File


+ 348
- 0
src/application/juce_Application.cpp View File

@@ -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

src/juce_appframework/application/juce_Application.h → src/application/juce_Application.h View File


src/juce_appframework/application/juce_ApplicationCommandID.h → src/application/juce_ApplicationCommandID.h View File


+ 73
- 0
src/application/juce_ApplicationCommandInfo.cpp View File

@@ -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

+ 196
- 0
src/application/juce_ApplicationCommandInfo.h View File

@@ -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__

+ 342
- 0
src/application/juce_ApplicationCommandManager.cpp View File

@@ -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

+ 367
- 0
src/application/juce_ApplicationCommandManager.h View File

@@ -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__

+ 202
- 0
src/application/juce_ApplicationCommandTarget.cpp View File

@@ -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

src/juce_appframework/application/juce_ApplicationCommandTarget.h → src/application/juce_ApplicationCommandTarget.h View File


+ 161
- 0
src/application/juce_ApplicationProperties.cpp View File

@@ -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

+ 165
- 0
src/application/juce_ApplicationProperties.h View File

@@ -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__

src/juce_core/containers/juce_Array.h → src/containers/juce_Array.h View File


src/juce_core/containers/juce_ArrayAllocationBase.h → src/containers/juce_ArrayAllocationBase.h View File


+ 951
- 0
src/containers/juce_BitArray.cpp View File

@@ -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

src/juce_core/containers/juce_BitArray.h → src/containers/juce_BitArray.h View File


src/juce_core/containers/juce_ElementComparator.h → src/containers/juce_ElementComparator.h View File


+ 397
- 0
src/containers/juce_MemoryBlock.cpp View File

@@ -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

src/juce_core/containers/juce_MemoryBlock.h → src/containers/juce_MemoryBlock.h View File


src/juce_core/containers/juce_OwnedArray.h → src/containers/juce_OwnedArray.h View File


+ 249
- 0
src/containers/juce_PropertySet.cpp View File

@@ -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

src/juce_core/containers/juce_PropertySet.h → src/containers/juce_PropertySet.h View File


+ 792
- 0
src/containers/juce_ReferenceCountedArray.h View File

@@ -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__

+ 252
- 0
src/containers/juce_ReferenceCountedObject.h View File

@@ -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__

src/juce_core/containers/juce_SortedSet.h → src/containers/juce_SortedSet.h View File


src/juce_core/containers/juce_SparseSet.h → src/containers/juce_SparseSet.h View File


+ 438
- 0
src/containers/juce_Variant.cpp View File

@@ -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

src/juce_core/containers/juce_Variant.h → src/containers/juce_Variant.h View File


src/juce_core/containers/juce_VoidArray.h → src/containers/juce_VoidArray.h View File


+ 453
- 0
src/cryptography/juce_BlowFish.cpp View File

@@ -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

src/juce_core/cryptography/juce_BlowFish.h → src/cryptography/juce_BlowFish.h View File


+ 382
- 0
src/cryptography/juce_MD5.cpp View File

@@ -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

+ 126
- 0
src/cryptography/juce_MD5.h View File

@@ -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__

+ 260
- 0
src/cryptography/juce_Primes.cpp View File

@@ -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

src/juce_core/cryptography/juce_Primes.h → src/cryptography/juce_Primes.h View File


+ 155
- 0
src/cryptography/juce_RSAKey.cpp View File

@@ -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

src/juce_core/cryptography/juce_RSAKey.h → src/cryptography/juce_RSAKey.h View File


+ 70
- 0
src/events/juce_ActionBroadcaster.cpp View File

@@ -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

src/juce_appframework/events/juce_ActionBroadcaster.h → src/events/juce_ActionBroadcaster.h View File


+ 56
- 0
src/events/juce_ActionListener.h View File

@@ -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__

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

@@ -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

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

@@ -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__

+ 73
- 0
src/events/juce_AsyncUpdater.cpp View File

@@ -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

src/juce_appframework/events/juce_AsyncUpdater.h → src/events/juce_AsyncUpdater.h View File


src/juce_appframework/events/juce_CallbackMessage.h → src/events/juce_CallbackMessage.h View File


+ 81
- 0
src/events/juce_ChangeBroadcaster.cpp View File

@@ -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

src/juce_appframework/events/juce_ChangeBroadcaster.h → src/events/juce_ChangeBroadcaster.h View File


src/juce_appframework/events/juce_ChangeListener.h → src/events/juce_ChangeListener.h View File


+ 107
- 0
src/events/juce_ChangeListenerList.cpp View File

@@ -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

+ 119
- 0
src/events/juce_ChangeListenerList.h View File

@@ -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__

+ 387
- 0
src/events/juce_InterprocessConnection.cpp View File

@@ -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

+ 215
- 0
src/events/juce_InterprocessConnection.h View File

@@ -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__

+ 97
- 0
src/events/juce_InterprocessConnectionServer.cpp View File

@@ -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

src/juce_appframework/events/juce_InterprocessConnectionServer.h → src/events/juce_InterprocessConnectionServer.h View File


+ 53
- 0
src/events/juce_Message.cpp View File

@@ -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

src/juce_appframework/events/juce_Message.h → src/events/juce_Message.h View File


+ 65
- 0
src/events/juce_MessageListener.cpp View File

@@ -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

src/juce_appframework/events/juce_MessageListener.h → src/events/juce_MessageListener.h View File


+ 343
- 0
src/events/juce_MessageManager.cpp View File

@@ -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

+ 315
- 0
src/events/juce_MessageManager.h View File

@@ -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__

+ 142
- 0
src/events/juce_MultiTimer.cpp View File

@@ -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

+ 131
- 0
src/events/juce_MultiTimer.h View File

@@ -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__

+ 382
- 0
src/events/juce_Timer.cpp View File

@@ -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

src/juce_appframework/events/juce_Timer.h → src/events/juce_Timer.h View File


+ 76
- 0
src/gui/components/buttons/juce_ArrowButton.cpp View File

@@ -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

src/juce_appframework/gui/components/buttons/juce_ArrowButton.h → src/gui/components/buttons/juce_ArrowButton.h View File


+ 725
- 0
src/gui/components/buttons/juce_Button.cpp View File

@@ -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

+ 500
- 0
src/gui/components/buttons/juce_Button.h View File

@@ -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__

+ 307
- 0
src/gui/components/buttons/juce_DrawableButton.cpp View File

@@ -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

src/juce_appframework/gui/components/buttons/juce_DrawableButton.h → src/gui/components/buttons/juce_DrawableButton.h View File


+ 115
- 0
src/gui/components/buttons/juce_HyperlinkButton.cpp View File

@@ -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

+ 120
- 0
src/gui/components/buttons/juce_HyperlinkButton.h View File

@@ -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__

+ 236
- 0
src/gui/components/buttons/juce_ImageButton.cpp View File

@@ -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

src/juce_appframework/gui/components/buttons/juce_ImageButton.h → src/gui/components/buttons/juce_ImageButton.h View File


+ 132
- 0
src/gui/components/buttons/juce_ShapeButton.cpp View File

@@ -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

src/juce_appframework/gui/components/buttons/juce_ShapeButton.h → src/gui/components/buttons/juce_ShapeButton.h View File


+ 81
- 0
src/gui/components/buttons/juce_TextButton.cpp View File

@@ -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

src/juce_appframework/gui/components/buttons/juce_TextButton.h → src/gui/components/buttons/juce_TextButton.h View File


+ 65
- 0
src/gui/components/buttons/juce_ToggleButton.cpp View File

@@ -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

src/juce_appframework/gui/components/buttons/juce_ToggleButton.h → src/gui/components/buttons/juce_ToggleButton.h View File


+ 93
- 0
src/gui/components/buttons/juce_ToolbarButton.cpp View File

@@ -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

src/juce_appframework/gui/components/buttons/juce_ToolbarButton.h → src/gui/components/buttons/juce_ToolbarButton.h View File


+ 610
- 0
src/gui/components/controls/juce_ComboBox.cpp View File

@@ -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

+ 393
- 0
src/gui/components/controls/juce_ComboBox.h View File

@@ -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__

+ 461
- 0
src/gui/components/controls/juce_Label.cpp View File

@@ -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

src/juce_appframework/gui/components/controls/juce_Label.h → src/gui/components/controls/juce_Label.h View File


+ 970
- 0
src/gui/components/controls/juce_ListBox.cpp View File

@@ -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

+ 582
- 0
src/gui/components/controls/juce_ListBox.h View File

@@ -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__

+ 123
- 0
src/gui/components/controls/juce_ProgressBar.cpp View File

@@ -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

src/juce_appframework/gui/components/controls/juce_ProgressBar.h → src/gui/components/controls/juce_ProgressBar.h View File


+ 1413
- 0
src/gui/components/controls/juce_Slider.cpp
File diff suppressed because it is too large
View File


+ 745
- 0
src/gui/components/controls/juce_Slider.h View File

@@ -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__

src/juce_appframework/gui/components/controls/juce_SliderListener.h → src/gui/components/controls/juce_SliderListener.h View File


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save