banshee r3924 - in trunk/banshee: . src/Core/Banshee.ThickClient src/Core/Banshee.ThickClient/Banshee.Gui.Dialogs src/Core/Banshee.ThickClient/Banshee.Query.Gui src/Core/Banshee.Widgets src/Core/Banshee.Widgets/Banshee.Widgets src/Core/Banshee.Widgets/Resources src/Extensions/Banshee.NotificationArea/Banshee.NotificationArea src/Libraries/Hyena.Gui src/Libraries/Hyena.Gui/Hyena.Data.Gui src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView src/Libraries/Hyena.Gui/Hyena.Gui src/Libraries/Hyena.Gui/Hyena.Widgets src/Libraries/Hyena.Gui/Resources



Author: abock
Date: Sat May 17 21:35:43 2008
New Revision: 3924
URL: http://svn.gnome.org/viewvc/banshee?rev=3924&view=rev

Log:
2008-05-17  Aaron Bockover  <abock gnome org>

* src/Libraries/Hyena.Gui/Hyena.Data.Gui/Column.cs: FIxed improper ctor
chain; set Min/MaxWidth if a cell is ISizeRequestCell when packing
by calling GetSize on the cell

* src/Libraries/Hyena.Gui/Hyena.Data.Gui/ColumnCell.cs: Allow setting
BoundObject on a cell, added a BoundObjectParent that can be used
to track which data item (instead of which data property on an item)
is being bound

* src/Libraries/Hyena.Gui/Hyena.Data.Gui/ColumnCellCheckBox.cs: Implemented
a check box interactive cell renderer that binds to boolean properties

* src/Libraries/Hyena.Gui/Hyena.Data.Gui/ColumnCellRating.cs: Implemented
a rating cell renderer based on the new rating renderer

* src/Libraries/Hyena.Gui/Hyena.Data.Gui/IInteractiveCell.cs: Interface
for cells that would like to have keyboard/mouse events proxied to them

* src/Libraries/Hyena.Gui/Hyena.Data.Gui/ISizeRequestCell.cs: Interface
for cells that actually care about their size and refuse to adapt to
what the view can provide

* src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Interaction.cs:
Implemented first pass at cell level interaction - namely through 
ProxyEventToCell which currently supports transforming and proxying
Gdk.EventButton and Gdk.EventMotion events from the view to a cell; also
fix a bunch of potential NRE when the model selection is null

* src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Model.cs:
* src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Rendering.cs:
Fix a bunch of potential NRE when the model selection is null

* src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListViewTestModule.cs: Started
a test module for stressing functionality of the ListView

* src/Libraries/Hyena.Gui/Hyena.Gui/RatingRenderer.cs: A new renderer to
do all drawing/logic for things that want to embed a rating

* src/Libraries/Hyena.Gui/Hyena.Widgets/ComplexMenuItem.cs: Moved to
Hyena.Widgets

* src/Libraries/Hyena.Gui/Hyena.Widgets/RatingEntry.cs: Moved to 
Hyena.Widgets and largely rewritten to use the new RatingRenderer

* src/Libraries/Hyena.Gui/Hyena.Widgets/RatingMenuItem.cs:  Moved to
Hyena.Widgets and do not call into the EventBox rendering hack since the
RatingEntry no longer derives from EventBox

* src/Core/Banshee.ThickClient/Banshee.Gui.Dialogs/TrackEditor.cs:
* src/Core/Banshee.ThickClient/Banshee.Query.Gui/RatingQueryValueEntry.cs:
* src/Core/Banshee.Widgets/Banshee.Widgets/CustomActionProxy.cs:
* src/Core/Banshee.Widgets/Banshee.Widgets/RatingActionProxy.cs:
* src/Extensions/Banshee.NotificationArea/Banshee.NotificationArea/NotificationAreaService.cs:
Updated to reflect the move of Rating stuff to Hyena.Gui



Added:
   trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ColumnCellCheckBox.cs
   trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ColumnCellRating.cs
   trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/IInteractiveCell.cs
   trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ISizeRequestCell.cs
   trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListViewTestModule.cs
   trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Gui/RatingRenderer.cs
   trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Widgets/ComplexMenuItem.cs
      - copied, changed from r3918, /trunk/banshee/src/Core/Banshee.Widgets/Banshee.Widgets/ComplexMenuItem.cs
   trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Widgets/RatingEntry.cs
      - copied, changed from r3918, /trunk/banshee/src/Core/Banshee.Widgets/Banshee.Widgets/RatingEntry.cs
   trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Widgets/RatingMenuItem.cs
      - copied, changed from r3918, /trunk/banshee/src/Core/Banshee.Widgets/Banshee.Widgets/RatingMenuItem.cs
   trunk/banshee/src/Libraries/Hyena.Gui/Resources/
Removed:
   trunk/banshee/src/Core/Banshee.Widgets/Banshee.Widgets/ComplexMenuItem.cs
   trunk/banshee/src/Core/Banshee.Widgets/Banshee.Widgets/RatingEntry.cs
   trunk/banshee/src/Core/Banshee.Widgets/Banshee.Widgets/RatingMenuItem.cs
   trunk/banshee/src/Core/Banshee.Widgets/Resources/rating-rated.png
   trunk/banshee/src/Core/Banshee.Widgets/Resources/rating-unrated.png
