[almanah] Bug 647690 — Make search asynchronous
- From: Philip Withnall <pwithnall src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [almanah] Bug 647690 — Make search asynchronous
- Date: Mon, 27 Jun 2011 22:29:40 +0000 (UTC)
commit cb8ad21e8c32fdce37359e192a7dc3d387af9558
Author: Ãlvaro PeÃa <alvaropg gmail com>
Date: Mon Jun 27 23:28:41 2011 +0100
Bug 647690 â Make search asynchronous
Add an asynchronous search API and make use of it in the search dialogue.
Also give the search dialogue a bit of a facelift.
Closes: bgo#647690
data/almanah.ui | 201 +++++++++++++++++++++++++++++++------------------
src/main-window.c | 2 +-
src/search-dialog.c | 175 ++++++++++++++++++++++++++++++++++++-------
src/storage-manager.c | 162 +++++++++++++++++++++++++++++++++++++++
src/storage-manager.h | 7 ++
5 files changed, 448 insertions(+), 99 deletions(-)
---
diff --git a/data/almanah.ui b/data/almanah.ui
index 27d10b2..031c057 100644
--- a/data/almanah.ui
+++ b/data/almanah.ui
@@ -477,116 +477,172 @@
<object class="AlmanahSearchDialog" id="almanah_search_dialog">
<property name="border-width">5</property>
+ <property name="default_width">400</property>
<child internal-child="vbox">
<object class="GtkVBox" id="vbox2">
<property name="spacing">6</property>
+ <property name="visible">True</property>
<child>
<object class="GtkVBox" id="almanah_search_dialog_vbox">
<property name="spacing">6</property>
- <property name="border-width">5</property>
+ <property name="border-width">6</property>
+ <property name="visible">True</property>
<child>
- <object class="GtkAlignment" id="alignment0">
- <property name="bottom-padding">12</property>
+ <object class="GtkHBox" id="hbox0">
+ <property name="spacing">6</property>
+ <property name="visible">True</property>
<child>
- <object class="GtkHBox" id="hbox0">
- <property name="spacing">6</property>
- <child>
- <object class="GtkEntry" id="almanah_sd_search_entry">
- <property name="activates-default">True</property>
- <accessibility>
- <relation target="almanah_sd_search_button" type="labelled-by"/>
- </accessibility>
- <child internal-child="accessible">
- <object class="AtkObject" id="a11y-almanah_sd_search_entry">
- <property name="AtkObject::accessible-name" translatable="yes">Search entry</property>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="GtkButton" id="almanah_sd_search_button">
- <property name="image">almanah_sd_search_button_image</property>
- <property name="label" translatable="yes">Search</property>
- <property name="can-default">True</property>
- <property name="has-default">True</property>
- <signal name="clicked" handler="sd_search_button_clicked_cb"/>
- <accessibility>
- <relation target="almanah_sd_search_entry" type="label-for"/>
- </accessibility>
+ <object class="GtkEntry" id="almanah_sd_search_entry">
+ <property name="activates-default">True</property>
+ <property name="visible">True</property>
+ <accessibility>
+ <relation target="almanah_sd_search_button" type="labelled-by"/>
+ </accessibility>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="a11y-almanah_sd_search_entry">
+ <property name="AtkObject::accessible-name" translatable="yes">Search entry</property>
</object>
- <packing>
- <property name="expand">False</property>
- </packing>
</child>
</object>
</child>
+ <child>
+ <object class="GtkButton" id="almanah_sd_search_button">
+ <property name="image">almanah_sd_search_button_image</property>
+ <property name="label" translatable="yes">Search</property>
+ <property name="can-default">True</property>
+ <property name="has-default">True</property>
+ <property name="visible">True</property>
+ <signal name="clicked" handler="sd_search_button_clicked_cb"/>
+ <accessibility>
+ <relation target="almanah_sd_search_entry" type="label-for"/>
+ </accessibility>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="almanah_sd_cancel_button">
+ <property name="use-stock">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="can-default">True</property>
+ <property name="has-default">True</property>
+ <property name="sensitive">False</property>
+ <property name="visible">True</property>
+ <signal name="clicked" handler="sd_cancel_button_clicked_cb"/>
+ <accessibility>
+ <relation target="almanah_sd_search_entry" type="label-for"/>
+ </accessibility>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
</object>
- <packing>
- <property name="expand">False</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="almanah_sd_results_label">
- <property name="label" translatable="yes">Results:</property>
- <property name="xalign">0</property>
- <accessibility>
- <relation target="almanah_sd_results_tree_view" type="label-for"/>
- </accessibility>
- </object>
- <packing>
- <property name="expand">False</property>
- </packing>
</child>
<child>
- <object class="GtkScrolledWindow" id="scrolledwindow2">
- <property name="hscrollbar-policy">GTK_POLICY_AUTOMATIC</property>
- <property name="vscrollbar-policy">GTK_POLICY_AUTOMATIC</property>
- <property name="shadow-type">GTK_SHADOW_IN</property>
+ <object class="GtkAlignment" id="almanah_sd_results_alignment">
+ <property name="top-padding">12</property>
+ <property name="visible">False</property>
<child>
- <object class="GtkTreeView" id="almanah_sd_results_tree_view">
- <property name="model">almanah_sd_results_store</property>
- <property name="headers-visible">False</property>
- <signal name="row-activated" handler="sd_results_tree_view_row_activated_cb"/>
- <accessibility>
- <relation target="almanah_sd_results_label" type="labelled-by"/>
- </accessibility>
- <child internal-child="accessible">
- <object class="AtkObject" id="a11y-almanah_sd_results_tree_view">
- <property name="AtkObject::accessible-name" translatable="yes">Result List</property>
- </object>
- </child>
+ <object class="GtkVBox" id="almanah_sd_results_vbox">
+ <property name="spacing">6</property>
+ <property name="visible">True</property>
<child>
- <object class="GtkTreeViewColumn" id="almanah_sd_results_column_icon">
+ <object class="GtkHBox" id="almanah_sd_results_hbox">
+ <property name="spacing">6</property>
+ <property name="visible">True</property>
<child>
- <object class="GtkCellRendererPixbuf" id="almanah_sd_results_column_icon_renderer"/>
- <attributes>
- <attribute name="icon-name">4</attribute>
- </attributes>
+ <object class="GtkLabel" id="almanah_sd_results_label">
+ <property name="label"></property>
+ <property name="xalign">0</property>
+ <property name="visible">True</property>
+ <accessibility>
+ <relation target="almanah_sd_results_tree_view" type="label-for"/>
+ </accessibility>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinner" id="almanah_sd_search_spinner">
+ <property name="can_focus">False</property>
+ <property name="visible">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
</child>
</object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
</child>
<child>
- <object class="GtkTreeViewColumn" id="almanah_sd_results_column_date">
+ <object class="GtkScrolledWindow" id="almanah_sd_results_scrolled">
+ <property name="hscrollbar-policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar-policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow-type">GTK_SHADOW_IN</property>
+ <property name="visible">True</property>
<child>
- <object class="GtkCellRendererText" id="almanah_sd_results_column_date_renderer"/>
- <attributes>
- <attribute name="text">3</attribute>
- </attributes>
+ <object class="GtkTreeView" id="almanah_sd_results_tree_view">
+ <property name="model">almanah_sd_results_store</property>
+ <property name="headers-visible">False</property>
+ <property name="visible">True</property>
+ <signal name="row-activated" handler="sd_results_tree_view_row_activated_cb"/>
+ <accessibility>
+ <relation target="almanah_sd_results_label" type="labelled-by"/>
+ </accessibility>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="a11y-almanah_sd_results_tree_view">
+ <property name="AtkObject::accessible-name" translatable="yes">Result List</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="almanah_sd_results_column_icon">
+ <child>
+ <object class="GtkCellRendererPixbuf" id="almanah_sd_results_column_icon_renderer"/>
+ <attributes>
+ <attribute name="icon-name">4</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="almanah_sd_results_column_date">
+ <child>
+ <object class="GtkCellRendererText" id="almanah_sd_results_column_date_renderer"/>
+ <attributes>
+ <attribute name="text">3</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
</child>
</object>
+ <packing>
+ <property name="expand">True</property>
+ </packing>
</child>
</object>
</child>
</object>
+ <packing>
+ <property name="expand">True</property>
+ </packing>
</child>
</object>
</child>
<child internal-child="action_area">
<object class="GtkHButtonBox" id="hbuttonbox2">
+ <property name="visible">True</property>
<child>
<object class="GtkButton" id="almanah_sd_view_button">
<property name="label" translatable="yes">View Entry</property>
<property name="sensitive">False</property>
+ <property name="visible">True</property>
<signal name="clicked" handler="sd_view_button_clicked_cb"/>
</object>
</child>
@@ -594,6 +650,7 @@
<object class="GtkButton" id="almanah_sd_close_button">
<property name="use-stock">True</property>
<property name="label">gtk-close</property>
+ <property name="visible">True</property>
</object>
</child>
</object>
diff --git a/src/main-window.c b/src/main-window.c
index 0ccd056..fc088da 100644
--- a/src/main-window.c
+++ b/src/main-window.c
@@ -945,7 +945,7 @@ mw_search_activate_cb (GtkAction *action, AlmanahMainWindow *main_window)
gtk_window_set_application (GTK_WINDOW (dialog), gtk_window_get_application (GTK_WINDOW (main_window)));
gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (main_window));
- gtk_widget_show_all (GTK_WIDGET (dialog));
+ gtk_widget_show (GTK_WIDGET (dialog));
gtk_dialog_run (GTK_DIALOG (dialog));
gtk_widget_destroy (GTK_WIDGET (dialog));
diff --git a/src/search-dialog.c b/src/search-dialog.c
index 002b0b2..ba2b881 100644
--- a/src/search-dialog.c
+++ b/src/search-dialog.c
@@ -31,13 +31,23 @@ static void sd_results_selection_changed_cb (GtkTreeSelection *tree_selection, G
/* GtkBuilder callbacks */
void sd_search_button_clicked_cb (GtkButton *self, AlmanahSearchDialog *search_dialog);
+void sd_cancel_button_clicked_cb (GtkButton *self, AlmanahSearchDialog *search_dialog);
void sd_results_tree_view_row_activated_cb (GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, AlmanahSearchDialog *self);
void sd_view_button_clicked_cb (GtkButton *self, AlmanahSearchDialog *search_dialog);
+static void sd_search_progress_cb (AlmanahStorageManager *storage_manager, AlmanahEntry *entry, AlmanahSearchDialog **search_dialog_weak_pointer);
+static void sd_search_ready_cb (AlmanahStorageManager *storage_manager, GAsyncResult *res, AlmanahSearchDialog **search_dialog_weak_pointer);
+
struct _AlmanahSearchDialogPrivate {
GtkEntry *sd_search_entry;
+ GtkWidget *sd_search_button;
+ GtkWidget *sd_cancel_button;
+ GtkSpinner *sd_search_spinner;
+ GtkLabel *sd_results_label;
+ GtkWidget *sd_results_alignment;
GtkListStore *sd_results_store;
GtkTreeSelection *sd_results_selection;
+ GCancellable *sd_cancellable;
};
G_DEFINE_TYPE (AlmanahSearchDialog, almanah_search_dialog, GTK_TYPE_DIALOG)
@@ -70,6 +80,7 @@ almanah_search_dialog_new (void)
const gchar *object_names[] = {
"almanah_search_dialog",
"almanah_sd_search_button_image",
+ "almanah_sd_cancel_button_image",
"almanah_sd_results_store",
NULL
};
@@ -104,15 +115,22 @@ almanah_search_dialog_new (void)
priv = ALMANAH_SEARCH_DIALOG (search_dialog)->priv;
+ priv->sd_cancellable = NULL;
+
/* Grab our child widgets */
priv->sd_search_entry = GTK_ENTRY (gtk_builder_get_object (builder, "almanah_sd_search_entry"));
priv->sd_results_store = GTK_LIST_STORE (gtk_builder_get_object (builder, "almanah_sd_results_store"));
priv->sd_results_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (gtk_builder_get_object (builder, "almanah_sd_results_tree_view")));
+ priv->sd_results_alignment = GTK_WIDGET (gtk_builder_get_object (builder, "almanah_sd_results_alignment"));
+ priv->sd_search_spinner = GTK_SPINNER (gtk_builder_get_object (builder, "almanah_sd_search_spinner"));
+ priv->sd_results_label = GTK_LABEL (gtk_builder_get_object (builder, "almanah_sd_results_label"));
+ priv->sd_search_button = GTK_WIDGET (gtk_builder_get_object (builder, "almanah_sd_search_button"));
+ priv->sd_cancel_button = GTK_WIDGET (gtk_builder_get_object (builder, "almanah_sd_cancel_button"));
g_signal_connect (priv->sd_results_selection, "changed", G_CALLBACK (sd_results_selection_changed_cb),
gtk_builder_get_object (builder, "almanah_sd_view_button"));
- gtk_widget_grab_default (GTK_WIDGET (gtk_builder_get_object (builder, "almanah_sd_search_button")));
+ gtk_widget_grab_default (priv->sd_search_button);
g_object_unref (builder);
@@ -129,52 +147,157 @@ static void
sd_response_cb (GtkDialog *dialog, gint response_id, AlmanahSearchDialog *search_dialog)
{
/* Ensure everything's tidy before we leave the room */
+ if (search_dialog->priv->sd_cancellable != NULL) {
+ g_cancellable_cancel (search_dialog->priv->sd_cancellable);
+ }
+
gtk_list_store_clear (search_dialog->priv->sd_results_store);
gtk_entry_set_text (search_dialog->priv->sd_search_entry, "");
gtk_widget_hide (GTK_WIDGET (dialog));
}
+static void
+sd_search_progress_cb (AlmanahStorageManager *storage_manager, AlmanahEntry *entry, AlmanahSearchDialog **search_dialog_weak_pointer)
+{
+ AlmanahSearchDialogPrivate *priv;
+ GDate date;
+ gchar formatted_date[100];
+ GtkTreeIter tree_iter;
+
+ if (*search_dialog_weak_pointer == NULL) {
+ /* The dialogue's been finalised already, so we'd better just return */
+ return;
+ }
+
+ g_return_if_fail (ALMANAH_IS_STORAGE_MANAGER (storage_manager));
+ g_return_if_fail (ALMANAH_IS_ENTRY (entry));
+ g_return_if_fail (ALMANAH_IS_SEARCH_DIALOG (*search_dialog_weak_pointer));
+
+ priv = ALMANAH_SEARCH_DIALOG (*search_dialog_weak_pointer)->priv;
+
+ almanah_entry_get_date (entry, &date);
+ /* Translators: This is a strftime()-format string for the dates displayed in search results. */
+ g_date_strftime (formatted_date, sizeof (formatted_date), _("%A, %e %B %Y"), &date);
+
+ gtk_list_store_append (priv->sd_results_store, &tree_iter);
+ gtk_list_store_set (priv->sd_results_store, &tree_iter,
+ 0, g_date_get_day (&date),
+ 1, g_date_get_month (&date),
+ 2, g_date_get_year (&date),
+ 3, &formatted_date,
+ 4, (almanah_entry_is_important (entry) == TRUE) ? "emblem-important" : NULL,
+ -1);
+}
+
+static void
+sd_search_ready_cb (AlmanahStorageManager *storage_manager, GAsyncResult *res, AlmanahSearchDialog **search_dialog_weak_pointer)
+{
+ AlmanahSearchDialogPrivate *priv;
+ AlmanahSearchDialog *search_dialog;
+ gint counter;
+ GError *error = NULL;
+
+ /* Finish the operation */
+ counter = almanah_storage_manager_search_entries_async_finish (storage_manager, res, &error);
+
+ if (*search_dialog_weak_pointer == NULL) {
+ /* The dialogue's been finalised already, so we'd better just return */
+ g_clear_error (&error);
+ return;
+ }
+
+ g_return_if_fail (ALMANAH_IS_SEARCH_DIALOG (*search_dialog_weak_pointer));
+
+ search_dialog = ALMANAH_SEARCH_DIALOG (*search_dialog_weak_pointer);
+ priv = search_dialog->priv;
+
+ /* Return the search result count to the user */
+ gtk_spinner_stop (priv->sd_search_spinner);
+ gtk_widget_hide (GTK_WIDGET (priv->sd_search_spinner));
+ gtk_widget_set_sensitive (priv->sd_cancel_button, FALSE);
+ gtk_widget_set_sensitive (priv->sd_search_button, TRUE);
+
+ if (error != NULL && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) == TRUE) {
+ gtk_label_set_text (priv->sd_results_label, _("Search canceled."));
+ } else if (error != NULL) {
+ /* Translators: This is an error message wrapper for when searches encounter an error. The placeholder is for an error message. */
+ gchar *error_message = g_strdup_printf (_("Error: %s"), error->message);
+ gtk_label_set_text (priv->sd_results_label, error_message);
+ g_free (error_message);
+ } else {
+ /* Success! */
+ gchar *results_string = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, "Found %d entry:", "Found %d entries:", counter), counter);
+ gtk_label_set_text (priv->sd_results_label, results_string);
+ g_free (results_string);
+ }
+
+ g_clear_error (&error);
+
+ /* Tidy up */
+ g_object_remove_weak_pointer (G_OBJECT (search_dialog), (gpointer*) search_dialog_weak_pointer);
+ g_slice_free (AlmanahSearchDialog*, search_dialog_weak_pointer);
+
+ g_object_unref (priv->sd_cancellable);
+ search_dialog->priv->sd_cancellable = NULL;
+}
+
+void
+sd_cancel_button_clicked_cb (GtkButton *self, AlmanahSearchDialog *search_dialog)
+{
+ AlmanahSearchDialogPrivate *priv = search_dialog->priv;
+
+ if (priv->sd_cancellable != NULL) {
+ g_cancellable_cancel (priv->sd_cancellable);
+ }
+}
+
void
sd_search_button_clicked_cb (GtkButton *self, AlmanahSearchDialog *search_dialog)
{
AlmanahApplication *application;
AlmanahStorageManager *storage_manager;
- AlmanahEntry *entry;
- AlmanahStorageManagerIter iter;
AlmanahSearchDialogPrivate *priv = search_dialog->priv;
- const gchar *search_string = gtk_entry_get_text (priv->sd_search_entry);
+ const gchar *search_string;
+ AlmanahSearchDialog **search_dialog_weak_pointer;
+
+ if (priv->sd_cancellable != NULL) {
+ // Already searching
+ g_assert_not_reached ();
+ return;
+ }
/* Clear the results store of previous search results first */
- gtk_list_store_clear (search_dialog->priv->sd_results_store);
+ gtk_list_store_clear (priv->sd_results_store);
+
+ priv->sd_cancellable = g_cancellable_new ();
+
+ search_string = gtk_entry_get_text (priv->sd_search_entry);
+
+ /* Change UI to show the status */
+ gtk_widget_show (GTK_WIDGET (priv->sd_results_alignment));
+ gtk_label_set_text (priv->sd_results_label, _("Searchingâ"));
+ gtk_widget_show (GTK_WIDGET (priv->sd_search_spinner));
+ gtk_spinner_start (priv->sd_search_spinner);
/* Grab the storage manager */
application = ALMANAH_APPLICATION (gtk_window_get_application (GTK_WINDOW (search_dialog)));
storage_manager = almanah_application_dup_storage_manager (application);
- /* Search over all entries */
- almanah_storage_manager_iter_init (&iter);
- while ((entry = almanah_storage_manager_search_entries (storage_manager, search_string, &iter)) != NULL) {
- GDate date;
- gchar formatted_date[100];
- GtkTreeIter tree_iter;
-
- almanah_entry_get_date (entry, &date);
+ /* Launch the search */
+ search_dialog_weak_pointer = g_slice_new (AlmanahSearchDialog*);
+ *search_dialog_weak_pointer = search_dialog;
+ g_object_add_weak_pointer (G_OBJECT (search_dialog), (gpointer*) search_dialog_weak_pointer);
- /* Translators: This is a strftime()-format string for the dates displayed in search results. */
- g_date_strftime (formatted_date, sizeof (formatted_date), _("%A, %e %B %Y"), &date);
+ almanah_storage_manager_search_entries_async (storage_manager, search_string, priv->sd_cancellable,
+ (AlmanahStorageManagerSearchCallback) sd_search_progress_cb,
+ (gpointer) search_dialog_weak_pointer, NULL,
+ (GAsyncReadyCallback) sd_search_ready_cb, (gpointer) search_dialog_weak_pointer);
- gtk_list_store_append (priv->sd_results_store, &tree_iter);
- gtk_list_store_set (priv->sd_results_store, &tree_iter,
- 0, g_date_get_day (&date),
- 1, g_date_get_month (&date),
- 2, g_date_get_year (&date),
- 3, &formatted_date,
- 4, (almanah_entry_is_important (entry) == TRUE) ? "emblem-important" : NULL,
- -1);
-
- g_object_unref (entry);
- }
+ /* Allow the user to cancel the search */
+ gtk_widget_set_sensitive (priv->sd_search_button, FALSE);
+ gtk_widget_set_sensitive (priv->sd_cancel_button, TRUE);
+ gtk_widget_grab_default (priv->sd_cancel_button);
g_object_unref (storage_manager);
}
diff --git a/src/storage-manager.c b/src/storage-manager.c
index 5cb3407..5093670 100644
--- a/src/storage-manager.c
+++ b/src/storage-manager.c
@@ -992,6 +992,168 @@ almanah_storage_manager_search_entries (AlmanahStorageManager *self, const gchar
g_assert_not_reached ();
}
+typedef struct {
+ gchar *search_string;
+ AlmanahStorageManagerSearchCallback progress_callback;
+ gpointer progress_user_data;
+ GDestroyNotify progress_user_data_destroy;
+ guint count;
+} SearchAsyncData;
+
+static void
+search_async_data_free (SearchAsyncData *data)
+{
+ g_free (data->search_string);
+
+ g_slice_free (SearchAsyncData, data);
+}
+
+typedef struct {
+ AlmanahStorageManagerSearchCallback callback;
+ AlmanahStorageManager *storage_manager;
+ AlmanahEntry *entry;
+ gpointer user_data;
+} ProgressCallbackData;
+
+static void
+progress_callback_data_free (ProgressCallbackData *data)
+{
+ g_object_unref (data->entry);
+ g_object_unref (data->storage_manager);
+
+ g_slice_free (ProgressCallbackData, data);
+}
+
+static gboolean
+search_entry_async_progress_cb (ProgressCallbackData *data)
+{
+ data->callback (data->storage_manager, data->entry, data->user_data);
+
+ return FALSE;
+}
+
+static void
+search_entries_async_thread (GSimpleAsyncResult *result, AlmanahStorageManager *storage_manager, GCancellable *cancellable)
+{
+ AlmanahStorageManagerIter iter;
+ AlmanahEntry *entry;
+ SearchAsyncData *search_data;
+ ProgressCallbackData *progress_data;
+ GError *error = NULL;
+
+ search_data = g_simple_async_result_get_op_res_gpointer (result);
+
+ almanah_storage_manager_iter_init (&iter);
+ while ((entry = almanah_storage_manager_search_entries (storage_manager, search_data->search_string, &iter)) != NULL) {
+ /* Don't do any unnecessary work */
+ if (cancellable != NULL && g_cancellable_set_error_if_cancelled (cancellable, &error)) {
+ g_simple_async_result_set_from_error (result, error);
+ g_error_free (error);
+ return;
+ }
+
+ search_data->count++;
+
+ /* Queue a progress callback for the result */
+ progress_data = g_slice_new (ProgressCallbackData);
+ progress_data->callback = search_data->progress_callback;
+ progress_data->storage_manager = g_object_ref (storage_manager);
+ progress_data->entry = g_object_ref (entry);
+ progress_data->user_data = search_data->progress_user_data;
+
+ g_idle_add_full (G_PRIORITY_HIGH_IDLE, (GSourceFunc) search_entry_async_progress_cb,
+ progress_data, (GDestroyNotify) progress_callback_data_free);
+ }
+}
+
+/**
+ * almanah_storage_manager_search_entries_async_finish:
+ * @self: an #AlmanahStorageManager
+ * @result: a #GSimpleAsyncResult
+ * @error: a #GError or %NULL
+ *
+ * Finish an asynchronous search started with almanah_storage_manager_search_entries_async().
+ *
+ * Return value: the number of entries which matched the search string, or <code class="literal">-1</code> on error
+ */
+gint
+almanah_storage_manager_search_entries_async_finish (AlmanahStorageManager *self, GAsyncResult *result, GError **error)
+{
+ SearchAsyncData *search_data = NULL;
+ gint retval = -1;
+
+ g_return_val_if_fail (ALMANAH_IS_STORAGE_MANAGER (self), -1);
+ g_return_val_if_fail (G_IS_ASYNC_RESULT (result), -1);
+ g_return_val_if_fail (error == NULL || *error == NULL, -1);
+
+ if (g_simple_async_result_is_valid (result, G_OBJECT (self), almanah_storage_manager_search_entries_async) == FALSE) {
+ return -1;
+ }
+
+ /* Check for errors */
+ search_data = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
+
+ /* Extract the number of results */
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error) == FALSE) {
+ retval = search_data->count;
+ }
+
+ /* Notify of destruction of the user data. We have to do this here so that we can guarantee that all of the progress callbacks have
+ * been executed. */
+ if (search_data != NULL && search_data->progress_user_data_destroy != NULL) {
+ search_data->progress_user_data_destroy (search_data->progress_user_data);
+ }
+
+ return retval;
+}
+
+/**
+ * almanah_storage_manager_search_entries_async:
+ * @self: an #AlmanahStorageManager
+ * @search_string: the string of search terms being queried against
+ * @cancellable: (allow-none): a #GCancellable, or %NULL
+ * @progress_callback: (scope notified) (allow-none) (closure progress_user_data): a function to call for each result as it's found, or %NULL
+ * @progress_user_data: (closure): data to pass to @progress_callback
+ * @progress_user_data_destroy: (allow-none): a function to destroy the @progress_user_data when it will not be used any more, or %NULL
+ * @callback: a #GAsyncReadyCallback to call once the search is complete
+ * @user_data: the data to pass to @callback
+ *
+ * Launch an asynchronous search for @search_string in the content in entries in the database.
+ *
+ * When the @search_string was found in an entry, @progess_callback will be called with the #AlmanahEntry which was found.
+ *
+ * When the search finishes, @callback will be called, then you can call almanah_storage_manager_search_entries_async_finish() to get the number of
+ * entries found in total by the operation.
+ */
+void
+almanah_storage_manager_search_entries_async (AlmanahStorageManager *self, const gchar *search_string, GCancellable *cancellable,
+ AlmanahStorageManagerSearchCallback progress_callback, gpointer progress_user_data,
+ GDestroyNotify progress_user_data_destroy,
+ GAsyncReadyCallback callback, gpointer user_data)
+{
+ GSimpleAsyncResult *result;
+ SearchAsyncData *search_data;
+
+ g_return_if_fail (ALMANAH_IS_STORAGE_MANAGER (self));
+ g_return_if_fail (search_string != NULL);
+ g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+ g_return_if_fail (callback != NULL);
+
+ result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, almanah_storage_manager_search_entries_async);
+
+ search_data = g_slice_new (SearchAsyncData);
+ search_data->search_string = g_strdup (search_string);
+ search_data->progress_callback = progress_callback;
+ search_data->progress_user_data = progress_user_data;
+ search_data->progress_user_data_destroy = progress_user_data_destroy;
+ search_data->count = 0;
+
+ g_simple_async_result_set_op_res_gpointer (result, search_data, (GDestroyNotify) search_async_data_free);
+ g_simple_async_result_run_in_thread (result, (GSimpleAsyncThreadFunc) search_entries_async_thread, G_PRIORITY_DEFAULT, cancellable);
+
+ g_object_unref (result);
+}
+
/**
* almanah_storage_manager_get_entries:
* @self: an #AlmanahStorageManager
diff --git a/src/storage-manager.h b/src/storage-manager.h
index 7032e9d..74e956a 100644
--- a/src/storage-manager.h
+++ b/src/storage-manager.h
@@ -64,6 +64,8 @@ typedef struct {
gpointer user_data; /* to be used by #AlmanahStorageManager functions which need to associate data with a statement */
} AlmanahStorageManagerIter;
+typedef void (*AlmanahStorageManagerSearchCallback) (AlmanahStorageManager *storage_manager, AlmanahEntry *entry, gpointer user_data);
+
GType almanah_storage_manager_get_type (void);
GQuark almanah_storage_manager_error_quark (void);
AlmanahStorageManager *almanah_storage_manager_new (const gchar *filename, const gchar *encryption_key) G_GNUC_WARN_UNUSED_RESULT G_GNUC_MALLOC;
@@ -80,6 +82,11 @@ gboolean almanah_storage_manager_set_entry (AlmanahStorageManager *self, Almanah
void almanah_storage_manager_iter_init (AlmanahStorageManagerIter *iter);
AlmanahEntry *almanah_storage_manager_search_entries (AlmanahStorageManager *self, const gchar *search_string,
AlmanahStorageManagerIter *iter) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
+void almanah_storage_manager_search_entries_async (AlmanahStorageManager *self, const gchar *search_string, GCancellable *cancellable,
+ AlmanahStorageManagerSearchCallback progress_callback, gpointer progress_user_data,
+ GDestroyNotify progress_user_data_destroy,
+ GAsyncReadyCallback callback_ready, gpointer user_data);
+gint almanah_storage_manager_search_entries_async_finish (AlmanahStorageManager *self, GAsyncResult *result, GError **error);
AlmanahEntry *almanah_storage_manager_get_entries (AlmanahStorageManager *self,
AlmanahStorageManagerIter *iter) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]