2 using System
.Collections
;
3 using System
.Runtime
.InteropServices
;
4 using System
.Text
.RegularExpressions
;
5 using System
.Diagnostics
;
12 namespace Search
.Tiles
{
14 public abstract class Tile
: Gtk
.EventBox
{
16 public Tile (Hit hit
, Query query
) : base ()
23 this.timestamp
= hit
.Timestamp
;
24 this.score
= hit
.Score
;
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);
35 icon
= new Gtk
.Image ();
37 HBox
.PackStart (icon
, false, false, 0);
42 private Beagle
.Hit hit
;
43 public Beagle
.Hit Hit
{
47 private Beagle
.Query query
;
48 public Beagle
.Query Query
{
52 protected TileGroup
group;
53 public TileGroup Group
{
55 set { group = value; }
58 private Gtk
.HBox hbox
;
59 protected Gtk
.HBox HBox
{
63 protected Gtk
.Image icon
;
64 public Gtk
.Image Icon
{
70 public virtual string Title
{
72 set { title = value; }
75 private DateTime timestamp
;
76 public virtual DateTime Timestamp
{
77 get { return timestamp; }
78 set { timestamp = value; }
82 public virtual double 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
)
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,
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
)
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
))
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
);
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
));
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
);
192 if (Actions
.Count
> 0) {
193 SeparatorMenuItem si
= new SeparatorMenuItem ();
196 foreach (TileAction action
in Actions
) {
197 mi
= new ActionMenuItem (action
);
206 ///////////////////////////////////////////////////
208 protected override bool OnButtonPressEvent (Gdk
.EventButton b
)
215 } else if (b
.Type
== Gdk
.EventType
.TwoButtonPress
) {
217 if (b
.Button
== 2 || ((b
.State
& Gdk
.ModifierType
.ShiftMask
) != 0))
218 Gtk
.Application
.Quit ();
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
) {
236 if ((k
.State
& Gdk
.ModifierType
.ShiftMask
) != 0)
237 Gtk
.Application
.Quit ();
241 return base.OnKeyPressEvent (k
);
244 protected virtual void LoadIcon (Gtk
.Image image
, int size
)
246 image
.Pixbuf
= WidgetFu
.LoadMimeIcon (hit
.MimeType
, size
);
251 protected void RequestSnippet ()
256 SnippetRequest sreq
= new SnippetRequest (query
, hit
);
257 sreq
.RegisterAsyncResponseHandler (typeof (SnippetResponse
), SnippetResponseReceived
);
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
, "<font color=".*?"><b>(.*?)</b></font>", "<b>$1</b>");
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 ()
293 bool gotDetails
= false;
295 public Gtk
.Widget Details
{
298 details
= GetDetails ();
299 if (details
!= null) {
300 LoadIcon (details
.Icon
, 128);
301 if (details
.Snippet
!= null) {
302 GotSnippet
+= details
.GotSnippet
;
313 public virtual void Open ()
315 System
.Console
.WriteLine ("Warning: Open method not implemented for this tile type");
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
);
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
)
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";
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
);
354 if (command
== null) {
355 Console
.WriteLine ("Can't open MimeType '{0}'", hit
.MimeType
);
359 if (args_fallback
!= null)
360 argument
= args_fallback
;
365 argument
= String
.Format ("{0} '{1}'", argument
,
366 UriFu
.UriToSerializableString(hit
.Uri
));
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
379 int idx
= command
.IndexOf (' ');
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
;
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
;
415 } catch (Exception e
) {
416 Console
.WriteLine ("Could not load handler for {0}: {1}", uri
, e
);
420 Gnome
.Url
.Show (uri
);
421 } catch (Exception e
) {
422 Console
.WriteLine ("Could not load handler for {0}: {1}", uri
, e
);