Modified:
   trunk/banshee/ChangeLog
   trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Dialogs/TrackEditor.cs
   trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Query.Gui/RatingQueryValueEntry.cs
   trunk/banshee/src/Core/Banshee.ThickClient/Banshee.ThickClient.mdp
   trunk/banshee/src/Core/Banshee.Widgets/Banshee.Widgets.mdp
   trunk/banshee/src/Core/Banshee.Widgets/Banshee.Widgets/CustomActionProxy.cs
   trunk/banshee/src/Core/Banshee.Widgets/Banshee.Widgets/RatingActionProxy.cs
   trunk/banshee/src/Core/Banshee.Widgets/Makefile.am
   trunk/banshee/src/Extensions/Banshee.NotificationArea/Banshee.NotificationArea/NotificationAreaService.cs
   trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/Column.cs
   trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ColumnCell.cs
   trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Header.cs
   trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Interaction.cs
   trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Model.cs
   trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Rendering.cs
   trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Gui.mdp
   trunk/banshee/src/Libraries/Hyena.Gui/Makefile.am

Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Dialogs/TrackEditor.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Dialogs/TrackEditor.cs	(original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Dialogs/TrackEditor.cs	Sat May 17 21:35:43 2008
@@ -44,6 +44,7 @@
 using Banshee.Collection.Database;
 
 using Hyena.Gui;
+using Hyena.Widgets;
 
 namespace Banshee.Gui.Dialogs
 {

Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Query.Gui/RatingQueryValueEntry.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Query.Gui/RatingQueryValueEntry.cs	(original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Query.Gui/RatingQueryValueEntry.cs	Sat May 17 21:35:43 2008
@@ -31,6 +31,7 @@
 
 using Hyena.Query;
 using Hyena.Query.Gui;
+using Hyena.Widgets;
 
 using Banshee.Widgets;
 using Banshee.Query;

Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.ThickClient.mdp
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.ThickClient.mdp	(original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.ThickClient.mdp	Sat May 17 21:35:43 2008
@@ -129,6 +129,7 @@
     <ProjectReference type="Gac" localcopy="True" refto="Mono.Addins.Gui, Version=0.4.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756" />
     <ProjectReference type="Gac" localcopy="True" refto="Mono.Addins.Setup, Version=0.4.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756" />
     <ProjectReference type="Gac" localcopy="True" refto="System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
+    <ProjectReference type="Gac" localcopy="True" refto="taglib-sharp, Version=2.0.3.0, Culture=neutral, PublicKeyToken=db62eba44689b5b0" />
   </References>
   <Deployment.LinuxDeployData generateScript="False" />
   <MonoDevelop.Autotools.MakefileInfo IntegrationEnabled="True" RelativeMakefileName="Makefile.am">

Modified: trunk/banshee/src/Core/Banshee.Widgets/Banshee.Widgets.mdp
==============================================================================
--- trunk/banshee/src/Core/Banshee.Widgets/Banshee.Widgets.mdp	(original)
+++ trunk/banshee/src/Core/Banshee.Widgets/Banshee.Widgets.mdp	Sat May 17 21:35:43 2008
@@ -8,9 +8,6 @@
     </Configuration>
   </Configurations>
   <Contents>
-    <File name="Resources/rating-rated.png" subtype="Code" buildaction="EmbedAsResource" />
-    <File name="Resources/rating-unrated.png" subtype="Code" buildaction="EmbedAsResource" />
-    <File name="Banshee.Widgets/ComplexMenuItem.cs" subtype="Code" buildaction="Compile" />
     <File name="Banshee.Widgets/DateButton.cs" subtype="Code" buildaction="Compile" />
     <File name="Banshee.Widgets/DictionaryComboBox.cs" subtype="Code" buildaction="Compile" />
     <File name="Banshee.Widgets/DiscUsageDisplay.cs" subtype="Code" buildaction="Compile" />
@@ -20,8 +17,6 @@
     <File name="Banshee.Widgets/LinkLabel.cs" subtype="Code" buildaction="Compile" />
     <File name="Banshee.Widgets/MessagePane.cs" subtype="Code" buildaction="Compile" />
     <File name="Banshee.Widgets/PropertyTable.cs" subtype="Code" buildaction="Compile" />
-    <File name="Banshee.Widgets/RatingEntry.cs" subtype="Code" buildaction="Compile" />
-    <File name="Banshee.Widgets/RatingMenuItem.cs" subtype="Code" buildaction="Compile" />
     <File name="Banshee.Widgets/SearchEntry.cs" subtype="Code" buildaction="Compile" />
     <File name="Banshee.Widgets/SeekSlider.cs" subtype="Code" buildaction="Compile" />
     <File name="Banshee.Widgets/StreamPositionLabel.cs" subtype="Code" buildaction="Compile" />
@@ -32,6 +27,7 @@
     <File name="Banshee.Widgets/CustomActionProxy.cs" subtype="Code" buildaction="Compile" />
     <File name="Banshee.Widgets/RatingActionProxy.cs" subtype="Code" buildaction="Compile" />
     <File name="Banshee.Widgets/MenuTile.cs" subtype="Code" buildaction="Compile" />
+    <File name="Resources" subtype="Directory" buildaction="Compile" />
   </Contents>
   <References>
     <ProjectReference type="Gac" localcopy="True" refto="gtk-sharp, Version=2.8.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />

Modified: trunk/banshee/src/Core/Banshee.Widgets/Banshee.Widgets/CustomActionProxy.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Widgets/Banshee.Widgets/CustomActionProxy.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Widgets/Banshee.Widgets/CustomActionProxy.cs	Sat May 17 21:35:43 2008
@@ -31,6 +31,8 @@
 using Gtk;
 using Mono.Unix;
 
+using Hyena.Widgets;
+
 namespace Banshee.Widgets
 {
     public abstract class CustomActionProxy

Modified: trunk/banshee/src/Core/Banshee.Widgets/Banshee.Widgets/RatingActionProxy.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Widgets/Banshee.Widgets/RatingActionProxy.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Widgets/Banshee.Widgets/RatingActionProxy.cs	Sat May 17 21:35:43 2008
@@ -31,6 +31,8 @@
 using Gtk;
 using Mono.Unix;
 
+using Hyena.Widgets;
+
 namespace Banshee.Widgets
 {
     public class RatingActionProxy : CustomActionProxy

Modified: trunk/banshee/src/Core/Banshee.Widgets/Makefile.am
==============================================================================
--- trunk/banshee/src/Core/Banshee.Widgets/Makefile.am	(original)
+++ trunk/banshee/src/Core/Banshee.Widgets/Makefile.am	Sat May 17 21:35:43 2008
@@ -3,7 +3,6 @@
 ASSEMBLY_BUILD_FLAGS = -unsafe
 LINK = $(REF_BANSHEE_WIDGETS)
 SOURCES =  \
-	Banshee.Widgets/ComplexMenuItem.cs \
 	Banshee.Widgets/CustomActionProxy.cs \
 	Banshee.Widgets/DateButton.cs \
 	Banshee.Widgets/DictionaryComboBox.cs \
@@ -17,8 +16,6 @@
 	Banshee.Widgets/MessagePane.cs \
 	Banshee.Widgets/PropertyTable.cs \
 	Banshee.Widgets/RatingActionProxy.cs \
-	Banshee.Widgets/RatingEntry.cs \
-	Banshee.Widgets/RatingMenuItem.cs \
 	Banshee.Widgets/SearchEntry.cs \
 	Banshee.Widgets/SeekSlider.cs \
 	Banshee.Widgets/StreamPositionLabel.cs \
@@ -26,9 +23,7 @@
 	Banshee.Widgets/TileView.cs \
 	Banshee.Widgets/VolumeButton.cs  
 
-RESOURCES =  \
-	Resources/rating-rated.png \
-	Resources/rating-unrated.png
+RESOURCES =
 
 include $(top_srcdir)/build/build.mk
 

Modified: trunk/banshee/src/Extensions/Banshee.NotificationArea/Banshee.NotificationArea/NotificationAreaService.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.NotificationArea/Banshee.NotificationArea/NotificationAreaService.cs	(original)
+++ trunk/banshee/src/Extensions/Banshee.NotificationArea/Banshee.NotificationArea/NotificationAreaService.cs	Sat May 17 21:35:43 2008
@@ -42,7 +42,8 @@
 using Banshee.Gui;
 using Banshee.Collection.Gui;
 using Banshee.MediaEngine;
-using Banshee.Widgets;
+
+using Hyena.Widgets;
 
 namespace Banshee.NotificationArea 
 {

Modified: trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/Column.cs
==============================================================================
--- trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/Column.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/Column.cs	Sat May 17 21:35:43 2008
@@ -56,15 +56,15 @@
         {
         }
         
-        public Column (string title, ColumnCell cell, double width) 
-            : this (null, title, cell, width, true)
+        public Column (string title, ColumnCell cell, double width)
+            : this (title, cell, width, true)
         {
         }
         
         public Column (string title, ColumnCell cell, double width, bool visible) 
             : this (null, title, cell, width, visible)
         {
-            this.header_cell = new ColumnHeaderCellText(HeaderCellDataHandler);
+            this.header_cell = new ColumnHeaderCellText (HeaderCellDataHandler);
         }
         
         public Column (ColumnCell header_cell, string title, ColumnCell cell, double width)
@@ -93,6 +93,14 @@
         
         public void PackStart (ColumnCell cell)
         {
+            ISizeRequestCell sr_cell = cell as ISizeRequestCell;
+            if (sr_cell != null && sr_cell.RestrictSize) {
+                int w, h;
+                sr_cell.GetSize (out w, out h);
+                MinWidth = w;
+                MaxWidth = w;
+            }
+            
             cells.Insert (0, cell);
         }
         

Modified: trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ColumnCell.cs
==============================================================================
--- trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ColumnCell.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ColumnCell.cs	Sat May 17 21:35:43 2008
@@ -39,6 +39,7 @@
         private string property;
         private PropertyInfo property_info;
         private object bound_object;
+        private object bound_object_parent;
             
         public ColumnCell (string property, bool expand)
         {
@@ -48,17 +49,24 @@
 
         public void BindListItem (object item)
         {
-            if (item == null)
+            if (item == null) {
                 return;
-
+            }
+            
+            bound_object_parent = item;
+            
             if (property != null) {
-                if (property_info == null || property_info.ReflectedType != item.GetType ()) {
-                    property_info = item.GetType ().GetProperty (property);
-                }
-
-                bound_object = property_info.GetValue (item, null);
+                EnsurePropertyInfo ();
+                bound_object = property_info.GetValue (bound_object_parent, null);
             } else {
-                bound_object = item;
+                bound_object = bound_object_parent;
+            }
+        }
+        
+        private void EnsurePropertyInfo ()
+        {
+            if (property_info == null || property_info.ReflectedType != bound_object_parent.GetType ()) {
+                property_info = bound_object_parent.GetType ().GetProperty (property);
             }
         }
         
@@ -72,6 +80,16 @@
         
         protected object BoundObject {
             get { return bound_object; }
+            set {
+                if (property != null) {
+                    EnsurePropertyInfo ();
+                    property_info.SetValue (bound_object_parent, value, null);
+                }
+            }
+        }
+        
+        protected object BoundObjectParent {
+            get { return bound_object_parent; }
         }
         
         public abstract void Render (CellContext context, StateType state, double cellWidth, double cellHeight);

Added: trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ColumnCellCheckBox.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ColumnCellCheckBox.cs	Sat May 17 21:35:43 2008
@@ -0,0 +1,109 @@
+//
+// ColumnCellCheckBox.cs
+//
+// Author:
+//   Aaron Bockover <abockover novell com>
+//
+// Copyright (C) 2008 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 Gtk;
+
+namespace Hyena.Data.Gui
+{    
+    public class ColumnCellCheckBox : ColumnCell, IInteractiveCell, ISizeRequestCell
+    {   
+        public ColumnCellCheckBox (string property, bool expand) : base (property, expand)
+        {
+        }
+            
+        public override void Render (CellContext context, StateType state, double cellWidth, double cellHeight)
+        {
+            int cell_width = context.Area.Width - 2 * Xpad;
+            int cell_height = context.Area.Height - 2 * Ypad;
+            int x = context.Area.X + xpad + ((cell_width - Size) / 2);
+            int y = context.Area.Y + ypad + ((cell_height - Size) / 2);
+            
+            Style.PaintCheck (context.Widget.Style, context.Drawable, state, 
+                Value ? ShadowType.In : ShadowType.Out, 
+                context.Clip, context.Widget, "cellcheck", x, y, Size, Size);
+        }
+        
+        private object last_pressed_bound;
+        
+        public bool ButtonEvent (int x, int y, bool pressed, Gdk.EventButton evnt)
+        {
+            if (pressed) {
+                last_pressed_bound = BoundObjectParent;
+                return false;
+            }
+            
+            if (last_pressed_bound == BoundObjectParent) {
+                Value = !Value;
+                last_pressed_bound = null;
+            }
+            
+            return true;
+        }
+        
+        public bool MotionEvent (int x, int y, Gdk.EventMotion evnt)
+        {
+            return false;
+        }
+        
+        public void GetSize (out int width, out int height)
+        {
+            width = 2 * Xpad + Size;
+            height = 2 * Ypad + Size;
+        }
+        
+        private bool restrict_size = true;
+        public bool RestrictSize {
+            get { return restrict_size; }
+            set { restrict_size = value; }
+        }
+        
+        private bool Value {
+            get { return (bool)BoundObject; }
+            set { BoundObject = value; }
+        }
+        
+        private int size = 13;
+        public int Size {
+            get { return size; }
+            set { size = value; }
+        }
+        
+        private int xpad = 2;
+        public int Xpad {
+            get { return xpad; }
+            set { xpad = value; }
+        }
+        
+        public int ypad = 2;
+        public int Ypad {
+            get { return ypad; }
+            set { ypad = value; }
+        }
+    }
+}

Added: trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ColumnCellRating.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ColumnCellRating.cs	Sat May 17 21:35:43 2008
@@ -0,0 +1,140 @@
+//
+// ColumnCellRating.cs
+//
+// Author:
+//   Aaron Bockover <abockover novell com>
+//
+// Copyright (C) 2008 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 Gtk;
+
+using Hyena.Gui;
+using Hyena.Gui.Theming;
+
+namespace Hyena.Data.Gui
+{    
+    public class ColumnCellRating : ColumnCell, IInteractiveCell, ISizeRequestCell
+    {   
+        private object last_pressed_bound;
+        private object hover_bound;
+        private int hover_value;
+        private Gdk.Rectangle actual_area_hack;
+        private RatingRenderer renderer = new RatingRenderer ();
+        
+        public ColumnCellRating (string property, bool expand) : base (property, expand)
+        {
+        }
+            
+        public override void Render (CellContext context, StateType state, double cellWidth, double cellHeight)
+        {
+            Gdk.Rectangle area = new Gdk.Rectangle (0, 0, context.Area.Width, context.Area.Height);
+            
+            // FIXME: Compute font height and set to renderer.Size
+            
+            renderer.Value = Value;
+            renderer.Render (context.Context, area, context.Theme.Colors.GetWidgetColor (GtkColorClass.Text, state), 
+                hover_bound == BoundObjectParent && hover_bound != null, hover_value);
+            
+            // FIXME: Something is hosed in the view when computing cell dimensions
+            // The cell width request is always smaller than the actual cell, so
+            // this value is preserved once we compute it from rendering so the 
+            // input stuff can do its necessary calculations
+            actual_area_hack = area;
+        }
+        
+        public bool ButtonEvent (int x, int y, bool pressed, Gdk.EventButton evnt)
+        {
+            if (pressed) {
+                last_pressed_bound = BoundObjectParent;
+                return false;
+            }
+            
+            if (last_pressed_bound == BoundObjectParent) {
+                Value = RatingFromPosition (x);
+                last_pressed_bound = null;
+            }
+            
+            return true;
+        }
+        
+        public bool MotionEvent (int x, int y, Gdk.EventMotion evnt)
+        {
+            int value = RatingFromPosition (x);
+        
+            if (hover_bound == BoundObjectParent && value == hover_value) {
+                return false;
+            }
+            
+            hover_bound = BoundObjectParent;
+            hover_value = value;
+            return true;
+        }
+        
+        public void GetSize (out int width, out int height)
+        {
+            width = renderer.Width;
+            height = renderer.Height;
+        }
+        
+        private int RatingFromPosition (double x)
+        {
+            return renderer.RatingFromPosition (actual_area_hack, x);
+        }
+        
+        private bool restrict_size = true;
+        public bool RestrictSize {
+            get { return restrict_size; }
+            set { restrict_size = value; }
+        }
+        
+        private int Value {
+            get { return renderer.ClampValue ((int)BoundObject); }
+            set { BoundObject = renderer.ClampValue (value); }
+        }
+        
+        public int MaxRating {
+            get { return renderer.MaxRating; }
+            set { renderer.MaxRating = value; }
+        }
+        
+        public int MinRating {
+            get { return renderer.MinRating; }
+            set { renderer.MinRating = value; }
+        }
+        
+        public int RatingLevels {
+            get { return renderer.RatingLevels; }
+        }
+        
+        public int Xpad {
+            get { return renderer.Xpad; }
+            set { renderer.Xpad = value; }
+        }
+        
+        public int Ypad {
+            get { return renderer.Ypad; }
+            set { renderer.Ypad = value; }
+        }
+    }
+}

Added: trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/IInteractiveCell.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/IInteractiveCell.cs	Sat May 17 21:35:43 2008
@@ -0,0 +1,39 @@
+//
+// IInteractiveCell.cs
+//
+// Author:
+//   Aaron Bockover <abockover novell com>
+//
+// Copyright (C) 2008 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 Gdk;
+
+namespace Hyena.Data.Gui
+{
+    public interface IInteractiveCell
+    {
+        bool ButtonEvent (int x, int y, bool pressed, Gdk.EventButton evnt);
+        bool MotionEvent (int x, int y, Gdk.EventMotion evnt);
+    }
+}

Added: trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ISizeRequestCell.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ISizeRequestCell.cs	Sat May 17 21:35:43 2008
@@ -0,0 +1,39 @@
+//
+// ISizeRequestCell.cs
+//
+// Author:
+//   Aaron Bockover <abockover novell com>
+//
+// Copyright (C) 2008 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;
+
+namespace Hyena.Data.Gui
+{   
+    public interface ISizeRequestCell
+    {
+        bool RestrictSize { get; set; }
+        void GetSize (out int width, out int height);
+    }
+}

Modified: trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Header.cs
==============================================================================
--- trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Header.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Header.cs	Sat May 17 21:35:43 2008
@@ -358,6 +358,7 @@
                     return column.Column;
                 }
             }
+            
             return null;
         }
         

Modified: trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Interaction.cs
==============================================================================
--- trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Interaction.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Interaction.cs	Sat May 17 21:35:43 2008
@@ -81,25 +81,27 @@
             
             int row_index = Math.Min (Model.Count - 1, Math.Max (0, focused_row_index + relative_row));
 
-            if ((modifier & Gdk.ModifierType.ControlMask) != 0) {
-                // Don't change the selection
-            } else if ((modifier & Gdk.ModifierType.ShiftMask) != 0) {
-                // Behave like nautilus: if and arrow key + shift is pressed and the currently focused item
-                // is not selected, select it and don't move the focus or vadjustment.
-                // Otherwise, select the new row and scroll etc as necessary.
-                if ((relative_row * relative_row != 1)) {
-                    Selection.SelectFromFirst (row_index, true);
-                } else if (Selection.Contains (focused_row_index)) {
-                    Selection.SelectFromFirst (row_index, true);
+            if (Selection != null) {
+                if ((modifier & Gdk.ModifierType.ControlMask) != 0) {
+                    // Don't change the selection
+                } else if ((modifier & Gdk.ModifierType.ShiftMask) != 0) {
+                    // Behave like nautilus: if and arrow key + shift is pressed and the currently focused item
+                    // is not selected, select it and don't move the focus or vadjustment.
+                    // Otherwise, select the new row and scroll etc as necessary.
+                    if (relative_row * relative_row != 1) {
+                        Selection.SelectFromFirst (row_index, true);
+                    } else if (Selection.Contains (focused_row_index)) {
+                        Selection.SelectFromFirst (row_index, true);
+                    } else {
+                        Selection.Select (focused_row_index);
+                        return true;
+                    }
                 } else {
-                    Selection.Select (focused_row_index);
-                    return true;
+                    Selection.Clear (false);
+                    Selection.Select (row_index);
                 }
-            } else {
-                Selection.Clear (false);
-                Selection.Select (row_index);
             }
-
+            
             // Scroll if needed
             double y_at_row = GetYAtRow (row_index);
             if (align_y) {
@@ -174,7 +176,7 @@
 
                 case Gdk.Key.Return:
                 case Gdk.Key.KP_Enter:
-                    if (focused_row_index != -1) {
+                    if (Selection != null && focused_row_index != -1) {
                         Selection.Clear (false);
                         Selection.Select (focused_row_index);
                         OnRowActivated ();
@@ -183,7 +185,7 @@
                     break;
                 
                 case Gdk.Key.space:
-                    if (focused_row_index != 1) {
+                    if (Selection != null && focused_row_index != 1) {
                         Selection.ToggleSelect (focused_row_index);
                         handled = true;
                     }
@@ -197,6 +199,77 @@
             return base.OnKeyPressEvent (press);
         }
         
+        private void ProxyEventToCell (Gdk.Event evnt, bool press)
+        {
+            int evnt_x, evnt_y;
+            
+            Gdk.EventButton evnt_button = evnt as Gdk.EventButton;
+            Gdk.EventMotion evnt_motion = evnt as Gdk.EventMotion;
+            
+            if (evnt_motion != null) {
+                evnt_x = (int)evnt_motion.X;
+                evnt_y = (int)evnt_motion.Y;
+            } else if (evnt_button != null) {
+                evnt_x = (int)evnt_button.X;
+                evnt_y = (int)evnt_button.Y;
+            } else {
+                return;
+            }
+            
+            int y = evnt_y - list_interaction_alloc.Y;
+            int x = evnt_x - list_interaction_alloc.X;
+            
+            int row_index = GetRowAtY (y);
+            if (row_index < 0 || row_index >= Model.Count) {
+                return;
+            }
+            
+            Column column = GetColumnAt (x);
+            if (column == null) {
+                return;
+            }
+            
+            CachedColumn cached_column = GetCachedColumnForColumn (column);
+            
+            ColumnCell cell = column.GetCell (0);
+            IInteractiveCell icell = cell as IInteractiveCell;
+            if (icell == null) {
+                return;
+            }
+            
+            // Turn the view-absolute coordinates into cell-relative coordinates
+            x -= cached_column.X1;
+            int page_offset = (int)vadjustment.Value % RowHeight;
+            y = (y + page_offset) % RowHeight;
+            
+            // Bind the row to the cell and then send it a synthesized input event
+            cell.BindListItem (model[row_index]);
+            bool redraw = false;
+            
+            if (evnt_motion != null) {
+                redraw = icell.MotionEvent (x, y, evnt_motion);
+            } else if (evnt_button != null) {
+                redraw = icell.ButtonEvent (x, y, press, evnt_button);
+            }
+            
+            // FIXME: This rectangle might not be correct, but I don't
+            // think QueueDrawArea works at all... maybe Scott has
+            // some insight? I smell issues with the blit canvases
+            // --Aaron
+            //
+            // Gdk.Rectangle rect = new Gdk.Rectangle ();
+            // rect.X = cached_column.X1;
+            // rect.Y = (int)GetYAtRow (row_index);
+            // rect.Width = cached_column.Width;
+            // rect.Height = RowHeight;
+            //
+            // QueueDrawArea (rect.X, rect.Y, rect.Width, rect.Height);
+            
+            if (redraw) {
+                QueueDraw (); // OUCH
+            }
+        }   
+        
 #region OnButtonPress
 
         protected override bool OnButtonPressEvent (Gdk.EventButton evnt)
@@ -252,11 +325,13 @@
             
             int row_index = GetRowAtY (y);
 
-            if (row_index >= Model.Count) {
+            if (row_index < 0 || row_index >= Model.Count) {
                 return true;
             }
             
-            if (evnt.Button == 1 && evnt.Type != Gdk.EventType.TwoButtonPress && 
+            ProxyEventToCell (evnt, true);
+            
+            if (Selection != null && evnt.Button == 1 && evnt.Type != Gdk.EventType.TwoButtonPress && 
                 (evnt.State & Gdk.ModifierType.ControlMask) == 0 && Selection.Contains (row_index)) {
                 return true;
             }
@@ -268,7 +343,7 @@
 
             if (evnt.Button == 1 && evnt.Type == Gdk.EventType.TwoButtonPress) {
                 OnRowActivated ();
-            } else {
+            } else if (Selection != null) {
                 if ((evnt.State & Gdk.ModifierType.ControlMask) != 0) {
                     if (evnt.Button == 3) {
                         if (!Selection.Contains (row_index)) {
@@ -331,6 +406,7 @@
                 return OnHeaderButtonRelease (evnt);
             } else if (list_interaction_alloc.Contains ((int)evnt.X, (int)evnt.Y) && model != null &&
                 (evnt.State & (Gdk.ModifierType.ShiftMask | Gdk.ModifierType.ControlMask)) == 0) {
+                ProxyEventToCell (evnt, false);
                 return OnListButtonRelease (evnt);
             }
 
@@ -373,11 +449,12 @@
                 return true;
             }
             
-            if (Selection.Contains (row_index) && Selection.Count > 1) {
+            if (Selection != null && Selection.Contains (row_index) && Selection.Count > 1) {
                 Selection.Clear (false);
                 Selection.Select (row_index);
                 FocusRow (row_index);
             }
+            
             return true;
         }
         
@@ -409,6 +486,8 @@
                 ResizeColumn (x);
             }
             
+            ProxyEventToCell (evnt, false);
+            
             return true;
         }
         
@@ -497,7 +576,7 @@
 
         protected double GetYAtRow (int row)
         {
-            double y = (double) RowHeight * row;
+            double y = (double)RowHeight * row;
             return y;
         }
           
@@ -541,8 +620,13 @@
                 }
             }
             
-            hadjustment.Change ();
-            vadjustment.Change ();
+            if (hadjustment != null) {
+                hadjustment.Change ();
+            }
+
+            if (vadjustment != null) {
+                vadjustment.Change ();
+            }
         }
         
         private void OnHadjustmentChanged (object o, EventArgs args)
@@ -563,7 +647,9 @@
         
         private void ScrollTo (Adjustment adjustment, double val)
         {
-            adjustment.Value = Math.Max (0.0, Math.Min (val, adjustment.Upper - adjustment.PageSize));
+            if (adjustment != null) {
+                adjustment.Value = Math.Max (0.0, Math.Min (val, adjustment.Upper - adjustment.PageSize));
+            }
         }
 
         public void ScrollTo (int index)

Modified: trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Model.cs
==============================================================================
--- trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Model.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Model.cs	Sat May 17 21:35:43 2008
@@ -94,7 +94,7 @@
                 }
             }
             
-            if (Model != null) {
+            if (Model != null && Selection != null) {
                 Selection.MaxIndex = Model.Count - 1;
             }
             

Modified: trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Rendering.cs
==============================================================================
--- trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Rendering.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Rendering.cs	Sat May 17 21:35:43 2008
@@ -200,10 +200,6 @@
 
         private void PaintList ()
         {
-            if (model == null) {
-                return;
-            }
-            
             // Render the sort effect to the GdkWindow.
             if (sort_column_index != -1 && (!pressed_column_is_dragging || pressed_column_index != sort_column_index)) {
                 CachedColumn col = column_cache[sort_column_index];
@@ -265,7 +261,7 @@
                 // If the bottom of the stuff we're shifting up is part of a selection
                 // that continues down into the new stuff, be sure that we render the
                 // whole selection block so the gradient looks nice.
-                while (Selection.Contains (canvas_last_row) && canvas_last_row > first_row) {
+                while (Selection != null && Selection.Contains (canvas_last_row) && canvas_last_row > first_row) {
                     canvas_last_row--;
                 }
                 
@@ -281,14 +277,16 @@
                 // If the top of the stuff we're shifting down is part of a selection
                 // that continues up into the new stuff, be sure that we render the
                 // whole selection block so the gradient looks nice.
-                while (Selection.Contains (canvas_first_row) && canvas_first_row < last_row) {
+                while (Selection != null && Selection.Contains (canvas_first_row) && canvas_first_row < last_row) {
                     canvas_first_row++;
                 }
                 
                 last_row = canvas_first_row;
             }
             
-            PaintRows (first_row, Math.Min (model.Count, last_row), first_row_y);
+            if (model != null) {
+                PaintRows (first_row, Math.Min (model.Count, last_row), first_row_y);
+            }
             
             // Destroy the cairo context.
             ((IDisposable)cairo_context.Target).Dispose ();
@@ -319,7 +317,7 @@
             selected_rows.Clear ();
 
             for (int ri = first_row; ri < last_row; ri++) {
-                if (Selection.Contains (ri)) {
+                if (Selection != null && Selection.Contains (ri)) {
                     if (selection_height == 0) {
                         selection_y = single_list_alloc.Y;
                     }
@@ -347,7 +345,7 @@
                         cairo_context.Restore ();
                     }
                     
-                    if (focused_row_index == ri && !Selection.Contains (ri) && HasFocus) {
+                    if (Selection != null && focused_row_index == ri && !Selection.Contains (ri) && HasFocus) {
                         CairoCorners corners = CairoCorners.All;
                         
                         if (Selection.Contains (ri - 1)) {
@@ -379,7 +377,8 @@
                     canvas_alloc.Width, selection_height);
             }
             
-            if (Selection.Count > 1 && !selected_focus_alloc.Equals (Rectangle.Zero) && HasFocus) {
+            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));

Added: trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListViewTestModule.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListViewTestModule.cs	Sat May 17 21:35:43 2008
@@ -0,0 +1,202 @@
+//
+// ListViewTestModule.cs
+//
+// Author:
+//   Aaron Bockover <abockover novell com>
+//
+// Copyright (C) 2008 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;
+
+using Hyena.Data;
+using Hyena.Collections;
+using Hyena.Gui;
+
+using Selection = Hyena.Collections.Selection;
+
+namespace Hyena.Data.Gui
+{
+    [TestModule ("List View")]
+    public class ListViewTestModule : Window
+    {
+        private View view;
+        private Model model;
+        
+        public ListViewTestModule () : base ("ListView")
+        {
+            WindowPosition = WindowPosition.Center;
+            SetDefaultSize (800, 600);
+            
+            ScrolledWindow scroll = new ScrolledWindow ();
+            scroll.HscrollbarPolicy = PolicyType.Automatic;
+            scroll.VscrollbarPolicy = PolicyType.Automatic;
+            
+            view = new View ();
+            model = new Model ();
+            
+            scroll.Add (view);
+            Add (scroll);
+            ShowAll ();
+            
+            view.SetModel (model);
+        }
+        
+        private class View : ListView<ModelItem>
+        {
+            public View ()
+            {
+                ColumnController = new ColumnController ();
+                ColumnController.AddRange (
+                    new Column (String.Empty, new ColumnCellCheckBox ("F", true), 1),
+                    new Column ("Apples", new ColumnCellText ("B", true), 1),
+                    new Column ("Pears", new ColumnCellText ("C", true), 1),
+                    new Column ("How Hot", new ColumnCellRating ("G", true), 1),
+                    new Column ("Peaches", new ColumnCellText ("D", true), 1),
+                    new Column ("Doodle", new ColumnCellDoodle ("E", true), 1),
+                    new Column ("GUIDs!OMG", new ColumnCellText ("A", true), 1)
+                );
+            }
+        }
+        
+        private class Model : IListModel<ModelItem>
+        {
+            private List<ModelItem> store = new List<ModelItem> ();
+            private Selection selection = new Selection ();
+            
+            public event EventHandler Cleared;
+            public event EventHandler Reloaded;
+            
+            public Model ()
+            {
+                Random random = new Random (0);
+                for (int i = 0; i < 1000; i++) {
+                    store.Add (new ModelItem (i, random));
+                }
+            }
+            
+            public void Clear ()
+            {
+            }
+            
+            public void Reload ()
+            {
+            }
+            
+            public int Count {
+                get { return store.Count; }
+            }
+            
+            public ModelItem this[int index] {
+                get { return store[index]; }
+            }
+            
+            public Selection Selection {
+                get { return selection; }
+            }
+        }
+        
+        private class ModelItem 
+        {
+            public ModelItem (int i, Random rand)
+            {
+                a = Guid.NewGuid ().ToString ();
+                b = rand.Next (0, 255);
+                c = rand.NextDouble ();
+                d = String.Format ("Item {0}", i);
+                e = new List<Gdk.Point> ();
+                f = rand.Next (0, 1) == 1;
+                g = rand.Next (0, 5);
+            }
+        
+            string a; public string A { get { return a; } }
+            int b;    public int    B { get { return b; } }
+            double c; public double C { get { return c; } }
+            string d; public string D { get { return d; } }
+            List<Gdk.Point> e; public List<Gdk.Point> E { get { return e; } }
+            bool f; public bool F { get { return f; } set { f = value; } }
+            int g; public int G { get { return g; } set { g = value; } }
+        }
+        
+        private class ColumnCellDoodle : ColumnCell, IInteractiveCell
+        {
+            private Random random = new Random ();
+            private bool red = false;
+            
+            public ColumnCellDoodle (string property, bool expand) : base (property, expand)
+            {
+            }
+            
+            public override void Render (CellContext context, StateType state, double cellWidth, double cellHeight)
+            {
+                red = !red;
+                Cairo.Context cr = context.Context;
+                cr.Rectangle (0, 0, cellWidth, cellHeight);
+                cr.Color = CairoExtensions.RgbaToColor (red ? 0xff000099 : 0x00000099);
+                cr.Fill ();
+                
+                List<Gdk.Point> points = Points;
+                for (int i = 0, n = points.Count; i < n; i++) {
+                    if (i == 0) {
+                        cr.MoveTo (points[i].X, points[i].Y);
+                    } else {
+                        cr.LineTo (points[i].X, points[i].Y);
+                    }
+                }
+                
+                cr.Color = CairoExtensions.RgbToColor ((uint)random.Next (0xffffff));
+                cr.LineWidth = 1;
+                cr.Stroke ();
+            }
+            
+            private object last_pressed_bound;
+            
+            public bool ButtonEvent (int x, int y, bool pressed, Gdk.EventButton evnt)
+            {
+                if (!pressed) {
+                    last_pressed_bound = null;
+                    return false;
+                }
+                
+                last_pressed_bound = BoundObject;
+                Points.Add (new Gdk.Point (x, y));
+                return true;
+            }
+            
+            public bool MotionEvent (int x, int y, Gdk.EventMotion evnt)
+            {
+                if (last_pressed_bound == BoundObject) {
+                    Points.Add (new Gdk.Point (x, y));
+                    return true;
+                }
+                
+                return false;
+            }
+            
+            private List<Gdk.Point> Points {
+                get { return (List<Gdk.Point>)BoundObject; }
+            }
+        }
+    }
+}

Modified: trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Gui.mdp
==============================================================================
--- trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Gui.mdp	(original)
+++ trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Gui.mdp	Sat May 17 21:35:43 2008
@@ -73,6 +73,16 @@
     <File name="Hyena.Gui/TestModuleAttribute.cs" subtype="Code" buildaction="Compile" />
     <File name="Hyena.Gui/TestModuleRunner.cs" subtype="Code" buildaction="Compile" />
     <File name="Hyena.Widgets/SegmentedBar.cs" subtype="Code" buildaction="Compile" />
+    <File name="Hyena.Data.Gui/ListViewTestModule.cs" subtype="Code" buildaction="Compile" />
+    <File name="Hyena.Data.Gui/IInteractiveCell.cs" subtype="Code" buildaction="Compile" />
+    <File name="Hyena.Data.Gui/ColumnCellCheckBox.cs" subtype="Code" buildaction="Compile" />
+    <File name="Hyena.Data.Gui/ISizeRequestCell.cs" subtype="Code" buildaction="Compile" />
+    <File name="Hyena.Data.Gui/ColumnCellRating.cs" subtype="Code" buildaction="Compile" />
+    <File name="Hyena.Widgets/RatingEntry.cs" subtype="Code" buildaction="Compile" />
+    <File name="Hyena.Widgets/RatingMenuItem.cs" subtype="Code" buildaction="Compile" />
+    <File name="Hyena.Widgets/ComplexMenuItem.cs" subtype="Code" buildaction="Compile" />
+    <File name="Hyena.Gui/RatingRenderer.cs" subtype="Code" buildaction="Compile" />
+    <File name="Resources" subtype="Directory" buildaction="Compile" />
   </Contents>
   <References>
     <ProjectReference type="Gac" localcopy="True" refto="System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
@@ -83,12 +93,13 @@
     <ProjectReference type="Gac" localcopy="True" refto="pango-sharp, Version=2.8.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
     <ProjectReference type="Gac" localcopy="True" refto="glib-sharp, Version=2.10.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
     <ProjectReference type="Gac" localcopy="True" refto="System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
+    <ProjectReference type="Gac" localcopy="True" refto="atk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
   </References>
   <Deployment.LinuxDeployData generateScript="False" />
   <MonoDevelop.Autotools.MakefileInfo IntegrationEnabled="True" RelativeMakefileName="Makefile.am">
     <BuildFilesVar Sync="True" Name="SOURCES" />
     <DeployFilesVar />
-    <ResourcesVar />
+    <ResourcesVar Sync="True" Name="RESOURCES" />
     <OthersVar />
     <GacRefVar />
     <AsmRefVar />

Added: trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Gui/RatingRenderer.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Gui/RatingRenderer.cs	Sat May 17 21:35:43 2008
@@ -0,0 +1,178 @@
+//
+// RatingRenderer.cs
+//
+// Author:
+//   Aaron Bockover <abockover novell com>
+//
+// Copyright (C) 2008 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 Gtk;
+using Cairo;
+
+namespace Hyena.Gui
+{
+    public class RatingRenderer
+    {
+        private static double [,] star_plot = new double[,] { 
+            { 0, 0.425 },
+            { 0.375, 0.375 },
+            { 0.5, 0.05 },
+            { 0.625, 0.375 },
+            { 1, 0.425 },
+            { 0.75, 0.625 },
+            { 0.8, 0.95 },
+            { 0.5, 0.75 },
+            { 0.2, 0.95 },
+            { 0.25, 0.625 },
+            { 0, 0.425 },
+        };
+
+        public RatingRenderer ()
+        {
+        }
+        
+        public virtual void Render (Context cr, Gdk.Rectangle area, Color color, bool isHovering, int hoverValue)
+        {
+            if (!(Value > MinRating || (Value == MinRating && isHovering))) {
+                return;
+            }
+            
+            Cairo.Color fill_color = color;
+            fill_color.A = 0.8;
+            Cairo.Color stroke_color = fill_color;
+            stroke_color.A = 0.35;
+            Cairo.Color hover_fill_color = fill_color;
+            hover_fill_color.A = 0.45;
+                
+            double x, y;
+            ComputePosition (area, out x, out y);
+            
+            cr.LineWidth = 1.0;
+            cr.Translate (0.5, 0.5);
+                    
+            for (int i = MinRating + 1, s = isHovering ? MaxRating : Value; i <= s; i++, x += Size) {
+                bool fill = i <= Value && Value > MinRating;
+                bool hover_fill = i <= hoverValue && hoverValue > MinRating;
+                double scale = fill || hover_fill ? Size : Size - 2;
+                double ofs = fill || hover_fill ? 0 : 1;
+                    
+                for (int p = 0, n = star_plot.GetLength (0); p < n; p++) {
+                    double px = x + ofs + star_plot[p, 0] * scale;
+                    double py = y + ofs + star_plot[p, 1] * scale;
+                    if (p == 0) {
+                        cr.MoveTo (px, py);
+                    } else {
+                        cr.LineTo (px, py);
+                    }
+                }
+                cr.ClosePath ();
+                
+                if (fill || hover_fill) {
+                    cr.Color = fill ? fill_color : hover_fill_color;
+                    cr.Fill ();
+                } else {
+                    cr.Color = stroke_color;
+                    cr.Stroke ();
+                }
+            }
+        }
+        
+        private void ComputePosition (Gdk.Rectangle area, out double x, out double y)
+        {
+            double cell_width = area.Width - 2 * Xpad;
+            double cell_height = area.Height - 2 * Ypad;
+            
+            double stars_width = MaxRating * Size;
+            double stars_height = Size;
+            
+            x = area.X + Xpad + (cell_width - stars_width) / 2.0;
+            y = area.Y + Ypad + (cell_height - stars_height) / 2.0;
+        }
+        
+        public int RatingFromPosition (Gdk.Rectangle area, double x)
+        {
+            double r_x, r_y;
+            ComputePosition (area, out r_x, out r_y);
+            return x <= r_x ? 0 : Clamp (MinRating, MaxRating, (int)Math.Ceiling ((x - r_x) / Size) + MinRating);
+        }
+        
+        private static int Clamp (int min, int max, int value)
+        {
+            return Math.Max (min, Math.Min (max, value));
+        }
+        
+        public int ClampValue (int value)
+        {
+            return Clamp (MinRating, MaxRating, value);
+        }
+        
+        private int value;
+        public int Value {
+            get { return ClampValue (this.value); }
+            set { this.value = ClampValue (value); }
+        }
+        
+        private int size = 14;
+        public int Size {
+            get { return size; }
+            set { size = value; }
+        }
+        
+        private int min_rating = 0;
+        public int MinRating {
+            get { return min_rating; }
+            set { min_rating = value; }
+        }
+        
+        private int max_rating = 5;
+        public int MaxRating {
+            get { return max_rating; }
+            set { max_rating = value; }
+        }
+        
+        public int RatingLevels {
+            get { return MaxRating - MinRating + 1; }
+        }
+        
+        private int xpad = 2;
+        public int Xpad {
+            get { return xpad; }
+            set { xpad = value; }
+        }
+        
+        public int ypad = 2;
+        public int Ypad {
+            get { return ypad; }
+            set { ypad = value; }
+        }
+        
+        public int Width {
+            get { return Xpad * 2 + RatingLevels * Size; }
+        }
+        
+        public int Height {
+            get { return Ypad * 2 + Size; }
+        }
+    }
+}

Copied: trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Widgets/ComplexMenuItem.cs (from r3918, /trunk/banshee/src/Core/Banshee.Widgets/Banshee.Widgets/ComplexMenuItem.cs)
==============================================================================
--- /trunk/banshee/src/Core/Banshee.Widgets/Banshee.Widgets/ComplexMenuItem.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Widgets/ComplexMenuItem.cs	Sat May 17 21:35:43 2008
@@ -5,7 +5,7 @@
 //   Aaron Bockover <abockover novell com>
 //   Gabriel Burt <gburt novell com>
 //
-// Copyright (C) 2007 Novell, Inc.
+// Copyright (C) 2007-2008 Novell, Inc.
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
@@ -30,7 +30,7 @@
 using System;
 using Gtk;
 
-namespace Banshee.Widgets
+namespace Hyena.Widgets
 {
     public class ComplexMenuItem : MenuItem
     {

Copied: trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Widgets/RatingEntry.cs (from r3918, /trunk/banshee/src/Core/Banshee.Widgets/Banshee.Widgets/RatingEntry.cs)
==============================================================================
--- /trunk/banshee/src/Core/Banshee.Widgets/Banshee.Widgets/RatingEntry.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Widgets/RatingEntry.cs	Sat May 17 21:35:43 2008
@@ -1,12 +1,12 @@
 // 
 // RatingEntry.cs
 //
-// Author:
-//   Gabriel Burt <gabriel burt gmail com>
+// Authors:
 //   Aaron Bockover <abockover novell com>
+//   Gabriel Burt <gabriel burt gmail com>
 //
+// Copyright (C) 2006-2008 Novell, Inc.
 // Copyright (C) 2006 Gabriel Burt
-// Copyright (C) 2006-2007 Novell, Inc.
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
@@ -29,187 +29,207 @@
 //
 
 using Gtk;
-using Gdk;
 using System;
 
-namespace Banshee.Widgets
+using Hyena.Gui;
+
+namespace Hyena.Widgets
 {
-    public class RatingEntry : Gtk.EventBox
+    public class RatingEntry : Widget
     {
-        private static Pixbuf default_icon_rated;
-        private static Pixbuf default_icon_not_rated;
-        
-        private int max_rating = 5;
-        private int min_rating = 1;
-        private Pixbuf icon_rated;
-        private Pixbuf icon_blank;
-        
-        public object RatedObject;
-        
-        private int rating;
-        private bool embedded;
-        private int y_offset = 4, x_offset = 4;
-        private bool preview_on_hover = false;
-        private Gdk.Pixbuf display_pixbuf;
+        private RatingRenderer renderer;
+        private Gdk.Rectangle event_alloc;
+        private int hover_value = -1;
+        private bool interior_focus;
+        private int focus_width;
+        private Gdk.Window event_window;
         
         public event EventHandler Changing;
         public event EventHandler Changed;
         
-#region Constructors
-        
-        public RatingEntry () : this (1) 
-        {
-        }
-        
-        public RatingEntry (int rating) : this (rating, false)
+        public RatingEntry () : this (0) 
         {
+            WidgetFlags |= Gtk.WidgetFlags.NoWindow;
         }
 
-        public RatingEntry (int rating, bool embedded)
+        public RatingEntry (int rating)
         {
-            if (IconRated.Height != IconNotRated.Height || IconRated.Width != IconNotRated.Width) {
-                throw new ArgumentException ("Rating widget requires that rated and blank icons have the same height and width");
-            }
-            
-            this.rating = rating;
-            this.embedded = embedded;
-            
-            if (embedded) {
-                y_offset = 0;
-                x_offset = 0;
-            }
-            
-            //PreviewOnHover = true;
-            //Events |= Gdk.EventMask.PointerMotionMask | Gdk.EventMask.LeaveNotifyMask;
-            
+            renderer = new RatingRenderer ();
+            renderer.Value = rating;
             CanFocus = true;
-            
-            display_pixbuf = new Pixbuf (Gdk.Colorspace.Rgb, true, 8, Width, Height);
-            display_pixbuf.Fill (0xffffff00);
-            DrawRating (Value);
-            
-            EnsureStyle ();
-            ShowAll ();
+            Name = "GtkEntry";
         }
         
-#endregion
-
-        ~RatingEntry ()
+        protected virtual void OnChanging ()
         {
-            display_pixbuf.Dispose ();
-            display_pixbuf = null;
-            
-            icon_rated = null;
-            icon_blank = null;
+            EventHandler handler = Changing;
+            if (handler != null) {
+                handler (this, new EventArgs ());
+            }
         }
-        
-#region Public API
-        
-        /*public Pixbuf DrawRating (int val)
+
+        protected virtual void OnChanged ()
         {
-            Pixbuf buf = new Pixbuf (Gdk.Colorspace.Rgb, true, 8, Width, Height);
-            DrawRating (buf, val);
-            return buf;
-        }*/
-        
+            QueueDraw ();
+
+            EventHandler handler = Changed;
+            if (handler != null) {
+                handler (this, new EventArgs ());
+            }
+        }
+
         internal void SetValueFromPosition (int x)
         {
-            Value = RatingFromPosition (x);
+            Value = renderer.RatingFromPosition (event_alloc, x);
         }
         
-#endregion
-
 #region Public Properties
 
+        private bool preview_on_hover = true;
+        public bool PreviewOnHover {
+            get { return preview_on_hover; }
+            set { preview_on_hover = value; }
+        }
+
+        private bool has_frame = true;
+        public bool HasFrame {
+            get { return has_frame; }
+            set { has_frame = value; QueueResize (); }
+        }
+
         public int Value {
-            get { return rating; }
+            get { return renderer.Value; }
             set {
-                if (rating != value && value >= min_rating - 1 && value <= max_rating) {
-                    rating = value;
+                if (renderer.Value != value && renderer.Value >= renderer.MinRating && value <= renderer.MaxRating) {
+                    renderer.Value = value;
                     OnChanging ();
                     OnChanged ();
                 }
             }
         }
         
-        public int XOffset {
-            get { return x_offset; }
-        }
-        
-        public int YOffset {
-            get { return y_offset; }
-        }
-        
-        public Pixbuf DisplayPixbuf {
-            get { return display_pixbuf; }
-        }
-        
         public int MaxRating {
-            get { return max_rating; }
-            set { max_rating = value; }
+            get { return renderer.MaxRating; }
+            set { renderer.MaxRating = value; }
         }
         
         public int MinRating {
-            get { return min_rating; }
-            set { min_rating = value; }
+            get { return renderer.MinRating; }
+            set { renderer.MinRating = value; }
         }
         
-        public int NumLevels {
-            get { return max_rating - min_rating + 1; }
+        public int RatingLevels {
+            get { return renderer.RatingLevels; }
         }
         
-        public static Pixbuf DefaultIconRated {
-            get { return default_icon_rated ?? default_icon_rated = Gdk.Pixbuf.LoadFromResource ("rating-rated.png"); }
-            set { default_icon_rated = value; }
+        private object rated_object;
+        public object RatedObject {
+            get { return rated_object; }
+            set { rated_object = value; }
         }
         
-        public static Pixbuf DefaultIconNotRated {
-            get { return default_icon_not_rated ?? default_icon_not_rated = Gdk.Pixbuf.LoadFromResource ("rating-unrated.png"); }
-            set { default_icon_not_rated = value; }
+#endregion
+
+#region Protected Gtk.Widget Overrides
+
+        protected override void OnRealized ()
+        {
+            WidgetFlags |= WidgetFlags.Realized | WidgetFlags.NoWindow;
+            GdkWindow = Parent.GdkWindow;
+            
+            Gdk.WindowAttr attributes = new Gdk.WindowAttr ();
+            attributes.WindowType = Gdk.WindowType.Child;
+            attributes.X = Allocation.X;
+            attributes.Y = Allocation.Y;
+            attributes.Width = Allocation.Width;
+            attributes.Height = Allocation.Height;
+            attributes.Wclass = Gdk.WindowClass.InputOnly;
+            attributes.EventMask = (int)(
+                Gdk.EventMask.PointerMotionMask |
+                Gdk.EventMask.EnterNotifyMask |
+                Gdk.EventMask.LeaveNotifyMask |
+                Gdk.EventMask.KeyPressMask |
+                Gdk.EventMask.KeyReleaseMask |
+                Gdk.EventMask.ButtonPressMask |
+                Gdk.EventMask.ButtonReleaseMask |
+                Gdk.EventMask.ExposureMask);
+                
+            Gdk.WindowAttributesType attributes_mask =
+                Gdk.WindowAttributesType.X | 
+                Gdk.WindowAttributesType.Y |
+                Gdk.WindowAttributesType.Wmclass;
+            
+            event_window = new Gdk.Window (GdkWindow, attributes, attributes_mask);
+            event_window.UserData = Handle;
+            
+            base.OnRealized ();
         }
-        
-        public Pixbuf IconRated {
-            get { return icon_rated ?? icon_rated = DefaultIconRated; }
-            set { icon_rated = value; }
+         
+        protected override void OnUnrealized ()
+        {
+            WidgetFlags &= ~WidgetFlags.Realized;
+            
+            event_window.UserData = IntPtr.Zero;
+            event_window.Destroy ();
+            event_window = null;
+            
+            base.OnUnrealized ();
         }
         
-        public Pixbuf IconNotRated {
-            get { return icon_blank ?? icon_blank = DefaultIconNotRated; }
-            set { icon_blank = value; }
+        protected override void OnMapped ()
+        {
+            WidgetFlags |= WidgetFlags.Mapped;
+            event_window.Show ();
         }
         
-        public bool PreviewOnHover {
-            get { return preview_on_hover; }
-            set {
-                if (preview_on_hover == value)
-                    return;
-                    
-                preview_on_hover = value;
-                
-                /*if (value)
-                    Events |= Gdk.EventMask.PointerMotionMask;
-                else
-                    Events = Events & ~Gdk.EventMask.PointerMotionMask;*/
-            }
+        protected override void OnUnmapped ()
+        {
+            WidgetFlags &= ~WidgetFlags.Mapped;
+            event_window.Hide ();
         }
         
-        public int Width {
-            get { return IconRated.Width * NumLevels; }
+        private bool changing_style;
+        protected override void OnStyleSet (Style previous_style)
+        {
+            if (changing_style) {
+                return;
+            }
+            
+            changing_style = true;
+            focus_width = (int)StyleGetProperty ("focus-line-width");
+            interior_focus = (bool)StyleGetProperty ("interior-focus");
+            
+            ModifyBg (StateType.Normal, Style.Base (StateType.Normal));
+            changing_style = false;
         }
         
-        public int Height {
-            get { return IconRated.Height; }
+        protected override void OnSizeAllocated (Gdk.Rectangle allocation)
+        {
+            base.OnSizeAllocated (allocation);
+            event_alloc = new Gdk.Rectangle (0, 0, allocation.Width, allocation.Height);
+            if (IsRealized) {
+                event_window.MoveResize (allocation);
+            }
         }
         
-#endregion
-
-#region Protected Gtk.Widget Overrides
-        
         protected override void OnSizeRequested (ref Gtk.Requisition requisition)
         {
-            requisition.Width = Width + (2 * x_offset);
-            requisition.Height = Height + (2 * y_offset);
-            base.OnSizeRequested (ref requisition);
+            EnsureStyle ();
+            
+            Pango.FontMetrics metrics = PangoContext.GetMetrics (Style.FontDescription, PangoContext.Language);
+            renderer.Size = ((int)(metrics.Ascent + metrics.Descent) + 512) >> 10; // PANGO_PIXELS(d)
+            metrics.Dispose ();
+            
+            if (HasFrame) {
+                renderer.Xpad = Style.Xthickness + (interior_focus ? focus_width : 0) + 2;
+                renderer.Ypad = Style.Ythickness + (interior_focus ? focus_width : 0) + 2;
+            } else {
+                renderer.Xpad = 0;
+                renderer.Ypad = 0;
+            }
+            
+            requisition.Width = renderer.Width;
+            requisition.Height = renderer.Height;
         }
         
         protected override bool OnExposeEvent (Gdk.EventExpose evnt)
@@ -218,16 +238,19 @@
                 return true;
             }
             
-            int y_mid = (Allocation.Height - Height) / 2;
-
-            if (!embedded) {            
+            if (HasFrame) {
+                int y_mid = (Allocation.Height - renderer.Height) / 2;
+                Gtk.Style.PaintFlatBox (Style, GdkWindow, StateType.Normal, ShadowType.None, evnt.Area, this, "entry", 
+                    Allocation.X, Allocation.Y + y_mid, Allocation.Width, renderer.Height);
                 Gtk.Style.PaintShadow (Style, GdkWindow, StateType.Normal, ShadowType.In,
-                    evnt.Area, this, "entry", 0, y_mid - y_offset, Allocation.Width, 
-                    Height + (y_offset * 2));
+                    evnt.Area, this, "entry", Allocation.X, Allocation.Y + y_mid, Allocation.Width, renderer.Height);
             }
-
-            GdkWindow.DrawPixbuf (Style.BackgroundGC (StateType.Normal), 
-                display_pixbuf, 0, 0, x_offset, y_mid, Width, Height, Gdk.RgbDither.None, 0, 0);
+            
+            Cairo.Context cr = Gdk.CairoHelper.Create (GdkWindow);
+            renderer.Render (cr, Allocation, CairoExtensions.GdkColorToCairoColor (
+                Style.Foreground (State)), PreviewOnHover && hover_value >= renderer.MinRating, hover_value);
+            ((IDisposable)cr.Target).Dispose ();
+            ((IDisposable)cr).Dispose ();
 
             return true;
         }
@@ -239,30 +262,34 @@
             }
             
             HasFocus = true;
-            Value = RatingFromPosition (evnt.X);
+            Value = renderer.RatingFromPosition (event_alloc, evnt.X);
             
             return true;
         }
         
+        protected override bool OnEnterNotifyEvent (Gdk.EventCrossing evnt)
+        {
+            hover_value = renderer.MinRating;
+            QueueDraw ();
+            return true;
+        }
+        
         protected override bool OnLeaveNotifyEvent (Gdk.EventCrossing crossing)
         {
-            DrawRating (Value);
+            hover_value = renderer.MinRating - 1;
             QueueDraw ();
             return true;
         }
         
         protected override bool OnMotionNotifyEvent (Gdk.EventMotion motion)
         {
+            hover_value = renderer.RatingFromPosition (event_alloc, motion.X);
             if ((motion.State & Gdk.ModifierType.Button1Mask) != 0) {
-                Value = RatingFromPosition (motion.X);
-                return true;
-            } else if (preview_on_hover) {
-                DrawRating (RatingFromPosition (motion.X));
-                QueueDraw ();
-                return true;
+                Value = hover_value;
             }
             
-            return false;
+            QueueDraw ();
+            return true;
         }
         
         protected override bool OnKeyPressEvent (Gdk.EventKey evnt)
@@ -282,7 +309,7 @@
                     return true;
             }
             
-            if (evnt.KeyValue >= (48 + MinRating - 1) && evnt.KeyValue <= (48 + MaxRating) && evnt.KeyValue <= 59) {
+            if (evnt.KeyValue >= (48 + MinRating) && evnt.KeyValue <= (48 + MaxRating) && evnt.KeyValue <= 59) {
                 Value = (int)evnt.KeyValue - 48;
                 return true;
             }
@@ -290,7 +317,7 @@
             return false;
         }
         
-        protected override bool OnScrollEvent (EventScroll args)
+        protected override bool OnScrollEvent (Gdk.EventScroll args)
         {
             return HandleScroll (args);
         }
@@ -304,7 +331,7 @@
             return this.OnKeyPressEvent (evnt);
         }
 
-        internal bool HandleScroll (EventScroll args)
+        internal bool HandleScroll (Gdk.EventScroll args)
         {
             switch (args.Direction) {
                 case Gdk.ScrollDirection.Up:
@@ -323,51 +350,53 @@
         
 #endregion
 
-#region Protected methods
-
-        protected virtual void OnChanging ()
-        {
-            EventHandler handler = Changing;
-            if (handler != null) {
-                handler (this, new EventArgs ());
-            }
-        }
-
-        protected virtual void OnChanged ()
-        {
-            DrawRating (Value);
-            QueueDraw ();
-
-            EventHandler handler = Changed;
-            if (handler != null) {
-                handler (this, new EventArgs ());
-            }
-        }
-
-#endregion
-
-#region Private methods
+    }
+    
+#region Test Module
 
-        private void DrawRating (int val)
-        {
-            for (int i = 0; i < MaxRating; i++) {
-                if (i <= val - MinRating) {
-                    IconRated.CopyArea (0, 0, IconRated.Width, IconRated.Height, 
-                        DisplayPixbuf, i * IconRated.Width, 0);
-                } else {
-                    IconNotRated.CopyArea (0, 0, IconRated.Width, IconRated.Height,
-                        DisplayPixbuf, i * IconRated.Width, 0);
-                }
-            }
-        }
-        
-        private int RatingFromPosition (double x)
+    [Hyena.Gui.TestModule ("Rating Entry")]
+    internal class RatingEntryTestModule : Gtk.Window
+    {
+        public RatingEntryTestModule () : base ("Rating Entry")
         {
-            return x < x_offset + 1 ? 0 : (int) Math.Max ( 0, Math.Min (((x - x_offset) 
-                / (double)icon_rated.Width) + 1, MaxRating));
+            VBox pbox = new VBox ();
+            Add (pbox);
+            
+            Menu m = new Menu ();
+            MenuBar b = new MenuBar ();
+            MenuItem item = new MenuItem ("Rate Me!");
+            item.Submenu = m;
+            b.Append (item);
+            m.Append (new MenuItem ("Apples"));
+            m.Append (new MenuItem ("Pears"));
+            m.Append (new RatingMenuItem ());
+            m.Append (new ImageMenuItem ("gtk-remove", null));
+            m.ShowAll ();
+            pbox.PackStart (b, false, false, 0);
+            
+            VBox box = new VBox ();
+            box.BorderWidth = 10;
+            box.Spacing = 10;
+            pbox.PackStart (box, true, true, 0);
+            
+            RatingEntry entry1 = new RatingEntry ();
+            box.PackStart (entry1, true, true, 0);
+            
+            RatingEntry entry2 = new RatingEntry ();
+            box.PackStart (entry2, false, false, 0);
+            
+            box.PackStart (new Entry ("Normal GtkEntry"), false, false, 0);
+            
+            RatingEntry entry3 = new RatingEntry ();
+            Pango.FontDescription fd = entry3.PangoContext.FontDescription.Copy ();
+            fd.Size = (int)(fd.Size * Pango.Scale.XXLarge);
+            entry3.ModifyFont (fd);
+            box.PackStart (entry3, true, true, 0);
+            
+            pbox.ShowAll ();
         }
-
+    }
+    
 #endregion
 
-    }
 }

Copied: trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Widgets/RatingMenuItem.cs (from r3918, /trunk/banshee/src/Core/Banshee.Widgets/Banshee.Widgets/RatingMenuItem.cs)
==============================================================================
--- /trunk/banshee/src/Core/Banshee.Widgets/Banshee.Widgets/RatingMenuItem.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Widgets/RatingMenuItem.cs	Sat May 17 21:35:43 2008
@@ -4,7 +4,7 @@
 // Author:
 //   Aaron Bockover <abockover novell com>
 //
-// Copyright (C) 2007 Novell, Inc.
+// Copyright (C) 2007-2008 Novell, Inc.
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
@@ -30,32 +30,34 @@
 using Gtk;
 using Mono.Unix;
 
-namespace Banshee.Widgets
+namespace Hyena.Widgets
 {
     public class RatingMenuItem : ComplexMenuItem
     {
         private RatingEntry entry;
         private bool pressing;
         private bool can_activate = true;
+        private Box box;
 
         public RatingMenuItem () : base ()
         {
-            HBox box = new HBox ();
+            box = new HBox ();
             box.Spacing = 5;
             
             Label label = new Label ();
             label.Markup = String.Format ("<i>{0}</i>", 
                 GLib.Markup.EscapeText (Catalog.GetString ("Rating:")));
             box.PackStart (label, false, false, 0);
+            label.Show ();
             
-            entry = new RatingEntry (0, true);
+            entry = new RatingEntry ();
+            entry.HasFrame = false;
+            entry.PreviewOnHover = false;
             entry.Changed += OnEntryChanged;
             box.PackStart (entry, false, false, 0);
             
             box.ShowAll ();
             Add (box);
-            
-            ConnectChildExpose (entry);
         }
 
         private int TransformX (double inx)

Modified: trunk/banshee/src/Libraries/Hyena.Gui/Makefile.am
==============================================================================
--- trunk/banshee/src/Libraries/Hyena.Gui/Makefile.am	(original)
+++ trunk/banshee/src/Libraries/Hyena.Gui/Makefile.am	Sat May 17 21:35:43 2008
@@ -5,11 +5,15 @@
 	Hyena.Data.Gui/CellContext.cs \
 	Hyena.Data.Gui/Column.cs \
 	Hyena.Data.Gui/ColumnCell.cs \
+	Hyena.Data.Gui/ColumnCellCheckBox.cs \
+	Hyena.Data.Gui/ColumnCellRating.cs \
 	Hyena.Data.Gui/ColumnCellText.cs \
 	Hyena.Data.Gui/ColumnController.cs \
 	Hyena.Data.Gui/ColumnHeaderCellText.cs \
 	Hyena.Data.Gui/IHeaderCell.cs \
+	Hyena.Data.Gui/IInteractiveCell.cs \
 	Hyena.Data.Gui/IListView.cs \
+	Hyena.Data.Gui/ISizeRequestCell.cs \
 	Hyena.Data.Gui/ListView/ListView.cs \
 	Hyena.Data.Gui/ListView/ListView_DragAndDrop.cs \
 	Hyena.Data.Gui/ListView/ListView_Header.cs \
@@ -17,6 +21,7 @@
 	Hyena.Data.Gui/ListView/ListView_Model.cs \
 	Hyena.Data.Gui/ListView/ListView_Rendering.cs \
 	Hyena.Data.Gui/ListView/ListView_Windowing.cs \
+	Hyena.Data.Gui/ListViewTestModule.cs \
 	Hyena.Data.Gui/ObjectListView.cs \
 	Hyena.Data.Gui/RowActivatedHandler.cs \
 	Hyena.Data.Gui/SortableColumn.cs \
@@ -38,6 +43,7 @@
 	Hyena.Gui/EntryUndoAdapter.cs \
 	Hyena.Gui/GtkUtilities.cs \
 	Hyena.Gui/PangoCairoHelper.cs \
+	Hyena.Gui/RatingRenderer.cs \
 	Hyena.Gui/ShadingTestWindow.cs \
 	Hyena.Gui/TestModuleAttribute.cs \
 	Hyena.Gui/TestModuleRunner.cs \
@@ -60,14 +66,19 @@
 	Hyena.Widgets/AnimatedImage.cs \
 	Hyena.Widgets/AnimatedVBox.cs \
 	Hyena.Widgets/AnimatedWidget.cs \
+	Hyena.Widgets/ComplexMenuItem.cs \
 	Hyena.Widgets/MenuButton.cs \
 	Hyena.Widgets/MessageBar.cs \
+	Hyena.Widgets/RatingEntry.cs \
+	Hyena.Widgets/RatingMenuItem.cs \
 	Hyena.Widgets/RoundedFrame.cs \
 	Hyena.Widgets/ScrolledWindow.cs \
 	Hyena.Widgets/SegmentedBar.cs \
 	Hyena.Widgets/SmoothScrolledWindow.cs \
 	Hyena.Widgets/WrapLabel.cs
 
+RESOURCES =
+
 include $(top_srcdir)/build/build.mk
 
 EXTRA_DIST += Hyena.Gui.dll.config



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