forked from AuroraMiddleware/gtk
f4f683a469
Make it more clear that the function is supposed to be called during the dispose sequence of a widget.
492 lines
14 KiB
C
492 lines
14 KiB
C
/*
|
|
* Copyright (c) 2020 Red Hat, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include <glib/gi18n-lib.h>
|
|
|
|
#include "a11y.h"
|
|
#include "window.h"
|
|
|
|
#include "gtktypebuiltins.h"
|
|
#include "gtklabel.h"
|
|
#include "gtkstack.h"
|
|
#include "gtkbinlayout.h"
|
|
#include "gtkaccessibleprivate.h"
|
|
#include "gtkaccessiblevalueprivate.h"
|
|
#include "gtkatcontextprivate.h"
|
|
#include "gtkcolumnview.h"
|
|
#include "gtksignallistitemfactory.h"
|
|
#include "gtklistitem.h"
|
|
#include "gtknoselection.h"
|
|
#include "gtkfilterlistmodel.h"
|
|
#include "gtkboolfilter.h"
|
|
#ifdef G_OS_UNIX
|
|
#include "a11y/gtkatspicontextprivate.h"
|
|
#endif
|
|
|
|
typedef enum {
|
|
STATE,
|
|
PROPERTY,
|
|
RELATION
|
|
} AttributeKind;
|
|
|
|
typedef struct _AccessibleAttribute AccessibleAttribute;
|
|
typedef struct _AccessibleAttributeClass AccessibleAttributeClass;
|
|
|
|
struct _AccessibleAttribute
|
|
{
|
|
GObject parent_instance;
|
|
AttributeKind kind;
|
|
int attribute;
|
|
char *name;
|
|
gboolean is_default;
|
|
GtkAccessibleValue *value;
|
|
};
|
|
|
|
struct _AccessibleAttributeClass
|
|
{
|
|
GObjectClass parent_class;
|
|
};
|
|
|
|
enum {
|
|
PROP_KIND = 1,
|
|
PROP_ATTRIBUTE,
|
|
PROP_NAME,
|
|
PROP_IS_DEFAULT,
|
|
PROP_VALUE
|
|
};
|
|
|
|
static GType accessible_attribute_get_type (void);
|
|
|
|
G_DEFINE_TYPE (AccessibleAttribute, accessible_attribute, G_TYPE_OBJECT);
|
|
|
|
static void
|
|
accessible_attribute_init (AccessibleAttribute *object)
|
|
{
|
|
}
|
|
|
|
static void
|
|
accessible_attribute_finalize (GObject *object)
|
|
{
|
|
AccessibleAttribute *self = (AccessibleAttribute *)object;
|
|
|
|
g_free (self->name);
|
|
gtk_accessible_value_unref (self->value);
|
|
|
|
G_OBJECT_CLASS (accessible_attribute_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
accessible_attribute_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
AccessibleAttribute *self = (AccessibleAttribute *)object;
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_KIND:
|
|
self->kind = g_value_get_uint (value);
|
|
break;
|
|
|
|
case PROP_ATTRIBUTE:
|
|
self->attribute = g_value_get_uint (value);
|
|
break;
|
|
|
|
case PROP_NAME:
|
|
g_clear_pointer (&self->name, g_free);
|
|
self->name = g_value_dup_string (value);
|
|
break;
|
|
|
|
case PROP_IS_DEFAULT:
|
|
self->is_default = g_value_get_boolean (value);
|
|
break;
|
|
|
|
case PROP_VALUE:
|
|
g_clear_pointer (&self->value, gtk_accessible_value_unref);
|
|
self->value = g_value_dup_boxed (value);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
accessible_attribute_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
AccessibleAttribute *self = (AccessibleAttribute *)object;
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_KIND:
|
|
g_value_set_uint (value, self->kind);
|
|
break;
|
|
|
|
case PROP_ATTRIBUTE:
|
|
g_value_set_uint (value, self->attribute);
|
|
break;
|
|
|
|
case PROP_NAME:
|
|
g_value_set_string (value, self->name);
|
|
break;
|
|
|
|
case PROP_IS_DEFAULT:
|
|
g_value_set_boolean (value, self->is_default);
|
|
break;
|
|
|
|
case PROP_VALUE:
|
|
g_value_set_boxed (value, self->value);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
accessible_attribute_class_init (AccessibleAttributeClass *class)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
|
|
|
object_class->finalize = accessible_attribute_finalize;
|
|
object_class->set_property = accessible_attribute_set_property;
|
|
object_class->get_property = accessible_attribute_get_property;
|
|
|
|
g_object_class_install_property (object_class, PROP_KIND,
|
|
g_param_spec_uint ("kind", NULL, NULL,
|
|
0, 2, 0,
|
|
G_PARAM_READWRITE));
|
|
g_object_class_install_property (object_class, PROP_ATTRIBUTE,
|
|
g_param_spec_uint ("attribute", NULL, NULL,
|
|
0, G_MAXUINT, 0,
|
|
G_PARAM_READWRITE));
|
|
g_object_class_install_property (object_class, PROP_NAME,
|
|
g_param_spec_string ("name", NULL, NULL,
|
|
NULL,
|
|
G_PARAM_READWRITE));
|
|
g_object_class_install_property (object_class, PROP_IS_DEFAULT,
|
|
g_param_spec_boolean ("is-default", NULL, NULL,
|
|
FALSE,
|
|
G_PARAM_READWRITE));
|
|
g_object_class_install_property (object_class, PROP_VALUE,
|
|
g_param_spec_boxed ("value", NULL, NULL,
|
|
GTK_TYPE_ACCESSIBLE_VALUE,
|
|
G_PARAM_READWRITE));
|
|
}
|
|
|
|
struct _GtkInspectorA11y
|
|
{
|
|
GtkWidget parent;
|
|
|
|
GObject *object;
|
|
|
|
GtkWidget *box;
|
|
GtkWidget *role;
|
|
GtkWidget *path;
|
|
GtkWidget *attributes;
|
|
};
|
|
|
|
typedef struct _GtkInspectorA11yClass
|
|
{
|
|
GtkWidgetClass parent_class;
|
|
} GtkInspectorA11yClass;
|
|
|
|
G_DEFINE_TYPE (GtkInspectorA11y, gtk_inspector_a11y, GTK_TYPE_WIDGET)
|
|
|
|
static void
|
|
update_role (GtkInspectorA11y *sl)
|
|
{
|
|
GtkAccessibleRole role;
|
|
GEnumClass *eclass;
|
|
GEnumValue *value;
|
|
|
|
role = gtk_accessible_get_accessible_role (GTK_ACCESSIBLE (sl->object));
|
|
|
|
eclass = g_type_class_ref (GTK_TYPE_ACCESSIBLE_ROLE);
|
|
value = g_enum_get_value (eclass, role);
|
|
gtk_label_set_label (GTK_LABEL (sl->role), value->value_nick);
|
|
g_type_class_unref (eclass);
|
|
}
|
|
|
|
static void
|
|
update_path (GtkInspectorA11y *sl)
|
|
{
|
|
const char *path = "—";
|
|
#ifdef G_OS_UNIX
|
|
GtkATContext *context;
|
|
|
|
context = gtk_accessible_get_at_context (GTK_ACCESSIBLE (sl->object));
|
|
if (GTK_IS_AT_SPI_CONTEXT (context))
|
|
path = gtk_at_spi_context_get_context_path (GTK_AT_SPI_CONTEXT (context));
|
|
#endif
|
|
|
|
gtk_label_set_label (GTK_LABEL (sl->path), path);
|
|
}
|
|
|
|
extern GType gtk_string_pair_get_type (void);
|
|
|
|
static void
|
|
update_attributes (GtkInspectorA11y *sl)
|
|
{
|
|
GtkATContext *context;
|
|
GListStore *store;
|
|
GtkBoolFilter *filter;
|
|
GtkFilterListModel *filter_model;
|
|
GtkNoSelection *selection;
|
|
GObject *obj;
|
|
GEnumClass *eclass;
|
|
guint i;
|
|
const char *name;
|
|
GtkAccessibleState state;
|
|
GtkAccessibleProperty prop;
|
|
GtkAccessibleRelation rel;
|
|
GtkAccessibleValue *value;
|
|
gboolean has_value;
|
|
|
|
context = gtk_accessible_get_at_context (GTK_ACCESSIBLE (sl->object));
|
|
if (!context)
|
|
return;
|
|
|
|
store = g_list_store_new (G_TYPE_OBJECT);
|
|
|
|
eclass = g_type_class_ref (GTK_TYPE_ACCESSIBLE_STATE);
|
|
|
|
for (i = 0; i < eclass->n_values; i++)
|
|
{
|
|
state = eclass->values[i].value;
|
|
name = eclass->values[i].value_nick;
|
|
has_value = gtk_at_context_has_accessible_state (context, state);
|
|
value = gtk_at_context_get_accessible_state (context, state);
|
|
obj = g_object_new (accessible_attribute_get_type (),
|
|
"kind", STATE,
|
|
"attribute", state,
|
|
"name", name,
|
|
"is-default", !has_value,
|
|
"value", value,
|
|
NULL);
|
|
g_list_store_append (store, obj);
|
|
g_object_unref (obj);
|
|
}
|
|
|
|
g_type_class_unref (eclass);
|
|
|
|
eclass = g_type_class_ref (GTK_TYPE_ACCESSIBLE_PROPERTY);
|
|
|
|
for (i = 0; i < eclass->n_values; i++)
|
|
{
|
|
prop = eclass->values[i].value;
|
|
name = eclass->values[i].value_nick;
|
|
has_value = gtk_at_context_has_accessible_property (context, prop);
|
|
value = gtk_at_context_get_accessible_property (context, prop);
|
|
obj = g_object_new (accessible_attribute_get_type (),
|
|
"kind", PROPERTY,
|
|
"attribute", prop,
|
|
"name", name,
|
|
"is-default", !has_value,
|
|
"value", value,
|
|
NULL);
|
|
g_list_store_append (store, obj);
|
|
g_object_unref (obj);
|
|
}
|
|
|
|
g_type_class_unref (eclass);
|
|
|
|
eclass = g_type_class_ref (GTK_TYPE_ACCESSIBLE_RELATION);
|
|
|
|
for (i = 0; i < eclass->n_values; i++)
|
|
{
|
|
rel = eclass->values[i].value;
|
|
name = eclass->values[i].value_nick;
|
|
has_value = gtk_at_context_has_accessible_relation (context, rel);
|
|
value = gtk_at_context_get_accessible_relation (context, rel);
|
|
obj = g_object_new (accessible_attribute_get_type (),
|
|
"kind", RELATION,
|
|
"attribute", rel,
|
|
"name", name,
|
|
"is-default", !has_value,
|
|
"value", value,
|
|
NULL);
|
|
g_list_store_append (store, obj);
|
|
g_object_unref (obj);
|
|
}
|
|
|
|
g_type_class_unref (eclass);
|
|
|
|
filter = gtk_bool_filter_new (gtk_property_expression_new (accessible_attribute_get_type (), NULL, "is-default"));
|
|
gtk_bool_filter_set_invert (filter, TRUE);
|
|
|
|
filter_model = gtk_filter_list_model_new (G_LIST_MODEL (store), GTK_FILTER (filter));
|
|
selection = gtk_no_selection_new (G_LIST_MODEL (filter_model));
|
|
gtk_column_view_set_model (GTK_COLUMN_VIEW (sl->attributes), GTK_SELECTION_MODEL (selection));
|
|
g_object_unref (selection);
|
|
|
|
if (g_list_model_get_n_items (G_LIST_MODEL (filter_model)) > 0)
|
|
gtk_widget_show (sl->attributes);
|
|
else
|
|
gtk_widget_hide (sl->attributes);
|
|
}
|
|
|
|
static void
|
|
setup_cell_cb (GtkSignalListItemFactory *factory,
|
|
GtkListItem *list_item)
|
|
{
|
|
GtkWidget *label;
|
|
|
|
label = gtk_label_new (NULL);
|
|
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
|
|
gtk_widget_set_margin_start (label, 6);
|
|
gtk_widget_set_margin_end (label, 6);
|
|
gtk_list_item_set_child (list_item, label);
|
|
}
|
|
|
|
static void
|
|
bind_name_cb (GtkSignalListItemFactory *factory,
|
|
GtkListItem *list_item)
|
|
{
|
|
AccessibleAttribute *item;
|
|
GtkWidget *label;
|
|
|
|
item = gtk_list_item_get_item (list_item);
|
|
label = gtk_list_item_get_child (list_item);
|
|
|
|
if (item->is_default)
|
|
gtk_widget_add_css_class (label, "dim-label");
|
|
else
|
|
gtk_widget_remove_css_class (label, "dim-label");
|
|
|
|
gtk_label_set_label (GTK_LABEL (label), item->name);
|
|
}
|
|
|
|
static void
|
|
bind_value_cb (GtkSignalListItemFactory *factory,
|
|
GtkListItem *list_item)
|
|
{
|
|
AccessibleAttribute *item;
|
|
GtkWidget *label;
|
|
char *string;
|
|
|
|
item = gtk_list_item_get_item (list_item);
|
|
label = gtk_list_item_get_child (list_item);
|
|
|
|
if (item->is_default)
|
|
gtk_widget_add_css_class (label, "dim-label");
|
|
else
|
|
gtk_widget_remove_css_class (label, "dim-label");
|
|
|
|
string = gtk_accessible_value_to_string (item->value);
|
|
gtk_label_set_label (GTK_LABEL (label), string);
|
|
g_free (string);
|
|
}
|
|
|
|
static void
|
|
refresh_all (GtkInspectorA11y *sl)
|
|
{
|
|
update_role (sl);
|
|
update_path (sl);
|
|
update_attributes (sl);
|
|
}
|
|
|
|
void
|
|
gtk_inspector_a11y_set_object (GtkInspectorA11y *sl,
|
|
GObject *object)
|
|
{
|
|
GtkWidget *stack;
|
|
GtkStackPage *page;
|
|
GtkATContext *context;
|
|
|
|
if (sl->object && GTK_IS_ACCESSIBLE (sl->object))
|
|
{
|
|
context = gtk_accessible_get_at_context (GTK_ACCESSIBLE (sl->object));
|
|
if (context)
|
|
g_signal_handlers_disconnect_by_func (context, refresh_all, sl);
|
|
}
|
|
|
|
g_set_object (&sl->object, object);
|
|
|
|
stack = gtk_widget_get_parent (GTK_WIDGET (sl));
|
|
page = gtk_stack_get_page (GTK_STACK (stack), GTK_WIDGET (sl));
|
|
|
|
if (GTK_IS_ACCESSIBLE (object))
|
|
{
|
|
context = gtk_accessible_get_at_context (GTK_ACCESSIBLE (sl->object));
|
|
if (context)
|
|
g_signal_connect_swapped (context, "state-change", G_CALLBACK (refresh_all), sl);
|
|
gtk_stack_page_set_visible (page, TRUE);
|
|
update_role (sl);
|
|
update_path (sl);
|
|
update_attributes (sl);
|
|
}
|
|
else
|
|
{
|
|
gtk_stack_page_set_visible (page, FALSE);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_inspector_a11y_init (GtkInspectorA11y *sl)
|
|
{
|
|
gtk_widget_init_template (GTK_WIDGET (sl));
|
|
}
|
|
|
|
static void
|
|
dispose (GObject *o)
|
|
{
|
|
GtkInspectorA11y *sl = GTK_INSPECTOR_A11Y (o);
|
|
|
|
if (sl->object && GTK_IS_ACCESSIBLE (sl->object))
|
|
{
|
|
GtkATContext *context;
|
|
|
|
context = gtk_accessible_get_at_context (GTK_ACCESSIBLE (sl->object));
|
|
g_signal_handlers_disconnect_by_func (context, refresh_all, sl);
|
|
}
|
|
|
|
g_clear_object (&sl->object);
|
|
|
|
gtk_widget_dispose_template (GTK_WIDGET (o), GTK_TYPE_INSPECTOR_A11Y);
|
|
|
|
G_OBJECT_CLASS (gtk_inspector_a11y_parent_class)->dispose (o);
|
|
}
|
|
|
|
static void
|
|
gtk_inspector_a11y_class_init (GtkInspectorA11yClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
|
|
|
object_class->dispose = dispose;
|
|
|
|
gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/inspector/a11y.ui");
|
|
gtk_widget_class_bind_template_child (widget_class, GtkInspectorA11y, box);
|
|
gtk_widget_class_bind_template_child (widget_class, GtkInspectorA11y, role);
|
|
gtk_widget_class_bind_template_child (widget_class, GtkInspectorA11y, path);
|
|
gtk_widget_class_bind_template_child (widget_class, GtkInspectorA11y, attributes);
|
|
|
|
gtk_widget_class_bind_template_callback (widget_class, setup_cell_cb);
|
|
gtk_widget_class_bind_template_callback (widget_class, bind_name_cb);
|
|
gtk_widget_class_bind_template_callback (widget_class, bind_value_cb);
|
|
|
|
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
|
|
}
|
|
|
|
// vim: set et sw=2 ts=2:
|