Commit adc0821d authored by Andrei Lisita's avatar Andrei Lisita 🎮

history-dialog: Add selection mode

Closes GNOME/epiphany#903
parent dce900bc
Pipeline #196354 passed with stages
in 17 minutes and 42 seconds
......@@ -225,15 +225,21 @@ delete_selected (EphyHistoryDialog *self)
}
static void
forget_clicked (GtkButton *button,
gpointer user_data)
row_check_button_toggled (GtkCheckButton *check_button,
GtkListBoxRow *row)
{
EphyHistoryDialog *self = EPHY_HISTORY_DIALOG (user_data);
GtkListBoxRow *row = g_object_get_data (G_OBJECT (button), "row");
GtkListBox *listbox = GTK_LIST_BOX (gtk_widget_get_parent (GTK_WIDGET (row)));
gboolean row_selected = gtk_list_box_row_is_selected (row);
gboolean button_checked = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check_button));
gtk_list_box_select_row (GTK_LIST_BOX (self->listbox), row);
if (button_checked == row_selected)
return;
delete_selected (self);
/* If the user ticks a checkbox, then the corresponding row should get selected */
if (button_checked)
gtk_list_box_select_row (listbox, row);
else
gtk_list_box_unselect_row (listbox, row);
}
static GtkWidget *
......@@ -244,7 +250,7 @@ create_row (EphyHistoryDialog *self,
GtkWidget *date;
GtkWidget *row;
GtkWidget *separator;
GtkWidget *button;
GtkWidget *check_button;
/* Row */
row = hdy_action_row_new ();
......@@ -263,23 +269,37 @@ create_row (EphyHistoryDialog *self,
gtk_widget_set_margin_top (separator, 8);
gtk_widget_set_margin_bottom (separator, 8);
/* Button */
button = gtk_button_new_from_icon_name ("user-trash-symbolic", GTK_ICON_SIZE_BUTTON);
gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
g_object_set_data (G_OBJECT (button), "row", row);
gtk_widget_set_tooltip_text (button, _("Remove the selected pages from history"));
g_signal_connect (button, "clicked", G_CALLBACK (forget_clicked), self);
gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
/* Added in reverse order because actions are packed from the end. */
/* CheckButton */
check_button = gtk_check_button_new ();
g_object_set_data (G_OBJECT (row), "check-button", check_button);
gtk_widget_set_valign (check_button, GTK_ALIGN_CENTER);
gtk_widget_set_tooltip_text (check_button, _("Remove the selected pages from history"));
gtk_button_set_relief (GTK_BUTTON (check_button), GTK_RELIEF_NONE);
g_signal_connect (check_button, "toggled", G_CALLBACK (row_check_button_toggled), row);
/* Separator and CheckButton should be visible only during selection mode */
g_object_bind_property (self, "selection-active",
separator, "visible",
G_BINDING_DEFAULT);
g_object_bind_property (self, "selection-active",
check_button, "visible",
G_BINDING_DEFAULT);
hdy_action_row_add_prefix (HDY_ACTION_ROW (row), check_button);
hdy_action_row_add_prefix (HDY_ACTION_ROW (row), separator);
gtk_container_add (GTK_CONTAINER (row), date);
gtk_container_add (GTK_CONTAINER (row), separator);
gtk_container_add (GTK_CONTAINER (row), button);
gtk_widget_set_sensitive (button, ephy_embed_shell_get_mode (shell) != EPHY_EMBED_SHELL_MODE_INCOGNITO);
gtk_widget_set_sensitive (check_button, ephy_embed_shell_get_mode (shell) != EPHY_EMBED_SHELL_MODE_INCOGNITO);
gtk_widget_show_all (row);
/* Hide the Separator and CheckButton if selection isn't active */
if (!ephy_data_dialog_get_selection_active (EPHY_DATA_DIALOG (self))) {
gtk_widget_set_visible (separator, FALSE);
gtk_widget_set_visible (check_button, FALSE);
}
return row;
}
......@@ -560,18 +580,116 @@ on_listbox_row_activated (GtkListBox *box,
ephy_history_url_free (url);
}
static void
on_listbox_selected_rows_changed (GtkListBox *listbox,
EphyHistoryDialog *self)
{
GList *selected_rows = gtk_list_box_get_selected_rows (listbox);
GList *rows = gtk_container_get_children (GTK_CONTAINER (listbox));
GList *iter = NULL;
for (iter = rows; iter != NULL; iter = g_list_next (iter)) {
GtkWidget *row = iter->data;
GtkCheckButton *check_button = GTK_CHECK_BUTTON (g_object_get_data (G_OBJECT (row), "check-button"));
gboolean row_selected = gtk_list_box_row_is_selected (GTK_LIST_BOX_ROW (row));
gboolean button_checked = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check_button));
/* Ensure selected rows have their checkboxes ticked */
if (button_checked == row_selected)
continue;
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button), row_selected);
}
if (selected_rows == NULL)
ephy_data_dialog_set_selection_delete_sensitive (EPHY_DATA_DIALOG (self), FALSE);
else
ephy_data_dialog_set_selection_delete_sensitive (EPHY_DATA_DIALOG (self), TRUE);
g_list_free (selected_rows);
g_list_free (rows);
}
static void
select_list_index_interval (GtkListBox *listbox,
gint index_a,
gint index_b)
{
gint start = 0;
gint end = 0;
gint index = 0;
if (index_a < index_b) {
start = index_a;
end = index_b;
} else {
start = index_b;
end = index_a;
}
for (index = start; index <= end; index++) {
GtkListBoxRow *row = gtk_list_box_get_row_at_index (listbox, index);
gtk_list_box_select_row (listbox, row);
}
}
static void
handle_selection_click_event (EphyHistoryDialog *self,
GtkListBoxRow *clicked_row,
GdkEventButton *event)
{
GList *selected_rows = NULL;
GtkListBox *listbox = GTK_LIST_BOX (self->listbox);
gboolean shift_pressed = event->state & GDK_SHIFT_MASK;
/* If Shift isn't pressed, event simply toggles the row's selection */
if (!shift_pressed) {
if (gtk_list_box_row_is_selected (clicked_row))
gtk_list_box_unselect_row (listbox, clicked_row);
else
gtk_list_box_select_row (listbox, clicked_row);
return;
}
/* If Shift key is pressed, do the interval selection logic */
selected_rows = gtk_list_box_get_selected_rows (listbox);
if (g_list_length (selected_rows) == 1) {
/* If there's exactly one other row selected we select the interval between
* that one and the currently clicked row */
gint index_a = gtk_list_box_row_get_index (clicked_row);
gint index_b = gtk_list_box_row_get_index (selected_rows->data);
select_list_index_interval (listbox, index_a, index_b);
} else {
/* If there are zero or more than one other rows selected,
* then we select the clicked row and unselect all the others */
gtk_list_box_unselect_all (listbox);
gtk_list_box_select_row (listbox, clicked_row);
}
g_list_free (selected_rows);
}
static gboolean
on_listbox_button_press_event (GtkWidget *widget,
GdkEventButton *event,
EphyHistoryDialog *self)
{
GtkListBoxRow *row = gtk_list_box_get_row_at_y (GTK_LIST_BOX (self->listbox), event->y);
gboolean selection_active = ephy_data_dialog_get_selection_active (EPHY_DATA_DIALOG (self));
if (!row)
return GDK_EVENT_PROPAGATE;
if (event->button == GDK_BUTTON_SECONDARY) {
GtkListBoxRow *row = gtk_list_box_get_row_at_y (GTK_LIST_BOX (self->listbox), event->y);
GList *rows = NULL;
int n;
if (!row)
return GDK_EVENT_PROPAGATE;
/* If selection mode is active, secondary button has no effect */
if (selection_active)
return GDK_EVENT_STOP;
if (!gtk_list_box_row_is_selected (row))
gtk_list_box_unselect_all (GTK_LIST_BOX (self->listbox));
......@@ -585,6 +703,14 @@ on_listbox_button_press_event (GtkWidget *widget,
gtk_menu_popup_at_pointer (GTK_MENU (self->popup_menu), (GdkEvent *)event);
return GDK_EVENT_STOP;
} else if (event->button == GDK_BUTTON_PRIMARY) {
/* If selection mode isn't active, primary button has no effect */
if (!selection_active)
return GDK_EVENT_STOP;
handle_selection_click_event (self, row, event);
return GDK_EVENT_STOP;
}
......@@ -671,6 +797,18 @@ on_edge_reached (GtkScrolledWindow *scrolled,
}
}
static void
on_dialog_selection_active_toggled (EphyHistoryDialog *self)
{
gtk_list_box_unselect_all (GTK_LIST_BOX (self->listbox));
}
static void
on_dialog_selection_delete_clicked (EphyHistoryDialog *self)
{
delete_selected (self);
}
static void
ephy_history_dialog_class_init (EphyHistoryDialogClass *klass)
{
......@@ -697,10 +835,13 @@ ephy_history_dialog_class_init (EphyHistoryDialogClass *klass)
gtk_widget_class_bind_template_callback (widget_class, on_listbox_row_activated);
gtk_widget_class_bind_template_callback (widget_class, on_listbox_row_selected);
gtk_widget_class_bind_template_callback (widget_class, on_listbox_selected_rows_changed);
gtk_widget_class_bind_template_callback (widget_class, on_listbox_button_press_event);
gtk_widget_class_bind_template_callback (widget_class, on_listbox_key_press_event);
gtk_widget_class_bind_template_callback (widget_class, on_key_press_event);
gtk_widget_class_bind_template_callback (widget_class, on_search_text_changed);
gtk_widget_class_bind_template_callback (widget_class, on_dialog_selection_active_toggled);
gtk_widget_class_bind_template_callback (widget_class, on_dialog_selection_delete_clicked);
gtk_widget_class_bind_template_callback (widget_class, on_edge_reached);
}
......
......@@ -26,20 +26,26 @@
typedef struct {
GtkWidget *box;
GtkWidget *header_bars_stack;
GtkWidget *window_header_bar;
GtkWidget *selection_header_bar;
GtkWidget *child;
GtkWidget *clear_all_button;
GtkWidget *search_bar;
GtkWidget *search_entry;
GtkWidget *search_button;
GtkWidget *stack;
GtkWidget *data_presentation_stack;
GtkWidget *empty_title_label;
GtkWidget *empty_description_label;
GtkWidget *spinner;
GtkWidget *selection_action_bar_revealer;
GtkWidget *selection_delete_button;
gboolean is_loading : 1;
gboolean has_data : 1;
gboolean has_search_results : 1;
gboolean can_clear : 1;
gboolean selection_active : 1;
char *search_text;
} EphyDataDialogPrivate;
......@@ -58,6 +64,7 @@ enum {
PROP_HAS_DATA,
PROP_HAS_SEARCH_RESULTS,
PROP_CAN_CLEAR,
PROP_SELECTION_ACTIVE,
LAST_PROP,
};
......@@ -65,6 +72,7 @@ static GParamSpec *obj_properties[LAST_PROP];
enum {
CLEAR_ALL_CLICKED,
SELECTION_DELETE_CLICKED,
LAST_SIGNAL,
};
......@@ -74,22 +82,23 @@ static void
update (EphyDataDialog *self)
{
EphyDataDialogPrivate *priv = ephy_data_dialog_get_instance_private (self);
GtkStack *data_stack = GTK_STACK (priv->data_presentation_stack);
gboolean has_data = priv->has_data && priv->child && gtk_widget_get_visible (priv->child);
if (priv->is_loading) {
gtk_stack_set_visible_child_name (GTK_STACK (priv->stack), "loading");
gtk_stack_set_visible_child_name (data_stack, "loading");
gtk_spinner_start (GTK_SPINNER (priv->spinner));
} else {
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->search_button))) {
if (has_data && priv->has_search_results)
gtk_stack_set_visible_child (GTK_STACK (priv->stack), priv->child);
gtk_stack_set_visible_child (data_stack, priv->child);
else
gtk_stack_set_visible_child_name (GTK_STACK (priv->stack), "no-results");
gtk_stack_set_visible_child_name (data_stack, "no-results");
} else {
if (has_data)
gtk_stack_set_visible_child (GTK_STACK (priv->stack), priv->child);
gtk_stack_set_visible_child (data_stack, priv->child);
else
gtk_stack_set_visible_child_name (GTK_STACK (priv->stack), "empty");
gtk_stack_set_visible_child_name (data_stack, "empty");
}
gtk_spinner_stop (GTK_SPINNER (priv->spinner));
}
......@@ -104,6 +113,35 @@ on_clear_all_button_clicked (EphyDataDialog *self)
g_signal_emit (self, signals[CLEAR_ALL_CLICKED], 0);
}
static void
on_selection_button_clicked (GtkButton *button,
EphyDataDialog *self)
{
EphyDataDialogPrivate *priv = ephy_data_dialog_get_instance_private (self);
GtkStack *header_bars_stack = GTK_STACK (priv->header_bars_stack);
ephy_data_dialog_set_selection_active (self, TRUE);
gtk_stack_set_visible_child (header_bars_stack, priv->selection_header_bar);
}
static void
on_selection_cancel_button_clicked (GtkButton *button,
EphyDataDialog *self)
{
EphyDataDialogPrivate *priv = ephy_data_dialog_get_instance_private (self);
GtkStack *header_bars_stack = GTK_STACK (priv->header_bars_stack);
ephy_data_dialog_set_selection_active (self, FALSE);
gtk_stack_set_visible_child (header_bars_stack, priv->window_header_bar);
}
static void
on_selection_delete_button_clicked (GtkButton *button,
EphyDataDialog *self)
{
g_signal_emit (self, signals[SELECTION_DELETE_CLICKED], 0);
}
static void
on_search_entry_changed (GtkSearchEntry *entry,
EphyDataDialog *self)
......@@ -184,6 +222,9 @@ ephy_data_dialog_set_property (GObject *object,
case PROP_CAN_CLEAR:
ephy_data_dialog_set_can_clear (self, g_value_get_boolean (value));
break;
case PROP_SELECTION_ACTIVE:
ephy_data_dialog_set_selection_active (self, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
......@@ -233,6 +274,9 @@ ephy_data_dialog_get_property (GObject *object,
case PROP_CAN_CLEAR:
g_value_set_boolean (value, ephy_data_dialog_get_can_clear (self));
break;
case PROP_SELECTION_ACTIVE:
g_value_set_boolean (value, ephy_data_dialog_get_selection_active (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
......@@ -256,6 +300,7 @@ ephy_data_dialog_add (GtkContainer *container,
{
EphyDataDialog *self = EPHY_DATA_DIALOG (container);
EphyDataDialogPrivate *priv = ephy_data_dialog_get_instance_private (self);
GtkStack *data_stack = GTK_STACK (priv->data_presentation_stack);
if (!priv->box) {
GTK_CONTAINER_CLASS (ephy_data_dialog_parent_class)->add (container, child);
......@@ -265,7 +310,7 @@ ephy_data_dialog_add (GtkContainer *container,
g_assert (!priv->child);
priv->child = child;
gtk_container_add (GTK_CONTAINER (priv->stack), child);
gtk_container_add (GTK_CONTAINER (data_stack), child);
update (self);
}
......@@ -360,8 +405,24 @@ ephy_data_dialog_class_init (EphyDataDialogClass *klass)
FALSE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
obj_properties[PROP_SELECTION_ACTIVE] =
g_param_spec_boolean ("selection-active",
"Selection active",
"Is selection active?",
FALSE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, LAST_PROP, obj_properties);
signals[SELECTION_DELETE_CLICKED] =
g_signal_new ("selection-delete-clicked",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST,
0, NULL, NULL, NULL,
G_TYPE_NONE,
0,
G_TYPE_NONE);
/**
* EphyLocationEntry::user-changed:
* @entry: the object on which the signal is emitted
......@@ -381,6 +442,9 @@ ephy_data_dialog_class_init (EphyDataDialogClass *klass)
gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/epiphany/gtk/data-dialog.ui");
gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, box);
gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, header_bars_stack);
gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, window_header_bar);
gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, selection_header_bar);
gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, clear_all_button);
gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, empty_title_label);
gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, empty_description_label);
......@@ -388,10 +452,15 @@ ephy_data_dialog_class_init (EphyDataDialogClass *klass)
gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, search_button);
gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, search_entry);
gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, spinner);
gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, stack);
gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, data_presentation_stack);
gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, selection_action_bar_revealer);
gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, selection_delete_button);
gtk_widget_class_bind_template_callback (widget_class, on_key_press_event);
gtk_widget_class_bind_template_callback (widget_class, on_clear_all_button_clicked);
gtk_widget_class_bind_template_callback (widget_class, on_selection_button_clicked);
gtk_widget_class_bind_template_callback (widget_class, on_selection_cancel_button_clicked);
gtk_widget_class_bind_template_callback (widget_class, on_selection_delete_button_clicked);
gtk_widget_class_bind_template_callback (widget_class, on_search_entry_changed);
}
......@@ -569,6 +638,51 @@ ephy_data_dialog_set_can_clear (EphyDataDialog *self,
g_object_notify_by_pspec (G_OBJECT (self), obj_properties[PROP_CAN_CLEAR]);
}
gboolean
ephy_data_dialog_get_selection_active (EphyDataDialog *self)
{
EphyDataDialogPrivate *priv;
g_assert (EPHY_IS_DATA_DIALOG (self));
priv = ephy_data_dialog_get_instance_private (self);
return priv->selection_active;
}
void
ephy_data_dialog_set_selection_active (EphyDataDialog *self,
gboolean selection_active)
{
EphyDataDialogPrivate *priv;
g_assert (EPHY_IS_DATA_DIALOG (self));
priv = ephy_data_dialog_get_instance_private (self);
if (priv->selection_active == selection_active)
return;
priv->selection_active = selection_active;
update (self);
g_object_notify_by_pspec (G_OBJECT (self), obj_properties[PROP_SELECTION_ACTIVE]);
}
void
ephy_data_dialog_set_selection_delete_sensitive (EphyDataDialog *self,
gboolean is_sensitive)
{
EphyDataDialogPrivate *priv;
g_assert (EPHY_IS_DATA_DIALOG (self));
priv = ephy_data_dialog_get_instance_private (self);
gtk_widget_set_sensitive (priv->selection_delete_button, is_sensitive);
}
const gchar *
ephy_data_dialog_get_search_text (EphyDataDialog *self)
{
......
......@@ -54,6 +54,13 @@ gboolean ephy_data_dialog_get_can_clear (EphyDataDialog *self);
void ephy_data_dialog_set_can_clear (EphyDataDialog *self,
gboolean can_clear);
gboolean ephy_data_dialog_get_selection_active (EphyDataDialog *self);
void ephy_data_dialog_set_selection_active (EphyDataDialog *self,
gboolean selection_active);
void ephy_data_dialog_set_selection_delete_sensitive (EphyDataDialog *self,
gboolean is_sensitive);
const gchar *ephy_data_dialog_get_search_text (EphyDataDialog *self);
G_END_DECLS
......@@ -3,11 +3,6 @@
<interface>
<requires lib="gtk+" version="3.20"/>
<requires lib="libhandy" version="0.0"/>
<object class="GtkImage" id="search_image">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">edit-find-symbolic</property>
</object>
<object class="GtkImage" id="clear_all_image">
<property name="visible">True</property>
<property name="can_focus">False</property>
......@@ -28,47 +23,128 @@
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="HdyHeaderBar">
<object class="GtkStack" id="header_bars_stack">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="show_close_button">True</property>
<property name="title" bind-source="EphyDataDialog" bind-property="title"/>
<property name="transition-type">GTK_STACK_TRANSITION_TYPE_CROSSFADE</property>
<child>
<object class="GtkButton" id="clear_all_button">
<object class="HdyHeaderBar" id="window_header_bar">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="image">clear_all_image</property>
<property name="always_show_image">True</property>
<signal name="clicked" handler="on_clear_all_button_clicked" swapped="yes"/>
<accelerator key="Delete" modifiers="GDK_SHIFT_MASK" signal="clicked"/>
<style>
<class name="destructive-action"/>
<class name="image-button"/>
</style>
<property name="can_focus">False</property>
<property name="show_close_button">True</property>
<property name="title" bind-source="EphyDataDialog" bind-property="title"/>
<child>
<object class="GtkButton" id="clear_all_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="image">clear_all_image</property>
<property name="always_show_image">True</property>
<signal name="clicked" handler="on_clear_all_button_clicked" swapped="yes"/>
<accelerator key="Delete" modifiers="GDK_SHIFT_MASK" signal="clicked"/>
<style>
<class name="destructive-action"/>
<class name="image-button"/>
</style>
</object>
</child>
<child>
<object class="GtkButton" id="selection_button">
<property name="visible">True</property>
<property name="valign">center</property>
<property name="use-underline">True</property>
<signal name="clicked" handler="on_selection_button_clicked"/>
<style>
<class name="image-button"/>
</style>
<child internal-child="accessible">
<object class="AtkObject" id="a11y-button3">
<property name="accessible-name" translatable="yes">Select Items</property>
</object>
</child>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="icon-name">object-select-symbolic</property>
<property name="icon-size">1</property>
</object>
</child>
</object>
<packing>
<property name="pack-type">end</property>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="search_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="always_show_image">True</property>
<property name="active" bind-source="search_bar" bind-property="search-mode-enabled" bind-flags="sync-create|bidirectional"/>
<accelerator key="F" modifiers="GDK_CONTROL_MASK" signal="clicked"/>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">edit-find-symbolic</property>
</object>
</child>
<child internal-child="accessible">
<object class="AtkObject">
<property name="AtkObject::accessible-name" translatable="yes">Search</property>
</object>
</child>
</object>
<packing>
<property name="pack_type">end</property>
</packing>
</child>
</object>
</child>
<child>
<object class="GtkToggleButton" id="search_button">
<object class="HdyHeaderBar" id="selection_header_bar">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="image">search_image</property>
<property name="always_show_image">True</property>
<property name="active" bind-source="search_bar" bind-property="search-mode-enabled" bind-flags="sync-create|bidirectional"/>
<accelerator key="F" modifiers="GDK_CONTROL_MASK" signal="clicked"/>
<property name="title" bind-source="EphyDataDialog" bind-property="title"/>
<style>
<class name="image-button"/>
<class name="selection-mode"/>
</style>
<child internal-child="accessible">
<object class="AtkObject">
<property name="AtkObject::accessible-name" translatable="yes">Search</property>
<child>
<object class="GtkButton" id="selection_cancel_button">
<property name="visible">True</property>
<property name="valign">center</property>
<property name="use-underline">True</property>
<property name="label" translatable="yes">_Cancel</property>
<signal name="clicked" handler="on_selection_cancel_button_clicked"/>
<style>
<class name="text-button"/>
</style>
</object>
<packing>
<property name="pack-type">end</property>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="selection_search_button">
<property name="visible">True</property>
<property name="active" bind-source="search_bar" bind-property="search-mode-enabled" bind-flags="sync-create|bidirectional"/>
<accelerator key="F" modifiers="GDK_CONTROL_MASK" signal="clicked"/>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">edit-find-symbolic</property>
</object>
</child>
<child internal-child="accessible">
<object class="AtkObject">
<property name="AtkObject::accessible-name" translatable="yes">Search</property>
</object>
</child>
</object>
<packing>
<property name="pack_type">end</property>
</packing>
</child>
</object>
<packing>
<property name="pack_type">end</property>
</packing>
</child>
</object>
</child>
......@@ -104,7 +180,7 @@