]> git.sesse.net Git - vlc/blobdiff - modules/gui/qt4/main_interface.cpp
Qt: speed and time button actionable visual feedback
[vlc] / modules / gui / qt4 / main_interface.cpp
index 99ef957bcd56502df818168d862e725402077dd5..99e1409e1f7ed549550385b87c0f0de4b8e0ebb7 100644 (file)
@@ -34,7 +34,7 @@
 #include "actions_manager.hpp"                  // killInstance
 #include "extensions_manager.hpp"               // killInstance
 
-#include "util/customwidgets.hpp"               // qtEventToVLCKey
+#include "util/customwidgets.hpp"               // qtEventToVLCKey, QVLCStackedWidget
 #include "util/qt_dirs.hpp"                     // toNativeSeparators
 
 #include "components/interface_widgets.hpp"     // bgWidget, videoWidget
 #include <vlc_keys.h>                       /* Wheel event */
 #include <vlc_vout_display.h>               /* vout_thread_t and VOUT_ events */
 
-#ifdef WIN32 /* Win7 taskbar */
- #include <vlc_windows_interfaces.h>
- #include <QBitmap>
-#endif
-
 // #define DEBUG_INTF
 
 /* Callback prototypes */
@@ -80,6 +75,7 @@ MainInterface::MainInterface( intf_thread_t *_p_intf ) : QVLCMW( _p_intf )
     bgWidget             = NULL;
     videoWidget          = NULL;
     playlistWidget       = NULL;
+    stackCentralOldWidget= NULL;
 #ifndef HAVE_MAEMO
     sysTray              = NULL;
 #endif
@@ -89,9 +85,8 @@ MainInterface::MainInterface( intf_thread_t *_p_intf ) : QVLCMW( _p_intf )
     inputC               = NULL;
 
     b_hideAfterCreation  = false; // --qt-start-minimized
-    playlistVisible      = false; // FIXME remove
+    playlistVisible      = false;
     input_name           = "";
-    i_bg_height          = 0;
 
 
     /* Ask for Privacy */
@@ -223,20 +218,16 @@ MainInterface::MainInterface( intf_thread_t *_p_intf ) : QVLCMW( _p_intf )
     {
         CONNECT( this, askVideoToResize( unsigned int, unsigned int ),
                  videoWidget, SetSizing( unsigned int, unsigned int ) );
+        CONNECT( videoWidget, sizeChanged( int, int ),
+                 this, resizeStack( int,  int ) );
         CONNECT( this, askVideoSetFullScreen( bool ),
                  videoWidget, SetFullScreen( bool ) );
         CONNECT( videoWidget, keyPressed( QKeyEvent * ),
                  this, handleKeyPress( QKeyEvent * ) );
     }
 
-    CONNECT( this, askUpdate(), this, doComponentsUpdate() );
     CONNECT( THEDP, toolBarConfUpdated(), this, recreateToolbars() );
 
-    /* Enable the popup menu in the MI */
-    setContextMenuPolicy( Qt::CustomContextMenu );
-    CONNECT( this, customContextMenuRequested( const QPoint& ),
-             this, popupMenu( const QPoint& ) );
-
     /** END of CONNECTS**/
 
 
@@ -303,10 +294,10 @@ MainInterface::~MainInterface()
     CoUninitialize();
 #endif
 
-    /* Be sure to kill the actionsManager... FIXME */
+    /* Be sure to kill the actionsManager... Only used in the MI and control */
     ActionsManager::killInstance();
 
-    /* Idem, FIXME */
+    /* Idem */
     ExtensionsManager::killInstance();
 
     /* Delete the FSC controller */
@@ -343,16 +334,15 @@ MainInterface::~MainInterface()
 void MainInterface::recreateToolbars()
 {
     // FIXME: do the same for the FSC
-    //msg_Dbg( p_intf, "Recreating the toolbars" );
     settings->beginGroup( "MainWindow" );
     delete controls;
     delete inputC;
 
     controls = new ControlsWidget( p_intf, false, this ); /* FIXME */
     CONNECT( controls, advancedControlsToggled( bool ),
-             this, doComponentsUpdate() );
+             this, adaptGeometry() );
     CONNECT( controls, sizeChanged(),
-             this, doComponentsUpdate() );
+             this, adaptGeometry() );
 
     inputC = new InputControlsWidget( p_intf, this );
 
