[banshee/a11y: 9/27] [a11y] Basic ListView accessibility.



commit a2cc549c122e7039e1d30a5579bf4fae77b7bcf4
Author: Eitan Isaacson <eitan ascender com>
Date:   Wed Sep 30 15:48:35 2009 -0700

    [a11y] Basic ListView accessibility.

 .../Accessibility/ColumnCellAccessible.cs          |  113 +++++++++
 .../Accessibility/ICellAccessibleParent.cs         |   14 +
 .../Accessibility/ListViewAccessible.cs            |  187 ++++++++++++++
 .../Accessibility/ListViewAccessible_Selection.cs  |   60 +++++
 .../Accessibility/ListViewAccessible_Table.cs      |  171 +++++++++++++
 .../Hyena.Gui/Hyena.Data.Gui/ColumnCell.cs         |   14 +-
 .../Hyena.Data.Gui/ListView/ListView_Accessible.cs |  150 +++++++++++
 .../Hyena.Data.Gui/ListView/ListView_Header.cs     |    6 +
 .../Hyena.Data.Gui/ListView/ListView_Model.cs      |   10 +
 src/Libraries/Hyena.Gui/Hyena.Gui.csproj           |   12 +-
 .../Hyena.Gui/Hyena.Gui/BaseWidgetAccessible.cs    |  260 ++++++++++++++++++++
 src/Libraries/Hyena.Gui/Makefile.am                |    7 +
 src/Libraries/Hyena/Hyena.Collections/Selection.cs |    2 +-
 13 files changed, 1003 insertions(+), 3 deletions(-)
