GnuCash  5.6-150-g038405b370+
dialog-preferences.c
Go to the documentation of this file.
1 /********************************************************************\
2  * dialog-preferences.c -- preferences dialog *
3  * *
4  * Copyright (C) 2005 David Hampton *
5  * Copyright (C) 2011 Robert Fewell *
6  * Copyright (C) 2013 Geert Janssens *
7  * *
8  * This program is free software; you can redistribute it and/or *
9  * modify it under the terms of the GNU General Public License as *
10  * published by the Free Software Foundation; either version 2 of *
11  * the License, or (at your option) any later version. *
12  * *
13  * This program is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License*
19  * along with this program; if not, contact: *
20  * *
21  * Free Software Foundation Voice: +1-617-542-5942 *
22  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
23  * Boston, MA 02110-1301, USA gnu@gnu.org *
24 \********************************************************************/
25 
63 #include <config.h>
64 
65 #include <gtk/gtk.h>
66 #include <glib/gi18n.h>
67 
68 #include "dialog-utils.h"
69 #include "gnc-currency-edit.h"
70 #include "gnc-date-edit.h"
71 #include "gnc-gobject-utils.h"
72 #include "gnc-period-select.h"
73 #include "gnc-engine.h"
74 #include "Account.h"
75 #include "gnc-prefs.h"
76 #include "gnc-ui.h"
77 #include "gnc-ui-util.h"
78 #include <gnc-session.h>
79 #include "gnc-component-manager.h"
80 #include "dialog-preferences.h"
81 #include "dialog-doclink-utils.h"
82 
83 #define DIALOG_PREFERENCES_CM_CLASS "dialog-newpreferences"
84 #define GNC_PREFS_GROUP "dialogs.preferences"
85 #define PREF_PREFIX_LEN sizeof("pref/") - 1
86 #define PREFS_WIDGET_HASH "prefs_widget_hash"
87 #define NOTEBOOK "notebook"
88 
90 static QofLogModule log_module = GNC_MOD_PREFS;
91 
92 void gnc_preferences_response_cb (GtkDialog *dialog, gint response, GtkDialog *unused);
93 void gnc_account_separator_pref_changed_cb (GtkEntry *entry, GtkWidget *dialog);
94 void gnc_save_on_close_expires_cb (GtkToggleButton *button, GtkWidget *dialog);
95 gboolean gnc_preferences_delete_event_cb (GtkWidget *widget,
96  GdkEvent *event,
97  gpointer user_data);
98 
101 typedef struct addition_t
102 {
105  gchar *filename;
109  gchar *widgetname;
112  gchar *tabname;
115  gboolean full_page;
116 } addition;
117 
121 GSList *add_ins = NULL;
122 
123 static gchar *gnc_account_separator_is_valid (const gchar *separator,
124  gchar **normalized_separator)
125 {
126  QofBook *book;
127  GList *conflict_accts = NULL;
128  gchar *message = NULL;
129 
130  if (!gnc_current_session_exist())
131  return NULL;
132 
133  book = gnc_get_current_book ();
134  *normalized_separator = gnc_normalize_account_separator (separator);
135  conflict_accts = gnc_account_list_name_violations (book, *normalized_separator);
136  if (conflict_accts)
137  message = gnc_account_name_violations_errmsg (*normalized_separator,
138  conflict_accts);
139 
140  g_list_free_full (conflict_accts, g_free);
141  return message;
142 }
143 
154 void
155 gnc_account_separator_pref_changed_cb (GtkEntry *entry, GtkWidget *dialog)
156 {
157  GtkWidget *label, *image;
158  gchar *sample;
159  gchar *separator = NULL;
160 
161  gchar *conflict_msg = gnc_account_separator_is_valid (gtk_entry_get_text (entry), &separator);
162 
163  label = g_object_get_data (G_OBJECT(dialog), "sample_account");
164  DEBUG("Sample Account pointer is %p", label);
165  /* Translators: Both %s will be the account separator character; the
166  resulting string is a demonstration how the account separator
167  character will look like. You can replace these three account
168  names with other account names that are more suitable for your
169  language - just keep in mind to have exactly two %s in your
170  translation. */
171  sample = g_strdup_printf (_("Income%sSalary%sTaxable"),
172  separator, separator);
173  PINFO(" Label set to '%s'", sample);
174  gtk_label_set_text (GTK_LABEL(label), sample);
175  g_free (sample);
176 
177  /* Check if the new separator clashes with existing account names */
178  image = g_object_get_data (G_OBJECT(dialog), "separator_error");
179  DEBUG("Separator Error Image pointer is %p", image);
180 
181  if (conflict_msg)
182  {
183  gtk_widget_set_tooltip_text (GTK_WIDGET(image), conflict_msg);
184  gtk_widget_show (GTK_WIDGET(image));
185  g_free (conflict_msg);
186  }
187  else
188  gtk_widget_hide (GTK_WIDGET(image));
189 
190  g_free (separator);
191 }
192 
193 
203 static gboolean
204 gnc_account_separator_validate (GtkWidget *dialog)
205 {
206  GtkWidget *entry = g_object_get_data (G_OBJECT(dialog), "account-separator");
207  gboolean ret = TRUE;
208  gchar *separator = NULL;
209  gchar *conflict_msg = gnc_account_separator_is_valid (gtk_entry_get_text (GTK_ENTRY(entry)), &separator);
210 
211  /* Check if the new separator clashes with existing account names */
212  if (conflict_msg)
213  {
214  GtkWidget *msg_dialog, *msg_label;
215  GtkBuilder *builder;
216  gint response;
217 
218  builder = gtk_builder_new ();
219  gnc_builder_add_from_file (builder, "dialog-preferences.glade", "separator_validation_dialog");
220 
221  msg_dialog = GTK_WIDGET(gtk_builder_get_object (builder, "separator_validation_dialog"));
222 
223  msg_label = GTK_WIDGET(gtk_builder_get_object (builder, "conflict_message"));
224 
225  gtk_label_set_text (GTK_LABEL(msg_label), conflict_msg);
226 
227  g_object_unref (G_OBJECT(builder));
228  gtk_widget_show_all (msg_dialog);
229 
230  response = gtk_dialog_run (GTK_DIALOG(msg_dialog));
231  if (response == GTK_RESPONSE_ACCEPT) // reset to original
232  {
233  gchar *original_sep = g_object_get_data (G_OBJECT(entry), "original_text");
234 
235  if (original_sep != NULL)
236  gtk_entry_set_text (GTK_ENTRY(entry), original_sep);
237  }
238  else
239  ret = FALSE;
240 
241  g_free (conflict_msg);
242  gtk_widget_destroy (msg_dialog);
243  }
244  g_free (separator);
245  return ret;
246 }
247 
248 
257 static void
258 gnc_preferences_select_account_page (GtkDialog *dialog)
259 {
260  GtkWidget *notebook = g_object_get_data (G_OBJECT(dialog), NOTEBOOK);
261  GList *children = gtk_container_get_children (GTK_CONTAINER(notebook));
262 
263  if (children)
264  {
265  GtkWidget *acc_page = NULL;
266  GList *node;
267 
268  for (node = children; node; node = node->next)
269  {
270  if (g_strcmp0 (gtk_widget_get_name (GTK_WIDGET(node->data)), "accounts_page") == 0)
271  acc_page = node->data;
272  }
273 
274  if (acc_page)
275  gtk_notebook_set_current_page (GTK_NOTEBOOK(notebook),
276  gtk_notebook_page_num (GTK_NOTEBOOK(notebook),
277  acc_page));
278  }
279  g_list_free (children);
280 }
281 
282 
288 void
289 gnc_save_on_close_expires_cb (GtkToggleButton *button, GtkWidget *dialog)
290 {
291  GtkWidget *spinner = g_object_get_data (G_OBJECT(dialog),
292  "save_on_close_wait_time");
293  gtk_widget_set_sensitive (spinner, gtk_toggle_button_get_active (button));
294 }
295 
307 static gint
308 gnc_prefs_compare_addins (addition *a,
309  addition *b)
310 {
311  return g_utf8_collate (a->tabname, b->tabname);
312 }
313 
314 
334 static void
335 gnc_preferences_add_page_internal (const gchar *filename,
336  const gchar *widgetname,
337  const gchar *tabname,
338  gboolean full_page)
339 {
340  addition *add_in, *preexisting;
341  gboolean error = FALSE;
342  GSList *ptr;
343 
344  ENTER("file %s, widget %s, tab %s full page %d",
345  filename, widgetname, tabname, full_page);
346 
347  add_in = g_malloc (sizeof(addition));
348  if (add_in == NULL)
349  {
350  g_critical ("Unable to allocate memory.\n");
351  LEAVE("no memory");
352  return;
353  }
354 
355  add_in->filename = g_strdup (filename);
356  add_in->widgetname = g_strdup (widgetname);
357  add_in->tabname = g_strdup (tabname);
358  add_in->full_page = full_page;
359  if (!add_in->filename || !add_in->widgetname || !add_in->tabname)
360  {
361  g_critical ("Unable to allocate memory.\n");
362  g_free (add_in->filename);
363  g_free (add_in->widgetname);
364  g_free (add_in->tabname);
365  g_free (add_in);
366  LEAVE("no memory");
367  return;
368  }
369 
370  ptr = g_slist_find_custom (add_ins, add_in, (GCompareFunc)gnc_prefs_compare_addins);
371  if (ptr)
372  {
373  /* problem? */
374  preexisting = ptr->data;
375 
376  if (preexisting->full_page)
377  {
378  g_warning ("New tab %s(%s/%s/%s) conflicts with existing tab %s(%s/%s/full)",
379  add_in->tabname, add_in->filename, add_in->widgetname,
380  add_in->full_page ? "full" : "partial",
381  preexisting->tabname, preexisting->filename, preexisting->widgetname);
382  error = TRUE;
383  }
384  else if (add_in->full_page)
385  {
386  g_warning ("New tab %s(%s/%s/%s) conflicts with existing tab %s(%s/%s/partial)",
387  add_in->tabname, add_in->filename, add_in->widgetname,
388  add_in->full_page ? "full" : "partial",
389  preexisting->tabname, preexisting->filename, preexisting->widgetname);
390  error = TRUE;
391  }
392  }
393 
394  if (error)
395  {
396  g_free (add_in->filename);
397  g_free (add_in->widgetname);
398  g_free (add_in->tabname);
399  g_free (add_in);
400  LEAVE("err");
401  return;
402  }
403  else
404  {
405  add_ins = g_slist_append (add_ins, add_in);
406  }
407  LEAVE("");
408 }
409 
410 
411 /* This function adds a full page of preferences to the preferences
412  * dialog. When the dialog is created, the specified content will be
413  * pulled from the specified glade file and added to the preferences
414  * dialog with the specified tab name. The tab name may not be
415  * duplicated. For example, the Business code might have a full page
416  * of its own preferences. */
417 void
418 gnc_preferences_add_page (const gchar *filename,
419  const gchar *widgetname,
420  const gchar *tabname)
421 {
422  gnc_preferences_add_page_internal (filename, widgetname, tabname, TRUE);
423 }
424 
425 
426 /* This function adds a partial page of preferences to the
427  * preferences dialog. When the dialog is created, the specified
428  * content will be pulled from the glade file and added to the
429  * preferences dialog with the specified tab name. The tab name
430  * may be duplicated. For example, the HBCI preferences may share a
431  * "Data Import" page with QIF and other methods. */
432 void
433 gnc_preferences_add_to_page (const gchar *filename,
434  const gchar *widgetname,
435  const gchar *tabname)
436 {
437  gnc_preferences_add_page_internal (filename, widgetname, tabname, FALSE);
438 }
439 
440 
441 /*******************************************************************/
442 
456 static void
457 gnc_prefs_build_widget_table (GtkBuilder *builder,
458  GtkWidget *dialog)
459 {
460  GHashTable *prefs_table;
461  GSList *interesting, *runner;
462  const gchar *name;
463  const gchar *wname;
464  GtkWidget *widget;
465 
466  prefs_table = g_object_get_data (G_OBJECT(dialog), PREFS_WIDGET_HASH);
467 
468  interesting = gtk_builder_get_objects (builder);
469 
470  for (runner = interesting; runner; runner = g_slist_next(runner))
471  {
472  widget = runner->data;
473  if (GTK_IS_WIDGET(widget))
474  {
475  wname = gtk_widget_get_name (widget);
476  name = gtk_buildable_get_name (GTK_BUILDABLE(widget));
477  DEBUG("Widget type is %s and buildable get name is %s", wname, name);
478  if (g_str_has_prefix (name, "pref"))
479  g_hash_table_insert (prefs_table, (gchar *)name, widget);
480  }
481  }
482  g_slist_free (interesting);
483 
484 }
485 
486 
492 struct copy_data
493 {
495  GtkGrid *grid_from;
497  GtkGrid *grid_to;
499  gint cols, rows;
500 };
501 
502 
503 static GtkWidget *
504 gnc_prefs_find_page (GtkNotebook *notebook, const gchar *name)
505 {
506  int n_pages, i;
507  GtkWidget *child;
508  const gchar *child_name;
509 
510  g_return_val_if_fail (GTK_IS_NOTEBOOK(notebook), NULL);
511  g_return_val_if_fail (name, NULL);
512 
513  ENTER("");
514 
515  n_pages = gtk_notebook_get_n_pages (notebook);
516 
517  for (i = 0; i < n_pages; i++)
518  {
519  child = gtk_notebook_get_nth_page (notebook, i);
520  g_return_val_if_fail (child, NULL);
521 
522  child_name = gtk_notebook_get_tab_label_text (notebook, child);
523  g_return_val_if_fail (child_name, NULL);
524 
525  if (g_utf8_collate (name, child_name) == 0)
526  {
527  LEAVE("found at index: %d", i);
528  return child;
529  }
530  }
531 
532  LEAVE("not found");
533  return NULL;
534 }
535 
536 
548 static void
549 gnc_prefs_get_grid_size (GtkWidget *child, gpointer data)
550 {
551  struct copy_data *copydata = data;
552  gint top, left, height, width;
553 
554  gtk_container_child_get (GTK_CONTAINER(copydata->grid_to), child,
555  "left-attach", &left,
556  "top-attach", &top,
557  "height", &height,
558  "width", &width,
559  NULL);
560 
561  if (left + width >= copydata->cols)
562  copydata->cols = left + width;
563 
564  if (top + height >= copydata->rows)
565  copydata->rows = top + height;
566 }
567 
568 
581 static void
582 gnc_prefs_move_grid_entry (GtkWidget *child,
583  gpointer data)
584 {
585  struct copy_data *copydata = data;
586  gint top, left, height, width;
587  gboolean hexpand, vexpand;
588  GtkAlign halign, valign;
589  gint topm, bottomm, leftm, rightm;
590 
591  ENTER("child %p, copy data %p", child, data);
592  gtk_container_child_get (GTK_CONTAINER(copydata->grid_from), child,
593  "left-attach", &left,
594  "top-attach", &top,
595  "height", &height,
596  "width", &width,
597  NULL);
598  hexpand = gtk_widget_get_hexpand (child);
599  vexpand = gtk_widget_get_vexpand (child);
600  halign = gtk_widget_get_halign (child);
601  valign = gtk_widget_get_valign (child);
602 
603  g_object_get (child, "margin-top", &topm, "margin-bottom", &bottomm, NULL);
604  g_object_get (child, "margin-left", &leftm, "margin-right", &rightm, NULL);
605 
606  g_object_ref (child);
607  gtk_container_remove (GTK_CONTAINER(copydata->grid_from), child);
608 
609  gtk_grid_attach (copydata->grid_to, child, left, copydata->rows + top , width, height);
610 
611  gtk_widget_set_hexpand (child, hexpand);
612  gtk_widget_set_vexpand (child, vexpand);
613  gtk_widget_set_halign (child, halign);
614  gtk_widget_set_valign (child, valign);
615 
616  g_object_set (child, "margin-left", leftm, "margin-right", rightm, NULL);
617  g_object_set (child, "margin-top", topm, "margin-bottom", bottomm, NULL);
618 
619  g_object_unref (child);
620  LEAVE(" ");
621 }
622 
623 
635 static void
636 gnc_preferences_build_page (gpointer data,
637  gpointer user_data)
638 {
639  GtkBuilder *builder;
640  GtkWidget *dialog, *existing_content, *new_content, *label;
641  GtkNotebook *notebook;
642  addition *add_in;
643  struct copy_data copydata = {NULL, NULL, 0, 0};
644  gchar **widgetname;
645  gint i;
646 
647  ENTER("add_in %p, dialog %p", data, user_data);
648  add_in = (addition *)data;
649  dialog = user_data;
650 
651  DEBUG("Opening %s to get %s", add_in->filename, add_in->widgetname);
652  builder = gtk_builder_new ();
653 
654  /* Adjustments etc... must come before dialog information */
655  widgetname = g_strsplit (add_in->widgetname, ",", -1);
656 
657  for (i = 0; widgetname[i]; i++)
658  {
659  DEBUG("Opening %s to get content %s", add_in->filename, widgetname[i]);
660  gnc_builder_add_from_file (builder, add_in->filename, widgetname[i]);
661  }
662 
663  DEBUG("Widget Content is %s", widgetname[i - 1]);
664  new_content = GTK_WIDGET(gtk_builder_get_object (builder, widgetname[i - 1]));
665 
666  g_strfreev (widgetname);
667  DEBUG("done");
668 
669  /* Add to the list of interesting widgets */
670  gnc_prefs_build_widget_table (builder, dialog);
671 
672  /* Connect the signals in this glade file. The dialog is passed in
673  * so the callback can find "interesting" widgets from other
674  * glade files if necessary (via the GPREFS_WIDGET_HASH hash table). */
675  gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, dialog);
676 
677  /* Prepare for recursion */
678  notebook = g_object_get_data (G_OBJECT(dialog), NOTEBOOK);
679 
680  if (add_in->full_page)
681  {
682  label = gtk_label_new (add_in->tabname);
683  gnc_label_set_alignment (label, 0.0, 0.5);
684  gtk_notebook_append_page (notebook, new_content, label);
685  g_object_unref (G_OBJECT(builder));
686  LEAVE("appended page");
687  return;
688  }
689 
690  /* Copied grids must be grids */
691  if (!GTK_IS_GRID(new_content))
692  {
693  g_critical ("The object name %s in file %s is not a GtkGrid. It cannot "
694  "be added to the preferences dialog.",
695  add_in->widgetname, add_in->filename);
696  g_object_unref (G_OBJECT(builder));
697  LEAVE("");
698  return;
699  }
700 
701  /* Does the page exist or must we create it */
702  existing_content = gnc_prefs_find_page (notebook, add_in->tabname);
703 
704  if (!existing_content)
705  {
706  /* No existing content with this name. Create a blank page */
707  existing_content = gtk_grid_new ();
708  gtk_container_set_border_width (GTK_CONTAINER(existing_content), 6);
709  label = gtk_label_new (add_in->tabname);
710  gnc_label_set_alignment (label, 0.0, 0.5);
711  gtk_notebook_append_page (notebook, existing_content, label);
712  gtk_widget_show_all (existing_content);
713  DEBUG("created new page %s, appended it", add_in->tabname);
714  }
715  else
716  {
717  /* Lets get the size of the existing grid */
718  copydata.grid_to = GTK_GRID(existing_content);
719  gtk_container_foreach (GTK_CONTAINER(existing_content), gnc_prefs_get_grid_size, &copydata);
720 
721  DEBUG("found existing page %s, grid size is %d x %d", add_in->tabname, copydata.rows, copydata.cols);
722  }
723 
724  /* Maybe add a spacer row */
725  if (copydata.rows > 0)
726  {
727  label = gtk_label_new ("");
728  gtk_widget_show (label);
729  gtk_grid_attach (GTK_GRID(existing_content), label, 0, copydata.rows, 1, 1);
730  copydata.rows = copydata.rows + 1;
731 
732  DEBUG("add spacer row");
733  }
734 
735  /* Now copy all the entries in the grid */
736  copydata.grid_from = GTK_GRID(new_content);
737  copydata.grid_to = GTK_GRID(existing_content);
738  gtk_container_foreach (GTK_CONTAINER(new_content), gnc_prefs_move_grid_entry, &copydata);
739 
740  g_object_ref_sink (new_content);
741  g_object_unref (G_OBJECT(builder));
742 
743  LEAVE("added content to page");
744 }
745 
746 
747 static gint
748 tab_cmp (GtkWidget *page_a, GtkWidget *page_b, GtkNotebook *notebook)
749 {
750  return g_utf8_collate (gtk_notebook_get_tab_label_text (notebook, page_a),
751  gtk_notebook_get_tab_label_text (notebook, page_b));
752 }
753 
754 
755 static void
756 gnc_prefs_sort_pages (GtkNotebook *notebook)
757 {
758  gint n_pages, i;
759  GList *tabs = NULL, *iter = NULL;
760 
761  g_return_if_fail (GTK_IS_NOTEBOOK(notebook));
762 
763  /* gather tabs */
764  n_pages = gtk_notebook_get_n_pages (notebook);
765  for (i = n_pages - 1; i >= 0; i--)
766  tabs = g_list_prepend (tabs, gtk_notebook_get_nth_page (notebook, i));
767 
768  /* sort in local copy */
769  tabs = g_list_sort_with_data (tabs, (GCompareDataFunc) tab_cmp, notebook);
770 
771  /* reorder tabs */
772  for (i = 0, iter = tabs; iter; i++, iter = iter->next)
773  gtk_notebook_reorder_child (notebook, GTK_WIDGET(iter->data), i);
774 
775  g_list_free (tabs);
776 }
777 
778 
779 /*******************************/
780 /* Dynamically added Callbacks */
781 /*******************************/
782 
783 static void
784 gnc_prefs_split_widget_name (const gchar *name, gchar **group, gchar **pref)
785 {
786  const gchar *group_with_pref = name + PREF_PREFIX_LEN;
787  gchar **splits = g_strsplit (group_with_pref, "/", 0);
788 
789  *group = g_strdup (splits[0]);
790  *pref = g_strdup (splits[1]);
791  g_strfreev (splits);
792 }
793 
794 /****************************************************************************/
795 
802 static void
803 gnc_prefs_connect_font_button (GtkFontButton *fb)
804 {
805  gchar *group, *pref;
806 
807  g_return_if_fail (GTK_IS_FONT_BUTTON(fb));
808 
809  gnc_prefs_split_widget_name (gtk_buildable_get_name (GTK_BUILDABLE(fb)), &group, &pref);
810  gnc_prefs_bind (group, pref, G_OBJECT (fb), "font-name");
811 
812  g_free (group);
813  g_free (pref);
814 
815  gtk_widget_show_all (GTK_WIDGET(fb));
816 }
817 
818 /****************************************************************************/
819 
826 static void
827 file_chooser_selected_cb (GtkFileChooser *fc, gpointer user_data)
828 {
829  GtkImage *image = g_object_get_data (G_OBJECT(fc), "path_head_error");
830  const gchar *group = g_object_get_data (G_OBJECT(fc), "group");
831  const gchar *pref = g_object_get_data (G_OBJECT(fc), "pref");
832  gchar *folder_uri = gtk_file_chooser_get_uri (fc);
833  gchar *old_path_head_uri = gnc_doclink_get_path_head ();
834 
835  // make sure path_head ends with a trailing '/', 3.5 onwards
836  if (!g_str_has_suffix (folder_uri, "/"))
837  {
838  gchar *folder_with_slash = g_strconcat (folder_uri, "/", NULL);
839  g_free (folder_uri);
840  folder_uri = g_strdup (folder_with_slash);
841  g_free (folder_with_slash);
842  }
843 
844  gtk_widget_hide (GTK_WIDGET(image));
845 
846  if (!gnc_prefs_set_string (group, pref, folder_uri))
847  PINFO("Failed to save preference at %s, %s with %s", group, pref, folder_uri);
848  else
849  gnc_doclink_pref_path_head_changed (
850  GTK_WINDOW(gtk_widget_get_toplevel (GTK_WIDGET(fc))),
851  old_path_head_uri);
852 
853  g_free (old_path_head_uri);
854  g_free (folder_uri);
855 }
856 
865 static void
866 gnc_prefs_connect_file_chooser_button (GtkFileChooserButton *fcb, const gchar *boxname)
867 {
868  GtkImage *image;
869  gchar *group, *pref;
870  gchar *uri;
871  gboolean folder_set = TRUE;
872 
873  g_return_if_fail (GTK_FILE_CHOOSER_BUTTON(fcb));
874 
875  if (boxname == NULL)
876  gnc_prefs_split_widget_name (gtk_buildable_get_name (GTK_BUILDABLE(fcb)), &group, &pref);
877  else
878  gnc_prefs_split_widget_name (boxname, &group, &pref);
879 
880  uri = gnc_prefs_get_string (group, pref);
881 
882  PINFO("Uri is %s", uri);
883 
884  if (uri && *uri) // default entry
885  {
886  gchar *path_head = g_filename_from_uri (uri, NULL, NULL);
887 
888  // test for current folder present and set chooser to it
889  if (g_file_test (path_head, G_FILE_TEST_IS_DIR))
890  gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER(fcb), uri);
891  else
892  folder_set = FALSE;
893 
894  g_free (path_head);
895  }
896 
897  image = g_object_get_data (G_OBJECT(fcb), "path_head_error");
898 
899  if (folder_set) // If current folder missing, display error and tt message
900  gtk_widget_hide (GTK_WIDGET(image));
901  else
902  {
903  gchar *path_head = gnc_doclink_get_unescape_uri (NULL, uri, "file");
904  gchar *ttip = g_strconcat (_("Path does not exist, "), path_head, NULL);
905 
906  gtk_widget_set_tooltip_text (GTK_WIDGET(image), ttip);
907  gtk_widget_show (GTK_WIDGET(image));
908 
909  g_free (ttip);
910  g_free (path_head);
911  }
912 
913  g_signal_connect (GTK_FILE_CHOOSER(fcb), "selection-changed",
914  G_CALLBACK(file_chooser_selected_cb), NULL);
915 
916  g_object_set_data_full (G_OBJECT(fcb),"group", g_strdup (group), (GDestroyNotify) g_free);
917  g_object_set_data_full (G_OBJECT(fcb),"pref", g_strdup (pref), (GDestroyNotify) g_free);
918 
919  g_free (group);
920  g_free (pref);
921  g_free (uri);
922 
923  gtk_widget_show_all (GTK_WIDGET(fcb));
924 }
925 
934 static void
935 file_chooser_clear_cb (GtkButton *button, gpointer user_data)
936 {
937  GtkFileChooserButton *fcb = GTK_FILE_CHOOSER_BUTTON(user_data);
938  const gchar *group = g_object_get_data (G_OBJECT(fcb), "group");
939  const gchar *pref = g_object_get_data (G_OBJECT(fcb), "pref");
940  GtkImage *image = g_object_get_data (G_OBJECT(fcb), "path_head_error");
941  GtkWidget *box;
942  GtkWidget *fcb_new;
943  gchar *boxname;
944  gchar *old_path_head_uri = gnc_doclink_get_path_head ();
945 
946  /* We need to destroy the GtkFileChooserButton and recreate as there
947  does not seem to be away of resetting the folder path to NONE */
948  box = gtk_widget_get_parent (GTK_WIDGET(fcb));
949  g_signal_handlers_disconnect_by_func (button, file_chooser_clear_cb, fcb);
950 
951  if (!gnc_prefs_set_string (group, pref, ""))
952  PINFO("Failed to Clear preference at %s, %s", group, pref);
953  else
954  gnc_doclink_pref_path_head_changed (
955  GTK_WINDOW(gtk_widget_get_toplevel (GTK_WIDGET(fcb))),
956  old_path_head_uri);
957 
958  gtk_widget_destroy (GTK_WIDGET(fcb));
959 
960  fcb_new = gtk_file_chooser_button_new (_("Select a folder"),
961  GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
962 
963  g_object_set_data (G_OBJECT(fcb_new), "path_head_error", image);
964  g_object_set_data_full (G_OBJECT(fcb_new),"group", g_strdup (group), (GDestroyNotify) g_free);
965  g_object_set_data_full (G_OBJECT(fcb_new),"pref", g_strdup (pref), (GDestroyNotify) g_free);
966 
967  gtk_box_pack_start (GTK_BOX(box), fcb_new, TRUE, TRUE, 0);
968  gtk_box_reorder_child (GTK_BOX(box), fcb_new, 0);
969  gtk_widget_show (fcb_new);
970 
971  g_signal_connect (GTK_BUTTON(button), "clicked",
972  G_CALLBACK(file_chooser_clear_cb), fcb_new);
973 
974  boxname = g_strconcat ("pref/", group, "/", pref, NULL);
975 
976  gnc_prefs_connect_file_chooser_button (GTK_FILE_CHOOSER_BUTTON(fcb_new), boxname);
977  g_free (boxname);
978  g_free (old_path_head_uri);
979 }
980 
981 /****************************************************************************/
982 
990 static void
991 gnc_prefs_connect_radio_button (GtkRadioButton *button)
992 {
993  gchar *group, *pref;
994 
995  g_return_if_fail (GTK_IS_RADIO_BUTTON(button));
996 
997  gnc_prefs_split_widget_name (gtk_buildable_get_name (GTK_BUILDABLE(button)), &group, &pref);
998 
999  gnc_prefs_bind (group, pref, G_OBJECT(button), "active");
1000 
1001  g_free (group);
1002  g_free (pref);
1003 }
1004 
1005 /****************************************************************************/
1006 
1014 static void
1015 gnc_prefs_connect_check_button (GtkCheckButton *button)
1016 {
1017  gchar *group, *pref;
1018 
1019  g_return_if_fail (GTK_IS_CHECK_BUTTON(button));
1020 
1021  gnc_prefs_split_widget_name (gtk_buildable_get_name (GTK_BUILDABLE(button)), &group, &pref);
1022 
1023  gnc_prefs_bind (group, pref, G_OBJECT(button), "active");
1024 
1025  g_free (group);
1026  g_free (pref);
1027 }
1028 
1029 /****************************************************************************/
1030 
1038 static void
1039 gnc_prefs_connect_spin_button (GtkSpinButton *spin)
1040 {
1041  gchar *group, *pref;
1042 
1043  g_return_if_fail (GTK_IS_SPIN_BUTTON(spin));
1044 
1045  gnc_prefs_split_widget_name (gtk_buildable_get_name (GTK_BUILDABLE(spin)), &group, &pref);
1046 
1047  gnc_prefs_bind (group, pref, G_OBJECT(spin), "value");
1048 
1049  g_free (group);
1050  g_free (pref);
1051 }
1052 
1053 /****************************************************************************/
1054 
1061 static void
1062 gnc_prefs_connect_combo_box (GtkComboBox *box)
1063 {
1064  gchar *group, *pref;
1065 
1066  g_return_if_fail (GTK_IS_COMBO_BOX(box));
1067 
1068  gnc_prefs_split_widget_name (gtk_buildable_get_name (GTK_BUILDABLE(box)), &group, &pref);
1069 
1070  gnc_prefs_bind (group, pref, G_OBJECT(box), "active");
1071 
1072  g_free (group);
1073  g_free (pref);
1074 }
1075 
1076 /****************************************************************************/
1077 
1084 static void
1085 gnc_prefs_connect_currency_edit (GNCCurrencyEdit *gce, const gchar *boxname )
1086 {
1087  gchar *group, *pref;
1088 
1089  g_return_if_fail (GNC_IS_CURRENCY_EDIT(gce));
1090 
1091  gnc_prefs_split_widget_name (boxname, &group, &pref);
1092 
1093  gnc_prefs_bind (group, pref, G_OBJECT(gce), "mnemonic");
1094 
1095  g_free (group);
1096  g_free (pref);
1097 
1098  gtk_widget_show_all (GTK_WIDGET(gce));
1099 }
1100 
1101 /****************************************************************************/
1102 
1109 static void
1110 gnc_prefs_connect_entry (GtkEntry *entry)
1111 {
1112  gchar *group, *pref;
1113 
1114  g_return_if_fail (GTK_IS_ENTRY(entry));
1115 
1116  gnc_prefs_split_widget_name (gtk_buildable_get_name (GTK_BUILDABLE(entry)), &group, &pref);
1117 
1118  gnc_prefs_bind (group, pref, G_OBJECT(entry), "text");
1119 
1120  g_free (group);
1121  g_free (pref);
1122 }
1123 
1124 /****************************************************************************/
1125 
1132 static void
1133 gnc_prefs_connect_period_select (GncPeriodSelect *period, const gchar *boxname )
1134 {
1135  gchar *group, *pref;
1136 
1137  g_return_if_fail (GNC_IS_PERIOD_SELECT(period));
1138 
1139  gnc_prefs_split_widget_name (boxname, &group, &pref);
1140 
1141  gnc_prefs_bind (group, pref, G_OBJECT(period), "active");
1142 
1143  g_free (group);
1144  g_free (pref);
1145 }
1146 
1147 /****************************************************************************/
1148 
1155 static void
1156 gnc_prefs_connect_date_edit (GNCDateEdit *gde , const gchar *boxname )
1157 {
1158  gchar *group, *pref;
1159 
1160  g_return_if_fail (GNC_IS_DATE_EDIT(gde));
1161 
1162  gnc_prefs_split_widget_name (boxname, &group, &pref);
1163 
1164  gnc_prefs_bind (group, pref, G_OBJECT(gde), "time");
1165 
1166  g_free (group);
1167  g_free (pref);
1168 }
1169 
1170 
1171 /****************************************************************************/
1172 
1173 /********************/
1174 /* Callbacks */
1175 /********************/
1176 
1177 gboolean
1178 gnc_preferences_delete_event_cb (GtkWidget *widget,
1179  GdkEvent *event,
1180  gpointer user_data)
1181 {
1182  /* need to block this for the account separator test */
1183  return TRUE;
1184 }
1185 
1199 void
1200 gnc_preferences_response_cb (GtkDialog *dialog, gint response, GtkDialog *unused)
1201 {
1202  switch (response)
1203  {
1204  case GTK_RESPONSE_HELP:
1205  gnc_gnome_help (GTK_WINDOW(dialog), DF_MANUAL, DL_GLOBPREFS);
1206  break;
1207 
1208  case GTK_RESPONSE_DELETE_EVENT:
1209  default:
1210  if (gnc_account_separator_validate (GTK_WIDGET(dialog)))
1211  {
1212  gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(dialog));
1213  gnc_unregister_gui_component_by_data (DIALOG_PREFERENCES_CM_CLASS,
1214  dialog);
1215  gtk_widget_destroy (GTK_WIDGET(dialog));
1216  }
1217  else
1218  gnc_preferences_select_account_page (dialog);
1219  break;
1220  }
1221 }
1222 
1223 
1224 /********************/
1225 /* Creation */
1226 /********************/
1227 
1239 static void
1240 gnc_prefs_connect_one (const gchar *name,
1241  GtkWidget *widget,
1242  gpointer user_data)
1243 {
1244  /* These tests must be ordered from more specific widget to less
1245  * specific widget. */
1246 
1247  if (GTK_IS_FONT_BUTTON(widget))
1248  {
1249  DEBUG(" %s - font button", name);
1250  gnc_prefs_connect_font_button (GTK_FONT_BUTTON(widget));
1251  }
1252  else if (GTK_IS_FILE_CHOOSER_BUTTON(widget))
1253  {
1254  DEBUG(" %s - file chooser button", name);
1255  gnc_prefs_connect_file_chooser_button (GTK_FILE_CHOOSER_BUTTON(widget), NULL);
1256  }
1257  else if (GTK_IS_RADIO_BUTTON(widget))
1258  {
1259  DEBUG(" %s - radio button", name);
1260  gnc_prefs_connect_radio_button (GTK_RADIO_BUTTON(widget));
1261  }
1262  else if (GTK_IS_CHECK_BUTTON(widget))
1263  {
1264  DEBUG(" %s - check button", name);
1265  gnc_prefs_connect_check_button (GTK_CHECK_BUTTON(widget));
1266  }
1267  else if (GTK_IS_SPIN_BUTTON(widget))
1268  {
1269  DEBUG(" %s - spin button", name);
1270  gnc_prefs_connect_spin_button (GTK_SPIN_BUTTON(widget));
1271  }
1272  else if (GTK_IS_COMBO_BOX(widget))
1273  {
1274  DEBUG(" %s - combo box", name);
1275  gnc_prefs_connect_combo_box (GTK_COMBO_BOX(widget));
1276  }
1277  else if (GTK_IS_ENTRY(widget))
1278  {
1279  DEBUG(" %s - entry", name);
1280  gnc_prefs_connect_entry (GTK_ENTRY(widget));
1281  }
1282  else if (GTK_IS_BOX(widget))
1283  {
1284  /* Test custom widgets are all children of a hbox */
1285  GtkWidget *widget_child;
1286  GList* child = gtk_container_get_children (GTK_CONTAINER(widget));
1287  widget_child = child->data;
1288  g_list_free (child);
1289  DEBUG(" %s - box", name);
1290  DEBUG("Box widget type is %s and name is %s", gtk_widget_get_name (GTK_WIDGET(widget_child)), name);
1291  if (GNC_IS_CURRENCY_EDIT(widget_child))
1292  {
1293  DEBUG(" %s - currency_edit", name);
1294  gnc_prefs_connect_currency_edit (GNC_CURRENCY_EDIT(widget_child), name );
1295  }
1296  else if (GNC_IS_PERIOD_SELECT(widget_child))
1297  {
1298  DEBUG(" %s - period_select", name);
1299  gnc_prefs_connect_period_select (GNC_PERIOD_SELECT(widget_child), name );
1300  }
1301  else if (GNC_IS_DATE_EDIT(widget_child))
1302  {
1303  DEBUG(" %s - date_edit", name);
1304  gnc_prefs_connect_date_edit (GNC_DATE_EDIT(widget_child), name );
1305  }
1306  else if (GTK_FILE_CHOOSER_BUTTON(widget_child))
1307  {
1308  DEBUG(" %s - file chooser button", name);
1309  gnc_prefs_connect_file_chooser_button (GTK_FILE_CHOOSER_BUTTON(widget_child), name );
1310  }
1311  }
1312  else
1313  {
1314  DEBUG(" %s - unsupported %s", name,
1315  G_OBJECT_TYPE_NAME(G_OBJECT(widget)));
1316  }
1317 }
1318 
1319 
1331 static GtkWidget *
1332 gnc_preferences_dialog_create (GtkWindow *parent)
1333 {
1334  GtkBuilder *builder;
1335  GtkWidget *dialog, *notebook, *label, *image, *spinner, *entry;
1336  GtkWidget *box, *date, *period, *currency, *fcb, *button;
1337  GHashTable *prefs_table;
1338  GDate* gdate = NULL;
1339  gchar buf[128];
1340  GtkListStore *store;
1341  GtkTreePath *path;
1342  GtkTreeIter iter;
1343  gnc_commodity *locale_currency;
1344  const gchar *currency_name;
1345  GDate fy_end;
1346  gboolean date_is_valid = FALSE;
1347 
1348  ENTER("");
1349  DEBUG("Opening dialog-preferences.glade:");
1350  builder = gtk_builder_new ();
1351 
1352  gnc_builder_add_from_file (builder, "dialog-preferences.glade", "auto_decimal_places_adj");
1353  gnc_builder_add_from_file (builder, "dialog-preferences.glade", "autosave_interval_minutes_adj");
1354  gnc_builder_add_from_file (builder, "dialog-preferences.glade", "save_on_close_adj");
1355  gnc_builder_add_from_file (builder, "dialog-preferences.glade", "date_backmonth_adj");
1356  gnc_builder_add_from_file (builder, "dialog-preferences.glade", "default_zoom_adj");
1357  gnc_builder_add_from_file (builder, "dialog-preferences.glade", "max_transactions_adj");
1358  gnc_builder_add_from_file (builder, "dialog-preferences.glade", "key_length_adj");
1359  gnc_builder_add_from_file (builder, "dialog-preferences.glade", "new_search_limit_adj");
1360  gnc_builder_add_from_file (builder, "dialog-preferences.glade", "retain_days_adj");
1361  gnc_builder_add_from_file (builder, "dialog-preferences.glade", "tab_width_adj");
1362  gnc_builder_add_from_file (builder, "dialog-preferences.glade", "date_formats");
1363  gnc_builder_add_from_file (builder, "dialog-preferences.glade", "atm_fee_adj");
1364  gnc_builder_add_from_file (builder, "dialog-preferences.glade", "auto_add_adj");
1365  gnc_builder_add_from_file (builder, "dialog-preferences.glade", "auto_clear_adj");
1366  gnc_builder_add_from_file (builder, "dialog-preferences.glade", "match_adj");
1367  gnc_builder_add_from_file (builder, "dialog-preferences.glade", "likely_day_threshold");
1368  gnc_builder_add_from_file (builder, "dialog-preferences.glade", "unlikely_day_threshold");
1369  gnc_builder_add_from_file (builder, "dialog-preferences.glade", "gnucash_preferences_dialog");
1370 
1371  dialog = GTK_WIDGET(gtk_builder_get_object (builder, "gnucash_preferences_dialog"));
1372 
1373  // Set the name for this dialog so it can be easily manipulated with css
1374  gtk_widget_set_name (GTK_WIDGET(dialog), "gnc-id-preferences");
1375 
1376  /* parent */
1377  gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(parent));
1378 
1379  label = GTK_WIDGET(gtk_builder_get_object (builder, "sample_account"));
1380  g_object_set_data (G_OBJECT(dialog), "sample_account", label);
1381 
1382  image = GTK_WIDGET(gtk_builder_get_object (builder, "separator_error"));
1383  g_object_set_data (G_OBJECT(dialog), "separator_error", image);
1384 
1385  entry = GTK_WIDGET(gtk_builder_get_object (builder, "pref/general/account-separator"));
1386  g_object_set_data (G_OBJECT(dialog), "account-separator", entry);
1387 
1388  spinner = GTK_WIDGET(gtk_builder_get_object (builder, "pref/general/save-on-close-wait-time"));
1389  g_object_set_data (G_OBJECT(dialog), "save_on_close_wait_time", spinner);
1390 
1391  DEBUG("autoconnect");
1392  gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, dialog);
1393 
1394  DEBUG("done");
1395 
1396  notebook = GTK_WIDGET(gtk_builder_get_object (builder, "notebook1"));
1397  prefs_table = g_hash_table_new (g_str_hash, g_str_equal);
1398  g_object_set_data (G_OBJECT(dialog), NOTEBOOK, notebook);
1399  g_object_set_data_full (G_OBJECT(dialog), PREFS_WIDGET_HASH,
1400  prefs_table, (GDestroyNotify)g_hash_table_destroy);
1401 
1402 
1403  if (gnc_current_session_exist())
1404  {
1405  QofBook *book = gnc_get_current_book ();
1406  g_date_clear (&fy_end, 1);
1407  qof_instance_get (QOF_INSTANCE(book),
1408  "fy-end", &fy_end,
1409  NULL);
1410  }
1411  box = GTK_WIDGET(gtk_builder_get_object (builder,
1412  "pref/" GNC_PREFS_GROUP_ACCT_SUMMARY "/" GNC_PREF_START_PERIOD));
1413  period = gnc_period_select_new (TRUE);
1414  gtk_widget_show (period);
1415  gtk_box_pack_start (GTK_BOX(box), period, TRUE, TRUE, 0);
1416  if (date_is_valid)
1417  gnc_period_select_set_fy_end (GNC_PERIOD_SELECT(period), &fy_end);
1418 
1419  box = GTK_WIDGET(gtk_builder_get_object (builder,
1420  "pref/" GNC_PREFS_GROUP_ACCT_SUMMARY "/" GNC_PREF_END_PERIOD));
1421  period = gnc_period_select_new (FALSE);
1422  gtk_widget_show (period);
1423  gtk_box_pack_start (GTK_BOX(box), period, TRUE, TRUE, 0);
1424  if (date_is_valid)
1425  gnc_period_select_set_fy_end (GNC_PERIOD_SELECT(period), &fy_end);
1426 
1427  box = GTK_WIDGET(gtk_builder_get_object (builder,
1428  "pref/" GNC_PREFS_GROUP_ACCT_SUMMARY "/" GNC_PREF_START_DATE));
1429  date = gnc_date_edit_new (gnc_time (NULL), FALSE, FALSE);
1430  gtk_widget_show (date);
1431  gtk_box_pack_start (GTK_BOX(box), date, TRUE, TRUE, 0);
1432 
1433  box = GTK_WIDGET(gtk_builder_get_object (builder,
1434  "pref/" GNC_PREFS_GROUP_ACCT_SUMMARY "/" GNC_PREF_END_DATE));
1435  date = gnc_date_edit_new (gnc_time (NULL), FALSE, FALSE);
1436  gtk_widget_show (date);
1437  gtk_box_pack_start (GTK_BOX(box), date, TRUE, TRUE, 0);
1438 
1439  box = GTK_WIDGET(gtk_builder_get_object (builder,
1440  "pref/" GNC_PREFS_GROUP_GENERAL "/" GNC_PREF_CURRENCY_OTHER));
1441  currency = gnc_currency_edit_new ();
1442  gnc_currency_edit_set_currency (GNC_CURRENCY_EDIT(currency), gnc_default_currency());
1443  gtk_widget_show (currency);
1444  gtk_box_pack_start (GTK_BOX(box), currency, TRUE, TRUE, 0);
1445 
1446  box = GTK_WIDGET(gtk_builder_get_object (builder,
1447  "pref/" GNC_PREFS_GROUP_GENERAL_REPORT "/" GNC_PREF_CURRENCY_OTHER));
1448  currency = gnc_currency_edit_new ();
1449  gnc_currency_edit_set_currency (GNC_CURRENCY_EDIT(currency), gnc_default_currency());
1450  gtk_widget_show (currency);
1451  gtk_box_pack_start (GTK_BOX(box), currency, TRUE, TRUE, 0);
1452 
1453  box = GTK_WIDGET(gtk_builder_get_object (builder,
1454  "pref/" GNC_PREFS_GROUP_GENERAL "/" GNC_DOC_LINK_PATH_HEAD));
1455  fcb = gtk_file_chooser_button_new (_("Select a folder"),
1456  GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
1457  gtk_box_pack_start (GTK_BOX(box), fcb, TRUE, TRUE, 0);
1458  button = gtk_button_new_with_label (_("Clear"));
1459  gtk_box_pack_start (GTK_BOX(box), button, TRUE, TRUE, 0);
1460  gtk_widget_show (button);
1461  g_signal_connect (GTK_BUTTON(button), "clicked",
1462  G_CALLBACK(file_chooser_clear_cb), fcb);
1463 
1464  image = GTK_WIDGET(gtk_builder_get_object (builder, "path_head_error"));
1465  g_object_set_data (G_OBJECT(fcb), "path_head_error", image);
1466 
1467  /* Add to the list of interesting widgets */
1468  gnc_prefs_build_widget_table (builder, dialog);
1469 
1470  g_slist_foreach (add_ins, gnc_preferences_build_page, dialog);
1471 
1472  /* Sort tabs alphabetically */
1473  gnc_prefs_sort_pages (GTK_NOTEBOOK(notebook));
1474  gtk_notebook_set_current_page (GTK_NOTEBOOK(notebook), 0);
1475 
1476  DEBUG("We have the following interesting widgets:");
1477  gnc_prefs_block_all (); // Block All Registered callbacks
1478  g_hash_table_foreach (prefs_table, (GHFunc)gnc_prefs_connect_one, dialog);
1479  gnc_prefs_unblock_all (); // UnBlock All Registered callbacks
1480  DEBUG("Done with interesting widgets.");
1481 
1482  /* Other stuff */
1483  gdate = g_date_new_dmy (31, G_DATE_JULY, 2013);
1484  g_date_strftime (buf, sizeof(buf), "%x", gdate);
1485  store = GTK_LIST_STORE(gtk_builder_get_object (builder, "date_formats"));
1486  path = gtk_tree_path_new_from_indices (QOF_DATE_FORMAT_LOCALE, -1);
1487  if (gtk_tree_model_get_iter (GTK_TREE_MODEL(store), &iter, path))
1488  gtk_list_store_set (store, &iter, 1, buf, -1);
1489  g_date_free (gdate);
1490  gtk_tree_path_free (path);
1491 
1492  locale_currency = gnc_locale_default_currency ();
1493  currency_name = gnc_commodity_get_printname (locale_currency);
1494  label = GTK_WIDGET(gtk_builder_get_object (builder, "locale_currency"));
1495  gtk_label_set_label (GTK_LABEL(label), currency_name);
1496  label = GTK_WIDGET(gtk_builder_get_object (builder, "locale_currency2"));
1497  gtk_label_set_label (GTK_LABEL(label), currency_name);
1498 
1499  button = GTK_WIDGET(gtk_builder_get_object (builder, "pref/general/save-on-close-expires"));
1500  gnc_save_on_close_expires_cb (GTK_TOGGLE_BUTTON(button), dialog);
1501 
1502  g_object_unref (G_OBJECT(builder));
1503 
1504  /* save the original account separator in case it changes */
1505  g_object_set_data_full (G_OBJECT(entry), "original_text",
1506  g_strdup (gtk_entry_get_text (GTK_ENTRY(entry))),
1507  g_free);
1508 
1509  LEAVE("dialog %p", dialog);
1510  return dialog;
1511 }
1512 
1513 
1514 /*************************************/
1515 /* Common callback code */
1516 /*************************************/
1517 
1518 
1519 
1534 static gboolean
1535 show_handler (const char *class_name, gint component_id,
1536  gpointer user_data, gpointer iter_data)
1537 {
1538  GtkWidget *dialog;
1539 
1540  ENTER(" ");
1541  dialog = GTK_WIDGET(user_data);
1542  gtk_window_present (GTK_WINDOW(dialog));
1543  LEAVE(" ");
1544  return(TRUE);
1545 }
1546 
1547 
1554 static void
1555 close_handler (gpointer user_data)
1556 {
1557  GtkWidget *dialog;
1558 
1559  ENTER(" ");
1560  dialog = GTK_WIDGET(user_data);
1561  gnc_unregister_gui_component_by_data (DIALOG_PREFERENCES_CM_CLASS, dialog);
1562  gtk_widget_destroy (dialog);
1563  LEAVE(" ");
1564 }
1565 
1566 
1567 /* This function creates the preferences dialog and presents it to
1568  * the user. The preferences dialog is a singleton, so if a
1569  * preferences dialog already exists it will be raised to the top of
1570  * the window stack instead of creating a new dialog. */
1571 void
1572 gnc_preferences_dialog (GtkWindow *parent)
1573 {
1574  GtkWidget *dialog;
1575 
1576  ENTER("");
1577  if (gnc_forall_gui_components (DIALOG_PREFERENCES_CM_CLASS,
1578  show_handler, NULL))
1579  {
1580  LEAVE("existing window");
1581  return;
1582  }
1583 
1584  dialog = gnc_preferences_dialog_create(parent);
1585 
1586  gnc_restore_window_size (GNC_PREFS_GROUP, GTK_WINDOW(dialog), parent);
1587  gtk_widget_show (dialog);
1588 
1589  gnc_register_gui_component (DIALOG_PREFERENCES_CM_CLASS,
1590  NULL, close_handler, dialog);
1591 
1592  LEAVE(" ");
1593 }
1594 
void gnc_preferences_dialog(GtkWindow *parent)
This function creates the preferences dialog and presents it to the user.
void gnc_save_on_close_expires_cb(GtkToggleButton *button, GtkWidget *dialog)
Called when the save-on-close checkbutton is toggled.
void gnc_preferences_add_page(const gchar *filename, const gchar *widgetname, const gchar *tabname)
This function adds a full page of preferences to the preferences dialog.
void qof_instance_get(const QofInstance *inst, const gchar *first_prop,...)
Wrapper for g_object_get.
gchar * gnc_prefs_get_string(const gchar *group, const gchar *pref_name)
Get a string value from the preferences backend.
void gnc_currency_edit_set_currency(GNCCurrencyEdit *gce, const gnc_commodity *currency)
Set the widget to display a certain currency name.
GtkGrid * grid_from
The grid being copied from.
utility functions for the GnuCash UI
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
void gnc_preferences_add_to_page(const gchar *filename, const gchar *widgetname, const gchar *tabname)
This function adds a partial page of preferences to the preferences dialog.
GList * gnc_account_list_name_violations(QofBook *book, const gchar *separator)
Runs through all the accounts and returns a list of account names that contain the provided separator...
Definition: Account.cpp:271
gboolean full_page
TRUE if this addition represents a full page in the preferences dialog.
GSList * add_ins
A list of all additions that have been made to the preferences dialog.
gboolean gnc_prefs_set_string(const gchar *group, const gchar *pref_name, const gchar *value)
Store a string into the preferences backend.
Definition: gnc-prefs.c:319
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
gnc_commodity * gnc_default_currency(void)
Return the default currency set by the user.
Currency selection widget.
GtkGrid * grid_to
The grid being copied to.
Account handling public routines.
Gobject helper routines.
gchar * gnc_account_name_violations_errmsg(const gchar *separator, GList *invalid_account_names)
Composes a translatable error message showing which account names clash with the current account sepa...
Definition: Account.cpp:233
void gnc_prefs_bind(const gchar *group, const gchar *pref_name, gpointer object, const gchar *property)
Bind a setting to a g_object property.
Definition: gnc-prefs.c:181
This data structure is used while building the preferences dialog to copy a grid from a glade file to...
gint cols
The number of columns and rows in the grid.
void gnc_gnome_help(GtkWindow *parent, const char *file_name, const char *anchor)
Launch the systems default help browser, gnome&#39;s yelp for linux, and open to a given link within a gi...
gchar * tabname
The name of the tab within the preferences dialog where these widgets should be placed.
void gnc_period_select_set_fy_end(GncPeriodSelect *period, const GDate *fy_end)
Set the fiscal year end on a GncPeriodSelect widget.
Dialog for handling user preferences.
All type declarations for the whole Gnucash engine.
gchar * widgetname
The name of the widget within the glade data file that should be added to the preferences dialog...
Generic api to store and retrieve preferences.
void gnc_preferences_response_cb(GtkDialog *dialog, gint response, GtkDialog *unused)
Handle a user click on one of the buttons at the bottom of the preference dialog. ...
const char * gnc_commodity_get_printname(const gnc_commodity *cm)
Retrieve the &#39;print&#39; name for the specified commodity.
gchar * filename
The relative name of the file where the glade data for this addition can be found.
GtkWidget * gnc_currency_edit_new(void)
Create a new GNCCurrencyEdit widget which can be used to provide an easy way to enter ISO currency co...
This data structure holds the information for a single addition to the preferences dialog...
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
time64 gnc_time(time64 *tbuf)
get the current time
Definition: gnc-date.cpp:261
GtkWidget * gnc_period_select_new(gboolean starting_labels)
Create a new GncPeriodSelect widget which is used to select a accounting period like "previous month"...
void gnc_prefs_block_all(void)
Block all preference callbacks.
Definition: gnc-prefs.c:379
Take from locale information.
Definition: gnc-date.h:128
void gnc_prefs_unblock_all(void)
Unblock all preferences callbacks.
Definition: gnc-prefs.c:385
A custom widget for selecting accounting periods.
gnc_commodity * gnc_locale_default_currency(void)
Returns the default currency of the current locale.
void gnc_account_separator_pref_changed_cb(GtkEntry *entry, GtkWidget *dialog)
This function is called whenever the account separator is changed in the preferences dialog...