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