---
diff --git a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/Accessibility/ColumnCellAccessible.cs b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/Accessibility/ColumnCellAccessible.cs
new file mode 100644
index 0000000..f376eb9
--- /dev/null
+++ b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/Accessibility/ColumnCellAccessible.cs
@@ -0,0 +1,113 @@
+
+using System;
+
+namespace Hyena.Data.Gui.Accessibility
+{
+    public class ColumnCellAccessible: Atk.Object, Atk.ComponentImplementor
+    {
+        protected ColumnCell cell;
+        protected object bound_object;
+
+        public ColumnCellAccessible (object bound_object, ColumnCell cell, ICellAccessibleParent parent)
+        {
+            Role = Atk.Role.TableCell;
+            this.bound_object = bound_object;
+            this.cell = cell;
+            Parent = (Atk.Object) parent;
+        }
+
+        protected override Atk.StateSet OnRefStateSet ()
+        {
+            Atk.StateSet states = base.OnRefStateSet ();
+            states.AddState (Atk.StateType.Transient);
+            states.AddState (Atk.StateType.Focusable);
+            states.AddState (Atk.StateType.Enabled);
+            states.AddState (Atk.StateType.Sensitive);
+            states.AddState (Atk.StateType.Visible);
+
+            if (((ICellAccessibleParent)Parent).IsCellShowing (this))
+                states.AddState (Atk.StateType.Showing);
+
+            if (((ICellAccessibleParent)Parent).IsCellFocused (this))
+                states.AddState (Atk.StateType.Focused);
+
+            if (((ICellAccessibleParent)Parent).IsCellSelected (this))
+                states.AddState (Atk.StateType.Selected);
+
+            return states;
+        }
+
+        protected override int OnGetIndexInParent()
+        {
+            return ((ICellAccessibleParent)Parent).GetCellIndex (this);
+        }
+
+        public double Alpha {
+            get { return 1.0; }
+        }
+
+        public bool SetSize (int w, int h)
+        {
+            return false;
+        }
+
+        public bool SetPosition (int x, int y, Atk.CoordType coordType)
+        {
+            return false;
+        }
+
+        public bool SetExtents (int x, int y, int w, int h, Atk.CoordType coordType)
+        {
+            return false;
+        }
+
+        public void RemoveFocusHandler (uint handlerId)
+        {
+        }
+
+        public bool GrabFocus ()
+        {
+            return false;
+        }
+
+        public void GetSize (out int w, out int h)
+        {
+            Gdk.Rectangle rectangle = ((ICellAccessibleParent)Parent).GetCellExtents(this, Atk.CoordType.Screen);
+            w = rectangle.Width;
+            h = rectangle.Height;
+        }
+
+        public void GetPosition (out int x, out int y, Atk.CoordType coordType)
+        {
+            Gdk.Rectangle rectangle = ((ICellAccessibleParent)Parent).GetCellExtents(this, coordType);
+
+            x = rectangle.X;
+            y = rectangle.Y;
+        }
+
+        public void GetExtents (out int x, out int y, out int w, out int h, Atk.CoordType coordType)
+        {
+            Gdk.Rectangle rectangle = ((ICellAccessibleParent)Parent).GetCellExtents(this, coordType);
+
+            x = rectangle.X;
+            y = rectangle.Y;
+            w = rectangle.Width;
+            h = rectangle.Height;
+        }
+
+        public virtual Atk.Object RefAccessibleAtPoint (int x, int y, Atk.CoordType coordType)
+        {
+            return null;
+        }
+
+        public bool Contains (int x, int y, Atk.CoordType coordType)
+        {
+            return false;
+        }
+
+        public uint AddFocusHandler (Atk.FocusHandler handler)
+        {
+            return 0;
+        }
+    }
+}
diff --git a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/Accessibility/ICellAccessibleParent.cs b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/Accessibility/ICellAccessibleParent.cs
new file mode 100644
index 0000000..132066b
--- /dev/null
+++ b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/Accessibility/ICellAccessibleParent.cs
@@ -0,0 +1,14 @@
+
+using System;
+
+namespace Hyena.Data.Gui.Accessibility
+{
+    public interface ICellAccessibleParent
+    {
+        Gdk.Rectangle GetCellExtents (ColumnCellAccessible cell, Atk.CoordType coord_type);
+        int GetCellIndex (ColumnCellAccessible cell);
+        bool IsCellShowing (ColumnCellAccessible cell);
+        bool IsCellFocused (ColumnCellAccessible cell);
+        bool IsCellSelected (ColumnCellAccessible cell);
+    }
+}
diff --git a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/Accessibility/ListViewAccessible.cs b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/Accessibility/ListViewAccessible.cs
new file mode 100644
index 0000000..3264139
--- /dev/null
+++ b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/Accessibility/ListViewAccessible.cs
@@ -0,0 +1,187 @@
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+
+using Hyena.Data.Gui;
+
+namespace Hyena.Data.Gui.Accessibility
+{
+    public partial class ListViewAccessible<T> : Hyena.Gui.BaseWidgetAccessible,
+             ICellAccessibleParent
+    {
+        private ListView<T> list_view;
+        private Dictionary<int, ColumnCellAccessible> cell_cache;
+
+        public ListViewAccessible (GLib.Object widget): base (widget as Gtk.Widget)
+        {
+            list_view = widget as ListView<T>;
+            // TODO replace with list_view.Name?
+            Name = "ListView";
+            Description = "ListView";
+            Role = Atk.Role.Table;
+            Parent = list_view.Parent.RefAccessible();
+
+            cell_cache = new Dictionary<int, ColumnCellAccessible>();
+
+            list_view.ModelChanged += (o, a) => OnModelChanged ();
+            list_view.Model.Reloaded += (o, a) => OnModelChanged ();
+            OnModelChanged ();
+
+            list_view.Selection.FocusRowChanged += OnSelectionFocusChanged;
+            list_view.Selection.FocusColumnChanged += OnSelectionFocusChanged;
+
+            ListViewAccessible_Selection ();
+            ListViewAccessible_Table ();
+        }
+
+        protected override Atk.StateSet OnRefStateSet ()
+        {
+            Atk.StateSet states = base.OnRefStateSet ();
+            states.AddState (Atk.StateType.ManagesDescendants);
+
+            return states;
+        }
+
+
+        protected override int OnGetIndexInParent()
+        {
+            for (int i=0; i < Parent.NAccessibleChildren; i++)
+                if (Parent.RefAccessibleChild (i) == this)
+                    return i;
+
+            return -1;
+        }
+
+        protected override int OnGetNChildren()
+        {
+            return n_columns * n_rows + n_columns;
+        }
+
+        protected override Atk.Object OnRefChild(int index)
+        {
+            ColumnCellAccessible child;
+
+            if (cell_cache.ContainsKey(index))
+            {
+                return cell_cache[index];
+            }
+            if (0 > index - n_columns)
+            {
+                child = (ColumnCellAccessible) list_view.ColumnController.Where (c => c.Visible).ElementAtOrDefault (index).HeaderCell.GetAccessible (this);
+            }
+            else
+            {
+                int column = (index - n_columns)%n_columns;
+                int row = (index - n_columns)/n_columns;
+                var cell = list_view.ColumnController.Where (c => c.Visible).ElementAtOrDefault (column).GetCell (0);
+                cell.BindListItem (list_view.Model[row]);
+                child = (ColumnCellAccessible) cell.GetAccessible (this);
+            }
+
+            cell_cache.Add(index, child);
+
+            return child;
+        }
+
+        public override Atk.Object RefAccessibleAtPoint (int x, int y, Atk.CoordType coordType)
+        {
+            int row, col;
+            list_view.GetCellAtPoint(x, y, coordType, out row, out col);
+            return RefAt (row, col);
+        }
+
+        private void OnModelChanged ()
+        {
+            GLib.Signal.Emit (this, "model_changed");
+            cell_cache.Clear();
+            /*var handler = ModelChanged;
+            if (handler != null) {
+                handler (this, EventArgs.Empty);
+            }*/
+        }
+
+        private void OnSelectionFocusChanged (object o, EventArgs a)
+        {
+            Atk.Object cell;
+
+            if (list_view.HeaderFocused)
+                cell = OnRefChild (list_view.Selection.FocusedColumnIndex);
+            else
+                cell = RefAt (list_view.Selection.FocusedRowIndex,
+                    list_view.Selection.FocusedColumnIndex);
+
+            GLib.Signal.Emit (this, "active-descendant-changed", cell.Handle);
+        }
+
+        private int n_columns {
+            get { return list_view.ColumnController.Count (c => c.Visible); }
+            set {}
+        }
+
+        private int n_rows {
+            get { return list_view.Model.Count; }
+            set {}
+        }
+
+        # region ICellAccessibleParent
+
+        public int GetCellIndex (ColumnCellAccessible cell)
+        {
+            foreach (KeyValuePair<int, ColumnCellAccessible> kv in cell_cache)
+            {
+                if ((ColumnCellAccessible)kv.Value == cell)
+                    return (int)kv.Key;
+            }
+
+            return -1;
+        }
+
+        public Gdk.Rectangle GetCellExtents (ColumnCellAccessible cell, Atk.CoordType coord_type)
+        {
+            int cache_index = GetCellIndex (cell);
+            int minval = int.MinValue;
+            if (cache_index == -1)
+                return new Gdk.Rectangle(minval, minval, minval, minval);
+
+            if (cache_index - n_columns >= 0)
+            {
+                int column = (cache_index - NColumns)%NColumns;
+                int row = (cache_index - NColumns)/NColumns;
+                return list_view.GetColumnCellExtents (row, column, true, coord_type);
+            } else
+            {
+                return list_view.GetColumnHeaderCellExtents (cache_index, true, coord_type);
+            }
+        }
+
+        public bool IsCellShowing (ColumnCellAccessible cell)
+        {
+            Gdk.Rectangle cell_extents = GetCellExtents (cell, Atk.CoordType.Window);
+
+            if (cell_extents.X == int.MinValue && cell_extents.Y == int.MinValue)
+                return false;
+
+            return true;
+        }
+
+        public bool IsCellFocused (ColumnCellAccessible cell)
+        {
+            int cell_index = GetCellIndex (cell);
+            if (cell_index%NColumns != 0)
+                return false; // Only 0 column cells get focus now.
+
+            int row = cell_index/NColumns;
+
+            return row == list_view.Selection.FocusedRowIndex;
+        }
+
+        public bool IsCellSelected (ColumnCellAccessible cell)
+        {
+            int cell_index = GetCellIndex (cell);
+            return IsChildSelected (cell_index);
+        }
+
+        # endregion ICellAccessibleParent
+    }
+}
diff --git a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/Accessibility/ListViewAccessible_Selection.cs b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/Accessibility/ListViewAccessible_Selection.cs
new file mode 100644
index 0000000..392155a
--- /dev/null
+++ b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/Accessibility/ListViewAccessible_Selection.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Linq;
+using System.Collections.Generic;
+
+using Hyena.Data.Gui;
+
+namespace Hyena.Data.Gui.Accessibility
+{
+    public partial class ListViewAccessible<T> : Atk.SelectionImplementor
+    {
+        public void ListViewAccessible_Selection ()
+        {
+            list_view.SelectionProxy.Changed += OnSelectionChanged;
+        }
+
+        public bool AddSelection (int index)
+        {
+            return AddRowSelection (GetRowAtIndex (index));
+        }
+
+        public bool ClearSelection ()
+        {
+            list_view.Selection.Clear ();
+            return true;
+        }
+
+        public bool IsChildSelected (int index)
+        {
+            return IsRowSelected (GetRowAtIndex (index));
+        }
+
+        public bool RemoveSelection (int index)
+        {
+            int row = list_view.Selection.RangeCollection [index/n_columns];
+            return RemoveRowSelection (row);
+        }
+
+        public Atk.Object RefSelection (int index)
+        {
+            int row = list_view.Selection.RangeCollection [index/n_columns];
+            int column = index%n_columns;
+            return RefAt (row, column);
+        }
+
+        public int SelectionCount {
+            get { return list_view.Selection.Count * n_columns; }
+        }
+
+        public bool SelectAllSelection ()
+        {
+            list_view.Selection.SelectAll ();
+            return true;
+        }
+
+        private void OnSelectionChanged (object o, EventArgs a)
+        {
+            GLib.Signal.Emit (this, "selection_changed");
+        }
+    }
+}
diff --git a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/Accessibility/ListViewAccessible_Table.cs b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/Accessibility/ListViewAccessible_Table.cs
new file mode 100644
index 0000000..129c721
--- /dev/null
+++ b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/Accessibility/ListViewAccessible_Table.cs
@@ -0,0 +1,171 @@
+using System;
+using System.Linq;
+using System.Collections.Generic;
+
+using Hyena.Data.Gui;
+
+namespace Hyena.Data.Gui.Accessibility
+{
+    public partial class ListViewAccessible<T> : Atk.TableImplementor
+    {
+        public void ListViewAccessible_Table ()
+        {
+        }
+
+        public Atk.Object Caption {
+            get { return new Atk.NoOpObject (list_view); }
+            set {}
+        }
+
+        public int NColumns {
+            get { return n_columns; }
+            set {}
+        }
+
+        public int NRows {
+            get { return n_rows; }
+            set {}
+        }
+
+        public Atk.Object Summary {
+            get { return new Atk.NoOpObject (list_view); }
+            set {}
+        }
+
+        public bool AddColumnSelection (int column)
+        {
+            return false;
+        }
+
+        public bool AddRowSelection (int row)
+        {
+            list_view.Selection.Select (row);
+            return true;
+        }
+
+        public int GetColumnAtIndex (int index)
+        {
+            if (NColumns == 0)
+                return -1;
+            return (index-NColumns)%NColumns;
+        }
+
+        public string GetColumnDescription (int column)
+        {
+            var col = list_view.ColumnController.Where (c => c.Visible).ElementAtOrDefault (column);
+            return col == null ? null : col.LongTitle;
+        }
+
+        public int GetColumnExtentAt (int row, int column)
+        {
+            return 1;
+        }
+
+        public Atk.Object GetColumnHeader (int column)
+        {
+            if (column >= NColumns)
+                return new Atk.NoOpObject (list_view);
+            else
+                return OnRefChild (column);
+        }
+
+        public int GetIndexAt (int row, int column)
+        {
+            return row * NColumns + column + NColumns;
+        }
+
+        public int GetRowAtIndex (int index)
+        {
+            if (NColumns == 0)
+                return -1;
+            return (index-NColumns)/NColumns;
+        }
+
+        public string GetRowDescription (int row)
+        {
+            return "";
+        }
+
+        public int GetRowExtentAt (int row, int column)
+        {
+            return 1;
+        }
+
+        public Atk.Object GetRowHeader (int row)
+        {
+            return new Atk.NoOpObject (list_view);
+        }
+
+        private static readonly int [] empty_int_array = new int[0];
+        public int [] SelectedColumns {
+            get { return empty_int_array; }
+        }
+
+        public int [] SelectedRows {
+            get { return list_view.Selection.ToArray (); }
+        }
+
+        public bool IsColumnSelected (int column)
+        {
+            return false;
+        }
+
+        public bool IsRowSelected (int row)
+        {
+            return list_view.Selection.Contains (row);
+        }
+
+        public bool IsSelected (int row, int column)
+        {
+            return list_view.Selection.Contains (row);
+        }
+
+        public Atk.Object RefAt (int row, int column)
+        {
+            int index = NColumns*row + column + NColumns;
+            return OnRefChild (index);
+        }
+
+        public bool RemoveColumnSelection (int column)
+        {
+            return false;
+        }
+
+        public bool RemoveRowSelection (int row)
+        {
+            list_view.Selection.Unselect (row);
+            return true;
+        }
+
+        public void SetColumnDescription (int column, string description)
+        {
+        }
+
+        public void SetColumnHeader (int column, Atk.Object header)
+        {
+        }
+
+        public void SetRowDescription (int row, string description)
+        {
+        }
+
+        public void SetRowHeader (int row, Atk.Object header)
+        {
+        }
+
+        #pragma warning disable 0067
+
+        /*
+        public event Atk.ColumnDeletedHandler ColumnDeleted;
+        public event Atk.ColumnInsertedHandler ColumnInserted;
+        public event EventHandler ColumnReordered;
+        public event EventHandler ModelChanged;
+        public event Atk.RowDeletedHandler RowDeleted;
+        public event Atk.RowInsertedHandler RowInserted;
+        public event EventHandler RowReordered;
+        */
+
+        #pragma warning restore 0067
+
+    }
+}
\ No newline at end of file
diff --git a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ColumnCell.cs b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ColumnCell.cs
index 7812d1a..05cfb2a 100644
--- a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ColumnCell.cs
+++ b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ColumnCell.cs
@@ -31,6 +31,8 @@ using System.Reflection;
 using Gtk;
 using Cairo;
 
