using System; using Mono.Unix; using Gtk; using Gdk; using GLib; namespace FSpot { public class GroupSelector : Fixed { internal static GType groupSelectorGType; int border = 6; int box_spacing = 2; int box_top_padding = 6; public static int MIN_BOX_WIDTH = 20; private Glass glass; private Limit min_limit; private Limit max_limit; private Gtk.Button left; private Gtk.Button right; private Delay left_delay; private Delay right_delay; private Gdk.Window event_window; public Gdk.Rectangle background; public Gdk.Rectangle legend; public Gdk.Rectangle action; Pango.Layout [] tick_layouts; int [] box_counts = new int [0]; int box_count_max; int min_filled; int max_filled; bool has_limits; protected FSpot.GroupAdaptor adaptor; public FSpot.GroupAdaptor Adaptor { set { if (adaptor != null) { adaptor.Changed -= HandleAdaptorChanged; adaptor.Dispose (); } adaptor = value; HandleAdaptorChanged (adaptor); has_limits = adaptor is FSpot.ILimitable; if (has_limits) { min_limit.SetPosition (0, false); max_limit.SetPosition (adaptor.Count () - 1, false); } if (adaptor is TimeAdaptor) { MainWindow.ToolTips.SetTip (left, Catalog.GetString ("More dates"), null); MainWindow.ToolTips.SetTip (right, Catalog.GetString ("More dates"), null); } else if (adaptor is DirectoryAdaptor) { MainWindow.ToolTips.SetTip (left, Catalog.GetString ("More directories"), null); MainWindow.ToolTips.SetTip (right, Catalog.GetString ("More directories"), null); } else { MainWindow.ToolTips.SetTip (left, Catalog.GetString ("More"), null); MainWindow.ToolTips.SetTip (right, Catalog.GetString ("More"), null); } adaptor.Changed += HandleAdaptorChanged; } get { return adaptor; } } public int GlassPosition { get { return glass.Position; } } private void HandleAdaptorChanged (GroupAdaptor adaptor) { bool size_changed = box_counts.Length != adaptor.Count (); int [] box_values = new int [adaptor.Count ()]; if (tick_layouts != null) { foreach (Pango.Layout l in tick_layouts) { if (l != null) l.Dispose (); } } tick_layouts = new Pango.Layout [adaptor.Count ()]; int i = 0; while (i < adaptor.Count ()) { box_values [i] = adaptor.Value (i); string label = adaptor.TickLabel (i); if (label != null) { tick_layouts [i] = CreatePangoLayout (label); } i++; } if (glass.Position >= adaptor.Count()) glass.SetPosition (adaptor.Count() - 1); Counts = box_values; if (has_limits && size_changed) { min_limit.SetPosition (0, false); max_limit.SetPosition (adaptor.Count () - 1, false); } for (i = min_limit.Position; i < box_counts.Length; i++) if (box_counts [i] > 0) break; SetPosition (i < box_counts.Length ? i : min_limit.Position); ScrollTo (min_limit.Position); this.QueueDraw (); } private int [] Counts { set { bool min_found = false; box_count_max = 0; min_filled = 0; max_filled = 0; if (value != null) box_counts = value; else value = new int [0]; for (int i = 0; i < box_counts.Length; i++){ int count = box_counts [i]; box_count_max = Math.Max (count, box_count_max); if (count > 0) { if (!min_found) { min_filled = i; min_found = true; } max_filled = i; } } } } public enum RangeType { All, Fixed, Min } private RangeType mode = RangeType.Min; public RangeType Mode { get { return mode; } set { mode = value; if (Visible) GdkWindow.InvalidateRect (Allocation, false); } } private void ScrollTo (int position) { if (position == min_filled) position = 0; else if (position == max_filled) position = box_counts.Length - 1; Gdk.Rectangle box = new Box (this, position).Bounds; if (box.Right > background.Right) Offset -= box.X + box.Width - (background.X + background.Width); else if (box.X < background.X) Offset += background.X - box.X; } private int scroll_offset; public int Offset { get { return scroll_offset; } set { scroll_offset = value; int total_width = (int)(box_counts.Length * BoxWidth); if (total_width + scroll_offset < background.Width) scroll_offset = background.Width - total_width; if (total_width <= background.Width) scroll_offset = 0; UpdateButtons (); if (Visible) GdkWindow.InvalidateRect (Allocation, false); } } private void UpdateButtons () { left.Sensitive = (scroll_offset < 0); right.Sensitive = (box_counts.Length * BoxWidth > background.Width - scroll_offset); if (!left.Sensitive && left_delay.IsPending) left_delay.Stop (); if (!right.Sensitive && right_delay.IsPending) right_delay.Stop (); } private void BoxXHitFilled (double x, out int out_position) { x -= BoxX (0); double position = (x / BoxWidth); position = System.Math.Max (0, position); position = System.Math.Min (position, box_counts.Length - 1); if (box_counts [(int)position] > 0) { out_position = (int)position; } else { int upper = (int)position; while (upper < box_counts.Length && box_counts [upper] == 0) upper++; int lower = (int)position; while (lower >= 0 && box_counts [lower] == 0) lower--; if (lower == -1 && upper == box_counts.Length) { out_position = (int)position; } else if (lower == -1 && upper < box_counts.Length) { out_position = upper; } else if (upper == box_counts.Length && lower > -1){ out_position = lower; } else if (upper + 1 - position > position - lower) { out_position = lower; } else { out_position = upper; } } } private bool BoxXHit (double x, out int position) { x -= BoxX (0); position = (int) (x / BoxWidth); if (position < 0) { position = 0; return false; } else if (position >= box_counts.Length) { position = box_counts.Length -1; return false; } return true; } private bool BoxHit (double x, double y, out int position) { if (BoxXHit (x, out position)) { Box box = new Box (this, position); if (box.Bounds.Contains ((int) x, (int) y)) return true; position++; } return false; } public void SetPosition (int group) { if (!glass.Dragging) glass.SetPosition(group); } protected override bool OnButtonPressEvent (Gdk.EventButton args) { if (args.Button == 3) return DrawOrderMenu (args); double x = args.X + action.X; double y = args.Y + action.Y; if (glass.Contains (x, y)) { glass.StartDrag (x, y, args.Time); } else if (has_limits && min_limit.Contains (x, y)) { min_limit.StartDrag (x, y, args.Time); } else if (has_limits && max_limit.Contains (x, y)) { max_limit.StartDrag (x, y, args.Time); } else { int position; if (BoxHit (x, y, out position)) { BoxXHitFilled (x, out position); glass.Dragging = true; glass.SetPosition (position); glass.Dragging = false; return true; } } return base.OnButtonPressEvent (args); } protected override bool OnButtonReleaseEvent (Gdk.EventButton args) { double x = args.X + action.X; double y = args.Y + action.Y; if (glass.Dragging) { glass.EndDrag (x, y); } else if (min_limit.Dragging) { min_limit.EndDrag (x, y); } else if (max_limit.Dragging) { max_limit.EndDrag (x, y); } return base.OnButtonReleaseEvent (args); } public void UpdateLimits () { if (adaptor != null && has_limits && min_limit != null && max_limit != null) ((ILimitable)adaptor).SetLimits (min_limit.Position, max_limit.Position); } protected override bool OnMotionNotifyEvent (Gdk.EventMotion args) { double x = args.X + action.X; double y = args.Y + action.Y; //Rectangle box = glass.Bounds (); //Console.WriteLine ("please {0} and {1} in box {2}", x, y, box); if (glass.Dragging) { glass.UpdateDrag (x, y); } else if (min_limit.Dragging) { min_limit.UpdateDrag (x, y); } else if (max_limit.Dragging) { max_limit.UpdateDrag (x, y); } else { glass.State = glass.Contains (x, y) ? StateType.Prelight : StateType.Normal; min_limit.State = min_limit.Contains (x, y) ? StateType.Prelight : StateType.Normal; max_limit.State = max_limit.Contains (x, y) ? StateType.Prelight : StateType.Normal; } return base.OnMotionNotifyEvent (args); } protected override void OnRealized () { SetFlag (WidgetFlags.Realized); GdkWindow = ParentWindow; base.OnRealized (); WindowAttr attr = WindowAttr.Zero; attr.WindowType = Gdk.WindowType.Child; attr.X = action.X; attr.Y = action.Y; attr.Width = action.Width; attr.Height = action.Height; attr.Wclass = WindowClass.InputOnly; attr.EventMask = (int) Events; attr.EventMask |= (int) (EventMask.ButtonPressMask | EventMask.KeyPressMask | EventMask.KeyReleaseMask | EventMask.ButtonReleaseMask | EventMask.PointerMotionMask); event_window = new Gdk.Window (GdkWindow, attr, (int) (WindowAttributesType.X | WindowAttributesType.Y)); event_window.UserData = this.Handle; } protected override void OnUnrealized () { event_window.Dispose (); event_window = null; base.OnUnrealized (); } private Double BoxWidth { get { switch (mode) { case RangeType.All: return Math.Max (1.0, background.Width / (double) box_counts.Length); case RangeType.Fixed: return background.Width / (double) 12; case RangeType.Min: return Math.Max (MIN_BOX_WIDTH, background.Width / (double) box_counts.Length); default: return (double) MIN_BOX_WIDTH; } } } private int BoxX (int item) { return scroll_offset + background.X + (int) Math.Round (BoxWidth * item); } private struct Box { Gdk.Rectangle bounds; Gdk.Rectangle bar; public Box (GroupSelector selector, int item) { bounds.Height = selector.background.Height; bounds.Y = selector.background.Y; bounds.X = selector.BoxX (item); bounds.Width = Math.Max (selector.BoxX (item + 1) - bounds.X, 1); if (item < 0 || item > selector.box_counts.Length - 1) return; double percent = selector.box_counts [item] / (double) Math.Max (selector.box_count_max, 1); bar = bounds; bar.Height = (int) Math.Ceiling ((bounds.Height - selector.box_top_padding) * percent); bar.Y += bounds.Height - bar.Height - 1; bar.Inflate (- selector.box_spacing, 0); } public Gdk.Rectangle Bounds { get { return bounds; } } public Gdk.Rectangle Bar { get { return bar; } } } private void DrawBox (Rectangle area, int item) { Box box = new Box (this, item); Rectangle bar = box.Bar; if (bar.Intersect (area, out area)) { if (item < min_limit.Position || item > max_limit.Position) { #if false bar.Height += 1; //GdkWindow.DrawRectangle (Style.ForegroundGC (StateType.Normal), false, box); Style.PaintShadow (this.Style, GdkWindow, State, ShadowType.In, area, this, null, bar.X, bar.Y, bar.Width, bar.Height); #else GdkWindow.DrawRectangle (Style.BackgroundGC (StateType.Active), true, area); #endif } else { GdkWindow.DrawRectangle (Style.BaseGC (StateType.Selected), true, area); } } } public Rectangle TickBounds (int item) { Rectangle bounds = Rectangle.Zero; bounds.X = BoxX (item); bounds.Y = legend.Y + 3; bounds.Width = 1; bounds.Height = 6; return bounds; } public void DrawTick (Rectangle area, int item) { Rectangle tick = TickBounds (item); Pango.Layout layout = null; if (item < tick_layouts.Length) { layout = tick_layouts [item]; if (layout != null) { int width, height; layout.GetPixelSize (out width, out height); Style.PaintLayout (Style, GdkWindow, State, true, area, this, "GroupSelector:Tick", tick.X + 3, tick.Y + tick.Height, layout); } } if (layout == null) tick.Height /= 2; if (tick.Intersect (area, out area)) { GdkWindow.DrawRectangle (Style.ForegroundGC (State), true, area); } } protected override bool OnPopupMenu () { DrawOrderMenu (null); return true; } private bool DrawOrderMenu (Gdk.EventButton args) { Gtk.Menu order_menu = new Gtk.Menu(); GtkUtil.MakeCheckMenuItem (order_menu, Catalog.GetString ("Arrange by _Month"), MainWindow.Toplevel.HandleArrangeByTime, true, (adaptor is TimeAdaptor), true); GtkUtil.MakeCheckMenuItem (order_menu, Catalog.GetString ("Arrange by _Folder"), MainWindow.Toplevel.HandleArrangeByDirectory, true, (adaptor is DirectoryAdaptor), true); GtkUtil.MakeMenuSeparator (order_menu); GtkUtil.MakeCheckMenuItem (order_menu, Catalog.GetString ("_Reverse Order"), MainWindow.Toplevel.HandleReverseOrder, true, adaptor.OrderAscending, false); if (adaptor is TimeAdaptor && adaptor.Query.Range != null) { GtkUtil.MakeMenuSeparator (order_menu); GtkUtil.MakeMenuItem (order_menu, Catalog.GetString ("_Clear Date Range"), MainWindow.Toplevel.HandleClearDateRange); } if (args != null) order_menu.Popup (null, null, null, args.Button, args.Time); else order_menu.Popup (null, null, null, 0, Gtk.Global.CurrentEventTime); return true; } public abstract class Manipulator { protected GroupSelector selector; protected Delay timer; public bool Dragging; public Point DragStart; public Manipulator (GroupSelector selector) { this.selector = selector; timer = new Delay (50, new GLib.IdleHandler (DragTimeout)); } protected int drag_offset; public int DragOffset { set { Rectangle then = Bounds (); drag_offset = value; Rectangle now = Bounds (); if (selector.Visible) { selector.GdkWindow.InvalidateRect (then, false); selector.GdkWindow.InvalidateRect (now, false); } } get { if (Dragging) return drag_offset; else return 0; } } public virtual void StartDrag (double x, double y, uint time) { State = StateType.Active; //timer.Start (); Dragging = true; DragStart.X = (int)x; DragStart.Y = (int)y; } private bool DragTimeout () { int x, y; selector.GetPointer (out x, out y); x += selector.Allocation.X; y += selector.Allocation.Y; UpdateDrag ((double) x, (double) y); return true; } protected bool PositionValid (int position) { if (position < 0 || position > selector.box_counts.Length - 1) return false; return true; } public virtual void UpdateDrag (double x, double y) { Rectangle bounds = Bounds (); int scroll = selector.background.X - bounds.Left; if (scroll > 0 && selector.Offset < 0) { selector.Offset = Math.Min (selector.Offset + scroll, 0); if (selector.Offset == 0) return; } //System.Console.WriteLine ("scroll {0}, x {1}", scroll, x); DragOffset = (int)x - DragStart.X; } public virtual void EndDrag (double x, double y) { timer.Stop (); Rectangle box = Bounds (); double middle = box.X + (box.Width / 2.0); int position; DragOffset = 0; if (selector.BoxXHit (middle, out position)) { this.SetPosition (position); State = StateType.Prelight; } else { State = selector.State; } Dragging = false; } private StateType state; public StateType State { get { return state; } set { if (state != value) { selector.GdkWindow.InvalidateRect (Bounds (), false); } state = value; } } public void SetPosition (int position) { SetPosition (position, true); } public void SetPosition (int position, bool update) { if (! PositionValid (position)) return; Rectangle then = Bounds (); this.position = position; Rectangle now = Bounds (); if (selector.Visible) { then = now.Union (then); selector.GdkWindow.InvalidateRect (then, false); //selector.GdkWindow.InvalidateRect (now, false); } if (update) PositionChanged (); } private int position; public int Position { get { return position; } } public abstract void Draw (Rectangle area); public abstract void PositionChanged (); public abstract Rectangle Bounds (); public virtual bool Contains (double x, double y) { return Bounds ().Contains ((int)x, (int)y); } } private class Glass : Manipulator { Gtk.Window popup_window; Gtk.Label popup_label; int drag_position; public Glass (GroupSelector selector) : base (selector) { popup_window = new TipWindow (); popup_label = new Gtk.Label (String.Empty); popup_label.Show (); popup_window.Add (popup_label); } public int handle_height = 15; private int border { get { return selector.box_spacing * 2; } } private void UpdatePopupPosition () { int x = 0, y = 0; Rectangle bounds = Bounds (); Requisition requisition = popup_window.SizeRequest (); popup_window.Resize (requisition.Width, requisition.Height); selector.GdkWindow.GetOrigin (out x, out y); x += bounds.X + (bounds.Width - requisition.Width) / 2; y += bounds.Y - requisition.Height; x = Math.Max (x, 0); x = Math.Min (x, selector.Screen.Width - requisition.Width); popup_window.Move (x, y); } public override void StartDrag (double x, double y, uint time) { base.StartDrag (x, y, time); popup_label.Text = selector.Adaptor.GlassLabel (this.Position); popup_window.Show (); UpdatePopupPosition (); drag_position = this.Position; } public override void UpdateDrag (double x, double y) { Rectangle box = Bounds (); double middle = box.X + (box.Width / 2.0); int current_position; base.UpdateDrag (x, y); if (selector.BoxXHit (middle, out current_position)) { if (current_position != drag_position) popup_label.Text = selector.Adaptor.GlassLabel (current_position); drag_position = current_position; } UpdatePopupPosition (); selector.ScrollTo (drag_position); } public override void EndDrag (double x, double y) { timer.Stop (); Rectangle box = Bounds (); double middle = box.X + (box.Width / 2.0); int position; DragOffset = 0; selector.BoxXHitFilled (middle, out position); this.SetPosition (position); State = StateType.Prelight; Dragging = false; popup_window.Hide (); } private Rectangle InnerBounds () { Rectangle box = new Box (selector, Position).Bounds; box.X += DragOffset; return box; } public override Rectangle Bounds () { Rectangle box = InnerBounds (); box.Inflate (border, border); box.Height += handle_height; return box; } public override void Draw (Rectangle area) { if (! PositionValid (Position)) return; Rectangle inner = InnerBounds (); Rectangle bounds = Bounds (); if (! bounds.Intersect (area, out area)) return; int i = 0; Rectangle box = inner; box.Width -= 1; box.Height -= 1; while (i < border) { box.Inflate (1, 1); selector.Style.BackgroundGC (State).ClipRectangle = area; selector.GdkWindow.DrawRectangle (selector.Style.BackgroundGC (State), false, box); i++; } Style.PaintFlatBox (selector.Style, selector.GdkWindow, State, ShadowType.In, area, selector, "glass", bounds.X, inner.Y + inner.Height + border, bounds.Width, handle_height); Style.PaintHandle (selector.Style, selector.GdkWindow, State, ShadowType.In, area, selector, "glass", bounds.X, inner.Y + inner.Height + border, bounds.Width, handle_height, Orientation.Horizontal); Style.PaintShadow (selector.Style, selector.GdkWindow, State, ShadowType.Out, area, selector, null, bounds.X, bounds.Y, bounds.Width, bounds.Height); Style.PaintShadow (selector.Style, selector.GdkWindow, State, ShadowType.In, area, selector, null, inner.X, inner.Y, inner.Width, inner.Height); } public override void PositionChanged () { if (Dragging) selector.adaptor.SetGlass (Position); selector.ScrollTo (Position); } } public class Limit : Manipulator { int width = 10; int handle_height = 10; public enum LimitType { Min, Max } private LimitType limit_type; public override Rectangle Bounds () { int limit_offset = limit_type == LimitType.Max ? 1 : 0; Rectangle bounds = new Rectangle (0, 0, width, selector.background.Height + handle_height); bounds.X = DragOffset + selector.BoxX (Position + limit_offset) - bounds.Width /2; bounds.Y = selector.background.Y - handle_height/2; return bounds; } public override void Draw (Rectangle area) { Rectangle bounds = Bounds (); Rectangle top = new Rectangle (bounds.X, bounds.Y, bounds.Width, handle_height); Rectangle bottom = new Rectangle (bounds.X, bounds.Y + bounds.Height - handle_height, bounds.Width, handle_height); #if false Gdk.Point [] top_points = { new Gdk.Point (top.X, top.Y), new Gdk.Point (top.X, top.Y + top.Height), new Gdk.Point ((int)(top.X + top.Width/2), (int)(top.Y + top.Height * 1.5)), new Gdk.Point (top.X + top.Width, top.Y + top.Height), new Gdk.Point (top.X + top.Width, top.Y), new Gdk.Point (top.X, top.Y) }; Style.PaintPolygon (selector.Style, selector.GdkWindow, State, ShadowType.In, area, selector, "IconView:Selector", top_points, true); Gdk.Point [] bottom_points = { new Gdk.Point (bottom.X, bottom.Y + bottom.Height), new Gdk.Point (bottom.X, bottom.Y), new Gdk.Point ((int)(bottom.X + bottom.Width/2), (int)(bottom.Y - bottom.Height/2)), new Gdk.Point (bottom.X + bottom.Width, bottom.Y), new Gdk.Point (bottom.X + bottom.Width, bottom.Y + bottom.Height), new Gdk.Point (bottom.X, bottom.Y + bottom.Height) }; Style.PaintPolygon (selector.Style, selector.GdkWindow, State, ShadowType.In, area, selector, "IconView:Selector", bottom_points, true); #else Style.PaintBox (selector.Style, selector.GdkWindow, State, ShadowType.Out, area, selector, null, top.X, top.Y, top.Width, top.Height); Style.PaintBox (selector.Style, selector.GdkWindow, State, ShadowType.Out, area, selector, null, bottom.X, bottom.Y, bottom.Width, bottom.Height); #endif } public Limit (GroupSelector selector, LimitType type) : base (selector) { limit_type = type; } public override void PositionChanged () { selector.UpdateLimits (); } } protected override void OnMapped () { base.OnMapped (); if (event_window != null) event_window.Show (); } protected override bool OnExposeEvent (Gdk.EventExpose args) { Rectangle area; //Console.WriteLine ("expose {0}", args.Area); foreach (Rectangle sub in args.Region.GetRectangles ()) { if (sub.Intersect (background, out area)) { Rectangle active = background; int min_x = BoxX (min_limit.Position); int max_x = BoxX (max_limit.Position + 1); active.X = min_x; active.Width = max_x - min_x; if (active.Intersect (area, out active)) { GdkWindow.DrawRectangle (Style.BaseGC (State), true, active); } int i; BoxXHit (area.X, out i); int end; BoxXHit (area.X + area.Width, out end); while (i <= end) DrawBox (area, i++); } Style.PaintShadow (this.Style, GdkWindow, State, ShadowType.In, area, this, null, background.X, background.Y, background.Width, background.Height); if (sub.Intersect (legend, out area)) { int i = 0; while (i < box_counts.Length) DrawTick (area, i++); } if (has_limits) { if (min_limit != null) { min_limit.Draw (sub); } if (max_limit != null) { max_limit.Draw (sub); } } if (glass != null) { glass.Draw (sub); } } return base.OnExposeEvent (args); } protected override void OnSizeRequested (ref Requisition requisition) { requisition.Width = 500; requisition.Height = (int) (LegendHeight () + glass.handle_height + 3 * border); base.OnSizeRequested (ref requisition); } // FIXME I can't find a c# wrapper for the C PANGO_PIXELS () macro // So this Function is for that. public static int PangoPixels (int val) { return val >= 0 ? (val + 1024 / 2) / 1024 : (val - 1024 / 2) / 1024; } private int LegendHeight () { int max_height = 0; Pango.FontMetrics metrics = this.PangoContext.GetMetrics (this.Style.FontDescription, Pango.Language.FromString ("en_US")); max_height += PangoPixels (metrics.Ascent + metrics.Descent); foreach (Pango.Layout l in tick_layouts) { if (l != null) { int width, height; l.GetPixelSize (out width, out height); max_height = Math.Max (height, max_height); } } return (int) (max_height * 1.5); } private bool HandleScrollRight () { Offset -= 10; return true; } private bool HandleScrollLeft () { Offset += 10; return true; } private void HandleLeftPressed (object sender, System.EventArgs ars) { HandleScrollLeft (); left_delay.Start (); } private void HandleRightPressed (object sender, System.EventArgs ars) { HandleScrollRight (); right_delay.Start (); } [GLib.ConnectBefore] private void HandleScrollReleaseEvent (object sender, ButtonReleaseEventArgs args) { right_delay.Stop (); left_delay.Stop (); } protected override void OnSizeAllocated (Gdk.Rectangle alloc) { base.OnSizeAllocated (alloc); int legend_height = LegendHeight (); Gdk.Rectangle bar = new Rectangle (alloc.X + border, alloc.Y + border, alloc.Width - 2 * border, alloc.Height - 2 * border - glass.handle_height); if (left.Allocation.Y != bar.Y || left.Allocation.X != bar.X) { left.SetSizeRequest (-1, bar.Height); this.Move (left, bar.X - Allocation.X, bar.Y - Allocation.Y); } if (right.Allocation.Y != bar.Y || right.Allocation.X != bar.X + bar.Width - right.Allocation.Width) { right.SetSizeRequest (-1, bar.Height); this.Move (right, bar.X - Allocation.X + bar.Width - right.Allocation.Width, bar.Y - Allocation.Y); } background = new Rectangle (bar.X + left.Allocation.Width, bar.Y, bar.Width - left.Allocation.Width - right.Allocation.Width, bar.Height); legend = new Rectangle (background.X, background.Y, background.Width, legend_height); action = background.Union (glass.Bounds ()); if (event_window != null) event_window.MoveResize (action.X, action.Y, action.Width, action.Height); this.Offset = this.Offset; UpdateButtons (); } public void ResetLimits () { min_limit.SetPosition(0,false); max_limit.SetPosition(adaptor.Count () - 1, false); } public void SetLimitsToDates(DateTime start, DateTime stop) { if (((TimeAdaptor)adaptor).OrderAscending) { min_limit.SetPosition(((TimeAdaptor)adaptor).IndexFromDate(start),false); max_limit.SetPosition(((TimeAdaptor)adaptor).IndexFromDate(stop),false); } else { min_limit.SetPosition(((TimeAdaptor)adaptor).IndexFromDate(stop),false); max_limit.SetPosition(((TimeAdaptor)adaptor).IndexFromDate(start),false); } } public GroupSelector () : base () { SetFlag (WidgetFlags.NoWindow); background = Rectangle.Zero; glass = new Glass (this); min_limit = new Limit (this, Limit.LimitType.Min); max_limit = new Limit (this, Limit.LimitType.Max); left = new Gtk.Button (); //left.Add (new Gtk.Image (Gtk.Stock.GoBack, Gtk.IconSize.Button)); left.Add (new Gtk.Arrow (Gtk.ArrowType.Left, Gtk.ShadowType.None)); left.Relief = Gtk.ReliefStyle.None; //left.Clicked += HandleScrollLeft; left.Pressed += HandleLeftPressed; left.ButtonReleaseEvent += HandleScrollReleaseEvent; left_delay = new Delay (50, new GLib.IdleHandler (HandleScrollLeft)); right = new Gtk.Button (); //right.Add (new Gtk.Image (Gtk.Stock.GoForward, Gtk.IconSize.Button)); right.Add (new Gtk.Arrow (Gtk.ArrowType.Right, Gtk.ShadowType.None)); right.Relief = Gtk.ReliefStyle.None; right.Pressed += HandleRightPressed; right.ButtonReleaseEvent += HandleScrollReleaseEvent; right_delay = new Delay (50, new GLib.IdleHandler (HandleScrollRight)); //right.Clicked += HandleScrollRight; this.Put (left, 0, 0); this.Put (right, 100, 0); left.Show (); right.Show (); CanFocus = true; Mode = RangeType.Min; UpdateButtons (); } public GroupSelector (IntPtr raw) : base (raw) {} #if TEST_MAIN private void HandleKeyPressEvent (object sender, KeyPressEventArgs args) { switch (args.Event.Key) { case Gdk.Key.Left: if (glass.Position > 0) this.glass.Position--; else glass.Position = box_counts.Length - 1; break; case Gdk.Key.Right: if (glass.Position < box_counts.Length - 1) glass.Position++; else glass.Position = 0; break; case Gdk.Key.Down: if (Mode > 0) Mode--; else Mode = 2; break; case Gdk.Key.Up: if (mode < 2) Mode++; else Mode = 0; break; case Gdk.Key.Home: Offset += 10; break; case Gdk.Key.End: Offset -= 10; break; } } public static int Main (string [] args) { Application.Init (); Gtk.Window win = new Gtk.Window ("testing"); VBox vbox = new VBox (false, 10); GroupSelector gs = new GroupSelector (); gs.Counts = new int [] {20, 100, 123, 10, 5, 2, 3, 50, 8, 10, 22, 0, 55, 129, 120, 30, 14, 200, 21, 55}; gs.Mode = RangeType.Fixed; vbox.PackStart (gs); gs = new GroupSelector (); gs.Counts = new int [] {20, 100, 123, 10, 5, 2, 3, 50, 8, 10, 22, 0, 55, 129, 120, 30, 14, 200, 21, 55}; gs.Mode = RangeType.Fixed; vbox.PackStart (gs); win.Add (vbox); win.ShowAll (); win.AddEvents ((int) EventMask.KeyPressMask); win.KeyPressEvent += gs.HandleKeyPressEvent; Application.Run (); return 0; } #endif } }