[hyena] Sync Hyena.Gui with Banshee



commit c92fb23f08b9bcc5ed58af0f4f55d0637ff649e8
Author: Gabriel Burt <gabriel burt gmail com>
Date:   Tue Jan 12 12:15:19 2010 -0800

    Sync Hyena.Gui with Banshee

 .../Accessibility/ColumnCellAccessible.cs          |  151 +++++++++++
 .../Accessibility/ColumnCellTextAccessible.cs      |   40 +++
 .../ColumnHeaderCellTextAccessible.cs              |  114 +++++++++
 .../Accessibility/ICellAccessibleParent.cs         |   45 ++++
 .../Accessibility/ListViewAccessible.cs            |  245 ++++++++++++++++++
 .../Accessibility/ListViewAccessible_Selection.cs  |   92 +++++++
 .../Accessibility/ListViewAccessible_Table.cs      |  192 ++++++++++++++
 src/Hyena.Gui/Hyena.Data.Gui/CellContext.cs        |    8 +-
 src/Hyena.Gui/Hyena.Data.Gui/ColumnCell.cs         |   12 +
 src/Hyena.Gui/Hyena.Data.Gui/ColumnCellRating.cs   |   10 +
 src/Hyena.Gui/Hyena.Data.Gui/ColumnCellText.cs     |   27 ++-
 .../Hyena.Data.Gui/ColumnHeaderCellText.cs         |    7 +
 .../Hyena.Data.Gui/ListView/ListView_Accessible.cs |  177 +++++++++++++
 .../Hyena.Data.Gui/ListView/ListView_Header.cs     |   57 +++++-
 .../ListView/ListView_Interaction.cs               |  156 +++++++++----
 .../Hyena.Data.Gui/ListView/ListView_Model.cs      |   53 ++--
 .../Hyena.Data.Gui/ListView/ListView_Rendering.cs  |   53 +++--
 .../Hyena.Data.Gui/ListView/ListView_Windowing.cs  |    4 +
 src/Hyena.Gui/Hyena.Gui.Dialogs/ExceptionDialog.cs |    1 +
 src/Hyena.Gui/Hyena.Gui.Theming/GtkTheme.cs        |   63 +++++-
 src/Hyena.Gui/Hyena.Gui.Theming/Theme.cs           |   20 ++-
 src/Hyena.Gui/Hyena.Gui/BaseWidgetAccessible.cs    |  262 ++++++++++++++++++++
 src/Hyena.Gui/Hyena.Widgets/AnimatedWidget.cs      |    7 +-
 src/Hyena.Gui/Hyena.Widgets/EntryPopup.cs          |  226 +++++++++++++++++
 src/Hyena.Gui/Hyena.Widgets/HigMessageDialog.cs    |   11 +-
 src/Hyena.Gui/Hyena.Widgets/ImageButton.cs         |   18 ++-
 src/Hyena.Gui/Hyena.Widgets/MenuButton.cs          |    1 +
 src/Hyena.Gui/Hyena.Widgets/MessageBar.cs          |    5 +-
 src/Hyena.Gui/Hyena.Widgets/RatingEntry.cs         |   79 ++++++
 src/Hyena.Gui/Hyena.Widgets/SimpleTable.cs         |  103 ++++++++
 src/Hyena.Gui/Hyena.Widgets/WrapLabel.cs           |    7 +-
 src/Hyena.Gui/Makefile.am                          |   11 +
 32 files changed, 2131 insertions(+), 126 deletions(-)