@@ -372,7 +362,7 @@ void MainInterface::createMainWidget( QSettings *settings )
     mainLayout->setSpacing( 0 ); mainLayout->setMargin( 0 );
 
     /* */
-    stackCentralW = new QStackedWidget( main );
+    stackCentralW = new QVLCStackedWidget( main );
 
     /* Bg Cone */
     bgWidget = new BackgroundWidget( p_intf );
@@ -390,9 +380,9 @@ void MainInterface::createMainWidget( QSettings *settings )
     controls = new ControlsWidget( p_intf,
                    settings->value( "adv-controls", false ).toBool(), this );
     CONNECT( controls, advancedControlsToggled( bool ),
-             this, doComponentsUpdate() );
+             this, adaptGeometry() );
     CONNECT( controls, sizeChanged(),
-             this, doComponentsUpdate() );
+             this, adaptGeometry() );
     inputC = new InputControlsWidget( p_intf, this );
 
     mainLayout->insertWidget( 2, inputC );
@@ -408,6 +398,11 @@ void MainInterface::createMainWidget( QSettings *settings )
 
     getSettings()->endGroup();
 
+    /* Enable the popup menu in the MI */
+    main->setContextMenuPolicy( Qt::CustomContextMenu );
+    CONNECT( main, customContextMenuRequested( const QPoint& ),
+             this, popupMenu( const QPoint& ) );
+
     if ( depth() > 8 ) /* 8bit depth has too many issues with opacity */
         /* Create the FULLSCREEN CONTROLS Widget */
         if( var_InheritBool( p_intf, "qt-fs-controller" ) )
@@ -458,6 +453,10 @@ inline void MainInterface::createStatusBar()
     timeLabel->setFrameStyle( QFrame::Sunken | QFrame::Panel );
     speedLabel->setFrameStyle( QFrame::Sunken | QFrame::Panel );
     nameLabel->setFrameStyle( QFrame::Sunken | QFrame::StyledPanel);
+    timeLabel->setStyleSheet(
+            "QLabel:hover { background-color: rgba(255, 255, 255, 50%) }" );
+    speedLabel->setStyleSheet(
+            "QLabel:hover { background-color: rgba(255, 255, 255, 50%) }" );
 
     /* and adding those */
     statusBarr->addWidget( nameLabel, 8 );
@@ -477,292 +476,33 @@ inline void MainInterface::createStatusBar()
              timeLabel, setDisplayPosition( float ) );
 }
 
-#ifdef WIN32
-void MainInterface::createTaskBarButtons()
-{
-    taskbar_wmsg = WM_NULL;
-    /*Here is the code for the taskbar thumb buttons
-    FIXME:We need pretty buttons in 16x16 px that are handled correctly by masks in Qt
-    FIXME:the play button's picture doesn't changed to pause when clicked
-    */
-
-    CoInitialize( 0 );
-
-    if( S_OK == CoCreateInstance( &clsid_ITaskbarList,
-                NULL, CLSCTX_INPROC_SERVER,
-                &IID_ITaskbarList3,
-                (void **)&p_taskbl) )
-    {
-        p_taskbl->vt->HrInit(p_taskbl);
-
-        if(himl = ImageList_Create( 15, //cx
-                        18, //cy
-                        ILC_COLOR,//flags
-                        4,//initial nb of images
-                        0//nb of images that can be added
-                        ))
-        {
-            QPixmap img   = QPixmap(":/toolbar/previous_b");
-            QPixmap img2  = QPixmap(":/toolbar/pause_b");
-            QPixmap img3  = QPixmap(":/toolbar/play_b");
-            QPixmap img4  = QPixmap(":/toolbar/next_b");
-            QBitmap mask  = img.createMaskFromColor(Qt::transparent);
-            QBitmap mask2 = img2.createMaskFromColor(Qt::transparent);
-            QBitmap mask3 = img3.createMaskFromColor(Qt::transparent);
-            QBitmap mask4 = img4.createMaskFromColor(Qt::transparent);
-
-            if(-1 == ImageList_Add(himl, img.toWinHBITMAP(QPixmap::PremultipliedAlpha),mask.toWinHBITMAP()))
-                msg_Err( p_intf, "ImageList_Add failed" );
-            if(-1 == ImageList_Add(himl, img2.toWinHBITMAP(QPixmap::PremultipliedAlpha),mask2.toWinHBITMAP()))
-                msg_Err( p_intf, "ImageList_Add failed" );
-            if(-1 == ImageList_Add(himl, img3.toWinHBITMAP(QPixmap::PremultipliedAlpha),mask3.toWinHBITMAP()))
-                msg_Err( p_intf, "ImageList_Add failed" );
-            if(-1 == ImageList_Add(himl, img4.toWinHBITMAP(QPixmap::PremultipliedAlpha),mask4.toWinHBITMAP()))
-                msg_Err( p_intf, "ImageList_Add failed" );
-        }
-
-        // Define an array of two buttons. These buttons provide images through an
-        // image list and also provide tooltips.
-        DWORD dwMask = THB_BITMAP | THB_FLAGS;
-
-        THUMBBUTTON thbButtons[3];
-        thbButtons[0].dwMask = dwMask;
-        thbButtons[0].iId = 0;
-        thbButtons[0].iBitmap = 0;
-        thbButtons[0].dwFlags = THBF_HIDDEN;
-
-        thbButtons[1].dwMask = dwMask;
-        thbButtons[1].iId = 1;
-        thbButtons[1].iBitmap = 2;
-        thbButtons[1].dwFlags = THBF_HIDDEN;
-
-        thbButtons[2].dwMask = dwMask;
-        thbButtons[2].iId = 2;
-        thbButtons[2].iBitmap = 3;
-        thbButtons[2].dwFlags = THBF_HIDDEN;
-
-        HRESULT hr = p_taskbl->vt->ThumbBarSetImageList(p_taskbl, winId(), himl );
-        if(S_OK != hr)
-            msg_Err( p_intf, "ThumbBarSetImageList failed with error %08x", hr );
-        else
-        {
-            hr = p_taskbl->vt->ThumbBarAddButtons(p_taskbl, winId(), 3, thbButtons);
-            if(S_OK != hr)
-                msg_Err( p_intf, "ThumbBarAddButtons failed with error %08x", hr );
-        }
-        CONNECT( THEMIM->getIM(), statusChanged( int ), this, changeThumbbarButtons( int ) );
-    }
-    else
-    {
-        himl = NULL;
-        p_taskbl = NULL;
-    }
-
-}
-
-bool MainInterface::winEvent ( MSG * msg, long * result )
-{
-    if (msg->message == taskbar_wmsg)
-    {
-        //We received the taskbarbuttoncreated, now we can really create th buttons
-        createTaskBarButtons();
-    }
-
-    short cmd;
-    switch( msg->message )
-    {
-        case WM_COMMAND:
-            if (HIWORD(msg->wParam) == THBN_CLICKED)
-            {
-                switch(LOWORD(msg->wParam))
-                {
-                    case 0:
-                        THEMIM->prev();
-                        break;
-                    case 1:
-                        THEMIM->togglePlayPause();
-                        break;
-                    case 2:
-                        THEMIM->next();
-                        break;
-                }
-            }
-            break;
-        case WM_APPCOMMAND:
-            cmd = GET_APPCOMMAND_LPARAM(msg->lParam);
-            switch(cmd)
-            {
-                case APPCOMMAND_MEDIA_PLAY_PAUSE:
-                    THEMIM->togglePlayPause();
-                    break;
-                case APPCOMMAND_MEDIA_PLAY:
-                    THEMIM->play();
-                    break;
-                case APPCOMMAND_MEDIA_PAUSE:
-                    THEMIM->pause();
-                    break;
-                case APPCOMMAND_MEDIA_PREVIOUSTRACK:
-                    THEMIM->prev();
-                    break;
-                case APPCOMMAND_MEDIA_NEXTTRACK:
-                    THEMIM->next();
-                    break;
-                case APPCOMMAND_MEDIA_STOP:
-                    THEMIM->stop();
-                    break;
-                case APPCOMMAND_VOLUME_DOWN:
-                    THEAM->AudioDown();
-                    break;
-                case APPCOMMAND_VOLUME_UP:
-                    THEAM->AudioUp();
-                    break;
-                case APPCOMMAND_VOLUME_MUTE:
-                    THEAM->toggleMuteAudio();
-                    break;
-                default:
-                     msg_Dbg( p_intf, "unknown APPCOMMAND = %d", cmd);
-                     break;
-            }
-            break;
-    }
-    return false;
-}
-#endif
-
 /**********************************************************************
  * Handling of sizing of the components
  **********************************************************************/
 
