Change the GC.GetTotalMemory() threshold to 10%; otherwise there are just too many...
[beagle.git] / search / Tiles / Tile.cs
blob85f19050cb6a0d55a8148f2cabc9c8e0341392b3
1 using System;
2 using System.Collections;
3 using System.Runtime.InteropServices;
4 using System.Text.RegularExpressions;
5 using System.Diagnostics;
6 using Mono.Unix;
8 using Gtk;
9 using Beagle;
10 using Beagle.Util;
12 namespace Search.Tiles {
14 public abstract class Tile : Gtk.EventBox {
16 public Tile (Hit hit, Query query) : base ()
18 AboveChild = true;
19 AppPaintable = true;
20 CanFocus = true;
22 this.hit = hit;
23 this.timestamp = hit.Timestamp;
24 this.score = hit.Score;
25 this.query = query;
26 this.group = TileGroup.Documents;
28 Gtk.Drag.SourceSet (this, Gdk.ModifierType.Button1Mask,
29 targets, Gdk.DragAction.Copy | Gdk.DragAction.Move);
31 hbox = new Gtk.HBox (false, 5);
32 hbox.BorderWidth = 2;
33 hbox.Show ();
35 icon = new Gtk.Image ();
36 icon.Show ();
37 HBox.PackStart (icon, false, false, 0);
39 Add (hbox);
42 private Beagle.Hit hit;
43 public Beagle.Hit Hit {
44 get { return hit; }
47 private Beagle.Query query;
48 public Beagle.Query Query {
49 get { return query; }
52 private TileGroup group;
53 public TileGroup Group {
54 get { return group; }
55 set { group = value; }
58 private Gtk.HBox hbox;
59 protected Gtk.HBox HBox {
60 get { return hbox; }
63 private Gtk.Image icon;
64 public Gtk.Image Icon {
65 get { return icon; }
66 set { icon = value; }
69 private string title;
70 public virtual string Title {
71 get { return title; }
72 set { title = value; }
75 private DateTime timestamp;
76 public virtual DateTime Timestamp {
77 get { return timestamp; }
78 set { timestamp = value; }
81 private double score;
82 public virtual double Score {
83 get { return score; }
84 set { score = value; }
87 protected bool EnableOpenWith = false;
89 static Gtk.TargetEntry[] targets = new Gtk.TargetEntry[] {
90 new Gtk.TargetEntry ("text/uri-list", 0, 0)
93 public event EventHandler Selected;
95 protected override void OnDragBegin (Gdk.DragContext context)
97 if (!icon.Visible)
98 return;
100 WidgetFu.SetDragImage (context, icon);
103 protected override void OnDragDataGet (Gdk.DragContext dragContext,
104 Gtk.SelectionData selectionData,
105 uint info, uint time)
107 byte[] data = System.Text.Encoding.UTF8.GetBytes (Hit.EscapedUri + "\r\n");
108 selectionData.Set (selectionData.Target, 8, data);
111 protected override void OnSizeRequested (ref Gtk.Requisition req)
113 // FIXME: "base.OnSizeRequested (ref req)" should work,
114 // but it doesn't
115 req = hbox.SizeRequest ();
117 int pad = (int)StyleGetProperty ("focus-line-width") +
118 (int)StyleGetProperty ("focus-padding") + 1;
119 req.Width += 2 * (pad + Style.Xthickness);
120 req.Height += 2 * (pad + Style.Ythickness);
123 protected override void OnSizeAllocated (Gdk.Rectangle alloc)
125 int pad = (int)StyleGetProperty ("focus-line-width") +
126 (int)StyleGetProperty ("focus-padding") + 1;
128 alloc.X += pad + Style.Xthickness;
129 alloc.Width -= pad + Style.Xthickness;
130 alloc.Y += pad + Style.Ythickness;
131 alloc.Height -= pad + Style.Ythickness;
133 base.OnSizeAllocated (alloc);
136 protected override bool OnExposeEvent (Gdk.EventExpose evt)
138 if (!IsDrawable)
139 return false;
141 GdkWindow.DrawRectangle (Style.BaseGC (State), true,
142 evt.Area.X, evt.Area.Y,
143 evt.Area.Width, evt.Area.Height);
145 if (base.OnExposeEvent (evt))
146 return true;
148 if (HasFocus) {
149 Gdk.Rectangle alloc = Allocation;
150 int focusPad = (int)StyleGetProperty ("focus-padding");
152 int x = focusPad + Style.Xthickness;
153 int y = focusPad + Style.Ythickness;
154 int width = alloc.Width - 2 * (focusPad + Style.Xthickness);
155 int height = alloc.Height - 2 * (focusPad + Style.Ythickness);
156 Style.PaintFocus (Style, GdkWindow, State, evt.Area, this,
157 null, x, y, width, height);
160 return false;
163 ///////////////////////////////////////////////////
165 public ArrayList actions = new ArrayList ();
166 public ICollection Actions {
167 get { return actions; }
170 protected void AddAction (TileAction action)
172 actions.Add (action);
175 private void ShowPopupMenu ()
177 Gtk.Menu menu = new Gtk.Menu ();
179 ActionMenuItem mi = new ActionMenuItem (new TileAction (Catalog.GetString ("Open"), Stock.Open, Open));
180 menu.Append (mi);
182 #if ENABLE_OPEN_WITH
183 if (EnableOpenWith) {
184 // FIXME: Not sure if going with the parent is
185 // the right thing to do in all cases.
186 OpenWithMenu owm = new OpenWithMenu (Utils.GetFirstPropertyOfParent (hit, "beagle:MimeType"));
187 owm.ApplicationActivated += OpenWith;
188 owm.AppendToMenu (menu);
190 #endif
192 if (Actions.Count > 0) {
193 SeparatorMenuItem si = new SeparatorMenuItem ();
194 menu.Append (si);
196 foreach (TileAction action in Actions) {
197 mi = new ActionMenuItem (action);
198 menu.Append (mi);
202 menu.ShowAll ();
203 menu.Popup ();
206 ///////////////////////////////////////////////////
208 protected override bool OnButtonPressEvent (Gdk.EventButton b)
210 GrabFocus ();
212 if (b.Button == 3) {
213 ShowPopupMenu ();
214 return true;
215 } else if (b.Type == Gdk.EventType.TwoButtonPress) {
216 Open ();
217 if (b.Button == 2 || ((b.State & Gdk.ModifierType.ShiftMask) != 0))
218 Gtk.Application.Quit ();
219 return true;
222 return base.OnButtonPressEvent (b);
225 protected override bool OnFocusInEvent (Gdk.EventFocus f)
227 if (Selected != null)
228 Selected (this, EventArgs.Empty);
229 return base.OnFocusInEvent (f);
232 protected override bool OnKeyPressEvent (Gdk.EventKey k)
234 if (k.Key == Gdk.Key.Return || k.Key == Gdk.Key.KP_Enter) {
235 Open ();
236 if ((k.State & Gdk.ModifierType.ShiftMask) != 0)
237 Gtk.Application.Quit ();
238 return true;
241 return base.OnKeyPressEvent (k);
244 protected virtual void LoadIcon (Gtk.Image image, int size)
246 // This is a hack to prevent large mime icons when we
247 // dont have a thumbnail.
248 if (size > 48)
249 size = 48;
251 image.Pixbuf = WidgetFu.LoadMimeIcon (hit.MimeType, size);
254 string snippet;
256 protected void RequestSnippet ()
258 if (snippet != null)
259 EmitGotSnippet ();
260 else {
261 SnippetRequest sreq = new SnippetRequest (query, hit);
262 sreq.RegisterAsyncResponseHandler (typeof (SnippetResponse), SnippetResponseReceived);
263 sreq.SendAsync ();
267 private void SnippetResponseReceived (ResponseMessage response)
269 // The returned snippet uses
270 // <font color="..."><b>blah</b></font>
271 // to mark matches. The rest of the snippet might be HTML, or
272 // it might be plain text, including unescaped '<'s and '&'s.
273 // So we escape it, fix the match highlighting, and leave any
274 // other tags escaped.
276 // FIXME: hacky, fix the snippeting in the daemon
277 snippet = GLib.Markup.EscapeText (((SnippetResponse)response).Snippet);
278 snippet = Regex.Replace (snippet, "&lt;font color=&quot;.*?&quot;&gt;&lt;b&gt;(.*?)&lt;/b&gt;&lt;/font&gt;", "<b>$1</b>");
280 EmitGotSnippet ();
283 private void EmitGotSnippet ()
285 if (snippet != null && snippet != "" && GotSnippet != null)
286 GotSnippet (snippet);
289 public delegate void GotSnippetHandler (string snippet);
290 public event GotSnippetHandler GotSnippet;
292 protected virtual DetailsPane GetDetails ()
294 return null;
297 DetailsPane details;
298 public Gtk.Widget Details {
299 get {
300 if (details == null) {
301 details = GetDetails ();
302 if (details != null) {
303 if (details.Icon.Pixbuf == null)
304 LoadIcon (details.Icon, 128);
306 if (details.Snippet != null) {
307 GotSnippet += details.GotSnippet;
308 RequestSnippet ();
311 details.Show ();
314 return details;
318 public virtual void Open ()
320 System.Console.WriteLine ("Warning: Open method not implemented for this tile type");
323 #if ENABLE_OPEN_WITH
324 private void OpenWith (Gnome.Vfs.MimeApplication mime_application)
326 GLib.List uri_list = new GLib.List (typeof (string));
327 uri_list.Append (Hit.EscapedUri);
328 mime_application.Launch (uri_list);
330 #endif
332 protected void OpenFromMime (Hit hit)
334 string command = null, item;
335 bool expects_uris = false;
337 // FIXME: This is evil. Nautilus should be handling
338 // inode/directory, not just x-directory/normal
339 if (hit.MimeType == "inode/directory")
340 hit.MimeType = "x-directory/normal";
342 #if ENABLE_DESKTOP_LAUNCH
343 command = "desktop-launch";
344 expects_uris = true;
345 #else
346 GnomeFu.VFSMimeApplication app;
347 app = GnomeFu.GetDefaultAction (hit.MimeType);
348 if (app.command != null) {
349 command = app.command;
350 expects_uris = (app.expects_uris != GnomeFu.VFSMimeApplicationArgumentType.Path);
352 #endif
353 if (command == null) {
354 Console.WriteLine ("Can't open MimeType '{0}'", hit.MimeType);
355 return;
358 if (expects_uris) {
359 // FIXME: I'm not sure that opening the parent
360 // URI (if present) is the right thing to do in
361 // all cases, but it does work for all our
362 // current cases.
363 if (hit.ParentUri != null)
364 item = hit.EscapedParentUri;
365 else
366 item = hit.EscapedUri;
367 } else
368 item = hit.Path;
370 // Sometimes the command is 'quoted'
371 if (command.IndexOf ('\'') == 0 && command.LastIndexOf ('\'') == command.Length - 1)
372 command = command.Trim ('\'');
374 // This won't work if a program really has a space in
375 // the command filename, but I think other things would
376 // break with that too, and in practice it doesn't seem to
377 // happen.
379 // A bigger issue is that the arguments are split up by
380 // spaces, so quotation marks used to indicate a single
381 // entry in the argv won't work. This probably should
382 // be fixed.
383 string[] arguments = null;
384 int idx = command.IndexOf (' ');
385 if (idx != -1) {
386 arguments = command.Substring (idx + 1).Split (' ');
387 command = command.Substring (0, idx);
390 string[] argv;
391 if (arguments == null)
392 argv = new string [] { command, item };
393 else {
394 argv = new string [arguments.Length + 2];
395 argv [0] = command;
396 argv [argv.Length - 1] = item;
397 Array.Copy (arguments, 0, argv, 1, arguments.Length);
400 Console.WriteLine ("Cmd: {0}", command);
401 Console.WriteLine ("Arg: {0}", String.Join (" ", argv, 1, argv.Length - 2));
402 Console.WriteLine ("Itm: {0}", item);
404 SafeProcess p = new SafeProcess ();
405 p.Arguments = argv;
407 try {
408 p.Start ();
409 } catch (Exception e) {
410 Console.WriteLine ("Error in OpenFromMime: " + e);
414 public void OpenFromUri (Uri uri)
416 OpenFromUri (UriFu.UriToEscapedString (uri));
419 public void OpenFromUri (string uri)
421 #if ENABLE_DESKTOP_LAUNCH
422 SafeProcess p = new SafeProcess ();
423 p.Arguments = new string[] { "desktop-launch", uri };
425 try {
426 p.Start ();
427 } catch (Exception e) {
428 Console.WriteLine ("Could not load handler for {0}: {1}", uri, e);
430 #else
431 try {
432 Gnome.Url.Show (uri);
433 } catch (Exception e) {
434 Console.WriteLine ("Could not load handler for {0}: {1}", uri, e);
436 #endif