mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-01-13 15:56:23 +01:00
docs: Replace Maman in the tutorial with a more meaningful example
Change it to a running example of a file viewer application with a file class and various derived classes and related interfaces. Hopefully the reader can relate to this a little better than to their maman. https://bugzilla.gnome.org/show_bug.cgi?id=753935
This commit is contained in:
parent
419f57137a
commit
bf33f1d98d
@ -55,68 +55,68 @@
|
||||
<para>
|
||||
Objects which inherit from GObject are allowed to override this
|
||||
constructed class method.
|
||||
The example below shows how <type>MamanBar</type> overrides the parent's construction process:
|
||||
The example below shows how <type>ViewerFile</type> overrides the parent's construction process:
|
||||
<informalexample><programlisting>
|
||||
#define MAMAN_TYPE_BAR maman_bar_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (MamanBar, maman_bar, MAMAN, BAR, GObject)
|
||||
#define VIEWER_TYPE_FILE viewer_file_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (ViewerFile, viewer_file, VIEWER, FILE, GObject)
|
||||
|
||||
struct _MamanBar
|
||||
struct _ViewerFile
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
/* instance members */
|
||||
};
|
||||
|
||||
/* will create maman_bar_get_type and set maman_bar_parent_class */
|
||||
G_DEFINE_TYPE (MamanBar, maman_bar, G_TYPE_OBJECT);
|
||||
/* will create viewer_file_get_type and set viewer_file_parent_class */
|
||||
G_DEFINE_TYPE (ViewerFile, viewer_file, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
maman_bar_constructed (GObject *obj)
|
||||
viewer_file_constructed (GObject *obj)
|
||||
{
|
||||
/* update the object state depending on constructor properties */
|
||||
|
||||
/* Always chain up to the parent constructed function to complete object
|
||||
* initialisation. */
|
||||
G_OBJECT_CLASS (maman_bar_parent_class)->constructed (obj);
|
||||
G_OBJECT_CLASS (viewer_file_parent_class)->constructed (obj);
|
||||
}
|
||||
|
||||
static void
|
||||
maman_bar_class_init (MamanBarClass *klass)
|
||||
viewer_file_class_init (ViewerFileClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->constructed = maman_bar_constructed;
|
||||
object_class->constructed = viewer_file_constructed;
|
||||
}
|
||||
|
||||
static void
|
||||
maman_bar_init (MamanBar *self)
|
||||
viewer_file_init (ViewerFile *self)
|
||||
{
|
||||
/* initialize the object */
|
||||
}
|
||||
|
||||
</programlisting></informalexample>
|
||||
If the user instantiates an object <type>MamanBar</type> with:
|
||||
If the user instantiates an object <type>ViewerFile</type> with:
|
||||
<informalexample><programlisting>
|
||||
MamanBar *bar = g_object_new (MAMAN_TYPE_BAR, NULL);
|
||||
ViewerFile *file = g_object_new (VIEWER_TYPE_FILE, NULL);
|
||||
</programlisting></informalexample>
|
||||
If this is the first instantiation of such an object, the
|
||||
<function>maman_bar_class_init</function> function will be invoked
|
||||
after any <function>maman_bar_base_class_init</function> function.
|
||||
<function>viewer_file_class_init</function> function will be invoked
|
||||
after any <function>viewer_file_base_class_init</function> function.
|
||||
This will make sure the class structure of this new object is
|
||||
correctly initialized. Here, <function>maman_bar_class_init</function>
|
||||
correctly initialized. Here, <function>viewer_file_class_init</function>
|
||||
is expected to override the object's class methods and setup the
|
||||
class' own methods. In the example above, the constructor method is
|
||||
the only overridden method: it is set to
|
||||
<function>maman_bar_constructor</function>.
|
||||
<function>viewer_file_constructor</function>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Once <function><link linkend="g-object-new">g_object_new</link></function> has obtained a reference to an initialized
|
||||
class structure, it invokes its constructor method to create an instance of the new
|
||||
object, if the constructor has been overridden in <function>maman_bar_class_init</function>.
|
||||
object, if the constructor has been overridden in <function>viewer_file_class_init</function>.
|
||||
Overridden constructors must chain up to their parent’s constructor. In
|
||||
order to find the parent class and chain up to the parent class
|
||||
constructor, we can use the <literal>maman_bar_parent_class</literal>
|
||||
constructor, we can use the <literal>viewer_file_parent_class</literal>
|
||||
pointer that has been set up for us by the
|
||||
<link linkend="G-DEFINE-TYPE:CAPS"><literal>G_DEFINE_TYPE</literal></link>
|
||||
macro.
|
||||
@ -467,32 +467,32 @@ MamanBar *bar = g_object_new (MAMAN_TYPE_BAR, NULL);
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_MAMAN_NAME = 1,
|
||||
PROP_PAPA_NUMBER,
|
||||
PROP_FILENAME = 1,
|
||||
PROP_ZOOM_LEVEL,
|
||||
N_PROPERTIES
|
||||
};
|
||||
|
||||
static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
|
||||
|
||||
static void
|
||||
maman_bar_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
viewer_file_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
MamanBar *self = MAMAN_BAR (object);
|
||||
ViewerFile *self = VIEWER_FILE (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_MAMAN_NAME:
|
||||
g_free (self->priv->name);
|
||||
self->priv->name = g_value_dup_string (value);
|
||||
g_print ("maman: %s\n", self->priv->name);
|
||||
case PROP_FILENAME:
|
||||
g_free (self->priv->filename);
|
||||
self->priv->filename = g_value_dup_string (value);
|
||||
g_print ("filename: %s\n", self->priv->filename);
|
||||
break;
|
||||
|
||||
case PROP_PAPA_NUMBER:
|
||||
self->priv->papa_number = g_value_get_uchar (value);
|
||||
g_print ("papa: %u\n", self->priv->papa_number);
|
||||
case PROP_ZOOM_LEVEL:
|
||||
self->priv->zoom_level = g_value_get_uint (value);
|
||||
g_print ("zoom level: %u\n", self->priv->zoom_level);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -503,21 +503,21 @@ maman_bar_set_property (GObject *object,
|
||||
}
|
||||
|
||||
static void
|
||||
maman_bar_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
viewer_file_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
MamanBar *self = MAMAN_BAR (object);
|
||||
ViewerFile *self = VIEWER_FILE (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_MAMAN_NAME:
|
||||
g_value_set_string (value, self->priv->name);
|
||||
case PROP_FILENAME:
|
||||
g_value_set_string (value, self->priv->filename);
|
||||
break;
|
||||
|
||||
case PROP_PAPA_NUMBER:
|
||||
g_value_set_uchar (value, self->priv->papa_number);
|
||||
case PROP_ZOOM_LEVEL:
|
||||
g_value_set_uint (value, self->priv->zoom_level);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -528,28 +528,28 @@ maman_bar_get_property (GObject *object,
|
||||
}
|
||||
|
||||
static void
|
||||
maman_bar_class_init (MamanBarClass *klass)
|
||||
viewer_file_class_init (ViewerFileClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->set_property = maman_bar_set_property;
|
||||
object_class->get_property = maman_bar_get_property;
|
||||
object_class->set_property = viewer_file_set_property;
|
||||
object_class->get_property = viewer_file_get_property;
|
||||
|
||||
obj_properties[PROP_MAMAN_NAME] =
|
||||
g_param_spec_string ("maman-name",
|
||||
"Maman construct prop",
|
||||
"Set maman's name",
|
||||
"no-name-set" /* default value */,
|
||||
obj_properties[PROP_FILENAME] =
|
||||
g_param_spec_string ("filename",
|
||||
"Filename",
|
||||
"Name of the file to load and display from.",
|
||||
NULL /* default value */,
|
||||
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
|
||||
|
||||
obj_properties[PROP_PAPA_NUMBER] =
|
||||
g_param_spec_uchar ("papa-number",
|
||||
"Number of current Papa",
|
||||
"Set/Get papa's number",
|
||||
0 /* minimum value */,
|
||||
10 /* maximum value */,
|
||||
2 /* default value */,
|
||||
G_PARAM_READWRITE));
|
||||
obj_properties[PROP_ZOOM_LEVEL] =
|
||||
g_param_spec_uint ("zoom-level",
|
||||
"Zoom level",
|
||||
"Zoom level to view the file at.",
|
||||
0 /* minimum value */,
|
||||
10 /* maximum value */,
|
||||
2 /* default value */,
|
||||
G_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_properties (object_class,
|
||||
N_PROPERTIES,
|
||||
@ -560,15 +560,15 @@ maman_bar_class_init (MamanBarClass *klass)
|
||||
/* Use */
|
||||
/************************************************/
|
||||
|
||||
GObject *bar;
|
||||
ViewerFile *file;
|
||||
GValue val = G_VALUE_INIT;
|
||||
|
||||
bar = g_object_new (MAMAN_TYPE_BAR, NULL);
|
||||
file = g_object_new (VIEWER_TYPE_FILE, NULL);
|
||||
|
||||
g_value_init (&val, G_TYPE_CHAR);
|
||||
g_value_init (&val, G_TYPE_UINT);
|
||||
g_value_set_char (&val, 11);
|
||||
|
||||
g_object_set_property (G_OBJECT (bar), "papa-number", &val);
|
||||
g_object_set_property (G_OBJECT (file), "zoom-level", &val);
|
||||
|
||||
g_value_unset (&val);
|
||||
</programlisting></informalexample>
|
||||
@ -577,7 +577,7 @@ g_value_unset (&val);
|
||||
|
||||
<para>
|
||||
<function><link linkend="g-object-set-property">g_object_set_property</link></function> first ensures a property
|
||||
with this name was registered in bar's <function>class_init</function> handler. If so it walks the class hierarchy,
|
||||
with this name was registered in <emphasis>file</emphasis>'s <function>class_init</function> handler. If so it walks the class hierarchy,
|
||||
from bottom-most most-derived type, to top-most fundamental type to find the class
|
||||
which registered that property. It then tries to convert the user-provided
|
||||
<link linkend="GValue"><type>GValue</type></link>
|
||||
@ -615,13 +615,13 @@ g_value_unset (&val);
|
||||
If the user's GValue had been set to a valid value, <function><link linkend="g-object-set-property">g_object_set_property</link></function>
|
||||
would have proceeded with calling the object's
|
||||
<function>set_property</function> class method. Here, since our
|
||||
implementation of <type>Foo</type> did override this method, execution would jump to
|
||||
<function>foo_set_property</function> after having retrieved from the
|
||||
implementation of <type>ViewerFile</type> did override this method, execution would jump to
|
||||
<function>viewer_file_set_property</function> after having retrieved from the
|
||||
<link linkend="GParamSpec"><type>GParamSpec</type></link> the <emphasis>param_id</emphasis>
|
||||
<footnote>
|
||||
<para>
|
||||
It should be noted that the param_id used here need only to uniquely identify each
|
||||
<link linkend="GParamSpec"><type>GParamSpec</type></link> within the <type>FooClass</type> such that the switch
|
||||
<link linkend="GParamSpec"><type>GParamSpec</type></link> within the <type>ViewerFileClass</type> such that the switch
|
||||
used in the set and get methods actually works. Of course, this locally-unique
|
||||
integer is purely an optimization: it would have been possible to use a set of
|
||||
<emphasis>if (strcmp (a, b) == 0) {} else if (strcmp (a, b) == 0) {}</emphasis> statements.
|
||||
@ -667,11 +667,11 @@ g_value_unset (&val);
|
||||
<function><link linkend="g-object-set-valist">g_object_set_valist</link></function> (variadic version) functions can be used to set
|
||||
multiple properties at once. The client code shown above can then be re-written as:
|
||||
<informalexample><programlisting>
|
||||
MamanBar *foo;
|
||||
foo = /* */;
|
||||
g_object_set (G_OBJECT (foo),
|
||||
"papa-number", 2,
|
||||
"maman-name", "test",
|
||||
ViewerFile *file;
|
||||
file = /* */;
|
||||
g_object_set (G_OBJECT (file),
|
||||
"zoom-level", 6,
|
||||
"filename", "~/some-file.txt",
|
||||
NULL);
|
||||
</programlisting></informalexample>
|
||||
This saves us from managing the GValues that we were needing to handle when using
|
||||
|
@ -154,9 +154,9 @@ static void test_object (void)
|
||||
GObject *obj;
|
||||
GValue obj_vala = G_VALUE_INIT;
|
||||
GValue obj_valb = G_VALUE_INIT;
|
||||
obj = g_object_new (MAMAN_TYPE_BAR, NULL);
|
||||
obj = g_object_new (VIEWER_TYPE_FILE, NULL);
|
||||
|
||||
g_value_init (&obj_vala, MAMAN_TYPE_BAR);
|
||||
g_value_init (&obj_vala, VIEWER_TYPE_FILE);
|
||||
g_value_set_object (&obj_vala, obj);
|
||||
|
||||
g_value_init (&obj_valb, G_TYPE_OBJECT);
|
||||
@ -164,7 +164,7 @@ static void test_object (void)
|
||||
/* g_value_copy's semantics for G_TYPE_OBJECT types is to copy the reference.
|
||||
* This function thus calls g_object_ref.
|
||||
* It is interesting to note that the assignment works here because
|
||||
* MAMAN_TYPE_BAR is a G_TYPE_OBJECT.
|
||||
* VIEWER_TYPE_FILE is a G_TYPE_OBJECT.
|
||||
*/
|
||||
g_value_copy (&obj_vala, &obj_valb);
|
||||
|
||||
@ -206,27 +206,20 @@ static void test_object (void)
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
Use the <function>object_method</function> pattern for function names: to invoke
|
||||
the method named foo on an instance of object type bar, call
|
||||
<function>bar_foo</function>.
|
||||
the method named <function>save</function> on an instance of object type <type>file</type>, call
|
||||
<function>file_save</function>.
|
||||
</para></listitem>
|
||||
<listitem><para>Use prefixing to avoid namespace conflicts with other projects.
|
||||
If your library (or application) is named <emphasis>Maman</emphasis>,
|
||||
<footnote>
|
||||
<para>
|
||||
<emphasis>Maman</emphasis> is the French word for <emphasis>mum</emphasis>
|
||||
or <emphasis>mother</emphasis> — nothing more and nothing less.
|
||||
</para>
|
||||
</footnote>
|
||||
|
||||
prefix all your function names with <emphasis>maman_</emphasis>.
|
||||
For example: <function>maman_object_method</function>.
|
||||
If your library (or application) is named <emphasis>Viewer</emphasis>,
|
||||
prefix all your function names with <emphasis>viewer_</emphasis>.
|
||||
For example: <function>viewer_object_method</function>.
|
||||
</para></listitem>
|
||||
<listitem><para>Create a macro named <function>PREFIX_TYPE_OBJECT</function> which always
|
||||
returns the GType for the associated object type. For an object of type
|
||||
<emphasis>Bar</emphasis> in a library prefixed by <emphasis>maman</emphasis>,
|
||||
use: <function>MAMAN_TYPE_BAR</function>.
|
||||
<emphasis>File</emphasis> in the <emphasis>Viewer</emphasis> namespace,
|
||||
use: <function>VIEWER_TYPE_FILE</function>.
|
||||
This macro is implemented using a function named
|
||||
<function>prefix_object_get_type</function>.
|
||||
<function>prefix_object_get_type</function>; for example, <function>viewer_file_get_type</function>.
|
||||
</para></listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
@ -241,25 +234,28 @@ static void test_object (void)
|
||||
dynamic type safety by doing runtime checks. It is possible to disable the dynamic
|
||||
type checks in production builds (see <link linkend="glib-building">building GLib</link>).
|
||||
For example, we would create
|
||||
<function>MAMAN_BAR (obj)</function> to keep the previous example.
|
||||
<function>VIEWER_FILE (obj)</function> to keep the previous example.
|
||||
</para></listitem>
|
||||
<listitem><para><function>PREFIX_OBJECT_CLASS (klass)</function>, which
|
||||
is strictly equivalent to the previous casting macro: it does static casting with
|
||||
dynamic type checking of class structures. It is expected to return a pointer
|
||||
to a class structure of type <type>PrefixObjectClass</type>. An example is:
|
||||
<function>MAMAN_BAR_CLASS</function>.
|
||||
<function>VIEWER_FILE_CLASS</function>.
|
||||
</para></listitem>
|
||||
<listitem><para><function>PREFIX_IS_BAR (obj)</function>, which
|
||||
<listitem><para><function>PREFIX_IS_OBJECT (obj)</function>, which
|
||||
returns a <type>gboolean</type> which indicates whether the input
|
||||
object instance pointer is non-<type>NULL</type> and of type BAR.
|
||||
object instance pointer is non-<type>NULL</type> and of type <type>OBJECT</type>.
|
||||
For example, <function>VIEWER_IS_FILE</function>.
|
||||
</para></listitem>
|
||||
<listitem><para><function>PREFIX_IS_OBJECT_CLASS (klass)</function>, which returns a boolean
|
||||
if the input class pointer is a pointer to a class of type OBJECT.
|
||||
For example, <function>VIEWER_IS_FILE_CLASS</function>.
|
||||
</para></listitem>
|
||||
<listitem><para><function>PREFIX_OBJECT_GET_CLASS (obj)</function>,
|
||||
which returns the class pointer associated to an instance of a given type. This macro
|
||||
is used for static and dynamic type safety purposes (just like the previous casting
|
||||
macros).
|
||||
For example, <function>VIEWER_FILE_GET_CLASS</function>.
|
||||
</para></listitem>
|
||||
</itemizedlist>
|
||||
</listitem>
|
||||
@ -268,8 +264,8 @@ static void test_object (void)
|
||||
macros are provided in <filename>gtype.h</filename>. For the example we used above, we would
|
||||
write the following trivial code to declare the macros:
|
||||
<informalexample><programlisting>
|
||||
#define MAMAN_TYPE_BAR maman_bar_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (MamanBar, maman_bar, MAMAN, BAR, GObject)
|
||||
#define VIEWER_TYPE_FILE viewer_file_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (ViewerFile, viewer_file, VIEWER, FILE, GObject)
|
||||
</programlisting></informalexample>
|
||||
</para>
|
||||
|
||||
@ -278,15 +274,15 @@ G_DECLARE_FINAL_TYPE (MamanBar, maman_bar, MAMAN, BAR, GObject)
|
||||
<function><link linkend="G-DEFINE-TYPE:CAPS">G_DEFINE_TYPE</link></function>
|
||||
macro to define a class:
|
||||
<informalexample><programlisting>
|
||||
G_DEFINE_TYPE (MamanBar, maman_bar, G_TYPE_OBJECT)
|
||||
G_DEFINE_TYPE (ViewerFile, viewer_file, G_TYPE_OBJECT)
|
||||
</programlisting></informalexample>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Otherwise, the <function>maman_bar_get_type</function> function must be
|
||||
Otherwise, the <function>viewer_file_get_type</function> function must be
|
||||
implemented manually:
|
||||
<informalexample><programlisting>
|
||||
GType maman_bar_get_type (void)
|
||||
GType viewer_file_get_type (void)
|
||||
{
|
||||
static GType type = 0;
|
||||
if (type == 0) {
|
||||
@ -294,7 +290,7 @@ GType maman_bar_get_type (void)
|
||||
/* You fill this structure. */
|
||||
};
|
||||
type = g_type_register_static (G_TYPE_OBJECT,
|
||||
"MamanBarType",
|
||||
"ViewerFile",
|
||||
&info, 0);
|
||||
}
|
||||
return type;
|
||||
@ -384,45 +380,51 @@ GType maman_bar_get_type (void)
|
||||
<informalexample><programlisting>
|
||||
typedef struct {
|
||||
GObject parent;
|
||||
|
||||
/* instance members */
|
||||
int field_a;
|
||||
} MamanBar;
|
||||
gchar *filename;
|
||||
} ViewerFile;
|
||||
|
||||
typedef struct {
|
||||
GObjectClass parent;
|
||||
|
||||
/* class members */
|
||||
void (*do_action_public_virtual) (MamanBar *self, guint8 i);
|
||||
/* the first is public, pure and virtual */
|
||||
void (*open) (ViewerFile *self,
|
||||
GError **error);
|
||||
|
||||
void (*do_action_public_pure_virtual) (MamanBar *self, guint8 i);
|
||||
} MamanBarClass;
|
||||
/* the second is public and virtual */
|
||||
void (*close) (ViewerFile *self,
|
||||
GError **error);
|
||||
} ViewerFileClass;
|
||||
|
||||
#define MAMAN_TYPE_BAR (maman_bar_get_type ())
|
||||
#define VIEWER_TYPE_FILE (viewer_file_get_type ())
|
||||
|
||||
GType
|
||||
maman_bar_get_type (void)
|
||||
viewer_file_get_type (void)
|
||||
{
|
||||
static GType type = 0;
|
||||
if (type == 0) {
|
||||
const GTypeInfo info = {
|
||||
sizeof (MamanBarClass),
|
||||
sizeof (ViewerFileClass),
|
||||
NULL, /* base_init */
|
||||
NULL, /* base_finalize */
|
||||
(GClassInitFunc) foo_class_init,
|
||||
(GClassInitFunc) viewer_file_class_init,
|
||||
NULL, /* class_finalize */
|
||||
NULL, /* class_data */
|
||||
sizeof (MamanBar),
|
||||
sizeof (ViewerFile),
|
||||
0, /* n_preallocs */
|
||||
(GInstanceInitFunc) NULL /* instance_init */
|
||||
};
|
||||
type = g_type_register_static (G_TYPE_OBJECT,
|
||||
"BarType",
|
||||
"ViewerFile",
|
||||
&info, 0);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
</programlisting></informalexample>
|
||||
Upon the first call to <function>maman_bar_get_type</function>, the type named
|
||||
<emphasis>BarType</emphasis> will be registered in the type system as inheriting
|
||||
Upon the first call to <function>viewer_file_get_type</function>, the type named
|
||||
<emphasis>ViewerFile</emphasis> will be registered in the type system as inheriting
|
||||
from the type <emphasis>G_TYPE_OBJECT</emphasis>.
|
||||
</para>
|
||||
|
||||
@ -650,28 +652,37 @@ B *b;
|
||||
classed type which derives from
|
||||
<link linkend="GTypeInterface"><type>GTypeInterface</type></link>. The following piece of code declares such an interface.
|
||||
<informalexample><programlisting>
|
||||
#define MAMAN_TYPE_IBAZ maman_ibaz_get_type ()
|
||||
G_DECLARE_INTERFACE(MamanIbaz, maman_ibaz, MAMAN, IBAZ, GObject)
|
||||
#define VIEWER_TYPE_EDITABLE viewer_editable_get_type ()
|
||||
G_DECLARE_INTERFACE (ViewerEditable, viewer_editable, VIEWER, EDITABLE, GObject)
|
||||
|
||||
struct _MamanIbazInterface {
|
||||
struct _ViewerEditableInterface {
|
||||
GTypeInterface parent;
|
||||
|
||||
void (*do_action) (MamanIbaz *self);
|
||||
void (*save) (ViewerEditable *self,
|
||||
GError **error);
|
||||
};
|
||||
|
||||
void maman_ibaz_do_action (MamanIbaz *self);
|
||||
void viewer_editable_save (ViewerEditable *self,
|
||||
GError **error);
|
||||
</programlisting></informalexample>
|
||||
The interface function, <function>maman_ibaz_do_action</function> is implemented
|
||||
The interface function, <function>viewer_editable_save</function> is implemented
|
||||
in a pretty simple way:
|
||||
<informalexample><programlisting>
|
||||
void maman_ibaz_do_action (MamanIbaz *self)
|
||||
void
|
||||
viewer_editable_save (ViewerEditable *self,
|
||||
GError **error)
|
||||
{
|
||||
g_return_if_fail (MAMAN_IS_IBAZ (self));
|
||||
ViewerEditableinterface *iface;
|
||||
|
||||
MAMAN_IBAZ_GET_INTERFACE (self)->do_action (self);
|
||||
g_return_if_fail (VIEWER_IS_EDITABLE (self));
|
||||
g_return_if_fail (error == NULL || *error == NULL);
|
||||
|
||||
iface = VIEWER_EDITABLE_GET_INTERFACE (self);
|
||||
g_return_if_fail (iface->save != NULL);
|
||||
iface->save (self);
|
||||
}
|
||||
</programlisting></informalexample>
|
||||
<function>maman_ibaz_get_type</function> registers a type named <emphasis>MamanIbaz</emphasis>
|
||||
<function>viewer_editable_get_type</function> registers a type named <emphasis>ViewerEditable</emphasis>
|
||||
which inherits from <type>G_TYPE_INTERFACE</type>. All interfaces must
|
||||
be children of <type>G_TYPE_INTERFACE</type> in the inheritance tree.
|
||||
</para>
|
||||
@ -681,7 +692,7 @@ void maman_ibaz_do_action (MamanIbaz *self)
|
||||
a <link linkend="GTypeInterface"><type>GTypeInterface</type></link> structure. The interface structure is expected to
|
||||
contain the function pointers of the interface methods. It is good style to
|
||||
define helper functions for each of the interface methods which simply call
|
||||
the interface's method directly: <function>maman_ibaz_do_action</function>
|
||||
the interface's method directly: <function>viewer_editable_save</function>
|
||||
is one of these.
|
||||
</para>
|
||||
|
||||
@ -691,20 +702,20 @@ void maman_ibaz_do_action (MamanIbaz *self)
|
||||
to implement an interface:
|
||||
<informalexample><programlisting>
|
||||
static void
|
||||
maman_baz_do_action (MamanIbaz *self)
|
||||
viewer_file_save (ViewerEditable *self)
|
||||
{
|
||||
g_print ("Baz implementation of Ibaz interface Action.\n");
|
||||
g_print ("File implementation of editable interface save method.\n");
|
||||
}
|
||||
|
||||
static void
|
||||
maman_ibaz_interface_init (MamanIbazInterface *iface)
|
||||
viewer_file_editable_interface_init (ViewerEditableInterface *iface)
|
||||
{
|
||||
iface->do_action = maman_baz_do_action;
|
||||
iface->save = viewer_file_save;
|
||||
}
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (MamanBaz, maman_baz, G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (MAMAN_TYPE_IBAZ,
|
||||
maman_ibaz_interface_init));
|
||||
G_DEFINE_TYPE_WITH_CODE (ViewerFile, viewer_file, VIEWER_TYPE_FILE,
|
||||
G_IMPLEMENT_INTERFACE (VIEWER_TYPE_EDITABLE,
|
||||
viewer_file_editable_interface_init));
|
||||
</programlisting></informalexample>
|
||||
</para>
|
||||
|
||||
@ -712,51 +723,52 @@ G_DEFINE_TYPE_WITH_CODE (MamanBaz, maman_baz, G_TYPE_OBJECT,
|
||||
If your code does have special requirements, you must write a custom
|
||||
<function>get_type</function> function to register your GType which
|
||||
inherits from some <link linkend="GObject"><type>GObject</type></link>
|
||||
and which implements the interface <type>MamanIbaz</type>. For
|
||||
example, this code registers a new <type>MamanBaz</type> class which
|
||||
implements <type>MamanIbaz</type>:
|
||||
and which implements the interface <type>ViewerEditable</type>. For
|
||||
example, this code registers a new <type>ViewerFile</type> class which
|
||||
implements <type>ViewerEditable</type>:
|
||||
<informalexample><programlisting>
|
||||
static void
|
||||
maman_baz_do_action (MamanIbaz *self)
|
||||
viewer_file_save (ViewerEditable *editable)
|
||||
{
|
||||
g_print ("Baz implementation of Ibaz interface Action.\n");
|
||||
g_print ("File implementation of editable interface save method.\n");
|
||||
}
|
||||
|
||||
static void
|
||||
baz_interface_init (gpointer g_iface,
|
||||
gpointer iface_data)
|
||||
viewer_file_editable_interface_init (gpointer g_iface,
|
||||
gpointer iface_data)
|
||||
{
|
||||
MamanIbazInterface *iface = (MamanIbazInterface *)g_iface;
|
||||
iface->do_action = maman_baz_do_action;
|
||||
ViewerEditableInterface *iface = g_iface;
|
||||
|
||||
iface->save = viewer_file_save;
|
||||
}
|
||||
|
||||
GType
|
||||
maman_baz_get_type (void)
|
||||
viewer_file_get_type (void)
|
||||
{
|
||||
static GType type = 0;
|
||||
if (type == 0) {
|
||||
const GTypeInfo info = {
|
||||
sizeof (MamanBazClass),
|
||||
sizeof (ViewerFileClass),
|
||||
NULL, /* base_init */
|
||||
NULL, /* base_finalize */
|
||||
NULL, /* class_init */
|
||||
NULL, /* class_finalize */
|
||||
NULL, /* class_data */
|
||||
sizeof (MamanBaz),
|
||||
sizeof (ViewerFile),
|
||||
0, /* n_preallocs */
|
||||
NULL /* instance_init */
|
||||
};
|
||||
const GInterfaceInfo ibaz_info = {
|
||||
(GInterfaceInitFunc) baz_interface_init, /* interface_init */
|
||||
NULL, /* interface_finalize */
|
||||
NULL /* interface_data */
|
||||
const GInterfaceInfo editable_info = {
|
||||
(GInterfaceInitFunc) viewer_file_editable_interface_init, /* interface_init */
|
||||
NULL, /* interface_finalize */
|
||||
NULL /* interface_data */
|
||||
};
|
||||
type = g_type_register_static (G_TYPE_OBJECT,
|
||||
"MamanBazType",
|
||||
type = g_type_register_static (VIEWER_TYPE_FILE,
|
||||
"ViewerFile",
|
||||
&info, 0);
|
||||
g_type_add_interface_static (type,
|
||||
MAMAN_TYPE_IBAZ,
|
||||
&ibaz_info);
|
||||
VIEWER_TYPE_EDITABLE,
|
||||
&editable_info);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
@ -824,12 +836,12 @@ struct _GInterfaceInfo
|
||||
<link linkend="G-DEFINE-INTERFACE:CAPS">G_DEFINE_INTERFACE</link>
|
||||
which can be used to define the interface:
|
||||
<informalexample><programlisting>
|
||||
G_DEFINE_INTERFACE (MamanIbaz, maman_ibaz, G_TYPE_OBJECT);
|
||||
G_DEFINE_INTERFACE (ViewerEditable, viewer_editable, G_TYPE_OBJECT);
|
||||
|
||||
static void
|
||||
maman_ibaz_default_init (MamanIbazInterface *iface)
|
||||
viewer_editable_default_init (ViewerEditableInterface *iface)
|
||||
{
|
||||
/* add properties and signals here, will only called once */
|
||||
/* add properties and signals here, will only be called once */
|
||||
}
|
||||
</programlisting></informalexample>
|
||||
</para>
|
||||
@ -838,15 +850,15 @@ maman_ibaz_default_init (MamanIbazInterface *iface)
|
||||
Or you can do that yourself in a GType function for your interface:
|
||||
<informalexample><programlisting>
|
||||
GType
|
||||
maman_ibaz_get_type (void)
|
||||
viewer_editable_get_type (void)
|
||||
{
|
||||
static volatile gsize type_id = 0;
|
||||
if (g_once_init_enter (&type_id)) {
|
||||
const GTypeInfo info = {
|
||||
sizeof (MamanIbazInterface),
|
||||
sizeof (ViewerEditableInterface),
|
||||
NULL, /* base_init */
|
||||
NULL, /* base_finalize */
|
||||
maman_ibaz_default_init, /* class_init */
|
||||
viewer_editable_default_init, /* class_init */
|
||||
NULL, /* class_finalize */
|
||||
NULL, /* class_data */
|
||||
0, /* instance_size */
|
||||
@ -854,15 +866,15 @@ maman_ibaz_get_type (void)
|
||||
NULL /* instance_init */
|
||||
};
|
||||
GType type = g_type_register_static (G_TYPE_INTERFACE,
|
||||
"MamanIbaz",
|
||||
&info, 0);
|
||||
"ViewerEditable",
|
||||
&info, 0);
|
||||
g_once_init_leave (&type_id, type);
|
||||
}
|
||||
return type_id;
|
||||
}
|
||||
|
||||
static void
|
||||
maman_ibaz_default_init (MamanIbazInterface *iface)
|
||||
viewer_editable_default_init (ViewerEditableInterface *iface)
|
||||
{
|
||||
/* add properties and signals here, will only called once */
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user