-/* This function is probably wrong, but we don't have many many choices...
-   Since we can't know from the playlist Widget if we are inside a dock or not,
-   because the playlist Widget can be called by THEDP, as a separate windows for
-   the skins.
-   Maybe the other solution is to redefine the sizeHint() of the playlist and
-   ask _parent->isFloating()...
-   If you think this would be better, please FIXME it...
-*/
-#if 0
-
-QSize MainInterface::sizeHint() const
-{
-#if 0
-    if( b_keep_size )
-    {
-        if( i_visualmode )
-        {
-                return mainVideoSize;
-        }
-        else
-        {
-            if( VISIBLE( bgWidget) ||
-                ( videoIsActive && videoWidget->isVisible() )
-              )
-                return mainVideoSize;
-            else
-                return mainBasedSize;
-        }
-    }
-#endif
-
-    int nwidth  = __MAX( controls->sizeHint().width(),
-                         menuBar()->sizeHint().width() );
-
-    int nheight = controls->isVisible() ?
-                  controls->size().height()
-                  + inputC->size().height()
-                  + menuBar()->size().height()
-                  + statusBar()->size().height()
-                  : 0 ;
-
-  /*  if( stackCentralW->isVisible() )
-    {
-        nheight += stackCentralW->height();
-        nwidth  = __MAX( nwidth, stackCentralW->width() );
-    }*/
-
-/*    if( VISIBLE( bgWidget ) )
-    {
-        msg_Warn( p_intf, "Hello here" );
-        if( i_bg_height )
-            nheight += i_bg_height;
-        else
-            nheight += bgWidget->size().height();
-        nwidth  = __MAX( nwidth, bgWidget->size().width() );
-    }
-    else if( videoIsActive && videoWidget->isVisible() )
-    {
-        msg_Warn( p_intf, "Hello there" );
-        nheight += videoWidget->sizeHint().height();
-        nwidth  = __MAX( nwidth, videoWidget->sizeHint().width() );
-    }*/
-#if 0
-    if( !dockPL->isFloating() && dockPL->isVisible() && dockPL->widget()  )
-    {
-        nheight += dockPL->size().height();
-        nwidth = __MAX( nwidth, dockPL->size().width() );
-        msg_Warn( p_intf, "3 %i %i", nheight, nwidth );
-    }
-#endif
-    return QSize( nwidth, nheight );
-}
-#endif
-
-/* Video widget cannot do this synchronously as it runs in another thread */
-/* Well, could it, actually ? Probably dangerous ... */
-
 /* This function is called:
-   - toggling of minimal View
-   - through askUpdate() by Vout thread request video and resize video (zoom)
    - Advanced buttons toggled
+   - Toolbar geom changed
  */
