Add example code to use beagle as a service provider.
[beagle.git] / search / Tiles / ThumbnailFactory.cs
blob5200bc11f78d39f02dbe2c9013a28677c72ba56b
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 string ThumbnailFile;
31 public Beagle.Hit Hit;
32 public int Size;
33 public bool Succeeded;
35 public ThumbnailRequest (Gtk.Image image, string thumbnail_file, Beagle.Hit hit, int size)
37 Image = image;
38 ThumbnailFile = thumbnail_file;
39 Hit = hit;
40 Size = size;
43 public static ThumbnailRequest Select (IEnumerable thumbnails)
45 ThumbnailRequest first = null;
46 Gtk.Widget w;
48 foreach (ThumbnailRequest req in thumbnails) {
50 // FIXME: we ought to be able to just look at
51 // req.Image.IsMapped. But for some reason, the
52 // images are still showing up as mapped even
53 // when their parents aren't, which seems
54 // impossible to me from looking at the code,
55 // but...
56 for (w = req.Image; w != null; w = w.Parent) {
57 if (!w.IsMapped)
58 break;
60 if (w == null)
61 return req;
63 if (first == null)
64 first = req;
66 return first;
70 public bool SetThumbnailIcon (Gtk.Image image, Beagle.Hit hit, int size)
72 DateTime mtime = (hit.FileInfo != null) ? hit.FileInfo.LastWriteTime : DateTime.Now;
74 if (hit.MimeType == null ||
75 !factory.CanThumbnail (hit.EscapedUri, hit.MimeType, mtime))
76 return false;
78 string thumbnail = Gnome.Thumbnail.PathForUri (hit.EscapedUri, Gnome.ThumbnailSize.Normal);
79 bool failed_thumb = factory.HasValidFailedThumbnail (hit.EscapedUri, mtime);
81 if (! File.Exists (thumbnail) && ! failed_thumb) {
82 lock (in_queue) {
83 in_queue.Add (new ThumbnailRequest (image, thumbnail, hit, size));
84 if (thread == null) {
85 thread = new Thread (GenerateThumbnails);
86 thread.Start ();
89 return false;
92 if (failed_thumb)
93 return false;
95 Gdk.Pixbuf icon = new Gdk.Pixbuf (thumbnail);
96 if (icon == null)
97 return false;
99 int width = icon.Width, height = icon.Height;
100 if (icon.Height > size) {
101 if (icon.Width > icon.Height) {
102 width = size;
103 height = (size * icon.Height) / icon.Width;
104 } else {
105 height = size;
106 width = (size * icon.Width) / icon.Height;
108 } else if (icon.Width > size) {
109 width = size;
110 height = (size * icon.Height) / icon.Width;
112 icon = icon.ScaleSimple (width, height, Gdk.InterpType.Bilinear);
114 image.Pixbuf = icon;
115 return true;
118 private void GenerateThumbnails ()
120 ThumbnailRequest req;
122 while (true) {
123 lock (in_queue) {
124 if (in_queue.Count == 0) {
125 thread = null;
126 return;
129 req = ThumbnailRequest.Select (in_queue);
130 in_queue.Remove (req);
133 Gdk.Pixbuf icon = factory.GenerateThumbnail (req.Hit.EscapedUri, req.Hit.MimeType);
135 if (icon == null) {
136 if (req.Hit.FileInfo != null)
137 factory.CreateFailedThumbnail (req.Hit.EscapedUri, req.Hit.FileInfo.LastWriteTime);
138 } else {
139 factory.SaveThumbnail (icon, req.Hit.EscapedUri, DateTime.Now);
141 if (File.Exists (req.ThumbnailFile))
142 req.Succeeded = true;
145 lock (out_queue)
146 out_queue.Add (req);
147 GLib.Idle.Add (FinishSetThumbnail);
151 private bool FinishSetThumbnail ()
153 ThumbnailRequest req;
154 lock (out_queue) {
155 req = (ThumbnailRequest)out_queue[0];
156 out_queue.RemoveAt (0);
159 if (req.Succeeded)
160 SetThumbnailIcon (req.Image, req.Hit, req.Size);
162 return false;