GnuCash  5.6-150-g038405b370+
dialog-utils.c
1 /********************************************************************\
2  * dialog-utils.c -- utility functions for creating dialogs *
3  * for GnuCash *
4  * Copyright (C) 1999-2000 Linas Vepstas *
5  * Copyright (C) 2005 David Hampton <hampton@employees.org> *
6  * *
7  * This program is free software; you can redistribute it and/or *
8  * modify it under the terms of the GNU General Public License as *
9  * published by the Free Software Foundation; either version 2 of *
10  * the License, or (at your option) any later version. *
11  * *
12  * This program is distributed in the hope that it will be useful, *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15  * GNU General Public License for more details. *
16  * *
17  * You should have received a copy of the GNU General Public License*
18  * along with this program; if not, contact: *
19  * *
20  * Free Software Foundation Voice: +1-617-542-5942 *
21  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
22  * Boston, MA 02110-1301, USA gnu@gnu.org *
23  * *
24 \********************************************************************/
25 
26 #include <config.h>
27 
28 #include <gtk/gtk.h>
29 #include <gdk/gdkkeysyms.h>
30 #include <glib/gi18n.h>
31 #include <gmodule.h>
32 #ifdef HAVE_DLFCN_H
33 # include <dlfcn.h>
34 #endif
35 
36 #include "dialog-utils.h"
37 #include "gnc-commodity.h"
38 #include "gnc-date.h"
39 #include "gnc-path.h"
40 #include "gnc-engine.h"
41 #include "gnc-euro.h"
42 #include "gnc-ui.h"
43 #include "gnc-ui-util.h"
44 #include "gnc-prefs.h"
45 #include "gnc-main-window.h"
46 
47 /* This static indicates the debugging module that this .o belongs to. */
48 static QofLogModule log_module = GNC_MOD_GUI;
49 
50 #define GNC_PREF_LAST_GEOMETRY "last-geometry"
51 
52 /********************************************************************\
53  * gnc_set_label_color *
54  * sets the color of the label given the value *
55  * *
56  * Args: label - gtk label widget *
57  * value - value to use to set color *
58  * Returns: none *
59  \*******************************************************************/
60 void
61 gnc_set_label_color(GtkWidget *label, gnc_numeric value)
62 {
63  gboolean deficit;
64 
65  if (!gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_NEGATIVE_IN_RED))
66  return;
67 
68  deficit = gnc_numeric_negative_p (value);
69 
70  if (deficit)
71  {
72  gnc_widget_style_context_remove_class (GTK_WIDGET(label), "gnc-class-default-color");
73  gnc_widget_style_context_add_class (GTK_WIDGET(label), "gnc-class-negative-numbers");
74  }
75  else
76  {
77  gnc_widget_style_context_remove_class (GTK_WIDGET(label), "gnc-class-negative-numbers");
78  gnc_widget_style_context_add_class (GTK_WIDGET(label), "gnc-class-default-color");
79  }
80 }
81 
82 
83 /********************************************************************\
84  * gnc_restore_window_size *
85  * restores the position and size of the given window, if these *
86  * these parameters have been saved earlier. Does nothing if no *
87  * saved values are found. *
88  * *
89  * Args: group - the preferences group to look in for saved coords *
90  * window - the window for which the coords are to be *
91  * restored *
92  * parent - the parent window that can be used to position *
93  * window when it still has default entries *
94  * Returns: nothing *
95  \*******************************************************************/
96 void
97 gnc_restore_window_size(const char *group, GtkWindow *window, GtkWindow *parent)
98 {
99  gint wpos[2], wsize[2];
100  GVariant *geometry;
101 
102  ENTER("");
103 
104  g_return_if_fail(group != NULL);
105  g_return_if_fail(window != NULL);
106  g_return_if_fail(parent != NULL);
107 
108  if (!gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_SAVE_GEOMETRY))
109  return;
110 
111  geometry = gnc_prefs_get_value (group, GNC_PREF_LAST_GEOMETRY);
112 
113  if (g_variant_is_of_type (geometry, (const GVariantType *) "(iiii)") )
114  {
115  GdkWindow *win = gtk_widget_get_window (GTK_WIDGET(parent));
116  GdkRectangle monitor_size;
117  GdkDisplay *display = gdk_window_get_display (win);
118  GdkMonitor *mon;
119 
120  g_variant_get (geometry, "(iiii)",
121  &wpos[0], &wpos[1],
122  &wsize[0], &wsize[1]);
123 
124  mon = gdk_display_get_monitor_at_point (display, wpos[0], wpos[1]);
125  gdk_monitor_get_geometry (mon, &monitor_size);
126 
127  DEBUG("monitor left top corner x: %d, y: %d, width: %d, height: %d",
128  monitor_size.x, monitor_size.y, monitor_size.width, monitor_size.height);
129  DEBUG("geometry from preferences - group, %s, wpos[0]: %d, wpos[1]: %d, wsize[0]: %d, wsize[1]: %d",
130  group, wpos[0], wpos[1], wsize[0], wsize[1]);
131 
132  /* (-1, -1) means no geometry was saved (default preferences value) */
133  if ((wpos[0] != -1) && (wpos[1] != -1))
134  {
135  /* Keep the window on screen if possible */
136  if (wpos[0] - monitor_size.x + wsize[0] > monitor_size.x + monitor_size.width)
137  wpos[0] = monitor_size.x + monitor_size.width - wsize[0];
138 
139  if (wpos[1] - monitor_size.y + wsize[1] > monitor_size.y + monitor_size.height)
140  wpos[1] = monitor_size.y + monitor_size.height - wsize[1];
141 
142  /* make sure the coordinates have not left the monitor */
143  if (wpos[0] < monitor_size.x)
144  wpos[0] = monitor_size.x;
145 
146  if (wpos[1] < monitor_size.y)
147  wpos[1] = monitor_size.y;
148 
149  DEBUG("geometry after screen adaption - wpos[0]: %d, wpos[1]: %d, wsize[0]: %d, wsize[1]: %d",
150  wpos[0], wpos[1], wsize[0], wsize[1]);
151 
152  gtk_window_move(window, wpos[0], wpos[1]);
153  }
154  else
155  {
156  /* preference is at default value -1,-1,-1,-1 */
157  if (parent != NULL)
158  {
159  gint parent_wpos[2], parent_wsize[2], window_wsize[2];
160  gtk_window_get_position (GTK_WINDOW(parent), &parent_wpos[0], &parent_wpos[1]);
161  gtk_window_get_size (GTK_WINDOW(parent), &parent_wsize[0], &parent_wsize[1]);
162  gtk_window_get_size (GTK_WINDOW(window), &window_wsize[0], &window_wsize[1]);
163 
164  DEBUG("parent window - wpos[0]: %d, wpos[1]: %d, wsize[0]: %d, wsize[1]: %d - window size is %dx%d",
165  parent_wpos[0], parent_wpos[1], parent_wsize[0], parent_wsize[1],
166  window_wsize[0], window_wsize[1]);
167 
168  /* check for gtk default size, no window size specified, let gtk decide location */
169  if ((window_wsize[0] == 200) && (window_wsize[1] == 200))
170  DEBUG("window size not specified, let gtk locate it");
171  else
172  gtk_window_move (window, parent_wpos[0] + (parent_wsize[0] - window_wsize[0])/2,
173  parent_wpos[1] + (parent_wsize[1] - window_wsize[1])/2);
174  }
175  }
176 
177  /* Don't attempt to restore invalid sizes */
178  if ((wsize[0] > 0) && (wsize[1] > 0))
179  {
180  wsize[0] = MIN(wsize[0], monitor_size.width - 10);
181  wsize[1] = MIN(wsize[1], monitor_size.height - 10);
182 
183  gtk_window_resize(window, wsize[0], wsize[1]);
184  }
185  }
186  g_variant_unref (geometry);
187  LEAVE("");
188 }
189 
190 
191 /********************************************************************\
192  * gnc_save_window_size *
193  * save the window position and size into options whose names are *
194  * prefixed by the group name. *
195  * *
196  * Args: group - preferences group to save the options in *
197  * window - the window for which current position and size *
198  * are to be saved *
199  * Returns: nothing *
200 \********************************************************************/
201 void
202 gnc_save_window_size(const char *group, GtkWindow *window)
203 {
204  gint wpos[2], wsize[2];
205  GVariant *geometry;
206 
207  ENTER("");
208 
209  g_return_if_fail(group != NULL);
210  g_return_if_fail(window != NULL);
211 
212  if (!gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_SAVE_GEOMETRY))
213  return;
214 
215  gtk_window_get_position(GTK_WINDOW(window), &wpos[0], &wpos[1]);
216  gtk_window_get_size(GTK_WINDOW(window), &wsize[0], &wsize[1]);
217 
218  DEBUG("save geometry - wpos[0]: %d, wpos[1]: %d, wsize[0]: %d, wsize[1]: %d",
219  wpos[0], wpos[1], wsize[0], wsize[1]);
220 
221  geometry = g_variant_new ("(iiii)", wpos[0], wpos[1],
222  wsize[0], wsize[1]);
223  gnc_prefs_set_value (group, GNC_PREF_LAST_GEOMETRY, geometry);
224  /* Don't unref geometry here, it is consumed by gnc_prefs_set_value */
225  LEAVE("");
226 }
227 
228 
229 /********************************************************************\
230  * gnc_window_adjust_for_screen *
231  * adjust the window size if it is bigger than the screen size. *
232  * *
233  * Args: window - the window to adjust *
234  * Returns: nothing *
235 \********************************************************************/
236 void
237 gnc_window_adjust_for_screen(GtkWindow * window)
238 {
239  GdkWindow *win;
240  GdkDisplay *display;
241  GdkMonitor *mon;
242  GdkRectangle monitor_size;
243  gint wpos[2];
244  gint width;
245  gint height;
246 
247  ENTER("");
248 
249  if (window == NULL)
250  return;
251 
252  g_return_if_fail(GTK_IS_WINDOW(window));
253  if (gtk_widget_get_window (GTK_WIDGET(window)) == NULL)
254  return;
255 
256  win = gtk_widget_get_window (GTK_WIDGET(window));
257  display = gdk_window_get_display (win);
258 
259  gtk_window_get_position(GTK_WINDOW(window), &wpos[0], &wpos[1]);
260  gtk_window_get_size(GTK_WINDOW(window), &width, &height);
261 
262  mon = gdk_display_get_monitor_at_point (display, wpos[0], wpos[1]);
263  gdk_monitor_get_geometry (mon, &monitor_size);
264 
265  DEBUG("monitor width is %d, height is %d; wwindow width is %d, height is %d",
266  monitor_size.width, monitor_size.height, width, height);
267 
268  if ((width <= monitor_size.width) && (height <= monitor_size.height))
269  return;
270 
271  /* Keep the window on screen if possible */
272  if (wpos[0] - monitor_size.x + width > monitor_size.x + monitor_size.width)
273  wpos[0] = monitor_size.x + monitor_size.width - width;
274 
275  if (wpos[1] - monitor_size.y + height > monitor_size.y + monitor_size.height)
276  wpos[1] = monitor_size.y + monitor_size.height - height;
277 
278  /* make sure the coordinates have not left the monitor */
279  if (wpos[0] < monitor_size.x)
280  wpos[0] = monitor_size.x;
281 
282  if (wpos[1] < monitor_size.y)
283  wpos[1] = monitor_size.y;
284 
285  DEBUG("move window to position %d, %d", wpos[0], wpos[1]);
286 
287  gtk_window_move(window, wpos[0], wpos[1]);
288 
289  /* if window is bigger, set it to monitor sizes */
290  width = MIN(width, monitor_size.width - 10);
291  height = MIN(height, monitor_size.height - 10);
292 
293  DEBUG("resize window to width %d, height %d", width, height);
294 
295  gtk_window_resize(GTK_WINDOW(window), width, height);
296  gtk_widget_queue_resize(GTK_WIDGET(window));
297  LEAVE("");
298 }
299 
300 /********************************************************************\
301  * Sets the alignment of a Label Widget, GTK3 version specific. *
302  * *
303  * Args: widget - the label widget to set alignment on *
304  * xalign - x alignment *
305  * yalign - y alignment *
306  * Returns: nothing *
307 \********************************************************************/
308 void
309 gnc_label_set_alignment (GtkWidget *widget, gfloat xalign, gfloat yalign)
310 {
311  gtk_label_set_xalign (GTK_LABEL (widget), xalign);
312  gtk_label_set_yalign (GTK_LABEL (widget), yalign);
313 }
314 
315 /********************************************************************\
316  * Get the preference for showing tree view grid lines *
317  * *
318  * Args: none *
319  * Returns: GtkTreeViewGridLines setting *
320 \********************************************************************/
321 GtkTreeViewGridLines
322 gnc_tree_view_get_grid_lines_pref (void)
323 {
324  GtkTreeViewGridLines grid_lines;
325  gboolean h_lines = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_GRID_LINES_HORIZONTAL);
326  gboolean v_lines = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_GRID_LINES_VERTICAL);
327 
328  if (h_lines)
329  {
330  if (v_lines)
331  grid_lines = GTK_TREE_VIEW_GRID_LINES_BOTH;
332  else
333  grid_lines = GTK_TREE_VIEW_GRID_LINES_HORIZONTAL;
334  }
335  else if (v_lines)
336  grid_lines = GTK_TREE_VIEW_GRID_LINES_VERTICAL;
337  else
338  grid_lines = GTK_TREE_VIEW_GRID_LINES_NONE;
339  return grid_lines;
340 }
341 
342 /********************************************************************\
343  * Add a style context to a Widget so it can be altered with css *
344  * *
345  * Args: widget - widget to add css style too *
346  * gnc_class - character string for css class name *
347  * Returns: nothing *
348 \********************************************************************/
349 void
350 gnc_widget_style_context_add_class (GtkWidget *widget, const char *gnc_class)
351 {
352  GtkStyleContext *context = gtk_widget_get_style_context (widget);
353  gtk_style_context_add_class (context, gnc_class);
354 }
355 
356 /********************************************************************\
357  * Remove a style context class from a Widget *
358  * *
359  * Args: widget - widget to remove style class from *
360  * gnc_class - character string for css class name *
361  * Returns: nothing *
362 \********************************************************************/
363 void
364 gnc_widget_style_context_remove_class (GtkWidget *widget, const char *gnc_class)
365 {
366  GtkStyleContext *context = gtk_widget_get_style_context (widget);
367 
368  if (gtk_style_context_has_class (context, gnc_class))
369  gtk_style_context_remove_class (context, gnc_class);
370 }
371 
372 /********************************************************************\
373  * Draw an arrow on a Widget so it can be altered with css *
374  * *
375  * Args: widget - widget to add arrow to in the draw callback *
376  * cr - cairo context for the draw callback *
377  * direction - 0 for up, 1 for down *
378  * Returns: TRUE, stop other handlers being invoked for the event *
379 \********************************************************************/
380 gboolean
381 gnc_draw_arrow_cb (GtkWidget *widget, cairo_t *cr, gpointer direction)
382 {
383  GtkStyleContext *context = gtk_widget_get_style_context (widget);
384  gint width = gtk_widget_get_allocated_width (widget);
385  gint height = gtk_widget_get_allocated_height (widget);
386  gint size;
387 
388  gtk_render_background (context, cr, 0, 0, width, height);
389  gtk_style_context_add_class (context, GTK_STYLE_CLASS_ARROW);
390 
391  size = MIN(width / 2, height / 2);
392 
393  if (GPOINTER_TO_INT(direction) == 0)
394  gtk_render_arrow (context, cr, 0,
395  (width - size)/2, (height - size)/2, size);
396  else
397  gtk_render_arrow (context, cr, G_PI,
398  (width - size)/2, (height - size)/2, size);
399 
400  return TRUE;
401 }
402 
403 
404 gboolean
405 gnc_gdate_in_valid_range (GDate *test_date, gboolean warn)
406 {
407  gboolean use_autoreadonly = qof_book_uses_autoreadonly (gnc_get_current_book());
408  GDate *max_date = g_date_new_dmy (1,1,10000);
409  GDate *min_date;
410  gboolean ret = FALSE;
411  gboolean max_date_ok = FALSE;
412  gboolean min_date_ok = FALSE;
413 
414  if (use_autoreadonly)
415  min_date = qof_book_get_autoreadonly_gdate (gnc_get_current_book());
416  else
417  min_date = g_date_new_dmy (1,1,1400);
418 
419  // max date
420  if (g_date_compare (max_date, test_date) > 0)
421  max_date_ok = TRUE;
422 
423  // min date
424  if (g_date_compare (min_date, test_date) <= 0)
425  min_date_ok = TRUE;
426 
427  if (use_autoreadonly && warn)
428  ret = max_date_ok;
429  else
430  ret = min_date_ok & max_date_ok;
431 
432  if (warn && !ret)
433  {
434  // Translators: Use your locale date format here!
435  gchar *dialog_msg = _("The entered date is out of the range "
436  "01/01/1400 - 31/12/9999, resetting to this year");
437  gchar *dialog_title = _("Date out of range");
438  GtkWidget *dialog = gtk_message_dialog_new (gnc_ui_get_main_window (NULL),
439  0,
440  GTK_MESSAGE_ERROR,
441  GTK_BUTTONS_OK,
442  "%s", dialog_title);
443  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG(dialog),
444  "%s", dialog_msg);
445  gtk_dialog_run (GTK_DIALOG(dialog));
446  gtk_widget_destroy (dialog);
447  }
448  g_date_free (max_date);
449  g_date_free (min_date);
450  return ret;
451 }
452 
453 
454 gboolean
455 gnc_handle_date_accelerator (GdkEventKey *event,
456  struct tm *tm,
457  const char *date_str)
458 {
459  GDate gdate;
460 
461  g_return_val_if_fail (event != NULL, FALSE);
462  g_return_val_if_fail (tm != NULL, FALSE);
463  g_return_val_if_fail (date_str != NULL, FALSE);
464 
465  if (event->type != GDK_KEY_PRESS)
466  return FALSE;
467 
468  if ((tm->tm_mday <= 0) || (tm->tm_mon == -1) || (tm->tm_year == -1))
469  return FALSE;
470 
471  // Make sure we have a valid date before we proceed
472  if (!g_date_valid_dmy (tm->tm_mday, tm->tm_mon + 1, tm->tm_year + 1900))
473  return FALSE;
474 
475  g_date_set_dmy (&gdate,
476  tm->tm_mday,
477  tm->tm_mon + 1,
478  tm->tm_year + 1900);
479 
480  /*
481  * Check those keys where the code does different things depending
482  * upon the modifiers.
483  */
484  switch (event->keyval)
485  {
486  case GDK_KEY_KP_Add:
487  case GDK_KEY_plus:
488  case GDK_KEY_equal:
489  case GDK_KEY_semicolon: // See https://bugs.gnucash.org/show_bug.cgi?id=798386
490  if (event->state & GDK_SHIFT_MASK)
491  g_date_add_days (&gdate, 7);
492  else if (event->state & GDK_MOD1_MASK)
493  g_date_add_months (&gdate, 1);
494  else if (event->state & GDK_CONTROL_MASK)
495  g_date_add_years (&gdate, 1);
496  else
497  g_date_add_days (&gdate, 1);
498 
499  if (gnc_gdate_in_valid_range (&gdate, FALSE))
500  g_date_to_struct_tm (&gdate, tm);
501  return TRUE;
502 
503  case GDK_KEY_minus:
504  case GDK_KEY_KP_Subtract:
505  case GDK_KEY_underscore:
506  if ((strlen (date_str) != 0) && (dateSeparator () == '-'))
507  {
508  const char *c;
509  gunichar uc;
510  int count = 0;
511 
512  /* rough check for existing date */
513  c = date_str;
514  while (*c)
515  {
516  uc = g_utf8_get_char (c);
517  if (uc == '-')
518  count++;
519  c = g_utf8_next_char (c);
520  }
521 
522  if (count < 2)
523  return FALSE;
524  }
525 
526  if (event->state & GDK_SHIFT_MASK)
527  g_date_subtract_days (&gdate, 7);
528  else if (event->state & GDK_MOD1_MASK)
529  g_date_subtract_months (&gdate, 1);
530  else if (event->state & GDK_CONTROL_MASK)
531  g_date_subtract_years (&gdate, 1);
532  else
533  g_date_subtract_days (&gdate, 1);
534 
535  if (gnc_gdate_in_valid_range (&gdate, FALSE))
536  g_date_to_struct_tm (&gdate, tm);
537  return TRUE;
538 
539  default:
540  break;
541  }
542 
543  /*
544  * Control and Alt key combinations should be ignored by this
545  * routine so that the menu system gets to handle them. This
546  * prevents weird behavior of the menu accelerators (i.e. work in
547  * some widgets but not others.)
548  */
549  if (event->state & (GDK_MODIFIER_INTENT_DEFAULT_MOD_MASK))
550  return FALSE;
551 
552  /* Now check for the remaining keystrokes. */
553  switch (event->keyval)
554  {
555  case GDK_KEY_braceright:
556  case GDK_KEY_bracketright:
557  /* increment month */
558  g_date_add_months (&gdate, 1);
559  break;
560 
561  case GDK_KEY_braceleft:
562  case GDK_KEY_bracketleft:
563  /* decrement month */
564  g_date_subtract_months (&gdate, 1);
565  break;
566 
567  case GDK_KEY_M:
568  case GDK_KEY_m:
569  /* beginning of month */
570  g_date_set_day (&gdate, 1);
571  break;
572 
573  case GDK_KEY_H:
574  case GDK_KEY_h:
575  /* end of month */
576  g_date_set_day (&gdate, 1);
577  g_date_add_months (&gdate, 1);
578  g_date_subtract_days (&gdate, 1);
579  break;
580 
581  case GDK_KEY_Y:
582  case GDK_KEY_y:
583  /* beginning of year */
584  g_date_set_day (&gdate, 1);
585  g_date_set_month (&gdate, 1);
586  break;
587 
588  case GDK_KEY_R:
589  case GDK_KEY_r:
590  /* end of year */
591  g_date_set_day (&gdate, 1);
592  g_date_set_month (&gdate, 1);
593  g_date_add_years (&gdate, 1);
594  g_date_subtract_days (&gdate, 1);
595  break;
596 
597  case GDK_KEY_T:
598  case GDK_KEY_t:
599  /* today */
600  gnc_gdate_set_today (&gdate);
601  break;
602 
603  default:
604  return FALSE;
605  }
606  if (gnc_gdate_in_valid_range (&gdate, FALSE))
607  g_date_to_struct_tm (&gdate, tm);
608 
609  return TRUE;
610 }
611 
612 
613 /*--------------------------------------------------------------------------
614  * GtkBuilder support functions
615  *-------------------------------------------------------------------------*/
616 
617 GModule *allsymbols = NULL;
618 
619 /* gnc_builder_add_from_file:
620  *
621  * a convenience wrapper for gtk_builder_add_objects_from_file.
622  * It takes care of finding the directory for glade files and prints a
623  * warning message in case of an error.
624  */
625 gboolean
626 gnc_builder_add_from_file (GtkBuilder *builder, const char *filename, const char *root)
627 {
628  GError* error = NULL;
629  char *fname;
630  gchar *gnc_builder_dir;
631  gboolean result;
632 
633  g_return_val_if_fail (builder != NULL, FALSE);
634  g_return_val_if_fail (filename != NULL, FALSE);
635  g_return_val_if_fail (root != NULL, FALSE);
636 
637  gnc_builder_dir = gnc_path_get_gtkbuilderdir ();
638  fname = g_build_filename(gnc_builder_dir, filename, (char *)NULL);
639  g_free (gnc_builder_dir);
640 
641  {
642  gchar *localroot = g_strdup(root);
643  gchar *objects[] = { localroot, NULL };
644  result = gtk_builder_add_objects_from_file (builder, fname, objects, &error);
645  if (!result)
646  {
647  PWARN ("Couldn't load builder file: %s", error->message);
648  g_error_free (error);
649  }
650  g_free (localroot);
651  }
652 
653  g_free (fname);
654 
655  return result;
656 }
657 
658 
659 /*---------------------------------------------------------------------
660  * The following function is built from a couple of glade functions.
661  *--------------------------------------------------------------------*/
662 void
663 gnc_builder_connect_full_func(GtkBuilder *builder,
664  GObject *signal_object,
665  const gchar *signal_name,
666  const gchar *handler_name,
667  GObject *connect_object,
668  GConnectFlags flags,
669  gpointer user_data)
670 {
671  GCallback func;
672  GCallback *p_func = &func;
673 
674  if (allsymbols == NULL)
675  {
676  /* get a handle on the main executable -- use this to find symbols */
677  allsymbols = g_module_open(NULL, 0);
678  }
679 
680  if (!g_module_symbol(allsymbols, handler_name, (gpointer *)p_func))
681  {
682 #ifdef HAVE_DLSYM
683  /* Fallback to dlsym -- necessary for *BSD linkers */
684  func = dlsym(RTLD_DEFAULT, handler_name);
685 #else
686  func = NULL;
687 #endif
688  if (func == NULL)
689  {
690  PWARN("ggaff: could not find signal handler '%s'.", handler_name);
691  return;
692  }
693  }
694 
695  if (connect_object)
696  g_signal_connect_object (signal_object, signal_name, func,
697  connect_object, flags);
698  else
699  g_signal_connect_data(signal_object, signal_name, func,
700  user_data, NULL , flags);
701 }
702 /*--------------------------------------------------------------------------
703  * End of GtkBuilder utilities
704  *-------------------------------------------------------------------------*/
705 
706 
707 void
708 gnc_gtk_dialog_add_button (GtkWidget *dialog, const gchar *label, const gchar *icon_name, guint response)
709 {
710  GtkWidget *button;
711 
712  button = gtk_button_new_with_mnemonic(label);
713  if (icon_name)
714  {
715  GtkWidget *image;
716 
717  image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_BUTTON);
718  gtk_button_set_image (GTK_BUTTON(button), image);
719  g_object_set (button, "always-show-image", TRUE, NULL);
720  }
721  g_object_set (button, "can-default", TRUE, NULL);
722  gtk_widget_show_all(button);
723  gtk_dialog_add_action_widget(GTK_DIALOG(dialog), button, response);
724 }
725 
726 static void
727 gnc_perm_button_cb (GtkButton *perm, gpointer user_data)
728 {
729  gboolean perm_active;
730 
731  perm_active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(perm));
732  gtk_widget_set_sensitive(user_data, !perm_active);
733 }
734 
735 gint
736 gnc_dialog_run (GtkDialog *dialog, const gchar *pref_name)
737 {
738  GtkWidget *perm, *temp;
739  gboolean ask = TRUE;
740  gint response;
741 
742  /* Does the user want to see this question? If not, return the
743  * previous answer. */
744  response = gnc_prefs_get_int(GNC_PREFS_GROUP_WARNINGS_PERM, pref_name);
745  if (response != 0)
746  return response;
747  response = gnc_prefs_get_int(GNC_PREFS_GROUP_WARNINGS_TEMP, pref_name);
748  if (response != 0)
749  return response;
750 
751  /* Add in the checkboxes to find out if the answer should be remembered. */
752  if (GTK_IS_MESSAGE_DIALOG(dialog))
753  {
754  GtkMessageType type;
755  g_object_get(dialog, "message-type", &type, (gchar*)NULL);
756  ask = (type == GTK_MESSAGE_QUESTION || type == GTK_MESSAGE_WARNING);
757  }
758  perm = gtk_check_button_new_with_mnemonic
759  (ask
760  ? _("Remember and don't _ask me again.")
761  : _("Don't _tell me again."));
762  temp = gtk_check_button_new_with_mnemonic
763  (ask
764  ? _("Remember and don't ask me again this _session.")
765  : _("Don't tell me again this _session."));
766  gtk_widget_show(perm);
767  gtk_widget_show(temp);
768  gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (dialog)), perm, TRUE, TRUE, 0);
769  gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (dialog)), temp, TRUE, TRUE, 0);
770  g_signal_connect(perm, "clicked", G_CALLBACK(gnc_perm_button_cb), temp);
771 
772  /* OK. Present the dialog. */
773  response = gtk_dialog_run(dialog);
774  if ((response == GTK_RESPONSE_NONE) || (response == GTK_RESPONSE_DELETE_EVENT))
775  {
776  return GTK_RESPONSE_CANCEL;
777  }
778 
779  if (response != GTK_RESPONSE_CANCEL)
780  {
781  /* Save the answer? */
782  if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(perm)))
783  {
784  gnc_prefs_set_int(GNC_PREFS_GROUP_WARNINGS_PERM, pref_name, response);
785  }
786  else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(temp)))
787  {
788  gnc_prefs_set_int(GNC_PREFS_GROUP_WARNINGS_TEMP, pref_name, response);
789  }
790  }
791  return response;
792 }
793 
794 /* If this is a new book, this function can be used to display book options
795  * dialog so user can specify options, before any transactions can be
796  * imported/entered, since the book options can affect how transactions are
797  * created. Note: This dialog is modal! */
798 gboolean
799 gnc_new_book_option_display (GtkWidget *parent)
800 {
801  GtkWidget *window;
802  gint result = GTK_RESPONSE_HELP;
803 
804  window = gnc_book_options_dialog_cb (TRUE, _( "New Book Options"),
805  GTK_WINDOW (parent));
806  if (window)
807  {
808  /* close dialog and proceed unless help button selected */
809  while (result == GTK_RESPONSE_HELP)
810  {
811  result = gtk_dialog_run(GTK_DIALOG(window));
812  }
813  return FALSE;
814  }
815  return TRUE;
816 }
817 
818 gchar*
819 gnc_get_negative_color (void)
820 {
821  GdkRGBA color;
822  GtkWidget *label = gtk_label_new ("Color");
823  GtkStyleContext *context = gtk_widget_get_style_context (GTK_WIDGET(label));
824  gtk_style_context_add_class (context, "gnc-class-negative-numbers");
825  gtk_style_context_get_color (context, GTK_STATE_FLAG_NORMAL, &color);
826 
827  return gdk_rgba_to_string (&color);
828 }
829 
830 void
831 gnc_owner_window_set_title (GtkWindow *window, const char *header,
832  GtkWidget *owner_entry, GtkWidget *id_entry)
833 {
834  const char *name = gtk_entry_get_text (GTK_ENTRY (owner_entry));
835  if (!name || *name == '\0')
836  name = _("<No name>");
837 
838  const char *id = gtk_entry_get_text (GTK_ENTRY (id_entry));
839 
840  char *title = (id && *id) ?
841  g_strdup_printf ("%s - %s (%s)", header, name, id) :
842  g_strdup_printf ("%s - %s", header, name);
843 
844  gtk_window_set_title (window, title);
845 
846  g_free (title);
847 }
gchar dateSeparator(void)
dateSeparator Return the field separator for the current date format
Definition: gnc-date.cpp:926
gboolean gnc_prefs_set_value(const gchar *group, const gchar *pref_name, GVariant *value)
Store an arbitrary combination of values into the preferences backend.
Definition: gnc-prefs.c:351
Date and Time handling routines.
GtkWindow * gnc_ui_get_main_window(GtkWidget *widget)
Get a pointer to the final GncMainWindow widget is rooted in.
utility functions for the GnuCash UI
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
void gnc_gdate_set_today(GDate *gd)
Set a GDate to the current day.
Definition: gnc-date.cpp:1236
gboolean gnc_prefs_set_int(const gchar *group, const gchar *pref_name, gint value)
Store an integer value into the preferences backend.
Definition: gnc-prefs.c:289
GtkWidget * gnc_book_options_dialog_cb(gboolean modal, gchar *title, GtkWindow *parent)
Opens the Book Options dialog.
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
gint gnc_prefs_get_int(const gchar *group, const gchar *pref_name)
Get an integer value from the preferences backend.
Functions for adding content to a window.
gboolean gnc_numeric_negative_p(gnc_numeric a)
Returns 1 if a < 0, otherwise returns 0.
#define PWARN(format, args...)
Log a warning.
Definition: qoflog.h:250
GDate * qof_book_get_autoreadonly_gdate(const QofBook *book)
Returns the GDate that is the threshold for auto-read-only.
Definition: qofbook.cpp:988
All type declarations for the whole Gnucash engine.
GVariant * gnc_prefs_get_value(const gchar *group, const gchar *pref_name)
Get an arbitrary combination of values from the preferences backend.
Definition: gnc-prefs.c:268
Generic api to store and retrieve preferences.
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Get a boolean value from the preferences backend.
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
gboolean qof_book_uses_autoreadonly(const QofBook *book)
Returns TRUE if the auto-read-only feature should be used, otherwise FALSE.
Definition: qofbook.cpp:962
Commodity handling public routines.