/* * * Author(s) * * Larry Ewing * * This is free software. See COPYING for details * */ using System; using Cairo; using Gdk; using FSpot.Widgets; namespace FSpot { public class PreviewPopup : Gtk.Window { private IconView view; private Gtk.Image image; private Gtk.Label label; private bool composited; private bool show_histogram; public bool ShowHistogram { get { return show_histogram; } set { if (value != show_histogram) { preview_cache.Dispose (); preview_cache = new ThumbnailCache (50); item = -1; } show_histogram = value; } } private FSpot.Histogram hist; private ThumbnailCache preview_cache = new ThumbnailCache (50); private int item; new public int Item { get { return item; } set { if (value != item) { item = value; UpdateImage (); } UpdatePosition (); } } private void AddHistogram (Gdk.Pixbuf pixbuf) { if (show_histogram) { hist.FillValues (pixbuf); Gdk.Pixbuf image = hist.GeneratePixbuf (); double scalex = 0.5; double scaley = 0.5; int width = (int)(image.Width * scalex); int height = (int)(image.Height * scaley); image.Composite (pixbuf, pixbuf.Width - width - 10, pixbuf.Height - height - 10, width, height, pixbuf.Width - width - 10, pixbuf.Height - height - 10, scalex, scaley, Gdk.InterpType.Bilinear, 200); } } protected override void OnRealized () { bool composited = CompositeUtils.IsComposited (Screen) && CompositeUtils.SetRgbaColormap (this); AppPaintable = composited; base.OnRealized (); } protected override bool OnExposeEvent (Gdk.EventExpose args) { int round = 12; Context g = CairoUtils.CreateContext (GdkWindow); g.Operator = Operator.Source; g.Source = new SolidPattern (new Cairo.Color (0, 0, 0, 0)); g.Paint (); g.Operator = Operator.Over; #if true g.Source = new SolidPattern (new Cairo.Color (0, 0, 0, .7)); g.MoveTo (round, 0); //g.LineTo (Allocation.Width - round, 0); g.Arc (Allocation.Width - round, round, round, - Math.PI * 0.5, 0); //g.LineTo (Allocation.Width, Allocation.Height - round); g.Arc (Allocation.Width - round, Allocation.Height - round, round, 0, Math.PI * 0.5); //g.LineTo (round, Allocation.Height); g.Arc (round, Allocation.Height - round, round, Math.PI * 0.5, Math.PI); g.Arc (round, round, round, Math.PI, Math.PI * 1.5); g.ClosePath (); g.Fill (); #endif ((IDisposable)g).Dispose (); return base.OnExposeEvent (args); } private void UpdateImage () { FSpot.IBrowsableItem item = view.Collection.Items [Item]; string orig_path = item.DefaultVersionUri.LocalPath; Gdk.Pixbuf pixbuf = preview_cache.GetThumbnailForPath (orig_path); if (pixbuf == null) { // A bizarre pixbuf = hack to try to deal with cinematic displays, etc. int preview_size = ((this.Screen.Width + this.Screen.Height)/2)/3; try { if (item is Photo) pixbuf = FSpot.PhotoLoader.LoadAtMaxSize ((Photo)item, preview_size, preview_size); else pixbuf = PixbufUtils.LoadAtMaxSize (orig_path, preview_size, preview_size); } catch (Exception) { pixbuf = null; } if (pixbuf != null) { preview_cache.AddThumbnail (orig_path, pixbuf); AddHistogram (pixbuf); image.Pixbuf = pixbuf; } else { image.Pixbuf = PixbufUtils.ErrorPixbuf; } } else { image.Pixbuf = pixbuf; pixbuf.Dispose (); } string desc = String.Empty; if (item.Description != null && item.Description.Length > 0) desc = item.Description + Environment.NewLine; desc += item.Time.ToString () + " " + item.Name; label.Text = desc; } private void UpdatePosition () { int x, y; Gdk.Rectangle bounds = view.CellBounds (this.Item); Gtk.Requisition requisition = this.SizeRequest (); this.Resize (requisition.Width, requisition.Height); view.GdkWindow.GetOrigin (out x, out y); // Acount for scrolling bounds.X -= (int)view.Hadjustment.Value; bounds.Y -= (int)view.Vadjustment.Value; // calculate the cell center x += bounds.X + (bounds.Width / 2); y += bounds.Y + (bounds.Height / 2); // find the window's x location limiting it to the screen x = Math.Max (0, x - requisition.Width / 2); x = Math.Min (x, this.Screen.Width - requisition.Width); // find the window's y location offset above or below depending on space #if USE_OFFSET_PREVIEW int margin = (int) (bounds.Height * .6); if (y - requisition.Height - margin < 0) y += margin; else y = y - requisition.Height - margin; #else y = Math.Max (0, y - requisition.Height / 2); y = Math.Min (y, this.Screen.Height - requisition.Height); #endif this.Move (x, y); } private void UpdateItem (int x, int y) { int item = view.CellAtPosition (x, y); if (item >= 0) { this.Item = item; UpdatePosition (); Show (); } else { this.Hide (); } } private void UpdateItem () { int x, y; view.GetPointer (out x, out y); x += (int) view.Hadjustment.Value; y += (int) view.Vadjustment.Value; UpdateItem (x, y); } private void HandleIconViewMotion (object sender, Gtk.MotionNotifyEventArgs args) { if (!this.Visible) return; int x = (int) args.Event.X; int y = (int) args.Event.Y; view.GrabFocus (); UpdateItem (x, y); } private void HandleIconViewKeyPress (object sender, Gtk.KeyPressEventArgs args) { switch (args.Event.Key) { case Gdk.Key.v: ShowHistogram = false; UpdateItem (); args.RetVal = true; break; case Gdk.Key.V: ShowHistogram = true; UpdateItem (); args.RetVal = true; break; } } private void HandleKeyRelease (object sender, Gtk.KeyReleaseEventArgs args) { switch (args.Event.Key) { case Gdk.Key.v: case Gdk.Key.V: case Gdk.Key.h: this.Hide (); break; } } private void HandleButtonPress (object sender, Gtk.ButtonPressEventArgs args) { this.Hide (); } private void HandleIconViewDestroy (object sender, Gtk.DestroyEventArgs args) { this.Destroy (); } private void HandleDestroyed (object sender, System.EventArgs args) { this.preview_cache.Dispose (); } protected override bool OnMotionNotifyEvent (Gdk.EventMotion args) { // // We look for motion events on the popup window so that // if the pointer manages to get over the window we can // Update the image properly and/or get out of the way. // UpdateItem (); return false; } public PreviewPopup (IconView view) : base (Gtk.WindowType.Toplevel) { Gtk.VBox vbox = new Gtk.VBox (); this.Add (vbox); this.AddEvents ((int) (Gdk.EventMask.PointerMotionMask | Gdk.EventMask.KeyReleaseMask | Gdk.EventMask.ButtonPressMask)); this.Decorated = false; this.SkipTaskbarHint = true; this.SkipPagerHint = true; this.SetPosition (Gtk.WindowPosition.None); this.KeyReleaseEvent += HandleKeyRelease; this.ButtonPressEvent += HandleButtonPress; this.Destroyed += HandleDestroyed; this.view = view; view.MotionNotifyEvent += HandleIconViewMotion; view.KeyPressEvent += HandleIconViewKeyPress; view.KeyReleaseEvent += HandleKeyRelease; view.DestroyEvent += HandleIconViewDestroy; this.BorderWidth = 6; hist = new FSpot.Histogram (); hist.Color [0] = 127; hist.Color [1] = 127; hist.Color [2] = 127; hist.Color [3] = 0xff; image = new Gtk.Image (); image.CanFocus = false; label = new Gtk.Label (String.Empty); label.CanFocus = false; label.ModifyFg (Gtk.StateType.Normal, new Gdk.Color (127, 127, 127)); label.ModifyBg (Gtk.StateType.Normal, new Gdk.Color (0, 0, 0)); this.ModifyFg (Gtk.StateType.Normal, new Gdk.Color (127, 127, 127)); this.ModifyBg (Gtk.StateType.Normal, new Gdk.Color (0, 0, 0)); vbox.PackStart (image, true, true, 0); vbox.PackStart (label, true, false, 0); vbox.ShowAll (); } } }