static void
add_config_file (const gchar   *path,
                 GHashTable    *group_name_to_key_file_data,
                 GList        **key_files_to_free)
{
  GKeyFile *key_file;
  GError *error;

  key_file = g_key_file_new ();

  error = NULL;
  if (!g_key_file_load_from_file (key_file,
                                  path,
                                  G_KEY_FILE_NONE,
                                  &error))
    {
      if (!(error->domain == G_FILE_ERROR && error->code == G_FILE_ERROR_NOENT))
        {
          goa_warning ("Error loading %s: %s (%s, %d)",
                       path,
                       error->message, g_quark_to_string (error->domain), error->code);
        }
      g_error_free (error);
      g_key_file_free (key_file);
    }
  else
    {
      gchar **groups;
      gsize num_groups;
      guint n;

      groups = g_key_file_get_groups (key_file, &num_groups);
      for (n = 0; n < num_groups; n++)
        {
          if (g_str_has_prefix (groups[n], "Account "))
            {
              g_hash_table_insert (group_name_to_key_file_data,
                                   groups[n], /* steals string */
                                   key_file_data_new (key_file, path));
            }
          else
            {
              goa_warning ("Unexpected group \"%s\" in file %s", groups[n], path);
              g_free (groups[n]);
            }
        }
      g_free (groups);

      *key_files_to_free = g_list_prepend (*key_files_to_free, key_file);
    }
}
static void
notification_cb (NotifyNotification *notification,
                 gchar              *action,
                 gpointer            user_data)
{
  GAppLaunchContext *ctx;
  GDesktopAppInfo *app;
  GError *error;

  /* TODO: Hmm, would be nice to set the screen, timestamp etc etc */
  ctx = g_app_launch_context_new ();

  app = g_desktop_app_info_new ("gnome-online-accounts-panel.desktop");

  error = NULL;
  if (!g_app_info_launch (G_APP_INFO (app),
                          NULL, /* files */
                          ctx,
                          &error))
    {
      goa_warning ("Error launching: %s (%s, %d)",
                   error->message, g_quark_to_string (error->domain), error->code);
      g_error_free (error);
    }
  g_object_unref (app);
  g_object_unref (ctx);
}
static GFileMonitor *
create_monitor (const gchar *path, gboolean is_dir)
{
  GFile *file;
  GFileMonitor *monitor;
  GError *error;

  error = NULL;
  file = g_file_new_for_path (path);
  if (is_dir)
    monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, &error);
  else
    monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, &error);

  if (monitor == NULL)
    {
      goa_warning ("Error monitoring %s at %s: %s (%s, %d)",
                   is_dir ? "directory" : "file",
                   path,
                   error->message, g_quark_to_string (error->domain), error->code);
      g_error_free (error);
    }
  g_object_unref (file);

  return monitor;
}
static gboolean
on_timer_source_ready (GObject *stream, GoaAlarm *self)
{
  gint64 number_of_fires;
  gssize bytes_read;
  gboolean run_again = FALSE;

  g_return_val_if_fail (GOA_IS_ALARM (self), FALSE);
  g_return_val_if_fail (self->priv->type == GOA_ALARM_TYPE_TIMER, FALSE);

  g_rec_mutex_lock (&self->priv->lock);
  if (g_cancellable_is_cancelled (self->priv->cancellable))
    goto out;

  bytes_read =
    g_pollable_input_stream_read_nonblocking (G_POLLABLE_INPUT_STREAM (stream),
                                              &number_of_fires, sizeof (gint64),
                                              NULL, NULL);

  if (bytes_read == sizeof (gint64))
    {
      if (number_of_fires < 0 || number_of_fires > 1)
        {
          goa_warning ("GoaAlarm: expected timerfd to report firing once,"
                       "but it reported firing %ld times\n", (long) number_of_fires);
        }
    }

  fire_or_rearm_alarm (self);
  run_again = TRUE;
out:
  g_rec_mutex_unlock (&self->priv->lock);
  return run_again;
}
static void
goa_daemon_init (GoaDaemon *daemon)
{
  static volatile GQuark goa_error_domain = 0;
  GoaObjectSkeleton *object;
  gchar *path;

  /* this will force associating errors in the GOA_ERROR error domain
   * with org.freedesktop.Goa.Error.* errors via g_dbus_error_register_error_domain().
   */
  goa_error_domain = GOA_ERROR;
  goa_error_domain; /* shut up -Wunused-but-set-variable */

  notify_init ("goa-daemon");

  /* TODO: maybe nicer to pass in a GDBusConnection* construct property */
  daemon->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);

  /* Create object manager */
  daemon->object_manager = g_dbus_object_manager_server_new ("/org/gnome/OnlineAccounts");

  /* Create and export Manager */
  daemon->manager = goa_manager_skeleton_new ();
  g_signal_connect (daemon->manager,
                    "handle-add-account",
                    G_CALLBACK (on_manager_handle_add_account),
                    daemon);
  object = goa_object_skeleton_new ("/org/gnome/OnlineAccounts/Manager");
  goa_object_skeleton_set_manager (object, daemon->manager);
  g_dbus_object_manager_server_export (daemon->object_manager, G_DBUS_OBJECT_SKELETON (object));
  g_object_unref (object);

  /* create ~/.config/goa-1.0 directory */
  path = g_strdup_printf ("%s/goa-1.0", g_get_user_config_dir ());
  if (g_mkdir_with_parents (path, 0755) != 0)
    {
      goa_warning ("Error creating directory %s: %m", path);
    }
  g_free (path);

  /* set up file monitoring */
  path = g_strdup_printf ("%s/goa-1.0/accounts.conf", g_get_user_config_dir ());
  daemon->home_conf_file_monitor = create_monitor (path, FALSE);
  if (daemon->home_conf_file_monitor != NULL)
    g_signal_connect (daemon->home_conf_file_monitor, "changed", G_CALLBACK (on_file_monitor_changed), daemon);
  g_free (path);

  /* prime the list of accounts */
  goa_daemon_reload_configuration (daemon);

  /* Export objects */
  g_dbus_object_manager_server_set_connection (daemon->object_manager, daemon->connection);
}
static void
update_account (GoaPanelAccountsModel  *model,
                GoaObject              *object)
{
  GtkTreeIter iter;
  if (!find_iter_for_object (model, object, &iter))
    {
      goa_warning ("Error updating object %s - not in tree", g_dbus_object_get_object_path (G_DBUS_OBJECT (object)));
    }
  else
    {
      set_values (model, object, &iter);
    }
}
static void
remove_account (GoaPanelAccountsModel  *model,
                GoaObject              *object)
{
  GtkTreeIter iter;
  if (!find_iter_for_object (model, object, &iter))
    {
      goa_warning ("Error removing object %s - not in tree", g_dbus_object_get_object_path (G_DBUS_OBJECT (object)));
    }
  else
    {
      gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
    }
}
static void
clear_scheduled_timer_wakeups (GoaAlarm *self)
{
#ifdef HAVE_TIMERFD
  GError *error;
  gboolean is_closed;

  g_clear_pointer (&self->priv->timer.source, (GDestroyNotify) g_source_destroy);

  error = NULL;
  is_closed = g_input_stream_close (self->priv->timer.stream, NULL, &error);

  if (!is_closed)
    {
      goa_warning ("GoaAlarm: could not close timer stream: %s", error->message);
      g_error_free (error);
    }

  g_clear_object (&self->priv->timer.stream);
#endif
}
static void
set_values (GoaPanelAccountsModel  *model,
            GoaObject              *object,
            GtkTreeIter            *iter)
{
  GoaAccount *account;
  GIcon *icon;
  gchar *markup;
  GError *error;

  account = goa_object_peek_account (object);

  error = NULL;
  icon = g_icon_new_for_string (goa_account_get_provider_icon (account), &error);
  if (icon == NULL)
    {
      goa_warning ("Error creating GIcon for account: %s (%s, %d)",
                   error->message, g_quark_to_string (error->domain), error->code);
      g_error_free (error);
    }

  markup = g_strdup_printf ("<b>%s</b>\n<small>%s</small>",
                            goa_account_get_provider_name (account),
                            goa_account_get_presentation_identity (account));

  gtk_list_store_set (GTK_LIST_STORE (model),
                      iter,
                      GOA_PANEL_ACCOUNTS_MODEL_COLUMN_SORT_KEY, goa_account_get_id (account),
                      GOA_PANEL_ACCOUNTS_MODEL_COLUMN_OBJECT, object,
                      GOA_PANEL_ACCOUNTS_MODEL_COLUMN_ATTENTION_NEEDED, goa_account_get_attention_needed (account),
                      GOA_PANEL_ACCOUNTS_MODEL_COLUMN_MARKUP, markup,
                      GOA_PANEL_ACCOUNTS_MODEL_COLUMN_ICON, icon,
                      -1);

  g_free (markup);
  g_clear_object (&icon);
}
static void
goa_daemon_update_notifications (GoaDaemon *daemon)
{
  gboolean show_notification;
  GList *objects;
  GList *l;

  show_notification = FALSE;
  objects = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (daemon->object_manager));
  for (l = objects; l != NULL; l = l->next)
    {
      GoaObject *object = GOA_OBJECT (l->data);
      GoaAccount *account;
      account = goa_object_peek_account (object);
      if (account != NULL)
        {
          if (goa_account_get_attention_needed (account))
            {
              show_notification = TRUE;
              break;
            }
        }
    }

  if (show_notification)
    {
      if (daemon->notification == NULL)
        {
          GError *error;

          daemon->notification = notify_notification_new (_("An online account needs attention"),
                                                          NULL,
                                                          NULL);
          notify_notification_set_timeout (daemon->notification, NOTIFY_EXPIRES_NEVER);
          g_object_set (daemon->notification, "icon-name", "gtk-dialog-error", NULL);
          notify_notification_add_action (daemon->notification,
                                          "open-online-accounts",
                                          _("Open Online Accounts..."),
                                          notification_cb,
                                          daemon,
                                          NULL); /* GFreeFunc */

          error = NULL;
          if (!notify_notification_show (daemon->notification, &error))
            {
              goa_warning ("Error showing notification: %s (%s, %d)",
                           error->message, g_quark_to_string (error->domain), error->code);
              g_error_free (error);
              g_object_unref (daemon->notification);
              daemon->notification = NULL;
            }
          else
            {
              g_signal_connect (daemon->notification,
                                "closed",
                                G_CALLBACK (on_notification_closed),
                                daemon);
            }
        }
    }
  else
    {
      if (daemon->notification != NULL)
        {
          GError *error;
          error = NULL;
          if (!notify_notification_close (daemon->notification, &error))
            {
              goa_warning ("Error closing notification: %s (%s, %d)",
                           error->message, g_quark_to_string (error->domain), error->code);
              g_error_free (error);
            }
          g_signal_handlers_disconnect_by_func (daemon->notification,
                                                G_CALLBACK (on_notification_closed),
                                                daemon);
          g_object_unref (daemon->notification);
          daemon->notification = NULL;
        }
    }

  g_list_foreach (objects, (GFunc) g_object_unref, NULL);
  g_list_free (objects);
}
static void
process_config_entries (GoaDaemon  *daemon,
                        GHashTable *group_name_to_key_file_data)
{
  GHashTableIter iter;
  const gchar *id;
  KeyFileData *key_file_data;
  GList *existing_object_paths;
  GList *config_object_paths;
  GList *added;
  GList *removed;
  GList *unchanged;
  GList *l;

  existing_object_paths = NULL;
  {
    GList *existing_objects;
    existing_objects = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (daemon->object_manager));
    for (l = existing_objects; l != NULL; l = l->next)
      {
        GoaObject *object = GOA_OBJECT (l->data);
        const gchar *object_path;
        object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (object));
        if (g_str_has_prefix (object_path, "/org/gnome/OnlineAccounts/Accounts/"))
          existing_object_paths = g_list_prepend (existing_object_paths, g_strdup (object_path));
      }
    g_list_foreach (existing_objects, (GFunc) g_object_unref, NULL);
    g_list_free (existing_objects);
  }

  config_object_paths = NULL;
  g_hash_table_iter_init (&iter, group_name_to_key_file_data);
  while (g_hash_table_iter_next (&iter, (gpointer*) &id, (gpointer*) &key_file_data))
    {
      gchar *object_path;

      /* create and validate object path */
      object_path = g_strdup_printf ("/org/gnome/OnlineAccounts/Accounts/%s", id + sizeof "Account " - 1);
      if (strstr (id + sizeof "Account " - 1, "/") != NULL || !g_variant_is_object_path (object_path))
        {
          goa_warning ("`%s' is not a valid account identifier", id);
          g_free (object_path);
          continue;
        }
      /* steals object_path variable */
      config_object_paths = g_list_prepend (config_object_paths, object_path);
    }

  existing_object_paths = g_list_sort (existing_object_paths, (GCompareFunc) g_strcmp0);
  config_object_paths = g_list_sort (config_object_paths, (GCompareFunc) g_strcmp0);
  diff_sorted_lists (existing_object_paths,
                     config_object_paths,
                     (GCompareFunc) g_strcmp0,
                     &added,
                     &removed,
                     &unchanged);

  for (l = removed; l != NULL; l = l->next)
    {
      const gchar *object_path = l->data;
      GoaObject *object;
      object = GOA_OBJECT (g_dbus_object_manager_get_object (G_DBUS_OBJECT_MANAGER (daemon->object_manager), object_path));
      g_warn_if_fail (object != NULL);
      g_signal_handlers_disconnect_by_func (goa_object_peek_account (object),
                                            G_CALLBACK (account_on_attention_needed_notify),
                                            daemon);
      g_signal_handlers_disconnect_by_func (goa_object_peek_account (object),
                                            G_CALLBACK (on_account_handle_remove),
                                            daemon);
      goa_debug ("removing %s", object_path);
      g_warn_if_fail (g_dbus_object_manager_server_unexport (daemon->object_manager, object_path));
    }
  for (l = added; l != NULL; l = l->next)
    {
      const gchar *object_path = l->data;
      GoaObjectSkeleton *object;
      gchar *group;

      goa_debug ("adding %s", object_path);

      group = object_path_to_group (object_path);
      key_file_data = g_hash_table_lookup (group_name_to_key_file_data, group);
      g_warn_if_fail (key_file_data != NULL);

      object = goa_object_skeleton_new (object_path);
      if (update_account_object (daemon,
                                 object,
                                 key_file_data->path,
                                 group,
                                 key_file_data->key_file,
                                 TRUE))
        {
          g_dbus_object_manager_server_export (daemon->object_manager, G_DBUS_OBJECT_SKELETON (object));
          g_signal_connect (goa_object_peek_account (GOA_OBJECT (object)),
                            "notify::attention-needed",
                            G_CALLBACK (account_on_attention_needed_notify),
                            daemon);
          g_signal_connect (goa_object_peek_account (GOA_OBJECT (object)),
                            "handle-remove",
                            G_CALLBACK (on_account_handle_remove),
                            daemon);
          g_signal_connect (goa_object_peek_account (GOA_OBJECT (object)),
                            "handle-ensure-credentials",
                            G_CALLBACK (on_account_handle_ensure_credentials),
                            daemon);
        }
      g_object_unref (object);
      g_free (group);
    }
  for (l = unchanged; l != NULL; l = l->next)
    {
      const gchar *object_path = l->data;
      GoaObject *object;
      gchar *group;

      goa_debug ("unchanged %s", object_path);

      group = object_path_to_group (object_path);
      key_file_data = g_hash_table_lookup (group_name_to_key_file_data, group);
      g_warn_if_fail (key_file_data != NULL);

      object = GOA_OBJECT (g_dbus_object_manager_get_object (G_DBUS_OBJECT_MANAGER (daemon->object_manager), object_path));
      g_warn_if_fail (object != NULL);
      if (!update_account_object (daemon,
                                  GOA_OBJECT_SKELETON (object),
                                  key_file_data->path,
                                  group,
                                  key_file_data->key_file,
                                  FALSE))
        {
          g_signal_handlers_disconnect_by_func (goa_object_peek_account (object),
                                                G_CALLBACK (account_on_attention_needed_notify),
                                                daemon);
          g_signal_handlers_disconnect_by_func (goa_object_peek_account (object),
                                                G_CALLBACK (on_account_handle_remove),
                                                daemon);
          g_signal_handlers_disconnect_by_func (goa_object_peek_account (object),
                                                G_CALLBACK (on_account_handle_ensure_credentials),
                                                daemon);
          g_warn_if_fail (g_dbus_object_manager_server_unexport (daemon->object_manager, object_path));
        }
      g_object_unref (object);
      g_free (group);
    }

  g_list_free (removed);
  g_list_free (added);
  g_list_free (unchanged);
  g_list_foreach (existing_object_paths, (GFunc) g_free, NULL);
  g_list_free (existing_object_paths);
  g_list_foreach (config_object_paths, (GFunc) g_free, NULL);
  g_list_free (config_object_paths);
}
/* returns FALSE if object is not (or no longer) valid */
static gboolean
update_account_object (GoaDaemon           *daemon,
                       GoaObjectSkeleton   *object,
                       const gchar         *path,
                       const gchar         *group,
                       GKeyFile            *key_file,
                       gboolean             just_added)
{
  GoaAccount *account;
  GoaProvider *provider;
  gboolean ret;
  gchar *identity;
  gchar *presentation_identity;
  gchar *type;
  gchar *name;
  GIcon *icon;
  gchar *serialized_icon;
  GError *error;

  g_return_val_if_fail (GOA_IS_DAEMON (daemon), FALSE);
  g_return_val_if_fail (G_IS_DBUS_OBJECT_SKELETON (object), FALSE);
  g_return_val_if_fail (group != NULL, FALSE);
  g_return_val_if_fail (key_file != NULL, FALSE);

  ret = FALSE;
  identity = NULL;
  type = NULL;
  account = NULL;
  name = NULL;
  icon = NULL;
  serialized_icon = NULL;

  goa_debug ("updating %s %d", g_dbus_object_get_object_path (G_DBUS_OBJECT (object)), just_added);

  type = g_key_file_get_string (key_file, group, "Provider", NULL);
  identity = g_key_file_get_string (key_file, group, "Identity", NULL);
  presentation_identity = g_key_file_get_string (key_file, group, "PresentationIdentity", NULL);
  if (just_added)
    {
      account = goa_account_skeleton_new ();
      goa_object_skeleton_set_account (object, account);
    }
  else
    {
      account = goa_object_get_account (GOA_OBJECT (object));
    }

  provider = goa_provider_get_for_provider_type (type);
  if (provider == NULL)
    {
      goa_warning ("Unsupported account type %s for identity %s (no provider)", type, identity);
      goto out;
    }

  name = goa_provider_get_provider_name (provider, GOA_OBJECT (object));
  icon = goa_provider_get_provider_icon (provider, GOA_OBJECT (object));
  serialized_icon = g_icon_to_string (icon);

  goa_account_set_id (account, g_strrstr (g_dbus_object_get_object_path (G_DBUS_OBJECT (object)), "/") + 1);
  goa_account_set_provider_type (account, type);
  goa_account_set_provider_name (account, name);
  goa_account_set_provider_icon (account, serialized_icon);
  goa_account_set_identity (account, identity);
  goa_account_set_presentation_identity (account, presentation_identity);

  error = NULL;
  if (!goa_provider_build_object (provider, object, key_file, group, &error))
    {
      goa_warning ("Error parsing account: %s (%s, %d)",
                   error->message, g_quark_to_string (error->domain), error->code);
      g_error_free (error);
      goto out;
    }

  ret = TRUE;

 out:
  g_free (serialized_icon);
  if (icon != NULL)
    g_object_unref (icon);
  g_free (name);
  if (provider != NULL)
    g_object_unref (provider);
  g_object_unref (account);
  g_free (type);
  g_free (identity);
  return ret;
}
static void
goa_panel_init (GoaPanel *panel)
{
  GtkWidget *button;
  GtkWidget *w;
  GError *error;
  GtkStyleContext *context;
  GtkTreeViewColumn *column;
  GtkCellRenderer *renderer;
  GtkTreeIter iter;

  panel->builder = gtk_builder_new ();
  error = NULL;
  if (gtk_builder_add_from_file (panel->builder,
                                 CINNAMONCC_UI_DIR "/online-accounts.ui",
                                 &error) == 0)
    {
      goa_warning ("Error loading UI file: %s (%s, %d)",
                   error->message, g_quark_to_string (error->domain), error->code);
      g_error_free (error);
      goto out;
    }

  panel->toolbar = GTK_WIDGET (gtk_builder_get_object (panel->builder, "accounts-tree-toolbar"));
  panel->toolbar_add_button = GTK_WIDGET (gtk_builder_get_object (panel->builder, "accounts-tree-toolbutton-add"));
  g_signal_connect (panel->toolbar_add_button,
                    "clicked",
                    G_CALLBACK (on_toolbar_add_button_clicked),
                    panel);
  panel->toolbar_remove_button = GTK_WIDGET (gtk_builder_get_object (panel->builder, "accounts-tree-toolbutton-remove"));
  g_signal_connect (panel->toolbar_remove_button,
                    "clicked",
                    G_CALLBACK (on_toolbar_remove_button_clicked),
                    panel);

  context = gtk_widget_get_style_context (GTK_WIDGET (gtk_builder_get_object (panel->builder, "accounts-tree-scrolledwindow")));
  gtk_style_context_set_junction_sides (context, GTK_JUNCTION_BOTTOM);
  context = gtk_widget_get_style_context (panel->toolbar);
  gtk_style_context_set_junction_sides (context, GTK_JUNCTION_TOP);

  panel->accounts_treeview = GTK_WIDGET (gtk_builder_get_object (panel->builder, "accounts-tree-treeview"));
  g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (panel->accounts_treeview)),
                    "changed",
                    G_CALLBACK (on_tree_view_selection_changed),
                    panel);

  button = GTK_WIDGET (gtk_builder_get_object (panel->builder, "accounts-button-add"));
  g_signal_connect (button,
                    "clicked",
                    G_CALLBACK (on_add_button_clicked),
                    panel);

  panel->accounts_vbox = GTK_WIDGET (gtk_builder_get_object (panel->builder, "accounts-vbox"));

  /* TODO: probably want to avoid _sync() ... */
  error = NULL;
  panel->client = goa_client_new_sync (NULL /* GCancellable */, &error);
  if (panel->client == NULL)
    {
      goa_warning ("Error getting a GoaClient: %s (%s, %d)",
                   error->message, g_quark_to_string (error->domain), error->code);
      w = GTK_WIDGET (gtk_builder_get_object (panel->builder, "goa-top-widget"));
      gtk_widget_set_sensitive (w, FALSE);
      g_error_free (error);
      goto out;
    }
  g_signal_connect (panel->client,
                    "account-changed",
                    G_CALLBACK (on_account_changed),
                    panel);

  panel->accounts_model = goa_panel_accounts_model_new (panel->client);
  gtk_tree_view_set_model (GTK_TREE_VIEW (panel->accounts_treeview), GTK_TREE_MODEL (panel->accounts_model));
  g_signal_connect (panel->accounts_model, "row-deleted", G_CALLBACK (on_model_row_deleted), panel);
  g_signal_connect (panel->accounts_model, "row-inserted", G_CALLBACK (on_model_row_inserted), panel);

  column = gtk_tree_view_column_new ();
  gtk_tree_view_append_column (GTK_TREE_VIEW (panel->accounts_treeview), column);

  renderer = gtk_cell_renderer_pixbuf_new ();
  gtk_tree_view_column_pack_start (column, renderer, FALSE);
  g_object_set (G_OBJECT (renderer),
                "stock-size", GTK_ICON_SIZE_DIALOG,
                NULL);
  gtk_tree_view_column_set_attributes (column,
                                       renderer,
                                       "gicon", GOA_PANEL_ACCOUNTS_MODEL_COLUMN_ICON,
                                       NULL);

  renderer = gtk_cell_renderer_text_new ();
  gtk_tree_view_column_pack_start (column, renderer, FALSE);
  g_object_set (G_OBJECT (renderer),
                "ellipsize", PANGO_ELLIPSIZE_END,
                "ellipsize-set", TRUE,
                "width-chars", 30,
                NULL);
  gtk_tree_view_column_set_attributes (column,
                                       renderer,
                                       "markup", GOA_PANEL_ACCOUNTS_MODEL_COLUMN_MARKUP,
                                       NULL);

  renderer = gtk_cell_renderer_pixbuf_new ();
  gtk_tree_view_column_pack_end (column, renderer, FALSE);
  g_object_set (G_OBJECT (renderer),
                "icon-name", "dialog-error-symbolic",
                NULL);
  gtk_tree_view_column_set_attributes (column,
                                       renderer,
                                       "visible", GOA_PANEL_ACCOUNTS_MODEL_COLUMN_ATTENTION_NEEDED,
                                       NULL);

  /* Select the first row, if any */
  if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (panel->accounts_model),
                                     &iter))
    gtk_tree_selection_select_iter (gtk_tree_view_get_selection (GTK_TREE_VIEW (panel->accounts_treeview)),
                                    &iter);

 out:
  w = GTK_WIDGET (gtk_builder_get_object (panel->builder, "goa-top-widget"));
  gtk_widget_reparent (w, GTK_WIDGET (panel));
  gtk_widget_show_all (w);
}