/*
* Copyright (C) 2006-2007 Eskil Bylund
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using Gtk;
using Glade;
using Mono.Unix;
using DCSharp.Backend.Connections;
using DCSharp.Backend.Objects;
using DCSharp.Backend.Managers;
namespace DCSharp.GUI{
public class SearchPage : Page
{
private SearchManager searchManager;
private SearchInfo searchInfo;
private MessageArea messageArea;
private Widget focusWidget;
private bool searchEnabled;
private SearchResultStore searchStore;
private TreeModelFilter filterModel;
private TreeModelSort sortModel;
private Dictionary<string, SearchResult> alwaysVisibleResults;
private SortType sortOrder;
private int sortColumnId;
private VisibleColumnsWindow columnsWindow;
private ArrayList addQueue;
private int count;
[Widget]
private HistoryEntry searchEntry;
[Widget]
private ComboBox sizeRestrictComboBox;
[Widget]
private SpinButton sizeSpinButton;
[Widget]
private ComboBox sizeComboBox;
[Widget]
private SearchFileTypeComboBox fileTypeComboBox;
[Widget]
private Table searchTable;
[Widget]
private SearchResultView searchResultView;
[Widget]
private CheckButton freeSlotsCheckButton;
[Widget]
private Button searchButton;
#region Constructors
public SearchPage(SearchManager searchManager) : base("SearchPage.glade")
{
if (searchManager == null)
{
throw new ArgumentNullException("searchManager");
}
this.searchManager = searchManager;
addQueue = ArrayList.Synchronized(new ArrayList());
alwaysVisibleResults = new Dictionary<string, SearchResult>();
tooltip = null;
// User interface
ActionEntry[] entries = new ActionEntry[] {
new ActionEntry("VisibleColumns", null, Catalog.GetString("_Visible Columns..."), null,
Catalog.GetString("Edit visible columns"),
OnVisibleColumns),
};
actionGroup = new ActionGroup("SearchPageActions");
actionGroup.Add(entries);
// Model
searchStore = new SearchResultStore();
filterModel = new TreeModelFilter(searchStore, null);
filterModel.RowInserted += OnRowInserted;
filterModel.RowDeleted += OnRowDeleted;
filterModel.VisibleFunc = FilterSearchResults;
sortModel = new TreeModelSort(filterModel);
sortModel.SetSortColumnId((int)SearchResultStore.Column.Name,
SortType.Ascending);
// View
searchResultView.Model = sortModel;
searchResultView.PopulatePopup += OnPopulatePopup;
searchResultView.SearchTTH += OnSearchTTH;
searchResultView.Show();
searchResultView.GetColumn("TTH").Visible = false;
// Set the text style for the columns.
foreach (TreeViewColumn column in searchResultView.Columns)
{
foreach (CellRenderer renderer in column.CellRenderers)
{
if (renderer is CellRendererText)
{
column.SetCellDataFunc(renderer, SetTextStyle);
}
}
}
// Widgets
searchEntry.Activated += OnSearchActivated;
searchEntry.Changed += delegate
{
searchButton.Sensitive = searchEntry.Text.Length > 0 &&
searchEnabled;
};
searchEntry.Show();
searchEntry.GrabFocus();
sizeRestrictComboBox.Active = 0;
sizeComboBox.Active = 2;
sizeSpinButton.Activated += OnSearchActivated;
searchButton.Clicked += OnSearchActivated;
freeSlotsCheckButton.Toggled += OnFilterToggled;
fileTypeComboBox.Show();
searchEnabled = true;
UpdateSensitivity();
// Events
searchManager.SearchTimedOut += OnSearchTimedOut;
}
#endregion
#region Properties
public override string Title
{
get { return Catalog.GetString("Search"); }
}
public override Gdk.Pixbuf Icon
{
get { return RenderIcon(Stock.Find, IconSize.Menu, null); }
}
public override string Tooltip
{
get
{
if (tooltip == null)
{
return Catalog.GetString("Search for files");
}
return tooltip;
}
}
private string tooltip;
public override ActionGroup Actions
{
get { return actionGroup; }
}
private ActionGroup actionGroup;
public int VisibleColumns
{
get { return Util.GetVisibleColumns(searchResultView.Columns); }
set { Util.SetVisibleColumns(searchResultView.Columns, value | 1); }
}
#endregion
#region Methods
public override void Dispose()
{
searchManager.SearchTimedOut -= OnSearchTimedOut;
StopSearch();
if (columnsWindow != null)
{
columnsWindow.Destroy();
columnsWindow = null;
}
base.Dispose();
}
public void StartSearch(SearchInfo searchInfo)
{
StopSearch();
Status = String.Format(Catalog.GetString("Searching for {0}..."),
searchEntry.Text);
tooltip = Status;
Window window = Toplevel as Window;
if (window != null)
{
focusWidget = window.Focus;
}
IsWorking = true;
searchTable.Sensitive = false;
searchButton.Label = Stock.Stop;
searchButton.GrabFocus();
searchStore.Clear();
alwaysVisibleResults.Clear();
if (searchInfo == null)
{
searchInfo = CreateSearchInfo();
}
this.searchInfo = searchInfo;
searchInfo.SearchResultAdded += OnSearchResultAdded;
searchManager.Search(searchInfo, new TimeSpan(0, 0, 10));
OnPageChanged();
}
public void StartSearch()
{
StartSearch(null);
}
public void StopSearch()
{
if (searchButton.Label != Stock.Find)
{
addQueue.Clear();
tooltip = null;
IsWorking = false;
searchButton.Label = Stock.Find;
searchTable.Sensitive = true;
Window window = Toplevel as Window;
if (window != null && (window.Focus == null ||
window.Focus == searchButton))
{
focusWidget.GrabFocus();
}
UpdateStatus();
}
if (searchInfo != null)
{
searchManager.StopSearch(searchInfo);
searchInfo.SearchResultAdded -= OnSearchResultAdded;
searchInfo = null;
}
}
public void FocusSearchEntry()
{
searchEntry.GrabFocus();
}
public void SetSearchEnabled(bool enabled)//, string message)
{
searchEnabled = enabled;
UpdateSensitivity();
}
protected override bool OnKeyPressEvent(Gdk.EventKey evnt)
{
if (evnt.Key == Gdk.Key.Escape)
{
StopSearch();
return true;
}
return base.OnKeyPressEvent(evnt);
}
private bool FilterSearchResults(TreeModel model, TreeIter iter)
{
if (!freeSlotsCheckButton.Active)
{
return true;
}
SearchResult result = model.GetValue(iter,
(int)SearchResultStore.Column.Object) as SearchResult;
if (result != null && (result.FreeSlots > 0 ||
alwaysVisibleResults.ContainsValue(result)))
{
return true;
}
return false;
}
private void UpdateSensitivity()
{
if (!searchEnabled)
{
if (messageArea == null)
{
messageArea = new MessageArea(Catalog.GetString("Before you can search you have to connect to one or more hubs"),
null, MessageType.Info);
Add(messageArea);
}
messageArea.Show();
}
else if (messageArea != null)
{
messageArea.Hide();
}
searchButton.Sensitive = searchEntry.Text.Length > 0 &&
searchEnabled;
}
private void UpdateStatus()
{
if (count > 0)
{
Status = String.Format(Catalog.GetPluralString("{0} file found",
"{0} files found", count), count);
}
else
{
Status = Catalog.GetString("No files found");
}
}
private SearchInfo CreateSearchInfo()
{
// Keywords
string pattern = searchEntry.Text.Trim();
if (pattern.ToUpper().StartsWith("TTH:"))
{
return new SearchInfo(pattern.Substring(4));
}
string[] keywords = Regex.Split(pattern, @"\s+");
// Type
SearchFileType type = fileTypeComboBox.ActiveType;
// Size
long? size = (long)sizeSpinButton.Value;
if (size <= 0)
{
size = null;
}
else if (sizeComboBox.Active > 0)
{
size *= (long)Math.Pow(1024, sizeComboBox.Active);
}
bool isMaximumSize = sizeRestrictComboBox.Active == 1;
long? maxSize = isMaximumSize ? size : null;
long? minSize = !isMaximumSize ? size : null;
return new SearchInfo(keywords, type, maxSize, minSize);
}
private void OnVisibleColumns(object obj, EventArgs args)
{
if (columnsWindow == null)
{
columnsWindow = new VisibleColumnsWindow(searchResultView.Columns,
Toplevel as Window, searchResultView.Columns[0]);
columnsWindow.Window.Destroyed += delegate
{
columnsWindow = null;
};
}
columnsWindow.Show();
}
private void OnSearchActivated(object obj, EventArgs args)
{
if (searchEntry.Text.Length == 0 || !searchEnabled)
{
return;
}
if (searchButton.Label == Stock.Find)
{
StartSearch();
}
else
{
StopSearch();
}
}
private void OnPopulatePopup(object obj, PopulatePopupArgs args)
{
Action searchAction = searchResultView.Uim.GetAction("/Popup/SearchTTH");
searchAction.Sensitive = searchButton.Sensitive &&
GetSelectedTTH() != null;
}
private void OnSearchTTH(object obj, EventArgs args)
{
string tth = GetSelectedTTH();
if (tth != null && searchButton.Sensitive)
{
StopSearch();
searchEntry.Text = "TTH:" + tth;
SearchInfo searchInfo = new SearchInfo(tth);
StartSearch(searchInfo);
}
}
private string GetSelectedTTH()
{
object[] results = searchResultView.GetSelectedObjects(
(int)SearchResultStore.Column.Object);
if (results.Length == 0)
{
return null;
}
string tth = ((SearchResult)results[0]).TTH;
foreach (SearchResult result in results)
{
if (result.TTH != tth)
{
return null;
}
}
return tth;
}
private void OnFilterToggled(object obj, EventArgs args)
{
filterModel.Refilter();
}
private void OnRowInserted(object obj, RowInsertedArgs args)
{
// Do not count child rows
if (args.Path.Indices.Length == 1)
{
count++;
UpdateStatus();
}
}
private void OnRowDeleted(object obj, RowDeletedArgs args)
{
// Do not count child rows
if (args.Path.Indices.Length == 1)
{
count--;
UpdateStatus();
}
}
private void SetTextStyle(TreeViewColumn column, CellRenderer renderer,
TreeModel model, TreeIter iter)
{
CellRendererText textRenderer = renderer as CellRendererText;
if (textRenderer != null)
{
if (freeSlotsCheckButton.Active)
{
SearchResult result = (SearchResult)model.GetValue(iter,
(int)SearchResultStore.Column.Object);
if (result.FreeSlots == 0 &&
alwaysVisibleResults.ContainsValue(result))
{
textRenderer.Foreground = "dim grey";
return;
}
}
textRenderer.Foreground = "black";
}
}
private void OnSearchResultAdded(object obj, SearchResultEventArgs args)
{
bool addIdleHandler = addQueue.Count == 0;
addQueue.Add(args.Result);
if (addIdleHandler)
{
GLib.Idle.Add(AddResultsInQueue);
}
}
private bool AddResultsInQueue()
{
long ticks = DateTime.Now.Ticks;
DisableSorting();
while (addQueue.Count > 0)
{
SearchResult result = (SearchResult)addQueue[0];
addQueue.Remove(result);
TreeIter iter = searchStore.AddResult(result);
// Handle the case where the parent should be invisible (no
// slots), but there are children that should be visible.
if (result.TTH != null && result.FreeSlots != 0 &&
!alwaysVisibleResults.ContainsKey(result.TTH))
{
TreeIter parent;
if (searchStore.IterParent(out parent, iter))
{
SearchResult parentResult = (SearchResult)searchStore.GetValue(
parent, (int)SearchResultStore.Column.Object);
if (parentResult.FreeSlots == 0)
{
alwaysVisibleResults.Add(result.TTH, parentResult);
}
// Refilter the parent row to make the expander arrow
// visible. This is to work around a bug in TreeModelFilter.
if (searchStore.IterNChildren(parent) == 1)
{
TreePath parentPath = searchStore.GetPath(parent);
searchStore.EmitRowChanged(parentPath,
parent);
}
}
}
// Are we blocking the GUI?
if ((DateTime.Now.Ticks - ticks) >= 400000)
{
RestoreSorting();
return true;
}
}
RestoreSorting();
OnPageChanged();
return false;
}
private void DisableSorting()
{
if (sortModel.GetSortColumnId(out sortColumnId,
out sortOrder))
{
sortModel.SetSortColumnId(-1, SortType.Ascending);
}
}
private void RestoreSorting()
{
sortModel.SetSortColumnId(sortColumnId, sortOrder);
}
private void OnSearchTimedOut(object obj, SearchEventArgs args)
{
Application.Invoke(delegate
{
if (args.SearchInfo == searchInfo)
{
StopSearch();
}
});
}
#endregion
}
}
|