GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
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 <[email protected]> *
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 [email protected] *
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-path.h"
39 #include "gnc-engine.h"
40 #include "gnc-euro.h"
41 #include "gnc-ui-util.h"
42 #include "gnc-prefs.h"
43 #include "guile-util.h"
44 #include "gnc-main-window.h"
45 #include <gnc-gdate-utils.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 /********************************************************************\
54  * gnc_get_deficit_color *
55  * fill in the 3 color values for the color of deficit values *
56  * *
57  * Args: color - color structure *
58  * Returns: none *
59  \*******************************************************************/
60 void
61 gnc_get_deficit_color(GdkColor *color)
62 {
63  color->red = 50000;
64  color->green = 0;
65  color->blue = 0;
66 }
67 
68 
69 /********************************************************************\
70  * gnc_set_label_color *
71  * sets the color of the label given the value *
72  * *
73  * Args: label - gtk label widget *
74  * value - value to use to set color *
75  * Returns: none *
76  \*******************************************************************/
77 void
78 gnc_set_label_color(GtkWidget *label, gnc_numeric value)
79 {
80  gboolean deficit;
81  GdkColormap *cm;
82  GtkStyle *style;
83 
84  if (!gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_NEGATIVE_IN_RED))
85  return;
86 
87  cm = gtk_widget_get_colormap(GTK_WIDGET(label));
88  gtk_widget_ensure_style(GTK_WIDGET(label));
89  style = gtk_widget_get_style(GTK_WIDGET(label));
90 
91  style = gtk_style_copy(style);
92 
93  deficit = gnc_numeric_negative_p (value);
94 
95  if (deficit)
96  {
97  gnc_get_deficit_color(&style->fg[GTK_STATE_NORMAL]);
98  gdk_colormap_alloc_color(cm, &style->fg[GTK_STATE_NORMAL], FALSE, TRUE);
99  }
100  else
101  style->fg[GTK_STATE_NORMAL] = style->black;
102 
103  gtk_widget_set_style(label, style);
104 
105  g_object_unref(style);
106 }
107 
108 
109 /********************************************************************\
110  * gnc_restore_window_size *
111  * restores the position and size of the given window, if these *
112  * these parameters have been saved earlier. Does nothing if no *
113  * saved values are found. *
114  * *
115  * Args: group - the preferences group to look in for saved coords *
116  * window - the window for which the coords are to be *
117  * restored *
118  * Returns: nothing *
119  \*******************************************************************/
120 void
121 gnc_restore_window_size(const char *group, GtkWindow *window)
122 {
123  gint wpos[2], wsize[2];
124  GVariant *geometry;
125 
126  ENTER("");
127 
128  g_return_if_fail(group != NULL);
129  g_return_if_fail(window != NULL);
130 
131  if (!gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_SAVE_GEOMETRY))
132  return;
133 
134  geometry = gnc_prefs_get_value (group, GNC_PREF_LAST_GEOMETRY);
135  if (g_variant_is_of_type (geometry, (const GVariantType *) "(iiii)") )
136  {
137  gint screen_width = gdk_screen_width();
138  gint screen_height = gdk_screen_height();
139 
140  g_variant_get (geometry, "(iiii)",
141  &wpos[0], &wpos[1],
142  &wsize[0], &wsize[1]);
143  DEBUG("geometry from preferences - wpos[0]: %d, wpos[1]: %d, wsize[0]: %d, wsize[1]: %d",
144  wpos[0], wpos[1], wsize[0], wsize[1]);
145 
146  /* (-1, -1) means no geometry was saved (default preferences value) */
147  if ((wpos[0] != -1) && (wpos[1] != -1))
148  {
149  /* Keep the window on screen if possible */
150  if (screen_width != 0)
151  wpos[0] = wpos[0] % screen_width;
152  if (screen_height != 0)
153  wpos[1] = wpos[1] % screen_height;
154  DEBUG("geometry after screen adaption - wpos[0]: %d, wpos[1]: %d, wsize[0]: %d, wsize[1]: %d",
155  wpos[0], wpos[1], wsize[0], wsize[1]);
156 
157  gtk_window_move(window, wpos[0], wpos[1]);
158  }
159 
160  /* Don't attempt to restore invalid sizes */
161  if ((wsize[0] > 0) && (wsize[1] > 0))
162  gtk_window_resize(window, wsize[0], wsize[1]);
163  }
164  g_variant_unref (geometry);
165 
166  LEAVE("");
167 }
168 
169 
170 /********************************************************************\
171  * gnc_save_window_size *
172  * save the window position and size into options whose names are *
173  * prefixed by the group name. *
174  * *
175  * Args: group - preferences group to save the options in *
176  * window - the window for which current position and size *
177  * are to be saved *
178  * Returns: nothing *
179 \********************************************************************/
180 void
181 gnc_save_window_size(const char *group, GtkWindow *window)
182 {
183  gint wpos[2], wsize[2];
184  GVariant *geometry;
185 
186  g_return_if_fail(group != NULL);
187  g_return_if_fail(window != NULL);
188 
189  if (!gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_SAVE_GEOMETRY))
190  return;
191 
192  gtk_window_get_position(GTK_WINDOW(window), &wpos[0], &wpos[1]);
193  gtk_window_get_size(GTK_WINDOW(window), &wsize[0], &wsize[1]);
194  geometry = g_variant_new ("(iiii)", wpos[0], wpos[1],
195  wsize[0], wsize[1]);
196  gnc_prefs_set_value (group, GNC_PREF_LAST_GEOMETRY, geometry);
197  /* Don't unref geometry here, it is consumed by gnc_prefs_set_value */
198 }
199 
200 
201 /********************************************************************\
202  * gnc_window_adjust_for_screen *
203  * adjust the window size if it is bigger than the screen size. *
204  * *
205  * Args: window - the window to adjust *
206  * Returns: nothing *
207 \********************************************************************/
208 void
209 gnc_window_adjust_for_screen(GtkWindow * window)
210 {
211  gint screen_width;
212  gint screen_height;
213  gint width;
214  gint height;
215 
216  if (window == NULL)
217  return;
218 
219  g_return_if_fail(GTK_IS_WINDOW(window));
220  if (GTK_WIDGET(window)->window == NULL)
221  return;
222 
223  screen_width = gdk_screen_width();
224  screen_height = gdk_screen_height();
225  gdk_drawable_get_size(GTK_WIDGET(window)->window, &width, &height);
226 
227  if ((width <= screen_width) && (height <= screen_height))
228  return;
229 
230  width = MIN(width, screen_width - 10);
231  width = MAX(width, 0);
232 
233  height = MIN(height, screen_height - 10);
234  height = MAX(height, 0);
235 
236  gdk_window_resize(GTK_WIDGET(window)->window, width, height);
237  gtk_widget_queue_resize(GTK_WIDGET(window));
238 }
239 
240 gboolean
241 gnc_handle_date_accelerator (GdkEventKey *event,
242  struct tm *tm,
243  const char *date_str)
244 {
245  GDate gdate;
246 
247  g_return_val_if_fail (event != NULL, FALSE);
248  g_return_val_if_fail (tm != NULL, FALSE);
249  g_return_val_if_fail (date_str != NULL, FALSE);
250 
251  if (event->type != GDK_KEY_PRESS)
252  return FALSE;
253 
254  if ((tm->tm_mday <= 0) || (tm->tm_mon == -1) || (tm->tm_year == -1))
255  return FALSE;
256 
257  g_date_set_dmy (&gdate,
258  tm->tm_mday,
259  tm->tm_mon + 1,
260  tm->tm_year + 1900);
261 
262  /*
263  * Check those keys where the code does different things depending
264  * upon the modifiers.
265  */
266  switch (event->keyval)
267  {
268  case GDK_KP_Add:
269  case GDK_plus:
270  case GDK_equal:
271  if (event->state & GDK_SHIFT_MASK)
272  g_date_add_days (&gdate, 7);
273  else if (event->state & GDK_MOD1_MASK)
274  g_date_add_months (&gdate, 1);
275  else if (event->state & GDK_CONTROL_MASK)
276  g_date_add_years (&gdate, 1);
277  else
278  g_date_add_days (&gdate, 1);
279  g_date_to_struct_tm (&gdate, tm);
280  return TRUE;
281 
282  case GDK_minus:
283  case GDK_KP_Subtract:
284  case GDK_underscore:
285  if ((strlen (date_str) != 0) && (dateSeparator () == '-'))
286  {
287  const char *c;
288  gunichar uc;
289  int count = 0;
290 
291  /* rough check for existing date */
292  c = date_str;
293  while (*c)
294  {
295  uc = g_utf8_get_char (c);
296  if (uc == '-')
297  count++;
298  c = g_utf8_next_char (c);
299  }
300 
301  if (count < 2)
302  return FALSE;
303  }
304 
305  if (event->state & GDK_SHIFT_MASK)
306  g_date_subtract_days (&gdate, 7);
307  else if (event->state & GDK_MOD1_MASK)
308  g_date_subtract_months (&gdate, 1);
309  else if (event->state & GDK_CONTROL_MASK)
310  g_date_subtract_years (&gdate, 1);
311  else
312  g_date_subtract_days (&gdate, 1);
313  g_date_to_struct_tm (&gdate, tm);
314  return TRUE;
315 
316  default:
317  break;
318  }
319 
320  /*
321  * Control and Alt key combinations should be ignored by this
322  * routine so that the menu system gets to handle them. This
323  * prevents weird behavior of the menu accelerators (i.e. work in
324  * some widgets but not others.)
325  */
326  if (event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK))
327  return FALSE;
328 
329  /* Now check for the remaining keystrokes. */
330  switch (event->keyval)
331  {
332  case GDK_braceright:
333  case GDK_bracketright:
334  /* increment month */
335  g_date_add_months (&gdate, 1);
336  break;
337 
338  case GDK_braceleft:
339  case GDK_bracketleft:
340  /* decrement month */
341  g_date_subtract_months (&gdate, 1);
342  break;
343 
344  case GDK_M:
345  case GDK_m:
346  /* beginning of month */
347  g_date_set_day (&gdate, 1);
348  break;
349 
350  case GDK_H:
351  case GDK_h:
352  /* end of month */
353  g_date_set_day (&gdate, 1);
354  g_date_add_months (&gdate, 1);
355  g_date_subtract_days (&gdate, 1);
356  break;
357 
358  case GDK_Y:
359  case GDK_y:
360  /* beginning of year */
361  g_date_set_day (&gdate, 1);
362  g_date_set_month (&gdate, 1);
363  break;
364 
365  case GDK_R:
366  case GDK_r:
367  /* end of year */
368  g_date_set_day (&gdate, 1);
369  g_date_set_month (&gdate, 1);
370  g_date_add_years (&gdate, 1);
371  g_date_subtract_days (&gdate, 1);
372  break;
373 
374  case GDK_T:
375  case GDK_t:
376  /* today */
377  gnc_gdate_set_today (&gdate);
378  break;
379 
380  default:
381  return FALSE;
382  }
383 
384  g_date_to_struct_tm (&gdate, tm);
385 
386  return TRUE;
387 }
388 
389 
390 /*--------------------------------------------------------------------------
391  * GtkBuilder support functions
392  *-------------------------------------------------------------------------*/
393 
394 GModule *allsymbols = NULL;
395 
396 /* gnc_builder_add_from_file:
397  *
398  * a convenience wrapper for gtk_builder_add_objects_from_file.
399  * It takes care of finding the directory for glade files and prints a
400  * warning message in case of an error.
401  */
402 gboolean
403 gnc_builder_add_from_file (GtkBuilder *builder, const char *filename, const char *root)
404 {
405  GError* error = NULL;
406  char *fname;
407  gchar *gnc_builder_dir;
408  gboolean result;
409 
410  g_return_val_if_fail (builder != NULL, FALSE);
411  g_return_val_if_fail (filename != NULL, FALSE);
412  g_return_val_if_fail (root != NULL, FALSE);
413 
414  gnc_builder_dir = gnc_path_get_gtkbuilderdir ();
415  fname = g_build_filename(gnc_builder_dir, filename, (char *)NULL);
416  g_free (gnc_builder_dir);
417 
418  {
419  gchar *localroot = g_strdup(root);
420  gchar *objects[] = { localroot, NULL };
421  result = gtk_builder_add_objects_from_file (builder, fname, objects, &error);
422  if (!result)
423  {
424  PWARN ("Couldn't load builder file: %s", error->message);
425  g_error_free (error);
426  }
427  g_free (localroot);
428  }
429 
430  g_free (fname);
431 
432  return result;
433 }
434 
435 
436 /*---------------------------------------------------------------------
437  * The following function is built from a couple of glade functions.
438  *--------------------------------------------------------------------*/
439 void
440 gnc_builder_connect_full_func(GtkBuilder *builder,
441  GObject *signal_object,
442  const gchar *signal_name,
443  const gchar *handler_name,
444  GObject *connect_object,
445  GConnectFlags flags,
446  gpointer user_data)
447 {
448  GCallback func;
449  GCallback *p_func = &func;
450 
451  if (allsymbols == NULL)
452  {
453  /* get a handle on the main executable -- use this to find symbols */
454  allsymbols = g_module_open(NULL, 0);
455  }
456 
457  if (!g_module_symbol(allsymbols, handler_name, (gpointer *)p_func))
458  {
459 #ifdef HAVE_DLSYM
460  /* Fallback to dlsym -- necessary for *BSD linkers */
461  func = dlsym(RTLD_DEFAULT, handler_name);
462 #else
463  func = NULL;
464 #endif
465  if (func == NULL)
466  {
467  PWARN("ggaff: could not find signal handler '%s'.", handler_name);
468  return;
469  }
470  }
471 
472  if (connect_object)
473  g_signal_connect_object (signal_object, signal_name, func,
474  connect_object, flags);
475  else
476  g_signal_connect_data(signal_object, signal_name, func,
477  user_data, NULL , flags);
478 }
479 /*--------------------------------------------------------------------------
480  * End of GtkBuilder utilities
481  *-------------------------------------------------------------------------*/
482 
483 
484 void
485 gnc_gtk_dialog_add_button (GtkWidget *dialog, const gchar *label, const gchar *stock_id, guint response)
486 {
487  GtkWidget *button;
488 
489  button = gtk_button_new_with_mnemonic(label);
490  if (stock_id)
491  {
492  GtkWidget *image;
493 
494  image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON);
495  gtk_button_set_image(GTK_BUTTON(button), image);
496  }
497  g_object_set (button, "can-default", TRUE, NULL);
498  gtk_widget_show_all(button);
499  gtk_dialog_add_action_widget(GTK_DIALOG(dialog), button, response);
500 }
501 
502 static void
503 gnc_perm_button_cb (GtkButton *perm, gpointer user_data)
504 {
505  gboolean perm_active;
506 
507  perm_active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(perm));
508  gtk_widget_set_sensitive(user_data, !perm_active);
509 }
510 
511 gint
512 gnc_dialog_run (GtkDialog *dialog, const gchar *pref_name)
513 {
514  GtkWidget *perm, *temp;
515  gboolean ask = TRUE;
516  gint response;
517 
518  /* Does the user want to see this question? If not, return the
519  * previous answer. */
520  response = gnc_prefs_get_int(GNC_PREFS_GROUP_WARNINGS_PERM, pref_name);
521  if (response != 0)
522  return response;
523  response = gnc_prefs_get_int(GNC_PREFS_GROUP_WARNINGS_TEMP, pref_name);
524  if (response != 0)
525  return response;
526 
527  /* Add in the checkboxes to find out if the answer should be remembered. */
528 #if 0
529  if (GTK_IS_MESSAGE_DIALOG(dialog))
530  {
531  GtkMessageType type;
532  g_object_get(dialog, "message-type", &type, (gchar*)NULL);
533  ask = (type == GTK_MESSAGE_QUESTION);
534  }
535  else
536  {
537  ask = FALSE;
538  }
539 #endif
540  perm = gtk_check_button_new_with_mnemonic
541  (ask
542  ? _("Remember and don't _ask me again.")
543  : _("Don't _tell me again."));
544  temp = gtk_check_button_new_with_mnemonic
545  (ask
546  ? _("Remember and don't ask me again this _session.")
547  : _("Don't tell me again this _session."));
548  gtk_widget_show(perm);
549  gtk_widget_show(temp);
550  gtk_box_pack_start(GTK_BOX(dialog->vbox), perm, TRUE, TRUE, 0);
551  gtk_box_pack_start(GTK_BOX(dialog->vbox), temp, TRUE, TRUE, 0);
552  g_signal_connect(perm, "clicked", G_CALLBACK(gnc_perm_button_cb), temp);
553 
554  /* OK. Present the dialog. */
555  response = gtk_dialog_run(dialog);
556  if ((response == GTK_RESPONSE_NONE) || (response == GTK_RESPONSE_DELETE_EVENT))
557  {
558  return GTK_RESPONSE_CANCEL;
559  }
560 
561  if (response != GTK_RESPONSE_CANCEL)
562  {
563  /* Save the answer? */
564  if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(perm)))
565  {
566  gnc_prefs_set_int(GNC_PREFS_GROUP_WARNINGS_PERM, pref_name, response);
567  }
568  else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(temp)))
569  {
570  gnc_prefs_set_int(GNC_PREFS_GROUP_WARNINGS_TEMP, pref_name, response);
571  }
572  }
573  return response;
574 }
575 
576 /* If this is a new book, this function can be used to display book options
577  * dialog so user can specify options, before any transactions can be
578  * imported/entered, since they can affect how transactions are created
579  * Note: This dialog is modal! */
580 gboolean
581 gnc_new_book_option_display (GtkWidget *parent)
582 {
583  GtkWidget *window;
584  gint result = GTK_RESPONSE_HELP;
585 
586  window = gnc_book_options_dialog_cb (TRUE, _( "New Book Options"));
587  if (parent)
588  gtk_window_set_transient_for (GTK_WINDOW(window), GTK_WINDOW(parent));
589 
590  if (window)
591  {
592  /* close dialog and proceed unless help button selected */
593  while (result == GTK_RESPONSE_HELP)
594  {
595  result = gtk_dialog_run(GTK_DIALOG(window));
596  }
597  return FALSE;
598  }
599  return TRUE;
600 }
void gnc_gdate_set_today(GDate *gd)
GtkWidget * gnc_book_options_dialog_cb(gboolean modal, gchar *title)
gboolean gnc_prefs_set_value(const gchar *group, const gchar *pref_name, GVariant *value)
Definition: gnc-prefs.c:355
utility functions for the GnuCash UI
#define DEBUG(format, args...)
Definition: qoflog.h:255
gboolean gnc_prefs_set_int(const gchar *group, const gchar *pref_name, gint value)
Definition: gnc-prefs.c:293
gchar dateSeparator(void)
#define ENTER(format, args...)
Definition: qoflog.h:261
gint gnc_prefs_get_int(const gchar *group, const gchar *pref_name)
Definition: gnc-prefs.c:206
Functions for adding content to a window.
gboolean gnc_numeric_negative_p(gnc_numeric a)
#define PWARN(format, args...)
Definition: qoflog.h:243
All type declarations for the whole Gnucash engine.
GVariant * gnc_prefs_get_value(const gchar *group, const gchar *pref_name)
Definition: gnc-prefs.c:272
Generic api to store and retrieve preferences.
GDate helper routines.
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Definition: gnc-prefs.c:196
#define LEAVE(format, args...)
Definition: qoflog.h:271
Commodity handling public routines.
const gchar * QofLogModule
Definition: qofid.h:89