|
GnuCash 2.4.99
|
00001 /* gnc-plugin-page-report.c 00002 * Copyright (C) 2004 Joshua Sled <jsled@asynchronous.org> 00003 * Copyright (C) 2005 David Hampton <hampton@employees.org> 00004 * 00005 * Originally from window-report.c: 00006 * Copyright (C) 1997 Robin D. Clark 00007 * Copyright (C) 1998 Linas Vepstas 00008 * Copyright (C) 1999 Jeremy Collins ( gtk-xmhtml port ) 00009 * Copyright (C) 2000 Dave Peticolas 00010 * Copyright (C) 2000 Bill Gribble 00011 * 00012 * This program is free software; you can redistribute it and/or 00013 * modify it under the terms of the GNU General Public License as 00014 * published by the Free Software Foundation; either version 2 of 00015 * the License, or (at your option) any later version. 00016 * 00017 * This program is distributed in the hope that it will be useful, 00018 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00019 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00020 * GNU General Public License for more details. 00021 * 00022 * You should have received a copy of the GNU General Public License 00023 * along with this program; if not, contact: 00024 * 00025 * Free Software Foundation Voice: +1-617-542-5942 00026 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 00027 * Boston, MA 02110-1301, USA gnu@gnu.org 00028 */ 00029 00040 #include "config.h" 00041 00042 #include <gtk/gtk.h> 00043 #include <glib/gi18n.h> 00044 #include <glib/gstdio.h> 00045 #include <libguile.h> 00046 #include <sys/stat.h> 00047 #include <errno.h> 00048 00049 #include "gfec.h" 00050 #include "gnc-component-manager.h" 00051 #include "gnc-engine.h" 00052 #include "gnc-gconf-utils.h" 00053 #include "gnc-gnome-utils.h" 00054 #include "gnc-html-history.h" 00055 #include "gnc-html.h" 00056 #include "gnc-html-factory.h" 00057 #include "gnc-file.h" 00058 #include "gnc-plugin.h" 00059 #include "gnc-plugin-page-report.h" 00060 #include "gnc-plugin-file-history.h" 00061 #include "gnc-report.h" 00062 #include "gnc-session.h" 00063 #include "gnc-ui-util.h" 00064 #include "gnc-ui.h" 00065 #include "gnc-window.h" 00066 #include "guile-util.h" 00067 #include "option-util.h" 00068 #include "window-report.h" 00069 #include "swig-runtime.h" 00070 #include "app-utils/business-options.h" 00071 #include "gnome-utils/gnc-icons.h" 00072 #include "gnome-utils/print-session.h" 00073 00074 #define WINDOW_REPORT_CM_CLASS "window-report" 00075 00076 /* NW: you can add GNC_MOD_REPORT to gnc-engine.h 00077 or simply define it locally. Any unique string with 00078 a gnucash- prefix will do. Then just set a log level 00079 with qof_log_set_level().*/ 00080 static QofLogModule log_module = GNC_MOD_GUI; 00081 00082 static GObjectClass *parent_class = NULL; 00083 00084 // A static GHashTable to record the usage count for each printer 00085 // output name. FIXME: Currently this isn't cleaned up at program 00086 // shutdown because there isn't a place to easily insert a finalize() 00087 // function for this. Oh well. 00088 static GHashTable *static_report_printnames = NULL; 00089 00090 // Property-id values. 00091 enum 00092 { 00093 PROP_0, 00094 PROP_REPORT_ID, 00095 }; 00096 00097 typedef struct GncPluginPageReportPrivate 00098 { 00100 int reportId; 00101 gint component_manager_id; 00102 00104 SCM cur_report; 00106 GNCOptionDB *cur_odb; 00107 SCM option_change_cb_id; 00108 00109 /* initial_report is special; it's the one that's saved and 00110 * restored. The name_change_callback only gets called when 00111 * the initial_report name is changed. */ 00112 SCM initial_report; 00113 GNCOptionDB * initial_odb; 00114 SCM name_change_cb_id; 00115 00116 /* keep a list of edited reports so that we can destroy them when 00117 * the window is closed. */ 00118 SCM edited_reports; 00119 00120 /* This is set to mark the fact that we need to reload the html */ 00121 gboolean need_reload; 00122 00123 /* The page is in the process of reloading the html */ 00124 gboolean reloading; 00125 00127 // gnc_html *html; 00128 GncHtml *html; 00129 00131 GtkContainer *container; 00132 } GncPluginPageReportPrivate; 00133 00134 #define GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(o) \ 00135 (G_TYPE_INSTANCE_GET_PRIVATE ((o), GNC_TYPE_PLUGIN_PAGE_REPORT, GncPluginPageReportPrivate)) 00136 00137 static void gnc_plugin_page_report_class_init( GncPluginPageReportClass *klass ); 00138 static void gnc_plugin_page_report_init( GncPluginPageReport *plugin_page ); 00139 static GObject *gnc_plugin_page_report_constructor(GType this_type, guint n_properties, GObjectConstructParam *properties); 00140 static void gnc_plugin_page_report_finalize (GObject *object); 00141 static void gnc_plugin_page_report_setup( GncPluginPage *ppage ); 00142 00143 static void gnc_plugin_page_report_constr_init(GncPluginPageReport *plugin_page, gint reportId); 00144 00145 static GtkWidget* gnc_plugin_page_report_create_widget( GncPluginPage *plugin_page ); 00146 static void gnc_plugin_page_report_destroy_widget( GncPluginPage *plugin_page ); 00147 static void gnc_plugin_page_report_save_page (GncPluginPage *plugin_page, GKeyFile *file, const gchar *group); 00148 static GncPluginPage *gnc_plugin_page_report_recreate_page (GtkWidget *window, GKeyFile *file, const gchar *group); 00149 static void gnc_plugin_page_report_name_changed (GncPluginPage *page, const gchar *name); 00150 static void gnc_plugin_page_report_update_edit_menu (GncPluginPage *page, gboolean hide); 00151 static gboolean gnc_plugin_page_report_finish_pending (GncPluginPage *page); 00152 00153 static int gnc_plugin_page_report_check_urltype(URLType t); 00154 //static void gnc_plugin_page_report_load_cb(gnc_html * html, URLType type, 00155 static void gnc_plugin_page_report_load_cb(GncHtml * html, URLType type, 00156 const gchar * location, const gchar * label, 00157 gpointer data); 00158 static void gnc_plugin_page_report_expose_event_cb(GtkWidget *unused, GdkEventExpose *unused1, gpointer data); 00159 static void gnc_plugin_page_report_refresh (gpointer data); 00160 static void gnc_plugin_page_report_set_fwd_button(GncPluginPageReport * page, int enabled); 00161 static void gnc_plugin_page_report_set_back_button(GncPluginPageReport * page, int enabled); 00162 static void gnc_plugin_page_report_history_destroy_cb(gnc_html_history_node * node, gpointer user_data); 00163 static void close_handler(gpointer user_data); 00164 void gnc_plugin_page_report_destroy(GncPluginPageReportPrivate *priv); 00165 static void gnc_plugin_page_report_option_change_cb(gpointer data); 00166 00167 void gnc_plugin_page_report_remove_edited_report(GncPluginPageReportPrivate *priv, SCM report); 00168 void gnc_plugin_page_report_add_edited_report(GncPluginPageReportPrivate *priv, SCM report); 00169 void gnc_plugin_page_report_raise_editor(SCM report); 00170 00171 static void gnc_plugin_page_report_forw_cb(GtkAction *action, GncPluginPageReport *rep); 00172 static void gnc_plugin_page_report_back_cb(GtkAction *action, GncPluginPageReport *rep); 00173 static void gnc_plugin_page_report_reload_cb(GtkAction *action, GncPluginPageReport *rep); 00174 static void gnc_plugin_page_report_stop_cb(GtkAction *action, GncPluginPageReport *rep); 00175 static void gnc_plugin_page_report_save_cb(GtkAction *action, GncPluginPageReport *rep); 00176 static void gnc_plugin_page_report_export_cb(GtkAction *action, GncPluginPageReport *rep); 00177 static void gnc_plugin_page_report_options_cb(GtkAction *action, GncPluginPageReport *rep); 00178 static void gnc_plugin_page_report_print_cb(GtkAction *action, GncPluginPageReport *rep); 00179 static void gnc_plugin_page_report_exportpdf_cb(GtkAction *action, GncPluginPageReport *rep); 00180 static void gnc_plugin_page_report_copy_cb(GtkAction *action, GncPluginPageReport *rep); 00181 00182 GType 00183 gnc_plugin_page_report_get_type (void) 00184 { 00185 static GType gnc_plugin_page_report_type = 0; 00186 00187 if (gnc_plugin_page_report_type == 0) 00188 { 00189 static const GTypeInfo our_info = 00190 { 00191 sizeof (GncPluginPageReportClass), 00192 NULL, 00193 NULL, 00194 (GClassInitFunc) gnc_plugin_page_report_class_init, 00195 NULL, 00196 NULL, 00197 sizeof (GncPluginPageReport), 00198 0, 00199 (GInstanceInitFunc) gnc_plugin_page_report_init 00200 }; 00201 00202 gnc_plugin_page_report_type = g_type_register_static (GNC_TYPE_PLUGIN_PAGE, 00203 "GncPluginPageReport", 00204 &our_info, 0); 00205 } 00206 00207 return gnc_plugin_page_report_type; 00208 } 00209 00210 static void 00211 gnc_plugin_page_report_get_property( GObject *obj, 00212 guint prop_id, 00213 GValue *value, 00214 GParamSpec *pspec ) 00215 { 00216 GncPluginPageReport *rep; 00217 GncPluginPageReportPrivate *priv; 00218 00219 rep = GNC_PLUGIN_PAGE_REPORT( obj ); 00220 priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(rep); 00221 00222 switch ( prop_id ) 00223 { 00224 case PROP_REPORT_ID: 00225 g_value_set_int( value, priv->reportId ); 00226 break; 00227 default: 00228 PERR( "Unknown property id %d", prop_id ); 00229 break; 00230 } 00231 } 00232 00233 static void 00234 gnc_plugin_page_report_set_property( GObject *obj, 00235 guint prop_id, 00236 const GValue *value, 00237 GParamSpec *pspec ) 00238 { 00239 GncPluginPageReport *rep; 00240 GncPluginPageReportPrivate *priv; 00241 00242 rep = GNC_PLUGIN_PAGE_REPORT( obj ); 00243 priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(rep); 00244 00245 DEBUG( "setting property with id %d / %p to value %d", 00246 prop_id, priv, g_value_get_int( value ) ); 00247 00248 switch ( prop_id ) 00249 { 00250 case PROP_REPORT_ID: 00251 priv->reportId = g_value_get_int( value ); 00252 break; 00253 default: 00254 PERR( "unknown property id %d", prop_id ); 00255 break; 00256 } 00257 00258 } 00259 00260 static void 00261 gnc_plugin_page_report_class_init (GncPluginPageReportClass *klass) 00262 { 00263 GObjectClass *object_class = G_OBJECT_CLASS (klass); 00264 GncPluginPageClass *gnc_plugin_page_class = GNC_PLUGIN_PAGE_CLASS(klass); 00265 00266 parent_class = g_type_class_peek_parent (klass); 00267 00268 object_class->constructor = gnc_plugin_page_report_constructor; 00269 object_class->finalize = gnc_plugin_page_report_finalize; 00270 00271 object_class->set_property = gnc_plugin_page_report_set_property; 00272 object_class->get_property = gnc_plugin_page_report_get_property; 00273 00274 // FIXME: stock reporting icon? 00275 //gnc_plugin_page_class->tab_icon = GNC_STOCK_ACCOUNT; 00276 gnc_plugin_page_class->plugin_name = GNC_PLUGIN_PAGE_REPORT_NAME; 00277 00278 gnc_plugin_page_class->create_widget = gnc_plugin_page_report_create_widget; 00279 gnc_plugin_page_class->destroy_widget = gnc_plugin_page_report_destroy_widget; 00280 gnc_plugin_page_class->save_page = gnc_plugin_page_report_save_page; 00281 gnc_plugin_page_class->recreate_page = gnc_plugin_page_report_recreate_page; 00282 gnc_plugin_page_class->page_name_changed = gnc_plugin_page_report_name_changed; 00283 gnc_plugin_page_class->update_edit_menu_actions = gnc_plugin_page_report_update_edit_menu; 00284 gnc_plugin_page_class->finish_pending = gnc_plugin_page_report_finish_pending; 00285 00286 g_type_class_add_private(klass, sizeof(GncPluginPageReportPrivate)); 00287 00288 // create the "reportId" property 00289 g_object_class_install_property( object_class, 00290 PROP_REPORT_ID, 00291 g_param_spec_int( "report-id", 00292 _("The numeric ID of the report."), 00293 _("The numeric ID of the report."), 00294 -1, G_MAXINT, -1, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE ) ); 00295 00296 /* JSLED: report-selected? 00297 plugin_page_signals[ACCOUNT_SELECTED] = 00298 g_signal_new ("account_selected", 00299 G_OBJECT_CLASS_TYPE (object_class), 00300 G_SIGNAL_RUN_FIRST, 00301 G_STRUCT_OFFSET (GncPluginPageReportClass, account_selected), 00302 NULL, NULL, 00303 g_cclosure_marshal_VOID__POINTER, 00304 G_TYPE_NONE, 1, 00305 G_TYPE_POINTER); 00306 */ 00307 00308 // Also initialize the report name usage count table 00309 if (!static_report_printnames) 00310 static_report_printnames = g_hash_table_new_full(g_str_hash, 00311 g_str_equal, g_free, NULL); 00312 } 00313 00314 static void 00315 gnc_plugin_page_report_finalize (GObject *object) 00316 { 00317 GncPluginPageReport *page; 00318 GncPluginPageReportPrivate *priv; 00319 00320 g_return_if_fail (GNC_IS_PLUGIN_PAGE_REPORT (object)); 00321 00322 ENTER("object %p", object); 00323 page = GNC_PLUGIN_PAGE_REPORT (object); 00324 priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(page); 00325 00326 G_OBJECT_CLASS (parent_class)->finalize (object); 00327 LEAVE(" "); 00328 } 00329 00330 static 00331 GtkWidget* 00332 gnc_plugin_page_report_create_widget( GncPluginPage *page ) 00333 { 00334 GncPluginPageReport *report; 00335 GncPluginPageReportPrivate *priv; 00336 GtkWindow *topLvl; 00337 URLType type; 00338 char * id_name; 00339 char * child_name; 00340 char * url_location = NULL; 00341 char * url_label = NULL; 00342 00343 ENTER("page %p", page); 00344 00345 report = GNC_PLUGIN_PAGE_REPORT(page); 00346 priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report); 00347 00348 topLvl = GTK_WINDOW(gnc_ui_get_toplevel()); 00349 // priv->html = gnc_html_new( topLvl ); 00350 priv->html = gnc_html_factory_create_html(); 00351 gnc_html_set_parent( priv->html, topLvl ); 00352 00353 gnc_html_history_set_node_destroy_cb(gnc_html_get_history(priv->html), 00354 gnc_plugin_page_report_history_destroy_cb, 00355 (gpointer)priv); 00356 00357 priv->container = GTK_CONTAINER(gtk_frame_new(NULL)); 00358 gtk_frame_set_shadow_type(GTK_FRAME(priv->container), GTK_SHADOW_NONE); 00359 00360 gtk_container_add(GTK_CONTAINER(priv->container), 00361 gnc_html_get_widget(priv->html)); 00362 00363 priv->component_manager_id = 00364 gnc_register_gui_component(WINDOW_REPORT_CM_CLASS, NULL, 00365 close_handler, page); 00366 gnc_gui_component_set_session(priv->component_manager_id, 00367 gnc_get_current_session()); 00368 00369 gnc_html_set_urltype_cb(priv->html, gnc_plugin_page_report_check_urltype); 00370 gnc_html_set_load_cb(priv->html, gnc_plugin_page_report_load_cb, report); 00371 00372 // FIXME. This is f^-1(f(x)), isn't it? 00373 DEBUG( "id=%d", priv->reportId ); 00374 id_name = g_strdup_printf("id=%d", priv->reportId ); 00375 child_name = gnc_build_url( URL_TYPE_REPORT, id_name, NULL ); 00376 type = gnc_html_parse_url( priv->html, child_name, &url_location, &url_label); 00377 DEBUG( "passing id_name=[%s] child_name=[%s] type=[%s], location=[%s], label=[%s]", 00378 id_name, child_name ? child_name : "(null)", 00379 type ? type : "(null)", url_location ? url_location : "(null)", 00380 url_label ? url_label : "(null)" ); 00381 00382 g_free(id_name); 00383 g_free(child_name); 00384 gnc_window_set_progressbar_window( GNC_WINDOW(page->window) ); 00385 gnc_html_show_url(priv->html, type, url_location, url_label, 0); 00386 g_free(url_location); 00387 gnc_window_set_progressbar_window( NULL ); 00388 00389 g_signal_connect(priv->container, "expose_event", 00390 G_CALLBACK(gnc_plugin_page_report_expose_event_cb), report); 00391 00392 gtk_widget_show_all( GTK_WIDGET(priv->container) ); 00393 00394 LEAVE("container %p", priv->container); 00395 00396 return GTK_WIDGET( priv->container ); 00397 } 00398 00399 /******************************************************************** 00400 * gnc_plugin_page_report_check_urltype 00401 * is it OK to show a certain URLType in this window? 00402 ********************************************************************/ 00403 static int 00404 gnc_plugin_page_report_check_urltype(URLType t) 00405 { 00406 if (!safe_strcmp (t, URL_TYPE_REPORT)) 00407 { 00408 return TRUE; 00409 } 00410 else 00411 { 00412 return FALSE; 00413 } 00414 } 00415 00420 static void 00421 gnc_plugin_page_report_setup( GncPluginPage *ppage ) 00422 { 00423 GncPluginPageReport *report = GNC_PLUGIN_PAGE_REPORT(ppage); 00424 GncPluginPageReportPrivate *priv; 00425 SCM set_needs_save = scm_c_eval_string("gnc:report-set-needs-save?!"); 00426 SCM inst_report; 00427 int report_id; 00428 00429 priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report); 00430 priv->cur_report = SCM_BOOL_F; 00431 priv->initial_report = SCM_BOOL_F; 00432 priv->edited_reports = SCM_EOL; 00433 priv->name_change_cb_id = SCM_BOOL_F; 00434 00435 g_object_get( ppage, "report-id", &report_id, NULL ); 00436 00437 PINFO("report-id: %d\n", report_id); 00438 00439 /* get the inst-report from the Scheme-side hash, and get its 00440 * options and editor thunk */ 00441 if ((inst_report = gnc_report_find(report_id)) == SCM_BOOL_F) 00442 { 00443 return; 00444 } 00445 00446 if (priv->initial_report == SCM_BOOL_F) 00447 { 00448 priv->initial_report = inst_report; 00449 scm_gc_protect_object(priv->initial_report); 00450 } 00451 00452 // all reports need [to be] saved immediately after they're created. 00453 PINFO("set needs save"); 00454 scm_call_2(set_needs_save, inst_report, SCM_BOOL_T); 00455 } 00456 00457 /******************************************************************** 00458 * gnc_plugin_page_report_load_cb 00459 * called after a report is loaded into the gnc_html widget 00460 ********************************************************************/ 00461 static void 00462 //gnc_plugin_page_report_load_cb(gnc_html * html, URLType type, 00463 gnc_plugin_page_report_load_cb(GncHtml * html, URLType type, 00464 const gchar * location, const gchar * label, 00465 gpointer data) 00466 { 00467 GncPluginPageReport *report = GNC_PLUGIN_PAGE_REPORT(data); 00468 GncPluginPageReportPrivate *priv; 00469 int report_id; 00470 SCM get_options = scm_c_eval_string("gnc:report-options"); 00471 SCM set_needs_save = scm_c_eval_string("gnc:report-set-needs-save?!"); 00472 SCM inst_report; 00473 00474 ENTER( "load_cb: type=[%s], location=[%s], label=[%s]", 00475 type ? type : "(null)", location ? location : "(null)", 00476 label ? label : "(null)" ); 00477 00478 /* we get this callback if a new report is requested to be loaded OR 00479 * if any URL is clicked. If an options URL is clicked, we want to 00480 * know about it */ 00481 priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report); 00482 if (!safe_strcmp (type, URL_TYPE_REPORT) 00483 && location 00484 && (strlen(location) > 3) 00485 && !strncmp("id=", location, 3)) 00486 { 00487 report_id = atoi(location + 3); 00488 DEBUG( "parsed id=%d", report_id ); 00489 } 00490 else if (!safe_strcmp( type, URL_TYPE_OPTIONS) 00491 && location 00492 && (strlen(location) > 10) 00493 && !strncmp("report-id=", location, 10)) 00494 { 00495 report_id = atoi(location + 10); 00496 inst_report = gnc_report_find(report_id); 00497 if (inst_report != SCM_BOOL_F) 00498 { 00499 gnc_plugin_page_report_add_edited_report(priv, inst_report); 00500 } 00501 LEAVE(""); 00502 return; 00503 } 00504 else 00505 { 00506 LEAVE( " unknown URL type [%s] location [%s]", type, location ); 00507 return; 00508 } 00509 00510 /* get the inst-report from the hash, and get its 00511 * options and editor thunk */ 00512 if ((inst_report = gnc_report_find(report_id)) == SCM_BOOL_F) 00513 { 00514 LEAVE( "error getting inst_report" ); 00515 return; 00516 } 00517 00518 if (priv->initial_report == SCM_BOOL_F) 00519 { 00520 priv->initial_report = inst_report; 00521 scm_gc_protect_object(priv->initial_report); 00522 00523 DEBUG("calling set_needs_save for report with id=%d", report_id); 00524 scm_call_2(set_needs_save, inst_report, SCM_BOOL_T); 00525 00526 priv->initial_odb = gnc_option_db_new(scm_call_1(get_options, inst_report)); 00527 priv->name_change_cb_id = 00528 gnc_option_db_register_change_callback(priv->initial_odb, 00529 gnc_plugin_page_report_refresh, 00530 priv, 00531 "General", "Report name"); 00532 } 00533 00534 if ((priv->cur_report != SCM_BOOL_F) && (priv->cur_odb != NULL)) 00535 { 00536 gnc_option_db_unregister_change_callback_id(priv->cur_odb, 00537 priv->option_change_cb_id); 00538 gnc_option_db_destroy(priv->cur_odb); 00539 priv->cur_odb = NULL; 00540 } 00541 00542 if (priv->cur_report != SCM_BOOL_F) 00543 scm_gc_unprotect_object(priv->cur_report); 00544 priv->cur_report = inst_report; 00545 scm_gc_protect_object(priv->cur_report); 00546 00547 priv->cur_odb = gnc_option_db_new(scm_call_1(get_options, inst_report)); 00548 priv->option_change_cb_id = 00549 gnc_option_db_register_change_callback(priv->cur_odb, 00550 gnc_plugin_page_report_option_change_cb, 00551 report, NULL, NULL); 00552 00553 if (gnc_html_history_forward_p(gnc_html_get_history(priv->html))) 00554 { 00555 gnc_plugin_page_report_set_fwd_button(report, TRUE); 00556 } 00557 else 00558 { 00559 gnc_plugin_page_report_set_fwd_button(report, FALSE); 00560 } 00561 00562 if (gnc_html_history_back_p(gnc_html_get_history(priv->html))) 00563 { 00564 gnc_plugin_page_report_set_back_button(report, TRUE); 00565 } 00566 else 00567 { 00568 gnc_plugin_page_report_set_back_button(report, FALSE); 00569 } 00570 00571 LEAVE( "done" ); 00572 } 00573 00574 00586 static void 00587 gnc_plugin_page_report_option_change_cb(gpointer data) 00588 { 00589 GncPluginPageReport *report; 00590 GncPluginPageReportPrivate *priv; 00591 GtkActionGroup *action_group; 00592 GtkAction *action; 00593 SCM dirty_report = scm_c_eval_string("gnc:report-set-dirty?!"); 00594 const gchar *old_name; 00595 gchar *new_name; 00596 00597 g_return_if_fail(GNC_IS_PLUGIN_PAGE_REPORT(data)); 00598 report = GNC_PLUGIN_PAGE_REPORT(data); 00599 priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report); 00600 00601 DEBUG( "option_change" ); 00602 if (priv->cur_report == SCM_BOOL_F) 00603 return; 00604 DEBUG( "set-dirty, queue-draw" ); 00605 00606 /* Update the page (i.e. the notebook tab and window title) */ 00607 old_name = gnc_plugin_page_get_page_name(GNC_PLUGIN_PAGE(report)); 00608 new_name = gnc_option_db_lookup_string_option(priv->cur_odb, "General", 00609 "Report name", NULL); 00610 if (strcmp(old_name, new_name) != 0) 00611 { 00612 main_window_update_page_name(GNC_PLUGIN_PAGE(report), new_name); 00613 action_group = gnc_plugin_page_get_action_group(GNC_PLUGIN_PAGE(report)); 00614 action = gtk_action_group_get_action (action_group, "ReportSaveAction"); 00615 gtk_action_set_sensitive(action, TRUE); 00616 } 00617 g_free(new_name); 00618 00619 /* it's probably already dirty, but make sure */ 00620 scm_call_2(dirty_report, priv->cur_report, SCM_BOOL_T); 00621 00622 /* Now queue the fact that we need to reload this report */ 00623 priv->need_reload = TRUE; 00624 // jsled: this doesn't seem to cause any effect. 00625 gtk_widget_queue_draw( GTK_WIDGET(priv->container) ); 00626 // jsled: this does. 00627 gnc_html_reload( priv->html ); 00628 } 00629 00630 /* FIXME: This function does... nothing. */ 00631 static void 00632 gnc_plugin_page_report_history_destroy_cb(gnc_html_history_node * node, 00633 gpointer user_data) 00634 { 00635 #if 0 00636 static SCM remover = SCM_BOOL_F; 00637 int report_id; 00638 00639 if (remover == SCM_BOOL_F) 00640 { 00641 remover = scm_c_eval_string("gnc:report-remove-by-id"); 00642 } 00643 00644 if (node 00645 && !safe_strcmp (node->type, URL_TYPE_REPORT)\ 00646 && !strncmp("id=", node->location, 3)) 00647 { 00648 sscanf(node->location + 3, "%d", &report_id); 00649 /* printf("unreffing report %d and children\n", report_id); 00650 scm_call_1(remover, scm_int2num(report_id)); */ 00651 } 00652 else 00653 { 00654 return; 00655 } 00656 #endif 00657 } 00658 00659 /* We got a draw event. See if we need to reload the report */ 00660 static void 00661 gnc_plugin_page_report_expose_event_cb(GtkWidget *unused, GdkEventExpose *unused1, gpointer data) 00662 { 00663 GncPluginPageReport *page = data; 00664 GncPluginPageReportPrivate *priv; 00665 00666 g_return_if_fail(GNC_IS_PLUGIN_PAGE_REPORT(page)); 00667 00668 priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(page); 00669 ENTER( "report_draw" ); 00670 if (!priv->need_reload) 00671 { 00672 LEAVE( "no reload needed" ); 00673 return; 00674 } 00675 00676 priv->need_reload = FALSE; 00677 gnc_window_set_progressbar_window( GNC_WINDOW(GNC_PLUGIN_PAGE(page)->window) ); 00678 gnc_html_reload(priv->html); 00679 gnc_window_set_progressbar_window( NULL ); 00680 LEAVE( "reload forced" ); 00681 } 00682 00683 // @param data is actually GncPluginPageReportPrivate 00684 static void 00685 gnc_plugin_page_report_refresh(gpointer data) 00686 { 00687 // FIXME? 00688 DEBUG( "report-refresh called" ); 00689 // something like ... gnc_plugin_page_report_redraw( NULL, (GncPluginPageReportPrivate*)data ); 00690 return; 00691 } 00692 00693 static void 00694 gnc_plugin_page_report_destroy_widget(GncPluginPage *plugin_page) 00695 { 00696 GncPluginPageReportPrivate *priv; 00697 00698 // FIXME: cleanup other resources. 00699 00700 PINFO("destroy widget"); 00701 priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(plugin_page); 00702 00703 if (priv->component_manager_id) 00704 { 00705 gnc_unregister_gui_component(priv->component_manager_id); 00706 priv->component_manager_id = 0; 00707 } 00708 00709 gnc_plugin_page_report_destroy(priv); 00710 gnc_report_remove_by_id(priv->reportId); 00711 } 00712 00713 00716 #define SCHEME_OPTIONS "SchemeOptions" 00717 #define SCHEME_OPTIONS_N "SchemeOptions%d" 00718 00719 00729 static void 00730 gnc_plugin_page_report_save_page (GncPluginPage *plugin_page, 00731 GKeyFile *key_file, 00732 const gchar *group_name) 00733 { 00734 GncPluginPageReport *report; 00735 GncPluginPageReportPrivate *priv; 00736 SCM gen_save_text, scm_text; 00737 SCM get_embedded_list, embedded, item, tmp_report; 00738 gint count, id; 00739 gchar *text, *key_name; 00740 char * str; 00741 00742 g_return_if_fail (GNC_IS_PLUGIN_PAGE_REPORT(plugin_page)); 00743 g_return_if_fail (key_file != NULL); 00744 g_return_if_fail (group_name != NULL); 00745 00746 ENTER("page %p, key_file %p, group_name %s", plugin_page, key_file, 00747 group_name); 00748 00749 report = GNC_PLUGIN_PAGE_REPORT(plugin_page); 00750 priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report); 00751 00752 if (!priv || !priv->cur_report || scm_is_null(priv->cur_report) || 00753 SCM_UNBNDP(priv->cur_report) || SCM_BOOL_F == priv->cur_report) 00754 { 00755 LEAVE("not saving invalid report"); 00756 return; 00757 } 00758 00759 gen_save_text = scm_c_eval_string("gnc:report-generate-restore-forms"); 00760 get_embedded_list = scm_c_eval_string("gnc:report-embedded-list"); 00761 embedded = scm_call_1(get_embedded_list, priv->cur_report); 00762 count = scm_ilength(embedded); 00763 while (count-- > 0) 00764 { 00765 item = SCM_CAR(embedded); 00766 embedded = SCM_CDR(embedded); 00767 if (!scm_is_number(item)) 00768 continue; 00769 id = SCM_INUM(item); 00770 tmp_report = gnc_report_find(id); 00771 scm_text = scm_call_1(gen_save_text, tmp_report); 00772 if (!scm_is_string (scm_text)) 00773 { 00774 DEBUG("child report %d: nothing to save", id); 00775 continue; 00776 } 00777 00778 key_name = g_strdup_printf(SCHEME_OPTIONS_N, id); 00779 scm_dynwind_begin (0); 00780 str = scm_to_locale_string (scm_text); 00781 text = gnc_guile_strip_comments(str); 00782 g_key_file_set_string(key_file, group_name, key_name, text); 00783 g_free(text); 00784 scm_dynwind_free (str); 00785 scm_dynwind_end (); 00786 g_free(key_name); 00787 } 00788 00789 scm_text = scm_call_1(gen_save_text, priv->cur_report); 00790 if (!scm_is_string (scm_text)) 00791 { 00792 LEAVE("nothing to save"); 00793 return; 00794 } 00795 00796 scm_dynwind_begin (0); 00797 str = scm_to_locale_string (scm_text); 00798 text = gnc_guile_strip_comments(str); 00799 g_key_file_set_string(key_file, group_name, SCHEME_OPTIONS, text); 00800 g_free(text); 00801 scm_dynwind_free (str); 00802 scm_dynwind_end (); 00803 LEAVE(" "); 00804 } 00805 00806 00816 static GncPluginPage * 00817 gnc_plugin_page_report_recreate_page (GtkWidget *window, 00818 GKeyFile *key_file, 00819 const gchar *group_name) 00820 { 00821 GncPluginPage *page; 00822 gchar **keys; 00823 gsize i, num_keys; 00824 GError *error = NULL; 00825 gchar *option_string; 00826 gint report_id; 00827 SCM scm_id, final_id = SCM_BOOL_F; 00828 SCM report; 00829 00830 g_return_val_if_fail(key_file, NULL); 00831 g_return_val_if_fail(group_name, NULL); 00832 ENTER("key_file %p, group_name %s", key_file, group_name); 00833 00834 keys = g_key_file_get_keys(key_file, group_name, &num_keys, &error); 00835 if (error) 00836 { 00837 g_warning("error reading group %s key list: %s", 00838 group_name, error->message); 00839 g_error_free(error); 00840 LEAVE("no keys"); 00841 return NULL; 00842 } 00843 00844 for (i = 0; i < num_keys; i++) 00845 { 00846 if (strncmp(keys[i], SCHEME_OPTIONS, strlen(SCHEME_OPTIONS)) != 0) 00847 continue; 00848 option_string = g_key_file_get_string(key_file, group_name, 00849 keys[i], &error); 00850 if (error) 00851 { 00852 g_warning("error reading group %s key %s: %s", 00853 group_name, keys[i], error->message); 00854 g_error_free(error); 00855 LEAVE("bad value"); 00856 return NULL; 00857 } 00858 00859 scm_id = scm_c_eval_string(option_string); 00860 g_free(option_string); 00861 00862 if (!scm_integer_p(scm_id)) 00863 { 00864 DEBUG("report id not an integer for key %s", keys[i]); 00865 return NULL; 00866 } 00867 00868 if (final_id == SCM_BOOL_F) 00869 { 00870 if (strcmp(keys[i], SCHEME_OPTIONS) == 0) 00871 { 00872 final_id = scm_id; 00873 } 00874 } 00875 } 00876 00877 if (final_id == SCM_BOOL_F) 00878 { 00879 LEAVE("report not specified"); 00880 return NULL; 00881 } 00882 00883 report_id = scm_num2int(final_id, SCM_ARG1, G_STRFUNC); 00884 report = gnc_report_find(report_id); 00885 if (!report) 00886 { 00887 LEAVE("report doesn't exist"); 00888 return NULL; 00889 } 00890 00891 page = gnc_plugin_page_report_new( report_id ); 00892 00893 LEAVE(" "); 00894 return page; 00895 } 00896 00897 00908 static void 00909 gnc_plugin_page_report_name_changed (GncPluginPage *page, const gchar *name) 00910 { 00911 GncPluginPageReportPrivate *priv; 00912 GtkActionGroup *action_group; 00913 GtkAction *action; 00914 static gint count = 1, max_count = 10; 00915 const gchar *old_name; 00916 00917 g_return_if_fail(GNC_IS_PLUGIN_PAGE_REPORT(page)); 00918 g_return_if_fail(name != NULL); 00919 g_return_if_fail(count++ <= max_count); 00920 00921 ENTER("page %p, name %s", page, name); 00922 priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(page); 00923 00924 /* Is this a redundant call? */ 00925 old_name = gnc_option_db_lookup_string_option(priv->cur_odb, "General", 00926 "Report name", NULL); 00927 DEBUG("Comparing old name '%s' to new name '%s'", 00928 old_name ? old_name : "(null)", name); 00929 if (old_name && (strcmp(old_name, name) == 0)) 00930 { 00931 LEAVE("no change"); 00932 return; 00933 } 00934 00935 /* Store the new name for the report. */ 00936 gnc_option_db_set_string_option(priv->cur_odb, "General", 00937 "Report name", name); 00938 00939 /* Have to manually call the option change hook. */ 00940 gnc_plugin_page_report_option_change_cb(page); 00941 00942 /* Careful. This is called at report construction time. */ 00943 action_group = gnc_plugin_page_get_action_group(page); 00944 if (action_group) 00945 { 00946 /* Allow the user to save the report now. */ 00947 action = gtk_action_group_get_action (action_group, "ReportSaveAction"); 00948 gtk_action_set_sensitive(action, TRUE); 00949 } 00950 LEAVE(" "); 00951 } 00952 00953 static void 00954 gnc_plugin_page_report_update_edit_menu (GncPluginPage *page, gboolean hide) 00955 { 00956 GtkAction *action; 00957 00958 action = gnc_plugin_page_get_action (page, "EditCopyAction"); 00959 gtk_action_set_sensitive (action, TRUE); 00960 gtk_action_set_visible (action, TRUE); 00961 action = gnc_plugin_page_get_action (page, "EditCutAction"); 00962 gtk_action_set_sensitive (action, FALSE); 00963 gtk_action_set_visible (action, !hide); 00964 action = gnc_plugin_page_get_action (page, "EditPasteAction"); 00965 gtk_action_set_sensitive (action, FALSE); 00966 gtk_action_set_visible (action, !hide); 00967 } 00968 00969 static gboolean 00970 gnc_plugin_page_report_finish_pending (GncPluginPage *page) 00971 { 00972 GncPluginPageReportPrivate *priv; 00973 GncPluginPageReport *report; 00974 00975 report = GNC_PLUGIN_PAGE_REPORT(page); 00976 priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report); 00977 return !priv->reloading; 00978 } 00979 00980 00981 /******************************************************************** 00982 * gnc_report_window_destroy 00983 * free and destroy a window 00984 ********************************************************************/ 00985 void 00986 gnc_plugin_page_report_destroy(GncPluginPageReportPrivate * priv) 00987 { 00988 SCM get_editor = scm_c_eval_string("gnc:report-editor-widget"); 00989 SCM set_editor = scm_c_eval_string("gnc:report-set-editor-widget!"); 00990 SCM edited, editor; 00991 00992 /* close any open editors */ 00993 for (edited = scm_list_copy(priv->edited_reports); !scm_is_null(edited); 00994 edited = SCM_CDR(edited)) 00995 { 00996 editor = scm_call_1(get_editor, SCM_CAR(edited)); 00997 scm_call_2(set_editor, SCM_CAR(edited), SCM_BOOL_F); 00998 if (editor != SCM_BOOL_F) 00999 { 01000 GtkWidget *w = NULL; 01001 #define FUNC_NAME "gtk_widget_destroy" 01002 w = SWIG_MustGetPtr(editor, 01003 SWIG_TypeQuery("_p_GtkWidget"), 1, 0); 01004 #undef FUNC_NAME 01005 gtk_widget_destroy(GTK_WIDGET(w)); 01006 } 01007 } 01008 01009 if (priv->initial_odb) 01010 { 01011 gnc_option_db_unregister_change_callback_id(priv->initial_odb, 01012 priv->name_change_cb_id); 01013 01014 gnc_option_db_destroy(priv->initial_odb); 01015 priv->initial_odb = NULL; 01016 } 01017 01018 gnc_html_destroy(priv->html); 01019 01020 priv->container = NULL; 01021 priv->html = NULL; 01022 01023 if (priv->cur_report != SCM_BOOL_F) 01024 scm_gc_unprotect_object(priv->cur_report); 01025 if (priv->edited_reports != SCM_EOL) 01026 scm_gc_unprotect_object(priv->edited_reports); 01027 } 01028 01029 static GtkActionEntry report_actions[] = 01030 { 01031 { 01032 "FilePrintAction", GTK_STOCK_PRINT, N_("_Print Report..."), "<control>p", 01033 N_("Print the current report"), 01034 G_CALLBACK(gnc_plugin_page_report_print_cb) 01035 }, 01036 { 01037 "FilePrintPDFAction", GNC_STOCK_PDF_EXPORT, N_("Export as P_DF..."), NULL, 01038 N_("Export the current report as a PDF document"), 01039 G_CALLBACK(gnc_plugin_page_report_exportpdf_cb) 01040 }, 01041 { 01042 "EditCutAction", GTK_STOCK_CUT, N_("Cu_t"), NULL, 01043 N_("Cut the current selection and copy it to clipboard"), 01044 NULL 01045 }, 01046 { 01047 "EditCopyAction", GTK_STOCK_COPY, N_("_Copy"), NULL, 01048 N_("Copy the current selection to clipboard"), 01049 G_CALLBACK(gnc_plugin_page_report_copy_cb) 01050 }, 01051 { 01052 "EditPasteAction", GTK_STOCK_PASTE, N_("_Paste"), NULL, 01053 N_("Paste the clipboard content at the cursor position"), 01054 NULL 01055 }, 01056 { 01057 "ViewRefreshAction", GTK_STOCK_REFRESH, N_("_Refresh"), "<control>r", 01058 N_("Refresh this window"), 01059 G_CALLBACK (gnc_plugin_page_report_reload_cb) 01060 }, 01061 { 01062 "ReportSaveAction", GTK_STOCK_SAVE, N_("Add _Report"), "", 01063 N_("Add the current report to the `Custom' menu for later use. " 01064 "The report will be saved in the file ~/.gnucash/saved-reports-2.4. " 01065 "It will be accessible as menu entry in the report menu at the " 01066 "next startup of GnuCash."), 01067 G_CALLBACK(gnc_plugin_page_report_save_cb) 01068 }, 01069 { 01070 "ReportExportAction", GTK_STOCK_CONVERT, N_("Export _Report"), NULL, 01071 N_("Export HTML-formatted report to file"), 01072 G_CALLBACK(gnc_plugin_page_report_export_cb) 01073 }, 01074 { 01075 "ReportOptionsAction", GTK_STOCK_PROPERTIES, N_("_Report Options"), NULL, 01076 N_("Edit report options"), 01077 G_CALLBACK(gnc_plugin_page_report_options_cb) 01078 }, 01079 01080 { 01081 "ReportBackAction", GTK_STOCK_GO_BACK, N_("Back"), NULL, 01082 N_("Move back one step in the history"), 01083 G_CALLBACK(gnc_plugin_page_report_back_cb) 01084 }, 01085 { 01086 "ReportForwAction", GTK_STOCK_GO_FORWARD, N_("Forward"), NULL, 01087 N_("Move forward one step in the history"), 01088 G_CALLBACK(gnc_plugin_page_report_forw_cb) 01089 }, 01090 { 01091 "ReportReloadAction", GTK_STOCK_REFRESH, N_("Reload"), NULL, 01092 N_("Reload the current page"), 01093 G_CALLBACK(gnc_plugin_page_report_reload_cb) 01094 }, 01095 { 01096 "ReportStopAction", GTK_STOCK_STOP, N_("Stop"), NULL, 01097 N_("Cancel outstanding HTML requests"), 01098 G_CALLBACK(gnc_plugin_page_report_stop_cb) 01099 }, 01100 }; 01101 static guint num_report_actions = G_N_ELEMENTS( report_actions ); 01102 01104 static action_toolbar_labels toolbar_labels[] = 01105 { 01106 { "FilePrintAction", N_("Print") }, 01107 { "ReportExportAction", N_("Export") }, 01108 { "ReportOptionsAction", N_("Options") }, 01109 { NULL, NULL }, 01110 }; 01111 01112 static const gchar *initially_insensitive_actions[] = 01113 { 01114 "ReportSaveAction", 01115 NULL 01116 }; 01117 01118 static void 01119 gnc_plugin_page_report_init ( GncPluginPageReport *plugin_page ) 01120 { 01121 } 01122 01123 static GObject* 01124 gnc_plugin_page_report_constructor(GType this_type, guint n_properties, GObjectConstructParam *properties) 01125 { 01126 GObject *obj; 01127 GncPluginPageReportClass *our_class; 01128 GObjectClass *parent_class; 01129 gint reportId = -42; 01130 int i; 01131 01132 our_class = GNC_PLUGIN_PAGE_REPORT_CLASS ( 01133 g_type_class_peek (GNC_TYPE_PLUGIN_PAGE_REPORT)); 01134 parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (our_class)); 01135 obj = parent_class->constructor(this_type, n_properties, properties); 01136 01137 for (i = 0; i < n_properties; i++) 01138 { 01139 GObjectConstructParam prop = properties[i]; 01140 if (strcmp(prop.pspec->name, "report-id") == 0) 01141 { 01142 reportId = g_value_get_int(prop.value); 01143 } 01144 } 01145 01146 gnc_plugin_page_report_constr_init(GNC_PLUGIN_PAGE_REPORT(obj), reportId); 01147 01148 return obj; 01149 } 01150 01151 static void 01152 gnc_plugin_page_report_constr_init(GncPluginPageReport *plugin_page, gint reportId) 01153 { 01154 GncPluginPageReportPrivate *priv; 01155 GtkActionGroup *action_group; 01156 GncPluginPage *parent; 01157 gboolean use_new; 01158 gchar *name; 01159 01160 DEBUG( "property reportId=%d", reportId ); 01161 priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(plugin_page); 01162 priv->reportId = reportId; 01163 01164 gnc_plugin_page_report_setup( GNC_PLUGIN_PAGE(plugin_page) ); 01165 01166 /* Init parent declared variables */ 01167 parent = GNC_PLUGIN_PAGE(plugin_page); 01168 use_new = gnc_gconf_get_bool(GCONF_GENERAL_REPORT, KEY_USE_NEW, NULL); 01169 name = gnc_report_name( priv->initial_report ); 01170 g_object_set(G_OBJECT(plugin_page), 01171 "page-name", name, 01172 "page-uri", "default:", 01173 "ui-description", "gnc-plugin-page-report-ui.xml", 01174 "use-new-window", use_new, 01175 NULL); 01176 g_free(name); 01177 01178 /* change me when the system supports multiple books */ 01179 gnc_plugin_page_add_book(parent, gnc_get_current_book()); 01180 01181 /* Create menu and toolbar information */ 01182 action_group = 01183 gnc_plugin_page_create_action_group(parent, 01184 "GncPluginPageReportActions"); 01185 gtk_action_group_add_actions( action_group, 01186 report_actions, 01187 num_report_actions, 01188 plugin_page ); 01189 gnc_plugin_update_actions(action_group, 01190 initially_insensitive_actions, 01191 "sensitive", FALSE); 01192 gnc_plugin_init_short_names (action_group, toolbar_labels); 01193 } 01194 01195 GncPluginPage* 01196 gnc_plugin_page_report_new( int reportId ) 01197 { 01198 GncPluginPageReport *plugin_page; 01199 01200 DEBUG( "report id = %d", reportId ); 01201 plugin_page = g_object_new( GNC_TYPE_PLUGIN_PAGE_REPORT, 01202 "report-id", reportId, NULL ); 01203 DEBUG( "plugin_page: %p", plugin_page ); 01204 DEBUG( "set %d on page %p", reportId, plugin_page ); 01205 return GNC_PLUGIN_PAGE( plugin_page ); 01206 } 01207 01208 void 01209 gnc_plugin_page_report_remove_edited_report(GncPluginPageReportPrivate *priv, 01210 SCM report) 01211 { 01212 SCM new_edited = scm_delete(priv->edited_reports, report); 01213 if (priv->edited_reports != SCM_EOL) 01214 scm_gc_unprotect_object(priv->edited_reports); 01215 priv->edited_reports = new_edited; 01216 if (new_edited != SCM_EOL) 01217 scm_gc_protect_object(priv->edited_reports); 01218 } 01219 01220 void 01221 gnc_plugin_page_report_add_edited_report(GncPluginPageReportPrivate *priv, 01222 SCM report) 01223 { 01224 SCM new_edited = scm_cons(report, priv->edited_reports); 01225 if (priv->edited_reports != SCM_EOL) 01226 scm_gc_unprotect_object(priv->edited_reports); 01227 priv->edited_reports = new_edited; 01228 if (new_edited != SCM_EOL) 01229 scm_gc_protect_object(priv->edited_reports); 01230 } 01231 01232 void 01233 gnc_plugin_page_report_raise_editor(SCM report) 01234 { 01235 SCM get_editor = scm_c_eval_string("gnc:report-editor-widget"); 01236 SCM editor = scm_call_1(get_editor, report); 01237 #define FUNC_NAME "gtk_window_present" 01238 GtkWidget *w = SWIG_MustGetPtr(editor, 01239 SWIG_TypeQuery("_p_GtkWidget"), 1, 0); 01240 #undef FUNC_NAME 01241 gtk_window_present(GTK_WINDOW(w)); 01242 } 01243 01244 static void 01245 close_handler (gpointer user_data) 01246 { 01247 GncPluginPage *plugin_page = GNC_PLUGIN_PAGE(user_data); 01248 DEBUG("in close handler\n"); 01249 gnc_main_window_close_page (plugin_page); 01250 } 01251 01252 static void 01253 gnc_plugin_page_report_set_fwd_button(GncPluginPageReport *report, int enabled) 01254 { 01255 GtkAction *act; 01256 01257 act = gnc_plugin_page_get_action(GNC_PLUGIN_PAGE(report), 01258 "ReportForwAction" ); 01259 gtk_action_set_sensitive(act, enabled); 01260 } 01261 01262 static void 01263 gnc_plugin_page_report_set_back_button(GncPluginPageReport *report, int enabled) 01264 { 01265 GtkAction *act; 01266 01267 act = gnc_plugin_page_get_action(GNC_PLUGIN_PAGE(report), 01268 "ReportBackAction" ); 01269 gtk_action_set_sensitive(act, enabled); 01270 } 01271 01272 // ------------------------------------------------------------ 01273 // GTK ACTION CALLBACKS 01274 01275 static void 01276 gnc_plugin_page_report_forw_cb( GtkAction *action, GncPluginPageReport *report ) 01277 { 01278 GncPluginPageReportPrivate *priv; 01279 gnc_html_history_node * node = NULL; 01280 01281 DEBUG( "forw" ); 01282 priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report); 01283 gnc_html_history_forward(gnc_html_get_history(priv->html)); 01284 node = gnc_html_history_get_current(gnc_html_get_history(priv->html)); 01285 if (node) 01286 { 01287 gnc_html_show_url(priv->html, node->type, node->location, 01288 node->label, 0); 01289 } 01290 } 01291 01292 static void 01293 gnc_plugin_page_report_back_cb( GtkAction *action, GncPluginPageReport *report ) 01294 { 01295 GncPluginPageReportPrivate *priv; 01296 gnc_html_history_node * node; 01297 01298 DEBUG( "back" ); 01299 priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report); 01300 gnc_html_history_back(gnc_html_get_history(priv->html)); 01301 node = gnc_html_history_get_current(gnc_html_get_history(priv->html)); 01302 if (node) 01303 { 01304 gnc_html_show_url(priv->html, node->type, node->location, 01305 node->label, 0); 01306 } 01307 } 01308 01309 static void 01310 gnc_plugin_page_report_reload_cb( GtkAction *action, GncPluginPageReport *report ) 01311 { 01312 GncPluginPageReportPrivate *priv; 01313 SCM dirty_report; 01314 01315 DEBUG( "reload" ); 01316 priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report); 01317 if (priv->cur_report == SCM_BOOL_F) 01318 return; 01319 01320 DEBUG( "reload-redraw" ); 01321 dirty_report = scm_c_eval_string("gnc:report-set-dirty?!"); 01322 scm_call_2(dirty_report, priv->cur_report, SCM_BOOL_T); 01323 01324 priv->need_reload = TRUE; 01325 /* now queue the fact that we need to reload this report */ 01326 01327 // this doens't seem to do anything... 01328 gtk_widget_queue_draw( GTK_WIDGET(priv->container) ); 01329 01330 // this does... 01331 priv->reloading = TRUE; 01332 gnc_html_reload( priv->html ); 01333 priv->reloading = FALSE; 01334 } 01335 01336 static void 01337 gnc_plugin_page_report_stop_cb( GtkAction *action, GncPluginPageReport *report ) 01338 { 01339 GncPluginPageReportPrivate *priv; 01340 01341 priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report); 01342 gnc_html_cancel(priv->html); 01343 } 01344 01345 /* Returns SCM_BOOL_F if cancel. Returns SCM_BOOL_T if html. 01346 * Otherwise returns pair from export_types. */ 01347 static SCM 01348 gnc_get_export_type_choice (SCM export_types) 01349 { 01350 GList * choices = NULL; 01351 gboolean bad = FALSE; 01352 GList * node; 01353 int choice; 01354 SCM tail; 01355 01356 if (!scm_is_list (export_types)) 01357 return SCM_BOOL_F; 01358 01359 for (tail = export_types; !scm_is_null (tail); tail = SCM_CDR (tail)) 01360 { 01361 SCM pair = SCM_CAR (tail); 01362 char * name; 01363 SCM scm; 01364 01365 if (!scm_is_pair (pair)) 01366 { 01367 g_warning ("unexpected list element"); 01368 bad = TRUE; 01369 break; 01370 } 01371 01372 scm = SCM_CAR (pair); 01373 if (!scm_is_string (scm)) 01374 { 01375 g_warning ("unexpected pair element"); 01376 bad = TRUE; 01377 break; 01378 } 01379 01380 scm_dynwind_begin (0); 01381 name = scm_to_locale_string (scm); 01382 choices = g_list_prepend (choices, g_strdup (name)); 01383 scm_dynwind_free (name); 01384 scm_dynwind_end (); 01385 } 01386 01387 if (!bad) 01388 { 01389 choices = g_list_reverse (choices); 01390 01391 choices = g_list_prepend (choices, g_strdup (_("HTML"))); 01392 01393 choice = gnc_choose_radio_option_dialog 01394 (NULL, _("Choose export format"), 01395 _("Choose the export format for this report:"), 01396 NULL, 0, choices); 01397 } 01398 else 01399 choice = -1; 01400 01401 for (node = choices; node; node = node->next) 01402 g_free (node->data); 01403 g_list_free (choices); 01404 01405 if (choice < 0) 01406 return SCM_BOOL_F; 01407 01408 if (choice == 0) 01409 return SCM_BOOL_T; 01410 01411 choice--; 01412 if (choice >= scm_ilength (export_types)) 01413 return SCM_BOOL_F; 01414 01415 return scm_list_ref (export_types, scm_int2num (choice)); 01416 } 01417 01418 static char * 01419 gnc_get_export_filename (SCM choice) 01420 { 01421 char * filepath; 01422 struct stat statbuf; 01423 char * title; 01424 const gchar * html_type = _("HTML"); 01425 char * type; 01426 int rc; 01427 char * default_dir; 01428 01429 if (choice == SCM_BOOL_T) 01430 type = g_strdup (html_type); 01431 else 01432 { 01433 char * str; 01434 scm_dynwind_begin (0); 01435 str = scm_to_locale_string(SCM_CAR (choice)); 01436 type = g_strdup (str); 01437 scm_dynwind_free (str); 01438 scm_dynwind_end (); 01439 } 01440 01441 /* %s is the type of what is about to be saved, e.g. "HTML". */ 01442 title = g_strdup_printf (_("Save %s To File"), type); 01443 default_dir = gnc_get_default_directory(GCONF_DIR_REPORT); 01444 01445 filepath = gnc_file_dialog (title, NULL, default_dir, GNC_FILE_DIALOG_EXPORT); 01446 01447 /* Try to test for extension on file name, add if missing */ 01448 if (g_strrstr(filepath, ".") == NULL) 01449 filepath = g_strconcat(filepath, ".", g_ascii_strdown(type, strlen(type)), NULL); 01450 01451 g_free (type); 01452 g_free (title); 01453 g_free (default_dir); 01454 01455 if (!filepath) 01456 return NULL; 01457 01458 default_dir = g_path_get_dirname(filepath); 01459 gnc_set_default_directory (GCONF_DIR_REPORT, default_dir); 01460 g_free(default_dir); 01461 01462 rc = g_stat (filepath, &statbuf); 01463 01464 /* Check for an error that isn't a non-existent file. */ 01465 if (rc != 0 && errno != ENOENT) 01466 { 01467 /* %s is the strerror(3) string of the error that occurred. */ 01468 const char *format = _("You cannot save to that filename.\n\n%s"); 01469 01470 gnc_error_dialog (NULL, format, strerror(errno)); 01471 g_free(filepath); 01472 return NULL; 01473 } 01474 01475 /* Check for a file that isn't a regular file. */ 01476 if (rc == 0 && !S_ISREG (statbuf.st_mode)) 01477 { 01478 const char *message = _("You cannot save to that file."); 01479 01480 gnc_error_dialog (NULL, "%s", message); 01481 g_free(filepath); 01482 return NULL; 01483 } 01484 01485 if (rc == 0) 01486 { 01487 const char *format = _("The file %s already exists. " 01488 "Are you sure you want to overwrite it?"); 01489 01490 if (!gnc_verify_dialog (NULL, FALSE, format, filepath)) 01491 { 01492 g_free(filepath); 01493 return NULL; 01494 } 01495 } 01496 01497 return filepath; 01498 } 01499 01500 static void 01501 gnc_plugin_page_report_save_cb( GtkAction *action, GncPluginPageReport *report ) 01502 { 01503 GncPluginPageReportPrivate *priv; 01504 SCM save_func; 01505 01506 priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report); 01507 if (priv->cur_report == SCM_BOOL_F) 01508 return; 01509 01510 save_func = scm_c_eval_string("gnc:report-save-to-savefile"); 01511 scm_call_1(save_func, priv->cur_report); 01512 01513 { 01514 GtkActionGroup *action_group = 01515 gnc_plugin_page_get_action_group(GNC_PLUGIN_PAGE(report)); 01516 GtkAction *action = 01517 gtk_action_group_get_action (action_group, "ReportSaveAction"); 01518 gtk_action_set_sensitive(action, FALSE); 01519 } 01520 } 01521 01522 static void 01523 gnc_plugin_page_report_export_cb( GtkAction *action, GncPluginPageReport *report ) 01524 { 01525 GncPluginPageReportPrivate *priv; 01526 char * filepath; 01527 SCM export_types; 01528 SCM export_thunk; 01529 gboolean result; 01530 SCM choice; 01531 01532 priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report); 01533 export_types = scm_call_1 (scm_c_eval_string ("gnc:report-export-types"), 01534 priv->cur_report); 01535 01536 export_thunk = scm_call_1 (scm_c_eval_string ("gnc:report-export-thunk"), 01537 priv->cur_report); 01538 01539 if (scm_is_list (export_types) && scm_is_procedure (export_thunk)) 01540 choice = gnc_get_export_type_choice (export_types); 01541 else 01542 choice = SCM_BOOL_T; 01543 01544 if (choice == SCM_BOOL_F) 01545 return; 01546 01547 filepath = gnc_get_export_filename (choice); 01548 if (!filepath) 01549 return; 01550 01551 if (scm_is_pair (choice)) 01552 { 01553 SCM file_scm; 01554 SCM res; 01555 01556 choice = SCM_CDR (choice); 01557 file_scm = scm_makfrom0str (filepath); 01558 01559 res = scm_call_3 (export_thunk, priv->cur_report, choice, file_scm); 01560 01561 result = (res != SCM_BOOL_F); 01562 } 01563 else 01564 result = gnc_html_export_to_file (priv->html, filepath); 01565 01566 if (!result) 01567 { 01568 const char *fmt = _("Could not open the file %s. " 01569 "The error is: %s"); 01570 gnc_error_dialog( NULL, fmt, filepath ? filepath : "(null)", 01571 strerror (errno) ? strerror (errno) : "" ); 01572 } 01573 01574 g_free(filepath); 01575 return; 01576 } 01577 01578 static void 01579 error_handler(const char *str) 01580 { 01581 PWARN("Report Error: %s", str); 01582 } 01583 01584 static void 01585 gnc_plugin_page_report_options_cb( GtkAction *action, GncPluginPageReport *report ) 01586 { 01587 GncPluginPageReportPrivate *priv; 01588 SCM start_editor = scm_c_eval_string("gnc:report-edit-options"); 01589 SCM result; 01590 01591 priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report); 01592 if (priv->cur_report == SCM_BOOL_F) 01593 return; 01594 01595 result = gfec_apply(start_editor, scm_cons(priv->cur_report, SCM_EOL), 01596 error_handler); 01597 if (result == SCM_BOOL_F || result == SCM_UNDEFINED) 01598 { 01599 gnc_warning_dialog(GTK_WIDGET(gnc_ui_get_toplevel()), "%s", 01600 _("There are no options for this report.")); 01601 } 01602 else 01603 { 01604 gnc_plugin_page_report_add_edited_report(priv, priv->cur_report); 01605 } 01606 } 01607 01608 static GncInvoice *lookup_invoice(GncPluginPageReportPrivate *priv) 01609 { 01610 g_assert(priv); 01611 return gnc_option_db_lookup_invoice_option(priv->cur_odb, "General", 01612 "Invoice Number", NULL); 01613 } 01614 01615 #define GCONF_GENERAL_REPORT_PDFEXPORT GCONF_GENERAL_REPORT "/pdf_export" 01616 01617 static gchar *report_create_jobname(GncPluginPageReportPrivate *priv) 01618 { 01619 gchar *job_name = NULL; 01620 gchar *report_name = NULL; 01621 const gchar *report_number = ""; 01622 gchar *job_date; 01623 const gchar *default_jobname = N_("GnuCash-Report"); 01624 01625 g_assert(priv); 01626 01627 { 01628 // Look up the date format that was chosen in the gconf registry 01629 QofDateFormat date_format_here; 01630 QofDateFormat date_format_old = qof_date_format_get(); 01631 char *format_code = gnc_gconf_get_string(GCONF_GENERAL_REPORT_PDFEXPORT, 01632 "filename_date_format", NULL); 01633 01634 if (format_code == NULL) 01635 { 01636 format_code = g_strdup("locale"); 01637 g_warning("No gconf key found for " GCONF_GENERAL_REPORT_PDFEXPORT 01638 "/filename_date_format, using default %s", format_code); 01639 } 01640 if (*format_code == '\0') 01641 { 01642 g_free(format_code); 01643 format_code = g_strdup("locale"); 01644 } 01645 01646 if (gnc_date_string_to_dateformat(format_code, &date_format_here)) 01647 { 01648 PERR("Incorrect date format code"); 01649 if (format_code != NULL) 01650 free(format_code); 01651 } 01652 01653 // To apply this chosen date format, temporarily switch the 01654 // process-wide default to our chosen date format. Note: It is a 01655 // totally brain-dead implementation of qof_print_date() to not offer a 01656 // variation where the QofDateFormat can be passed as an argument. 01657 // Hrmpf. 01658 qof_date_format_set(date_format_here); 01659 01660 job_date = qof_print_date( time( NULL ) ); 01661 01662 // Restore to the original general date format 01663 qof_date_format_set(date_format_old); 01664 } 01665 01666 01667 if (priv->cur_report == SCM_BOOL_F) 01668 report_name = g_strdup (_(default_jobname)); 01669 else 01670 { 01671 /* Gather some information from the report to generate a 01672 * decent print job name. 01673 * FIXME: this is a bit of a hack. It would be better if each 01674 * report had a hidden job name option, because the 01675 * generic reporting code shouldn't know what makes 01676 * a decent job name for each report. 01677 * 01678 * Also, the "Report name" for an invoice report is 01679 * "Printable Invoice", which is not what the user wants to see, 01680 * so I added yet another hack below for this. cstim. 01681 */ 01682 GncInvoice *invoice; 01683 report_name = gnc_option_db_lookup_string_option(priv->cur_odb, "General", 01684 "Report name", NULL); 01685 if (!report_name) 01686 report_name = g_strdup (_(default_jobname)); 01687 if (g_strcmp0(report_name, _("Printable Invoice")) == 0 01688 || g_strcmp0(report_name, _("Tax Invoice")) == 0 01689 || g_strcmp0(report_name, _("Easy Invoice")) == 0 01690 || g_strcmp0(report_name, _("Fancy Invoice")) == 0) 01691 { 01692 /* Again HACK alert: We modify this single known string here into 01693 * something more appropriate. */ 01694 g_free(report_name); 01695 report_name = g_strdup(_("Invoice")); 01696 } 01697 01698 invoice = lookup_invoice(priv); 01699 if (invoice) 01700 { 01701 // Report is for an invoice. Hence, we get a number of the invoice. 01702 report_number = gncInvoiceGetID(invoice); 01703 } 01704 } 01705 01706 if (report_name && job_date) 01707 { 01708 // Look up the sprintf format of the output name from the gconf registry 01709 char* format = gnc_gconf_get_string(GCONF_GENERAL_REPORT_PDFEXPORT, "filename_format", NULL); 01710 if (!format) 01711 { 01712 // Fallback name format in case the gconf does not contain this key 01713 format = g_strdup("%s_%s_%s"); 01714 g_warning("No gconf key found for " GCONF_GENERAL_REPORT_PDFEXPORT "/filename_format, using default %s", format); 01715 } 01716 01717 job_name = g_strdup_printf(format, report_name, report_number, job_date); 01718 01719 g_free(format); 01720 } 01721 g_free (report_name); 01722 g_free (job_date); 01723 01724 { 01725 char forbidden_char = '/'; 01726 // Now remove the characters that are not allowed in file 01727 // names. FIXME: Check for all disallowed characters here! 01728 while (strchr(job_name, forbidden_char)) 01729 { 01730 *strchr(job_name, forbidden_char) = '_'; 01731 } 01732 } 01733 01734 { 01735 /* And one final checking issue: We want to avoid allocating 01736 * the same name twice for a saved PDF. Hence, we keep a 01737 * GHashTable with the usage count of existing output 01738 * names. (Because I'm lazy, I just use a static GHashTable 01739 * for this.) */ 01740 gpointer value; 01741 gboolean already_found; 01742 g_assert(static_report_printnames); 01743 01744 // Lookup the existing usage count 01745 value = g_hash_table_lookup(static_report_printnames, job_name); 01746 already_found = (value != NULL); 01747 if (!value) 01748 { 01749 value = GINT_TO_POINTER(0); 01750 } 01751 01752 // Increment the stored usage count 01753 value = GINT_TO_POINTER(1 + GPOINTER_TO_INT(value)); 01754 // and store it again 01755 g_hash_table_insert(static_report_printnames, g_strdup(job_name), value); 01756 01757 // If the previous usage count was more than 0, append the current 01758 // count (which is now 2 or higher) to the resulting name 01759 if (already_found) 01760 { 01761 // The name was already in use, so modify the name again 01762 gchar *tmp = g_strdup_printf("%s_%d", job_name, (int) GPOINTER_TO_INT(value)); 01763 g_free(job_name); 01764 job_name = tmp; 01765 } 01766 } 01767 01768 return job_name; 01769 } 01770 01771 static void 01772 gnc_plugin_page_report_print_cb( GtkAction *action, GncPluginPageReport *report ) 01773 { 01774 GncPluginPageReportPrivate *priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report); 01775 gchar *job_name = report_create_jobname(priv); 01776 01777 //g_warning("Setting job name=%s", job_name); 01778 01779 gnc_html_print(priv->html, job_name, FALSE); 01780 01781 g_free (job_name); 01782 } 01783 01784 #define KVP_OWNER_EXPORT_PDF_DIRNAME "export-pdf-directory" 01785 01786 static void 01787 gnc_plugin_page_report_exportpdf_cb( GtkAction *action, GncPluginPageReport *report ) 01788 { 01789 GncPluginPageReportPrivate *priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report); 01790 gchar *job_name = report_create_jobname(priv); 01791 GncInvoice *invoice; 01792 GncOwner *owner = NULL; 01793 KvpFrame *kvp = NULL; 01794 01795 // Do we have an invoice report? 01796 invoice = lookup_invoice(priv); 01797 if (invoice) 01798 { 01799 // Does this invoice also have an owner? 01800 owner = (GncOwner*) gncInvoiceGetOwner(invoice); 01801 if (owner) 01802 { 01803 // Yes. In the kvp, look up the key for the Export-PDF output 01804 // directory. If it exists, prepend this to the job name so that 01805 // we can export to PDF. 01806 kvp = gncOwnerGetSlots(owner); 01807 if (kvp) 01808 { 01809 const char *dirname = kvp_frame_get_string(kvp, KVP_OWNER_EXPORT_PDF_DIRNAME); 01810 if (dirname && g_file_test(dirname, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) 01811 { 01812 gchar *tmp = g_build_filename(dirname, job_name, NULL); 01813 g_free(job_name); 01814 job_name = tmp; 01815 } 01816 } 01817 } 01818 } 01819 01820 //g_warning("Setting job name=%s", job_name); 01821 01822 gnc_html_print(priv->html, job_name, TRUE); 01823 01824 if (owner && kvp) 01825 { 01826 // As this is an invoice report with some owner, we will try to look up the 01827 // chosen output directory from the print settings and store it again in the owner kvp. 01828 GtkPrintSettings *print_settings = gnc_print_get_settings(); 01829 if (print_settings && gtk_print_settings_has_key(print_settings, GNC_GTK_PRINT_SETTINGS_EXPORT_DIR)) 01830 { 01831 const char* dirname = gtk_print_settings_get(print_settings, 01832 GNC_GTK_PRINT_SETTINGS_EXPORT_DIR); 01833 // Only store the directory if it exists. 01834 if (g_file_test(dirname, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) 01835 { 01836 QofInstance *qofinstance = qofOwnerGetOwner(owner); 01837 //gncOwnerBeginEdit(owner); 01838 kvp_frame_set_string(kvp, KVP_OWNER_EXPORT_PDF_DIRNAME, dirname); 01839 if (qofinstance) 01840 qof_instance_set_dirty(qofinstance); 01841 // shoot... there is no such thing as: gncOwnerCommitEdit(owner); 01842 } 01843 } 01844 } 01845 01846 g_free (job_name); 01847 } 01848 01849 static void 01850 gnc_plugin_page_report_copy_cb(GtkAction *action, GncPluginPageReport *report) 01851 { 01852 GncPluginPageReportPrivate *priv; 01853 01854 priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report); 01855 gnc_html_copy_to_clipboard(priv->html); 01856 } 01857 01858 /******************************************************************** 01859 * gnc_main_window_open_report() 01860 * open an report in a top level window from an ID number 01861 ********************************************************************/ 01862 01863 void 01864 gnc_main_window_open_report(int report_id, GncMainWindow *window) 01865 { 01866 GncPluginPage *reportPage; 01867 01868 if (window) 01869 g_return_if_fail(GNC_IS_MAIN_WINDOW(window)); 01870 01871 reportPage = gnc_plugin_page_report_new( report_id ); 01872 gnc_main_window_open_page( window, reportPage ); 01873 } 01874 01875 void 01876 gnc_main_window_open_report_url(const char * url, GncMainWindow *window) 01877 { 01878 GncPluginPage *reportPage; 01879 01880 DEBUG( "report url: [%s]\n", url ); 01881 01882 if (window) 01883 g_return_if_fail(GNC_IS_MAIN_WINDOW(window)); 01884 01885 reportPage = gnc_plugin_page_report_new( 42 /* url? */ ); 01886 gnc_main_window_open_page( window, reportPage ); 01887 } 01888
1.7.4