static void
move_selected_input_down (GtkButton *button, gpointer data)
{
  GtkBuilder *builder = data;
  GtkTreeModel *model;
  GtkTreeIter iter, next;
  GtkTreePath *path;

  g_debug ("move selected input source down");

  if (!get_selected_iter (builder, &model, &iter))
    return;

  next = iter;
  if (!gtk_tree_model_iter_next (model, &next))
    return;

  path = gtk_tree_model_get_path (model, &next);

  gtk_list_store_swap (GTK_LIST_STORE (model), &iter, &next);

  set_selected_path (builder, path);
  gtk_tree_path_free (path);

  update_button_sensitivity (builder);
  update_configuration (model);
}
static void
input_sources_changed (GSettings  *settings,
                       gchar      *key,
                       GtkBuilder *builder)
{
  GtkWidget *treeview;
  GtkTreeModel *store;
  GtkTreePath *path;
  GtkTreeIter iter;
  GtkTreeModel *model;

  treeview = WID("active_input_sources");
  store = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview));

  if (get_selected_iter (builder, &model, &iter))
    path = gtk_tree_model_get_path (model, &iter);
  else
    path = NULL;

  gtk_list_store_clear (GTK_LIST_STORE (store));
  populate_with_active_sources (GTK_LIST_STORE (store));

  if (path)
    {
      set_selected_path (builder, path);
      gtk_tree_path_free (path);
    }
}
static void
remove_selected_input (GtkButton *button, gpointer data)
{
  GtkBuilder *builder = data;
  GtkTreeModel *model;
  GtkTreeIter iter;
  GtkTreePath *path;

  g_debug ("remove selected input source");

  if (get_selected_iter (builder, &model, &iter) == FALSE)
    return;

  path = gtk_tree_model_get_path (model, &iter);

  gtk_list_store_remove (GTK_LIST_STORE (model), &iter);

  if (!gtk_tree_model_get_iter (model, &iter, path))
    gtk_tree_path_prev (path);

  set_selected_path (builder, path);

  gtk_tree_path_free (path);

  update_button_sensitivity (builder);
  update_configuration (model);
}
static void
remove_selected_layout (GtkWidget * button, GtkBuilder * dialog)
{
	GtkTreeModel *model;
	GtkTreeIter iter;

	if (get_selected_iter (dialog, &model, &iter) == FALSE)
		return;

	gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
	update_layouts_list (model, dialog);
}
static void
update_button_sensitivity (GtkBuilder *builder)
{
  GtkWidget *remove_button;
  GtkWidget *up_button;
  GtkWidget *down_button;
  GtkWidget *show_button;
  GtkWidget *settings_button;
  GtkTreeView *tv;
  GtkTreeModel *model;
  GtkTreeIter iter;
  gint n_active;
  gint index;
  gboolean settings_sensitive;
  GDesktopAppInfo *app_info;

  remove_button = WID("input_source_remove");
  show_button = WID("input_source_show");
  up_button = WID("input_source_move_up");
  down_button = WID("input_source_move_down");
  settings_button = WID("input_source_settings");

  tv = GTK_TREE_VIEW (WID ("active_input_sources"));
  n_active = gtk_tree_model_iter_n_children (gtk_tree_view_get_model (tv), NULL);

  if (get_selected_iter (builder, &model, &iter))
    {
      index = idx_from_model_iter (model, &iter);
      gtk_tree_model_get (model, &iter, SETUP_COLUMN, &app_info, -1);
    }
  else
    {
      index = -1;
      app_info = NULL;
    }

  settings_sensitive = (index >= 0 && app_info != NULL);

  if (app_info)
    g_object_unref (app_info);

  gtk_widget_set_sensitive (remove_button, index >= 0 && n_active > 1);
  gtk_widget_set_sensitive (show_button, index >= 0);
  gtk_widget_set_sensitive (up_button, index > 0);
  gtk_widget_set_sensitive (down_button, index >= 0 && index < n_active - 1);
  gtk_widget_set_sensitive (settings_button, settings_sensitive);
}
static gint
find_selected_layout_idx (GtkBuilder *dialog)
{
	GtkTreeIter selected_iter;
	GtkTreeModel *model;
	GtkTreePath *path;
	gint *indices;
	gint rv;

	if (!get_selected_iter (dialog, &model, &selected_iter))
		return -1;

	path = gtk_tree_model_get_path (model, &selected_iter);
	if (path == NULL)
		return -1;

	indices = gtk_tree_path_get_indices (path);
	rv = indices[0];
	gtk_tree_path_free (path);
	return rv;
}
static void
show_selected_settings (GtkButton *button, gpointer data)
{
  GtkBuilder *builder = data;
  GtkTreeModel *model;
  GtkTreeIter iter;
  GdkAppLaunchContext *ctx;
  GDesktopAppInfo *app_info;
  gchar *id;
  GError *error = NULL;

  g_debug ("show selected layout");

  if (!get_selected_iter (builder, &model, &iter))
    return;

  gtk_tree_model_get (model, &iter, SETUP_COLUMN, &app_info, -1);

  if (!app_info)
    return;

  ctx = gdk_display_get_app_launch_context (gdk_display_get_default ());
  gdk_app_launch_context_set_timestamp (ctx, gtk_get_current_event_time ());

  gtk_tree_model_get (model, &iter, ID_COLUMN, &id, -1);
  g_app_launch_context_setenv (G_APP_LAUNCH_CONTEXT (ctx),
                               "IBUS_ENGINE_NAME",
                               id);
  g_free (id);

  if (!g_app_info_launch (G_APP_INFO (app_info), NULL, G_APP_LAUNCH_CONTEXT (ctx), &error))
    {
      g_warning ("Failed to launch input source setup: %s", error->message);
      g_error_free (error);
    }

  g_object_unref (ctx);
  g_object_unref (app_info);
}
static void
move_down_selected_layout (GtkWidget * button, GtkBuilder * dialog)
{
	GtkTreeModel *model;
	GtkTreeIter iter, next;
	GtkTreePath *path;

	if (get_selected_iter (dialog, &model, &iter) == FALSE)
		return;

	next = iter;
	if (!gtk_tree_model_iter_next (model, &next))
		return;

	path = gtk_tree_model_get_path (model, &next);

	gtk_list_store_swap (GTK_LIST_STORE (model), &iter, &next);

	update_layouts_list (model, dialog);

	set_selected_path (dialog, path);

	gtk_tree_path_free (path);	
}
static void
show_selected_layout (GtkButton *button, gpointer data)
{
  GtkBuilder *builder = data;
  GtkTreeModel *model;
  GtkTreeIter iter;
  gchar *type;
  gchar *id;
  gchar *kbd_viewer_args;
  const gchar *xkb_layout;
  const gchar *xkb_variant;

  g_debug ("show selected layout");

  if (!get_selected_iter (builder, &model, &iter))
    return;

  gtk_tree_model_get (model, &iter,
                      TYPE_COLUMN, &type,
                      ID_COLUMN, &id,
                      -1);

  if (g_str_equal (type, INPUT_SOURCE_TYPE_XKB))
    {
      gnome_xkb_info_get_layout_info (xkb_info, id, NULL, NULL, &xkb_layout, &xkb_variant);

      if (!xkb_layout || !xkb_layout[0])
        {
          g_warning ("Couldn't find XKB input source '%s'", id);
          goto exit;
        }
    }
  else if (g_str_equal (type, INPUT_SOURCE_TYPE_IBUS))
    {
#ifdef HAVE_IBUS
      IBusEngineDesc *engine_desc = NULL;

      if (ibus_engines)
        engine_desc = g_hash_table_lookup (ibus_engines, id);

      if (engine_desc)
        {
          xkb_layout = ibus_engine_desc_get_layout (engine_desc);
          xkb_variant = "";
        }
      else
        {
          g_warning ("Couldn't find IBus input source '%s'", id);
          goto exit;
        }
#else
      g_warning ("IBus input source type specified but IBus support was not compiled");
      goto exit;
#endif
    }
  else
    {
      g_warning ("Unknown input source type '%s'", type);
      goto exit;
    }

  if (xkb_variant[0])
    kbd_viewer_args = g_strdup_printf ("gkbd-keyboard-display -l \"%s\t%s\"",
                                       xkb_layout, xkb_variant);
  else
    kbd_viewer_args = g_strdup_printf ("gkbd-keyboard-display -l %s",
                                       xkb_layout);

  g_spawn_command_line_async (kbd_viewer_args, NULL);

  g_free (kbd_viewer_args);
 exit:
  g_free (type);
  g_free (id);
}