GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
gnc-plugin-page-report.c
Go to the documentation of this file.
1 /* gnc-plugin-page-report.c
2  * Copyright (C) 2004 Joshua Sled <[email protected]>
3  * Copyright (C) 2005 David Hampton <[email protected]>
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 [email protected]
28  */
29 
40 #include "config.h"
41 
42 #include <gtk/gtk.h>
43 #include <glib/gi18n.h>
44 #include <glib/gstdio.h>
45 #include <libguile.h>
46 #include <sys/stat.h>
47 #include <errno.h>
48 
49 #include "gfec.h"
50 #include "dialog-custom-report.h"
51 #include "gnc-component-manager.h"
52 #include "gnc-engine.h"
53 #include "gnc-gnome-utils.h"
54 #include "gnc-guile-utils.h"
55 #include "gnc-html-history.h"
56 #include "gnc-html.h"
57 #include "gnc-html-factory.h"
58 #include "gnc-file.h"
59 #include "gnc-plugin.h"
60 #include "gnc-plugin-page-report.h"
62 #include "gnc-prefs.h"
63 #include "gnc-report.h"
64 #include "gnc-session.h"
65 #include "gnc-ui-util.h"
66 #include "gnc-ui.h"
67 #include "gnc-window.h"
68 #include "option-util.h"
69 #include "window-report.h"
70 #include "swig-runtime.h"
71 #include "app-utils/business-options.h"
72 #include "gnome-utils/gnc-icons.h"
74 
75 #define WINDOW_REPORT_CM_CLASS "window-report"
76 
77 /* NW: you can add GNC_MOD_REPORT to gnc-engine.h
78 or simply define it locally. Any unique string with
79 a gnucash- prefix will do. Then just set a log level
80 with qof_log_set_level().*/
81 static QofLogModule log_module = GNC_MOD_GUI;
82 
83 static GObjectClass *parent_class = NULL;
84 
85 // A static GHashTable to record the usage count for each printer
86 // output name. FIXME: Currently this isn't cleaned up at program
87 // shutdown because there isn't a place to easily insert a finalize()
88 // function for this. Oh well.
89 static GHashTable *static_report_printnames = NULL;
90 
91 // Property-id values.
92 enum
93 {
94  PROP_0,
95  PROP_REPORT_ID,
96 };
97 
99 {
101  int reportId;
102  gint component_manager_id;
103 
108  SCM option_change_cb_id;
109 
110  /* initial_report is special; it's the one that's saved and
111  * restored. The name_change_callback only gets called when
112  * the initial_report name is changed. */
113  SCM initial_report;
114  GNCOptionDB * initial_odb;
115  SCM name_change_cb_id;
116 
117  /* keep a list of edited reports so that we can destroy them when
118  * the window is closed. */
119  SCM edited_reports;
120 
121  /* This is set to mark the fact that we need to reload the html */
122  gboolean need_reload;
123 
124  /* The page is in the process of reloading the html */
125  gboolean reloading;
126 
128 // gnc_html *html;
130 
132  GtkContainer *container;
134 
135 #define GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(o) \
136  (G_TYPE_INSTANCE_GET_PRIVATE ((o), GNC_TYPE_PLUGIN_PAGE_REPORT, GncPluginPageReportPrivate))
137 
138 static void gnc_plugin_page_report_class_init( GncPluginPageReportClass *klass );
139 static void gnc_plugin_page_report_init( GncPluginPageReport *plugin_page );
140 static GObject *gnc_plugin_page_report_constructor(GType this_type, guint n_properties, GObjectConstructParam *properties);
141 static void gnc_plugin_page_report_finalize (GObject *object);
142 static void gnc_plugin_page_report_setup( GncPluginPage *ppage );
143 
144 static void gnc_plugin_page_report_constr_init(GncPluginPageReport *plugin_page, gint reportId);
145 
146 static GtkWidget* gnc_plugin_page_report_create_widget( GncPluginPage *plugin_page );
147 static void gnc_plugin_page_report_destroy_widget( GncPluginPage *plugin_page );
148 static void gnc_plugin_page_report_save_page (GncPluginPage *plugin_page, GKeyFile *file, const gchar *group);
149 static GncPluginPage *gnc_plugin_page_report_recreate_page (GtkWidget *window, GKeyFile *file, const gchar *group);
150 static void gnc_plugin_page_report_name_changed (GncPluginPage *page, const gchar *name);
151 static void gnc_plugin_page_report_update_edit_menu (GncPluginPage *page, gboolean hide);
152 static gboolean gnc_plugin_page_report_finish_pending (GncPluginPage *page);
153 
154 static int gnc_plugin_page_report_check_urltype(URLType t);
155 //static void gnc_plugin_page_report_load_cb(gnc_html * html, URLType type,
156 static void gnc_plugin_page_report_load_cb(GncHtml * html, URLType type,
157  const gchar * location, const gchar * label,
158  gpointer data);
159 static void gnc_plugin_page_report_expose_event_cb(GtkWidget *unused, GdkEventExpose *unused1, gpointer data);
160 static void gnc_plugin_page_report_refresh (gpointer data);
161 static void gnc_plugin_page_report_set_fwd_button(GncPluginPageReport * page, int enabled);
162 static void gnc_plugin_page_report_set_back_button(GncPluginPageReport * page, int enabled);
163 static void gnc_plugin_page_report_history_destroy_cb(gnc_html_history_node * node, gpointer user_data);
164 static void close_handler(gpointer user_data);
165 void gnc_plugin_page_report_destroy(GncPluginPageReportPrivate *priv);
166 static void gnc_plugin_page_report_option_change_cb(gpointer data);
167 
168 void gnc_plugin_page_report_remove_edited_report(GncPluginPageReportPrivate *priv, SCM report);
169 void gnc_plugin_page_report_add_edited_report(GncPluginPageReportPrivate *priv, SCM report);
170 void gnc_plugin_page_report_raise_editor(SCM report);
171 
172 static void gnc_plugin_page_report_forw_cb(GtkAction *action, GncPluginPageReport *rep);
173 static void gnc_plugin_page_report_back_cb(GtkAction *action, GncPluginPageReport *rep);
174 static void gnc_plugin_page_report_reload_cb(GtkAction *action, GncPluginPageReport *rep);
175 static void gnc_plugin_page_report_stop_cb(GtkAction *action, GncPluginPageReport *rep);
176 static void gnc_plugin_page_report_save_cb(GtkAction *action, GncPluginPageReport *rep);
177 static void gnc_plugin_page_report_save_as_cb(GtkAction *action, GncPluginPageReport *rep);
178 static void gnc_plugin_page_report_export_cb(GtkAction *action, GncPluginPageReport *rep);
179 static void gnc_plugin_page_report_options_cb(GtkAction *action, GncPluginPageReport *rep);
180 static void gnc_plugin_page_report_print_cb(GtkAction *action, GncPluginPageReport *rep);
181 static void gnc_plugin_page_report_exportpdf_cb(GtkAction *action, GncPluginPageReport *rep);
182 static void gnc_plugin_page_report_copy_cb(GtkAction *action, GncPluginPageReport *rep);
183 
184 GType
186 {
187  static GType gnc_plugin_page_report_type = 0;
188 
189  if (gnc_plugin_page_report_type == 0)
190  {
191  static const GTypeInfo our_info =
192  {
193  sizeof (GncPluginPageReportClass),
194  NULL,
195  NULL,
196  (GClassInitFunc) gnc_plugin_page_report_class_init,
197  NULL,
198  NULL,
199  sizeof (GncPluginPageReport),
200  0,
201  (GInstanceInitFunc) gnc_plugin_page_report_init
202  };
203 
204  gnc_plugin_page_report_type = g_type_register_static (GNC_TYPE_PLUGIN_PAGE,
205  "GncPluginPageReport",
206  &our_info, 0);
207  }
208 
209  return gnc_plugin_page_report_type;
210 }
211 
212 static void
213 gnc_plugin_page_report_get_property( GObject *obj,
214  guint prop_id,
215  GValue *value,
216  GParamSpec *pspec )
217 {
218  GncPluginPageReport *rep;
220 
221  rep = GNC_PLUGIN_PAGE_REPORT( obj );
222  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(rep);
223 
224  switch ( prop_id )
225  {
226  case PROP_REPORT_ID:
227  g_value_set_int( value, priv->reportId );
228  break;
229  default:
230  PERR( "Unknown property id %d", prop_id );
231  break;
232  }
233 }
234 
235 static void
236 gnc_plugin_page_report_set_property( GObject *obj,
237  guint prop_id,
238  const GValue *value,
239  GParamSpec *pspec )
240 {
241  GncPluginPageReport *rep;
243 
244  rep = GNC_PLUGIN_PAGE_REPORT( obj );
245  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(rep);
246 
247  DEBUG( "setting property with id %d / %p to value %d",
248  prop_id, priv, g_value_get_int( value ) );
249 
250  switch ( prop_id )
251  {
252  case PROP_REPORT_ID:
253  priv->reportId = g_value_get_int( value );
254  break;
255  default:
256  PERR( "unknown property id %d", prop_id );
257  break;
258  }
259 
260 }
261 
262 static void
263 gnc_plugin_page_report_class_init (GncPluginPageReportClass *klass)
264 {
265  GObjectClass *object_class = G_OBJECT_CLASS (klass);
266  GncPluginPageClass *gnc_plugin_page_class = GNC_PLUGIN_PAGE_CLASS(klass);
267 
268  parent_class = g_type_class_peek_parent (klass);
269 
270  object_class->constructor = gnc_plugin_page_report_constructor;
271  object_class->finalize = gnc_plugin_page_report_finalize;
272 
273  object_class->set_property = gnc_plugin_page_report_set_property;
274  object_class->get_property = gnc_plugin_page_report_get_property;
275 
276  // FIXME: stock reporting icon?
277  //gnc_plugin_page_class->tab_icon = GNC_STOCK_ACCOUNT;
278  gnc_plugin_page_class->plugin_name = GNC_PLUGIN_PAGE_REPORT_NAME;
279 
280  gnc_plugin_page_class->create_widget = gnc_plugin_page_report_create_widget;
281  gnc_plugin_page_class->destroy_widget = gnc_plugin_page_report_destroy_widget;
282  gnc_plugin_page_class->save_page = gnc_plugin_page_report_save_page;
283  gnc_plugin_page_class->recreate_page = gnc_plugin_page_report_recreate_page;
284  gnc_plugin_page_class->page_name_changed = gnc_plugin_page_report_name_changed;
285  gnc_plugin_page_class->update_edit_menu_actions = gnc_plugin_page_report_update_edit_menu;
286  gnc_plugin_page_class->finish_pending = gnc_plugin_page_report_finish_pending;
287 
288  g_type_class_add_private(klass, sizeof(GncPluginPageReportPrivate));
289 
290  // create the "reportId" property
291  g_object_class_install_property( object_class,
292  PROP_REPORT_ID,
293  g_param_spec_int( "report-id",
294  _("The numeric ID of the report."),
295  _("The numeric ID of the report."),
296  -1, G_MAXINT, -1, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE ) );
297 
298  /* JSLED: report-selected?
299  plugin_page_signals[ACCOUNT_SELECTED] =
300  g_signal_new ("account_selected",
301  G_OBJECT_CLASS_TYPE (object_class),
302  G_SIGNAL_RUN_FIRST,
303  G_STRUCT_OFFSET (GncPluginPageReportClass, account_selected),
304  NULL, NULL,
305  g_cclosure_marshal_VOID__POINTER,
306  G_TYPE_NONE, 1,
307  G_TYPE_POINTER);
308  */
309 
310  // Also initialize the report name usage count table
311  if (!static_report_printnames)
312  static_report_printnames = g_hash_table_new_full(g_str_hash,
313  g_str_equal, g_free, NULL);
314 }
315 
316 static void
317 gnc_plugin_page_report_finalize (GObject *object)
318 {
319  g_return_if_fail (GNC_IS_PLUGIN_PAGE_REPORT (object));
320 
321  ENTER("object %p", object);
322  G_OBJECT_CLASS (parent_class)->finalize (object);
323  LEAVE(" ");
324 }
325 
326 static
327 GtkWidget*
328 gnc_plugin_page_report_create_widget( GncPluginPage *page )
329 {
330  GncPluginPageReport *report;
332  GtkWindow *topLvl;
333  URLType type;
334  char * id_name;
335  char * child_name;
336  char * url_location = NULL;
337  char * url_label = NULL;
338 
339  ENTER("page %p", page);
340 
341  report = GNC_PLUGIN_PAGE_REPORT(page);
342  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
343 
344  topLvl = GTK_WINDOW(gnc_ui_get_toplevel());
345 // priv->html = gnc_html_new( topLvl );
346  priv->html = gnc_html_factory_create_html();
347  gnc_html_set_parent( priv->html, topLvl );
348 
349  gnc_html_history_set_node_destroy_cb(gnc_html_get_history(priv->html),
350  gnc_plugin_page_report_history_destroy_cb,
351  (gpointer)priv);
352 
353  priv->container = GTK_CONTAINER(gtk_frame_new(NULL));
354  gtk_frame_set_shadow_type(GTK_FRAME(priv->container), GTK_SHADOW_NONE);
355 
356  gtk_container_add(GTK_CONTAINER(priv->container),
357  gnc_html_get_widget(priv->html));
358 
359  priv->component_manager_id =
360  gnc_register_gui_component(WINDOW_REPORT_CM_CLASS, NULL,
361  close_handler, page);
362  gnc_gui_component_set_session(priv->component_manager_id,
363  gnc_get_current_session());
364 
365  gnc_html_set_urltype_cb(priv->html, gnc_plugin_page_report_check_urltype);
366  gnc_html_set_load_cb(priv->html, gnc_plugin_page_report_load_cb, report);
367 
368  // FIXME. This is f^-1(f(x)), isn't it?
369  DEBUG( "id=%d", priv->reportId );
370  id_name = g_strdup_printf("id=%d", priv->reportId );
371  child_name = gnc_build_url( URL_TYPE_REPORT, id_name, NULL );
372  type = gnc_html_parse_url( priv->html, child_name, &url_location, &url_label);
373  DEBUG( "passing id_name=[%s] child_name=[%s] type=[%s], location=[%s], label=[%s]",
374  id_name, child_name ? child_name : "(null)",
375  type ? type : "(null)", url_location ? url_location : "(null)",
376  url_label ? url_label : "(null)" );
377 
378  g_free(id_name);
379  g_free(child_name);
380  gnc_window_set_progressbar_window( GNC_WINDOW(page->window) );
381  gnc_html_show_url(priv->html, type, url_location, url_label, 0);
382  g_free(url_location);
383  gnc_window_set_progressbar_window( NULL );
384 
385  g_signal_connect(priv->container, "expose_event",
386  G_CALLBACK(gnc_plugin_page_report_expose_event_cb), report);
387 
388  gtk_widget_show_all( GTK_WIDGET(priv->container) );
389 
390  LEAVE("container %p", priv->container);
391 
392  return GTK_WIDGET( priv->container );
393 }
394 
395 /********************************************************************
396  * gnc_plugin_page_report_check_urltype
397  * is it OK to show a certain URLType in this window?
398  ********************************************************************/
399 static int
400 gnc_plugin_page_report_check_urltype(URLType t)
401 {
402  if (!g_strcmp0 (t, URL_TYPE_REPORT))
403  {
404  return TRUE;
405  }
406  else
407  {
408  return FALSE;
409  }
410 }
411 
416 static void
417 gnc_plugin_page_report_setup( GncPluginPage *ppage )
418 {
419  GncPluginPageReport *report = GNC_PLUGIN_PAGE_REPORT(ppage);
421  SCM set_needs_save = scm_c_eval_string("gnc:report-set-needs-save?!");
422  SCM inst_report;
423  int report_id;
424 
425  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
426  priv->cur_report = SCM_BOOL_F;
427  priv->initial_report = SCM_BOOL_F;
428  priv->edited_reports = SCM_EOL;
429  priv->name_change_cb_id = SCM_BOOL_F;
430 
431  g_object_get( ppage, "report-id", &report_id, NULL );
432 
433  PINFO("report-id: %d\n", report_id);
434 
435  /* get the inst-report from the Scheme-side hash, and get its
436  * options and editor thunk */
437  if ((inst_report = gnc_report_find(report_id)) == SCM_BOOL_F)
438  {
439  return;
440  }
441 
442  if (priv->initial_report == SCM_BOOL_F)
443  {
444  priv->initial_report = inst_report;
445  scm_gc_protect_object(priv->initial_report);
446  }
447 
448  // all reports need [to be] saved immediately after they're created.
449  PINFO("set needs save");
450  scm_call_2(set_needs_save, inst_report, SCM_BOOL_T);
451 }
452 
453 /********************************************************************
454  * gnc_plugin_page_report_load_cb
455  * called after a report is loaded into the gnc_html widget
456  ********************************************************************/
457 static void
458 //gnc_plugin_page_report_load_cb(gnc_html * html, URLType type,
459 gnc_plugin_page_report_load_cb(GncHtml * html, URLType type,
460  const gchar * location, const gchar * label,
461  gpointer data)
462 {
463  GncPluginPageReport *report = GNC_PLUGIN_PAGE_REPORT(data);
465  int report_id;
466  SCM get_options = scm_c_eval_string("gnc:report-options");
467  SCM set_needs_save = scm_c_eval_string("gnc:report-set-needs-save?!");
468  SCM inst_report;
469 
470  ENTER( "load_cb: type=[%s], location=[%s], label=[%s]",
471  type ? type : "(null)", location ? location : "(null)",
472  label ? label : "(null)" );
473 
474  /* we get this callback if a new report is requested to be loaded OR
475  * if any URL is clicked. If an options URL is clicked, we want to
476  * know about it */
477  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
478  if (!g_strcmp0 (type, URL_TYPE_REPORT)
479  && location
480  && (strlen(location) > 3)
481  && !strncmp("id=", location, 3))
482  {
483  report_id = atoi(location + 3);
484  DEBUG( "parsed id=%d", report_id );
485  }
486  else if (!g_strcmp0( type, URL_TYPE_OPTIONS)
487  && location
488  && (strlen(location) > 10)
489  && !strncmp("report-id=", location, 10))
490  {
491  report_id = atoi(location + 10);
492  inst_report = gnc_report_find(report_id);
493  if (inst_report != SCM_BOOL_F)
494  {
495  gnc_plugin_page_report_add_edited_report(priv, inst_report);
496  }
497  LEAVE("");
498  return;
499  }
500  else
501  {
502  LEAVE( " unknown URL type [%s] location [%s]", type, location );
503  return;
504  }
505 
506  /* get the inst-report from the hash, and get its
507  * options and editor thunk */
508  if ((inst_report = gnc_report_find(report_id)) == SCM_BOOL_F)
509  {
510  LEAVE( "error getting inst_report" );
511  return;
512  }
513 
514  if (priv->initial_report == SCM_BOOL_F)
515  {
516  priv->initial_report = inst_report;
517  scm_gc_protect_object(priv->initial_report);
518 
519  DEBUG("calling set_needs_save for report with id=%d", report_id);
520  scm_call_2(set_needs_save, inst_report, SCM_BOOL_T);
521 
522  priv->initial_odb = gnc_option_db_new(scm_call_1(get_options, inst_report));
523  priv->name_change_cb_id =
524  gnc_option_db_register_change_callback(priv->initial_odb,
525  gnc_plugin_page_report_refresh,
526  priv,
527  "General", "Report name");
528  }
529 
530  if ((priv->cur_report != SCM_BOOL_F) && (priv->cur_odb != NULL))
531  {
532  gnc_option_db_unregister_change_callback_id(priv->cur_odb,
533  priv->option_change_cb_id);
534  gnc_option_db_destroy(priv->cur_odb);
535  priv->cur_odb = NULL;
536  }
537 
538  if (priv->cur_report != SCM_BOOL_F)
539  scm_gc_unprotect_object(priv->cur_report);
540  priv->cur_report = inst_report;
541  scm_gc_protect_object(priv->cur_report);
542 
543  priv->cur_odb = gnc_option_db_new(scm_call_1(get_options, inst_report));
544  priv->option_change_cb_id =
545  gnc_option_db_register_change_callback(priv->cur_odb,
546  gnc_plugin_page_report_option_change_cb,
547  report, NULL, NULL);
548 
549  if (gnc_html_history_forward_p(gnc_html_get_history(priv->html)))
550  {
551  gnc_plugin_page_report_set_fwd_button(report, TRUE);
552  }
553  else
554  {
555  gnc_plugin_page_report_set_fwd_button(report, FALSE);
556  }
557 
558  if (gnc_html_history_back_p(gnc_html_get_history(priv->html)))
559  {
560  gnc_plugin_page_report_set_back_button(report, TRUE);
561  }
562  else
563  {
564  gnc_plugin_page_report_set_back_button(report, FALSE);
565  }
566 
567  LEAVE( "done" );
568 }
569 
570 
582 static void
583 gnc_plugin_page_report_option_change_cb(gpointer data)
584 {
585  GncPluginPageReport *report;
587  SCM dirty_report = scm_c_eval_string("gnc:report-set-dirty?!");
588  const gchar *old_name;
589  gchar *new_name;
590  gchar *new_name_escaped;
591 
592  g_return_if_fail(GNC_IS_PLUGIN_PAGE_REPORT(data));
593  report = GNC_PLUGIN_PAGE_REPORT(data);
594  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
595 
596  DEBUG( "option_change" );
597  if (priv->cur_report == SCM_BOOL_F)
598  return;
599  DEBUG( "set-dirty, queue-draw" );
600 
601  /* Update the page (i.e. the notebook tab and window title) */
602  old_name = gnc_plugin_page_get_page_name(GNC_PLUGIN_PAGE(report));
603  new_name = gnc_option_db_lookup_string_option(priv->cur_odb, "General",
604  "Report name", NULL);
605  if (strcmp(old_name, new_name) != 0)
606  {
607  /* Bug 727130 - escape the non-printable characters from the name */
608  new_name_escaped = g_strescape(new_name,NULL);
609  ENTER("Escaped new report name: %s", new_name_escaped);
610  main_window_update_page_name(GNC_PLUGIN_PAGE(report), new_name_escaped);
611  g_free(new_name_escaped);
612  }
613  g_free(new_name);
614 
615  /* it's probably already dirty, but make sure */
616  scm_call_2(dirty_report, priv->cur_report, SCM_BOOL_T);
617 
618  /* Now queue the fact that we need to reload this report */
619  priv->need_reload = TRUE;
620  // jsled: this doesn't seem to cause any effect.
621  gtk_widget_queue_draw( GTK_WIDGET(priv->container) );
622  // jsled: this does.
623  gnc_html_reload( priv->html );
624 }
625 
626 /* FIXME: This function does... nothing. */
627 static void
628 gnc_plugin_page_report_history_destroy_cb(gnc_html_history_node * node,
629  gpointer user_data)
630 {
631 #if 0
632  static SCM remover = SCM_BOOL_F;
633  int report_id;
634 
635  if (remover == SCM_BOOL_F)
636  {
637  remover = scm_c_eval_string("gnc:report-remove-by-id");
638  }
639 
640  if (node
641  && !g_strcmp0 (node->type, URL_TYPE_REPORT)\
642  && !strncmp("id=", node->location, 3))
643  {
644  sscanf(node->location + 3, "%d", &report_id);
645  /* printf("unreffing report %d and children\n", report_id);
646  scm_call_1(remover, scm_from_int (report_id)); */
647  }
648  else
649  {
650  return;
651  }
652 #endif
653 }
654 
655 /* We got a draw event. See if we need to reload the report */
656 static void
657 gnc_plugin_page_report_expose_event_cb(GtkWidget *unused, GdkEventExpose *unused1, gpointer data)
658 {
659  GncPluginPageReport *page = data;
661 
662  g_return_if_fail(GNC_IS_PLUGIN_PAGE_REPORT(page));
663 
664  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(page);
665  ENTER( "report_draw" );
666  if (!priv->need_reload)
667  {
668  LEAVE( "no reload needed" );
669  return;
670  }
671 
672  priv->need_reload = FALSE;
673  gnc_window_set_progressbar_window( GNC_WINDOW(GNC_PLUGIN_PAGE(page)->window) );
674  gnc_html_reload(priv->html);
675  gnc_window_set_progressbar_window( NULL );
676  LEAVE( "reload forced" );
677 }
678 
679 // @param data is actually GncPluginPageReportPrivate
680 static void
681 gnc_plugin_page_report_refresh(gpointer data)
682 {
683  // FIXME?
684  DEBUG( "report-refresh called" );
685  // something like ... gnc_plugin_page_report_redraw( NULL, (GncPluginPageReportPrivate*)data );
686  return;
687 }
688 
689 static void
690 gnc_plugin_page_report_destroy_widget(GncPluginPage *plugin_page)
691 {
693 
694  // FIXME: cleanup other resources.
695 
696  PINFO("destroy widget");
697  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(plugin_page);
698 
699  if (priv->component_manager_id)
700  {
701  gnc_unregister_gui_component(priv->component_manager_id);
702  priv->component_manager_id = 0;
703  }
704 
705  gnc_plugin_page_report_destroy(priv);
706  gnc_report_remove_by_id(priv->reportId);
707 }
708 
709 
712 #define SCHEME_OPTIONS "SchemeOptions"
713 #define SCHEME_OPTIONS_N "SchemeOptions%d"
714 
715 
725 static void
726 gnc_plugin_page_report_save_page (GncPluginPage *plugin_page,
727  GKeyFile *key_file,
728  const gchar *group_name)
729 {
730  GncPluginPageReport *report;
732  SCM gen_save_text, scm_text;
733  SCM get_embedded_list, embedded, item, tmp_report;
734  SCM get_options;
735  gint count, id;
736  gchar *text, *key_name;
737 
738  g_return_if_fail (GNC_IS_PLUGIN_PAGE_REPORT(plugin_page));
739  g_return_if_fail (key_file != NULL);
740  g_return_if_fail (group_name != NULL);
741 
742  ENTER("page %p, key_file %p, group_name %s", plugin_page, key_file,
743  group_name);
744 
745  report = GNC_PLUGIN_PAGE_REPORT(plugin_page);
746  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
747 
748  if (!priv || !priv->cur_report || scm_is_null(priv->cur_report) ||
749  SCM_UNBNDP(priv->cur_report) || SCM_BOOL_F == priv->cur_report)
750  {
751  LEAVE("not saving invalid report");
752  return;
753  }
754 
755  gen_save_text = scm_c_eval_string("gnc:report-serialize");
756  get_embedded_list = scm_c_eval_string("gnc:report-embedded-list");
757  get_options = scm_c_eval_string("gnc:report-options");
758  embedded = scm_call_1(get_embedded_list, scm_call_1(get_options, priv->cur_report));
759  count = scm_ilength(embedded);
760  while (count-- > 0)
761  {
762  item = SCM_CAR(embedded);
763  embedded = SCM_CDR(embedded);
764  if (!scm_is_number(item))
765  continue;
766  id = scm_to_int (item);
767  tmp_report = gnc_report_find(id);
768  scm_text = scm_call_1(gen_save_text, tmp_report);
769  if (!scm_is_string (scm_text))
770  {
771  DEBUG("child report %d: nothing to save", id);
772  continue;
773  }
774 
775  key_name = g_strdup_printf(SCHEME_OPTIONS_N, id);
776  text = gnc_scm_strip_comments(scm_text);
777  g_key_file_set_value(key_file, group_name, key_name, text);
778  g_free(text);
779  g_free(key_name);
780  }
781 
782  scm_text = scm_call_1(gen_save_text, priv->cur_report);
783  if (!scm_is_string (scm_text))
784  {
785  LEAVE("nothing to save");
786  return;
787  }
788 
789  text = gnc_scm_strip_comments(scm_text);
790  g_key_file_set_value(key_file, group_name, SCHEME_OPTIONS, text);
791  g_free(text);
792  LEAVE(" ");
793 }
794 
795 
805 static GncPluginPage *
806 gnc_plugin_page_report_recreate_page (GtkWidget *window,
807  GKeyFile *key_file,
808  const gchar *group_name)
809 {
810  GncPluginPage *page;
811  gchar **keys;
812  gsize i, num_keys;
813  GError *error = NULL;
814  gchar *option_string;
815  gint report_id;
816  SCM scm_id, final_id = SCM_BOOL_F;
817  SCM report;
818 
819  g_return_val_if_fail(key_file, NULL);
820  g_return_val_if_fail(group_name, NULL);
821  ENTER("key_file %p, group_name %s", key_file, group_name);
822 
823  keys = g_key_file_get_keys(key_file, group_name, &num_keys, &error);
824  if (error)
825  {
826  g_warning("error reading group %s key list: %s",
827  group_name, error->message);
828  g_error_free(error);
829  LEAVE("no keys");
830  return NULL;
831  }
832 
833  for (i = 0; i < num_keys; i++)
834  {
835  if (strncmp(keys[i], SCHEME_OPTIONS, strlen(SCHEME_OPTIONS)) != 0)
836  continue;
837  option_string = g_key_file_get_value(key_file, group_name,
838  keys[i], &error);
839  if (error)
840  {
841  g_warning("error reading group %s key %s: %s",
842  group_name, keys[i], error->message);
843  g_error_free(error);
844  LEAVE("bad value");
845  return NULL;
846  }
847 
848  scm_id = scm_c_eval_string(option_string);
849  g_free(option_string);
850 
851  if (!scm_integer_p(scm_id))
852  {
853  DEBUG("report id not an integer for key %s", keys[i]);
854  return NULL;
855  }
856 
857  if (final_id == SCM_BOOL_F)
858  {
859  if (g_strcmp0(keys[i], SCHEME_OPTIONS) == 0)
860  {
861  final_id = scm_id;
862  }
863  }
864  }
865 
866  if (final_id == SCM_BOOL_F)
867  {
868  LEAVE("report not specified");
869  return NULL;
870  }
871 
872  report_id = scm_to_int(final_id);
873  report = gnc_report_find(report_id);
874  if (!report)
875  {
876  LEAVE("report doesn't exist");
877  return NULL;
878  }
879 
880  page = gnc_plugin_page_report_new( report_id );
881 
882  LEAVE(" ");
883  return page;
884 }
885 
886 
897 static void
898 gnc_plugin_page_report_name_changed (GncPluginPage *page, const gchar *name)
899 {
901  const gchar *old_name;
902 
903  g_return_if_fail(GNC_IS_PLUGIN_PAGE_REPORT(page));
904  g_return_if_fail(name != NULL);
905 
906  ENTER("page %p, name %s", page, name);
907  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(page);
908 
909  /* Is this a redundant call? */
910  old_name = gnc_option_db_lookup_string_option(priv->cur_odb, "General",
911  "Report name", NULL);
912  DEBUG("Comparing old name '%s' to new name '%s'",
913  old_name ? old_name : "(null)", name);
914  if (old_name && (strcmp(old_name, name) == 0))
915  {
916  LEAVE("no change");
917  return;
918  }
919 
920  /* Store the new name for the report. */
921  gnc_option_db_set_string_option(priv->cur_odb, "General",
922  "Report name", name);
923 
924  /* Have to manually call the option change hook. */
925  gnc_plugin_page_report_option_change_cb(page);
926  LEAVE(" ");
927 }
928 
929 static void
930 gnc_plugin_page_report_update_edit_menu (GncPluginPage *page, gboolean hide)
931 {
932  GtkAction *action;
933 
934  action = gnc_plugin_page_get_action (page, "EditCopyAction");
935  gtk_action_set_sensitive (action, TRUE);
936  gtk_action_set_visible (action, TRUE);
937  action = gnc_plugin_page_get_action (page, "EditCutAction");
938  gtk_action_set_sensitive (action, FALSE);
939  gtk_action_set_visible (action, !hide);
940  action = gnc_plugin_page_get_action (page, "EditPasteAction");
941  gtk_action_set_sensitive (action, FALSE);
942  gtk_action_set_visible (action, !hide);
943 }
944 
945 static gboolean
946 gnc_plugin_page_report_finish_pending (GncPluginPage *page)
947 {
949  GncPluginPageReport *report;
950 
951  report = GNC_PLUGIN_PAGE_REPORT(page);
952  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
953  return !priv->reloading;
954 }
955 
956 
957 /********************************************************************
958  * gnc_report_window_destroy
959  * free and destroy a window
960  ********************************************************************/
961 void
962 gnc_plugin_page_report_destroy(GncPluginPageReportPrivate * priv)
963 {
964  SCM get_editor = scm_c_eval_string("gnc:report-editor-widget");
965  SCM set_editor = scm_c_eval_string("gnc:report-set-editor-widget!");
966  SCM edited, editor;
967 
968  /* close any open editors */
969  for (edited = scm_list_copy(priv->edited_reports); !scm_is_null(edited);
970  edited = SCM_CDR(edited))
971  {
972  editor = scm_call_1(get_editor, SCM_CAR(edited));
973  scm_call_2(set_editor, SCM_CAR(edited), SCM_BOOL_F);
974  if (editor != SCM_BOOL_F)
975  {
976  GtkWidget *w = NULL;
977 #define FUNC_NAME "gtk_widget_destroy"
978  w = SWIG_MustGetPtr(editor,
979  SWIG_TypeQuery("_p_GtkWidget"), 1, 0);
980 #undef FUNC_NAME
981  gtk_widget_destroy(GTK_WIDGET(w));
982  }
983  }
984 
985  if (priv->initial_odb)
986  {
987  gnc_option_db_unregister_change_callback_id(priv->initial_odb,
988  priv->name_change_cb_id);
989 
990  gnc_option_db_destroy(priv->initial_odb);
991  priv->initial_odb = NULL;
992  }
993 
994  gnc_html_destroy(priv->html);
995 
996  priv->container = NULL;
997  priv->html = NULL;
998 
999  if (priv->cur_report != SCM_BOOL_F)
1000  scm_gc_unprotect_object(priv->cur_report);
1001  if (priv->edited_reports != SCM_EOL)
1002  scm_gc_unprotect_object(priv->edited_reports);
1003 }
1004 
1005 static GtkActionEntry report_actions[] =
1006 {
1007  {
1008  "FilePrintAction", GTK_STOCK_PRINT, N_("_Print Report..."), "<control>p",
1009  N_("Print the current report"),
1010  G_CALLBACK(gnc_plugin_page_report_print_cb)
1011  },
1012  {
1013  "FilePrintPDFAction", GNC_STOCK_PDF_EXPORT, N_("Export as P_DF..."), NULL,
1014  N_("Export the current report as a PDF document"),
1015  G_CALLBACK(gnc_plugin_page_report_exportpdf_cb)
1016  },
1017  {
1018  "EditCutAction", GTK_STOCK_CUT, N_("Cu_t"), NULL,
1019  N_("Cut the current selection and copy it to clipboard"),
1020  NULL
1021  },
1022  {
1023  "EditCopyAction", GTK_STOCK_COPY, N_("_Copy"), NULL,
1024  N_("Copy the current selection to clipboard"),
1025  G_CALLBACK(gnc_plugin_page_report_copy_cb)
1026  },
1027  {
1028  "EditPasteAction", GTK_STOCK_PASTE, N_("_Paste"), NULL,
1029  N_("Paste the clipboard content at the cursor position"),
1030  NULL
1031  },
1032  {
1033  "ViewRefreshAction", GTK_STOCK_REFRESH, N_("_Refresh"), "<control>r",
1034  N_("Refresh this window"),
1035  G_CALLBACK (gnc_plugin_page_report_reload_cb)
1036  },
1037  {
1038  "ReportSaveAction", GTK_STOCK_SAVE, N_("Save _Report Configuration"), "<control><alt>s",
1039  N_("Update the current report's saved configuration. "
1040  "The report will be saved in the file ~/.gnucash/saved-reports-2.4. "),
1041  G_CALLBACK(gnc_plugin_page_report_save_cb)
1042  },
1043  {
1044  "ReportSaveAsAction", GTK_STOCK_SAVE_AS, N_("Save Report Configuration As..."), "<control><alt><shift>s",
1045  N_("Add the current report's configuration to the `Saved Report Configurations' menu. "
1046  "The report will be saved in the file ~/.gnucash/saved-reports-2.4. "),
1047  G_CALLBACK(gnc_plugin_page_report_save_as_cb)
1048  },
1049  {
1050  "ReportExportAction", GTK_STOCK_CONVERT, N_("Export _Report"), NULL,
1051  N_("Export HTML-formatted report to file"),
1052  G_CALLBACK(gnc_plugin_page_report_export_cb)
1053  },
1054  {
1055  "ReportOptionsAction", GTK_STOCK_PROPERTIES, N_("_Report Options"), NULL,
1056  N_("Edit report options"),
1057  G_CALLBACK(gnc_plugin_page_report_options_cb)
1058  },
1059 
1060  {
1061  "ReportBackAction", GTK_STOCK_GO_BACK, N_("Back"), NULL,
1062  N_("Move back one step in the history"),
1063  G_CALLBACK(gnc_plugin_page_report_back_cb)
1064  },
1065  {
1066  "ReportForwAction", GTK_STOCK_GO_FORWARD, N_("Forward"), NULL,
1067  N_("Move forward one step in the history"),
1068  G_CALLBACK(gnc_plugin_page_report_forw_cb)
1069  },
1070  {
1071  "ReportReloadAction", GTK_STOCK_REFRESH, N_("Reload"), NULL,
1072  N_("Reload the current page"),
1073  G_CALLBACK(gnc_plugin_page_report_reload_cb)
1074  },
1075  {
1076  "ReportStopAction", GTK_STOCK_STOP, N_("Stop"), NULL,
1077  N_("Cancel outstanding HTML requests"),
1078  G_CALLBACK(gnc_plugin_page_report_stop_cb)
1079  },
1080 };
1081 static guint num_report_actions = G_N_ELEMENTS( report_actions );
1082 
1084 static action_toolbar_labels toolbar_labels[] =
1085 {
1086  { "FilePrintAction", N_("Print") },
1087  { "ReportExportAction", N_("Export") },
1088  { "ReportOptionsAction", N_("Options") },
1089  { NULL, NULL },
1090 };
1091 
1092 static const gchar *initially_insensitive_actions[] =
1093 {
1094  NULL
1095 };
1096 
1097 static void
1098 gnc_plugin_page_report_init ( GncPluginPageReport *plugin_page )
1099 {
1100 }
1101 
1102 static GObject*
1103 gnc_plugin_page_report_constructor(GType this_type, guint n_properties, GObjectConstructParam *properties)
1104 {
1105  GObject *obj;
1106  GncPluginPageReportClass *our_class;
1107  GObjectClass *parent_class;
1108  gint reportId = -42;
1109  int i;
1110 
1111  our_class = GNC_PLUGIN_PAGE_REPORT_CLASS (
1112  g_type_class_peek (GNC_TYPE_PLUGIN_PAGE_REPORT));
1113  parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (our_class));
1114  obj = parent_class->constructor(this_type, n_properties, properties);
1115 
1116  for (i = 0; i < n_properties; i++)
1117  {
1118  GObjectConstructParam prop = properties[i];
1119  if (strcmp(prop.pspec->name, "report-id") == 0)
1120  {
1121  reportId = g_value_get_int(prop.value);
1122  }
1123  }
1124 
1125  gnc_plugin_page_report_constr_init(GNC_PLUGIN_PAGE_REPORT(obj), reportId);
1126 
1127  return obj;
1128 }
1129 
1130 static void
1131 gnc_plugin_page_report_constr_init(GncPluginPageReport *plugin_page, gint reportId)
1132 {
1134  GtkActionGroup *action_group;
1135  GncPluginPage *parent;
1136  gboolean use_new;
1137  gchar *name;
1138 
1139  DEBUG( "property reportId=%d", reportId );
1140  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(plugin_page);
1141  priv->reportId = reportId;
1142 
1143  gnc_plugin_page_report_setup( GNC_PLUGIN_PAGE(plugin_page) );
1144 
1145  /* Init parent declared variables */
1146  parent = GNC_PLUGIN_PAGE(plugin_page);
1147  use_new = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL_REPORT, GNC_PREF_USE_NEW);
1148  name = gnc_report_name( priv->initial_report );
1149  g_object_set(G_OBJECT(plugin_page),
1150  "page-name", name,
1151  "page-uri", "default:",
1152  "ui-description", "gnc-plugin-page-report-ui.xml",
1153  "use-new-window", use_new,
1154  NULL);
1155  g_free(name);
1156 
1157  /* change me when the system supports multiple books */
1158  gnc_plugin_page_add_book(parent, gnc_get_current_book());
1159 
1160  /* Create menu and toolbar information */
1161  action_group =
1163  "GncPluginPageReportActions");
1164  gtk_action_group_add_actions( action_group,
1165  report_actions,
1166  num_report_actions,
1167  plugin_page );
1168  gnc_plugin_update_actions(action_group,
1169  initially_insensitive_actions,
1170  "sensitive", FALSE);
1171  gnc_plugin_init_short_names (action_group, toolbar_labels);
1172 }
1173 
1176 {
1177  GncPluginPageReport *plugin_page;
1178 
1179  DEBUG( "report id = %d", reportId );
1180  plugin_page = g_object_new( GNC_TYPE_PLUGIN_PAGE_REPORT,
1181  "report-id", reportId, NULL );
1182  DEBUG( "plugin_page: %p", plugin_page );
1183  DEBUG( "set %d on page %p", reportId, plugin_page );
1184  return GNC_PLUGIN_PAGE( plugin_page );
1185 }
1186 
1187 void
1188 gnc_plugin_page_report_remove_edited_report(GncPluginPageReportPrivate *priv,
1189  SCM report)
1190 {
1191  SCM new_edited = scm_delete(priv->edited_reports, report);
1192  if (priv->edited_reports != SCM_EOL)
1193  scm_gc_unprotect_object(priv->edited_reports);
1194  priv->edited_reports = new_edited;
1195  if (new_edited != SCM_EOL)
1196  scm_gc_protect_object(priv->edited_reports);
1197 }
1198 
1199 void
1200 gnc_plugin_page_report_add_edited_report(GncPluginPageReportPrivate *priv,
1201  SCM report)
1202 {
1203  SCM new_edited = scm_cons(report, priv->edited_reports);
1204  if (priv->edited_reports != SCM_EOL)
1205  scm_gc_unprotect_object(priv->edited_reports);
1206  priv->edited_reports = new_edited;
1207  if (new_edited != SCM_EOL)
1208  scm_gc_protect_object(priv->edited_reports);
1209 }
1210 
1211 void
1212 gnc_plugin_page_report_raise_editor(SCM report)
1213 {
1214  SCM get_editor = scm_c_eval_string("gnc:report-editor-widget");
1215  SCM editor = scm_call_1(get_editor, report);
1216 #define FUNC_NAME "gtk_window_present"
1217  GtkWidget *w = SWIG_MustGetPtr(editor,
1218  SWIG_TypeQuery("_p_GtkWidget"), 1, 0);
1219 #undef FUNC_NAME
1220  gtk_window_present(GTK_WINDOW(w));
1221 }
1222 
1223 static void
1224 close_handler (gpointer user_data)
1225 {
1226  GncPluginPage *plugin_page = GNC_PLUGIN_PAGE(user_data);
1227  DEBUG("in close handler\n");
1228  gnc_main_window_close_page (plugin_page);
1229 }
1230 
1231 static void
1232 gnc_plugin_page_report_set_fwd_button(GncPluginPageReport *report, int enabled)
1233 {
1234  GtkAction *act;
1235 
1236  act = gnc_plugin_page_get_action(GNC_PLUGIN_PAGE(report),
1237  "ReportForwAction" );
1238  gtk_action_set_sensitive(act, enabled);
1239 }
1240 
1241 static void
1242 gnc_plugin_page_report_set_back_button(GncPluginPageReport *report, int enabled)
1243 {
1244  GtkAction *act;
1245 
1246  act = gnc_plugin_page_get_action(GNC_PLUGIN_PAGE(report),
1247  "ReportBackAction" );
1248  gtk_action_set_sensitive(act, enabled);
1249 }
1250 
1251 // ------------------------------------------------------------
1252 // GTK ACTION CALLBACKS
1253 
1254 static void
1255 gnc_plugin_page_report_forw_cb( GtkAction *action, GncPluginPageReport *report )
1256 {
1258  gnc_html_history_node * node = NULL;
1259 
1260  DEBUG( "forw" );
1261  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
1262  gnc_html_history_forward(gnc_html_get_history(priv->html));
1263  node = gnc_html_history_get_current(gnc_html_get_history(priv->html));
1264  if (node)
1265  {
1266  gnc_html_show_url(priv->html, node->type, node->location,
1267  node->label, 0);
1268  }
1269 }
1270 
1271 static void
1272 gnc_plugin_page_report_back_cb( GtkAction *action, GncPluginPageReport *report )
1273 {
1275  gnc_html_history_node * node;
1276 
1277  DEBUG( "back" );
1278  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
1279  gnc_html_history_back(gnc_html_get_history(priv->html));
1280  node = gnc_html_history_get_current(gnc_html_get_history(priv->html));
1281  if (node)
1282  {
1283  gnc_html_show_url(priv->html, node->type, node->location,
1284  node->label, 0);
1285  }
1286 }
1287 
1288 static void
1289 gnc_plugin_page_report_reload_cb( GtkAction *action, GncPluginPageReport *report )
1290 {
1292  SCM dirty_report;
1293 
1294  DEBUG( "reload" );
1295  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
1296  if (priv->cur_report == SCM_BOOL_F)
1297  return;
1298 
1299  DEBUG( "reload-redraw" );
1300  dirty_report = scm_c_eval_string("gnc:report-set-dirty?!");
1301  scm_call_2(dirty_report, priv->cur_report, SCM_BOOL_T);
1302 
1303  priv->need_reload = TRUE;
1304  /* now queue the fact that we need to reload this report */
1305 
1306  // this doens't seem to do anything...
1307  gtk_widget_queue_draw( GTK_WIDGET(priv->container) );
1308 
1309  // this does...
1310  priv->reloading = TRUE;
1311  gnc_html_reload( priv->html );
1312  priv->reloading = FALSE;
1313 }
1314 
1315 static void
1316 gnc_plugin_page_report_stop_cb( GtkAction *action, GncPluginPageReport *report )
1317 {
1319 
1320  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
1321  gnc_html_cancel(priv->html);
1322 }
1323 
1324 /* Returns SCM_BOOL_F if cancel. Returns SCM_BOOL_T if html.
1325  * Otherwise returns pair from export_types. */
1326 static SCM
1327 gnc_get_export_type_choice (SCM export_types)
1328 {
1329  GList * choices = NULL;
1330  gboolean bad = FALSE;
1331  GList * node;
1332  int choice;
1333  SCM tail;
1334 
1335  if (!scm_is_list (export_types))
1336  return SCM_BOOL_F;
1337 
1338  for (tail = export_types; !scm_is_null (tail); tail = SCM_CDR (tail))
1339  {
1340  SCM pair = SCM_CAR (tail);
1341  char * name;
1342  SCM scm;
1343 
1344  if (!scm_is_pair (pair))
1345  {
1346  g_warning ("unexpected list element");
1347  bad = TRUE;
1348  break;
1349  }
1350 
1351  scm = SCM_CAR (pair);
1352  if (!scm_is_string (scm))
1353  {
1354  g_warning ("unexpected pair element");
1355  bad = TRUE;
1356  break;
1357  }
1358 
1359  name = gnc_scm_to_utf8_string (scm);
1360  choices = g_list_prepend (choices, name);
1361  }
1362 
1363  if (!bad)
1364  {
1365  choices = g_list_reverse (choices);
1366 
1367  choices = g_list_prepend (choices, g_strdup (_("HTML")));
1368 
1369  choice = gnc_choose_radio_option_dialog
1370  (NULL, _("Choose export format"),
1371  _("Choose the export format for this report:"),
1372  NULL, 0, choices);
1373  }
1374  else
1375  choice = -1;
1376 
1377  for (node = choices; node; node = node->next)
1378  g_free (node->data);
1379  g_list_free (choices);
1380 
1381  if (choice < 0)
1382  return SCM_BOOL_F;
1383 
1384  if (choice == 0)
1385  return SCM_BOOL_T;
1386 
1387  choice--;
1388  if (choice >= scm_ilength (export_types))
1389  return SCM_BOOL_F;
1390 
1391  return scm_list_ref (export_types, scm_from_int (choice));
1392 }
1393 
1394 static char *
1395 gnc_get_export_filename (SCM choice)
1396 {
1397  char * filepath;
1398  struct stat statbuf;
1399  char * title;
1400  const gchar * html_type = _("HTML");
1401  char * type;
1402  int rc;
1403  char * default_dir;
1404 
1405  if (choice == SCM_BOOL_T)
1406  type = g_strdup (html_type);
1407  else
1408  type = gnc_scm_to_utf8_string(SCM_CAR (choice));
1409 
1410  /* %s is the type of what is about to be saved, e.g. "HTML". */
1411  title = g_strdup_printf (_("Save %s To File"), type);
1412  default_dir = gnc_get_default_directory(GNC_PREFS_GROUP_REPORT);
1413 
1414  filepath = gnc_file_dialog (title, NULL, default_dir, GNC_FILE_DIALOG_EXPORT);
1415 
1416  /* Try to test for extension on file name, add if missing */
1417  if (g_strrstr(filepath, ".") == NULL)
1418  filepath = g_strconcat(filepath, ".", g_ascii_strdown(type, strlen(type)), NULL);
1419 
1420  g_free (type);
1421  g_free (title);
1422  g_free (default_dir);
1423 
1424  if (!filepath)
1425  return NULL;
1426 
1427  default_dir = g_path_get_dirname(filepath);
1428  gnc_set_default_directory (GNC_PREFS_GROUP_REPORT, default_dir);
1429  g_free(default_dir);
1430 
1431  rc = g_stat (filepath, &statbuf);
1432 
1433  /* Check for an error that isn't a non-existent file. */
1434  if (rc != 0 && errno != ENOENT)
1435  {
1436  /* %s is the strerror(3) string of the error that occurred. */
1437  const char *format = _("You cannot save to that filename.\n\n%s");
1438 
1439  gnc_error_dialog (NULL, format, strerror(errno));
1440  g_free(filepath);
1441  return NULL;
1442  }
1443 
1444  /* Check for a file that isn't a regular file. */
1445  if (rc == 0 && !S_ISREG (statbuf.st_mode))
1446  {
1447  const char *message = _("You cannot save to that file.");
1448 
1449  gnc_error_dialog (NULL, "%s", message);
1450  g_free(filepath);
1451  return NULL;
1452  }
1453 
1454  if (rc == 0)
1455  {
1456  const char *format = _("The file %s already exists. "
1457  "Are you sure you want to overwrite it?");
1458 
1459  if (!gnc_verify_dialog (NULL, FALSE, format, filepath))
1460  {
1461  g_free(filepath);
1462  return NULL;
1463  }
1464  }
1465 
1466  return filepath;
1467 }
1468 
1469 static void
1470 gnc_plugin_page_report_save_as_cb( GtkAction *action, GncPluginPageReport *report )
1471 {
1473  SCM save_func;
1474  SCM rpt_id;
1475 
1476  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
1477  if (priv->cur_report == SCM_BOOL_F)
1478  return;
1479 
1480  /* Create a new report template based on the current report's settings
1481  * and allow the user to rename the template.
1482  */
1483  save_func = scm_c_eval_string("gnc:report-to-template-new");
1484  rpt_id = scm_call_1(save_func, priv->cur_report);
1485 
1486  /* Open Preconfigured Reports dialog to allow user to change the name */
1487  if (!scm_is_null (rpt_id))
1488  {
1489  GncPluginPage *reportPage = GNC_PLUGIN_PAGE (report);
1490  GtkWidget *window = reportPage->window;
1491 
1492  if (window)
1493  g_return_if_fail(GNC_IS_MAIN_WINDOW(window));
1494 
1495  gnc_ui_custom_report_edit_name (GNC_MAIN_WINDOW (window), rpt_id);
1496  }
1497 
1498 }
1499 
1500 static void
1501 gnc_plugin_page_report_save_cb( GtkAction *action, GncPluginPageReport *report )
1502 {
1504  SCM check_func, save_func;
1505  SCM rpt_id;
1506 
1507  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
1508  if (priv->cur_report == SCM_BOOL_F)
1509  return;
1510 
1511  check_func = scm_c_eval_string("gnc:is-custom-report-type");
1512  if (scm_is_true (scm_call_1 (check_func, priv->cur_report)))
1513  {
1514  /* The current report is already based on a custom report.
1515  * Replace the existing one instead of adding a new one
1516  */
1517  save_func = scm_c_eval_string("gnc:report-to-template-update");
1518  rpt_id = scm_call_1(save_func, priv->cur_report);
1519  }
1520  else
1521  {
1522  /* The current report is not based on a custom report.
1523  * So let's create a new report template based on this report
1524  * and allow the user to change the name.
1525  */
1526  gnc_plugin_page_report_save_as_cb (action, report);
1527  }
1528 }
1529 
1530 static void
1531 gnc_plugin_page_report_export_cb( GtkAction *action, GncPluginPageReport *report )
1532 {
1534  char * filepath;
1535  SCM export_types;
1536  SCM export_thunk;
1537  gboolean result;
1538  SCM choice;
1539 
1540  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
1541  export_types = scm_call_1 (scm_c_eval_string ("gnc:report-export-types"),
1542  priv->cur_report);
1543 
1544  export_thunk = scm_call_1 (scm_c_eval_string ("gnc:report-export-thunk"),
1545  priv->cur_report);
1546 
1547  if (scm_is_list (export_types) && scm_is_procedure (export_thunk))
1548  choice = gnc_get_export_type_choice (export_types);
1549  else
1550  choice = SCM_BOOL_T;
1551 
1552  if (choice == SCM_BOOL_F)
1553  return;
1554 
1555  filepath = gnc_get_export_filename (choice);
1556  if (!filepath)
1557  return;
1558 
1559  if (scm_is_pair (choice))
1560  {
1561  SCM file_scm;
1562  SCM res;
1563 
1564  choice = SCM_CDR (choice);
1565  file_scm = scm_from_locale_string (filepath);
1566 
1567  res = scm_call_3 (export_thunk, priv->cur_report, choice, file_scm);
1568 
1569  result = (res != SCM_BOOL_F);
1570  }
1571  else
1572  result = gnc_html_export_to_file (priv->html, filepath);
1573 
1574  if (!result)
1575  {
1576  const char *fmt = _("Could not open the file %s. "
1577  "The error is: %s");
1578  gnc_error_dialog( NULL, fmt, filepath ? filepath : "(null)",
1579  strerror (errno) ? strerror (errno) : "" );
1580  }
1581 
1582  g_free(filepath);
1583  return;
1584 }
1585 
1586 static void
1587 error_handler(const char *str)
1588 {
1589  PWARN("Report Error: %s", str);
1590 }
1591 
1592 static void
1593 gnc_plugin_page_report_options_cb( GtkAction *action, GncPluginPageReport *report )
1594 {
1596 
1597  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
1598  if (priv->cur_report == SCM_BOOL_F)
1599  return;
1600 
1601  if (gnc_report_edit_options (priv->cur_report))
1602  gnc_plugin_page_report_add_edited_report(priv, priv->cur_report);
1603 }
1604 
1605 static GncInvoice *lookup_invoice(GncPluginPageReportPrivate *priv)
1606 {
1607  g_assert(priv);
1608  return gnc_option_db_lookup_invoice_option(priv->cur_odb, "General",
1609  "Invoice Number", NULL);
1610 }
1611 
1612 #define GNC_PREFS_GROUP_REPORT_PDFEXPORT GNC_PREFS_GROUP_GENERAL_REPORT ".pdf-export"
1613 #define GNC_PREF_FILENAME_DATE_FMT "filename-date-format"
1614 #define GNC_PREF_FILENAME_FMT "filename-format"
1615 
1616 static gchar *report_create_jobname(GncPluginPageReportPrivate *priv)
1617 {
1618  gchar *job_name = NULL;
1619  gchar *report_name = NULL;
1620  const gchar *report_number = "";
1621  gchar *job_date;
1622  const gchar *default_jobname = N_("GnuCash-Report");
1623 
1624  g_assert(priv);
1625 
1626  {
1627  // Look up the date format that was chosen in the preferences database
1628  QofDateFormat date_format_here;
1629  QofDateFormat date_format_old = qof_date_format_get();
1630  char *format_code = gnc_prefs_get_string(GNC_PREFS_GROUP_REPORT_PDFEXPORT,
1631  GNC_PREF_FILENAME_DATE_FMT);
1632  if (*format_code == '\0')
1633  {
1634  g_free(format_code);
1635  format_code = g_strdup("locale");
1636  }
1637 
1638  if (gnc_date_string_to_dateformat(format_code, &date_format_here))
1639  {
1640  PERR("Incorrect date format code");
1641  if (format_code != NULL)
1642  free(format_code);
1643  }
1644 
1645  // To apply this chosen date format, temporarily switch the
1646  // process-wide default to our chosen date format. Note: It is a
1647  // totally brain-dead implementation of qof_print_date() to not offer a
1648  // variation where the QofDateFormat can be passed as an argument.
1649  // Hrmpf.
1650  qof_date_format_set(date_format_here);
1651 
1652  job_date = qof_print_date( time( NULL ) );
1653 
1654  // Restore to the original general date format
1655  qof_date_format_set(date_format_old);
1656  }
1657 
1658 
1659  if (priv->cur_report == SCM_BOOL_F)
1660  report_name = g_strdup (_(default_jobname));
1661  else
1662  {
1663  /* Gather some information from the report to generate a
1664  * decent print job name.
1665  * FIXME: this is a bit of a hack. It would be better if each
1666  * report had a hidden job name option, because the
1667  * generic reporting code shouldn't know what makes
1668  * a decent job name for each report.
1669  *
1670  * Also, the "Report name" for an invoice report is
1671  * "Printable Invoice", which is not what the user wants to see,
1672  * so I added yet another hack below for this. cstim.
1673  */
1674  GncInvoice *invoice;
1675  report_name = gnc_option_db_lookup_string_option(priv->cur_odb, "General",
1676  "Report name", NULL);
1677  if (!report_name)
1678  report_name = g_strdup (_(default_jobname));
1679  if (g_strcmp0(report_name, _("Printable Invoice")) == 0
1680  || g_strcmp0(report_name, _("Tax Invoice")) == 0
1681  || g_strcmp0(report_name, _("Easy Invoice")) == 0
1682  || g_strcmp0(report_name, _("Fancy Invoice")) == 0)
1683  {
1684  /* Again HACK alert: We modify this single known string here into
1685  * something more appropriate. */
1686  g_free(report_name);
1687  report_name = g_strdup(_("Invoice"));
1688  }
1689 
1690  invoice = lookup_invoice(priv);
1691  if (invoice)
1692  {
1693  // Report is for an invoice. Hence, we get a number of the invoice.
1694  report_number = gncInvoiceGetID(invoice);
1695  }
1696  }
1697 
1698  if (report_name && job_date)
1699  {
1700  // Look up the sprintf format of the output name from the preferences database
1701  char* format = gnc_prefs_get_string(GNC_PREFS_GROUP_REPORT_PDFEXPORT, GNC_PREF_FILENAME_FMT);
1702 
1703  job_name = g_strdup_printf(format, report_name, report_number, job_date);
1704 
1705  g_free(format);
1706  }
1707  g_free (report_name);
1708  g_free (job_date);
1709 
1710  {
1711  char forbidden_char = '/';
1712  // Now remove the characters that are not allowed in file
1713  // names. FIXME: Check for all disallowed characters here!
1714  while (strchr(job_name, forbidden_char))
1715  {
1716  *strchr(job_name, forbidden_char) = '_';
1717  }
1718  }
1719 
1720  {
1721  /* And one final checking issue: We want to avoid allocating
1722  * the same name twice for a saved PDF. Hence, we keep a
1723  * GHashTable with the usage count of existing output
1724  * names. (Because I'm lazy, I just use a static GHashTable
1725  * for this.) */
1726  gpointer value;
1727  gboolean already_found;
1728  g_assert(static_report_printnames);
1729 
1730  // Lookup the existing usage count
1731  value = g_hash_table_lookup(static_report_printnames, job_name);
1732  already_found = (value != NULL);
1733  if (!value)
1734  {
1735  value = GINT_TO_POINTER(0);
1736  }
1737 
1738  // Increment the stored usage count
1739  value = GINT_TO_POINTER(1 + GPOINTER_TO_INT(value));
1740  // and store it again
1741  g_hash_table_insert(static_report_printnames, g_strdup(job_name), value);
1742 
1743  // If the previous usage count was more than 0, append the current
1744  // count (which is now 2 or higher) to the resulting name
1745  if (already_found)
1746  {
1747  // The name was already in use, so modify the name again
1748  gchar *tmp = g_strdup_printf("%s_%d", job_name, (int) GPOINTER_TO_INT(value));
1749  g_free(job_name);
1750  job_name = tmp;
1751  }
1752  }
1753 
1754  return job_name;
1755 }
1756 
1757 static void
1758 gnc_plugin_page_report_print_cb( GtkAction *action, GncPluginPageReport *report )
1759 {
1760  GncPluginPageReportPrivate *priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
1761  gchar *job_name = report_create_jobname(priv);
1762 
1763  //g_warning("Setting job name=%s", job_name);
1764 
1765  gnc_html_print(priv->html, job_name, FALSE);
1766 
1767  g_free (job_name);
1768 }
1769 
1770 static void
1771 gnc_plugin_page_report_exportpdf_cb( GtkAction *action, GncPluginPageReport *report )
1772 {
1773  GncPluginPageReportPrivate *priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
1774  gchar *job_name = report_create_jobname(priv);
1775  GncInvoice *invoice;
1776  GncOwner *owner = NULL;
1777 
1778  // Do we have an invoice report?
1779  invoice = lookup_invoice(priv);
1780  if (invoice)
1781  {
1782  // Does this invoice also have an owner?
1783  owner = (GncOwner*) gncInvoiceGetOwner(invoice);
1784  if (owner)
1785  {
1786  QofInstance *inst = qofOwnerGetOwner (owner);
1787  gchar *dirname = NULL;
1788  qof_instance_get (inst, "export-pdf-dir", &dirname, NULL);
1789  // Yes. In the kvp, look up the key for the Export-PDF output
1790  // directory. If it exists, prepend this to the job name so that
1791  // we can export to PDF.
1792  if (dirname && g_file_test(dirname,
1793  G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
1794  {
1795  gchar *tmp = g_build_filename(dirname, job_name, NULL);
1796  g_free(job_name);
1797  job_name = tmp;
1798  }
1799  }
1800  }
1801 
1802  //g_warning("Setting job name=%s", job_name);
1803 
1804  gnc_html_print(priv->html, job_name, TRUE);
1805 
1806  if (owner)
1807  {
1808  /* As this is an invoice report with some owner, we will try
1809  * to look up the chosen output directory from the print
1810  * settings and store it again in the owner kvp.
1811  */
1812  GtkPrintSettings *print_settings = gnc_print_get_settings();
1813  if (print_settings &&
1814  gtk_print_settings_has_key(print_settings,
1816  {
1817  const char* dirname = gtk_print_settings_get(print_settings,
1819  // Only store the directory if it exists.
1820  if (g_file_test(dirname, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
1821  {
1822  QofInstance *inst = qofOwnerGetOwner(owner);
1823  gncOwnerBeginEdit(owner);
1824  qof_instance_set (inst, "export-pdf-dir", dirname);
1825  gncOwnerCommitEdit(owner);
1826  }
1827  }
1828  }
1829 
1830  g_free (job_name);
1831 }
1832 
1833 static void
1834 gnc_plugin_page_report_copy_cb(GtkAction *action, GncPluginPageReport *report)
1835 {
1837 
1838  priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
1839  gnc_html_copy_to_clipboard(priv->html);
1840 }
1841 
1842 /********************************************************************
1843  * gnc_main_window_open_report()
1844  * open an report in a top level window from an ID number
1845  ********************************************************************/
1846 
1847 void
1848 gnc_main_window_open_report(int report_id, GncMainWindow *window)
1849 {
1850  GncPluginPage *reportPage;
1851 
1852  if (window)
1853  g_return_if_fail(GNC_IS_MAIN_WINDOW(window));
1854 
1855  reportPage = gnc_plugin_page_report_new( report_id );
1856  gnc_main_window_open_page( window, reportPage );
1857 }
1858 
1859 void
1860 gnc_main_window_open_report_url(const char * url, GncMainWindow *window)
1861 {
1862  GncPluginPage *reportPage;
1863 
1864  DEBUG( "report url: [%s]\n", url );
1865 
1866  if (window)
1867  g_return_if_fail(GNC_IS_MAIN_WINDOW(window));
1868 
1869  reportPage = gnc_plugin_page_report_new( 42 /* url? */ );
1870  gnc_main_window_open_page( window, reportPage );
1871 }
1872 
GncHtml * html
the gnc_html abstraction this PluginPage contains
GtkContainer * container
the container the above HTML widget is in.
void qof_instance_get(const QofInstance *inst, const gchar *first_param,...)
Wrapper for g_object_get.
gchar * gnc_prefs_get_string(const gchar *group, const gchar *pref_name)
Definition: gnc-prefs.c:237
GncPluginPage *(* recreate_page)(GtkWidget *window, GKeyFile *file, const gchar *group)
void(* page_name_changed)(GncPluginPage *plugin_page, const gchar *name)
void(* update_edit_menu_actions)(GncPluginPage *plugin_page, gboolean hide)
#define GNC_GTK_PRINT_SETTINGS_EXPORT_DIR
Definition: print-session.h:70
utility functions for the GnuCash UI
#define PINFO(format, args...)
Definition: qoflog.h:249
#define DEBUG(format, args...)
Definition: qoflog.h:255
const gchar * gnc_plugin_page_get_page_name(GncPluginPage *page)
Functions that are supported by all types of windows.
void qof_instance_set(QofInstance *inst, const gchar *first_param,...)
Wrapper for g_object_set Group setting multiple parameters in a single begin/commit/rollback.
GtkWidget * window
#define PERR(format, args...)
Definition: qoflog.h:237
#define ENTER(format, args...)
Definition: qoflog.h:261
void gncOwnerBeginEdit(GncOwner *owner)
Definition: gncOwner.c:73
void gnc_main_window_open_page(GncMainWindow *window, GncPluginPage *page)
QofInstance * qofOwnerGetOwner(const GncOwner *owner)
Definition: gncOwner.c:253
gboolean(* finish_pending)(GncPluginPage *plugin_page)
#define PWARN(format, args...)
Definition: qoflog.h:243
char * qof_print_date(time64 secs)
GType gnc_plugin_page_report_get_type(void)
Functions providing the file history menu.
void(* destroy_widget)(GncPluginPage *plugin_page)
void gnc_plugin_init_short_names(GtkActionGroup *action_group, action_toolbar_labels *toolbar_labels)
Definition: gnc-plugin.c:263
const gchar * plugin_name
Gnome specific utility functions.
All type declarations for the whole Gnucash engine.
GncPluginPage * gnc_plugin_page_report_new(int reportId)
GtkPrintSettings * gnc_print_get_settings()
Generic api to store and retrieve preferences.
GNCOptionDB * cur_odb
The Option DB for this report.
GtkAction * gnc_plugin_page_get_action(GncPluginPage *page, const gchar *name)
void gnc_plugin_update_actions(GtkActionGroup *action_group, const gchar **action_names, const gchar *property_name, gboolean value)
Definition: gnc-plugin.c:313
SCM cur_report
The report which this Page is satisfying.
GtkWidget *(* create_widget)(GncPluginPage *plugin_page)
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Definition: gnc-prefs.c:196
void gnc_main_window_close_page(GncPluginPage *page)
Functions for adding plugins to a GnuCash window.
QofDateFormat qof_date_format_get(void)
#define LEAVE(format, args...)
Definition: qoflog.h:271
void(* save_page)(GncPluginPage *page, GKeyFile *file, const gchar *group)
void qof_date_format_set(QofDateFormat df)
void gnc_plugin_page_add_book(GncPluginPage *page, QofBook *book)
GtkWidget * gnc_ui_get_toplevel(void)
GtkActionGroup * gnc_plugin_page_create_action_group(GncPluginPage *page, const gchar *group_name)
gboolean gnc_date_string_to_dateformat(const gchar *format_string, QofDateFormat *format)
Converts the date format to a printable string.
void main_window_update_page_name(GncPluginPage *page, const gchar *name_in)
QofDateFormat
Definition: gnc-date.h:121
#define SCHEME_OPTIONS
const gchar * QofLogModule
Definition: qofid.h:89