New progressbar option: vertical=<boolean>
if 'true', draws the whole progressbar-block vertically instead of horizontally.
This commit is contained in:
parent
f4447f0026
commit
04ff373a63
|
@ -211,6 +211,7 @@ cfg_opt_t widget_progressbar_opts[] =
|
||||||
CFG_INT((char *) "gap", 2, CFGF_NONE),
|
CFG_INT((char *) "gap", 2, CFGF_NONE),
|
||||||
CFG_INT((char *) "padding", 0, CFGF_NONE),
|
CFG_INT((char *) "padding", 0, CFGF_NONE),
|
||||||
CFG_FLOAT((char *) "height", 0.67, CFGF_NONE),
|
CFG_FLOAT((char *) "height", 0.67, CFGF_NONE),
|
||||||
|
CFG_BOOL((char *) "vertical", cfg_false, CFGF_NONE),
|
||||||
CFG_AWESOME_END()
|
CFG_AWESOME_END()
|
||||||
};
|
};
|
||||||
cfg_opt_t statusbar_opts[] =
|
cfg_opt_t statusbar_opts[] =
|
||||||
|
|
|
@ -213,45 +213,39 @@ draw_text(DrawCtx *ctx,
|
||||||
|
|
||||||
/** Setup color-source for cairo (gradient or mono)
|
/** Setup color-source for cairo (gradient or mono)
|
||||||
* \param ctx Draw context
|
* \param ctx Draw context
|
||||||
* \param x x-offset of pattern start
|
* \param rect x,y to x+x_offset,y+y_offset
|
||||||
* \param y y-offset of widget
|
* \param color color to use at start (x,y)
|
||||||
* \param width pattern width
|
|
||||||
* \param color color to use at start (x)
|
|
||||||
* \param pcolor_center color at 50% of width
|
* \param pcolor_center color at 50% of width
|
||||||
* \param pcolor_end color at pattern start (x) + pattern width (width)
|
* \param pcolor_end color at pattern end (x + x_offset, y + y_offset)
|
||||||
* \return pat pattern or NULL; needs to get cairo_pattern_destroy()'ed;
|
* \return pat pattern or NULL; needs to get cairo_pattern_destroy()'ed;
|
||||||
*/
|
*/
|
||||||
static cairo_pattern_t *
|
static cairo_pattern_t *
|
||||||
draw_setup_cairo_color_source(DrawCtx *ctx, int x, int y, int width,
|
draw_setup_cairo_color_source(DrawCtx *ctx, Area rect,
|
||||||
XColor *pcolor, XColor *pcolor_center, XColor *pcolor_end)
|
XColor *pcolor, XColor *pcolor_center, XColor *pcolor_end)
|
||||||
{
|
{
|
||||||
cairo_pattern_t *pat = NULL;
|
cairo_pattern_t *pat = NULL;
|
||||||
|
|
||||||
/* no need for a real pattern on the next two: */
|
/* no need for a real pattern: */
|
||||||
if(!pcolor_end && !pcolor_center)
|
if(!pcolor_end && !pcolor_center)
|
||||||
cairo_set_source_rgb(ctx->cr, pcolor->red / 65535.0, pcolor->green / 65535.0, pcolor->blue / 65535.0);
|
cairo_set_source_rgb(ctx->cr, pcolor->red / 65535.0, pcolor->green / 65535.0, pcolor->blue / 65535.0);
|
||||||
else if(!pcolor && !pcolor_center)
|
|
||||||
cairo_set_source_rgb(ctx->cr, pcolor_end->red / 65535.0, pcolor_end->green / 65535.0, pcolor_end->blue / 65535.0);
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
pat = cairo_pattern_create_linear(x, y, x + width, y);
|
pat = cairo_pattern_create_linear(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height);
|
||||||
|
|
||||||
/* use pcolor as pcolor_end (and vice versa) when one is not set */
|
/* pcolor is always set (so far in awesome) */
|
||||||
if(pcolor)
|
cairo_pattern_add_color_stop_rgb(pat, 0, pcolor->red / 65535.0,
|
||||||
cairo_pattern_add_color_stop_rgb(pat, 0, pcolor->red / 65535.0,
|
|
||||||
pcolor->green / 65535.0, pcolor->blue / 65535.0);
|
pcolor->green / 65535.0, pcolor->blue / 65535.0);
|
||||||
else
|
|
||||||
cairo_pattern_add_color_stop_rgb(pat, 0, pcolor_end->red / 65535.0,
|
if(pcolor_center)
|
||||||
pcolor_end->green / 65535.0, pcolor_end->blue / 65535.0);
|
cairo_pattern_add_color_stop_rgb(pat, 0.5, pcolor_center->red / 65535.0,
|
||||||
|
pcolor_center->green / 65535.0, pcolor_center->blue / 65535.0);
|
||||||
|
|
||||||
if(pcolor_end)
|
if(pcolor_end)
|
||||||
cairo_pattern_add_color_stop_rgb(pat, 1, pcolor_end->red / 65535.0,
|
cairo_pattern_add_color_stop_rgb(pat, 1, pcolor_end->red / 65535.0,
|
||||||
pcolor_end->green / 65535.0, pcolor_end->blue / 65535.0);
|
pcolor_end->green / 65535.0, pcolor_end->blue / 65535.0);
|
||||||
else
|
else
|
||||||
cairo_pattern_add_color_stop_rgb(pat, 1, pcolor->red / 65535.0,
|
cairo_pattern_add_color_stop_rgb(pat, 1, pcolor->red / 65535.0,
|
||||||
pcolor->green / 65535.0, pcolor->blue / 65535.0);
|
pcolor->green / 65535.0, pcolor->blue / 65535.0);
|
||||||
if(pcolor_center)
|
|
||||||
cairo_pattern_add_color_stop_rgb(pat, 0.5, pcolor_center->red / 65535.0,
|
|
||||||
pcolor_center->green / 65535.0, pcolor_center->blue / 65535.0);
|
|
||||||
cairo_set_source(ctx->cr, pat);
|
cairo_set_source(ctx->cr, pat);
|
||||||
}
|
}
|
||||||
return pat;
|
return pat;
|
||||||
|
@ -284,23 +278,22 @@ draw_rectangle(DrawCtx *ctx, Area geometry, Bool filled, XColor color)
|
||||||
* \param ctx Draw context
|
* \param ctx Draw context
|
||||||
* \param geometry geometry
|
* \param geometry geometry
|
||||||
* \param filled filled rectangle?
|
* \param filled filled rectangle?
|
||||||
* \param pattern_start_x pattern start x coord
|
* \param pattern__x pattern start x coord
|
||||||
* \param pattern_width pattern width
|
* \param pattern_width pattern width
|
||||||
* \param color color to use at start
|
* \param color color to use at start
|
||||||
* \param pcolor_center color at 50% of width
|
* \param pcolor_center color at 50% of width
|
||||||
* \param pcolor_end color at pattern_start + pattern_width
|
* \param pcolor_end color at pattern_start + pattern_width
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
draw_rectangle_gradient(DrawCtx *ctx, Area geometry, Bool filled, int pattern_start_x, int pattern_width,
|
draw_rectangle_gradient(DrawCtx *ctx, Area geometry, Bool filled,
|
||||||
XColor *pcolor, XColor *pcolor_center, XColor *pcolor_end)
|
Area pattern_rect, XColor *pcolor, XColor *pcolor_center, XColor *pcolor_end)
|
||||||
{
|
{
|
||||||
cairo_pattern_t *pat;
|
cairo_pattern_t *pat;
|
||||||
|
|
||||||
cairo_set_antialias(ctx->cr, CAIRO_ANTIALIAS_NONE);
|
cairo_set_antialias(ctx->cr, CAIRO_ANTIALIAS_NONE);
|
||||||
cairo_set_line_width(ctx->cr, 1.0);
|
cairo_set_line_width(ctx->cr, 1.0);
|
||||||
|
|
||||||
pat = draw_setup_cairo_color_source(ctx, pattern_start_x, geometry.y, pattern_width,
|
pat = draw_setup_cairo_color_source(ctx, pattern_rect, pcolor, pcolor_center, pcolor_end);
|
||||||
pcolor, pcolor_center, pcolor_end);
|
|
||||||
|
|
||||||
if(filled)
|
if(filled)
|
||||||
{
|
{
|
||||||
|
@ -347,7 +340,13 @@ draw_graph(DrawCtx *ctx, int x, int y, int w, int *from, int *to, int cur_index,
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
cairo_pattern_t *pat;
|
cairo_pattern_t *pat;
|
||||||
pat = draw_setup_cairo_color_source(ctx, x, y, w, pcolor, pcolor_center, pcolor_end);
|
Area rect;
|
||||||
|
rect.x = x;
|
||||||
|
rect.y = y;
|
||||||
|
rect.width = w;
|
||||||
|
rect.height = 0;
|
||||||
|
|
||||||
|
pat = draw_setup_cairo_color_source(ctx, rect, pcolor, pcolor_center, pcolor_end);
|
||||||
|
|
||||||
i = -1;
|
i = -1;
|
||||||
while(++i < w)
|
while(++i < w)
|
||||||
|
@ -385,7 +384,13 @@ draw_graph_line(DrawCtx *ctx, int x, int y, int w, int *to, int cur_index,
|
||||||
int flag = 0; /* used to prevent drawing a line from 0 to 0 values */
|
int flag = 0; /* used to prevent drawing a line from 0 to 0 values */
|
||||||
cairo_pattern_t *pat;
|
cairo_pattern_t *pat;
|
||||||
|
|
||||||
pat = draw_setup_cairo_color_source(ctx, x, y, w, pcolor, pcolor_center, pcolor_end);
|
Area rect;
|
||||||
|
rect.x = x;
|
||||||
|
rect.y = y;
|
||||||
|
rect.width = w;
|
||||||
|
rect.height = 0;
|
||||||
|
|
||||||
|
pat = draw_setup_cairo_color_source(ctx, rect, pcolor, pcolor_center, pcolor_end);
|
||||||
|
|
||||||
/* x-1 (on the border), paints *from* the last point (... not included itself) */
|
/* x-1 (on the border), paints *from* the last point (... not included itself) */
|
||||||
/* makes sense when you assume there is already some line drawn to it. */
|
/* makes sense when you assume there is already some line drawn to it. */
|
||||||
|
|
|
@ -96,7 +96,7 @@ void draw_context_delete(DrawCtx *);
|
||||||
|
|
||||||
void draw_text(DrawCtx *, Area, Alignment, int, XftFont *, char *, int, XColor fg, XColor bg);
|
void draw_text(DrawCtx *, Area, Alignment, int, XftFont *, char *, int, XColor fg, XColor bg);
|
||||||
void draw_rectangle(DrawCtx *, Area, Bool, XColor);
|
void draw_rectangle(DrawCtx *, Area, Bool, XColor);
|
||||||
void draw_rectangle_gradient(DrawCtx *, Area, Bool, int, int, XColor *, XColor *, XColor *);
|
void draw_rectangle_gradient(DrawCtx *, Area, Bool, Area, XColor *, XColor *, XColor *);
|
||||||
|
|
||||||
void draw_graph_setup(DrawCtx *);
|
void draw_graph_setup(DrawCtx *);
|
||||||
void draw_graph(DrawCtx *, int, int, int, int *, int *, int, XColor *, XColor *, XColor *);
|
void draw_graph(DrawCtx *, int, int, int, int *, int *, int, XColor *, XColor *, XColor *);
|
||||||
|
|
|
@ -40,6 +40,8 @@ typedef struct
|
||||||
int gap;
|
int gap;
|
||||||
/** reverse drawing */
|
/** reverse drawing */
|
||||||
Bool *reverse;
|
Bool *reverse;
|
||||||
|
/** 90 Degree's turned */
|
||||||
|
Bool vertical;
|
||||||
/** Number of data_items (bars) */
|
/** Number of data_items (bars) */
|
||||||
int data_items;
|
int data_items;
|
||||||
/** Height 0-1, where 1 is height of statusbar */
|
/** Height 0-1, where 1 is height of statusbar */
|
||||||
|
@ -60,16 +62,14 @@ static int
|
||||||
progressbar_draw(Widget *widget, DrawCtx *ctx, int offset,
|
progressbar_draw(Widget *widget, DrawCtx *ctx, int offset,
|
||||||
int used __attribute__ ((unused)))
|
int used __attribute__ ((unused)))
|
||||||
{
|
{
|
||||||
int i, width, pwidth, margin_top, pb_height, left_offset;
|
int i, pb_x, pb_y, pb_height, pb_width, pb_progress, pb_offset = 0;
|
||||||
Area rectangle;
|
Area rectangle, pattern_rect;
|
||||||
|
|
||||||
Data *d = widget->data;
|
Data *d = widget->data;
|
||||||
|
|
||||||
if (!d->data_items)
|
if (!d->data_items)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
width = d->width - 2 * d->padding;
|
|
||||||
|
|
||||||
if(!widget->user_supplied_x)
|
if(!widget->user_supplied_x)
|
||||||
widget->area.x = widget_calculate_offset(widget->statusbar->width,
|
widget->area.x = widget_calculate_offset(widget->statusbar->width,
|
||||||
d->width,
|
d->width,
|
||||||
|
@ -79,51 +79,158 @@ progressbar_draw(Widget *widget, DrawCtx *ctx, int offset,
|
||||||
if(!widget->user_supplied_y)
|
if(!widget->user_supplied_y)
|
||||||
widget->area.y = 0;
|
widget->area.y = 0;
|
||||||
|
|
||||||
margin_top = (int) (widget->statusbar->height * (1 - d->height)) / 2 + 0.5 + widget->area.y;
|
/* data for the first rectangle (data-bar) to draw */
|
||||||
pb_height = (int) ((widget->statusbar->height * d->height - (d->gap * (d->data_items - 1))) / d->data_items + 0.5);
|
if(d->vertical)
|
||||||
left_offset = widget->area.x + d->padding;
|
|
||||||
|
|
||||||
for(i = 0; i < d->data_items; i++)
|
|
||||||
{
|
{
|
||||||
if(d->reverse[i])
|
pb_width = (int) ((d->width - d->padding - (d->gap * (d->data_items - 1))) / d->data_items + 0.5);
|
||||||
pwidth = (int)(((width - 2) * (100 - d->percent[i])) / 100);
|
pb_height = (int) (widget->statusbar->height * d->height);
|
||||||
else
|
pb_y = widget->area.y + (int)((widget->statusbar->height - pb_height) / 2 + 0.5);
|
||||||
pwidth = (int)(((width - 2) * d->percent[i]) / 100);
|
}
|
||||||
|
else /* horizontal */
|
||||||
|
{
|
||||||
|
pb_width = d->width - d->padding;
|
||||||
|
pb_height = (int) ((widget->statusbar->height * d->height - (d->gap * (d->data_items - 1))) / d->data_items + 0.5);
|
||||||
|
pb_y = widget->area.y + (int)((widget->statusbar->height - widget->statusbar->height * d->height) / 2 + 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
rectangle.x = left_offset;
|
pb_x = widget->area.x + d->padding;
|
||||||
rectangle.y = margin_top;
|
|
||||||
rectangle.width = width;
|
|
||||||
rectangle.height = pb_height;
|
|
||||||
|
|
||||||
draw_rectangle(ctx, rectangle, False, d->bordercolor[i]);
|
/* for a 'reversed' progressbar:
|
||||||
|
* 1. the full space gets the size of the formerly empty one
|
||||||
if(pwidth > 0) /* filled area */
|
* 2. the pattern must be mirrored
|
||||||
|
* 3. the formerly 'empty' side gets drawed with fg colors, the 'full' with bg-color
|
||||||
|
*
|
||||||
|
* Might could be put into a single function (removing same/similar code) - feel free
|
||||||
|
*/
|
||||||
|
if(d->vertical)
|
||||||
|
{
|
||||||
|
for(i = 0; i < d->data_items; i++)
|
||||||
{
|
{
|
||||||
rectangle.x = left_offset + 1;
|
/* border rectangle */
|
||||||
rectangle.y = margin_top + 1;
|
rectangle.x = pb_x + pb_offset;
|
||||||
rectangle.width = pwidth;
|
rectangle.y = pb_y;
|
||||||
rectangle.height = pb_height - 2;
|
rectangle.width = pb_width;
|
||||||
if(d->reverse[i])
|
rectangle.height = pb_height;
|
||||||
draw_rectangle(ctx, rectangle, True, d->bg[i]);
|
draw_rectangle(ctx, rectangle, False, d->bordercolor[i]);
|
||||||
else
|
|
||||||
draw_rectangle_gradient(ctx, rectangle, True, left_offset + 1, width - 2,
|
|
||||||
&(d->fg[i]), d->pfg_center[i], d->pfg_end[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(width - 2 - pwidth > 0) /* not filled area */
|
/* new value/progress in px + pattern setup */
|
||||||
|
if(!d->reverse[i])
|
||||||
|
{
|
||||||
|
pb_progress = (int)(((pb_height - 2) * d->percent[i]) / 100 + 0.5);
|
||||||
|
/* bottom to top */
|
||||||
|
pattern_rect.x = pb_x;
|
||||||
|
pattern_rect.y = pb_y + 1 + pb_height ;
|
||||||
|
pattern_rect.width = 0;
|
||||||
|
pattern_rect.height = -pb_height;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pb_progress = (int)(((pb_height - 2) * (100 - d->percent[i])) / 100 + 0.5);
|
||||||
|
/* top to bottom */
|
||||||
|
pattern_rect.x = pb_x + 1;
|
||||||
|
pattern_rect.y = pb_y + 1;
|
||||||
|
pattern_rect.width = 0;
|
||||||
|
pattern_rect.height = pb_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* bottom part */
|
||||||
|
if(pb_progress > 0)
|
||||||
|
{
|
||||||
|
rectangle.x = pb_x + pb_offset + 1;
|
||||||
|
rectangle.y = pb_y + 1 + (pb_height - 2) - pb_progress;
|
||||||
|
rectangle.width = pb_width - 2;
|
||||||
|
rectangle.height = pb_progress;
|
||||||
|
|
||||||
|
/* fg color */
|
||||||
|
if(!d->reverse[i])
|
||||||
|
draw_rectangle_gradient(ctx, rectangle, True, pattern_rect,
|
||||||
|
&(d->fg[i]), d->pfg_center[i], d->pfg_end[i]);
|
||||||
|
else /*REV: bg */
|
||||||
|
draw_rectangle(ctx, rectangle, True, d->bg[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* top part */
|
||||||
|
if((pb_height - 2) - pb_progress > 0) /* not filled area */
|
||||||
|
{
|
||||||
|
rectangle.x = pb_x + 1 + pb_offset;
|
||||||
|
rectangle.y = pb_y + 1;
|
||||||
|
rectangle.width = pb_width - 2;
|
||||||
|
rectangle.height = pb_height - 2 - pb_progress;
|
||||||
|
|
||||||
|
/* bg color */
|
||||||
|
if(!d->reverse[i])
|
||||||
|
draw_rectangle(ctx, rectangle, True, d->bg[i]);
|
||||||
|
else /* REV: bg */
|
||||||
|
draw_rectangle_gradient(ctx, rectangle, True, pattern_rect,
|
||||||
|
&(d->fg[i]), d->pfg_center[i], d->pfg_end[i]);
|
||||||
|
}
|
||||||
|
pb_offset += pb_width + d->gap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else /* a horizontal progressbar */
|
||||||
|
{
|
||||||
|
for(i = 0; i < d->data_items; i++)
|
||||||
{
|
{
|
||||||
rectangle.x = left_offset + 1 + pwidth;
|
/* border rectangle */
|
||||||
rectangle.y = margin_top + 1;
|
rectangle.x = pb_x;
|
||||||
rectangle.width = width - 2 - pwidth;
|
rectangle.y = pb_y + pb_offset;
|
||||||
rectangle.height = pb_height - 2;
|
rectangle.width = pb_width;
|
||||||
if(d->reverse[i])
|
rectangle.height = pb_height;
|
||||||
draw_rectangle_gradient(ctx, rectangle, True, left_offset + 1, width - 2,
|
draw_rectangle(ctx, rectangle, False, d->bordercolor[i]);
|
||||||
d->pfg_end[i], d->pfg_center[i], &(d->fg[i]));
|
|
||||||
else
|
|
||||||
draw_rectangle(ctx, rectangle, True, d->bg[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
margin_top += (pb_height + d->gap);
|
/* new value/progress in px + pattern setup */
|
||||||
|
if(!d->reverse[i])
|
||||||
|
{
|
||||||
|
pb_progress = (int)(((pb_width - 2) * d->percent[i]) / 100 + 0.5);
|
||||||
|
/* left to right */
|
||||||
|
pattern_rect.x = pb_x + 1;
|
||||||
|
pattern_rect.y = pb_y + 1;
|
||||||
|
pattern_rect.width = pb_width;
|
||||||
|
pattern_rect.height = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pb_progress = (int)(((pb_width - 2) * (100 - d->percent[i])) / 100 + 0.5);
|
||||||
|
/* REV: right to left */
|
||||||
|
pattern_rect.x = pb_x + 1 + pb_width;
|
||||||
|
pattern_rect.y = pb_y + 1;
|
||||||
|
pattern_rect.width = -pb_width;
|
||||||
|
pattern_rect.height = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* left part */
|
||||||
|
if(pb_progress > 0)
|
||||||
|
{
|
||||||
|
rectangle.x = pb_x + 1;
|
||||||
|
rectangle.y = pb_y + 1 + pb_offset;
|
||||||
|
rectangle.width = pb_progress;
|
||||||
|
rectangle.height = pb_height - 2;
|
||||||
|
|
||||||
|
/* fg color */
|
||||||
|
if(!d->reverse[i])
|
||||||
|
draw_rectangle_gradient(ctx, rectangle, True, pattern_rect,
|
||||||
|
&(d->fg[i]), d->pfg_center[i], d->pfg_end[i]);
|
||||||
|
else /* REV: bg */
|
||||||
|
draw_rectangle(ctx, rectangle, True, d->bg[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* right part */
|
||||||
|
if(pb_width - 2 - pb_progress > 0)
|
||||||
|
{
|
||||||
|
rectangle.x = pb_x + 1 + pb_progress;
|
||||||
|
rectangle.y = pb_y + 1 + pb_offset;
|
||||||
|
rectangle.width = pb_width - 2 - pb_progress;
|
||||||
|
rectangle.height = pb_height - 2;
|
||||||
|
|
||||||
|
/* bg color */
|
||||||
|
if(!d->reverse[i])
|
||||||
|
draw_rectangle(ctx, rectangle, True, d->bg[i]);
|
||||||
|
else /* REV: fg */
|
||||||
|
draw_rectangle_gradient(ctx, rectangle, True, pattern_rect,
|
||||||
|
&(d->fg[i]), d->pfg_center[i], d->pfg_end[i]);
|
||||||
|
}
|
||||||
|
pb_offset += pb_height + d->gap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
widget->area.width = d->width;
|
widget->area.width = d->width;
|
||||||
|
@ -319,6 +426,9 @@ progressbar_new(Statusbar *statusbar, cfg_t *config)
|
||||||
|
|
||||||
d->height = cfg_getfloat(config, "height");
|
d->height = cfg_getfloat(config, "height");
|
||||||
d->gap = cfg_getint(config, "gap");
|
d->gap = cfg_getint(config, "gap");
|
||||||
|
d->padding = cfg_getint(config, "padding");
|
||||||
|
if(!(d->vertical = cfg_getbool(config, "vertical")))
|
||||||
|
d->vertical = False;
|
||||||
|
|
||||||
w->alignment = draw_get_align(cfg_getstr(config, "align"));
|
w->alignment = draw_get_align(cfg_getstr(config, "align"));
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue