search: introduce the new Search Locations dialog

https://wiki.gnome.org/Design/SystemSettings/Search

https://bugzilla.gnome.org/show_bug.cgi?id=766513
This commit is contained in:
Felipe Borges 2016-05-16 15:38:12 +02:00
parent 8bc4156375
commit f473ec45c7
4 changed files with 232 additions and 224 deletions

View file

@ -19,6 +19,7 @@
*/ */
#include "cc-search-locations-dialog.h" #include "cc-search-locations-dialog.h"
#include "shell/list-box-helper.h"
#include <glib/gi18n.h> #include <glib/gi18n.h>
@ -38,11 +39,39 @@ typedef struct {
GFile *location; GFile *location;
gchar *display_name; gchar *display_name;
PlaceType place_type; PlaceType place_type;
GIcon *icon;
GCancellable *cancellable; GCancellable *cancellable;
const gchar *settings_key; const gchar *settings_key;
} Place; } Place;
struct _CcSearchLocationsDialog {
GtkDialog parent;
GtkWidget *places_list;
GtkWidget *bookmarks_list;
GtkWidget *others_list;
GtkWidget *locations_add;
};
struct _CcSearchLocationsDialogClass {
GtkDialogClass parent_class;
};
G_DEFINE_TYPE (CcSearchLocationsDialog, cc_search_locations_dialog, GTK_TYPE_DIALOG);
static void
cc_search_locations_dialog_finalize (GObject *object)
{
g_clear_object (&tracker_preferences);
G_OBJECT_CLASS (cc_search_locations_dialog_parent_class)->finalize (object);
}
static void
cc_search_locations_dialog_init (CcSearchLocationsDialog *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
}
static void static void
place_free (Place * p) place_free (Place * p)
{ {
@ -52,7 +81,6 @@ place_free (Place * p)
g_object_unref (p->cancellable); g_object_unref (p->cancellable);
} }
g_clear_object (&p->icon);
g_object_unref (p->location); g_object_unref (p->location);
g_free (p->display_name); g_free (p->display_name);
@ -419,7 +447,6 @@ place_query_info_ready (GObject *source,
GtkWidget *row, *box, *w; GtkWidget *row, *box, *w;
Place *place; Place *place;
GFileInfo *info; GFileInfo *info;
const gchar *desktop_path;
gchar *path; gchar *path;
info = g_file_query_info_finish (G_FILE (source), res, NULL); info = g_file_query_info_finish (G_FILE (source), res, NULL);
@ -432,17 +459,7 @@ place_query_info_ready (GObject *source,
box = gtk_bin_get_child (GTK_BIN (row)); box = gtk_bin_get_child (GTK_BIN (row));
/* FIXME: GLib is currently buggy and returns a non-existent icon name
* when asked for the desktop symbolic icon.
*/
desktop_path = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP);
path = g_file_get_path (G_FILE (source)); path = g_file_get_path (G_FILE (source));
if (g_strcmp0 (path, desktop_path) == 0)
place->icon = g_themed_icon_new ("folder-symbolic");
else
place->icon = g_object_ref (g_file_info_get_symbolic_icon (info));
if (g_strcmp0 (path, g_get_home_dir ()) == 0) if (g_strcmp0 (path, g_get_home_dir ()) == 0)
place->settings_key = TRACKER_KEY_SINGLE_DIRECTORIES; place->settings_key = TRACKER_KEY_SINGLE_DIRECTORIES;
else else
@ -450,13 +467,11 @@ place_query_info_ready (GObject *source,
g_free (path); g_free (path);
w = gtk_image_new_from_gicon (place->icon, GTK_ICON_SIZE_MENU);
gtk_container_add (GTK_CONTAINER (box), w);
w = gtk_label_new (place->display_name); w = gtk_label_new (place->display_name);
gtk_container_add (GTK_CONTAINER (box), w); gtk_container_add (GTK_CONTAINER (box), w);
w = gtk_switch_new (); w = gtk_switch_new ();
gtk_widget_set_valign (w, GTK_ALIGN_CENTER);
gtk_box_pack_end (GTK_BOX (box), w, FALSE, FALSE, 0); gtk_box_pack_end (GTK_BOX (box), w, FALSE, FALSE, 0);
g_settings_bind_with_mapping (tracker_preferences, place->settings_key, g_settings_bind_with_mapping (tracker_preferences, place->settings_key,
w, "active", w, "active",
@ -469,70 +484,19 @@ place_query_info_ready (GObject *source,
g_object_unref (info); g_object_unref (info);
} }
static const gchar *
get_heading_name (PlaceType place)
{
if (place == PLACE_XDG)
return C_("Search Location", "Places");
if (place == PLACE_BOOKMARKS)
return C_("Search Location", "Bookmarks");
if (place == PLACE_OTHER)
return C_("Search Location", "Other");
g_assert_not_reached ();
return NULL;
}
static void static void
place_header_func (GtkListBoxRow *row, remove_button_clicked (GtkWidget *widget,
GtkListBoxRow *before, gpointer user_data)
gpointer user_data)
{ {
gboolean need_separator; GtkWidget *row = user_data;
GtkWidget *current; GPtrArray *new_values;
Place *place, *place_before; Place *place;
gchar *text;
GtkWidget *w;
need_separator = FALSE;
place = g_object_get_data (G_OBJECT (row), "place"); place = g_object_get_data (G_OBJECT (row), "place");
new_values = place_get_new_settings_values (place, TRUE);
g_settings_set_strv (tracker_preferences, place->settings_key, (const gchar **) new_values->pdata);
if (before != NULL) g_ptr_array_unref (new_values);
{
place_before = g_object_get_data (G_OBJECT (before), "place");
if (place_before->place_type < place->place_type)
/* use a separator before the first item of a new type */
need_separator = TRUE;
}
else
{
/* always put a separator before the first item */
need_separator = TRUE;
}
current = gtk_list_box_row_get_header (row);
if (need_separator && current == NULL)
{
text = g_strdup_printf ("<b>%s</b>", get_heading_name (place->place_type));
w = gtk_label_new (NULL);
g_object_set (w,
"margin-top", 6,
"margin-end", 10,
"margin-bottom", 6,
"margin-start", 10,
NULL);
gtk_label_set_markup (GTK_LABEL (w), text);
gtk_widget_set_halign (w, GTK_ALIGN_START);
gtk_style_context_add_class (gtk_widget_get_style_context (w), "dim-label");
gtk_list_box_row_set_header (row, w);
g_free (text);
}
else if (!need_separator && current != NULL)
{
gtk_list_box_row_set_header (row, NULL);
}
} }
static gint static gint
@ -573,16 +537,28 @@ place_compare_func (gconstpointer a,
static GtkWidget * static GtkWidget *
create_row_for_place (Place *place) create_row_for_place (Place *place)
{ {
GtkWidget *child, *row; GtkWidget *child, *row, *remove_button;
row = gtk_list_box_row_new (); row = gtk_list_box_row_new ();
gtk_list_box_row_set_selectable (GTK_LIST_BOX_ROW (row), FALSE);
gtk_list_box_row_set_activatable (GTK_LIST_BOX_ROW (row), FALSE);
child = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); child = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
gtk_container_add (GTK_CONTAINER (row), child); gtk_container_add (GTK_CONTAINER (row), child);
g_object_set (row, "margin-start", 16, "margin-end", 16, NULL); g_object_set (row, "margin", 5, "margin-left", 16, NULL);
g_object_set_data_full (G_OBJECT (row), "place", place, (GDestroyNotify) place_free); g_object_set_data_full (G_OBJECT (row), "place", place, (GDestroyNotify) place_free);
if (place->place_type == PLACE_OTHER)
{
remove_button = gtk_button_new_from_icon_name ("window-close-symbolic", GTK_ICON_SIZE_MENU);
gtk_style_context_add_class (gtk_widget_get_style_context (remove_button), "flat");
gtk_box_pack_end (GTK_BOX (child), remove_button, FALSE, FALSE, 2);
g_signal_connect (remove_button, "clicked",
G_CALLBACK (remove_button_clicked), row);
}
place->cancellable = g_cancellable_new (); place->cancellable = g_cancellable_new ();
g_file_query_info_async (place->location, "standard::symbolic-icon", g_file_query_info_async (place->location, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
G_FILE_QUERY_INFO_NONE, G_PRIORITY_DEFAULT, G_FILE_QUERY_INFO_NONE, G_PRIORITY_DEFAULT,
place->cancellable, place_query_info_ready, row); place->cancellable, place_query_info_ready, row);
@ -590,57 +566,37 @@ create_row_for_place (Place *place)
} }
static void static void
populate_list_box (GtkWidget *list_box) populate_list_boxes (CcSearchLocationsDialog *self)
{ {
GList *places, *l; GList *places, *l;
Place *place;
GtkWidget *row; GtkWidget *row;
places = get_places_list (); places = get_places_list ();
for (l = places; l != NULL; l = l->next) for (l = places; l != NULL; l = l->next)
{ {
/* assumes ownership of place */ place = l->data;
row = create_row_for_place (l->data); row = create_row_for_place (place);
gtk_container_add (GTK_CONTAINER (list_box), row);
switch (place->place_type)
{
case PLACE_XDG:
gtk_container_add (GTK_CONTAINER (self->places_list), row);
break;
case PLACE_BOOKMARKS:
gtk_container_add (GTK_CONTAINER (self->bookmarks_list), row);
break;
case PLACE_OTHER:
gtk_container_add (GTK_CONTAINER (self->others_list), row);
break;
default:
g_assert_not_reached ();
}
} }
g_list_free (places); g_list_free (places);
} }
static void
list_box_row_selected (GtkListBox *list_box,
GtkListBoxRow *row,
gpointer user_data)
{
GtkWidget *remove_button = user_data;
Place *place;
gboolean sensitive = FALSE;
if (row != NULL)
{
place = g_object_get_data (G_OBJECT (row), "place");
sensitive = (place->place_type == PLACE_OTHER);
}
gtk_widget_set_sensitive (remove_button, sensitive);
}
static void
remove_button_clicked (GtkWidget *widget,
gpointer user_data)
{
GtkWidget *list_box = user_data;
GtkListBoxRow *row;
Place *place;
GPtrArray *new_values;
row = gtk_list_box_get_selected_row (GTK_LIST_BOX (list_box));
place = g_object_get_data (G_OBJECT (row), "place");
new_values = place_get_new_settings_values (place, TRUE);
g_settings_set_strv (tracker_preferences, place->settings_key, (const gchar **) new_values->pdata);
g_ptr_array_unref (new_values);
}
static void static void
add_file_chooser_response (GtkDialog *widget, add_file_chooser_response (GtkDialog *widget,
GtkResponseType response, GtkResponseType response,
@ -688,74 +644,50 @@ add_button_clicked (GtkWidget *widget,
} }
static void static void
locations_dialog_refresh (GtkWidget *list_box) other_places_refresh (CcSearchLocationsDialog *self)
{ {
gtk_container_foreach (GTK_CONTAINER (list_box), (GtkCallback) gtk_widget_destroy, NULL); GList *places, *l;
populate_list_box (list_box); Place *place;
} GtkWidget *row;
static void gtk_container_foreach (GTK_CONTAINER (self->others_list), (GtkCallback) gtk_widget_destroy, NULL);
locations_dialog_destroy (GtkWidget *widget)
{
g_clear_object (&tracker_preferences);
}
GtkWidget * places = get_places_list ();
cc_search_locations_dialog_new (CcSearchPanel *self) for (l = places; l != NULL; l = l->next)
{
GtkWidget *locations_dialog, *widget, *list_box;
GtkBuilder *dialog_builder;
GError *error = NULL;
dialog_builder = gtk_builder_new ();
gtk_builder_add_from_resource (dialog_builder,
"/org/gnome/control-center/search/search-locations-dialog.ui",
&error);
if (error != NULL)
{ {
g_warning ("Could not load interface file: %s", error->message); place = l->data;
g_error_free (error); if (place->place_type != PLACE_OTHER)
return NULL; continue;
row = create_row_for_place (place);
gtk_container_add (GTK_CONTAINER (self->others_list), row);
} }
g_list_free (places);
}
CcSearchLocationsDialog *
cc_search_locations_dialog_new (CcSearchPanel *panel)
{
CcSearchLocationsDialog *self;
self = g_object_new (CC_SEARCH_LOCATIONS_DIALOG_TYPE, NULL);
tracker_preferences = g_settings_new (TRACKER_SCHEMA); tracker_preferences = g_settings_new (TRACKER_SCHEMA);
populate_list_boxes (self);
locations_dialog = GTK_WIDGET (gtk_builder_get_object (dialog_builder, "locations_dialog")); gtk_list_box_set_sort_func (GTK_LIST_BOX (self->others_list),
widget = GTK_WIDGET (gtk_builder_get_object (dialog_builder, "locations_scrolledwindow"));
list_box = GTK_WIDGET (gtk_list_box_new ());
gtk_container_add (GTK_CONTAINER (widget), list_box);
gtk_list_box_set_sort_func (GTK_LIST_BOX (list_box),
(GtkListBoxSortFunc)place_compare_func, NULL, NULL); (GtkListBoxSortFunc)place_compare_func, NULL, NULL);
gtk_list_box_set_header_func (GTK_LIST_BOX (list_box),
place_header_func, NULL, NULL);
gtk_widget_show (list_box);
widget = GTK_WIDGET (gtk_builder_get_object (dialog_builder, "locations_remove")); gtk_list_box_set_header_func (GTK_LIST_BOX (self->others_list), cc_list_box_update_header_func, NULL, NULL);
gtk_widget_set_sensitive (widget, FALSE);
g_signal_connect (list_box, "row-selected",
G_CALLBACK (list_box_row_selected), widget);
g_signal_connect (widget, "clicked",
G_CALLBACK (remove_button_clicked), list_box);
g_signal_connect_swapped (tracker_preferences, "changed::" TRACKER_KEY_RECURSIVE_DIRECTORIES, g_signal_connect_swapped (tracker_preferences, "changed::" TRACKER_KEY_RECURSIVE_DIRECTORIES,
G_CALLBACK (locations_dialog_refresh), list_box); G_CALLBACK (other_places_refresh), self);
widget = GTK_WIDGET (gtk_builder_get_object (dialog_builder, "locations_add")); gtk_window_set_transient_for (GTK_WINDOW (self),
g_signal_connect (widget, "clicked", GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (panel))));
G_CALLBACK (add_button_clicked), list_box);
gtk_window_set_transient_for (GTK_WINDOW (locations_dialog), return self;
GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))));
g_signal_connect (locations_dialog, "response",
G_CALLBACK (gtk_widget_destroy), NULL);
g_signal_connect (locations_dialog, "destroy",
G_CALLBACK (locations_dialog_destroy), NULL);
populate_list_box (list_box);
g_object_unref (dialog_builder);
return locations_dialog;
} }
gboolean gboolean
@ -775,3 +707,22 @@ cc_search_locations_dialog_is_available (void)
g_settings_schema_unref (schema); g_settings_schema_unref (schema);
return TRUE; return TRUE;
} }
static void
cc_search_locations_dialog_class_init (CcSearchLocationsDialogClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = cc_search_locations_dialog_finalize;
gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/control-center/search/search-locations-dialog.ui");
gtk_widget_class_bind_template_child (widget_class, CcSearchLocationsDialog, places_list);
gtk_widget_class_bind_template_child (widget_class, CcSearchLocationsDialog, bookmarks_list);
gtk_widget_class_bind_template_child (widget_class, CcSearchLocationsDialog, others_list);
gtk_widget_class_bind_template_child (widget_class, CcSearchLocationsDialog, locations_add);
gtk_widget_class_bind_template_callback (widget_class, add_button_clicked);
}

View file

@ -23,7 +23,15 @@
#include "cc-search-panel.h" #include "cc-search-panel.h"
GtkWidget *cc_search_locations_dialog_new (CcSearchPanel *panel); #define CC_SEARCH_LOCATIONS_DIALOG_TYPE (cc_search_locations_dialog_get_type ())
#define CC_SEARCH_LOCATIONS_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CC_SEARCH_LOCATIONS_DIALOG_TYPE, CcSearchLocationsDialog))
typedef struct _CcSearchLocationsDialog CcSearchLocationsDialog;
typedef struct _CcSearchLocationsDialogClass CcSearchLocationsDialogClass;
GType cc_search_locations_dialog_get_type (void);
CcSearchLocationsDialog *cc_search_locations_dialog_new (CcSearchPanel *panel);
gboolean cc_search_locations_dialog_is_available (void); gboolean cc_search_locations_dialog_is_available (void);

View file

@ -40,7 +40,7 @@ struct _CcSearchPanelPrivate
GSettings *search_settings; GSettings *search_settings;
GHashTable *sort_order; GHashTable *sort_order;
GtkWidget *locations_dialog; CcSearchLocationsDialog *locations_dialog;
}; };
#define SHELL_PROVIDER_GROUP "Shell Search Provider" #define SHELL_PROVIDER_GROUP "Shell Search Provider"
@ -701,7 +701,7 @@ cc_search_panel_finalize (GObject *object)
g_hash_table_destroy (priv->sort_order); g_hash_table_destroy (priv->sort_order);
if (priv->locations_dialog) if (priv->locations_dialog)
gtk_widget_destroy (priv->locations_dialog); gtk_widget_destroy (GTK_WIDGET (priv->locations_dialog));
G_OBJECT_CLASS (cc_search_panel_parent_class)->finalize (object); G_OBJECT_CLASS (cc_search_panel_parent_class)->finalize (object);
} }

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<interface> <interface>
<!-- interface-requires gtk+ 3.0 --> <!-- interface-requires gtk+ 3.0 -->
<object class="GtkDialog" id="locations_dialog"> <template class="CcSearchLocationsDialog" parent="GtkDialog">
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="default_height">400</property> <property name="default_height">400</property>
<property name="default_width">400</property> <property name="default_width">400</property>
@ -13,76 +13,125 @@
<object class="GtkBox" id="dialog-vbox1"> <object class="GtkBox" id="dialog-vbox1">
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<property name="spacing">2</property> <property name="border-width">0</property>
<child> <child>
<object class="GtkBox" id="locations_main_box"> <object class="GtkNotebook">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="border-width">5</property>
<child> <child>
<object class="GtkScrolledWindow" id="locations_scrolledwindow"> <object class="GtkBox">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property>
<property name="shadow_type">in</property>
<property name="vexpand">True</property> <property name="vexpand">True</property>
<property name="border-width">35</property>
<child> <child>
<placeholder/> <object class="GtkListBox" id="places_list">
<property name="visible">True</property>
<property name="expand">True</property>
<child>
<placeholder/>
</child>
</object>
</child> </child>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="tab-expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing> </packing>
</child> </child>
<child> <child type="tab">
<object class="GtkToolbar" id="locations_toolbar"> <object class="GtkLabel">
<property name="visible">True</property>
<property name="label" translatable="yes">Places</property>
</object>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_size">1</property>
<style>
<class name="inline-toolbar"/>
</style>
<child> <child>
<object class="GtkToolButton" id="locations_add"> <object class="GtkBox">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="vexpand">True</property>
<property name="use_underline">True</property> <property name="border-width">35</property>
<property name="icon_name">list-add-symbolic</property> <child>
<object class="GtkListBox" id="bookmarks_list">
<property name="visible">True</property>
<property name="expand">True</property>
<child>
<placeholder/>
</child>
</object>
</child>
</object> </object>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
</packing>
</child>
<child>
<object class="GtkToolButton" id="locations_remove">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="use_underline">True</property>
<property name="icon_name">list-remove-symbolic</property>
</object>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
</packing>
</child> </child>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="tab-expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing> </packing>
</child> </child>
<child type="tab">
<object class="GtkLabel">
<property name="visible">True</property>
<property name="label" translatable="yes">Bookmarks</property>
</object>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="vexpand">True</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkListBox" id="others_list">
<property name="visible">True</property>
<child>
<placeholder/>
</child>
</object>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
</object>
</child>
<child>
<object class="GtkButton" id="locations_add">
<property name="visible">True</property>
<property name="halign">center</property>
<property name="margin">5</property>
<signal name="clicked" handler="add_button_clicked" swapped="no"/>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="icon-name">list-add-symbolic</property>
<property name="icon-size">1</property>
</object>
</child>
<style>
<class name="flat"/>
</style>
</object>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="tab-expand">True</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel">
<property name="visible">True</property>
<property name="label" translatable="yes">Other</property>
</object>
</child>
</object> </object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child> </child>
</object> </object>
</child> </child>
</object> </template>
</interface> </interface>