]> git.sesse.net Git - vlc/commitdiff
* all: first support of graphical equalizer in the skins2 interface.
authorCyril Deguet <asmax@videolan.org>
Wed, 2 Nov 2005 22:01:06 +0000 (22:01 +0000)
committerCyril Deguet <asmax@videolan.org>
Wed, 2 Nov 2005 22:01:06 +0000 (22:01 +0000)
  It can be defined in the XML with usual sliders, using the new
  variables equalizer.band(0), ..., equalizer.band(9)
  (0% means -20dB, and 100% means +20 dB).
  More things to come, but it is already working as is.
* all: AsyncQueue::push() now does also a remove() by default, as
  the two methods are always called together

13 files changed:
modules/gui/skins2/Modules.am
modules/gui/skins2/commands/async_queue.cpp
modules/gui/skins2/commands/async_queue.hpp
modules/gui/skins2/commands/cmd_vars.cpp
modules/gui/skins2/commands/cmd_vars.hpp
modules/gui/skins2/controls/ctrl_resize.cpp
modules/gui/skins2/controls/ctrl_video.cpp
modules/gui/skins2/src/dialogs.cpp
modules/gui/skins2/src/theme_repository.cpp
modules/gui/skins2/src/vlcproc.cpp
modules/gui/skins2/src/vlcproc.hpp
modules/gui/skins2/vars/equalizer.cpp [new file with mode: 0644]
modules/gui/skins2/vars/equalizer.hpp [new file with mode: 0644]

index 6853d819c99c9524c1a8a744f21823ddb70443a2..9246346ce11c963e51b9b8961a42cc641172976a 100644 (file)
@@ -160,6 +160,8 @@ SOURCES_skins2 = \
        utils/var_tree.cpp \
        utils/var_tree.hpp \
        \
+       vars/equalizer.cpp \
+       vars/equalizer.hpp \
        vars/playlist.cpp \
        vars/playlist.hpp \
        vars/playtree.cpp \
index bb670d1dc9089f758ede3a36ec775025d2b12545..170cec763386391efb262333fb754b97417dbc02 100644 (file)
@@ -75,8 +75,13 @@ void AsyncQueue::destroy( intf_thread_t *pIntf )
 }
 
 
-void AsyncQueue::push( const CmdGenericPtr &rcCommand )
+void AsyncQueue::push( const CmdGenericPtr &rcCommand, bool removePrev )
 {
+    if( removePrev )
+    {
+        // Remove the commands of the same type
+        remove( rcCommand.get()->getType() );
+    }
     m_cmdList.push_back( rcCommand );
 }
 
index 109b1adcf2a679e0bdc6bc0118e542f1f34a78e7..e22666b17ea8574b4e703bad451f7530f0a85a5b 100644 (file)
@@ -44,8 +44,9 @@ class AsyncQueue: public SkinObject
         /// Destroy the instance of AsyncQueue
         static void destroy( intf_thread_t *pIntf );
 
-        /// Add a command in the queue
-        void push( const CmdGenericPtr &rcCommand );
+        /// Add a command in the queue, after having removed the commands
+        /// of the same type already in the queue if needed
+        void push( const CmdGenericPtr &rcCommand, bool removePrev = true );
 
         /// Remove the commands of the given type
         void remove( const string &rType );
index 9cd849a71c9bbb7abcf395f8e30005df25d59e67..9c846f0d70cbb45face805119efb2df2e7f6c730 100644 (file)
@@ -24,6 +24,7 @@
 #include "cmd_vars.hpp"
 #include "../src/vlcproc.hpp"
 #include "../utils/var_text.hpp"
+#include "../vars/equalizer.hpp"
 #include "../vars/playlist.hpp"
 #include "../vars/playtree.hpp"
 
@@ -57,3 +58,10 @@ void CmdSetText::execute()
     m_rText.set( m_value );
 }
 
