Compute lucene-style scores for our hits.
[beagle.git] / Best / BestWindow.cs
blob18be0608de58ed3590284c437d5bd20b66051f3d
1 //
2 // BestWindow.cs
3 //
4 // Copyright (C) 2004 Novell, Inc.
5 //
7 //
8 // Permission is hereby granted, free of charge, to any person obtaining a copy
9 // of this software and associated documentation files (the "Software"), to deal
10 // in the Software without restriction, including without limitation the rights
11 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 // copies of the Software, and to permit persons to whom the Software is
13 // furnished to do so, subject to the following conditions:
15 // The above copyright notice and this permission notice shall be included in all
16 // copies or substantial portions of the Software.
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 // SOFTWARE.
28 using System;
29 using System.Collections;
31 using Gnome;
32 using Gtk;
34 using Mono.Posix;
36 using Beagle;
37 using BU = Beagle.Util;
38 using Beagle.Tile;
40 namespace Best {
42 public class TypeMenuItem : Gtk.MenuItem {
44 public string Type;
46 public TypeMenuItem (string label, string type) : base (label)
48 this.Type = type;
52 public class BestWindow : Gtk.Window {
54 public BestWindow (string query) : base (WindowType.Toplevel)
56 CreateWindow (query);
59 public BestWindow () : base (WindowType.Toplevel)
61 CreateWindow (null);
64 public void FocusEntry () {
65 entry.SelectRegion (0, -1);
66 entry.GtkEntry.GrabFocus ();
69 public void FocusEntryHandler (object o, EventArgs args) {
70 FocusEntry ();
73 Gtk.AccelGroup accel_group;
74 BU.GlobalKeybinder global_keys;
76 void CreateWindow (string query)
78 Title = Best.DefaultWindowTitle;
80 DeleteEvent += new DeleteEventHandler (this.DoDelete);
81 MapEvent += new MapEventHandler (MapIt);
82 UnmapEvent += new UnmapEventHandler (UnmapIt);
84 Icon = Images.GetPixbuf ("best.png");
86 Widget content = CreateContents ();
88 VBox main = new VBox (false, 3);
89 main.PackStart (content, true, true, 3);
90 content.Show ();
91 Add (main);
92 main.Show ();
93 main.Realize ();
94 canvas.Realize ();
96 root = new SimpleRootTile ();
97 canvas.Root = root;
99 DefaultWidth = 600;
100 DefaultHeight = 675;
102 accel_group = new Gtk.AccelGroup ();
103 this.AddAccelGroup (accel_group);
104 global_keys = new BU.GlobalKeybinder (accel_group);
106 // Close window (Ctrl-W)
107 global_keys.AddAccelerator (new EventHandler (this.HideWindowHandler),
108 (uint) Gdk.Key.w,
109 Gdk.ModifierType.ControlMask,
110 Gtk.AccelFlags.Visible);
112 // Close window (Escape)
113 global_keys.AddAccelerator (new EventHandler (this.HideWindowHandler),
114 (uint) Gdk.Key.Escape,
116 Gtk.AccelFlags.Visible);
118 // Show source (Ctrl+U)
119 global_keys.AddAccelerator (new EventHandler (this.ShowSource),
120 (uint) Gdk.Key.U,
121 Gdk.ModifierType.ControlMask,
122 Gtk.AccelFlags.Visible);
124 // Focus Entry (Ctrl+L)
125 global_keys.AddAccelerator (new EventHandler (this.FocusEntryHandler),
126 (uint) Gdk.Key.L,
127 Gdk.ModifierType.ControlMask,
128 Gtk.AccelFlags.Visible);
130 // Previous Page (PageUp)
131 global_keys.AddAccelerator (new EventHandler (this.PageBackHandler),
132 (uint) Gdk.Key.Page_Up,
134 Gtk.AccelFlags.Visible);
136 // Next Page (PageDown)
137 global_keys.AddAccelerator (new EventHandler (this.PageForwardHandler),
138 (uint) Gdk.Key.Page_Down,
140 Gtk.AccelFlags.Visible);
142 //DBusisms.BeagleDown += OnBeagleDown;
144 UpdatePage ();
146 if (query != null)
147 Search (query);
151 //////////////////////////
153 int posX = 0, posY = 0;
155 public new void Present ()
157 base.Present ();
159 Move (posX, posY);
162 public new void Hide ()
164 // FIXME: Hack, why does Hide () gets invoked twice, the second time with (0,0) as window position?
166 int new_posX = 0, new_posY = 0;
168 GetPosition (out new_posX, out new_posY);
170 if (new_posX != 0 && new_posY != 0) {
171 posX = new_posX;
172 posY = new_posY;
175 base.Hide ();
178 //////////////////////////
180 Query query = null;
181 string hit_type = null;
183 private void SetBusy (bool busy)
185 if (busy) {
186 this.GdkWindow.Cursor = new Gdk.Cursor (Gdk.CursorType.Watch);
187 } else {
188 this.GdkWindow.Cursor = null;
193 private void OnBeagleDown ()
195 SetBusy (false);
196 DetachQuery ();
199 private void DoDelete (object o, DeleteEventArgs args)
201 Hide ();
204 public bool WindowIsVisible;
206 private void MapIt (object o, MapEventArgs args)
208 WindowIsVisible = true;
211 private void UnmapIt (object o, UnmapEventArgs args)
213 WindowIsVisible = false;
216 private void HideWindowHandler (object o, EventArgs args)
218 Hide ();
221 private void ShowSource (object o, EventArgs args)
223 Gtk.Window win = new Gtk.Window ("Source");
224 win.SetDefaultSize (800,500);
225 Gtk.ScrolledWindow sw = new ScrolledWindow ();
226 sw.HscrollbarPolicy = Gtk.PolicyType.Automatic;
227 sw.VscrollbarPolicy = Gtk.PolicyType.Automatic;
228 Gtk.TextView view = new Gtk.TextView ();
229 view.CursorVisible = false;
230 view.Editable = false;
231 Gtk.TextBuffer buffer = view.Buffer;
232 buffer.Text = canvas.Source;
233 view.Buffer = buffer;
234 sw.Add (view);
235 win.Add (sw);
236 win.ShowAll ();
239 //////////////////////////
241 private void PageForward ()
243 if (!root.HitCollection.CanPageForward)
244 return;
246 root.HitCollection.PageForward ();
247 UpdatePage ();
250 private void PageBack ()
252 if (!root.HitCollection.CanPageBack)
253 return;
255 root.HitCollection.PageBack ();
256 UpdatePage ();
259 private void PageForwardHandler (object o, EventArgs args)
261 PageForward ();
264 private void PageBackHandler (object o, EventArgs args)
266 PageBack ();
269 //////////////////////////
271 private Gnome.Entry entry;
273 private TileCanvas canvas;
274 private SimpleRootTile root;
275 private Gtk.Label page_label;
276 private Gtk.Button back_button;
277 private Gtk.Button forward_button;
278 private ArrayList menu_items = new ArrayList ();
280 private Gtk.Button StockButton (string stockid, string label)
282 Gtk.HBox button_contents = new HBox (false, 0);
283 button_contents.Show ();
284 Gtk.Widget button_image = new Gtk.Image (stockid, Gtk.IconSize.Button);
285 button_image.Show ();
286 button_contents.PackStart (button_image, false, false, 1);
287 Gtk.Label button_label = new Gtk.Label (label);
288 button_label.Show ();
289 button_contents.PackStart (button_label, false, false, 1);
291 Gtk.Button button = new Gtk.Button ();
292 button.Add (button_contents);
294 return button;
297 private Gtk.OptionMenu TypesOptionMenu ()
299 Gtk.OptionMenu opt = new Gtk.OptionMenu ();
301 Gtk.MenuItem mi;
302 mi = new TypeMenuItem (Catalog.GetString ("Anywhere"), null);
303 this.menu_items.Add (mi);
304 mi = new TypeMenuItem (Catalog.GetString ("in Files"), "File");
305 this.menu_items.Add (mi);
306 mi = new TypeMenuItem (Catalog.GetString ("in Addressbook"), "Contact");
307 this.menu_items.Add (mi);
308 mi = new TypeMenuItem (Catalog.GetString ("in Mail"), "MailMessage");
309 this.menu_items.Add (mi);
310 mi = new TypeMenuItem (Catalog.GetString ("in Web Pages"), "WebHistory");
311 this.menu_items.Add (mi);
312 mi = new TypeMenuItem (Catalog.GetString ("in Chats"), "IMLog");
313 this.menu_items.Add (mi);
315 Gtk.Menu menu = new Gtk.Menu ();
316 foreach (Gtk.MenuItem item in this.menu_items)
317 menu.Append (item);
318 opt.Menu = menu;
320 return opt;
323 private Gtk.Widget CreateContents ()
325 Gtk.HBox entryLine = new HBox (false, 3);
327 Gtk.Label words = new Gtk.Label (Catalog.GetString ("Search terms:"));
328 entryLine.PackStart (words, false, false, 3);
330 entry = new Gnome.Entry ("");
331 entry.Activated += new EventHandler (this.DoSearch);
332 entryLine.PackStart (entry, true, true, 3);
334 words = new Gtk.Label ("");
335 entryLine.PackStart (words, false, false, 3);
337 Gtk.OptionMenu types = TypesOptionMenu ();
338 types.Changed += new EventHandler (this.ChangeType);
339 entryLine.PackStart (types, false, false, 3);
341 Gtk.HBox buttonContents = new HBox (false, 0);
342 Gtk.Widget buttonImg = Beagle.Images.GetWidget ("icon-search.png");
343 buttonContents.PackStart (buttonImg, false, false, 1);
344 Gtk.Label buttonLabel = new Gtk.Label (Catalog.GetString ("Find"));
345 buttonContents.PackStart (buttonLabel, false, false, 1);
347 Gtk.Button button = new Gtk.Button ();
348 button.Add (buttonContents);
349 button.Clicked += new EventHandler (this.DoSearch);
350 entryLine.PackStart (button, false, false, 3);
352 canvas = new TileCanvas ();
353 canvas.Show ();
355 HBox pager = new HBox ();
356 page_label = new Label ();
357 page_label.Show ();
358 pager.PackStart (page_label, false, false, 3);
360 forward_button = StockButton ("gtk-go-forward",
361 Catalog.GetString ("Show More Results"));
362 forward_button.Show ();
363 forward_button.Clicked += new EventHandler (PageForwardHandler);
364 pager.PackEnd (forward_button, false, false, 3);
366 back_button = StockButton ("gtk-go-back",
367 Catalog.GetString ("Show Previous Results"));
368 back_button.Show ();
370 back_button.Clicked += new EventHandler (PageBackHandler);
371 pager.PackEnd (back_button, false, false, 3);
373 pager.Show ();
375 VBox contents = new VBox (false, 3);
376 contents.PackStart (entryLine, false, true, 3);
377 contents.PackStart (canvas, true, true, 3);
378 contents.PackStart (pager, false, false, 3);
380 entryLine.ShowAll ();
381 canvas.ShowAll ();
383 return contents;
386 private void UpdatePage ()
388 //Console.WriteLine ("In UpdatePage");
389 back_button.Sensitive = root.HitCollection.CanPageBack;
390 forward_button.Sensitive = root.HitCollection.CanPageForward;
392 string label;
394 int results;
395 if (this.hit_type == null)
396 results = root.HitCollection.NumResults;
397 else
398 results = root.HitCollection.NumDisplayableResults;
400 if (results == 0) {
401 label = Catalog.GetString ("No results.");
402 } else if (root.HitCollection.FirstDisplayed == 0) {
403 /* To translators: {0} is the current count of results shown of {1} in total, this is the message that is initially shown */
404 /* when results are returned to the user. */
405 label = String.Format (Catalog.GetString ("Best <b>{0} results of {1}</b> are shown."),
406 root.HitCollection.LastDisplayed + 1,
407 results);
408 } else {
409 /* To translators: {0} to {1} is the interval of results currently shown of {2} results in total*/
410 label = String.Format (Catalog.GetString ("Results <b>{0} through {1} of {2}</b> are shown."),
411 root.HitCollection.FirstDisplayed + 1,
412 root.HitCollection.LastDisplayed + 1,
413 results);
415 page_label.Markup = label;
418 private string delayedQuery = null;
420 private bool RunDelayedQuery ()
422 if (delayedQuery != null) {
423 string tmp = delayedQuery;
424 delayedQuery = null;
425 System.Console.WriteLine ("Delayed query fired");
426 StartQuery ();
429 return false;
432 private void QueueDelayedQuery (string query)
434 delayedQuery = query;
435 GLib.Timeout.Add (10000, new GLib.TimeoutHandler (RunDelayedQuery));
438 private void DoSearch (object o, EventArgs args)
440 if (entry.GtkEntry.Text != null && entry.GtkEntry.Text != "")
441 Search (entry.GtkEntry.Text);
444 //private string lastType = null;
446 private void ChangeType (object o, EventArgs args)
448 Gtk.OptionMenu opt = (Gtk.OptionMenu) o;
449 TypeMenuItem mi = (TypeMenuItem) this.menu_items [opt.History];
451 if (this.hit_type == mi.Type)
452 return;
454 this.hit_type = mi.Type;
456 root.SetSource (this.hit_type);
457 root.HitCollection.PageFirst ();
458 UpdatePage ();
460 //if (this.query != null && lastType != this.hit_type) {
461 // Search (entry.GtkEntry.Text);
462 // lastType = this.hit_type;
464 //UpdatePage ();
467 //////////////////////////
469 private void OnHitsAdded (HitsAddedResponse response)
471 root.Add (response.Hits);
472 UpdatePage ();
475 private void OnHitsSubtracted (HitsSubtractedResponse response)
477 root.Subtract (response.Uris);
478 UpdatePage ();
481 private void OnFinished (FinishedResponse repsonse)
483 SetBusy (false);
486 private void OnCancelled (CancelledResponse response)
488 SetBusy (false);
491 private void AttachQuery ()
493 query.HitsAddedEvent += OnHitsAdded;
494 query.HitsSubtractedEvent += OnHitsSubtracted;
495 query.FinishedEvent += OnFinished;
496 query.CancelledEvent += OnCancelled;
499 private void DetachQuery ()
501 if (query != null) {
502 query.HitsAddedEvent -= OnHitsAdded;
503 query.HitsSubtractedEvent -= OnHitsSubtracted;
504 query.FinishedEvent -= OnFinished;
505 query.CancelledEvent -= OnCancelled;
506 query.Close ();
508 query = null;
512 private void StartQuery ()
514 try {
515 query.SendAsync ();
516 SetBusy (true);
517 } catch (Beagle.ResponseMessageException e) {
518 QueueDelayedQuery (entry.GtkEntry.Text);
520 /* To translators: {0} represents the current query keywords */
521 root.Error (String.Format (Catalog.GetString ("The query for <i>{0}</i> failed." +
522 "<br>The likely cause is that the beagle daemon isn't running."), entry.GtkEntry.Text));
523 root.OfferDaemonRestart = true;
524 } catch (Exception e) {
525 /* To translators: {0} represents the current query keywords, {1} contains the errormessage */
526 root.Error (String.Format (Catalog.GetString ("The query for <i>{0}</i> failed with error:<br>{1}<br>"),
527 entry.GtkEntry.Text, e));
531 private void Search (String searchString)
533 StoreSearch (searchString);
534 entry.GtkEntry.Text = searchString;
536 if (query != null) {
537 try { DetachQuery (); } catch (ObjectDisposedException e) {}
540 query = new Query ();
541 query.AddDomain (QueryDomain.Neighborhood);
543 // FIXME: Disable non-local searching for now.
544 //query.AddDomain (QueryDomain.Global);
546 query.AddText (searchString);
547 root.SetSource (hit_type);
549 AttachQuery ();
551 root.Query = query;
552 root.Start ();
554 StartQuery ();
556 UpdatePage ();
559 ArrayList recentSearches;
561 private void StoreSearch (string newQuery)
563 if (recentSearches == null) {
564 recentSearches = new ArrayList ();
567 if (newQuery != null && newQuery != "") {
568 int duplicate = recentSearches.IndexOf (newQuery);
569 if (duplicate >= 0) {
570 recentSearches.RemoveAt (duplicate);
572 recentSearches.Insert (0, newQuery);
575 if (recentSearches.Count == 11) {
576 recentSearches.RemoveAt (10);
580 public ArrayList RetriveSearches ()
582 return recentSearches;
585 public void ClearHistory ()
587 entry.ClearHistory ();
588 recentSearches.Clear ();
591 public void QuickSearch (string query)
593 Search (query);