sdgui: load dictionaries asynchronously

This is a must when loading huge dictionaries,
where not even parallelization helps much.
This commit is contained in:
Přemysl Eric Janouch 2022-09-04 13:16:40 +02:00
parent 7ef502759e
commit 27dcf87a64
Signed by: p
GPG Key ID: A0420B94F92B9493
2 changed files with 75 additions and 29 deletions

View File

@ -42,6 +42,8 @@ static struct
gint last; ///< The last dictionary index
GPtrArray *dictionaries; ///< All open dictionaries
gboolean loading; ///< Dictionaries are being loaded
gboolean watch_selection; ///< Following X11 PRIMARY?
}
g;
@ -49,19 +51,19 @@ g;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void
init (gchar **filenames)
load_from_filenames (GPtrArray *out, gchar **filenames)
{
for (gsize i = 0; filenames[i]; i++)
{
Dictionary *dict = g_malloc0 (sizeof *dict);
dict->filename = g_strdup (filenames[i]);
g_ptr_array_add (g.dictionaries, dict);
g_ptr_array_add (out, dict);
}
}
// TODO: try to deduplicate, similar to app_load_config_values()
static gboolean
init_from_key_file (GKeyFile *kf, GError **error)
load_from_key_file (GPtrArray *out, GKeyFile *kf, GError **error)
{
const gchar *dictionaries = "Dictionaries";
gchar **names = g_key_file_get_keys (kf, dictionaries, NULL, NULL);
@ -72,13 +74,13 @@ init_from_key_file (GKeyFile *kf, GError **error)
{
Dictionary *dict = g_malloc0 (sizeof *dict);
dict->name = names[i];
g_ptr_array_add (g.dictionaries, dict);
g_ptr_array_add (out, dict);
}
g_free (names);
for (gsize i = 0; i < g.dictionaries->len; i++)
for (gsize i = 0; i < out->len; i++)
{
Dictionary *dict = g_ptr_array_index (g.dictionaries, i);
Dictionary *dict = g_ptr_array_index (out, i);
gchar *path =
g_key_file_get_string (kf, dictionaries, dict->name, error);
if (!path)
@ -95,13 +97,13 @@ init_from_key_file (GKeyFile *kf, GError **error)
}
static gboolean
init_from_config (GError **error)
load_from_config (GPtrArray *out, GError **error)
{
GKeyFile *key_file = load_project_config_file (error);
if (!key_file)
return FALSE;
gboolean result = init_from_key_file (key_file, error);
gboolean result = load_from_key_file (out, key_file, error);
g_key_file_free (key_file);
return result;
}
@ -111,6 +113,8 @@ search (Dictionary *dict)
{
GtkEntryBuffer *buf = gtk_entry_get_buffer (GTK_ENTRY (g.entry));
const gchar *input_utf8 = gtk_entry_buffer_get_text (buf);
if (!dict->dict)
return;
StardictIterator *iterator =
stardict_dict_search (dict->dict, input_utf8, NULL);
@ -295,20 +299,68 @@ show_error_dialog (GError *error)
g_error_free (error);
}
static gboolean
reload_dictionaries (GPtrArray *new_dictionaries, GError **error)
static void
on_new_dictionaries_loaded (G_GNUC_UNUSED GObject* source_object,
GAsyncResult* res, G_GNUC_UNUSED gpointer user_data)
{
if (!load_dictionaries (new_dictionaries, error))
return FALSE;
g.loading = FALSE;
GError *error = NULL;
GPtrArray *new_dictionaries =
g_task_propagate_pointer (G_TASK (res), &error);
if (!new_dictionaries)
{
show_error_dialog (error);
return;
}
while (gtk_notebook_get_n_pages (GTK_NOTEBOOK (g.notebook)))
gtk_notebook_remove_page (GTK_NOTEBOOK (g.notebook), -1);
g.dictionary = -1;
if (g.dictionaries)
g_ptr_array_free (g.dictionaries, TRUE);
stardict_view_set_position (STARDICT_VIEW (g.view), NULL, 0);
g_ptr_array_free (g.dictionaries, TRUE);
g.dictionaries = new_dictionaries;
init_tabs ();
}
static void
on_reload_dictionaries_task (GTask *task, G_GNUC_UNUSED gpointer source_object,
gpointer task_data, G_GNUC_UNUSED GCancellable *cancellable)
{
GError *error = NULL;
if (load_dictionaries (task_data, &error))
{
g_task_return_pointer (task,
g_ptr_array_ref (task_data), (GDestroyNotify) g_ptr_array_unref);
}
else
g_task_return_error (task, error);
}
static gboolean
reload_dictionaries (GPtrArray *new_dictionaries, GError **error)
{
// TODO: We could cancel that task.
if (g.loading)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"already loading dictionaries");
return FALSE;
}
// TODO: Some other kind of indication.
// Note that "action widgets" aren't visible without GtkNotebook tabs.
g.loading = TRUE;
GTask *task = g_task_new (NULL, NULL, on_new_dictionaries_loaded, NULL);
g_task_set_name (task, __func__);
g_task_set_task_data (task,
new_dictionaries, (GDestroyNotify) g_ptr_array_unref);
g_task_run_in_thread (task, on_reload_dictionaries_task);
g_object_unref (task);
return TRUE;
}
@ -429,19 +481,19 @@ main (int argc, char *argv[])
gtk_window_set_default_icon_name (PROJECT_NAME);
g.dictionaries =
GPtrArray *new_dictionaries =
g_ptr_array_new_with_free_func ((GDestroyNotify) dictionary_destroy);
if (filenames)
init (filenames);
else if (!init_from_config (&error) && error)
{
load_from_filenames (new_dictionaries, filenames);
g_strfreev (filenames);
}
else if (!load_from_config (new_dictionaries, &error) && error)
die_with_dialog (error->message);
g_strfreev (filenames);
if (!g.dictionaries->len)
if (!new_dictionaries->len)
die_with_dialog (_("No dictionaries found either in "
"the configuration or on the command line"));
if (!load_dictionaries (g.dictionaries, &error))
die_with_dialog (error->message);
// Some Adwaita stupidity, plus defaults for our own widget.
// All the named colours have been there since GNOME 3.4
@ -530,7 +582,6 @@ main (int argc, char *argv[])
g.view = stardict_view_new ();
gtk_box_pack_end (GTK_BOX (superbox), g.view, TRUE, TRUE, 0);
init_tabs ();
GtkClipboard *clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
g_signal_connect (clipboard, "owner-change",
@ -544,6 +595,9 @@ main (int argc, char *argv[])
g_signal_connect (g.view, "send",
G_CALLBACK (on_send), NULL);
if (!reload_dictionaries (new_dictionaries, &error))
die_with_dialog (error->message);
gtk_widget_show_all (g.window);
gtk_main ();
return 0;

View File

@ -269,7 +269,6 @@ load_dictionaries_sequentially (GPtrArray *dictionaries, GError **e)
}
// Parallelize dictionary loading if possible, because of collation reindexing
#if GLIB_CHECK_VERSION (2, 36, 0)
static void
load_worker (gpointer data, gpointer user_data)
{
@ -304,10 +303,3 @@ load_dictionaries (GPtrArray *dictionaries, GError **e)
g_async_queue_unref (error_queue);
return result;
}
#else // GLib < 2.36
gboolean
load_dictionaries (GPtrArray *dictionaries, GError **e)
{
return load_dictionaries_sequentially (dictionaries, e);
}
#endif // GLib < 2.36