Forgot to bump FSQ version. Weekend syndrome.
[beagle.git] / search / Tiles / Tile.cs
blob25c6d681b05c81dfabf9915504e99f276b5fb555
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 protected 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 protected 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.Uri + "\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 NOT_YET
183 // FIXME: Disabled until we have a reasonable workaround
184 // for lower gtk# versions.
185 if (EnableOpenWith) {
186 OpenWithMenu owm = new OpenWithMenu (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 image.Pixbuf = WidgetFu.LoadMimeIcon (hit.MimeType, size);
249 string snippet;
251 protected void RequestSnippet ()
253 if (snippet != null)
254 EmitGotSnippet ();
255 else {
256 SnippetRequest sreq = new SnippetRequest (query, hit);
257 sreq.RegisterAsyncResponseHandler (typeof (SnippetResponse), SnippetResponseReceived);
258 sreq.SendAsync ();
262 private void SnippetResponseReceived (ResponseMessage response)
264 // The returned snippet uses
265 // <font color="..."><b>blah</b></font>
266 // to mark matches. The rest of the snippet might be HTML, or
267 // it might be plain text, including unescaped '<'s and '&'s.
268 // So we escape it, fix the match highlighting, and leave any
269 // other tags escaped.
271 // FIXME: hacky, fix the snippeting in the daemon
272 snippet = GLib.Markup.EscapeText (((SnippetResponse)response).Snippet);
273 snippet = Regex.Replace (snippet, "&lt;font color=&quot;.*?&quot;&gt;&lt;b&gt;(.*?)&lt;/b&gt;&lt;/font&gt;", "<b>$1</b>");
275 EmitGotSnippet ();
278 private void EmitGotSnippet ()
280 if (snippet != null && snippet != "" && GotSnippet != null)
281 GotSnippet (snippet);
284 public delegate void GotSnippetHandler (string snippet);
285 public event GotSnippetHandler GotSnippet;
287 protected virtual DetailsPane GetDetails ()
289 return null;
292 DetailsPane details;
293 bool gotDetails = false;
295 public Gtk.Widget Details {
296 get {
297 if (!gotDetails) {
298 details = GetDetails ();
299 if (details != null) {
300 LoadIcon (details.Icon, 128);
301 if (details.Snippet != null) {
302 GotSnippet += details.GotSnippet;
303 RequestSnippet ();
305 details.Show ();
307 gotDetails = true;
309 return details;
313 public virtual void Open ()
315 System.Console.WriteLine ("Warning: Open method not implemented for this tile type");
318 #if NOT_YET
319 private void OpenWith (Gnome.Vfs.MimeApplication mime_application)
321 GLib.List uri_list = new GLib.List (typeof (string));
322 uri_list.Append (Hit.UriAsString);
323 mime_application.Launch (uri_list);
325 #endif
327 protected void OpenFromMime (Hit hit)
329 OpenFromMime (hit, null, null, false);
332 protected void OpenFromMime (Hit hit, string command_fallback,
333 string args_fallback, bool expects_uris_fallback)
335 string argument;
336 string command = command_fallback;
337 bool expects_uris = expects_uris_fallback;
339 // FIXME: This is evil. Nautilus should be handling
340 // inode/directory, not just x-directory/normal
341 if (hit.MimeType == "inode/directory")
342 hit.MimeType = "x-directory/normal";
343 #if ENABLE_DESKTOP_LAUNCH
344 command = "desktop-launch";
345 expects_uris = true;
346 #else
347 GnomeFu.VFSMimeApplication app;
348 app = GnomeFu.GetDefaultAction (hit.MimeType);
349 if (app.command != null) {
350 command = app.command;
351 expects_uris = (app.expects_uris != GnomeFu.VFSMimeApplicationArgumentType.Path);
353 #endif
354 if (command == null) {
355 Console.WriteLine ("Can't open MimeType '{0}'", hit.MimeType);
356 return;
359 if (args_fallback != null)
360 argument = args_fallback;
361 else
362 argument = "";
364 if (expects_uris) {
365 argument = String.Format ("{0} '{1}'", argument,
366 UriFu.UriToSerializableString(hit.Uri));
367 } else {
368 argument = String.Format ("{0} {1}", argument, hit.PathQuoted);
371 // Sometimes the command is 'quoted'
372 if (command.IndexOf ('\'') == 0 && command.LastIndexOf ('\'') == command.Length - 1)
373 command = command.Trim ('\'');
375 // This won't work if a program really has a space in
376 // the filename, but I think other things would break
377 // with that too, and in practice it doesn't seem to
378 // happen.
379 int idx = command.IndexOf (' ');
380 if (idx != -1) {
381 argument = String.Format ("{0} {1}", command.Substring (idx + 1), argument);
382 command = command.Substring (0, idx);
385 Console.WriteLine ("Cmd: {0}", command);
386 Console.WriteLine ("Arg: {0}", argument);
388 Process p = new Process ();
389 p.StartInfo.UseShellExecute = false;
390 p.StartInfo.FileName = command;
391 p.StartInfo.Arguments = argument;
393 try {
394 p.Start ();
395 } catch (Exception e) {
396 Console.WriteLine ("Error in OpenFromMime: " + e);
400 public void OpenFromUri (Uri uri)
402 OpenFromUri (uri.ToString ());
405 public void OpenFromUri (string uri)
407 #if ENABLE_DESKTOP_LAUNCH
408 Process p = new Process ();
409 p.StartInfo.UseShellExecute = false;
410 p.StartInfo.FileName = "desktop-launch";
411 p.StartInfo.Arguments = uri;
413 try {
414 p.Start ();
415 } catch (Exception e) {
416 Console.WriteLine ("Could not load handler for {0}: {1}", uri, e);
418 #else
419 try {
420 Gnome.Url.Show (uri);
421 } catch (Exception e) {
422 Console.WriteLine ("Could not load handler for {0}: {1}", uri, e);
424 #endif