Merge branch 'master' into zynaddsubfx-master
This commit is contained in:
@@ -79,6 +79,7 @@ private:
|
||||
virtual void applyQualitySettings();
|
||||
virtual void run();
|
||||
|
||||
volatile bool m_quit;
|
||||
|
||||
bool m_convertEndian;
|
||||
|
||||
|
||||
@@ -30,36 +30,59 @@ class trackContainer;
|
||||
class ResourceItem;
|
||||
|
||||
|
||||
/*! \brief The ResourceAction class provides centralized functionality for all actions
|
||||
* related to a ResourceItem.
|
||||
*
|
||||
* These actions are for example loading projects, samples, presets,
|
||||
* plugin-specific presets etc. Using this class we can avoid duplicated
|
||||
* functionality in ResourcePreviewer, ResourceBrowser, TrackContainerView,
|
||||
* InstrumentTrack & Co.
|
||||
*/
|
||||
|
||||
class ResourceAction
|
||||
{
|
||||
public:
|
||||
/*! Lists all supported actions. */
|
||||
enum Actions
|
||||
{
|
||||
EditProperties,
|
||||
LoadProject,
|
||||
LoadInNewTrackSongEditor,
|
||||
LoadInNewTrackBBEditor,
|
||||
LoadInActiveInstrumentTrack,
|
||||
DownloadIntoCollection,
|
||||
UploadToWWW,
|
||||
DeleteLocalResource,
|
||||
ImportFile,
|
||||
EditProperties, /*!< Open a dialog to edit properties of the ResourceItem */
|
||||
LoadProject, /*!< Load the project represented by the ResourceItem */
|
||||
LoadInNewTrackSongEditor, /*!< Load preset, sample etc. in a new track in Song Editor */
|
||||
LoadInNewTrackBBEditor, /*!< Load preset, sample etc. in a new track in BB Editor */
|
||||
LoadInActiveInstrumentTrack,/*!< Load preset, sample etc. in active instrument track */
|
||||
DownloadIntoCollection, /*!< Download the resource into local collection */
|
||||
UploadToWWW, /*!< Upload the resource to Web */
|
||||
DeleteLocalResource, /*!< Delete local resource (=file) */
|
||||
ImportFile, /*!< Try to import the resource via import filter plugins */
|
||||
NumActions
|
||||
} ;
|
||||
typedef Actions Action;
|
||||
|
||||
ResourceAction( const ResourceItem * _item,
|
||||
Action _action = NumActions ) :
|
||||
m_action( _action ),
|
||||
m_item( _item )
|
||||
/*! \brief Constructs a ResourceAction object.
|
||||
* \param item The ResourceItem the action is about
|
||||
* \param action An optional action from the Action enumeration used for the defaultTrigger() method
|
||||
*/
|
||||
ResourceAction( const ResourceItem * item,
|
||||
Action action = NumActions ) :
|
||||
m_action( action ),
|
||||
m_item( item )
|
||||
{
|
||||
}
|
||||
|
||||
bool loadProject();
|
||||
bool loadByPlugin( InstrumentTrack * _target );
|
||||
bool loadPreset( InstrumentTrack * _target );
|
||||
bool importProject( trackContainer * _target );
|
||||
// most actions can be triggered without any further information
|
||||
bool loadByPlugin( InstrumentTrack * target );
|
||||
bool loadPreset( InstrumentTrack * target );
|
||||
bool importProject( trackContainer * target );
|
||||
|
||||
/*! \brief Triggers the action passed to the constructor without any further options.
|
||||
*
|
||||
* Most actions can be triggered without any further information.
|
||||
* This allows simple but powerful code constructs:
|
||||
* \code
|
||||
* ResourceAction( myItem, ResourceAction::LoadProject ).defaultTrigger();
|
||||
* \endcode
|
||||
* \return true if the operation succeeded, otherwise false.
|
||||
*/
|
||||
bool defaultTrigger();
|
||||
|
||||
|
||||
|
||||
@@ -34,20 +34,45 @@
|
||||
#include "ResourceItem.h"
|
||||
|
||||
|
||||
/*! \brief The ResourceDB class organizes and caches ResourceItems.
|
||||
*
|
||||
* ResourceItem sets are organized in the ResourceDB::ItemHashMap. This
|
||||
* allows fast lookup of ResourceItems by hash string. ResourceItems are added
|
||||
* and removed by a ResourceProvider.
|
||||
*
|
||||
* The ResourceItem array can be cached to a file and restored later. This is
|
||||
* essential as otherwise e.g. the LocalResourceProvider would have to re-read
|
||||
* all directories and files and recompute hash strings based on file contents.
|
||||
*
|
||||
* One can also descend the hierarchical ResourceItem tree by starting from
|
||||
* ResourceDB::topLevelNode().
|
||||
*/
|
||||
|
||||
class EXPORT ResourceDB : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/*! A QHash instantiation used for storing ResourceItems and allow lookup
|
||||
* by hash string */
|
||||
typedef QHash<QString, ResourceItem *> ItemHashMap;
|
||||
|
||||
|
||||
ResourceDB( ResourceProvider * _provider );
|
||||
/*! \brief Constructs a ResourceDB object.
|
||||
* \param provider The ResourceProvider that will manage ResourceItems in this DB
|
||||
*/
|
||||
ResourceDB( ResourceProvider * provider );
|
||||
~ResourceDB();
|
||||
|
||||
/*! \brief Initializes ResourceDB object, i.e. restore cache and update itself via the ResourceProvider. */
|
||||
void init();
|
||||
|
||||
void load( const QString & _file );
|
||||
void save( const QString & _file );
|
||||
/*! \brief Dumps all ResourceItems with their relations to a file.
|
||||
* \param file The filename to save data to */
|
||||
void load( const QString & file );
|
||||
|
||||
/*! \brief Restores ResourceItems with their relations from a file.
|
||||
* \param file The filename to load data from */
|
||||
void save( const QString & file );
|
||||
|
||||
inline ResourceProvider * provider()
|
||||
{
|
||||
@@ -69,21 +94,31 @@ public:
|
||||
return &m_topLevelNode;
|
||||
}
|
||||
|
||||
// similiar to items()[_hash] but faster and returns NULL if not found
|
||||
const ResourceItem * itemByHash( const QString & _hash ) const;
|
||||
/*! \brief Looks up a ResourceItem by hash string - similiar to items()[hash]
|
||||
* but faster and returns NULL if not found.
|
||||
* \param hash The hash string that is searched for
|
||||
* \return A const pointer to the matching ResourceItem */
|
||||
const ResourceItem * itemByHash( const QString & hash ) const;
|
||||
|
||||
// return a list of ResourceItems who somehow match the given keywords
|
||||
ResourceItemList matchItems( const QStringList & _keyWords );
|
||||
/*! \brief Return a list of ResourceItems which somehow match the given keywords.
|
||||
* \param keywords A list of keywords that are searched for
|
||||
* \return A ResourceItemList which the result */
|
||||
ResourceItemList matchItems( const QStringList & keywords );
|
||||
|
||||
// return an item which matches a resource desceibed in _item as
|
||||
// good as possible
|
||||
const ResourceItem * nearestMatch( const ResourceItem & _item );
|
||||
/*! \brief Returns a ResourceItem which matches a given ResourceItem best.
|
||||
* \param item A ResourceItem to search the best match for
|
||||
* \return A const pointer to the best marching ResourceItem in the DB */
|
||||
const ResourceItem * nearestMatch( const ResourceItem & item );
|
||||
|
||||
// add given item to DB
|
||||
void addItem( ResourceItem * _newItem );
|
||||
/*! \brief Adds given ResourceItem to DB.
|
||||
* \param newItem The ResourceItem to be added */
|
||||
void addItem( ResourceItem * newItem );
|
||||
|
||||
void recursiveRemoveItems( ResourceItem::Relation * parent,
|
||||
bool removeTopLevelParent = true );
|
||||
/*! \brief Remove items recursively starting from a certain ResourceItem::Relation
|
||||
* \param parent The parent relation to start from with removing
|
||||
* \param removeParent A boolean which specifies whether to also remove the parent item */
|
||||
void removeItemsRecursively( ResourceItem::Relation * parent,
|
||||
bool removeParent = true );
|
||||
|
||||
|
||||
private:
|
||||
@@ -140,9 +175,12 @@ private:
|
||||
|
||||
|
||||
signals:
|
||||
/*! \brief Forwarded signal of ResourceProvider::itemsChanged() */
|
||||
void itemsChanged();
|
||||
void directoryItemAdded( const QString & _path );
|
||||
void directoryItemRemoved( const QString & _path );
|
||||
/*! \brief Emitted whenever a ResourceItem of type ResourceItem::TypeDirectory is added */
|
||||
void directoryItemAdded( const QString & path );
|
||||
/*! \brief Emitted whenever a ResourceItem of type ResourceItem::TypeDirectory is removed */
|
||||
void directoryItemRemoved( const QString & path );
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
@@ -35,59 +35,96 @@
|
||||
#include "TreeRelation.h"
|
||||
|
||||
|
||||
/*! \brief The ResourceItem class provides information about a local/remote file/directory.
|
||||
*
|
||||
* All relevant properties of a file or directory are stored within a
|
||||
* ResourceItem and can be accessed easily. All resources are identified by
|
||||
* a unique hash (based on file content or (absolute) directory name).
|
||||
*
|
||||
* ResourceItems are managed within a ResourceDB. Reading and writing resource
|
||||
* data is abstracted into the ResourceProvider class.
|
||||
*
|
||||
* The ResourceItem class does not provide any actual functionality.
|
||||
* Use ResourceAction or more high level classes like ResourcePreviewer and
|
||||
* ResourceBrowser.
|
||||
*/
|
||||
|
||||
class EXPORT ResourceItem
|
||||
{
|
||||
public:
|
||||
/*! A relation specifies how ResourceItems are organized among each other.
|
||||
* See documentation of TreeRelation for details. */
|
||||
typedef TreeRelation<ResourceItem> Relation;
|
||||
|
||||
/*! Lists all supported base directories for ResourceItems. */
|
||||
enum BaseDirectories
|
||||
{
|
||||
BaseRoot,
|
||||
BaseWorkingDir,
|
||||
BaseDataDir,
|
||||
BaseHome,
|
||||
BaseURL,
|
||||
BaseRoot, /*!< Item is relative to root directory */
|
||||
BaseWorkingDir, /*!< Item is relative to working directory */
|
||||
BaseDataDir, /*!< Item is relative to LMMS' data directory */
|
||||
BaseHome, /*!< Item is relative to user's home directory */
|
||||
BaseURL, /*!< Item is relative to the URL of the ResourceProvider */
|
||||
NumBaseDirectories
|
||||
} ;
|
||||
typedef BaseDirectories BaseDirectory;
|
||||
|
||||
/*! Lists all supported ResourceItem types. */
|
||||
enum Types
|
||||
{
|
||||
TypeUnknown,
|
||||
TypeDirectory,
|
||||
TypeSample,
|
||||
TypePreset,
|
||||
TypePluginSpecificResource,
|
||||
TypeProject,
|
||||
TypeMidiFile,
|
||||
TypeForeignProject,
|
||||
TypePlugin,
|
||||
TypeImage,
|
||||
TypeUnknown, /*!< No known resource type */
|
||||
TypeDirectory, /*!< Item is a directory */
|
||||
TypeSample, /*!< Item is a supported sample file */
|
||||
TypePreset, /*!< Item is a LMMS-specific preset */
|
||||
TypePluginSpecificResource, /* Item is a file supported by one of the available plugins */
|
||||
TypeProject, /*!< Item is a LMMS project */
|
||||
TypeMidiFile, /*!< Item is a MIDI file (and can be imported via MIDI import filter) */
|
||||
TypeForeignProject, /*!< Item is any other kind of project which can be imported via an ImportFilter plugin */
|
||||
TypePlugin, /*!< Item is a Plugin binary */
|
||||
TypeImage, /*!< Item is an image */
|
||||
NumTypes
|
||||
} ;
|
||||
typedef Types Type;
|
||||
|
||||
ResourceItem( ResourceProvider * _provider,
|
||||
const QString & _name,
|
||||
/*! \brief Constructs a ResourceItem object.
|
||||
* \param provider The provider this item belongs to
|
||||
* \param name The name used to identify the item towards the user
|
||||
* \param baseDir The base directory this item is relative to
|
||||
* \param path The path from base directory to this item
|
||||
* \param hash A unique hash based on file content, pass QString::null to compute it automatically
|
||||
* \param author A string describing the author
|
||||
* \param tags A comma-separated list of tags for this item
|
||||
* \param size The size of the item, pass -1 to compute it automatically
|
||||
* \param lastMod The date and time of the last modification of the item
|
||||
*/
|
||||
ResourceItem( ResourceProvider * provider,
|
||||
const QString & name,
|
||||
Type _type,
|
||||
BaseDirectory _base_dir = BaseWorkingDir,
|
||||
const QString & _path = QString::null,
|
||||
const QString & _hash = QString::null,
|
||||
const QString & _author = QString::null,
|
||||
const QString & _tags = QString::null,
|
||||
int _size = -1,
|
||||
const QDateTime & _last_mod = QDateTime() );
|
||||
// copy constructor
|
||||
ResourceItem( const ResourceItem & _item );
|
||||
BaseDirectory baseDir = BaseWorkingDir,
|
||||
const QString & path = QString::null,
|
||||
const QString & hash = QString::null,
|
||||
const QString & author = QString::null,
|
||||
const QString & tags = QString::null,
|
||||
int size = -1,
|
||||
const QDateTime & lastMod = QDateTime() );
|
||||
/*! \brief Copy constructor. */
|
||||
ResourceItem( const ResourceItem & item );
|
||||
|
||||
inline void setHidden( bool _h, const QAbstractItemModel * _model )
|
||||
/*! \brief Sets hidden property for the given item model
|
||||
* \param hidden A boolean specifying the desired value
|
||||
* \param model A pointer to a QAbstractItemModel (allows to use this item
|
||||
* for multiple models and views) */
|
||||
inline void setHidden( bool hidden, const QAbstractItemModel * model )
|
||||
{
|
||||
m_hidden[_model] = _h;
|
||||
m_hidden[model] = hidden;
|
||||
}
|
||||
|
||||
inline bool isHidden( const QAbstractItemModel * _model ) const
|
||||
/*! \brief Returns whether item is hidden for the given item model
|
||||
* \param model A pointer to a QAbstractItemModel (allows to use this item
|
||||
* for multiple models and views)
|
||||
* \return true if the item is hidden, false otherwise */
|
||||
inline bool isHidden( const QAbstractItemModel * model ) const
|
||||
{
|
||||
return m_hidden[_model];
|
||||
return m_hidden[model];
|
||||
}
|
||||
|
||||
|
||||
@@ -206,6 +243,8 @@ public:
|
||||
return m_provider->dataSize( this );
|
||||
}
|
||||
|
||||
/*! \brief Fetch data (contents) of the resource via ResourceProvider.
|
||||
* \return QByteArray with complete contents of the resource */
|
||||
QByteArray fetchData( int _maxSize = -1 ) const
|
||||
{
|
||||
return m_provider->fetchData( this );
|
||||
@@ -213,15 +252,19 @@ public:
|
||||
|
||||
void reload();
|
||||
|
||||
// returns true if all given keywords match name, tags etc.
|
||||
/*! \brief Returns, whether keywords match certain properties.
|
||||
* \return true if all given keywords match name, tags etc. */
|
||||
bool keywordMatch( const QStringList & _keywords ) const;
|
||||
|
||||
// return true, if given ResourceItem is equal
|
||||
/*! \brief Tests for equality with another ResourceItem.
|
||||
* \return true, if given ResourceItem is equal */
|
||||
bool operator==( const ResourceItem & _other ) const;
|
||||
|
||||
// rates equality with given item
|
||||
/*! \brief Rates equality with another ResourceItem.
|
||||
* \return An integer specifying how close the two ResourceItems are (between 0 and about 250) */
|
||||
int equalityLevel( const ResourceItem & _other ) const;
|
||||
|
||||
/*! \brief Guesses resource type by various criteria */
|
||||
Type guessType() const;
|
||||
|
||||
static const char * mimeKey()
|
||||
|
||||
@@ -278,7 +278,7 @@ qDebug() << "read dir" << d.canonicalPath();
|
||||
{
|
||||
ResourceItem::Relation * item = *it;
|
||||
it = curParent->children().erase( it );
|
||||
database()->recursiveRemoveItems( item );
|
||||
database()->removeItemsRecursively( item );
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -98,7 +98,7 @@ void ResourceDB::init()
|
||||
|
||||
void ResourceDB::load( const QString & _file )
|
||||
{
|
||||
recursiveRemoveItems( topLevelNode(), false );
|
||||
removeItemsRecursively( topLevelNode(), false );
|
||||
|
||||
multimediaProject m( _file );
|
||||
|
||||
@@ -283,7 +283,7 @@ void ResourceDB::addItem( ResourceItem * newItem )
|
||||
ResourceItem::Relation * oldRelation = oldItem->relation();
|
||||
if( oldRelation )
|
||||
{
|
||||
recursiveRemoveItems( oldRelation, false );
|
||||
removeItemsRecursively( oldRelation, false );
|
||||
delete oldRelation;
|
||||
}
|
||||
if( oldItem->type() == ResourceItem::TypeDirectory )
|
||||
@@ -299,8 +299,8 @@ void ResourceDB::addItem( ResourceItem * newItem )
|
||||
|
||||
|
||||
|
||||
void ResourceDB::recursiveRemoveItems( ResourceItem::Relation * parent,
|
||||
bool removeTopLevelParent )
|
||||
void ResourceDB::removeItemsRecursively( ResourceItem::Relation * parent,
|
||||
bool removeParent )
|
||||
{
|
||||
if( !parent )
|
||||
{
|
||||
@@ -309,10 +309,10 @@ void ResourceDB::recursiveRemoveItems( ResourceItem::Relation * parent,
|
||||
|
||||
while( !parent->children().isEmpty() )
|
||||
{
|
||||
recursiveRemoveItems( parent->children().front() );
|
||||
removeItemsRecursively( parent->children().front() );
|
||||
}
|
||||
|
||||
if( removeTopLevelParent && parent->item() )
|
||||
if( removeParent && parent->item() )
|
||||
{
|
||||
if( parent->item()->type() == ResourceItem::TypeDirectory )
|
||||
{
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "gui_templates.h"
|
||||
#include "templates.h"
|
||||
#include "Cpu.h"
|
||||
#include "engine.h"
|
||||
|
||||
|
||||
static void stream_write_callback(pa_stream *s, size_t length, void *userdata)
|
||||
@@ -51,6 +52,7 @@ AudioPulseAudio::AudioPulseAudio( bool & _success_ful, mixer * _mixer ) :
|
||||
DEFAULT_CHANNELS, SURROUND_CHANNELS ),
|
||||
_mixer ),
|
||||
m_s( NULL ),
|
||||
m_quit( false ),
|
||||
m_convertEndian( false )
|
||||
{
|
||||
_success_ful = false;
|
||||
@@ -68,11 +70,6 @@ AudioPulseAudio::AudioPulseAudio( bool & _success_ful, mixer * _mixer ) :
|
||||
AudioPulseAudio::~AudioPulseAudio()
|
||||
{
|
||||
stopProcessing();
|
||||
|
||||
if( m_s != NULL )
|
||||
{
|
||||
pa_stream_unref( m_s );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -173,11 +170,29 @@ static void context_state_callback(pa_context *c, void *userdata)
|
||||
_this->m_s = pa_stream_new( c, "lmms", &_this->m_sampleSpec, NULL);
|
||||
pa_stream_set_state_callback( _this->m_s, stream_state_callback, _this );
|
||||
pa_stream_set_write_callback( _this->m_s, stream_write_callback, _this );
|
||||
pa_stream_connect_playback( _this->m_s, NULL, NULL,
|
||||
(pa_stream_flags) 0,
|
||||
pa_cvolume_set( &cv, _this->m_sampleSpec.channels,
|
||||
PA_VOLUME_NORM ),
|
||||
NULL );
|
||||
|
||||
pa_buffer_attr buffer_attr;
|
||||
|
||||
buffer_attr.maxlength = (uint32_t)(-1);
|
||||
|
||||
// play silence in case of buffer underun instead of using default rewind
|
||||
buffer_attr.prebuf = 0;
|
||||
|
||||
buffer_attr.minreq = (uint32_t)(-1);
|
||||
buffer_attr.fragsize = (uint32_t)(-1);
|
||||
|
||||
double latency = (double)( engine::getMixer()->framesPerPeriod() ) /
|
||||
(double)_this->sampleRate();
|
||||
|
||||
// ask PulseAudio for the desired latency (which might not be approved)
|
||||
buffer_attr.tlength = pa_usec_to_bytes( latency * PA_USEC_PER_MSEC,
|
||||
&_this->m_sampleSpec );
|
||||
|
||||
pa_stream_connect_playback( _this->m_s, NULL, &buffer_attr,
|
||||
PA_STREAM_ADJUST_LATENCY,
|
||||
pa_cvolume_set( &cv, _this->m_sampleSpec.channels,
|
||||
PA_VOLUME_NORM ),
|
||||
NULL );
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -195,38 +210,45 @@ static void context_state_callback(pa_context *c, void *userdata)
|
||||
|
||||
void AudioPulseAudio::run()
|
||||
{
|
||||
pa_mainloop * m = NULL;
|
||||
|
||||
|
||||
if (!(m = pa_mainloop_new())) {
|
||||
pa_mainloop * mainLoop = pa_mainloop_new();
|
||||
if( !mainLoop )
|
||||
{
|
||||
qCritical( "pa_mainloop_new() failed.\n" );
|
||||
return;
|
||||
}
|
||||
pa_mainloop_api * mainloop_api = pa_mainloop_get_api(m);
|
||||
pa_mainloop_api * mainloop_api = pa_mainloop_get_api( mainLoop );
|
||||
|
||||
pa_context *context = pa_context_new(mainloop_api, "lmms");
|
||||
pa_context *context = pa_context_new( mainloop_api, "lmms" );
|
||||
if ( context == NULL )
|
||||
{
|
||||
qCritical( "pa_context_new() failed." );
|
||||
qCritical( "pa_context_new() failed." );
|
||||
return;
|
||||
}
|
||||
|
||||
pa_context_set_state_callback(context, context_state_callback, this );
|
||||
/* Connect the context */
|
||||
pa_context_connect(context, NULL, (pa_context_flags) 0, NULL);
|
||||
pa_context_set_state_callback( context, context_state_callback, this );
|
||||
// connect the context
|
||||
pa_context_connect( context, NULL, (pa_context_flags) 0, NULL );
|
||||
|
||||
int ret;
|
||||
/* Run the main loop */
|
||||
if (pa_mainloop_run(m, &ret) < 0)
|
||||
// run the main loop
|
||||
int ret = 0;
|
||||
m_quit = false;
|
||||
while( m_quit == false && pa_mainloop_iterate( mainLoop, 1, &ret ) >= 0 )
|
||||
{
|
||||
qCritical( "pa_mainloop_run() failed.\n" );
|
||||
}
|
||||
|
||||
pa_stream_disconnect( m_s );
|
||||
pa_stream_unref( m_s );
|
||||
|
||||
pa_context_disconnect( context );
|
||||
pa_context_unref( context );
|
||||
|
||||
pa_mainloop_free( mainLoop );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void AudioPulseAudio::streamWriteCallback(pa_stream *s, size_t length)
|
||||
void AudioPulseAudio::streamWriteCallback( pa_stream *s, size_t length )
|
||||
{
|
||||
const fpp_t fpp = getMixer()->framesPerPeriod();
|
||||
sampleFrameA * temp = CPU::allocFrames( fpp );
|
||||
@@ -234,12 +256,13 @@ void AudioPulseAudio::streamWriteCallback(pa_stream *s, size_t length)
|
||||
sizeof(Sint16) );
|
||||
|
||||
size_t fd = 0;
|
||||
while( fd < length/4 )
|
||||
while( fd < length/4 && m_quit == false )
|
||||
{
|
||||
const fpp_t frames = getNextBuffer( temp );
|
||||
if( !frames )
|
||||
{
|
||||
return;
|
||||
m_quit = true;
|
||||
break;
|
||||
}
|
||||
int bytes = CPU::convertToS16( temp,
|
||||
(intSampleFrameA *) pcmbuf,
|
||||
|
||||
Reference in New Issue
Block a user