GnuCash  5.6-150-g038405b370+
dialog-report-style-sheet.cpp
1 /********************************************************************
2  * dialog-report-style-sheet.c -- window for configuring HTML style *
3  * sheets in GnuCash *
4  * Copyright (C) 2000 Bill Gribble <grib@billgribble.com> *
5  * Copyright (c) 2006 David Hampton <hampton@employees.org> *
6  * *
7  * This program is free software; you can redistribute it and/or *
8  * modify it under the terms of the GNU General Public License as *
9  * published by the Free Software Foundation; either version 2 of *
10  * the License, or (at your option) any later version. *
11  * *
12  * This program is distributed in the hope that it will be useful, *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15  * GNU General Public License for more details. *
16  * *
17  * You should have received a copy of the GNU General Public License*
18  * along with this program; if not, contact: *
19  * *
20  * Free Software Foundation Voice: +1-617-542-5942 *
21  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
22  * Boston, MA 02110-1301, USA gnu@gnu.org *
23  ********************************************************************/
24 
25 #include <gtk/gtk.h>
26 #include <glib/gi18n.h>
27 #include <dialog-options.hpp>
28 #include <gnc-optiondb.h>
29 #include <libguile.h>
30 
31 #include <config.h>
32 
33 #include "dialog-report-style-sheet.h"
34 #include "dialog-utils.h"
35 #include "gnc-component-manager.h"
36 #include "gnc-session.h"
37 #include "gnc-gtk-utils.h"
38 #include "gnc-gnome-utils.h"
39 #include "gnc-guile-utils.h"
40 #include "gnc-ui.h"
41 #include <guile-mappings.h>
42 #include "gnc-report.h"
43 
44 #define DIALOG_STYLE_SHEETS_CM_CLASS "style-sheets-dialog"
45 #define GNC_PREFS_GROUP "dialogs.style-sheet"
46 
47 StyleSheetDialog * gnc_style_sheet_dialog = NULL;
48 
50 {
51  GtkWidget * toplevel;
52  GtkTreeView * list_view;
53  GtkListStore * list_store;
54  GtkWidget * options_frame;
55  gint component_id;
56  QofSession * session;
57 };
58 
59 typedef struct ss_info
60 {
61  GncOptionsDialog * odialog;
62  GncOptionDB * odb;
63  SCM stylesheet;
64  GtkTreeRowReference *row_ref;
65 } ss_info;
66 
67 enum
68 {
69  COLUMN_NAME,
70  COLUMN_STYLESHEET,
71  COLUMN_DIALOG,
72  N_COLUMNS
73 };
74 extern "C" // So that gtk_builder_connect_full can find them.
75 {
76 void gnc_style_sheet_select_dialog_new_cb (GtkWidget *widget, gpointer user_data);
77 void gnc_style_sheet_select_dialog_edit_cb (GtkWidget *widget, gpointer user_data);
78 void gnc_style_sheet_select_dialog_delete_cb (GtkWidget *widget, gpointer user_data);
79 void gnc_style_sheet_select_dialog_close_cb (GtkWidget *widget, gpointer user_data);
80 void gnc_style_sheet_select_dialog_destroy_cb (GtkWidget *widget, gpointer user_data);
81 }
82 /************************************************************
83  * Style Sheet Edit Dialog (I.E. an options dialog) *
84  ************************************************************/
85 
86 static void
87 dirty_same_stylesheet (gpointer key, gpointer val, gpointer data)
88 {
89  auto dirty_ss{static_cast<SCM>(data)};
90  auto report{static_cast<SCM>(val)};
91  SCM func, rep_ss;
92 
93  func = scm_c_eval_string ("gnc:report-stylesheet");
94  if (scm_is_procedure (func))
95  rep_ss = scm_call_1 (func, report);
96  else
97  return;
98 
99  if (scm_is_true (scm_eq_p (rep_ss, dirty_ss)))
100  {
101  func = scm_c_eval_string ("gnc:report-set-dirty?!");
102  /* This makes _me_ feel dirty! */
103  if (scm_is_procedure (func))
104  scm_call_2 (func, report, SCM_BOOL_T);
105  }
106 }
107 
108 static void
109 gnc_style_sheet_options_apply_cb (GncOptionsDialog * propertybox,
110  gpointer user_data)
111 {
112  ss_info * ssi = (ss_info *)user_data;
113  GList *results = NULL, *iter;
114 
115  gnc_reports_foreach (dirty_same_stylesheet, ssi->stylesheet);
116 
117  results = gnc_option_db_commit (ssi->odb);
118  for (iter = results; iter; iter = iter->next)
119  {
120  GtkWidget *dialog = gtk_message_dialog_new(nullptr,
121  GTK_DIALOG_MODAL,
122  GTK_MESSAGE_ERROR,
123  GTK_BUTTONS_OK,
124  "%s",
125  (char*)iter->data);
126  gtk_dialog_run(GTK_DIALOG(dialog));
127  gtk_widget_destroy(dialog);
128  g_free (iter->data);
129  }
130  g_list_free (results);
131 }
132 
133 static void
134 gnc_style_sheet_options_close_cb (GncOptionsDialog *opt_dialog,
135  gpointer user_data)
136 {
137  auto ssi{static_cast<ss_info*>(user_data)};
138 
139  if (gnc_style_sheet_dialog && gtk_tree_row_reference_valid (ssi->row_ref))
140  {
141  auto ss = gnc_style_sheet_dialog;
142  auto path = gtk_tree_row_reference_get_path (ssi->row_ref);
143  GtkTreeIter iter;
144  if (gtk_tree_model_get_iter (GTK_TREE_MODEL(ss->list_store), &iter, path))
145  gtk_list_store_set (ss->list_store, &iter,
146  COLUMN_DIALOG, NULL,
147  -1);
148  gtk_tree_path_free (path);
149  }
150  gtk_tree_row_reference_free (ssi->row_ref);
151  delete ssi->odialog;
152  gnc_option_db_destroy (ssi->odb);
153  scm_gc_unprotect_object (ssi->stylesheet);
154  g_free (ssi);
155 }
156 
157 static ss_info *
158 gnc_style_sheet_dialog_create (StyleSheetDialog * ss,
159  gchar *name,
160  SCM sheet_info,
161  GtkTreeRowReference *row_ref)
162 {
163  SCM get_options = scm_c_eval_string ("gnc:html-style-sheet-options");
164 
165  SCM scm_dispatch = scm_call_1 (get_options, sheet_info);
166  ss_info * ssinfo = g_new0 (ss_info, 1);
167  gchar * title;
168  GtkWindow * parent = GTK_WINDOW(gtk_widget_get_toplevel (GTK_WIDGET(ss->list_view)));
169 
170  title = g_strdup_printf(_("HTML Style Sheet Properties: %s"), name);
171  ssinfo->odialog = new GncOptionsDialog(title, parent);
172  ssinfo->odb = gnc_get_optiondb_from_dispatcher(scm_dispatch);
173  ssinfo->stylesheet = sheet_info;
174  ssinfo->row_ref = row_ref;
175  g_free (title);
176 
177  scm_gc_protect_object (ssinfo->stylesheet);
178  g_object_ref (ssinfo->odialog->get_widget());
179 
180  ssinfo->odialog->build_contents(ssinfo->odb);
181 
182  ssinfo->odialog->set_apply_cb(gnc_style_sheet_options_apply_cb, ssinfo);
183  ssinfo->odialog->set_close_cb(gnc_style_sheet_options_close_cb, ssinfo);
184  ssinfo->odialog->set_style_sheet_help_cb();
185  auto window = ssinfo->odialog->get_widget();
186  gtk_window_set_transient_for (GTK_WINDOW(window),
187  GTK_WINDOW(gnc_style_sheet_dialog->toplevel));
188  gtk_window_set_destroy_with_parent (GTK_WINDOW(window), TRUE);
189  gtk_window_present (GTK_WINDOW(window));
190  return (ssinfo);
191 }
192 
193 static SCM
194 gnc_style_sheet_new (StyleSheetDialog * ssd)
195 {
196  SCM make_ss = scm_c_eval_string ("gnc:make-html-style-sheet");
197  SCM templates = scm_c_eval_string ("(gnc:get-html-templates)");
198  SCM t_name = scm_c_eval_string ("gnc:html-style-sheet-template-name");
199  SCM new_ss = SCM_BOOL_F;
200  GtkWidget * template_combo;
201  GtkTreeModel * template_model;
202  GtkTreeIter iter;
203  GtkWidget * name_entry;
204  gint dialog_retval;
205  GList * template_names = NULL;
206 
207  /* get the new name for the style sheet */
208  GtkBuilder * builder;
209  GtkWidget * dlg;
210 
211  builder = gtk_builder_new ();
212  gnc_builder_add_from_file (builder, "dialog-report.glade", "template_liststore");
213  gnc_builder_add_from_file (builder, "dialog-report.glade", "new_style_sheet_dialog");
214 
215  dlg = GTK_WIDGET(gtk_builder_get_object (builder, "new_style_sheet_dialog"));
216  template_combo = GTK_WIDGET(gtk_builder_get_object (builder, "template_combobox"));
217  name_entry = GTK_WIDGET(gtk_builder_get_object (builder, "name_entry"));
218 
219  // Set the name for this dialog so it can be easily manipulated with css
220  gtk_widget_set_name (GTK_WIDGET(dlg), "gnc-id-style-sheet-new");
221  gnc_widget_style_context_add_class (GTK_WIDGET(dlg), "gnc-class-style-sheets");
222 
223  g_assert (ssd);
224 
225  template_model = gtk_combo_box_get_model (GTK_COMBO_BOX(template_combo));
226 
227  /* put in the list of style sheet type names */
228  for (; !scm_is_null (templates); templates = SCM_CDR(templates))
229  {
230  gchar* orig_name;
231 
232  SCM t = SCM_CAR(templates);
233  orig_name = gnc_scm_call_1_to_string (t_name, t);
234 
235  /* Store the untranslated names for lookup later */
236  template_names = g_list_prepend (template_names, (gpointer)orig_name);
237 
238  /* The displayed name should be translated */
239  gtk_list_store_append (GTK_LIST_STORE(template_model), &iter);
240  gtk_list_store_set (GTK_LIST_STORE(template_model), &iter, 0, _(orig_name), -1);
241 
242  /* Note: don't g_free orig_name here - template_names still refers to it*/
243  }
244  gtk_combo_box_set_active (GTK_COMBO_BOX(template_combo), 0);
245 
246  /* get the name */
247  gtk_window_set_transient_for (GTK_WINDOW(dlg), GTK_WINDOW(ssd->toplevel));
248  dialog_retval = gtk_dialog_run (GTK_DIALOG(dlg));
249 
250  if (dialog_retval == GTK_RESPONSE_OK)
251  {
252  gint choice = gtk_combo_box_get_active (GTK_COMBO_BOX(template_combo));
253  auto template_str{static_cast<const char *>(g_list_nth_data (template_names, choice))};
254  const char *name_str = gtk_entry_get_text(GTK_ENTRY(name_entry));
255  if (name_str && strlen(name_str) == 0)
256  {
257  /* If the name is empty, we display an error dialog but
258  * refuse to create the new style sheet. */
259  gnc_error_dialog (GTK_WINDOW(ssd->toplevel), "%s", _("You must provide a name for the new style sheet."));
260  name_str = NULL;
261  }
262  if (template_str && name_str)
263  {
264  new_ss = scm_call_2 (make_ss,
265  scm_from_utf8_string (template_str),
266  scm_from_utf8_string (name_str));
267  }
268  }
269 
270  g_list_free_full (template_names, g_free);
271 
272  g_object_unref (G_OBJECT(builder));
273 
274  gtk_widget_destroy (dlg);
275  return (new_ss);
276 }
277 
278 /************************************************************
279  * Style Sheet Selection Dialog *
280  ************************************************************/
281 static void
282 gnc_style_sheet_select_dialog_add_one (StyleSheetDialog * ss,
283  SCM sheet_info,
284  gboolean select)
285 {
286  SCM get_name;
287  gchar *c_name;
288  GtkTreeIter iter;
289 
290  get_name = scm_c_eval_string ("gnc:html-style-sheet-name");
291  c_name = gnc_scm_call_1_to_string (get_name, sheet_info);
292  if (!c_name)
293  return;
294 
295  /* add the column name */
296  scm_gc_protect_object (sheet_info);
297  gtk_list_store_append (ss->list_store, &iter);
298  gtk_list_store_set (ss->list_store, &iter,
299  /* Translate the displayed name */
300  COLUMN_NAME, _(c_name),
301  COLUMN_STYLESHEET, sheet_info,
302  -1);
303  g_free (c_name);
304  /* The translation of the name fortunately doesn't affect the
305  * lookup because that is done through the sheet_info argument. */
306 
307  if (select)
308  {
309  GtkTreeSelection * selection = gtk_tree_view_get_selection (ss->list_view);
310  gtk_tree_selection_select_iter (selection, &iter);
311  }
312 }
313 
314 static void
315 gnc_style_sheet_select_dialog_fill (StyleSheetDialog * ss)
316 {
317  SCM stylesheets = scm_c_eval_string ("(gnc:get-html-style-sheets)");
318  SCM sheet_info;
319 
320  /* pack it full of content */
321  for (; !scm_is_null (stylesheets); stylesheets = SCM_CDR(stylesheets))
322  {
323  sheet_info = SCM_CAR(stylesheets);
324  gnc_style_sheet_select_dialog_add_one (ss, sheet_info, FALSE);
325  }
326 }
327 
328 static void
329 gnc_style_sheet_select_dialog_event_cb (GtkWidget *widget,
330  GdkEvent *event,
331  gpointer user_data)
332 {
333  StyleSheetDialog * ss = (StyleSheetDialog *)user_data;
334 
335  g_return_if_fail (event != NULL);
336  g_return_if_fail (ss != NULL);
337 
338  if (event->type != GDK_2BUTTON_PRESS)
339  return;
340 
341  /* Synthesize a click of the edit button */
342  gnc_style_sheet_select_dialog_edit_cb (NULL, ss);
343 }
344 
345 void
346 gnc_style_sheet_select_dialog_new_cb (GtkWidget *widget, gpointer user_data)
347 {
348  StyleSheetDialog * ss = (StyleSheetDialog *)user_data;
349  SCM sheet_info;
350 
351  sheet_info = gnc_style_sheet_new (ss);
352  if (sheet_info == SCM_BOOL_F)
353  return;
354 
355  gnc_style_sheet_select_dialog_add_one (ss, sheet_info, TRUE);
356 
357  // now start the edit dialog
358  gnc_style_sheet_select_dialog_edit_cb (NULL, ss);
359 }
360 
361 void
362 gnc_style_sheet_select_dialog_edit_cb (GtkWidget *widget, gpointer user_data)
363 {
364  StyleSheetDialog * ss = (StyleSheetDialog *)user_data;
365  GtkTreeSelection * selection = gtk_tree_view_get_selection (ss->list_view);
366  GtkTreeModel * model;
367  GtkTreeIter iter;
368 
369  if (gtk_tree_selection_get_selected (selection, &model, &iter))
370  {
371  GtkTreeRowReference * row_ref;
372  GtkTreePath * path;
373  ss_info * ssinfo;
374  gchar * name;
375 
376  SCM sheet_info;
377 
378  gtk_tree_model_get (model, &iter,
379  COLUMN_NAME, &name,
380  COLUMN_STYLESHEET, &sheet_info,
381  -1);
382  /* Fire off options dialog here */
383  path = gtk_tree_model_get_path (GTK_TREE_MODEL(ss->list_store), &iter);
384  row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL(ss->list_store), path);
385  ssinfo = gnc_style_sheet_dialog_create (ss, name, sheet_info, row_ref);
386  gtk_list_store_set (ss->list_store, &iter,
387  COLUMN_DIALOG, ssinfo,
388  -1);
389  gtk_tree_path_free (path);
390  g_free (name);
391  }
392 }
393 
394 void
395 gnc_style_sheet_select_dialog_delete_cb (GtkWidget *widget, gpointer user_data)
396 {
397  StyleSheetDialog * ss = (StyleSheetDialog *)user_data;
398  GtkTreeSelection * selection = gtk_tree_view_get_selection (ss->list_view);
399  GtkTreeModel * model;
400  GtkTreeIter iter;
401 
402  if (gtk_tree_selection_get_selected (selection, &model, &iter))
403  {
404  ss_info * ssinfo;
405 
406  SCM sheet_info;
407  SCM remover;
408 
409  gtk_tree_model_get (model, &iter,
410  COLUMN_STYLESHEET, &sheet_info,
411  COLUMN_DIALOG, &ssinfo,
412  -1);
413  gtk_list_store_remove (ss->list_store, &iter);
414 
415  if (ssinfo)
416  gnc_style_sheet_options_close_cb (NULL, ssinfo);
417  remover = scm_c_eval_string ("gnc:html-style-sheet-remove");
418  scm_call_1 (remover, sheet_info);
419  scm_gc_unprotect_object (sheet_info);
420  }
421 }
422 
423 void
424 gnc_style_sheet_select_dialog_close_cb (GtkWidget *widget, gpointer user_data)
425 {
426  StyleSheetDialog * ss = (StyleSheetDialog *)user_data;
427  gnc_close_gui_component (ss->component_id);
428 }
429 
430 static gboolean
431 gnc_style_sheet_select_dialog_delete_event_cb (GtkWidget *widget,
432  GdkEvent *event,
433  gpointer user_data)
434 {
435  auto ss{static_cast<StyleSheetDialog*>(user_data)};
436  gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(ss->toplevel));
437  return FALSE;
438 }
439 
440 void
441 gnc_style_sheet_select_dialog_destroy_cb (GtkWidget *widget, gpointer user_data)
442 {
443  StyleSheetDialog *ss = (StyleSheetDialog *)user_data;
444 
445  if (!ss)
446  return;
447 
448  gnc_unregister_gui_component (ss->component_id);
449 
450  g_object_unref (ss->list_store);
451  if (ss->toplevel)
452  {
453  gtk_widget_destroy (ss->toplevel);
454  ss->toplevel = NULL;
455  }
456  gnc_style_sheet_dialog = NULL;
457  g_free (ss);
458 }
459 
460 static void
461 gnc_style_sheet_window_close_handler (gpointer user_data)
462 {
463  StyleSheetDialog *ss = (StyleSheetDialog *)user_data;
464  g_return_if_fail (ss);
465 
466  gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(ss->toplevel));
467  gtk_widget_destroy (ss->toplevel);
468 }
469 
470 static gboolean
471 gnc_style_sheet_select_dialog_check_escape_cb (GtkWidget *widget,
472  GdkEventKey *event,
473  gpointer user_data)
474 {
475  if (event->keyval == GDK_KEY_Escape)
476  {
477  StyleSheetDialog * ss = (StyleSheetDialog *)user_data;
478  gnc_close_gui_component (ss->component_id);
479  return TRUE;
480  }
481  return FALSE;
482 }
483 
484 static StyleSheetDialog *
485 gnc_style_sheet_select_dialog_create (GtkWindow *parent)
486 {
487  StyleSheetDialog * ss = g_new0 (StyleSheetDialog, 1);
488  GtkBuilder * builder;
489  GtkCellRenderer * renderer;
490  GtkTreeSelection * selection;
491 
492  builder = gtk_builder_new ();
493  gnc_builder_add_from_file (builder, "dialog-report.glade", "select_style_sheet_window");
494 
495  ss->toplevel = GTK_WIDGET(gtk_builder_get_object (builder, "select_style_sheet_window"));
496 
497  ss->session = gnc_get_current_session ();
498 
499  // Set the name for this dialog so it can be easily manipulated with css
500  gtk_widget_set_name (GTK_WIDGET(ss->toplevel), "gnc-id-style-sheet-select");
501  gnc_widget_style_context_add_class (GTK_WIDGET(ss->toplevel), "gnc-class-style-sheets");
502 
503  ss->list_view = GTK_TREE_VIEW(gtk_builder_get_object (builder, "style_sheet_list_view"));
504  ss->list_store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_POINTER);
505  gtk_tree_view_set_model (ss->list_view, GTK_TREE_MODEL(ss->list_store));
506 
507  renderer = gtk_cell_renderer_text_new ();
508  gtk_tree_view_insert_column_with_attributes (ss->list_view, -1,
509  _("Style Sheet Name"), renderer,
510  "text", COLUMN_NAME,
511  NULL);
512 
513  selection = gtk_tree_view_get_selection (ss->list_view);
514  gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
515 
516  g_signal_connect (ss->list_view, "event-after",
517  G_CALLBACK(gnc_style_sheet_select_dialog_event_cb), ss);
518 
519  g_signal_connect (ss->toplevel, "destroy",
520  G_CALLBACK(gnc_style_sheet_select_dialog_destroy_cb), ss);
521 
522  g_signal_connect (ss->toplevel, "delete-event",
523  G_CALLBACK(gnc_style_sheet_select_dialog_delete_event_cb), ss);
524 
525  g_signal_connect (ss->toplevel, "key-press-event",
526  G_CALLBACK(gnc_style_sheet_select_dialog_check_escape_cb), ss);
527 
528  gnc_style_sheet_select_dialog_fill (ss);
529 
530  gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, ss);
531  g_object_unref (G_OBJECT(builder));
532  return ss;
533 }
534 
535 void
536 gnc_style_sheet_dialog_open (GtkWindow *parent)
537 {
538  if (gnc_style_sheet_dialog)
539  gtk_window_present (GTK_WINDOW(gnc_style_sheet_dialog->toplevel));
540  else
541  {
542  gnc_style_sheet_dialog =
543  gnc_style_sheet_select_dialog_create (parent);
544 
545  /* register with component manager */
546  gnc_style_sheet_dialog->component_id =
547  gnc_register_gui_component (DIALOG_STYLE_SHEETS_CM_CLASS,
548  NULL, //no refresh handler
549  gnc_style_sheet_window_close_handler,
550  gnc_style_sheet_dialog);
551 
552  gnc_gui_component_set_session (gnc_style_sheet_dialog->component_id,
553  gnc_style_sheet_dialog->session);
554 
555  gnc_restore_window_size (GNC_PREFS_GROUP,
556  GTK_WINDOW(gnc_style_sheet_dialog->toplevel),
557  GTK_WINDOW(parent));
558  gtk_widget_show_all (gnc_style_sheet_dialog->toplevel);
559  }
560 }
Holds all of the options for a book, report, or stylesheet, organized by GncOptionSections.
gtk helper routines.
C public interface for the Options Database.
void gnc_option_db_destroy(GncOptionDB *odb)
Destruct and release a GncOptionDB.
Gnome specific utility functions.
GList * gnc_option_db_commit(GncOptionDB *odb)
Write all changed ui_item values to their options.