diff --git a/build/macosx/platform_specific_code/juce_mac_Windowing.cpp b/build/macosx/platform_specific_code/juce_mac_Windowing.cpp index 459e7753f7..10c6733062 100644 --- a/build/macosx/platform_specific_code/juce_mac_Windowing.cpp +++ b/build/macosx/platform_specific_code/juce_mac_Windowing.cpp @@ -1937,15 +1937,15 @@ bool juce_isHIViewCreatedByJuce (HIViewRef view) && HIObjectIsOfClass ((HIObjectRef) view, juceHiViewClassNameCFString); } -bool juce_isWindowCreatedByJuce (WindowRef window) -{ - for (int i = ComponentPeer::getNumPeers(); --i >= 0;) - if (ComponentPeer::getPeer(i)->getNativeHandle() == window) - return true; - - return false; -} - +bool juce_isWindowCreatedByJuce (WindowRef window) +{ + for (int i = ComponentPeer::getNumPeers(); --i >= 0;) + if (ComponentPeer::getPeer(i)->getNativeHandle() == window) + return true; + + return false; +} + static void trackNextMouseEvent() { UInt32 mods; diff --git a/docs/JUCE changelist.txt b/docs/JUCE changelist.txt index 004e3e12f3..e5a9d80a68 100644 --- a/docs/JUCE changelist.txt +++ b/docs/JUCE changelist.txt @@ -8,11 +8,11 @@ Changelist for version 1.45 - big new project in the "extras" folder - a basic audio plugin host! Currently it loads VSTs on PC/Mac, and lets you put them together in a filter graph, which it plays. Hosting functionality is very basic at the moment, but I'm laying down a good architecture to hopefully develop into a full cross-platform plugin host. - audio plugins: I've simplified the processBlock() call in AudioFilterBase. It now just takes a single buffer for all input and output channels, and the accumulate parameter has gone. This will mean tweaking your plugin code, but will probably make it much less complicated. -- audio plugins: AudioFilterBase now requires a few extra methods to be implemented by your plugin: getInputChannelName, getOutputChannelName, isInputChannelStereoPair, isOutputChannelStereoPair. +- audio plugins: AudioFilterBase now requires a few extra methods to be implemented by your plugin: getInputChannelName, getOutputChannelName, isInputChannelStereoPair, isOutputChannelStereoPair, getLatencySamples (which supersedes the old macro for setting the latency) - audio plugins: new method AudioFilterBase::updateHostDisplay() to tell the host that something about your plugin has changed and that it should refresh its display. - audio plugins: new methods AudioFilterBase::beginParameterChangeGesture() and endParameterChangeGesture() let you tell the host when a parameter-change action starts and finishes. - new class: FileSearchPathListComponent, for letting the user edit a FileSearchPath. - +- added a critical section option to ReferenceCountedArray ============================================================================== Changelist for version 1.44 diff --git a/extras/audio plugins/How to use this framework.txt b/extras/audio plugins/How to use this framework.txt index 3fbc2c5f7e..9cde1475a6 100644 --- a/extras/audio plugins/How to use this framework.txt +++ b/extras/audio plugins/How to use this framework.txt @@ -151,27 +151,28 @@ Compiling the juce demo RTAS on the PC: - In Visual Studio, add all of this lot to your include path: - c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\PluginLibrary\EffectClasses - c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\PluginLibrary\ProcessClasses - c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\PluginLibrary\ProcessClasses\Interfaces - c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\PluginLibrary\Utilities - c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\PluginLibrary\RTASP_Adapt - c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\PluginLibrary\CoreClasses - c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\PluginLibrary\Controls - c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\PluginLibrary\Meters - c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\PluginLibrary\ViewClasses - c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\PluginLibrary\DSPClasses - c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\PluginLibrary\Interfaces - c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\common - c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\common\Platform - c:\yourdirectory\PT_711_SDK\AlturaPorts\TDMPlugins\SignalProcessing\Public - c:\yourdirectory\PT_711_SDK\AlturaPorts\SADriver\Interfaces - c:\yourdirectory\PT_711_SDK\AlturaPorts\DigiPublic\Interfaces - c:\yourdirectory\PT_711_SDK\AlturaPorts\Fic\Interfaces\DAEClient - c:\yourdirectory\PT_711_SDK\AlturaPorts\NewFileLibs\Cmn - c:\yourdirectory\PT_711_SDK\AlturaPorts\NewFileLibs\DOA - c:\yourdirectory\PT_711_SDK\AlturaPorts\AlturaSource\PPC_H - c:\yourdirectory\PT_711_SDK\AlturaPorts\AlturaSource\AppSupport + c:\yourdirectory\PT_73_SDK\AlturaPorts\TDMPlugins\PluginLibrary\EffectClasses + c:\yourdirectory\PT_73_SDK\AlturaPorts\TDMPlugins\PluginLibrary\ProcessClasses + c:\yourdirectory\PT_73_SDK\AlturaPorts\TDMPlugins\PluginLibrary\ProcessClasses\Interfaces + c:\yourdirectory\PT_73_SDK\AlturaPorts\TDMPlugins\PluginLibrary\Utilities + c:\yourdirectory\PT_73_SDK\AlturaPorts\TDMPlugins\PluginLibrary\RTASP_Adapt + c:\yourdirectory\PT_73_SDK\AlturaPorts\TDMPlugins\PluginLibrary\CoreClasses + c:\yourdirectory\PT_73_SDK\AlturaPorts\TDMPlugins\PluginLibrary\Controls + c:\yourdirectory\PT_73_SDK\AlturaPorts\TDMPlugins\PluginLibrary\Meters + c:\yourdirectory\PT_73_SDK\AlturaPorts\TDMPlugins\PluginLibrary\ViewClasses + c:\yourdirectory\PT_73_SDK\AlturaPorts\TDMPlugins\PluginLibrary\DSPClasses + c:\yourdirectory\PT_73_SDK\AlturaPorts\TDMPlugins\PluginLibrary\Interfaces + c:\yourdirectory\PT_73_SDK\AlturaPorts\TDMPlugins\common + c:\yourdirectory\PT_73_SDK\AlturaPorts\TDMPlugins\common\Platform + c:\yourdirectory\PT_73_SDK\AlturaPorts\TDMPlugins\SignalProcessing\Public + c:\yourdirectory\PT_73_SDK\AlturaPorts\SADriver\Interfaces + c:\yourdirectory\PT_73_SDK\AlturaPorts\DigiPublic\Interfaces + c:\yourdirectory\PT_73_SDK\AlturaPorts\Fic\Interfaces\DAEClient + c:\yourdirectory\PT_73_SDK\AlturaPorts\NewFileLibs\Cmn + c:\yourdirectory\PT_73_SDK\AlturaPorts\NewFileLibs\DOA + c:\yourdirectory\PT_73_SDK\AlturaPorts\AlturaSource\PPC_H + c:\yourdirectory\PT_73_SDK\AlturaPorts\AlturaSource\AppSupport + c:\yourdirectory\PT_73_SDK\AvidCode\AVX2sdk\AVX\avx2\avx2sdk\inc - The Visual Studio juce_RTAS project contains a folder called "libs", with "debug" and "release" subdirectories - these should contain links to the Digidesign lib files @@ -180,6 +181,12 @@ Compiling the juce demo RTAS on the PC: - Fingers crossed, this should now compile.. +- IMPORTANT NOTE! If you're using MSVC2005 to build your plugin, the users will need to + have the Microsoft VC8 Runtime installed on their machines, otherwise the DLL will + silently fail to load. You should probably add the runtime to your plugin's installer, + and you can get a copy of it here: + http://www.microsoft.com/downloads/details.aspx?FamilyID=32bc1bee-a3f9-4c13-9c99-220b62a191ee&DisplayLang=en + Creating a project for your own RTAS on the PC: ================================================= diff --git a/extras/audio plugins/demo/build/RTAS_Win32/juce_RTAS.vcproj b/extras/audio plugins/demo/build/RTAS_Win32/juce_RTAS.vcproj index f782ce1c17..eeb6728f52 100644 --- a/extras/audio plugins/demo/build/RTAS_Win32/juce_RTAS.vcproj +++ b/extras/audio plugins/demo/build/RTAS_Win32/juce_RTAS.vcproj @@ -542,7 +542,7 @@ Name="debug" > GetStreamFormat().mChannelsPerFrame : 0; + const int numOuts = GetOutput(0) != 0 ? GetOutput(0)->GetStreamFormat().mChannelsPerFrame : 0; + + bool isValidChannelConfig = false; + + for (int i = 0; i < numChannelConfigs; ++i) + if (numIns == channelConfigs[i][0] && numOuts == channelConfigs[i][1]) + isValidChannelConfig = true; + + if (! isValidChannelConfig) + return kAudioUnitErr_FormatNotSupported; + prepareToPlay(); return noErr; } diff --git a/extras/audio plugins/wrapper/formats/RTAS/juce_RTASUtilities.cpp b/extras/audio plugins/wrapper/formats/RTAS/juce_RTASUtilities.cpp index 3f415b196f..cab2f03d33 100644 --- a/extras/audio plugins/wrapper/formats/RTAS/juce_RTASUtilities.cpp +++ b/extras/audio plugins/wrapper/formats/RTAS/juce_RTASUtilities.cpp @@ -35,8 +35,16 @@ // at the same time as the Digi headers) #include + +#ifdef _MSC_VER + #pragma pack (push, 8) +#endif + #include "../../juce_AudioFilterBase.h" -#include "../../juce_AudioFilterEditor.h" + +#ifdef _MSC_VER + #pragma pack (pop) +#endif //============================================================================== diff --git a/extras/audio plugins/wrapper/formats/RTAS/juce_RTASWrapper.cpp b/extras/audio plugins/wrapper/formats/RTAS/juce_RTASWrapper.cpp index 92eaa515ac..a8db307062 100644 --- a/extras/audio plugins/wrapper/formats/RTAS/juce_RTASWrapper.cpp +++ b/extras/audio plugins/wrapper/formats/RTAS/juce_RTASWrapper.cpp @@ -79,9 +79,18 @@ #include "CPluginControl_OnOff.h" //============================================================================== +#ifdef _MSC_VER + #pragma pack (push, 8) +#endif + #include "../../juce_AudioFilterBase.h" #include "../../juce_AudioFilterEditor.h" #include "../../juce_IncludeCharacteristics.h" + +#ifdef _MSC_VER + #pragma pack (pop) +#endif + #undef Component //============================================================================== @@ -526,7 +535,7 @@ protected: ComponentResult GetDelaySamplesLong (long* aNumSamples) { if (aNumSamples != 0) - *aNumSamples = JucePlugin_Latency; + *aNumSamples = juceFilter != 0 ? juceFilter->getLatencySamples() : 0; return noErr; } diff --git a/extras/audio plugins/wrapper/formats/RTAS/juce_RTAS_dlldefs.def b/extras/audio plugins/wrapper/formats/RTAS/juce_RTAS_dlldefs.def index 709ecd9f07..31b09258e4 100644 --- a/extras/audio plugins/wrapper/formats/RTAS/juce_RTAS_dlldefs.def +++ b/extras/audio plugins/wrapper/formats/RTAS/juce_RTAS_dlldefs.def @@ -1,5 +1,3 @@ EXPORTS NewPlugIn @1 _PI_GetRoutineDescriptor @2 - - diff --git a/extras/audio plugins/wrapper/formats/VST/juce_VstWrapper.cpp b/extras/audio plugins/wrapper/formats/VST/juce_VstWrapper.cpp index fb710cdf6c..9ae7650be4 100644 --- a/extras/audio plugins/wrapper/formats/VST/juce_VstWrapper.cpp +++ b/extras/audio plugins/wrapper/formats/VST/juce_VstWrapper.cpp @@ -91,7 +91,16 @@ #endif //============================================================================== +#ifdef _MSC_VER + #pragma pack (push, 8) +#endif + #include "../../juce_AudioFilterBase.h" + +#ifdef _MSC_VER + #pragma pack (pop) +#endif + #undef MemoryBlock class JuceVSTWrapper; @@ -349,7 +358,7 @@ public: isSynth ((JucePlugin_IsSynth) != 0); noTail ((JucePlugin_SilenceInProducesSilenceOut) != 0); - setInitialDelay (JucePlugin_Latency); + setInitialDelay (filter->getLatencySamples()); programsAreChunks (true); activePlugins.add (this); @@ -669,6 +678,8 @@ public: filter->prepareToPlay (rate, blockSize); midiEvents.clear(); + setInitialDelay (filter->getLatencySamples()); + AudioEffectX::resume(); #if JucePlugin_ProducesMidiOutput diff --git a/extras/audio plugins/wrapper/juce_AudioFilterBase.cpp b/extras/audio plugins/wrapper/juce_AudioFilterBase.cpp index 1fdc87a02a..f076088a8c 100644 --- a/extras/audio plugins/wrapper/juce_AudioFilterBase.cpp +++ b/extras/audio plugins/wrapper/juce_AudioFilterBase.cpp @@ -40,6 +40,7 @@ AudioFilterBase::AudioFilterBase() blockSize (0), numInputChannels (0), numOutputChannels (0), + latencySamples (0), suspended (false) { } @@ -71,6 +72,15 @@ void AudioFilterBase::setPlayConfigDetails (const int numIns, blockSize = blockSize_; } +void JUCE_CALLTYPE AudioFilterBase::setLatencySamples (const int newLatency) +{ + if (latencySamples != newLatency) + { + latencySamples = newLatency; + updateHostDisplay(); + } +} + void AudioFilterBase::setParameterNotifyingHost (const int parameterIndex, const float newValue) { diff --git a/extras/audio plugins/wrapper/juce_AudioFilterBase.h b/extras/audio plugins/wrapper/juce_AudioFilterBase.h index 1a6b099633..6bb89f5f20 100644 --- a/extras/audio plugins/wrapper/juce_AudioFilterBase.h +++ b/extras/audio plugins/wrapper/juce_AudioFilterBase.h @@ -32,10 +32,6 @@ #ifndef __JUCE_AUDIOFILTERBASE_JUCEHEADER__ #define __JUCE_AUDIOFILTERBASE_JUCEHEADER__ -#ifdef _MSC_VER - #pragma pack (push, 8) -#endif - #include "../../../juce.h" #include "juce_AudioFilterEditor.h" #undef MemoryBlock @@ -274,6 +270,21 @@ public: /** Returns true if the specified channel is part of a stereo pair with its neighbour. */ virtual bool JUCE_CALLTYPE isOutputChannelStereoPair (int index) const = 0; + /** This returns the number of samples delay that the filter imposes on the audio + passing through it. + + The host will call this to find the latency - the filter itself should set this value + by calling setLatencySamples() as soon as it can during its initialisation. + */ + int JUCE_CALLTYPE getLatencySamples() const throw() { return latencySamples; } + + /** The filter should call this to set the number of samples delay that it introduces. + + The filter should call this as soon as it can during initialisation, and can call it + later if the value changes. + */ + void JUCE_CALLTYPE setLatencySamples (const int newLatency); + //============================================================================== /** This returns a critical section that will automatically be locked while the host is calling the processBlock() method. @@ -586,7 +597,7 @@ protected: private: AudioFilterEditor* activeEditor; double sampleRate; - int blockSize, numInputChannels, numOutputChannels; + int blockSize, numInputChannels, numOutputChannels, latencySamples; bool suspended; CriticalSection callbackLock; @@ -601,9 +612,5 @@ private: */ extern AudioFilterBase* JUCE_CALLTYPE createPluginFilter(); -#ifdef _MSC_VER - #pragma pack (pop) -#endif - #endif // __JUCE_AUDIOFILTERBASE_JUCEHEADER__ diff --git a/extras/audio plugins/wrapper/juce_AudioFilterEditor.h b/extras/audio plugins/wrapper/juce_AudioFilterEditor.h index 843cc280b6..d5f685c554 100644 --- a/extras/audio plugins/wrapper/juce_AudioFilterEditor.h +++ b/extras/audio plugins/wrapper/juce_AudioFilterEditor.h @@ -32,10 +32,6 @@ #ifndef __JUCE_AUDIOFILTEREDITOR_JUCEHEADER__ #define __JUCE_AUDIOFILTEREDITOR_JUCEHEADER__ -#ifdef _MSC_VER - #pragma pack (push, 8) -#endif - #include "../../../juce.h" class AudioFilterBase; @@ -72,8 +68,5 @@ private: AudioFilterBase* const ownerFilter; }; -#ifdef _MSC_VER - #pragma pack (pop) -#endif #endif // __JUCE_AUDIOFILTEREDITOR_JUCEHEADER__ diff --git a/extras/audio plugins/wrapper/juce_IncludeCharacteristics.h b/extras/audio plugins/wrapper/juce_IncludeCharacteristics.h index 34779ca0d9..fc67d8ec97 100644 --- a/extras/audio plugins/wrapper/juce_IncludeCharacteristics.h +++ b/extras/audio plugins/wrapper/juce_IncludeCharacteristics.h @@ -79,8 +79,8 @@ #error "You need to define the JucePlugin_PreferredChannelConfigurations value in your JucePluginCharacteristics.h file!" #endif -#ifndef JucePlugin_Latency - #error "You need to define the JucePlugin_Latency value in your JucePluginCharacteristics.h file!" +#ifdef JucePlugin_Latency + #error "JucePlugin_Latency is now deprecated - instead, call the AudioFilterBase::setLatencySamples() method if your plugin has a non-zero delay" #endif #ifndef JucePlugin_SilenceInProducesSilenceOut diff --git a/extras/juce demo/src/MainDemoWindow.cpp b/extras/juce demo/src/MainDemoWindow.cpp index e87c898430..133098d12f 100644 --- a/extras/juce demo/src/MainDemoWindow.cpp +++ b/extras/juce demo/src/MainDemoWindow.cpp @@ -1,605 +1,605 @@ -/* - ============================================================================== - - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-7 by Raw Material Software ltd. - - ------------------------------------------------------------------------------ - - JUCE can be redistributed and/or modified under the terms of the - GNU General Public License, as published by the Free Software Foundation; - either version 2 of the License, or (at your option) any later version. - - 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. - - You should have received a copy of the GNU General Public License - along with JUCE; if not, visit www.gnu.org/licenses or write to the - Free Software Foundation, Inc., 59 Temple Place, Suite 330, - Boston, MA 02111-1307 USA - - ------------------------------------------------------------------------------ - - If you'd like to release a closed-source product which uses JUCE, commercial - licenses are also available: visit www.rawmaterialsoftware.com/juce for - more information. - - ============================================================================== -*/ - -#include "jucedemo_headers.h" -#include "MainDemoWindow.h" - - -//============================================================================== -class SourceCodeWindow; -static SourceCodeWindow* sourceWindow = 0; - - -//============================================================================== -class SourceCodeWindow : public DialogWindow -{ - TextEditor* textBox; - -public: - SourceCodeWindow() - : DialogWindow (T("JUCE Demo Source Code!"), - Colours::floralwhite, - false) - { - setContentComponent (textBox = new TextEditor()); - - textBox->setColour (TextEditor::backgroundColourId, Colours::white); - textBox->setMultiLine (true, false); - textBox->setReturnKeyStartsNewLine (true); - - setResizable (true, true); // we'll choose a corner-resizer component for this window, - // as a contrast to the resizable border on the main window - } - - ~SourceCodeWindow() - { - // the text editor gets deleted automatically because it's the - // window's content component. - - sourceWindow = 0; - } - - void closeButtonPressed() - { - delete this; - } - - void updateSourceCode (const String& text) - { - Font font (14.0f); - font.setTypefaceName (Font::getDefaultMonospacedFontName()); - textBox->setFont (font); - - textBox->setText (text); - - toFront (true); - } -}; - -//============================================================================== -class ContentComp : public Component, - public MenuBarModel, - public ApplicationCommandTarget -{ - //============================================================================== - MainDemoWindow* mainWindow; - - OldSchoolLookAndFeel oldLookAndFeel; - - Component* currentDemo; - int currentDemoId; - const char* demoSourceCodeText; - - TooltipWindow tooltipWindow; // to add tooltips to an application, you - // just need to create one of these and leave it - // there to do its work.. - - enum CommandIDs - { - showPathsAndTransforms = 0x2000, - showFontsAndText = 0x2001, - showWidgets = 0x2002, - showThreading = 0x2003, - showTreeView = 0x2004, - showAudio = 0x2005, - showDragAndDrop = 0x2006, - showOpenGL = 0x2007, - showQuicktime = 0x2008, - showInterprocessComms = 0x2009, - showTable = 0x2010, - - showSourceCode = 0x200a, - - setDefaultLookAndFeel = 0x200b, - setOldSchoolLookAndFeel = 0x200c, - useNativeTitleBar = 0x200d, - useNativeMenus = 0x200e - }; - -public: - //============================================================================== - ContentComp (MainDemoWindow* mainWindow_) - : mainWindow (mainWindow_), - currentDemo (0), - currentDemoId (0), - demoSourceCodeText (0) - { - invokeDirectly (showWidgets, true); - } - - ~ContentComp() - { - // (need to do this because the old school look-and-feel object is one of our members, - // so will be deleted with us, and would leave a dangling pointer if it's selected) - LookAndFeel::setDefaultLookAndFeel (0); - - deleteAllChildren(); - - deleteAndZero (sourceWindow); - } - - //============================================================================== - void resized() - { - if (currentDemo != 0) - currentDemo->setBounds (0, 0, getWidth(), getHeight()); - } - - //============================================================================== - void showDemo (Component* demoComp, const char* sourceCodeText) - { - delete currentDemo; - currentDemo = demoComp; - - addAndMakeVisible (currentDemo); - resized(); - - demoSourceCodeText = sourceCodeText; - } - - void showSource() - { - if (sourceWindow == 0) - { - sourceWindow = new SourceCodeWindow(); - sourceWindow->centreAroundComponent (this, 750, 600); - sourceWindow->setVisible (true); - } - - sourceWindow->updateSourceCode (demoSourceCodeText); - } - - //============================================================================== - const StringArray getMenuBarNames() - { - const tchar* const names[] = { T("Demo"), T("Source Code"), T("Look-and-feel"), 0 }; - - return StringArray ((const tchar**) names); - } - - const PopupMenu getMenuForIndex (int menuIndex, - const String& menuName) - { - ApplicationCommandManager* const commandManager = mainWindow->commandManager; - - PopupMenu menu; - - if (menuIndex == 0) - { - menu.addCommandItem (commandManager, showPathsAndTransforms); - menu.addCommandItem (commandManager, showFontsAndText); - menu.addCommandItem (commandManager, showWidgets); - menu.addCommandItem (commandManager, showThreading); - menu.addCommandItem (commandManager, showTreeView); - menu.addCommandItem (commandManager, showTable); - menu.addCommandItem (commandManager, showAudio); - menu.addCommandItem (commandManager, showDragAndDrop); - menu.addCommandItem (commandManager, showOpenGL); - menu.addCommandItem (commandManager, showQuicktime); - menu.addCommandItem (commandManager, showInterprocessComms); - - menu.addSeparator(); - menu.addCommandItem (commandManager, StandardApplicationCommandIDs::quit); - } - else if (menuIndex == 1) - { - menu.addCommandItem (commandManager, showSourceCode); - } - else if (menuIndex == 2) - { - menu.addCommandItem (commandManager, setDefaultLookAndFeel); - menu.addCommandItem (commandManager, setOldSchoolLookAndFeel); - menu.addSeparator(); - menu.addCommandItem (commandManager, useNativeTitleBar); - -#if JUCE_MAC - menu.addCommandItem (commandManager, useNativeMenus); -#endif - } - - return menu; - } - - void menuItemSelected (int menuItemID, - int topLevelMenuIndex) - { - // all our menu items are invoked automatically as commands, so no need to do - // anything in this callback - } - - //============================================================================== - // The following methods implement the ApplicationCommandTarget interface, allowing - // this window to publish a set of actions it can perform, and which can be mapped - // onto menus, keypresses, etc. - - ApplicationCommandTarget* getNextCommandTarget() - { - // this will return the next parent component that is an ApplicationCommandTarget (in this - // case, there probably isn't one, but it's best to use this method in your own apps). - return findFirstTargetParentComponent(); - } - - void getAllCommands (Array & commands) - { - // this returns the set of all commands that this target can perform.. - const CommandID ids[] = { showPathsAndTransforms, - showFontsAndText, - showWidgets, - showThreading, - showTreeView, - showTable, - showAudio, - showDragAndDrop, - showOpenGL, - showQuicktime, - showInterprocessComms, - showSourceCode, - setDefaultLookAndFeel, - setOldSchoolLookAndFeel, - useNativeTitleBar -#if JUCE_MAC - , useNativeMenus -#endif - }; - - commands.addArray (ids, numElementsInArray (ids)); - } - - // This method is used when something needs to find out the details about one of the commands - // that this object can perform.. - void getCommandInfo (const CommandID commandID, ApplicationCommandInfo& result) - { - const String generalCategory (T("General")); - const String demosCategory (T("Demos")); - - switch (commandID) - { - case showPathsAndTransforms: - result.setInfo (T("Paths and Transforms"), T("Shows the paths & transforms demo"), demosCategory, 0); - result.setTicked (currentDemoId == showPathsAndTransforms); - result.addDefaultKeypress (T('1'), ModifierKeys::commandModifier); - break; - - case showFontsAndText: - result.setInfo (T("Fonts and Text"), T("Shows the fonts & text demo"), demosCategory, 0); - result.setTicked (currentDemoId == showFontsAndText); - result.addDefaultKeypress (T('2'), ModifierKeys::commandModifier); - break; - - case showWidgets: - result.setInfo (T("Widgets"), T("Shows the widgets demo"), demosCategory, 0); - result.setTicked (currentDemoId == showWidgets); - result.addDefaultKeypress (T('3'), ModifierKeys::commandModifier); - break; - - case showThreading: - result.setInfo (T("Multithreading"), T("Shows the threading demo"), demosCategory, 0); - result.setTicked (currentDemoId == showThreading); - result.addDefaultKeypress (T('4'), ModifierKeys::commandModifier); - break; - - case showTreeView: - result.setInfo (T("Treeviews"), T("Shows the treeviews demo"), demosCategory, 0); - result.setTicked (currentDemoId == showTreeView); - result.addDefaultKeypress (T('5'), ModifierKeys::commandModifier); - break; - - case showTable: - result.setInfo (T("Table Components"), T("Shows the table component demo"), demosCategory, 0); - result.setTicked (currentDemoId == showTable); - result.addDefaultKeypress (T('6'), ModifierKeys::commandModifier); - break; - - case showAudio: - result.setInfo (T("Audio"), T("Shows the audio demo"), demosCategory, 0); - result.setTicked (currentDemoId == showAudio); - result.addDefaultKeypress (T('7'), ModifierKeys::commandModifier); - break; - - case showDragAndDrop: - result.setInfo (T("Drag-and-drop"), T("Shows the drag & drop demo"), demosCategory, 0); - result.setTicked (currentDemoId == showDragAndDrop); - result.addDefaultKeypress (T('8'), ModifierKeys::commandModifier); - break; - - case showOpenGL: - result.setInfo (T("OpenGL"), T("Shows the OpenGL demo"), demosCategory, 0); - result.addDefaultKeypress (T('9'), ModifierKeys::commandModifier); - result.setTicked (currentDemoId == showOpenGL); -#ifndef JUCE_OPENGL - result.setActive (false); -#endif - break; - - case showQuicktime: - result.setInfo (T("Quicktime"), T("Shows the Quicktime demo"), demosCategory, 0); - result.addDefaultKeypress (T('b'), ModifierKeys::commandModifier); - result.setTicked (currentDemoId == showQuicktime); -#if ! (JUCE_QUICKTIME && ! JUCE_LINUX) - result.setActive (false); -#endif - break; - - case showInterprocessComms: - result.setInfo (T("Interprocess Comms"), T("Shows the interprocess communications demo"), demosCategory, 0); - result.addDefaultKeypress (T('0'), ModifierKeys::commandModifier); - result.setTicked (currentDemoId == showInterprocessComms); - break; - - case showSourceCode: - result.setInfo (T("Show the source code for this demo"), T("Opens a window containing this demo's source code"), generalCategory, 0); - result.addDefaultKeypress (T('s'), ModifierKeys::commandModifier); - break; - - case setDefaultLookAndFeel: - result.setInfo (T("Use default look-and-feel"), String::empty, generalCategory, 0); - result.setTicked ((typeid (LookAndFeel) == typeid (getLookAndFeel())) != 0); - break; - - case setOldSchoolLookAndFeel: - result.setInfo (T("Use the old, original juce look-and-feel"), String::empty, generalCategory, 0); - result.setTicked ((typeid (OldSchoolLookAndFeel) == typeid (getLookAndFeel())) != 0); - break; - - case useNativeTitleBar: - result.setInfo (T("Use native window title bar"), String::empty, generalCategory, 0); - result.setTicked (mainWindow->isUsingNativeTitleBar()); - break; - -#if JUCE_MAC - case useNativeMenus: - result.setInfo (T("Use the native OSX menu bar"), String::empty, generalCategory, 0); - result.setTicked (MenuBarModel::getMacMainMenu() != 0); - break; -#endif - - default: - break; - }; - } - - // this is the ApplicationCommandTarget method that is used to actually perform one of our commands.. - bool perform (const InvocationInfo& info) - { - switch (info.commandID) - { - case showPathsAndTransforms: - showDemo (createPathsAndTransformsDemo(), BinaryData::pathsandtransformsdemo_cpp); - currentDemoId = showPathsAndTransforms; - break; - - case showFontsAndText: - showDemo (createFontsAndTextDemo(), BinaryData::fontsandtextdemo_cpp); - currentDemoId = showFontsAndText; - break; - - case showWidgets: - showDemo (createWidgetsDemo (mainWindow->commandManager), BinaryData::widgetsdemo_cpp); - currentDemoId = showWidgets; - break; - - case showThreading: - showDemo (createThreadingDemo(), BinaryData::threadingdemo_cpp); - currentDemoId = showThreading; - break; - - case showTreeView: - showDemo (createTreeViewDemo(), BinaryData::treeviewdemo_cpp); - currentDemoId = showTreeView; - break; - - case showTable: - showDemo (createTableDemo(), BinaryData::tabledemo_cpp); - currentDemoId = showTable; - break; - - case showAudio: - showDemo (createAudioDemo(), BinaryData::audiodemo_cpp); - currentDemoId = showAudio; - break; - - case showDragAndDrop: - showDemo (createDragAndDropDemo(), BinaryData::draganddropdemo_cpp); - currentDemoId = showDragAndDrop; - break; - - case showOpenGL: -#if JUCE_OPENGL - showDemo (createOpenGLDemo(), BinaryData::opengldemo_cpp); - currentDemoId = showOpenGL; -#endif - break; - - case showQuicktime: -#if JUCE_QUICKTIME && ! JUCE_LINUX - showDemo (createQuickTimeDemo(), BinaryData::quicktimedemo_cpp); - currentDemoId = showQuicktime; -#endif - break; - - case showInterprocessComms: - showDemo (createInterprocessCommsDemo(), BinaryData::interprocesscommsdemo_cpp); - currentDemoId = showInterprocessComms; - break; - - case showSourceCode: - showSource(); - break; - - case setDefaultLookAndFeel: - LookAndFeel::setDefaultLookAndFeel (0); - break; - - case setOldSchoolLookAndFeel: - LookAndFeel::setDefaultLookAndFeel (&oldLookAndFeel); - break; - - case useNativeTitleBar: - mainWindow->setUsingNativeTitleBar (! mainWindow->isUsingNativeTitleBar()); - break; - -#if JUCE_MAC - case useNativeMenus: - if (MenuBarModel::getMacMainMenu() != 0) - { - MenuBarModel::setMacMainMenu (0); - mainWindow->setMenuBar ((ContentComp*) mainWindow->getContentComponent()); - } - else - { - MenuBarModel::setMacMainMenu ((ContentComp*) mainWindow->getContentComponent()); - mainWindow->setMenuBar (0); - } - - break; -#endif - - default: - return false; - }; - - return true; - } - - juce_UseDebuggingNewOperator -}; - -//============================================================================== -#if JUCE_WIN32 || JUCE_LINUX - -// Just add a simple icon to the Window system tray area.. -class DemoTaskbarComponent : public SystemTrayIconComponent -{ -public: - DemoTaskbarComponent() - { - // Create an icon which is just a square with a "j" in it.. - Image icon (Image::RGB, 32, 32, true); - Graphics g (icon); - g.fillAll (Colours::lightblue); - g.setColour (Colours::black); - g.setFont ((float) icon.getHeight(), Font::bold); - g.drawText (T("j"), 0, 0, icon.getWidth(), icon.getHeight(), Justification::centred, false); - - setIconImage (icon); - - setIconTooltip (T("Juce Demo App!")); - } - - ~DemoTaskbarComponent() - { - } - - void mouseDown (const MouseEvent& e) - { - PopupMenu m; - m.addItem (1, T("Quit the Juce demo")); - - const int result = m.show(); - - if (result == 1) - JUCEApplication::getInstance()->systemRequestedQuit(); - } -}; - -#endif - -//============================================================================== -MainDemoWindow::MainDemoWindow() - : DocumentWindow (T("JUCE Demo!"), - Colours::azure, - DocumentWindow::allButtons, - true) -{ - commandManager = new ApplicationCommandManager(); - - setResizable (true, false); // resizability is a property of ResizableWindow - - ContentComp* contentComp = new ContentComp (this); - - commandManager->registerAllCommandsForTarget (contentComp); - commandManager->registerAllCommandsForTarget (JUCEApplication::getInstance()); - - // this lets the command manager use keypresses that arrive in our window to send - // out commands - addKeyListener (commandManager->getKeyMappings()); - - // sets the main content component for the window to be this tabbed - // panel. This will be deleted when the window is deleted. - setContentComponent (contentComp); - - // this tells the DocumentWindow to automatically create and manage a MenuBarComponent - // which uses our contentComp as its MenuBarModel - setMenuBar (contentComp); - - // tells our menu bar model that it should watch this command manager for - // changes, and send change messages accordingly. - contentComp->setApplicationCommandManagerToWatch (commandManager); - - setVisible (true); - -#if JUCE_WIN32 || JUCE_LINUX - taskbarIcon = new DemoTaskbarComponent(); -#endif -} - -MainDemoWindow::~MainDemoWindow() -{ -#if JUCE_WIN32 || JUCE_LINUX - deleteAndZero (taskbarIcon); -#endif - - // because we've set the content comp to be used as our menu bar model, we - // have to switch this off before deleting the content comp.. - setMenuBar (0); - - // setting our content component to 0 will delete the current one, and - // that will in turn delete all its child components. You don't always - // have to do this explicitly, because the base class's destructor will - // also delete the content component, but in this case we need to - // make sure our content comp has gone away before deleting our command - // manager. - setContentComponent (0, true); - - delete commandManager; -} - -void MainDemoWindow::closeButtonPressed() -{ - // The correct thing to do when you want the app to quit is to call the - // JUCEApplication::systemRequestedQuit() method. - - // That means that requests to quit that come from your own UI, or from other - // OS-specific sources (e.g. the dock menu on the mac) all get handled in the - // same way. - - JUCEApplication::getInstance()->systemRequestedQuit(); -} +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + 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. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#include "jucedemo_headers.h" +#include "MainDemoWindow.h" + + +//============================================================================== +class SourceCodeWindow; +static SourceCodeWindow* sourceWindow = 0; + + +//============================================================================== +class SourceCodeWindow : public DialogWindow +{ + TextEditor* textBox; + +public: + SourceCodeWindow() + : DialogWindow (T("JUCE Demo Source Code!"), + Colours::floralwhite, + false) + { + setContentComponent (textBox = new TextEditor()); + + textBox->setColour (TextEditor::backgroundColourId, Colours::white); + textBox->setMultiLine (true, false); + textBox->setReturnKeyStartsNewLine (true); + + setResizable (true, true); // we'll choose a corner-resizer component for this window, + // as a contrast to the resizable border on the main window + } + + ~SourceCodeWindow() + { + // the text editor gets deleted automatically because it's the + // window's content component. + + sourceWindow = 0; + } + + void closeButtonPressed() + { + delete this; + } + + void updateSourceCode (const String& text) + { + Font font (14.0f); + font.setTypefaceName (Font::getDefaultMonospacedFontName()); + textBox->setFont (font); + + textBox->setText (text); + + toFront (true); + } +}; + +//============================================================================== +class ContentComp : public Component, + public MenuBarModel, + public ApplicationCommandTarget +{ + //============================================================================== + MainDemoWindow* mainWindow; + + OldSchoolLookAndFeel oldLookAndFeel; + + Component* currentDemo; + int currentDemoId; + const char* demoSourceCodeText; + + TooltipWindow tooltipWindow; // to add tooltips to an application, you + // just need to create one of these and leave it + // there to do its work.. + + enum CommandIDs + { + showPathsAndTransforms = 0x2000, + showFontsAndText = 0x2001, + showWidgets = 0x2002, + showThreading = 0x2003, + showTreeView = 0x2004, + showAudio = 0x2005, + showDragAndDrop = 0x2006, + showOpenGL = 0x2007, + showQuicktime = 0x2008, + showInterprocessComms = 0x2009, + showTable = 0x2010, + + showSourceCode = 0x200a, + + setDefaultLookAndFeel = 0x200b, + setOldSchoolLookAndFeel = 0x200c, + useNativeTitleBar = 0x200d, + useNativeMenus = 0x200e + }; + +public: + //============================================================================== + ContentComp (MainDemoWindow* mainWindow_) + : mainWindow (mainWindow_), + currentDemo (0), + currentDemoId (0), + demoSourceCodeText (0) + { + invokeDirectly (showWidgets, true); + } + + ~ContentComp() + { + // (need to do this because the old school look-and-feel object is one of our members, + // so will be deleted with us, and would leave a dangling pointer if it's selected) + LookAndFeel::setDefaultLookAndFeel (0); + + deleteAllChildren(); + + deleteAndZero (sourceWindow); + } + + //============================================================================== + void resized() + { + if (currentDemo != 0) + currentDemo->setBounds (0, 0, getWidth(), getHeight()); + } + + //============================================================================== + void showDemo (Component* demoComp, const char* sourceCodeText) + { + delete currentDemo; + currentDemo = demoComp; + + addAndMakeVisible (currentDemo); + resized(); + + demoSourceCodeText = sourceCodeText; + } + + void showSource() + { + if (sourceWindow == 0) + { + sourceWindow = new SourceCodeWindow(); + sourceWindow->centreAroundComponent (this, 750, 600); + sourceWindow->setVisible (true); + } + + sourceWindow->updateSourceCode (demoSourceCodeText); + } + + //============================================================================== + const StringArray getMenuBarNames() + { + const tchar* const names[] = { T("Demo"), T("Source Code"), T("Look-and-feel"), 0 }; + + return StringArray ((const tchar**) names); + } + + const PopupMenu getMenuForIndex (int menuIndex, + const String& menuName) + { + ApplicationCommandManager* const commandManager = mainWindow->commandManager; + + PopupMenu menu; + + if (menuIndex == 0) + { + menu.addCommandItem (commandManager, showPathsAndTransforms); + menu.addCommandItem (commandManager, showFontsAndText); + menu.addCommandItem (commandManager, showWidgets); + menu.addCommandItem (commandManager, showThreading); + menu.addCommandItem (commandManager, showTreeView); + menu.addCommandItem (commandManager, showTable); + menu.addCommandItem (commandManager, showAudio); + menu.addCommandItem (commandManager, showDragAndDrop); + menu.addCommandItem (commandManager, showOpenGL); + menu.addCommandItem (commandManager, showQuicktime); + menu.addCommandItem (commandManager, showInterprocessComms); + + menu.addSeparator(); + menu.addCommandItem (commandManager, StandardApplicationCommandIDs::quit); + } + else if (menuIndex == 1) + { + menu.addCommandItem (commandManager, showSourceCode); + } + else if (menuIndex == 2) + { + menu.addCommandItem (commandManager, setDefaultLookAndFeel); + menu.addCommandItem (commandManager, setOldSchoolLookAndFeel); + menu.addSeparator(); + menu.addCommandItem (commandManager, useNativeTitleBar); + +#if JUCE_MAC + menu.addCommandItem (commandManager, useNativeMenus); +#endif + } + + return menu; + } + + void menuItemSelected (int menuItemID, + int topLevelMenuIndex) + { + // all our menu items are invoked automatically as commands, so no need to do + // anything in this callback + } + + //============================================================================== + // The following methods implement the ApplicationCommandTarget interface, allowing + // this window to publish a set of actions it can perform, and which can be mapped + // onto menus, keypresses, etc. + + ApplicationCommandTarget* getNextCommandTarget() + { + // this will return the next parent component that is an ApplicationCommandTarget (in this + // case, there probably isn't one, but it's best to use this method in your own apps). + return findFirstTargetParentComponent(); + } + + void getAllCommands (Array & commands) + { + // this returns the set of all commands that this target can perform.. + const CommandID ids[] = { showPathsAndTransforms, + showFontsAndText, + showWidgets, + showThreading, + showTreeView, + showTable, + showAudio, + showDragAndDrop, + showOpenGL, + showQuicktime, + showInterprocessComms, + showSourceCode, + setDefaultLookAndFeel, + setOldSchoolLookAndFeel, + useNativeTitleBar +#if JUCE_MAC + , useNativeMenus +#endif + }; + + commands.addArray (ids, numElementsInArray (ids)); + } + + // This method is used when something needs to find out the details about one of the commands + // that this object can perform.. + void getCommandInfo (const CommandID commandID, ApplicationCommandInfo& result) + { + const String generalCategory (T("General")); + const String demosCategory (T("Demos")); + + switch (commandID) + { + case showPathsAndTransforms: + result.setInfo (T("Paths and Transforms"), T("Shows the paths & transforms demo"), demosCategory, 0); + result.setTicked (currentDemoId == showPathsAndTransforms); + result.addDefaultKeypress (T('1'), ModifierKeys::commandModifier); + break; + + case showFontsAndText: + result.setInfo (T("Fonts and Text"), T("Shows the fonts & text demo"), demosCategory, 0); + result.setTicked (currentDemoId == showFontsAndText); + result.addDefaultKeypress (T('2'), ModifierKeys::commandModifier); + break; + + case showWidgets: + result.setInfo (T("Widgets"), T("Shows the widgets demo"), demosCategory, 0); + result.setTicked (currentDemoId == showWidgets); + result.addDefaultKeypress (T('3'), ModifierKeys::commandModifier); + break; + + case showThreading: + result.setInfo (T("Multithreading"), T("Shows the threading demo"), demosCategory, 0); + result.setTicked (currentDemoId == showThreading); + result.addDefaultKeypress (T('4'), ModifierKeys::commandModifier); + break; + + case showTreeView: + result.setInfo (T("Treeviews"), T("Shows the treeviews demo"), demosCategory, 0); + result.setTicked (currentDemoId == showTreeView); + result.addDefaultKeypress (T('5'), ModifierKeys::commandModifier); + break; + + case showTable: + result.setInfo (T("Table Components"), T("Shows the table component demo"), demosCategory, 0); + result.setTicked (currentDemoId == showTable); + result.addDefaultKeypress (T('6'), ModifierKeys::commandModifier); + break; + + case showAudio: + result.setInfo (T("Audio"), T("Shows the audio demo"), demosCategory, 0); + result.setTicked (currentDemoId == showAudio); + result.addDefaultKeypress (T('7'), ModifierKeys::commandModifier); + break; + + case showDragAndDrop: + result.setInfo (T("Drag-and-drop"), T("Shows the drag & drop demo"), demosCategory, 0); + result.setTicked (currentDemoId == showDragAndDrop); + result.addDefaultKeypress (T('8'), ModifierKeys::commandModifier); + break; + + case showOpenGL: + result.setInfo (T("OpenGL"), T("Shows the OpenGL demo"), demosCategory, 0); + result.addDefaultKeypress (T('9'), ModifierKeys::commandModifier); + result.setTicked (currentDemoId == showOpenGL); +#ifndef JUCE_OPENGL + result.setActive (false); +#endif + break; + + case showQuicktime: + result.setInfo (T("Quicktime"), T("Shows the Quicktime demo"), demosCategory, 0); + result.addDefaultKeypress (T('b'), ModifierKeys::commandModifier); + result.setTicked (currentDemoId == showQuicktime); +#if ! (JUCE_QUICKTIME && ! JUCE_LINUX) + result.setActive (false); +#endif + break; + + case showInterprocessComms: + result.setInfo (T("Interprocess Comms"), T("Shows the interprocess communications demo"), demosCategory, 0); + result.addDefaultKeypress (T('0'), ModifierKeys::commandModifier); + result.setTicked (currentDemoId == showInterprocessComms); + break; + + case showSourceCode: + result.setInfo (T("Show the source code for this demo"), T("Opens a window containing this demo's source code"), generalCategory, 0); + result.addDefaultKeypress (T('s'), ModifierKeys::commandModifier); + break; + + case setDefaultLookAndFeel: + result.setInfo (T("Use default look-and-feel"), String::empty, generalCategory, 0); + result.setTicked ((typeid (LookAndFeel) == typeid (getLookAndFeel())) != 0); + break; + + case setOldSchoolLookAndFeel: + result.setInfo (T("Use the old, original juce look-and-feel"), String::empty, generalCategory, 0); + result.setTicked ((typeid (OldSchoolLookAndFeel) == typeid (getLookAndFeel())) != 0); + break; + + case useNativeTitleBar: + result.setInfo (T("Use native window title bar"), String::empty, generalCategory, 0); + result.setTicked (mainWindow->isUsingNativeTitleBar()); + break; + +#if JUCE_MAC + case useNativeMenus: + result.setInfo (T("Use the native OSX menu bar"), String::empty, generalCategory, 0); + result.setTicked (MenuBarModel::getMacMainMenu() != 0); + break; +#endif + + default: + break; + }; + } + + // this is the ApplicationCommandTarget method that is used to actually perform one of our commands.. + bool perform (const InvocationInfo& info) + { + switch (info.commandID) + { + case showPathsAndTransforms: + showDemo (createPathsAndTransformsDemo(), BinaryData::pathsandtransformsdemo_cpp); + currentDemoId = showPathsAndTransforms; + break; + + case showFontsAndText: + showDemo (createFontsAndTextDemo(), BinaryData::fontsandtextdemo_cpp); + currentDemoId = showFontsAndText; + break; + + case showWidgets: + showDemo (createWidgetsDemo (mainWindow->commandManager), BinaryData::widgetsdemo_cpp); + currentDemoId = showWidgets; + break; + + case showThreading: + showDemo (createThreadingDemo(), BinaryData::threadingdemo_cpp); + currentDemoId = showThreading; + break; + + case showTreeView: + showDemo (createTreeViewDemo(), BinaryData::treeviewdemo_cpp); + currentDemoId = showTreeView; + break; + + case showTable: + showDemo (createTableDemo(), BinaryData::tabledemo_cpp); + currentDemoId = showTable; + break; + + case showAudio: + showDemo (createAudioDemo(), BinaryData::audiodemo_cpp); + currentDemoId = showAudio; + break; + + case showDragAndDrop: + showDemo (createDragAndDropDemo(), BinaryData::draganddropdemo_cpp); + currentDemoId = showDragAndDrop; + break; + + case showOpenGL: +#if JUCE_OPENGL + showDemo (createOpenGLDemo(), BinaryData::opengldemo_cpp); + currentDemoId = showOpenGL; +#endif + break; + + case showQuicktime: +#if JUCE_QUICKTIME && ! JUCE_LINUX + showDemo (createQuickTimeDemo(), BinaryData::quicktimedemo_cpp); + currentDemoId = showQuicktime; +#endif + break; + + case showInterprocessComms: + showDemo (createInterprocessCommsDemo(), BinaryData::interprocesscommsdemo_cpp); + currentDemoId = showInterprocessComms; + break; + + case showSourceCode: + showSource(); + break; + + case setDefaultLookAndFeel: + LookAndFeel::setDefaultLookAndFeel (0); + break; + + case setOldSchoolLookAndFeel: + LookAndFeel::setDefaultLookAndFeel (&oldLookAndFeel); + break; + + case useNativeTitleBar: + mainWindow->setUsingNativeTitleBar (! mainWindow->isUsingNativeTitleBar()); + break; + +#if JUCE_MAC + case useNativeMenus: + if (MenuBarModel::getMacMainMenu() != 0) + { + MenuBarModel::setMacMainMenu (0); + mainWindow->setMenuBar ((ContentComp*) mainWindow->getContentComponent()); + } + else + { + MenuBarModel::setMacMainMenu ((ContentComp*) mainWindow->getContentComponent()); + mainWindow->setMenuBar (0); + } + + break; +#endif + + default: + return false; + }; + + return true; + } + + juce_UseDebuggingNewOperator +}; + +//============================================================================== +#if JUCE_WIN32 || JUCE_LINUX + +// Just add a simple icon to the Window system tray area.. +class DemoTaskbarComponent : public SystemTrayIconComponent +{ +public: + DemoTaskbarComponent() + { + // Create an icon which is just a square with a "j" in it.. + Image icon (Image::RGB, 32, 32, true); + Graphics g (icon); + g.fillAll (Colours::lightblue); + g.setColour (Colours::black); + g.setFont ((float) icon.getHeight(), Font::bold); + g.drawText (T("j"), 0, 0, icon.getWidth(), icon.getHeight(), Justification::centred, false); + + setIconImage (icon); + + setIconTooltip (T("Juce Demo App!")); + } + + ~DemoTaskbarComponent() + { + } + + void mouseDown (const MouseEvent& e) + { + PopupMenu m; + m.addItem (1, T("Quit the Juce demo")); + + const int result = m.show(); + + if (result == 1) + JUCEApplication::getInstance()->systemRequestedQuit(); + } +}; + +#endif + +//============================================================================== +MainDemoWindow::MainDemoWindow() + : DocumentWindow (T("JUCE Demo!"), + Colours::azure, + DocumentWindow::allButtons, + true) +{ + commandManager = new ApplicationCommandManager(); + + setResizable (true, false); // resizability is a property of ResizableWindow + + ContentComp* contentComp = new ContentComp (this); + + commandManager->registerAllCommandsForTarget (contentComp); + commandManager->registerAllCommandsForTarget (JUCEApplication::getInstance()); + + // this lets the command manager use keypresses that arrive in our window to send + // out commands + addKeyListener (commandManager->getKeyMappings()); + + // sets the main content component for the window to be this tabbed + // panel. This will be deleted when the window is deleted. + setContentComponent (contentComp); + + // this tells the DocumentWindow to automatically create and manage a MenuBarComponent + // which uses our contentComp as its MenuBarModel + setMenuBar (contentComp); + + // tells our menu bar model that it should watch this command manager for + // changes, and send change messages accordingly. + contentComp->setApplicationCommandManagerToWatch (commandManager); + + setVisible (true); + +#if JUCE_WIN32 || JUCE_LINUX + taskbarIcon = new DemoTaskbarComponent(); +#endif +} + +MainDemoWindow::~MainDemoWindow() +{ +#if JUCE_WIN32 || JUCE_LINUX + deleteAndZero (taskbarIcon); +#endif + + // because we've set the content comp to be used as our menu bar model, we + // have to switch this off before deleting the content comp.. + setMenuBar (0); + + // setting our content component to 0 will delete the current one, and + // that will in turn delete all its child components. You don't always + // have to do this explicitly, because the base class's destructor will + // also delete the content component, but in this case we need to + // make sure our content comp has gone away before deleting our command + // manager. + setContentComponent (0, true); + + delete commandManager; +} + +void MainDemoWindow::closeButtonPressed() +{ + // The correct thing to do when you want the app to quit is to call the + // JUCEApplication::systemRequestedQuit() method. + + // That means that requests to quit that come from your own UI, or from other + // OS-specific sources (e.g. the dock menu on the mac) all get handled in the + // same way. + + JUCEApplication::getInstance()->systemRequestedQuit(); +} diff --git a/src/juce_core/containers/juce_ReferenceCountedArray.h b/src/juce_core/containers/juce_ReferenceCountedArray.h index 7b7a956a86..61d8a60275 100644 --- a/src/juce_core/containers/juce_ReferenceCountedArray.h +++ b/src/juce_core/containers/juce_ReferenceCountedArray.h @@ -46,9 +46,12 @@ 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 +template class ReferenceCountedArray : private ArrayAllocationBase { public: @@ -68,26 +71,32 @@ public: } /** Creates a copy of another array */ - ReferenceCountedArray (const ReferenceCountedArray& other) throw() + ReferenceCountedArray (const ReferenceCountedArray& other) throw() : ArrayAllocationBase (other.granularity), numUsed (other.numUsed) { + other.lockArray(); 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& operator= (const ReferenceCountedArray& other) throw() + const ReferenceCountedArray& operator= (const ReferenceCountedArray& other) throw() { if (this != &other) { + other.lockArray(); + lock.enter(); + clear(); this->granularity = other.granularity; @@ -99,6 +108,9 @@ public: for (int i = numUsed; --i >= 0;) if (this->elements[i] != 0) this->elements[i]->incReferenceCount(); + + lock.exit(); + other.unlockArray(); } return *this; @@ -120,12 +132,16 @@ public: */ 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. */ @@ -144,8 +160,10 @@ public: */ inline ObjectClass* operator[] (const int index) const throw() { + lock.enter(); return (index >= 0 && index < numUsed) ? this->elements [index] : (ObjectClass*) 0; + lock.exit(); } /** Returns a pointer to the object at this index in the array, without checking whether the index is in-range. @@ -155,8 +173,10 @@ public: */ inline ObjectClass* getUnchecked (const int index) const throw() { + lock.enter(); jassert (index >= 0 && index < numUsed); return this->elements [index]; + lock.exit(); } /** Returns a pointer to the first object in the array. @@ -166,8 +186,12 @@ public: */ inline ObjectClass* getFirst() const throw() { - return (numUsed > 0) ? this->elements [0] - : (ObjectClass*) 0; + lock.enter(); + ObjectClass* const result = (numUsed > 0) ? this->elements [0] + : (ObjectClass*) 0; + lock.exit(); + + return result; } /** Returns a pointer to the last object in the array. @@ -177,8 +201,12 @@ public: */ inline ObjectClass* getLast() const throw() { - return (numUsed > 0) ? this->elements [numUsed - 1] - : (ObjectClass*) 0; + lock.enter(); + ObjectClass* const result = (numUsed > 0) ? this->elements [numUsed - 1] + : (ObjectClass*) 0; + lock.exit(); + + return result; } //============================================================================== @@ -189,17 +217,24 @@ public: */ 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) - return (int) (e - this->elements); + { + result = (int) (e - this->elements); + break; + } ++e; } - return -1; + lock.exit(); + return result; } /** Returns true if the array contains a specified object. @@ -209,16 +244,21 @@ public: */ 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; } @@ -231,11 +271,14 @@ public: */ 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. @@ -256,6 +299,7 @@ public: { if (indexToInsertAt >= 0) { + lock.enter(); if (indexToInsertAt > numUsed) indexToInsertAt = numUsed; @@ -273,6 +317,7 @@ public: newObject->incReferenceCount(); ++numUsed; + lock.exit(); } else { @@ -289,8 +334,12 @@ public: */ 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. @@ -310,6 +359,8 @@ public: { if (indexToChange >= 0) { + lock.enter(); + if (newObject != 0) newObject->incReferenceCount(); @@ -325,6 +376,8 @@ public: this->ensureAllocatedSize (numUsed + 1); this->elements [numUsed++] = newObject; } + + lock.exit(); } } @@ -337,10 +390,13 @@ public: all available elements will be copied. @see add */ - void addArray (const ReferenceCountedArray& arrayToAddFrom, + void addArray (const ReferenceCountedArray& arrayToAddFrom, int startIndex = 0, int numElementsToAdd = -1) throw() { + arrayToAddFrom.lockArray(); + lock.enter(); + if (startIndex < 0) { jassertfalse @@ -357,6 +413,9 @@ public: while (--numElementsToAdd >= 0) add (arrayToAddFrom.getUnchecked (startIndex++)); } + + lock.exit(); + arrayToAddFrom.unlockArray(); } /** Inserts a new object into the array assuming that the array is sorted. @@ -374,7 +433,9 @@ public: void addSorted (ElementComparator& comparator, ObjectClass* newObject) throw() { + lock.enter(); insert (findInsertIndexInSortedArray (comparator, this->elements, newObject, 0, numUsed), newObject); + lock.exit(); } //============================================================================== @@ -393,6 +454,8 @@ public: */ void remove (const int indexToRemove) { + lock.enter(); + if (indexToRemove >= 0 && indexToRemove < numUsed) { ObjectClass** const e = this->elements + indexToRemove; @@ -409,6 +472,8 @@ public: if ((numUsed << 1) < this->numAllocated) minimiseStorageOverheads(); } + + lock.exit(); } /** Removes the first occurrence of a specified object from the array. @@ -421,7 +486,9 @@ public: */ void removeObject (ObjectClass* const objectToRemove) { + lock.enter(); remove (indexOf (objectToRemove)); + lock.exit(); } /** Removes a range of objects from the array. @@ -442,6 +509,8 @@ public: 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); @@ -471,6 +540,8 @@ public: if ((numUsed << 1) < this->numAllocated) minimiseStorageOverheads(); } + + lock.exit(); } /** Removes the last n objects from the array. @@ -483,11 +554,15 @@ public: */ 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. @@ -498,12 +573,16 @@ public: void swap (const int index1, const int index2) throw() { + lock.enter(); + if (index1 >= 0 && index1 < numUsed && index2 >= 0 && index2 < numUsed) { swapVariables (this->elements [index1], this->elements [index2]); } + + lock.exit(); } /** Moves one of the objects to a different position. @@ -524,6 +603,8 @@ public: { if (currentIndex != newIndex) { + lock.enter(); + if (currentIndex >= 0 && currentIndex < numUsed) { if (newIndex < 0 || newIndex > numUsed - 1) @@ -546,6 +627,8 @@ public: this->elements [newIndex] = value; } + + lock.exit(); } } @@ -554,23 +637,36 @@ public: @returns true only if the other array contains the same objects in the same order */ - bool operator== (const ReferenceCountedArray& other) const throw() + bool operator== (const ReferenceCountedArray& other) const throw() { - if (numUsed != other.numUsed) - return false; + other.lockArray(); + lock.enter(); - for (int i = numUsed; --i >= 0;) - if (this->elements [i] != other.elements [i]) - return false; + bool result = numUsed == other.numUsed; + + if (result) + { + for (int i = numUsed; --i >= 0;) + { + if (this->elements [i] != other.elements [i]) + { + result = false; + break; + } + } + } - return true; + lock.exit(); + other.unlockArray(); + + return result; } /** Compares this array to another one. @see operator== */ - bool operator!= (const ReferenceCountedArray& other) const throw() + bool operator!= (const ReferenceCountedArray& other) const throw() { return ! operator== (other); } @@ -608,7 +704,10 @@ public: { (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(); } //============================================================================== @@ -620,6 +719,8 @@ public: */ void minimiseStorageOverheads() throw() { + lock.enter(); + if (numUsed == 0) { this->setAllocatedSize (0); @@ -631,6 +732,33 @@ public: 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(); } @@ -639,6 +767,7 @@ public: private: int numUsed; + TypeOfCriticalSectionToUse lock; }; diff --git a/src/juce_core/containers/juce_ReferenceCountedObject.h b/src/juce_core/containers/juce_ReferenceCountedObject.h index 4dbdcae525..e13cb9c1ec 100644 --- a/src/juce_core/containers/juce_ReferenceCountedObject.h +++ b/src/juce_core/containers/juce_ReferenceCountedObject.h @@ -178,10 +178,11 @@ public: if (newObject != 0) newObject->incReferenceCount(); - if (referencedObject != 0) - referencedObject->decReferenceCount(); - + ReferenceCountedObjectClass* const oldObject = referencedObject; referencedObject = newObject; + + if (oldObject != 0) + oldObject->decReferenceCount(); } return *this; @@ -199,10 +200,11 @@ public: if (newObject != 0) newObject->incReferenceCount(); - if (referencedObject != 0) - referencedObject->decReferenceCount(); - + ReferenceCountedObjectClass* const oldObject = referencedObject; referencedObject = newObject; + + if (oldObject != 0) + oldObject->decReferenceCount(); } return *this;