//
// BestWindow.cs
//
// Copyright (C) 2004 Novell, Inc.
//
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
using System;
using System.Collections;
using Gnome;
using Gtk;
using GD=Gdk;
using Mono.Unix;
using Beagle;
using Beagle.Util;
using Beagle.Tile;
namespace Best {
public class TypeMenuItem : Gtk.MenuItem {
public string Type;
public TypeMenuItem (string label, string type) : base (label)
{
this.Type = type;
}
}
public class BestWindow : Gtk.Window {
Gtk.AccelGroup accel_group;
GlobalKeybinder global_keys;
public BestWindow (string query) : base (WindowType.Toplevel)
{
CreateWindow (query);
}
public BestWindow () : base (WindowType.Toplevel)
{
CreateWindow (null);
}
public void FocusEntry ()
{
entry.GrabFocus ();
}
public void FocusEntryHandler (object o, EventArgs args)
{
FocusEntry ();
}
private void CreateWindow (string query)
{
Title = Best.DefaultWindowTitle;
DeleteEvent += new DeleteEventHandler (this.DoDelete);
MapEvent += new MapEventHandler (MapIt);
UnmapEvent += new UnmapEventHandler (UnmapIt);
Icon = Images.GetPixbuf ("best.png");
Widget content = CreateContents ();
VBox main = new VBox (false, 3);
main.PackStart (content, true, true, 3);
content.Show ();
Add (main);
main.Show ();
main.Realize ();
canvas.Realize ();
root = new SimpleRootTile ();
canvas.Root = root;
DefaultWidth = 600;
DefaultHeight = 675;
accel_group = new Gtk.AccelGroup ();
this.AddAccelGroup (accel_group);
global_keys = new GlobalKeybinder (accel_group);
// Close window (Ctrl-W)
global_keys.AddAccelerator (new EventHandler (this.HideWindowHandler),
(uint) Gdk.Key.w,
Gdk.ModifierType.ControlMask,
Gtk.AccelFlags.Visible);
// Close window (Escape)
global_keys.AddAccelerator (new EventHandler (this.HideWindowHandler),
(uint) Gdk.Key.Escape,
0,
Gtk.AccelFlags.Visible);
// Show source (Ctrl+U)
global_keys.AddAccelerator (new EventHandler (this.ShowSource),
(uint) Gdk.Key.U,
Gdk.ModifierType.ControlMask,
Gtk.AccelFlags.Visible);
// Focus Entry (Ctrl+L)
global_keys.AddAccelerator (new EventHandler (this.FocusEntryHandler),
(uint) Gdk.Key.L,
Gdk.ModifierType.ControlMask,
Gtk.AccelFlags.Visible);
// Previous Page (PageUp)
global_keys.AddAccelerator (new EventHandler (this.PageBackHandler),
(uint) Gdk.Key.Page_Up,
0,
Gtk.AccelFlags.Visible);
// Next Page (PageDown)
global_keys.AddAccelerator (new EventHandler (this.PageForwardHandler),
(uint) Gdk.Key.Page_Down,
0,
Gtk.AccelFlags.Visible);
UpdateFromConf ();
UpdatePage ();
if (query != null)
Search (query);
}
//////////////////////////
int posX = 0, posY = 0;
public new void Present ()
{
base.Present ();
Move (posX, posY);
}
public new void Hide ()
{
// FIXME: Hack, why does Hide () gets invoked twice, the second time with (0,0) as window position?
int new_posX = 0, new_posY = 0;
GetPosition (out new_posX, out new_posY);
if (new_posX != 0 && new_posY != 0) {
posX = new_posX;
posY = new_posY;
}
base.Hide ();
}
//////////////////////////
Query query = null;
string hit_type = null;
private void SetBusy (bool busy)
{
if (busy) {
this.GdkWindow.Cursor = new Gdk.Cursor (Gdk.CursorType.Watch);
} else {
this.GdkWindow.Cursor = null;
}
}
private void DoDelete (object o, DeleteEventArgs args)
{
Hide ();
}
public bool WindowIsVisible;
private void MapIt (object o, MapEventArgs args)
{
WindowIsVisible = true;
}
private void UnmapIt (object o, UnmapEventArgs args)
{
WindowIsVisible = false;
}
private void HideWindowHandler (object o, EventArgs args)
{
Hide ();
}
private void ShowSource (object o, EventArgs args)
{
Gtk.Window win = new Gtk.Window ("Source");
win.SetDefaultSize (800,500);
Gtk.ScrolledWindow sw = new ScrolledWindow ();
sw.HscrollbarPolicy = Gtk.PolicyType.Automatic;
sw.VscrollbarPolicy = Gtk.PolicyType.Automatic;
Gtk.TextView view = new Gtk.TextView ();
view.CursorVisible = false;
view.Editable = false;
Gtk.TextBuffer buffer = view.Buffer;
buffer.Text = canvas.Source;
view.Buffer = buffer;
sw.Add (view);
win.Add (sw);
win.ShowAll ();
}
//////////////////////////
private void PageForward ()
{
if (!root.HitCollection.CanPageForward)
return;
root.HitCollection.PageForward ();
UpdatePage ();
}
private void PageBack ()
{
if (!root.HitCollection.CanPageBack)
return;
root.HitCollection.PageBack ();
UpdatePage ();
}
private void PageForwardHandler (object o, EventArgs args)
{
PageForward ();
}
private void PageBackHandler (object o, EventArgs args)
{
PageBack ();
}
//////////////////////////
private Gtk.Entry entry;
private Gtk.ListStore history;
private Gtk.ListStore filter_data;
private TileCanvas canvas;
private SimpleRootTile root;
private Gtk.Label page_label;
private Gtk.Button back_button;
private Gtk.Button forward_button;
private Gtk.Button StockButton (string stockid, string label)
{
Gtk.HBox button_contents = new HBox (false, 0);
button_contents.Show ();
Gtk.Widget button_image = new Gtk.Image (stockid, Gtk.IconSize.Button);
button_image.Show ();
button_contents.PackStart (button_image, false, false, 1);
Gtk.Label button_label = new Gtk.Label (label);
button_label.Show ();
button_contents.PackStart (button_label, false, false, 1);
Gtk.Button button = new Gtk.Button ();
button.Add (button_contents);
return button;
}
private Gtk.ComboBox FilterComboBox ()
{
filter_data = new Gtk.ListStore (new Type[] { typeof (string), typeof (string) });
Gtk.TreeIter iter;
iter = filter_data.Append ();
filter_data.SetValue (iter, 0, Catalog.GetString ("Anywhere"));
filter_data.SetValue (iter, 1, null);
iter = filter_data.Append ();
filter_data.SetValue (iter, 0, Catalog.GetString ("in Files"));
filter_data.SetValue (iter, 1, "File");
iter = filter_data.Append ();
filter_data.SetValue (iter, 0, Catalog.GetString ("in Addressbook"));
filter_data.SetValue (iter, 1, "Contact");
iter = filter_data.Append ();
filter_data.SetValue (iter, 0, Catalog.GetString ("in Mail"));
filter_data.SetValue (iter, 1, "MailMessage");
iter = filter_data.Append ();
filter_data.SetValue (iter, 0, Catalog.GetString ("in Web Pages"));
filter_data.SetValue (iter, 1, "WebHistory");
iter = filter_data.Append ();
filter_data.SetValue (iter, 0, Catalog.GetString ("in Chats"));
filter_data.SetValue (iter, 1, "IMLog");
Gtk.ComboBox combo = new Gtk.ComboBox (filter_data);
combo.Active = 0;
Gtk.CellRendererText renderer = new Gtk.CellRendererText ();
combo.PackStart (renderer, false);
combo.SetAttributes (renderer, new object[] { "text", 0 });
return combo;
}
private Gtk.Widget CreateContents ()
{
Gtk.HBox entryLine = new HBox (false, 4);
Gtk.Label words = new Gtk.Label (Catalog.GetString ("Search terms:"));
entryLine.PackStart (words, false, false, 3);
history = new Gtk.ListStore (new Type[] { typeof (string) });
Gtk.EntryCompletion comp = new Gtk.EntryCompletion ();
comp.Model = history;
comp.TextColumn = 0;
entry = new Gtk.Entry ("");
entry.Activated += new EventHandler (this.DoSearch);
entry.Completion = comp;
entryLine.PackStart (entry, true, true, 3);
words = new Gtk.Label ("");
entryLine.PackStart (words, false, false, 3);
Gtk.ComboBox combo = FilterComboBox ();
combo.Changed += new EventHandler (this.ChangeType);
entryLine.PackStart (combo, false, false, 3);
Gtk.HBox buttonContents = new HBox (false, 0);
Gtk.Widget buttonImg = Beagle.Images.GetWidget ("icon-search.png");
buttonContents.PackStart (buttonImg, false, false, 1);
Gtk.Label buttonLabel = new Gtk.Label (Catalog.GetString ("Find"));
buttonContents.PackStart (buttonLabel, false, false, 1);
Gtk.Button button = new Gtk.Button ();
button.Add (buttonContents);
button.Clicked += new EventHandler (this.DoSearch);
entryLine.PackStart (button, false, false, 3);
Gtk.Button clearButton = new Gtk.Button ();
clearButton.Label = "Clear";
clearButton.Clicked += new EventHandler (this.ClearSearch);
entryLine.PackStart (clearButton, false, false, 4);
canvas = new TileCanvas ();
canvas.Show ();
HBox pager = new HBox ();
page_label = new Label ();
page_label.Show ();
pager.PackStart (page_label, false, false, 3);
forward_button = StockButton ("gtk-go-forward",
Catalog.GetString ("Show More Results"));
forward_button.Show ();
forward_button.Clicked += new EventHandler (PageForwardHandler);
pager.PackEnd (forward_button, false, false, 3);
back_button = StockButton ("gtk-go-back",
Catalog.GetString ("Show Previous Results"));
back_button.Show ();
back_button.Clicked += new EventHandler (PageBackHandler);
pager.PackEnd (back_button, false, false, 3);
pager.Show ();
VBox contents = new VBox (false, 3);
contents.PackStart (entryLine, false, true, 3);
contents.PackStart (canvas, true, true, 3);
contents.PackStart (pager, false, false, 3);
entryLine.ShowAll ();
canvas.ShowAll ();
return contents;
}
private void UpdateFromConf ()
{
Console.WriteLine ("Reading settings from Config");
// FIXME: there might be weird cases with multiple screens,
// multiple monitors, resolution related problems that might
// cause problem is remapping the stored values to current
// screen coordinates
int res_x = GD.Screen.Default.Width;
int res_y = GD.Screen.Default.Height;
int pos_x = (int)(Conf.Searching.BestPosX * res_x / 100);
int pos_y = (int)(Conf.Searching.BestPosY * res_y / 100);
int width = (int)(Conf.Searching.BestWidth * res_x / 100 );
int height = (int)(Conf.Searching.BestHeight * res_y / 100);
if (pos_x != 0 || pos_y != 0) {
posX = pos_x;
posY = pos_y;
Move (pos_x, pos_y);
}
if (width != 0)
DefaultWidth = width;
if (height != 0)
DefaultHeight = height;
Gtk.TreeIter iter;
foreach (string search in Conf.Searching.SearchHistory) {
iter = history.Append ();
history.SetValue (iter, 0, search);
}
}
public void StoreSettingsInConf (bool with_tray)
{
Console.WriteLine ("Storing setting in Config");
int pos_x = posX, pos_y = posY;
if (with_tray && WindowIsVisible)
GetPosition (out pos_x, out pos_y);
int width = 0, height = 0;
GetSize (out width, out height);
int res_x = GD.Screen.Default.Width;
int res_y = GD.Screen.Default.Height;
Conf.Searching.BestPosX = ((float) pos_x / res_x) * 100;
Conf.Searching.BestPosY = ((float) pos_y / res_y) * 100;
Conf.Searching.BestWidth = ((float) width / res_x) * 100;
Conf.Searching.BestHeight = ((float) height / res_y) * 100;
Conf.Searching.SearchHistory = RetriveSearches ();
Conf.Searching.SaveNeeded = true;
Conf.Save ();
}
private void UpdatePage ()
{
//Console.WriteLine ("In UpdatePage");
back_button.Sensitive = root.HitCollection.CanPageBack;
forward_button.Sensitive = root.HitCollection.CanPageForward;
string label;
int results;
if (this.hit_type == null)
results = root.HitCollection.NumResults;
else
results = root.HitCollection.NumDisplayableResults;
if (results == 0) {
label = Catalog.GetString ("No results.");
} else if (root.HitCollection.FirstDisplayed == 0) {
/* To translators: {0} is the current count of results shown of {1} in total, this is the message that is initially shown */
/* when results are returned to the user. */
label = String.Format (Catalog.GetString ("Best {0} results of {1} are shown."),
root.HitCollection.LastDisplayed + 1,
results);
} else {
/* To translators: {0} to {1} is the interval of results currently shown of {2} results in total*/
label = String.Format (Catalog.GetString ("Results {0} through {1} of {2} are shown."),
root.HitCollection.FirstDisplayed + 1,
root.HitCollection.LastDisplayed + 1,
results);
}
page_label.Markup = label;
}
private string delayedQuery = null;
private bool RunDelayedQuery ()
{
if (delayedQuery != null) {
delayedQuery = null;
System.Console.WriteLine ("Delayed query fired");
StartQuery ();
}
return false;
}
private void QueueDelayedQuery (string query)
{
delayedQuery = query;
GLib.Timeout.Add (10000, new GLib.TimeoutHandler (RunDelayedQuery));
}
private void DoSearch (object o, EventArgs args)
{
if (entry.Text != null && entry.Text != "")
Search (entry.Text);
}
//private string lastType = null;
private void ChangeType (object o, EventArgs args)
{
Gtk.ComboBox combo = (Gtk.ComboBox) o;
string hit_type = null;
Gtk.TreeIter iter;
if (combo.GetActiveIter (out iter))
hit_type = (string) filter_data.GetValue (iter, 1);
if (this.hit_type == hit_type)
return;
this.hit_type = hit_type;
root.SetSource (this.hit_type);
root.HitCollection.PageFirst ();
UpdatePage ();
}
//////////////////////////
private void OnHitsAdded (HitsAddedResponse response)
{
root.Add (response.Hits);
UpdatePage ();
}
private void OnHitsSubtracted (HitsSubtractedResponse response)
{
root.Subtract (response.Uris);
UpdatePage ();
}
private void OnFinished (FinishedResponse repsonse)
{
SetBusy (false);
}
private void AttachQuery ()
{
query.HitsAddedEvent += OnHitsAdded;
query.HitsSubtractedEvent += OnHitsSubtracted;
query.FinishedEvent += OnFinished;
}
private void DetachQuery ()
{
if (query != null) {
query.HitsAddedEvent -= OnHitsAdded;
query.HitsSubtractedEvent -= OnHitsSubtracted;
query.FinishedEvent -= OnFinished;
query.Close ();
query = null;
}
}
private void StartQuery ()
{
try {
query.SendAsync ();
SetBusy (true);
} catch (Beagle.ResponseMessageException e) {
QueueDelayedQuery (entry.Text);
/* To translators: {0} represents the current query keywords */
root.Error (String.Format (Catalog.GetString ("The query for {0} failed." +
"
The likely cause is that the beagle daemon isn't running."), entry.Text));
root.OfferDaemonRestart = true;
} catch (Exception e) {
/* To translators: {0} represents the current query keywords, {1} contains the errormessage */
root.Error (String.Format (Catalog.GetString ("The query for {0} failed with error:
{1}
"),
entry.Text, e));
}
}
private void Search (String searchString)
{
entry.Text = searchString;
StoreSearch (searchString);
if (query != null) {
try { DetachQuery (); } catch (ObjectDisposedException e) {}
}
query = new Query ();
query.AddDomain (QueryDomain.Neighborhood);
// FIXME: Disable non-local searching for now.
//query.AddDomain (QueryDomain.Global);
query.AddText (searchString);
root.SetSource (hit_type);
AttachQuery ();
root.Query = query;
root.Start ();
StartQuery ();
UpdatePage ();
}
private void ClearSearch (object o, EventArgs args)
{
root.Clear ();
UpdatePage ();
entry.Text = "";
// clear search is also "stop current search" - implicitly
DetachQuery ();
}
private void StoreSearch (string query)
{
Gtk.TreeIter iter;
if (history.GetIterFirst (out iter)) {
string val;
do {
val = (string) history.GetValue (iter, 0);
if (val == query)
history.Remove (ref iter);
} while (val != query && history.IterNext (ref iter));
}
iter = history.Insert (0);
history.SetValue (iter, 0, query);
}
public ArrayList RetriveSearches ()
{
ArrayList searches = new ArrayList ();
int i = 0;
foreach (object[] o in history) {
searches.Add (o[0]);
i++;
if (i == 10)
break;
}
return searches;
}
public void ClearHistory ()
{
history.Clear ();
}
public void QuickSearch (string query)
{
Search (query);
}
}
}