[recipes] Allow exporting multiple recipes at once
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [recipes] Allow exporting multiple recipes at once
- Date: Tue, 10 Jan 2017 00:32:10 +0000 (UTC)
commit f2e9d42cfe5fc782aa62a304073aab1c2d1dc170
Author: Matthias Clasen <mclasen redhat com>
Date: Sun Jan 8 22:36:59 2017 -0500
Allow exporting multiple recipes at once
Add a dialog where we can collect a list of recipes before
exporting them.
src/gr-recipe-exporter.c | 202 +++++++++++++++++++++++++++++++++++++----
src/recipe-export-dialog.ui | 55 ++++++++++++
src/recipes-ui.gresource.xml | 1 +
3 files changed, 238 insertions(+), 20 deletions(-)
---
diff --git a/src/gr-recipe-exporter.c b/src/gr-recipe-exporter.c
index dd62fbd..5c8f348 100644
--- a/src/gr-recipe-exporter.c
+++ b/src/gr-recipe-exporter.c
@@ -50,6 +50,8 @@ struct _GrRecipeExporter
GFile *output;
GList *sources;
char *dir;
+
+ GtkWidget *dialog_heading;
};
G_DEFINE_TYPE (GrRecipeExporter, gr_recipe_exporter, G_TYPE_OBJECT)
@@ -107,6 +109,7 @@ cleanup_export (GrRecipeExporter *exporter)
g_clear_pointer (&exporter->dir, g_free);
g_list_free_full (exporter->recipes, g_object_unref);
+ exporter->recipes = NULL;
g_clear_object (&exporter->output);
g_clear_object (&exporter->dest);
g_list_free_full (exporter->sources, g_object_unref);
@@ -394,29 +397,11 @@ error_cb (gpointer compressor,
cleanup_export (exporter);
}
-void
-gr_recipe_exporter_export (GrRecipeExporter *exporter,
- GrRecipe *recipe)
+static void
+start_export (GrRecipeExporter *exporter)
{
- g_autofree char *dir = NULL;
- g_autofree char *basename = NULL;
- g_autofree char *path = NULL;
- g_autoptr(GFile) file = NULL;
g_autoptr(GError) error = NULL;
- dir = g_dir_make_tmp ("recipesXXXXXX", NULL);
- basename = g_strconcat (gr_recipe_get_name (recipe), ".tar.gz", NULL);
- if (dir)
- path = g_build_filename (dir, basename, NULL);
- else
- path = g_strdup (basename);
- file = g_file_new_for_path (path);
-
- g_list_free_full (exporter->recipes, g_object_unref);
- exporter->recipes = g_list_append (exporter->recipes, g_object_ref (recipe));
-
- g_set_object (&exporter->output, file);
-
if (!prepare_export (exporter, &error)) {
error_cb (NULL, error, exporter);
return;
@@ -433,3 +418,180 @@ gr_recipe_exporter_export (GrRecipeExporter *exporter,
autoar_compressor_start_async (exporter->compressor, NULL);
#endif
}
+
+static void
+export_dialog_response (GtkWidget *dialog,
+ int response_id,
+ GrRecipeExporter *exporter)
+{
+ if (response_id == GTK_RESPONSE_CANCEL) {
+ g_message ("not exporting now\n");
+ }
+ else if (response_id == GTK_RESPONSE_OK) {
+ g_autofree char *dir = NULL;
+ g_autofree char *path = NULL;
+
+ g_message ("exporting %d recipes now\n", g_list_length (exporter->recipes));
+ dir = g_dir_make_tmp ("recipesXXXXXX", NULL);
+ path = g_build_filename (dir, "recipes.tar.gz", NULL);
+ exporter->output = g_file_new_for_path (path);
+
+ start_export (exporter);
+ }
+
+ gtk_widget_destroy (dialog);
+ exporter->dialog_heading = NULL;
+}
+
+static void
+update_heading (GrRecipeExporter *exporter)
+{
+ g_autofree char *tmp = NULL;
+ int n;
+
+ n = g_list_length (exporter->recipes);
+ tmp = g_strdup_printf (ngettext ("%d recipe selected for export",
+ "%d recipes selected for export", n), n);
+ gtk_label_set_label (GTK_LABEL (exporter->dialog_heading), tmp);
+}
+
+static void
+row_activated (GtkListBox *list,
+ GtkListBoxRow *row,
+ GrRecipeExporter *exporter)
+{
+ GrRecipe *recipe;
+ GtkWidget *image;
+
+ recipe = GR_RECIPE (g_object_get_data (G_OBJECT (row), "recipe"));
+ image = GTK_WIDGET (g_object_get_data (G_OBJECT (row), "check"));
+
+ if (gtk_widget_get_opacity (image) > 0.5) {
+ exporter->recipes = g_list_remove (exporter->recipes, recipe);
+ g_object_unref (recipe);
+ gtk_widget_set_opacity (image, 0.0);
+ }
+ else {
+ exporter->recipes = g_list_append (exporter->recipes, g_object_ref (recipe));
+ gtk_widget_set_opacity (image, 1.0);
+ }
+
+ update_heading (exporter);
+}
+
+static void
+add_recipe_row (GrRecipeExporter *exporter,
+ GtkWidget *list,
+ GrRecipe *recipe)
+{
+ GtkWidget *row;
+ GtkWidget *box;
+ GtkWidget *label;
+ GtkWidget *image;
+
+ box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 20);
+ gtk_widget_show (box);
+ g_object_set (box, "margin", 10, NULL);
+ label = gtk_label_new (gr_recipe_get_name (recipe));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_widget_show (label);
+ gtk_box_pack_start (GTK_BOX (box), label, TRUE, TRUE, 0);
+
+ image = gtk_image_new_from_icon_name ("object-select-symbolic", 1);
+ gtk_widget_show (image);
+ gtk_style_context_add_class (gtk_widget_get_style_context (image), "dim-label");
+ gtk_box_pack_start (GTK_BOX (box), image, FALSE, TRUE, 0);
+
+ row = gtk_list_box_row_new ();
+ gtk_widget_show (row);
+ gtk_container_add (GTK_CONTAINER (row), box);
+ g_object_set_data_full (G_OBJECT (row), "recipe", g_object_ref (recipe), g_object_unref);
+ g_object_set_data (G_OBJECT (row), "check", image);
+
+ gtk_container_add (GTK_CONTAINER (list), row);
+
+}
+
+static void
+all_headers (GtkListBoxRow *row,
+ GtkListBoxRow *before,
+ gpointer user_data)
+{
+ GtkWidget *header;
+
+ header = gtk_list_box_row_get_header (row);
+ if (header)
+ return;
+
+ header = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
+ gtk_list_box_row_set_header (row, header);
+}
+
+static int
+sort_recipe_row (GtkListBoxRow *row1,
+ GtkListBoxRow *row2,
+ gpointer data)
+{
+ GrRecipe *r1, *r2;
+
+ r1 = GR_RECIPE (g_object_get_data (G_OBJECT (row1), "recipe"));
+ r2 = GR_RECIPE (g_object_get_data (G_OBJECT (row2), "recipe"));
+
+ return g_strcmp0 (gr_recipe_get_name (r1), gr_recipe_get_name (r2));
+}
+
+static void
+populate_recipe_list (GrRecipeExporter *exporter,
+ GtkWidget *list)
+{
+ GList *l;
+
+ gtk_list_box_set_header_func (GTK_LIST_BOX (list), all_headers, NULL, NULL);
+ gtk_list_box_set_sort_func (GTK_LIST_BOX (list), sort_recipe_row, NULL, NULL);
+
+ g_signal_connect (list, "row-activated", G_CALLBACK (row_activated), exporter);
+ for (l = exporter->recipes; l; l = l->next) {
+ GrRecipe *recipe = l->data;
+ add_recipe_row (exporter, list, recipe);
+ }
+}
+
+static void
+show_export_dialog (GrRecipeExporter *exporter)
+{
+ g_autoptr(GtkBuilder) builder = NULL;
+ GtkWidget *dialog;
+ GtkWidget *list;
+ g_autofree char *tmp = NULL;
+
+ builder = gtk_builder_new_from_resource ("/org/gnome/Recipes/recipe-export-dialog.ui");
+ dialog = GTK_WIDGET (gtk_builder_get_object (builder, "dialog"));
+ gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (exporter->window));
+
+ list = GTK_WIDGET (gtk_builder_get_object (builder, "recipe_list"));
+ populate_recipe_list (exporter, list);
+
+ exporter->dialog_heading = GTK_WIDGET (gtk_builder_get_object (builder, "heading"));
+ update_heading (exporter);
+
+ g_signal_connect (dialog, "response", G_CALLBACK (export_dialog_response), exporter);
+ gtk_widget_show (dialog);
+}
+
+void
+gr_recipe_exporter_export (GrRecipeExporter *exporter,
+ GrRecipe *recipe)
+{
+ GList *l;
+
+ // TODO: listen for ::recipe-removed and filter out the list
+ for (l = exporter->recipes; l; l = l->next) {
+ if (l->data == (gpointer)recipe)
+ goto dialog;
+ }
+
+ exporter->recipes = g_list_append (exporter->recipes, g_object_ref (recipe));
+
+dialog:
+ show_export_dialog (exporter);
+}
diff --git a/src/recipe-export-dialog.ui b/src/recipe-export-dialog.ui
new file mode 100644
index 0000000..3d15b1f
--- /dev/null
+++ b/src/recipe-export-dialog.ui
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface domain="recipes">
+ <object class="GtkDialog" id="dialog">
+ <property name="use-header-bar">1</property>
+ <property name="modal">1</property>
+ <property name="resizable">0</property>
+ <property name="destroy-with-parent">1</property>
+ <property name="default-width">420</property>
+ <property name="default-height">280</property>
+ <child type="action">
+ <object class="GtkButton" id="button_later">
+ <property name="visible">1</property>
+ <property name="label" translatable="yes">Close</property>
+ </object>
+ </child>
+ <child type="action">
+ <object class="GtkButton" id="button_now">
+ <property name="visible">1</property>
+ <property name="label" translatable="yes">Export</property>
+ </object>
+ </child>
+ <child internal-child="vbox">
+ <object class="GtkBox">
+ <property name="margin">20</property>
+ <property name="spacing">20</property>
+ <child>
+ <object class="GtkLabel" id="heading">
+ <property name="visible">1</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">1</property>
+ <property name="hscrollbar-policy">never</property>
+ <property name="vscrollbar-policy">automatic</property>
+ <property name="shadow-type">in</property>
+ <child>
+ <object class="GtkListBox" id="recipe_list">
+ <property name="visible">1</property>
+ <property name="selection-mode">none</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="cancel">button_later</action-widget>
+ <action-widget response="ok">button_now</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/src/recipes-ui.gresource.xml b/src/recipes-ui.gresource.xml
index 42e8e53..47b90e1 100644
--- a/src/recipes-ui.gresource.xml
+++ b/src/recipes-ui.gresource.xml
@@ -24,6 +24,7 @@
<file preprocess="xml-stripblanks">gr-window.ui</file>
<file preprocess="xml-stripblanks">chef-conflict-dialog.ui</file>
<file preprocess="xml-stripblanks">recipe-conflict-dialog.ui</file>
+ <file preprocess="xml-stripblanks">recipe-export-dialog.ui</file>
</gresource>
<gresource prefix="/org/gnome/Recipes/gtk">
<file preprocess="xml-stripblanks">menus.ui</file>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]