GnuCash  5.6-150-g038405b370+
gnc-plugin-page-report.cpp
1 /* gnc-plugin-page-report.c
2  * Copyright (C) 2004 Joshua Sled <jsled@asynchronous.org>
3  * Copyright (C) 2005 David Hampton <hampton@employees.org>
4  *
5  * Originally from window-report.c:
6  * Copyright (C) 1997 Robin D. Clark
7  * Copyright (C) 1998 Linas Vepstas
8  * Copyright (C) 1999 Jeremy Collins ( gtk-xmhtml port )
9  * Copyright (C) 2000 Dave Peticolas
10  * Copyright (C) 2000 Bill Gribble
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License as
14  * published by the Free Software Foundation; either version 2 of
15  * the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, contact:
24  *
25  * Free Software Foundation Voice: +1-617-542-5942
26  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
27  * Boston, MA 02110-1301, USA gnu@gnu.org
28  */
29 
39 #include <gtk/gtk.h>
40 #include <glib/gi18n.h>
41 #include <glib/gstdio.h>
42 #include <gnc-optiondb-impl.hpp>
43 #include <libguile.h>
44 
45 #include <config.h>
46 
47 #include <sys/stat.h>
48 #include <errno.h>
49 
50 #include <gnc-glib-utils.h>
51 #include "gfec.h"
52 #include "dialog-custom-report.h"
53 #include "dialog-utils.h"
54 #include "gnc-component-manager.h"
55 #include "gnc-engine.h"
56 #include "gnc-gnome-utils.h"
57 #include "gnc-guile-utils.h"
58 #include "gnc-html-history.h"
59 #include "gnc-html.h"
60 #include "gnc-html-factory.hpp"
61 #include "gnc-file.h"
62 #include "gnc-filepath-utils.h"
63 #include "gnc-gtk-utils.h"
64 #include "gnc-plugin.h"
65 #include "gnc-plugin-page-report.h"
67 #include "gnc-prefs.h"
68 #include "gnc-session.h"
69 #include "gnc-ui-util.h"
70 #include "gnc-ui.h"
71 #include "gnc-window.h"
72 #include "window-report.h"
73 #include "swig-runtime.h"
74 #include "guile-mappings.h"
75 #include "gnc-icons.h"
76 #include "print-session.h"
77 
78 #include <unordered_map>
79 #include <memory>
80 #include <gnc-report.h>
81 
82 /* NW: you can add GNC_MOD_REPORT to gnc-engine.h
83 or simply define it locally. Any unique string with
84 a gnucash- prefix will do. Then just set a log level
85 with qof_log_set_level().*/
86 static QofLogModule log_module = GNC_MOD_GUI;
87 
88 // A static GHashTable to record the usage count for each printer
89 // output name.
90 
91 static std::unordered_map<std::string,unsigned> static_report_printnames;
92 
93 // Property-id values.
94 enum
95 {
96  PROP_0,
97  PROP_REPORT_ID,
98 };
99 
101 {
103  int reportId;
104  gint component_manager_id;
105  GSimpleActionGroup* action_group;
110  size_t option_change_cb_id = 0;
111 
112  /* initial_report is special; it's the one that's saved and
113  * restored. The name_change_callback only gets called when
114  * the initial_report name is changed. */
115  SCM initial_report;
116  GncOptionDB * initial_odb;
117  size_t name_change_cb_id;
118 
119  /* keep a list of edited reports so that we can destroy them when
120  * the window is closed. */
121  SCM edited_reports;
122 
123  /* The page is in the process of reloading the html */
124  gboolean reloading;
125  gboolean loaded;
126 
128 // gnc_html *html;
129  GncHtml *html;
130  gboolean webkit2;
131 
133  GtkContainer *container;
135 
136 G_DEFINE_TYPE_WITH_PRIVATE(GncPluginPageReport, gnc_plugin_page_report, GNC_TYPE_PLUGIN_PAGE)
137 
138 #define GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(o) \
139  ((GncPluginPageReportPrivate*)gnc_plugin_page_report_get_instance_private((GncPluginPageReport*)o))
140 
141 static GObject *gnc_plugin_page_report_constructor(GType this_type, guint n_properties, GObjectConstructParam *properties);
142 static void gnc_plugin_page_report_finalize (GObject *object);
143 static void gnc_plugin_page_report_setup( GncPluginPage *ppage );
144 
145 static void gnc_plugin_page_report_constr_init(GncPluginPageReport *plugin_page, gint reportId);
146 
147 static GtkWidget* gnc_plugin_page_report_create_widget( GncPluginPage *plugin_page );
148 static void gnc_plugin_page_report_destroy_widget( GncPluginPage *plugin_page );
149 static void gnc_plugin_page_report_save_page (GncPluginPage *plugin_page, GKeyFile *file, const gchar *group);
150 static GncPluginPage *gnc_plugin_page_report_recreate_page (GtkWidget *window, GKeyFile *file, const gchar *group);
151 static void gnc_plugin_page_report_name_changed (GncPluginPage *page, const gchar *name);
152 static void gnc_plugin_page_report_update_edit_menu (GncPluginPage *page, gboolean hide);
153 static gboolean gnc_plugin_page_report_finish_pending (GncPluginPage *page);
154 static void gnc_plugin_page_report_load_uri (GncPluginPage *page);
155 
156 static int gnc_plugin_page_report_check_urltype(URLType t);
157 //static void gnc_plugin_page_report_load_cb(gnc_html * html, URLType type,
158 static void gnc_plugin_page_report_load_cb(GncHtml * html, URLType type,
159  const gchar * location, const gchar * label,
160  gpointer data);
161 static void gnc_plugin_page_report_refresh (gpointer data);
162 static void gnc_plugin_page_report_set_fwd_button(GncPluginPageReport * page, int enabled);
163 static void gnc_plugin_page_report_set_back_button(GncPluginPageReport * page, int enabled);
164 static void gnc_plugin_page_report_history_destroy_cb(gnc_html_history_node * node, gpointer user_data);
165 static void close_handler(gpointer user_data);
166 void gnc_plugin_page_report_destroy(GncPluginPageReportPrivate *priv);
167 static void gnc_plugin_page_report_option_change_cb(gpointer data);
168 
169 void gnc_plugin_page_report_remove_edited_report(GncPluginPageReportPrivate *priv, SCM report);
170 void gnc_plugin_page_report_add_edited_report(GncPluginPageReportPrivate *priv, SCM report);
171 static void gnc_plugin_page_report_menu_updates (GncPluginPage *plugin_page);
172 void gnc_plugin_page_report_raise_editor(SCM report);
173 
174 static void gnc_plugin_page_report_forw_cb (GSimpleAction *simple, GVariant *parameter, gpointer user_data);
175 static void gnc_plugin_page_report_back_cb (GSimpleAction *simple, GVariant *parameter, gpointer user_data);
176 static void gnc_plugin_page_report_reload_cb (GSimpleAction *simple, GVariant *parameter, gpointer user_data);
177 static void gnc_plugin_page_report_stop_cb (GSimpleAction *simple, GVariant *parameter, gpointer user_data);
178 static void gnc_plugin_page_report_save_cb (GSimpleAction *simple, GVariant *parameter, gpointer user_data);
179 static void gnc_plugin_page_report_save_as_cb (GSimpleAction *simple, GVariant *parameter, gpointer user_data);
180 static void gnc_plugin_page_report_export_cb (GSimpleAction *simple, GVariant *parameter, gpointer user_data);
181 static void gnc_plugin_page_report_options_cb (GSimpleAction *simple, GVariant *parameter, gpointer user_data);
182 static void gnc_plugin_page_report_print_cb (GSimpleAction *simple, GVariant *parameter, gpointer user_data);
183 static void gnc_plugin_page_report_exportpdf_cb (GSimpleAction *simple, GVariant *parameter, gpointer user_data);
184 static void gnc_plugin_page_report_copy_cb (GSimpleAction *simple, GVariant *parameter, gpointer user_data);
185 static void gnc_plugin_page_report_edit_tax_cb (GSimpleAction *simple, GVariant *parameter, gpointer user_data);
186 
187 static GActionEntry report_actions[] =
188 {
189  { "FilePrintAction", gnc_plugin_page_report_print_cb, nullptr, nullptr, nullptr },
190  { "FilePrintPDFAction", gnc_plugin_page_report_exportpdf_cb, nullptr, nullptr, nullptr },
191  { "EditCopyAction", gnc_plugin_page_report_copy_cb, nullptr, nullptr, nullptr },
192  { "EditTaxOptionsAction", gnc_plugin_page_report_edit_tax_cb, nullptr, nullptr, nullptr },
193  { "ViewRefreshAction", gnc_plugin_page_report_reload_cb, nullptr, nullptr, nullptr },
194  { "ReportSaveAction", gnc_plugin_page_report_save_cb, nullptr, nullptr, nullptr },
195  { "ReportSaveAsAction", gnc_plugin_page_report_save_as_cb, nullptr, nullptr, nullptr },
196  { "ReportExportAction", gnc_plugin_page_report_export_cb, nullptr, nullptr, nullptr },
197  { "ReportOptionsAction", gnc_plugin_page_report_options_cb, nullptr, nullptr, nullptr },
198  { "ReportBackAction", gnc_plugin_page_report_back_cb, nullptr, nullptr, nullptr },
199  { "ReportForwAction", gnc_plugin_page_report_forw_cb, nullptr, nullptr, nullptr },
200  { "ReportReloadAction", gnc_plugin_page_report_reload_cb, nullptr, nullptr, nullptr },
201  { "ReportStopAction", gnc_plugin_page_report_stop_cb, nullptr, nullptr, nullptr },
202 };
203 static guint num_report_actions = G_N_ELEMENTS(report_actions);
204 static const gchar *initially_insensitive_actions[] =
205 {
206  nullptr
207 };
208 
209 static const gchar *disable_during_load_actions[] =
210 {
211  "FilePrintAction",
212  "FilePrintPDFAction",
213  "ReportOptionsAction",
214  nullptr
215 };
216 
218 static const gchar *gnc_plugin_load_ui_items [] =
219 {
220  "FilePlaceholder3",
221  "FilePlaceholder4",
222  "FilePlaceholder5",
223  "EditPlaceholder3",
224  "EditPlaceholder5",
225  "EditPlaceholder6",
226  "ViewPlaceholder4",
227  "ReportsPlaceholder1",
228  NULL,
229 };
230 
232 static GncToolBarShortNames toolbar_labels[] =
233 {
234  { "FilePrintAction", N_("Print") },
235  { "ReportExportAction", N_("Export") },
236  { "ReportOptionsAction", N_("Options") },
237  /* Translators: This string is meant to be a short alternative for "Save Report Configuration"
238  to be used as toolbar button label. */
239  { "ReportSaveAction", N_("Save Config") },
240  /* Translators: This string is meant to be a short alternative for "Save Report Configuration As…"
241  to be used as toolbar button label. */
242  { "ReportSaveAsAction", N_("Save Config As…") },
243  { "FilePrintPDFAction", N_("Export as PDF") },
244  { nullptr, nullptr },
245 };
246 
247 static void
248 gnc_plugin_page_report_get_property( GObject *obj,
249  guint prop_id,
250  GValue *value,
251  GParamSpec *pspec )
252 {
253  GncPluginPageReport *rep;
255 
256  rep = GNC_PLUGIN_PAGE_REPORT( obj );
257  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(rep);
258 
259  switch ( prop_id )
260  {
261  case PROP_REPORT_ID:
262  g_value_set_int( value, priv->reportId );
263  break;
264  default:
265  PERR( "Unknown property id %d", prop_id );
266  break;
267  }
268 }
269 
270 static void
271 gnc_plugin_page_report_set_property( GObject *obj,
272  guint prop_id,
273  const GValue *value,
274  GParamSpec *pspec )
275 {
276  GncPluginPageReport *rep;
278 
279  rep = GNC_PLUGIN_PAGE_REPORT( obj );
280  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(rep);
281 
282  DEBUG( "setting property with id %d / %p to value %d",
283  prop_id, priv, g_value_get_int( value ) );
284 
285  switch ( prop_id )
286  {
287  case PROP_REPORT_ID:
288  priv->reportId = g_value_get_int( value );
289  break;
290  default:
291  PERR( "unknown property id %d", prop_id );
292  break;
293  }
294 
295 }
296 
301 static gboolean
302 gnc_plugin_page_report_focus_widget (GncPluginPage *report_plugin_page)
303 {
304  if (GNC_IS_PLUGIN_PAGE_REPORT(report_plugin_page))
305  {
306  GncPluginPageReportPrivate *priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report_plugin_page);
307  GtkWidget *window;
308  GAction *action;
309 
310  if (!priv)
311  return FALSE;
312 
313  /* Disable the Transaction Menu */
314  action = gnc_main_window_find_action (GNC_MAIN_WINDOW(report_plugin_page->window), "TransactionAction");
315  g_simple_action_set_enabled (G_SIMPLE_ACTION(action), FALSE);
316  /* Disable the Schedule menu */
317  action = gnc_main_window_find_action (GNC_MAIN_WINDOW(report_plugin_page->window), "ScheduledAction");
318  g_simple_action_set_enabled (G_SIMPLE_ACTION(action), FALSE);
319 
320  gnc_main_window_update_menu_and_toolbar (GNC_MAIN_WINDOW(report_plugin_page->window),
321  report_plugin_page,
322  gnc_plugin_load_ui_items);
323 
324  // setup any short toolbar names
325  gnc_main_window_init_short_names (GNC_MAIN_WINDOW(report_plugin_page->window), toolbar_labels);
326 
327  gnc_plugin_page_report_menu_updates (report_plugin_page);
328 
329  window = gnc_plugin_page_get_window (report_plugin_page);
330 
331  if (window && !gnc_main_window_is_restoring_pages (GNC_MAIN_WINDOW(window)))
332  {
333  GtkWidget *widget = gnc_html_get_webview (priv->html);
334 
335  if (!priv->loaded) // so we only do the load once
336  gnc_plugin_page_report_load_uri (report_plugin_page);
337 
338  if (widget && GTK_IS_WIDGET(widget))
339  {
340  if (!gtk_widget_is_focus (GTK_WIDGET(widget)))
341  gtk_widget_grab_focus (GTK_WIDGET(widget));
342  }
343  }
344  }
345  return FALSE;
346 }
347 
348 static void
349 gnc_plugin_page_report_class_init (GncPluginPageReportClass *klass)
350 {
351  GObjectClass *object_class = G_OBJECT_CLASS (klass);
352  GncPluginPageClass *gnc_plugin_page_class = GNC_PLUGIN_PAGE_CLASS(klass);
353 
354  object_class->constructor = gnc_plugin_page_report_constructor;
355  object_class->finalize = gnc_plugin_page_report_finalize;
356 
357  object_class->set_property = gnc_plugin_page_report_set_property;
358  object_class->get_property = gnc_plugin_page_report_get_property;
359 
360  gnc_plugin_page_class->tab_icon = GNC_ICON_ACCOUNT_REPORT;
361  gnc_plugin_page_class->plugin_name = GNC_PLUGIN_PAGE_REPORT_NAME;
362 
363  gnc_plugin_page_class->create_widget = gnc_plugin_page_report_create_widget;
364  gnc_plugin_page_class->destroy_widget = gnc_plugin_page_report_destroy_widget;
365  gnc_plugin_page_class->save_page = gnc_plugin_page_report_save_page;
366  gnc_plugin_page_class->recreate_page = gnc_plugin_page_report_recreate_page;
367  gnc_plugin_page_class->page_name_changed = gnc_plugin_page_report_name_changed;
368  gnc_plugin_page_class->update_edit_menu_actions = gnc_plugin_page_report_update_edit_menu;
369  gnc_plugin_page_class->finish_pending = gnc_plugin_page_report_finish_pending;
370  gnc_plugin_page_class->focus_page_function = gnc_plugin_page_report_focus_widget;
371 
372  // create the "reportId" property
373  auto paramspec{static_cast<GParamFlags>(G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)};
374  g_object_class_install_property( object_class,
375  PROP_REPORT_ID,
376  g_param_spec_int( "report-id",
377  _("The numeric ID of the report."),
378  _("The numeric ID of the report."),
379  -1, G_MAXINT, -1,
380  paramspec));
381 }
382 
383 static void
384 gnc_plugin_page_report_finalize (GObject *object)
385 {
386  g_return_if_fail (GNC_IS_PLUGIN_PAGE_REPORT (object));
387 
388  ENTER("object %p", object);
389  G_OBJECT_CLASS (gnc_plugin_page_report_parent_class)->finalize (object);
390  LEAVE(" ");
391 }
392 
393 static void
394 gnc_plugin_page_report_set_progressbar (GncPluginPage *page, gboolean set)
395 {
396  GtkWidget *progressbar;
397  GtkAllocation allocation;
398 
399  progressbar = gnc_window_get_progressbar (GNC_WINDOW(page->window));
400  gtk_widget_get_allocation (GTK_WIDGET(progressbar), &allocation);
401 
402  // this sets the minimum size of the progressbar to that allocated
403  if (set)
404  gtk_widget_set_size_request (GTK_WIDGET(progressbar), -1, allocation.height);
405  else
406  gtk_widget_set_size_request (GTK_WIDGET(progressbar), -1, -1); //reset
407 }
408 
409 static void
410 gnc_plugin_page_report_load_uri (GncPluginPage *page)
411 {
412  GncPluginPageReport *report;
414  GncPluginPage *weak_page = page;
415  URLType type;
416  char * id_name;
417  char * child_name;
418  char * url_location = nullptr;
419  char * url_label = nullptr;
420 
421  report = GNC_PLUGIN_PAGE_REPORT(page);
422  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
423  if (!priv)
424  return; // No priv means the page doesn't exist anymore.
425 
426  DEBUG( "Load uri id=%d", priv->reportId );
427  id_name = g_strdup_printf("id=%d", priv->reportId );
428  child_name = gnc_build_url( URL_TYPE_REPORT, id_name, nullptr );
429  type = gnc_html_parse_url( priv->html, child_name, &url_location, &url_label);
430  DEBUG( "passing id_name=[%s] child_name=[%s] type=[%s], location=[%s], label=[%s]",
431  id_name, child_name ? child_name : "(null)",
432  type ? type : "(null)", url_location ? url_location : "(null)",
433  url_label ? url_label : "(null)" );
434 
435  g_free(id_name);
436  g_free(child_name);
437 
438  g_object_add_weak_pointer(G_OBJECT(page), (gpointer*)(&weak_page));
439  gtk_widget_show_all( GTK_WIDGET(priv->container) );
440 
441  priv->loaded = TRUE;
442 
443  // this sets the window for the progressbar
444  gnc_window_set_progressbar_window( GNC_WINDOW(page->window) );
445 
446  // this sets the minimum size of the progressbar to that allocated
447  gnc_plugin_page_report_set_progressbar( page, TRUE );
448 
449  gnc_html_show_url(priv->html, type, url_location, url_label, 0);
450  g_free(url_location);
451 
452  if (weak_page)
453  {
454  gnc_plugin_page_report_set_progressbar( page, FALSE );
455  g_object_remove_weak_pointer(G_OBJECT(page), (gpointer*)(&weak_page));
456  }
457 
458  gnc_plugin_set_actions_enabled(G_ACTION_MAP(priv->action_group),
459  disable_during_load_actions, TRUE);
460  // this resets the window for the progressbar to nullptr
461  gnc_window_set_progressbar_window( nullptr );
462 }
463 
464 /* used to capture Ctrl+Alt+PgUp/Down for tab selection */
465 static gboolean
466 webkit_key_press_event_cb (GtkWidget *widget, GdkEventKey *event, gpointer user_data)
467 {
468  GncPluginPageReport *report = GNC_PLUGIN_PAGE_REPORT(user_data);
469  GncPluginPageReportPrivate *priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
470  GdkModifierType modifiers = gtk_accelerator_get_default_mod_mask ();
471  GtkWidget *window = gnc_plugin_page_get_window (GNC_PLUGIN_PAGE(report));
472 
473  if (GNC_PLUGIN_PAGE(report) != gnc_main_window_get_current_page (GNC_MAIN_WINDOW(window)))
474  return FALSE;
475 
476  if ((event->keyval == GDK_KEY_Page_Up || event->keyval == GDK_KEY_Page_Down ||
477  event->keyval == GDK_KEY_KP_Page_Up || event->keyval == GDK_KEY_KP_Page_Down)
478  && (event->state & modifiers) == (GDK_CONTROL_MASK | GDK_MOD1_MASK))
479  {
480  GtkNotebook *notebook = GTK_NOTEBOOK(gtk_widget_get_parent (GTK_WIDGET(priv->container)));
481  gint pages = gtk_notebook_get_n_pages (notebook);
482  gint current_page = gtk_notebook_get_current_page (notebook);
483 
484  if (event->keyval == GDK_KEY_Page_Up || event->keyval == GDK_KEY_KP_Page_Up)
485  {
486  if (current_page == 0)
487  gtk_notebook_set_current_page (notebook, pages - 1);
488  else
489  gtk_notebook_prev_page (notebook);
490  }
491  else
492  {
493  if (pages == current_page + 1)
494  gtk_notebook_set_current_page (notebook, 0);
495  else
496  gtk_notebook_next_page (notebook);
497  }
498  return TRUE;
499  }
500  return FALSE;
501 }
502 
503 static
504 GtkWidget*
505 gnc_plugin_page_report_create_widget( GncPluginPage *page )
506 {
507  GncPluginPageReport *report;
509  GtkWindow *topLvl;
510  GtkWidget *webview;
511  URLType type;
512  char * id_name;
513  char * child_name;
514  char * url_location = nullptr;
515  char * url_label = nullptr;
516 
517  ENTER("page %p", page);
518 
519  report = GNC_PLUGIN_PAGE_REPORT(page);
520  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
521 
522 #ifndef WEBKIT1
523  /* Hide the ExportPdf action for Webkit2 */
524  priv->webkit2 = TRUE;
525 #endif
526 
527  topLvl = gnc_ui_get_main_window (nullptr);
528 // priv->html = gnc_html_new( topLvl );
529  priv->html = gnc_html_factory_create_html();
530  gnc_html_set_parent( priv->html, topLvl );
531  priv->loaded = FALSE;
532 
533  gnc_html_history_set_node_destroy_cb(gnc_html_get_history(priv->html),
534  gnc_plugin_page_report_history_destroy_cb,
535  (gpointer)priv);
536 
537  priv->container = GTK_CONTAINER(gtk_frame_new(nullptr));
538  gtk_frame_set_shadow_type(GTK_FRAME(priv->container), GTK_SHADOW_NONE);
539 
540  // Set the name for this widget so it can be easily manipulated with css
541  gtk_widget_set_name (GTK_WIDGET(priv->container), "gnc-id-report-page");
542 
543  gtk_container_add(GTK_CONTAINER(priv->container),
544  gnc_html_get_widget(priv->html));
545 
546  priv->component_manager_id =
547  gnc_register_gui_component(WINDOW_REPORT_CM_CLASS, nullptr,
548  close_handler, page);
549  gnc_gui_component_set_session(priv->component_manager_id,
550  gnc_get_current_session());
551 
552  gnc_html_set_urltype_cb(priv->html, gnc_plugin_page_report_check_urltype);
553  gnc_html_set_load_cb(priv->html, gnc_plugin_page_report_load_cb, report);
554 
555  /* We need to call the load call back so the report appears to have been run
556  so it will get saved properly if the report is not realized in session */
557  id_name = g_strdup_printf("id=%d", priv->reportId );
558  child_name = gnc_build_url( URL_TYPE_REPORT, id_name, nullptr );
559  type = gnc_html_parse_url( priv->html, child_name, &url_location, &url_label);
560 
561  gnc_plugin_page_report_load_cb (priv->html, type, id_name, url_label, report);
562  g_free(id_name);
563  g_free(child_name);
564  g_free (url_label);
565  g_free (url_location);
566 
567  // FIXME. This is f^-1(f(x)), isn't it?
568  DEBUG( "id=%d", priv->reportId );
569 
570  g_signal_connect (G_OBJECT(page), "inserted",
571  G_CALLBACK(gnc_plugin_page_inserted_cb),
572  nullptr);
573 
574  // used to capture Ctrl+Alt+PgUp/Down for tab selection
575  webview = gnc_html_get_webview (priv->html);
576  if (webview)
577  {
578  gtk_widget_add_events (webview, gtk_widget_get_events (webview) |
579  GDK_KEY_PRESS_MASK);
580 
581  g_signal_connect (webview, "key-press-event",
582  G_CALLBACK(webkit_key_press_event_cb),
583  page);
584  }
585 
586  gtk_widget_show_all( GTK_WIDGET(priv->container) );
587  LEAVE("container %p", priv->container);
588  return GTK_WIDGET( priv->container );
589 }
590 
591 /********************************************************************
592  * gnc_plugin_page_report_check_urltype
593  * is it OK to show a certain URLType in this window?
594  ********************************************************************/
595 static int
596 gnc_plugin_page_report_check_urltype(URLType t)
597 {
598  if (!g_strcmp0 (t, URL_TYPE_REPORT))
599  {
600  return TRUE;
601  }
602  else
603  {
604  return FALSE;
605  }
606 }
607 
612 static void
613 gnc_plugin_page_report_setup( GncPluginPage *ppage )
614 {
615  GncPluginPageReport *report = GNC_PLUGIN_PAGE_REPORT(ppage);
617  SCM set_needs_save = scm_c_eval_string("gnc:report-set-needs-save?!");
618  SCM inst_report;
619  int report_id;
620 
621  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
622  priv->cur_report = SCM_BOOL_F;
623  priv->initial_report = SCM_BOOL_F;
624  priv->edited_reports = SCM_EOL;
625  priv->name_change_cb_id = 0;
626 
627  g_object_get( ppage, "report-id", &report_id, nullptr );
628 
629  PINFO("report-id: %d\n", report_id);
630 
631  /* get the inst-report from the Scheme-side hash, and get its
632  * options and editor thunk */
633  if ((inst_report = gnc_report_find(report_id)) == SCM_BOOL_F)
634  {
635  return;
636  }
637 
638  if (priv->initial_report == SCM_BOOL_F)
639  {
640  priv->initial_report = inst_report;
641  scm_gc_protect_object(priv->initial_report);
642  }
643 
644  // all reports need [to be] saved immediately after they're created.
645  PINFO("set needs save");
646  scm_call_2(set_needs_save, inst_report, SCM_BOOL_T);
647 }
648 
649 /********************************************************************
650  * gnc_plugin_page_report_load_cb
651  * called after a report is loaded into the gnc_html widget
652  ********************************************************************/
653 static void
654 gnc_plugin_page_report_load_cb (GncHtml * html, URLType type,
655  const gchar * location, const gchar * label,
656  gpointer data)
657 {
658  GncPluginPageReport *report = GNC_PLUGIN_PAGE_REPORT(data);
660  int report_id;
661  SCM set_needs_save = scm_c_eval_string("gnc:report-set-needs-save?!");
662  SCM inst_report;
663 
664  ENTER( "load_cb: type=[%s], location=[%s], label=[%s]",
665  type ? type : "(null)", location ? location : "(null)",
666  label ? label : "(null)" );
667 
668  /* we get this callback if a new report is requested to be loaded OR
669  * if any URL is clicked. If an options URL is clicked, we want to
670  * know about it */
671  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
672  if (!g_strcmp0 (type, URL_TYPE_REPORT)
673  && location
674  && (strlen(location) > 3)
675  && !strncmp("id=", location, 3))
676  {
677  report_id = gnc_report_id_string_to_report_id (location + 3);
678  if (report_id < 0)
679  {
680  LEAVE ("id_string error %s", location);
681  return;
682  }
683  DEBUG( "parsed id=%d", report_id );
684  }
685  else if (!g_strcmp0( type, URL_TYPE_OPTIONS)
686  && location
687  && (strlen(location) > 10)
688  && !strncmp("report-id=", location, 10))
689  {
690  report_id = atoi(location + 10);
691  inst_report = gnc_report_find(report_id);
692  if (inst_report != SCM_BOOL_F)
693  {
694  gnc_plugin_page_report_add_edited_report(priv, inst_report);
695  }
696  LEAVE("");
697  return;
698  }
699  else
700  {
701  LEAVE( " unknown URL type [%s] location [%s]", type, location );
702  return;
703  }
704 
705  /* get the inst-report from the hash, and get its
706  * options and editor thunk */
707  if ((inst_report = gnc_report_find(report_id)) == SCM_BOOL_F)
708  {
709  LEAVE( "error getting inst_report" );
710  return;
711  }
712 
713  if (priv->initial_report == SCM_BOOL_F)
714  {
715  priv->initial_report = inst_report;
716  scm_gc_protect_object(priv->initial_report);
717 
718  DEBUG("calling set_needs_save for report with id=%d", report_id);
719  scm_call_2(set_needs_save, inst_report, SCM_BOOL_T);
720 
721  priv->initial_odb = gnc_get_report_optiondb(inst_report);
722 
723  priv->name_change_cb_id =
724  priv->initial_odb->register_callback(
725  gnc_plugin_page_report_refresh, priv);
726 
727  }
728 
729  if ((priv->cur_report != SCM_BOOL_F) && (priv->cur_odb != nullptr))
730  {
731  priv->cur_odb->unregister_callback(priv->option_change_cb_id);
732  priv->option_change_cb_id = 0;
733  priv->cur_odb = nullptr;
734  }
735 
736  if (priv->cur_report != SCM_BOOL_F)
737  scm_gc_unprotect_object(priv->cur_report);
738  priv->cur_report = inst_report;
739  scm_gc_protect_object(priv->cur_report);
740 
741  priv->cur_odb = gnc_get_report_optiondb(inst_report);
742 
743  priv->option_change_cb_id =
744  priv->cur_odb->register_callback(
745  gnc_plugin_page_report_option_change_cb, report);
746 
747  if (gnc_html_history_forward_p(gnc_html_get_history(priv->html)))
748  {
749  gnc_plugin_page_report_set_fwd_button(report, TRUE);
750  }
751  else
752  {
753  gnc_plugin_page_report_set_fwd_button(report, FALSE);
754  }
755 
756  if (gnc_html_history_back_p(gnc_html_get_history(priv->html)))
757  {
758  gnc_plugin_page_report_set_back_button(report, TRUE);
759  }
760  else
761  {
762  gnc_plugin_page_report_set_back_button(report, FALSE);
763  }
764 
765  LEAVE( "done" );
766 }
767 
768 
780 static void
781 gnc_plugin_page_report_option_change_cb(gpointer data)
782 {
783  GncPluginPage *page;
784  GncPluginPageReport *report;
786  SCM dirty_report = scm_c_eval_string("gnc:report-set-dirty?!");
787 
788  g_return_if_fail(GNC_IS_PLUGIN_PAGE_REPORT(data));
789  report = GNC_PLUGIN_PAGE_REPORT(data);
790  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
791  page = GNC_PLUGIN_PAGE(report);
792 
793  DEBUG( "option_change" );
794  if (priv->cur_report == SCM_BOOL_F)
795  return;
796  DEBUG( "set-dirty, queue-draw" );
797 
798  /* Update the page (i.e. the notebook tab and window title) */
799  std::string old_name{gnc_plugin_page_get_page_name(GNC_PLUGIN_PAGE(report))};
800  auto new_name{priv->cur_odb->lookup_string_option("General",
801  "Report name")};
802  if (new_name != old_name)
803  {
804  /* Bug 727130, 760711 - remove only the non-printable
805  * characters from the new name */
806  char *clean_name{g_strdup(new_name.c_str())};
808  ENTER("Cleaned-up new report name: %s", clean_name ? clean_name : "(null)");
809  main_window_update_page_name(GNC_PLUGIN_PAGE(report), clean_name);
810  g_free(clean_name);
811  }
812 
813  /* it's probably already dirty, but make sure */
814  scm_call_2(dirty_report, priv->cur_report, SCM_BOOL_T);
815 
816  gnc_plugin_set_actions_enabled(G_ACTION_MAP(priv->action_group),
817  disable_during_load_actions, FALSE);
818  // prevent closing this page while loading...
819  priv->reloading = TRUE;
820 
821  // this sets the window for the progressbar
822  gnc_window_set_progressbar_window( GNC_WINDOW(page->window) );
823 
824  // this sets the minimum size of the progressbar to that allocated
825  gnc_plugin_page_report_set_progressbar( page, TRUE );
826 
827  /* Now queue the fact that we need to reload this report */
828  gnc_html_reload( priv->html, TRUE ); //reload by rebuild
829 
830  gnc_plugin_page_report_set_progressbar( page, FALSE );
831 
832  // this resets the window for the progressbar to nullptr
833  gnc_window_set_progressbar_window( nullptr );
834 
835  gnc_plugin_set_actions_enabled(G_ACTION_MAP(priv->action_group),
836  disable_during_load_actions, TRUE);
837  priv->reloading = FALSE;
838 }
839 
840 /* FIXME: This function does... nothing? */
841 static void
842 gnc_plugin_page_report_history_destroy_cb(gnc_html_history_node * node,
843  gpointer user_data)
844 {
845 #if 0
846  static SCM remover = SCM_BOOL_F;
847  int report_id;
848 
849  if (remover == SCM_BOOL_F)
850  {
851  remover = scm_c_eval_string("gnc:report-remove-by-id");
852  }
853 
854  if (node
855  && !g_strcmp0 (node->type, URL_TYPE_REPORT)\
856  && !strncmp("id=", node->location, 3))
857  {
858  sscanf(node->location + 3, "%d", &report_id);
859  /* printf("unreffing report %d and children\n", report_id);
860  scm_call_1(remover, scm_from_int (report_id)); */
861  }
862  else
863  {
864  return;
865  }
866 #endif
867 }
868 
869 // @param data is actually GncPluginPageReportPrivate
870 static void
871 gnc_plugin_page_report_refresh(gpointer data)
872 {
873  // FIXME?
874  DEBUG( "report-refresh called" );
875  // something like ... gnc_plugin_page_report_redraw( nullptr, (GncPluginPageReportPrivate*)data );
876  return;
877 }
878 
879 static void
880 gnc_plugin_page_report_destroy_widget(GncPluginPage *plugin_page)
881 {
883 
884  // FIXME: cleanup other resources.
885 
886  PINFO("destroy widget");
887  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(plugin_page);
888 
889  // Remove the page_changed signal callback
890  gnc_plugin_page_disconnect_page_changed (GNC_PLUGIN_PAGE(plugin_page));
891 
892  // Remove the page focus idle function if present
893  g_idle_remove_by_data (plugin_page);
894 
895  if (priv->component_manager_id)
896  {
897  gnc_unregister_gui_component(priv->component_manager_id);
898  priv->component_manager_id = 0;
899  }
900 
901  gnc_plugin_page_report_destroy(priv);
902  gnc_report_remove_by_id(priv->reportId);
903 }
904 
905 
908 #define SCHEME_OPTIONS "SchemeOptions"
909 #define SCHEME_OPTIONS_N "SchemeOptions%d"
910 
911 
921 static void
922 gnc_plugin_page_report_save_page (GncPluginPage *plugin_page,
923  GKeyFile *key_file,
924  const gchar *group_name)
925 {
926  GncPluginPageReport *report;
928  SCM gen_save_text, scm_text;
929  SCM get_embedded_list, embedded, item, tmp_report;
930  SCM get_options;
931  gint count, id;
932  gchar *text, *key_name;
933 
934  g_return_if_fail (GNC_IS_PLUGIN_PAGE_REPORT(plugin_page));
935  g_return_if_fail (key_file != nullptr);
936  g_return_if_fail (group_name != nullptr);
937 
938  ENTER("page %p, key_file %p, group_name %s", plugin_page, key_file,
939  group_name);
940 
941  report = GNC_PLUGIN_PAGE_REPORT(plugin_page);
942  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
943 
944  if (!priv || !priv->cur_report || scm_is_null(priv->cur_report) ||
945  SCM_UNBNDP(priv->cur_report) || SCM_BOOL_F == priv->cur_report)
946  {
947  LEAVE("not saving invalid report");
948  return;
949  }
950 
951  gen_save_text = scm_c_eval_string("gnc:report-serialize");
952  get_embedded_list = scm_c_eval_string("gnc:report-embedded-list");
953  get_options = scm_c_eval_string("gnc:report-options");
954  embedded = scm_call_1(get_embedded_list, scm_call_1(get_options, priv->cur_report));
955  count = scm_ilength(embedded);
956  while (count-- > 0)
957  {
958  item = SCM_CAR(embedded);
959  embedded = SCM_CDR(embedded);
960  if (!scm_is_number(item))
961  continue;
962  id = scm_to_int (item);
963  tmp_report = gnc_report_find(id);
964  scm_text = scm_call_1(gen_save_text, tmp_report);
965  if (!scm_is_string (scm_text))
966  {
967  DEBUG("child report %d: nothing to save", id);
968  continue;
969  }
970 
971  key_name = g_strdup_printf(SCHEME_OPTIONS_N, id);
972  text = gnc_scm_strip_comments(scm_text);
973  g_key_file_set_value(key_file, group_name, key_name, text);
974  g_free(text);
975  g_free(key_name);
976  }
977 
978  scm_text = scm_call_1(gen_save_text, priv->cur_report);
979  if (!scm_is_string (scm_text))
980  {
981  LEAVE("nothing to save");
982  return;
983  }
984 
985  text = gnc_scm_strip_comments(scm_text);
986  g_key_file_set_value(key_file, group_name, SCHEME_OPTIONS, text);
987  g_free(text);
988  LEAVE(" ");
989 }
990 
991 
1001 static GncPluginPage *
1002 gnc_plugin_page_report_recreate_page (GtkWidget *window,
1003  GKeyFile *key_file,
1004  const gchar *group_name)
1005 {
1006  GncPluginPage *page;
1007  gchar **keys;
1008  gsize i, num_keys;
1009  GError *error = nullptr;
1010  gchar *option_string;
1011  gint report_id;
1012  SCM scm_id, final_id = SCM_BOOL_F;
1013  SCM report;
1014 
1015  g_return_val_if_fail(key_file, nullptr);
1016  g_return_val_if_fail(group_name, nullptr);
1017  ENTER("key_file %p, group_name %s", key_file, group_name);
1018 
1019  keys = g_key_file_get_keys(key_file, group_name, &num_keys, &error);
1020  if (error)
1021  {
1022  g_warning("error reading group %s key list: %s",
1023  group_name, error->message);
1024  g_error_free(error);
1025  LEAVE("no keys");
1026  return nullptr;
1027  }
1028 
1029  for (i = 0; i < num_keys; i++)
1030  {
1031  if (strncmp(keys[i], SCHEME_OPTIONS, strlen(SCHEME_OPTIONS)) != 0)
1032  continue;
1033  option_string = g_key_file_get_value(key_file, group_name,
1034  keys[i], &error);
1035  if (error)
1036  {
1037  g_warning("error reading group %s key %s: %s",
1038  group_name, keys[i], error->message);
1039  g_error_free(error);
1040  g_strfreev (keys);
1041  LEAVE("bad value");
1042  return nullptr;
1043  }
1044  scm_id = scm_eval_string(scm_from_utf8_string(option_string));
1045  g_free(option_string);
1046 
1047  if (!scm_integer_p(scm_id))
1048  {
1049  DEBUG("report id not an integer for key %s", keys[i]);
1050  g_strfreev (keys);
1051  return nullptr;
1052  }
1053 
1054  if (final_id == SCM_BOOL_F)
1055  {
1056  if (g_strcmp0(keys[i], SCHEME_OPTIONS) == 0)
1057  {
1058  final_id = scm_id;
1059  }
1060  }
1061  }
1062  g_strfreev (keys);
1063 
1064  if (final_id == SCM_BOOL_F)
1065  {
1066  LEAVE("report not specified");
1067  return nullptr;
1068  }
1069 
1070  report_id = scm_to_int(final_id);
1071  report = gnc_report_find(report_id);
1072  if (!report)
1073  {
1074  LEAVE("report doesn't exist");
1075  return nullptr;
1076  }
1077 
1078  page = gnc_plugin_page_report_new( report_id );
1079 
1080  LEAVE(" ");
1081  return page;
1082 }
1083 
1084 
1095 static void
1096 gnc_plugin_page_report_name_changed (GncPluginPage *page, const gchar *name)
1097 {
1099 
1100  g_return_if_fail(GNC_IS_PLUGIN_PAGE_REPORT(page));
1101  g_return_if_fail(name != nullptr);
1102 
1103  ENTER("page %p, name %s", page, name);
1104  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(page);
1105 
1106  if (priv->cur_odb)
1107  {
1108 
1109  /* Is this a redundant call? */
1110  auto old_name = priv->cur_odb->lookup_string_option("General",
1111  "Report name");
1112  std::string name_str{name};
1113  DEBUG("Comparing old name '%s' to new name '%s'",
1114  old_name.empty() ? "(null)" : old_name.c_str(), name);
1115  if (old_name == name_str)
1116  {
1117  LEAVE("no change");
1118  return;
1119  }
1120 
1121  /* Store the new name for the report. */
1122  priv->cur_odb->set_string_option("General", "Report name", name_str);
1123 
1124  }
1125 
1126  /* Have to manually call the option change hook. */
1127  gnc_plugin_page_report_option_change_cb(page);
1128  LEAVE(" ");
1129 }
1130 
1131 static void
1132 gnc_plugin_page_report_update_edit_menu (GncPluginPage *page, gboolean hide)
1133 {
1134  GncMainWindow *window = (GncMainWindow*)gnc_plugin_page_get_window (GNC_PLUGIN_PAGE(page));
1135  GAction *action;
1136 
1137  action = gnc_main_window_find_action (window, "EditCopyAction");
1138  g_simple_action_set_enabled (G_SIMPLE_ACTION(action), TRUE);
1139  action = gnc_main_window_find_action (window, "EditCutAction");
1140  g_simple_action_set_enabled (G_SIMPLE_ACTION(action), FALSE);
1141  action = gnc_main_window_find_action (window, "EditPasteAction");
1142  g_simple_action_set_enabled (G_SIMPLE_ACTION(action), FALSE);
1143 }
1144 
1145 static gboolean
1146 gnc_plugin_page_report_finish_pending (GncPluginPage *page)
1147 {
1149  GncPluginPageReport *report;
1150 
1151  report = GNC_PLUGIN_PAGE_REPORT(page);
1152  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
1153  return !priv->reloading;
1154 }
1155 
1156 
1157 /********************************************************************
1158  * gnc_report_window_destroy
1159  * free and destroy a window
1160  ********************************************************************/
1161 void
1162 gnc_plugin_page_report_destroy(GncPluginPageReportPrivate * priv)
1163 {
1164  SCM get_editor = scm_c_eval_string("gnc:report-editor-widget");
1165  SCM set_editor = scm_c_eval_string("gnc:report-set-editor-widget!");
1166  SCM edited, editor;
1167 
1168  /* close any open editors */
1169  for (edited = scm_list_copy(priv->edited_reports); !scm_is_null(edited);
1170  edited = SCM_CDR(edited))
1171  {
1172  editor = scm_call_1(get_editor, SCM_CAR(edited));
1173  scm_call_2(set_editor, SCM_CAR(edited), SCM_BOOL_F);
1174  if (editor != SCM_BOOL_F)
1175  {
1176 #define FUNC_NAME "gtk_widget_destroy"
1177  auto w{static_cast<GtkWidget*>(SWIG_MustGetPtr(editor, SWIG_TypeQuery("_p_GtkWidget"), 1, 0))};
1178 #undef FUNC_NAME
1179  gtk_widget_destroy(GTK_WIDGET(w));
1180  }
1181  }
1182 
1183  if (priv->initial_odb)
1184  {
1185 //Remove this if there's a double-free
1186  gnc_option_db_destroy(priv->initial_odb);
1187  priv->initial_odb = nullptr;
1188  }
1189 
1190  gnc_html_destroy(priv->html);
1191  priv->html = nullptr;
1192 
1193  g_object_unref(priv->container);
1194  priv->container = nullptr;
1195 
1196  if (priv->cur_report != SCM_BOOL_F)
1197  scm_gc_unprotect_object(priv->cur_report);
1198  if (priv->edited_reports != SCM_EOL)
1199  scm_gc_unprotect_object(priv->edited_reports);
1200 }
1201 
1202 static void
1203 gnc_plugin_page_report_init ( GncPluginPageReport *plugin_page )
1204 {
1205  GncPluginPageReportPrivate *priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(plugin_page);
1206  priv->action_group = NULL;
1207 }
1208 
1209 static GObject*
1210 gnc_plugin_page_report_constructor(GType this_type, guint n_properties, GObjectConstructParam *properties)
1211 {
1212  GObject *obj = G_OBJECT_CLASS (gnc_plugin_page_report_parent_class)->constructor(this_type, n_properties, properties);
1213 
1214  gint reportId = -42;
1215  for (decltype(n_properties) i = 0; i < n_properties; i++)
1216  {
1217  GObjectConstructParam prop = properties[i];
1218  if (strcmp(prop.pspec->name, "report-id") == 0)
1219  {
1220  reportId = g_value_get_int(prop.value);
1221  }
1222  }
1223 
1224  gnc_plugin_page_report_constr_init(GNC_PLUGIN_PAGE_REPORT(obj), reportId);
1225 
1226  return obj;
1227 }
1228 
1229 static void
1230 gnc_plugin_page_report_menu_update (GncPluginPage *plugin_page,
1231  action_toolbar_labels *tooltip_list)
1232 {
1233  GncMainWindow *window = GNC_MAIN_WINDOW(GNC_PLUGIN_PAGE(plugin_page)->window);
1234  GtkWidget *tool_item;
1235 
1236  for (gint i = 0; (tooltip_list[i].action_name != nullptr); i++)
1237  {
1239  tooltip_list[i].action_name,
1240  _(tooltip_list[i].label),
1241  _(tooltip_list[i].tooltip));
1242 
1243  tool_item = gnc_main_window_toolbar_find_tool_item (window,
1244  tooltip_list[i].action_name);
1245  if (tool_item)
1246  { // only need to update the tooltip here
1247  gtk_widget_set_tooltip_text (GTK_WIDGET(tool_item), _(tooltip_list[i].tooltip));
1248  g_object_set (G_OBJECT(tool_item), "has-tooltip", false, nullptr);
1249  }
1250  }
1251  // need to add the accelerator keys for the updated menu items
1253 }
1254 
1255 static void
1256 gnc_plugin_page_report_menu_updates (GncPluginPage *plugin_page)
1257 {
1259  GncPluginPageReport *report;
1260  GncMainWindow *window;
1261  action_toolbar_labels tooltip_list[3];
1262  GAction *action;
1263 
1264  gchar *saved_reports_path = gnc_build_userdata_path (SAVED_REPORTS_FILE);
1265  gchar *report_save_str = g_strdup_printf (
1266  _("Update the current report's saved configuration. "
1267  "The report configuration will be saved in the file %s."), saved_reports_path);
1268  gchar *report_saveas_str = g_strdup_printf (
1269  _("Add the current report's configuration to the 'Reports->Saved Report Configurations' menu. "
1270  "The report configuration will be saved in the file %s."), saved_reports_path);
1271 
1272  report = GNC_PLUGIN_PAGE_REPORT(plugin_page);
1273  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
1274 
1275  window = (GncMainWindow*)gnc_plugin_page_get_window (GNC_PLUGIN_PAGE(plugin_page));
1276 
1277  tooltip_list[0] = { "ReportSaveAction", N_("Save _Report Configuration"), report_save_str };
1278  tooltip_list[1] = { "ReportSaveAsAction", N_("Save Report Configuration As…"), report_saveas_str };
1279  tooltip_list[2] = { nullptr, nullptr, nullptr };
1280 
1281  gnc_plugin_page_report_menu_update (plugin_page, tooltip_list);
1282 
1283  /* Enable the FilePrintAction */
1284  action = gnc_main_window_find_action (window, "FilePrintAction");
1285  g_simple_action_set_enabled (G_SIMPLE_ACTION(action), true);
1286 
1287  if (priv->webkit2)
1288  {
1289  GtkWidget *pdf_item = gnc_main_window_menu_find_menu_item (window, "FilePrintPDFAction");
1290  gtk_widget_hide (pdf_item);
1291  }
1292  g_free (saved_reports_path);
1293  g_free (report_save_str);
1294  g_free (report_saveas_str);
1295 }
1296 
1297 static void
1298 gnc_plugin_page_report_constr_init (GncPluginPageReport *plugin_page, gint reportId)
1299 {
1301  GncPluginPage *parent;
1302  gboolean use_new;
1303  gchar *name;
1304 
1305  DEBUG("property reportId=%d", reportId);
1306  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(plugin_page);
1307  priv->reportId = reportId;
1308  priv->webkit2 = FALSE;
1309 
1310  gnc_plugin_page_report_setup( GNC_PLUGIN_PAGE(plugin_page));
1311 
1312  /* Init parent declared variables */
1313  parent = GNC_PLUGIN_PAGE(plugin_page);
1314  use_new = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL_REPORT, GNC_PREF_USE_NEW);
1315  name = gnc_report_name (priv->initial_report);
1316  g_object_set (G_OBJECT(plugin_page),
1317  "page-name", name,
1318  "ui-description", "gnc-plugin-page-report.ui",
1319  "use-new-window", use_new,
1320  nullptr);
1321  g_free (name);
1322 
1323  /* change me when the system supports multiple books */
1324  gnc_plugin_page_add_book (parent, gnc_get_current_book());
1325 
1326  /* Create menu and toolbar information */
1327  priv->action_group = gnc_plugin_page_create_action_group (parent, "GncPluginPageReportActions");
1328  g_action_map_add_action_entries (G_ACTION_MAP(priv->action_group),
1329  report_actions,
1330  num_report_actions,
1331  plugin_page);
1332 
1333  gnc_plugin_set_actions_enabled (G_ACTION_MAP(priv->action_group), initially_insensitive_actions,
1334  FALSE);
1335  gnc_plugin_set_actions_enabled(G_ACTION_MAP(priv->action_group),
1336  disable_during_load_actions, FALSE);
1337 }
1338 
1341 {
1342  DEBUG( "report id = %d", reportId );
1343  auto plugin_page{g_object_new(GNC_TYPE_PLUGIN_PAGE_REPORT, "report-id",
1344  reportId, nullptr)};
1345  DEBUG( "plugin_page: %p", plugin_page );
1346  DEBUG( "set %d on page %p", reportId, plugin_page );
1347  return GNC_PLUGIN_PAGE( plugin_page );
1348 }
1349 
1350 void
1351 gnc_plugin_page_report_remove_edited_report(GncPluginPageReportPrivate *priv,
1352  SCM report)
1353 {
1354  SCM new_edited = scm_delete(priv->edited_reports, report);
1355  if (priv->edited_reports != SCM_EOL)
1356  scm_gc_unprotect_object(priv->edited_reports);
1357  priv->edited_reports = new_edited;
1358  if (new_edited != SCM_EOL)
1359  scm_gc_protect_object(priv->edited_reports);
1360 }
1361 
1362 void
1363 gnc_plugin_page_report_add_edited_report(GncPluginPageReportPrivate *priv,
1364  SCM report)
1365 {
1366  SCM new_edited = scm_cons(report, priv->edited_reports);
1367  if (priv->edited_reports != SCM_EOL)
1368  scm_gc_unprotect_object(priv->edited_reports);
1369  priv->edited_reports = new_edited;
1370  if (new_edited != SCM_EOL)
1371  scm_gc_protect_object(priv->edited_reports);
1372 }
1373 
1374 void
1375 gnc_plugin_page_report_raise_editor(SCM report)
1376 {
1377  SCM get_editor = scm_c_eval_string("gnc:report-editor-widget");
1378  SCM editor = scm_call_1(get_editor, report);
1379 #define FUNC_NAME "gtk_window_present"
1380  auto w{static_cast<GtkWidget *>(SWIG_MustGetPtr(editor, SWIG_TypeQuery("_p_GtkWidget"), 1, 0))};
1381 #undef FUNC_NAME
1382  gtk_window_present(GTK_WINDOW(w));
1383 }
1384 
1385 static void
1386 close_handler (gpointer user_data)
1387 {
1388  GncPluginPage *plugin_page = GNC_PLUGIN_PAGE(user_data);
1389  DEBUG("in close handler\n");
1390  gnc_main_window_close_page (plugin_page);
1391 }
1392 
1393 static void
1394 gnc_plugin_page_report_set_fwd_button (GncPluginPageReport *report, int enabled)
1395 {
1396  GAction *action = gnc_plugin_page_get_action (GNC_PLUGIN_PAGE(report),
1397  "ReportForwAction");
1398  if (action != NULL)
1399  g_simple_action_set_enabled (G_SIMPLE_ACTION(action), enabled);
1400 }
1401 
1402 static void
1403 gnc_plugin_page_report_set_back_button (GncPluginPageReport *report, int enabled)
1404 {
1405  GAction *action = gnc_plugin_page_get_action (GNC_PLUGIN_PAGE(report),
1406  "ReportBackAction");
1407  if (action != NULL)
1408  g_simple_action_set_enabled (G_SIMPLE_ACTION(action), enabled);
1409 }
1410 
1411 // ------------------------------------------------------------
1412 // GTK ACTION CALLBACKS
1413 
1414 static void
1415 gnc_plugin_page_report_forw_cb (GSimpleAction *simple,
1416  GVariant *parameter,
1417  gpointer user_data)
1418 {
1419  GncPluginPageReport *report = (GncPluginPageReport*)user_data;
1421  gnc_html_history_node * node = nullptr;
1422 
1423  DEBUG( "forw" );
1424  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
1425  gnc_html_history_forward(gnc_html_get_history(priv->html));
1426  node = gnc_html_history_get_current(gnc_html_get_history(priv->html));
1427  if (node)
1428  {
1429  gnc_html_show_url(priv->html, node->type, node->location,
1430  node->label, 0);
1431  }
1432 }
1433 
1434 static void
1435 gnc_plugin_page_report_back_cb (GSimpleAction *simple,
1436  GVariant *parameter,
1437  gpointer user_data)
1438 {
1439  GncPluginPageReport *report = (GncPluginPageReport*)user_data;
1441  gnc_html_history_node * node;
1442 
1443  DEBUG( "back" );
1444  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
1445  gnc_html_history_back(gnc_html_get_history(priv->html));
1446  node = gnc_html_history_get_current(gnc_html_get_history(priv->html));
1447  if (node)
1448  {
1449  gnc_html_show_url(priv->html, node->type, node->location,
1450  node->label, 0);
1451  }
1452 }
1453 
1454 void
1455 gnc_plugin_page_report_reload (GncPluginPageReport *report)
1456 {
1457  gnc_plugin_page_report_reload_cb (nullptr, nullptr, report);
1458 }
1459 
1460 static void
1461 gnc_plugin_page_report_reload_cb (GSimpleAction *simple,
1462  GVariant *parameter,
1463  gpointer user_data)
1464 {
1465  GncPluginPageReport *report = (GncPluginPageReport*)user_data;
1466  GncPluginPage *page;
1468  SCM dirty_report;
1469 
1470  DEBUG( "reload" );
1471  page = GNC_PLUGIN_PAGE(report);
1472  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
1473  if (priv->cur_report == SCM_BOOL_F)
1474  return;
1475 
1476  DEBUG( "reload-redraw" );
1477  dirty_report = scm_c_eval_string("gnc:report-set-dirty?!");
1478  scm_call_2(dirty_report, priv->cur_report, SCM_BOOL_T);
1479 
1480  /* now queue the fact that we need to reload this report */
1481  // Disable some actions reported to crash while loading
1482  gnc_plugin_set_actions_enabled(G_ACTION_MAP(priv->action_group),
1483  disable_during_load_actions, FALSE);
1484  // prevent closing this page while loading...
1485  priv->reloading = TRUE;
1486  // this sets the window for the progressbar
1487  gnc_window_set_progressbar_window( GNC_WINDOW(page->window) );
1488 
1489  // this sets the minimum size of the progressbar to that allocated
1490  gnc_plugin_page_report_set_progressbar( page, TRUE );
1491 
1492  gnc_html_reload( priv->html, TRUE ); //reload by rebuild
1493 
1494  gnc_plugin_page_report_set_progressbar( page, FALSE );
1495 
1496  // this resets the window for the progressbar to nullptr
1497  gnc_window_set_progressbar_window( nullptr );
1498  gnc_plugin_set_actions_enabled(G_ACTION_MAP(priv->action_group),
1499  disable_during_load_actions, TRUE);
1500  priv->reloading = FALSE;
1501 }
1502 
1503 static void
1504 gnc_plugin_page_report_stop_cb (GSimpleAction *simple,
1505  GVariant *parameter,
1506  gpointer user_data)
1507 {
1508  GncPluginPageReport *report = (GncPluginPageReport*)user_data;
1510 
1511  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
1512  gnc_html_cancel(priv->html);
1513 }
1514 
1515 /* Returns SCM_BOOL_F if cancel. Returns SCM_BOOL_T if html.
1516  * Otherwise returns pair from export_types. */
1517 static SCM
1518 gnc_get_export_type_choice (SCM export_types, GtkWindow *parent)
1519 {
1520  GList * choices = nullptr;
1521  gboolean bad = FALSE;
1522  GList * node;
1523  int choice;
1524  SCM tail;
1525 
1526  if (!scm_is_list (export_types))
1527  return SCM_BOOL_F;
1528 
1529  for (tail = export_types; !scm_is_null (tail); tail = SCM_CDR (tail))
1530  {
1531  SCM pair = SCM_CAR (tail);
1532  char * name;
1533  SCM scm;
1534 
1535  if (!scm_is_pair (pair))
1536  {
1537  g_warning ("unexpected list element");
1538  bad = TRUE;
1539  break;
1540  }
1541 
1542  scm = SCM_CAR (pair);
1543  if (!scm_is_string (scm))
1544  {
1545  g_warning ("unexpected pair element");
1546  bad = TRUE;
1547  break;
1548  }
1549 
1550  name = gnc_scm_to_utf8_string (scm);
1551  choices = g_list_prepend (choices, name);
1552  }
1553 
1554  if (!bad)
1555  {
1556  choices = g_list_reverse (choices);
1557 
1558  choices = g_list_prepend (choices, g_strdup (_("HTML")));
1559 
1560  choice = gnc_choose_radio_option_dialog
1561  (GTK_WIDGET (parent), _("Choose export format"),
1562  _("Choose the export format for this report:"),
1563  nullptr, 0, choices);
1564  }
1565  else
1566  choice = -1;
1567 
1568  for (node = choices; node; node = node->next)
1569  g_free (node->data);
1570  g_list_free (choices);
1571 
1572  if (choice < 0)
1573  return SCM_BOOL_F;
1574 
1575  if (choice == 0)
1576  return SCM_BOOL_T;
1577 
1578  choice--;
1579  if (choice >= scm_ilength (export_types))
1580  return SCM_BOOL_F;
1581 
1582  return scm_list_ref (export_types, scm_from_int (choice));
1583 }
1584 
1585 static char *
1586 gnc_get_export_filename (SCM choice, GtkWindow *parent)
1587 {
1588  char * filepath;
1589  GStatBuf statbuf;
1590  char * title;
1591  const gchar * html_type = _("HTML");
1592  char * type;
1593  int rc;
1594  char * default_dir;
1595 
1596  if (choice == SCM_BOOL_T)
1597  type = g_strdup (html_type);
1598  else
1599  type = gnc_scm_to_utf8_string(SCM_CAR (choice));
1600 
1601  /* %s is the type of what is about to be saved, e.g. "HTML". */
1602  title = g_strdup_printf (_("Save %s To File"), type);
1603  default_dir = gnc_get_default_directory(GNC_PREFS_GROUP_REPORT);
1604 
1605  filepath = gnc_file_dialog (parent, title, nullptr, default_dir,
1606  GNC_FILE_DIALOG_EXPORT);
1607 
1608  /* Try to test for extension on file name, add if missing */
1609  if (filepath && strchr (filepath, '.') == nullptr)
1610  {
1611  char* extension = g_ascii_strdown (type, -1);
1612  char* newpath = g_strdup_printf ("%s.%s", filepath, extension);
1613  g_free (extension);
1614  g_free (filepath);
1615  filepath = newpath;
1616  }
1617 
1618  g_free (type);
1619  g_free (title);
1620  g_free (default_dir);
1621 
1622  if (!filepath)
1623  return nullptr;
1624 
1625  default_dir = g_path_get_dirname(filepath);
1626  gnc_set_default_directory (GNC_PREFS_GROUP_REPORT, default_dir);
1627  g_free(default_dir);
1628 
1629  rc = g_stat (filepath, &statbuf);
1630 
1631  /* Check for an error that isn't a non-existent file. */
1632  if (rc != 0 && errno != ENOENT)
1633  {
1634  /* %s is the strerror(3) string of the error that occurred. */
1635  const char *format = _("You cannot save to that filename.\n\n%s");
1636 
1637  gnc_error_dialog (parent, format, strerror(errno));
1638  g_free(filepath);
1639  return nullptr;
1640  }
1641 
1642  /* Check for a file that isn't a regular file. */
1643  if (rc == 0 && !S_ISREG (statbuf.st_mode))
1644  {
1645  const char *message = _("You cannot save to that file.");
1646 
1647  gnc_error_dialog (parent, "%s", message);
1648  g_free(filepath);
1649  return nullptr;
1650  }
1651 
1652  if (rc == 0)
1653  {
1654  const char *format = _("The file %s already exists. "
1655  "Are you sure you want to overwrite it?");
1656 
1657  if (!gnc_verify_dialog (parent, FALSE, format, filepath))
1658  {
1659  g_free(filepath);
1660  return nullptr;
1661  }
1662  }
1663 
1664  return filepath;
1665 }
1666 
1667 static void
1668 gnc_plugin_page_report_edit_tax_cb (GSimpleAction *simple,
1669  GVariant *parameter,
1670  gpointer user_data)
1671 {
1672  GncPluginPageReport *report = (GncPluginPageReport*)user_data;
1673  GtkWidget *window;
1674 
1675  window = GNC_PLUGIN_PAGE(report)->window;
1676  gnc_tax_info_dialog (window, nullptr);
1677 }
1678 
1679 static void
1680 gnc_plugin_page_report_save_as_cb (GSimpleAction *simple,
1681  GVariant *parameter,
1682  gpointer user_data)
1683 {
1684  GncPluginPageReport *report = (GncPluginPageReport*)user_data;
1686  SCM save_func;
1687  SCM rpt_id;
1688 
1689  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
1690  if (priv->cur_report == SCM_BOOL_F)
1691  return;
1692 
1693  /* Create a new report template based on the current report's settings
1694  * and allow the user to rename the template.
1695  */
1696  save_func = scm_c_eval_string("gnc:report-to-template-new");
1697  rpt_id = scm_call_1(save_func, priv->cur_report);
1698 
1699  /* Open Preconfigured Reports dialog to allow user to change the name */
1700  if (!scm_is_null (rpt_id))
1701  {
1702  GncPluginPage *reportPage = GNC_PLUGIN_PAGE (report);
1703  GtkWidget *window = reportPage->window;
1704 
1705  if (window)
1706  g_return_if_fail(GNC_IS_MAIN_WINDOW(window));
1707 
1708  gnc_ui_custom_report_edit_name (GNC_MAIN_WINDOW (window), rpt_id);
1709  }
1710 
1711 }
1712 
1713 static void
1714 gnc_plugin_page_report_save_cb (GSimpleAction *simple,
1715  GVariant *parameter,
1716  gpointer user_data)
1717 {
1718  GncPluginPageReport *report = (GncPluginPageReport*)user_data;
1720  SCM check_func, save_func;
1721  SCM rpt_id;
1722 
1723  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
1724  if (priv->cur_report == SCM_BOOL_F)
1725  return;
1726 
1727  check_func = scm_c_eval_string("gnc:is-custom-report-type");
1728  if (scm_is_true (scm_call_1 (check_func, priv->cur_report)))
1729  {
1730  auto report_name_str{priv->cur_odb->lookup_string_option("General", "Report name")};
1731  auto window{GTK_WINDOW(gnc_plugin_page_get_window (GNC_PLUGIN_PAGE(report)))};
1732 
1733  if (!gnc_action_dialog (window, _("_Overwrite"), false, _("This will update and \
1734 overwrite the existing saved report named \"%s\"."), report_name_str.c_str()))
1735  return;
1736 
1737  /* The current report is already based on a custom report.
1738  * Replace the existing one instead of adding a new one
1739  */
1740  save_func = scm_c_eval_string("gnc:report-to-template-update");
1741  rpt_id = scm_call_1(save_func, priv->cur_report);
1742  (void)rpt_id;
1743  }
1744  else
1745  {
1746  /* The current report is not based on a custom report.
1747  * So let's create a new report template based on this report
1748  * and allow the user to change the name.
1749  */
1750  gnc_plugin_page_report_save_as_cb (simple, parameter, report);
1751  }
1752 }
1753 
1754 static void
1755 gnc_plugin_page_report_export_cb (GSimpleAction *simple,
1756  GVariant *parameter,
1757  gpointer user_data)
1758 {
1759  GncPluginPageReport *report = (GncPluginPageReport*)user_data;
1761  char * filepath;
1762  SCM export_types;
1763  SCM export_thunk;
1764  gboolean result;
1765  SCM choice;
1766  GtkWindow *parent = GTK_WINDOW (gnc_plugin_page_get_window
1767  (GNC_PLUGIN_PAGE (report)));
1768 
1769  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
1770  export_types = scm_call_1 (scm_c_eval_string ("gnc:report-export-types"),
1771  priv->cur_report);
1772 
1773  export_thunk = scm_call_1 (scm_c_eval_string ("gnc:report-export-thunk"),
1774  priv->cur_report);
1775 
1776  if (scm_is_list (export_types) && scm_is_procedure (export_thunk))
1777  choice = gnc_get_export_type_choice (export_types, parent);
1778  else
1779  choice = SCM_BOOL_T;
1780 
1781  if (choice == SCM_BOOL_F)
1782  return;
1783 
1784  filepath = gnc_get_export_filename (choice, parent);
1785  if (!filepath)
1786  return;
1787 
1788  if (scm_is_pair (choice))
1789  {
1790  SCM type = scm_cdr (choice);
1791  SCM document = scm_call_2 (export_thunk, priv->cur_report, type);
1792  SCM query_result = scm_c_eval_string ("gnc:html-document?");
1793  SCM get_export_string = scm_c_eval_string ("gnc:html-document-export-string");
1794  SCM get_export_error = scm_c_eval_string ("gnc:html-document-export-error");
1795 
1796  if (scm_is_false (scm_call_1 (query_result, document)))
1797  gnc_error_dialog (parent, "%s",
1798  _("This report must be upgraded to return a "
1799  "document object with export-string or "
1800  "export-error."));
1801  else
1802  {
1803  SCM export_string = scm_call_1 (get_export_string, document);
1804  SCM export_error = scm_call_1 (get_export_error, document);
1805 
1806  if (scm_is_string (export_string))
1807  {
1808  GError *err = nullptr;
1809  gchar *exported = scm_to_utf8_string (export_string);
1810  if (!g_file_set_contents (filepath, exported, -1, &err))
1811  gnc_error_dialog (parent, "Error during export: %s", err->message);
1812  g_free (exported);
1813  if (err)
1814  g_error_free (err);
1815  }
1816  else if (scm_is_string (export_error))
1817  {
1818  gchar *str = scm_to_utf8_string (export_error);
1819  gnc_error_dialog (parent, "error during export: %s", str);
1820  g_free (str);
1821  }
1822  else
1823  gnc_error_dialog (parent, "%s",
1824  _("This report must be upgraded to return a "
1825  "document object with export-string or "
1826  "export-error."));
1827  }
1828  result = TRUE;
1829  }
1830  else
1831  result = gnc_html_export_to_file (priv->html, filepath);
1832 
1833  if (!result)
1834  {
1835  const char *fmt = _("Could not open the file %s. "
1836  "The error is: %s");
1837  gnc_error_dialog (parent, fmt, filepath ? filepath : "(null)",
1838  strerror (errno) ? strerror (errno) : "" );
1839  }
1840 
1841  g_free(filepath);
1842  return;
1843 }
1844 
1845 static void
1846 gnc_plugin_page_report_options_cb (GSimpleAction *simple,
1847  GVariant *parameter,
1848  gpointer user_data)
1849 {
1850  GncPluginPageReport *report = (GncPluginPageReport*)user_data;
1852  GtkWindow *parent = GTK_WINDOW (gnc_plugin_page_get_window (GNC_PLUGIN_PAGE (report)));
1853  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
1854  if (priv->cur_report == SCM_BOOL_F)
1855  return;
1856 
1857  if (gnc_report_edit_options (priv->cur_report, parent))
1858  gnc_plugin_page_report_add_edited_report(priv, priv->cur_report);
1859 }
1860 
1861 static GncInvoice*
1862 lookup_invoice(GncPluginPageReportPrivate *priv)
1863 {
1864  const QofInstance* opt_val =
1866  "Invoice Number");
1867  return GNC_INVOICE(opt_val);
1868 }
1869 
1870 #define GNC_PREFS_GROUP_REPORT_PDFEXPORT GNC_PREFS_GROUP_GENERAL_REPORT ".pdf-export"
1871 #define GNC_PREF_FILENAME_DATE_FMT "filename-date-format"
1872 #define GNC_PREF_FILENAME_FMT "filename-format"
1873 
1874 static gchar *report_create_jobname(GncPluginPageReportPrivate *priv)
1875 {
1876  gchar *job_name = nullptr;
1877  gchar *report_name = nullptr;
1878  const gchar *report_number = "";
1879  gchar *job_date;
1880  const gchar *default_jobname = N_("GnuCash-Report");
1881 
1882  g_assert(priv);
1883 
1884  {
1885  // Look up the date format that was chosen in the preferences database
1886  QofDateFormat date_format_here = QOF_DATE_FORMAT_ISO;
1887  char *format_code = gnc_prefs_get_string (GNC_PREFS_GROUP_REPORT_PDFEXPORT,
1888  GNC_PREF_FILENAME_DATE_FMT);
1889  const gchar *date_format_string;
1890  if (!(format_code && *format_code))
1891  {
1892  g_free(format_code);
1893  format_code = g_strdup("locale");
1894  }
1895 
1896  if (gnc_date_string_to_dateformat (format_code, &date_format_here))
1897  PERR("Incorrect date format code, using ISO-8601.");
1898 
1899  date_format_string = qof_date_format_get_string (date_format_here);
1900 
1901  job_date = gnc_print_time64 (gnc_time (nullptr), date_format_string);
1902  g_free (format_code);
1903  }
1904 
1905 
1906  if (priv->cur_report == SCM_BOOL_F)
1907  report_name = g_strdup (_(default_jobname));
1908  else
1909  {
1910  /* Gather some information from the report to generate a
1911  * decent print job name.
1912  * FIXME: this is a bit of a hack. It would be better if each
1913  * report had a hidden job name option, because the
1914  * generic reporting code shouldn't know what makes
1915  * a decent job name for each report.
1916  *
1917  * Also, the "Report name" for an invoice report is
1918  * "Printable Invoice", which is not what the user wants to see,
1919  * so I added yet another hack below for this. cstim.
1920  */
1921  GncInvoice *invoice;
1922  auto report_name_str = priv->cur_odb->lookup_string_option("General",
1923  "Report name");
1924  if (!report_name_str.empty())
1925  report_name = g_strdup(report_name_str.c_str());
1926  else
1927  report_name = g_strdup (_(default_jobname));
1928 
1929  if (g_strcmp0(report_name, _("Printable Invoice")) == 0
1930  || g_strcmp0(report_name, _("Tax Invoice")) == 0
1931  || g_strcmp0(report_name, _("Easy Invoice")) == 0
1932  || g_strcmp0(report_name, _("Fancy Invoice")) == 0)
1933  {
1934  /* Again HACK alert: We modify this single known string here into
1935  * something more appropriate. */
1936  g_free(report_name);
1937  report_name = g_strdup(_("Invoice"));
1938  }
1939 
1940  invoice = lookup_invoice(priv);
1941  if (invoice)
1942  {
1943  // Report is for an invoice. Hence, we get a number of the invoice.
1944  report_number = gncInvoiceGetID(invoice);
1945  }
1946  }
1947 
1948  if (report_name && job_date)
1949  {
1950  // Look up the printf format of the output name from the preferences database
1951  char* format = gnc_prefs_get_string(GNC_PREFS_GROUP_REPORT_PDFEXPORT, GNC_PREF_FILENAME_FMT);
1952 
1953  if (format && *format)
1954  {
1955  job_name = g_strdup_printf(format, report_name,
1956  report_number, job_date);
1957  }
1958  else
1959  {
1960  PWARN("No GNC_PREF_FILENAME_FMT!");
1961  job_name = g_strdup_printf ("%s %s %s", report_name,
1962  report_number, job_date);
1963  }
1964  g_free(format);
1965  }
1966  g_free (report_name);
1967  g_free (job_date);
1968 
1969  {
1970  char forbidden_char = '/';
1971  // Now remove the characters that are not allowed in file
1972  // names. FIXME: Check for all disallowed characters here!
1973  while (strchr(job_name, forbidden_char))
1974  {
1975  *strchr(job_name, forbidden_char) = '_';
1976  }
1977  }
1978 
1979  {
1980  /* And one final checking issue: We want to avoid allocating
1981  * the same name twice for a saved PDF. Hence, we keep a
1982  * unordered_map with the usage count of existing output
1983  * names. */
1984 
1985  auto& value = static_report_printnames[job_name];
1986  value++;
1987 
1988  if (value > 1)
1989  {
1990  // The name was already in use, so modify the name again
1991  gchar *tmp = g_strdup_printf("%s_%d", job_name, value);
1992  g_free(job_name);
1993  job_name = tmp;
1994  }
1995  }
1996 
1997  return job_name;
1998 }
1999 
2000 static void
2001 gnc_plugin_page_report_print_cb (GSimpleAction *simple,
2002  GVariant *parameter,
2003  gpointer user_data)
2004 {
2005  GncPluginPageReport *report = (GncPluginPageReport*)user_data;
2006  GncPluginPageReportPrivate *priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
2007  gchar *job_name = report_create_jobname(priv);
2008 
2009  //g_warning("Setting job name=%s", job_name);
2010 
2011 #ifdef WEBKIT1
2012  gnc_html_print (priv->html, job_name, FALSE);
2013 #else
2014  gnc_html_print (priv->html, job_name);
2015 #endif
2016 
2017  g_free (job_name);
2018 }
2019 
2020 static void
2021 gnc_plugin_page_report_exportpdf_cb (GSimpleAction *simple,
2022  GVariant *parameter,
2023  gpointer user_data)
2024 {
2025  GncPluginPageReport *report = (GncPluginPageReport*)user_data;
2026  GncPluginPageReportPrivate *priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
2027  gchar *job_name = report_create_jobname(priv);
2028  GncInvoice *invoice;
2029  GncOwner *owner = nullptr;
2030 
2031  // Do we have an invoice report?
2032  invoice = lookup_invoice(priv);
2033  if (invoice)
2034  {
2035  // Does this invoice also have an owner?
2036  owner = (GncOwner*) gncInvoiceGetOwner(invoice);
2037  if (owner)
2038  {
2039  QofInstance *inst = qofOwnerGetOwner (owner);
2040  gchar *dirname = nullptr;
2041  qof_instance_get (inst, "export-pdf-dir", &dirname, nullptr);
2042  // Yes. In the kvp, look up the key for the Export-PDF output
2043  // directory. If it exists, prepend this to the job name so that
2044  // we can export to PDF.
2045  if (dirname && g_file_test (dirname,
2046  (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))
2047  {
2048  gchar *tmp = g_build_filename (dirname, job_name, nullptr);
2049  g_free (job_name);
2050  job_name = tmp;
2051  }
2052  }
2053  }
2054 
2055  //g_warning("Setting job name=%s", job_name);
2056 
2057 #ifdef WEBKIT1
2058  gnc_html_print (priv->html, job_name, TRUE);
2059 #else
2060  gnc_html_print (priv->html, job_name);
2061 #endif
2062 
2063  if (owner)
2064  {
2065  /* As this is an invoice report with some owner, we will try
2066  * to look up the chosen output directory from the print
2067  * settings and store it again in the owner kvp.
2068  */
2069  GtkPrintSettings *print_settings = gnc_print_get_settings();
2070  if (print_settings && gtk_print_settings_has_key (print_settings,
2072  {
2073  const char* dirname = gtk_print_settings_get (print_settings,
2075  // Only store the directory if it exists.
2076  if (g_file_test (dirname,
2077  (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))
2078  {
2079  QofInstance *inst = qofOwnerGetOwner (owner);
2080  gncOwnerBeginEdit (owner);
2081  qof_instance_set (inst, "export-pdf-dir", dirname);
2082  gncOwnerCommitEdit (owner);
2083  }
2084  }
2085  }
2086  g_free (job_name);
2087 }
2088 
2089 static void
2090 gnc_plugin_page_report_copy_cb (GSimpleAction *simple,
2091  GVariant *parameter,
2092  gpointer user_data)
2093 {
2094  GncPluginPageReport *report = (GncPluginPageReport*)user_data;
2096 
2097  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
2098  gnc_html_copy_to_clipboard(priv->html);
2099 }
2100 
2101 /********************************************************************
2102  * gnc_main_window_open_report()
2103  * open an report in a top level window from an ID number
2104  ********************************************************************/
2105 
2106 void
2107 gnc_main_window_open_report(int report_id, GncMainWindow *window)
2108 {
2109  GncPluginPage *reportPage;
2110 
2111  if (window)
2112  g_return_if_fail(GNC_IS_MAIN_WINDOW(window));
2113 
2114  reportPage = gnc_plugin_page_report_new( report_id );
2115  gnc_main_window_open_page( window, reportPage );
2116 }
2117 
2118 void
2119 gnc_main_window_open_report_url(const char * url, GncMainWindow *window)
2120 {
2121  GncPluginPage *reportPage;
2122 
2123  DEBUG( "report url: [%s]\n", url );
2124 
2125  if (window)
2126  g_return_if_fail(GNC_IS_MAIN_WINDOW(window));
2127 
2128  reportPage = gnc_plugin_page_report_new( 42 /* url? */ );
2129  gnc_main_window_open_page( window, reportPage );
2130 }
2131 
Holds all of the options for a book, report, or stylesheet, organized by GncOptionSections.
ISO: yyyy-mm-dd.
Definition: gnc-date.h:127
GncHtml * html
the gnc_html abstraction this PluginPage contains
GtkWidget * gnc_plugin_page_get_window(GncPluginPage *page)
Retrieve a pointer to the GncMainWindow (GtkWindow) containing this page.
const gchar * tab_icon
The relative name of the icon that should be shown on the tab for this page.
GtkContainer * container
the container the above HTML widget is in.
const QofInstance * gnc_option_db_lookup_qofinstance_value(GncOptionDB *odb, const char *section, const char *name)
Retrieve the QofInstance value of an option in the GncOptionDB.
gboolean(* focus_page_function)(GncPluginPage *plugin_page)
This function performs specific actions to set the focus on a specific widget.
void gnc_main_window_update_menu_and_toolbar(GncMainWindow *window, GncPluginPage *page, const gchar **ui_updates)
Update the main window menu with the placeholders listed in ui_updates and load the page specific too...
void qof_instance_get(const QofInstance *inst, const gchar *first_prop,...)
Wrapper for g_object_get.
The instance data structure for a content plugin.
gchar * gnc_prefs_get_string(const gchar *group, const gchar *pref_name)
Get a string value from the preferences backend.
gchar * gnc_build_userdata_path(const gchar *filename)
Make a path to filename in the user&#39;s gnucash data directory.
#define GNC_GTK_PRINT_SETTINGS_EXPORT_DIR
Key for saving the PDF-export directory in the print settings.
Definition: print-session.h:74
GtkWindow * gnc_ui_get_main_window(GtkWidget *widget)
Get a pointer to the final GncMainWindow widget is rooted in.
utility functions for the GnuCash UI
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
gtk helper routines.
GncPluginPage *(* recreate_page)(GtkWidget *window, GKeyFile *file, const gchar *group)
Create a new page based on the information saved during a previous instantiation of gnucash...
void gnc_main_window_menu_add_accelerator_keys(GncMainWindow *window)
Scan the main window menu and add accelerator keys to main window accelerator group.
void gnc_main_window_init_short_names(GncMainWindow *window, GncToolBarShortNames *toolbar_labels)
Update the labels of the toolbar items with short names.
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
const gchar * gnc_plugin_page_get_page_name(GncPluginPage *page)
Retrieve the name of this page.
Functions that are supported by all types of windows.
void qof_instance_set(QofInstance *inst, const gchar *first_prop,...)
Wrapper for g_object_set Group setting multiple parameters in a single begin/commit/rollback.
A structure for defining alternate action names for use in the toolbar.
Definition: gnc-plugin.h:204
GtkWidget * gnc_main_window_menu_find_menu_item(GncMainWindow *window, const gchar *action_name)
Find the menu item with the given action name for the window specified.
void gnc_utf8_strip_invalid_and_controls(gchar *str)
Strip any non-utf8 characters and any control characters (everything < 0x20, , , ...
GtkWidget * window
The window that contains the display widget for this plugin.
gboolean gnc_main_window_is_restoring_pages(GncMainWindow *window)
Check if the main window is restoring the plugin pages.
GSimpleActionGroup * gnc_plugin_page_create_action_group(GncPluginPage *page, const gchar *group_name)
Create the GSimpleActionGroup object associated with this page.
gboolean gnc_main_window_update_menu_for_action(GncMainWindow *window, const gchar *action_name, const gchar *label, const gchar *tooltip)
Find the GMenuModel item given the action name for the window specified.
#define PERR(format, args...)
Log a serious error.
Definition: qoflog.h:244
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
void gncOwnerBeginEdit(GncOwner *owner)
These are convenience wrappers around gnc{Vendor,Customer,Job,Employee}* functions.
Definition: gncOwner.c:72
GncPluginPage * gnc_main_window_get_current_page(GncMainWindow *window)
Retrieve a pointer to the page that is currently at the front of the specified window.
void gnc_main_window_open_page(GncMainWindow *window, GncPluginPage *page)
Display a data plugin page in a window.
QofInstance * qofOwnerGetOwner(const GncOwner *owner)
return the owner itself as an entity.
Definition: gncOwner.c:275
void(* update_edit_menu_actions)(GncPluginPage *plugin_page, gboolean hide)
This function vector allows page specific actions to override the generic code for setting the sensit...
void(* destroy_widget)(GncPluginPage *plugin_page)
Function called to destroy the display widget for a particular type of plugin.
#define PWARN(format, args...)
Log a warning.
Definition: qoflog.h:250
This file contains the functions to present a GUI to manage custom reports.
The class data structure for a content plugin.
GAction * gnc_main_window_find_action(GncMainWindow *window, const gchar *action_name)
Find the GAction in the main window.
Functions providing the file history menu.
void gnc_plugin_page_disconnect_page_changed(GncPluginPage *page)
Disconnect the page_changed_id signal callback.
void gnc_option_db_destroy(GncOptionDB *odb)
Destruct and release a GncOptionDB.
char * gnc_print_time64(time64 time, const char *format)
print a time64 as a date string per format
Definition: gnc-date.cpp:370
const gchar * plugin_name
The textual name of this plugin.
GtkWidget *(* create_widget)(GncPluginPage *plugin_page)
Function called to create the display widget for a particular type of plugin.
void(* page_name_changed)(GncPluginPage *plugin_page, const gchar *name)
This function vector allows page specific actions to occur when the page name is changed.
void gnc_plugin_set_actions_enabled(GActionMap *action_map, const gchar **action_names, gboolean enable)
This function sets the sensitivity of a GAction in a specific group.
Definition: gnc-plugin.c:250
Gnome specific utility functions.
gboolean(* finish_pending)(GncPluginPage *plugin_page)
This function vector is called to finish any outstanding activities.
All type declarations for the whole Gnucash engine.
void(* save_page)(GncPluginPage *page, GKeyFile *file, const gchar *group)
Save enough information about this page so that it can be recreated next time the user starts gnucash...
GncPluginPage * gnc_plugin_page_report_new(int reportId)
GtkPrintSettings * gnc_print_get_settings()
Returns the pointer to our static GtkPrintSettings object.
GLib helper routines.
Generic api to store and retrieve preferences.
A structure for defining alternate action names for use in the toolbar.
SCM cur_report
The report which this Page is satisfying.
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Get a boolean value from the preferences backend.
void gnc_plugin_page_inserted_cb(GncPluginPage *page, gpointer user_data)
Set up the page_changed callback for when the current page is changed.
void gnc_main_window_close_page(GncPluginPage *page)
Remove a data plugin page from a window and display the previous page.
Functions for adding plugins to a GnuCash window.
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
time64 gnc_time(time64 *tbuf)
get the current time
Definition: gnc-date.cpp:262
Implementation details for GncOptionDB.
void gnc_plugin_page_add_book(GncPluginPage *page, QofBook *book)
Add a book reference to the specified page.
File path resolution utility functions.
gboolean gnc_date_string_to_dateformat(const gchar *format_string, QofDateFormat *format)
Converts the date format to a printable string.
GncOptionDB * cur_odb
The Option DB for this report.
const gchar * qof_date_format_get_string(QofDateFormat df)
This function returns a strftime formatting string for printing an all numeric date (e...
Definition: gnc-date.cpp:502
void main_window_update_page_name(GncPluginPage *page, const gchar *name_in)
Update the name of the page in the main window.
GAction * gnc_plugin_page_get_action(GncPluginPage *page, const gchar *name)
Retrieve a GAction object associated with this page.
QofDateFormat
Enum for determining a date format.
Definition: gnc-date.h:122
GtkWidget * gnc_main_window_toolbar_find_tool_item(GncMainWindow *window, const gchar *action_name)
Find the toolbar item with the given action name for the window specified.
#define SCHEME_OPTIONS
The key name used it the state file for storing the report options.
const char * action_name
The name of the action.
Definition: gnc-plugin.h:207