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