[f-spot/icon-view-cleanup: 12/24] Factor Selection and Collection handling out of IconView to CollectionCellGridView



commit 4c0d4fb59faf11606cdd325a7ff2d93139215854
Author: Mike Gemünde <mike gemuende de>
Date:   Sat Aug 14 13:43:21 2010 +0200

    Factor Selection and Collection handling out of IconView to CollectionCellGridView

 .../MainApp/FSpot.UI.Dialog/AdjustTimeDialog.cs    |    2 +-
 .../FSpot.Widgets/CollectionCellGridView.cs        |  555 ++++++++++++
 src/Clients/MainApp/FSpot.Widgets/IconView.cs      |  880 +------------------
 .../MainApp/FSpot.Widgets/ScalingIconView.cs       |    2 +-
 .../MainApp/FSpot.Widgets/SelectionCollection.cs   |  341 ++++++++
 src/Clients/MainApp/FSpot/SingleView.cs            |    2 +-
 src/Clients/MainApp/Makefile.am                    |    2 +
 7 files changed, 948 insertions(+), 836 deletions(-)
---
diff --git a/src/Clients/MainApp/FSpot.UI.Dialog/AdjustTimeDialog.cs b/src/Clients/MainApp/FSpot.UI.Dialog/AdjustTimeDialog.cs
index 39c9cde..d0bb2ab 100644
--- a/src/Clients/MainApp/FSpot.UI.Dialog/AdjustTimeDialog.cs
+++ b/src/Clients/MainApp/FSpot.UI.Dialog/AdjustTimeDialog.cs
@@ -254,7 +254,7 @@ namespace FSpot.UI.Dialog {
 		void HandleSelectionChanged (IBrowsableCollection sender)
 		{
 			if (sender.Count > 0) {
-				view.Item.Index = ((FSpot.Widgets.IconView.SelectionCollection)sender).Ids[0];
+				view.Item.Index = ((FSpot.Widgets.SelectionCollection)sender).Ids[0];
 
 			}
 		}
diff --git a/src/Clients/MainApp/FSpot.Widgets/CollectionCellGridView.cs b/src/Clients/MainApp/FSpot.Widgets/CollectionCellGridView.cs
new file mode 100644
index 0000000..abbc5ff
--- /dev/null
+++ b/src/Clients/MainApp/FSpot.Widgets/CollectionCellGridView.cs
@@ -0,0 +1,555 @@
+/*
+ * CollectionGridView.cs
+ *
+ * Author(s)
+ *  Etore Perazzoli
+ *  Larry Ewing <lewing novell com>
+ *  Stephane Delcroix <stephane delcroix org>
+ *  Mike Gemuende <mike gemuende de>
+ *
+ * This is free software. See COPYING for details.
+ */
+
+using System;
+
+using Gtk;
+using Gdk;
+
+using FSpot.Core;
+
+
+namespace FSpot.Widgets
+{
+
+    // TODO: This event ahndler is a hack. The default event from a widget
+    //       (DragBegin) should be used, but therfore, the event must be fired
+    //       correctly.
+    public delegate void StartDragHandler (object o, StartDragArgs args);
+
+    public class StartDragArgs {
+        public Event Event { get; set; }
+        public uint Button { get; set; }
+
+        public StartDragArgs (uint but, Event evt) {
+            this.Button = but;
+            this.Event = evt;
+        }
+    }
+
+    /// <summary>
+    ///    This class extends CellGridView to provide a grid view for a photo collection with selection.
+    /// </summary>
+    public abstract class CollectionGridView : CellGridView
+    {
+
+#region Public Properties
+
+        public IBrowsableCollection Collection {
+            get; private set;
+        }
+
+        public SelectionCollection Selection {
+            get; private set;
+        }
+
+        // Focus Handling
+        private int real_focus_cell;
+        public int FocusCell {
+            get { return real_focus_cell; }
+            set {
+                if (value != real_focus_cell) {
+                    value = Math.Max (value, 0);
+                    value = Math.Min (value, Collection.Count - 1);
+                    InvalidateCell (value);
+                    InvalidateCell (real_focus_cell);
+                    real_focus_cell = value;
+                }
+            }
+        }
+
+#endregion
+
+#region Constructors
+
+        public CollectionGridView (IntPtr raw) : base (raw)
+        {
+        }
+
+        public CollectionGridView (IBrowsableCollection collection) : base ()
+        {
+            Collection = collection;
+            Selection = new SelectionCollection (Collection);
+
+            Collection.Changed += delegate (IBrowsableCollection sender) {
+                //suppress_scroll = true;
+                QueueResize ();
+            };
+
+            Selection.DetailedChanged += delegate(IBrowsableCollection sender, Int32 [] ids) {
+                if (ids == null)
+                    QueueDraw ();
+                else
+                    foreach (int id in ids)
+                        InvalidateCell (id);
+            };
+
+            ButtonPressEvent += new ButtonPressEventHandler (HandleButtonPressEvent);
+            ButtonReleaseEvent += new ButtonReleaseEventHandler (HandleButtonReleaseEvent);
+            KeyPressEvent += new KeyPressEventHandler (HandleKeyPressEvent);
+
+            MotionNotifyEvent += new MotionNotifyEventHandler (HandleSelectMotionNotify);
+
+            AddEvents ((int) EventMask.KeyPressMask
+                     | (int) EventMask.KeyReleaseMask
+                     | (int) EventMask.ButtonPressMask
+                     | (int) EventMask.ButtonReleaseMask
+                     | (int) EventMask.PointerMotionMask
+                     | (int) EventMask.PointerMotionHintMask);
+
+            CanFocus = true;
+        }
+
+#endregion
+
+#region Implementation of Base Class Layout Properties
+
+        protected override int CellCount {
+            get {
+                if (Collection == null)
+                    return 0;
+
+                return Collection.Count;
+            }
+        }
+
+#endregion
+
+#region Event Handlers
+
+        public event EventHandler<BrowsableEventArgs> DoubleClicked;
+
+        // TODO: hack. See definition of StartDragHandler
+        public event StartDragHandler StartDrag;
+
+#endregion
+
+#region Drawing Methods
+
+        protected override void DrawCell (int cell_num, Rectangle expose_area)
+        {
+            bool selected = Selection.Contains (cell_num);
+            bool focussed = FocusCell == cell_num;
+
+            Rectangle cell_area = CellBounds (cell_num);
+
+            DrawPhoto (cell_num, cell_area, expose_area, selected, focussed);
+        }
+
+        protected abstract void DrawPhoto (int cell_num, Rectangle cell_area, Rectangle expose_area, bool selected, bool focussed);
+
+        protected override bool OnExposeEvent (Gdk.EventExpose args)
+        {
+            bool ret = base.OnExposeEvent (args);
+
+            foreach (Rectangle area in args.Region.GetRectangles ()) {
+                DrawSelection (area);
+            }
+
+            return ret;
+        }
+
+        private void DrawSelection (Rectangle expose_area)
+        {
+            if ( ! isRectSelection)
+                return;
+
+            Gdk.Rectangle region;
+            if ( ! expose_area.Intersect (rect_select, out region))
+                return;
+
+            // draw selection
+            using (Cairo.Context cairo_g = CairoHelper.Create (BinWindow)) {
+
+                Gdk.Color color = Style.Background(StateType.Selected);
+                cairo_g.Color = new Cairo.Color (color.Red/65535.0, color.Green/65535.0, color.Blue/65535.0, 0.5);
+                cairo_g.Rectangle (region.X, region.Y, region.Width, region.Height);
+                cairo_g.Fill ();
+
+            }
+
+            //((IDisposable) cairo_g.Target).Dispose ();
+            //((IDisposable) cairo_g).Dispose ();
+        }
+
+#endregion
+
+#region Utility Methods
+
+        // TODO: move this to SelectionCollection
+        public void SelectAllCells ()
+        {
+            Selection.Add (0, Collection.Count - 1);
+        }
+
+        protected virtual void ContextMenu (ButtonPressEventArgs args, int cell_num)
+        {
+        }
+
+#endregion
+
+#region Event Handler
+
+        // TODO: the following code need to be cleaned up.
+        // TODO: rubberband selection behaves different than Gtk.IconView. This needs to be fixed.
+        // TODO: selection by clicks behaves different than Gtk.IconView. This needs to be fixed.
+
+        private void HandleButtonPressEvent (object obj, ButtonPressEventArgs args)
+        {
+            int cell_num = CellAtPosition ((int) args.Event.X, (int) args.Event.Y, false, false);
+
+            args.RetVal = true;
+
+            start_select_event = args.Event;
+            start_press_x = (int) args.Event.X;
+            start_press_y = (int) args.Event.Y;
+            isRectSelection = false;
+            isDragDrop = false;
+
+            switch (args.Event.Type) {
+            case EventType.TwoButtonPress:
+                if (args.Event.Button != 1 ||
+                    (args.Event.State &  (ModifierType.ControlMask | ModifierType.ShiftMask)) != 0)
+                    return;
+                if (DoubleClicked != null)
+                    DoubleClicked (this, new BrowsableEventArgs (cell_num, null));
+                return;
+
+            case EventType.ButtonPress:
+                GrabFocus ();
+                // on a cell : context menu if button 3
+                // cell selection is done on button release
+                if (args.Event.Button == 3){
+                    ContextMenu (args, cell_num);
+                    return;
+                } else args.RetVal = false;
+
+                break;
+
+            default:
+                args.RetVal = false;
+                break;
+            }
+        }
+
+        private void HandleButtonReleaseEvent (object sender, ButtonReleaseEventArgs args)
+        {
+            if (isRectSelection) {
+                // remove scrolling and rectangular selection
+                if (scroll_timeout != 0) {
+                    GLib.Source.Remove (scroll_timeout);
+                    scroll_timeout = 0;
+                }
+                SelectMotion ();
+
+                isRectSelection = false;
+                if (BinWindow != null) {
+                    BinWindow.InvalidateRect (rect_select, false);
+                    BinWindow.ProcessUpdates (true);
+                }
+                rect_select = new Rectangle();
+            } else if (!isDragDrop) {
+                int cell_num = CellAtPosition ((int) args.Event.X, (int) args.Event.Y, false, true);
+                if (cell_num != -1) {
+                    if ((args.Event.State & ModifierType.ControlMask) != 0) {
+                        Selection.ToggleCell (cell_num);
+                    } else if ((args.Event.State & ModifierType.ShiftMask) != 0) {
+                        Selection.Add (FocusCell, cell_num);
+                    } else {
+                        Selection.Clear ();
+                        Selection.Add (cell_num);
+                    }
+                    FocusCell = cell_num;
+                }
+            }
+            isDragDrop = false;
+        }
+
+        // rectangle of dragging selection
+        private Rectangle rect_select;
+
+        private bool isRectSelection = false;
+        private bool isDragDrop = false;
+
+        // initial click and scroll value
+        private int start_select_x, start_select_y, start_select_vadj, start_select_hadj;
+        // initial selection
+        private int[] start_select_selection;
+        // initial event used to detect drag&drop
+        private EventButton start_select_event;
+        // timer using when scrolling selection
+        private uint scroll_timeout = 0;
+        // initial click
+            private int start_press_x, start_press_y;
+
+        // during pointer motion, select/toggle pictures between initial x/y (param)
+        // and current x/y (get pointer)
+        private void SelectMotion ()
+        {
+            int x2, y2;
+            Gdk.ModifierType mod;
+            Display.GetPointer (out x2, out y2, out mod);
+            GetPointer (out x2, out y2);
+
+            // check new coord
+            int x1 = start_select_x;
+            if (x1 < 0)
+                x1 = 0;
+            int y1 = start_select_y;
+            if (y1 < 0)
+                y1 = 0;
+            if (y1 > Allocation.Height)
+                y1 = (int) Allocation.Height;
+            x1 += start_select_hadj;
+            y1 += start_select_vadj;
+
+            if (x2 < 0)
+                x2 = 0;
+            if (y2 < 0)
+                y2 = 0;
+            if (y2 > Allocation.Height)
+                y2 = (int) Allocation.Height;
+            x2 += (int) Hadjustment.Value;
+            y2 += (int) Vadjustment.Value;
+
+            int start_x = x1 < x2 ? x1 : x2;
+            int end_x =   x1 > x2 ? x1 : x2;
+            int start_y = y1 < y2 ? y1 : y2;
+            int end_y =   y1 > y2 ? y1 : y2;
+
+            // Restore initial selection
+            var initial_selection = Selection.ToBitArray();
+            Selection.Clear (false);
+            foreach (int i in start_select_selection)
+                Selection.Add (i, false);
+
+            // Select or toggle according to modifiers
+            int start_row  = (start_x - BorderSize) / cell_width;
+            int start_line = (start_y - BorderSize) / cell_height;
+            int end_row    = (end_x - BorderSize + cell_width - 1) / cell_width;
+            int end_line   = (end_y - BorderSize + cell_height - 1) / cell_height;
+            if (start_row > cells_per_row)
+                start_row = cells_per_row;
+            if (end_row > cells_per_row)
+                end_row = cells_per_row;
+
+
+            FocusCell = start_line * cells_per_row + start_row;
+
+            if ((mod & ModifierType.ControlMask) == 0)
+                Selection.SelectRect (start_row, end_row, start_line, end_line, cells_per_row);
+            else
+                Selection.ToggleRect (start_row, end_row, start_line, end_line, cells_per_row);
+
+            // fire events for cells which have changed selection flag
+            var new_selection = Selection.ToBitArray();
+            var selection_changed = initial_selection.Xor (new_selection);
+            System.Collections.Generic.List<int> changed = new System.Collections.Generic.List<int>();
+            for (int i = 0; i < selection_changed.Length; i++)
+                if (selection_changed.Get(i))
+                    changed.Add (i);
+            if (selection_changed.Length != 0)
+                Selection.SignalChange (changed.ToArray());
+
+            // redraw selection box
+            if (BinWindow != null) {
+                BinWindow.InvalidateRect (rect_select, true); // old selection
+                rect_select = new Rectangle (start_x, start_y, end_x - start_x, end_y - start_y);
+                BinWindow.InvalidateRect (rect_select, true); // new selection
+                BinWindow.ProcessUpdates (true);
+            }
+        }
+
+        // if scroll is required, a timeout is fired
+        // until the button is release or the pointer is
+        // in window again
+        private int deltaVscroll;
+        private bool HandleMotionTimeout()
+        {
+            int new_x, new_y;
+            ModifierType new_mod;
+            Display.GetPointer (out new_x, out new_y, out new_mod);
+            GetPointer (out new_x, out new_y);
+
+            // do scroll
+            double newVadj = Vadjustment.Value;
+            if (deltaVscroll < 130)
+                deltaVscroll += 15;
+
+            if (new_y <= 0) {
+                newVadj -= deltaVscroll;
+                if (newVadj < 0)
+                    newVadj = 0;
+            } else if ((new_y > Allocation.Height) &&
+                   (newVadj < Vadjustment.Upper - Allocation.Height - deltaVscroll))
+                newVadj += deltaVscroll;
+            Vadjustment.Value = newVadj;
+            Vadjustment.ChangeValue();
+
+            // do again selection after scroll
+            SelectMotion ();
+
+            // stop firing timeout when no button pressed
+            return (new_mod & (ModifierType.Button1Mask | ModifierType.Button3Mask)) != 0;
+        }
+
+        private void HandleSelectMotionNotify (object sender, MotionNotifyEventArgs args)
+        {
+            if ((args.Event.State & (ModifierType.Button1Mask | ModifierType.Button3Mask)) != 0 ) {
+                if (Gtk.Drag.CheckThreshold (this, start_press_x, start_press_y,
+                                 (int) args.Event.X, (int) args.Event.Y))
+                    if (isRectSelection) {
+                        // scroll if out of window
+                        double d_x, d_y;
+                        deltaVscroll = 30;
+                        if (EventHelper.GetCoords (args.Event, out d_x, out d_y)) {
+                            int new_y = (int) d_y;
+                            if ((new_y <= 0) || (new_y >= Allocation.Height)) {
+                                if (scroll_timeout == 0)
+                                    scroll_timeout = GLib.Timeout.Add (100, new GLib.TimeoutHandler (HandleMotionTimeout));
+                            }
+                        } else if (scroll_timeout != 0) {
+                            GLib.Source.Remove (scroll_timeout);
+                            scroll_timeout = 0;
+                        }
+
+                        // handle selection
+                        SelectMotion ();
+                    } else  {
+                        int cell_num = CellAtPosition (start_press_x, start_press_y, false, false);
+                        if (Selection.Contains (cell_num)) {
+                            // on a selected cell : do drag&drop
+                            isDragDrop = true;
+                            if (StartDrag != null) {
+                                uint but;
+                                if ((args.Event.State & ModifierType.Button1Mask) != 0)
+                                    but = 1;
+                                else
+                                    but = 3;
+                                StartDrag (this, new StartDragArgs(but, start_select_event));
+                            }
+                        } else {
+                            // not on a selected cell : do rectangular select
+                            isRectSelection = true;
+                            start_select_hadj = (int) Hadjustment.Value;
+                            start_select_vadj = (int) Vadjustment.Value;
+                            start_select_x = start_press_x - start_select_hadj;
+                            start_select_y = start_press_y - start_select_vadj;
+
+                            // ctrl : toggle selected, shift : keep selected
+                            if ((args.Event.State & (ModifierType.ShiftMask | ModifierType.ControlMask)) == 0)
+                                Selection.Clear ();
+
+                            start_select_selection = Selection.Ids; // keep initial selection
+                            // no rect draw at beginning
+                            rect_select = new Rectangle ();
+
+                            args.RetVal = false;
+                        }
+                    }
+            }
+        }
+
+
+        private void HandleKeyPressEvent (object sender, KeyPressEventArgs args)
+        {
+            int focus_old = FocusCell;
+            args.RetVal = true;
+
+            bool shift = ModifierType.ShiftMask == (args.Event.State & ModifierType.ShiftMask);
+            bool control = ModifierType.ControlMask == (args.Event.State & ModifierType.ControlMask);
+
+            switch (args.Event.Key) {
+            case Gdk.Key.Down:
+            case Gdk.Key.J:
+            case Gdk.Key.j:
+                FocusCell += VisibleColums;
+                break;
+
+            case Gdk.Key.Left:
+            case Gdk.Key.H:
+            case Gdk.Key.h:
+                if (control && shift)
+                    FocusCell -= FocusCell % VisibleColums;
+                else
+                    FocusCell--;
+                break;
+
+            case Gdk.Key.Right:
+            case Gdk.Key.L:
+            case Gdk.Key.l:
+                if (control && shift)
+                    FocusCell += VisibleColums - (FocusCell % VisibleColums) - 1;
+                else
+                    FocusCell++;
+                break;
+
+            case Gdk.Key.Up:
+            case Gdk.Key.K:
+            case Gdk.Key.k:
+                FocusCell -= VisibleColums;
+                break;
+
+            case Gdk.Key.Page_Up:
+                FocusCell -= VisibleColums * VisibleRows;
+                break;
+
+            case Gdk.Key.Page_Down:
+                FocusCell += VisibleColums * VisibleRows;
+                break;
+
+            case Gdk.Key.Home:
+                FocusCell = 0;
+                break;
+
+            case Gdk.Key.End:
+                FocusCell = Collection.Count - 1;
+                break;
+
+            case Gdk.Key.R:
+            case Gdk.Key.r:
+                FocusCell = new Random().Next(0, Collection.Count - 1);
+                break;
+
+            case Gdk.Key.space:
+                Selection.ToggleCell (FocusCell);
+                break;
+
+            case Gdk.Key.Return:
+                if (DoubleClicked != null)
+                    DoubleClicked (this, new BrowsableEventArgs (FocusCell, null));
+                break;
+
+            default:
+                args.RetVal = false;
+                return;
+            }
+
+            if (shift) {
+                if (focus_old != FocusCell && Selection.Contains (focus_old) && Selection.Contains (FocusCell))
+                    Selection.Remove (FocusCell, focus_old);
+                else
+                    Selection.Add (focus_old, FocusCell);
+
+            } else if (!control) {
+                Selection.Clear ();
+                Selection.Add (FocusCell);
+            }
+
+            ScrollTo (FocusCell);
+        }
+
+#endregion
+
+    }
+}
+
diff --git a/src/Clients/MainApp/FSpot.Widgets/IconView.cs b/src/Clients/MainApp/FSpot.Widgets/IconView.cs
index a5a9171..3d8eee7 100644
--- a/src/Clients/MainApp/FSpot.Widgets/IconView.cs
+++ b/src/Clients/MainApp/FSpot.Widgets/IconView.cs
@@ -24,20 +24,8 @@ using FSpot.Platform;
 
 namespace FSpot.Widgets
 {
-    public class StartDragArgs {
-        public Event Event { get; set; }
-        public uint Button { get; set; }
 
-        public StartDragArgs (uint but, Event evt) {
-            this.Button = but;
-            this.Event = evt;
-        }
-    }
-
-    public delegate void StartDragHandler (object o, StartDragArgs args);
-
-
-	public class IconView : CellGridView
+	public class IconView : CollectionGridView
     {
 
 		// Public properties.
@@ -46,9 +34,7 @@ namespace FSpot.Widgets
 #region Public Events
 
         // Public events.
-        public event EventHandler<BrowsableEventArgs> DoubleClicked;
         public event EventHandler ZoomChanged;
-        public event StartDragHandler StartDrag;
 
 #endregion
 
@@ -117,15 +103,6 @@ namespace FSpot.Widgets
 
 #region Implement base class layout properties
 
-        protected override int CellCount {
-            get {
-                if (collection == null)
-                    return 0;
-
-                return collection.Count;
-            }
-        }
-
         protected override int CellHeight {
             get {
                 int cell_height = ThumbnailHeight + 2 * cell_border_width;
@@ -248,456 +225,42 @@ namespace FSpot.Widgets
 		// Various other layout values.
 		protected int cell_details;
 
-		// Focus Handling
-		private int real_focus_cell;
-		public int FocusCell {
-			set {
-				if (value != real_focus_cell) {
-					value = Math.Max (value, 0);
-					value = Math.Min (value, collection.Count - 1);
-					InvalidateCell (value);
-					InvalidateCell (real_focus_cell);
-					real_focus_cell = value;
-				}
-			}
-			get {
-				return real_focus_cell;
-			}
-		}
-
 		// Public API.
 		public IconView (IntPtr raw) : base (raw) {}
 
-		protected IconView () : base ()
+		public IconView (IBrowsableCollection collection) : base (collection)
 		{
-			cache = new FSpot.PixbufCache ();
-			cache.OnPixbufLoaded += HandlePixbufLoaded;
-
-			ButtonPressEvent += new ButtonPressEventHandler (HandleButtonPressEvent);
-			ButtonReleaseEvent += new ButtonReleaseEventHandler (HandleButtonReleaseEvent);
-			KeyPressEvent += new KeyPressEventHandler (HandleKeyPressEvent);
-			ScrollEvent += new ScrollEventHandler(HandleScrollEvent);
-			MotionNotifyEvent += new MotionNotifyEventHandler (HandleSelectMotionNotify);
-
-			Destroyed += HandleDestroyed;
-
-			AddEvents (
-				(int) EventMask.KeyPressMask
-				| (int) EventMask.KeyReleaseMask
-				| (int) EventMask.PointerMotionMask
-				| (int) EventMask.PointerMotionHintMask
-				| (int) EventMask.ButtonPressMask
-				| (int) EventMask.ButtonReleaseMask);
-
-			CanFocus = true;
-		}
-
-		public IconView (IBrowsableCollection collection) : this ()
-		{
-			this.collection = collection;
-			this.selection = new SelectionCollection (collection);
+            cache = new FSpot.PixbufCache ();
+            cache.OnPixbufLoaded += HandlePixbufLoaded;
 
 			Name = "ImageContainer";
-			collection.Changed += HandleChanged;
-			collection.ItemsChanged += HandleItemsChanged;
-
-			selection.DetailedChanged += HandleSelectionChanged;
-		}
-
-		private void HandleSelectionChanged (IBrowsableCollection collection, int [] ids)
-		{
-			if (ids == null)
-				QueueDraw ();
-			else
-				foreach (int id in ids)
-					InvalidateCell (id);
-		}
-
-		private void HandleChanged (IBrowsableCollection sender)
-		{
-			// FIXME we should probably try to merge the selection forward
-			// but it needs some thought to be efficient.
-			//suppress_scroll = true;
-			QueueResize ();
-		}
-
-		private void HandleItemsChanged (IBrowsableCollection sender, BrowsableEventArgs args)
-		{
-			foreach (int item in args.Items) {
-				if (args.Changes.DataChanged)
-					UpdateThumbnail (item);
-				InvalidateCell (item);
-			}
-		}
-
-		//
-		// IPhotoSelection
-		//
+			Collection.ItemsChanged += HandleItemsChanged;
 
-		protected IBrowsableCollection collection;
-		public IBrowsableCollection Collection {
-			get {
-				return collection;
-			}
-		}
+            ScrollEvent += new ScrollEventHandler(HandleScrollEvent);
 
-		protected SelectionCollection selection;
-		public SelectionCollection Selection {
-			get {
-				return selection;
-			}
+            Destroyed += HandleDestroyed;
 		}
 
-		// FIXME right now a selection change triggers a complete view redraw
-		// This should be optimized away by directly notifyiing the view of changed
-		// indexes rather than having the view connect to the collection.Changed event.
-		public class SelectionCollection : IBrowsableCollection {
-			IBrowsableCollection parent;
-			Hashtable selected_cells;
-			BitArray bit_array;
-			int [] selection;
-			IPhoto [] items;
-			IPhoto [] old;
-
-			public SelectionCollection (IBrowsableCollection collection)
-			{
-				this.selected_cells = new Hashtable ();
-				this.parent = collection;
-				this.bit_array = new BitArray (this.parent.Count);
-				this.parent.Changed += HandleParentChanged;
-				this.parent.ItemsChanged += HandleParentItemsChanged;
-			}
-
-			private void HandleParentChanged (IBrowsableCollection collection)
-			{
-				IPhoto [] local = old;
-				selected_cells.Clear ();
-				bit_array = new BitArray (parent.Count);
-				ClearCached ();
-
-				if (old != null) {
-					int i = 0;
-
-					for (i = 0; i < local.Length; i++) {
-						int parent_index = parent.IndexOf (local [i]);
-						if (parent_index >= 0)
-							this.Add (parent_index, false);
-					}
-				}
-
-				// Call the directly so that we don't reset old immediately this way the old selection
-				// set isn't actually lost until we change it.
-				if (this.Changed != null)
-					Changed (this);
-
-				if (this.DetailedChanged != null)
-					DetailedChanged (this, null);
-
-			}
-
-			public void MarkChanged (int item, IBrowsableItemChanges changes)
-			{
-				// Forward the change event up to our parent
-				// we'll fire the event when the parent calls us back
-				parent.MarkChanged ((int) selected_cells [item], changes);
-			}
-
-			private void HandleParentItemsChanged (IBrowsableCollection collection, BrowsableEventArgs args)
-			{
-				if (this.ItemsChanged == null)
-					return;
-
-				ArrayList local_ids = new ArrayList ();
-				foreach (int parent_index in args.Items) {
-					// If the item isn't part of the selection ignore it
-					if (!this.Contains (collection [parent_index]))
-						return;
-
-					int local_index = this.IndexOf (parent_index);
-					if (local_index >= 0)
-						local_ids.Add (local_index);
-				}
-
-				if (local_ids.Count == 0)
-					return;
-
-				int [] items = (int [])local_ids.ToArray (typeof (int));
-				ItemsChanged (this, new BrowsableEventArgs (items, args.Changes));
-			}
-
-			public BitArray ToBitArray () {
-				return bit_array;
-			}
-
-			public int [] Ids {
-				get {
-					if (selection != null)
-						return selection;
-
-					selection = new int [selected_cells.Count];
-
-					int i = 0;
-					foreach (int cell in selected_cells.Values)
-						selection [i ++] = cell;
-
-					Array.Sort (selection);
-					return selection;
-				}
-			}
-
-			public IPhoto this [int index] {
-				get {
-					int [] ids = this.Ids;
-					return parent [ids[index]];
-				}
-			}
-
-			public IPhoto [] Items {
-				get {
-					if (items != null)
-						return items;
-
-					int [] ids = this.Ids;
-					items = new IPhoto [ids.Length];
-					for (int i = 0; i < items.Length; i++) {
-						items [i] = parent [ids[i]];
-					}
-					return items;
-				}
-			}
-
-			public void Clear ()
-			{
-				Clear (true);
-			}
-
-			public void Clear (bool update)
-			{
-				int [] ids = Ids;
-				selected_cells.Clear ();
-				bit_array.SetAll (false);
-				SignalChange (ids);
-			}
-
-			public void Add (IPhoto item)
-			{
-				if (this.Contains (item))
-					return;
-
-				int index = parent.IndexOf (item);
-				this.Add (index);
-			}
-
-			public int Count {
-				get {
-					return selected_cells.Count;
-				}
-			}
-
-			public bool Contains (IPhoto item)
-			{
-				return selected_cells.ContainsKey (item);
-			}
-
-			public bool Contains (int num)
-			{
-				if (num < 0 || num > parent.Count)
-					return false;
-
-				return this.Contains (parent [num]);
-			}
-
-			public void Add (int num)
-			{
-				this.Add (num, true);
-			}
-
-			public void Add (int num, bool notify)
-			{
-				if (num == -1)
-					return;
-
-				if (this.Contains (num))
-					return;
-
-				IPhoto item = parent [num];
-				selected_cells [item] = num;
-				bit_array.Set (num, true);
-
-				if (notify)
-					SignalChange (new int [] {num});
-			}
-
-			public void Add (int start, int end)
-			{
-				if (start == -1 || end == -1)
-					return;
-
-				int current = Math.Min (start, end);
-				int final = Math.Max (start, end);
-				int count = final - current + 1;
-				int [] ids = new int [count];
-
-				for (int i = 0; i < count; i++) {
-					this.Add (current, false);
-					ids [i] = current;
-					current++;
-				}
-
-				SignalChange (ids);
-			}
-
-			public void Remove (int cell, bool notify)
-			{
-				IPhoto item = parent [cell];
-				if (item != null)
-					this.Remove (item, notify);
-
-			}
 
-			public void Remove (IPhoto item)
-			{
-				Remove (item, true);
-			}
-
-			public void Remove (int cell)
-			{
-				Remove (cell, true);
-			}
-
-			private void Remove (IPhoto item, bool notify)
-			{
-				if (item == null)
-					return;
-
-				int parent_index = (int) selected_cells [item];
-				selected_cells.Remove (item);
-				bit_array.Set (parent_index, false);
-
-				if (notify)
-					SignalChange (new int [] {parent_index});
-			}
-
-			// Remove a range, except the start entry
-			public void Remove (int start, int end)
-			{
-				if (start == -1 || end == -1)
-					return;
-
-				int current = Math.Min (start + 1, end);
-				int final = Math.Max (start - 1, end);
-				int count = final - current + 1;
-				int [] ids = new int [count];
-
-				for (int i = 0; i < count; i++) {
-					this.Remove (current, false);
-					ids [i] = current;
-					current++;
-				}
-
-				SignalChange (ids);
-			}
-
-			public int IndexOf (int parent_index)
-			{
-				return System.Array.IndexOf (this.Ids, parent_index);
-			}
-
-			public int IndexOf (IPhoto item)
-			{
-				if (!this.Contains (item))
-					return -1;
-
-				int parent_index = (int) selected_cells [item];
-				return System.Array.IndexOf (Ids, parent_index);
-			}
-
-			private void ToggleCell (int cell_num, bool notify)
-			{
-				if (Contains (cell_num))
-					Remove (cell_num, notify);
-				else
-					Add (cell_num, notify);
-			}
-
-			public void ToggleCell (int cell_num)
-			{
-				ToggleCell (cell_num, true);
-			}
-
-			public void SelectionInvert ()
-			{
-				int [] changed_cell = new int[parent.Count];
-				for (int i = 0; i < parent.Count; i++) {
-					ToggleCell (i, false);
-					changed_cell[i] = i;
-				}
-
-				SignalChange (changed_cell);
-			}
-
-			public void SelectRect (int start_row, int end_row, int start_line, int end_line, int cells_per_row)
-			{
-				for (int row = start_row; row < end_row; row++)
-					for (int line = start_line; line < end_line; line++) {
-						int index = line*cells_per_row + row;
-						if (index < parent.Count)
-							Add (index, false);
-					}
-			}
-
-			public void ToggleRect (int start_row, int end_row, int start_line, int end_line, int cells_per_row)
-			{
-				for  (int row = start_row; row < end_row; row++)
-					for (int line = start_line; line < end_line; line++) {
-						int index = line*cells_per_row + row;
-						if (index < parent.Count)
-							ToggleCell (index, false);
-					}
-			}
-
-
-			public event IBrowsableCollectionChangedHandler Changed;
-			public event IBrowsableCollectionItemsChangedHandler ItemsChanged;
-
-			public delegate void DetailedCollectionChanged (IBrowsableCollection collection, int [] ids);
-			public event DetailedCollectionChanged DetailedChanged;
-
-			private void ClearCached ()
-			{
-				selection = null;
-				items = null;
-			}
-
-			public void SignalChange (int [] ids)
-			{
-				ClearCached ();
-				old = this.Items;
-
-
-				if (Changed != null)
-					Changed (this);
+        private void HandleItemsChanged (IBrowsableCollection sender, BrowsableEventArgs args)
+        {
+            foreach (int item in args.Items) {
+                if (args.Changes.DataChanged)
+                    UpdateThumbnail (item);
+                InvalidateCell (item);
+            }
+        }
 
-				if (DetailedChanged!= null)
-					DetailedChanged (this, ids);
-			}
-		}
 
 		// Updating.
 		public void UpdateThumbnail (int thumbnail_num)
 		{
-			IPhoto photo = collection [thumbnail_num];
+			IPhoto photo = Collection [thumbnail_num];
 			cache.Remove (photo.DefaultVersion.Uri);
 			InvalidateCell (thumbnail_num);
 		}
 
 
-		// Private utility methods.
-		public void SelectAllCells ()
-		{
-			selection.Add (0, collection.Count - 1);
-		}
-
 		// Layout and drawing.
 /*		protected virtual void UpdateLayout ()
 		{
@@ -810,65 +373,59 @@ namespace FSpot.Widgets
 			return expansion;
 		}
 
-		// rectangle of dragging selection
-		private Rectangle rect_select;
-
 		System.Collections.Hashtable date_layouts = new Hashtable ();
 		// FIXME Cache the GCs?
-		protected override void DrawCell (int thumbnail_num, Gdk.Rectangle area)
+		protected override void DrawPhoto (int cell_num, Rectangle cell_area, Rectangle expose_area, bool selected, bool focussed)
 		{
-			Gdk.Rectangle bounds = CellBounds (thumbnail_num);
-
-			if (!bounds.Intersect (area, out area))
+			if (!cell_area.Intersect (expose_area, out expose_area))
 				return;
 
-			IPhoto photo = collection [thumbnail_num];
+			IPhoto photo = Collection [cell_num];
 
 			FSpot.PixbufCache.CacheEntry entry = cache.Lookup (photo.DefaultVersion.Uri);
 			if (entry == null)
-				cache.Request (photo.DefaultVersion.Uri, thumbnail_num, ThumbnailWidth, ThumbnailHeight);
+				cache.Request (photo.DefaultVersion.Uri, cell_num, ThumbnailWidth, ThumbnailHeight);
 			else
-				entry.Data = thumbnail_num;
+				entry.Data = cell_num;
 
-			bool selected = selection.Contains (thumbnail_num);
 			StateType cell_state = selected ? (HasFocus ? StateType.Selected : StateType.Active) : State;
 
 			if (cell_state != State)
 				Style.PaintBox (Style, BinWindow, cell_state,
-					ShadowType.Out, area, this, "IconView",
-					bounds.X, bounds.Y,
-					bounds.Width - 1, bounds.Height - 1);
+					ShadowType.Out, expose_area, this, "IconView",
+					cell_area.X, cell_area.Y,
+					cell_area.Width - 1, cell_area.Height - 1);
 
-			Gdk.Rectangle focus = Gdk.Rectangle.Inflate (bounds, -3, -3);
+			Gdk.Rectangle focus = Gdk.Rectangle.Inflate (cell_area, -3, -3);
 
-			if (HasFocus && thumbnail_num == FocusCell) {
+			if (HasFocus && focussed) {
 				Style.PaintFocus(Style, BinWindow,
-						cell_state, area,
+						cell_state, expose_area,
 						this, null,
 						focus.X, focus.Y,
 						focus.Width, focus.Height);
 			}
 
 			Gdk.Rectangle region = Gdk.Rectangle.Zero;
-			Gdk.Rectangle image_bounds = Gdk.Rectangle.Inflate (bounds, -cell_border_width, -cell_border_width);
-			int expansion = ThrobExpansion (thumbnail_num, selected);
+			Gdk.Rectangle image_bounds = Gdk.Rectangle.Inflate (cell_area, -cell_border_width, -cell_border_width);
+			int expansion = ThrobExpansion (cell_num, selected);
 
 			Gdk.Pixbuf thumbnail = null;
 			if (entry != null)
 				thumbnail = entry.ShallowCopyPixbuf ();
 
 			Gdk.Rectangle draw = Gdk.Rectangle.Zero;
-			if (Gdk.Rectangle.Inflate (image_bounds, expansion + 1, expansion + 1).Intersect (area, out image_bounds) && thumbnail != null) {
+			if (Gdk.Rectangle.Inflate (image_bounds, expansion + 1, expansion + 1).Intersect (expose_area, out image_bounds) && thumbnail != null) {
 
 				PixbufUtils.Fit (thumbnail, ThumbnailWidth, ThumbnailHeight,
 						true, out region.Width, out region.Height);
 
-				region.X = (int) (bounds.X + (bounds.Width - region.Width) / 2);
-				region.Y = (int) bounds.Y + ThumbnailHeight - region.Height + cell_border_width;
+				region.X = (int) (cell_area.X + (cell_area.Width - region.Width) / 2);
+				region.Y = (int) cell_area.Y + ThumbnailHeight - region.Height + cell_border_width;
 
 				if (Math.Abs (region.Width - thumbnail.Width) > 1
 					&& Math.Abs (region.Height - thumbnail.Height) > 1)
-				cache.Reload (entry, thumbnail_num, thumbnail.Width, thumbnail.Height);
+				cache.Reload (entry, cell_num, thumbnail.Width, thumbnail.Height);
 
 				region = Gdk.Rectangle.Inflate (region, expansion, expansion);
 				Pixbuf temp_thumbnail;
@@ -909,12 +466,12 @@ namespace FSpot.Widgets
 
 				if (!temp_thumbnail.HasAlpha)
 					Style.PaintShadow (Style, BinWindow, cell_state,
-						ShadowType.Out, area, this,
+						ShadowType.Out, expose_area, this,
 						"IconView",
 						draw.X, draw.Y,
 						draw.Width, draw.Height);
 
-				if (region.Intersect (area, out draw)) {
+				if (region.Intersect (expose_area, out draw)) {
 					Cms.Profile screen_profile;
 					if (FSpot.ColorManagement.Profiles.TryGetValue (Preferences.Get<string> (Preferences.COLOR_MANAGEMENT_DISPLAY_PROFILE), out screen_profile)) {
 						Pixbuf t = temp_thumbnail.Copy ();
@@ -969,9 +526,9 @@ namespace FSpot.Widgets
 
 				layout.GetPixelSize (out layout_bounds.Width, out layout_bounds.Height);
 
-				layout_bounds.Y = bounds.Y + bounds.Height -
+				layout_bounds.Y = cell_area.Y + cell_area.Height -
 					cell_border_width - layout_bounds.Height + tag_icon_vspacing;
-				layout_bounds.X = bounds.X + (bounds.Width - layout_bounds.Width) / 2;
+				layout_bounds.X = cell_area.X + (cell_area.Width - layout_bounds.Width) / 2;
 
 				if (DisplayTags)
 					layout_bounds.Y -= tag_icon_size;
@@ -980,9 +537,9 @@ namespace FSpot.Widgets
 					layout_bounds.Y -= Style.FontDescription.MeasureTextHeight (PangoContext);
 				}
 
-				if (layout_bounds.Intersect (area, out region)) {
+				if (layout_bounds.Intersect (expose_area, out region)) {
 					Style.PaintLayout (Style, BinWindow, cell_state,
-							true, area, this, "IconView",
+							true, expose_area, this, "IconView",
 							layout_bounds.X, layout_bounds.Y,
 							layout);
 				}
@@ -996,15 +553,15 @@ namespace FSpot.Widgets
 
 				layout.GetPixelSize (out layout_bounds.Width, out layout_bounds.Height);
 
-				layout_bounds.Y = bounds.Y + bounds.Height - cell_border_width - layout_bounds.Height + tag_icon_vspacing;
-				layout_bounds.X = bounds.X + (bounds.Width - layout_bounds.Width) / 2;
+				layout_bounds.Y = cell_area.Y + cell_area.Height - cell_border_width - layout_bounds.Height + tag_icon_vspacing;
+				layout_bounds.X = cell_area.X + (cell_area.Width - layout_bounds.Width) / 2;
 
 				if (DisplayTags)
 					layout_bounds.Y -= tag_icon_size;
 
-				if (layout_bounds.Intersect (area, out region)) {
+				if (layout_bounds.Intersect (expose_area, out region)) {
 					Style.PaintLayout (Style, BinWindow, cell_state,
-							true, area, this, "IconView",
+							true, expose_area, this, "IconView",
 							layout_bounds.X, layout_bounds.Y,
 							layout);
 				}
@@ -1015,8 +572,8 @@ namespace FSpot.Widgets
 				Tag [] tags = photo.Tags;
 				Gdk.Rectangle tag_bounds;
 
-				tag_bounds.X = bounds.X + (bounds.Width  + tag_icon_hspacing - tags.Length * (tag_icon_size + tag_icon_hspacing)) / 2;
-				tag_bounds.Y = bounds.Y + bounds.Height - cell_border_width - tag_icon_size + tag_icon_vspacing;
+				tag_bounds.X = cell_area.X + (cell_area.Width  + tag_icon_hspacing - tags.Length * (tag_icon_size + tag_icon_hspacing)) / 2;
+				tag_bounds.Y = cell_area.Y + cell_area.Height - cell_border_width - tag_icon_size + tag_icon_vspacing;
 				tag_bounds.Width = tag_icon_size;
 				tag_bounds.Height = tag_icon_size;
 
@@ -1035,7 +592,7 @@ namespace FSpot.Widgets
 					if (icon == null)
 						continue;
 
-					if (tag_bounds.Intersect (area, out region)) {
+					if (tag_bounds.Intersect (expose_area, out region)) {
 						Pixbuf scaled_icon;
 						if (icon.Width == tag_bounds.Width) {
 							scaled_icon = icon;
@@ -1067,7 +624,7 @@ namespace FSpot.Widgets
 
         protected override void PreloadCell (int cell_num)
         {
-            var photo = collection [cell_num];
+            var photo = Collection [cell_num];
             var entry = cache.Lookup (photo.DefaultVersion.Uri);
 
             if (entry == null)
@@ -1192,349 +749,6 @@ namespace FSpot.Widgets
 			SetColors ();
 		}
 
-/*		protected override void OnSizeAllocated (Gdk.Rectangle allocation)
-		{
-			scroll_value = (Vadjustment.Value)/ (Vadjustment.Upper);
-			scroll = !suppress_scroll;
-			suppress_scroll = false;
-			UpdateLayout (allocation);
-			base.OnSizeAllocated (allocation);
-		}*/
-
-
-		private bool isRectSelection = false;
-		private bool isDragDrop = false;
-
-		// initial click and scroll value
-		private int start_select_x, start_select_y, start_select_vadj, start_select_hadj;
-		// initial selection
-		private int[] start_select_selection;
-		// initial event used to detect drag&drop
-		private EventButton start_select_event;
-		// timer using when scrolling selection
-		private uint scroll_timeout = 0;
-		// initial click
-	        private int start_press_x, start_press_y;
-
-		// during pointer motion, select/toggle pictures between initial x/y (param)
-		// and current x/y (get pointer)
-		private void SelectMotion ()
-		{
-/*			int x2, y2;
-			Gdk.ModifierType mod;
-			Display.GetPointer (out x2, out y2, out mod);
-			GetPointer (out x2, out y2);
-
-			// check new coord
-			int x1 = start_select_x;
-			if (x1 < 0)
-				x1 = 0;
-			int y1 = start_select_y;
-			if (y1 < 0)
-				y1 = 0;
-			if (y1 > Allocation.Height)
-				y1 = (int) Allocation.Height;
-			x1 += start_select_hadj;
-			y1 += start_select_vadj;
-
-			if (x2 < 0)
-				x2 = 0;
-			if (y2 < 0)
-				y2 = 0;
-			if (y2 > Allocation.Height)
-				y2 = (int) Allocation.Height;
-			x2 += (int) Hadjustment.Value;
-			y2 += (int) Vadjustment.Value;
-
-			int start_x = x1 < x2 ? x1 : x2;
-			int end_x =   x1 > x2 ? x1 : x2;
-			int start_y = y1 < y2 ? y1 : y2;
-			int end_y =   y1 > y2 ? y1 : y2;
-
-			// Restore initial selection
-			BitArray initial_selection = selection.ToBitArray();
-			selection.Clear (false);
-			foreach (int i in start_select_selection)
-				selection.Add (i, false);
-
-			// Select or toggle according to modifiers
-			int start_row  = (start_x - BORDER_SIZE) / cell_width;
-			int start_line = (start_y - BORDER_SIZE) / cell_height;
-			int end_row    = (end_x - BORDER_SIZE + cell_width - 1) / cell_width;
-			int end_line   = (end_y - BORDER_SIZE + cell_height - 1) / cell_height;
-			if (start_row > cells_per_row)
-				start_row = cells_per_row;
-			if (end_row > cells_per_row)
-				end_row = cells_per_row;
-
-
-			FocusCell = start_line * cells_per_row + start_row;
-
-			if ((mod & ModifierType.ControlMask) == 0)
-				selection.SelectRect (start_row, end_row, start_line, end_line, cells_per_row);
-			else
-				selection.ToggleRect (start_row, end_row, start_line, end_line, cells_per_row);
-
-			// fire events for cells which have changed selection flag
-			BitArray new_selection = selection.ToBitArray();
-			BitArray selection_changed = initial_selection.Xor (new_selection);
-			System.Collections.Generic.List<int> changed = new System.Collections.Generic.List<int>();
-			for (int i = 0; i < selection_changed.Length; i++)
-				if (selection_changed.Get(i))
-					changed.Add (i);
-			if (selection_changed.Length != 0)
-				selection.SignalChange (changed.ToArray());
-
-			// redraw selection box
-			if (BinWindow != null) {
-				BinWindow.InvalidateRect (rect_select, true); // old selection
-				rect_select = new Rectangle (start_x, start_y, end_x - start_x, end_y - start_y);
-				BinWindow.InvalidateRect (rect_select, true); // new selection
-				BinWindow.ProcessUpdates (true);
-			}*/
-		}
-
-		// if scroll is required, a timeout is fired
-		// until the button is release or the pointer is
-		// in window again
-		private int deltaVscroll;
-		private bool HandleMotionTimeout()
-		{
-			int new_x, new_y;
-			ModifierType new_mod;
-			Display.GetPointer (out new_x, out new_y, out new_mod);
-			GetPointer (out new_x, out new_y);
-
-			// do scroll
-			double newVadj = Vadjustment.Value;
-			if (deltaVscroll < 130)
-				deltaVscroll += 15;
-
-			if (new_y <= 0) {
-				newVadj -= deltaVscroll;
-				if (newVadj < 0)
-					newVadj = 0;
-			} else if ((new_y > Allocation.Height) &&
-				   (newVadj < Vadjustment.Upper - Allocation.Height - deltaVscroll))
-				newVadj += deltaVscroll;
-			Vadjustment.Value = newVadj;
-			Vadjustment.ChangeValue();
-
-			// do again selection after scroll
-			SelectMotion ();
-
-			// stop firing timeout when no button pressed
-			return (new_mod & (ModifierType.Button1Mask | ModifierType.Button3Mask)) != 0;
-		}
-
-		private void HandleSelectMotionNotify (object sender, MotionNotifyEventArgs args)
-		{
-			if ((args.Event.State & (ModifierType.Button1Mask | ModifierType.Button3Mask)) != 0 ) {
-				if (Gtk.Drag.CheckThreshold (this, start_press_x, start_press_y,
-							     (int) args.Event.X, (int) args.Event.Y))
-					if (isRectSelection) {
-						// scroll if out of window
-						double d_x, d_y;
-						deltaVscroll = 30;
-						if (EventHelper.GetCoords (args.Event, out d_x, out d_y)) {
-							int new_y = (int) d_y;
-							if ((new_y <= 0) || (new_y >= Allocation.Height)) {
-								if (scroll_timeout == 0)
-									scroll_timeout = GLib.Timeout.Add (100, new GLib.TimeoutHandler (HandleMotionTimeout));
-							}
-						} else if (scroll_timeout != 0) {
-							GLib.Source.Remove (scroll_timeout);
-							scroll_timeout = 0;
-						}
-
-						// handle selection
-						SelectMotion ();
-					} else  {
-						int cell_num = CellAtPosition (start_press_x, start_press_y, false, false);
-						if (selection.Contains (cell_num)) {
-							// on a selected cell : do drag&drop
-							isDragDrop = true;
-							if (StartDrag != null) {
-								uint but;
-								if ((args.Event.State & ModifierType.Button1Mask) != 0)
-									but = 1;
-								else
-									but = 3;
-								StartDrag (this, new StartDragArgs(but, start_select_event));
-							}
-						} else {
-							// not on a selected cell : do rectangular select
-							isRectSelection = true;
-							start_select_hadj = (int) Hadjustment.Value;
-							start_select_vadj = (int) Vadjustment.Value;
-							start_select_x = start_press_x - start_select_hadj;
-							start_select_y = start_press_y - start_select_vadj;
-
-							// ctrl : toggle selected, shift : keep selected
-							if ((args.Event.State & (ModifierType.ShiftMask | ModifierType.ControlMask)) == 0)
-								selection.Clear ();
-
-							start_select_selection = selection.Ids; // keep initial selection
-							// no rect draw at beginning
-							rect_select = new Rectangle ();
-
-							args.RetVal = false;
-						}
-					}
-			}
-		}
-
-		private void HandleButtonPressEvent (object obj, ButtonPressEventArgs args)
-		{
-			int cell_num = CellAtPosition ((int) args.Event.X, (int) args.Event.Y, false, false);
-
-			args.RetVal = true;
-
-			start_select_event = args.Event;
-			start_press_x = (int) args.Event.X;
-			start_press_y = (int) args.Event.Y;
-			isRectSelection = false;
-			isDragDrop = false;
-
-			switch (args.Event.Type) {
-			case EventType.TwoButtonPress:
-				if (args.Event.Button != 1 ||
-				    (args.Event.State &  (ModifierType.ControlMask | ModifierType.ShiftMask)) != 0)
-					return;
-				if (DoubleClicked != null)
-					DoubleClicked (this, new BrowsableEventArgs (cell_num, null));
-				return;
-
-			case EventType.ButtonPress:
-				GrabFocus ();
-				// on a cell : context menu if button 3
-				// cell selection is done on button release
-				if (args.Event.Button == 3){
-					ContextMenu (args, cell_num);
-					return;
-				} else args.RetVal = false;
-
-				break;
-
-			default:
-				args.RetVal = false;
-				break;
-			}
-		}
-
-		protected virtual void ContextMenu (ButtonPressEventArgs args, int cell_num)
-		{
-		}
-
-		private void HandleButtonReleaseEvent (object sender, ButtonReleaseEventArgs args)
-		{
-			if (isRectSelection) {
-				// remove scrolling and rectangular selection
-				if (scroll_timeout != 0) {
-					GLib.Source.Remove (scroll_timeout);
-					scroll_timeout = 0;
-				}
-				SelectMotion ();
-
-				isRectSelection = false;
-				if (BinWindow != null) {
-					BinWindow.InvalidateRect (rect_select, false);
-					BinWindow.ProcessUpdates (true);
-				}
-				rect_select = new Rectangle();
-			} else if (!isDragDrop) {
-				int cell_num = CellAtPosition ((int) args.Event.X, (int) args.Event.Y, false, true);
-				if (cell_num != -1) {
-					if ((args.Event.State & ModifierType.ControlMask) != 0) {
-						selection.ToggleCell (cell_num);
-					} else if ((args.Event.State & ModifierType.ShiftMask) != 0) {
-						selection.Add (FocusCell, cell_num);
-					} else {
-						selection.Clear ();
-						selection.Add (cell_num);
-					}
-					FocusCell = cell_num;
-				}
-			}
-			isDragDrop = false;
-		}
-
-		private void HandleKeyPressEvent (object sender, KeyPressEventArgs args)
-		{
-			int focus_old;
-			args.RetVal = true;
-			bool shift = ModifierType.ShiftMask == (args.Event.State & ModifierType.ShiftMask);
-			bool control = ModifierType.ControlMask == (args.Event.State & ModifierType.ControlMask);
-
-			focus_old = FocusCell;
-			switch (args.Event.Key) {
-			case Gdk.Key.Down:
-			case Gdk.Key.J:
-			case Gdk.Key.j:
-				FocusCell += VisibleColums;
-				break;
-			case Gdk.Key.Left:
-			case Gdk.Key.H:
-			case Gdk.Key.h:
-				if (control && shift)
-					FocusCell -= FocusCell % VisibleColums;
-				else
-					FocusCell--;
-				break;
-			case Gdk.Key.Right:
-			case Gdk.Key.L:
-			case Gdk.Key.l:
-				if (control && shift)
-					FocusCell += VisibleColums - (FocusCell % VisibleColums) - 1;
-				else
-					FocusCell++;
-				break;
-			case Gdk.Key.Up:
-			case Gdk.Key.K:
-			case Gdk.Key.k:
-				FocusCell -= VisibleColums;
-				break;
-			case Gdk.Key.Page_Up:
-				FocusCell -= VisibleColums * VisibleRows;
-				break;
-			case Gdk.Key.Page_Down:
-				FocusCell += VisibleColums * VisibleRows;
-				break;
-			case Gdk.Key.Home:
-				FocusCell = 0;
-				break;
-			case Gdk.Key.End:
-				FocusCell = collection.Count - 1;
-				break;
-			case Gdk.Key.R:
-			case Gdk.Key.r:
-                                FocusCell = new Random().Next(0, collection.Count - 1);
-                                break;
-			case Gdk.Key.space:
-				selection.ToggleCell (FocusCell);
-				break;
-			case Gdk.Key.Return:
-				if (DoubleClicked != null)
-					DoubleClicked (this, new BrowsableEventArgs (FocusCell, null));
-				break;
-			default:
-				args.RetVal = false;
-				return;
-			}
-
-			if (shift) {
-				if (focus_old != FocusCell && selection.Contains (focus_old) && selection.Contains (FocusCell))
-					selection.Remove (FocusCell, focus_old);
-				else
-					selection.Add (focus_old, FocusCell);
-			} else if (!control) {
-				selection.Clear ();
-				selection.Add (FocusCell);
-			}
-
-			ScrollTo (FocusCell);
-		}
-
 		private void HandleDestroyed (object sender, System.EventArgs args)
 		{
 			cache.OnPixbufLoaded -= HandlePixbufLoaded;
diff --git a/src/Clients/MainApp/FSpot.Widgets/ScalingIconView.cs b/src/Clients/MainApp/FSpot.Widgets/ScalingIconView.cs
index 0e992e7..39ad688 100644
--- a/src/Clients/MainApp/FSpot.Widgets/ScalingIconView.cs
+++ b/src/Clients/MainApp/FSpot.Widgets/ScalingIconView.cs
@@ -14,7 +14,7 @@ namespace FSpot.Widgets {
 	public class ScalingIconView : IconView {
 		protected ScalingIconView (IntPtr raw) : base (raw) {}
 
-		public ScalingIconView () : base () { }
+		//public ScalingIconView () : base () { }
 		public ScalingIconView (IBrowsableCollection collection) : base (collection) { }
 
 		/*protected override void UpdateLayout ()
diff --git a/src/Clients/MainApp/FSpot.Widgets/SelectionCollection.cs b/src/Clients/MainApp/FSpot.Widgets/SelectionCollection.cs
new file mode 100644
index 0000000..5c0451d
--- /dev/null
+++ b/src/Clients/MainApp/FSpot.Widgets/SelectionCollection.cs
@@ -0,0 +1,341 @@
+using System;
+using System.Collections;
+
+using FSpot.Core;
+
+namespace FSpot.Widgets
+{
+    public class SelectionCollection : IBrowsableCollection
+    {
+        IBrowsableCollection parent;
+        Hashtable selected_cells;
+        BitArray bit_array;
+        int [] selection;
+        IPhoto [] items;
+        IPhoto [] old;
+
+        public SelectionCollection (IBrowsableCollection collection)
+        {
+            this.selected_cells = new Hashtable ();
+            this.parent = collection;
+            this.bit_array = new BitArray (this.parent.Count);
+            this.parent.Changed += HandleParentChanged;
+            this.parent.ItemsChanged += HandleParentItemsChanged;
+        }
+
+        private void HandleParentChanged (IBrowsableCollection collection)
+        {
+            IPhoto [] local = old;
+            selected_cells.Clear ();
+            bit_array = new BitArray (parent.Count);
+            ClearCached ();
+
+            if (old != null) {
+                int i = 0;
+
+                for (i = 0; i < local.Length; i++) {
+                    int parent_index = parent.IndexOf (local [i]);
+                    if (parent_index >= 0)
+                        this.Add (parent_index, false);
+                }
+            }
+
+            // Call the directly so that we don't reset old immediately this way the old selection
+            // set isn't actually lost until we change it.
+            if (this.Changed != null)
+                Changed (this);
+
+            if (this.DetailedChanged != null)
+                DetailedChanged (this, null);
+
+        }
+
+        public void MarkChanged (int item, IBrowsableItemChanges changes)
+        {
+            // Forward the change event up to our parent
+            // we'll fire the event when the parent calls us back
+            parent.MarkChanged ((int) selected_cells [item], changes);
+        }
+
+        private void HandleParentItemsChanged (IBrowsableCollection collection, BrowsableEventArgs args)
+        {
+            if (this.ItemsChanged == null)
+                return;
+
+            ArrayList local_ids = new ArrayList ();
+            foreach (int parent_index in args.Items) {
+                // If the item isn't part of the selection ignore it
+                if (!this.Contains (collection [parent_index]))
+                    return;
+
+                int local_index = this.IndexOf (parent_index);
+                if (local_index >= 0)
+                    local_ids.Add (local_index);
+            }
+
+            if (local_ids.Count == 0)
+                return;
+
+            int [] items = (int [])local_ids.ToArray (typeof (int));
+            ItemsChanged (this, new BrowsableEventArgs (items, args.Changes));
+        }
+
+        public BitArray ToBitArray () {
+            return bit_array;
+        }
+
+        public int [] Ids {
+            get {
+                if (selection != null)
+                    return selection;
+
+                selection = new int [selected_cells.Count];
+
+                int i = 0;
+                foreach (int cell in selected_cells.Values)
+                    selection [i ++] = cell;
+
+                Array.Sort (selection);
+                return selection;
+            }
+        }
+
+        public IPhoto this [int index] {
+            get {
+                int [] ids = this.Ids;
+                return parent [ids[index]];
+            }
+        }
+
+        public IPhoto [] Items {
+            get {
+                if (items != null)
+                    return items;
+
+                int [] ids = this.Ids;
+                items = new IPhoto [ids.Length];
+                for (int i = 0; i < items.Length; i++) {
+                    items [i] = parent [ids[i]];
+                }
+                return items;
+            }
+        }
+
+        public void Clear ()
+        {
+            Clear (true);
+        }
+
+        public void Clear (bool update)
+        {
+            int [] ids = Ids;
+            selected_cells.Clear ();
+            bit_array.SetAll (false);
+            SignalChange (ids);
+        }
+
+        public void Add (IPhoto item)
+        {
+            if (this.Contains (item))
+                return;
+
+            int index = parent.IndexOf (item);
+            this.Add (index);
+        }
+
+        public int Count {
+            get {
+                return selected_cells.Count;
+            }
+        }
+
+        public bool Contains (IPhoto item)
+        {
+            return selected_cells.ContainsKey (item);
+        }
+
+        public bool Contains (int num)
+        {
+            if (num < 0 || num > parent.Count)
+                return false;
+
+            return this.Contains (parent [num]);
+        }
+
+        public void Add (int num)
+        {
+            this.Add (num, true);
+        }
+
+        public void Add (int num, bool notify)
+        {
+            if (num == -1)
+                return;
+
+            if (this.Contains (num))
+                return;
+
+            IPhoto item = parent [num];
+            selected_cells [item] = num;
+            bit_array.Set (num, true);
+
+            if (notify)
+                SignalChange (new int [] {num});
+        }
+
+        public void Add (int start, int end)
+        {
+            if (start == -1 || end == -1)
+                return;
+
+            int current = Math.Min (start, end);
+            int final = Math.Max (start, end);
+            int count = final - current + 1;
+            int [] ids = new int [count];
+
+            for (int i = 0; i < count; i++) {
+                this.Add (current, false);
+                ids [i] = current;
+                current++;
+            }
+
+            SignalChange (ids);
+        }
+
+        public void Remove (int cell, bool notify)
+        {
+            IPhoto item = parent [cell];
+            if (item != null)
+                this.Remove (item, notify);
+
+        }
+
+        public void Remove (IPhoto item)
+        {
+            Remove (item, true);
+        }
+
+        public void Remove (int cell)
+        {
+            Remove (cell, true);
+        }
+
+        private void Remove (IPhoto item, bool notify)
+        {
+            if (item == null)
+                return;
+
+            int parent_index = (int) selected_cells [item];
+            selected_cells.Remove (item);
+            bit_array.Set (parent_index, false);
+
+            if (notify)
+                SignalChange (new int [] {parent_index});
+        }
+
+        // Remove a range, except the start entry
+        public void Remove (int start, int end)
+        {
+            if (start == -1 || end == -1)
+                return;
+
+            int current = Math.Min (start + 1, end);
+            int final = Math.Max (start - 1, end);
+            int count = final - current + 1;
+            int [] ids = new int [count];
+
+            for (int i = 0; i < count; i++) {
+                this.Remove (current, false);
+                ids [i] = current;
+                current++;
+            }
+
+            SignalChange (ids);
+        }
+
+        public int IndexOf (int parent_index)
+        {
+            return System.Array.IndexOf (this.Ids, parent_index);
+        }
+
+        public int IndexOf (IPhoto item)
+        {
+            if (!this.Contains (item))
+                return -1;
+
+            int parent_index = (int) selected_cells [item];
+            return System.Array.IndexOf (Ids, parent_index);
+        }
+
+        private void ToggleCell (int cell_num, bool notify)
+        {
+            if (Contains (cell_num))
+                Remove (cell_num, notify);
+            else
+                Add (cell_num, notify);
+        }
+
+        public void ToggleCell (int cell_num)
+        {
+            ToggleCell (cell_num, true);
+        }
+
+        public void SelectionInvert ()
+        {
+            int [] changed_cell = new int[parent.Count];
+            for (int i = 0; i < parent.Count; i++) {
+                ToggleCell (i, false);
+                changed_cell[i] = i;
+            }
+
+            SignalChange (changed_cell);
+        }
+
+        public void SelectRect (int start_row, int end_row, int start_line, int end_line, int cells_per_row)
+        {
+            for (int row = start_row; row < end_row; row++)
+                for (int line = start_line; line < end_line; line++) {
+                    int index = line*cells_per_row + row;
+                    if (index < parent.Count)
+                        Add (index, false);
+                }
+        }
+
+        public void ToggleRect (int start_row, int end_row, int start_line, int end_line, int cells_per_row)
+        {
+            for  (int row = start_row; row < end_row; row++)
+                for (int line = start_line; line < end_line; line++) {
+                    int index = line*cells_per_row + row;
+                    if (index < parent.Count)
+                        ToggleCell (index, false);
+                }
+        }
+
+
+        public event IBrowsableCollectionChangedHandler Changed;
+        public event IBrowsableCollectionItemsChangedHandler ItemsChanged;
+
+        public delegate void DetailedCollectionChanged (IBrowsableCollection collection, int [] ids);
+        public event DetailedCollectionChanged DetailedChanged;
+
+        private void ClearCached ()
+        {
+            selection = null;
+            items = null;
+        }
+
+        public void SignalChange (int [] ids)
+        {
+            ClearCached ();
+            old = this.Items;
+
+
+            if (Changed != null)
+                Changed (this);
+
+            if (DetailedChanged!= null)
+                DetailedChanged (this, ids);
+        }
+    }
+}
+
diff --git a/src/Clients/MainApp/FSpot/SingleView.cs b/src/Clients/MainApp/FSpot/SingleView.cs
index 9f21414..da395db 100644
--- a/src/Clients/MainApp/FSpot/SingleView.cs
+++ b/src/Clients/MainApp/FSpot/SingleView.cs
@@ -252,7 +252,7 @@ namespace FSpot {
 		{
 
 			if (selection.Count > 0) {
-				image_view.Item.Index = ((FSpot.Widgets.IconView.SelectionCollection)selection).Ids[0];
+				image_view.Item.Index = ((FSpot.Widgets.SelectionCollection)selection).Ids[0];
 
 				zoom_scale.Value = image_view.NormalizedZoom;
 			}
diff --git a/src/Clients/MainApp/Makefile.am b/src/Clients/MainApp/Makefile.am
index 8e6ef75..6f05eb6 100644
--- a/src/Clients/MainApp/Makefile.am
+++ b/src/Clients/MainApp/Makefile.am
@@ -89,6 +89,7 @@ SOURCES =  \
 	FSpot.UI.Dialog/ThreadProgressDialog.cs \
 	FSpot.Widgets/CellGridView.cs \
 	FSpot.Widgets/CellRendererTextProgress.cs \
+	FSpot.Widgets/CollectionCellGridView.cs \
 	FSpot.Widgets/EditorPage.cs \
 	FSpot.Widgets/Filmstrip.cs \
 	FSpot.Widgets/FindBar.cs \
@@ -105,6 +106,7 @@ SOURCES =  \
 	FSpot.Widgets/QueryView.cs \
 	FSpot.Widgets/RatingMenuItem.cs \
 	FSpot.Widgets/ScalingIconView.cs \
+	FSpot.Widgets/SelectionCollection.cs \
 	FSpot.Widgets/Sharpener.cs \
 	FSpot.Widgets/Sidebar.cs \
 	FSpot.Widgets/SlideShow.cs \



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