+
+void CmdSetEqBands::execute()
+{
+    // Change the equalizer bands
+    m_rEqBands.set( m_value );
+}
+
index fbeb07276e635409606f5e815423e2ad58276ffc..de31378a4cfc53b9aa8ca45944b8e9b4faed2928 100644 (file)
@@ -27,6 +27,7 @@
 #include "cmd_generic.hpp"
 #include "../utils/ustring.hpp"
 
+class EqualizerBands;
 class VarText;
 
 /// Command to notify the playlist of a change
@@ -78,4 +79,27 @@ class CmdSetText: public CmdGeneric
 };
 
 
+/// Command to set the equalizerbands
+class CmdSetEqBands: public CmdGeneric
+{
+    public:
+        CmdSetEqBands( intf_thread_t *pIntf, EqualizerBands &rEqBands,
+                       const string &rValue ):
+            CmdGeneric( pIntf ), m_rEqBands( rEqBands ), m_value( rValue ) {}
+        virtual ~CmdSetEqBands() {}
+
+        /// This method does the real job of the command
+        virtual void execute();
+
+        /// Return the type of the command
+        virtual string getType() const { return "set equalizer bands"; }
+
+    private:
+        /// Equalizer variable to set
+        EqualizerBands &m_rEqBands;
+        /// Value to set
+        const string m_value;
+};
+
+
 #endif
index e7de6156f31f6775102e02c0823883b5bf6120f5..46ade24792ce2497e69fe4e76f11a070235533eb 100644 (file)
@@ -168,6 +168,5 @@ void CtrlResize::CmdResizeResize::execute()
                                       newWidth, newHeight );
     // Push the command in the asynchronous command queue
     AsyncQueue *pQueue = AsyncQueue::instance( m_pParent->getIntf() );
-    pQueue->remove( "resize" );
     pQueue->push( CmdGenericPtr( pCmd ) );
 }
index a8ea232d18b5cc5093d2b22d695e7508b8d1b6bc..6bedf51ff0806d4a0641ca2f2ce1b313b74377d7 100644 (file)
@@ -119,7 +119,6 @@ void CtrlVideo::onUpdate( Subject<VarBox> &rVoutSize )
                                       newHeight );
     // Push the command in the asynchronous command queue
     AsyncQueue *pQueue = AsyncQueue::instance( getIntf() );
-    pQueue->remove( "resize" );
     pQueue->push( CmdGenericPtr( pCmd ) );
 }
 
index 7c23d7fdf3cf810863a5ff62ac1b51f7f7802b8e..7605242c9fbfcde29436de62bde61dfb3745dc15 100644 (file)
@@ -45,7 +45,6 @@ void Dialogs::showChangeSkinCB( intf_dialog_args_t *pArg )
 
             // Push the command in the asynchronous command queue
             AsyncQueue *pQueue = AsyncQueue::instance( pIntf );
-            pQueue->remove( "change skin" );
             pQueue->push( CmdGenericPtr( pCmd ) );
         }
     }
@@ -71,8 +70,6 @@ void Dialogs::showPlaylistLoadCB( intf_dialog_args_t *pArg )
 
         // Push the command in the asynchronous command queue
         AsyncQueue *pQueue = AsyncQueue::instance( pIntf );
-        pQueue->remove( "load playlist" );
-        pQueue->remove( "load playtree" );
         pQueue->push( CmdGenericPtr( pCmd ) );
     }
 }
@@ -90,8 +87,6 @@ void Dialogs::showPlaylistSaveCB( intf_dialog_args_t *pArg )
 
         // Push the command in the asynchronous command queue
         AsyncQueue *pQueue = AsyncQueue::instance( pIntf );
-        pQueue->remove( "load playlist" );
-        pQueue->remove( "load playtree" );
         pQueue->push( CmdGenericPtr( pCmd ) );
     }
 }
index fd1f9ec93c0e5722a763538d7cb31b6d7ff6f729..ab3d1c5feaf360bb699f9aa2201c2656cdcd5004 100644 (file)
@@ -163,7 +163,6 @@ int ThemeRepository::changeSkin( vlc_object_t *pIntf, char const *pCmd,
         CmdChangeSkin *pCmd = new CmdChangeSkin( pThis->getIntf(),
                                                  newval.psz_string );
         AsyncQueue *pQueue = AsyncQueue::instance( pThis->getIntf() );
-        pQueue->remove( "change skin" );
         pQueue->push( CmdGenericPtr( pCmd ) );
     }
 
