GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
dialog-custom-report.c
1 /**************************************************************************\
2  * dialog-custom-report.c -- dialog for managing custom reports *
3  * *
4  * Copyright (C) 2009 Andrew Sackville-West ([email protected]) *
5  * *
6  * This program is free software; you can redistribute it and/or *
7  * modify it under the terms of the GNU General Public License as *
8  * published by the Free Software Foundation; either version 2 of *
9  * the License, or (at your option) any later version. *
10  * *
11  * This program is distributed in the hope that it will be useful, *
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14  * GNU General Public License for more details. *
15  * *
16  * You should have received a copy of the GNU General Public License *
17  * along with this program; if not, contact: *
18  * *
19  * Free Software Foundation Voice: +1-617-542-5942 *
20  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
21  * Boston, MA 02110-1301, USA [email protected] *
22 \*************************************************************************/
23 
24 #include "config.h"
25 
26 #include <glib/gi18n.h>
27 #include <gtk/gtk.h>
28 #include <libguile.h>
29 #include "swig-runtime.h"
30 
31 #include "dialog-custom-report.h"
32 #include "dialog-options.h"
33 #include "dialog-utils.h"
34 #include "gnc-main-window.h"
35 #include "option-util.h"
36 #include "window-report.h"
37 #include "guile-mappings.h"
38 #include "gnc-guile-utils.h"
39 #include "gnc-gui-query.h"
40 #include "gnc-ui.h"
41 #include "gnc-report.h"
42 #include "gnc-plugin-page-report.h"
43 
44 #define GNC_PREFS_GROUP_REPORT_SAVED_CONFIGS "dialogs.report-saved-configs"
45 
46 /* convenience for accessing columns in the GtkListStore that holds
47  the reports */
48 enum
49 {
50  COL_NAME = 0,
51  COL_NUM,
52  NUM_COLS
53 };
54 
55 enum
56 {
57  VIEW_COL_NAME = 0,
58  VIEW_COL_RUN,
59  VIEW_COL_EDIT,
60  VIEW_COL_DELETE,
61  NUM_VIEW_COLS
62 };
63 
64 /* all the pertinent stuff needed to pass around */
65 typedef struct _CustomReportDialog
66 {
67  /* dialog */
68  GtkWidget *dialog;
69  GtkWidget *reportview;
70  GncMainWindow *window;
71  GtkTreeViewColumn *namecol;
72  GtkCellRenderer *namerenderer;
73  GtkTreeViewColumn *runcol;
74  GtkTreeViewColumn *editcol;
75  GtkTreeViewColumn *delcol;
76 
77  /* data */
78  SCM reportlist;
79 
81 
82 void custom_report_dialog_close_cb(GtkWidget* widget, gpointer data);
83 void custom_report_help_cb(GtkWidget* widget, gpointer data);
84 void close_custom_report_clicked_cb(GtkWidget* widget, gpointer data);
85 void custom_report_list_view_row_activated_cb(GtkTreeView *view, GtkTreePath *path,
86  GtkTreeViewColumn *column, gpointer data);
87 void custom_report_list_view_clicked_cb(GtkTreeView *view, GdkEventButton *event, gpointer data);
88 void custom_report_name_edited_cb(GtkCellRendererText *renderer, gchar *path, gchar *new_text, gpointer data);
89 void custom_report_query_tooltip_cb (GtkTreeView *view,
90  gint x,
91  gint y,
92  gboolean keyboard_mode,
93  GtkTooltip *tooltip,
94  gpointer data);
95 
96 void
97 custom_report_dialog_close_cb(GtkWidget* widget, gpointer data)
98 {
99  CustomReportDialog *crd = data;
100  gnc_save_window_size(GNC_PREFS_GROUP_REPORT_SAVED_CONFIGS, GTK_WINDOW(crd->dialog));
101 
102  gtk_widget_destroy(crd->dialog);
103  g_free(crd);
104 }
105 
106 void
107 custom_report_help_cb (GtkWidget *widget, gpointer data)
108 {
109  gnc_gnome_help(HF_HELP, HL_USAGE_CUSTOMREP);
110 }
111 
112 void
113 close_custom_report_clicked_cb(GtkWidget* widget, gpointer data)
114 {
115  CustomReportDialog *crd = data;
116  custom_report_dialog_close_cb(NULL, crd);
117 }
118 
119 
120 /********************************************************************
121  * update_report_list
122  *
123  * this procedure does the real work of displaying a sorted list of
124  * available custom reports
125  ********************************************************************/
126 static void
127 update_report_list(GtkListStore *store, CustomReportDialog *crd)
128 {
129  SCM get_rpt_guids = scm_c_eval_string("gnc:custom-report-template-guids");
130  SCM template_menu_name = scm_c_eval_string("gnc:report-template-menu-name/report-guid");
131  SCM rpt_guids;
132  int i;
133  GtkTreeIter iter;
134  GtkTreeModel *model = GTK_TREE_MODEL (store);
135  gboolean valid_iter;
136 
137  gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), COL_NAME, GTK_SORT_ASCENDING);
138 
139  crd->reportlist = scm_call_0(get_rpt_guids);
140  rpt_guids = crd->reportlist;
141 
142  /* Empty current liststore */
143  valid_iter = gtk_tree_model_get_iter_first (model, &iter);
144  while (valid_iter)
145  {
146  GValue value = { 0, };
147  GncGUID *row_guid;
148  g_value_init ( &value, G_TYPE_POINTER);
149  gtk_tree_model_get_value (model, &iter, COL_NUM, &value);
150  row_guid = (GncGUID *) g_value_get_pointer (&value);
151  guid_free (row_guid);
152  g_value_unset (&value);
153  valid_iter = gtk_tree_model_iter_next (model, &iter);
154  }
155  gtk_list_store_clear(store);
156 
157  if (scm_is_list(rpt_guids))
158  {
159  /* for all the report guids in the list, store them, with a reference,
160  in the gtkliststore */
161  for (i = 0; !scm_is_null(rpt_guids); i++)
162  {
163  GncGUID *guid = guid_malloc ();
164  gchar *guid_str = scm_to_utf8_string (SCM_CAR(rpt_guids));
165  gchar *name = gnc_scm_to_utf8_string (scm_call_2(template_menu_name, SCM_CAR(rpt_guids), SCM_BOOL_F));
166 
167  if (string_to_guid (guid_str, guid))
168  {
169  gtk_list_store_append(store, &iter);
170  gtk_list_store_set(store, &iter,
171  COL_NAME, name,
172  COL_NUM, guid,
173  -1);
174  }
175  g_free (name);
176  g_free (guid_str);
177 
178  rpt_guids = SCM_CDR(rpt_guids);
179  }
180  }
181 }
182 
183 
184 static GtkTreeModel *
185 create_and_fill_report_list(CustomReportDialog *crd)
186 {
187  GtkListStore *store;
188 
189  store = gtk_list_store_new(NUM_COLS, G_TYPE_STRING, G_TYPE_POINTER);
190 
191  update_report_list(store, crd);
192 
193  return GTK_TREE_MODEL (store);
194 }
195 
196 
197 static void
198 set_reports_view_and_model(CustomReportDialog *crd)
199 {
200  GtkCellRenderer *renderer;
201  GtkTreeModel *model;
202  GtkTreeViewColumn * col;
203  gint colnum;
204 
205  crd->namerenderer = gtk_cell_renderer_text_new();
206  g_signal_connect (G_OBJECT (crd->namerenderer), "edited",
207  G_CALLBACK (custom_report_name_edited_cb), crd);
208  gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (crd->reportview), -1,
209  "Report Name", crd->namerenderer,
210  "text", COL_NAME,
211  NULL);
212  crd->namecol = gtk_tree_view_get_column (GTK_TREE_VIEW (crd->reportview), VIEW_COL_NAME);
213  gtk_tree_view_column_set_expand (crd->namecol, TRUE);
214 
215  renderer = gtk_cell_renderer_pixbuf_new();
216  g_object_set (G_OBJECT (renderer), "stock-id", GTK_STOCK_EXECUTE, NULL);
217  gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (crd->reportview), -1,
218  "R", renderer,
219  NULL);
220  crd->runcol = gtk_tree_view_get_column (GTK_TREE_VIEW (crd->reportview), VIEW_COL_RUN);
221 
222  renderer = gtk_cell_renderer_pixbuf_new();
223  g_object_set (G_OBJECT (renderer), "stock-id", GTK_STOCK_EDIT, NULL);
224  gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (crd->reportview), -1,
225  "E", renderer,
226  NULL);
227  crd->editcol = gtk_tree_view_get_column (GTK_TREE_VIEW (crd->reportview), VIEW_COL_EDIT);
228 
229  renderer = gtk_cell_renderer_pixbuf_new();
230  g_object_set (G_OBJECT (renderer), "stock-id", GTK_STOCK_DELETE, NULL);
231  colnum = gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (crd->reportview), -1,
232  "D", renderer,
233  NULL);
234  crd->delcol = gtk_tree_view_get_column (GTK_TREE_VIEW (crd->reportview), VIEW_COL_DELETE);
235 
236  model = create_and_fill_report_list(crd);
237 
238  gtk_tree_view_set_model (GTK_TREE_VIEW (crd->reportview), model);
239 
240  g_object_unref(model);
241 }
242 
243 
244 /**************************************************************
245  * custom_report_run_report
246  *
247  * this procedure sets up and calls the report on the scheme
248  * side. This is what makes the report actually run.
249  **************************************************************/
250 static void
251 custom_report_run_report(SCM guid,
252  CustomReportDialog *crd)
253 {
254  SCM make_report = scm_c_eval_string("gnc:make-report");
255  int report_id;
256  GncMainWindow *window = crd->window;
257 
258  if (scm_is_null(guid))
259  return;
260 
261  /* this generates the report */
262  report_id = scm_to_int (scm_call_1(make_report, guid));
263 
264  /* do this *before* displaying the report because sometimes that
265  takes a while... */
266  custom_report_dialog_close_cb(NULL, crd);
267 
268  /* display the report */
269  gnc_main_window_open_report(report_id, window);
270 
271 }
272 
273 /**************************************************************
274  * custom_report_run_report
275  *
276  * this procedure sets up and calls the report on the scheme
277  * side. This is what makes the report actually run.
278  **************************************************************/
279 static void
280 custom_report_edit_report_name (SCM guid,
281  CustomReportDialog *crd,
282  gchar *new_name)
283 {
284  SCM rename_report = scm_c_eval_string("gnc:rename-report");
285  SCM new_name_scm = scm_from_utf8_string(new_name);
286 
287  if (scm_is_null(guid) || !new_name || (*new_name == '\0'))
288  return;
289 
290  /* rename the report */
291  scm_call_2(rename_report, guid, new_name_scm);
292  update_report_list(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(crd->reportview))),
293  crd);
294 
295 }
296 
297 /*********************************************************************
298  * custom_report_delete
299  *
300  * this will delete the report, update the reports list and leave the
301  * dialog active for additional usage.
302  *********************************************************************/
303 static void
304 custom_report_delete (SCM guid, CustomReportDialog *crd)
305 {
306  SCM template_menu_name = scm_c_eval_string("gnc:report-template-menu-name/report-guid");
307  gchar *report_name;
308 
309  if (scm_is_null (guid))
310  return;
311 
312  report_name = gnc_scm_to_utf8_string(scm_call_2(template_menu_name, guid, SCM_BOOL_F));
313 
314  /* we must confirm the user wants to delete their precious custom report! */
315  if (gnc_verify_dialog(crd->dialog, FALSE, "Are you sure you want to delete %s?", report_name))
316  {
317  SCM del_report = scm_c_eval_string("gnc:delete-report");
318  scm_call_1(del_report, guid);
319  update_report_list(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(crd->reportview))),
320  crd);
321  }
322  g_free (report_name);
323 }
324 
325 
326 
327 /********************************************************************
328  * get_custom_report_selection
329  *
330  * this helper function is called to get the selection when the user
331  * clicks on "Run" or "Delete". Includes calling a dialog when there
332  * is no selection.
333  *
334  * const gchar* message -- the message to provide user if there is no
335  * actual selection found.
336  *********************************************************************/
337 static SCM
338 get_custom_report_selection(CustomReportDialog *crd,
339  const gchar* message)
340 {
341  GtkTreeSelection *sel;
342  GtkTreeModel *model;
343  GtkTreeIter iter;
344  GncGUID *guid = guid_malloc ();
345  gchar *guid_str;
346 
347  sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(crd->reportview));
348 
349  if (gtk_tree_selection_get_selected(sel, &model, &iter))
350  {
351  gtk_tree_model_get(model, &iter, COL_NUM, &guid, -1);
352  guid_str = g_new0 (gchar, GUID_ENCODING_LENGTH+1 );
353  guid_to_string_buff (guid, guid_str);
354  }
355  else
356  {
357  /* no selection, notify user */
358  gnc_error_dialog(GTK_WIDGET(crd->window), "%s", message);
359  return SCM_EOL;
360 
361  }
362  return scm_from_utf8_string (guid_str);
363 }
364 
365 
366 /**************************************************************
367  * custom_report_list_view_row_activated_cb
368  *
369  * this is the double-click signal. No need to call
370  * get_custom_report_selection as the double-click implies the
371  * selection.
372  **************************************************************/
373 void
374 custom_report_list_view_row_activated_cb(GtkTreeView *view, GtkTreePath *path,
375  GtkTreeViewColumn *column, gpointer data)
376 {
377  CustomReportDialog *crd = data;
378  GtkTreeModel *model;
379  GtkTreeIter iter;
380 
381  model = gtk_tree_view_get_model(view);
382 
383  if (gtk_tree_model_get_iter(model, &iter, path))
384  {
385  GncGUID *guid = guid_malloc ();
386  gchar *guid_str;
387 
388  gtk_tree_model_get(model, &iter, COL_NUM, &guid, -1);
389  guid_str = g_new0 (gchar, GUID_ENCODING_LENGTH+1 );
390  guid_to_string_buff (guid, guid_str);
391 
392  custom_report_run_report(scm_from_utf8_string (guid_str), crd);
393  }
394 }
395 
396 
397 /**************************************************************
398  * custom_report_list_view_clicked_cb
399  *
400  * this callback is called whenever a user clicked somewhere in
401  * the treeview widget. If the click was on an edit or delete
402  * pictogram, the corresponding action will be executed on the
403  * selected row.
404  **************************************************************/
405 void
406 custom_report_list_view_clicked_cb(GtkTreeView *view, GdkEventButton *event, gpointer data)
407 {
408  CustomReportDialog *crd = data;
409  GtkTreePath *path = NULL;
410  GtkTreeViewColumn *column = NULL;
411  gint cellx, celly;
412 
413  g_return_if_fail ( view != NULL );
414 
415  if (gtk_tree_view_get_path_at_pos (view, event->x, event->y,
416  &path, &column,
417  &cellx, &celly))
418  {
419  if (column == crd->runcol)
420  {
421  SCM guid = get_custom_report_selection(crd, _("You must select a report configuration to load."));
422  custom_report_run_report (guid, crd);
423  }
424  else if (column == crd->editcol)
425  {
426  g_object_set(G_OBJECT(crd->namerenderer), "editable", TRUE, NULL);
427  gtk_tree_view_set_cursor_on_cell (view, path, crd->namecol,
428  crd->namerenderer, TRUE);
429  }
430  else if (column == crd->delcol)
431  {
432  SCM guid = get_custom_report_selection(crd, _("You must select a report configuration to delete."));
433  custom_report_delete (guid, crd);
434  }
435  }
436 }
437 
438 void custom_report_name_edited_cb(GtkCellRendererText *renderer, gchar *path, gchar *new_text, gpointer data)
439 {
440  CustomReportDialog *crd = data;
441  SCM guid = get_custom_report_selection(crd, _("Unable to change report configuration name."));
442  SCM unique_name_func = scm_c_eval_string("gnc:report-template-has-unique-name?");
443  SCM new_name_scm = scm_from_utf8_string(new_text);
444 
445  g_object_set(G_OBJECT(crd->namerenderer), "editable", FALSE, NULL);
446  if (scm_is_null (guid))
447  return;
448 
449  if (scm_is_true (scm_call_2 (unique_name_func, guid, new_name_scm)))
450  custom_report_edit_report_name (guid, crd, new_text);
451  else
452  gnc_error_dialog(crd->dialog, "%s",
453  _("A saved report configuration with this name already exists, please choose another name.") );
454 
455 
456 }
457 void custom_report_query_tooltip_cb (GtkTreeView *view,
458  gint x,
459  gint y,
460  gboolean keyboard_mode,
461  GtkTooltip *tooltip,
462  gpointer data)
463 {
464  CustomReportDialog *crd = data;
465  GtkTreePath *path = NULL;
466  GtkTreeViewColumn *column = NULL;
467  gint cellx, celly;
468 
469  g_return_if_fail ( view != NULL );
470 
471  if (gtk_tree_view_get_path_at_pos (view, x, y,
472  &path, &column,
473  &cellx, &celly))
474  {
475  gtk_tree_view_set_tooltip_cell (view, tooltip, path, column, NULL);
476  if (column == crd->runcol)
477  gtk_tooltip_set_text (tooltip, _("Load report configuration"));
478  else if (column == crd->editcol)
479  gtk_tooltip_set_text (tooltip, _("Edit report configuration name"));
480  else if (column == crd->delcol)
481  gtk_tooltip_set_text (tooltip, _("Delete report configuration"));
482  else
483  gtk_tooltip_set_text (tooltip, NULL);
484  }
485 
486 }
487 
488 /* Internal function that builds the dialog */
489 static CustomReportDialog *gnc_ui_custom_report_internal(GncMainWindow * window)
490 {
491 
492  GtkBuilder *builder;
493  CustomReportDialog *crd;
494  GtkTreeIter iter;
495  GtkTreeModel *model;
496  GtkWidget *no_report_notification;
497 
498  crd = g_new0(CustomReportDialog, 1);
499 
500  builder = gtk_builder_new();
501  gnc_builder_add_from_file (builder, "dialog-custom-report.glade", "custom_report_dialog");
502 
503  crd->dialog = GTK_WIDGET(gtk_builder_get_object (builder, "custom_report_dialog"));
504  crd->reportview = GTK_WIDGET(gtk_builder_get_object (builder, "custom_report_list_view"));
505  no_report_notification = GTK_WIDGET(gtk_builder_get_object (builder, "label2"));
506  set_reports_view_and_model(crd);
507  crd->window = window;
508 
509  gnc_restore_window_size (GNC_PREFS_GROUP_REPORT_SAVED_CONFIGS, GTK_WINDOW(crd->dialog));
510 
511  /* connect the signals */
512  gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, crd);
513 
514  gtk_widget_show_all(crd->dialog);
515 
516  /* check if there are currently saved reports available
517  * by checking if there is a first element */
518  model = gtk_tree_view_get_model (GTK_TREE_VIEW (crd->reportview));
519  if (gtk_tree_model_get_iter_first (model, &iter))
520  {
521  /* saved reports available
522  -> hide the "no reports available" notification */
523  gtk_widget_hide(no_report_notification);
524  }
525  else
526  {
527  /* hide the scrolled window of the report list */
528  gtk_widget_hide(crd->reportview);
529  }
530 
531  g_object_unref(G_OBJECT(builder));
532 
533  return crd;
534 }
535 
536 
537 /***********************************************************
538  * gnc_ui_custom_report
539  *
540  * this is the primary driver for the custom report dialog.
541  ***********************************************************/
542 void gnc_ui_custom_report(GncMainWindow * window)
543 {
544  gnc_ui_custom_report_internal (window);
545 }
546 
547 
548 /***********************************************************
549  * gnc_ui_custom_report_edit_name
550  *
551  * open the custom report dialog and highlight the given
552  * report's name for editing.
553  ***********************************************************/
554 void gnc_ui_custom_report_edit_name (GncMainWindow * window, SCM scm_guid)
555 {
556  SCM is_custom_report;
557  CustomReportDialog *crd = gnc_ui_custom_report_internal (window);
558  GtkTreeModel *model;
559  GtkTreeIter iter;
560  GncGUID *guid;
561  gchar *guid_str;
562  gboolean valid_iter;
563 
564  is_custom_report = scm_c_eval_string ("gnc:report-template-is-custom/template-guid?");
565  if (scm_is_false (scm_call_1 (is_custom_report, scm_guid)))
566  return;
567 
568  guid = guid_malloc ();
569  guid_str = scm_to_utf8_string (scm_guid);
570  if (!string_to_guid (guid_str, guid))
571  goto cleanup;
572 
573  /* Look up the row for the requested guid */
574  model = gtk_tree_view_get_model (GTK_TREE_VIEW (crd->reportview));
575  valid_iter = gtk_tree_model_get_iter_first (model, &iter);
576 
577  while (valid_iter)
578  {
579  GValue value = { 0, };
580  GncGUID *row_guid;
581  g_value_init (&value, G_TYPE_POINTER);
582  gtk_tree_model_get_value (model, &iter, COL_NUM, &value);
583  row_guid = (GncGUID *) g_value_get_pointer (&value);
584 
585  if (guid_equal (guid, row_guid))
586  {
587  /* We found the row for the requested guid
588  * Now let's set the report's name cell in edit mode
589  * so the user can edit the name.
590  */
591  GtkTreePath *path;
592  GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (crd->reportview));
593  gtk_tree_selection_select_iter (selection, &iter);
594  path = gtk_tree_model_get_path (model, &iter);
595  g_object_set(G_OBJECT(crd->namerenderer), "editable", TRUE, NULL);
596  gtk_tree_view_set_cursor_on_cell (GTK_TREE_VIEW (crd->reportview),
597  path, crd->namecol,
598  crd->namerenderer, TRUE);
599  break;
600  }
601 
602  g_value_unset (&value);
603  valid_iter = gtk_tree_model_iter_next (model, &iter);
604  }
605 
606 cleanup:
607  guid_free (guid);
608 }
gboolean string_to_guid(const gchar *string, GncGUID *guid)
gchar * guid_to_string_buff(const GncGUID *guid, gchar *buff)
Functions for adding content to a window.
Definition: guid.h:65
GncGUID * guid_malloc(void)
gboolean guid_equal(const GncGUID *guid_1, const GncGUID *guid_2)
#define GUID_ENCODING_LENGTH
Definition: guid.h:74
void gnc_gnome_help(const char *file_name, const char *anchor)