# include "config.h"
#endif
+#include <vlc_vout.h>
+
#include "dialogs_provider.hpp"
#include "components/interface_widgets.hpp"
#include "main_interface.hpp"
#include "menus.hpp"
#include "util/input_slider.hpp"
#include "util/customwidgets.hpp"
-#include <vlc_vout.h>
#include <QLabel>
#include <QSpacerItem>
#include <QPalette>
#include <QResizeEvent>
#include <QDate>
+
#ifdef Q_WS_X11
# include <X11/Xlib.h>
# include <qx11info_x11.h>
connect( this, SIGNAL(askVideoWidgetToShow( unsigned int, unsigned int)),
this, SLOT(SetSizing(unsigned int, unsigned int )) );
#endif
-
-
}
void VideoWidget::paintEvent(QPaintEvent *ev)
* Request the video to avoid the conflicts
**/
void *VideoWidget::request( vout_thread_t *p_nvout, int *pi_x, int *pi_y,
- unsigned int *pi_width, unsigned int *pi_height )
+ unsigned int *pi_width, unsigned int *pi_height )
{
msg_Dbg( p_intf, "Video was requested %i, %i", *pi_x, *pi_y );
emit askVideoWidgetToShow( *pi_width, *pi_height );
}
BackgroundWidget::~BackgroundWidget()
-{
-}
+{}
void BackgroundWidget::resizeEvent( QResizeEvent * event )
{
layout->addWidget( prevButton );
layout->addWidget( nextButton );
- layout->addItem( new QSpacerItem( 40,20,
- QSizePolicy::Expanding, QSizePolicy::Minimum ) );
- layout->addWidget( new QLabel( qtr( "Current visualization:" ) ) );
+ layout->addStretch( 10 );
+ layout->addWidget( new QLabel( qtr( "Current visualization" ) ) );
current = new QLabel( qtr( "None" ) );
layout->addWidget( current );
}
VisualSelector::~VisualSelector()
-{
-}
+{}
void VisualSelector::prev()
{
frameButton->setMaximumSize( QSize( 26, 26 ) );
frameButton->setIconSize( QSize( 20, 20 ) );
advLayout->addWidget( frameButton );
- BUTTON_SET_ACT( frameButton, "Fr", qtr( "Frame by Frame" ), frame() );
+ BUTTON_SET_ACT( frameButton, "Fr", qtr( "Frame by frame" ), frame() );
#endif
recordButton = new QPushButton( "R" );
bool b_fsCreation) :
QFrame( _p_mi ), p_intf( _p_i )
{
- controlLayout = new QGridLayout( );
-
- controlLayout->setSpacing( 0 );
- controlLayout->setLayoutMargins( 7, 5, 7, 3, 6 );
-
- if( !b_fsCreation )
- setLayout( controlLayout );
-
setSizePolicy( QSizePolicy::Preferred , QSizePolicy::Maximum );
/** The main Slider **/
slider = new InputSlider( Qt::Horizontal, NULL );
- controlLayout->addWidget( slider, 0, 1, 1, 16 );
/* Update the position when the IM has changed */
CONNECT( THEMIM->getIM(), positionUpdated( float, int, int ),
slider, setPosition( float, int, int ) );
slowerButton->setMaximumSize( QSize( 26, 20 ) );
BUTTON_SET_ACT( slowerButton, "-", qtr( "Slower" ), slower() );
- controlLayout->addWidget( slowerButton, 0, 0 );
fasterButton = new QToolButton;
fasterButton->setAutoRaise( true );
fasterButton->setMaximumSize( QSize( 26, 20 ) );
BUTTON_SET_ACT( fasterButton, "+", qtr( "Faster" ), faster() );
- controlLayout->addWidget( fasterButton, 0, 17 );
/* advanced Controls handling */
b_advancedVisible = b_advControls;
advControls = new AdvControlsWidget( p_intf );
- controlLayout->addWidget( advControls, 1, 3, 2, 4, Qt::AlignBottom );
if( !b_advancedVisible ) advControls->hide();
/** Disc and Menus handling */
setupSmallButton( nextSectionButton );
discLayout->addWidget( nextSectionButton );
- controlLayout->addWidget( discFrame, 1, 10, 2, 3, Qt::AlignBottom );
-
BUTTON_SET_IMG( prevSectionButton, "", previous.png, "" );
BUTTON_SET_IMG( nextSectionButton, "", next.png, "" );
BUTTON_SET_IMG( menuButton, "", previous.png, qtr( "Menu" ) );
telexPage->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Minimum );
telexLayout->addWidget( telexPage );
- if( !b_fsCreation )
- controlLayout->addWidget( telexFrame, 1, 10, 2, 4, Qt::AlignBottom );
telexFrame->hide(); /* default hidden */
CONNECT( telexPage, valueChanged( int ), THEMIM->getIM(),
CONNECT( THEMIM->getIM(), toggleTelexTransparency(),
this, toggleTeletextTransparency() );
CONNECT( THEMIM->getIM(), teletextEnabled( bool ),
- telexFrame, setVisible( bool ) );
+ this, enableTeletext( bool ) );
/** Play Buttons **/
QSizePolicy sizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
playButton->setMinimumSize( QSize( 36, 36 ) );
playButton->setIconSize( QSize( 30, 30 ) );
- controlLayout->addWidget( playButton, 2, 0, 2, 2 );
-
- controlLayout->setColumnMinimumWidth( 2, 20 );
- controlLayout->setColumnStretch( 2, 0 );
/** Prev + Stop + Next Block **/
controlButLayout = new QHBoxLayout;
controlButLayout->addWidget( nextButton );
/* Add this block to the main layout */
- if( !b_fsCreation )
- controlLayout->addLayout( controlButLayout, 3, 3, 1, 3 );
BUTTON_SET_ACT_I( playButton, "", play.png, qtr( "Play" ), play() );
BUTTON_SET_ACT_I( prevButton, "" , previous.png,
BUTTON_SET_ACT_I( nextButton, "", next.png, qtr( "Next" ), next() );
BUTTON_SET_ACT_I( stopButton, "", stop.png, qtr( "Stop" ), stop() );
- controlLayout->setColumnMinimumWidth( 7, 20 );
- controlLayout->setColumnStretch( 7, 0 );
- controlLayout->setColumnStretch( 8, 0 );
- controlLayout->setColumnStretch( 9, 0 );
-
/*
* Other first Line buttons
*/
fullscreenButton = new QPushButton( "F" );
BUTTON_SET_ACT( fullscreenButton, "F", qtr( "Fullscreen" ), fullscreen() );
setupSmallButton( fullscreenButton );
- controlLayout->addWidget( fullscreenButton, 3, 10, Qt::AlignBottom );
-
- /** Playlist Button **/
- playlistButton = new QPushButton;
- setupSmallButton( playlistButton );
- controlLayout->addWidget( playlistButton, 3, 11, Qt::AlignBottom );
- BUTTON_SET_IMG( playlistButton, "" , playlist.png, qtr( "Show playlist" ) );
- CONNECT( playlistButton, clicked(), _p_mi, togglePlaylist() );
-
- /** extended Settings **/
- extSettingsButton = new QPushButton;
- BUTTON_SET_ACT( extSettingsButton, "Ex", qtr( "Extended Settings" ),
- extSettings() );
- setupSmallButton( extSettingsButton );
- controlLayout->addWidget( extSettingsButton, 3, 12, Qt::AlignBottom );
-
- controlLayout->setColumnStretch( 13, 0 );
- controlLayout->setColumnMinimumWidth( 13, 24 );
- controlLayout->setColumnStretch( 14, 5 );
+
+ if( !b_fsCreation )
+ {
+ /** Playlist Button **/
+ playlistButton = new QPushButton;
+ setupSmallButton( playlistButton );
+ BUTTON_SET_IMG( playlistButton, "" , playlist.png, qtr( "Show playlist" ) );
+ CONNECT( playlistButton, clicked(), _p_mi, togglePlaylist() );
+
+ /** extended Settings **/
+ extSettingsButton = new QPushButton;
+ BUTTON_SET_ACT( extSettingsButton, "Ex", qtr( "Extended settings" ),
+ extSettings() );
+ setupSmallButton( extSettingsButton );
+ }
/* Volume */
hVolLabel = new VolumeClickHandler( p_intf, this );
volMuteLabel->setPixmap( QPixmap( ":/pixmaps/volume-medium.png" ) );
volMuteLabel->setToolTip( qtr( "Mute" ) );
volMuteLabel->installEventFilter( hVolLabel );
- controlLayout->addWidget( volMuteLabel, 3, 15, Qt::AlignBottom );
if( b_shiny )
{
volumeSlider->setMaximumSize( QSize( 200, 40 ) );
volumeSlider->setMinimumSize( QSize( 106, 30 ) );
volumeSlider->setFocusPolicy( Qt::NoFocus );
- controlLayout->addWidget( volumeSlider, 2, 16, 2 , 2, Qt::AlignBottom );
/* Set the volume from the config */
volumeSlider->setValue( ( config_GetInt( p_intf, "volume" ) ) *
CONNECT( volumeSlider, valueChanged( int ), this, updateVolume( int ) );
CONNECT( THEMIM, volumeChanged( void ), this, updateVolume( void ) );
+ if( !b_fsCreation )
+ {
+ controlLayout = new QGridLayout( this );
+
+ controlLayout->setSpacing( 0 );
+ controlLayout->setLayoutMargins( 7, 5, 7, 3, 6 );
+
+ controlLayout->addWidget( slider, 0, 1, 1, 16 );
+ controlLayout->addWidget( slowerButton, 0, 0 );
+ controlLayout->addWidget( fasterButton, 0, 17 );
+
+ controlLayout->addWidget( advControls, 1, 3, 2, 4, Qt::AlignBottom );
+ controlLayout->addWidget( discFrame, 1, 10, 2, 3, Qt::AlignBottom );
+ controlLayout->addWidget( telexFrame, 1, 10, 2, 4, Qt::AlignBottom );
+
+ controlLayout->addWidget( playButton, 2, 0, 2, 2 );
+ controlLayout->setColumnMinimumWidth( 2, 20 );
+ controlLayout->setColumnStretch( 2, 0 );
+
+ controlLayout->addLayout( controlButLayout, 3, 3, 1, 3 );
+ controlLayout->setColumnMinimumWidth( 7, 20 );
+ controlLayout->setColumnStretch( 7, 0 );
+ controlLayout->setColumnStretch( 8, 0 );
+ controlLayout->setColumnStretch( 9, 0 );
+
+ controlLayout->addWidget( fullscreenButton, 3, 10, Qt::AlignBottom );
+ controlLayout->addWidget( playlistButton, 3, 11, Qt::AlignBottom );
+ controlLayout->addWidget( extSettingsButton, 3, 12, Qt::AlignBottom );
+
+ controlLayout->setColumnStretch( 13, 0 );
+ controlLayout->setColumnMinimumWidth( 13, 24 );
+ controlLayout->setColumnStretch( 14, 5 );
+
+ controlLayout->addWidget( volMuteLabel, 3, 15, Qt::AlignBottom );
+ controlLayout->addWidget( volumeSlider, 2, 16, 2 , 2, Qt::AlignBottom );
+ }
+
updateInput();
}
}
}
+void ControlsWidget::enableTeletext( bool b_enable )
+{
+ telexFrame->setVisible( b_enable );
+ bool b_on = THEMIM->teletextState();
+
+ telexOn->setChecked( b_on );
+ telexTransparent->setEnabled( b_on );
+ telexPage->setEnabled( b_on );
+ b_telexEnabled = b_on;
+}
+
void ControlsWidget::toggleTeletextTransparency()
{
if( b_telexTransparent )
FullscreenControllerWidget::FullscreenControllerWidget( intf_thread_t *_p_i,
MainInterface *_p_mi, bool b_advControls, bool b_shiny )
: ControlsWidget( _p_i, _p_mi, b_advControls, b_shiny, true ),
- i_lastPosX( -1 ), i_lastPosY( -1 ), i_hideTimeout( 1 ),
- b_mouseIsOver( false ), b_isFullscreen( false )
+ i_mouse_last_x( -1 ), i_mouse_last_y( -1 ), b_mouse_over(false),
+ b_slow_hide_begin(false), i_slow_hide_timeout(1),
+ b_fullscreen( false ), i_hide_timeout( 1 )
{
setWindowFlags( Qt::ToolTip );
setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
QGridLayout *fsLayout = new QGridLayout( this );
- controlLayout->setSpacing( 0 );
- controlLayout->setLayoutMargins( 5, 1, 5, 1, 5 );
+ fsLayout->setLayoutMargins( 5, 1, 5, 1, 5 );
fsLayout->addWidget( slowerButton, 0, 0 );
slider->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Minimum);
/* hiding timer */
p_hideTimer = new QTimer( this );
- CONNECT( p_hideTimer, timeout(), this, hideFSControllerWidget() );
+ CONNECT( p_hideTimer, timeout(), this, hideFSC() );
p_hideTimer->setSingleShot( true );
/* slow hiding timer */
move( p_desktop->width() / 2 - width() / 2,
p_desktop->height() - height() );
- #ifdef WIN32TRICK
+#ifdef WIN32TRICK
setWindowOpacity( 0.0 );
fscHidden = true;
show();
- #endif
+#endif
+
+ vlc_mutex_init_recursive( &lock );
}
FullscreenControllerWidget::~FullscreenControllerWidget()
{
+ vlc_mutex_destroy( &lock );
+}
+
+/**
+ * Show fullscreen controller
+ */
+void FullscreenControllerWidget::showFSC()
+{
+#ifdef WIN32TRICK
+ // after quiting and going to fs, we need to call show()
+ if( isHidden() )
+ show();
+
+ if( fscHidden )
+ {
+ fscHidden = false;
+ setWindowOpacity( 1.0 );
+ }
+#else
+ show();
+#endif
+
+#if HAVE_TRANSPARENCY
+ setWindowOpacity( DEFAULT_OPACITY );
+#endif
}
/**
* FIXME: under windows it have to be done by moving out of screen
* because hide() doesnt work
*/
-void FullscreenControllerWidget::hideFSControllerWidget()
+void FullscreenControllerWidget::hideFSC()
{
- #ifdef WIN32TRICK
+#ifdef WIN32TRICK
fscHidden = true;
setWindowOpacity( 0.0 ); // simulate hidding
- #else
+#else
hide();
- #endif
+#endif
+}
+
+/**
+ * Plane to hide fullscreen controller
+ */
+void FullscreenControllerWidget::planHideFSC()
+{
+ vlc_mutex_lock( &lock );
+ int i_timeout = i_hide_timeout;
+ vlc_mutex_unlock( &lock );
+
+ p_hideTimer->start( i_timeout );
+
+#if HAVE_TRANSPARENCY
+ b_slow_hide_begin = true;
+ i_slow_hide_timeout = i_timeout;
+ p_slowHideTimer->start( i_slow_hide_timeout / 2 );
+#endif
}
/**
void FullscreenControllerWidget::slowHideFSC()
{
#if HAVE_TRANSPARENCY
- static bool first_call = true;
-
- if ( first_call )
+ if( b_slow_hide_begin )
{
- first_call = false;
+ b_slow_hide_begin = false;
p_slowHideTimer->stop();
/* the last part of time divided to 100 pieces */
- p_slowHideTimer->start(
- (int) ( i_hideTimeout / 2 / ( windowOpacity() * 100 ) ) );
+ p_slowHideTimer->start( (int)( i_slow_hide_timeout / 2 / ( windowOpacity() * 100 ) ) );
+
}
else
{
setWindowOpacity( windowOpacity() - 0.02 );
}
- if ( windowOpacity() == 0.0 )
- {
- first_call = true;
+ if ( windowOpacity() <= 0.0 )
p_slowHideTimer->stop();
- }
}
#endif
}
-/**
- * Get state of visibility of FS controller on screen
- * On windows control if it is on hidden position
- */
-bool FullscreenControllerWidget::isFSCHidden()
-{
- #ifdef WIN32TRICK
- return fscHidden;
- #endif
-
- return isHidden();
-}
-
/**
* event handling
* events: show, hide, start timer for hidding
*/
void FullscreenControllerWidget::customEvent( QEvent *event )
{
- int type = event->type();
+ bool b_fs;
- if ( type == FullscreenControlShow_Type && b_isFullscreen )
+ switch( event->type() )
{
- #ifdef WIN32TRICK
- // after quiting and going to fs, we need to call show()
- if ( isHidden() )
- show();
-
- if ( fscHidden )
- {
- fscHidden = false;
- setWindowOpacity( 1.0 );
- }
- #else
- show();
- #endif
-
-#if HAVE_TRANSPARENCY
- setWindowOpacity( DEFAULT_OPACITY );
-#endif
- }
- else if ( type == FullscreenControlHide_Type )
- {
- hideFSControllerWidget();
- }
- else if ( type == FullscreenControlPlanHide_Type && !b_mouseIsOver )
- {
- p_hideTimer->start( i_hideTimeout );
-#if HAVE_TRANSPARENCY
- p_slowHideTimer->start( i_hideTimeout / 2 );
-#endif
+ case FullscreenControlShow_Type:
+ vlc_mutex_lock( &lock );
+ b_fs = b_fullscreen;
+ vlc_mutex_unlock( &lock );
+
+ if( b_fs ) // FIXME I am not sure about that one
+ showFSC();
+ break;
+ case FullscreenControlHide_Type:
+ hideFSC();
+ break;
+ case FullscreenControlPlanHide_Type:
+ if( !b_mouse_over ) // Only if the mouse is not over FSC
+ planHideFSC();
+ break;
}
}
{
if ( event->buttons() == Qt::LeftButton )
{
- int i_moveX = event->globalX() - i_lastPosX;
- int i_moveY = event->globalY() - i_lastPosY;
+ int i_moveX = event->globalX() - i_mouse_last_x;
+ int i_moveY = event->globalY() - i_mouse_last_y;
move( x() + i_moveX, y() + i_moveY );
- i_lastPosX = event->globalX();
- i_lastPosY = event->globalY();
+ i_mouse_last_x = event->globalX();
+ i_mouse_last_y = event->globalY();
}
}
*/
void FullscreenControllerWidget::mousePressEvent( QMouseEvent *event )
{
- i_lastPosX = event->globalX();
- i_lastPosY = event->globalY();
+ i_mouse_last_x = event->globalX();
+ i_mouse_last_y = event->globalY();
}
/**
*/
void FullscreenControllerWidget::enterEvent( QEvent *event )
{
+ b_mouse_over = true;
+
p_hideTimer->stop();
#if HAVE_TRANSPARENCY
p_slowHideTimer->stop();
#endif
- b_mouseIsOver = true;
}
/**
*/
void FullscreenControllerWidget::leaveEvent( QEvent *event )
{
- p_hideTimer->start( i_hideTimeout );
-#if HAVE_TRANSPARENCY
- p_slowHideTimer->start( i_hideTimeout / 2 );
-#endif
- b_mouseIsOver = false;
+ planHideFSC();
+
+ b_mouse_over = false;
}
/**
event->ignore();
}
-/**
- * It is called when video start
- */
-void FullscreenControllerWidget::regFullscreenCallback( vout_thread_t *p_vout )
+/* */
+static int FullscreenControllerWidgetFullscreenChanged( vlc_object_t *vlc_object, const char *variable,
+ vlc_value_t old_val, vlc_value_t new_val,
+ void *data )
{
- if ( p_vout )
- {
- var_AddCallback( p_vout, "fullscreen", regMouseMoveCallback, this );
- }
+ vout_thread_t *p_vout = (vout_thread_t *) vlc_object;
+ FullscreenControllerWidget *p_fs = (FullscreenControllerWidget *)data;
+
+ p_fs->fullscreenChanged( p_vout, new_val.b_bool, var_GetInteger( p_vout, "mouse-hide-timeout" ) );
+
+ return VLC_SUCCESS;
}
+/* */
+static int FullscreenControllerWidgetMouseMoved( vlc_object_t *vlc_object, const char *variable,
+ vlc_value_t old_val, vlc_value_t new_val,
+ void *data )
+{
+ FullscreenControllerWidget *p_fs = (FullscreenControllerWidget *)data;
+
+ /* Show event */
+ IMEvent *eShow = new IMEvent( FullscreenControlShow_Type, 0 );
+ QApplication::postEvent( p_fs, static_cast<QEvent *>(eShow) );
+
+ /* Plan hide event */
+ IMEvent *eHide = new IMEvent( FullscreenControlPlanHide_Type, 0 );
+ QApplication::postEvent( p_fs, static_cast<QEvent *>(eHide) );
+
+ return VLC_SUCCESS;
+}
+
/**
- * It is called after turn off video, because p_vout is NULL now
- * we cannt delete callback, just hide if FScontroller is visible
+ * It is called when video start
*/
-void FullscreenControllerWidget::unregFullscreenCallback()
+void FullscreenControllerWidget::attachVout( vout_thread_t *p_vout )
{
- if ( isVisible() )
- hide();
-}
+ assert( p_vout );
+ vlc_mutex_lock( &lock );
+ var_AddCallback( p_vout, "fullscreen", FullscreenControllerWidgetFullscreenChanged, this ); /* I miss a add and fire */
+ fullscreenChanged( p_vout, var_GetBool( p_vout, "fullscreen" ), var_GetInteger( p_vout, "mouse-hide-timeout" ) );
+ vlc_mutex_unlock( &lock );
+}
/**
- * Register and unregister callback for mouse moving
+ * It is called after turn off video.
*/
-static int regMouseMoveCallback( vlc_object_t *vlc_object, const char *variable,
- vlc_value_t old_val, vlc_value_t new_val,
- void *data )
+void FullscreenControllerWidget::detachVout( vout_thread_t *p_vout )
{
- vout_thread_t *p_vout = (vout_thread_t *) vlc_object;
+ assert( p_vout );
- static bool b_registered = false;
- FullscreenControllerWidget *p_fs = (FullscreenControllerWidget *) data;
-
- if ( var_GetBool( p_vout, "fullscreen" ) && !b_registered )
- {
- p_fs->setHideTimeout( var_GetInteger( p_vout, "mouse-hide-timeout" ) );
- p_fs->setIsFullscreen( true );
- var_AddCallback( p_vout, "mouse-moved",
- showFullscreenControllCallback, (void *) p_fs );
- b_registered = true;
- }
-
- if ( !var_GetBool( p_vout, "fullscreen" ) && b_registered )
- {
- p_fs->setIsFullscreen( false );
- p_fs->hide();
- var_DelCallback( p_vout, "mouse-moved",
- showFullscreenControllCallback, (void *) p_fs );
- b_registered = false;
- }
-
- return VLC_SUCCESS;
+ var_DelCallback( p_vout, "fullscreen", FullscreenControllerWidgetFullscreenChanged, this );
+ vlc_mutex_lock( &lock );
+ fullscreenChanged( p_vout, false, 0 );
+ vlc_mutex_unlock( &lock );
}
/**
- * Show fullscreen controller after mouse move
- * after show immediately plan hide event
+ * Register and unregister callback for mouse moving
*/
-static int showFullscreenControllCallback( vlc_object_t *vlc_object, const char *variable,
- vlc_value_t old_val, vlc_value_t new_val,
- void *data )
+void FullscreenControllerWidget::fullscreenChanged( vout_thread_t *p_vout, bool b_fs, int i_timeout )
{
- FullscreenControllerWidget *p_fs = (FullscreenControllerWidget *) data;
-
- if ( p_fs->isFSCHidden() || p_fs->windowOpacity() < DEFAULT_OPACITY )
+ vlc_mutex_lock( &lock );
+ if( b_fs && !b_fullscreen )
{
- IMEvent *event = new IMEvent( FullscreenControlShow_Type, 0 );
- QApplication::postEvent( p_fs, static_cast<QEvent *>(event) );
+ b_fullscreen = true;
+ i_hide_timeout = i_timeout;
+ var_AddCallback( p_vout, "mouse-moved", FullscreenControllerWidgetMouseMoved, this );
}
+ else if( !b_fs && b_fullscreen )
+ {
+ b_fullscreen = false;
+ i_hide_timeout = i_timeout;
+ var_DelCallback( p_vout, "mouse-moved", FullscreenControllerWidgetMouseMoved, this );
- IMEvent *e = new IMEvent( FullscreenControlPlanHide_Type, 0 );
- QApplication::postEvent( p_fs, static_cast<QEvent *>(e) );
-
- return VLC_SUCCESS;
+ /* Force fs hidding */
+ IMEvent *eHide = new IMEvent( FullscreenControlHide_Type, 0 );
+ QApplication::postEvent( this, static_cast<QEvent *>(eHide) );
+ }
+ vlc_mutex_unlock( &lock );
}
/**********************************************************************