---
diff --git a/src/Hyena.Gui/Hyena.Data.Gui/Accessibility/ColumnCellAccessible.cs b/src/Hyena.Gui/Hyena.Data.Gui/Accessibility/ColumnCellAccessible.cs
new file mode 100644
index 0000000..f0e797a
--- /dev/null
+++ b/src/Hyena.Gui/Hyena.Data.Gui/Accessibility/ColumnCellAccessible.cs
@@ -0,0 +1,151 @@
+//
+// ColumnCellAccessible.cs
+//
+// Author:
+//   Eitan Isaacson <eitan ascender com>
+//
+// Copyright (C) 2009 Eitan Isaacson.
+//
+// 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;
+
+namespace Hyena.Data.Gui.Accessibility
+{
+    public class ColumnCellAccessible: Atk.Object, Atk.ComponentImplementor
+    {
+        protected ColumnCell cell;
+        protected object bound_object;
+        private ICellAccessibleParent cell_parent;
+
+        public ColumnCellAccessible (object bound_object, ColumnCell cell, ICellAccessibleParent parent)
+        {
+            Role = Atk.Role.TableCell;
+            this.bound_object = bound_object;
+            this.cell = cell;
+            cell_parent = parent;
+            Parent = (Atk.Object) parent;
+        }
+
+#if ENABLE_ATK
+        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 (cell_parent.IsCellShowing (this))
+                states.AddState (Atk.StateType.Showing);
+
+            if (cell_parent.IsCellFocused (this))
+                states.AddState (Atk.StateType.Focused);
+
+            if (cell_parent.IsCellSelected (this))
+                states.AddState (Atk.StateType.Selected);
+
+            if (cell_parent.IsCellActive (this))
+                states.AddState (Atk.StateType.Active);
+
+            return states;
+        }
+
+        protected override int OnGetIndexInParent ()
+        {
+            return cell_parent.GetCellIndex (this);
+        }
+#endif
+
+        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 = cell_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 = cell_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 = cell_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;
+        }
+
+        public virtual void Redrawn ()
+        {
+        }
+    }
+}
diff --git a/src/Hyena.Gui/Hyena.Data.Gui/Accessibility/ColumnCellTextAccessible.cs b/src/Hyena.Gui/Hyena.Data.Gui/Accessibility/ColumnCellTextAccessible.cs
new file mode 100644
index 0000000..76eb0c3
--- /dev/null
+++ b/src/Hyena.Gui/Hyena.Data.Gui/Accessibility/ColumnCellTextAccessible.cs
@@ -0,0 +1,40 @@
+//
+// ColumnCellTextAccessible.cs
+//
+// Author:
+//   Eitan Isaacson <eitan ascender com>
+//
+// Copyright (C) 2009 Eitan Isaacson.
+//
+// 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;
+
+namespace Hyena.Data.Gui.Accessibility
+{
+    class ColumnCellTextAccessible : ColumnCellAccessible
+    {
+        public ColumnCellTextAccessible (object bound_object, ColumnCellText cell, ICellAccessibleParent parent): base (bound_object, cell as ColumnCell, parent)
+        {
+            Name = cell.GetTextAlternative (bound_object);
+        }
+    }
+}
diff --git a/src/Hyena.Gui/Hyena.Data.Gui/Accessibility/ColumnHeaderCellTextAccessible.cs b/src/Hyena.Gui/Hyena.Data.Gui/Accessibility/ColumnHeaderCellTextAccessible.cs
new file mode 100644
index 0000000..2b6704f
--- /dev/null
+++ b/src/Hyena.Gui/Hyena.Data.Gui/Accessibility/ColumnHeaderCellTextAccessible.cs
@@ -0,0 +1,114 @@
+//
+// ColumnHeaderCellTextAccessible.cs
+//
+// Author:
+//   Eitan Isaacson <eitan ascender com>
+//
+// Copyright (C) 2009 Eitan Isaacson.
+//
+// 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 Mono.Unix;
+
+namespace Hyena.Data.Gui.Accessibility
+{
+    class ColumnHeaderCellTextAccessible: ColumnCellTextAccessible, Atk.ActionImplementor
+    {
+        private static string [] action_descriptions  = new string[] {"", Catalog.GetString ("open context menu")};
+        private static string [] action_names_localized = new string[] {Catalog.GetString ("click"), Catalog.GetString ("menu")};
+
+        private enum Actions {
+            Click,
+            Menu,
+            Last
+        };
+
+        public ColumnHeaderCellTextAccessible (object bound_object, ColumnHeaderCellText cell, ICellAccessibleParent parent)
+            : base (bound_object, cell as ColumnCellText, parent)
+        {
+            Role = Atk.Role.TableColumnHeader;
+        }
+
+#if ENABLE_ATK
+        protected override Atk.StateSet OnRefStateSet ()
+        {
+            Atk.StateSet states = base.OnRefStateSet ();
+            states.RemoveState (Atk.StateType.Selectable);
+            states.RemoveState (Atk.StateType.Transient);
+            return states;
+        }
+#endif
+
+        public string GetLocalizedName (int action)
+        {
+            if (action >= action_names_localized.Length)
+                return "";
+
+            return action_names_localized[action];
+        }
+
+        public string GetName (int action)
+        {
+            if (action >= (int)Actions.Last)
+                return "";
+
+            return ((Actions)action).ToString ().ToLower ();
+        }
+
+        public string GetDescription (int action)
+        {
+            if (action >= action_descriptions.Length)
+                return "";
+
+            return action_descriptions[action];
+        }
+
+        public string GetKeybinding (int action)
+        {
+            return "";
+        }
+
+        public int NActions {
+            get { return (int)Actions.Last; }
+        }
+
+        public bool DoAction (int action)
+        {
+            ICellAccessibleParent parent = (ICellAccessibleParent)Parent;
+            switch ((Actions)action) {
+                case Actions.Menu: parent.InvokeColumnHeaderMenu (this); break;
+                case Actions.Click: parent.ClickColumnHeader (this); break;
+            }
+
+            if (action == (int)Actions.Menu) {
+                ((ICellAccessibleParent)Parent).InvokeColumnHeaderMenu (this);
+            }
+
+            return true;
+        }
+
+        public bool SetDescription (int action, string description)
+        {
+            return false;
+        }
+    }
+}
diff --git a/src/Hyena.Gui/Hyena.Data.Gui/Accessibility/ICellAccessibleParent.cs b/src/Hyena.Gui/Hyena.Data.Gui/Accessibility/ICellAccessibleParent.cs
new file mode 100644
index 0000000..499a2ab
--- /dev/null
+++ b/src/Hyena.Gui/Hyena.Data.Gui/Accessibility/ICellAccessibleParent.cs
@@ -0,0 +1,45 @@
+//
+// ICellAccessibleParent.cs
+//
+// Author:
+//   Eitan Isaacson <eitan ascender com>
+//
+// Copyright (C) 2009 Eitan Isaacson.
+//
+// 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;
+
+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);
+        bool IsCellActive (ColumnCellAccessible cell);
+        void InvokeColumnHeaderMenu (ColumnCellAccessible column);
+        void ClickColumnHeader (ColumnCellAccessible column);
+        void CellRedrawn (int column, int row);
+    }
+}
diff --git a/src/Hyena.Gui/Hyena.Data.Gui/Accessibility/ListViewAccessible.cs b/src/Hyena.Gui/Hyena.Data.Gui/Accessibility/ListViewAccessible.cs
new file mode 100644
index 0000000..dc2f8c9
--- /dev/null
+++ b/src/Hyena.Gui/Hyena.Data.Gui/Accessibility/ListViewAccessible.cs
@@ -0,0 +1,245 @@
+//
+// ListViewAccessible.cs
+//
+// Authors:
+//   Eitan Isaacson <eitan ascender com>
+//   Gabriel Burt <gburt novell com>
+//
+// Copyright (C) 2009 Novell, Inc.
+// Copyright (C) 2009 Eitan Isaacson
+//
+// 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 Hyena.Data.Gui;
+
+namespace Hyena.Data.Gui.Accessibility
+{
+#if ENABLE_ATK
+    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.FocusChanged += OnSelectionFocusChanged;
+            list_view.ActiveColumnChanged += 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];
+            }
+
+            var columns = list_view.ColumnController.Where (c => c.Visible);
+
+            if (index - n_columns < 0) {
+                child = columns.ElementAtOrDefault (index)
+                               .HeaderCell
+                               .GetAccessible (this) as ColumnCellAccessible;
+            } else {
+                int column = (index - n_columns) % n_columns;
+                int row = (index - n_columns) / n_columns;
+                var cell = columns.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)
+        {
+            GLib.Signal.Emit (this, "active-descendant-changed", ActiveCell.Handle);
+        }
+
+        private Atk.Object ActiveCell {
+            get {
+                if (list_view.HeaderFocused)
+                    return OnRefChild (list_view.ActiveColumn);
+                else
+                    return RefAt (list_view.Selection.FocusedIndex, list_view.ActiveColumn);
+            }
+        }
+
+        private int n_columns {
+            get { return list_view.ColumnController.Count (c => c.Visible); }
+        }
+
+        private int n_rows {
+            get { return list_view.Model.Count; }
+        }
+
+        #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 = Int32.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 == Int32.MinValue && cell_extents.Y == Int32.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.FocusedIndex;
+        }
+
+        public bool IsCellSelected (ColumnCellAccessible cell)
+        {
+            return IsChildSelected (GetCellIndex (cell));
+        }
+
+        public bool IsCellActive (ColumnCellAccessible cell)
+        {
+            return (ActiveCell == (Atk.Object)cell);
+        }
+
+        public void InvokeColumnHeaderMenu (ColumnCellAccessible cell)
+        {
+            list_view.InvokeColumnHeaderMenu (GetCellIndex (cell));
+        }
+
+        public void ClickColumnHeader (ColumnCellAccessible cell)
+        {
+            list_view.ClickColumnHeader (GetCellIndex (cell));
+        }
+
+        public void CellRedrawn (int column, int row)
+        {
+            int index;
+            if (row >= 0)
+                index = row * n_columns + column + n_columns;
+            else
+                index = column;
+
+            if (cell_cache.ContainsKey (index)) {
+                cell_cache[index].Redrawn ();
+            }
+        }
+
+        #endregion
+    }
+#endif
+}
diff --git a/src/Hyena.Gui/Hyena.Data.Gui/Accessibility/ListViewAccessible_Selection.cs b/src/Hyena.Gui/Hyena.Data.Gui/Accessibility/ListViewAccessible_Selection.cs
new file mode 100644
index 0000000..caa6728
--- /dev/null
+++ b/src/Hyena.Gui/Hyena.Data.Gui/Accessibility/ListViewAccessible_Selection.cs
@@ -0,0 +1,92 @@
+//
+// ListViewAccessible_Selection.cs
+//
+// Authors:
+//   Eitan Isaacson <eitan ascender com>
+//   Gabriel Burt <gburt novell com>
+//
+// Copyright (C) 2009 Novell, Inc.
+// Copyright (C) 2009 Eitan Isaacson
+//
+// 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 Hyena.Data.Gui;
+
+namespace Hyena.Data.Gui.Accessibility
+{
+#if ENABLE_ATK
+    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");
+        }
+    }
+#endif
+}
diff --git a/src/Hyena.Gui/Hyena.Data.Gui/Accessibility/ListViewAccessible_Table.cs b/src/Hyena.Gui/Hyena.Data.Gui/Accessibility/ListViewAccessible_Table.cs
new file mode 100644
index 0000000..ed43a8c
--- /dev/null
+++ b/src/Hyena.Gui/Hyena.Data.Gui/Accessibility/ListViewAccessible_Table.cs
@@ -0,0 +1,192 @@
+//
+// ListViewAccessible_Table.cs
+//
+// Authors:
+//   Eitan Isaacson <eitan ascender com>
+//   Gabriel Burt <gburt novell com>
+//
+// Copyright (C) 2009 Novell, Inc.
+// Copyright (C) 2009 Eitan Isaacson
+//
+// 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 Hyena.Data.Gui;
+
+namespace Hyena.Data.Gui.Accessibility
+{
+#if ENABLE_ATK
+    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)
+        {
+            return NColumns == 0 ? -1 : (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);
+        }
+
+// Ensure https://bugzilla.novell.com/show_bug.cgi?id=512477 is fixed
+#if ENABLE_ATK
+        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 (); }
+        }
+#else
+        public int GetSelectedRows (out int row) { row = 0; return 0; }
+        public int GetSelectedColumns (out int cols) { cols = 0; return 0; }
+#endif
+
+        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)
+        {
+        }
+    }
+#endif
+}
diff --git a/src/Hyena.Gui/Hyena.Data.Gui/CellContext.cs b/src/Hyena.Gui/Hyena.Data.Gui/CellContext.cs
index 97f54ff..f25ec0d 100644
--- a/src/Hyena.Gui/Hyena.Data.Gui/CellContext.cs
+++ b/src/Hyena.Gui/Hyena.Data.Gui/CellContext.cs
@@ -42,7 +42,7 @@ namespace Hyena.Data.Gui
         private Gdk.Rectangle area;
         private Gdk.Rectangle clip;
         private bool text_as_foreground = false;
-        private bool sensitive = true;
+        private bool opaque = true;
 
         public Cairo.Context Context {
             get { return context; }
@@ -84,9 +84,9 @@ namespace Hyena.Data.Gui
             set { text_as_foreground = value; }
         }
 
-        public bool Sensitive {
-            get { return sensitive; }
-            set { sensitive = value; }
+        public bool Opaque {
+            get { return opaque; }
+            set { opaque = value; }
         }
     }
 }
diff --git a/src/Hyena.Gui/Hyena.Data.Gui/ColumnCell.cs b/src/Hyena.Gui/Hyena.Data.Gui/ColumnCell.cs
index eb6043b..65b3904 100644
--- a/src/Hyena.Gui/Hyena.Data.Gui/ColumnCell.cs
+++ b/src/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
@@ -41,6 +43,16 @@ namespace Hyena.Data.Gui
         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 "";
