/*
This file is part of Dconf Editor
Dconf Editor 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 3 of the License, or
(at your option) any later version.
Dconf Editor 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 Dconf Editor. If not, see .
*/
using Gtk;
class RegistrySearch : RegistryList
{
private string current_path;
private string [] bookmarks;
private SortingOptions sorting_options;
public ModificationsHandler modifications_handler { private get; set; }
construct
{
placeholder.label = _("No matches");
key_list_box.set_header_func (update_search_results_header);
}
/*\
* * Updating
\*/
private void ensure_selection ()
{
ListBoxRow? row = key_list_box.get_selected_row ();
if (row == null)
select_first_row ();
}
public override void select_first_row ()
{
ListBoxRow? row = key_list_box.get_row_at_index (0);
if (row != null)
key_list_box.select_row ((!) row);
key_list_box.get_adjustment ().set_value (0);
}
/*\
* * Key ListBox
\*/
private Widget new_list_box_row (Object item)
{
ClickableListBoxRow row;
SettingObject setting_object = (SettingObject) item;
string full_name = setting_object.full_name;
string parent_path = SettingsModel.get_parent_path (full_name);
bool is_local_result = parent_path == current_path;
if (setting_object is Directory)
{
row = new FolderListBoxRow (setting_object.name, setting_object.full_name, setting_object.parent_path, !is_local_result);
}
else
{
if (setting_object is GSettingsKey)
row = new KeyListBoxRowEditable ((GSettingsKey) setting_object, modifications_handler, !is_local_result);
else
row = new KeyListBoxRowEditableNoSchema ((DConfKey) setting_object, modifications_handler, !is_local_result);
KeyListBoxRow key_row = (KeyListBoxRow) row;
key_row.small_keys_list_rows = _small_keys_list_rows;
ulong delayed_modifications_changed_handler = modifications_handler.delayed_changes_changed.connect (() => key_row.set_delayed_icon ());
key_row.set_delayed_icon ();
row.destroy.connect (() => modifications_handler.disconnect (delayed_modifications_changed_handler));
}
ulong button_press_event_handler = row.button_press_event.connect (on_button_pressed);
row.destroy.connect (() => row.disconnect (button_press_event_handler));
/* Wrapper ensures max width for rows */
ListBoxRowWrapper wrapper = new ListBoxRowWrapper ();
wrapper.set_halign (Align.CENTER);
wrapper.add (row);
if (row is FolderListBoxRow)
{
wrapper.get_style_context ().add_class ("folder-row");
wrapper.action_name = "ui.open-folder";
wrapper.set_action_target ("s", setting_object.full_name);
}
else
{
wrapper.get_style_context ().add_class ("key-row");
wrapper.action_name = "ui.open-object";
string context = (setting_object is GSettingsKey) ? ((GSettingsKey) setting_object).schema_id : ".dconf";
wrapper.set_action_target ("(ss)", setting_object.full_name, context);
}
return wrapper;
}
private bool on_button_pressed (Widget widget, Gdk.EventButton event)
{
ListBoxRow list_box_row = (ListBoxRow) widget.get_parent ();
Container list_box = (Container) list_box_row.get_parent ();
key_list_box.select_row (list_box_row);
if (event.button == Gdk.BUTTON_SECONDARY)
{
if (list_box.get_focus_child () != null)
list_box_row.grab_focus ();
ClickableListBoxRow row = (ClickableListBoxRow) widget;
int event_x = (int) event.x;
if (event.window != widget.get_window ()) // boolean value switch
{
int widget_x, unused;
event.window.get_position (out widget_x, out unused);
event_x += widget_x;
}
row.show_right_click_popover (event_x);
rows_possibly_with_popover.append (row);
}
else
list_box_row.grab_focus ();
return false;
}
public bool return_pressed ()
{
ListBoxRow? selected_row = (ListBoxRow?) key_list_box.get_selected_row ();
if (selected_row == null)
return false;
((!) selected_row).activate ();
return true;
}
public override bool up_or_down_pressed (bool is_down)
{
ListBoxRow? selected_row = key_list_box.get_selected_row ();
uint n_items = list_model.get_n_items ();
if (selected_row != null)
{
Widget? row_content = ((!) selected_row).get_child ();
if (row_content != null && ((ClickableListBoxRow) (!) row_content).right_click_popover_visible ())
return false;
int position = ((!) selected_row).get_index ();
ListBoxRow? row = null;
if (!is_down && (position >= 1))
row = key_list_box.get_row_at_index (position - 1);
if (is_down && (position < n_items - 1))
row = key_list_box.get_row_at_index (position + 1);
if (row != null)
{
Container list_box = (Container) ((!) selected_row).get_parent ();
scroll_to_row ((!) row, list_box.get_focus_child () != null);
}
return true;
}
else if (n_items >= 1)
{
key_list_box.select_row (key_list_box.get_row_at_index (is_down ? 0 : (int) n_items - 1));
return true;
}
return false;
}
/*\
* * Keyboard calls
\*/
public string? get_copy_path_text ()
{
ListBoxRow? selected_row = key_list_box.get_selected_row ();
if (selected_row == null)
return null;
Variant variant = ((!) selected_row).get_action_target_value ();
string action_target;
if (((!) variant).get_type_string () == "s") // directory
action_target = ((!) variant).get_string ();
else
{
string unused;
((!) variant).get ("(ss)", out action_target, out unused);
}
return action_target;
}
/*\
* * Search
\*/
private string? old_term;
// indices for the start of each section. used to know where to insert search hits and to update the headers
// must be updated before changing the list model, so that the header function works correctly
private int post_local;
private int post_bookmarks;
private int post_folders;
private uint? search_source = null;
private GLib.Queue search_nodes = new GLib.Queue ();
public void clean ()
{
key_list_box.bind_model (null, null);
stop_global_search ();
list_model.remove_all ();
post_local = -1;
post_bookmarks = -1;
post_folders = -1;
old_term = null;
}
public void start_search (string term)
{
if (old_term != null && term == (!) old_term)
{
ensure_selection ();
return;
}
SettingsModel model = modifications_handler.model;
if (old_term != null && term.has_prefix ((!) old_term))
{
pause_global_search ();
refine_local_results (term);
refine_bookmarks_results (term);
if ((!) old_term == "")
start_global_search (model, current_path, term);
else
{
refine_global_results (term);
resume_global_search (current_path, term); // update search term
}
ensure_selection ();
}
else
{
stop_global_search ();
list_model.remove_all ();
post_local = -1;
post_folders = -1;
local_search (model, sorting_options, SettingsModel.get_base_path (current_path), term);
bookmark_search (model, current_path, term, bookmarks);
key_list_box.bind_model (list_model, new_list_box_row);
select_first_row ();
if (term != "")
start_global_search (model, current_path, term);
}
old_term = term;
}
private void refine_local_results (string term)
{
for (int i = post_local - 1; i >= 0; i--)
{
SettingObject item = (SettingObject) list_model.get_item (i);
if (!(term in item.name))
{
post_local--;
post_bookmarks--;
post_folders--;
list_model.remove (i);
}
}
}
private void refine_bookmarks_results (string term)
{
for (int i = post_bookmarks - 1; i >= post_local; i--)
{
SettingObject item = (SettingObject) list_model.get_item (i);
if (!(term in item.name))
{
post_bookmarks--;
post_folders--;
list_model.remove (i);
}
}
}
private void refine_global_results (string term)
{
for (int i = (int) list_model.get_n_items () - 1; i >= post_folders; i--)
{
SettingObject item = (SettingObject) list_model.get_item (i);
if (!(term in item.name))
list_model.remove (i);
}
for (int i = post_folders - 1; i >= post_local; i--)
{
SettingObject item = (SettingObject) list_model.get_item (i);
if (!(term in item.name))
{
post_folders--;
list_model.remove (i);
}
}
}
private bool local_search (SettingsModel model, SortingOptions sorting_options, string current_path, string term)
{
SettingComparator comparator = sorting_options.get_comparator ();
GLib.CompareDataFunc compare = (a, b) => comparator.compare((SettingObject) a, (SettingObject) b);
if (!SettingsModel.is_key_path (current_path))
{
GLib.ListStore? key_model = model.get_children (current_path);
for (uint i = 0; i < ((!) key_model).get_n_items (); i++)
{
SettingObject item = (SettingObject) ((!) key_model).get_item (i);
if (term in item.name)
list_model.insert_sorted (item, compare);
}
}
post_local = (int) list_model.get_n_items ();
post_bookmarks = post_local;
post_folders = post_local;
if (term == "")
return false;
return true;
}
private bool bookmark_search (SettingsModel model, string current_path, string term, string [] bookmarks)
{
string [] installed_bookmarks = {}; // TODO move check in Bookmarks
foreach (string bookmark in bookmarks)
{
if (bookmark in installed_bookmarks)
continue;
installed_bookmarks += bookmark;
if (bookmark == current_path)
continue;
if (SettingsModel.get_parent_path (bookmark) == current_path)
continue;
SettingObject? setting_object = model.get_object (bookmark);
if (setting_object == null)
continue;
if (term in ((!) setting_object).name)
{
post_bookmarks++;
post_folders++;
list_model.insert (post_bookmarks - 1, (!) setting_object);
}
}
return true;
}
private void stop_global_search ()
{
pause_global_search ();
search_nodes.clear ();
}
private void start_global_search (SettingsModel model, string current_path, string term)
{
search_nodes.push_head (model.get_root_directory ());
resume_global_search (current_path, term);
}
private void pause_global_search ()
{
if (search_source == null)
return;
Source.remove ((!) search_source);
search_source = null;
}
private void resume_global_search (string current_path, string term)
{
search_source = Idle.add (() => {
if (global_search_step (current_path, term))
return true;
search_source = null;
return false;
});
}
private bool global_search_step (string current_path, string term)
{
SettingsModel model = modifications_handler.model;
if (!search_nodes.is_empty ())
{
Directory next = (!) search_nodes.pop_head ();
bool local_again = next.full_name == current_path;
GLib.ListStore? next_key_model = model.get_children (next.full_name);
if (next_key_model == null)
return true;
for (uint i = 0; i < ((!) next_key_model).get_n_items (); i++)
{
SettingObject item = (SettingObject) ((!) next_key_model).get_item (i);
if (item is Directory)
{
if (!local_again && term in item.name)
list_model.insert (post_folders++, item);
search_nodes.push_tail ((Directory) item); // we still search local children
}
else
{
if (!local_again && term in item.name)
list_model.append (item);
}
}
ensure_selection ();
return true;
}
return false;
}
private void update_search_results_header (ListBoxRow row, ListBoxRow? before)
{
string? label_text = null;
if (before == null && post_local > 0)
label_text = _("Current folder");
else if (row.get_index () == post_local && post_local != post_bookmarks)
label_text = _("Bookmarks");
else if (row.get_index () == post_bookmarks && post_bookmarks != post_folders)
label_text = _("Folders");
else if (row.get_index () == post_folders)
label_text = _("Keys");
ListBoxRowHeader header = new ListBoxRowHeader (before == null, label_text);
row.set_header (header);
}
public void set_search_parameters (string current_path, string [] bookmarks, SortingOptions sorting_options)
{
clean ();
this.current_path = current_path;
this.bookmarks = bookmarks;
this.sorting_options = sorting_options;
}
}