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