index 16bdd036c0e03958da2afc1f31d79b5abb497c61..6340dea2c2ce821965d66c852cf13e120941bca5 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <vlc/aout.h>
 #include <vlc/vout.h>
+#include <aout_internal.h>
 
 #include "vlcproc.hpp"
 #include "os_factory.hpp"
@@ -38,6 +39,7 @@
 #include "../commands/cmd_resize.hpp"
 #include "../commands/cmd_vars.hpp"
 #include "../utils/var_bool.hpp"
+#include <sstream>
 
 
 VlcProc *VlcProc::instance( intf_thread_t *pIntf )
@@ -61,8 +63,9 @@ void VlcProc::destroy( intf_thread_t *pIntf )
 }
 
 
-VlcProc::VlcProc( intf_thread_t *pIntf ): SkinObject( pIntf ), m_pVout( NULL ),
-    m_cmdManage( this ), m_varVoutSize( pIntf )
+VlcProc::VlcProc( intf_thread_t *pIntf ): SkinObject( pIntf ),
+    m_varVoutSize( pIntf ), m_varEqBands( pIntf ), m_pVout( NULL ),
+    m_pAout( NULL ), m_cmdManage( this )
 {
     // Create a timer to poll the status of the vlc
     OSFactory *pOsFactory = OSFactory::instance( pIntf );
@@ -100,6 +103,14 @@ VlcProc::VlcProc( intf_thread_t *pIntf ): SkinObject( pIntf ), m_pVout( NULL ),
     m_cVarStreamURI = VariablePtr( new VarText( getIntf(), false ) );
     pVarManager->registerVar( m_cVarStreamURI, "streamURI" );
 
+    // Register the equalizer bands
+    for( int i = 0; i < EqualizerBands::kNbBands; i++)
+    {
+        stringstream ss;
+        ss << "equalizer.band(" << i << ")";
+        pVarManager->registerVar( m_varEqBands.getBand( i ), ss.str() );
+    }
+
     // XXX WARNING XXX
     // The object variable callbacks are called from other VLC threads,
     // so they must put commands in the queue and NOT do anything else
@@ -208,24 +219,18 @@ void VlcProc::manage()
 
     // Get the VLC variables
     StreamTime *pTime = (StreamTime*)m_cVarTime.get();
-    Volume *pVolume = (Volume*)m_cVarVolume.get();
     VarBoolImpl *pVarPlaying = (VarBoolImpl*)m_cVarPlaying.get();
     VarBoolImpl *pVarStopped = (VarBoolImpl*)m_cVarStopped.get();
     VarBoolImpl *pVarPaused = (VarBoolImpl*)m_cVarPaused.get();
     VarBoolImpl *pVarSeekable = (VarBoolImpl*)m_cVarSeekable.get();
-    VarBoolImpl *pVarMute = (VarBoolImpl*)m_cVarMute.get();
     VarBoolImpl *pVarRandom = (VarBoolImpl*)m_cVarRandom.get();
     VarBoolImpl *pVarLoop = (VarBoolImpl*)m_cVarLoop.get();
     VarBoolImpl *pVarRepeat = (VarBoolImpl*)m_cVarRepeat.get();
 
-    // Refresh sound volume
-    audio_volume_t volume;
-    aout_VolumeGet( getIntf(), &volume );
-    pVolume->set( (double)volume * 2.0 / AOUT_VOLUME_MAX );
-    // Set the mute variable
-    pVarMute->set( volume == 0 );
+    // Refresh audio variables
+    refreshAudio();
 
-    // Update the input
+   // Update the input
     if( getIntf()->p_sys->p_input == NULL )
     {
         getIntf()->p_sys->p_input = (input_thread_t *)vlc_object_find(
@@ -287,6 +292,39 @@ void VlcProc::CmdManage::execute()
 }
 
 
+void VlcProc::refreshAudio()
+{
+    // Check if the audio output has changed
+    aout_instance_t *pAout = (aout_instance_t *)vlc_object_find( getIntf(),
+            VLC_OBJECT_AOUT, FIND_ANYWHERE );
+    if( pAout != NULL )
+    {
+        if( pAout != m_pAout )
+        {
+            // Register the equalizer callback
+            if( !var_AddCallback( pAout, "equalizer-bands",
+                                 onEqBandsChange, this ) )
+            {
+                m_pAout = pAout;
+
+                //char * psz_bands = var_GetString( p_aout, "equalizer-bands" );
+            }
+        }
+        vlc_object_release( pAout );
+    }
+
+    // Refresh sound volume
+    audio_volume_t volume;
+    aout_VolumeGet( getIntf(), &volume );
+    Volume *pVolume = (Volume*)m_cVarVolume.get();
+    pVolume->set( (double)volume * 2.0 / AOUT_VOLUME_MAX );
+
+    // Set the mute variable
+    VarBoolImpl *pVarMute = (VarBoolImpl*)m_cVarMute.get();
+    pVarMute->set( volume == 0 );
+}
+
+
 int VlcProc::onIntfChange( vlc_object_t *pObj, const char *pVariable,
                            vlc_value_t oldVal, vlc_value_t newVal,
                            void *pParam )
@@ -304,8 +342,6 @@ int VlcProc::onIntfChange( vlc_object_t *pObj, const char *pVariable,
 
     // Push the command in the asynchronous command queue
     AsyncQueue *pQueue = AsyncQueue::instance( pThis->getIntf() );
-    pQueue->remove( "notify playlist" );
-    pQueue->remove( "playtree changed" );
     pQueue->push( CmdGenericPtr( pCmd ) );
     pQueue->push( CmdGenericPtr( pCmdTree ) );
 
@@ -327,7 +363,6 @@ int VlcProc::onIntfShow( vlc_object_t *pObj, const char *pVariable,
 
         // Push the command in the asynchronous command queue
         AsyncQueue *pQueue = AsyncQueue::instance( pThis->getIntf() );
-        pQueue->remove( "raise all windows" );
         pQueue->push( CmdGenericPtr( pCmd ) );
     }
 
@@ -354,8 +389,6 @@ int VlcProc::onItemChange( vlc_object_t *pObj, const char *pVariable,
 
     // Push the command in the asynchronous command queue
     AsyncQueue *pQueue = AsyncQueue::instance( pThis->getIntf() );
-    pQueue->remove( "notify playlist" );
-    pQueue->remove( "playtree update" );
     pQueue->push( CmdGenericPtr( pCmd ) );
     pQueue->push( CmdGenericPtr( pCmdTree ) );
 
@@ -382,8 +415,6 @@ int VlcProc::onPlaylistChange( vlc_object_t *pObj, const char *pVariable,
     CmdPlaytreeChanged *pCmdTree = new CmdPlaytreeChanged( pThis->getIntf() );
 
     // Push the command in the asynchronous command queue
-    pQueue->remove( "notify playlist" );
-    pQueue->remove( "playtree changed" );
     pQueue->push( CmdGenericPtr( pCmd ) );
     pQueue->push( CmdGenericPtr( pCmdTree ) );
 
@@ -403,7 +434,6 @@ int VlcProc::onSkinToLoad( vlc_object_t *pObj, const char *pVariable,
 
     // Push the command in the asynchronous command queue
     AsyncQueue *pQueue = AsyncQueue::instance( pThis->getIntf() );
-    pQueue->remove( "change skin" );
     pQueue->push( CmdGenericPtr( pCmd ) );
 
     return VLC_SUCCESS;
@@ -461,7 +491,6 @@ void *VlcProc::getWindow( intf_thread_t *pIntf, vout_thread_t *pVout,
         CmdResizeVout *pCmd = new CmdResizeVout( pThis->getIntf(), pWindow,
                                                  *pWidthHint, *pHeightHint );
         AsyncQueue *pQueue = AsyncQueue::instance( pThis->getIntf() );
-        pQueue->remove( "resize vout" );
         pQueue->push( CmdGenericPtr( pCmd ) );
         return pWindow;
     }
@@ -492,7 +521,6 @@ int VlcProc::controlWindow( intf_thread_t *pIntf, void *pWindow,
                                        pThis->m_pVout->i_window_width,
                                        pThis->m_pVout->i_window_height );
                 AsyncQueue *pQueue = AsyncQueue::instance( pThis->getIntf() );
-                pQueue->remove( "resize vout" );
                 pQueue->push( CmdGenericPtr( pCmd ) );
             }
         }
@@ -505,3 +533,20 @@ int VlcProc::controlWindow( intf_thread_t *pIntf, void *pWindow,
     return VLC_SUCCESS;
 }
 
+
+int VlcProc::onEqBandsChange( vlc_object_t *pObj, const char *pVariable,
+                              vlc_value_t oldVal, vlc_value_t newVal,
+                              void *pParam )
+{
+    VlcProc *pThis = (VlcProc*)pParam;
+
+    // Post a set equalizer bands command
+    CmdSetEqBands *pCmd = new CmdSetEqBands( pThis->getIntf(),
+                                             pThis->m_varEqBands,
+                                             newVal.psz_string );
+    AsyncQueue *pQueue = AsyncQueue::instance( pThis->getIntf() );
+    pQueue->push( CmdGenericPtr( pCmd ) );
+
+    return VLC_SUCCESS;
+}
+
index d56a59e1fe3114b853b50a87808848c28dc184ea..08da857c3689921246c4df55d23bd724016b9c2a 100644 (file)
@@ -27,6 +27,7 @@
 
 #include <set>
 
+#include "../vars/equalizer.hpp"
 #include "../vars/playlist.hpp"
 #include "../vars/playtree.hpp"
 #include "../vars/time.hpp"
@@ -37,6 +38,7 @@
 
 class OSTimer;
 class VarBool;
+struct aout_instance_t;
 
 
 /// Singleton object handling VLC internal state and playlist
@@ -117,6 +119,8 @@ class VlcProc: public SkinObject
         VariablePtr m_cVarSeekable;
         /// Variable for the vout
         VarBox m_varVoutSize;
+        /// Equalizer variable
+        EqualizerBands m_varEqBands;
 
         /// Set of handles of vout windows
         /**
@@ -126,6 +130,8 @@ class VlcProc: public SkinObject
         set<void *> m_handleSet;
         /// Vout thread
         vout_thread_t *m_pVout;
+        /// Audio output
+        aout_instance_t *m_pAout;
 
         /**
          * Poll VLC internals to update the status (volume, current time in
@@ -139,6 +145,9 @@ class VlcProc: public SkinObject
         /// Define the command that calls manage()
         DEFINE_CALLBACK( VlcProc, Manage );
 
+        /// Refresh audio variables
+        void refreshAudio();
+
         /// Update the stream name variable
         void updateStreamName( playlist_t *p_playlist );
 
@@ -179,6 +188,11 @@ class VlcProc: public SkinObject
         /// Callback to change a vout window
         static int controlWindow( intf_thread_t *pIntf, void *pWindow,
                                   int query, va_list args );
+
+        /// Callback for equalizer-bands variable
+        static int onEqBandsChange( vlc_object_t *pObj, const char *pVariable,
+                                    vlc_value_t oldVal, vlc_value_t newVal,
+                                    void *pParam );
 };
 
 
diff --git a/modules/gui/skins2/vars/equalizer.cpp b/modules/gui/skins2/vars/equalizer.cpp
new file mode 100644 (file)
index 0000000..a9eb507
--- /dev/null
@@ -0,0 +1,109 @@
+/*****************************************************************************
+ * equalizer.cpp
+ *****************************************************************************
+ * Copyright (C) 2003 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Cyril Deguet     <asmax@via.ecp.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it 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.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ *****************************************************************************/
+
+
+#include "vlc/aout.h"
+#include "aout_internal.h"
+#include "equalizer.hpp"
+#include "../utils/var_percent.hpp"
+#include <ios>
+#include <iomanip>
+#include <sstream>
+
+
+EqualizerBands::EqualizerBands( intf_thread_t *pIntf ): SkinObject( pIntf ),
+    m_isUpdating( false )
+{
+    for( int i = 0; i < kNbBands; i++ )
+    {
+        // Create and observe the band variables
+        VarPercent *pVar = new VarPercent( pIntf );
+        m_cBands[i] = VariablePtr( pVar );
+        pVar->set( 0.5f );
+        pVar->addObserver( this );
+    }
+}
+
+
+EqualizerBands::~EqualizerBands()
+{
+    for( int i = 0; i < kNbBands; i++ )
+    {
+        ((VarPercent*)m_cBands[i].get())->delObserver( this );
+    }
+}
+
+
+void EqualizerBands::set( string bands )
+{
+    float val;
+    stringstream ss( bands );
+
+    m_isUpdating = true;
+    // Parse the string
+    for( int i = 0; i < kNbBands; i++ )
+    {
+        ss >> val;
+        // Set the band value in percent
+        ((VarPercent*)m_cBands[i].get())->set( (val + 20) / 40 );
+    }
+    m_isUpdating = false;
+}
+
+
+VariablePtr EqualizerBands::getBand( int band )
+{
+    return m_cBands[band];
+}
+
+
+void EqualizerBands::onUpdate( Subject<VarPercent> &rBand )
+{
+    // Make sure we are not called from set()
+    if (!m_isUpdating)
+    {
+        float val;
+        stringstream ss;
+        // Write one digit after the floating point
+        ss << setprecision( 1 ) << setiosflags( ios::fixed );
+
+        // Convert the band values to a string
+        val = 40 * ((VarPercent*)m_cBands[0].get())->get() - 20;
+        ss << val;
+        for( int i = 1; i < kNbBands; i++ )
+        {
+            val = 40 * ((VarPercent*)m_cBands[i].get())->get() - 20;
+            ss << " " << val;
+        }
+
+        aout_instance_t *pAout= (aout_instance_t *)vlc_object_find( getIntf(),
+                VLC_OBJECT_AOUT, FIND_ANYWHERE );
+        if( pAout )
+        {
+            // Update the audio output
+            var_SetString( pAout, "equalizer-bands", (char*)ss.str().c_str() );
+            vlc_object_release( pAout );
+        }
+    }
+}
+
diff --git a/modules/gui/skins2/vars/equalizer.hpp b/modules/gui/skins2/vars/equalizer.hpp
new file mode 100644 (file)
index 0000000..08f45bc
--- /dev/null
@@ -0,0 +1,59 @@
+/*****************************************************************************
+ * equalizer.hpp
+ *****************************************************************************
+ * Copyright (C) 2003 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Cyril Deguet     <asmax@via.ecp.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it 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.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ *****************************************************************************/
+
+#ifndef EQUALIZER_HPP
+#define EQUALIZER_HPP
+
+#include "../utils/var_percent.hpp"
+#include <string>
+
+
+/// Variable for graphical equalizer
+class EqualizerBands: public SkinObject, public Observer<VarPercent>
+{
+    public:
+        /// Number of bands
+        static const int kNbBands = 10;
+
+        EqualizerBands( intf_thread_t *pIntf );
+        virtual ~EqualizerBands();
+
+        /// Set the equalizer bands from a configuration string,
+        /// e.g. "1 5.2 -3.6 0 0 2.5 0 0 0 0"
+        void set( string bands );
+
+        /// Return the variable for a specific band
+        VariablePtr getBand( int band );
+
+    private:
+        /// Array of equalizer bands
+        VariablePtr m_cBands[kNbBands];
+        /// Flag set when an update is in progress
+        bool m_isUpdating;
+
+        /// Callback for band updates
+        virtual void onUpdate( Subject<VarPercent> &rBand );
+};
+
+
+#endif