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-uri-utils.h>
79 #include <gnc-session.h>
80 #include "gnc-component-manager.h"
81 #include "dialog-preferences.h"
82 #include "dialog-doclink-utils.h"
83 
84 #define DIALOG_PREFERENCES_CM_CLASS "dialog-newpreferences"
85 #define GNC_PREFS_GROUP "dialogs.preferences"
86 #define PREF_PREFIX_LEN sizeof("pref/") - 1
87 #define PREFS_WIDGET_HASH "prefs_widget_hash"
88 #define NOTEBOOK "notebook"
89 
91 static QofLogModule log_module = GNC_MOD_PREFS;
92 
93 void gnc_preferences_response_cb (GtkDialog *dialog, gint response, GtkDialog *unused);
94 void gnc_account_separator_pref_changed_cb (GtkEntry *entry, GtkWidget *dialog);
95 void gnc_save_on_close_expires_cb (GtkToggleButton *button, GtkWidget *dialog);
96 gboolean gnc_preferences_delete_event_cb (GtkWidget *widget,
97  GdkEvent *event,
98  gpointer user_data);
99 
102 typedef struct addition_t
103 {
106  gchar *filename;
110  gchar *widgetname;
113  gchar *tabname;
116  gboolean full_page;
117 } addition;
118 
122 GSList *add_ins = NULL;
123 
124 static gchar *gnc_account_separator_is_valid (const gchar *separator,
125  gchar **normalized_separator)
126 {
127  QofBook *book;
128  GList *conflict_accts = NULL;
129  gchar *message = NULL;
130 
131  if (!gnc_current_session_exist())
132  return NULL;
133 
134  book = gnc_get_current_book ();
135  *normalized_separator = gnc_normalize_account_separator (separator);
136  conflict_accts = gnc_account_list_name_violations (book, *normalized_separator);
137  if (conflict_accts)
138  message = gnc_account_name_violations_errmsg (*normalized_separator,
139  conflict_accts);
140 
141  g_list_free_full (conflict_accts, g_free);
142  return message;
143 }
144 
155 void
156 gnc_account_separator_pref_changed_cb (GtkEntry *entry, GtkWidget *dialog)
157 {
158  GtkWidget *label, *image;
159  gchar *sample;
160  gchar *separator = NULL;
161 
162  gchar *conflict_msg = gnc_account_separator_is_valid (gtk_entry_get_text (entry), &separator);
163 
164  label = g_object_get_data (G_OBJECT(dialog), "sample_account");
165  DEBUG("Sample Account pointer is %p", label);
166  /* Translators: Both %s will be the account separator character; the
167  resulting string is a demonstration how the account separator
168  character will look like. You can replace these three account
169  names with other account names that are more suitable for your
170  language - just keep in mind to have exactly two %s in your
171  translation. */
172  sample = g_strdup_printf (_("Income%sSalary%sTaxable"),
173  separator, separator);
174  PINFO(" Label set to '%s'", sample);
175  gtk_label_set_text (GTK_LABEL(label), sample);
176  g_free (sample);
177 
178  /* Check if the new separator clashes with existing account names */
179  image = g_object_get_data (G_OBJECT(dialog), "separator_error");
180  DEBUG("Separator Error Image pointer is %p", image);
181 
182  if (conflict_msg)
183  {
184  gtk_widget_set_tooltip_text (GTK_WIDGET(image), conflict_msg);
185  gtk_widget_show (GTK_WIDGET(image));
186  g_free (conflict_msg);
187  }
188  else
189  gtk_widget_hide (GTK_WIDGET(image));
190 
191  g_free (separator);
192 }
193 
194 
204 static gboolean
205 gnc_account_separator_validate (GtkWidget *dialog)
206 {
207  GtkWidget *entry = g_object_get_data (G_OBJECT(dialog), "account-separator");
208  gboolean ret = TRUE;
209  gchar *separator = NULL;
210  gchar *conflict_msg = gnc_account_separator_is_valid (gtk_entry_get_text (GTK_ENTRY(entry)), &separator);
211 
212  /* Check if the new separator clashes with existing account names */
213  if (conflict_msg)
214  {
215  GtkWidget *msg_dialog, *msg_label;
216  GtkBuilder *builder;
217  gint response;
218 
219  builder = gtk_builder_new ();
220  gnc_builder_add_from_file (builder, "dialog-preferences.glade", "separator_validation_dialog");
221 
222  msg_dialog = GTK_WIDGET(gtk_builder_get_object (builder, "separator_validation_dialog"));
223 
224  msg_label = GTK_WIDGET(gtk_builder_get_object (builder, "conflict_message"));
225 
226  gtk_label_set_text (GTK_LABEL(msg_label), conflict_msg);
227 
228  g_object_unref (G_OBJECT(builder));
229  gtk_widget_show_all (msg_dialog);
230 
231  response = gtk_dialog_run (GTK_DIALOG(msg_dialog));
232  if (response == GTK_RESPONSE_ACCEPT) // reset to original
233  {
234  gchar *original_sep = g_object_get_data (G_OBJECT(entry), "original_text");
235 
236  if (original_sep != NULL)
237  gtk_entry_set_text (GTK_ENTRY(entry), original_sep);
238  }
239  else
240  ret = FALSE;
241 
242  g_free (conflict_msg);
243  gtk_widget_destroy (msg_dialog);
244  }
245  g_free (separator);
246  return ret;
247 }
248 
249 
258 static void
259 gnc_preferences_select_account_page (GtkDialog *dialog)
260 {
261  GtkWidget *notebook = g_object_get_data (G_OBJECT(dialog), NOTEBOOK);
262  GList *children = gtk_container_get_children (GTK_CONTAINER(notebook));
263 
264  if (children)
265  {
266  GtkWidget *acc_page = NULL;
267  GList *node;
268 
269  for (node = children; node; node = node->next)
270  {
271  if (g_strcmp0 (gtk_widget_get_name (GTK_WIDGET(node->data)), "accounts_page") == 0)
272  acc_page = node->data;
273  }
274 
275  if (acc_page)
276  gtk_notebook_set_current_page (GTK_NOTEBOOK(notebook),
277  gtk_notebook_page_num (GTK_NOTEBOOK(notebook),
278  acc_page));
279  }
280  g_list_free (children);
281 }
282 
283 
289 void
290 gnc_save_on_close_expires_cb (GtkToggleButton *button, GtkWidget *dialog)
291 {
292  GtkWidget *spinner = g_object_get_data (G_OBJECT(dialog),
293  "save_on_close_wait_time");
294  gtk_widget_set_sensitive (spinner, gtk_toggle_button_get_active (button));
295 }
296 
308 static gint
309 gnc_prefs_compare_addins (addition *a,
310  addition *b)
311 {
312  return g_utf8_collate (a->tabname, b->tabname);
313 }
314 
315 
335 static void
336 gnc_preferences_add_page_internal (const gchar *filename,
337  const gchar *widgetname,
338  const gchar *tabname,
339  gboolean full_page)
340 {
341  addition *add_in, *preexisting;
342  gboolean error = FALSE;
343  GSList *ptr;
344 
345  ENTER("file %s, widget %s, tab %s full page %d",
346  filename, widgetname, tabname, full_page);
347 
348  add_in = g_malloc (sizeof(addition));
349  if (add_in == NULL)
350  {
351  g_critical ("Unable to allocate memory.\n");
352  LEAVE("no memory");
353  return;
354  }
355 
356  add_in->filename = g_strdup (filename);
357  add_in->widgetname = g_strdup (widgetname);
358  add_in->tabname = g_strdup (tabname);
359  add_in->full_page = full_page;
360  if (!add_in->filename || !add_in->widgetname || !add_in->tabname)
361  {
362  g_critical ("Unable to allocate memory.\n");
363  g_free (add_in->filename);
364  g_free (add_in->widgetname);
365  g_free (add_in->tabname);
366  g_free (add_in);
367  LEAVE("no memory");
368  return;
369  }
370 
371  ptr = g_slist_find_custom (add_ins, add_in, (GCompareFunc)gnc_prefs_compare_addins);
372  if (ptr)
373  {
374  /* problem? */
375  preexisting = ptr->data;
376 
377  if (preexisting->full_page)
378  {
379  g_warning ("New tab %s(%s/%s/%s) conflicts with existing tab %s(%s/%s/full)",
380  add_in->tabname, add_in->filename, add_in->widgetname,
381  add_in->full_page ? "full" : "partial",
382  preexisting->tabname, preexisting->filename, preexisting->widgetname);
383  error = TRUE;
384  }
385  else if (add_in->full_page)
386  {
387  g_warning ("New tab %s(%s/%s/%s) conflicts with existing tab %s(%s/%s/partial)",
388  add_in->tabname, add_in->filename, add_in->widgetname,
389  add_in->full_page ? "full" : "partial",
390  preexisting->tabname, preexisting->filename, preexisting->widgetname);
391  error = TRUE;
392  }
393  }
394 
395  if (error)
396  {
397  g_free (add_in->filename);
398  g_free (add_in->widgetname);
399  g_free (add_in->tabname);
400  g_free (add_in);
401  LEAVE("err");
402  return;
403  }
404  else
405  {
406  add_ins = g_slist_append (add_ins, add_in);
407  }
408  LEAVE("");
409 }
410 
411 
412 /* This function adds a full page of preferences to the preferences
413  * dialog. When the dialog is created, the specified content will be
414  * pulled from the specified glade file and added to the preferences
415  * dialog with the specified tab name. The tab name may not be
416  * duplicated. For example, the Business code might have a full page
417  * of its own preferences. */
418 void
419 gnc_preferences_add_page (const gchar *filename,
420  const gchar *widgetname,
421  const gchar *tabname)
422 {
423  gnc_preferences_add_page_internal (filename, widgetname, tabname, TRUE);
424 }
425 
426 
427 /* This function adds a partial page of preferences to the
428  * preferences dialog. When the dialog is created, the specified
429  * content will be pulled from the glade file and added to the
430  * preferences dialog with the specified tab name. The tab name
431  * may be duplicated. For example, the HBCI preferences may share a
432  * "Data Import" page with QIF and other methods. */
433 void
434 gnc_preferences_add_to_page (const gchar *filename,
435  const gchar *widgetname,
436  const gchar *tabname)
437 {
438  gnc_preferences_add_page_internal (filename, widgetname, tabname, FALSE);
439 }
440 
441 
442 /*******************************************************************/
443 
457 static void
458 gnc_prefs_build_widget_table (GtkBuilder *builder,
459  GtkWidget *dialog)
460 {
461  GHashTable *prefs_table;
462  GSList *interesting, *runner;
463  const gchar *name;
464  const gchar *wname;
465  GtkWidget *widget;
466 
467  prefs_table = g_object_get_data (G_OBJECT(dialog), PREFS_WIDGET_HASH);
468 
469  interesting = gtk_builder_get_objects (builder);
470 
471  for (runner = interesting; runner; runner = g_slist_next(runner))
472  {
473  widget = runner->data;
474  if (GTK_IS_WIDGET(widget))
475  {
476  wname = gtk_widget_get_name (widget);
477  name = gtk_buildable_get_name (GTK_BUILDABLE(widget));
478  DEBUG("Widget type is %s and buildable get name is %s", wname, name);
479  if (g_str_has_prefix (name, "pref"))
480  g_hash_table_insert (prefs_table, (gchar *)name, widget);
481  }
482  }
483  g_slist_free (interesting);
484 
485 }
486 
487 
493 struct copy_data
494 {
496  GtkGrid *grid_from;
498  GtkGrid *grid_to;
500  gint cols, rows;
501 };
502 
503 
504 static GtkWidget *
505 gnc_prefs_find_page (GtkNotebook *notebook, const gchar *name)
506 {
507  int n_pages, i;
508  GtkWidget *child;
509  const gchar *child_name;
510 
511  g_return_val_if_fail (GTK_IS_NOTEBOOK(notebook), NULL);
512  g_return_val_if_fail (name, NULL);
513 
514  ENTER("");
515 
516  n_pages = gtk_notebook_get_n_pages (notebook);
517 
518  for (i = 0; i < n_pages; i++)
519  {
520  child = gtk_notebook_get_nth_page (notebook, i);
521  g_return_val_if_fail (child, NULL);
522 
523  child_name = gtk_notebook_get_tab_label_text (notebook, child);
524  g_return_val_if_fail (child_name, NULL);
525 
526  if (g_utf8_collate (name, child_name) == 0)
527  {
528  LEAVE("found at index: %d", i);
529  return child;
530  }
531  }
532 
533  LEAVE("not found");
534  return NULL;
535 }
536 
537 
549 static void
550 gnc_prefs_get_grid_size (GtkWidget *child, gpointer data)
551 {
552  struct copy_data *copydata = data;
553  gint top, left, height, width;
554 
555  gtk_container_child_get (GTK_CONTAINER(copydata->grid_to), child,
556  "left-attach", &left,
557  "top-attach", &top,
558  "height", &height,
559  "width", &width,
560  NULL);
561 
562  if (left + width >= copydata->cols)
563  copydata->cols = left + width;
564 
565  if (top + height >= copydata->rows)
566  copydata->rows = top + height;
567 }
568 
569 
582 static void
583 gnc_prefs_move_grid_entry (GtkWidget *child,
584  gpointer data)
585 {
586  struct copy_data *copydata = data;
587  gint top, left, height, width;
588  gboolean hexpand, vexpand;
589  GtkAlign halign, valign;
590  gint topm, bottomm, leftm, rightm;
591 
592  ENTER("child %p, copy data %p", child, data);
593  gtk_container_child_get (GTK_CONTAINER(copydata->grid_from), child,
594  "left-attach", &left,
595  "top-attach", &top,
596  "height", &height,
597  "width", &width,
598  NULL);
599  hexpand = gtk_widget_get_hexpand (child);
600  vexpand = gtk_widget_get_vexpand (child);
601  halign = gtk_widget_get_halign (child);
602  valign = gtk_widget_get_valign (child);
603 
604  g_object_get (child, "margin-top", &topm, "margin-bottom", &bottomm, NULL);
605  g_object_get (child, "margin-left", &leftm, "margin-right", &rightm, NULL);
606 
607  g_object_ref (child);
608  gtk_container_remove (GTK_CONTAINER(copydata->grid_from), child);
609 
610  gtk_grid_attach (copydata->grid_to, child, left, copydata->rows + top , width, height);
611 
612  gtk_widget_set_hexpand (child, hexpand);
613  gtk_widget_set_vexpand (child, vexpand);
614  gtk_widget_set_halign (child, halign);
615  gtk_widget_set_valign (child, valign);
616 
617  g_object_set (child, "margin-left", leftm, "margin-right", rightm, NULL);
618  g_object_set (child, "margin-top", topm, "margin-bottom", bottomm, NULL);
619 
620  g_object_unref (child);
621  LEAVE(" ");
622 }
623 
624 
636 static void
637 gnc_preferences_build_page (gpointer data,
638  gpointer user_data)
639 {
640  GtkBuilder *builder;
641  GtkWidget *dialog, *existing_content, *new_content, *label;
642  GtkNotebook *notebook;
643  addition *add_in;
644  struct copy_data copydata = {NULL, NULL, 0, 0};
645  gchar **widgetname;
646  gint i;
647 
648  ENTER("add_in %p, dialog %p", data, user_data);
649  add_in = (addition *)data;
650  dialog = user_data;
651 
652  DEBUG("Opening %s to get %s", add_in->filename, add_in->widgetname);
653  builder = gtk_builder_new ();
654 
655  /* Adjustments etc... must come before dialog information */
656  widgetname = g_strsplit (add_in->widgetname, ",", -1);
657 
658  for (i = 0; widgetname[i]; i++)
659  {
660  DEBUG("Opening %s to get content %s", add_in->filename, widgetname[i]);
661  gnc_builder_add_from_file (builder, add_in->filename, widgetname[i]);
662  }
663 
664  DEBUG("Widget Content is %s", widgetname[i - 1]);
665  new_content = GTK_WIDGET(gtk_builder_get_object (builder, widgetname[i - 1]));
666 
667  g_strfreev (widgetname);
668  DEBUG("done");
669 
670  /* Add to the list of interesting widgets */
671  gnc_prefs_build_widget_table (builder, dialog);
672 
673  /* Connect the signals in this glade file. The dialog is passed in
674  * so the callback can find "interesting" widgets from other
675  * glade files if necessary (via the GPREFS_WIDGET_HASH hash table). */
676  gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, dialog);
677 
678  /* Prepare for recursion */
679  notebook = g_object_get_data (G_OBJECT(dialog), NOTEBOOK);
680 
681  if (add_in->full_page)
682  {
683  label = gtk_label_new (add_in->tabname);
684  gnc_label_set_alignment (label, 0.0, 0.5);
685  gtk_notebook_append_page (notebook, new_content, label);
686  g_object_unref (G_OBJECT(builder));
687  LEAVE("appended page");
688  return;
689  }
690 
691  /* Copied grids must be grids */
692  if (!GTK_IS_GRID(new_content))
693  {
694  g_critical ("The object name %s in file %s is not a GtkGrid. It cannot "
695  "be added to the preferences dialog.",
696  add_in->widgetname, add_in->filename);
697  g_object_unref (G_OBJECT(builder));
698  LEAVE("");
699  return;
700  }
701 
702  /* Does the page exist or must we create it */
703  existing_content = gnc_prefs_find_page (notebook, add_in->tabname);
704 
705  if (!existing_content)
706  {
707  /* No existing content with this name. Create a blank page */
708  existing_content = gtk_grid_new ();
709  gtk_container_set_border_width (GTK_CONTAINER(existing_content), 6);
710  label = gtk_label_new (add_in->tabname);
711  gnc_label_set_alignment (label, 0.0, 0.5);
712  gtk_notebook_append_page (notebook, existing_content, label);
713  gtk_widget_show_all (existing_content);
714  DEBUG("created new page %s, appended it", add_in->tabname);
715  }
716  else
717  {
718  /* Lets get the size of the existing grid */
719  copydata.grid_to = GTK_GRID(existing_content);
720  gtk_container_foreach (GTK_CONTAINER(existing_content), gnc_prefs_get_grid_size, &copydata);
721 
722  DEBUG("found existing page %s, grid size is %d x %d", add_in->tabname, copydata.rows, copydata.cols);
723  }
724 
725  /* Maybe add a spacer row */
726  if (copydata.rows > 0)
727  {
728  label = gtk_label_new ("");
729  gtk_widget_show (label);
730  gtk_grid_attach (GTK_GRID(existing_content), label, 0, copydata.rows, 1, 1);
731  copydata.rows = copydata.rows + 1;
732 
733  DEBUG("add spacer row");
734  }
735 
736  /* Now copy all the entries in the grid */
737  copydata.grid_from = GTK_GRID(new_content);
738  copydata.grid_to = GTK_GRID(existing_content);
739  gtk_container_foreach (GTK_CONTAINER(new_content), gnc_prefs_move_grid_entry, &copydata);
740 
741  g_object_ref_sink (new_content);
742  g_object_unref (G_OBJECT(builder));
743 
744  LEAVE("added content to page");
745 }
746 
747 
748 static gint
749 tab_cmp (GtkWidget *page_a, GtkWidget *page_b, GtkNotebook *notebook)
750 {
751  return g_utf8_collate (gtk_notebook_get_tab_label_text (notebook, page_a),
752  gtk_notebook_get_tab_label_text (notebook, page_b));
753 }
754 
755 
756 static void
757 gnc_prefs_sort_pages (GtkNotebook *notebook)
758 {
759  gint n_pages, i;
760  GList *tabs = NULL, *iter = NULL;
761 
762  g_return_if_fail (GTK_IS_NOTEBOOK(notebook));
763 
764  /* gather tabs */
765  n_pages = gtk_notebook_get_n_pages (notebook);
766  for (i = n_pages - 1; i >= 0; i--)
767  tabs = g_list_prepend (tabs, gtk_notebook_get_nth_page (notebook, i));
768 
769  /* sort in local copy */
770  tabs = g_list_sort_with_data (tabs, (GCompareDataFunc) tab_cmp, notebook);
771 
772  /* reorder tabs */
773  for (i = 0, iter = tabs; iter; i++, iter = iter->next)
774  gtk_notebook_reorder_child (notebook, GTK_WIDGET(iter->data), i);
775 
776  g_list_free (tabs);
777 }
778 
779 
780 /*******************************/
781 /* Dynamically added Callbacks */
782 /*******************************/
783 
784 static void
785 gnc_prefs_split_widget_name (const gchar *name, gchar **group, gchar **pref, gchar **value)
786 {
787  const gchar *group_with_pref = name + PREF_PREFIX_LEN;
788  gchar **splits = g_strsplit (group_with_pref, "/", 0);
789  gchar **value_splits = g_strsplit (splits[1], "=", 0);
790 
791  *group = g_strdup (splits[0]);
792  *pref = g_strdup (value_splits[0]);
793  if (value)
794  *value = g_strdup (value_splits[1]); /* may be NULL */
795  g_strfreev (splits);
796  g_strfreev (value_splits);
797 }
798 
799 /****************************************************************************/
800 
807 static void
808 gnc_prefs_connect_font_button (GtkFontButton *fb)
809 {
810  gchar *group, *pref;
811 
812  g_return_if_fail (GTK_IS_FONT_BUTTON(fb));
813 
814  gnc_prefs_split_widget_name (gtk_buildable_get_name (GTK_BUILDABLE(fb)), &group, &pref, NULL);
815  gnc_prefs_bind (group, pref, NULL, G_OBJECT (fb), "font-name");
816 
817  g_free (group);
818  g_free (pref);
819 
820  gtk_widget_show_all (GTK_WIDGET(fb));
821 }
822 
823 /****************************************************************************/
824 
831 static void
832 file_chooser_selected_cb (GtkFileChooser *fc, gpointer user_data)
833 {
834  GtkImage *image = g_object_get_data (G_OBJECT(fc), "path_head_error");
835  const gchar *group = g_object_get_data (G_OBJECT(fc), "group");
836  const gchar *pref = g_object_get_data (G_OBJECT(fc), "pref");
837  gchar *folder_uri = gtk_file_chooser_get_uri (fc);
838  gchar *old_path_head_uri = gnc_doclink_get_path_head ();
839 
840  // make sure path_head ends with a trailing '/', 3.5 onwards
841  if (!g_str_has_suffix (folder_uri, "/"))
842  {
843  gchar *folder_with_slash = g_strconcat (folder_uri, "/", NULL);
844  g_free (folder_uri);
845  folder_uri = g_strdup (folder_with_slash);
846  g_free (folder_with_slash);
847  }
848 
849  gtk_widget_hide (GTK_WIDGET(image));
850 
851  if (!gnc_prefs_set_string (group, pref, folder_uri))
852  PINFO("Failed to save preference at %s, %s with %s", group, pref, folder_uri);
853  else
854  gnc_doclink_pref_path_head_changed (
855  GTK_WINDOW(gtk_widget_get_toplevel (GTK_WIDGET(fc))),
856  old_path_head_uri);
857 
858  g_free (old_path_head_uri);
859  g_free (folder_uri);
860 }
861 
870 static void
871 gnc_prefs_connect_file_chooser_button (GtkFileChooserButton *fcb, const gchar *boxname)
872 {
873  GtkImage *image;
874  gchar *group, *pref;
875  gchar *uri;
876  gboolean folder_set = TRUE;
877 
878  g_return_if_fail (GTK_FILE_CHOOSER_BUTTON(fcb));
879 
880  if (boxname == NULL)
881  gnc_prefs_split_widget_name (gtk_buildable_get_name (GTK_BUILDABLE(fcb)), &group, &pref, NULL);
882  else
883  gnc_prefs_split_widget_name (boxname, &group, &pref, NULL);
884 
885  uri = gnc_prefs_get_string (group, pref);
886 
887  PINFO("Uri is %s", uri);
888 
889  if (uri && *uri) // default entry
890  {
891  gchar *path_head = gnc_uri_get_path (uri);
892 
893  // test for current folder present and set chooser to it
894  if (g_file_test (path_head, G_FILE_TEST_IS_DIR))
895  gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER(fcb), uri);
896  else
897  folder_set = FALSE;
898 
899  g_free (path_head);
900  }
901 
902  image = g_object_get_data (G_OBJECT(fcb), "path_head_error");
903 
904  if (folder_set) // If current folder missing, display error and tt message
905  gtk_widget_hide (GTK_WIDGET(image));
906  else
907  {
908  gchar *path_head = gnc_doclink_get_unescape_uri (NULL, uri, "file");
909  gchar *ttip = g_strconcat (_("Path does not exist, "), path_head, NULL);
910 
911  gtk_widget_set_tooltip_text (GTK_WIDGET(image), ttip);
912  gtk_widget_show (GTK_WIDGET(image));
913 
914  g_free (ttip);
915  g_free (path_head);
916  }
917 
918  g_signal_connect (GTK_FILE_CHOOSER(fcb), "selection-changed",
919  G_CALLBACK(file_chooser_selected_cb), NULL);
920 
921  g_object_set_data_full (G_OBJECT(fcb),"group", g_strdup (group), (GDestroyNotify) g_free);
922  g_object_set_data_full (G_OBJECT(fcb),"pref", g_strdup (pref), (GDestroyNotify) g_free);
923 
924  g_free (group);
925  g_free (pref);
926  g_free (uri);
927 
928  gtk_widget_show_all (GTK_WIDGET(fcb));
929 }
930 
939 static void
940 file_chooser_clear_cb (GtkButton *button, gpointer user_data)
941 {
942  GtkFileChooserButton *fcb = GTK_FILE_CHOOSER_BUTTON(user_data);
943  const gchar *group = g_object_get_data (G_OBJECT(fcb), "group");
944  const gchar *pref = g_object_get_data (G_OBJECT(fcb), "pref");
945  GtkImage *image = g_object_get_data (G_OBJECT(fcb), "path_head_error");
946  GtkWidget *box;
947  GtkWidget *fcb_new;
948  gchar *boxname;
949  gchar *old_path_head_uri = gnc_doclink_get_path_head ();
950 
951  /* We need to destroy the GtkFileChooserButton and recreate as there
952  does not seem to be away of resetting the folder path to NONE */
953  box = gtk_widget_get_parent (GTK_WIDGET(fcb));
954  g_signal_handlers_disconnect_by_func (button, file_chooser_clear_cb, fcb);
955 
956  if (!gnc_prefs_set_string (group, pref, ""))
957  PINFO("Failed to Clear preference at %s, %s", group, pref);
958  else
959  gnc_doclink_pref_path_head_changed (
960  GTK_WINDOW(gtk_widget_get_toplevel (GTK_WIDGET(fcb))),
961  old_path_head_uri);
962 
963  gtk_widget_destroy (GTK_WIDGET(fcb));
964 
965  fcb_new = gtk_file_chooser_button_new (_("Select a folder"),
966  GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
967 
968  g_object_set_data (G_OBJECT(fcb_new), "path_head_error", image);
969  g_object_set_data_full (G_OBJECT(fcb_new),"group", g_strdup (group), (GDestroyNotify) g_free);
970  g_object_set_data_full (G_OBJECT(fcb_new),"pref", g_strdup (pref), (GDestroyNotify) g_free);
971 
972  gtk_box_pack_start (GTK_BOX(box), fcb_new, TRUE, TRUE, 0);
973  gtk_box_reorder_child (GTK_BOX(box), fcb_new, 0);
974  gtk_widget_show (fcb_new);
975 
976  g_signal_connect (GTK_BUTTON(button), "clicked",
977  G_CALLBACK(file_chooser_clear_cb), fcb_new);
978 
979  boxname = g_strconcat ("pref/", group, "/", pref, NULL);
980 
981  gnc_prefs_connect_file_chooser_button (GTK_FILE_CHOOSER_BUTTON(fcb_new), boxname);
982  g_free (boxname);
983  g_free (old_path_head_uri);
984 }
985 
986 /****************************************************************************/
987 
995 static void
996 gnc_prefs_connect_radio_button (GtkRadioButton *button)
997 {
998  gchar *group, *pref, *value;
999 
1000  g_return_if_fail (GTK_IS_RADIO_BUTTON(button));
1001 
1002  gnc_prefs_split_widget_name (gtk_buildable_get_name (GTK_BUILDABLE(button)), &group, &pref, &value);
1003 
1004  gnc_prefs_bind (group, pref, value, G_OBJECT(button), "active");
1005 
1006  g_free (group);
1007  g_free (pref);
1008  g_free (value);
1009 }
1010 
1011 /****************************************************************************/
1012 
1020 static void
1021 gnc_prefs_connect_check_button (GtkCheckButton *button)
1022 {
1023  gchar *group, *pref, *value;
1024 
1025  g_return_if_fail (GTK_IS_CHECK_BUTTON(button));
1026 
1027  gnc_prefs_split_widget_name (gtk_buildable_get_name (GTK_BUILDABLE(button)), &group, &pref, &value);
1028 
1029  gnc_prefs_bind (group, pref, NULL, G_OBJECT(button), "active");
1030 
1031  g_free (group);
1032  g_free (pref);
1033  g_free (value);
1034 }
1035 
1036 /****************************************************************************/
1037 
1045 static void
1046 gnc_prefs_connect_spin_button (GtkSpinButton *spin)
1047 {
1048  gchar *group, *pref;
1049 
1050  g_return_if_fail (GTK_IS_SPIN_BUTTON(spin));
1051 
1052  gnc_prefs_split_widget_name (gtk_buildable_get_name (GTK_BUILDABLE(spin)), &group, &pref, NULL);
1053 
1054  gnc_prefs_bind (group, pref, NULL, G_OBJECT(spin), "value");
1055 
1056  g_free (group);
1057  g_free (pref);
1058 }
1059 
1060 /****************************************************************************/
1061 
1068 static void
1069 gnc_prefs_connect_combo_box (GtkComboBox *box)
1070 {
1071  gchar *group, *pref;
1072 
1073  g_return_if_fail (GTK_IS_COMBO_BOX(box));
1074 
1075  gnc_prefs_split_widget_name (gtk_buildable_get_name (GTK_BUILDABLE(box)), &group, &pref, NULL);
1076 
1077  gnc_prefs_bind (group, pref, NULL, G_OBJECT(box), "active");
1078 
1079  g_free (group);
1080  g_free (pref);
1081 }
1082 
1083 /****************************************************************************/
1084 
1091 static void
1092 gnc_prefs_connect_currency_edit (GNCCurrencyEdit *gce, const gchar *boxname )
1093 {
1094  gchar *group, *pref;
1095 
1096  g_return_if_fail (GNC_IS_CURRENCY_EDIT(gce));
1097 
1098  gnc_prefs_split_widget_name (boxname, &group, &pref, NULL);
1099 
1100  gnc_prefs_bind (group, pref, NULL, G_OBJECT(gce), "mnemonic");
1101 
1102  g_free (group);
1103  g_free (pref);
1104 
1105  gtk_widget_show_all (GTK_WIDGET(gce));
1106 }
1107 
1108 /****************************************************************************/
1109 
1116 static void
1117 gnc_prefs_connect_entry (GtkEntry *entry)
1118 {
1119  gchar *group, *pref;
1120 
1121  g_return_if_fail (GTK_IS_ENTRY(entry));
1122 
1123  gnc_prefs_split_widget_name (gtk_buildable_get_name (GTK_BUILDABLE(entry)), &group, &pref, NULL);
1124 
1125  gnc_prefs_bind (group, pref, NULL, G_OBJECT(entry), "text");
1126 
1127  g_free (group);
1128  g_free (pref);
1129 }
1130 
1131 /****************************************************************************/
1132 
1139 static void
1140 gnc_prefs_connect_period_select (GncPeriodSelect *period, const gchar *boxname )
1141 {
1142  gchar *group, *pref;
1143 
1144  g_return_if_fail (GNC_IS_PERIOD_SELECT(period));
1145 
1146  gnc_prefs_split_widget_name (boxname, &group, &pref, NULL);
1147 
1148  gnc_prefs_bind (group, pref, NULL, G_OBJECT(period), "active");
1149 
1150  g_free (group);
1151  g_free (pref);
1152 }
1153 
1154 /****************************************************************************/
1155 
1162 static void
1163 gnc_prefs_connect_date_edit (GNCDateEdit *gde , const gchar *boxname )
1164 {
1165  gchar *group, *pref;
1166 
1167  g_return_if_fail (GNC_IS_DATE_EDIT(gde));
1168 
1169  gnc_prefs_split_widget_name (boxname, &group, &pref, NULL);
1170 
1171  gnc_prefs_bind (group, pref, NULL, G_OBJECT(gde), "time");
1172 
1173  g_free (group);
1174  g_free (pref);
1175 }
1176 
1177 
1178 /****************************************************************************/
1179 
1180 /********************/
1181 /* Callbacks */
1182 /********************/
1183 
1184 gboolean
1185 gnc_preferences_delete_event_cb (GtkWidget *widget,
1186  GdkEvent *event,
1187  gpointer user_data)
1188 {
1189  /* need to block this for the account separator test */
1190  return TRUE;
1191 }
1192 
1206 void
1207 gnc_preferences_response_cb (GtkDialog *dialog, gint response, GtkDialog *unused)
1208 {
1209  switch (response)
1210  {
1211  case GTK_RESPONSE_HELP:
1212  gnc_gnome_help (GTK_WINDOW(dialog), DF_MANUAL, DL_GLOBPREFS);
1213  break;
1214 
1215  case GTK_RESPONSE_DELETE_EVENT:
1216  default:
1217  if (gnc_account_separator_validate (GTK_WIDGET(dialog)))
1218  {
1219  gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(dialog));
1220  gnc_unregister_gui_component_by_data (DIALOG_PREFERENCES_CM_CLASS,
1221  dialog);
1222  gtk_widget_destroy (GTK_WIDGET(dialog));
1223  }
1224  else
1225  gnc_preferences_select_account_page (dialog);
1226  break;
1227  }
1228 }
1229 
1230 
1231 /********************/
1232 /* Creation */
1233 /********************/
1234 
1246 static void
1247 gnc_prefs_connect_one (const gchar *name,
1248  GtkWidget *widget,
1249  gpointer user_data)
1250 {
1251  /* These tests must be ordered from more specific widget to less
1252  * specific widget. */
1253 
1254  if (GTK_IS_FONT_BUTTON(widget))
1255  {
1256  DEBUG(" %s - font button", name);
1257  gnc_prefs_connect_font_button (GTK_FONT_BUTTON(widget));
1258  }
1259  else if (GTK_IS_FILE_CHOOSER_BUTTON(widget))
1260  {
1261  DEBUG(" %s - file chooser button", name);
1262  gnc_prefs_connect_file_chooser_button (GTK_FILE_CHOOSER_BUTTON(widget), NULL);
1263  }
1264  else if (GTK_IS_RADIO_BUTTON(widget))
1265  {
1266  DEBUG(" %s - radio button", name);
1267  gnc_prefs_connect_radio_button (GTK_RADIO_BUTTON(widget));
1268  }
1269  else if (GTK_IS_CHECK_BUTTON(widget))
1270  {
1271  DEBUG(" %s - check button", name);
1272  gnc_prefs_connect_check_button (GTK_CHECK_BUTTON(widget));
1273  }
1274  else if (GTK_IS_SPIN_BUTTON(widget))
1275  {
1276  DEBUG(" %s - spin button", name);
1277  gnc_prefs_connect_spin_button (GTK_SPIN_BUTTON(widget));
1278  }
1279  else if (GTK_IS_COMBO_BOX(widget))
1280  {
1281  DEBUG(" %s - combo box", name);
1282  gnc_prefs_connect_combo_box (GTK_COMBO_BOX(widget));
1283  }
1284  else if (GTK_IS_ENTRY(widget))
1285  {
1286  DEBUG(" %s - entry", name);
1287  gnc_prefs_connect_entry (GTK_ENTRY(widget));
1288  }
1289  else if (GTK_IS_BOX(widget))
1290  {
1291  /* Test custom widgets are all children of a hbox */
1292  GtkWidget *widget_child;
1293  GList* child = gtk_container_get_children (GTK_CONTAINER(widget));
1294  widget_child = child->data;
1295  g_list_free (child);
1296  DEBUG(" %s - box", name);
1297  DEBUG("Box widget type is %s and name is %s", gtk_widget_get_name (GTK_WIDGET(widget_child)), name);
1298  if (GNC_IS_CURRENCY_EDIT(widget_child))
1299  {
1300  DEBUG(" %s - currency_edit", name);
1301  gnc_prefs_connect_currency_edit (GNC_CURRENCY_EDIT(widget_child), name );
1302  }
1303  else if (GNC_IS_PERIOD_SELECT(widget_child))
1304  {
1305  DEBUG(" %s - period_select", name);
1306  gnc_prefs_connect_period_select (GNC_PERIOD_SELECT(widget_child), name );
1307  }
1308  else if (GNC_IS_DATE_EDIT(widget_child))
1309  {
1310  DEBUG(" %s - date_edit", name);
1311  gnc_prefs_connect_date_edit (GNC_DATE_EDIT(widget_child), name );
1312  }
1313  else if (GTK_FILE_CHOOSER_BUTTON(widget_child))
1314  {
1315  DEBUG(" %s - file chooser button", name);
1316  gnc_prefs_connect_file_chooser_button (GTK_FILE_CHOOSER_BUTTON(widget_child), name );
1317  }
1318  }
1319  else
1320  {
1321  DEBUG(" %s - unsupported %s", name,
1322  G_OBJECT_TYPE_NAME(G_OBJECT(widget)));
1323  }
1324 }
1325 
1326 
1338 static GtkWidget *
1339 gnc_preferences_dialog_create (GtkWindow *parent)
1340 {
1341  GtkBuilder *builder;
1342  GtkWidget *dialog, *notebook, *label, *image, *spinner, *entry;
1343  GtkWidget *box, *date, *period, *currency, *fcb, *button;
1344  GHashTable *prefs_table;
1345  GDate* gdate = NULL;
1346  gchar buf[128];
1347  GtkListStore *store;
1348  GtkTreePath *path;
1349  GtkTreeIter iter;
1350  gnc_commodity *locale_currency;
1351  const gchar *currency_name;
1352  GDate fy_end;
1353  gboolean date_is_valid = FALSE;
1354 
1355  ENTER("");
1356  DEBUG("Opening dialog-preferences.glade:");
1357  builder = gtk_builder_new ();
1358 
1359  gnc_builder_add_from_file (builder, "dialog-preferences.glade", "auto_decimal_places_adj");
1360  gnc_builder_add_from_file (builder, "dialog-preferences.glade", "autosave_interval_minutes_adj");
1361  gnc_builder_add_from_file (builder, "dialog-preferences.glade", "save_on_close_adj");
1362  gnc_builder_add_from_file (builder, "dialog-preferences.glade", "date_backmonth_adj");
1363  gnc_builder_add_from_file (builder, "dialog-preferences.glade", "default_zoom_adj");
1364  gnc_builder_add_from_file (builder, "dialog-preferences.glade", "max_transactions_adj");
1365  gnc_builder_add_from_file (builder, "dialog-preferences.glade", "key_length_adj");
1366  gnc_builder_add_from_file (builder, "dialog-preferences.glade", "new_search_limit_adj");
1367  gnc_builder_add_from_file (builder, "dialog-preferences.glade", "retain_days_adj");
1368  gnc_builder_add_from_file (builder, "dialog-preferences.glade", "tab_width_adj");
1369  gnc_builder_add_from_file (builder, "dialog-preferences.glade", "date_formats");
1370  gnc_builder_add_from_file (builder, "dialog-preferences.glade", "atm_fee_adj");
1371  gnc_builder_add_from_file (builder, "dialog-preferences.glade", "auto_add_adj");
1372  gnc_builder_add_from_file (builder, "dialog-preferences.glade", "auto_clear_adj");
1373  gnc_builder_add_from_file (builder, "dialog-preferences.glade", "match_adj");
1374  gnc_builder_add_from_file (builder, "dialog-preferences.glade", "likely_day_threshold");
1375  gnc_builder_add_from_file (builder, "dialog-preferences.glade", "unlikely_day_threshold");
1376  gnc_builder_add_from_file (builder, "dialog-preferences.glade", "gnucash_preferences_dialog");
1377 
1378  dialog = GTK_WIDGET(gtk_builder_get_object (builder, "gnucash_preferences_dialog"));
1379 
1380  // Set the name for this dialog so it can be easily manipulated with css
1381  gtk_widget_set_name (GTK_WIDGET(dialog), "gnc-id-preferences");
1382 
1383  /* parent */
1384  gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(parent));
1385 
1386  label = GTK_WIDGET(gtk_builder_get_object (builder, "sample_account"));
1387  g_object_set_data (G_OBJECT(dialog), "sample_account", label);
1388 
1389  image = GTK_WIDGET(gtk_builder_get_object (builder, "separator_error"));
1390  g_object_set_data (G_OBJECT(dialog), "separator_error", image);
1391 
1392  entry = GTK_WIDGET(gtk_builder_get_object (builder, "pref/general/account-separator"));
1393  g_object_set_data (G_OBJECT(dialog), "account-separator", entry);
1394 
1395  spinner = GTK_WIDGET(gtk_builder_get_object (builder, "pref/general/save-on-close-wait-time"));
1396  g_object_set_data (G_OBJECT(dialog), "save_on_close_wait_time", spinner);
1397 
1398  DEBUG("autoconnect");
1399  gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, dialog);
1400 
1401  DEBUG("done");
1402 
1403  notebook = GTK_WIDGET(gtk_builder_get_object (builder, "notebook1"));
1404  prefs_table = g_hash_table_new (g_str_hash, g_str_equal);
1405  g_object_set_data (G_OBJECT(dialog), NOTEBOOK, notebook);
1406  g_object_set_data_full (G_OBJECT(dialog), PREFS_WIDGET_HASH,
1407  prefs_table, (GDestroyNotify)g_hash_table_destroy);
1408 
1409 
1410  if (gnc_current_session_exist())
1411  {
1412  QofBook *book = gnc_get_current_book ();
1413  g_date_clear (&fy_end, 1);
1414  qof_instance_get (QOF_INSTANCE(book),
1415  "fy-end", &fy_end,
1416  NULL);
1417  }
1418  box = GTK_WIDGET(gtk_builder_get_object (builder,
1419  "pref/" GNC_PREFS_GROUP_ACCT_SUMMARY "/" GNC_PREF_START_PERIOD));
1420  period = gnc_period_select_new (TRUE);
1421  gtk_widget_show (period);
1422  gtk_box_pack_start (GTK_BOX(box), period, TRUE, TRUE, 0);
1423  if (date_is_valid)
1424  gnc_period_select_set_fy_end (GNC_PERIOD_SELECT(period), &fy_end);
1425 
1426  box = GTK_WIDGET(gtk_builder_get_object (builder,
1427  "pref/" GNC_PREFS_GROUP_ACCT_SUMMARY "/" GNC_PREF_END_PERIOD));
1428  period = gnc_period_select_new (FALSE);
1429  gtk_widget_show (period);
1430  gtk_box_pack_start (GTK_BOX(box), period, TRUE, TRUE, 0);
1431  if (date_is_valid)
1432  gnc_period_select_set_fy_end (GNC_PERIOD_SELECT(period), &fy_end);
1433 
1434  box = GTK_WIDGET(gtk_builder_get_object (builder,
1435  "pref/" GNC_PREFS_GROUP_ACCT_SUMMARY "/" GNC_PREF_START_DATE));
1436  date = gnc_date_edit_new (gnc_time (NULL), FALSE, FALSE);
1437  gtk_widget_show (date);
1438  gtk_box_pack_start (GTK_BOX(box), date, TRUE, TRUE, 0);
1439 
1440  box = GTK_WIDGET(gtk_builder_get_object (builder,
1441  "pref/" GNC_PREFS_GROUP_ACCT_SUMMARY "/" GNC_PREF_END_DATE));
1442  date = gnc_date_edit_new (gnc_time (NULL), FALSE, FALSE);
1443  gtk_widget_show (date);
1444  gtk_box_pack_start (GTK_BOX(box), date, TRUE, TRUE, 0);
1445 
1446  box = GTK_WIDGET(gtk_builder_get_object (builder,
1447  "pref/" GNC_PREFS_GROUP_GENERAL "/" 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_REPORT "/" GNC_PREF_CURRENCY_OTHER));
1455  currency = gnc_currency_edit_new ();
1456  gnc_currency_edit_set_currency (GNC_CURRENCY_EDIT(currency), gnc_default_currency());
1457  gtk_widget_show (currency);
1458  gtk_box_pack_start (GTK_BOX(box), currency, TRUE, TRUE, 0);
1459 
1460  box = GTK_WIDGET(gtk_builder_get_object (builder,
1461  "pref/" GNC_PREFS_GROUP_GENERAL "/" GNC_DOC_LINK_PATH_HEAD));
1462  fcb = gtk_file_chooser_button_new (_("Select a folder"),
1463  GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
1464  gtk_box_pack_start (GTK_BOX(box), fcb, TRUE, TRUE, 0);
1465  button = gtk_button_new_with_label (_("Clear"));
1466  gtk_box_pack_start (GTK_BOX(box), button, TRUE, TRUE, 0);
1467  gtk_widget_show (button);
1468  g_signal_connect (GTK_BUTTON(button), "clicked",
1469  G_CALLBACK(file_chooser_clear_cb), fcb);
1470 
1471  image = GTK_WIDGET(gtk_builder_get_object (builder, "path_head_error"));
1472  g_object_set_data (G_OBJECT(fcb), "path_head_error", image);
1473 
1474  /* Add to the list of interesting widgets */
1475  gnc_prefs_build_widget_table (builder, dialog);
1476 
1477  g_slist_foreach (add_ins, gnc_preferences_build_page, dialog);
1478 
1479  /* Sort tabs alphabetically */
1480  gnc_prefs_sort_pages (GTK_NOTEBOOK(notebook));
1481  gtk_notebook_set_current_page (GTK_NOTEBOOK(notebook), 0);
1482 
1483  DEBUG("We have the following interesting widgets:");
1484  gnc_prefs_block_all (); // Block All Registered callbacks
1485  g_hash_table_foreach (prefs_table, (GHFunc)gnc_prefs_connect_one, dialog);
1486  gnc_prefs_unblock_all (); // UnBlock All Registered callbacks
1487  DEBUG("Done with interesting widgets.");
1488 
1489  /* Other stuff */
1490  gdate = g_date_new_dmy (31, G_DATE_JULY, 2013);
1491  g_date_strftime (buf, sizeof(buf), "%x", gdate);
1492  store = GTK_LIST_STORE(gtk_builder_get_object (builder, "date_formats"));
1493  path = gtk_tree_path_new_from_indices (QOF_DATE_FORMAT_LOCALE, -1);
1494  if (gtk_tree_model_get_iter (GTK_TREE_MODEL(store), &iter, path))
1495  gtk_list_store_set (store, &iter, 1, buf, -1);
1496  g_date_free (gdate);
1497  gtk_tree_path_free (path);
1498 
1499  locale_currency = gnc_locale_default_currency ();
1500  currency_name = gnc_commodity_get_printname (locale_currency);
1501  label = GTK_WIDGET(gtk_builder_get_object (builder, "locale_currency"));
1502  gtk_label_set_label (GTK_LABEL(label), currency_name);
1503  label = GTK_WIDGET(gtk_builder_get_object (builder, "locale_currency2"));
1504  gtk_label_set_label (GTK_LABEL(label), currency_name);
1505 
1506  button = GTK_WIDGET(gtk_builder_get_object (builder, "pref/general/save-on-close-expires"));
1507  gnc_save_on_close_expires_cb (GTK_TOGGLE_BUTTON(button), dialog);
1508 
1509  g_object_unref (G_OBJECT(builder));
1510 
1511  /* save the original account separator in case it changes */
1512  g_object_set_data_full (G_OBJECT(entry), "original_text",
1513  g_strdup (gtk_entry_get_text (GTK_ENTRY(entry))),
1514  g_free);
1515 
1516  LEAVE("dialog %p", dialog);
1517  return dialog;
1518 }
1519 
1520 
1521 /*************************************/
1522 /* Common callback code */
1523 /*************************************/
1524 
1525 
1526 
1541 static gboolean
1542 show_handler (const char *class_name, gint component_id,
1543  gpointer user_data, gpointer iter_data)
1544 {
1545  GtkWidget *dialog;
1546 
1547  ENTER(" ");
1548  dialog = GTK_WIDGET(user_data);
1549  gtk_window_present (GTK_WINDOW(dialog));
1550  LEAVE(" ");
1551  return(TRUE);
1552 }
1553 
1554 
1561 static void
1562 close_handler (gpointer user_data)
1563 {
1564  GtkWidget *dialog;
1565 
1566  ENTER(" ");
1567  dialog = GTK_WIDGET(user_data);
1568  gnc_unregister_gui_component_by_data (DIALOG_PREFERENCES_CM_CLASS, dialog);
1569  gtk_widget_destroy (dialog);
1570  LEAVE(" ");
1571 }
1572 
1573 
1574 /* This function creates the preferences dialog and presents it to
1575  * the user. The preferences dialog is a singleton, so if a
1576  * preferences dialog already exists it will be raised to the top of
1577  * the window stack instead of creating a new dialog. */
1578 void
1579 gnc_preferences_dialog (GtkWindow *parent)
1580 {
1581  GtkWidget *dialog;
1582 
1583  ENTER("");
1584  if (gnc_forall_gui_components (DIALOG_PREFERENCES_CM_CLASS,
1585  show_handler, NULL))
1586  {
1587  LEAVE("existing window");
1588  return;
1589  }
1590 
1591  dialog = gnc_preferences_dialog_create(parent);
1592 
1593  gnc_restore_window_size (GNC_PREFS_GROUP, GTK_WINDOW(dialog), parent);
1594  gtk_widget_show (dialog);
1595 
1596  gnc_register_gui_component (DIALOG_PREFERENCES_CM_CLASS,
1597  NULL, close_handler, dialog);
1598 
1599  LEAVE(" ");
1600 }
1601 
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
gchar * gnc_uri_get_path(const gchar *uri)
Extracts the path part from a uri.
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:273
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:320
#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:235
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.
void gnc_prefs_bind(const gchar *group, const gchar *pref_name, const gchar *pref_value, gpointer object, const gchar *property)
Bind a setting to a g_object property.
Definition: gnc-prefs.c:181
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:260
Utility functions for convert uri in separate components and back.
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:380
Take from locale information.
Definition: gnc-date.h:128
void gnc_prefs_unblock_all(void)
Unblock all preferences callbacks.
Definition: gnc-prefs.c:386
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...