[recipes: 16/19] Add a quick print implementation for shopping lists
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [recipes: 16/19] Add a quick print implementation for shopping lists
- Date: Sat, 14 Jan 2017 19:38:33 +0000 (UTC)
commit e9846232c067e26f414269cc9465db26d21adb5f
Author: Matthias Clasen <mclasen redhat com>
Date: Sat Jan 14 14:26:04 2017 -0500
Add a quick print implementation for shopping lists
Just like the recipe printing, the layout needs work,
but the basic functionality is there.
src/Makefile.am | 2 +
src/gr-shopping-list-printer.c | 326 ++++++++++++++++++++++++++++++++++++++++
src/gr-shopping-list-printer.h | 43 ++++++
src/gr-shopping-page.c | 89 +++++++++++
src/gr-shopping-page.ui | 1 +
5 files changed, 461 insertions(+), 0 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index cf0411e..f3b0e27 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -100,6 +100,8 @@ recipes_SOURCES = \
gr-search-page.c \
gr-season.h \
gr-season.c \
+ gr-shopping-list-printer.h \
+ gr-shopping-list-printer.c \
gr-shopping-page.h \
gr-shopping-page.c \
gr-timer.h \
diff --git a/src/gr-shopping-list-printer.c b/src/gr-shopping-list-printer.c
new file mode 100644
index 0000000..2c9d1f3
--- /dev/null
+++ b/src/gr-shopping-list-printer.c
@@ -0,0 +1,326 @@
+/* gr-shopping-list-printer.c:
+ *
+ * Copyright (C) 2017 Matthias Clasen <mclasen redhat com>
+ *
+ * Licensed under the GNU General Public License Version 3
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+
+#include "gr-shopping-list-printer.h"
+
+
+static void
+shopping_item_free (gpointer data)
+{
+ ShoppingListItem *item = data;
+
+ g_free (item->amount);
+ g_free (item->name);
+ g_free (item);
+}
+
+static ShoppingListItem *
+shopping_item_copy (ShoppingListItem *item)
+{
+ ShoppingListItem *item2;
+
+ item2 = g_new (ShoppingListItem, 1);
+ item2->amount = g_strdup (item->amount);
+ item2->name = g_strdup (item->name);
+
+ return item2;
+}
+
+struct _GrShoppingListPrinter
+{
+ GObject parent_instance;
+
+ GtkWindow *window;
+
+ PangoLayout *layout;
+ GList *page_breaks;
+
+ GList *recipes;
+ GList *items;
+};
+
+G_DEFINE_TYPE (GrShoppingListPrinter, gr_shopping_list_printer, G_TYPE_OBJECT)
+
+static void
+gr_shopping_list_printer_class_init (GrShoppingListPrinterClass *klass)
+{
+}
+
+static void
+gr_shopping_list_printer_init (GrShoppingListPrinter *self)
+{
+}
+
+GrShoppingListPrinter *
+gr_shopping_list_printer_new (GtkWindow *parent)
+{
+ GrShoppingListPrinter *printer;
+
+ printer = g_object_new (GR_TYPE_SHOPPING_LIST_PRINTER, NULL);
+
+ printer->window = parent;
+
+ return printer;
+}
+
+static void
+begin_print (GtkPrintOperation *operation,
+ GtkPrintContext *context,
+ GrShoppingListPrinter *printer)
+{
+ double height;
+ PangoFontDescription *title_font;
+ PangoFontDescription *subtitle_font;
+ PangoFontDescription *body_font;
+ PangoAttrList *attrs;
+ PangoAttribute *attr;
+ double page_height;
+ GList *page_breaks;
+ g_autoptr(GString) s = NULL;
+ g_autoptr(GArray) images = NULL;
+ int num_lines;
+ int line;
+ g_autofree char **segs = NULL;
+ g_auto(GStrv) ings = NULL;
+ GList *l;
+
+ height = gtk_print_context_get_height (context);
+
+ title_font = pango_font_description_from_string ("Cantarell Bold 24");
+ subtitle_font = pango_font_description_from_string ("Cantarell Bold 18");
+ body_font = pango_font_description_from_string ("Cantarell 18");
+
+ printer->layout = gtk_print_context_create_pango_layout (context);
+ pango_layout_set_font_description (printer->layout, body_font);
+
+ s = g_string_new ("");
+
+ attrs = pango_attr_list_new ();
+ attr = pango_attr_font_desc_new (title_font);
+ attr->start_index = s->len;
+
+ g_string_append (s, _("Shopping List"));
+ g_string_append (s, "\n\n");
+
+ attr->end_index = s->len;
+ pango_attr_list_insert (attrs, attr);
+
+ attr = pango_attr_font_desc_new (subtitle_font);
+ attr->start_index = s->len;
+
+ g_string_append (s, _("For the following recipes"));
+ g_string_append (s, "\n\n");
+
+ attr->end_index = s->len;
+ pango_attr_list_insert (attrs, attr);
+
+ for (l = printer->recipes; l; l = l->next) {
+ GrRecipe *recipe = l->data;
+ g_string_append (s, gr_recipe_get_translated_name (recipe));
+ g_string_append (s, "\n");
+ }
+
+ g_string_append (s, "\n");
+
+ attr = pango_attr_font_desc_new (title_font);
+ attr->start_index = s->len;
+
+ g_string_append (s, _("Items"));
+ g_string_append (s, "\n");
+
+ attr->end_index = s->len;
+ pango_attr_list_insert (attrs, attr);
+
+ for (l = printer->items; l; l = l->next) {
+ ShoppingListItem *item = l->data;
+
+ g_string_append (s, "\n");
+ g_string_append (s, item->amount);
+ g_string_append (s, " ");
+ g_string_append (s, item->name);
+ }
+
+ pango_layout_set_text (printer->layout, s->str, s->len);
+ pango_layout_set_attributes (printer->layout, attrs);
+ pango_attr_list_unref (attrs);
+
+ num_lines = pango_layout_get_line_count (printer->layout);
+
+ page_height = 0;
+ page_breaks = NULL;
+
+ for (line = 0; line < num_lines; line++) {
+ PangoLayoutLine *layout_line;
+ PangoRectangle logical_rect;
+ double line_height;
+
+ layout_line = pango_layout_get_line (printer->layout, line);
+ pango_layout_line_get_extents (layout_line, NULL, &logical_rect);
+ line_height = logical_rect.height / 1024.0;
+ if (page_height + line_height > height) {
+ page_breaks = g_list_prepend (page_breaks, GINT_TO_POINTER (line));
+ page_height = 0;
+ }
+
+ page_height += line_height;
+ }
+
+ page_breaks = g_list_reverse (page_breaks);
+ gtk_print_operation_set_n_pages (operation, g_list_length (page_breaks) + 1);
+ printer->page_breaks = page_breaks;
+
+ pango_font_description_free (title_font);
+ pango_font_description_free (subtitle_font);
+ pango_font_description_free (body_font);
+}
+
+static void
+end_print (GtkPrintOperation *operation,
+ GtkPrintContext *context,
+ GrShoppingListPrinter *printer)
+{
+ g_clear_object (&printer->layout);
+ g_list_free (printer->page_breaks);
+ printer->page_breaks = NULL;
+ g_list_free_full (printer->recipes, g_object_unref);
+ printer->recipes = NULL;
+ g_list_free_full (printer->items, shopping_item_free);
+ printer->items = NULL;
+}
+
+static void
+draw_page (GtkPrintOperation *operation,
+ GtkPrintContext *context,
+ int page_nr,
+ GrShoppingListPrinter *printer)
+{
+ cairo_t *cr;
+ int start, end, i;
+ PangoLayoutIter *iter;
+ double start_pos;
+ GList *pagebreak;
+
+ cr = gtk_print_context_get_cairo_context (context);
+
+ if (page_nr == 0) {
+ start = 0;
+ start_pos = 0;
+ }
+ else {
+ pagebreak = g_list_nth (printer->page_breaks, page_nr - 1);
+ start = GPOINTER_TO_INT (pagebreak->data);
+ start_pos = 0;
+ }
+
+ pagebreak = g_list_nth (printer->page_breaks, page_nr);
+ if (pagebreak == NULL)
+ end = pango_layout_get_line_count (printer->layout);
+ else
+ end = GPOINTER_TO_INT (pagebreak->data);
+
+ i = 0;
+ iter = pango_layout_get_iter (printer->layout);
+ do {
+ PangoRectangle rect;
+ PangoLayoutLine *line;
+ int base;
+
+ if (i >= start) {
+ line = pango_layout_iter_get_line (iter);
+ pango_layout_iter_get_line_extents (iter, NULL, &rect);
+ base = pango_layout_iter_get_baseline (iter);
+ if (i == start)
+ start_pos -= rect.y / 1024.0;
+
+ cairo_move_to (cr, rect.x / 1024.0, base / 1024.0 + start_pos);
+ pango_cairo_show_layout_line (cr, line);
+ }
+ i++;
+
+ } while (i < end && pango_layout_iter_next_line (iter));
+
+ pango_layout_iter_free (iter);
+}
+
+static void
+print_done (GtkPrintOperation *operation,
+ GtkPrintOperationResult res,
+ GrShoppingListPrinter *printer)
+{
+ GError *error = NULL;
+
+ if (res == GTK_PRINT_OPERATION_RESULT_ERROR)
+ {
+
+ GtkWidget *error_dialog;
+
+ gtk_print_operation_get_error (operation, &error);
+
+ error_dialog = gtk_message_dialog_new (GTK_WINDOW (printer->window),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_CLOSE,
+ "%s\n%s",
+ _("Error printing file:"),
+ error ? error->message : _("No details"));
+ g_signal_connect (error_dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL);
+ gtk_widget_show (error_dialog);
+ }
+ else if (res == GTK_PRINT_OPERATION_RESULT_APPLY)
+ {
+ /* TODO: print settings */
+ }
+
+ if (!gtk_print_operation_is_finished (operation))
+ {
+ /* TODO monitoring */
+ }
+
+ g_object_unref (operation);
+}
+
+void
+gr_shopping_list_printer_print (GrShoppingListPrinter *printer,
+ GList *recipes,
+ GList *items)
+{
+ GtkPrintOperation *operation;
+
+ printer->recipes = g_list_copy_deep (recipes, (GCopyFunc)g_object_ref, NULL);
+ printer->items = g_list_copy_deep (items, (GCopyFunc)shopping_item_copy, NULL);
+
+ operation = gtk_print_operation_new ();
+
+ /* TODO: print settings */
+ /* TODO: page setup */
+
+ g_signal_connect (operation, "begin-print", G_CALLBACK (begin_print), printer);
+ g_signal_connect (operation, "end-print", G_CALLBACK (end_print), printer);
+ g_signal_connect (operation, "draw-page", G_CALLBACK (draw_page), printer);
+ g_signal_connect (operation, "done", G_CALLBACK (print_done), printer);
+
+ gtk_print_operation_set_allow_async (operation, TRUE);
+
+ gtk_print_operation_run (operation, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, printer->window, NULL);
+}
diff --git a/src/gr-shopping-list-printer.h b/src/gr-shopping-list-printer.h
new file mode 100644
index 0000000..9553644
--- /dev/null
+++ b/src/gr-shopping-list-printer.h
@@ -0,0 +1,43 @@
+/* gr-shopping-list-printer.h:
+ *
+ * Copyright (C) 2017 Matthias Clasen <mclasen redhat com>
+ *
+ * Licensed under the GNU General Public License Version 3
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+#include "gr-recipe.h"
+
+G_BEGIN_DECLS
+
+#define GR_TYPE_SHOPPING_LIST_PRINTER (gr_shopping_list_printer_get_type())
+
+G_DECLARE_FINAL_TYPE (GrShoppingListPrinter, gr_shopping_list_printer, GR, SHOPPING_LIST_PRINTER, GObject)
+
+GrShoppingListPrinter *gr_shopping_list_printer_new (GtkWindow *window);
+
+typedef struct {
+ char *amount;
+ char *name;
+} ShoppingListItem;
+
+void gr_shopping_list_printer_print (GrShoppingListPrinter *printer,
+ GList *recipes,
+ GList *items);
+
+G_END_DECLS
diff --git a/src/gr-shopping-page.c b/src/gr-shopping-page.c
index d865654..c105601 100644
--- a/src/gr-shopping-page.c
+++ b/src/gr-shopping-page.c
@@ -32,6 +32,7 @@
#include "gr-ingredients-list.h"
#include "gr-window.h"
#include "gr-number.h"
+#include "gr-shopping-list-printer.h"
struct _GrShoppingPage
{
@@ -48,6 +49,8 @@ struct _GrShoppingPage
GtkSizeGroup *group;
GHashTable *ingredients;
+
+ GrShoppingListPrinter *printer;
};
G_DEFINE_TYPE (GrShoppingPage, gr_shopping_page, GTK_TYPE_BOX)
@@ -62,6 +65,7 @@ shopping_page_finalize (GObject *object)
g_clear_object (&self->search);
g_clear_object (&self->group);
g_clear_pointer (&self->ingredients, g_hash_table_unref);
+ g_clear_object (&self->printer);
G_OBJECT_CLASS (gr_shopping_page_parent_class)->finalize (object);
}
@@ -259,6 +263,8 @@ add_ingredient_row (GrShoppingPage *page,
gtk_container_add (GTK_CONTAINER (page->ingredients_list), box);
row = gtk_widget_get_parent (box);
g_object_set_data (G_OBJECT (row), "check", button);
+ g_object_set_data (G_OBJECT (row), "unit", unit_label);
+ g_object_set_data (G_OBJECT (row), "ing", ing_label);
}
static void
@@ -432,6 +438,88 @@ clear_list (GrShoppingPage *page)
gr_window_go_back (GR_WINDOW (window));
}
+static GList *
+get_active_recipes (GrShoppingPage *page)
+{
+ GList *children, *l, *recipes;
+
+ recipes = NULL;
+ children = gtk_container_get_children (GTK_CONTAINER (page->recipe_list));
+ for (l = children; l; l = l->next) {
+ GtkWidget *tile = gtk_bin_get_child (GTK_BIN (l->data));
+ gboolean active;
+
+ g_object_get (tile, "active", &active, NULL);
+ if (active) {
+ GrRecipe *recipe;
+
+ recipe = gr_recipe_small_tile_get_recipe (GR_RECIPE_SMALL_TILE (tile));
+ recipes = g_list_append (recipes, g_object_ref (recipe));
+ }
+ }
+ g_list_free (children);
+
+ return recipes;
+}
+
+static GList *
+get_active_ingredients (GrShoppingPage *page)
+{
+ GList *children, *l, *ingredients;
+
+ ingredients = NULL;
+ children = gtk_container_get_children (GTK_CONTAINER (page->ingredients_list));
+ for (l = children; l; l = l->next) {
+ GtkWidget *row = l->data;
+ GtkWidget *check = GTK_WIDGET (g_object_get_data (G_OBJECT (row), "check"));
+
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check))) {
+ GtkWidget *unit = GTK_WIDGET (g_object_get_data (G_OBJECT (row), "unit"));
+ GtkWidget *ing = GTK_WIDGET (g_object_get_data (G_OBJECT (row), "ing"));
+ ShoppingListItem *item;
+
+ item = g_new (ShoppingListItem, 1);
+ item->amount = g_strdup (gtk_label_get_label (GTK_LABEL (unit)));
+ item->name = g_strdup (gtk_label_get_label (GTK_LABEL (ing)));
+ ingredients = g_list_append (ingredients, item);
+ }
+ }
+ g_list_free (children);
+
+ return ingredients;
+}
+
+static void
+item_free (gpointer data)
+{
+ ShoppingListItem *item = data;
+
+ g_free (item->amount);
+ g_free (item->name);
+ g_free (item);
+}
+
+static void
+print_list (GrShoppingPage *page)
+{
+ GList *recipes, *items;
+
+ if (!page->printer) {
+ GtkWidget *window;
+
+ window = gtk_widget_get_ancestor (GTK_WIDGET (page), GTK_TYPE_APPLICATION_WINDOW);
+ page->printer = gr_shopping_list_printer_new (GTK_WINDOW (window));
+ }
+
+ recipes = get_active_recipes (page);
+ items = get_active_ingredients (page);
+
+ gr_shopping_list_printer_print (page->printer, recipes, items);
+
+ g_list_free_full (recipes, g_object_unref);
+ g_list_free_full (items, item_free);
+}
+
static void
all_headers (GtkListBoxRow *row,
GtkListBoxRow *before,
@@ -485,6 +573,7 @@ gr_shopping_page_class_init (GrShoppingPageClass *klass)
gtk_widget_class_bind_template_child (widget_class, GrShoppingPage, ingredients_list);
gtk_widget_class_bind_template_callback (widget_class, clear_list);
+ gtk_widget_class_bind_template_callback (widget_class, print_list);
}
GtkWidget *
diff --git a/src/gr-shopping-page.ui b/src/gr-shopping-page.ui
index dfa140b..b43b30c 100644
--- a/src/gr-shopping-page.ui
+++ b/src/gr-shopping-page.ui
@@ -135,6 +135,7 @@
<property name="visible">1</property>
<property name="use-underline">1</property>
<property name="label" translatable="yes">_Print</property>
+ <signal name="clicked" handler="print_list" swapped="yes"/>
</object>
<packing>
<property name="pack-type">end</property>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]