Is there a good way to copy a Gtk widget?
Asked Answered
K

2

10

Is there a way, using the Gtk library in C, to clone a Gtk button (for instance), and pack it somewhere else in the app. I know you can't pack the same widget twice. And that this code obviously wouldn't work, but shows what happens when I attempt a shallow copy of the button:

GtkButton *a = g_object_new(GTK_TYPE_BUTTON, "label", "o_0", NULL);
GtkButton *b = g_memdup(a, sizeof *a);
gtk_box_pack_start_defaults(GTK_BOX(vbox), GTK_WIDGET(b));

There is surrounding code which creates a vbox and packs it in a window and runs gtk_main(). This will result in these hard to understand error messages:

(main:6044): Gtk-CRITICAL **: gtk_widget_hide: assertion `GTK_IS_WIDGET (widget)' failed

(main:6044): Gtk-CRITICAL **: gtk_widget_realize: assertion `GTK_WIDGET_ANCHORED (widget) || GTK_IS_INVISIBLE (widget)' failed
**
Gtk:ERROR:/build/buildd/gtk+2.0-2.18.3/gtk/gtkwidget.c:8431:gtk_widget_real_map: assertion failed: (GTK_WIDGET_REALIZED (widget))

Along the same lines, if I were to write my own GObject (not necessarily a Gtk widget), is there a good way to write a copy constructor. Im thinking it should be an interface with optional hooks and based mostly on the properties, handling the class's hierarchy in some way.

I'd want to do this:

GtkButton *b = copyable_copy(COPYABLE(a));

If GtkButton could use a theoretical copyable interface.

Kyliekylila answered 9/6, 2010 at 6:56 Comment(1)
You could create an GObject interface that does the 'copyable' thing by providing the hooks and stuff... I wouldn't want to write it though, it'd probably wind up being tricky...Malignancy
C
3

A clone throught properties is a viable solution:

GObject *
g_object_clone(GObject *src)
{
    GObject *dst;
    GParameter *params;
    GParamSpec **specs;
    guint n, n_specs, n_params;

    specs = g_object_class_list_properties(G_OBJECT_GET_CLASS(src), &n_specs);
    params = g_new0(GParameter, n_specs);
    n_params = 0;

    for (n = 0; n < n_specs; ++n)
        if (strcmp(specs[n]->name, "parent") &&
            (specs[n]->flags & G_PARAM_READWRITE) == G_PARAM_READWRITE) {
            params[n_params].name = g_intern_string(specs[n]->name);
            g_value_init(&params[n_params].value, specs[n]->value_type);
            g_object_get_property(src, specs[n]->name, &params[n_params].value);
            ++ n_params;
        }

    dst = g_object_newv(G_TYPE_FROM_INSTANCE(src), n_params, params);
    g_free(specs);
    g_free(params);

    return dst;
}

Cloning a widget is not that trivial though, but the above approach is usable in most cases (on a GtkButton for sure).

I'd not care that much of states not exposed with properties (all proper widgets should be fully defined by properties to be usable with GtkBuilder) but a lot of corner cases will make a robust cloning quite difficult (interfaces and containers being the first ones that come to my mind).

Counterchange answered 13/6, 2010 at 0:26 Comment(3)
Thanks, this is the best there could be for a general copy constructor on a GObject (and works for my needs).Kyliekylila
You also don't want to set the GtkWidget::margin and GtkWidget::expand properties; they overwrite other properties.Microphone
While this might seem to work for as much as you tested it, it is not "viable" in the sense of being a safe recommendation to make to people, for the multiple reasons given in the other answer and its comments. It is therefore not "the best there could be for a general copy constructor on a GObject" and is not the real answer to the question. ('Is there a good way? No, but here's an unimaginably fragile hack.')Shortstop
B
4

I don't think so. As far as I know, there's no guarantee that widgets keep all their state in properties, that you can access from the outside. If a widget "hides" state by not exporting it, there's no way you can copy it from the outside.

Technically, widgets can just include fields in their core struct that you don't see from outside of the implementation, so you can't even copy the bits using a dumb memcpy(), unless you're willing to specify the byte-count by counting manually and using a literal.

That being said, it's also quite possible that enough widgets expose enough state through properties that a copy would still function, and perhaps only exhibit minor glitches. It would certainly be a pretty cool hack. I would recommend asking the core GTK+ developers directly, perhaps on the gtk-devel-list mailing list.

Blackstock answered 9/6, 2010 at 7:7 Comment(1)
Good answer for its caution, but maybe not cautious enough! A bitwise copy would not work, more so, for the same reason that one must write specific copy constructors in e.g. C++: because if the source holds references to other objects, a bitwise copy would create additional references to those but without incrementing their reference count, leading to use-after-frees, double-frees, and all other manner of horror. Also, I'm pretty sure the GTK+ developers would not be interested in this, for the reasons we've given here, and nor IMO should they be.Shortstop
C
3

A clone throught properties is a viable solution:

GObject *
g_object_clone(GObject *src)
{
    GObject *dst;
    GParameter *params;
    GParamSpec **specs;
    guint n, n_specs, n_params;

    specs = g_object_class_list_properties(G_OBJECT_GET_CLASS(src), &n_specs);
    params = g_new0(GParameter, n_specs);
    n_params = 0;

    for (n = 0; n < n_specs; ++n)
        if (strcmp(specs[n]->name, "parent") &&
            (specs[n]->flags & G_PARAM_READWRITE) == G_PARAM_READWRITE) {
            params[n_params].name = g_intern_string(specs[n]->name);
            g_value_init(&params[n_params].value, specs[n]->value_type);
            g_object_get_property(src, specs[n]->name, &params[n_params].value);
            ++ n_params;
        }

    dst = g_object_newv(G_TYPE_FROM_INSTANCE(src), n_params, params);
    g_free(specs);
    g_free(params);

    return dst;
}

Cloning a widget is not that trivial though, but the above approach is usable in most cases (on a GtkButton for sure).

I'd not care that much of states not exposed with properties (all proper widgets should be fully defined by properties to be usable with GtkBuilder) but a lot of corner cases will make a robust cloning quite difficult (interfaces and containers being the first ones that come to my mind).

Counterchange answered 13/6, 2010 at 0:26 Comment(3)
Thanks, this is the best there could be for a general copy constructor on a GObject (and works for my needs).Kyliekylila
You also don't want to set the GtkWidget::margin and GtkWidget::expand properties; they overwrite other properties.Microphone
While this might seem to work for as much as you tested it, it is not "viable" in the sense of being a safe recommendation to make to people, for the multiple reasons given in the other answer and its comments. It is therefore not "the best there could be for a general copy constructor on a GObject" and is not the real answer to the question. ('Is there a good way? No, but here's an unimaginably fragile hack.')Shortstop

© 2022 - 2024 — McMap. All rights reserved.