+using Hyena.Data.Gui.Accessibility;
+
 namespace Hyena.Data.Gui
 {
     public abstract class ColumnCell
@@ -40,7 +42,17 @@ namespace Hyena.Data.Gui
         private PropertyInfo property_info, sub_property_info;
         private object bound_object;
         private object bound_object_parent;
-            
+
+        public virtual Atk.Object GetAccessible (ICellAccessibleParent parent)
+        {
+            return new ColumnCellAccessible (BoundObject, this, parent);
+        }
+
+        public virtual string GetTextAlternative (object obj)
+        {
+            return string.Empty;
+        }
+
         public ColumnCell (string property, bool expand)
         {
             Property = property;
diff --git a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Accessible.cs b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Accessible.cs
new file mode 100644
index 0000000..e8f93c1
--- /dev/null
+++ b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Accessible.cs
@@ -0,0 +1,150 @@
+//
+// ListView_Accessible.cs
+//
+// Author:
+//   Gabriel Burt <gburt novell com>
+//
+// Copyright (C) 2009 Novell, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+
+using Gtk;
+
+using Hyena.Data.Gui.Accessibility;
+
+namespace Hyena.Data.Gui
+{
+    public partial class ListView<T> : ListViewBase
+    {
+        static ListView ()
+        {
+            ListViewAccessibleFactory<T>.Init ();
+        }
+
+        public Gdk.Rectangle GetColumnCellExtents (int row, int column)
+        {
+            return GetColumnCellExtents (row, column, true);
+        }
+
+        public Gdk.Rectangle GetColumnCellExtents (int row, int column, bool clip) {
+            return GetColumnCellExtents (row, column, clip, Atk.CoordType.Window);
+        }
+
+        public Gdk.Rectangle GetColumnCellExtents (int row, int column, bool clip, Atk.CoordType coord_type)
+        {
+            int width = GetColumnWidth (column);
+            int height = RowHeight;
+
+            int y = (int)GetYAtRow (row) - VadjustmentValue + ListAllocation.Y;
+
+            int x = ListAllocation.X - HadjustmentValue;
+            for (int index=0;index<column;index++)
+                x += GetColumnWidth(index);
+
+            Gdk.Rectangle rectangle = new Gdk.Rectangle(x, y, width, height);
+
+            if (clip && !ListAllocation.Contains (rectangle))
+                return new Gdk.Rectangle(int.MinValue, int.MinValue, int.MinValue, int.MinValue);
+
+            if (coord_type == Atk.CoordType.Window)
+                return rectangle;
+
+            int origin_x, origin_y;
+            GdkWindow.GetPosition (out origin_x, out origin_y);
+
+            rectangle.X += origin_x;
+            rectangle.Y += origin_y;
+
+            return rectangle;
+        }
+
+        public Gdk.Rectangle GetColumnHeaderCellExtents (int column, bool clip, Atk.CoordType coord_type)
+        {
+            if (!HeaderVisible)
+                return new Gdk.Rectangle(int.MinValue, int.MinValue,
+                                         int.MinValue, int.MinValue);
+            int width = GetColumnWidth (column);
+            int height = HeaderHeight;
+
+            int x = header_rendering_alloc.X - HadjustmentValue + Theme.BorderWidth;
+            if (column != 0)
+                x += Theme.InnerBorderWidth;
+            for (int index=0;index<column;index++)
+                x += GetColumnWidth(index);
+
+            int y = Theme.BorderWidth + header_rendering_alloc.Y;
+
+            Gdk.Rectangle rectangle = new Gdk.Rectangle(x, y, width, height);
+
+            if (coord_type == Atk.CoordType.Window)
+                return rectangle;
+
+            int origin_x, origin_y;
+            GdkWindow.GetPosition (out origin_x, out origin_y);
+
+            rectangle.X += origin_x;
+            rectangle.Y += origin_y;
+
+            return rectangle;
+        }
+
+        public void GetCellAtPoint (int x, int y, Atk.CoordType coord_type, out int row, out int col)
+        {
+            int origin_x = 0;
+            int origin_y = 0;
+            if (coord_type == Atk.CoordType.Screen)
+                GdkWindow.GetPosition (out origin_x, out origin_y);
+
+            x = x - ListAllocation.X - origin_x;
+            y = y - ListAllocation.Y - origin_y;
+
+            Column column = GetColumnAt (x);
+
+            CachedColumn cached_column = GetCachedColumnForColumn (column);
+
+            row = GetRowAtY (y);
+            col = cached_column.Index;
+        }
+    }
+
+    internal class ListViewAccessibleFactory<T> : Atk.ObjectFactory
+    {
+        public static void Init ()
+        {
+            new ListViewAccessibleFactory<T> ();
+            Atk.Global.DefaultRegistry.SetFactoryType ((GLib.GType)typeof(ListView<T>), (GLib.GType)typeof (ListViewAccessibleFactory<T>));
+        }
+
+        protected override Atk.Object OnCreateAccessible (GLib.Object obj)
+        {
+            return new ListViewAccessible<T> (obj);
+        }
+
+        protected override GLib.GType OnGetAccessibleType ()
+        {
+            return ListViewAccessible<T>.GType;
+        }
+    }
+}
diff --git a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Header.cs b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Header.cs
index a7e08c4..819585b 100644
--- a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Header.cs
+++ b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Header.cs
@@ -414,6 +414,12 @@ namespace Hyena.Data.Gui
             return null;
         }
 
+        protected int GetColumnWidth (int column_index)
+        {
+            CachedColumn cached_column = column_cache[column_index];
+            return cached_column.Width;
+        }
+
         private bool CanResizeColumn (int column_index)
         {
             // At least one column to the left (including the one being resized) should be resizable.
diff --git a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Model.cs b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Model.cs
index 891d557..e91accd 100644
--- a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Model.cs
+++ b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Model.cs
@@ -35,6 +35,10 @@ namespace Hyena.Data.Gui
 {
     public partial class ListView<T> : ListViewBase
     {
+        #pragma warning disable 0067
+        public event EventHandler ModelChanged;
+        #pragma warning restore 0067
+
         public void SetModel (IListModel<T> model)
         {
             SetModel (model, 0.0);
@@ -73,8 +77,14 @@ namespace Hyena.Data.Gui
             }
             
             RefreshViewForModel (vpos);
+
+            var handler = ModelChanged;
+            if (handler != null) {
+                handler (this, EventArgs.Empty);
+            }
         }
 
+
         private void RefreshViewForModel (double? vpos)
         {
             if (Model == null) {
diff --git a/src/Libraries/Hyena.Gui/Hyena.Gui.csproj b/src/Libraries/Hyena.Gui/Hyena.Gui.csproj
index 64e70de..9621dc4 100644
--- a/src/Libraries/Hyena.Gui/Hyena.Gui.csproj
+++ b/src/Libraries/Hyena.Gui/Hyena.Gui.csproj
@@ -149,6 +149,13 @@
     <Compile Include="Hyena.Gui.Theatrics\Pulsator.cs" />
     <Compile Include="Hyena.Gui\PixbufImageSurface.cs" />
     <Compile Include="Hyena.Widgets\TextViewEditable.cs" />
+    <Compile Include="Hyena.Data.Gui\ListView\ListView_Accessible.cs" />
+    <Compile Include="Hyena.Gui\BaseWidgetAccessible.cs" />
+    <Compile Include="Hyena.Data.Gui\Accessibility\ColumnCellAccessible.cs" />
+    <Compile Include="Hyena.Data.Gui\Accessibility\ListViewAccessible.cs" />
+    <Compile Include="Hyena.Data.Gui\Accessibility\ICellAccessibleParent.cs" />
+    <Compile Include="Hyena.Data.Gui\Accessibility\ListViewAccessible_Table.cs" />
+    <Compile Include="Hyena.Data.Gui\Accessibility\ListViewAccessible_Selection.cs" />
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <ProjectExtensions>
@@ -167,4 +174,7 @@
       </Properties>
     </MonoDevelop>
   </ProjectExtensions>
-</Project>
+  <ItemGroup>
+    <Folder Include="Hyena.Data.Gui\Accessibility\" />
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/src/Libraries/Hyena.Gui/Hyena.Gui/BaseWidgetAccessible.cs b/src/Libraries/Hyena.Gui/Hyena.Gui/BaseWidgetAccessible.cs
new file mode 100644
index 0000000..4ef1a3a
--- /dev/null
+++ b/src/Libraries/Hyena.Gui/Hyena.Gui/BaseWidgetAccessible.cs
@@ -0,0 +1,260 @@
+//
+// BaseWidgetAccessible.cs
+//
+// Author:
+//   Gabriel Burt <gburt novell com>
+//
+// Copyright (C) 2009 Novell, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+
+using Atk;
+
+namespace Hyena.Gui
+{
+    public class BaseWidgetAccessible : Gtk.Accessible, Atk.ComponentImplementor
+    {
+        private Gtk.Widget widget;
+        private uint focus_id = 0;
+        private Dictionary<uint, Atk.FocusHandler> focus_handlers = new Dictionary<uint, Atk.FocusHandler> ();
+
+        public BaseWidgetAccessible (Gtk.Widget widget)
+        {
+            this.widget = widget;
+            widget.SizeAllocated += OnAllocated;
+            widget.Mapped += OnMap;
+            widget.Unmapped += OnMap;
+            widget.FocusInEvent += OnFocus;
+            widget.FocusOutEvent += OnFocus;
+            widget.AddNotification ("sensitive", (o, a) => NotifyStateChange (StateType.Sensitive, widget.Sensitive));
+            widget.AddNotification ("visible",   (o, a) => NotifyStateChange (StateType.Visible, widget.Visible));
+        }
+
+        public virtual new Atk.Layer Layer {
+            get { return Layer.Widget; }
+        }
+
+        protected override Atk.StateSet OnRefStateSet ()
+        {
+            var s = base.OnRefStateSet ();
+
+            AddStateIf (s, widget.CanFocus,   StateType.Focusable);
+            AddStateIf (s, widget.HasFocus,   StateType.Focused);
+            AddStateIf (s, widget.Sensitive,  StateType.Sensitive);
+            AddStateIf (s, widget.Sensitive,  StateType.Enabled);
+            AddStateIf (s, widget.HasDefault, StateType.Default);
+            AddStateIf (s, widget.Visible,    StateType.Visible);
+            AddStateIf (s, widget.Visible && widget.IsMapped, StateType.Showing);
+
+            return s;
+        }
+
+        private static void AddStateIf (StateSet s, bool condition, StateType t)
+        {
+            if (condition) {
+                s.AddState (t);
+            }
+        }
+
+        private void OnFocus (object o, EventArgs args)
+        {
+            NotifyStateChange (StateType.Focused, widget.HasFocus);
+            var handler = FocusChanged;
+            if (handler != null) {
+                handler (this, widget.HasFocus);
+            }
+        }
+
+        private void OnMap (object o, EventArgs args)
+        {
+            NotifyStateChange (StateType.Showing, widget.Visible && widget.IsMapped);
+        }
+
+        private void OnAllocated (object o, EventArgs args)
+        {
+            var a = widget.Allocation;
+            var bounds = new Atk.Rectangle () { X = a.X, Y = a.Y, Width = a.Width, Height = a.Height };
+            GLib.Signal.Emit (this, "bounds_changed", bounds);
+            /*var handler = BoundsChanged;
+            if (handler != null) {
+                handler (this, new BoundsChangedArgs () { Args = new object [] { bounds } });
+            }*/
+        }
+
+        private event FocusHandler FocusChanged;
+
+        #region Atk.Component
+
+        public uint AddFocusHandler (Atk.FocusHandler handler)
+        {
+            if (!focus_handlers.ContainsValue (handler)) {
+                FocusChanged += handler;
+                focus_handlers[++focus_id] = handler;
+                return focus_id;
+            }
+            return 0;
+        }
+
+        public bool Contains (int x, int y, Atk.CoordType coordType)
+        {
+            int x_extents, y_extents, w, h;
+            GetExtents (out x_extents, out y_extents, out w, out h, coordType);
+            Gdk.Rectangle extents = new Gdk.Rectangle(x_extents, y_extents, w, h);
+            return extents.Contains(x, y);
+        }
+
+        public virtual Atk.Object RefAccessibleAtPoint (int x, int y, Atk.CoordType coordType)
+        {
+            return new NoOpObject (widget);
+        }
+
+        public void GetExtents (out int x, out int y, out int w, out int h, Atk.CoordType coordType)
+        {
+            w = widget.Allocation.Width;
+            h = widget.Allocation.Height;
+
+            GetPosition (out x, out y, coordType);
+        }
+
+        public void GetPosition (out int x, out int y, Atk.CoordType coordType)
+        {
+            Gdk.Window window = null;
+
+            if (!widget.IsDrawable) {
+                x = y = Int32.MinValue;
+                return;
+            }
+
+            if (widget.Parent != null) {
+                x = widget.Allocation.X;
+                y = widget.Allocation.Y;
+                window = widget.ParentWindow;
+            } else {
+                x = 0;
+                y = 0;
+                window = widget.GdkWindow;
+            }
+
+            int x_window, y_window;
+            window.GetOrigin (out x_window, out y_window);
+            x += x_window;
+            y += y_window;
+
+            if (coordType == Atk.CoordType.Window) {
+                window = widget.GdkWindow.Toplevel;
+                int x_toplevel, y_toplevel;
+                window.GetOrigin (out x_toplevel, out y_toplevel);
+
+                x -= x_toplevel;
+                y -= y_toplevel;
+            }
+        }
+
+        public void GetSize (out int w, out int h)
+        {
+            w = widget.Allocation.Width;
+            h = widget.Allocation.Height;
+        }
+
+        public bool GrabFocus ()
+        {
+            if (!widget.CanFocus) {
+                return false;
+            }
+
+            widget.GrabFocus ();
+
+            var toplevel_window = widget.Toplevel as Gtk.Window;
+            if (toplevel_window != null) {
+                toplevel_window.Present ();
+            }
+
+            return true;
+        }
+
+        public void RemoveFocusHandler (uint handlerId)
+        {
+            if (focus_handlers.ContainsKey (handlerId)) {
+                FocusChanged -= focus_handlers[handlerId];
+                focus_handlers.Remove (handlerId);
+            }
+        }
+
+        public bool SetExtents (int x, int y, int w, int h, Atk.CoordType coordType)
+        {
+            return SetSizeAndPosition (x, y, w, h, coordType, true);
+        }
+
+        public bool SetPosition (int x, int y, Atk.CoordType coordType)
+        {
+            return SetSizeAndPosition (x, y, 0, 0, coordType, false);
+        }
+
+        private bool SetSizeAndPosition (int x, int y, int w, int h, Atk.CoordType coordType, bool setSize)
+        {
+            if (!widget.IsTopLevel) {
+                return false;
+            }
+
+            if (coordType == CoordType.Window) {
+                int x_off, y_off;
+                widget.GdkWindow.GetOrigin (out x_off, out y_off);
+                x += x_off;
+                y += y_off;
+
+                if (x < 0 || y < 0) {
+                    return false;
+                }
+            }
+
+            #pragma warning disable 0612
+            widget.SetUposition (x, y);
+            #pragma warning restore 0612
+
+            if (setSize) {
+                widget.SetSizeRequest (w, h);
+            }
+
+            return true;
+        }
+
+        public bool SetSize (int w, int h)
+        {
+            if (widget.IsTopLevel) {
+                widget.SetSizeRequest (w, h);
+                return true;
+            } else {
+                return false;
+            }
+        }
+
+        public double Alpha {
+            get { return 1.0; }
+        }
+
+        #endregion Atk.Component
+
+    }
+}
diff --git a/src/Libraries/Hyena.Gui/Makefile.am b/src/Libraries/Hyena.Gui/Makefile.am
index b17a50d..d9952df 100644
--- a/src/Libraries/Hyena.Gui/Makefile.am
+++ b/src/Libraries/Hyena.Gui/Makefile.am
@@ -3,6 +3,11 @@ ASSEMBLY_BUILD_FLAGS = -unsafe
 TARGET = library
 LINK = $(REF_HYENA_GUI)
 SOURCES =  \
+	Hyena.Data.Gui/Accessibility/ColumnCellAccessible.cs \
+	Hyena.Data.Gui/Accessibility/ICellAccessibleParent.cs \
+	Hyena.Data.Gui/Accessibility/ListViewAccessible.cs \
+	Hyena.Data.Gui/Accessibility/ListViewAccessible_Selection.cs \
+	Hyena.Data.Gui/Accessibility/ListViewAccessible_Table.cs \
 	Hyena.Data.Gui/CellContext.cs \
 	Hyena.Data.Gui/Column.cs \
 	Hyena.Data.Gui/ColumnCell.cs \
@@ -18,6 +23,7 @@ SOURCES =  \
 	Hyena.Data.Gui/ITextCell.cs \
 	Hyena.Data.Gui/ITooltipCell.cs \
 	Hyena.Data.Gui/ListView/ListView.cs \
+	Hyena.Data.Gui/ListView/ListView_Accessible.cs \
 	Hyena.Data.Gui/ListView/ListView_DragAndDrop.cs \
 	Hyena.Data.Gui/ListView/ListView_Header.cs \
 	Hyena.Data.Gui/ListView/ListView_Interaction.cs \
@@ -42,6 +48,7 @@ SOURCES =  \
 	Hyena.Gui.Theming/Theme.cs \
 	Hyena.Gui.Theming/ThemeContext.cs \
 	Hyena.Gui.Theming/ThemeEngine.cs \
+	Hyena.Gui/BaseWidgetAccessible.cs \
 	Hyena.Gui/CairoExtensions.cs \
 	Hyena.Gui/CleanRoomStartup.cs \
 	Hyena.Gui/CompositeUtils.cs \
diff --git a/src/Libraries/Hyena/Hyena.Collections/Selection.cs b/src/Libraries/Hyena/Hyena.Collections/Selection.cs
index 078edc7..5640666 100644
--- a/src/Libraries/Hyena/Hyena.Collections/Selection.cs
+++ b/src/Libraries/Hyena/Hyena.Collections/Selection.cs
@@ -214,7 +214,7 @@ namespace Hyena.Collections
             }
         }
 
-        protected RangeCollection RangeCollection {
+        public RangeCollection RangeCollection {
             get { return ranges; }
         }
 



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