diff options
| author | Colomban Wendling <[email protected]> | 2020-06-10 18:54:37 +0200 | 
|---|---|---|
| committer | Robert Antoni Buj Gelonch <[email protected]> | 2020-08-08 23:24:58 +0200 | 
| commit | 24a04f3b71f18e37b655a5e717540a3dc3bef598 (patch) | |
| tree | ef19fe4e2ef56947410c4fe9c19a490ddbfc03f6 | |
| parent | 372c9903f7c8518eae842077bb7006f75fc27dfe (diff) | |
| download | mate-settings-daemon-24a04f3b71f18e37b655a5e717540a3dc3bef598.tar.bz2 mate-settings-daemon-24a04f3b71f18e37b655a5e717540a3dc3bef598.tar.xz  | |
a11y-keyboard: Add support for ringing the bell when CapsLock is active
| -rw-r--r-- | configure.ac | 9 | ||||
| -rw-r--r-- | plugins/a11y-keyboard/Makefile.am | 4 | ||||
| -rw-r--r-- | plugins/a11y-keyboard/msd-a11y-keyboard-atspi.c | 208 | ||||
| -rw-r--r-- | plugins/a11y-keyboard/msd-a11y-keyboard-atspi.h | 55 | ||||
| -rw-r--r-- | plugins/a11y-keyboard/msd-a11y-keyboard-manager.c | 25 | ||||
| -rw-r--r-- | plugins/a11y-keyboard/msd-a11y-preferences-dialog.c | 50 | ||||
| -rw-r--r-- | plugins/a11y-keyboard/msd-a11y-preferences-dialog.ui | 16 | 
7 files changed, 366 insertions, 1 deletions
diff --git a/configure.ac b/configure.ac index c79a96b..c2e1cfd 100644 --- a/configure.ac +++ b/configure.ac @@ -59,6 +59,7 @@ GTK_REQUIRED_VERSION=3.22.0  MATE_DESKTOP_REQUIRED_VERSION=1.23.2  LIBMATEKBD_REQUIRED_VERSION=1.17.0  LIBNOTIFY_REQUIRED_VERSION=0.7.0 +LIBATSPI_REQUIRED_VERSION=2.0  LIBMATEMIXER_REQUIRED_VERSION=1.10.0  PKG_CHECK_MODULES(SETTINGS_DAEMON, @@ -116,6 +117,14 @@ AC_SUBST(LIBNOTIFY_CFLAGS)  AC_SUBST(LIBNOTIFY_LIBS)  dnl --------------------------------------------------------------------------- +dnl - Check for libatspi +dnl --------------------------------------------------------------------------- + +PKG_CHECK_MODULES(LIBATSPI, atspi-2 >= $LIBATSPI_REQUIRED_VERSION, +          [AC_DEFINE(HAVE_LIBATSPI, 1, [Define if libatspi is available]) +          have_libatspi=yes], have_libatspi=no) + +dnl ---------------------------------------------------------------------------  dnl - Check for D-Bus  dnl --------------------------------------------------------------------------- diff --git a/plugins/a11y-keyboard/Makefile.am b/plugins/a11y-keyboard/Makefile.am index 0881088..92072e2 100644 --- a/plugins/a11y-keyboard/Makefile.am +++ b/plugins/a11y-keyboard/Makefile.am @@ -42,6 +42,8 @@ liba11y_keyboard_la_SOURCES = 		\  	msd-a11y-keyboard-plugin.c	\  	msd-a11y-keyboard-manager.h	\  	msd-a11y-keyboard-manager.c	\ +	msd-a11y-keyboard-atspi.h	\ +	msd-a11y-keyboard-atspi.c	\  	msd-a11y-preferences-dialog.h	\  	msd-a11y-preferences-dialog.c	\  	$(NULL) @@ -55,6 +57,7 @@ liba11y_keyboard_la_CPPFLAGS = \  liba11y_keyboard_la_CFLAGS =		\  	$(SETTINGS_PLUGIN_CFLAGS)	\  	$(LIBNOTIFY_CFLAGS)		\ +	$(LIBATSPI_CFLAGS)		\  	$(AM_CFLAGS)			\  	$(WARN_CFLAGS)			\  	$(NULL) @@ -66,6 +69,7 @@ liba11y_keyboard_la_LDFLAGS = 		\  liba11y_keyboard_la_LIBADD  = 		\  	$(SETTINGS_PLUGIN_LIBS)		\  	$(LIBNOTIFY_LIBS)		\ +	$(LIBATSPI_LIBS)		\  	$(NULL)  plugin_in_files = 		\ diff --git a/plugins/a11y-keyboard/msd-a11y-keyboard-atspi.c b/plugins/a11y-keyboard/msd-a11y-keyboard-atspi.c new file mode 100644 index 0000000..c6cafed --- /dev/null +++ b/plugins/a11y-keyboard/msd-a11y-keyboard-atspi.c @@ -0,0 +1,208 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2020 Colomban Wendling <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include "msd-a11y-keyboard-atspi.h" + +#include <glib-object.h> + +#ifdef HAVE_LIBATSPI +#include <gdk/gdk.h> +#include <atspi/atspi.h> + + +/* ugly workaround https://gitlab.gnome.org/GNOME/at-spi2-core/-/issues/22 */ +static AtspiDeviceListener * +WORKAROUND_atspi_device_listener_new (AtspiDeviceListenerCB callback, +                                      void                 *user_data, +                                      GDestroyNotify        callback_destroyed) +{ +        AtspiDeviceListener *listener; + +        listener = atspi_device_listener_new (callback, user_data, +                                              callback_destroyed); + +        /* Yes, we do leak a reference.  But we have to, because there's a +         * nasty bug in libatspi [1] where if a listener is destroyed it leads +         * to invalid memory access and potentially a crash. +         * [1] https://gitlab.gnome.org/GNOME/at-spi2-core/-/issues/22 */ +        return g_object_ref (listener); +} +#define atspi_device_listener_new WORKAROUND_atspi_device_listener_new + + +struct MsdA11yKeyboardAtspiPrivate +{ +        AtspiDeviceListener *listener; +        gboolean             listening; +}; + +G_DEFINE_TYPE_WITH_PRIVATE (MsdA11yKeyboardAtspi, msd_a11y_keyboard_atspi, G_TYPE_OBJECT) + +static void +msd_a11y_keyboard_atspi_finalize (GObject *obj) +{ +        MsdA11yKeyboardAtspi *self = MSD_A11Y_KEYBOARD_ATSPI (obj); + +        g_clear_object (&self->priv->listener); +        self->priv->listening = FALSE; + +        G_OBJECT_CLASS (msd_a11y_keyboard_atspi_parent_class)->finalize (obj); +} + +static void +msd_a11y_keyboard_atspi_class_init (MsdA11yKeyboardAtspiClass *klass) +{ +        GObjectClass *object_class = G_OBJECT_CLASS (klass); + +        object_class->finalize = msd_a11y_keyboard_atspi_finalize; +} + +static gboolean +on_key_press_event (const AtspiDeviceEvent *event, +                    void                   *user_data G_GNUC_UNUSED) +{ +        /* don't ring when disabling capslock */ +        if (event->id == GDK_KEY_Caps_Lock && +            event->modifiers & (1 << ATSPI_MODIFIER_SHIFTLOCK)) +                return FALSE; + +        gdk_display_beep (gdk_display_get_default ()); + +        return FALSE; +} + +static void +msd_a11y_keyboard_atspi_init (MsdA11yKeyboardAtspi *self) +{ +        self->priv = msd_a11y_keyboard_atspi_get_instance_private (self); + +        /* init AT-SPI if needed */ +        atspi_init (); + +        /* Ideally we'd create the listener in start() only -- we don't need +         * it otherwise.  But there's a bug [1] in libatspi where destroying a +         * listener leads to a crash, so we have a workaround keeping the +         * listener alive, and thus try and avoid creating (and not destroying) +         * listener more often than absolutely necessary. +         * [1] https://gitlab.gnome.org/GNOME/at-spi2-core/-/issues/22 */ +        self->priv->listener = atspi_device_listener_new (on_key_press_event, +                                                          self, NULL); +        self->priv->listening = FALSE; +} + +static void +register_deregister_events (MsdA11yKeyboardAtspi *self, +                            gboolean              do_register) +{ +        AtspiKeyDefinition shiftlock_key; +        GArray *shiftlock_key_set; + +        g_return_if_fail (MSD_IS_A11Y_KEYBOARD_ATSPI (self)); +        g_return_if_fail (ATSPI_IS_DEVICE_LISTENER (self->priv->listener)); + +        /* register listeners for CAPS_LOCK with any modifier but CAPS_LOCK, +         * and all keys with CAPS_LOCK modifier, so we grab when CAPS_LOCK is +         * active or gets activated */ +        shiftlock_key.keycode = 0; +        shiftlock_key.keysym = GDK_KEY_Caps_Lock; +        shiftlock_key.keystring = NULL; + +        shiftlock_key_set = g_array_new (FALSE, FALSE, sizeof shiftlock_key); +        g_array_append_val (shiftlock_key_set, shiftlock_key); + +        for (AtspiKeyMaskType mod_mask = 0; mod_mask < 256; mod_mask++) +        { +                GArray *key_set; + +                if (mod_mask & (1 << ATSPI_MODIFIER_SHIFTLOCK)) +                        key_set = NULL; +                else +                        key_set = shiftlock_key_set; + +                if (do_register) +                        atspi_register_keystroke_listener (self->priv->listener, +                                                           key_set, +                                                           mod_mask, +                                                           1 << ATSPI_KEY_PRESSED_EVENT, +                                                           ATSPI_KEYLISTENER_NOSYNC, +                                                           NULL); +                else +                        atspi_deregister_keystroke_listener (self->priv->listener, +                                                             key_set, +                                                             mod_mask, +                                                             1 << ATSPI_KEY_PRESSED_EVENT, +                                                             NULL); +        } + +        g_array_unref (shiftlock_key_set); + +        self->priv->listening = do_register; +} + +void +msd_a11y_keyboard_atspi_start (MsdA11yKeyboardAtspi *self) +{ +        g_return_if_fail (MSD_IS_A11Y_KEYBOARD_ATSPI (self)); + +        if (! self->priv->listening) +                register_deregister_events (self, TRUE); +} + +void +msd_a11y_keyboard_atspi_stop (MsdA11yKeyboardAtspi *self) +{ +        g_return_if_fail (MSD_IS_A11Y_KEYBOARD_ATSPI (self)); + +        if (self->priv->listening) +                register_deregister_events (self, FALSE); +} + +#else /* ! defined(HAVE_LIBATSPI): AT-SPI is not available, provide stubs */ + +G_DEFINE_TYPE (MsdA11yKeyboardAtspi, msd_a11y_keyboard_atspi, G_TYPE_OBJECT) + +static void +msd_a11y_keyboard_atspi_class_init (MsdA11yKeyboardAtspiClass *klass G_GNUC_UNUSED) +{ +} + +static void +msd_a11y_keyboard_atspi_init (MsdA11yKeyboardAtspi *self G_GNUC_UNUSED) +{ +} + +void +msd_a11y_keyboard_atspi_start (MsdA11yKeyboardAtspi *self G_GNUC_UNUSED) +{ +} + +void +msd_a11y_keyboard_atspi_stop (MsdA11yKeyboardAtspi *self G_GNUC_UNUSED) +{ +} + +#endif /* ! defined(HAVE_LIBATSPI) */ + +MsdA11yKeyboardAtspi * +msd_a11y_keyboard_atspi_new () +{ +        return g_object_new (MSD_TYPE_A11Y_KEYBOARD_ATSPI, NULL); +} diff --git a/plugins/a11y-keyboard/msd-a11y-keyboard-atspi.h b/plugins/a11y-keyboard/msd-a11y-keyboard-atspi.h new file mode 100644 index 0000000..32fc0e7 --- /dev/null +++ b/plugins/a11y-keyboard/msd-a11y-keyboard-atspi.h @@ -0,0 +1,55 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2020 Colomban Wendling <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MSD_A11Y_KEYBOARD_ATSPI_H +#define __MSD_A11Y_KEYBOARD_ATSPI_H + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define MSD_TYPE_A11Y_KEYBOARD_ATSPI            (msd_a11y_keyboard_atspi_get_type ()) +#define MSD_A11Y_KEYBOARD_ATSPI(o)              (G_TYPE_CHECK_INSTANCE_CAST ((o), MSD_TYPE_A11Y_KEYBOARD_ATSPI, MsdA11yKeyboardAtspi)) +#define MSD_A11Y_KEYBOARD_ATSPI_CLASS(k)        (G_TYPE_CHECK_CLASS_CAST((k), MSD_TYPE_A11Y_KEYBOARD_ATSPI, MsdA11yKeyboardAtspiClass)) +#define MSD_IS_A11Y_KEYBOARD_ATSPI(o)           (G_TYPE_CHECK_INSTANCE_TYPE ((o), MSD_TYPE_A11Y_KEYBOARD_ATSPI)) +#define MSD_IS_A11Y_KEYBOARD_ATSPI_CLASS(k)     (G_TYPE_CHECK_CLASS_TYPE ((k), MSD_TYPE_A11Y_KEYBOARD_ATSPI)) +#define MSD_A11Y_KEYBOARD_ATSPI_GET_CLASS(o)    (G_TYPE_INSTANCE_GET_CLASS ((o), MSD_TYPE_A11Y_KEYBOARD_ATSPI, MsdA11yKeyboardAtspiClass)) + +typedef struct MsdA11yKeyboardAtspiPrivate MsdA11yKeyboardAtspiPrivate; + +typedef struct +{ +        GObject                         parent; +        MsdA11yKeyboardAtspiPrivate    *priv; +} MsdA11yKeyboardAtspi; + +typedef struct +{ +        GObjectClass   parent_class; +} MsdA11yKeyboardAtspiClass; + +GType                   msd_a11y_keyboard_atspi_get_type        (void); + +MsdA11yKeyboardAtspi   *msd_a11y_keyboard_atspi_new             (void); +void                    msd_a11y_keyboard_atspi_start           (MsdA11yKeyboardAtspi *self); +void                    msd_a11y_keyboard_atspi_stop            (MsdA11yKeyboardAtspi *self); + +G_END_DECLS + +#endif diff --git a/plugins/a11y-keyboard/msd-a11y-keyboard-manager.c b/plugins/a11y-keyboard/msd-a11y-keyboard-manager.c index 57ce388..67a690b 100644 --- a/plugins/a11y-keyboard/msd-a11y-keyboard-manager.c +++ b/plugins/a11y-keyboard/msd-a11y-keyboard-manager.c @@ -49,9 +49,11 @@  #include "mate-settings-profile.h"  #include "msd-a11y-keyboard-manager.h" +#include "msd-a11y-keyboard-atspi.h"  #include "msd-a11y-preferences-dialog.h"  #define CONFIG_SCHEMA "org.mate.accessibility-keyboard" +#define KEY_CAPSLOCK_BEEP_ENABLED "capslock-beep-enable"  #define NOTIFICATION_TIMEOUT 30  struct MsdA11yKeyboardManagerPrivate @@ -64,6 +66,7 @@ struct MsdA11yKeyboardManagerPrivate          GtkWidget *preferences_dialog;          GtkStatusIcon *status_icon;          XkbDescRec *original_xkb_desc; +        MsdA11yKeyboardAtspi *capslock_beep;          GSettings  *settings; @@ -984,6 +987,17 @@ keyboard_callback (GSettings              *settings,          maybe_show_status_icon (manager);  } +static void +capslock_beep_callback (GSettings              *settings, +                        gchar                  *key G_GNUC_UNUSED, +                        MsdA11yKeyboardManager *manager) +{ +        if (g_settings_get_boolean (settings, KEY_CAPSLOCK_BEEP_ENABLED)) +                msd_a11y_keyboard_atspi_start (manager->priv->capslock_beep); +        else +                msd_a11y_keyboard_atspi_stop (manager->priv->capslock_beep); +} +  static gboolean  start_a11y_keyboard_idle_cb (MsdA11yKeyboardManager *manager)  { @@ -992,10 +1006,17 @@ start_a11y_keyboard_idle_cb (MsdA11yKeyboardManager *manager)          g_debug ("Starting a11y_keyboard manager");          mate_settings_profile_start (NULL); +        manager->priv->settings = g_settings_new (CONFIG_SCHEMA); + +        manager->priv->capslock_beep = msd_a11y_keyboard_atspi_new (); +        if (g_settings_get_boolean (manager->priv->settings, KEY_CAPSLOCK_BEEP_ENABLED)) +                msd_a11y_keyboard_atspi_start (manager->priv->capslock_beep); +        g_signal_connect (manager->priv->settings, "changed::"KEY_CAPSLOCK_BEEP_ENABLED, +                          G_CALLBACK (capslock_beep_callback), manager); +          if (!xkb_enabled (manager))                  goto out; -        manager->priv->settings = g_settings_new (CONFIG_SCHEMA);          g_signal_connect (manager->priv->settings, "changed", G_CALLBACK (keyboard_callback), manager);          set_devicepresence_handler (manager); @@ -1104,6 +1125,8 @@ msd_a11y_keyboard_manager_stop (MsdA11yKeyboardManager *manager)          p->slowkeys_shortcut_val = FALSE;          p->stickykeys_shortcut_val = FALSE; + +        g_clear_object (&p->capslock_beep);  }  static void diff --git a/plugins/a11y-keyboard/msd-a11y-preferences-dialog.c b/plugins/a11y-keyboard/msd-a11y-preferences-dialog.c index fa6d426..69cf418 100644 --- a/plugins/a11y-keyboard/msd-a11y-preferences-dialog.c +++ b/plugins/a11y-keyboard/msd-a11y-preferences-dialog.c @@ -49,6 +49,7 @@  #define KEY_BOUNCE_KEYS_ENABLED      "bouncekeys-enable"  #define KEY_SLOW_KEYS_ENABLED        "slowkeys-enable"  #define KEY_MOUSE_KEYS_ENABLED       "mousekeys-enable" +#define KEY_CAPSLOCK_BEEP_ENABLED    "capslock-beep-enable"  #define KEY_AT_SCHEMA                   "org.mate.applications-at"  #define KEY_AT_SCREEN_KEYBOARD_ENABLED  "screen-keyboard-enabled" @@ -91,6 +92,7 @@ struct MsdA11yPreferencesDialogPrivate          GtkWidget *sticky_keys_checkbutton;          GtkWidget *slow_keys_checkbutton;          GtkWidget *bounce_keys_checkbutton; +        GtkWidget *capslock_beep_checkbutton;          GtkWidget *large_print_checkbutton;          GtkWidget *high_contrast_checkbutton; @@ -323,6 +325,18 @@ config_set_slow_keys (MsdA11yPreferencesDialog *dialog, gboolean enabled)  }  static gboolean +config_get_capslock_beep (MsdA11yPreferencesDialog *dialog, gboolean *is_writable) +{ +        return config_get_bool (dialog->priv->settings_a11y, KEY_CAPSLOCK_BEEP_ENABLED, is_writable); +} + +static void +config_set_capslock_beep (MsdA11yPreferencesDialog *dialog, gboolean enabled) +{ +        g_settings_set_boolean (dialog->priv->settings_a11y, KEY_CAPSLOCK_BEEP_ENABLED, enabled); +} + +static gboolean  config_have_at_gsettings_condition (const char *condition)  {          DBusGProxy      *sm_proxy; @@ -422,6 +436,13 @@ on_slow_keys_checkbutton_toggled (GtkToggleButton          *button,  }  static void +on_capslock_beep_checkbutton_toggled (GtkToggleButton          *button, +                                      MsdA11yPreferencesDialog *dialog) +{ +        config_set_capslock_beep (dialog, gtk_toggle_button_get_active (button)); +} + +static void  on_high_contrast_checkbutton_toggled (GtkToggleButton          *button,                                        MsdA11yPreferencesDialog *dialog)  { @@ -493,6 +514,18 @@ ui_set_slow_keys (MsdA11yPreferencesDialog *dialog,  }  static void +ui_set_capslock_beep (MsdA11yPreferencesDialog *dialog, +                      gboolean                  enabled) +{ +        gboolean active; + +        active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->priv->capslock_beep_checkbutton)); +        if (active != enabled) { +                gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->priv->capslock_beep_checkbutton), enabled); +        } +} + +static void  ui_set_high_contrast (MsdA11yPreferencesDialog *dialog,                        gboolean                  enabled)  { @@ -569,6 +602,10 @@ key_changed_cb (GSettings                *settings,                  gboolean enabled;                  enabled = g_settings_get_boolean (settings, key);                  ui_set_slow_keys (dialog, enabled); +        } else if (g_strcmp0 (key, KEY_CAPSLOCK_BEEP_ENABLED) == 0) { +                gboolean enabled; +                enabled = g_settings_get_boolean (settings, key); +                ui_set_capslock_beep (dialog, enabled);          } else if (g_strcmp0 (key, KEY_AT_SCREEN_READER_ENABLED) == 0) {                  gboolean enabled;                  enabled = g_settings_get_boolean (settings, key); @@ -634,6 +671,19 @@ setup_dialog (MsdA11yPreferencesDialog *dialog,          }          widget = GTK_WIDGET (gtk_builder_get_object (builder, +                                                     "capslock_beep_checkbutton")); +        dialog->priv->capslock_beep_checkbutton = widget; +        g_signal_connect (widget, +                          "toggled", +                          G_CALLBACK (on_capslock_beep_checkbutton_toggled), +                          dialog); +        enabled = config_get_capslock_beep (dialog, &is_writable); +        ui_set_capslock_beep (dialog, enabled); +        if (! is_writable) { +                gtk_widget_set_sensitive (widget, FALSE); +        } + +        widget = GTK_WIDGET (gtk_builder_get_object (builder,                                                       "high_contrast_checkbutton"));          dialog->priv->high_contrast_checkbutton = widget;          g_signal_connect (widget, diff --git a/plugins/a11y-keyboard/msd-a11y-preferences-dialog.ui b/plugins/a11y-keyboard/msd-a11y-preferences-dialog.ui index 29b8015..5ed30ff 100644 --- a/plugins/a11y-keyboard/msd-a11y-preferences-dialog.ui +++ b/plugins/a11y-keyboard/msd-a11y-preferences-dialog.ui @@ -209,6 +209,22 @@                      <property name="position">7</property>                    </packing>                  </child> +                <child> +                  <object class="GtkCheckButton" id="capslock_beep_checkbutton"> +                    <property name="label" translatable="yes">_Beep when pressing a key while CapsLock is active</property> +                    <property name="visible">True</property> +                    <property name="can_focus">True</property> +                    <property name="receives_default">False</property> +                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> +                    <property name="use_underline">True</property> +                    <property name="draw_indicator">True</property> +                  </object> +                  <packing> +                    <property name="expand">True</property> +                    <property name="fill">True</property> +                    <property name="position">8</property> +                  </packing> +                </child>                </object>                <packing>                  <property name="expand">False</property>  | 
