mirror of
https://gitlab.gnome.org/GNOME/gimp.git
synced 2025-07-03 09:23:24 +00:00
libgimpbase, libgimp, app: add libgimp support for passing GFiles
Which means support for GParamSpecObject with value_type == G_TYPE_FILE, and converting between GFile and its URI for wire communication directly above the protocol layer. This change requires passing a GParamSpec's value type as generic member of GPParamDef, which in turn makes some members of its sub-structures obsolete.
This commit is contained in:
parent
5d365352ca
commit
a5ac3e45fe
4 changed files with 82 additions and 117 deletions
|
@ -40,7 +40,7 @@
|
|||
#include "gimp-intl.h"
|
||||
|
||||
|
||||
#define PLUG_IN_RC_FILE_VERSION 12
|
||||
#define PLUG_IN_RC_FILE_VERSION 13
|
||||
|
||||
|
||||
/*
|
||||
|
@ -762,10 +762,11 @@ plug_in_proc_arg_deserialize (GScanner *scanner,
|
|||
goto error;
|
||||
}
|
||||
|
||||
if (! gimp_scanner_parse_string (scanner, ¶m_def.type_name) ||
|
||||
! gimp_scanner_parse_string (scanner, ¶m_def.name) ||
|
||||
! gimp_scanner_parse_string (scanner, ¶m_def.nick) ||
|
||||
! gimp_scanner_parse_string (scanner, ¶m_def.blurb) ||
|
||||
if (! gimp_scanner_parse_string (scanner, ¶m_def.type_name) ||
|
||||
! gimp_scanner_parse_string (scanner, ¶m_def.value_type_name) ||
|
||||
! gimp_scanner_parse_string (scanner, ¶m_def.name) ||
|
||||
! gimp_scanner_parse_string (scanner, ¶m_def.nick) ||
|
||||
! gimp_scanner_parse_string (scanner, ¶m_def.blurb) ||
|
||||
! gimp_scanner_parse_int (scanner, (gint *) ¶m_def.flags))
|
||||
{
|
||||
token = G_TOKEN_STRING;
|
||||
|
@ -804,12 +805,6 @@ plug_in_proc_arg_deserialize (GScanner *scanner,
|
|||
break;
|
||||
|
||||
case GP_PARAM_DEF_TYPE_ENUM:
|
||||
if (! gimp_scanner_parse_string (scanner,
|
||||
¶m_def.meta.m_enum.type_name))
|
||||
{
|
||||
token = G_TOKEN_STRING;
|
||||
goto error;
|
||||
}
|
||||
if (! gimp_scanner_parse_int (scanner,
|
||||
¶m_def.meta.m_enum.default_val))
|
||||
{
|
||||
|
@ -887,15 +882,6 @@ plug_in_proc_arg_deserialize (GScanner *scanner,
|
|||
goto error;
|
||||
}
|
||||
break;
|
||||
|
||||
case GP_PARAM_DEF_TYPE_PARAM_DEF:
|
||||
if (! gimp_scanner_parse_string (scanner,
|
||||
¶m_def.meta.m_param_def.type_name))
|
||||
{
|
||||
token = G_TOKEN_STRING;
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (! gimp_scanner_parse_token (scanner, G_TOKEN_RIGHT_PAREN))
|
||||
|
@ -916,6 +902,7 @@ plug_in_proc_arg_deserialize (GScanner *scanner,
|
|||
error:
|
||||
|
||||
g_free (param_def.type_name);
|
||||
g_free (param_def.value_type_name);
|
||||
g_free (param_def.name);
|
||||
g_free (param_def.nick);
|
||||
g_free (param_def.blurb);
|
||||
|
@ -925,10 +912,7 @@ plug_in_proc_arg_deserialize (GScanner *scanner,
|
|||
case GP_PARAM_DEF_TYPE_DEFAULT:
|
||||
case GP_PARAM_DEF_TYPE_INT:
|
||||
case GP_PARAM_DEF_TYPE_UNIT:
|
||||
break;
|
||||
|
||||
case GP_PARAM_DEF_TYPE_ENUM:
|
||||
g_free (param_def.meta.m_enum.type_name);
|
||||
break;
|
||||
|
||||
case GP_PARAM_DEF_TYPE_BOOLEAN:
|
||||
|
@ -946,10 +930,6 @@ plug_in_proc_arg_deserialize (GScanner *scanner,
|
|||
case GP_PARAM_DEF_TYPE_ID_ARRAY:
|
||||
g_free (param_def.meta.m_id_array.type_name);
|
||||
break;
|
||||
|
||||
case GP_PARAM_DEF_TYPE_PARAM_DEF:
|
||||
g_free (param_def.meta.m_param_def.type_name);
|
||||
break;
|
||||
}
|
||||
|
||||
return token;
|
||||
|
@ -1032,6 +1012,7 @@ plug_in_rc_write_proc_arg (GimpConfigWriter *writer,
|
|||
gimp_config_writer_printf (writer, "%d", param_def.param_def_type);
|
||||
|
||||
gimp_config_writer_string (writer, param_def.type_name);
|
||||
gimp_config_writer_string (writer, param_def.value_type_name);
|
||||
gimp_config_writer_string (writer, g_param_spec_get_name (pspec));
|
||||
gimp_config_writer_string (writer, g_param_spec_get_nick (pspec));
|
||||
gimp_config_writer_string (writer, g_param_spec_get_blurb (pspec));
|
||||
|
@ -1062,8 +1043,6 @@ plug_in_rc_write_proc_arg (GimpConfigWriter *writer,
|
|||
break;
|
||||
|
||||
case GP_PARAM_DEF_TYPE_ENUM:
|
||||
gimp_config_writer_string (writer,
|
||||
param_def.meta.m_enum.type_name);
|
||||
gimp_config_writer_printf (writer, "%d",
|
||||
param_def.meta.m_enum.default_val);
|
||||
break;
|
||||
|
@ -1112,11 +1091,6 @@ plug_in_rc_write_proc_arg (GimpConfigWriter *writer,
|
|||
gimp_config_writer_string (writer,
|
||||
param_def.meta.m_id_array.type_name);
|
||||
break;
|
||||
|
||||
case GP_PARAM_DEF_TYPE_PARAM_DEF:
|
||||
gimp_config_writer_string (writer,
|
||||
param_def.meta.m_param_def.type_name);
|
||||
break;
|
||||
}
|
||||
|
||||
gimp_config_writer_close (writer);
|
||||
|
|
|
@ -58,6 +58,15 @@ _gimp_gp_param_def_to_param_spec (const GPParamDef *param_def)
|
|||
|
||||
if (! strcmp (param_def->type_name, "GimpParamParasite"))
|
||||
return gimp_param_spec_parasite (name, nick, blurb, flags);
|
||||
|
||||
if (! strcmp (param_def->type_name, "GParamParam"))
|
||||
return g_param_spec_param (name, nick, blurb,
|
||||
g_type_from_name (param_def->value_type_name),
|
||||
flags);
|
||||
|
||||
if (! strcmp (param_def->type_name, "GParamObject") &&
|
||||
! strcmp (param_def->value_type_name, "GFile"))
|
||||
return g_param_spec_object (name, nick, blurb, G_TYPE_FILE, flags);
|
||||
break;
|
||||
|
||||
case GP_PARAM_DEF_TYPE_INT:
|
||||
|
@ -95,14 +104,14 @@ _gimp_gp_param_def_to_param_spec (const GPParamDef *param_def)
|
|||
case GP_PARAM_DEF_TYPE_ENUM:
|
||||
if (! strcmp (param_def->type_name, "GParamEnum"))
|
||||
return g_param_spec_enum (name, nick, blurb,
|
||||
g_type_from_name (param_def->meta.m_enum.type_name),
|
||||
g_type_from_name (param_def->value_type_name),
|
||||
param_def->meta.m_enum.default_val,
|
||||
flags);
|
||||
|
||||
if (! strcmp (param_def->type_name, "GimpParamEnum"))
|
||||
/* FIXME GimpParamEnum */
|
||||
return g_param_spec_enum (name, nick, blurb,
|
||||
g_type_from_name (param_def->meta.m_enum.type_name),
|
||||
g_type_from_name (param_def->value_type_name),
|
||||
param_def->meta.m_enum.default_val,
|
||||
flags);
|
||||
break;
|
||||
|
@ -189,12 +198,6 @@ _gimp_gp_param_def_to_param_spec (const GPParamDef *param_def)
|
|||
return gimp_param_spec_object_array (name, nick, blurb,
|
||||
g_type_from_name (param_def->meta.m_id_array.type_name),
|
||||
flags);
|
||||
|
||||
case GP_PARAM_DEF_TYPE_PARAM_DEF:
|
||||
if (! strcmp (param_def->type_name, "GParamParam"))
|
||||
return g_param_spec_param (name, nick, blurb,
|
||||
g_type_from_name (param_def->meta.m_param_def.type_name),
|
||||
flags);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -208,16 +211,16 @@ void
|
|||
_gimp_param_spec_to_gp_param_def (GParamSpec *pspec,
|
||||
GPParamDef *param_def)
|
||||
{
|
||||
GType pspec_type;
|
||||
GType pspec_type = G_PARAM_SPEC_TYPE (pspec);
|
||||
GType value_type = G_PARAM_SPEC_VALUE_TYPE (pspec);
|
||||
|
||||
param_def->param_def_type = GP_PARAM_DEF_TYPE_DEFAULT;
|
||||
param_def->type_name = (gchar *) G_PARAM_SPEC_TYPE_NAME (pspec);
|
||||
param_def->name = (gchar *) g_param_spec_get_name (pspec);
|
||||
param_def->nick = (gchar *) g_param_spec_get_nick (pspec);
|
||||
param_def->blurb = (gchar *) g_param_spec_get_blurb (pspec);
|
||||
param_def->flags = pspec->flags;
|
||||
|
||||
pspec_type = G_PARAM_SPEC_TYPE (pspec);
|
||||
param_def->param_def_type = GP_PARAM_DEF_TYPE_DEFAULT;
|
||||
param_def->type_name = (gchar *) g_type_name (pspec_type);
|
||||
param_def->value_type_name = (gchar *) g_type_name (value_type);
|
||||
param_def->name = (gchar *) g_param_spec_get_name (pspec);
|
||||
param_def->nick = (gchar *) g_param_spec_get_nick (pspec);
|
||||
param_def->blurb = (gchar *) g_param_spec_get_blurb (pspec);
|
||||
param_def->flags = pspec->flags;
|
||||
|
||||
if (pspec_type == G_TYPE_PARAM_INT)
|
||||
{
|
||||
|
@ -262,12 +265,10 @@ _gimp_param_spec_to_gp_param_def (GParamSpec *pspec,
|
|||
}
|
||||
else if (G_IS_PARAM_SPEC_ENUM (pspec))
|
||||
{
|
||||
GParamSpecEnum *espec = G_PARAM_SPEC_ENUM (pspec);
|
||||
GType enum_type = pspec->value_type;
|
||||
GParamSpecEnum *espec = G_PARAM_SPEC_ENUM (pspec);
|
||||
|
||||
param_def->param_def_type = GP_PARAM_DEF_TYPE_ENUM;
|
||||
|
||||
param_def->meta.m_enum.type_name = (gchar *) g_type_name (enum_type);
|
||||
param_def->meta.m_enum.default_val = espec->default_value;
|
||||
}
|
||||
else if (pspec_type == G_TYPE_PARAM_BOOLEAN)
|
||||
|
@ -340,16 +341,9 @@ _gimp_param_spec_to_gp_param_def (GParamSpec *pspec,
|
|||
param_def->meta.m_id_array.type_name =
|
||||
(gchar *) g_type_name (GIMP_PARAM_SPEC_OBJECT_ARRAY (pspec)->object_type);
|
||||
}
|
||||
else if (G_IS_PARAM_SPEC_PARAM (pspec))
|
||||
else if (pspec_type == G_TYPE_PARAM_OBJECT &&
|
||||
value_type != G_TYPE_FILE)
|
||||
{
|
||||
param_def->param_def_type = GP_PARAM_DEF_TYPE_PARAM_DEF;
|
||||
|
||||
param_def->meta.m_param_def.type_name =
|
||||
(gchar *) g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec));
|
||||
}
|
||||
else if (pspec_type == G_TYPE_PARAM_OBJECT)
|
||||
{
|
||||
GType value_type = G_PARAM_SPEC_VALUE_TYPE (pspec);
|
||||
const gchar *type_name = NULL;
|
||||
|
||||
if (g_type_is_a (value_type, GIMP_TYPE_DISPLAY))
|
||||
|
@ -477,6 +471,12 @@ gimp_gp_param_to_value (gpointer gimp,
|
|||
{
|
||||
g_value_set_string (value, param->data.d_string);
|
||||
}
|
||||
else if (G_VALUE_TYPE (value) == G_TYPE_FILE)
|
||||
{
|
||||
g_value_take_object (value, (param->data.d_string ?
|
||||
g_file_new_for_uri (param->data.d_string) :
|
||||
NULL));
|
||||
}
|
||||
else if (GIMP_VALUE_HOLDS_RGB (value))
|
||||
{
|
||||
gimp_value_set_rgb (value, ¶m->data.d_color);
|
||||
|
@ -730,6 +730,14 @@ gimp_value_to_gp_param (const GValue *value,
|
|||
else
|
||||
param->data.d_string = (gchar *) g_value_get_string (value);
|
||||
}
|
||||
else if (G_VALUE_TYPE (value) == G_TYPE_FILE)
|
||||
{
|
||||
GFile *file = g_value_get_object (value);
|
||||
|
||||
param->param_type = GP_PARAM_TYPE_FILE;
|
||||
|
||||
param->data.d_string = file ? g_file_get_uri (file) : NULL;
|
||||
}
|
||||
else if (GIMP_VALUE_HOLDS_RGB (value))
|
||||
{
|
||||
param->param_type = GP_PARAM_TYPE_COLOR;
|
||||
|
@ -950,6 +958,11 @@ _gimp_gp_params_free (GPParam *params,
|
|||
g_free (params[i].data.d_string);
|
||||
break;
|
||||
|
||||
case GP_PARAM_TYPE_FILE:
|
||||
/* always free the uri */
|
||||
g_free (params[i].data.d_string);
|
||||
break;
|
||||
|
||||
case GP_PARAM_TYPE_COLOR:
|
||||
break;
|
||||
|
||||
|
|
|
@ -1028,6 +1028,11 @@ _gp_param_def_read (GIOChannel *channel,
|
|||
user_data))
|
||||
return FALSE;
|
||||
|
||||
if (! _gimp_wire_read_string (channel,
|
||||
¶m_def->value_type_name, 1,
|
||||
user_data))
|
||||
return FALSE;
|
||||
|
||||
if (! _gimp_wire_read_string (channel,
|
||||
¶m_def->name, 1,
|
||||
user_data))
|
||||
|
@ -1080,10 +1085,7 @@ _gp_param_def_read (GIOChannel *channel,
|
|||
break;
|
||||
|
||||
case GP_PARAM_DEF_TYPE_ENUM:
|
||||
if (! _gimp_wire_read_string (channel,
|
||||
¶m_def->meta.m_enum.type_name, 1,
|
||||
user_data) ||
|
||||
! _gimp_wire_read_int32 (channel,
|
||||
if (! _gimp_wire_read_int32 (channel,
|
||||
(guint32 *) ¶m_def->meta.m_enum.default_val, 1,
|
||||
user_data))
|
||||
return FALSE;
|
||||
|
@ -1139,13 +1141,6 @@ _gp_param_def_read (GIOChannel *channel,
|
|||
user_data))
|
||||
return FALSE;
|
||||
break;
|
||||
|
||||
case GP_PARAM_DEF_TYPE_PARAM_DEF:
|
||||
if (! _gimp_wire_read_string (channel,
|
||||
¶m_def->meta.m_param_def.type_name, 1,
|
||||
user_data))
|
||||
return FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
@ -1155,6 +1150,7 @@ static void
|
|||
_gp_param_def_destroy (GPParamDef *param_def)
|
||||
{
|
||||
g_free (param_def->type_name);
|
||||
g_free (param_def->value_type_name);
|
||||
g_free (param_def->name);
|
||||
g_free (param_def->nick);
|
||||
g_free (param_def->blurb);
|
||||
|
@ -1164,10 +1160,7 @@ _gp_param_def_destroy (GPParamDef *param_def)
|
|||
case GP_PARAM_DEF_TYPE_DEFAULT:
|
||||
case GP_PARAM_DEF_TYPE_INT:
|
||||
case GP_PARAM_DEF_TYPE_UNIT:
|
||||
break;
|
||||
|
||||
case GP_PARAM_DEF_TYPE_ENUM:
|
||||
g_free (param_def->meta.m_enum.type_name);
|
||||
break;
|
||||
|
||||
case GP_PARAM_DEF_TYPE_BOOLEAN:
|
||||
|
@ -1185,10 +1178,6 @@ _gp_param_def_destroy (GPParamDef *param_def)
|
|||
case GP_PARAM_DEF_TYPE_ID_ARRAY:
|
||||
g_free (param_def->meta.m_id_array.type_name);
|
||||
break;
|
||||
|
||||
case GP_PARAM_DEF_TYPE_PARAM_DEF:
|
||||
g_free (param_def->meta.m_param_def.type_name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1281,6 +1270,11 @@ _gp_param_def_write (GIOChannel *channel,
|
|||
user_data))
|
||||
return FALSE;
|
||||
|
||||
if (! _gimp_wire_write_string (channel,
|
||||
¶m_def->value_type_name, 1,
|
||||
user_data))
|
||||
return FALSE;
|
||||
|
||||
if (! _gimp_wire_write_string (channel,
|
||||
¶m_def->name, 1,
|
||||
user_data))
|
||||
|
@ -1333,10 +1327,7 @@ _gp_param_def_write (GIOChannel *channel,
|
|||
break;
|
||||
|
||||
case GP_PARAM_DEF_TYPE_ENUM:
|
||||
if (! _gimp_wire_write_string (channel,
|
||||
¶m_def->meta.m_enum.type_name, 1,
|
||||
user_data) ||
|
||||
! _gimp_wire_write_int32 (channel,
|
||||
if (! _gimp_wire_write_int32 (channel,
|
||||
(guint32 *) ¶m_def->meta.m_enum.default_val, 1,
|
||||
user_data))
|
||||
return FALSE;
|
||||
|
@ -1392,13 +1383,6 @@ _gp_param_def_write (GIOChannel *channel,
|
|||
user_data))
|
||||
return FALSE;
|
||||
break;
|
||||
|
||||
case GP_PARAM_DEF_TYPE_PARAM_DEF:
|
||||
if (! _gimp_wire_write_string (channel,
|
||||
¶m_def->meta.m_param_def.type_name, 1,
|
||||
user_data))
|
||||
return FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
@ -1568,10 +1552,8 @@ _gp_params_read (GIOChannel *channel,
|
|||
{
|
||||
if (! _gimp_wire_read_int32 (channel,
|
||||
(guint32 *) &(*params)[i].param_type, 1,
|
||||
user_data))
|
||||
goto cleanup;
|
||||
|
||||
if (! _gimp_wire_read_string (channel,
|
||||
user_data) ||
|
||||
! _gimp_wire_read_string (channel,
|
||||
&(*params)[i].type_name, 1,
|
||||
user_data))
|
||||
return;
|
||||
|
@ -1593,6 +1575,7 @@ _gp_params_read (GIOChannel *channel,
|
|||
break;
|
||||
|
||||
case GP_PARAM_TYPE_STRING:
|
||||
case GP_PARAM_TYPE_FILE:
|
||||
if (! _gimp_wire_read_string (channel,
|
||||
&(*params)[i].data.d_string, 1,
|
||||
user_data))
|
||||
|
@ -1769,6 +1752,7 @@ _gp_params_write (GIOChannel *channel,
|
|||
break;
|
||||
|
||||
case GP_PARAM_TYPE_STRING:
|
||||
case GP_PARAM_TYPE_FILE:
|
||||
if (! _gimp_wire_write_string (channel,
|
||||
¶ms[i].data.d_string, 1,
|
||||
user_data))
|
||||
|
@ -1871,6 +1855,7 @@ _gp_params_destroy (GPParam *params,
|
|||
break;
|
||||
|
||||
case GP_PARAM_TYPE_STRING:
|
||||
case GP_PARAM_TYPE_FILE:
|
||||
g_free (params[i].data.d_string);
|
||||
break;
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ G_BEGIN_DECLS
|
|||
|
||||
/* Increment every time the protocol changes
|
||||
*/
|
||||
#define GIMP_PROTOCOL_VERSION 0x010B
|
||||
#define GIMP_PROTOCOL_VERSION 0x010C
|
||||
|
||||
|
||||
enum
|
||||
|
@ -57,8 +57,7 @@ typedef enum
|
|||
GP_PARAM_DEF_TYPE_STRING,
|
||||
GP_PARAM_DEF_TYPE_COLOR,
|
||||
GP_PARAM_DEF_TYPE_ID,
|
||||
GP_PARAM_DEF_TYPE_ID_ARRAY,
|
||||
GP_PARAM_DEF_TYPE_PARAM_DEF
|
||||
GP_PARAM_DEF_TYPE_ID_ARRAY
|
||||
} GPParamDefType;
|
||||
|
||||
typedef enum
|
||||
|
@ -66,6 +65,7 @@ typedef enum
|
|||
GP_PARAM_TYPE_INT,
|
||||
GP_PARAM_TYPE_FLOAT,
|
||||
GP_PARAM_TYPE_STRING,
|
||||
GP_PARAM_TYPE_FILE,
|
||||
GP_PARAM_TYPE_COLOR,
|
||||
GP_PARAM_TYPE_PARASITE,
|
||||
GP_PARAM_TYPE_ARRAY,
|
||||
|
@ -89,7 +89,6 @@ typedef struct _GPParamDefString GPParamDefString;
|
|||
typedef struct _GPParamDefColor GPParamDefColor;
|
||||
typedef struct _GPParamDefID GPParamDefID;
|
||||
typedef struct _GPParamDefIDArray GPParamDefIDArray;
|
||||
typedef struct _GPParamDefParamDef GPParamDefParamDef;
|
||||
typedef struct _GPParam GPParam;
|
||||
typedef struct _GPParamArray GPParamArray;
|
||||
typedef struct _GPParamStringArray GPParamStringArray;
|
||||
|
@ -161,8 +160,7 @@ struct _GPParamDefUnit
|
|||
|
||||
struct _GPParamDefEnum
|
||||
{
|
||||
gchar *type_name;
|
||||
gint32 default_val;
|
||||
gint32 default_val;
|
||||
};
|
||||
|
||||
struct _GPParamDefBoolean
|
||||
|
@ -198,15 +196,11 @@ struct _GPParamDefIDArray
|
|||
gchar *type_name;
|
||||
};
|
||||
|
||||
struct _GPParamDefParamDef
|
||||
{
|
||||
gchar *type_name;
|
||||
};
|
||||
|
||||
struct _GPParamDef
|
||||
{
|
||||
GPParamDefType param_def_type;
|
||||
gchar *type_name;
|
||||
gchar *value_type_name;
|
||||
gchar *name;
|
||||
gchar *nick;
|
||||
gchar *blurb;
|
||||
|
@ -214,16 +208,15 @@ struct _GPParamDef
|
|||
|
||||
union
|
||||
{
|
||||
GPParamDefInt m_int;
|
||||
GPParamDefUnit m_unit;
|
||||
GPParamDefEnum m_enum;
|
||||
GPParamDefBoolean m_boolean;
|
||||
GPParamDefFloat m_float;
|
||||
GPParamDefString m_string;
|
||||
GPParamDefColor m_color;
|
||||
GPParamDefID m_id;
|
||||
GPParamDefIDArray m_id_array;
|
||||
GPParamDefParamDef m_param_def;
|
||||
GPParamDefInt m_int;
|
||||
GPParamDefUnit m_unit;
|
||||
GPParamDefEnum m_enum;
|
||||
GPParamDefBoolean m_boolean;
|
||||
GPParamDefFloat m_float;
|
||||
GPParamDefString m_string;
|
||||
GPParamDefColor m_color;
|
||||
GPParamDefID m_id;
|
||||
GPParamDefIDArray m_id_array;
|
||||
} meta;
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue