[f-spot/iconview-selection: 1/3] Mouse selection in Iconview
- From: Ruben Vermeersch <rubenv src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [f-spot/iconview-selection: 1/3] Mouse selection in Iconview
- Date: Sat, 15 May 2010 09:41:15 +0000 (UTC)
commit 18d24829efad68d7a8e7c3eca840f54f9f88d235
Author: Vincent Pomey <vpomey free fr>
Date: Sat May 15 11:31:57 2010 +0200
Mouse selection in Iconview
Fixes BGO#336906 - selection of multiple photos by mouse
src/MainWindow.cs | 31 +++-
src/Widgets/IconView.cs | 470 +++++++++++++++++++++++++++++++++++++----------
src/Widgets/InfoBox.cs | 4 +-
src/ui/main_window.ui | 8 +
4 files changed, 410 insertions(+), 103 deletions(-)
---
diff --git a/src/MainWindow.cs b/src/MainWindow.cs
index f7455e3..3a11b58 100644
--- a/src/MainWindow.cs
+++ b/src/MainWindow.cs
@@ -448,11 +448,13 @@ namespace FSpot
new FSpot.PreviewPopup (icon_view);
- Gtk.Drag.SourceSet (icon_view, Gdk.ModifierType.Button1Mask | Gdk.ModifierType.Button3Mask,
- icon_source_target_table, DragAction.Copy | DragAction.Move);
-
icon_view.DragBegin += HandleIconViewDragBegin;
+ icon_view.DragEnd += HandleIconViewDragEnd;
icon_view.DragDataGet += HandleIconViewDragDataGet;
+ icon_view.DragMotion += HandleIconViewDragMotion;
+ icon_view.DragDrop += HandleIconViewDragDrop;
+ // StartDrag is fired by IconView
+ icon_view.StartDrag += HandleIconViewStartDrag;
TagMenu tag_menu = new TagMenu (null, Database.Tags);
tag_menu.NewTagHandler += delegate { HandleCreateTagAndAttach (this, null); };
@@ -467,9 +469,6 @@ namespace FSpot
Gtk.Drag.DestSet (icon_view, DestDefaults.All, icon_dest_target_table,
DragAction.Copy | DragAction.Move);
- // icon_view.DragLeave += new DragLeaveHandler (HandleIconViewDragLeave);
- icon_view.DragMotion += HandleIconViewDragMotion;
- icon_view.DragDrop += HandleIconViewDragDrop;
icon_view.DragDataReceived += HandleIconViewDragDataReceived;
icon_view.KeyPressEvent += HandleIconViewKeyPressEvent;
@@ -1057,8 +1056,14 @@ namespace FSpot
//
// IconView Drag Handlers
//
+
+ public void HandleIconViewStartDrag (object sender, StartDragArgs args)
+ {
+ Gtk.Drag.Begin (icon_view, new TargetList (icon_source_target_table),
+ DragAction.Copy | DragAction.Move, (int) args.Button, args.Event);
+ }
- void HandleIconViewDragBegin (object sender, DragBeginArgs args)
+ public void HandleIconViewDragBegin (object sender, DragBeginArgs args)
{
Photo [] photos = SelectedPhotos ();
@@ -1106,6 +1111,9 @@ namespace FSpot
container.Dispose ();
}
}
+
+ void HandleIconViewDragEnd (object sender, DragEndArgs args) {
+ }
void HandleIconViewDragDataGet (object sender, DragDataGetArgs args)
{
@@ -1375,7 +1383,6 @@ namespace FSpot
void HandlePhotoViewDragDrop (object sender, DragDropArgs args)
{
//Widget source = Gtk.Drag.GetSourceWidget (args.Context);
- //Console.WriteLine ("Drag Drop {0}", source == null ? "null" : source.TypeName);
args.RetVal = true;
}
@@ -2254,6 +2261,12 @@ namespace FSpot
icon_view.Selection.Clear ();
UpdateStatusLabel();
}
+
+ void HandleSelectInvertCommand (object sender, EventArgs args)
+ {
+ icon_view.Selection.SelectionInvert ();
+ UpdateStatusLabel();
+ }
// This ConnectBefore is needed because otherwise the editability of the name
// column will steal returns, spaces, and clicks if the tag name is focused
@@ -2264,7 +2277,7 @@ namespace FSpot
switch (args.Event.Key) {
case Gdk.Key.Delete:
- HandleDeleteSelectedTagCommand (sender, (EventArgs) args);
+ HandleDeleteSelectedTagCommand (sender, (EventArgs) args);
break;
case Gdk.Key.space:
diff --git a/src/Widgets/IconView.cs b/src/Widgets/IconView.cs
index 2874e3a..6a7aa13 100644
--- a/src/Widgets/IconView.cs
+++ b/src/Widgets/IconView.cs
@@ -19,6 +19,16 @@ 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 class IconView : Gtk.Layout {
// Public properties.
@@ -144,6 +154,9 @@ namespace FSpot.Widgets
// Size of the frame around the thumbnail.
protected int cell_border_width = 10;
+ // Size of the frame that may be selected
+ protected int cell_border_padding = 3;
+
// Border around the scrolled area.
protected const int BORDER_SIZE = 6;
@@ -163,6 +176,7 @@ namespace FSpot.Widgets
protected int cells_per_row;
protected int cell_width;
protected int cell_height;
+ protected int cell_details;
protected int displayed_rows; //for pgUp pgDn support
// The first pixel line that is currently on the screen (i.e. in the current
@@ -192,9 +206,13 @@ namespace FSpot.Widgets
// about.
private int click_count;
+ private Gdk.GC rect_gc=null;
+
// Public events.
public event EventHandler<BrowsableEventArgs> DoubleClicked;
public event EventHandler ZoomChanged;
+ public delegate void StartDragHandler (object o, StartDragArgs args);
+ public event StartDragHandler StartDrag;
// Public API.
public IconView (IntPtr raw) : base (raw) {}
@@ -210,6 +228,7 @@ namespace FSpot.Widgets
ButtonReleaseEvent += new ButtonReleaseEventHandler (HandleButtonReleaseEvent);
KeyPressEvent += new KeyPressEventHandler (HandleKeyPressEvent);
ScrollEvent += new ScrollEventHandler(HandleScrollEvent);
+ MotionNotifyEvent += new MotionNotifyEventHandler (HandleSelectMotionNotify);
Destroyed += HandleDestroyed;
@@ -287,6 +306,7 @@ namespace FSpot.Widgets
public class SelectionCollection : IBrowsableCollection {
IBrowsableCollection parent;
Hashtable selected_cells;
+ BitArray bit_array;
int [] selection;
IBrowsableItem [] items;
IBrowsableItem [] old;
@@ -295,6 +315,7 @@ namespace FSpot.Widgets
{
this.selected_cells = new Hashtable ();
this.parent = collection;
+ this.bit_array = new BitArray (this.parent.Count);
this.parent.Changed += HandleParentChanged;
this.parent.ItemsChanged += HandleParentItemsChanged;
}
@@ -303,6 +324,7 @@ namespace FSpot.Widgets
{
IBrowsableItem [] local = old;
selected_cells.Clear ();
+ bit_array = new BitArray (parent.Count);
ClearCached ();
if (old != null) {
@@ -355,6 +377,10 @@ namespace FSpot.Widgets
ItemsChanged (this, new BrowsableEventArgs (items, args.Changes));
}
+ public BitArray ToBitArray () {
+ return bit_array;
+ }
+
public int [] Ids {
get {
if (selection != null)
@@ -401,6 +427,7 @@ namespace FSpot.Widgets
{
int [] ids = Ids;
selected_cells.Clear ();
+ bit_array.SetAll (false);
SignalChange (ids);
}
@@ -437,7 +464,7 @@ namespace FSpot.Widgets
this.Add (num, true);
}
- private void Add (int num, bool notify)
+ public void Add (int num, bool notify)
{
if (num == -1)
return;
@@ -447,6 +474,7 @@ namespace FSpot.Widgets
IBrowsableItem item = parent [num];
selected_cells [item] = num;
+ bit_array.Set (num, true);
if (notify)
SignalChange (new int [] {num});
@@ -471,6 +499,37 @@ namespace FSpot.Widgets
SignalChange (ids);
}
+ public void Remove (int cell, bool notify)
+ {
+ IBrowsableItem item = parent [cell];
+ if (item != null)
+ this.Remove (item, notify);
+
+ }
+
+ public void Remove (IBrowsableItem item)
+ {
+ Remove (item, true);
+ }
+
+ public void Remove (int cell)
+ {
+ Remove (cell, true);
+ }
+
+ private void Remove (IBrowsableItem 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)
{
@@ -505,36 +564,51 @@ namespace FSpot.Widgets
return System.Array.IndexOf (Ids, parent_index);
}
- public void Remove (int cell)
+ private void ToggleCell (int cell_num, bool notify)
{
- Remove (cell, true);
+ if (Contains (cell_num))
+ Remove (cell_num, notify);
+ else
+ Add (cell_num, notify);
}
- private void Remove (int cell, bool notify)
+ public void ToggleCell (int cell_num)
{
- IBrowsableItem item = parent [cell];
- if (item != null)
- this.Remove (item, notify);
-
+ ToggleCell (cell_num, true);
}
- public void Remove (IBrowsableItem item)
+ public void SelectionInvert ()
{
- Remove (item, true);
+ 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);
}
- private void Remove (IBrowsableItem item, bool notify)
+ public void SelectRect (int start_row, int end_row, int start_line, int end_line, int cells_per_row)
{
- if (item == null)
- return;
-
- int parent_index = (int) selected_cells [item];
- selected_cells.Remove (item);
+ 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);
+ }
+ }
- if (notify)
- SignalChange (new int [] {parent_index});
+ 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;
@@ -547,7 +621,7 @@ namespace FSpot.Widgets
items = null;
}
- private void SignalChange (int [] ids)
+ public void SignalChange (int [] ids)
{
ClearCached ();
old = this.Items;
@@ -572,18 +646,18 @@ namespace FSpot.Widgets
// Cell Geometry
public int CellAtPosition (int x, int y)
{
- return CellAtPosition (x, y, true);
+ return CellAtPosition (x, y, true, false);
}
- public int CellAtPosition (int x, int y, bool crop_visible)
+ public int CellAtPosition (int x, int y, bool crop_visible, bool include_border)
{
if (collection == null)
return -1;
if (crop_visible
- && ((y < (int)Vadjustment.Value || y > (int)Vadjustment.Value + Allocation.Height)
- || (x < (int)Hadjustment.Value || x > (int)Hadjustment.Value + Allocation.Width)))
- return -1;
+ && ((y < (int)Vadjustment.Value || y > (int)Vadjustment.Value + Allocation.Height)
+ || (x < (int)Hadjustment.Value || x > (int)Hadjustment.Value + Allocation.Width)))
+ return -1;
if (x < BORDER_SIZE || x >= BORDER_SIZE + cells_per_row * cell_width)
return -1;
@@ -593,11 +667,22 @@ namespace FSpot.Widgets
int column = (int) ((x - BORDER_SIZE) / cell_width);
int row = (int) ((y - BORDER_SIZE) / cell_height);
int cell_num = column + row * cells_per_row;
-
- if (cell_num < collection.Count)
- return (int) cell_num;
- else
+ if (cell_num >= collection.Count)
return -1;
+
+ // check if the click is in the gap between unselected cells
+ if (!include_border) {
+ Gdk.Rectangle displayed = CellBounds (cell_num);
+ displayed.Inflate (-cell_border_padding, -cell_border_padding-cell_details);
+ displayed.Offset (-cell_border_padding, -cell_border_padding);
+ if (displayed.Contains (x, y))
+ return cell_num;
+ else if (selection.Contains (cell_num))
+ return cell_num;
+ else
+ return -1;
+ } else
+ return cell_num;
}
public int TopLeftVisibleCell ()
@@ -631,15 +716,6 @@ namespace FSpot.Widgets
selection.Add (0, collection.Count - 1);
}
- private void ToggleCell (int cell_num)
- {
- if (selection.Contains (cell_num))
- selection.Remove (cell_num);
- else
- selection.Add (cell_num);
- }
-
-
// Layout and drawing.
// FIXME I can't find a c# wrapper for the C PANGO_PIXELS () macro
@@ -663,24 +739,26 @@ namespace FSpot.Widgets
cell_height = ThumbnailHeight + 2 * cell_border_width;
cells_per_row = Math.Max ((int) (available_width / cell_width), 1);
cell_width += (available_width - cells_per_row * cell_width) / cells_per_row;
+ cell_details = 0;
if (DisplayTags || DisplayDates || DisplayFilenames)
- cell_height += tag_icon_vspacing;
+ cell_details += tag_icon_vspacing;
if (DisplayTags)
- cell_height += tag_icon_size;
+ cell_details += tag_icon_size;
if (DisplayDates && this.Style != null) {
Pango.FontMetrics metrics = this.PangoContext.GetMetrics (this.Style.FontDescription,
Pango.Language.FromString ("en_US"));
- cell_height += PangoPixels (metrics.Ascent + metrics.Descent);
+ cell_details += PangoPixels (metrics.Ascent + metrics.Descent);
}
if (DisplayFilenames && this.Style != null) {
Pango.FontMetrics metrics = this.PangoContext.GetMetrics (this.Style.FontDescription,
Pango.Language.FromString ("en_US"));
- cell_height += PangoPixels (metrics.Ascent + metrics.Descent);
+ cell_details += PangoPixels (metrics.Ascent + metrics.Descent);
}
+ cell_height += cell_details;
displayed_rows = (int)Math.Max (available_height / cell_height, 1);
@@ -762,6 +840,9 @@ namespace FSpot.Widgets
return expansion;
}
+ // rectangle of dragging selection
+ private Rectangle rect_select;
+
System.Collections.Hashtable date_layouts = new Hashtable ();
// FIXME Cache the GCs?
private void DrawCell (int thumbnail_num, Gdk.Rectangle area)
@@ -916,7 +997,8 @@ 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.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;
if (DisplayTags)
@@ -1036,8 +1118,8 @@ namespace FSpot.Widgets
//Preload (area, false);
for (i = 0, cell_num = start_cell_num;
- i < num_rows && cell_num < collection.Count;
- i ++) {
+ i < num_rows && cell_num < collection.Count;
+ i ++) {
int cell_x = start_cell_x;
//Log.Debug ("Drawing row {0}", start_cell_row + i);
@@ -1049,6 +1131,33 @@ namespace FSpot.Widgets
cell_y += cell_height;
cell_num += cells_per_row;
}
+
+ // draw dragging selection
+ if (isRectSelection) {
+ Gdk.Rectangle inter;
+ if (area.Intersect(rect_select, out inter)) {
+#if CAIRO_1_2_5
+ Cairo.Context cairo_g = CairoHelper.Create (BinWindow);
+ Gdk.Color col = Style.Background(StateType.Selected);
+ cairo_g.Color = new Cairo.Color (col.Red/65535.0, col.Green/65535.0, col.Blue/65535.0, 0.5);
+ cairo_g.Rectangle (inter.X, inter.Y, inter.Width, inter.Height);
+ cairo_g.Fill ();
+
+ ((IDisposable) cairo_g.Target).Dispose ();
+ ((IDisposable) cairo_g).Dispose ();
+#else
+ if (rect_gc == null) {
+ rect_gc = new Gdk.GC(BinWindow);
+ rect_gc.Copy (Style.BackgroundGC(StateType.Selected));
+ rect_gc.Fill = Fill.Stippled;
+ string bitmap_data = new string ((char) 0x02, (char) 0x01);
+ Pixmap pix = Pixmap.CreateBitmapFromData (BinWindow, bitmap_data, 2, 2);
+ rect_gc.Stipple = pix;
+ }
+ BinWindow.DrawRectangle (rect_gc, true, inter);
+#endif
+ }
+ }
}
@@ -1415,22 +1524,206 @@ namespace FSpot.Widgets
return base.OnExposeEvent (args);
}
- private void HandleButtonPressEvent (object obj, ButtonPressEventArgs args)
+
+ 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;
+
+ // during pointer motion, select/toggle pictures between initial x/y (param)
+ // and current x/y (get pointer)
+ private void SelectMotion ()
{
- int cell_num = CellAtPosition ((int) args.Event.X, (int) args.Event.Y, false);
+ 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;
+ }
- args.RetVal = true;
+ private void HandleSelectMotionNotify (object sender, MotionNotifyEventArgs args)
+ {
+ if ((args.Event.State & (ModifierType.Button1Mask | ModifierType.Button3Mask)) != 0 ) {
+ if (Gtk.Drag.CheckThreshold (this, (int) start_select_event.X,
+ (int) start_select_event.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;
+ }
- if (cell_num < 0) {
- args.RetVal = false;
- selection.Clear ();
- return;
+ // handle selection
+ SelectMotion();
+ } else {
+ int cell_num = CellAtPosition ((int) args.Event.X, (int) args.Event.Y, false, false);
+ if (cell_num < 0) {
+ // not on a cell : do rectangular select
+ isRectSelection = true;
+ double d_x, d_y;
+ if (!EventHelper.GetCoords (args.Event, out d_x, out d_y)) {
+ d_x = d_y = 0;
+ }
+ start_select_hadj = (int) Hadjustment.Value;
+ start_select_vadj = (int) Vadjustment.Value;
+ start_select_x = (int) d_x - start_select_hadj;
+ start_select_y = (int) d_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;
+ } else 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));
+ }
+ }
+ }
}
+ }
+
+ 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;
+ isRectSelection = false;
+ isDragDrop = false;
switch (args.Event.Type) {
case EventType.TwoButtonPress:
if (args.Event.Button != 1 ||
- (args.Event.State & (ModifierType.ControlMask | ModifierType.ShiftMask)) != 0)
+ (args.Event.State & (ModifierType.ControlMask | ModifierType.ShiftMask)) != 0)
return;
if (DoubleClicked != null)
DoubleClicked (this, new BrowsableEventArgs (cell_num, null));
@@ -1438,29 +1731,18 @@ namespace FSpot.Widgets
case EventType.ButtonPress:
GrabFocus ();
- if ((args.Event.State & ModifierType.ControlMask) != 0) {
- ToggleCell (cell_num);
- } else if ((args.Event.State & ModifierType.ShiftMask) != 0) {
- selection.Add (FocusCell, cell_num);
- } else if (!selection.Contains (cell_num)) {
- selection.Clear ();
- selection.Add (cell_num);
- }
-
+ // 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;
- }
-
- if (args.Event.Button != 1)
- return;
-
- FocusCell = cell_num;
- return;
+ } else args.RetVal = false;
+
+ break;
default:
args.RetVal = false;
- return;
+ break;
}
}
@@ -1470,31 +1752,33 @@ namespace FSpot.Widgets
private void HandleButtonReleaseEvent (object sender, ButtonReleaseEventArgs args)
{
- int cell_num = CellAtPosition ((int) args.Event.X, (int) args.Event.Y);
-
- args.RetVal = true;
-
- if (cell_num < 0) {
- args.RetVal = false;
- return;
- }
-
- switch (args.Event.Type) {
- case EventType.ButtonRelease:
- if ((args.Event.State & ModifierType.ControlMask) == 0 &&
- (args.Event.State & ModifierType.ShiftMask ) == 0 &&
- (selection.Count > 1)) {
- selection.Clear ();
- selection.Add (FocusCell);
+ if (isRectSelection) {
+ // remove scrolling and rectangular selection
+ if (scroll_timeout != 0) {
+ GLib.Source.Remove (scroll_timeout);
+ scroll_timeout = 0;
+ }
+ 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;
}
-
- break;
-
- default:
- args.RetVal = false;
- break;
}
-
+ isDragDrop = false;
}
private void HandleKeyPressEvent (object sender, KeyPressEventArgs args)
@@ -1549,7 +1833,7 @@ namespace FSpot.Widgets
FocusCell = new Random().Next(0, collection.Count - 1);
break;
case Gdk.Key.space:
- ToggleCell (FocusCell);
+ selection.ToggleCell (FocusCell);
break;
case Gdk.Key.Return:
if (DoubleClicked != null)
diff --git a/src/Widgets/InfoBox.cs b/src/Widgets/InfoBox.cs
index afac668..8b4b836 100644
--- a/src/Widgets/InfoBox.cs
+++ b/src/Widgets/InfoBox.cs
@@ -469,8 +469,10 @@ namespace FSpot.Widgets
if (Photos == null || Photos.Length == 0) {
Hide ();
} else if (Photos.Length == 1) {
+ Show ();
UpdateSingle ();
} else if (Photos.Length > 1) {
+ Show ();
UpdateMultiple ();
}
return false;
@@ -649,7 +651,7 @@ namespace FSpot.Widgets
}
date_label.Visible = show_date;
date_value_label.Visible = show_date;
-
+
if (show_file_size) {
long file_size = 0;
foreach (Photo photo in Photos) {
diff --git a/src/ui/main_window.ui b/src/ui/main_window.ui
index 5c7ee79..7082d1d 100644
--- a/src/ui/main_window.ui
+++ b/src/ui/main_window.ui
@@ -130,6 +130,14 @@
<accelerator key="A" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK"/>
</child>
<child>
+ <object class="GtkAction" id="select_invert">
+ <property name="name">select_invert</property>
+ <property name="label" translatable="yes">_Invert Selection</property>
+ <signal handler="HandleSelectInvertCommand" name="activate"/>
+ </object>
+ <accelerator key="I" modifiers="GDK_CONTROL_MASK"/>
+ </child>
+ <child>
<object class="GtkAction" id="rotate_left">
<property name="name">rotate_left</property>
<property name="label" translatable="yes">Rotate _Left</property>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]