+        }
+
         public ColumnCell (string property, bool expand)
         {
             Property = property;
diff --git a/src/Hyena.Gui/Hyena.Data.Gui/ColumnCellRating.cs b/src/Hyena.Gui/Hyena.Data.Gui/ColumnCellRating.cs
index 420ccb4..e1efeb2 100644
--- a/src/Hyena.Gui/Hyena.Data.Gui/ColumnCellRating.cs
+++ b/src/Hyena.Gui/Hyena.Data.Gui/ColumnCellRating.cs
@@ -67,6 +67,10 @@ namespace Hyena.Data.Gui
 
         public bool ButtonEvent (int x, int y, bool pressed, Gdk.EventButton evnt)
         {
+            if (ReadOnly) {
+                return false;
+            }
+
             if (pressed) {
                 last_pressed_bound = BoundObjectParent;
                 return false;
@@ -82,6 +86,10 @@ namespace Hyena.Data.Gui
 
         public bool MotionEvent (int x, int y, Gdk.EventMotion evnt)
         {
+            if (ReadOnly) {
+                return false;
+            }
+
             int value = RatingFromPosition (x);
 
             if (hover_bound == BoundObjectParent && value == hover_value) {
@@ -144,5 +152,7 @@ namespace Hyena.Data.Gui
             get { return renderer.Ypad; }
             set { renderer.Ypad = value; }
         }
+
+        public bool ReadOnly { get; set; }
     }
 }
diff --git a/src/Hyena.Gui/Hyena.Data.Gui/ColumnCellText.cs b/src/Hyena.Gui/Hyena.Data.Gui/ColumnCellText.cs
index c331369..0a2c126 100644
--- a/src/Hyena.Gui/Hyena.Data.Gui/ColumnCellText.cs
+++ b/src/Hyena.Gui/Hyena.Data.Gui/ColumnCellText.cs
@@ -32,6 +32,7 @@ using Cairo;
 
 using Hyena.Gui;
 using Hyena.Gui.Theming;
+using Hyena.Data.Gui.Accessibility;
 
 namespace Hyena.Data.Gui
 {
@@ -44,7 +45,6 @@ namespace Hyena.Data.Gui
         private Pango.Weight font_weight = Pango.Weight.Normal;
         private Pango.EllipsizeMode ellipsize_mode = Pango.EllipsizeMode.End;
         private Pango.Alignment alignment = Pango.Alignment.Left;
-        private double opacity = 1.0;
         private int text_width;
         private int text_height;
         private string text_format = null;
@@ -56,12 +56,22 @@ namespace Hyena.Data.Gui
         {
         }
 
-        protected void SetMinMaxStrings (object min_max)
+        public override Atk.Object GetAccessible (ICellAccessibleParent parent)
+        {
+            return new ColumnCellTextAccessible (BoundObject, this, parent);
+        }
+
+        public override string GetTextAlternative (object obj)
+        {
+            return GetText (obj);
+        }
+
+        public void SetMinMaxStrings (object min_max)
         {
             SetMinMaxStrings (min_max, min_max);
         }
 
-        protected void SetMinMaxStrings (object min, object max)
+        public void SetMinMaxStrings (object min, object max)
         {
             // Set the min/max strings from the min/max objects
             MinString = GetText (min);
@@ -81,7 +91,7 @@ namespace Hyena.Data.Gui
             context.Context.MoveTo (Spacing, ((int)cellHeight - text_height) / 2);
             Cairo.Color color = context.Theme.Colors.GetWidgetColor (
                 context.TextAsForeground ? GtkColorClass.Foreground : GtkColorClass.Text, state);
-            color.A = (!context.Sensitive) ? 0.3 : opacity;
+            color.A = context.Opaque ? 1.0 : 0.5;
             context.Context.Color = color;
 
             PangoCairoHelper.ShowLayout (context.Context, context.Layout);
@@ -106,9 +116,13 @@ namespace Hyena.Data.Gui
             is_ellipsized = context.Layout.IsEllipsized;
         }
 
+        private static char[] lfcr = new char[] {'\n', '\r'};
         private void UpdateLayout (Pango.Layout layout, string text)
         {
             string final_text = GetFormattedText (text);
+            if (final_text.IndexOfAny (lfcr) >= 0) {
+                final_text = final_text.Replace ("\r\n", "\x20").Replace ('\n', '\x20').Replace ('\r', '\x20');
+            }
             if (use_markup) {
                 layout.SetMarkup (final_text);
             } else {
@@ -172,11 +186,6 @@ namespace Hyena.Data.Gui
             set { ellipsize_mode = value; }
         }
 
-        public virtual double Opacity {
-            get { return opacity; }
-            set { opacity = value; }
-        }
-
         internal static int ComputeRowHeight (Widget widget)
         {
             int w_width, row_height;
diff --git a/src/Hyena.Gui/Hyena.Data.Gui/ColumnHeaderCellText.cs b/src/Hyena.Gui/Hyena.Data.Gui/ColumnHeaderCellText.cs
index a63753e..a5d17b6 100644
--- a/src/Hyena.Gui/Hyena.Data.Gui/ColumnHeaderCellText.cs
+++ b/src/Hyena.Gui/Hyena.Data.Gui/ColumnHeaderCellText.cs
@@ -30,6 +30,8 @@ using System;
 using Gtk;
 using Cairo;
 
+using Hyena.Data.Gui.Accessibility;
+
 namespace Hyena.Data.Gui
 {
     public class ColumnHeaderCellText : ColumnCellText, IHeaderCell
@@ -44,6 +46,11 @@ namespace Hyena.Data.Gui
             this.data_handler = data_handler;
         }
 
+        public override Atk.Object GetAccessible (ICellAccessibleParent parent)
+        {
+            return new  ColumnHeaderCellTextAccessible (BoundObject, this, parent);
+        }
+
         public override void Render (CellContext context, StateType state, double cellWidth, double cellHeight)
         {
             if (data_handler == null) {
diff --git a/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Accessible.cs b/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Accessible.cs
new file mode 100644
index 0000000..1c8abc7
--- /dev/null
+++ b/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Accessible.cs
@@ -0,0 +1,177 @@
+//
+// ListView_Accessible.cs
+//
+// Authors:
+//   Gabriel Burt <gburt novell com>
+//   Eitan Isaacson <eitan ascender com>
+//
+// Copyright (C) 2009 Novell, Inc.
+// Copyright (C) 2009 Eitan Isaacson
+//
+// 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 ()
+        {
+#if ENABLE_ATK
+            ListViewAccessibleFactory<T>.Init ();
+#endif
+        }
+
+        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 (Int32.MinValue, Int32.MinValue, Int32.MinValue, Int32.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 (Int32.MinValue, Int32.MinValue, Int32.MinValue, Int32.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;
+        }
+
+        public void InvokeColumnHeaderMenu (int column)
+        {
+            Gdk.Rectangle rectangle = GetColumnHeaderCellExtents (column, true, Atk.CoordType.Window);
+            Column col = ColumnController.Where (c => c.Visible).ElementAtOrDefault (column);
+            OnColumnRightClicked (col, rectangle.X + rectangle.Width/2, rectangle.Y + rectangle.Height/2);
+        }
+
+        public void ClickColumnHeader (int column)
+        {
+            Column col = ColumnController.Where (c => c.Visible).ElementAtOrDefault (column);
+            OnColumnLeftClicked (col);
+        }
+
+        private void AccessibleCellRedrawn (int column, int row)
+        {
+            var accessible = Accessible as ICellAccessibleParent;
+            if (accessible != null) {
+                accessible.CellRedrawn (column, row);
+            }
+        }
+
+    }
+
+#if ENABLE_ATK
+    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;
+        }
+    }
+#endif
+}
diff --git a/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Header.cs b/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Header.cs
index 9173e83..f9aae51 100644
--- a/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Header.cs
+++ b/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Header.cs
@@ -57,6 +57,7 @@ namespace Hyena.Data.Gui
 
         private bool resizable;
         private int header_width;
+        private double list_width, max_width;
         private int sort_column_index = -1;
         private int resizing_column_index = -1;
         private int pressed_column_index = -1;
@@ -73,6 +74,14 @@ namespace Hyena.Data.Gui
         private CachedColumn [] column_cache;
         private List<int> elastic_columns;
 
+        public int Width {
+            get { return (int)list_width; }
+        }
+
+        public int MaxWidth {
+            get { return (int)max_width + Theme.TotalBorderWidth*2; }
+        }
+
 #region Columns
 
         private void InvalidateColumnCache ()
@@ -191,7 +200,7 @@ namespace Hyena.Data.Gui
 
                 double remaining_width = RecalculateColumnSizes (header_width, header_width);
 
-                while (remaining_width != 0 && elastic_columns.Count > 0) {
+                while (Math.Round (remaining_width) != 0.0 && elastic_columns.Count > 0) {
                     double total_elastic_width = 0.0;
                     foreach (int i in elastic_columns) {
                         total_elastic_width += column_cache[i].ElasticWidth;
@@ -203,6 +212,15 @@ namespace Hyena.Data.Gui
                     column_cache[i].Column.Width = column_cache[i].ElasticWidth / (double)header_width;
                 }
             }
+
+            double tmp_width = 0.0;
+            double tmp_max = 0.0;
+            foreach (var col in column_cache) {
+                tmp_width += col.ElasticWidth;
+                tmp_max += col.MaxWidth == Int32.MaxValue ? col.MinWidth : col.MaxWidth;
+            }
+            list_width = tmp_width;
+            max_width = tmp_max;
         }
 
         private double RecalculateColumnSizes (double total_width, double total_elastic_width)
@@ -246,6 +264,37 @@ namespace Hyena.Data.Gui
             QueueDraw ();
         }
 
+        protected virtual void OnColumnLeftClicked (Column clickedColumn)
+        {
+            if (Model is ISortable && clickedColumn is ISortableColumn) {
+                ISortableColumn sort_column = clickedColumn as ISortableColumn;
+                ISortable sortable = Model as ISortable;
+
+                // Change the sort-type with every click
+                if (sort_column == ColumnController.SortColumn) {
+                    switch (sort_column.SortType) {
+                        case SortType.Ascending:    sort_column.SortType = SortType.Descending; break;
+                        case SortType.Descending:   sort_column.SortType = SortType.None; break;
+                        case SortType.None:         sort_column.SortType = SortType.Ascending; break;
+                    }
+                }
+
+                // If we're switching from a different column, or we aren't reorderable, make sure sort type isn't None
+                if ((sort_column != ColumnController.SortColumn || !IsEverReorderable) && sort_column.SortType == SortType.None) {
+                    sort_column.SortType = SortType.Ascending;
+                }
+
+                sortable.Sort (sort_column);
+                ColumnController.SortColumn = sort_column;
+                IsReorderable = sortable.SortColumn == null || sortable.SortColumn.SortType == SortType.None;
+
+                Model.Reload ();
+                RecalculateColumnSizes ();
+                RegenerateColumnCache ();
+                InvalidateHeader ();
+            }
+        }
+
         protected virtual void OnColumnRightClicked (Column clickedColumn, int x, int y)
         {
             Column [] columns = ColumnController.ToArray ();
@@ -386,6 +435,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/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Interaction.cs b/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Interaction.cs
index 0465c5e..02a05de 100644
--- a/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Interaction.cs
+++ b/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Interaction.cs
@@ -1,11 +1,13 @@
 //
 // ListView_Interaction.cs
 //
-// Author:
+// Authors:
 //   Aaron Bockover <abockover novell com>
 //   Gabriel Burt <gburt novell com>
+//   Eitan Isaacson <eitan ascender com>
 //
-// Copyright (C) 2007-2008 Novell, Inc.
+// Copyright (C) 2007-2009 Novell, Inc.
+// Copyright (C) 2009 Eitan Isaacson
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
@@ -37,6 +39,32 @@ namespace Hyena.Data.Gui
 {
     public partial class ListView<T> : ListViewBase
     {
+        private bool header_focused = false;
+        public bool HeaderFocused {
+            get { return header_focused; }
+            set {
+                header_focused = value;
+                InvalidateHeader ();
+                InvalidateList ();
+            }
+        }
+
+        #pragma warning disable 0067
+        public event EventHandler ActiveColumnChanged;
+        #pragma warning restore 0067
+
+        private int active_column = 0;
+        public int ActiveColumn {
+            get { return active_column; }
+            set {
+                active_column = value;
+                var handler = ActiveColumnChanged;
+                if (handler != null) {
+                    handler (this, EventArgs.Empty);
+                }
+            }
+        }
+
         private Adjustment vadjustment;
         public Adjustment Vadjustment {
             get { return vadjustment; }
@@ -152,41 +180,72 @@ namespace Hyena.Data.Gui
                 case Gdk.Key.K:
                 case Gdk.Key.Up:
                 case Gdk.Key.KP_Up:
-                    handled = KeyboardScroll (press.State, -1, true);
+                    if (!HeaderFocused)
+                        handled = KeyboardScroll (press.State, -1, true);
                     break;
 
                 case Gdk.Key.j:
                 case Gdk.Key.J:
                 case Gdk.Key.Down:
                 case Gdk.Key.KP_Down:
-                    handled = KeyboardScroll (press.State, 1, true);
+                    if (!HeaderFocused) {
+                        handled = KeyboardScroll (press.State, 1, true);
+                    } else if (HeaderFocused) {
+                        handled = true;
+                        HeaderFocused = false;
+                    }
+                    break;
+                case Gdk.Key.Right:
+                case Gdk.Key.KP_Right:
+                    if (ActiveColumn + 1 < column_cache.Length) {
+                        ActiveColumn++;
+                        InvalidateHeader ();
+                    }
+                    handled = true;
+                    break;
+                case Gdk.Key.Left:
+                case Gdk.Key.KP_Left:
+                    if (ActiveColumn - 1 >= 0) {
+                        ActiveColumn--;
+                        InvalidateHeader ();
+                    }
+                    handled = true;
                     break;
-
                 case Gdk.Key.Page_Up:
                 case Gdk.Key.KP_Page_Up:
-                    handled = vadjustment != null && KeyboardScroll (press.State,
-                        (int)(-vadjustment.PageIncrement / (double)RowHeight), false);
+                    if (!HeaderFocused)
+                        handled = vadjustment != null && KeyboardScroll (press.State,
+                            (int)(-vadjustment.PageIncrement / (double)RowHeight), false);
                     break;
 
                 case Gdk.Key.Page_Down:
                 case Gdk.Key.KP_Page_Down:
-                    handled = vadjustment != null && KeyboardScroll (press.State,
-                        (int)(vadjustment.PageIncrement / (double)RowHeight), false);
+                    if (!HeaderFocused)
+                        handled = vadjustment != null && KeyboardScroll (press.State,
+                            (int)(vadjustment.PageIncrement / (double)RowHeight), false);
                     break;
 
                 case Gdk.Key.Home:
                 case Gdk.Key.KP_Home:
-                    handled = KeyboardScroll (press.State, -10000000, false);
+                    if (!HeaderFocused)
+                        handled = KeyboardScroll (press.State, -10000000, false);
                     break;
 
                 case Gdk.Key.End:
                 case Gdk.Key.KP_End:
-                    handled = KeyboardScroll (press.State, 10000000, false);
+                    if (!HeaderFocused)
+                        handled = KeyboardScroll (press.State, 10000000, false);
                     break;
 
                 case Gdk.Key.Return:
                 case Gdk.Key.KP_Enter:
-                    handled = ActivateSelection ();
+                    if (!HeaderFocused) {
+                        handled = ActivateSelection ();
+                    } else if (HeaderFocused && ActiveColumn >= 0) {
+                        OnColumnLeftClicked (
+                            column_cache[ActiveColumn].Column);
+                        handled = true;
+                    }
                     break;
 
                 case Gdk.Key.Escape:
@@ -194,11 +253,25 @@ namespace Hyena.Data.Gui
                     break;
 
                 case Gdk.Key.space:
-                    if (Selection != null && Selection.FocusedIndex != 1) {
+                    if (Selection != null && Selection.FocusedIndex != 1 &&
+                        !HeaderFocused) {
                         Selection.ToggleSelect (Selection.FocusedIndex);
                         handled = true;
                     }
                     break;
+
+                case Gdk.Key.F10:
+                    if ((press.State & Gdk.ModifierType.ShiftMask) != 0)
+                        goto case Gdk.Key.Menu;
+                    break;
+
+                case Gdk.Key.Menu:
+                    // OnPopupMenu() is reserved for list items in derived classes.
+                    if (HeaderFocused) {
+                        InvokeColumnHeaderMenu (ActiveColumn);
+                        handled = true;
+                    }
+                    break;
             }
 
             if (handled) {
@@ -345,8 +418,10 @@ namespace Hyena.Data.Gui
         {
             HasFocus = true;
             if (header_visible && header_interaction_alloc.Contains ((int)evnt.X, (int)evnt.Y)) {
+                HeaderFocused = true;
                 return OnHeaderButtonPressEvent (evnt);
             } else if (list_interaction_alloc.Contains ((int)evnt.X, (int)evnt.Y) && model != null) {
+                HeaderFocused = false;
                 return OnListButtonPressEvent (evnt);
             }
             return true;
@@ -399,6 +474,7 @@ namespace Hyena.Data.Gui
             int row_index = GetRowAtY (y);
 
             if (row_index < 0 || row_index >= Model.Count) {
+                Gtk.Drag.SourceUnset (this);
                 return true;
             }
 
@@ -493,31 +569,9 @@ namespace Hyena.Data.Gui
         {
             if (pressed_column_index >= 0 && pressed_column_index < column_cache.Length) {
                 Column column = column_cache[pressed_column_index].Column;
-                if (column != null && Model is ISortable && column is ISortableColumn) {
-                    ISortableColumn sort_column = column as ISortableColumn;
-                    ISortable sortable = Model as ISortable;
-
-                    // Change the sort-type with every click
-                    switch (sort_column.SortType) {
-                        case SortType.Ascending:    sort_column.SortType = SortType.Descending; break;
-                        case SortType.Descending:   sort_column.SortType = SortType.None; break;
-                        case SortType.None:         sort_column.SortType = SortType.Ascending; break;
-                    }
-
-                    // If we're switching to a different column or we aren't reorderable and the type is None, sort Ascending
-                    if (sort_column != ColumnController.SortColumn || (!IsEverReorderable && sort_column.SortType == SortType.None)) {
-                        sort_column.SortType = SortType.Ascending;
-                    }
-
-                    sortable.Sort (sort_column);
-                    ColumnController.SortColumn = sort_column;
-                    IsReorderable = sortable.SortColumn == null || sortable.SortColumn.SortType == SortType.None;
-
-                    Model.Reload ();
-                    RecalculateColumnSizes ();
-                    RegenerateColumnCache ();
-                    InvalidateHeader ();
-                }
+                ActiveColumn = pressed_column_index;
+                if (column != null)
+                    OnColumnLeftClicked (column);
 
                 pressed_column_index = -1;
                 return true;
@@ -652,14 +706,28 @@ namespace Hyena.Data.Gui
             return base.OnLeaveNotifyEvent (evnt);
         }
 
-        protected override bool OnFocusInEvent (Gdk.EventFocus evnt)
+        protected override bool OnFocused (Gtk.DirectionType directionType)
         {
-            return base.OnFocusInEvent (evnt);
-        }
+            if (!HeaderVisible)
+                return base.OnFocused (directionType);
 
-        protected override bool OnFocusOutEvent (Gdk.EventFocus evnt)
-        {
-            return base.OnFocusOutEvent (evnt);
+            if (HasFocus) {
+                if (directionType == DirectionType.TabForward && HeaderFocused)
+                    HeaderFocused = false;
+                else if (directionType == DirectionType.TabBackward && !HeaderFocused)
+                    HeaderFocused = true;
+                else
+                    return base.OnFocused (directionType);
+
+                return true;
+            } else {
+                if (directionType == DirectionType.TabForward )
+                    HeaderFocused = true;
+                else if (directionType == DirectionType.TabBackward)
+                    HeaderFocused = false;
+
+                return base.OnFocused (directionType);
+            }
         }
 
         protected virtual void OnRowActivated ()
diff --git a/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Model.cs b/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Model.cs
index 9c576a0..8663161 100644
--- a/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Model.cs
+++ b/src/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) {
@@ -131,41 +141,41 @@ namespace Hyena.Data.Gui
             get { return model; }
         }
 
-        private string row_sensitive_property_name = "Sensitive";
-        private PropertyInfo row_sensitive_property_info;
-        bool row_sensitive_property_invalid = false;
+        private string row_opaque_property_name = "Sensitive";
+        private PropertyInfo row_opaque_property_info;
+        bool row_opaque_property_invalid = false;
 
-        public string RowSensitivePropertyName {
-            get { return row_sensitive_property_name; }
+        public string RowOpaquePropertyName {
+            get { return row_opaque_property_name; }
             set {
-                if (value == row_sensitive_property_name) {
+                if (value == row_opaque_property_name) {
                     return;
                 }
 
-                row_sensitive_property_name = value;
-                row_sensitive_property_info = null;
-                row_sensitive_property_invalid = false;
+                row_opaque_property_name = value;
+                row_opaque_property_info = null;
+                row_opaque_property_invalid = false;
 
                 InvalidateList ();
             }
         }
 
-        private bool IsRowSensitive (object item)
+        private bool IsRowOpaque (object item)
         {
-            if (item == null || row_sensitive_property_invalid) {
+            if (item == null || row_opaque_property_invalid) {
                 return true;
             }
 
-            if (row_sensitive_property_info == null || row_sensitive_property_info.ReflectedType != item.GetType ()) {
-                row_sensitive_property_info = item.GetType ().GetProperty (row_sensitive_property_name);
-                if (row_sensitive_property_info == null || row_sensitive_property_info.PropertyType != typeof (bool)) {
-                    row_sensitive_property_info = null;
-                    row_sensitive_property_invalid = true;
+            if (row_opaque_property_info == null || row_opaque_property_info.ReflectedType != item.GetType ()) {
+                row_opaque_property_info = item.GetType ().GetProperty (row_opaque_property_name);
+                if (row_opaque_property_info == null || row_opaque_property_info.PropertyType != typeof (bool)) {
+                    row_opaque_property_info = null;
+                    row_opaque_property_invalid = true;
                     return true;
                 }
             }
 
-            return (bool)row_sensitive_property_info.GetValue (item, null);
+            return (bool)row_opaque_property_info.GetValue (item, null);
         }
 
         private string row_bold_property_name = "IsBold";
@@ -204,14 +214,5 @@ namespace Hyena.Data.Gui
 
             return (bool)row_bold_property_info.GetValue (item, null);
         }
-
-        #pragma warning disable 0169
-
-        private bool IsRowSensitive (int index)
-        {
-            return IsRowSensitive (model[index]);
-        }
-
-        #pragma warning restore 0169
     }
 }
diff --git a/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Rendering.cs b/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Rendering.cs
index 9f396fd..0cc1c68 100644
--- a/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Rendering.cs
+++ b/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Rendering.cs
@@ -103,9 +103,13 @@ namespace Hyena.Data.Gui
                 PaintHeader (damage);
             }
 
-            Theme.DrawFrameBorder (cairo_context, Allocation);
+            if (HasFocus)
+                Theme.DrawFrameBorderFocused (cairo_context, Allocation);
+            else
+                Theme.DrawFrameBorder (cairo_context, Allocation);
+
             if (Model != null) {
-                PaintRows(damage);
+                PaintRows (damage);
             }
 
             PaintDraggingColumn (damage);
@@ -131,7 +135,7 @@ namespace Hyena.Data.Gui
             cell_area.Height = header_rendering_alloc.Height;
 
             cell_context.Clip = clip;
-            cell_context.Sensitive = true;
+            cell_context.Opaque = true;
             cell_context.TextAsForeground = true;
 
             bool have_drawn_separator = false;
@@ -160,6 +164,10 @@ namespace Hyena.Data.Gui
             if (ci < 0 || column_cache.Length <= ci)
                 return;
 
+            if (ci == ActiveColumn && HasFocus && HeaderFocused) {
+                Theme.DrawColumnHeaderFocus (cairo_context, area);
+            }
+
             if (dragging) {
                 Theme.DrawColumnHighlight (cairo_context, area,
                     CairoExtensions.ColorShade (Theme.Colors.GetWidgetColor (GtkColorClass.Dark, StateType.Normal), 0.9));
@@ -260,13 +268,19 @@ namespace Hyena.Data.Gui
                             corners &= ~(CairoCorners.BottomLeft | CairoCorners.BottomRight);
                         }
 
-                        Theme.DrawRowSelection (cairo_context, single_list_alloc.X, single_list_alloc.Y,
-                            single_list_alloc.Width, single_list_alloc.Height, false, true,
-                            Theme.Colors.GetWidgetColor (GtkColorClass.Background, StateType.Selected), corners);
+                        if (HasFocus && !HeaderFocused) // Cursor out of selection.
+                            Theme.DrawRowCursor (cairo_context, single_list_alloc.X, single_list_alloc.Y,
+                                                 single_list_alloc.Width, single_list_alloc.Height,
+                                                 CairoExtensions.ColorShade (Theme.Colors.GetWidgetColor (GtkColorClass.Background, StateType.Selected), 0.85));
                     }
 
                     if (selection_height > 0) {
-                        Theme.DrawRowSelection (cairo_context, list_rendering_alloc.X, selection_y, list_rendering_alloc.Width, selection_height);
+                        Cairo.Color selection_color = Theme.Colors.GetWidgetColor (GtkColorClass.Background, StateType.Selected);
+                        if (!HasFocus || HeaderFocused)
+                            selection_color = CairoExtensions.ColorShade (selection_color, 1.1);
+
+                        Theme.DrawRowSelection (cairo_context, list_rendering_alloc.X, selection_y, list_rendering_alloc.Width, selection_height,
+                                                true, true, selection_color, CairoCorners.All);
                         selection_height = 0;
                     }
 
@@ -285,10 +299,11 @@ namespace Hyena.Data.Gui
             }
 
             if (Selection != null && Selection.Count > 1 &&
-                !selected_focus_alloc.Equals (Rectangle.Zero) && HasFocus) {
-                Theme.DrawRowSelection (cairo_context, selected_focus_alloc.X, selected_focus_alloc.Y,
-                    selected_focus_alloc.Width, selected_focus_alloc.Height, false, true,
-                    Theme.Colors.GetWidgetColor (GtkColorClass.Dark, StateType.Selected));
+                !selected_focus_alloc.Equals (Rectangle.Zero) &&
+                HasFocus && !HeaderFocused) { // Cursor inside selection.
+                Theme.DrawRowCursor (cairo_context, selected_focus_alloc.X, selected_focus_alloc.Y,
+                    selected_focus_alloc.Width, selected_focus_alloc.Height,
+                    Theme.Colors.GetWidgetColor (GtkColorClass.Text, StateType.Selected));
             }
 
             foreach (int ri in selected_rows) {
@@ -320,7 +335,7 @@ namespace Hyena.Data.Gui
             }
 
             object item = model[row_index];
-            bool sensitive = IsRowSensitive (item);
+            bool opaque = IsRowOpaque (item);
             bool bold = IsRowBold (item);
 
             Rectangle cell_area = new Rectangle ();
@@ -334,18 +349,18 @@ namespace Hyena.Data.Gui
 
                 cell_area.Width = column_cache[ci].Width;
                 cell_area.X = column_cache[ci].X1 + area.X;
-                PaintCell (item, ci, row_index, cell_area, sensitive, bold, state, false);
+                PaintCell (item, ci, row_index, cell_area, opaque, bold, state, false);
             }
 
             if (pressed_column_is_dragging && pressed_column_index >= 0) {
                 cell_area.Width = column_cache[pressed_column_index].Width;
                 cell_area.X = pressed_column_x_drag + list_rendering_alloc.X -
                     list_interaction_alloc.X - HadjustmentValue;
-                PaintCell (item, pressed_column_index, row_index, cell_area, sensitive, bold, state, true);
+                PaintCell (item, pressed_column_index, row_index, cell_area, opaque, bold, state, true);
             }
         }
 
-        private void PaintCell (object item, int column_index, int row_index, Rectangle area, bool sensitive, bool bold,
+        private void PaintCell (object item, int column_index, int row_index, Rectangle area, bool opaque, bool bold,
             StateType state, bool dragging)
         {
             ColumnCell cell = column_cache[column_index].Column.GetCell (0);
@@ -368,9 +383,11 @@ namespace Hyena.Data.Gui
             cairo_context.Save ();
             cairo_context.Translate (area.X, area.Y);
             cell_context.Area = area;
-            cell_context.Sensitive = sensitive;
+            cell_context.Opaque = opaque;
             cell.Render (cell_context, dragging ? StateType.Normal : state, area.Width, area.Height);
             cairo_context.Restore ();
+
+            AccessibleCellRedrawn (column_index, row_index);
         }
 
         private void PaintDraggingColumn (Rectangle clip)
@@ -405,7 +422,7 @@ namespace Hyena.Data.Gui
             cairo_context.Stroke ();
         }
 
-        private void InvalidateList ()
+        protected void InvalidateList ()
         {
             if (IsRealized) {
                 QueueDrawArea (list_rendering_alloc.X, list_rendering_alloc.Y, list_rendering_alloc.Width, list_rendering_alloc.Height);
@@ -455,7 +472,7 @@ namespace Hyena.Data.Gui
         }
 
         private int row_height = 32;
-        protected int RowHeight {
+        public int RowHeight {
             get {
                 if (RecomputeRowHeight) {
                     row_height = RowHeightProvider != null
diff --git a/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Windowing.cs b/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Windowing.cs
index 99ca456..c97f3a0 100644
--- a/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Windowing.cs
+++ b/src/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Windowing.cs
@@ -47,6 +47,10 @@ namespace Hyena.Data.Gui
             get { return list_rendering_alloc; }
         }
 
+        protected Gdk.Window EventWindow {
+            get { return event_window; }
+        }
+
         protected override void OnRealized ()
         {
             WidgetFlags |= WidgetFlags.Realized | WidgetFlags.NoWindow;
diff --git a/src/Hyena.Gui/Hyena.Gui.Dialogs/ExceptionDialog.cs b/src/Hyena.Gui/Hyena.Gui.Dialogs/ExceptionDialog.cs
index b9271c8..b185cea 100644
--- a/src/Hyena.Gui/Hyena.Gui.Dialogs/ExceptionDialog.cs
+++ b/src/Hyena.Gui/Hyena.Gui.Dialogs/ExceptionDialog.cs
@@ -82,6 +82,7 @@ namespace Hyena.Gui.Dialogs
             label.UseUnderline = false;
             label.Justify = Gtk.Justification.Left;
             label.LineWrap = true;
+            label.Selectable = true;
             label.SetAlignment(0.0f, 0.5f);
             label_vbox.PackStart(label, false, false, 0);
 
diff --git a/src/Hyena.Gui/Hyena.Gui.Theming/GtkTheme.cs b/src/Hyena.Gui/Hyena.Gui.Theming/GtkTheme.cs
index 3a40506..e67768a 100644
--- a/src/Hyena.Gui/Hyena.Gui.Theming/GtkTheme.cs
+++ b/src/Hyena.Gui/Hyena.Gui.Theming/GtkTheme.cs
@@ -143,10 +143,20 @@ namespace Hyena.Gui.Theming
         {
             cr.LineWidth = BorderWidth;
             cr.Color = border_color;
-            double offset = (double)BorderWidth / 2.0;
+            double offset = (double)cr.LineWidth / 2.0;
             CairoExtensions.RoundedRectangle (cr, alloc.X + offset, alloc.Y + offset,
-                alloc.Width - BorderWidth, alloc.Height - BorderWidth, Context.Radius, CairoCorners.All);
-            cr.Stroke();
+                alloc.Width - cr.LineWidth, alloc.Height - cr.LineWidth, Context.Radius, CairoCorners.All);
+            cr.Stroke ();
+        }
+
+        public override void DrawFrameBorderFocused (Cairo.Context cr, Gdk.Rectangle alloc)
+        {
+            cr.LineWidth = BorderWidth * 1.5;
+            cr.Color = CairoExtensions.ColorShade (border_color, 0.8);
+            double offset = (double)cr.LineWidth / 2.0;
+            CairoExtensions.RoundedRectangle (cr, alloc.X + offset, alloc.Y + offset,
+                alloc.Width - cr.LineWidth, alloc.Height - cr.LineWidth, Context.Radius, CairoCorners.All);
+            cr.Stroke ();
         }
 
         public override void DrawColumnHighlight (Cairo.Context cr, Gdk.Rectangle alloc, Cairo.Color color)
@@ -187,6 +197,41 @@ namespace Hyena.Gui.Theming
             grad.Destroy ();
         }
 
+        public override void DrawColumnHeaderFocus (Cairo.Context cr, Gdk.Rectangle alloc)
+        {
+            double top_offset = 2.0;
+            double right_offset = 2.0;
+
+            double margin = 0.5;
+            double line_width = 0.7;
+
+            Cairo.Color stroke_color = CairoExtensions.ColorShade (
+                Colors.GetWidgetColor (GtkColorClass.Background, StateType.Selected), 0.8);
+
+            stroke_color.A = 0.1;
+            cr.Color = stroke_color;
+
+            CairoExtensions.RoundedRectangle (cr,
+                alloc.X + margin + line_width + right_offset,
+                alloc.Y + margin + line_width + top_offset,
+                alloc.Width - (margin + line_width)*2.0 - right_offset,
+                alloc.Height - (margin + line_width)*2.0 - top_offset,
+                Context.Radius/2.0, CairoCorners.None);
+
+            cr.Fill ();
+
+            stroke_color.A = 1.0;
+            cr.LineWidth = line_width;
+            cr.Color = stroke_color;
+            CairoExtensions.RoundedRectangle (cr,
+                alloc.X + margin + line_width + right_offset,
+                alloc.Y + margin + line_width + top_offset,
+                alloc.Width - (line_width + margin)*2.0 - right_offset,
+                alloc.Height - (line_width + margin)*2.0 - right_offset,
+                Context.Radius/2.0, CairoCorners.All);
+            cr.Stroke ();
+        }
+
         public override void DrawHeaderSeparator (Cairo.Context cr, Gdk.Rectangle alloc, int x)
         {
             Cairo.Color gtk_background_color = Colors.GetWidgetColor (GtkColorClass.Background, StateType.Normal);
@@ -220,6 +265,16 @@ namespace Hyena.Gui.Theming
             cr.Fill ();
         }
 
+        public override void DrawRowCursor (Cairo.Context cr, int x, int y, int width, int height,
+                                            Cairo.Color color, CairoCorners corners)
+        {
+            cr.LineWidth = 1.25;
+            cr.Color = color;
+            CairoExtensions.RoundedRectangle (cr, x + cr.LineWidth/2.0, y + cr.LineWidth/2.0,
+                width - cr.LineWidth, height - cr.LineWidth, Context.Radius, corners, true);
+            cr.Stroke ();
+        }
+
         public override void DrawRowSelection (Cairo.Context cr, int x, int y, int width, int height,
             bool filled, bool stroked, Cairo.Color color, CairoCorners corners)
         {
@@ -264,7 +319,7 @@ namespace Hyena.Gui.Theming
             }
         }
 
-        public override void DrawRowRule(Cairo.Context cr, int x, int y, int width, int height)
+        public override void DrawRowRule (Cairo.Context cr, int x, int y, int width, int height)
         {
             cr.Color = new Cairo.Color (rule_color.R, rule_color.G, rule_color.B, Context.FillAlpha);
             cr.Rectangle (x, y, width, height);
diff --git a/src/Hyena.Gui/Hyena.Gui.Theming/Theme.cs b/src/Hyena.Gui/Hyena.Gui.Theming/Theme.cs
index db835fe..5d093c1 100644
--- a/src/Hyena.Gui/Hyena.Gui.Theming/Theme.cs
+++ b/src/Hyena.Gui/Hyena.Gui.Theming/Theme.cs
@@ -114,9 +114,13 @@ namespace Hyena.Gui.Theming
 
         public abstract void DrawFrameBorder (Cairo.Context cr, Gdk.Rectangle alloc);
 
+        public abstract void DrawFrameBorderFocused (Cairo.Context cr, Gdk.Rectangle alloc);
+
         public abstract void DrawHeaderBackground (Cairo.Context cr, Gdk.Rectangle alloc);
 
-        public abstract void DrawHeaderSeparator(Cairo.Context cr, Gdk.Rectangle alloc, int x);
+        public abstract void DrawColumnHeaderFocus (Cairo.Context cr, Gdk.Rectangle alloc);
+
+        public abstract void DrawHeaderSeparator (Cairo.Context cr, Gdk.Rectangle alloc, int x);
 
         public void DrawListBackground (Cairo.Context cr, Gdk.Rectangle alloc, bool baseColor)
         {
@@ -137,7 +141,7 @@ namespace Hyena.Gui.Theming
 
         public void DrawColumnHighlight (Cairo.Context cr, Gdk.Rectangle alloc)
         {
-            DrawColumnHighlight (cr, alloc, colors.GetWidgetColor(GtkColorClass.Background, StateType.Selected));
+            DrawColumnHighlight (cr, alloc, colors.GetWidgetColor (GtkColorClass.Background, StateType.Selected));
         }
 
         public abstract void DrawColumnHighlight (Cairo.Context cr, Gdk.Rectangle alloc, Cairo.Color color);
@@ -159,6 +163,18 @@ namespace Hyena.Gui.Theming
             DrawRowSelection (cr, x, y, width, height, filled, stroked, color, CairoCorners.All);
         }
 
+        public void DrawRowCursor (Cairo.Context cr, int x, int y, int width, int height)
+        {
+            DrawRowCursor (cr, x, y, width, height, colors.GetWidgetColor (GtkColorClass.Background, StateType.Selected));
+        }
+
+        public void DrawRowCursor (Cairo.Context cr, int x, int y, int width, int height, Cairo.Color color)
+        {
+            DrawRowCursor (cr, x, y, width, height, color, CairoCorners.All);
+        }
+
+        public abstract void DrawRowCursor (Cairo.Context cr, int x, int y, int width, int height, Cairo.Color color, CairoCorners corners);
+
         public abstract void DrawRowSelection (Cairo.Context cr, int x, int y, int width, int height,
             bool filled, bool stroked, Cairo.Color color, CairoCorners corners);
 
diff --git a/src/Hyena.Gui/Hyena.Gui/BaseWidgetAccessible.cs b/src/Hyena.Gui/Hyena.Gui/BaseWidgetAccessible.cs
new file mode 100644
index 0000000..dea5da0
--- /dev/null
+++ b/src/Hyena.Gui/Hyena.Gui/BaseWidgetAccessible.cs
@@ -0,0 +1,262 @@
+//
+// 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
+{
+#if ENABLE_ATK
+    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
+
+    }
+#endif
+}
diff --git a/src/Hyena.Gui/Hyena.Widgets/AnimatedWidget.cs b/src/Hyena.Gui/Hyena.Widgets/AnimatedWidget.cs
index e5e7165..a77eadf 100644
--- a/src/Hyena.Gui/Hyena.Widgets/AnimatedWidget.cs
+++ b/src/Hyena.Gui/Hyena.Widgets/AnimatedWidget.cs
@@ -167,7 +167,10 @@ namespace Hyena.Widgets
                         widget_alloc.Y = allocation.Height - widget_alloc.Height;
                     }
                 }
-                Widget.SizeAllocate (widget_alloc);
+
+                if (widget_alloc.Height > 0 && widget_alloc.Width > 0) {
+                    Widget.SizeAllocate (widget_alloc);
+                }
             }
         }
 
@@ -192,4 +195,4 @@ namespace Hyena.Widgets
 #endregion
 
     }
-}
\ No newline at end of file
+}
diff --git a/src/Hyena.Gui/Hyena.Widgets/EntryPopup.cs b/src/Hyena.Gui/Hyena.Widgets/EntryPopup.cs
new file mode 100644
index 0000000..f11d586
--- /dev/null
+++ b/src/Hyena.Gui/Hyena.Widgets/EntryPopup.cs
@@ -0,0 +1,226 @@
+//
+// EntryPopup.cs
+//
+// Author:
+//   Neil Loknath <neil loknath gmail com>
+//
+// Copyright (C) 2009 Neil Loknath
+//
+// 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.Timers;
+using Gdk;
+using Gtk;
+
+namespace Hyena.Widgets
+{
+    public class EntryPopup : Gtk.Window
+    {
+        private Entry text_entry;
+        private uint timeout_id = 0;
+
+        public event EventHandler<EventArgs> Changed;
+        public event EventHandler<KeyPressEventArgs> KeyPressed;
+
+        public EntryPopup (string text) : this ()
+        {
+            Text = text;
+        }
+
+        public EntryPopup () : base (Gtk.WindowType.Popup)
+        {
+            CanFocus = true;
+            Resizable = false;
+            TypeHint = Gdk.WindowTypeHint.Utility;
+            Modal = true;
+
+            Frame frame = new Frame ();
+            frame.Shadow = ShadowType.EtchedIn;
+            Add (frame);
+
+            HBox box = new HBox ();
+            text_entry = new Entry();
+            box.PackStart (text_entry, true, true, 0);
+            box.BorderWidth = 3;
+
+            frame.Add (box);
+            frame.ShowAll ();
+
+            text_entry.Text = String.Empty;
+            text_entry.CanFocus = true;
+
+            //TODO figure out why this event does not get raised
+            text_entry.FocusOutEvent += (o, a) => {
+                if (hide_when_focus_lost) {
+                    HidePopup ();
+                }
+            };
+
+            text_entry.KeyReleaseEvent += delegate (object o, KeyReleaseEventArgs args) {
+                if (args.Event.Key == Gdk.Key.Escape ||
+                    args.Event.Key == Gdk.Key.Return ||
+                    args.Event.Key == Gdk.Key.Tab) {
+
+                    HidePopup ();
+                }
+
+                InitializeDelayedHide ();
+            };
+
+            text_entry.KeyPressEvent += (o, a) => OnKeyPressed (a);
+
+            text_entry.Changed += (o, a) => {
+                if (GdkWindow.IsVisible) {
+                    OnChanged (a);
+                }
+            };
+        }
+
+        public new bool HasFocus {
+            get { return text_entry.HasFocus; }
+            set { text_entry.HasFocus = value; }
+        }
+
+        public string Text {
+            get { return text_entry.Text; }
+            set { text_entry.Text = value; }
+        }
+
+        public Entry Entry {
+            get { return text_entry; }
+        }
+
+        private bool hide_after_timeout = true;
+        public bool HideAfterTimeout {
+            get { return hide_after_timeout; }
+            set { hide_after_timeout = value; }
+        }
+
+        private uint timeout = 5000;
+        public uint Timeout {
+            get { return timeout; }
+            set { timeout = value; }
+        }
+
+        private bool hide_when_focus_lost = true;
+        public bool HideOnFocusOut {
+            get { return hide_when_focus_lost; }
+            set { hide_when_focus_lost = value; }
+        }
+
+        private bool reset_when_hiding = true;
+        public bool ResetOnHide {
+            get { return reset_when_hiding; }
+            set { reset_when_hiding = value; }
+        }
+
+        public override void Dispose ()
+        {
+            text_entry.Dispose ();
+            base.Dispose ();
+        }
+
+        public new void GrabFocus ()
+        {
+            text_entry.GrabFocus ();
+        }
+
+        private void ResetDelayedHide ()
+        {
+            if (timeout_id > 0) {
+                GLib.Source.Remove (timeout_id);
+                timeout_id = 0;
+            }
+        }
+
+        private void InitializeDelayedHide ()
+        {
+            ResetDelayedHide ();
+            timeout_id = GLib.Timeout.Add (timeout, delegate {
+                            HidePopup ();
+                            return false;
+                        });
+        }
+
+        private void HidePopup ()
+        {
+            ResetDelayedHide ();
+            Hide ();
+
+            if (reset_when_hiding) {
+                text_entry.Text = String.Empty;
+            }
+        }
+
+        protected virtual void OnChanged (EventArgs args)
+        {
+            var handler = Changed;
+            if (handler != null) {
+                handler (this, EventArgs.Empty);
+            }
+        }
+
+        protected virtual void OnKeyPressed (KeyPressEventArgs args)
+        {
+            var handler = KeyPressed;
+            if (handler != null) {
+                handler (this, args);
+            }
+        }
+
+        //TODO figure out why this event does not get raised
+        protected override bool OnFocusOutEvent (Gdk.EventFocus evnt)
+        {
+            if (hide_when_focus_lost) {
+                HidePopup ();
+                return true;
+            }
+
+            return base.OnFocusOutEvent (evnt);
+        }
+
+        protected override bool OnExposeEvent (Gdk.EventExpose evnt)
+        {
+            InitializeDelayedHide ();
+            return base.OnExposeEvent (evnt);
+        }
+
+        protected override bool OnButtonReleaseEvent (Gdk.EventButton evnt)
+        {
+            if (!text_entry.HasFocus && hide_when_focus_lost) {
+                HidePopup ();
+                return true;
+            }
+
+            return base.OnButtonReleaseEvent (evnt);
+        }
+
+        protected override bool OnButtonPressEvent (Gdk.EventButton evnt)
+        {
+            if (!text_entry.HasFocus && hide_when_focus_lost) {
+                HidePopup ();
+                return true;
+            }
+
+            return base.OnButtonPressEvent (evnt);
+        }
+    }
+}
diff --git a/src/Hyena.Gui/Hyena.Widgets/HigMessageDialog.cs b/src/Hyena.Gui/Hyena.Widgets/HigMessageDialog.cs
index d23d108..338dc8e 100644
--- a/src/Hyena.Gui/Hyena.Widgets/HigMessageDialog.cs
+++ b/src/Hyena.Gui/Hyena.Widgets/HigMessageDialog.cs
@@ -32,7 +32,6 @@ namespace Hyena.Widgets
 {
     public class HigMessageDialog : Gtk.Dialog
     {
-        private Gtk.AccelGroup accel_group;
         private Gtk.Image image;
         private Gtk.VBox label_vbox;
         private Gtk.Label message_label;
@@ -54,9 +53,6 @@ namespace Hyena.Widgets
             VBox.Spacing = 12;
             ActionArea.Layout = Gtk.ButtonBoxStyle.End;
 
-            accel_group = new Gtk.AccelGroup ();
-            AddAccelGroup (accel_group);
-
             Gtk.HBox hbox = new Gtk.HBox (false, 12);
             hbox.BorderWidth = 5;
             hbox.Show ();
@@ -177,12 +173,9 @@ namespace Hyena.Widgets
             AddActionWidget (button, response);
 
             if (isDefault) {
+                Default = button;
                 DefaultResponse = response;
-                button.AddAccelerator ("activate",
-                               accel_group,
-                               (uint) Gdk.Key.Return,
-                               0,
-                               Gtk.AccelFlags.Visible);
+                button.GrabDefault ();
             }
         }
 
diff --git a/src/Hyena.Gui/Hyena.Widgets/ImageButton.cs b/src/Hyena.Gui/Hyena.Widgets/ImageButton.cs
index 33f67e1..9431b0a 100644
--- a/src/Hyena.Gui/Hyena.Widgets/ImageButton.cs
+++ b/src/Hyena.Gui/Hyena.Widgets/ImageButton.cs
@@ -33,20 +33,32 @@ namespace Hyena.Widgets
 {
     public class ImageButton : Button
     {
+        private Image image;
+        private Label label;
+        private HBox hbox;
+
+        public Image ImageWidget { get { return image; } }
+        public Label LabelWidget { get { return label; } }
+
+        public uint InnerPadding {
+            get { return hbox.BorderWidth; }
+            set { hbox.BorderWidth = value; }
+        }
+
         public ImageButton (string text, string iconName) : this (text, iconName, Gtk.IconSize.Button)
         {
         }
 
         public ImageButton (string text, string iconName, Gtk.IconSize iconSize) : base ()
         {
-            Image image = new Image ();
+            image = new Image ();
             image.IconName = iconName;
             image.IconSize = (int) iconSize;
 
-            Label label = new Label ();
+            label = new Label ();
             label.MarkupWithMnemonic = text;
 
-            HBox hbox = new HBox ();
+            hbox = new HBox ();
             hbox.Spacing = 2;
             hbox.PackStart (image, false, false, 0);
             hbox.PackStart (label, true, true, 0);
diff --git a/src/Hyena.Gui/Hyena.Widgets/MenuButton.cs b/src/Hyena.Gui/Hyena.Widgets/MenuButton.cs
index f40dbbd..138bb86 100644
--- a/src/Hyena.Gui/Hyena.Widgets/MenuButton.cs
+++ b/src/Hyena.Gui/Hyena.Widgets/MenuButton.cs
@@ -73,6 +73,7 @@ namespace Hyena.Widgets
                 alignment.Add (arrow);
                 box.PackStart (alignment, false, false, 5);
                 size_widget = box;
+                FocusChain = new Widget[] {toggle_button, box};
             } else {
                 toggle_button.Add (button_widget);
                 size_widget = toggle_button;
diff --git a/src/Hyena.Gui/Hyena.Widgets/MessageBar.cs b/src/Hyena.Gui/Hyena.Widgets/MessageBar.cs
index c1e901e..1934697 100644
--- a/src/Hyena.Gui/Hyena.Widgets/MessageBar.cs
+++ b/src/Hyena.Gui/Hyena.Widgets/MessageBar.cs
@@ -39,7 +39,7 @@ namespace Hyena.Widgets
         private HBox box;
         private HBox button_box;
         private AnimatedImage image;
-        private Label label;
+        private WrapLabel label;
         private Button close_button;
 
         private Window win;
@@ -76,8 +76,7 @@ namespace Hyena.Widgets
             } catch {
             }
 
-            label = new Label ();
-            label.Xalign = 0.0f;
+            label = new WrapLabel ();
             label.Show ();
 
             box.PackStart (image, false, false, 0);
diff --git a/src/Hyena.Gui/Hyena.Widgets/RatingEntry.cs b/src/Hyena.Gui/Hyena.Widgets/RatingEntry.cs
index 4fcf73a..ddaf603 100644
--- a/src/Hyena.Gui/Hyena.Widgets/RatingEntry.cs
+++ b/src/Hyena.Gui/Hyena.Widgets/RatingEntry.cs
@@ -47,6 +47,13 @@ namespace Hyena.Widgets
         public event EventHandler Changing;
         public event EventHandler Changed;
 
+        static RatingEntry ()
+        {
+#if ENABLE_ATK
+            RatingAccessibleFactory.Init ();
+#endif
+        }
+
         public RatingEntry () : this (0)
         {
             WidgetFlags |= Gtk.WidgetFlags.NoWindow;
@@ -381,6 +388,78 @@ namespace Hyena.Widgets
 
 #region Test Module
 
+#if ENABLE_ATK
+    public class RatingAccessible : Atk.Object, Atk.Value, Atk.ValueImplementor
+    {
+        private RatingEntry rating;
+
+        public RatingAccessible (IntPtr raw) : base (raw)
+        {
+            Hyena.Log.Information ("RatingAccessible raw ctor..");
+        }
+
+        public RatingAccessible (GLib.Object widget): base ()
+        {
+            rating = widget as RatingEntry;
+            Name = "Rating entry";
+            Description = "Rating entry, from 0 to 5 stars";
+            Role = Atk.Role.Slider;
+        }
+
+        public void GetMaximumValue (ref GLib.Value val)
+        {
+            val = new GLib.Value (5);
+        }
+
+        public void GetMinimumIncrement (ref GLib.Value val)
+        {
+            val = new GLib.Value (1);
+        }
+
+        public void GetMinimumValue (ref GLib.Value val)
+        {
+            val = new GLib.Value (0);
+        }
+
+        public void GetCurrentValue (ref GLib.Value val)
+        {
+            val = new GLib.Value (rating.Value);
+        }
+
+        public bool SetCurrentValue (GLib.Value val)
+        {
+            int r = (int) val.Val;
+            if (r <= 0 || r > 5) {
+                return false;
+            }
+
+            rating.Value = (int) val.Val;
+            return true;
+        }
+    }
+#endif
+
+#if ENABLE_ATK
+    internal class RatingAccessibleFactory : Atk.ObjectFactory
+    {
+        public static void Init ()
+        {
+            new RatingAccessibleFactory ();
+            Atk.Global.DefaultRegistry.SetFactoryType ((GLib.GType)typeof (RatingEntry), (GLib.GType)typeof (RatingAccessibleFactory));
+        }
+
+        protected override Atk.Object OnCreateAccessible (GLib.Object obj)
+        {
+            return new RatingAccessible (obj);
+        }
+
+        protected override GLib.GType OnGetAccessibleType ()
+        {
+            return RatingAccessible.GType;
+        }
+    }
+#endif
+
     [Hyena.Gui.TestModule ("Rating Entry")]
     internal class RatingEntryTestModule : Gtk.Window
     {
diff --git a/src/Hyena.Gui/Hyena.Widgets/SimpleTable.cs b/src/Hyena.Gui/Hyena.Widgets/SimpleTable.cs
new file mode 100644
index 0000000..15417c7
--- /dev/null
+++ b/src/Hyena.Gui/Hyena.Widgets/SimpleTable.cs
@@ -0,0 +1,103 @@
+//
+// SimpleTable.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.Collections.Generic;
+using Gtk;
+
+namespace Hyena.Widgets
+{
+    public class SimpleTable<T> : Table
+    {
+        private bool added_any;
+
+        private List<T> items = new List<T> ();
+        private Dictionary<T, Widget []> item_widgets = new Dictionary<T, Widget []> ();
+
+        public SimpleTable () : base (1, 2, false)
+        {
+            ColumnSpacing = 5;
+            RowSpacing = 5;
+        }
+
+        public void AddRow (T item, params Widget [] cols)
+        {
+            InsertRow (item, (uint)items.Count, cols);
+        }
+
+        public void InsertRow (T item, uint row, params Widget [] cols)
+        {
+            if (!added_any) {
+                added_any = true;
+            } else if (NColumns != cols.Length) {
+                throw new ArgumentException ("cols", String.Format ("Expected {0} column widgets, same as previous calls to Add", NColumns));
+            }
+
+            Resize ((uint) items.Count + 1, (uint) cols.Length);
+
+            for (int y = items.Count - 1; y >= row; y--) {
+                for (uint x = 0; x < NColumns; x++) {
+                    var widget = item_widgets[items[y]][x];
+                    Remove (widget);
+                    Attach (widget, x, x + 1, (uint) y + 1, (uint) y + 2);
+                }
+            }
+
+            items.Insert ((int)row, item);
+            item_widgets[item] = cols;
+
+            for (uint x = 0; x < NColumns; x++) {
+                Attach (cols[x], x, x + 1, row, row + 1);
+            }
+        }
+
+        public void RemoveRow (T item)
+        {
+            FreezeChildNotify ();
+
+            foreach (var widget in item_widgets[item]) {
+                Remove (widget);
+            }
+
+            int index = items.IndexOf (item);
+            for (int y = index + 1; y < items.Count; y++) {
+                for (uint x = 0; x < NColumns; x++) {
+                    var widget = item_widgets[items[y]][x];
+                    Remove (widget);
+                    Attach (widget, x, x + 1, (uint) y - 1, (uint) y);
+                }
+            }
+
+            Resize ((uint) Math.Max (1, items.Count - 1), NColumns);
+
+            ThawChildNotify ();
+            items.Remove (item);
+            item_widgets.Remove (item);
+        }
+    }
+}
diff --git a/src/Hyena.Gui/Hyena.Widgets/WrapLabel.cs b/src/Hyena.Gui/Hyena.Widgets/WrapLabel.cs
index b0a04e7..c2a4923 100644
--- a/src/Hyena.Gui/Hyena.Widgets/WrapLabel.cs
+++ b/src/Hyena.Gui/Hyena.Widgets/WrapLabel.cs
@@ -102,8 +102,13 @@ namespace Hyena.Widgets
         protected override bool OnExposeEvent (Gdk.EventExpose evnt)
         {
             if (evnt.Window == GdkWindow) {
+                // Center the text vertically
+                int lw, lh;
+                layout.GetPixelSize (out lw, out lh);
+                int y = Allocation.Y + (Allocation.Height - lh) / 2;
+
                 Gtk.Style.PaintLayout (Style, GdkWindow, State, false,
-                    evnt.Area, this, null, Allocation.X, Allocation.Y, layout);
+                    evnt.Area, this, null, Allocation.X, y, layout);
             }
 
             return true;
diff --git a/src/Hyena.Gui/Makefile.am b/src/Hyena.Gui/Makefile.am
index 0178685..0cec6fe 100644
--- a/src/Hyena.Gui/Makefile.am
+++ b/src/Hyena.Gui/Makefile.am
@@ -97,6 +97,9 @@ FILES = \
 	Hyena.Gui.Theming/ThemeEngine.cs \
 	Hyena.Gui.Theming/GtkTheme.cs \
 	Hyena.Data.Gui/ListView/ListView_DragAndDrop.cs \
+	Hyena.Gui/BaseWidgetAccessible.cs \
+	Hyena.Widgets/EntryPopup.cs \
+	Hyena.Widgets/SimpleTable.cs \
 	Hyena.Gui/PangoCairoHelper.cs \
 	Hyena.Widgets/AnimatedBox.cs \
 	Hyena.Widgets/AnimatedHBox.cs \
@@ -123,6 +126,14 @@ FILES = \
 	Hyena.Data.Gui/ISizeRequestCell.cs \
 	Hyena.Data.Gui/ITooltipCell.cs \
 	Hyena.Data.Gui/ColumnCellRating.cs \
+	Hyena.Data.Gui/Accessibility/ColumnCellAccessible.cs \
+	Hyena.Data.Gui/Accessibility/ColumnCellTextAccessible.cs \
+	Hyena.Data.Gui/Accessibility/ColumnHeaderCellTextAccessible.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/ListView/ListView_Accessible.cs \
 	Hyena.Widgets/RatingEntry.cs \
 	Hyena.Widgets/RatingMenuItem.cs \
 	Hyena.Widgets/ComplexMenuItem.cs \



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