Jonathan Sternberg
Jonathan Sternberg

Reputation: 6647

Finding children of a GtkWidget

I need to be able to explore a GTK GUI's structure programmatically. I have the GtkWidget and I want to find any children of that widget. Now I know that GtkContainer's have a function to find children and that GtkContainer is derived from GtkWidget.

Is there anyway I can check if a widget is a GtkContainer and then perform the cast? If not, is there any other way I can discover the GtkWidget's that are children of the one I have?

Upvotes: 18

Views: 17744

Answers (4)

Ryan
Ryan

Reputation: 614

A different approach is needed if using GTK4 as GtkContainer no longer exists. See migration guide

A simple function traversing the widget heirarchy:

void print_widget_names(GtkWidget* parent, int level) {
  for (int i=0; i < level; i++) { 
    g_print("    ");
  }
  level++;
  g_print("%s\n", gtk_widget_get_name(GTK_WIDGET(parent)));
  GtkWidget *widget = gtk_widget_get_first_child(GTK_WIDGET(parent));
  GtkWidget *next;
  if (widget != NULL) {
    print_widget_names(widget, level);
    while ((next = gtk_widget_get_next_sibling(widget)) != NULL) {
      widget = next;
      print_widget_names(widget, level);
    }
  }
}

Upvotes: 0

samildeli
samildeli

Reputation: 1

I created these functions that are based on the same principal as the accepted answer.

In find_child_by_index, depth is equal to the amount of indices you need to pass. The first index is for the children of parent, the second index is for the grandchildren and so forth.

void print_children_helper(GtkWidget* parent, int indent_size, int depth)
{
    for (int i = 0; i < depth * indent_size; i++)
        printf(" ");
    printf("%s\n", gtk_widget_get_name(parent));

    GList* children = NULL;
    if (GTK_IS_CONTAINER(parent))
        children = gtk_container_get_children(GTK_CONTAINER(parent));

    while (children != NULL)
    {
        print_children(children->data, indent_size, depth + 1);
        children = children->next;
    }
}

void print_children(GtkWidget* parent, int indent_size)
{
    print_children_helper(parent, indent_size, 0);
}

GtkWidget* find_child_by_name(GtkWidget* parent, const gchar* name)
{
    if (g_strcmp0(gtk_widget_get_name(parent), name) == 0)
        return parent;

    GList* children = NULL;
    if (GTK_IS_CONTAINER(parent))
        children = gtk_container_get_children(GTK_CONTAINER(parent));

    while (children != NULL)
    {
        GtkWidget* widget = find_child_by_name(children->data, name);

        if (widget != NULL)
            return widget;

        children = children->next;
    }

    return NULL;
}

GtkWidget* find_child_by_index(GtkWidget* parent, int depth, ...)
{
    va_list argp;
    va_start(argp, depth);

    for (int i = 0; i < depth; i++)
    {
        int index = va_arg(argp, int);

        GList* children = NULL;
        if (GTK_IS_CONTAINER(parent))
            children = gtk_container_get_children(GTK_CONTAINER(parent));

        for (int j = 0; j < index; j++)
            if (children != NULL)
                children = children->next;

        if (children != NULL)
            parent = children->data;
        else
            return NULL;
    }

    va_end(argp);
    return parent;
}

Upvotes: 0

Darius Kucinskas
Darius Kucinskas

Reputation: 10671

In the following code I have implemented find_child function which recursively searches for the widget by name. Ideas build on the answer of @ptomato and from the following PHP example:

    #include <gtk/gtk.h>

    GtkWidget*
    find_child(GtkWidget* parent, const gchar* name)
    {
            if (g_strcasecmp(gtk_widget_get_name((GtkWidget*)parent), (gchar*)name) == 0) { 
                    return parent;
            }

            if (GTK_IS_BIN(parent)) {
                    GtkWidget *child = gtk_bin_get_child(GTK_BIN(parent));
                    return find_child(child, name);
            }

            if (GTK_IS_CONTAINER(parent)) {
                    GList *children = gtk_container_get_children(GTK_CONTAINER(parent));
                    while ((children = g_list_next(children)) != NULL) {
                            GtkWidget* widget = find_child(children->data, name);
                            if (widget != NULL) {
                                    return widget;
                            }
                    }
            }

            return NULL;
    }

    int
    main(int argc, char *argv[])
    {
            gtk_init(&argc, &argv);

            GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
            GtkWidget *frame = gtk_frame_new(NULL);
            GtkWidget *vbox1 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
            GtkWidget *textView = gtk_text_view_new();
            GtkWidget *hbox1 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
            GtkWidget *button1 = gtk_button_new_with_label("button1");
            gtk_widget_set_name(button1, "btn1");
            GtkWidget *button2 = gtk_button_new_with_label("button2");
            gtk_widget_set_name(button2, "btn2");

            gtk_window_set_title(GTK_WINDOW(window), "Hello");
            gtk_container_set_border_width(GTK_CONTAINER(window), 10);
            gtk_window_set_default_size(GTK_WINDOW(window), 450, 400);

            gtk_container_add(GTK_CONTAINER(window), frame);
            gtk_container_add(GTK_CONTAINER(frame), vbox1);

            gtk_box_pack_start(GTK_BOX(vbox1), textView, 1, 1, 0);
            gtk_box_pack_start(GTK_BOX(vbox1), hbox1, 0, 1, 0);
            gtk_box_pack_start(GTK_BOX(hbox1), button1, 0, 1, 0);
            gtk_box_pack_start(GTK_BOX(hbox1), button2, 0, 1, 0);

            gtk_widget_show_all(window);

            g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);

            GtkWidget* child = find_child(window, "btn2");
            if (child == button2) {
                    g_print("found it!\n");
            } else {
                    g_print("not found it!\n");
            }

            gtk_main();
            return 0;
    }

Upvotes: 7

ptomato
ptomato

Reputation: 57870

Yes, there are macros for every GObject type to check whether an object is of that type:

if(GTK_IS_CONTAINER(widget)) {
    GList *children = gtk_container_get_children(GTK_CONTAINER(widget));
    ...
}

If the widget is a GtkBin it has only one child. In that case, the following is simpler than dealing with a GList:

if(GTK_IS_BIN(widget)) {
    GtkWidget *child = gtk_bin_get_child(GTK_BIN(widget));
    ...
}

Upvotes: 18

Related Questions