app: add support for subpixel image grids
In particular, this enables grids whose points of intersection are at the middle of the image's pixels, which is useful for undistorted painting with odd-sized brushes using tools other than the pencil. This commit also changes the grid visibility behavior, so that the the visibiltiy of horizontal and vertical grid lines (depending on the zoom level) is independent.
This commit is contained in:
@ -108,14 +108,14 @@ gimp_grid_class_init (GimpGridClass *klass)
|
|||||||
"xspacing",
|
"xspacing",
|
||||||
_("Spacing X"),
|
_("Spacing X"),
|
||||||
_("Horizontal spacing of grid lines."),
|
_("Horizontal spacing of grid lines."),
|
||||||
1.0, GIMP_MAX_IMAGE_SIZE, 10.0,
|
0.0, GIMP_MAX_IMAGE_SIZE, 10.0,
|
||||||
GIMP_PARAM_STATIC_STRINGS);
|
GIMP_PARAM_STATIC_STRINGS);
|
||||||
|
|
||||||
GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_YSPACING,
|
GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_YSPACING,
|
||||||
"yspacing",
|
"yspacing",
|
||||||
_("Spacing Y"),
|
_("Spacing Y"),
|
||||||
_("Vertical spacing of grid lines."),
|
_("Vertical spacing of grid lines."),
|
||||||
1.0, GIMP_MAX_IMAGE_SIZE, 10.0,
|
0.0, GIMP_MAX_IMAGE_SIZE, 10.0,
|
||||||
GIMP_PARAM_STATIC_STRINGS);
|
GIMP_PARAM_STATIC_STRINGS);
|
||||||
|
|
||||||
GIMP_CONFIG_PROP_UNIT (object_class, PROP_SPACING_UNIT,
|
GIMP_CONFIG_PROP_UNIT (object_class, PROP_SPACING_UNIT,
|
||||||
@ -283,9 +283,8 @@ gimp_grid_get_spacing (GimpGrid *grid,
|
|||||||
{
|
{
|
||||||
g_return_if_fail (GIMP_IS_GRID (grid));
|
g_return_if_fail (GIMP_IS_GRID (grid));
|
||||||
|
|
||||||
/* FIXME subpixel grid */
|
if (xspacing) *xspacing = grid->xspacing;
|
||||||
if (xspacing) *xspacing = RINT (grid->xspacing);
|
if (yspacing) *yspacing = grid->yspacing;
|
||||||
if (yspacing) *yspacing = RINT (grid->yspacing);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -295,9 +294,8 @@ gimp_grid_get_offset (GimpGrid *grid,
|
|||||||
{
|
{
|
||||||
g_return_if_fail (GIMP_IS_GRID (grid));
|
g_return_if_fail (GIMP_IS_GRID (grid));
|
||||||
|
|
||||||
/* FIXME subpixel grid */
|
if (xoffset) *xoffset = grid->xoffset;
|
||||||
if (xoffset) *xoffset = RINT (grid->xoffset);
|
if (yoffset) *yoffset = grid->yoffset;
|
||||||
if (yoffset) *yoffset = RINT (grid->yoffset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const gchar *
|
const gchar *
|
||||||
|
@ -100,21 +100,17 @@ gimp_image_snap_x (GimpImage *image,
|
|||||||
GimpGrid *grid = gimp_image_get_grid (image);
|
GimpGrid *grid = gimp_image_get_grid (image);
|
||||||
gdouble xspacing;
|
gdouble xspacing;
|
||||||
gdouble xoffset;
|
gdouble xoffset;
|
||||||
gdouble i;
|
|
||||||
|
|
||||||
gimp_grid_get_spacing (grid, &xspacing, NULL);
|
gimp_grid_get_spacing (grid, &xspacing, NULL);
|
||||||
gimp_grid_get_offset (grid, &xoffset, NULL);
|
gimp_grid_get_offset (grid, &xoffset, NULL);
|
||||||
|
|
||||||
/* the snap-to-grid part could probably be rewritten */
|
if (xspacing > 0.0)
|
||||||
while (xoffset > xspacing)
|
|
||||||
xoffset -= xspacing;
|
|
||||||
|
|
||||||
for (i = xoffset; i <= gimp_image_get_width (image); i += xspacing)
|
|
||||||
{
|
{
|
||||||
if (i < 0)
|
gdouble nearest;
|
||||||
continue;
|
|
||||||
|
|
||||||
snapped |= gimp_image_snap_distance (x, i,
|
nearest = xoffset + RINT ((x - xoffset) / xspacing) * xspacing;
|
||||||
|
|
||||||
|
snapped |= gimp_image_snap_distance (x, nearest,
|
||||||
epsilon_x,
|
epsilon_x,
|
||||||
&mindist, tx);
|
&mindist, tx);
|
||||||
}
|
}
|
||||||
@ -183,22 +179,19 @@ gimp_image_snap_y (GimpImage *image,
|
|||||||
if (snap_to_grid)
|
if (snap_to_grid)
|
||||||
{
|
{
|
||||||
GimpGrid *grid = gimp_image_get_grid (image);
|
GimpGrid *grid = gimp_image_get_grid (image);
|
||||||
gdouble yspacing;
|
gdouble yspacing;
|
||||||
gdouble yoffset;
|
gdouble yoffset;
|
||||||
gdouble i;
|
|
||||||
|
|
||||||
gimp_grid_get_spacing (grid, NULL, &yspacing);
|
gimp_grid_get_spacing (grid, NULL, &yspacing);
|
||||||
gimp_grid_get_offset (grid, NULL, &yoffset);
|
gimp_grid_get_offset (grid, NULL, &yoffset);
|
||||||
|
|
||||||
while (yoffset > yspacing)
|
if (yspacing > 0.0)
|
||||||
yoffset -= yspacing;
|
|
||||||
|
|
||||||
for (i = yoffset; i <= gimp_image_get_height (image); i += yspacing)
|
|
||||||
{
|
{
|
||||||
if (i < 0)
|
gdouble nearest;
|
||||||
continue;
|
|
||||||
|
|
||||||
snapped |= gimp_image_snap_distance (y, i,
|
nearest = yoffset + RINT ((y - yoffset) / yspacing) * yspacing;
|
||||||
|
|
||||||
|
snapped |= gimp_image_snap_distance (y, nearest,
|
||||||
epsilon_y,
|
epsilon_y,
|
||||||
&mindist, ty);
|
&mindist, ty);
|
||||||
}
|
}
|
||||||
@ -291,33 +284,28 @@ gimp_image_snap_point (GimpImage *image,
|
|||||||
GimpGrid *grid = gimp_image_get_grid (image);
|
GimpGrid *grid = gimp_image_get_grid (image);
|
||||||
gdouble xspacing, yspacing;
|
gdouble xspacing, yspacing;
|
||||||
gdouble xoffset, yoffset;
|
gdouble xoffset, yoffset;
|
||||||
gdouble i;
|
|
||||||
|
|
||||||
gimp_grid_get_spacing (grid, &xspacing, &yspacing);
|
gimp_grid_get_spacing (grid, &xspacing, &yspacing);
|
||||||
gimp_grid_get_offset (grid, &xoffset, &yoffset);
|
gimp_grid_get_offset (grid, &xoffset, &yoffset);
|
||||||
|
|
||||||
while (xoffset > xspacing)
|
if (xspacing > 0.0)
|
||||||
xoffset -= xspacing;
|
|
||||||
|
|
||||||
while (yoffset > yspacing)
|
|
||||||
yoffset -= yspacing;
|
|
||||||
|
|
||||||
for (i = xoffset; i <= gimp_image_get_width (image); i += xspacing)
|
|
||||||
{
|
{
|
||||||
if (i < 0)
|
gdouble nearest;
|
||||||
continue;
|
|
||||||
|
|
||||||
snapped |= gimp_image_snap_distance (x, i,
|
nearest = xoffset + RINT ((x - xoffset) / xspacing) * xspacing;
|
||||||
|
|
||||||
|
snapped |= gimp_image_snap_distance (x, nearest,
|
||||||
epsilon_x,
|
epsilon_x,
|
||||||
&mindist_x, tx);
|
&mindist_x, tx);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = yoffset; i <= gimp_image_get_height (image); i += yspacing)
|
if (yspacing > 0.0)
|
||||||
{
|
{
|
||||||
if (i < 0)
|
gdouble nearest;
|
||||||
continue;
|
|
||||||
|
|
||||||
snapped |= gimp_image_snap_distance (y, i,
|
nearest = yoffset + RINT ((y - yoffset) / yspacing) * yspacing;
|
||||||
|
|
||||||
|
snapped |= gimp_image_snap_distance (y, nearest,
|
||||||
epsilon_y,
|
epsilon_y,
|
||||||
&mindist_y, ty);
|
&mindist_y, ty);
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,8 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
#include <gegl.h>
|
#include <gegl.h>
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
@ -191,6 +193,7 @@ gimp_canvas_grid_draw (GimpCanvasItem *item,
|
|||||||
GimpImage *image = gimp_canvas_item_get_image (item);
|
GimpImage *image = gimp_canvas_item_get_image (item);
|
||||||
gdouble xspacing, yspacing;
|
gdouble xspacing, yspacing;
|
||||||
gdouble xoffset, yoffset;
|
gdouble xoffset, yoffset;
|
||||||
|
gboolean vert, horz;
|
||||||
gdouble x, y;
|
gdouble x, y;
|
||||||
gdouble dx1, dy1, dx2, dy2;
|
gdouble dx1, dy1, dx2, dy2;
|
||||||
gint x0, x1, x2, x3;
|
gint x0, x1, x2, x3;
|
||||||
@ -203,14 +206,16 @@ gimp_canvas_grid_draw (GimpCanvasItem *item,
|
|||||||
gimp_grid_get_spacing (private->grid, &xspacing, &yspacing);
|
gimp_grid_get_spacing (private->grid, &xspacing, &yspacing);
|
||||||
gimp_grid_get_offset (private->grid, &xoffset, &yoffset);
|
gimp_grid_get_offset (private->grid, &xoffset, &yoffset);
|
||||||
|
|
||||||
g_return_if_fail (xspacing > 0.0 &&
|
g_return_if_fail (xspacing >= 0.0 &&
|
||||||
yspacing > 0.0);
|
yspacing >= 0.0);
|
||||||
|
|
||||||
/* skip grid drawing when the space between grid lines starts
|
/* skip grid drawing when the space between grid lines starts
|
||||||
* disappearing, see bug #599267.
|
* disappearing, see bug #599267.
|
||||||
*/
|
*/
|
||||||
if (xspacing * shell->scale_x < 2.0 ||
|
vert = (xspacing * shell->scale_x >= 2.0);
|
||||||
yspacing * shell->scale_y < 2.0)
|
horz = (yspacing * shell->scale_y >= 2.0);
|
||||||
|
|
||||||
|
if (! vert && ! horz)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
cairo_clip_extents (cr, &dx1, &dy1, &dx2, &dy2);
|
cairo_clip_extents (cr, &dx1, &dy1, &dx2, &dy2);
|
||||||
@ -223,84 +228,87 @@ gimp_canvas_grid_draw (GimpCanvasItem *item,
|
|||||||
width = gimp_image_get_width (image);
|
width = gimp_image_get_width (image);
|
||||||
height = gimp_image_get_height (image);
|
height = gimp_image_get_height (image);
|
||||||
|
|
||||||
while (xoffset > 0)
|
xoffset = fmod (xoffset, xspacing);
|
||||||
xoffset -= xspacing;
|
yoffset = fmod (yoffset, yspacing);
|
||||||
|
|
||||||
while (yoffset > 0)
|
|
||||||
yoffset -= yspacing;
|
|
||||||
|
|
||||||
switch (gimp_grid_get_style (private->grid))
|
switch (gimp_grid_get_style (private->grid))
|
||||||
{
|
{
|
||||||
case GIMP_GRID_DOTS:
|
case GIMP_GRID_DOTS:
|
||||||
for (x = xoffset; x <= width; x += xspacing)
|
if (vert && horz)
|
||||||
{
|
{
|
||||||
if (x < 0)
|
for (x = xoffset; x <= width; x += xspacing)
|
||||||
continue;
|
|
||||||
|
|
||||||
gimp_canvas_item_transform_xy (item, x, 0, &x_real, &y_real);
|
|
||||||
|
|
||||||
if (x_real < x1 || x_real >= x2)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
for (y = yoffset; y <= height; y += yspacing)
|
|
||||||
{
|
{
|
||||||
if (y < 0)
|
if (x < 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
gimp_canvas_item_transform_xy (item, x, y, &x_real, &y_real);
|
gimp_canvas_item_transform_xy (item, x, 0, &x_real, &y_real);
|
||||||
|
|
||||||
if (y_real >= y1 && y_real < y2)
|
if (x_real < x1 || x_real >= x2)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (y = yoffset; y <= height; y += yspacing)
|
||||||
{
|
{
|
||||||
cairo_move_to (cr, x_real, y_real + 0.5);
|
if (y < 0)
|
||||||
cairo_line_to (cr, x_real + 1, y_real + 0.5);
|
continue;
|
||||||
|
|
||||||
|
gimp_canvas_item_transform_xy (item, x, y, &x_real, &y_real);
|
||||||
|
|
||||||
|
if (y_real >= y1 && y_real < y2)
|
||||||
|
{
|
||||||
|
cairo_move_to (cr, x_real, y_real + 0.5);
|
||||||
|
cairo_line_to (cr, x_real + 1, y_real + 0.5);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GIMP_GRID_INTERSECTIONS:
|
case GIMP_GRID_INTERSECTIONS:
|
||||||
for (x = xoffset; x <= width; x += xspacing)
|
if (vert && horz)
|
||||||
{
|
{
|
||||||
if (x < 0)
|
for (x = xoffset; x <= width; x += xspacing)
|
||||||
continue;
|
|
||||||
|
|
||||||
gimp_canvas_item_transform_xy (item, x, 0, &x_real, &y_real);
|
|
||||||
|
|
||||||
if (x_real + CROSSHAIR < x1 || x_real - CROSSHAIR >= x2)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
for (y = yoffset; y <= height; y += yspacing)
|
|
||||||
{
|
{
|
||||||
if (y < 0)
|
if (x < 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
gimp_canvas_item_transform_xy (item, x, y, &x_real, &y_real);
|
gimp_canvas_item_transform_xy (item, x, 0, &x_real, &y_real);
|
||||||
|
|
||||||
if (y_real + CROSSHAIR < y1 || y_real - CROSSHAIR >= y2)
|
if (x_real + CROSSHAIR < x1 || x_real - CROSSHAIR >= x2)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (x_real >= x1 && x_real < x2)
|
for (y = yoffset; y <= height; y += yspacing)
|
||||||
{
|
{
|
||||||
cairo_move_to (cr,
|
if (y < 0)
|
||||||
x_real + 0.5,
|
continue;
|
||||||
CLAMP (y_real - CROSSHAIR,
|
|
||||||
y1, y2 - 1));
|
|
||||||
cairo_line_to (cr,
|
|
||||||
x_real + 0.5,
|
|
||||||
CLAMP (y_real + CROSSHAIR,
|
|
||||||
y1, y2 - 1) + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (y_real >= y1 && y_real < y2)
|
gimp_canvas_item_transform_xy (item, x, y, &x_real, &y_real);
|
||||||
{
|
|
||||||
cairo_move_to (cr,
|
if (y_real + CROSSHAIR < y1 || y_real - CROSSHAIR >= y2)
|
||||||
CLAMP (x_real - CROSSHAIR,
|
continue;
|
||||||
x1, x2 - 1),
|
|
||||||
y_real + 0.5);
|
if (x_real >= x1 && x_real < x2)
|
||||||
cairo_line_to (cr,
|
{
|
||||||
CLAMP (x_real + CROSSHAIR,
|
cairo_move_to (cr,
|
||||||
x1, x2 - 1) + 1,
|
x_real + 0.5,
|
||||||
y_real + 0.5);
|
CLAMP (y_real - CROSSHAIR,
|
||||||
|
y1, y2 - 1));
|
||||||
|
cairo_line_to (cr,
|
||||||
|
x_real + 0.5,
|
||||||
|
CLAMP (y_real + CROSSHAIR,
|
||||||
|
y1, y2 - 1) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y_real >= y1 && y_real < y2)
|
||||||
|
{
|
||||||
|
cairo_move_to (cr,
|
||||||
|
CLAMP (x_real - CROSSHAIR,
|
||||||
|
x1, x2 - 1),
|
||||||
|
y_real + 0.5);
|
||||||
|
cairo_line_to (cr,
|
||||||
|
CLAMP (x_real + CROSSHAIR,
|
||||||
|
x1, x2 - 1) + 1,
|
||||||
|
y_real + 0.5);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -312,31 +320,37 @@ gimp_canvas_grid_draw (GimpCanvasItem *item,
|
|||||||
gimp_canvas_item_transform_xy (item, 0, 0, &x0, &y0);
|
gimp_canvas_item_transform_xy (item, 0, 0, &x0, &y0);
|
||||||
gimp_canvas_item_transform_xy (item, width, height, &x3, &y3);
|
gimp_canvas_item_transform_xy (item, width, height, &x3, &y3);
|
||||||
|
|
||||||
for (x = xoffset; x < width; x += xspacing)
|
if (vert)
|
||||||
{
|
{
|
||||||
if (x < 0)
|
for (x = xoffset; x < width; x += xspacing)
|
||||||
continue;
|
|
||||||
|
|
||||||
gimp_canvas_item_transform_xy (item, x, 0, &x_real, &y_real);
|
|
||||||
|
|
||||||
if (x_real >= x1 && x_real < x2)
|
|
||||||
{
|
{
|
||||||
cairo_move_to (cr, x_real + 0.5, y0);
|
if (x < 0)
|
||||||
cairo_line_to (cr, x_real + 0.5, y3 + 1);
|
continue;
|
||||||
|
|
||||||
|
gimp_canvas_item_transform_xy (item, x, 0, &x_real, &y_real);
|
||||||
|
|
||||||
|
if (x_real >= x1 && x_real < x2)
|
||||||
|
{
|
||||||
|
cairo_move_to (cr, x_real + 0.5, y0);
|
||||||
|
cairo_line_to (cr, x_real + 0.5, y3 + 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (y = yoffset; y < height; y += yspacing)
|
if (horz)
|
||||||
{
|
{
|
||||||
if (y < 0)
|
for (y = yoffset; y < height; y += yspacing)
|
||||||
continue;
|
|
||||||
|
|
||||||
gimp_canvas_item_transform_xy (item, 0, y, &x_real, &y_real);
|
|
||||||
|
|
||||||
if (y_real >= y1 && y_real < y2)
|
|
||||||
{
|
{
|
||||||
cairo_move_to (cr, x0, y_real + 0.5);
|
if (y < 0)
|
||||||
cairo_line_to (cr, x3 + 1, y_real + 0.5);
|
continue;
|
||||||
|
|
||||||
|
gimp_canvas_item_transform_xy (item, 0, y, &x_real, &y_real);
|
||||||
|
|
||||||
|
if (y_real >= y1 && y_real < y2)
|
||||||
|
{
|
||||||
|
cairo_move_to (cr, x0, y_real + 0.5);
|
||||||
|
cairo_line_to (cr, x3 + 1, y_real + 0.5);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -192,12 +192,15 @@ gimp_grid_editor_constructed (GObject *object)
|
|||||||
gtk_table_set_row_spacings (GTK_TABLE (sizeentry), 2);
|
gtk_table_set_row_spacings (GTK_TABLE (sizeentry), 2);
|
||||||
|
|
||||||
gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (sizeentry),
|
gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (sizeentry),
|
||||||
_("Width"), 0, 1, 0.0);
|
_("Horizontal"), 0, 1, 0.0);
|
||||||
gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (sizeentry),
|
gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (sizeentry),
|
||||||
_("Height"), 0, 2, 0.0);
|
_("Vertical"), 0, 2, 0.0);
|
||||||
gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (sizeentry),
|
gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (sizeentry),
|
||||||
_("Pixels"), 1, 4, 0.0);
|
_("Pixels"), 1, 4, 0.0);
|
||||||
|
|
||||||
|
gimp_size_entry_set_refval_digits (GIMP_SIZE_ENTRY (sizeentry), 0, 2);
|
||||||
|
gimp_size_entry_set_refval_digits (GIMP_SIZE_ENTRY (sizeentry), 1, 2);
|
||||||
|
|
||||||
gtk_box_pack_start (GTK_BOX (hbox), sizeentry, FALSE, FALSE, 0);
|
gtk_box_pack_start (GTK_BOX (hbox), sizeentry, FALSE, FALSE, 0);
|
||||||
gtk_widget_show (sizeentry);
|
gtk_widget_show (sizeentry);
|
||||||
|
|
||||||
@ -224,12 +227,15 @@ gimp_grid_editor_constructed (GObject *object)
|
|||||||
gtk_table_set_row_spacings (GTK_TABLE (sizeentry), 2);
|
gtk_table_set_row_spacings (GTK_TABLE (sizeentry), 2);
|
||||||
|
|
||||||
gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (sizeentry),
|
gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (sizeentry),
|
||||||
_("Width"), 0, 1, 0.0);
|
_("Horizontal"), 0, 1, 0.0);
|
||||||
gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (sizeentry),
|
gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (sizeentry),
|
||||||
_("Height"), 0, 2, 0.0);
|
_("Vertical"), 0, 2, 0.0);
|
||||||
gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (sizeentry),
|
gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (sizeentry),
|
||||||
_("Pixels"), 1, 4, 0.0);
|
_("Pixels"), 1, 4, 0.0);
|
||||||
|
|
||||||
|
gimp_size_entry_set_refval_digits (GIMP_SIZE_ENTRY (sizeentry), 0, 2);
|
||||||
|
gimp_size_entry_set_refval_digits (GIMP_SIZE_ENTRY (sizeentry), 1, 2);
|
||||||
|
|
||||||
gtk_box_pack_start (GTK_BOX (hbox), sizeentry, FALSE, FALSE, 0);
|
gtk_box_pack_start (GTK_BOX (hbox), sizeentry, FALSE, FALSE, 0);
|
||||||
gtk_widget_show (sizeentry);
|
gtk_widget_show (sizeentry);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user