[evolution] Show ongoing progress of calendar views in UI
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution] Show ongoing progress of calendar views in UI
- Date: Wed, 15 Oct 2014 15:29:22 +0000 (UTC)
commit 98a1b231124db277c70d4389e5cf0fd917a0f381
Author: Milan Crha <mcrha redhat com>
Date: Wed Oct 15 14:13:05 2014 +0200
Show ongoing progress of calendar views in UI
This was a remaining thing from the 'Make calendar views non-UI-blocking'
work, to show progress of views in UI. This is currently done by a spinner
beside source's name in the ESourceSelector and a tooltip above that row.
calendar/gui/e-cal-data-model.c | 95 +++++++-
calendar/gui/e-cal-data-model.h | 15 +
e-util/e-source-selector.c | 399 ++++++++++++++++++++++++++-
e-util/e-source-selector.h | 14 +
e-util/test-source-selector.c | 73 +++++-
modules/calendar/e-cal-base-shell-content.c | 54 ++++-
po/POTFILES.in | 1 +
7 files changed, 647 insertions(+), 4 deletions(-)
---
diff --git a/calendar/gui/e-cal-data-model.c b/calendar/gui/e-cal-data-model.c
index f010540..740a2ce 100644
--- a/calendar/gui/e-cal-data-model.c
+++ b/calendar/gui/e-cal-data-model.c
@@ -59,6 +59,13 @@ enum {
PROP_TIMEZONE
};
+enum {
+ VIEW_STATE_CHANGED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
G_DEFINE_TYPE (ECalDataModel, e_cal_data_model, G_TYPE_OBJECT)
typedef struct _ComponentData {
@@ -299,6 +306,73 @@ subscriber_data_free (gpointer ptr)
}
}
+typedef struct _ViewStateChangedData {
+ ECalDataModel *data_model;
+ ECalClientView *view;
+ ECalDataModelViewState state;
+ guint percent;
+ gchar *message;
+ GError *error;
+} ViewStateChangedData;
+
+static void
+view_state_changed_data_free (gpointer ptr)
+{
+ ViewStateChangedData *vscd = ptr;
+
+ if (vscd) {
+ g_clear_object (&vscd->data_model);
+ g_clear_object (&vscd->view);
+ g_clear_error (&vscd->error);
+ g_free (vscd->message);
+ g_free (vscd);
+ }
+}
+
+static gboolean
+cal_data_model_emit_view_state_changed_timeout_cb (gpointer user_data)
+{
+ ViewStateChangedData *vscd = user_data;
+
+ g_return_val_if_fail (vscd != NULL, FALSE);
+ g_return_val_if_fail (E_IS_CAL_DATA_MODEL (vscd->data_model), FALSE);
+ g_return_val_if_fail (E_IS_CAL_CLIENT_VIEW (vscd->view), FALSE);
+
+ g_signal_emit (vscd->data_model, signals[VIEW_STATE_CHANGED], 0,
+ vscd->view, vscd->state, vscd->percent, vscd->message, vscd->error);
+
+ return FALSE;
+}
+
+static void
+cal_data_model_emit_view_state_changed (ECalDataModel *data_model,
+ ECalClientView *view,
+ ECalDataModelViewState state,
+ guint percent,
+ const gchar *message,
+ const GError *error)
+{
+ ViewStateChangedData *vscd;
+
+ g_return_if_fail (E_IS_CAL_DATA_MODEL (data_model));
+ g_return_if_fail (E_IS_CAL_CLIENT_VIEW (view));
+
+ if (e_cal_data_model_get_disposing (data_model))
+ return;
+
+ vscd = g_new0 (ViewStateChangedData, 1);
+ vscd->data_model = g_object_ref (data_model);
+ vscd->view = g_object_ref (view);
+ vscd->state = state;
+ vscd->percent = percent;
+ vscd->message = g_strdup (message);
+ vscd->error = error ? g_error_copy (error) : NULL;
+
+ g_timeout_add_full (G_PRIORITY_DEFAULT, 1,
+ cal_data_model_emit_view_state_changed_timeout_cb,
+ vscd, view_state_changed_data_free);
+}
+
typedef void (* InternalThreadJobFunc) (ECalDataModel *data_model, gpointer user_data);
typedef struct _InternalThreadJobData {
@@ -1261,6 +1335,8 @@ cal_data_model_view_progress (ECalClientView *view,
ECalDataModel *data_model)
{
g_return_if_fail (E_IS_CAL_DATA_MODEL (data_model));
+
+ cal_data_model_emit_view_state_changed (data_model, view, E_CAL_DATA_MODEL_VIEW_STATE_PROGRESS,
percent, message, NULL);
}
static void
@@ -1297,6 +1373,8 @@ cal_data_model_view_complete (ECalClientView *view,
view_data->lost_components = NULL;
}
+ cal_data_model_emit_view_state_changed (data_model, view, E_CAL_DATA_MODEL_VIEW_STATE_COMPLETE, 0,
NULL, error);
+
view_data_unlock (view_data);
view_data_unref (view_data);
}
@@ -1387,8 +1465,10 @@ cal_data_model_create_view_thread (EAlertSinkThreadJobData *job_data,
g_free (filter);
- if (!g_cancellable_is_cancelled (cancellable))
+ if (!g_cancellable_is_cancelled (cancellable)) {
+ cal_data_model_emit_view_state_changed (data_model, view, E_CAL_DATA_MODEL_VIEW_STATE_START,
0, NULL, NULL);
e_cal_client_view_start (view, error);
+ }
g_clear_object (&view);
}
@@ -1442,6 +1522,7 @@ cal_data_model_update_client_view (ECalDataModel *data_model,
if (view_data->view) {
view_data_disconnect_view (view_data);
+ cal_data_model_emit_view_state_changed (data_model, view_data->view,
E_CAL_DATA_MODEL_VIEW_STATE_STOP, 0, NULL, NULL);
g_clear_object (&view_data->view);
}
@@ -1556,6 +1637,9 @@ cal_data_model_remove_client_view (ECalDataModel *data_model,
cal_data_model_thaw_all_subscribers (data_model);
+ if (view_data->view)
+ cal_data_model_emit_view_state_changed (data_model, view_data->view,
E_CAL_DATA_MODEL_VIEW_STATE_STOP, 0, NULL, NULL);
+
view_data->is_used = FALSE;
view_data_unlock (view_data);
@@ -1818,6 +1902,15 @@ e_cal_data_model_class_init (ECalDataModelClass *class)
"Time Zone",
NULL,
G_PARAM_READWRITE));
+
+ signals[VIEW_STATE_CHANGED] = g_signal_new (
+ "view-state-changed",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ECalDataModelClass, view_state_changed),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 5, E_TYPE_CAL_CLIENT_VIEW, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING,
G_TYPE_ERROR);
}
static void
diff --git a/calendar/gui/e-cal-data-model.h b/calendar/gui/e-cal-data-model.h
index 8047644..c0696a3 100644
--- a/calendar/gui/e-cal-data-model.h
+++ b/calendar/gui/e-cal-data-model.h
@@ -45,6 +45,13 @@
G_BEGIN_DECLS
+typedef enum {
+ E_CAL_DATA_MODEL_VIEW_STATE_START,
+ E_CAL_DATA_MODEL_VIEW_STATE_PROGRESS,
+ E_CAL_DATA_MODEL_VIEW_STATE_COMPLETE,
+ E_CAL_DATA_MODEL_VIEW_STATE_STOP
+} ECalDataModelViewState;
+
typedef struct _ECalDataModel ECalDataModel;
typedef struct _ECalDataModelClass ECalDataModelClass;
typedef struct _ECalDataModelPrivate ECalDataModelPrivate;
@@ -56,6 +63,14 @@ struct _ECalDataModel {
struct _ECalDataModelClass {
GObjectClass parent_class;
+
+ /* Signals */
+ void (* view_state_changed) (ECalDataModel *data_model,
+ ECalClientView *view,
+ ECalDataModelViewState state,
+ guint percent,
+ const gchar *message,
+ const GError *error);
};
typedef GCancellable * (* ECalDataModelSubmitThreadJobFunc)
diff --git a/e-util/e-source-selector.c b/e-util/e-source-selector.c
index 527b576..c9bdabd 100644
--- a/e-util/e-source-selector.c
+++ b/e-util/e-source-selector.c
@@ -54,6 +54,10 @@ struct _ESourceSelectorPrivate {
gboolean show_colors;
gboolean show_icons;
gboolean show_toggles;
+
+ GtkCellRenderer *busy_renderer;
+ guint n_busy_sources;
+ gulong update_busy_renderer_id;
};
struct _AsyncContext {
@@ -91,6 +95,8 @@ enum {
COLUMN_SHOW_TOGGLE,
COLUMN_WEIGHT,
COLUMN_SOURCE,
+ COLUMN_TOOLTIP,
+ COLUMN_IS_BUSY,
NUM_COLUMNS
};
@@ -163,6 +169,80 @@ e_cell_renderer_safe_toggle_new (void)
return g_object_new (e_cell_renderer_safe_toggle_get_type (), NULL);
}
+static gboolean
+source_selector_pulse_busy_renderer_cb (gpointer user_data)
+{
+ ESourceSelector *selector = user_data;
+ GObject *busy_renderer;
+ guint pulse;
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), FALSE);
+
+ if (!selector->priv->busy_renderer)
+ return FALSE;
+
+ busy_renderer = G_OBJECT (selector->priv->busy_renderer);
+
+ g_object_get (busy_renderer, "pulse", &pulse, NULL);
+
+ pulse++;
+
+ g_object_set (busy_renderer, "pulse", pulse, NULL);
+
+ g_hash_table_iter_init (&iter, selector->priv->source_index);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ GtkTreeRowReference *reference = value;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter tree_iter;
+
+ if (reference && gtk_tree_row_reference_valid (reference)) {
+ gboolean is_busy = FALSE;
+
+ model = gtk_tree_row_reference_get_model (reference);
+ path = gtk_tree_row_reference_get_path (reference);
+ gtk_tree_model_get_iter (model, &tree_iter, path);
+
+ gtk_tree_model_get (
+ model, &tree_iter,
+ COLUMN_IS_BUSY, &is_busy,
+ -1);
+
+ if (is_busy)
+ gtk_tree_model_row_changed (model, path, &tree_iter);
+
+ gtk_tree_path_free (path);
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+source_selector_inc_busy_sources (ESourceSelector *selector)
+{
+ selector->priv->n_busy_sources++;
+
+ if (selector->priv->busy_renderer && !selector->priv->update_busy_renderer_id)
+ selector->priv->update_busy_renderer_id =
+ e_named_timeout_add (123, source_selector_pulse_busy_renderer_cb, selector);
+}
+
+static void
+source_selector_dec_busy_sources (ESourceSelector *selector)
+{
+ g_return_if_fail (selector->priv->n_busy_sources > 0);
+
+ selector->priv->n_busy_sources--;
+
+ if (selector->priv->n_busy_sources == 0 && selector->priv->update_busy_renderer_id) {
+ g_source_remove (selector->priv->update_busy_renderer_id);
+ selector->priv->update_busy_renderer_id = 0;
+ }
+}
+
static void
clear_saved_primary_selection (ESourceSelector *selector)
{
@@ -340,6 +420,107 @@ source_selector_save_expanded (GtkTreeView *tree_view,
g_queue_push_tail (queue, source);
}
+typedef struct _SavedStatus
+{
+ gboolean is_busy;
+ gchar *tooltip;
+} SavedStatusData;
+
+static void
+saved_status_data_free (gpointer ptr)
+{
+ SavedStatusData *data = ptr;
+
+ if (data) {
+ g_free (data->tooltip);
+ g_free (data);
+ }
+}
+
+static GHashTable *
+source_selector_save_sources_status (ESourceSelector *selector)
+{
+ GHashTable *status;
+ GHashTableIter iter;
+ gpointer key, value;
+
+ status = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, saved_status_data_free);
+
+ g_hash_table_iter_init (&iter, selector->priv->source_index);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ ESource *source = key;
+ GtkTreeRowReference *reference = value;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter tree_iter;
+
+ if (reference && gtk_tree_row_reference_valid (reference)) {
+ SavedStatusData *data;
+
+ model = gtk_tree_row_reference_get_model (reference);
+ path = gtk_tree_row_reference_get_path (reference);
+ gtk_tree_model_get_iter (model, &tree_iter, path);
+
+ data = g_new0 (SavedStatusData, 1);
+
+ gtk_tree_model_get (
+ model, &tree_iter,
+ COLUMN_IS_BUSY, &data->is_busy,
+ COLUMN_TOOLTIP, &data->tooltip,
+ -1);
+
+ if (data->is_busy)
+ source_selector_dec_busy_sources (selector);
+
+ gtk_tree_path_free (path);
+
+ g_hash_table_insert (status, g_strdup (e_source_get_uid (source)), data);
+ }
+ }
+
+ return status;
+}
+
+static void
+source_selector_load_sources_status (ESourceSelector *selector,
+ GHashTable *status)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_hash_table_iter_init (&iter, selector->priv->source_index);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ ESource *source = key;
+ GtkTreeRowReference *reference = value;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter tree_iter;
+
+ if (reference && gtk_tree_row_reference_valid (reference)) {
+ SavedStatusData *data;
+
+ model = gtk_tree_row_reference_get_model (reference);
+ path = gtk_tree_row_reference_get_path (reference);
+ gtk_tree_model_get_iter (model, &tree_iter, path);
+
+ gtk_tree_path_free (path);
+
+ data = g_hash_table_lookup (status, e_source_get_uid (source));
+ if (!data)
+ continue;
+
+ gtk_tree_store_set (
+ GTK_TREE_STORE (model), &tree_iter,
+ COLUMN_IS_BUSY, data->is_busy,
+ COLUMN_TOOLTIP, data->tooltip,
+ -1);
+
+ if (data->is_busy)
+ source_selector_inc_busy_sources (selector);
+ }
+ }
+}
+
static void
source_selector_build_model (ESourceSelector *selector)
{
@@ -349,6 +530,7 @@ source_selector_build_model (ESourceSelector *selector)
GtkTreeView *tree_view;
GtkTreeSelection *selection;
GtkTreeModel *model;
+ GHashTable *saved_status;
ESource *selected;
const gchar *extension_name;
GNode *root;
@@ -366,6 +548,7 @@ source_selector_build_model (ESourceSelector *selector)
source_index = selector->priv->source_index;
selected = e_source_selector_ref_primary_selection (selector);
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (selector));
+ saved_status = source_selector_save_sources_status (selector);
/* Signal is blocked to avoid "primary-selection-changed" signal
* on model clear. */
@@ -434,6 +617,9 @@ source_selector_build_model (ESourceSelector *selector)
e_source_selector_set_primary_selection (selector, selected);
g_object_unref (selected);
}
+
+ source_selector_load_sources_status (selector, saved_status);
+ g_hash_table_destroy (saved_status);
}
static void
@@ -518,6 +704,9 @@ source_selector_source_removed_cb (ESourceRegistry *registry,
if (!e_source_has_extension (source, extension_name))
return;
+ if (e_source_selector_get_source_is_busy (selector, source))
+ source_selector_dec_busy_sources (selector);
+
if (e_source_selector_source_is_selected (selector, source))
g_signal_emit (selector, signals[SOURCE_UNSELECTED], 0, source);
@@ -562,6 +751,9 @@ source_selector_source_disabled_cb (ESourceRegistry *registry,
if (!e_source_has_extension (source, extension_name))
return;
+ if (e_source_selector_get_source_is_busy (selector, source))
+ source_selector_dec_busy_sources (selector);
+
if (e_source_selector_source_is_selected (selector, source))
g_signal_emit (selector, signals[SOURCE_UNSELECTED], 0, source);
@@ -827,6 +1019,11 @@ source_selector_dispose (GObject *object)
priv = E_SOURCE_SELECTOR_GET_PRIVATE (object);
+ if (priv->update_busy_renderer_id) {
+ g_source_remove (priv->update_busy_renderer_id);
+ priv->update_busy_renderer_id = 0;
+ }
+
if (priv->source_added_handler_id > 0) {
g_signal_handler_disconnect (
priv->registry,
@@ -863,6 +1060,7 @@ source_selector_dispose (GObject *object)
}
g_clear_object (&priv->registry);
+ g_clear_object (&priv->busy_renderer);
g_hash_table_remove_all (priv->source_index);
g_hash_table_remove_all (priv->pending_writes);
@@ -1516,7 +1714,9 @@ e_source_selector_init (ESourceSelector *selector)
G_TYPE_BOOLEAN, /* COLUMN_SHOW_ICON */
G_TYPE_BOOLEAN, /* COLUMN_SHOW_TOGGLE */
G_TYPE_INT, /* COLUMN_WEIGHT */
- E_TYPE_SOURCE); /* COLUMN_SOURCE */
+ E_TYPE_SOURCE, /* COLUMN_SOURCE */
+ G_TYPE_STRING, /* COLUMN_TOOLTIP */
+ G_TYPE_BOOLEAN); /* COLUMN_IS_BUSY */
gtk_tree_view_set_model (tree_view, GTK_TREE_MODEL (tree_store));
@@ -1568,6 +1768,15 @@ e_source_selector_init (ESourceSelector *selector)
"weight", COLUMN_WEIGHT,
NULL);
+ renderer = gtk_cell_renderer_spinner_new ();
+ selector->priv->busy_renderer = g_object_ref (renderer);
+ gtk_tree_view_column_pack_end (column, renderer, FALSE);
+ gtk_tree_view_column_set_attributes (
+ column, renderer,
+ "visible", COLUMN_IS_BUSY,
+ "active", COLUMN_IS_BUSY,
+ NULL);
+
selection = gtk_tree_view_get_selection (tree_view);
gtk_tree_selection_set_select_function (
selection, (GtkTreeSelectionFunc)
@@ -1578,6 +1787,8 @@ e_source_selector_init (ESourceSelector *selector)
G_OBJECT (selector), 0);
gtk_tree_view_set_headers_visible (tree_view, FALSE);
+ gtk_tree_view_set_tooltip_column (tree_view, COLUMN_TOOLTIP);
+ gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), TRUE);
}
/**
@@ -2437,3 +2648,189 @@ e_source_selector_update_all_rows (ESourceSelector *selector)
g_list_free_full (list, (GDestroyNotify) g_object_unref);
}
+/**
+ * e_source_selector_set_source_tooltip:
+ * @selector: an #ESourceSelector
+ * @source: an #ESource for which to set the tooltip
+ *
+ * Updates tooltip for the given @source.
+ *
+ * Since: 3.14
+ **/
+void
+e_source_selector_set_source_tooltip (ESourceSelector *selector,
+ ESource *source,
+ const gchar *tooltip)
+{
+ GtkTreeRowReference *reference;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+
+ g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
+ g_return_if_fail (E_IS_SOURCE (source));
+
+ reference = g_hash_table_lookup (selector->priv->source_index, source);
+
+ /* If the ESource is not in our tree model then return silently. */
+ if (reference == NULL)
+ return;
+
+ /* If we do have a row reference, it should be valid. */
+ g_return_if_fail (gtk_tree_row_reference_valid (reference));
+
+ model = gtk_tree_row_reference_get_model (reference);
+ path = gtk_tree_row_reference_get_path (reference);
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_path_free (path);
+
+ gtk_tree_store_set (
+ GTK_TREE_STORE (model), &iter,
+ COLUMN_TOOLTIP, tooltip && *tooltip ? tooltip : NULL,
+ -1);
+}
+
+/**
+ * e_source_selector_dup_source_tooltip:
+ * @selector: an #ESourceSelector
+ * @source: an #ESource for which to read the tooltip
+ *
+ * Returns: Current tooltip for the given @source. Free the returned
+ * string with g_free() when done with it.
+ *
+ * Since: 3.14
+ **/
+gchar *
+e_source_selector_dup_source_tooltip (ESourceSelector *selector,
+ ESource *source)
+{
+ GtkTreeRowReference *reference;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ gchar *tooltip = NULL;
+
+ g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), NULL);
+ g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+
+ reference = g_hash_table_lookup (selector->priv->source_index, source);
+
+ /* If the ESource is not in our tree model then return silently. */
+ if (reference == NULL)
+ return NULL;
+
+ /* If we do have a row reference, it should be valid. */
+ g_return_val_if_fail (gtk_tree_row_reference_valid (reference), FALSE);
+
+ model = gtk_tree_row_reference_get_model (reference);
+ path = gtk_tree_row_reference_get_path (reference);
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_path_free (path);
+
+ gtk_tree_model_get (
+ model, &iter,
+ COLUMN_TOOLTIP, &tooltip,
+ -1);
+
+ return tooltip;
+}
+
+/**
+ * e_source_selector_set_source_is_busy:
+ * @selector: an #ESourceSelector
+ * @source: an #ESource for which to set the is-busy status
+ *
+ * Updates the is-busy flag status for the given @source.
+ *
+ * Since: 3.14
+ **/
+void
+e_source_selector_set_source_is_busy (ESourceSelector *selector,
+ ESource *source,
+ gboolean is_busy)
+{
+ GtkTreeRowReference *reference;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ gboolean old_is_busy = FALSE;
+
+ g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
+ g_return_if_fail (E_IS_SOURCE (source));
+
+ reference = g_hash_table_lookup (selector->priv->source_index, source);
+
+ /* If the ESource is not in our tree model then return silently. */
+ if (reference == NULL)
+ return;
+
+ /* If we do have a row reference, it should be valid. */
+ g_return_if_fail (gtk_tree_row_reference_valid (reference));
+
+ model = gtk_tree_row_reference_get_model (reference);
+ path = gtk_tree_row_reference_get_path (reference);
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_path_free (path);
+
+ gtk_tree_model_get (
+ GTK_TREE_MODEL (model), &iter,
+ COLUMN_IS_BUSY, &old_is_busy,
+ -1);
+
+ if ((old_is_busy ? 1 : 0) == (is_busy ? 1 : 0))
+ return;
+
+ gtk_tree_store_set (
+ GTK_TREE_STORE (model), &iter,
+ COLUMN_IS_BUSY, is_busy,
+ -1);
+
+ if (is_busy)
+ source_selector_inc_busy_sources (selector);
+ else
+ source_selector_dec_busy_sources (selector);
+}
+
+/**
+ * e_source_selector_get_source_is_busy:
+ * @selector: an #ESourceSelector
+ * @source: an #ESource for which to read the is-busy status
+ *
+ * Returns: Current is-busy flag status for the given @source.
+ *
+ * Since: 3.14
+ **/
+gboolean
+e_source_selector_get_source_is_busy (ESourceSelector *selector,
+ ESource *source)
+{
+ GtkTreeRowReference *reference;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ gboolean is_busy = FALSE;
+
+ g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), FALSE);
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+
+ reference = g_hash_table_lookup (selector->priv->source_index, source);
+
+ /* If the ESource is not in our tree model then return silently. */
+ if (reference == NULL)
+ return FALSE;
+
+ /* If we do have a row reference, it should be valid. */
+ g_return_val_if_fail (gtk_tree_row_reference_valid (reference), FALSE);
+
+ model = gtk_tree_row_reference_get_model (reference);
+ path = gtk_tree_row_reference_get_path (reference);
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_path_free (path);
+
+ gtk_tree_model_get (
+ model, &iter,
+ COLUMN_IS_BUSY, &is_busy,
+ -1);
+
+ return is_busy;
+}
diff --git a/e-util/e-source-selector.h b/e-util/e-source-selector.h
index f3e9e56..e186601 100644
--- a/e-util/e-source-selector.h
+++ b/e-util/e-source-selector.h
@@ -143,6 +143,20 @@ void e_source_selector_update_row (ESourceSelector *selector,
ESource *source);
void e_source_selector_update_all_rows
(ESourceSelector *selector);
+void e_source_selector_set_source_tooltip
+ (ESourceSelector *selector,
+ ESource *source,
+ const gchar *tooltip);
+gchar * e_source_selector_dup_source_tooltip
+ (ESourceSelector *selector,
+ ESource *source);
+void e_source_selector_set_source_is_busy
+ (ESourceSelector *selector,
+ ESource *source,
+ gboolean is_busy);
+gboolean e_source_selector_get_source_is_busy
+ (ESourceSelector *selector,
+ ESource *source);
G_END_DECLS
diff --git a/e-util/test-source-selector.c b/e-util/test-source-selector.c
index 87aac7b..6751d5a 100644
--- a/e-util/test-source-selector.c
+++ b/e-util/test-source-selector.c
@@ -21,6 +21,7 @@
#define OPENED_KEY "sources-opened-key"
#define SOURCE_TYPE_KEY "sources-source-type-key"
#define EXTENSION_NAME_KEY "sources-extension-name-key"
+#define TOOLTIP_ENTRY_KEY "sources-tooltip-entry-key"
static void
dump_selection (ESourceSelector *selector,
@@ -99,6 +100,19 @@ disable_widget_if_opened_cb (ESourceSelector *selector,
}
static void
+enable_widget_if_any_selected (ESourceSelector *selector,
+ GtkWidget *widget)
+{
+ ESource *source;
+ gboolean sensitive;
+
+ source = e_source_selector_ref_primary_selection (selector);
+ sensitive = (source != NULL);
+ gtk_widget_set_sensitive (widget, sensitive);
+ g_clear_object (&source);
+}
+
+static void
open_selected_clicked_cb (GtkWidget *button,
ESourceSelector *selector)
{
@@ -171,12 +185,44 @@ close_selected_clicked_cb (GtkWidget *button,
g_object_unref (source);
}
+static void
+flip_busy_clicked_cb (GtkWidget *button,
+ ESourceSelector *selector)
+{
+ ESource *source;
+
+ source = e_source_selector_ref_primary_selection (selector);
+ if (source)
+ e_source_selector_set_source_is_busy (selector, source,
+ !e_source_selector_get_source_is_busy (selector, source));
+
+ g_clear_object (&source);
+}
+
+static void
+set_tooltip_clicked_cb (GtkWidget *button,
+ ESourceSelector *selector)
+{
+ ESource *source;
+ GtkEntry *entry;
+
+ entry = g_object_get_data (G_OBJECT (button), TOOLTIP_ENTRY_KEY);
+ g_return_if_fail (entry != NULL);
+
+ source = e_source_selector_ref_primary_selection (selector);
+ if (source)
+ e_source_selector_set_source_tooltip (selector, source,
+ gtk_entry_get_text (entry));
+
+ g_clear_object (&source);
+}
+
static GtkWidget *
create_page (ESourceRegistry *registry,
const gchar *extension_name,
ECalClientSourceType source_type)
{
- GtkWidget *widget, *subwindow, *selector, *button_box;
+ GtkWidget *widget, *subwindow, *selector, *button_box, *entry;
GtkGrid *grid;
GHashTable *opened_sources;
@@ -235,6 +281,31 @@ create_page (ESourceRegistry *registry,
selector, "primary-selection-changed",
G_CALLBACK (enable_widget_if_opened_cb), widget);
+ widget = gtk_button_new_with_label ("Flip busy status");
+ gtk_widget_set_margin_top (widget, 10);
+ gtk_container_add (GTK_CONTAINER (button_box), widget);
+
+ g_signal_connect (
+ widget, "clicked",
+ G_CALLBACK (flip_busy_clicked_cb), selector);
+ g_signal_connect (
+ selector, "primary-selection-changed",
+ G_CALLBACK (enable_widget_if_any_selected), widget);
+
+ entry = gtk_entry_new ();
+ gtk_container_add (GTK_CONTAINER (button_box), entry);
+
+ widget = gtk_button_new_with_label ("Set Tooltip");
+ g_object_set_data (G_OBJECT (widget), TOOLTIP_ENTRY_KEY, entry);
+ gtk_container_add (GTK_CONTAINER (button_box), widget);
+
+ g_signal_connect (
+ widget, "clicked",
+ G_CALLBACK (set_tooltip_clicked_cb), selector);
+ g_signal_connect (
+ selector, "primary-selection-changed",
+ G_CALLBACK (enable_widget_if_any_selected), widget);
+
widget = gtk_label_new ("");
g_object_set (
G_OBJECT (widget),
diff --git a/modules/calendar/e-cal-base-shell-content.c b/modules/calendar/e-cal-base-shell-content.c
index e1d0129..f826a7d 100644
--- a/modules/calendar/e-cal-base-shell-content.c
+++ b/modules/calendar/e-cal-base-shell-content.c
@@ -21,7 +21,7 @@
#endif
#include <string.h>
-#include <glib/gi18n.h>
+#include <glib/gi18n-lib.h>
#include "e-cal-base-shell-sidebar.h"
#include "e-cal-base-shell-view.h"
@@ -31,6 +31,7 @@ struct _ECalBaseShellContentPrivate {
ECalDataModel *data_model;
ECalModel *model;
gulong object_created_id;
+ gulong view_state_changed_id;
};
enum {
@@ -103,6 +104,47 @@ cal_base_shell_content_object_created_cb (ECalBaseShellContent *cal_base_shell_c
}
static void
+cal_base_sahell_content_view_state_changed_cb (ECalDataModel *data_model,
+ ECalClientView *view,
+ ECalDataModelViewState state,
+ guint percent,
+ const gchar *message,
+ const GError *error,
+ ECalBaseShellContent *cal_base_shell_content)
+{
+ EShellView *shell_view;
+ EShellSidebar *shell_sidebar;
+ ESourceSelector *selector;
+ ESource *source;
+
+ shell_view = e_shell_content_get_shell_view (E_SHELL_CONTENT (cal_base_shell_content));
+ g_return_if_fail (E_IS_SHELL_VIEW (shell_view));
+
+ shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
+ g_return_if_fail (E_IS_SHELL_SIDEBAR (shell_sidebar));
+
+ selector = e_cal_base_shell_sidebar_get_selector (E_CAL_BASE_SHELL_SIDEBAR (shell_sidebar));
+ source = e_client_get_source (E_CLIENT (e_cal_client_view_get_client (view)));
+
+ if (state == E_CAL_DATA_MODEL_VIEW_STATE_START ||
+ state == E_CAL_DATA_MODEL_VIEW_STATE_PROGRESS) {
+ e_source_selector_set_source_is_busy (selector, source, TRUE);
+
+ if (message) {
+ gchar *tooltip;
+
+ /* Translators: This is a running activity whose percent complete is known. */
+ tooltip = g_strdup_printf (_("%s (%d%% complete)"), message, percent);
+ e_source_selector_set_source_tooltip (selector, source, tooltip);
+ g_free (tooltip);
+ }
+ } else {
+ e_source_selector_set_source_is_busy (selector, source, FALSE);
+ e_source_selector_set_source_tooltip (selector, source, NULL);
+ }
+}
+
+static void
cal_base_shell_content_view_created_cb (EShellWindow *shell_window,
EShellView *shell_view,
ECalBaseShellContent *cal_base_shell_content)
@@ -136,6 +178,10 @@ cal_base_shell_content_view_created_cb (EShellWindow *shell_window,
g_signal_connect (selector, "notify::primary-selection",
G_CALLBACK (cal_base_shell_content_primary_selection_changed_cb), cal_base_shell_content);
+ cal_base_shell_content->priv->view_state_changed_id = g_signal_connect (
+ cal_base_shell_content->priv->data_model, "view-state-changed",
+ G_CALLBACK (cal_base_sahell_content_view_state_changed_cb), cal_base_shell_content);
+
klass = E_CAL_BASE_SHELL_CONTENT_GET_CLASS (cal_base_shell_content);
g_return_if_fail (klass != NULL);
@@ -204,6 +250,12 @@ cal_base_shell_content_dispose (GObject *object)
e_cal_data_model_set_disposing (cal_base_shell_content->priv->data_model, TRUE);
+ if (cal_base_shell_content->priv->view_state_changed_id != 0) {
+ g_signal_handler_disconnect (cal_base_shell_content->priv->data_model,
+ cal_base_shell_content->priv->view_state_changed_id);
+ cal_base_shell_content->priv->view_state_changed_id = 0;
+ }
+
if (cal_base_shell_content->priv->object_created_id != 0) {
g_signal_handler_disconnect (cal_base_shell_content->priv->model,
cal_base_shell_content->priv->object_created_id);
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 6939e10..195c269 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -419,6 +419,7 @@ modules/cal-config-weather/evolution-cal-config-weather.c
modules/cal-config-webcal/evolution-cal-config-webcal.c
modules/calendar/e-cal-attachment-handler.c
modules/calendar/e-cal-base-shell-backend.c
+modules/calendar/e-cal-base-shell-content.c
modules/calendar/e-cal-base-shell-sidebar.c
modules/calendar/e-calendar-preferences.c
[type: gettext/glade]modules/calendar/e-calendar-preferences.ui
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]