-void MainInterface::doComponentsUpdate()
+void MainInterface::adaptGeometry()
 {
-    if( isFullScreen() || isMaximized() ) return;
-
-//    msg_Warn( p_intf, "Updating the geometry" );
-    /* Here we resize to sizeHint() and not adjustsize because we want
-       the videoWidget to be exactly the correctSize */
+  resize( sizeHint() );
 
 #ifdef DEBUG_INTF
     debug();
 #endif
-    /* This is WRONG, but I believe there is a Qt bug here */
-    setMinimumSize( 0, 0 );
-    //resize( sizeHint() );
-
-    //adjustSize() ; /* This is not needed, but might help in the future */
 }
 
 void MainInterface::debug()
 {
 #ifdef DEBUG_INTF
-    msg_Dbg( p_intf, "Stack Size: %i - %i", stackCentralW->size().height(), size().width() );
-    if( videoWidget )
-        msg_Dbg( p_intf, "Stack Size: %i - %i",
-                 videoWidget->size().height(),
-                 videoWidget->size().width() );
-    else
-        msg_Dbg( p_intf, "no embedded video" );
-
     msg_Dbg( p_intf, "size: %i - %i", size().height(), size().width() );
     msg_Dbg( p_intf, "sizeHint: %i - %i", sizeHint().height(), sizeHint().width() );
-    //msg_Dbg( p_intf, "maximumsize: %i - %i", maximumSize().height(), maximumSize().width() );
+    msg_Dbg( p_intf, "minimumsize: %i - %i", minimumSize().height(), minimumSize().width() );
 
     msg_Dbg( p_intf, "Stack size: %i - %i", stackCentralW->size().height(), stackCentralW->size().width() );
-    msg_Dbg( p_intf, "Stack minimumSize(): %i - %i", stackCentralW->minimumHeight(), stackCentralW->minimumWidth() );
-    msg_Dbg( p_intf, "Central minimumsize: %i - %i", centralWidget()->minimumSize().height(), centralWidget()->minimumSize().width() );
+    msg_Dbg( p_intf, "Stack sizeHint: %i - %i", stackCentralW->sizeHint().height(), stackCentralW->sizeHint().width() );
     msg_Dbg( p_intf, "Central size: %i - %i", centralWidget()->size().height(), centralWidget()->size().width() );
-    msg_Dbg( p_intf, "Menu minimumsize: %i - %i", menuBar()->minimumSize().height(), menuBar()->minimumSize().width() );
-    msg_Dbg( p_intf, "Input size: %i - %i", inputC->size().height(), inputC->size().width() );
-    msg_Dbg( p_intf, "Status minimumsize: %i - %i", statusBar()->minimumSize().height(), statusBar()->minimumSize().width() );
-    msg_Dbg( p_intf, "minimumsize: %i - %i", minimumSize().height(), minimumSize().width() );
-    msg_Dbg( p_intf, "bg Size: %i - %i", bgWidget->size().height(), bgWidget->size().width() );
-
-    /*if( videoWidget && videoWidget->isVisible() )
-    {
-        msg_Dbg( p_intf, "size: %i - %i", size().height(), size().width() );
-        msg_Dbg( p_intf, "sizeHint: %i - %i", sizeHint().height(), sizeHint().width() );
-    }*/
 #endif
 }
 
@@ -805,12 +545,7 @@ void MainInterface::destroyPopupMenu()
 
 void MainInterface::popupMenu( const QPoint &p )
 {
-    /* FIXME
-     * Ow, that's ugly: don't show the popup menu if cursor over
-     * the main menu bar or the status bar */
-    if( !childAt( p ) || ( ( childAt( p ) != menuBar() )
-                        && ( childAt( p )->parentWidget() != statusBar() ) ) )
-        QVLCMenu::PopupMenu( p_intf, true );
+    QVLCMenu::PopupMenu( p_intf, true );
 }
 
 void MainInterface::toggleFSC()
@@ -874,40 +609,29 @@ void MainInterface::getVideoSlot( WId *p_id, int *pi_x, int *pi_y,
 
         /* Consider the video active now */
         showVideo();
-
-        stackCentralW->resize( *pi_width, *pi_height );
-
-        emit askUpdate();
     }
 }
 
-
-
 /* Asynchronous call from the WindowClose function */
 void MainInterface::releaseVideo( void )
 {
-    emit askReleaseVideo( );
+    emit askReleaseVideo();
 }
 
 /* Function that is CONNECTED to the previous emit */
 void MainInterface::releaseVideoSlot( void )
 {
-    videoWidget->release( );
+    videoWidget->release();
 
     restoreStackOldWidget();
 
     /* We don't want to have a blank video to popup */
     stackCentralOldWidget = bgWidget;
-
-    /* Try to resize, except when you are in Fullscreen mode */
-//    doComponentsUpdate();
 }
 
 /* Asynchronous call from WindowControl function */
 int MainInterface::controlVideo( int i_query, va_list args )
 {
-    /* Debug to check if VOUT_WINDOW_SET_SIZE is called, because this is broken now */
-    msg_Warn( p_intf, "Control Video: %i", i_query );
     switch( i_query )
     {
     case VOUT_WINDOW_SET_SIZE:
@@ -915,8 +639,7 @@ int MainInterface::controlVideo( int i_query, va_list args )
         unsigned int i_width  = va_arg( args, unsigned int );
         unsigned int i_height = va_arg( args, unsigned int );
         emit askVideoToResize( i_width, i_height );
-        emit askUpdate();
-        return VLC_EGENERIC;
+        return VLC_SUCCESS;
     }
     case VOUT_WINDOW_SET_STATE:
     {
@@ -972,15 +695,11 @@ void MainInterface::togglePlaylist()
         /* Playlist is not visible, show it */
         if( stackCentralW->currentWidget() != playlistWidget )
         {
-            playlistWidget->forceShow();
             showTab( playlistWidget );
         }
         else /* Hide it! */
         {
             restoreStackOldWidget();
-            stackCentralW->updateGeometry();
-            // HACK: So it doesn't limit the stackWidget minimumSize
-            playlistWidget->forceHide();
         }
         playlistVisible = ( stackCentralW->currentWidget() == playlistWidget );
     }
@@ -1017,6 +736,9 @@ void MainInterface::dockPlaylist( bool p_docked )
     }
 }
 
