GnuCash  5.6-150-g038405b370+
gnc-html-webkit1.c
1 /********************************************************************
2  * gnc-html-webkit.c -- gnucash report renderer using webkit *
3  * *
4  * Copyright (C) 2000 Bill Gribble <grib@billgribble.com> *
5  * Copyright (C) 2001 Linas Vepstas <linas@linas.org> *
6  * Copyright (C) 2009 Phil Longstaff <plongstaff@rogers.com> *
7  * *
8  * This program is free software; you can redistribute it and/or *
9  * modify it under the terms of the GNU General Public License as *
10  * published by the Free Software Foundation; either version 2 of *
11  * the License, or (at your option) any later version. *
12  * *
13  * This program is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License*
19  * along with this program; if not, contact: *
20  * *
21  * Free Software Foundation Voice: +1-617-542-5942 *
22  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
23  * Boston, MA 02110-1301, USA gnu@gnu.org *
24  ********************************************************************/
25 
26 #include <config.h>
27 #include "platform.h"
28 
29 #include <gtk/gtk.h>
30 #include <glib/gi18n.h>
31 #include <glib/gstdio.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <unistd.h>
39 #include <regex.h>
40 
41 #include <webkit/webkit.h>
42 
43 #include "Account.h"
44 #include "gnc-prefs.h"
45 #include "gnc-gui-query.h"
46 #include "gnc-engine.h"
47 #include "gnc-html.h"
48 #include "gnc-html-webkit.h"
49 #include "gnc-html-history.h"
50 #include "print-session.h"
51 
52 G_DEFINE_TYPE(GncHtmlWebkit, gnc_html_webkit, GNC_TYPE_HTML )
53 
54 static void gnc_html_webkit_dispose( GObject* obj );
55 static void gnc_html_webkit_finalize( GObject* obj );
56 
57 #define GNC_HTML_WEBKIT_GET_PRIVATE(o) (GNC_HTML_WEBKIT(o)->priv)
58 
59 #include "gnc-html-webkit-p.h"
60 
61 /* indicates the debugging module that this .o belongs to. */
62 static QofLogModule log_module = GNC_MOD_HTML;
63 
64 /* hashes for URLType -> protocol and protocol -> URLType */
65 //extern GHashTable* gnc_html_type_to_proto_hash;
66 //extern GHashTable* gnc_html_proto_to_type_hash;
67 
68 /* hashes an HTML <object classid="ID"> classid to a handler function */
69 extern GHashTable* gnc_html_object_handlers;
70 
71 /* hashes handlers for loading different URLType data */
72 extern GHashTable* gnc_html_stream_handlers;
73 
74 /* hashes handlers for handling different URLType data */
75 extern GHashTable* gnc_html_url_handlers;
76 
77 static char error_404_format[] = "<html><body><h3>%s</h3><p>%s</body></html>";
78 static char error_404_title[] = N_("Not found");
79 static char error_404_body[] = N_("The specified URL could not be loaded.");
80 
81 #define BASE_URI_NAME "base-uri"
82 #define GNC_PREF_RPT_DFLT_ZOOM "default-zoom"
83 
84 static WebKitNavigationResponse webkit_navigation_requested_cb(
85  WebKitWebView* web_view,
86  WebKitWebFrame* frame,
87  WebKitNetworkRequest* request,
88  gpointer user_data );
89 static gboolean webkit_on_load_error (WebKitWebView *web_view,
90  WebKitWebFrame *web_frame, gchar *uri,
91  GError *error, gpointer data);
92 static void webkit_resource_load_error (WebKitWebView *web_view,
93  WebKitWebFrame *web_frame,
94  WebKitWebResource *resource,
95  GError *error, gpointer data);
96 static void webkit_on_url_cb( WebKitWebView* web_view, gchar* title, gchar* url,
97  gpointer data );
98 static gchar* handle_embedded_object( GncHtmlWebkit* self, gchar* html_str );
99 #if 0
100 static void gnc_html_set_base_cb( GtkHTML* gtkhtml, const gchar* base, gpointer data );
101 static void gnc_html_link_clicked_cb( GtkHTML* html, const gchar* url, gpointer data );
102 static gboolean gnc_html_object_requested_cb( GtkHTML* html, GtkHTMLEmbedded* eb,
103  gpointer data );
104 #endif
105 #if 0 /* Not Used */
106 static int gnc_html_button_press_cb( GtkWidget* widg, GdkEventButton* event,
107  gpointer user_data );
108 #endif /* Not Used */
109 static void impl_webkit_show_url( GncHtml* self, URLType type,
110  const gchar* location, const gchar* label,
111  gboolean new_window_hint );
112 static void impl_webkit_show_data( GncHtml* self, const gchar* data, int datalen );
113 static void impl_webkit_reload( GncHtml* self, gboolean force_rebuild );
114 static void impl_webkit_copy_to_clipboard( GncHtml* self );
115 static gboolean impl_webkit_export_to_file( GncHtml* self, const gchar* filepath );
116 static void impl_webkit_print( GncHtml* self, const gchar* jobname, gboolean export_pdf );
117 static void impl_webkit_cancel( GncHtml* self );
118 static void impl_webkit_set_parent( GncHtml* self, GtkWindow* parent );
119 static void impl_webkit_default_zoom_changed(gpointer prefs, gchar *pref, gpointer user_data);
120 
121 static void
122 gnc_html_webkit_init( GncHtmlWebkit* self )
123 {
124  GncHtmlWebkitPrivate* priv;
125  GncHtmlWebkitPrivate* new_priv;
126  GtkStyleContext *stylecontext;
127  WebKitWebSettings* webkit_settings = NULL;
128  const char* default_font_family = NULL;
129  PangoFontDescription *font_desc;
130  gdouble zoom = 1.0;
131 
132  new_priv = g_realloc( GNC_HTML(self)->priv, sizeof(GncHtmlWebkitPrivate) );
133  priv = self->priv = new_priv;
134  GNC_HTML(self)->priv = (GncHtmlPrivate*)priv;
135 
136  priv->html_string = NULL;
137  priv->web_view = WEBKIT_WEB_VIEW(webkit_web_view_new());
138 
139  /* Get the default font family from GtkStyleContext of a GtkWidget(priv->web_view). */
140  stylecontext = gtk_widget_get_style_context (GTK_WIDGET(priv->web_view));
141  gtk_style_context_get (stylecontext, gtk_widget_get_state_flags (GTK_WIDGET(priv->web_view)),
142  "font", &font_desc, NULL);
143 
144  default_font_family = pango_font_description_get_family (font_desc);
145  pango_font_description_free (font_desc);
146 
147  /* Set default webkit settings */
148  webkit_settings = webkit_web_view_get_settings (priv->web_view);
149  g_object_set (G_OBJECT(webkit_settings), "default-encoding", "utf-8", NULL);
150  if (default_font_family == NULL)
151  {
152  PWARN("webkit_settings: Cannot get default font family.");
153  }
154  else
155  {
156  g_object_set (G_OBJECT(webkit_settings),
157  "default-font-family", default_font_family,
158  NULL);
159  PINFO("webkit_settings: Set default font to [%s]", default_font_family);
160  }
161  /* Scale everything up */
162  zoom = gnc_prefs_get_float (GNC_PREFS_GROUP_GENERAL_REPORT, GNC_PREF_RPT_DFLT_ZOOM);
163  webkit_web_view_set_full_content_zoom (priv->web_view, TRUE);
164  webkit_web_view_set_zoom_level (priv->web_view, zoom);
165 
166 
167  gtk_container_add( GTK_CONTAINER(priv->base.container),
168  GTK_WIDGET(priv->web_view) );
169 
170  g_object_ref_sink( priv->base.container );
171 
172  /* signals */
173  g_signal_connect( priv->web_view, "navigation-requested",
174  G_CALLBACK(webkit_navigation_requested_cb),
175  self);
176 
177  g_signal_connect( priv->web_view, "hovering-over-link",
178  G_CALLBACK(webkit_on_url_cb),
179  self );
180 
181 #if 0
182  g_signal_connect( priv->html, "set_base",
183  G_CALLBACK(gnc_html_set_base_cb),
184  self);
185 
186  g_signal_connect(priv->html, "link_clicked",
187  G_CALLBACK(gnc_html_link_clicked_cb),
188  self);
189 
190  g_signal_connect (priv->html, "object_requested",
191  G_CALLBACK (gnc_html_object_requested_cb),
192  self);
193 
194  g_signal_connect (priv->html, "button_press_event",
195  G_CALLBACK (gnc_html_button_press_cb),
196  self);
197 
198  g_signal_connect (priv->html, "submit",
199  G_CALLBACK(gnc_html_submit_cb),
200  self);
201 #endif
202  g_signal_connect (priv->web_view, "load-error",
203  G_CALLBACK (webkit_on_load_error),
204  self);
205 
206  g_signal_connect (priv->web_view, "resource-load-failed",
207  G_CALLBACK (webkit_resource_load_error),
208  self);
209 
210  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL_REPORT,
211  GNC_PREF_RPT_DFLT_ZOOM,
212  impl_webkit_default_zoom_changed,
213  self);
214 
215  LEAVE("retval %p", self);
216 }
217 
218 static void
219 gnc_html_webkit_class_init( GncHtmlWebkitClass* klass )
220 {
221  GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
222  GncHtmlClass* html_class = GNC_HTML_CLASS(klass);
223 
224  gobject_class->dispose = gnc_html_webkit_dispose;
225  gobject_class->finalize = gnc_html_webkit_finalize;
226 
227  html_class->show_url = impl_webkit_show_url;
228  html_class->show_data = impl_webkit_show_data;
229  html_class->reload = impl_webkit_reload;
230  html_class->copy_to_clipboard = impl_webkit_copy_to_clipboard;
231  html_class->export_to_file = impl_webkit_export_to_file;
232  html_class->print = impl_webkit_print;
233  html_class->cancel = impl_webkit_cancel;
234  html_class->set_parent = impl_webkit_set_parent;
235 }
236 
237 static void
238 gnc_html_webkit_dispose( GObject* obj )
239 {
240  GncHtmlWebkit* self = GNC_HTML_WEBKIT(obj);
241  GncHtmlWebkitPrivate* priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);
242 
243  if ( priv->web_view != NULL )
244  {
245  gtk_container_remove( GTK_CONTAINER(priv->base.container),
246  GTK_WIDGET(priv->web_view) );
247  priv->web_view = NULL;
248  }
249 
250  if ( priv->html_string != NULL )
251  {
252  g_free( priv->html_string );
253  priv->html_string = NULL;
254  }
255 
256  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL_REPORT,
257  GNC_PREF_RPT_DFLT_ZOOM,
258  impl_webkit_default_zoom_changed,
259  obj);
260 
261  G_OBJECT_CLASS(gnc_html_webkit_parent_class)->dispose( obj );
262 }
263 
264 static void
265 gnc_html_webkit_finalize( GObject* obj )
266 {
267  GncHtmlWebkit* self = GNC_HTML_WEBKIT(obj);
268 
269 // if( self->priv != NULL ) {
270 // g_free( self->priv );
271  self->priv = NULL;
272 // }
273 
274  G_OBJECT_CLASS(gnc_html_webkit_parent_class)->finalize( obj );
275 }
276 
277 /*****************************************************************************/
278 
279 static char*
280 extract_base_name(URLType type, const gchar* path)
281 {
282  gchar machine_rexp[] = "^(//[^/]*)/*(/.*)?$";
283  gchar path_rexp[] = "^/*(.*)/+([^/]*)$";
284  regex_t compiled_m, compiled_p;
285  regmatch_t match[4];
286  gchar * machine = NULL, * location = NULL, * base = NULL;
287  gchar * basename = NULL;
288 
289  DEBUG(" ");
290  if (!path) return NULL;
291 
292  regcomp(&compiled_m, machine_rexp, REG_EXTENDED);
293  regcomp(&compiled_p, path_rexp, REG_EXTENDED);
294 
295  if (!g_strcmp0 (type, URL_TYPE_HTTP) ||
296  !g_strcmp0 (type, URL_TYPE_SECURE) ||
297  !g_strcmp0 (type, URL_TYPE_FTP))
298  {
299 
300  /* step 1: split the machine name away from the path
301  * components */
302  if (!regexec(&compiled_m, path, 4, match, 0))
303  {
304  /* $1 is the machine name */
305  if (match[1].rm_so != -1)
306  {
307  machine = g_strndup(path + match[1].rm_so,
308  match[1].rm_eo - match[1].rm_so);
309  }
310  /* $2 is the path */
311  if (match[2].rm_so != -1)
312  {
313  location = g_strndup(path + match[2].rm_so,
314  match[2].rm_eo - match[2].rm_so);
315  }
316  }
317 
318  }
319  else
320  {
321  location = g_strdup(path);
322  }
323  /* step 2: split up the path into prefix and file components */
324  if (location)
325  {
326  if (!regexec(&compiled_p, location, 4, match, 0))
327  {
328  if (match[1].rm_so != -1)
329  {
330  base = g_strndup(location + match[1].rm_so,
331  match[1].rm_eo - match[1].rm_so);
332  }
333  else
334  {
335  base = NULL;
336  }
337  }
338  }
339 
340  regfree(&compiled_m);
341  regfree(&compiled_p);
342 
343  if (machine)
344  {
345  if (base && (strlen(base) > 0))
346  {
347  basename = g_strconcat(machine, "/", base, "/", NULL);
348  }
349  else
350  {
351  basename = g_strconcat(machine, "/", NULL);
352  }
353  }
354  else
355  {
356  if (base && (strlen(base) > 0))
357  {
358  basename = g_strdup(base);
359  }
360  else
361  {
362  basename = NULL;
363  }
364  }
365 
366  g_free(machine);
367  g_free(base);
368  g_free(location);
369  return basename;
370 }
371 
372 static gboolean
373 http_allowed()
374 {
375  return TRUE;
376 }
377 
378 static gboolean
379 https_allowed()
380 {
381  return TRUE;
382 }
383 
384 static gchar*
385 handle_embedded_object( GncHtmlWebkit* self, gchar* html_str )
386 {
387  // Find the <object> tag and get the classid from it. This will provide the correct
388  // object callback handler. Pass the <object> entity text to the handler. What should
389  // come back is embedded image information.
390  gchar* remainder_str = html_str;
391  gchar* object_tag;
392  gchar* end_object_tag;
393  gchar* object_contents;
394  gchar* html_str_start = NULL;
395  gchar* html_str_middle;
396  gchar* html_str_result = NULL;
397  gchar* classid_start;
398  gchar* classid_end;
399  gchar* classid_str;
400  gchar* new_chunk;
401  GncHTMLObjectCB h;
402 
403  object_tag = g_strstr_len( remainder_str, -1, "<object classid=" );
404  while (object_tag)
405  {
406 
407  classid_start = object_tag + strlen( "<object classid=" ) + 1;
408  classid_end = g_strstr_len( classid_start, -1, "\"" );
409  classid_str = g_strndup( classid_start, (classid_end - classid_start) );
410 
411  end_object_tag = g_strstr_len( object_tag, -1, "</object>" );
412  if ( end_object_tag == NULL )
413  {
414  /* Hmmm... no object end tag
415  Return the original html string because we can't properly parse it */
416  g_free (classid_str);
417  g_free (html_str_result);
418  return g_strdup (html_str);
419  }
420  end_object_tag += strlen( "</object>" );
421  object_contents = g_strndup( object_tag, (end_object_tag - object_tag) );
422 
423  h = g_hash_table_lookup( gnc_html_object_handlers, classid_str );
424  if ( h != NULL )
425  {
426  (void)h( GNC_HTML(self), object_contents, &html_str_middle );
427  }
428  else
429  {
430  html_str_middle = g_strdup_printf( "No handler found for classid \"%s\"", classid_str );
431  }
432 
433  html_str_start = html_str_result;
434  new_chunk = g_strndup (remainder_str, (object_tag - remainder_str));
435  if (!html_str_start)
436  html_str_result = g_strconcat (new_chunk, html_str_middle, NULL);
437  else
438  html_str_result = g_strconcat (html_str_start, new_chunk, html_str_middle, NULL);
439 
440  g_free( html_str_start );
441  g_free( new_chunk );
442  g_free( html_str_middle );
443 
444  remainder_str = end_object_tag;
445  object_tag = g_strstr_len( remainder_str, -1, "<object classid=" );
446  }
447 
448  if (html_str_result)
449  {
450  html_str_start = html_str_result;
451  html_str_result = g_strconcat (html_str_start, remainder_str, NULL);
452  g_free (html_str_start);
453  }
454  else
455  html_str_result = g_strdup (remainder_str);
456 
457  return html_str_result;
458 }
459 
460 /********************************************************************
461  * load_to_stream : actually do the work of loading the HTML
462  * or binary data referenced by a URL and feeding it into the webkit
463  * widget.
464  ********************************************************************/
465 
466 static gboolean
467 load_to_stream( GncHtmlWebkit* self, URLType type,
468  const gchar* location, const gchar* label )
469 {
470  gchar* fdata = NULL;
471  int fdata_len = 0;
472  GncHtmlWebkitPrivate* priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);
473 
474  DEBUG( "type %s, location %s, label %s", type ? type : "(null)",
475  location ? location : "(null)", label ? label : "(null)");
476 
477  g_return_val_if_fail( self != NULL, FALSE );
478 
479  if ( gnc_html_stream_handlers != NULL )
480  {
481  GncHTMLStreamCB stream_handler;
482 
483  stream_handler = g_hash_table_lookup( gnc_html_stream_handlers, type );
484  if ( stream_handler )
485  {
486  GncHtml *weak_html = GNC_HTML(self);
487  gboolean ok;
488 
489  g_object_add_weak_pointer(G_OBJECT(self), (gpointer *)(&weak_html));
490 
491  ok = stream_handler(location, &fdata, &fdata_len);
492 
493  if (!weak_html) // will be NULL if self has been destroyed
494  {
495  g_free (fdata);
496  return FALSE;
497  }
498  else
499  {
500  g_object_remove_weak_pointer(G_OBJECT(self),
501  (gpointer*)(&weak_html));
502  }
503 
504  if (ok) {
505  fdata = fdata ? fdata : g_strdup("");
506 
507  // Until webkitgtk supports download requests, look for "<object
508  // classid=" indicating the beginning of an embedded graph. If
509  // found, handle it
510  if (g_strstr_len(fdata, -1, "<object classid=") != NULL) {
511  gchar *new_fdata;
512  new_fdata = handle_embedded_object(self, fdata);
513  g_free(fdata);
514  fdata = new_fdata;
515  }
516 
517  // Save a copy for export purposes
518  if (priv->html_string != NULL) {
519  g_free(priv->html_string);
520  }
521  priv->html_string = g_strdup(fdata);
522  impl_webkit_show_data(GNC_HTML(self), fdata, strlen(fdata));
523  // webkit_web_view_load_html_string( priv->web_view,
524  // fdata, BASE_URI_NAME );
525  }
526  else
527  {
528  fdata = fdata ? fdata :
529  g_strdup_printf( error_404_format,
530  _(error_404_title), _(error_404_body) );
531  webkit_web_view_load_html_string( priv->web_view, fdata, BASE_URI_NAME );
532  }
533 
534  g_free( fdata );
535 
536  if ( label )
537  {
538  while ( gtk_events_pending() )
539  {
540  gtk_main_iteration();
541  }
542  /* No action required: Webkit jumps to the anchor on its own. */
543  }
544 
545  return TRUE;
546  }
547  }
548 
549  do
550  {
551  if ( !g_strcmp0( type, URL_TYPE_SECURE ) ||
552  !g_strcmp0( type, URL_TYPE_HTTP ) )
553  {
554 
555  if ( !g_strcmp0( type, URL_TYPE_SECURE ) )
556  {
557  if ( !https_allowed() )
558  {
559  gnc_error_dialog (GTK_WINDOW (priv->base.parent), "%s",
560  _("Secure HTTP access is disabled. "
561  "You can enable it in the Network section of "
562  "the Preferences dialog."));
563  break;
564  }
565  }
566 
567  if ( !http_allowed() )
568  {
569  gnc_error_dialog (GTK_WINDOW (priv->base.parent), "%s",
570  _("Network HTTP access is disabled. "
571  "You can enable it in the Network section of "
572  "the Preferences dialog."));
573  }
574  else
575  {
576  gnc_build_url( type, location, label );
577  }
578 
579  }
580  else
581  {
582  PWARN( "load_to_stream for inappropriate type\n"
583  "\turl = '%s#%s'\n",
584  location ? location : "(null)",
585  label ? label : "(null)" );
586  fdata = g_strdup_printf( error_404_format,
587  _(error_404_title), _(error_404_body) );
588  webkit_web_view_load_html_string( priv->web_view, fdata, BASE_URI_NAME );
589  g_free( fdata );
590  }
591 
592  }
593  while ( FALSE );
594  return TRUE;
595 }
596 
597 #if 0
598 /********************************************************************
599  * gnc_html_link_clicked_cb - called when user left-clicks on html
600  * anchor.
601  ********************************************************************/
602 
603 static void
604 gnc_html_link_clicked_cb( GtkHTML* html, const gchar* url, gpointer data )
605 {
606  URLType type;
607  gchar* location = NULL;
608  gchar* label = NULL;
609  GncHtmlWebkit* self = GNC_HTML_WEBKIT(data);
610 
611  DEBUG("Clicked %s", url);
612  type = gnc_html_parse_url( GNC_HTML(self), url, &location, &label );
613  gnc_html_show_url( GNC_HTML(self), type, location, label, 0 );
614  g_free( location );
615  g_free( label );
616 }
617 #endif
618 
619 /********************************************************************
620  * webkit_navigation_requested_cb - called when a URL needs to be
621  * loaded within the loading of a page (embedded image).
622  ********************************************************************/
623 
624 static WebKitNavigationResponse
625 webkit_navigation_requested_cb( WebKitWebView* web_view, WebKitWebFrame* frame,
626  WebKitNetworkRequest* request,
627  gpointer data )
628 {
629  URLType type;
630  gchar* location = NULL;
631  gchar* label = NULL;
632  GncHtmlWebkit* self = GNC_HTML_WEBKIT(data);
633  const gchar* url = webkit_network_request_get_uri( request );
634 
635  ENTER( "requesting %s", url );
636  if ( strcmp( url, BASE_URI_NAME ) == 0 )
637  {
638  LEAVE("URI is %s", BASE_URI_NAME);
639  return WEBKIT_NAVIGATION_RESPONSE_ACCEPT;
640  }
641 
642  type = gnc_html_parse_url( GNC_HTML(self), url, &location, &label );
643  if ( strcmp( type, "file" ) == 0 )
644  {
645  LEAVE("URI type is 'file'");
646  return WEBKIT_NAVIGATION_RESPONSE_ACCEPT;
647  }
648  gnc_html_show_url( GNC_HTML(self), type, location, label, 0 );
649 // load_to_stream( self, type, location, label );
650  g_free( location );
651  g_free( label );
652 
653  LEAVE("");
654  return WEBKIT_NAVIGATION_RESPONSE_IGNORE;
655 }
656 
657 static gboolean
658 webkit_on_load_error (WebKitWebView *web_view, WebKitWebFrame *web_frame,
659  gchar *uri, GError *error, gpointer data)
660 {
661  PERR ("WebKit load of %s failed due to %s\n", uri, error->message);
662  return FALSE;
663 }
664 
665 static void
666 webkit_resource_load_error (WebKitWebView *web_view, WebKitWebFrame *web_frame,
667  WebKitWebResource *resource, GError *error,
668  gpointer data)
669 {
670  const gchar *uri = webkit_web_resource_get_uri (resource);
671  const gchar *type = webkit_web_resource_get_mime_type (resource);
672  PERR ("WebKit load of resource %s, type %s, failed due to %s\n",
673  uri, type, error->message);
674 }
675 
676 #if 0
677 /********************************************************************
678  * gnc_html_object_requested_cb - called when an applet needs to be
679  * loaded.
680  ********************************************************************/
681 
682 static gboolean
683 gnc_html_object_requested_cb( GtkHTML* html, GtkHTMLEmbedded* eb,
684  gpointer data )
685 {
686  GncHtmlWebkit* self = GNC_HTML_WEBKIT(data);
687  GncHTMLObjectCB h;
688 
689  DEBUG( " " );
690  if ( !eb || !(eb->classid) || !gnc_html_object_handlers ) return FALSE;
691 
692  h = g_hash_table_lookup( gnc_html_object_handlers, eb->classid );
693  if ( h )
694  {
695  return h( GNC_HTML(self), eb, data );
696  }
697  else
698  {
699  return FALSE;
700  }
701 }
702 #endif
703 
704 /********************************************************************
705  * webkit_on_url_cb - called when user rolls over html anchor
706  ********************************************************************/
707 
708 static void
709 webkit_on_url_cb( WebKitWebView* web_view, gchar* title, gchar* url, gpointer data )
710 {
711  GncHtmlWebkit* self = GNC_HTML_WEBKIT(data);
712  GncHtmlWebkitPrivate* priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);
713 
714  DEBUG( "Rollover %s", url ? url : "(null)" );
715  g_free( priv->base.current_link );
716  priv->base.current_link = g_strdup( url );
717  if ( priv->base.flyover_cb )
718  {
719  (priv->base.flyover_cb)( GNC_HTML(self), url, priv->base.flyover_cb_data );
720  }
721 }
722 
723 #if 0
724 /********************************************************************
725  * gnc_html_set_base_cb
726  ********************************************************************/
727 
728 static void
729 gnc_html_set_base_cb( GtkHTML* gtkhtml, const gchar* base,
730  gpointer data )
731 {
732  GncHtmlWebkit* self = GNC_HTML_WEBKIT(data);
733  GncHtmlWebkitPrivate* priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);
734  URLType type;
735  gchar* location = NULL;
736  gchar* label = NULL;
737 
738  DEBUG( "Setting base location to %s", base );
739  type = gnc_html_parse_url( GNC_HTML(self), base, &location, &label );
740 
741  g_free( priv->base.base_location );
742  g_free( label );
743 
744  priv->base.base_type = type;
745  priv->base.base_location = location;
746 }
747 #endif
748 
749 /********************************************************************
750  * gnc_html_button_press_cb
751  * mouse button callback (if any)
752  ********************************************************************/
753 
754 #if 0 /* Not Used */
755 static int
756 gnc_html_button_press_cb( GtkWidget* widg, GdkEventButton* event,
757  gpointer user_data )
758 {
759  GncHtmlWebkit* self = GNC_HTML_WEBKIT(user_data);
760  GncHtmlWebkitPrivate* priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);
761 
762  DEBUG( "Button Press" );
763  if ( priv->base.button_cb != NULL )
764  {
765  (priv->base.button_cb)( GNC_HTML(self), event, priv->base.button_cb_data );
766  return TRUE;
767  }
768  else
769  {
770  return FALSE;
771  }
772 }
773 #endif /* Not Used */
774 
775 /********************************************************************
776  * gnc_html_open_scm
777  * insert some scheme-generated HTML
778  ********************************************************************/
779 
780 static void
781 gnc_html_open_scm( GncHtmlWebkit* self, const gchar * location,
782  const gchar * label, int newwin )
783 {
784  PINFO("location='%s'", location ? location : "(null)");
785 }
786 
787 
788 /********************************************************************
789  * gnc_html_show_data
790  * display some HTML that the creator of the gnc-html got from
791  * somewhere.
792  ********************************************************************/
793 
794 static void
795 impl_webkit_show_data( GncHtml* self, const gchar* data, int datalen )
796 {
797  GncHtmlWebkitPrivate* priv;
798 #define TEMPLATE_REPORT_FILE_NAME "gnc-report-XXXXXX.html"
799  int fd;
800  gchar* uri;
801  gchar *filename;
802 
803  g_return_if_fail( self != NULL );
804  g_return_if_fail( GNC_IS_HTML_WEBKIT(self) );
805 
806  ENTER( "datalen %d, data %20.20s", datalen, data );
807 
808  priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);
809 
810  /* Export the HTML to a file and load the file URI. On Linux, this seems to get around some
811  security problems (otherwise, it can complain that embedded images aren't permitted to be
812  viewed because they are local resources). On Windows, this allows the embedded images to
813  be viewed (maybe for the same reason as on Linux, but I haven't found where it puts those
814  messages. */
815  filename = g_build_filename(g_get_tmp_dir(), TEMPLATE_REPORT_FILE_NAME, (gchar *)NULL);
816  fd = g_mkstemp( filename );
817  impl_webkit_export_to_file( self, filename );
818  close( fd );
819 #ifdef G_OS_WIN32
820  uri = g_strdup_printf( "file:///%s", filename );
821 #else
822  uri = g_strdup_printf( "file://%s", filename );
823 #endif
824  g_free(filename);
825  DEBUG("Loading uri '%s'", uri);
826  webkit_web_view_load_uri( priv->web_view, uri );
827  g_free( uri );
828 
829  LEAVE("");
830 }
831 
832 /********************************************************************
833  * gnc_html_show_url
834  *
835  * open a URL. This is called when the user clicks a link or
836  * for the creator of the gnc_html window to explicitly request
837  * a URL.
838  ********************************************************************/
839 
840 static void
841 impl_webkit_show_url( GncHtml* self, URLType type,
842  const gchar* location, const gchar* label,
843  gboolean new_window_hint )
844 {
845  GncHTMLUrlCB url_handler;
846  gboolean new_window;
847  GncHtmlWebkitPrivate* priv;
848  gboolean stream_loaded = FALSE;
849 
850  g_return_if_fail( self != NULL );
851  g_return_if_fail( GNC_IS_HTML_WEBKIT(self) );
852  g_return_if_fail( location != NULL );
853 
854  priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);
855 
856  /* make sure it's OK to show this URL type in this window */
857  if ( new_window_hint == 0 )
858  {
859  if ( priv->base.urltype_cb )
860  {
861  new_window = !((priv->base.urltype_cb)( type ));
862  }
863  else
864  {
865  new_window = FALSE;
866  }
867  }
868  else
869  {
870  new_window = TRUE;
871  }
872 
873  if ( !new_window )
874  {
875  gnc_html_cancel( GNC_HTML(self) );
876  }
877 
878  if ( gnc_html_url_handlers )
879  {
880  url_handler = g_hash_table_lookup( gnc_html_url_handlers, type );
881  }
882  else
883  {
884  url_handler = NULL;
885  }
886 
887  if ( url_handler )
888  {
889  GNCURLResult result;
890  gboolean ok;
891 
892  result.load_to_stream = FALSE;
893  result.url_type = type;
894  result.location = NULL;
895  result.label = NULL;
896  result.base_type = URL_TYPE_FILE;
897  result.base_location = NULL;
898  result.error_message = NULL;
899  result.parent = GTK_WINDOW (priv->base.parent);
900 
901  ok = url_handler( location, label, new_window, &result );
902  if ( !ok )
903  {
904  if ( result.error_message )
905  {
906  gnc_error_dialog (GTK_WINDOW (priv->base.parent), "%s", result.error_message );
907  }
908  else
909  {
910  /* %s is a URL (some location somewhere). */
911  gnc_error_dialog (GTK_WINDOW (priv->base.parent), _("There was an error accessing %s."), location );
912  }
913 
914  if ( priv->base.load_cb )
915  {
916  priv->base.load_cb( GNC_HTML(self), result.url_type,
917  location, label, priv->base.load_cb_data );
918  }
919  }
920  else if ( result.load_to_stream )
921  {
922  gnc_html_history_node *hnode;
923  const char *new_location;
924  const char *new_label;
925 
926  new_location = result.location ? result.location : location;
927  new_label = result.label ? result.label : label;
928  hnode = gnc_html_history_node_new( result.url_type, new_location, new_label );
929 
930  gnc_html_history_append( priv->base.history, hnode );
931 
932  g_free( priv->base.base_location );
933  priv->base.base_type = result.base_type;
934  priv->base.base_location =
935  g_strdup( extract_base_name( result.base_type, new_location ) );
936  DEBUG( "resetting base location to %s",
937  priv->base.base_location ? priv->base.base_location : "(null)" );
938 
939  stream_loaded = load_to_stream( GNC_HTML_WEBKIT(self),
940  result.url_type,
941  new_location, new_label );
942 
943  if ( stream_loaded && priv->base.load_cb != NULL )
944  {
945  priv->base.load_cb( GNC_HTML(self), result.url_type,
946  new_location, new_label, priv->base.load_cb_data );
947  }
948  }
949 
950  g_free( result.location );
951  g_free( result.label );
952  g_free( result.base_location );
953  g_free( result.error_message );
954 
955  return;
956  }
957 
958  if ( g_strcmp0( type, URL_TYPE_SCHEME ) == 0 )
959  {
960  gnc_html_open_scm( GNC_HTML_WEBKIT(self), location, label, new_window );
961 
962  }
963  else if ( g_strcmp0( type, URL_TYPE_JUMP ) == 0 )
964  {
965  /* Webkit jumps to the anchor on its own */
966  }
967  else if ( g_strcmp0( type, URL_TYPE_SECURE ) == 0 ||
968  g_strcmp0( type, URL_TYPE_HTTP ) == 0 ||
969  g_strcmp0( type, URL_TYPE_FILE ) == 0 )
970  {
971 
972  do
973  {
974  if ( g_strcmp0( type, URL_TYPE_SECURE ) == 0 )
975  {
976  if ( !https_allowed() )
977  {
978  gnc_error_dialog (GTK_WINDOW (priv->base.parent), "%s",
979  _("Secure HTTP access is disabled. "
980  "You can enable it in the Network section of "
981  "the Preferences dialog.") );
982  break;
983  }
984  }
985 
986  if ( g_strcmp0( type, URL_TYPE_HTTP ) == 0 )
987  {
988  if ( !http_allowed() )
989  {
990  gnc_error_dialog (GTK_WINDOW (priv->base.parent), "%s",
991  _("Network HTTP access is disabled. "
992  "You can enable it in the Network section of "
993  "the Preferences dialog.") );
994  break;
995  }
996  }
997 
998  priv->base.base_type = type;
999 
1000  if ( priv->base.base_location != NULL ) g_free( priv->base.base_location );
1001  priv->base.base_location = extract_base_name( type, location );
1002 
1003  /* FIXME : handle new_window = 1 */
1004  gnc_html_history_append( priv->base.history,
1005  gnc_html_history_node_new( type, location, label ) );
1006  stream_loaded = load_to_stream( GNC_HTML_WEBKIT(self), type,
1007  location, label );
1008 
1009  }
1010  while ( FALSE );
1011 
1012  }
1013  else
1014  {
1015  PERR( "URLType %s not supported.", type );
1016  }
1017 
1018  if ( stream_loaded && priv->base.load_cb != NULL )
1019  {
1020  (priv->base.load_cb)( GNC_HTML(self), type, location, label, priv->base.load_cb_data );
1021  }
1022 }
1023 
1024 
1025 /********************************************************************
1026  * gnc_html_reload
1027  * reload the current page
1028  * if force_rebuild is TRUE, the report is recreated, if FALSE, report
1029  * is reloaded by webkit
1030  ********************************************************************/
1031 
1032 static void
1033 impl_webkit_reload( GncHtml* self, gboolean force_rebuild )
1034 {
1035  GncHtmlWebkitPrivate* priv;
1036 
1037  g_return_if_fail( self != NULL );
1038  g_return_if_fail( GNC_IS_HTML_WEBKIT(self) );
1039 
1040  priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);
1041 
1042  if ( force_rebuild )
1043  {
1044  gnc_html_history_node *n = gnc_html_history_get_current( priv->base.history );
1045  if ( n != NULL )
1046  gnc_html_show_url( self, n->type, n->location, n->label, 0 );
1047  }
1048  else
1049  webkit_web_view_reload( priv->web_view );
1050 }
1051 
1052 
1053 /********************************************************************
1054  * gnc_html_new
1055  * create and set up a new webkit widget.
1056  ********************************************************************/
1057 
1058 GncHtml*
1059 gnc_html_webkit_new( void )
1060 {
1061  GncHtmlWebkit* self = g_object_new( GNC_TYPE_HTML_WEBKIT, NULL );
1062  return GNC_HTML(self);
1063 }
1064 
1065 /********************************************************************
1066  * gnc_html_cancel
1067  * cancel any outstanding HTML fetch requests.
1068  ********************************************************************/
1069 
1070 static gboolean
1071 webkit_cancel_helper(gpointer key, gpointer value, gpointer user_data)
1072 {
1073  g_free(key);
1074  g_list_free((GList *)value);
1075  return TRUE;
1076 }
1077 
1078 static void
1079 impl_webkit_cancel( GncHtml* self )
1080 {
1081  GncHtmlWebkitPrivate* priv;
1082 
1083  g_return_if_fail( self != NULL );
1084  g_return_if_fail( GNC_IS_HTML_WEBKIT(self) );
1085 
1086  priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);
1087 
1088  /* remove our own references to requests */
1089  //gnc_http_cancel_requests( priv->http );
1090 
1091  g_hash_table_foreach_remove( priv->base.request_info, webkit_cancel_helper, NULL );
1092 }
1093 
1094 static void
1095 impl_webkit_copy_to_clipboard( GncHtml* self )
1096 {
1097  GncHtmlWebkitPrivate* priv;
1098 
1099  g_return_if_fail( self != NULL );
1100  g_return_if_fail( GNC_IS_HTML_WEBKIT(self) );
1101 
1102  priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);
1103  if ( webkit_web_view_can_copy_clipboard( priv->web_view ) )
1104  {
1105  webkit_web_view_copy_clipboard( priv->web_view );
1106  }
1107 }
1108 
1109 /**************************************************************
1110  * gnc_html_export_to_file
1111  *
1112  * @param self GncHtmlWebkit object
1113  * @param filepath Where to write the HTML
1114  * @return TRUE if successful, FALSE if unsuccessful
1115  **************************************************************/
1116 static gboolean
1117 impl_webkit_export_to_file( GncHtml* self, const char *filepath )
1118 {
1119  FILE *fh;
1120  GncHtmlWebkitPrivate* priv;
1121 
1122  g_return_val_if_fail( self != NULL, FALSE );
1123  g_return_val_if_fail( GNC_IS_HTML_WEBKIT(self), FALSE );
1124  g_return_val_if_fail( filepath != NULL, FALSE );
1125 
1126  priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);
1127  if ( priv->html_string == NULL )
1128  {
1129  return FALSE;
1130  }
1131  fh = g_fopen( filepath, "w" );
1132  if ( fh != NULL )
1133  {
1134  gint written;
1135  gint len = strlen( priv->html_string );
1136 
1137  written = fwrite( priv->html_string, 1, len, fh );
1138  fclose (fh);
1139 
1140  if ( written != len )
1141  {
1142  return FALSE;
1143  }
1144 
1145  return TRUE;
1146  }
1147  else
1148  {
1149  return FALSE;
1150  }
1151 }
1152 
1165 static void
1166 impl_webkit_print( GncHtml* self, const gchar* jobname, gboolean export_pdf )
1167 {
1168  gchar *export_filename = NULL;
1169  GncHtmlWebkitPrivate* priv;
1170  WebKitWebFrame* frame;
1171  GtkPrintOperation* op = gtk_print_operation_new();
1172  GError* error = NULL;
1173  GtkPrintSettings *print_settings;
1174 
1175  priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);
1176  frame = webkit_web_view_get_main_frame( priv->web_view );
1177 
1178  gnc_print_operation_init( op, jobname );
1179  print_settings = gtk_print_operation_get_print_settings (op);
1180  if (!print_settings)
1181  {
1182  print_settings = gtk_print_settings_new();
1183  gtk_print_operation_set_print_settings(op, print_settings);
1184  }
1185 #ifdef G_OS_WIN32
1186  gtk_print_operation_set_unit( op, GTK_UNIT_POINTS );
1187 #endif
1188 
1189  // Make sure to generate a full export filename
1190  if (g_str_has_suffix(jobname, ".pdf"))
1191  {
1192  export_filename = g_strdup(jobname);
1193  }
1194  else
1195  {
1196  export_filename = g_strconcat(jobname, ".pdf", NULL);
1197  }
1198 
1199  // Two different modes of operation. Either export to PDF, or run the
1200  // normal print dialog
1201  if (export_pdf)
1202  {
1203  GtkWidget *dialog;
1204  gint result;
1205  gchar *export_dirname = NULL;
1206  gchar* basename;
1207 
1208  // Before we save the PDF file, we always ask the user for the export
1209  // file name. We will store the chosen directory in the gtk print settings
1210  // as well.
1211  dialog = gtk_file_chooser_dialog_new (_("Export to PDF File"),
1212  NULL,
1213  GTK_FILE_CHOOSER_ACTION_SAVE,
1214  _("_Cancel"), GTK_RESPONSE_CANCEL,
1215  _("_Save"), GTK_RESPONSE_ACCEPT,
1216  NULL);
1217  gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE);
1218 
1219  // Does the jobname look like a valid full file path?
1220  basename = g_path_get_basename(jobname);
1221  if (strcmp(basename, jobname) != 0)
1222  {
1223  gchar *tmp_basename;
1224  gchar *tmp_dirname = g_path_get_dirname(jobname);
1225 
1226  if (g_file_test(tmp_dirname, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
1227  {
1228  // Yes, the jobname starts with a directory name that actually
1229  // exists. Hence we use this as output directory.
1230  export_dirname = tmp_dirname;
1231  tmp_dirname = NULL;
1232 
1233  // As the prefix part of the "jobname" is the directory path, we
1234  // need to extract the suffix part for the filename.
1235  tmp_basename = g_path_get_basename(export_filename);
1236  g_free(export_filename);
1237  export_filename = tmp_basename;
1238  }
1239  g_free(tmp_dirname);
1240  }
1241  g_free(basename);
1242 
1243  // Set the output file name from the given jobname
1244  gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(dialog), export_filename);
1245 
1246  // Do we have a stored output directory?
1247  if (!export_dirname && gtk_print_settings_has_key(print_settings, GNC_GTK_PRINT_SETTINGS_EXPORT_DIR))
1248  {
1249  const char* tmp_dirname = gtk_print_settings_get(print_settings,
1251  // Only use the directory subsequently if it exists.
1252  if (g_file_test(tmp_dirname, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
1253  {
1254  export_dirname = g_strdup(tmp_dirname);
1255  }
1256  }
1257 
1258  // If we have an already existing directory, propose it now.
1259  if (export_dirname)
1260  {
1261  gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), export_dirname);
1262  }
1263  g_free(export_dirname);
1264 
1265  result = gtk_dialog_run (GTK_DIALOG (dialog));
1266  // Weird. In gtk_dialog_run, the gtk code will run a fstat() on the
1267  // proposed new output filename, which of course fails with "file not
1268  // found" as this file doesn't exist. It will still show a warning output
1269  // in the trace file, though.
1270 
1271  if (result == GTK_RESPONSE_ACCEPT)
1272  {
1273  // The user pressed "Ok", so use the file name for the actual file output.
1274  gchar *dirname;
1275  char *tmp = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
1276  g_free(export_filename);
1277  export_filename = tmp;
1278 
1279  // Store the directory part of the file for later
1280  dirname = g_path_get_dirname(export_filename);
1281  if (g_file_test(dirname, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
1282  {
1283  gtk_print_settings_set(print_settings, GNC_GTK_PRINT_SETTINGS_EXPORT_DIR, dirname);
1284  }
1285  g_free(dirname);
1286  }
1287  gtk_widget_destroy (dialog);
1288 
1289  if (result != GTK_RESPONSE_ACCEPT)
1290  {
1291  // User pressed cancel - no saving of the PDF file here.
1292  g_free(export_filename);
1293  g_object_unref( op );
1294  return;
1295  }
1296 
1297  // This function expects the full filename including (absolute?) path
1298  gtk_print_operation_set_export_filename(op, export_filename);
1299 
1300  // Run the "Export to PDF" print operation
1301  webkit_web_frame_print_full( frame, op, GTK_PRINT_OPERATION_ACTION_EXPORT, &error );
1302  }
1303  else
1304  {
1305 
1306  // Also store this export file name as output URI in the settings
1307  if (gtk_print_settings_has_key(print_settings, GTK_PRINT_SETTINGS_OUTPUT_URI))
1308  {
1309  // Get the previous output URI, extract the directory part, and
1310  // append the current filename.
1311  const gchar *olduri = gtk_print_settings_get(print_settings, GTK_PRINT_SETTINGS_OUTPUT_URI);
1312  gchar *dirname = g_path_get_dirname(olduri);
1313  gchar *newuri = (g_strcmp0(dirname, ".") == 0)
1314  ? g_strdup(export_filename)
1315  : g_build_filename(dirname, export_filename, NULL);
1316  //g_warning("olduri=%s newuri=%s", olduri, newuri);
1317 
1318  // This function expects the full filename including protocol, path, and name
1319  gtk_print_settings_set(print_settings, GTK_PRINT_SETTINGS_OUTPUT_URI, newuri);
1320 
1321  g_free(newuri);
1322  g_free(dirname);
1323  }
1324  else
1325  {
1326  // No stored output URI from the print settings, so just set our export filename
1327  gtk_print_settings_set(print_settings, GTK_PRINT_SETTINGS_OUTPUT_URI, export_filename);
1328  }
1329 
1330  // Run the normal printing dialog
1331  webkit_web_frame_print_full( frame, op, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, &error );
1332  }
1333 
1334  if ( error != NULL )
1335  {
1336  GtkWidget* window = gtk_widget_get_toplevel( GTK_WIDGET(priv->web_view) );
1337  GtkWidget* dialog = gtk_message_dialog_new( gtk_widget_is_toplevel(window) ? GTK_WINDOW(window) : NULL,
1338  GTK_DIALOG_DESTROY_WITH_PARENT,
1339  GTK_MESSAGE_ERROR,
1340  GTK_BUTTONS_CLOSE,
1341  "%s", error->message );
1342  g_error_free( error );
1343 
1344  g_signal_connect( dialog, "response", G_CALLBACK(gtk_widget_destroy), NULL);
1345  gtk_widget_show( dialog );
1346  }
1347 
1348  // Remember to save the printing settings after this print job
1350  g_object_unref( op );
1351  g_free(export_filename);
1352 }
1353 
1354 static void
1355 impl_webkit_set_parent( GncHtml* self, GtkWindow* parent )
1356 {
1357  GncHtmlWebkitPrivate* priv;
1358 
1359  g_return_if_fail( self != NULL );
1360  g_return_if_fail( GNC_IS_HTML_WEBKIT(self) );
1361 
1362  priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);
1363  priv->base.parent = GTK_WIDGET(parent);
1364 }
1365 
1366 static void
1367 impl_webkit_default_zoom_changed(gpointer prefs, gchar *pref, gpointer user_data)
1368 {
1369  gdouble zoom = 1.0;
1370  GncHtmlWebkit* self = GNC_HTML_WEBKIT(user_data);
1371  GncHtmlWebkitPrivate* priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);
1372 
1373  g_return_if_fail(user_data != NULL);
1374 
1375  zoom = gnc_prefs_get_float (GNC_PREFS_GROUP_GENERAL_REPORT, GNC_PREF_RPT_DFLT_ZOOM);
1376  webkit_web_view_set_zoom_level (priv->web_view, zoom);
1377 
1378 }
gulong gnc_prefs_register_cb(const char *group, const gchar *pref_name, gpointer func, gpointer user_data)
Register a callback that gets triggered when the given preference changes.
Definition: gnc-prefs.c:128
#define GNC_GTK_PRINT_SETTINGS_EXPORT_DIR
Key for saving the PDF-export directory in the print settings.
Definition: print-session.h:74
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
void gnc_print_operation_save_print_settings(GtkPrintOperation *op)
Retrieve the print settings from the GtkPrintOperation op and save them in a static variable...
Definition: print-session.c:39
#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
#define PWARN(format, args...)
Log a warning.
Definition: qoflog.h:250
Account handling public routines.
void gnc_print_operation_init(GtkPrintOperation *op, const gchar *jobname)
If print settings have been saved by gnc_print_operation_save_print_settings(), then set them on the ...
Definition: print-session.c:51
All type declarations for the whole Gnucash engine.
Generic api to store and retrieve preferences.
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
void gnc_prefs_remove_cb_by_func(const gchar *group, const gchar *pref_name, gpointer func, gpointer user_data)
Remove a function that was registered for a callback when the given preference changed.
Definition: gnc-prefs.c:143
gdouble gnc_prefs_get_float(const gchar *group, const gchar *pref_name)
Get an float value from the preferences backend.