2006-04-25 Hendrik Brandt <heb@gnome-de.org>
[beagle.git] / search / Tiles / ThumbnailFactory.cs
blob5423f02155d041b4eea2d1b56beba73210afac1d
1 using System;
2 using System.Collections;
3 using System.IO;
4 using System.Threading;
6 namespace Search.Tiles {
8 public class ThumbnailFactory : Gtk.Object {
10 private Gnome.ThumbnailFactory factory = new Gnome.ThumbnailFactory (Gnome.ThumbnailSize.Normal);
11 private Thread thread;
12 private ArrayList in_queue = new ArrayList ();
13 private ArrayList out_queue = new ArrayList ();
15 public ThumbnailFactory ()
17 Gtk.Quit.AddDestroy (1, this);
20 protected override void OnDestroyed ()
22 // Force the thumbnailing thread to exit cleanly once it
23 // finishes the current thumbnail
24 lock (in_queue)
25 in_queue.Clear ();
28 private class ThumbnailRequest {
29 public Gtk.Image Image;
30 public Beagle.Hit Hit;
31 public int Size;
32 public bool Succeeded;
34 public ThumbnailRequest (Gtk.Image image, Beagle.Hit hit, int size)
36 Image = image;
37 Hit = hit;
38 Size = size;
41 public static ThumbnailRequest Select (IEnumerable thumbnails)
43 ThumbnailRequest first = null;
44 Gtk.Widget w;
46 foreach (ThumbnailRequest req in thumbnails) {
48 // FIXME: we ought to be able to just look at
49 // req.Image.IsMapped. But for some reason, the
50 // images are still showing up as mapped even
51 // when their parents aren't, which seems
52 // impossible to me from looking at the code,
53 // but...
54 for (w = req.Image; w != null; w = w.Parent) {
55 if (!w.IsMapped)
56 break;
58 if (w == null)
59 return req;
61 if (first == null)
62 first = req;
64 return first;
68 public bool SetThumbnailIcon (Gtk.Image image, Beagle.Hit hit, int size)
70 DateTime mtime = (hit.FileInfo != null) ? hit.FileInfo.LastWriteTime : DateTime.Now;
72 if (hit.MimeType == null ||
73 !factory.CanThumbnail (hit.UriAsString, hit.MimeType, mtime))
74 return false;
76 string thumbnail = Gnome.Thumbnail.PathForUri (hit.UriAsString, Gnome.ThumbnailSize.Normal);
77 bool failed_thumb = factory.HasValidFailedThumbnail (hit.UriAsString, mtime);
79 if (! File.Exists (thumbnail) && ! failed_thumb) {
80 lock (in_queue) {
81 in_queue.Add (new ThumbnailRequest (image, hit, size));
82 if (thread == null) {
83 thread = new Thread (GenerateThumbnails);
84 thread.Start ();
87 return false;
90 if (failed_thumb)
91 return false;
93 Gdk.Pixbuf icon = new Gdk.Pixbuf (thumbnail);
94 if (icon == null)
95 return false;
97 int width = icon.Width, height = icon.Height;
98 if (icon.Height > size) {
99 if (icon.Width > icon.Height) {
100 width = size;
101 height = (size * icon.Height) / icon.Width;
102 } else {
103 height = size;
104 width = (size * icon.Width) / icon.Height;
106 } else if (icon.Width > size) {
107 width = size;
108 height = (size * icon.Height) / icon.Width;
110 icon = icon.ScaleSimple (width, height, Gdk.InterpType.Bilinear);
112 image.Pixbuf = icon;
113 return true;
116 private void GenerateThumbnails ()
118 ThumbnailRequest req;
120 while (true) {
121 lock (in_queue) {
122 if (in_queue.Count == 0) {
123 thread = null;
124 return;
127 req = ThumbnailRequest.Select (in_queue);
128 in_queue.Remove (req);
131 Gdk.Pixbuf icon = factory.GenerateThumbnail (req.Hit.UriAsString, req.Hit.MimeType);
133 if (icon == null) {
134 if (req.Hit.FileInfo != null)
135 factory.CreateFailedThumbnail (req.Hit.UriAsString, req.Hit.FileInfo.LastWriteTime);
136 } else {
137 factory.SaveThumbnail (icon, req.Hit.UriAsString, DateTime.Now);
138 req.Succeeded = true;
141 lock (out_queue)
142 out_queue.Add (req);
143 GLib.Idle.Add (FinishSetThumbnail);
147 private bool FinishSetThumbnail ()
149 ThumbnailRequest req;
150 lock (out_queue) {
151 req = (ThumbnailRequest)out_queue[0];
152 out_queue.RemoveAt (0);
155 if (req.Succeeded)
156 SetThumbnailIcon (req.Image, req.Hit, req.Size);
158 return false;