Do not add duplicate tags. At least makes sense in beagle.
[beagle.git] / search / Search.cs
blobdda6cfb7d5e56aa5759bc31d86bd01ee15332866
1 //
2 // Search.cs
3 //
4 // Copyright (c) 2006 Novell, Inc.
5 //
7 using System;
8 using System.Collections;
10 using Gtk;
11 using Beagle;
12 using Beagle.Util;
13 using Mono.Unix;
15 using Search.Tiles;
16 using Search.Tray;
18 namespace Search {
20 public class MainWindow : Window {
22 private Gtk.Button button;
23 private Gtk.Tooltips tips;
24 private Gtk.Notebook pages;
25 private Gtk.Statusbar statusbar;
27 private Search.UIManager uim;
28 private Search.NotificationArea notification_area;
29 private Search.GroupView view;
30 private Search.Entry entry;
31 private Search.Spinner spinner;
32 private Search.Panes panes;
33 private Search.Tray.TrayIcon tray;
35 private Search.Pages.QuickTips quicktips;
36 private Search.Pages.RootUser rootuser;
37 private Search.Pages.StartDaemon startdaemon;
38 private Search.Pages.NoMatch nomatch;
40 private uint timeout;
42 private string query_text;
43 private Beagle.Query current_query;
44 private Search.ScopeType scope = ScopeType.Everything;
45 private Search.SortType sort = SortType.Modified;
46 private Search.TypeFilter filter = null;
47 private bool show_details = true;
48 private int total_matches = -1;
50 private XKeybinder keybinder = new XKeybinder ();
52 public static bool IconEnabled = false;
54 public static void Main (string [] args)
56 SystemInformation.SetProcessName ("beagle-search");
58 Catalog.Init ("beagle", ExternalStringsHack.LocaleDir);
60 string query = ParseArgs (args);
62 Gnome.Program program = new Gnome.Program ("search", "0.0",
63 Gnome.Modules.UI, args);
65 MainWindow window = new MainWindow ();
67 if (query != null && query != "" && !IconEnabled) {
68 window.entry.Text = query;
69 window.Search (true);
72 program.Run ();
75 private static string ParseArgs (String[] args)
77 string query = "";
78 int i = 0;
80 while (i < args.Length) {
81 switch (args [i]) {
82 case "--help":
83 case "--usage":
84 PrintUsageAndExit ();
85 return null;
87 case "--icon":
88 IconEnabled = true;
89 break;
91 case "--autostarted":
92 // FIXME: This option is deprecated and will be removed in a future release.
93 break;
95 // Ignore session management
96 case "--sm-config-prefix":
97 case "--sm-client-id":
98 case "--screen":
99 // These all take an argument, so
100 // increment i
101 i++;
102 break;
104 default:
105 if (args [i].Length < 2 || args [i].Substring (0, 2) != "--") {
106 if (query.Length != 0)
107 query += " ";
108 query += args [i];
110 break;
113 i++;
116 return query;
119 public static void PrintUsageAndExit ()
121 string usage =
122 "beagle-search: GUI interface to the Beagle search system.\n" +
123 "Web page: http://www.beagle-project.org/\n" +
124 "Copyright (C) 2005-2006 Novell, Inc.\n\n";
126 usage +=
127 "Usage: beagle-search [OPTIONS] [<query string>]\n\n" +
128 "Options:\n" +
129 " --help\t\t\tPrint this usage message.\n" +
130 " --icon\t\t\tAdd an icon to the notification area rather than opening a search window.\n";
132 Console.WriteLine (usage);
133 System.Environment.Exit (0);
136 public MainWindow () : base (WindowType.Toplevel)
138 Title = Catalog.GetString ("Desktop Search");
139 Icon = Beagle.Images.GetPixbuf ("system-search.png");
141 DefaultWidth = 700;
142 DefaultHeight = 550;
143 DeleteEvent += OnWindowDelete;
145 VBox vbox = new VBox ();
146 vbox.Spacing = 3;
148 uim = new UIManager (this);
149 uim.ScopeChanged += OnScopeChanged;
150 uim.SortChanged += OnSortChanged;
151 uim.ToggleDetails += OnToggleDetails;
152 uim.ShowQuickTips += OnShowQuickTips;
153 vbox.PackStart (uim.MenuBar, false, false, 0);
155 HBox hbox = new HBox (false, 6);
157 Label label = new Label (Catalog.GetString ("_Find:"));
158 hbox.PackStart (label, false, false, 0);
160 entry = new Entry ();
161 entry.Activated += OnEntryActivated;
162 hbox.PackStart (entry, true, true, 0);
164 label.MnemonicWidget = entry;
165 uim.FocusSearchEntry += delegate () { entry.GrabFocus (); };
167 // The auto search after timeout feauture is now optional
168 // and can be disabled.
169 if (Beagle.Util.Conf.Searching.BeagleSearchAutoSearch) {
170 entry.Changed += OnEntryChanged;
171 entry.MoveCursor += OnEntryMoveCursor;
174 button = new Gtk.Button ();
175 Gtk.HBox button_hbox = new Gtk.HBox (false, 2);
176 Gtk.Image icon = new Gtk.Image (Gtk.Stock.Find, Gtk.IconSize.Button);
177 button_hbox.PackStart (icon, false, false, 0);
178 label = new Gtk.Label (Catalog.GetString ("Find Now"));
179 button_hbox.PackStart (label, false, false, 0);
180 button.Add (button_hbox);
181 button.Clicked += OnButtonClicked;
183 Gtk.VBox buttonVBox = new Gtk.VBox (false, 0);
184 buttonVBox.PackStart (button, true, false, 0);
185 hbox.PackStart (buttonVBox, false, false, 0);
187 spinner = new Spinner ();
188 hbox.PackStart (spinner, false, false, 0);
190 HBox padding_hbox = new HBox ();
191 padding_hbox.PackStart (hbox, true, true, 9);
192 vbox.PackStart (padding_hbox, false, true, 6);
194 VBox view_box = new VBox (false, 3);
195 vbox.PackStart (view_box, true, true, 0);
197 HBox na_padding = new HBox ();
198 view_box.PackStart (na_padding, false, true, 0);
200 notification_area = new NotificationArea ();
201 na_padding.PackStart (notification_area, true, true, 3);
203 pages = new Gtk.Notebook ();
204 pages.ShowTabs = false;
205 pages.ShowBorder = false;
206 pages.BorderWidth = 3;
207 view_box.PackStart (pages, true, true, 0);
209 quicktips = new Pages.QuickTips ();
210 quicktips.Show ();
211 pages.Add (quicktips);
213 rootuser = new Pages.RootUser ();
214 rootuser.Show ();
215 pages.Add (rootuser);
217 startdaemon = new Pages.StartDaemon ();
218 startdaemon.DaemonStarted += OnDaemonStarted;
219 startdaemon.Show ();
220 pages.Add (startdaemon);
222 panes = new Search.Panes ();
223 panes.Show ();
224 pages.Add (panes);
226 view = new GroupView ();
227 view.TileSelected += ShowInformation;
228 view.CategoryToggled += OnCategoryToggled;
229 panes.MainContents = view;
231 this.statusbar = new Gtk.Statusbar ();
232 vbox.PackEnd (this.statusbar, false, false, 0);
234 Add (vbox);
236 tips = new Gtk.Tooltips ();
237 tips.SetTip (entry, Catalog.GetString ("Type in search terms"), "");
238 tips.SetTip (button, Catalog.GetString ("Start searching"), "");
239 tips.Enable ();
241 if (Environment.UserName == "root" && ! Conf.Daemon.AllowRoot) {
242 pages.CurrentPage = pages.PageNum (rootuser);
243 entry.Sensitive = button.Sensitive = uim.Sensitive = false;
244 } else {
245 pages.CurrentPage = pages.PageNum (quicktips);
248 if (IconEnabled) {
249 tray = new Search.Tray.TrayIcon ();
250 tray.Clicked += OnTrayActivated;
251 tray.Search += OnTraySearch;
253 // Attach the hide/show keybinding
254 keybinder.Bind (Conf.Searching.ShowSearchWindowBinding.ToString (), OnTrayActivated);
255 } else {
256 ShowAll ();
259 StartCheckingIndexingStatus ();
262 private void SetWindowTitle (string query)
264 Title = String.Format ( Catalog.GetString ("Desktop Search: {0}"), query);
267 private int TotalMatches {
268 get { return this.total_matches; }
269 set {
270 if (this.total_matches != -1)
271 this.statusbar.Pop (0);
273 this.total_matches = value;
275 if (this.total_matches > -1) {
276 string message;
277 int tile_count = view.TileCount;
279 if (tile_count == this.total_matches)
280 message = String.Format (Catalog.GetString ("Showing all {0} matches"), this.total_matches);
281 else
282 message = String.Format (Catalog.GetString ("Showing the top {0} of {1} total matches"), view.TileCount, this.total_matches);
284 this.statusbar.Push (0, message);
289 // Whether we should grab focus from the text entry
290 private bool grab_focus;
292 private void Search (bool grab_focus)
294 if (timeout != 0) {
295 GLib.Source.Remove (timeout);
296 timeout = 0;
299 string query = query_text = entry.Text;
300 if (query == null || query == "")
301 return;
303 SetWindowTitle (query);
304 ShowInformation (null);
306 if (tray != null) {
307 tray.AddSearch (query);
310 filter = TypeFilter.MakeFilter (ref query);
312 view.Clear ();
313 view.Scope = scope;
314 view.Sort = sort;
315 pages.CurrentPage = pages.PageNum (panes);
317 this.grab_focus = grab_focus;
319 try {
320 if (current_query != null) {
321 TotalMatches = -1;
322 current_query.HitsAddedEvent -= OnHitsAdded;
323 current_query.HitsSubtractedEvent -= OnHitsSubtracted;
324 current_query.Close ();
327 TotalMatches = 0;
329 current_query = new Query ();
330 current_query.AddDomain (QueryDomain.Neighborhood);
332 // Don't search documentation by default
333 QueryPart_Property part = new QueryPart_Property ();
334 part.Logic = QueryPartLogic.Prohibited;
335 part.Type = PropertyType.Keyword;
336 part.Key = "beagle:Source";
337 part.Value = "documentation";
338 current_query.AddPart (part);
340 current_query.AddText (query);
341 current_query.HitsAddedEvent += OnHitsAdded;
342 current_query.HitsSubtractedEvent += OnHitsSubtracted;
343 current_query.FinishedEvent += OnFinished;
345 current_query.SendAsync ();
346 spinner.Start ();
347 } catch (Beagle.ResponseMessageException) {
348 pages.CurrentPage = pages.PageNum (startdaemon);
349 } catch (Exception e) {
350 Console.WriteLine ("Querying the Beagle daemon failed: {0}", e.Message);
354 private void OnEntryActivated (object obj, EventArgs args)
356 Search (true);
359 private void OnDaemonStarted ()
361 Search (true);
364 private void OnEntryChanged (object obj, EventArgs args)
366 if (timeout != 0)
367 GLib.Source.Remove (timeout);
368 timeout = GLib.Timeout.Add (1000, OnEntryTimeout);
371 private void OnEntryMoveCursor (object obj, EventArgs args)
373 if (timeout != 0)
374 GLib.Source.Remove (timeout);
375 timeout = GLib.Timeout.Add (1000, OnEntryTimeout);
378 private bool OnEntryTimeout ()
380 timeout = 0;
381 Search (false);
382 return false;
385 private void OnButtonClicked (object obj, EventArgs args)
387 Search (true);
390 private void OnWindowDelete (object o, Gtk.DeleteEventArgs args)
392 if (IconEnabled) {
393 Hide ();
394 args.RetVal = true;
395 } else {
396 Gtk.Application.Quit ();
400 private void OnScopeChanged (Search.ScopeType toggled, bool active)
402 if (active)
403 view.Scope = scope = scope | toggled;
404 else
405 view.Scope = scope = scope ^ toggled;
407 CheckNoMatch ();
410 private void OnCategoryToggled (ScopeType toggled)
412 string name = ScopeType.GetName (typeof (ScopeType), toggled);
413 try {
414 ToggleAction act = (ToggleAction) uim.GetAction ("/ui/MenuBar/Search/Scope/" + name);
415 act.Active = ! act.Active;
417 catch (Exception ex) {
418 Console.WriteLine("Exception caught when trying to deactivate menu entry {0}:",name);
419 Console.WriteLine(ex);
420 return;
425 private void OnSortChanged (Search.SortType newSort)
427 view.Sort = sort = newSort;
430 private void OnToggleDetails (bool active)
432 show_details = active;
433 if (panes.Details != null)
434 panes.ToggleDetails (show_details);
435 else
436 panes.ToggleDetails (false);
439 private void OnShowQuickTips ()
441 if (current_query != null) {
442 TotalMatches = -1;
443 current_query.HitsAddedEvent -= OnHitsAdded;
444 current_query.HitsSubtractedEvent -= OnHitsSubtracted;
445 current_query.Close ();
446 current_query = null;
449 pages.CurrentPage = pages.PageNum (quicktips);
452 private void ShowInformation (Tiles.Tile tile)
454 if (tile != null) {
455 panes.Details = tile.Details;
456 if (tile.Details != null)
457 panes.ToggleDetails (show_details);
458 else
459 panes.ToggleDetails (false);
460 } else {
461 panes.Details = null;
462 panes.ToggleDetails (false);
466 private void OnFinished (FinishedResponse response)
468 spinner.Stop ();
469 view.Finished (grab_focus);
470 grab_focus = false;
472 CheckNoMatch ();
475 private void OnHitsAdded (HitsAddedResponse response)
477 foreach (Hit hit in response.Hits) {
478 Tile tile = TileActivatorOrg.MakeTile (hit, current_query);
479 if (tile == null)
480 continue;
482 if (filter != null && !filter.Filter (tile))
483 continue;
485 view.AddHit (tile);
486 if (pages.CurrentPageWidget != panes)
487 pages.CurrentPage = pages.PageNum (panes);
490 TotalMatches += response.NumMatches;
493 private void OnHitsSubtracted (HitsSubtractedResponse response)
495 foreach (Uri uri in response.Uris)
496 view.SubtractHit (uri);
498 TotalMatches -= response.Uris.Count;
500 CheckNoMatch ();
503 private void CheckNoMatch ()
505 MatchType matches = view.MatchState;
506 if (matches == MatchType.Matched) {
507 pages.CurrentPage = pages.PageNum (panes);
508 return;
511 if (nomatch != null)
512 nomatch.Destroy ();
513 nomatch = new Pages.NoMatch (query_text, matches == MatchType.NoneInScope);
514 nomatch.Show ();
515 pages.Add (nomatch);
516 pages.CurrentPage = pages.PageNum (nomatch);
519 /////////////////////////////////////
521 private void OnTrayActivated (object o, EventArgs args)
523 if (! Visible) {
524 base.ShowAll ();
525 base.Present ();
526 entry.GrabFocus ();
527 } else {
528 base.Hide ();
532 private void OnTraySearch (string query)
534 if (!Visible)
535 ShowAll ();
537 entry.Text = query;
538 Search (true);
541 //////////////////////////////////////
543 private void StartCheckingIndexingStatus ()
545 InformationalMessagesRequest msg_request = new InformationalMessagesRequest ();
546 msg_request.IndexingStatusEvent += OnIndexingStatusEvent;
547 msg_request.SendAsync ();
550 private void OnIndexingStatusEvent (IndexingStatus status)
552 if (status == IndexingStatus.Running) {
553 NotificationMessage m = new NotificationMessage ();
554 m.Icon = Gtk.Stock.DialogInfo;
555 m.Title = Catalog.GetString ("Your data is being indexed");
556 m.Message = Catalog.GetString ("The search service is in the process of indexing your data. Search results may be incomplete until indexing has finished.");
557 notification_area.Display (m);
558 } else {
559 notification_area.Hide ();