GnuCash c935c2f+
Loading...
Searching...
No Matches
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
47StyleSheetDialog * 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
59typedef struct ss_info
60{
61 GncOptionsDialog * odialog;
62 GncOptionDB * odb;
63 SCM stylesheet;
64 GtkTreeRowReference *row_ref;
65} ss_info;
66
67enum
68{
69 COLUMN_NAME,
70 COLUMN_STYLESHEET,
71 COLUMN_DIALOG,
72 N_COLUMNS
73};
74extern "C" // So that gtk_builder_connect_full can find them.
75{
76void gnc_style_sheet_select_dialog_new_cb (GtkWidget *widget, gpointer user_data);
77void gnc_style_sheet_select_dialog_edit_cb (GtkWidget *widget, gpointer user_data);
78void gnc_style_sheet_select_dialog_delete_cb (GtkWidget *widget, gpointer user_data);
79void gnc_style_sheet_select_dialog_close_cb (GtkWidget *widget, gpointer user_data);
80void 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
86static void
87dirty_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
108static void
109gnc_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
133static void
134gnc_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
157static ss_info *
158gnc_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
193static SCM
194gnc_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 ************************************************************/
281static void
282gnc_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
314static void
315gnc_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
328static void
329gnc_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
345void
346gnc_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
361void
362gnc_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
394void
395gnc_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
423void
424gnc_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
430static gboolean
431gnc_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
440void
441gnc_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
460static void
461gnc_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
470static gboolean
471gnc_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
484static StyleSheetDialog *
485gnc_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
535void
536gnc_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.
Gnome specific utility functions.
gtk helper routines.
C public interface for the Options Database.
GList * gnc_option_db_commit(GncOptionDB *odb)
Write all changed ui_item values to their options.
void gnc_option_db_destroy(GncOptionDB *odb)
Destruct and release a GncOptionDB.