Audit module


Attached is a quick hack that isn't useful yet to audit GTK apps for
missing mnemonics and tooltips. (Though I've just been told on 
#interface that only toolbars should have tooltips, so maybe that part
is useless.)

Pretty much the same code would work to audit for whether the
appropriate accessibility features have been used. So I thought you
guys might be interested in developing this.

Usage is like this:

 gcc -shared -fPIC -rdynamic -Wall -g -O2 `pkg-config --cflags gtk+-2.0` auditmodule.c 

 GTK_MODULES=/full/path/to/module/a.out my_gtk_program

This will then colorize widgets that fail the "audit"

Clearly if the tool was made useful you'd want to use portable
automake/libtool instead of my gcc compile line and install it

This raises a GObject API issue, which is the need for the class_ref
on GtkWidgetClass - should GtkWidget get unloaded after get_type()? I
found that very surprising. And I found it pretty weird when
g_signal_lookup() silently returned 0 in this case.

Anyhow, I think this might be pretty handy for making everything
accessible - set GTK_MODULES in your shell startup, run GNOME, file
bugs for all the colored widgets you see in apps.


#include <gtk/gtk.h>

static void
recurse_widget_tree (GtkWidget *widget,
                     GSList **labels,
                     GSList **mnemonic_targets,
                     GSList **tooltip_targets)
  if (GTK_IS_CONTAINER (widget))
      GList *children;
      GList *tmp;
      children = gtk_container_children (GTK_CONTAINER (widget));

      tmp = children;
      while (tmp != NULL)
          recurse_widget_tree (tmp->data,
                               labels, mnemonic_targets,

          tmp = tmp->next;

      g_list_free (children);

  if (GTK_IS_LABEL (widget))
    *labels = g_slist_prepend (*labels, widget);

  if (GTK_WIDGET_CAN_FOCUS (widget) ||
      GTK_WIDGET_GET_CLASS (widget)->activate_signal)
      if (!(GTK_IS_MENU_ITEM (widget) &&
            GTK_BIN (widget)->child == NULL) &&
          !GTK_IS_TEAROFF_MENU_ITEM (widget))
        *mnemonic_targets = g_slist_prepend (*mnemonic_targets,
  if (!GTK_WIDGET_NO_WINDOW (widget) &&
      !GTK_IS_WINDOW (widget) &&
      !GTK_IS_MENU_ITEM (widget) &&
      !GTK_IS_MENU_SHELL (widget) &&
      !GTK_IS_SCROLLBAR (widget) &&
      !GTK_IS_PANED (widget) &&
      !GTK_IS_ENTRY (widget) &&
      !GTK_IS_TEXT_VIEW (widget))
    *tooltip_targets = g_slist_prepend (*tooltip_targets, widget);

static void
set_color (GtkWidget *widget,
           GdkColor *color)
  int i;

  i = 0;

  while (i < 5)
      /* given the modify_* implementation this is kind of
       * slow
      gtk_widget_modify_bg (widget, i, color);
      gtk_widget_modify_base (widget, i, color);

static gboolean
widget_shown_hook (GSignalInvocationHint *ihint,
                   guint                  n_param_values,
                   const GValue          *param_values,
                   gpointer               data)
  GtkWidget *widget;
  GSList *labels;
  GSList *mnemonic_targets;
  GSList *tooltip_targets;
  GSList *tmp;
  GdkColor red, yellow, orange;
  GHashTable *actual_targets;
  widget = g_value_get_object (&param_values[0]);

  g_return_val_if_fail (GTK_IS_WIDGET (widget), TRUE);

  if (!(GTK_IS_WINDOW (widget) ||
        GTK_IS_MENU (widget)))
    return TRUE;

  gdk_color_parse ("red", &red);
  gdk_color_parse ("yellow", &yellow);
  gdk_color_parse ("orange", &orange);
  labels = NULL;
  mnemonic_targets = NULL;
  tooltip_targets = NULL;
  recurse_widget_tree (widget, &labels, &mnemonic_targets,

  actual_targets = g_hash_table_new (NULL, NULL);
  tmp = labels;
  while (tmp != NULL)
      guint keyval = gtk_label_get_mnemonic_keyval (GTK_LABEL

      if (keyval != 0)
          /* This label has a mnemonic, find its target */
          GtkWidget *target = NULL;

          g_object_get (G_OBJECT (tmp->data), "mnemonic_widget",
                        &target, NULL);

          if (target == NULL)
              /* Try walking up tree */
              GtkWidget *parent;
              parent = GTK_WIDGET (tmp->data)->parent;
              while (parent)
                  if (GTK_WIDGET_CAN_FOCUS (parent) ||
                      (GTK_WIDGET_GET_CLASS (parent)->activate_signal)
                      (parent->parent && GTK_IS_NOTEBOOK
              (parent->parent)) ||
                      (GTK_IS_MENU_ITEM (parent)))
                      target = parent;
                  parent = parent->parent;

          if (target)
            g_hash_table_insert (actual_targets, target, target);
      tmp = tmp->next;

  tmp = mnemonic_targets;
  while (tmp)
      if (!g_hash_table_lookup (actual_targets, tmp->data))
          /* Check if we should also be a tooltip target, if so
           * we're orange and remove from tooltip list, otherwise
           * we're red.
          if (g_slist_find (tooltip_targets, tmp->data) &&
              !gtk_tooltips_data_get (tmp->data))
              tooltip_targets = g_slist_remove (tooltip_targets,
              set_color (tmp->data, &orange);
              set_color (tmp->data, &red);

      tmp = tmp->next;

  tmp = tooltip_targets;
  while (tmp)
      if (!gtk_tooltips_data_get (tmp->data))
          set_color (tmp->data, &yellow);
      tmp = tmp->next;

  g_slist_free (tooltip_targets);
  g_slist_free (mnemonic_targets);
  g_slist_free (labels);
  g_hash_table_destroy (actual_targets);
  return TRUE;

gtk_module_init (int argc, char **argv)
  guint signal_id;

  g_type_class_ref (GTK_TYPE_WIDGET);
  signal_id = g_signal_lookup ("show", GTK_TYPE_WIDGET);

  if (signal_id == 0)
      g_warning ("Signal doesn't exist");
  g_signal_add_emission_hook (signal_id, 0,
                              widget_shown_hook, NULL,

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]