+/*
+  If b_switch is false, then we are normalView
+ */
 void MainInterface::toggleMinimalView( bool b_switch )
 {
     if( i_visualmode == 0 )
@@ -1039,20 +761,16 @@ void MainInterface::toggleMinimalView( bool b_switch )
         }
     }
 
-    i_bg_height = stackCentralW->height();
-
     menuBar()->setVisible( !b_switch );
     controls->setVisible( !b_switch );
     statusBar()->setVisible( !b_switch );
     inputC->setVisible( !b_switch );
 
-    doComponentsUpdate();
-
     emit minimalViewToggled( b_switch );
 }
 
 /* toggling advanced controls buttons */
-void MainInterface::toggleAdvanced()
+void MainInterface::toggleAdvancedButtons()
 {
     controls->toggleAdvanced();
 //    if( fullscreenControls ) fullscreenControls->toggleAdvanced();
@@ -1083,7 +801,6 @@ void MainInterface::visual()
         visualSelector->hide();
         visualSelectorEnabled = false;
     }
-    doComponentsUpdate();
 }
 #endif
 
@@ -1431,12 +1148,11 @@ void MainInterface::closeEvent( QCloseEvent *e )
     THEDP->quit();
 }
 
-void MainInterface::toggleFullScreen( void )
+void MainInterface::toggleFullScreen()
 {
     if( isFullScreen() )
     {
         showNormal();
-        emit askUpdate(); // Needed if video was launched after the F11
         emit fullscreenInterfaceToggled( false );
     }
     else
@@ -1444,59 +1160,6 @@ void MainInterface::toggleFullScreen( void )
         showFullScreen();
         emit fullscreenInterfaceToggled( true );
     }
-
-}
-
-//moc doesn't know about #ifdef, so we have to build this method for every platform
-void MainInterface::changeThumbbarButtons( int i_status)
-{
-#ifdef WIN32
-    // Define an array of three buttons. These buttons provide images through an
-    // image list and also provide tooltips.
-    DWORD dwMask = THB_BITMAP | THB_FLAGS;
-
-    THUMBBUTTON thbButtons[3];
-    //prev
-    thbButtons[0].dwMask = dwMask;
-    thbButtons[0].iId = 0;
-    thbButtons[0].iBitmap = 0;
-
-    //play/pause
-    thbButtons[1].dwMask = dwMask;
-    thbButtons[1].iId = 1;
-
-    //next
-    thbButtons[2].dwMask = dwMask;
-    thbButtons[2].iId = 2;
-    thbButtons[2].iBitmap = 3;
-
-    switch( i_status )
-    {
-        case PLAYING_S:
-            {
-                thbButtons[0].dwFlags = THBF_ENABLED;
-                thbButtons[1].dwFlags = THBF_ENABLED;
-                thbButtons[2].dwFlags = THBF_ENABLED;
-                thbButtons[1].iBitmap = 1;
-                break;
-            }
-        case PAUSE_S:
-            {
-                thbButtons[0].dwFlags = THBF_ENABLED;
-                thbButtons[1].dwFlags = THBF_ENABLED;
-                thbButtons[2].dwFlags = THBF_ENABLED;
-                thbButtons[1].iBitmap = 2;
-                break;
-            }
-        default:
-            return;
-    }
-    HRESULT hr =  p_taskbl->vt->ThumbBarUpdateButtons(p_taskbl, this->winId(), 3, thbButtons);
-    if(S_OK != hr)
-        msg_Err( p_intf, "ThumbBarUpdateButtons failed with error %08x", hr );
-#else
-    ;
-#endif
 }
 
 /*****************************************************************************