GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
window-main-summarybar.c
1 /********************************************************************
2  * window-main-summarybar.c -- summary of financial info *
3  * Copyright (C) 1998,1999 Jeremy Collins *
4  * Copyright (C) 1998,1999,2000 Linas Vepstas *
5  * Copyright (C) 2001 Bill Gribble *
6  * Copyright (C) 2005 Joshua Sled <[email protected]> *
7  * *
8  * This program is free software; you can redistribute it and/or *
9  * modify it under the terms of the GNU General Public License as *
10  * published by the Free Software Foundation; either version 2 of *
11  * the License, or (at your option) any later version. *
12  * *
13  * This program is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License*
19  * along with this program; if not, contact: *
20  * *
21  * Free Software Foundation Voice: +1-617-542-5942 *
22  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
23  * Boston, MA 02110-1301, USA [email protected] *
24  ********************************************************************/
25 
26 #include "config.h"
27 
28 #include <gtk/gtk.h>
29 #include <glib/gi18n.h>
30 
31 #include "Account.h"
32 #include "gnc-accounting-period.h"
33 #include "gnc-component-manager.h"
34 #include "gnc-euro.h"
35 #include "gnc-event.h"
36 #include "gnc-prefs.h"
37 #include "gnc-locale-utils.h"
38 #include "gnc-ui-util.h"
39 #include "window-main-summarybar.h"
40 
41 typedef struct
42 {
43  GtkWidget * hbox;
44  GtkWidget * totals_combo;
45  GtkListStore *datamodel;
46  int component_id;
47  int cnxn_id;
49 
50 #define WINDOW_SUMMARYBAR_CM_CLASS "summary-bar"
51 
52 #define GNC_PREFS_GROUP "window.pages.account-tree.summary"
53 #define GNC_PREF_GRAND_TOTAL "grand-total"
54 #define GNC_PREF_NON_CURRENCY "non-currency"
55 
67 typedef struct
68 {
69  gnc_commodity * currency;
70  gnc_numeric assets;
71  gnc_numeric profits;
72  gint total_mode;
74 
75 
76 /* defines for total_mode in GNCCurrencyAcc and GNCCurrencyItem */
77 #define TOTAL_SINGLE 0
78 #define TOTAL_CURR_TOTAL 1
79 #define TOTAL_NON_CURR_TOTAL 2
80 #define TOTAL_GRAND_TOTAL 3
81 
82 
84 typedef struct
85 {
86  gnc_commodity *default_currency;
87  gboolean grand_total;
88  gboolean non_currency;
89  time64 start_date;
90  time64 end_date;
92 
97 static GNCCurrencyAcc *
98 gnc_ui_get_currency_accumulator(GList **list, gnc_commodity * currency, gint total_mode)
99 {
100  GList *current;
101  GNCCurrencyAcc *found;
102 
103  for (current = g_list_first(*list); current; current = g_list_next(current))
104  {
105  found = current->data;
106  if ((gnc_commodity_equiv(currency, found->currency))
107  && (found->total_mode == total_mode))
108  {
109  return found;
110  }
111  }
112 
113  found = g_new0 (GNCCurrencyAcc, 1);
114  found->currency = currency;
115  found->assets = gnc_numeric_zero ();
116  found->profits = gnc_numeric_zero ();
117  found->total_mode = total_mode;
118  *list = g_list_append (*list, found);
119 
120  return found;
121 }
122 
126 static void
127 gnc_ui_accounts_recurse (Account *parent, GList **currency_list,
128  GNCSummarybarOptions options)
129 {
130  gnc_numeric start_amount;
131  gnc_numeric start_amount_default_currency;
132  gnc_numeric end_amount;
133  gnc_numeric end_amount_default_currency;
134  GNCAccountType account_type;
135  gnc_commodity * account_currency;
136  GNCCurrencyAcc *currency_accum = NULL;
137  GNCCurrencyAcc *grand_total_accum = NULL;
138  GNCCurrencyAcc *non_curr_accum = NULL;
139  GList *children, *node;
140  gboolean non_currency = FALSE;
141  Timespec end_timespec;
142  Timespec start_timespec;
143 
144  if (parent == NULL) return;
145 
146  children = gnc_account_get_children(parent);
147  for (node = children; node; node = g_list_next(node))
148  {
149  Account *account = node->data;
150 
151  account_type = xaccAccountGetType(account);
152  account_currency = xaccAccountGetCommodity(account);
153 
154  if (options.grand_total)
155  grand_total_accum = gnc_ui_get_currency_accumulator(currency_list,
156  options.default_currency,
157  TOTAL_GRAND_TOTAL);
158 
159  if (!gnc_commodity_is_currency(account_currency))
160  {
161  non_currency = TRUE;
162  non_curr_accum = gnc_ui_get_currency_accumulator(currency_list,
163  options.default_currency,
164  TOTAL_NON_CURR_TOTAL);
165  }
166 
167  if (!non_currency || options.non_currency)
168  {
169  currency_accum = gnc_ui_get_currency_accumulator(currency_list,
170  account_currency,
171  TOTAL_SINGLE);
172  }
173 
174  switch (account_type)
175  {
176  case ACCT_TYPE_BANK:
177  case ACCT_TYPE_CASH:
178  case ACCT_TYPE_ASSET:
179  case ACCT_TYPE_STOCK:
180  case ACCT_TYPE_MUTUAL:
181  case ACCT_TYPE_CREDIT:
182  case ACCT_TYPE_LIABILITY:
183  case ACCT_TYPE_PAYABLE:
185  end_amount = xaccAccountGetBalanceAsOfDate(account, options.end_date);
186  timespecFromTime64(&end_timespec, options.end_date);
187  end_amount_default_currency =
188  xaccAccountConvertBalanceToCurrencyAsOfDate
189  (account, end_amount, account_currency, options.default_currency,
191 
192  if (!non_currency || options.non_currency)
193  {
194  currency_accum->assets =
195  gnc_numeric_add (currency_accum->assets, end_amount,
196  gnc_commodity_get_fraction (account_currency),
198  }
199 
200  if (non_currency)
201  {
202  non_curr_accum->assets =
203  gnc_numeric_add (non_curr_accum->assets, end_amount_default_currency,
204  gnc_commodity_get_fraction (options.default_currency),
206  }
207 
208  if (options.grand_total)
209  {
210  grand_total_accum->assets =
211  gnc_numeric_add (grand_total_accum->assets, end_amount_default_currency,
212  gnc_commodity_get_fraction (options.default_currency),
214  }
215 
216  gnc_ui_accounts_recurse(account, currency_list, options);
217  break;
218  case ACCT_TYPE_INCOME:
219  case ACCT_TYPE_EXPENSE:
220  start_amount = xaccAccountGetBalanceAsOfDate(account, options.start_date);
221  timespecFromTime64(&start_timespec, options.start_date);
222  start_amount_default_currency =
223  xaccAccountConvertBalanceToCurrencyAsOfDate
224  (account, start_amount, account_currency, options.default_currency,
225  timespecToTime64(timespecCanonicalDayTime(start_timespec)));
226  end_amount = xaccAccountGetBalanceAsOfDate(account, options.end_date);
227  timespecFromTime64(&end_timespec, options.end_date);
228  end_amount_default_currency =
229  xaccAccountConvertBalanceToCurrencyAsOfDate
230  (account, end_amount, account_currency, options.default_currency,
232 
233  if (!non_currency || options.non_currency)
234  {
235  currency_accum->profits =
236  gnc_numeric_add (currency_accum->profits, start_amount,
237  gnc_commodity_get_fraction (account_currency),
239  currency_accum->profits =
240  gnc_numeric_sub (currency_accum->profits, end_amount,
241  gnc_commodity_get_fraction (account_currency),
243  }
244 
245  if (non_currency)
246  {
247  non_curr_accum->profits =
248  gnc_numeric_add (non_curr_accum->profits, start_amount_default_currency,
249  gnc_commodity_get_fraction (options.default_currency),
251  non_curr_accum->profits =
252  gnc_numeric_sub (non_curr_accum->profits, end_amount_default_currency,
253  gnc_commodity_get_fraction (options.default_currency),
255  }
256 
257  if (options.grand_total)
258  {
259  grand_total_accum->profits =
260  gnc_numeric_add (grand_total_accum->profits,
261  start_amount_default_currency,
262  gnc_commodity_get_fraction (options.default_currency),
264  grand_total_accum->profits =
265  gnc_numeric_sub (grand_total_accum->profits,
266  end_amount_default_currency,
267  gnc_commodity_get_fraction (options.default_currency),
269  }
270 
271  gnc_ui_accounts_recurse(account, currency_list, options);
272  break;
273  case ACCT_TYPE_EQUITY:
274  /* no-op, see comments at top about summing assets */
275  break;
280  case ACCT_TYPE_TRADING:
281  break;
282  case ACCT_TYPE_CURRENCY:
283  default:
284  break;
285  }
286  }
287  g_list_free(children);
288 }
289 
290 static char*
291 get_total_mode_label(const char *mnemonic, int total_mode)
292 {
293  char *label_str;
294  // i.e., "$, grand total," [profits: $12,345.67, assets: $23,456.78]
295  switch (total_mode)
296  {
297  case TOTAL_CURR_TOTAL:
298  label_str = g_strdup_printf( _("%s, Total:"), mnemonic );
299  break;
300  case TOTAL_NON_CURR_TOTAL:
301  label_str = g_strdup_printf( _("%s, Non Currency Commodities Total:"), mnemonic );
302  break;
303  case TOTAL_GRAND_TOTAL:
304  label_str = g_strdup_printf( _("%s, Grand Total:"), mnemonic );
305  break;
306  case TOTAL_SINGLE:
307  default:
308  label_str = g_strdup_printf( _("%s:"), mnemonic );
309  break;
310  }
311  return label_str;
312 }
313 
314 enum
315 {
316  COLUMN_MNEMONIC_TYPE,
317  COLUMN_ASSETS,
318  COLUMN_ASSETS_VALUE,
319  COLUMN_PROFITS,
320  COLUMN_PROFITS_VALUE,
321  N_COLUMNS,
322 };
323 
324 /* The gnc_main_window_summary_refresh() subroutine redraws summary
325  * information. The statusbar includes two fields, titled 'profits'
326  * and 'assets'. The total assets equal the sum of all of the
327  * non-equity, non-income accounts. In theory, assets also equals the
328  * grand total value of the equity accounts, but that assumes that
329  * folks are using the equity account type correctly (which is not
330  * likely). Thus we show the sum of assets, rather than the sum of
331  * equities.
332  *
333  * The EURO gets special treatment. There can be one line with
334  * EUR amounts and a EUR (total) line which sums up all EURO
335  * member currencies.
336  *
337  * There can be a 'grand total', too, which sums up all accounts
338  * converted to one common currency and a total of all non
339  * currency commodities (e.g. stock, funds). */
340 
341 static void
342 gnc_main_window_summary_refresh (GNCMainSummary * summary)
343 {
344  Account *root;
345  char asset_string[256];
346  char profit_string[256];
347  GNCCurrencyAcc *currency_accum;
348  GList *currency_list;
349  GList *current;
350  GNCSummarybarOptions options;
351 
352 
353  root = gnc_get_current_root_account ();
354  options.default_currency = xaccAccountGetCommodity(root);
355  if (options.default_currency == NULL)
356  {
357  options.default_currency = gnc_default_currency ();
358  }
359 
360  options.grand_total =
361  gnc_prefs_get_bool(GNC_PREFS_GROUP, GNC_PREF_GRAND_TOTAL);
362  options.non_currency =
363  gnc_prefs_get_bool(GNC_PREFS_GROUP, GNC_PREF_NON_CURRENCY);
364  options.start_date = gnc_accounting_period_fiscal_start();
365  options.end_date = gnc_accounting_period_fiscal_end();
366 
367  currency_list = NULL;
368 
369  /* grand total should be first in the list */
370  if (options.grand_total)
371  {
372  gnc_ui_get_currency_accumulator (&currency_list, options.default_currency,
373  TOTAL_GRAND_TOTAL);
374  }
375  /* Make sure there's at least one accumulator in the list. */
376  gnc_ui_get_currency_accumulator (&currency_list, options.default_currency,
377  TOTAL_SINGLE);
378 
379  gnc_ui_accounts_recurse(root, &currency_list, options);
380 
381  {
382  GtkTreeIter iter;
383  char asset_amount_string[256], profit_amount_string[256];
384  struct lconv *lc;
385 
386  lc = gnc_localeconv();
387 
388  g_object_ref(summary->datamodel);
389  gtk_combo_box_set_model(GTK_COMBO_BOX(summary->totals_combo), NULL);
390  gtk_list_store_clear(summary->datamodel);
391  for (current = g_list_first(currency_list); current; current = g_list_next(current))
392  {
393  const char *mnemonic;
394  gchar *total_mode_label;
395 
396  currency_accum = current->data;
397 
398  mnemonic = gnc_commodity_get_nice_symbol (currency_accum->currency);
399  if (mnemonic == NULL)
400  mnemonic = "";
401 
402  *asset_string = '\0';
403  xaccSPrintAmount(asset_amount_string,
404  currency_accum->assets,
405  gnc_commodity_print_info(currency_accum->currency, TRUE));
406 
407  *profit_string = '\0';
408  xaccSPrintAmount(profit_amount_string,
409  currency_accum->profits,
410  gnc_commodity_print_info(currency_accum->currency, TRUE));
411 
412  gtk_list_store_append(summary->datamodel, &iter);
413  total_mode_label = get_total_mode_label(mnemonic, currency_accum->total_mode);
414  gtk_list_store_set(summary->datamodel, &iter,
415  COLUMN_MNEMONIC_TYPE, total_mode_label,
416  COLUMN_ASSETS, _("Net Assets:"),
417  COLUMN_ASSETS_VALUE, asset_amount_string,
418  COLUMN_PROFITS, _("Profits:"),
419  COLUMN_PROFITS_VALUE, profit_amount_string,
420  -1);
421  g_free(total_mode_label);
422  }
423  gtk_combo_box_set_model(GTK_COMBO_BOX(summary->totals_combo),
424  GTK_TREE_MODEL(summary->datamodel));
425  g_object_unref(summary->datamodel);
426 
427  gtk_combo_box_set_active(GTK_COMBO_BOX(summary->totals_combo), 0);
428  }
429 
430  /* Free the list we created for this */
431  for (current = g_list_first(currency_list);
432  current;
433  current = g_list_next(current))
434  {
435  g_free(current->data);
436  }
437  g_list_free(currency_list);
438 }
439 
440 static void
441 gnc_main_window_summary_destroy_cb(GNCMainSummary *summary, gpointer data)
442 {
443  gnc_prefs_remove_cb_by_id (GNC_PREFS_GROUP, summary->cnxn_id);
444  gnc_unregister_gui_component(summary->component_id);
445  g_free(summary);
446 }
447 
448 static void
449 summarybar_refresh_handler(GHashTable * changes, gpointer user_data)
450 {
451  GNCMainSummary * summary = user_data;
452  gnc_main_window_summary_refresh(summary);
453 }
454 
455 static void
456 prefs_changed_cb (gpointer prefs, gchar *pref, gpointer user_data)
457 {
458  GNCMainSummary * summary = user_data;
459  gnc_main_window_summary_refresh(summary);
460 }
461 
462 GtkWidget *
463 gnc_main_window_summary_new (void)
464 {
465  GNCMainSummary * retval = g_new0(GNCMainSummary, 1);
466  GtkCellRenderer *textRenderer;
467  int i;
468  // These options lead to a better looking layout for the combo-box, where
469  // the "Assets: $####.##" and "Profit: $####.##" values are visually next
470  // to each other.
471  gboolean expandOptions[] = { TRUE, FALSE, TRUE, FALSE, TRUE };
472 
473  retval->datamodel = gtk_list_store_new( N_COLUMNS,
474  G_TYPE_STRING,
475  G_TYPE_STRING,
476  G_TYPE_STRING,
477  G_TYPE_STRING,
478  G_TYPE_STRING );
479 
480  retval->hbox = gtk_hbox_new (FALSE, 5);
481  retval->totals_combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (retval->datamodel));
482  g_object_unref (retval->datamodel);
483 
484  retval->component_id = gnc_register_gui_component (WINDOW_SUMMARYBAR_CM_CLASS,
485  summarybar_refresh_handler,
486  NULL, retval);
487  gnc_gui_component_watch_entity_type (retval->component_id,
488  GNC_ID_ACCOUNT,
489  QOF_EVENT_DESTROY
490  | GNC_EVENT_ITEM_CHANGED);
491 
492  for ( i = 0; i < N_COLUMNS; i++ )
493  {
494  textRenderer = GTK_CELL_RENDERER(gtk_cell_renderer_text_new());
495  gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(retval->totals_combo), textRenderer, expandOptions[i] );
496  gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(retval->totals_combo), textRenderer, "text", i );
497  }
498 
499  gtk_container_set_border_width (GTK_CONTAINER (retval->hbox), 2);
500  gtk_box_pack_start (GTK_BOX(retval->hbox), retval->totals_combo, TRUE, TRUE, 5);
501  gtk_widget_show (retval->totals_combo);
502  gtk_widget_show (retval->hbox);
503 
504  g_signal_connect_swapped (G_OBJECT (retval->hbox), "destroy",
505  G_CALLBACK (gnc_main_window_summary_destroy_cb),
506  retval);
507 
508  gnc_main_window_summary_refresh(retval);
509 
510  retval->cnxn_id = gnc_prefs_register_cb (GNC_PREFS_GROUP, NULL,
511  prefs_changed_cb, retval);
512 
513  return retval->hbox;
514 }
515 
gboolean gnc_commodity_is_currency(const gnc_commodity *cm)
int gnc_commodity_get_fraction(const gnc_commodity *cm)
time64 timespecToTime64(Timespec ts)
gulong gnc_prefs_register_cb(const char *group, const gchar *pref_name, gpointer func, gpointer user_data)
Definition: gnc-prefs.c:128
utility functions for the GnuCash UI
GNCAccountType xaccAccountGetType(const Account *acc)
Definition: Account.c:3009
Timespec timespecCanonicalDayTime(Timespec t)
gnc_numeric gnc_numeric_add(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Use a 64-bit unsigned int timespec.
Definition: gnc-date.h:299
gnc_commodity * gnc_default_currency(void)
Definition: gnc-ui-util.c:939
void gnc_prefs_remove_cb_by_id(const gchar *group, guint id)
Definition: gnc-prefs.c:158
Account handling public routines.
General utilities for dealing with accounting periods.
const char * gnc_commodity_get_nice_symbol(const gnc_commodity *cm)
Additional event handling code.
gnc_numeric xaccAccountGetBalanceAsOfDate(Account *acc, time64 date)
Definition: Account.c:3288
int xaccSPrintAmount(char *bufp, gnc_numeric val, GNCPrintAmountInfo info)
Definition: gnc-ui-util.c:1437
GNCAccountType
Definition: Account.h:96
Generic api to store and retrieve preferences.
gnc_numeric gnc_numeric_sub(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
GList * gnc_account_get_children(const Account *account)
Definition: Account.c:2654
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Definition: Account.c:3148
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Definition: gnc-prefs.c:196
gint64 time64
Definition: gnc-date.h:83
gboolean gnc_commodity_equiv(const gnc_commodity *a, const gnc_commodity *b)
void timespecFromTime64(Timespec *ts, time64 t)