use native gtk_paint_expander() to implement DrawTreeItemButton() under GTK+ 2.0
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@32234 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
@@ -82,8 +82,15 @@ public:
|
|||||||
virtual wxSplitterRenderParams GetSplitterParams(const wxWindow *win);
|
virtual wxSplitterRenderParams GetSplitterParams(const wxWindow *win);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// FIXME: shouldn't we destroy these windows somewhere?
|
||||||
|
|
||||||
// used by DrawHeaderButton and DrawComboBoxDropButton
|
// used by DrawHeaderButton and DrawComboBoxDropButton
|
||||||
void PrepareButtonDraw();
|
static GtkWidget *GetButtonWidget();
|
||||||
|
|
||||||
|
#ifdef __WXGTK20__
|
||||||
|
// used by DrawTreeItemButton()
|
||||||
|
static GtkWidget *GetTreeWidget();
|
||||||
|
#endif // GTK+ 2.0
|
||||||
};
|
};
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -99,26 +106,49 @@ wxRendererNative& wxRendererNative::GetDefault()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// common code
|
// helper functions
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
static GtkWidget *gs_button = NULL;
|
GtkWidget *
|
||||||
static GtkWidget *gs_window = NULL;
|
wxRendererGTK::GetButtonWidget()
|
||||||
|
|
||||||
void
|
|
||||||
wxRendererGTK::PrepareButtonDraw()
|
|
||||||
{
|
{
|
||||||
// prepares gs_button and gs_window which are used when
|
static GtkWidget *s_button = NULL;
|
||||||
// drawing button based elements.
|
static GtkWidget *s_window = NULL;
|
||||||
wxASSERT ( !gs_button );
|
|
||||||
|
|
||||||
gs_window = gtk_window_new( GTK_WINDOW_POPUP );
|
if ( !s_button )
|
||||||
gtk_widget_realize( gs_window );
|
{
|
||||||
gs_button = gtk_button_new();
|
s_window = gtk_window_new( GTK_WINDOW_POPUP );
|
||||||
gtk_container_add( GTK_CONTAINER(gs_window), gs_button );
|
gtk_widget_realize( s_window );
|
||||||
gtk_widget_realize( gs_button );
|
s_button = gtk_button_new();
|
||||||
|
gtk_container_add( GTK_CONTAINER(s_window), s_button );
|
||||||
|
gtk_widget_realize( s_button );
|
||||||
|
}
|
||||||
|
|
||||||
|
return s_button;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __WXGTK20__
|
||||||
|
|
||||||
|
GtkWidget *
|
||||||
|
wxRendererGTK::GetTreeWidget()
|
||||||
|
{
|
||||||
|
static GtkWidget *s_tree = NULL;
|
||||||
|
static GtkWidget *s_window = NULL;
|
||||||
|
|
||||||
|
if ( !s_tree )
|
||||||
|
{
|
||||||
|
s_tree = gtk_tree_view_new();
|
||||||
|
s_window = gtk_window_new( GTK_WINDOW_POPUP );
|
||||||
|
gtk_widget_realize( s_window );
|
||||||
|
gtk_container_add( GTK_CONTAINER(s_window), s_tree );
|
||||||
|
gtk_widget_realize( s_tree );
|
||||||
|
}
|
||||||
|
|
||||||
|
return s_tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // GTK+ 2.0
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// list/tree controls drawing
|
// list/tree controls drawing
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
@@ -130,19 +160,18 @@ wxRendererGTK::DrawHeaderButton(wxWindow *win,
|
|||||||
int flags)
|
int flags)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (gs_button == NULL)
|
GtkWidget *button = GetButtonWidget();
|
||||||
PrepareButtonDraw();
|
|
||||||
|
|
||||||
gtk_paint_box
|
gtk_paint_box
|
||||||
(
|
(
|
||||||
gs_button->style,
|
button->style,
|
||||||
// FIXME: I suppose GTK_PIZZA(win->m_wxwindow)->bin_window doesn't work with wxMemoryDC.
|
// FIXME: I suppose GTK_PIZZA(win->m_wxwindow)->bin_window doesn't work with wxMemoryDC.
|
||||||
// Maybe use code similar as in DrawComboBoxDropButton below?
|
// Maybe use code similar as in DrawComboBoxDropButton below?
|
||||||
GTK_PIZZA(win->m_wxwindow)->bin_window,
|
GTK_PIZZA(win->m_wxwindow)->bin_window,
|
||||||
flags & wxCONTROL_DISABLED ? GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL,
|
flags & wxCONTROL_DISABLED ? GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL,
|
||||||
GTK_SHADOW_OUT,
|
GTK_SHADOW_OUT,
|
||||||
NULL,
|
NULL,
|
||||||
gs_button,
|
button,
|
||||||
"button",
|
"button",
|
||||||
dc.XLOG2DEV(rect.x) -1, rect.y -1, rect.width +2, rect.height +2
|
dc.XLOG2DEV(rect.x) -1, rect.y -1, rect.width +2, rect.height +2
|
||||||
);
|
);
|
||||||
@@ -151,53 +180,30 @@ wxRendererGTK::DrawHeaderButton(wxWindow *win,
|
|||||||
#ifdef __WXGTK20__
|
#ifdef __WXGTK20__
|
||||||
|
|
||||||
// draw a ">" or "v" button
|
// draw a ">" or "v" button
|
||||||
//
|
|
||||||
// TODO: replace the code below with gtk_paint_expander()
|
|
||||||
void
|
void
|
||||||
wxRendererGTK::DrawTreeItemButton(wxWindow* win,
|
wxRendererGTK::DrawTreeItemButton(wxWindow* win,
|
||||||
wxDC& dc, const wxRect& rect, int flags)
|
wxDC& dc, const wxRect& rect, int flags)
|
||||||
{
|
{
|
||||||
#define PM_SIZE 8
|
GtkWidget *tree = GetTreeWidget();
|
||||||
|
|
||||||
GtkPizza *pizza = GTK_PIZZA( win->m_wxwindow );
|
// VZ: I don't know how to get the size of the expander so as to centre it
|
||||||
GtkStyle *style = win->m_widget->style;
|
// in the given rectangle, +2/3 below is just what looks good here...
|
||||||
int x = rect.x;
|
gtk_paint_expander
|
||||||
int y = rect.y;
|
(
|
||||||
y = dc.LogicalToDeviceY( y );
|
tree->style,
|
||||||
x = dc.LogicalToDeviceX( x );
|
GTK_PIZZA(win->m_wxwindow)->bin_window,
|
||||||
|
GTK_STATE_NORMAL,
|
||||||
// This draws the GTK+ 2.2.4 triangle
|
NULL,
|
||||||
x--;
|
tree,
|
||||||
GdkPoint points[3];
|
"treeview",
|
||||||
|
dc.LogicalToDeviceX(rect.x) + 2,
|
||||||
if ( flags & wxCONTROL_EXPANDED )
|
dc.LogicalToDeviceY(rect.y) + 3,
|
||||||
{
|
flags & wxCONTROL_EXPANDED ? GTK_EXPANDER_EXPANDED
|
||||||
points[0].x = x;
|
: GTK_EXPANDER_COLLAPSED
|
||||||
points[0].y = y + (PM_SIZE + 2) / 6;
|
);
|
||||||
points[1].x = points[0].x + (PM_SIZE + 2);
|
|
||||||
points[1].y = points[0].y;
|
|
||||||
points[2].x = (points[0].x + (PM_SIZE + 2) / 2);
|
|
||||||
points[2].y = y + 2 * (PM_SIZE + 2) / 3;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
points[0].x = x + ((PM_SIZE + 2) / 6 + 2);
|
|
||||||
points[0].y = y - 1;
|
|
||||||
points[1].x = points[0].x;
|
|
||||||
points[1].y = points[0].y + (PM_SIZE + 2);
|
|
||||||
points[2].x = (points[0].x +
|
|
||||||
(2 * (PM_SIZE + 2) / 3 - 1));
|
|
||||||
points[2].y = points[0].y + (PM_SIZE + 2) / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( flags & wxCONTROL_CURRENT )
|
|
||||||
gdk_draw_polygon( pizza->bin_window, style->fg_gc[GTK_STATE_PRELIGHT], TRUE, points, 3);
|
|
||||||
else
|
|
||||||
gdk_draw_polygon( pizza->bin_window, style->base_gc[GTK_STATE_NORMAL], TRUE, points, 3);
|
|
||||||
gdk_draw_polygon( pizza->bin_window, style->fg_gc[GTK_STATE_NORMAL], FALSE, points, 3 );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // __WXGTK20__
|
#endif // GTK+ 2.0
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// splitter sash drawing
|
// splitter sash drawing
|
||||||
@@ -372,8 +378,7 @@ void wxRendererGTK::DrawComboBoxDropButton(wxWindow *win,
|
|||||||
const wxRect& rect,
|
const wxRect& rect,
|
||||||
int flags)
|
int flags)
|
||||||
{
|
{
|
||||||
if (gs_button == NULL)
|
GtkWidget *button = GetButtonWidget();
|
||||||
PrepareButtonDraw();
|
|
||||||
|
|
||||||
// device context must inherit from wxWindowDC
|
// device context must inherit from wxWindowDC
|
||||||
// (so it must be wxClientDC, wxMemoryDC or wxPaintDC)
|
// (so it must be wxClientDC, wxMemoryDC or wxPaintDC)
|
||||||
@@ -394,12 +399,12 @@ void wxRendererGTK::DrawComboBoxDropButton(wxWindow *win,
|
|||||||
// erase background first
|
// erase background first
|
||||||
gtk_paint_box
|
gtk_paint_box
|
||||||
(
|
(
|
||||||
gs_button->style,
|
button->style,
|
||||||
wdc.m_window,
|
wdc.m_window,
|
||||||
state,
|
state,
|
||||||
GTK_SHADOW_NONE,
|
GTK_SHADOW_NONE,
|
||||||
NULL,
|
NULL,
|
||||||
gs_button,
|
button,
|
||||||
"button",
|
"button",
|
||||||
rect.x, rect.y, rect.width, rect.height
|
rect.x, rect.y, rect.width, rect.height
|
||||||
);
|
);
|
||||||
@@ -407,12 +412,12 @@ void wxRendererGTK::DrawComboBoxDropButton(wxWindow *win,
|
|||||||
// draw arrow on button
|
// draw arrow on button
|
||||||
gtk_paint_arrow
|
gtk_paint_arrow
|
||||||
(
|
(
|
||||||
gs_button->style,
|
button->style,
|
||||||
wdc.m_window,
|
wdc.m_window,
|
||||||
state,
|
state,
|
||||||
flags & wxCONTROL_PRESSED ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
|
flags & wxCONTROL_PRESSED ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
|
||||||
NULL,
|
NULL,
|
||||||
gs_button,
|
button,
|
||||||
"arrow",
|
"arrow",
|
||||||
GTK_ARROW_DOWN,
|
GTK_ARROW_DOWN,
|
||||||
FALSE,
|
FALSE,
|
||||||
|
@@ -82,8 +82,15 @@ public:
|
|||||||
virtual wxSplitterRenderParams GetSplitterParams(const wxWindow *win);
|
virtual wxSplitterRenderParams GetSplitterParams(const wxWindow *win);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// FIXME: shouldn't we destroy these windows somewhere?
|
||||||
|
|
||||||
// used by DrawHeaderButton and DrawComboBoxDropButton
|
// used by DrawHeaderButton and DrawComboBoxDropButton
|
||||||
void PrepareButtonDraw();
|
static GtkWidget *GetButtonWidget();
|
||||||
|
|
||||||
|
#ifdef __WXGTK20__
|
||||||
|
// used by DrawTreeItemButton()
|
||||||
|
static GtkWidget *GetTreeWidget();
|
||||||
|
#endif // GTK+ 2.0
|
||||||
};
|
};
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -99,26 +106,49 @@ wxRendererNative& wxRendererNative::GetDefault()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// common code
|
// helper functions
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
static GtkWidget *gs_button = NULL;
|
GtkWidget *
|
||||||
static GtkWidget *gs_window = NULL;
|
wxRendererGTK::GetButtonWidget()
|
||||||
|
|
||||||
void
|
|
||||||
wxRendererGTK::PrepareButtonDraw()
|
|
||||||
{
|
{
|
||||||
// prepares gs_button and gs_window which are used when
|
static GtkWidget *s_button = NULL;
|
||||||
// drawing button based elements.
|
static GtkWidget *s_window = NULL;
|
||||||
wxASSERT ( !gs_button );
|
|
||||||
|
|
||||||
gs_window = gtk_window_new( GTK_WINDOW_POPUP );
|
if ( !s_button )
|
||||||
gtk_widget_realize( gs_window );
|
{
|
||||||
gs_button = gtk_button_new();
|
s_window = gtk_window_new( GTK_WINDOW_POPUP );
|
||||||
gtk_container_add( GTK_CONTAINER(gs_window), gs_button );
|
gtk_widget_realize( s_window );
|
||||||
gtk_widget_realize( gs_button );
|
s_button = gtk_button_new();
|
||||||
|
gtk_container_add( GTK_CONTAINER(s_window), s_button );
|
||||||
|
gtk_widget_realize( s_button );
|
||||||
|
}
|
||||||
|
|
||||||
|
return s_button;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __WXGTK20__
|
||||||
|
|
||||||
|
GtkWidget *
|
||||||
|
wxRendererGTK::GetTreeWidget()
|
||||||
|
{
|
||||||
|
static GtkWidget *s_tree = NULL;
|
||||||
|
static GtkWidget *s_window = NULL;
|
||||||
|
|
||||||
|
if ( !s_tree )
|
||||||
|
{
|
||||||
|
s_tree = gtk_tree_view_new();
|
||||||
|
s_window = gtk_window_new( GTK_WINDOW_POPUP );
|
||||||
|
gtk_widget_realize( s_window );
|
||||||
|
gtk_container_add( GTK_CONTAINER(s_window), s_tree );
|
||||||
|
gtk_widget_realize( s_tree );
|
||||||
|
}
|
||||||
|
|
||||||
|
return s_tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // GTK+ 2.0
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// list/tree controls drawing
|
// list/tree controls drawing
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
@@ -130,19 +160,18 @@ wxRendererGTK::DrawHeaderButton(wxWindow *win,
|
|||||||
int flags)
|
int flags)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (gs_button == NULL)
|
GtkWidget *button = GetButtonWidget();
|
||||||
PrepareButtonDraw();
|
|
||||||
|
|
||||||
gtk_paint_box
|
gtk_paint_box
|
||||||
(
|
(
|
||||||
gs_button->style,
|
button->style,
|
||||||
// FIXME: I suppose GTK_PIZZA(win->m_wxwindow)->bin_window doesn't work with wxMemoryDC.
|
// FIXME: I suppose GTK_PIZZA(win->m_wxwindow)->bin_window doesn't work with wxMemoryDC.
|
||||||
// Maybe use code similar as in DrawComboBoxDropButton below?
|
// Maybe use code similar as in DrawComboBoxDropButton below?
|
||||||
GTK_PIZZA(win->m_wxwindow)->bin_window,
|
GTK_PIZZA(win->m_wxwindow)->bin_window,
|
||||||
flags & wxCONTROL_DISABLED ? GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL,
|
flags & wxCONTROL_DISABLED ? GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL,
|
||||||
GTK_SHADOW_OUT,
|
GTK_SHADOW_OUT,
|
||||||
NULL,
|
NULL,
|
||||||
gs_button,
|
button,
|
||||||
"button",
|
"button",
|
||||||
dc.XLOG2DEV(rect.x) -1, rect.y -1, rect.width +2, rect.height +2
|
dc.XLOG2DEV(rect.x) -1, rect.y -1, rect.width +2, rect.height +2
|
||||||
);
|
);
|
||||||
@@ -151,53 +180,30 @@ wxRendererGTK::DrawHeaderButton(wxWindow *win,
|
|||||||
#ifdef __WXGTK20__
|
#ifdef __WXGTK20__
|
||||||
|
|
||||||
// draw a ">" or "v" button
|
// draw a ">" or "v" button
|
||||||
//
|
|
||||||
// TODO: replace the code below with gtk_paint_expander()
|
|
||||||
void
|
void
|
||||||
wxRendererGTK::DrawTreeItemButton(wxWindow* win,
|
wxRendererGTK::DrawTreeItemButton(wxWindow* win,
|
||||||
wxDC& dc, const wxRect& rect, int flags)
|
wxDC& dc, const wxRect& rect, int flags)
|
||||||
{
|
{
|
||||||
#define PM_SIZE 8
|
GtkWidget *tree = GetTreeWidget();
|
||||||
|
|
||||||
GtkPizza *pizza = GTK_PIZZA( win->m_wxwindow );
|
// VZ: I don't know how to get the size of the expander so as to centre it
|
||||||
GtkStyle *style = win->m_widget->style;
|
// in the given rectangle, +2/3 below is just what looks good here...
|
||||||
int x = rect.x;
|
gtk_paint_expander
|
||||||
int y = rect.y;
|
(
|
||||||
y = dc.LogicalToDeviceY( y );
|
tree->style,
|
||||||
x = dc.LogicalToDeviceX( x );
|
GTK_PIZZA(win->m_wxwindow)->bin_window,
|
||||||
|
GTK_STATE_NORMAL,
|
||||||
// This draws the GTK+ 2.2.4 triangle
|
NULL,
|
||||||
x--;
|
tree,
|
||||||
GdkPoint points[3];
|
"treeview",
|
||||||
|
dc.LogicalToDeviceX(rect.x) + 2,
|
||||||
if ( flags & wxCONTROL_EXPANDED )
|
dc.LogicalToDeviceY(rect.y) + 3,
|
||||||
{
|
flags & wxCONTROL_EXPANDED ? GTK_EXPANDER_EXPANDED
|
||||||
points[0].x = x;
|
: GTK_EXPANDER_COLLAPSED
|
||||||
points[0].y = y + (PM_SIZE + 2) / 6;
|
);
|
||||||
points[1].x = points[0].x + (PM_SIZE + 2);
|
|
||||||
points[1].y = points[0].y;
|
|
||||||
points[2].x = (points[0].x + (PM_SIZE + 2) / 2);
|
|
||||||
points[2].y = y + 2 * (PM_SIZE + 2) / 3;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
points[0].x = x + ((PM_SIZE + 2) / 6 + 2);
|
|
||||||
points[0].y = y - 1;
|
|
||||||
points[1].x = points[0].x;
|
|
||||||
points[1].y = points[0].y + (PM_SIZE + 2);
|
|
||||||
points[2].x = (points[0].x +
|
|
||||||
(2 * (PM_SIZE + 2) / 3 - 1));
|
|
||||||
points[2].y = points[0].y + (PM_SIZE + 2) / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( flags & wxCONTROL_CURRENT )
|
|
||||||
gdk_draw_polygon( pizza->bin_window, style->fg_gc[GTK_STATE_PRELIGHT], TRUE, points, 3);
|
|
||||||
else
|
|
||||||
gdk_draw_polygon( pizza->bin_window, style->base_gc[GTK_STATE_NORMAL], TRUE, points, 3);
|
|
||||||
gdk_draw_polygon( pizza->bin_window, style->fg_gc[GTK_STATE_NORMAL], FALSE, points, 3 );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // __WXGTK20__
|
#endif // GTK+ 2.0
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// splitter sash drawing
|
// splitter sash drawing
|
||||||
@@ -372,8 +378,7 @@ void wxRendererGTK::DrawComboBoxDropButton(wxWindow *win,
|
|||||||
const wxRect& rect,
|
const wxRect& rect,
|
||||||
int flags)
|
int flags)
|
||||||
{
|
{
|
||||||
if (gs_button == NULL)
|
GtkWidget *button = GetButtonWidget();
|
||||||
PrepareButtonDraw();
|
|
||||||
|
|
||||||
// device context must inherit from wxWindowDC
|
// device context must inherit from wxWindowDC
|
||||||
// (so it must be wxClientDC, wxMemoryDC or wxPaintDC)
|
// (so it must be wxClientDC, wxMemoryDC or wxPaintDC)
|
||||||
@@ -394,12 +399,12 @@ void wxRendererGTK::DrawComboBoxDropButton(wxWindow *win,
|
|||||||
// erase background first
|
// erase background first
|
||||||
gtk_paint_box
|
gtk_paint_box
|
||||||
(
|
(
|
||||||
gs_button->style,
|
button->style,
|
||||||
wdc.m_window,
|
wdc.m_window,
|
||||||
state,
|
state,
|
||||||
GTK_SHADOW_NONE,
|
GTK_SHADOW_NONE,
|
||||||
NULL,
|
NULL,
|
||||||
gs_button,
|
button,
|
||||||
"button",
|
"button",
|
||||||
rect.x, rect.y, rect.width, rect.height
|
rect.x, rect.y, rect.width, rect.height
|
||||||
);
|
);
|
||||||
@@ -407,12 +412,12 @@ void wxRendererGTK::DrawComboBoxDropButton(wxWindow *win,
|
|||||||
// draw arrow on button
|
// draw arrow on button
|
||||||
gtk_paint_arrow
|
gtk_paint_arrow
|
||||||
(
|
(
|
||||||
gs_button->style,
|
button->style,
|
||||||
wdc.m_window,
|
wdc.m_window,
|
||||||
state,
|
state,
|
||||||
flags & wxCONTROL_PRESSED ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
|
flags & wxCONTROL_PRESSED ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
|
||||||
NULL,
|
NULL,
|
||||||
gs_button,
|
button,
|
||||||
"arrow",
|
"arrow",
|
||||||
GTK_ARROW_DOWN,
|
GTK_ARROW_DOWN,
|
||||||
FALSE,
|
FALSE,
|
||||||
|
Reference in New Issue
Block a user