From 07467f13c6211193fdba32bea1198ea076d1b20d Mon Sep 17 00:00:00 2001 From: Andrew Sackville-West Date: Thu, 26 Mar 2009 19:38:38 +0000 Subject: [PATCH] Implement new custom report dialog. Implements a new interface for accessing "saved" or "custom" reports. Specifically, the current sub-menu is replaced with a dialog holding a GtkTreeView of the saved reports. From here the user can either run or delete an existing saved report. * allows deletion of existing saved reports * allows immediate access to new saved reports from this session (no need to restart gnucash to get access to new saved reports!) * keeps a single rolling backup of saved reports in case of errors git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@18001 57a11ea4-9604-0410-9ed3-97b8803252fd --- src/report/report-gnome/Makefile.am | 6 +- .../report-gnome/custom-report-dialog.glade | 183 +++++++++ .../report-gnome/dialog-custom-report.c | 359 ++++++++++++++++++ .../report-gnome/dialog-custom-report.h | 50 +++ src/report/report-gnome/report-gnome.i | 3 + src/report/report-gnome/report-gnome.scm | 17 +- src/report/report-system/report-system.scm | 2 + src/report/report-system/report.scm | 52 ++- 8 files changed, 658 insertions(+), 14 deletions(-) create mode 100644 src/report/report-gnome/custom-report-dialog.glade create mode 100644 src/report/report-gnome/dialog-custom-report.c create mode 100644 src/report/report-gnome/dialog-custom-report.h diff --git a/src/report/report-gnome/Makefile.am b/src/report/report-gnome/Makefile.am index 037e489c58..f25bce3a56 100644 --- a/src/report/report-gnome/Makefile.am +++ b/src/report/report-gnome/Makefile.am @@ -22,6 +22,7 @@ AM_CPPFLAGS = \ libgncmod_report_gnome_la_SOURCES = \ swig-report-gnome.c \ dialog-column-view.c \ + dialog-custom-report.c \ dialog-style-sheet.c \ gnc-plugin-page-report.c \ gncmod-report-gnome.c \ @@ -30,6 +31,7 @@ libgncmod_report_gnome_la_SOURCES = \ gncincludedir = ${GNC_INCLUDE_DIR} gncinclude_HEADERS = \ dialog-column-view.h \ + dialog-custom-report.h \ dialog-style-sheet.h \ gnc-plugin-page-report.h \ window-report.h @@ -61,7 +63,9 @@ gncmod_DATA = report-gnome.scm noinst_DATA = .scm-links gladedir = $(GNC_GLADE_DIR) -glade_DATA = report.glade +glade_DATA = \ + report.glade \ + custom-report-dialog.glade uidir = $(GNC_UI_DIR) diff --git a/src/report/report-gnome/custom-report-dialog.glade b/src/report/report-gnome/custom-report-dialog.glade new file mode 100644 index 0000000000..5795ca01fb --- /dev/null +++ b/src/report/report-gnome/custom-report-dialog.glade @@ -0,0 +1,183 @@ + + + + + + + 5 + + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_CENTER_ON_PARENT + False + True + False + True + True + True + GDK_WINDOW_TYPE_HINT_DIALOG + GDK_GRAVITY_NORTH_WEST + True + False + False + + + + + True + False + 2 + + + + True + GTK_BUTTONBOX_END + + + + True + Delete the currently selected report + True + gtk-delete + True + GTK_RELIEF_NORMAL + True + 0 + + + + + + + True + Exit the custom report dialog + True + gtk-cancel + True + GTK_RELIEF_NORMAL + True + 0 + + + + + + + True + Run the currently selected report + True + True + GTK_RELIEF_NORMAL + True + 0 + + + + + True + 0.5 + 0.5 + 0 + 0 + 0 + 0 + 0 + 0 + + + + True + False + 2 + + + + True + gtk-go-forward + 4 + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + True + _Run + True + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + + + + + + 0 + False + True + GTK_PACK_END + + + + + + 150 + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_NONE + GTK_CORNER_TOP_LEFT + + + + True + True + False + False + False + True + False + False + False + + + + + + 0 + True + True + + + + + + + diff --git a/src/report/report-gnome/dialog-custom-report.c b/src/report/report-gnome/dialog-custom-report.c new file mode 100644 index 0000000000..599a56f6bb --- /dev/null +++ b/src/report/report-gnome/dialog-custom-report.c @@ -0,0 +1,359 @@ +/********************************************************************\ + * dialog-custom-report.h -- dialog for managing custom reports * + * * + * * + * Copyright (C) 2009 * + * * + * Andrew Sackville-West * + * (andrew@swclan.homelinux.org) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ + + +#include "config.h" + +#include +#include +#include +#include +#include "swig-runtime.h" + +#include "dialog-custom-report.h" +#include "dialog-options.h" +#include "dialog-utils.h" +#include "gnc-main-window.h" +#include "option-util.h" +#include "window-report.h" +#include "guile-mappings.h" +#include "gnc-gui-query.h" +#include "gnc-ui.h" +#include "gnc-report.h" +#include "gnc-plugin-page-report.h" + + +/* convenience for accessing columns in the GtkListStore that holds + the reports */ +enum + { + COL_NAME = 0, + COL_NUM, + NUM_COLS + }; + +/* all the pertinent stuff needed to pass around */ +typedef struct _CustomReportDialog +{ + /* dialog */ + GtkWidget *dialog; + GtkWidget *reportview; + GncMainWindow *window; + + /* data */ + SCM reportlist; + +} CustomReportDialog; + +static void +custom_report_dialog_close_cb(GtkWidget* widget, + CustomReportDialog *crd) +{ + gtk_widget_destroy(crd->dialog); + g_free(crd); + +} + + +static void +cancel_custom_report_clicked_cb(GtkWidget* widget, + CustomReportDialog *crd) +{ + custom_report_dialog_close_cb(NULL, crd); +} + + +/** + * update_report_list + * + * this procedure does the real work of displaying a sorted list of + * available custom reports + * + */ +static void +update_report_list(GtkListStore *store, CustomReportDialog *crd) +{ + + SCM get_names = scm_c_eval_string("gnc:custom-report-template-names"); + SCM template_menu_name = scm_c_eval_string("gnc:report-template-menu-name/report-guid"); + SCM names; + const gchar *name; + int i; + GtkTreeIter iter; + + gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), COL_NAME, GTK_SORT_ASCENDING); + + crd->reportlist = scm_call_0(get_names); + names = crd->reportlist; + + gtk_list_store_clear(store); + + if(SCM_LISTP(names)) { + + /* for all the names in the list, store them, with a reference, + in the gtkliststore */ + for (i=0; !SCM_NULLP(names); i++) { + + name = SCM_STRING_CHARS(scm_call_2(template_menu_name, SCM_CAR(names), SCM_BOOL_F)); + + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, + COL_NAME, name, + COL_NUM, i, + -1); + names = SCM_CDR(names); + + } + + } + +} + + +static GtkTreeModel * +create_and_fill_report_list(CustomReportDialog *crd) +{ + GtkListStore *store; + GtkTreeIter iter; + + store = gtk_list_store_new(NUM_COLS, G_TYPE_STRING, G_TYPE_INT); + + update_report_list(store, crd); + + return GTK_TREE_MODEL (store); + +} + +static void +set_reports_model(CustomReportDialog *crd) +{ + GtkCellRenderer *renderer; + GtkTreeModel *model; + + renderer = gtk_cell_renderer_text_new(); + + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (crd->reportview), -1, "Report Name", renderer, "text", COL_NAME, NULL); + + model = create_and_fill_report_list(crd); + + gtk_tree_view_set_model (GTK_TREE_VIEW (crd->reportview), model); + + g_object_unref(model); + + +} + + +/** + * + * custom_report_run_report + * + * this procedure sets up and calls the report on the scheme + * side. This is what makes the report actually run. + */ +static void +custom_report_run_report(SCM guid, + CustomReportDialog *crd) +{ + + SCM make_report = scm_c_eval_string("gnc:make-report"); + int report_id; + GncMainWindow *window = crd->window; + + if(!SCM_NULLP(guid)) + { + + /* this runs the report */ + report_id = SCM_INUM(scm_call_1(make_report, guid)); + + /* do this *before* the report because sometimes the report + takes a while... */ + custom_report_dialog_close_cb(NULL, crd); + + /* display the report */ + gnc_main_window_open_report(report_id, window); + + } + +} + +/** + * get_custom_report_selection + * + * this helper function is called to get the selection when the user + * clicks on "Run" or "Delete". Includes calling a dialog when there + * is no selection. + * + * const gchar* message -- the message to provide user if there is no + * actual selection found. + * + */ +static SCM +get_custom_report_selection(CustomReportDialog *crd, + const gchar* message) +{ + GtkTreeSelection *sel; + GtkTreeModel *model; + GtkTreeIter iter; + SCM guid; + + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(crd->reportview)); + + if (gtk_tree_selection_get_selected(sel, &model, &iter)) + { + int num; + + gtk_tree_model_get(model, &iter, COL_NUM, &num, -1); + guid = scm_list_ref(crd->reportlist, scm_int2num(num)); + } + + else + { + /* no selection, notify user */ + gnc_error_dialog(GTK_WIDGET(crd->window), "%s", message); + return SCM_EOL; + + } + + return guid; + +} + +/** + * on_custom_report_list_view_row_activated + * + * this is the double-click signal. No need to call + * get_custom_report_selection as the double-click implies the + * selection. + * + */ +static void +on_custom_report_list_view_row_activated(GtkTreeView *view, + GtkTreePath *path, + GtkTreeViewColumn *column, + CustomReportDialog *crd) +{ + + GtkTreeModel *model; + GtkTreeIter iter; + + model = gtk_tree_view_get_model(view); + + if(gtk_tree_model_get_iter(model, &iter, path)) + { + int num; + SCM guid; + + gtk_tree_model_get(model, &iter, COL_NUM, &num, -1); + + guid = scm_list_ref(crd->reportlist, scm_int2num(num)); + + custom_report_run_report(guid, crd); + + } + +} + +/** + * on_delete_custom_report_clicked + * + * this will delete the report, update the reports list and leave the + * dialog active for additional usage. + * + */ +static void +on_delete_custom_report_clicked(GtkWidget *button, + CustomReportDialog *crd) +{ + GtkTreeSelection *sel; + GtkTreeModel *model; + GtkTreeIter iter; + + SCM template_menu_name = scm_c_eval_string("gnc:report-template-menu-name/report-guid"); + SCM guid; + gchar* report_name; + + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(crd->reportview)); + + guid = get_custom_report_selection(crd, _("You must select a report to delete.")); + report_name = SCM_STRING_CHARS(scm_call_2(template_menu_name, guid, SCM_BOOL_F)); + + /* we must confirm the user wants to delete their precious custom report! */ + if (!SCM_NULLP(guid) + && gnc_verify_dialog(crd->dialog, FALSE, "Are you sure you want to delete %s?", report_name)) + { + SCM del_report = scm_c_eval_string("gnc:delete-report"); + scm_call_1(del_report, guid); + update_report_list(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(crd->reportview))) + , crd); + } + +} + +static void +run_custom_report_clicked_cb (GtkWidget* button, + CustomReportDialog *crd) +{ + + SCM guid = get_custom_report_selection(crd, _("You must select a report to run.")); + custom_report_run_report(guid, crd); + +} + + + +/** + * gnc_ui_custom_report + * + * this is the primary driver for the custom report dialog. + * + */ +void gnc_ui_custom_report(GncMainWindow * window) +{ + + GladeXML* xml; + CustomReportDialog *crd; + + crd = g_new0(CustomReportDialog, 1); + + xml = gnc_glade_xml_new("custom-report-dialog.glade", "custom_report_dialog"); + + crd->dialog = glade_xml_get_widget(xml, "custom_report_dialog"); + crd->reportview = glade_xml_get_widget(xml, "custom_report_list_view"); + set_reports_model(crd); + crd->window = window; + + /* connect the signals */ + glade_xml_signal_connect_data(xml, "cancel_custom_report_clicked_cb", G_CALLBACK(cancel_custom_report_clicked_cb), crd); + glade_xml_signal_connect_data(xml, "custom_report_dialog_close_cb", G_CALLBACK(custom_report_dialog_close_cb), crd); + glade_xml_signal_connect_data(xml, "on_custom_report_list_view_row_activated", G_CALLBACK(on_custom_report_list_view_row_activated), crd); + glade_xml_signal_connect_data(xml, "on_delete_custom_report_clicked", G_CALLBACK(on_delete_custom_report_clicked), crd); + glade_xml_signal_connect_data(xml, "run_custom_report_clicked_cb", G_CALLBACK(run_custom_report_clicked_cb), crd); + + gtk_widget_show_all(crd->dialog); + +} diff --git a/src/report/report-gnome/dialog-custom-report.h b/src/report/report-gnome/dialog-custom-report.h new file mode 100644 index 0000000000..9b13c0e98f --- /dev/null +++ b/src/report/report-gnome/dialog-custom-report.h @@ -0,0 +1,50 @@ +/********************************************************************\ + * dialog-custom-report.h -- dialog for managing custom reports * + * * + * * + * Copyright (C) 2009 * + * * + * Andrew Sackville-West * + * (andrew@swclan.homelinux.org) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ + +#ifndef DIALOG_CUSTOM_REPORT_H +#define DIALOG_CUSTOM_REPORT_H + +#include "gnc-main-window.h" + +/** @addtogroup Reports + @{ */ +/** @file dialog-custom-report.h + * + * This file contains the functions to present a GUI to manage custom + * reports + */ + +void +gnc_ui_custom_report(GncMainWindow * window); + +/** @} */ + +#endif /* DIALOG_CUSTOM_REPORT_H */ + + + + diff --git a/src/report/report-gnome/report-gnome.i b/src/report/report-gnome/report-gnome.i index 7a1878180a..81c7ceae42 100644 --- a/src/report/report-gnome/report-gnome.i +++ b/src/report/report-gnome/report-gnome.i @@ -6,6 +6,7 @@ #include #include #include +#include SCM scm_init_sw_report_gnome_module (void); %} @@ -17,3 +18,5 @@ void gnc_main_window_open_report(int report_id, GncMainWindow *window); GtkWidget * gnc_report_window_default_params_editor(SCM options, SCM report); GtkWidget * gnc_column_view_edit_options(SCM options, SCM view); +void gnc_ui_custom_report(GncMainWindow * window); + diff --git a/src/report/report-gnome/report-gnome.scm b/src/report/report-gnome/report-gnome.scm index 20456f4b1f..3fc44d0658 100644 --- a/src/report/report-gnome/report-gnome.scm +++ b/src/report/report-gnome/report-gnome.scm @@ -96,6 +96,7 @@ (add-template-menu-item (car item) (cdr item))) (sort *template-items* sort-templates))) + (define (gnc:report-menu-setup) (define asset-liability-menu (gnc:make-menu gnc:menuname-asset-liability (list gnc:menuname-reports))) @@ -105,17 +106,22 @@ (gnc:make-menu gnc:menuname-budget (list gnc:menuname-reports))) (define utility-menu (gnc:make-menu gnc:menuname-utility (list gnc:menuname-reports))) - (define custom-menu - (gnc:make-menu gnc:menuname-custom (list gnc:menuname-reports))) (define tax-menu (gnc:make-menu gnc:menuname-taxes (list gnc:menuname-reports))) + (gnc-add-scm-extension + (gnc:make-menu-item + (N_ "Custom Reports") + (N_ "Manage and run custom reports") + (list gnc:menuname-reports) + (lambda (window) + (gnc:spawn-custom-report-dialog window)))) + ;; (gnc-add-scm-extension tax-menu) (gnc-add-scm-extension income-expense-menu) (gnc-add-scm-extension asset-liability-menu) (gnc-add-scm-extension budget-menu) (gnc-add-scm-extension utility-menu) - (gnc-add-scm-extension custom-menu) ;; run report-hook danglers (gnc:hook-run-danglers HOOK-REPORT) @@ -131,4 +137,9 @@ (list gnc:menuname-reports gnc:menuname-utility "") (lambda (window) (gnc-main-window-open-report (gnc:make-welcome-report) window)))) + ) + +(define (gnc:spawn-custom-report-dialog window) + (gnc:debug "called into custom report dialog, window is " window) + (gnc-ui-custom-report window)) diff --git a/src/report/report-system/report-system.scm b/src/report/report-system/report-system.scm index 887e521b58..56066bea5f 100644 --- a/src/report/report-system/report-system.scm +++ b/src/report/report-system/report-system.scm @@ -155,6 +155,8 @@ (export gnc:report-stylesheet) (export gnc:report-set-stylesheet!) (export gnc:all-report-template-names) +(export gnc:custom-report-template-names) +(export gnc:delete-report) (export gnc:find-report-template) (export gnc:report-generate-restore-forms) (export gnc:report-generate-saved-forms) diff --git a/src/report/report-system/report.scm b/src/report/report-system/report.scm index d2a91cf182..014391142c 100644 --- a/src/report/report-system/report.scm +++ b/src/report/report-system/report.scm @@ -455,6 +455,20 @@ '() *gnc:_report-templates_*) string Custom at the next startup of GnuCash.") + #f (_ "Your report \"%s\" has been saved into the configuration file \"%s\".") (if (and report-name (not (string-null? report-name))) (gnc:gettext report-name) (gnc:gettext "Untitled")) @@ -578,15 +592,24 @@ )))) (define (gnc:report-template-save-to-savefile report-template) - (let* ((conf-file-name gnc:current-saved-reports) - (saved-form (gnc:report-template-generate-saved-forms report-template)) - (save-result (eval-string saved-form))) - (if (record? save-result) - (begin - (display saved-form - (open-file conf-file-name "a")) - (force-output) - )))) + (let ((conf-file-name gnc:current-saved-reports) + (saved-form (gnc:report-template-generate-saved-forms report-template))) + (display saved-form + (open-file conf-file-name "a")) + (force-output))) + +;; save all custom reports, moving the old version of the +;; saved-reports file aside as a backup +(define (gnc:save-all-reports) + (let ((temp-path (gnc-build-dotgnucash-path "saved-reports-2.4-backup"))) + (gnc:debug "saving all reports...") + (rename-file gnc:current-saved-reports temp-path) + (hash-for-each (lambda (k v) + (if (gnc:report-template-parent-type v) + (begin + (gnc:debug "saving report " k) + (gnc:report-template-save-to-savefile v)))) + *gnc:_report-templates_*))) ;; gets the renderer from the report template; @@ -646,3 +669,12 @@ (let ((opt-value (gnc:option-value option))) (map (lambda (x) (car x)) opt-value)) #f))) + +;; delete an existing report from the hash table and then call to +;; resave the saved-reports file... report is gone +(define (gnc:delete-report report) + (if (hash-ref *gnc:_report-templates_* report) + (begin + (gnc:debug "Deleting report " report) + (hash-remove! *gnc:_report-templates_* report) + (gnc:save-all-reports)))) \ No newline at end of file