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 private TileGroup
group;
53 public TileGroup Group
{
55 set { group = value; }
58 private Gtk
.HBox hbox
;
59 protected Gtk
.HBox HBox
{
63 private 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
.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,
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 if (EnableOpenWith
) {
184 OpenWithMenu owm
= new OpenWithMenu (Hit
["beagle:MimeType"]);
185 owm
.ApplicationActivated
+= OpenWith
;
186 owm
.AppendToMenu (menu
);
190 if (Actions
.Count
> 0) {
191 SeparatorMenuItem si
= new SeparatorMenuItem ();
194 foreach (TileAction action
in Actions
) {
195 mi
= new ActionMenuItem (action
);
204 ///////////////////////////////////////////////////
206 protected override bool OnButtonPressEvent (Gdk
.EventButton b
)
213 } else if (b
.Type
== Gdk
.EventType
.TwoButtonPress
) {
215 if (b
.Button
== 2 || ((b
.State
& Gdk
.ModifierType
.ShiftMask
) != 0))
216 Gtk
.Application
.Quit ();
220 return base.OnButtonPressEvent (b
);
223 protected override bool OnFocusInEvent (Gdk
.EventFocus f
)
225 if (Selected
!= null)
226 Selected (this, EventArgs
.Empty
);
227 return base.OnFocusInEvent (f
);
230 protected override bool OnKeyPressEvent (Gdk
.EventKey k
)
232 if (k
.Key
== Gdk
.Key
.Return
|| k
.Key
== Gdk
.Key
.KP_Enter
) {
234 if ((k
.State
& Gdk
.ModifierType
.ShiftMask
) != 0)
235 Gtk
.Application
.Quit ();
239 return base.OnKeyPressEvent (k
);
242 protected virtual void LoadIcon (Gtk
.Image image
, int size
)
244 // This is a hack to prevent large mime icons when we
245 // dont have a thumbnail.
249 image
.Pixbuf
= WidgetFu
.LoadMimeIcon (hit
.MimeType
, size
);
254 protected void RequestSnippet ()
259 SnippetRequest sreq
= new SnippetRequest (query
, hit
);
260 sreq
.RegisterAsyncResponseHandler (typeof (SnippetResponse
), SnippetResponseReceived
);
265 private void SnippetResponseReceived (ResponseMessage response
)
267 // The returned snippet uses
268 // <font color="..."><b>blah</b></font>
269 // to mark matches. The rest of the snippet might be HTML, or
270 // it might be plain text, including unescaped '<'s and '&'s.
271 // So we escape it, fix the match highlighting, and leave any
272 // other tags escaped.
274 // FIXME: hacky, fix the snippeting in the daemon
275 snippet
= GLib
.Markup
.EscapeText (((SnippetResponse
)response
).Snippet
);
276 snippet
= Regex
.Replace (snippet
, "<font color=".*?"><b>(.*?)</b></font>", "<b>$1</b>");
281 private void EmitGotSnippet ()
283 if (snippet
!= null && snippet
!= "" && GotSnippet
!= null)
284 GotSnippet (snippet
);
287 public delegate void GotSnippetHandler (string snippet
);
288 public event GotSnippetHandler GotSnippet
;
290 protected virtual DetailsPane
GetDetails ()
296 public Gtk
.Widget Details
{
298 if (details
== null) {
299 details
= GetDetails ();
300 if (details
!= null) {
301 if (details
.Icon
.Pixbuf
== null)
302 LoadIcon (details
.Icon
, 128);
304 if (details
.Snippet
!= null) {
305 GotSnippet
+= details
.GotSnippet
;
316 public virtual void Open ()
318 System
.Console
.WriteLine ("Warning: Open method not implemented for this tile type");
322 private void OpenWith (Gnome
.Vfs
.MimeApplication mime_application
)
324 GLib
.List uri_list
= new GLib
.List (typeof (string));
325 uri_list
.Append (Hit
.EscapedUri
);
326 mime_application
.Launch (uri_list
);
330 protected void OpenFromMime (Hit hit
)
332 string command
= null, item
;
333 bool expects_uris
= false;
335 // FIXME: This is evil. Nautilus should be handling
336 // inode/directory, not just x-directory/normal
337 if (hit
.MimeType
== "inode/directory")
338 hit
.MimeType
= "x-directory/normal";
340 #if ENABLE_DESKTOP_LAUNCH
341 command
= "desktop-launch";
344 GnomeFu
.VFSMimeApplication app
;
345 app
= GnomeFu
.GetDefaultAction (hit
.MimeType
);
346 if (app
.command
!= null) {
347 command
= app
.command
;
348 expects_uris
= (app
.expects_uris
!= GnomeFu
.VFSMimeApplicationArgumentType
.Path
);
351 if (command
== null) {
352 Console
.WriteLine ("Can't open MimeType '{0}'", hit
.MimeType
);
357 item
= hit
.EscapedUri
;
361 // Sometimes the command is 'quoted'
362 if (command
.IndexOf ('\'') == 0 && command
.LastIndexOf ('\'') == command
.Length
- 1)
363 command
= command
.Trim ('\'');
365 // This won't work if a program really has a space in
366 // the command filename, but I think other things would
367 // break with that too, and in practice it doesn't seem to
370 // A bigger issue is that the arguments are split up by
371 // spaces, so quotation marks used to indicate a single
372 // entry in the argv won't work. This probably should
374 string[] arguments
= null;
375 int idx
= command
.IndexOf (' ');
377 arguments
= command
.Substring (idx
+ 1).Split (' ');
378 command
= command
.Substring (0, idx
);
382 if (arguments
== null)
383 argv
= new string [] { command, item }
;
385 argv
= new string [arguments
.Length
+ 2];
387 argv
[argv
.Length
- 1] = item
;
388 Array
.Copy (arguments
, 0, argv
, 1, arguments
.Length
);
391 Console
.WriteLine ("Cmd: {0}", command
);
392 Console
.WriteLine ("Arg: {0}", String
.Join (" ", argv
, 1, argv
.Length
- 2));
393 Console
.WriteLine ("Itm: {0}", item
);
395 SafeProcess p
= new SafeProcess ();
400 } catch (Exception e
) {
401 Console
.WriteLine ("Error in OpenFromMime: " + e
);
405 public void OpenFromUri (Uri uri
)
407 OpenFromUri (UriFu
.UriToEscapedString (uri
));
410 public void OpenFromUri (string uri
)
412 #if ENABLE_DESKTOP_LAUNCH
413 SafeProcess p
= new SafeProcess ();
414 p
.Arguments
= new string[] { "desktop-launch", uri }
;
418 } catch (Exception e
) {
419 Console
.WriteLine ("Could not load handler for {0}: {1}", uri
, e
);
423 Gnome
.Url
.Show (uri
);
424 } catch (Exception e
) {
425 Console
.WriteLine ("Could not load handler for {0}: {1}", uri
, e
);