From 07f3f536cc09209308c1b6c011a2ba684e24f1ae Mon Sep 17 00:00:00 2001
From: Robert Fewell <14uBobIT@gmail.com>
Date: Wed, 1 Feb 2023 16:26:38 +0000
Subject: [PATCH] Allow to select saved report on print invoice button
This change was inspired by mildred's #PR1247 and uses the new
GncReportCombo to allow selection of 'Saved Invoice reports' when the
invoice print button is used or when printing multiple invoices.
This change presents a dialog with the default for the report combo set
to the properties setting. This dialog has a timeout which is
adjustable under properties and will stop if a key is pressed or combo
popped so that a different report template can be selected and used.
There is also an 'OK' button that stops the time out and prints and a
'Cancel' button which cancels the print.
---
gnucash/gnome/dialog-invoice.c | 185 ++++++++++++++++++++++--
gnucash/gtkbuilder/dialog-invoice.glade | 127 ++++++++++++++++
libgnucash/engine/gnc-optiondb.cpp | 6 +-
libgnucash/engine/qofbook.cpp | 15 ++
libgnucash/engine/qofbook.h | 5 +
libgnucash/engine/qofbookslots.h | 1 +
6 files changed, 328 insertions(+), 11 deletions(-)
diff --git a/gnucash/gnome/dialog-invoice.c b/gnucash/gnome/dialog-invoice.c
index aeca0844ee..786da96556 100644
--- a/gnucash/gnome/dialog-invoice.c
+++ b/gnucash/gnome/dialog-invoice.c
@@ -79,6 +79,7 @@
#include "dialog-doclink-utils.h"
#include "dialog-transfer.h"
#include "gnc-uri-utils.h"
+#include "gnc-report-combo.h"
#define DIALOG_NEW_INVOICE_CM_CLASS "dialog-new-invoice"
#define DIALOG_VIEW_INVOICE_CM_CLASS "dialog-view-invoice"
@@ -778,15 +779,159 @@ gnc_invoice_window_blankCB (GtkWidget *widget, gpointer data)
}
}
+typedef struct dialog_args
+{
+ GtkProgressBar *pb;
+ GtkWidget *dialog;
+ gdouble timeout;
+} dialog_args;
+
+static gboolean
+update_progress_bar (gpointer user_data)
+{
+ dialog_args *args = user_data;
+ GtkProgressBar *pb = args->pb;
+ gdouble frac = gtk_progress_bar_get_fraction (pb);
+ gdouble step = 0.1 / (args->timeout);
+
+ frac -= step;
+
+ if (frac < step)
+ {
+ gtk_dialog_response (GTK_DIALOG(args->dialog), GTK_RESPONSE_OK);
+ return FALSE;
+ }
+ gtk_progress_bar_set_fraction (pb, frac);
+ return TRUE;
+}
+
+static void
+combo_popped_cb (GObject *gobject,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ gboolean popup_shown;
+
+ g_object_get (G_OBJECT(gobject), "popup-shown", &popup_shown, NULL);
+
+ if (popup_shown)
+ g_source_remove_by_user_data (user_data);
+}
+
+static gboolean
+dialog_key_press_event_cb (GtkWidget *widget, GdkEventKey *event,
+ gpointer user_data)
+{
+ g_source_remove_by_user_data (user_data);
+ return FALSE;
+}
+
+static void
+combo_changed_cb (GtkComboBox *widget, gpointer user_data)
+{
+ g_source_remove_by_user_data (user_data);
+}
+
+/* This function will return the selected invoice report guid if
+ * the countdown times out or a selection is made and OK pressed.
+ *
+ * If cancel is pressed then it return a NULL
+ */
+static char*
+use_default_report_template_or_change (GtkWindow *parent)
+{
+ QofBook *book = gnc_get_current_book ();
+ GtkWidget *combo;
+ GtkBuilder *builder;
+ GtkWidget *dialog;
+ GtkWidget *ok_button;
+ GtkWidget *report_combo_hbox;
+ GtkWidget *progress_bar;
+ gchar *ret_guid = NULL;
+ gchar *rep_guid = NULL;
+ gchar *rep_name = NULL;
+ gint result;
+ gdouble timeout;
+ dialog_args *args;
+
+ timeout = qof_book_get_default_invoice_report_timeout (book);
+
+ if (timeout == 0)
+ return gnc_get_default_invoice_print_report ();
+
+ combo = gnc_default_invoice_report_combo ("gnc:custom-report-invoice-template-guids");
+
+ builder = gtk_builder_new ();
+ gnc_builder_add_from_file (builder, "dialog-invoice.glade", "invoice_print_dialog");
+
+ dialog = GTK_WIDGET(gtk_builder_get_object (builder, "invoice_print_dialog"));
+
+ gtk_window_set_transient_for (GTK_WINDOW(dialog), parent);
+
+ gtk_dialog_set_default_response (GTK_DIALOG(dialog), GTK_RESPONSE_OK);
+
+ ok_button = GTK_WIDGET(gtk_builder_get_object (builder, "ok_button"));
+ report_combo_hbox = GTK_WIDGET(gtk_builder_get_object (builder, "report_combo_hbox"));
+ progress_bar = GTK_WIDGET(gtk_builder_get_object (builder, "progress_bar"));
+
+ gtk_box_pack_start (GTK_BOX(report_combo_hbox), GTK_WIDGET(combo), TRUE, TRUE, 0);
+
+ gtk_widget_grab_focus (ok_button);
+
+ rep_name = qof_book_get_default_invoice_report_name (book);
+ rep_guid = gnc_get_default_invoice_print_report ();
+
+ gnc_report_combo_set_active (GNC_REPORT_COMBO(combo),
+ rep_guid,
+ rep_name);
+ g_free (rep_guid);
+ g_free (rep_name);
+
+ gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(progress_bar), 1);
+
+ args = g_malloc (sizeof(dialog_args));
+ args->dialog = dialog;
+ args->pb = GTK_PROGRESS_BAR(progress_bar);
+ args->timeout = timeout;
+
+ gtk_widget_show_all (dialog);
+
+ g_object_unref (G_OBJECT(builder));
+
+ g_signal_connect (G_OBJECT(combo), "changed",
+ G_CALLBACK(combo_changed_cb), args);
+
+ g_signal_connect (G_OBJECT(dialog), "key_press_event",
+ G_CALLBACK(dialog_key_press_event_cb), args);
+
+ g_signal_connect (G_OBJECT(combo), "notify::popup-shown",
+ G_CALLBACK (combo_popped_cb), args);
+
+ g_timeout_add (100, update_progress_bar, args);
+
+ result = gtk_dialog_run (GTK_DIALOG(dialog));
+
+ g_source_remove_by_user_data (args);
+
+ if (result == GTK_RESPONSE_OK)
+ ret_guid = gnc_report_combo_get_active_guid (GNC_REPORT_COMBO(combo));
+
+ gtk_widget_destroy (dialog);
+ g_free (args);
+
+ return ret_guid;
+}
+
static GncPluginPage *
-gnc_invoice_window_print_invoice(GtkWindow *parent, GncInvoice *invoice)
+gnc_invoice_window_print_invoice (GtkWindow *parent, GncInvoice *invoice,
+ const gchar *report_guid)
{
SCM func, arg, arg2;
SCM args = SCM_EOL;
SCM is_invoice_guid;
SCM scm_guid;
int report_id;
- char *report_guid = gnc_get_default_invoice_print_report ();
+ const gchar *use_report_guid = NULL;
GncPluginPage *reportPage = NULL;
g_return_val_if_fail (invoice, NULL);
@@ -795,13 +940,15 @@ gnc_invoice_window_print_invoice(GtkWindow *parent, GncInvoice *invoice)
scm_guid = scm_from_utf8_string (report_guid);
if (scm_is_false (scm_call_1 (is_invoice_guid, scm_guid)))
- report_guid = g_strdup (gnc_get_builtin_default_invoice_print_report ()); // fallback if the option lookup failed
+ use_report_guid = gnc_get_builtin_default_invoice_print_report (); // fallback if the option lookup failed
+ else
+ use_report_guid = report_guid;
func = scm_c_eval_string ("gnc:invoice-report-create");
g_return_val_if_fail (scm_is_procedure (func), NULL);
arg = SWIG_NewPointerObj(invoice, SWIG_TypeQuery("_p__gncInvoice"), 0);
- arg2 = scm_from_utf8_string (report_guid);
+ arg2 = scm_from_utf8_string (use_report_guid);
args = scm_cons2 (arg, arg2, args);
/* scm_gc_protect_object(func); */
@@ -816,7 +963,6 @@ gnc_invoice_window_print_invoice(GtkWindow *parent, GncInvoice *invoice)
reportPage = gnc_plugin_page_report_new (report_id);
gnc_main_window_open_page (GNC_MAIN_WINDOW (parent), reportPage);
}
- g_free (report_guid);
return reportPage;
}
@@ -841,9 +987,17 @@ gnc_invoice_window_printCB (GtkWindow* parent, gpointer data)
iw->reportPage))
gnc_plugin_page_report_reload (GNC_PLUGIN_PAGE_REPORT (iw->reportPage));
else
- iw->reportPage = gnc_invoice_window_print_invoice
- (parent, iw_get_invoice (iw));
+ {
+ gchar *report_guid = use_default_report_template_or_change (parent);
+ if (!report_guid)
+ return;
+
+ iw->reportPage = gnc_invoice_window_print_invoice (parent,
+ iw_get_invoice (iw),
+ report_guid);
+ g_free (report_guid);
+ }
gnc_main_window_open_page (GNC_MAIN_WINDOW (iw->dialog), iw->reportPage);
}
@@ -3104,8 +3258,9 @@ edit_invoice_cb (GtkWindow *dialog, gpointer inv, gpointer user_data)
struct multi_edit_invoice_data
{
- gpointer user_data;
+ gpointer user_data;
GtkWindow *parent;
+ gchar *report_guid;
};
static void
@@ -3272,27 +3427,37 @@ multi_post_invoice_cb (GtkWindow *dialog, GList *invoice_list, gpointer user_dat
static void print_one_invoice_cb(GtkWindow *dialog, gpointer data, gpointer user_data)
{
GncInvoice *invoice = data;
- gnc_invoice_window_print_invoice (dialog, invoice);
+ struct multi_edit_invoice_data *meid = user_data;
+ gnc_invoice_window_print_invoice (dialog, invoice, meid->report_guid);
}
static void
multi_print_invoice_one (gpointer data, gpointer user_data)
{
struct multi_edit_invoice_data *meid = user_data;
- print_one_invoice_cb (gnc_ui_get_main_window (GTK_WIDGET(meid->parent)), data, meid->user_data);
+ print_one_invoice_cb (gnc_ui_get_main_window (GTK_WIDGET(meid->parent)), data, meid);
}
static void
multi_print_invoice_cb (GtkWindow *dialog, GList *invoice_list, gpointer user_data)
{
+ gchar *report_guid = NULL;
struct multi_edit_invoice_data meid;
if (!gnc_list_length_cmp (invoice_list, 0))
return;
+ report_guid = use_default_report_template_or_change (dialog);
+
+ if (!report_guid)
+ return;
+
meid.user_data = user_data;
meid.parent = dialog;
+ meid.report_guid = report_guid;
+
g_list_foreach (invoice_list, multi_print_invoice_one, &meid);
+ g_free (report_guid);
}
static gpointer
diff --git a/gnucash/gtkbuilder/dialog-invoice.glade b/gnucash/gtkbuilder/dialog-invoice.glade
index 9e42c84d65..eba6cd0fbd 100644
--- a/gnucash/gtkbuilder/dialog-invoice.glade
+++ b/gnucash/gtkbuilder/dialog-invoice.glade
@@ -628,6 +628,133 @@
+
diff --git a/libgnucash/engine/gnc-optiondb.cpp b/libgnucash/engine/gnc-optiondb.cpp
index a3bd62990a..cb13ed7b93 100644
--- a/libgnucash/engine/gnc-optiondb.cpp
+++ b/libgnucash/engine/gnc-optiondb.cpp
@@ -1277,9 +1277,13 @@ gnc_option_db_book_options(GncOptionDB* odb)
N_("The ID for your company (eg 'Tax-ID: 00-000000)."),
empty_string);
gnc_register_invoice_print_report_option(odb, business_section,
- OPTION_NAME_DEFAULT_INVOICE_REPORT, "e",
+ OPTION_NAME_DEFAULT_INVOICE_REPORT, "e1",
N_("The invoice report to be used for printing."),
empty_string);
+ gnc_register_number_range_option(odb, business_section,
+ OPTION_NAME_DEFAULT_INVOICE_REPORT_TIMEOUT, "e2",
+ N_("Length of time to change the used invoice report. A value of 0 means disabled."),
+ 0.0, 0.0, 10.0, 1.0);
gnc_register_taxtable_option(odb, business_section,
N_("Default Customer TaxTable"), "f1",
N_("The default tax table to apply to customers."),
diff --git a/libgnucash/engine/qofbook.cpp b/libgnucash/engine/qofbook.cpp
index d2a9a76ec3..f34c1d5799 100644
--- a/libgnucash/engine/qofbook.cpp
+++ b/libgnucash/engine/qofbook.cpp
@@ -1098,6 +1098,21 @@ qof_book_get_default_invoice_report_name (const QofBook *book)
return report_name;
}
+gdouble
+qof_book_get_default_invoice_report_timeout (const QofBook *book)
+{
+ double ret = 0;
+ KvpFrame *root = qof_instance_get_slots (QOF_INSTANCE(book));
+ KvpValue *value = root->get_slot ({KVP_OPTION_PATH,
+ OPTION_SECTION_BUSINESS,
+ OPTION_NAME_DEFAULT_INVOICE_REPORT_TIMEOUT});
+
+ if (value)
+ ret = {value->get()};
+
+ return ret;
+}
+
/* Note: this will fail if the book slots we're looking for here are flattened at some point !
* When that happens, this function can be removed. */
static Path opt_name_to_path (const char* opt_name)
diff --git a/libgnucash/engine/qofbook.h b/libgnucash/engine/qofbook.h
index 3ab6c09533..dc52cf4aad 100644
--- a/libgnucash/engine/qofbook.h
+++ b/libgnucash/engine/qofbook.h
@@ -305,6 +305,11 @@ gchar * qof_book_get_default_invoice_report_guid (const QofBook *book);
*/
gchar * qof_book_get_default_invoice_report_name (const QofBook *book);
+/** Get the length of time available to change the used Invoice Report
+ * when printing Invoices
+ */
+gdouble qof_book_get_default_invoice_report_timeout (const QofBook *book);
+
/** Returns TRUE if this book uses split action field as the 'Num' field, FALSE
* if it uses transaction number field */
gboolean qof_book_use_split_action_for_num_field (const QofBook *book);
diff --git a/libgnucash/engine/qofbookslots.h b/libgnucash/engine/qofbookslots.h
index ea0e32bcd8..5b9eae8bdb 100644
--- a/libgnucash/engine/qofbookslots.h
+++ b/libgnucash/engine/qofbookslots.h
@@ -72,6 +72,7 @@
#define OPTION_SECTION_BUSINESS N_("Business")
#define OPTION_NAME_DEFAULT_INVOICE_REPORT N_("Default Invoice Report")
+#define OPTION_NAME_DEFAULT_INVOICE_REPORT_TIMEOUT N_("Default Invoice Report Timeout")
/** @} */