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
gnc_set_label_color(GtkWidget * label,gnc_numeric value)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
gnc_restore_window_size(const char * group,GtkWindow * window,GtkWindow * parent)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
gnc_save_window_size(const char * group,GtkWindow * window)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
gnc_window_adjust_for_screen(GtkWindow * window)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 alignament 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
gnc_label_set_alignment(GtkWidget * widget,gfloat xalign,gfloat yalign)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
gnc_tree_view_get_grid_lines_pref(void)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
gnc_widget_style_context_add_class(GtkWidget * widget,const char * gnc_class)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
gnc_widget_style_context_remove_class(GtkWidget * widget,const char * gnc_class)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
gnc_draw_arrow_cb(GtkWidget * widget,cairo_t * cr,gpointer direction)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
gnc_gdate_in_valid_range(GDate * test_date,gboolean warn)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             // Transators: 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
gnc_handle_date_accelerator(GdkEventKey * event,struct tm * tm,const char * date_str)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         if (event->state & GDK_SHIFT_MASK)
490             g_date_add_days (&gdate, 7);
491         else if (event->state & GDK_MOD1_MASK)
492             g_date_add_months (&gdate, 1);
493         else if (event->state & GDK_CONTROL_MASK)
494             g_date_add_years (&gdate, 1);
495         else
496             g_date_add_days (&gdate, 1);
497 
498         if (gnc_gdate_in_valid_range (&gdate, FALSE))
499             g_date_to_struct_tm (&gdate, tm);
500         return TRUE;
501 
502     case GDK_KEY_minus:
503     case GDK_KEY_KP_Subtract:
504     case GDK_KEY_underscore:
505         if ((strlen (date_str) != 0) && (dateSeparator () == '-'))
506         {
507             const char *c;
508             gunichar uc;
509             int count = 0;
510 
511             /* rough check for existing date */
512             c = date_str;
513             while (*c)
514             {
515                 uc = g_utf8_get_char (c);
516                 if (uc == '-')
517                     count++;
518                 c = g_utf8_next_char (c);
519             }
520 
521             if (count < 2)
522                 return FALSE;
523         }
524 
525         if (event->state & GDK_SHIFT_MASK)
526             g_date_subtract_days (&gdate, 7);
527         else if (event->state & GDK_MOD1_MASK)
528             g_date_subtract_months (&gdate, 1);
529         else if (event->state & GDK_CONTROL_MASK)
530             g_date_subtract_years (&gdate, 1);
531         else
532             g_date_subtract_days (&gdate, 1);
533 
534         if (gnc_gdate_in_valid_range (&gdate, FALSE))
535             g_date_to_struct_tm (&gdate, tm);
536         return TRUE;
537 
538     default:
539         break;
540     }
541 
542     /*
543      * Control and Alt key combinations should be ignored by this
544      * routine so that the menu system gets to handle them.  This
545      * prevents weird behavior of the menu accelerators (i.e. work in
546      * some widgets but not others.)
547      */
548     if (event->state & (GDK_MODIFIER_INTENT_DEFAULT_MOD_MASK))
549         return FALSE;
550 
551     /* Now check for the remaining keystrokes. */
552     switch (event->keyval)
553     {
554     case GDK_KEY_braceright:
555     case GDK_KEY_bracketright:
556         /* increment month */
557         g_date_add_months (&gdate, 1);
558         break;
559 
560     case GDK_KEY_braceleft:
561     case GDK_KEY_bracketleft:
562         /* decrement month */
563         g_date_subtract_months (&gdate, 1);
564         break;
565 
566     case GDK_KEY_M:
567     case GDK_KEY_m:
568         /* beginning of month */
569         g_date_set_day (&gdate, 1);
570         break;
571 
572     case GDK_KEY_H:
573     case GDK_KEY_h:
574         /* end of month */
575         g_date_set_day (&gdate, 1);
576         g_date_add_months (&gdate, 1);
577         g_date_subtract_days (&gdate, 1);
578         break;
579 
580     case GDK_KEY_Y:
581     case GDK_KEY_y:
582         /* beginning of year */
583         g_date_set_day (&gdate, 1);
584         g_date_set_month (&gdate, 1);
585         break;
586 
587     case GDK_KEY_R:
588     case GDK_KEY_r:
589         /* end of year */
590         g_date_set_day (&gdate, 1);
591         g_date_set_month (&gdate, 1);
592         g_date_add_years (&gdate, 1);
593         g_date_subtract_days (&gdate, 1);
594         break;
595 
596     case GDK_KEY_T:
597     case GDK_KEY_t:
598         /* today */
599         gnc_gdate_set_today (&gdate);
600         break;
601 
602     default:
603         return FALSE;
604     }
605     if (gnc_gdate_in_valid_range (&gdate, FALSE))
606         g_date_to_struct_tm (&gdate, tm);
607 
608     return TRUE;
609 }
610 
611 
612 /*--------------------------------------------------------------------------
613  *   GtkBuilder support functions
614  *-------------------------------------------------------------------------*/
615 
616 GModule *allsymbols = NULL;
617 
618 /* gnc_builder_add_from_file:
619  *
620  *   a convenience wrapper for gtk_builder_add_objects_from_file.
621  *   It takes care of finding the directory for glade files and prints a
622  *   warning message in case of an error.
623  */
624 gboolean
gnc_builder_add_from_file(GtkBuilder * builder,const char * filename,const char * root)625 gnc_builder_add_from_file (GtkBuilder *builder, const char *filename, const char *root)
626 {
627     GError* error = NULL;
628     char *fname;
629     gchar *gnc_builder_dir;
630     gboolean result;
631 
632     g_return_val_if_fail (builder != NULL, FALSE);
633     g_return_val_if_fail (filename != NULL, FALSE);
634     g_return_val_if_fail (root != NULL, FALSE);
635 
636     gnc_builder_dir = gnc_path_get_gtkbuilderdir ();
637     fname = g_build_filename(gnc_builder_dir, filename, (char *)NULL);
638     g_free (gnc_builder_dir);
639 
640     {
641         gchar *localroot = g_strdup(root);
642         gchar *objects[] = { localroot, NULL };
643         result = gtk_builder_add_objects_from_file (builder, fname, objects, &error);
644         if (!result)
645         {
646             PWARN ("Couldn't load builder file: %s", error->message);
647             g_error_free (error);
648         }
649         g_free (localroot);
650     }
651 
652     g_free (fname);
653 
654     return result;
655 }
656 
657 
658 /*---------------------------------------------------------------------
659  * The following function is built from a couple of glade functions.
660  *--------------------------------------------------------------------*/
661 void
gnc_builder_connect_full_func(GtkBuilder * builder,GObject * signal_object,const gchar * signal_name,const gchar * handler_name,GObject * connect_object,GConnectFlags flags,gpointer user_data)662 gnc_builder_connect_full_func(GtkBuilder *builder,
663                               GObject *signal_object,
664                               const gchar *signal_name,
665                               const gchar *handler_name,
666                               GObject *connect_object,
667                               GConnectFlags flags,
668                               gpointer user_data)
669 {
670     GCallback func;
671     GCallback *p_func = &func;
672 
673     if (allsymbols == NULL)
674     {
675         /* get a handle on the main executable -- use this to find symbols */
676         allsymbols = g_module_open(NULL, 0);
677     }
678 
679     if (!g_module_symbol(allsymbols, handler_name, (gpointer *)p_func))
680     {
681 #ifdef HAVE_DLSYM
682         /* Fallback to dlsym -- necessary for *BSD linkers */
683         func = dlsym(RTLD_DEFAULT, handler_name);
684 #else
685         func = NULL;
686 #endif
687         if (func == NULL)
688         {
689             PWARN("ggaff: could not find signal handler '%s'.", handler_name);
690             return;
691         }
692     }
693 
694     if (connect_object)
695         g_signal_connect_object (signal_object, signal_name, func,
696                                  connect_object, flags);
697     else
698         g_signal_connect_data(signal_object, signal_name, func,
699                               user_data, NULL , flags);
700 }
701 /*--------------------------------------------------------------------------
702  * End of GtkBuilder utilities
703  *-------------------------------------------------------------------------*/
704 
705 
706 void
gnc_gtk_dialog_add_button(GtkWidget * dialog,const gchar * label,const gchar * icon_name,guint response)707 gnc_gtk_dialog_add_button (GtkWidget *dialog, const gchar *label, const gchar *icon_name, guint response)
708 {
709     GtkWidget *button;
710 
711     button = gtk_button_new_with_mnemonic(label);
712     if (icon_name)
713     {
714         GtkWidget *image;
715 
716         image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_BUTTON);
717         gtk_button_set_image (GTK_BUTTON(button), image);
718         g_object_set (button, "always-show-image", TRUE, NULL);
719     }
720     g_object_set (button, "can-default", TRUE, NULL);
721     gtk_widget_show_all(button);
722     gtk_dialog_add_action_widget(GTK_DIALOG(dialog), button, response);
723 }
724 
725 static void
gnc_perm_button_cb(GtkButton * perm,gpointer user_data)726 gnc_perm_button_cb (GtkButton *perm, gpointer user_data)
727 {
728     gboolean perm_active;
729 
730     perm_active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(perm));
731     gtk_widget_set_sensitive(user_data, !perm_active);
732 }
733 
734 gint
gnc_dialog_run(GtkDialog * dialog,const gchar * pref_name)735 gnc_dialog_run (GtkDialog *dialog, const gchar *pref_name)
736 {
737     GtkWidget *perm, *temp;
738     gboolean ask = TRUE;
739     gint response;
740 
741     /* Does the user want to see this question? If not, return the
742      * previous answer. */
743     response = gnc_prefs_get_int(GNC_PREFS_GROUP_WARNINGS_PERM, pref_name);
744     if (response != 0)
745         return response;
746     response = gnc_prefs_get_int(GNC_PREFS_GROUP_WARNINGS_TEMP, pref_name);
747     if (response != 0)
748         return response;
749 
750     /* Add in the checkboxes to find out if the answer should be remembered. */
751 #if 0
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);
757     }
758     else
759     {
760         ask = FALSE;
761     }
762 #endif
763     perm = gtk_check_button_new_with_mnemonic
764            (ask
765             ? _("Remember and don't _ask me again.")
766             : _("Don't _tell me again."));
767     temp = gtk_check_button_new_with_mnemonic
768            (ask
769             ? _("Remember and don't ask me again this _session.")
770             : _("Don't tell me again this _session."));
771     gtk_widget_show(perm);
772     gtk_widget_show(temp);
773     gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (dialog)), perm, TRUE, TRUE, 0);
774     gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (dialog)), temp, TRUE, TRUE, 0);
775     g_signal_connect(perm, "clicked", G_CALLBACK(gnc_perm_button_cb), temp);
776 
777     /* OK. Present the dialog. */
778     response = gtk_dialog_run(dialog);
779     if ((response == GTK_RESPONSE_NONE) || (response == GTK_RESPONSE_DELETE_EVENT))
780     {
781         return GTK_RESPONSE_CANCEL;
782     }
783 
784     if (response != GTK_RESPONSE_CANCEL)
785     {
786         /* Save the answer? */
787         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(perm)))
788         {
789             gnc_prefs_set_int(GNC_PREFS_GROUP_WARNINGS_PERM, pref_name, response);
790         }
791         else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(temp)))
792         {
793             gnc_prefs_set_int(GNC_PREFS_GROUP_WARNINGS_TEMP, pref_name, response);
794         }
795     }
796     return response;
797 }
798 
799 /* If this is a new book, this function can be used to display book options
800  * dialog so user can specify options, before any transactions can be
801  * imported/entered, since the book options can affect how transactions are
802  * created. Note: This dialog is modal! */
803 gboolean
gnc_new_book_option_display(GtkWidget * parent)804 gnc_new_book_option_display (GtkWidget *parent)
805 {
806     GtkWidget *window;
807     gint result = GTK_RESPONSE_HELP;
808 
809     window = gnc_book_options_dialog_cb (TRUE, _( "New Book Options"),
810                                          GTK_WINDOW (parent));
811     if (window)
812     {
813         /* close dialog and proceed unless help button selected */
814         while (result == GTK_RESPONSE_HELP)
815         {
816             result = gtk_dialog_run(GTK_DIALOG(window));
817         }
818         return FALSE;
819     }
820     return TRUE;
821 }
822 
823 /* This function returns a widget for selecting a cost policy
824  */
825 GtkWidget *
gnc_cost_policy_select_new(void)826 gnc_cost_policy_select_new (void)
827 {
828     GtkWidget *cost_policy_widget = NULL;
829     GList *list_of_policies = NULL;
830 
831     list_of_policies = gnc_get_valid_policy_list();
832 
833     g_return_val_if_fail(g_list_length (list_of_policies) >= 0, NULL);
834     if (list_of_policies)
835     {
836         GtkListStore *store = gtk_list_store_new (1, G_TYPE_STRING);
837         GtkTreeIter  iter;
838         GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
839         const char *description;
840         GList *l = NULL;
841 
842         /* Add values to the list store, entry and tooltip */
843         for (l = list_of_policies; l != NULL; l = l->next)
844         {
845             GNCPolicy *pcy = l->data;
846             description = PolicyGetDescription (pcy);
847 
848             gtk_list_store_append (store, &iter);
849             gtk_list_store_set (store, &iter,
850                     0, (description && *description) ? _(description) : "",
851                     -1);
852         }
853         g_list_free (list_of_policies);
854         /* Create the new Combo with the store */
855         cost_policy_widget = gtk_combo_box_new_with_model (GTK_TREE_MODEL(store));
856 
857         gtk_cell_layout_pack_start (GTK_CELL_LAYOUT(cost_policy_widget), renderer, TRUE);
858         gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT(cost_policy_widget),
859                                        renderer, "text", 0);
860         g_object_unref (store);
861     }
862     return cost_policy_widget;
863 }
864 
865 /* This function returns a string for the CSS 'gnc-class-negative-numbers' class,
866  * the returned string must be freed
867  */
868 gchar*
gnc_get_negative_color(void)869 gnc_get_negative_color (void)
870 {
871     GdkRGBA color;
872     GtkWidget *label = gtk_label_new ("Color");
873     GtkStyleContext *context = gtk_widget_get_style_context (GTK_WIDGET(label));
874     gtk_style_context_add_class (context, "gnc-class-negative-numbers");
875     gtk_style_context_get_color (context, GTK_STATE_FLAG_NORMAL, &color);
876 
877     return gdk_rgba_to_string (&color);
878 }
879