[libgda] GdaBrowser: improved feedback and UI locking when connection is busy
- From: Vivien Malerba <vivien src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [libgda] GdaBrowser: improved feedback and UI locking when connection is busy
- Date: Mon, 19 Oct 2009 18:41:48 +0000 (UTC)
commit fdd10cde088989699ad9069589fbefa58d0cc605
Author: Vivien Malerba <malerba gnome-db org>
Date: Tue Oct 13 19:23:29 2009 +0200
GdaBrowser: improved feedback and UI locking when connection is busy
tools/browser/browser-connection.c | 153 +++++++++++++++++++++++++-----
tools/browser/browser-window.c | 15 ++--
tools/browser/query-exec/query-console.c | 33 ++++++-
3 files changed, 167 insertions(+), 34 deletions(-)
---
diff --git a/tools/browser/browser-connection.c b/tools/browser/browser-connection.c
index d07abb3..be196d1 100644
--- a/tools/browser/browser-connection.c
+++ b/tools/browser/browser-connection.c
@@ -30,10 +30,28 @@
/* code inclusion */
#include "../dict-file-name.c"
+typedef struct {
+ GObject *result;
+ GError *error;
+ GdaSet *last_inserted_row;
+} StatementResult;
+
+static void
+statement_result_free (StatementResult *res)
+{
+ if (res->result)
+ g_object_unref (res->result);
+ if (res->last_inserted_row)
+ g_object_unref (res->last_inserted_row);
+ g_clear_error (&(res->error));
+ g_free (res);
+}
+
struct _BrowserConnectionPrivate {
GdaThreadWrapper *wrapper;
GSList *wrapper_jobs;
guint wrapper_results_timer;
+ GHashTable *executed_statements; /* key = guint exec ID, value = a StatementResult pointer */
gchar *name;
GdaConnection *cnc;
@@ -49,6 +67,16 @@ struct _BrowserConnectionPrivate {
BrowserFavorites *bfav;
};
+/* signals */
+enum {
+ BUSY,
+ META_CHANGED,
+ FAV_CHANGED,
+ TRANSACTION_STATUS_CHANGED,
+ LAST_SIGNAL
+};
+
+gint browser_connection_signals [LAST_SIGNAL] = { 0, 0, 0, 0 };
/* wrapper jobs handling */
static gboolean check_for_wrapper_result (BrowserConnection *bcnc);
@@ -72,6 +100,9 @@ wrapper_job_free (WrapperJob *wj)
g_free (wj);
}
+/*
+ * Pushes a job which has been asked to be exected in a sub thread using gda_thread_wrapper_execute()
+ */
static void
push_wrapper_job (BrowserConnection *bcnc, guint job_id, JobType job_type, const gchar *reason)
{
@@ -89,6 +120,9 @@ push_wrapper_job (BrowserConnection *bcnc, guint job_id, JobType job_type, const
if (reason)
wj->reason = g_strdup (reason);
bcnc->priv->wrapper_jobs = g_slist_append (bcnc->priv->wrapper_jobs, wj);
+
+ if (! bcnc->priv->wrapper_jobs->next)
+ g_signal_emit (bcnc, browser_connection_signals [BUSY], 0, TRUE, wj->reason);
}
static void
@@ -96,6 +130,7 @@ pop_wrapper_job (BrowserConnection *bcnc, WrapperJob *wj)
{
bcnc->priv->wrapper_jobs = g_slist_remove (bcnc->priv->wrapper_jobs, wj);
wrapper_job_free (wj);
+ g_signal_emit (bcnc, browser_connection_signals [BUSY], 0, FALSE, NULL);
}
@@ -109,17 +144,6 @@ static void browser_connection_dispose (GObject *object);
/* get a pointer to the parents to be able to call their destructor */
static GObjectClass *parent_class = NULL;
-/* signals */
-enum {
- BUSY,
- META_CHANGED,
- FAV_CHANGED,
- TRANSACTION_STATUS_CHANGED,
- LAST_SIGNAL
-};
-
-gint browser_connection_signals [LAST_SIGNAL] = { 0, 0, 0, 0 };
-
GType
browser_connection_get_type (void)
{
@@ -203,6 +227,7 @@ browser_connection_init (BrowserConnection *bcnc)
bcnc->priv->wrapper = gda_thread_wrapper_new ();
bcnc->priv->wrapper_jobs = NULL;
bcnc->priv->wrapper_results_timer = 0;
+ bcnc->priv->executed_statements = NULL;
bcnc->priv->name = g_strdup_printf (_("c%u"), index++);
bcnc->priv->cnc = NULL;
@@ -257,6 +282,9 @@ browser_connection_dispose (GObject *object)
bcnc = BROWSER_CONNECTION (object);
if (bcnc->priv) {
+ if (bcnc->priv->executed_statements)
+ g_hash_table_destroy (bcnc->priv->executed_statements);
+
clear_dsn_info (bcnc);
g_free (bcnc->priv->dict_file_name);
@@ -391,10 +419,29 @@ check_for_wrapper_result (BrowserConnection *bcnc)
g_mutex_unlock (bcnc->priv->p_mstruct_mutex);
break;
}
- case JOB_TYPE_STATEMENT_EXECUTE:
- TO_IMPLEMENT;
+ case JOB_TYPE_STATEMENT_EXECUTE: {
+ guint *id;
+ StatementResult *res;
+
+ if (! bcnc->priv->executed_statements)
+ bcnc->priv->executed_statements = g_hash_table_new_full (g_int_hash, g_int_equal,
+ g_free,
+ (GDestroyNotify) statement_result_free);
+ id = g_new (guint, 1);
+ *id = wj->job_id;
+ res = g_new0 (StatementResult, 1);
+ if (exec_res == (gpointer) 0x01)
+ res->error = lerror;
+ else {
+ res->result = G_OBJECT (exec_res);
+ res->last_inserted_row = g_object_get_data (exec_res, "__bcnc_last_inserted_row");
+ if (res->last_inserted_row)
+ g_object_set_data (exec_res, "__bcnc_last_inserted_row", NULL);
+ }
+ g_hash_table_insert (bcnc->priv->executed_statements, id, res);
break;
}
+ }
pop_wrapper_job (bcnc, wj);
}
@@ -402,7 +449,6 @@ check_for_wrapper_result (BrowserConnection *bcnc)
out:
if (!bcnc->priv->wrapper_jobs) {
bcnc->priv->wrapper_results_timer = 0;
- g_signal_emit (bcnc, browser_connection_signals [BUSY], 0, FALSE, NULL);
return FALSE;
}
else {
@@ -463,7 +509,7 @@ browser_connection_new (GdaConnection *cnc)
if (update_store) {
GError *lerror = NULL;
guint job_id;
- /*g_print ("UPDATING meta store...\n");*/
+ g_print ("UPDATING meta store...\n");
job_id = gda_thread_wrapper_execute (bcnc->priv->wrapper,
(GdaThreadWrapperFunc) wrapper_meta_store_update,
g_object_ref (bcnc), g_object_unref, &lerror);
@@ -913,6 +959,29 @@ browser_connection_render_pretty_sql (BrowserConnection *bcnc, GdaStatement *stm
NULL, NULL);
}
+typedef struct {
+ GdaConnection *cnc;
+ GdaStatement *stmt;
+ GdaSet *params;
+ GdaStatementModelUsage model_usage;
+ gboolean need_last_insert_row;
+} StmtExecData;
+
+/* executed in sub @bcnc->priv->wrapper's thread */
+static gpointer
+wrapper_statement_execute (StmtExecData *data, GError **error)
+{
+ GObject *obj;
+ GdaSet *last_insert_row = NULL;
+ obj = gda_connection_statement_execute (data->cnc, data->stmt,
+ data->params, data->model_usage,
+ data->need_last_insert_row ? &last_insert_row : NULL,
+ error);
+ if (obj && last_insert_row)
+ g_object_set_data (obj, "__bcnc_last_inserted_row", last_insert_row);
+ return obj ? obj : (gpointer) 0x01;
+}
+
/**
* browser_connection_execute_statement
* @bcnc: a #BrowserConnection
@@ -939,10 +1008,24 @@ browser_connection_execute_statement (BrowserConnection *bcnc,
g_return_val_if_fail (GDA_IS_STATEMENT (stmt), 0);
g_return_val_if_fail (!params || GDA_IS_SET (params), 0);
- return gda_connection_async_statement_execute (bcnc->priv->cnc, stmt,
- params, model_usage,
- NULL, need_last_insert_row,
- error);
+ StmtExecData *data;
+ guint job_id;
+
+ data = g_new0 (StmtExecData, 1);
+ data->cnc = bcnc->priv->cnc;
+ data->stmt = stmt;
+ data->params = params;
+ data->model_usage = model_usage;
+ data->need_last_insert_row = need_last_insert_row;
+
+ job_id = gda_thread_wrapper_execute (bcnc->priv->wrapper,
+ (GdaThreadWrapperFunc) wrapper_statement_execute,
+ data, (GDestroyNotify) g_free, error);
+ if (job_id > 0)
+ push_wrapper_job (bcnc, job_id, JOB_TYPE_STATEMENT_EXECUTE,
+ _("Executing a query"));
+
+ return job_id;
}
/**
@@ -954,16 +1037,40 @@ browser_connection_execute_statement (BrowserConnection *bcnc,
*
* Pick up the result of the @exec_id's execution.
*
- * Retunrs: see gda_connection_async_fetch_result()
+ * Returns: the execution result, or %NULL if either an error occurred or the result is not yet ready
*/
GObject *
browser_connection_execution_get_result (BrowserConnection *bcnc, guint exec_id,
GdaSet **last_insert_row, GError **error)
{
+ StatementResult *res;
+ guint id;
+ GObject *retval;
+
g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
g_return_val_if_fail (exec_id > 0, NULL);
+
+ if (! bcnc->priv->executed_statements)
+ return NULL;
+
+ id = exec_id;
+ res = g_hash_table_lookup (bcnc->priv->executed_statements, &id);
+ if (!res)
+ return NULL;
+
+ retval = res->result;
+ res->result = NULL;
+
+ if (last_insert_row) {
+ *last_insert_row = res->last_inserted_row;
+ res->last_inserted_row = NULL;
+ }
- return gda_connection_async_fetch_result (bcnc->priv->cnc, exec_id,
- last_insert_row,
- error);
+ if (res->error) {
+ g_propagate_error (error, res->error);
+ res->error = NULL;
+ }
+
+ g_hash_table_remove (bcnc->priv->executed_statements, &id);
+ return retval;
}
diff --git a/tools/browser/browser-window.c b/tools/browser/browser-window.c
index ce1e83a..66dfe53 100644
--- a/tools/browser/browser-window.c
+++ b/tools/browser/browser-window.c
@@ -351,14 +351,6 @@ browser_window_new (BrowserConnection *bcnc, BrowserPerspectiveFactory *factory)
gtk_toolbar_insert (GTK_TOOLBAR (toolbar), ti, -1);
gtk_widget_show_all (GTK_WIDGET (ti));
bwin->priv->spinner = spinner;
- gchar *reason;
- if (browser_connection_is_busy (bwin->priv->bcnc, &reason)) {
- browser_spinner_start (BROWSER_SPINNER (spinner));
- gtk_widget_set_tooltip_text (bwin->priv->spinner, reason);
- g_free (reason);
- }
- g_signal_connect (bwin->priv->bcnc, "busy",
- G_CALLBACK (connection_busy_cb), bwin);
guint mid;
GSList *connections, *list;
@@ -451,6 +443,13 @@ browser_window_new (BrowserConnection *bcnc, BrowserPerspectiveFactory *factory)
gtk_widget_show (bwin->priv->statusbar);
bwin->priv->cnc_statusbar_context = gtk_statusbar_get_context_id (GTK_STATUSBAR (bwin->priv->statusbar),
"cncbusy");
+ gchar *reason = NULL;
+ if (browser_connection_is_busy (bcnc, &reason)) {
+ connection_busy_cb (bcnc, TRUE, reason, bwin);
+ g_free (reason);
+ }
+ g_signal_connect (bwin->priv->bcnc, "busy",
+ G_CALLBACK (connection_busy_cb), bwin);
gtk_widget_show (GTK_WIDGET (bwin));
diff --git a/tools/browser/query-exec/query-console.c b/tools/browser/query-exec/query-console.c
index 617f9a9..ddcf9c2 100644
--- a/tools/browser/query-exec/query-console.c
+++ b/tools/browser/query-exec/query-console.c
@@ -113,6 +113,8 @@ struct _QueryConsolePrivate {
GtkWidget *vpaned; /* top=>query editor, bottom=>results */
QueryEditor *editor;
+ GtkWidget *exec_button;
+ GtkWidget *indent_button;
guint params_compute_id; /* timout ID to compute params */
GdaSet *past_params; /* keeps values given to old params */
GdaSet *params; /* execution params */
@@ -198,7 +200,8 @@ query_console_init (QueryConsole *tconsole, QueryConsoleClass *klass)
tconsole->priv->params = NULL;
tconsole->priv->params_popup = NULL;
}
-
+static void connection_busy_cb (BrowserConnection *bcnc, gboolean is_busy,
+ gchar *reason, QueryConsole *tconsole);
static void
query_console_dispose (GObject *object)
{
@@ -210,8 +213,11 @@ query_console_dispose (GObject *object)
g_source_remove (tconsole->priv->current_exec_id);
if (tconsole->priv->current_exec)
execution_batch_free (tconsole->priv->current_exec);
- if (tconsole->priv->bcnc)
+ if (tconsole->priv->bcnc) {
+ g_signal_handlers_disconnect_by_func (tconsole->priv->bcnc,
+ G_CALLBACK (connection_busy_cb), tconsole);
g_object_unref (tconsole->priv->bcnc);
+ }
if (tconsole->priv->parser)
g_object_unref (tconsole->priv->parser);
if (tconsole->priv->past_params)
@@ -378,6 +384,7 @@ query_console_new (BrowserConnection *bcnc)
G_CALLBACK (sql_variables_clicked_cb), tconsole);
button = make_small_button (FALSE, _("Execute"), GTK_STOCK_EXECUTE, _("Execute SQL in editor"));
+ tconsole->priv->exec_button = button;
gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 0);
g_signal_connect (button, "clicked",
G_CALLBACK (sql_execute_clicked_cb), tconsole);
@@ -385,6 +392,7 @@ query_console_new (BrowserConnection *bcnc)
button = make_small_button (FALSE, _("Indent"), GTK_STOCK_INDENT, _("Indent SQL in editor\n"
"and make the code more readable\n"
"(removes comments)"));
+ tconsole->priv->indent_button = button;
gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 0);
g_signal_connect (button, "clicked",
G_CALLBACK (sql_indent_clicked_cb), tconsole);
@@ -454,9 +462,25 @@ query_console_new (BrowserConnection *bcnc)
gtk_widget_show_all (vpaned);
gtk_widget_hide (tconsole->priv->params_top);
+ /* busy connection handling */
+ gchar *reason = NULL;
+ if (browser_connection_is_busy (tconsole->priv->bcnc, &reason)) {
+ connection_busy_cb (tconsole->priv->bcnc, TRUE, reason, tconsole);
+ g_free (reason);
+ }
+ g_signal_connect (tconsole->priv->bcnc, "busy",
+ G_CALLBACK (connection_busy_cb), tconsole);
+
return (GtkWidget*) tconsole;
}
+static void
+connection_busy_cb (BrowserConnection *bcnc, gboolean is_busy, gchar *reason, QueryConsole *tconsole)
+{
+ gtk_widget_set_sensitive (tconsole->priv->exec_button, !is_busy);
+ gtk_widget_set_sensitive (tconsole->priv->indent_button, !is_busy);
+}
+
static GtkWidget *
make_small_button (gboolean is_toggle, const gchar *label, const gchar *stock_id, const gchar *tooltip)
{
@@ -666,7 +690,10 @@ editor_changed_cb (QueryEditor *editor, QueryConsole *tconsole)
static void
editor_execute_request_cb (QueryEditor *editor, QueryConsole *tconsole)
{
- sql_execute_clicked_cb (NULL, tconsole);
+ gboolean sensitive;
+ g_object_get (tconsole->priv->exec_button, "sensitive", &sensitive, NULL);
+ if (sensitive)
+ sql_execute_clicked_cb (NULL, tconsole);
}
static void
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]