GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
window-autoclear.c
1 /********************************************************************\
2  * window-autoclear.c -- the autoclear window *
3  * Copyright (C) 2010 Cristian KLEIN *
4  * *
5  * This program is free software; you can redistribute it and/or *
6  * modify it under the terms of the GNU General Public License as *
7  * published by the Free Software Foundation; either version 2 of *
8  * the License, or (at your option) any later version. *
9  * *
10  * This program is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU General Public License*
16  * along with this program; if not, contact: *
17  * *
18  * Free Software Foundation Voice: +1-617-542-5942 *
19  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
20  * Boston, MA 02110-1301, USA [email protected] *
21 \********************************************************************/
22 
23 #include "config.h"
24 
25 #include <gtk/gtk.h>
26 #include <glib/gi18n.h>
27 
28 #include "Scrub.h"
29 #include "dialog-account.h"
30 #include "dialog-transfer.h"
31 #include "dialog-utils.h"
32 #include "gnc-amount-edit.h"
33 #include "gnc-component-manager.h"
34 #include "gnc-date-edit.h"
35 #include "gnc-event.h"
36 #include "gnc-gnome-utils.h"
37 #include "gnc-main-window.h"
39 #include "gnc-ui.h"
40 #include "guile-util.h"
41 #include "window-autoclear.h"
42 
43 #define WINDOW_AUTOCLEAR_CM_CLASS "window-autoclear"
44 
45 static QofLogModule log_module = GNC_MOD_GUI;
46 
49 {
50  Account *account; /* The account that we are auto-clearing */
51 
52  gint component_id; /* id of component */
53 
54  GtkWidget *window; /* The auto-clear window */
55  GNCAmountEdit *end_value;/* The ending value */
56  GtkWidget *ok_button;
57  GtkWidget *cancel_button;
58  GtkLabel *status_label;
59 };
60 
62 void gnc_autoclear_window_ok_cb (GtkWidget *widget,
63  AutoClearWindow *data);
64 void gnc_autoclear_window_cancel_cb (GtkWidget *widget,
65  AutoClearWindow *data);
66 
67 /********************************************************************\
68  * gnc_ui_autoclear_window_raise *
69  * shows and raises an auto-clear window *
70  * *
71  * Args: autoClearData - the auto-clear window structure *
72 \********************************************************************/
73 void
74 gnc_ui_autoclear_window_raise(AutoClearWindow * autoClearData)
75 {
76  if (autoClearData == NULL)
77  return;
78 
79  if (autoClearData->window == NULL)
80  return;
81 
82  gtk_window_present(GTK_WINDOW(autoClearData->window));
83 }
84 
85 static char *
86 gnc_autoclear_make_window_name(Account *account)
87 {
88  char *fullname;
89  char *title;
90 
91  fullname = gnc_account_get_full_name(account);
92  title = g_strconcat(fullname, " - ", _("Auto-clear"), NULL);
93 
94  g_free(fullname);
95 
96  return title;
97 }
98 
99 static gboolean
100 ght_gnc_numeric_equal(gconstpointer v1, gconstpointer v2)
101 {
102  gnc_numeric n1 = *(gnc_numeric *)v1, n2 = *(gnc_numeric *)v2;
103  return gnc_numeric_equal(n1, n2);
104 }
105 
106 static guint
107 ght_gnc_numeric_hash(gconstpointer v1)
108 {
109  gnc_numeric n1 = *(gnc_numeric *)v1;
110  gdouble d1 = gnc_numeric_to_double(n1);
111  return g_str_hash(&d1);
112 }
113 
114 typedef struct _sack_foreach_data_t
115 {
116  gnc_numeric split_value;
117  GList *reachable_list;
119 
120 static void sack_foreach_func(gpointer key, gpointer value, gpointer user_data)
121 {
122  sack_foreach_data_t data = (sack_foreach_data_t)user_data;
123  gnc_numeric thisvalue = *(gnc_numeric *)key;
124 
125  gnc_numeric reachable_value = gnc_numeric_add_fixed(thisvalue, data->split_value);
126  data->reachable_list = g_list_append(data->reachable_list, g_memdup(&reachable_value, sizeof(gnc_numeric)));
127  PINFO(" Sack: found %s, added %s\n", gnc_numeric_to_string(thisvalue), gnc_numeric_to_string(reachable_value));
128 }
129 
130 void
131 gnc_autoclear_window_ok_cb (GtkWidget *widget,
132  AutoClearWindow *data)
133 {
134  GList *node, *nc_list = 0, *toclear_list = 0;
135  gnc_numeric toclear_value;
136  GHashTable *sack;
137 
138  gtk_label_set_text(data->status_label, _("Searching for splits to clear ..."));
139 
140  /* Value we have to reach */
141  toclear_value = gnc_amount_edit_get_amount(data->end_value);
142  toclear_value = gnc_numeric_convert(toclear_value, xaccAccountGetCommoditySCU(data->account), GNC_HOW_RND_NEVER);
143 
144  /* Extract which splits are not cleared and compute the amount we have to clear */
145  for (node = xaccAccountGetSplitList(data->account); node; node = node->next)
146  {
147  Split *split = (Split *)node->data;
148  char recn;
149  gnc_numeric value;
150 
151  recn = xaccSplitGetReconcile (split);
152  value = xaccSplitGetAmount (split);
153 
154  if (recn == NREC)
155  nc_list = g_list_append(nc_list, split);
156  else
157  toclear_value = gnc_numeric_sub_fixed(toclear_value, value);
158  }
159 
160  /* Pretty print information */
161  PINFO("Amount to clear: %s\n", gnc_numeric_to_string(toclear_value));
162  PINFO("Available splits:\n");
163  for (node = nc_list; node; node = node->next)
164  {
165  Split *split = (Split *)node->data;
166  gnc_numeric value = xaccSplitGetAmount (split);
167  PINFO(" %s\n", gnc_numeric_to_string(value));
168  }
169 
170  /* Run knapsack */
171  /* Entries in the hash table are:
172  * - key = amount to which we know how to clear (freed by GHashTable)
173  * - value = last split we used to clear this amount (not managed by GHashTable)
174  */
175  PINFO("Knapsacking ...\n");
176  sack = g_hash_table_new_full (ght_gnc_numeric_hash, ght_gnc_numeric_equal, g_free, NULL);
177  for (node = nc_list; node; node = node->next)
178  {
179  Split *split = (Split *)node->data;
180  gnc_numeric split_value = xaccSplitGetAmount(split);
181 
182  GList *node;
183  struct _sack_foreach_data_t data[1];
184  data->split_value = split_value;
185  data->reachable_list = 0;
186 
187  PINFO(" Split value: %s\n", gnc_numeric_to_string(split_value));
188 
189  /* For each value in the sack, compute a new reachable value */
190  g_hash_table_foreach (sack, sack_foreach_func, data);
191 
192  /* Add the value of the split itself to the reachable_list */
193  data->reachable_list = g_list_append(data->reachable_list, g_memdup(&split_value, sizeof(gnc_numeric)));
194 
195  /* Add everything to the sack, looking out for duplicates */
196  for (node = data->reachable_list; node; node = node->next)
197  {
198  gnc_numeric *reachable_value = node->data;
199  Split *toinsert_split = split;
200 
201  PINFO(" Reachable value: %s ", gnc_numeric_to_string(*reachable_value));
202 
203  /* Check if it already exists */
204  if (g_hash_table_lookup_extended(sack, reachable_value, NULL, NULL))
205  {
206  /* If yes, we are in trouble, we reached an amount using two solutions */
207  toinsert_split = NULL;
208  PINFO("dup");
209  }
210  g_hash_table_insert (sack, reachable_value, toinsert_split);
211  PINFO("\n");
212  }
213  g_list_free(data->reachable_list);
214  }
215 
216  /* Check solution */
217  PINFO("Rebuilding solution ...\n");
218  while (!gnc_numeric_zero_p(toclear_value))
219  {
220  gpointer psplit = NULL;
221 
222  PINFO(" Left to clear: %s\n", gnc_numeric_to_string(toclear_value));
223  if (g_hash_table_lookup_extended(sack, &toclear_value, NULL, &psplit))
224  {
225  if (psplit != NULL)
226  {
227  /* Cast the gpointer to the kind of pointer we actually need */
228  Split *split = (Split *)psplit;
229  toclear_list = g_list_prepend(toclear_list, split);
230  toclear_value = gnc_numeric_sub_fixed(toclear_value,
231  xaccSplitGetAmount(split));
232  PINFO(" Cleared: %s -> %s\n",
234  gnc_numeric_to_string(toclear_value));
235  }
236  else
237  {
238  /* We couldn't reconstruct the solution */
239  PINFO(" Solution not unique.\n");
240  gtk_label_set_text(data->status_label, _("Cannot uniquely clear splits. Found multiple possibilities."));
241  return;
242  }
243  }
244  else
245  {
246  PINFO(" No solution found.\n");
247  gtk_label_set_text(data->status_label, _("The selected amount cannot be cleared."));
248  return;
249  }
250  }
251  g_hash_table_destroy (sack);
252 
253  /* Show solution */
254  PINFO("Clearing splits:\n");
255  for (node = toclear_list; node; node = node->next)
256  {
257  Split *split = node->data;
258  char recn;
259  gnc_numeric value;
260 
261  recn = xaccSplitGetReconcile (split);
262  value = xaccSplitGetAmount (split);
263 
264  PINFO(" %c %s\n", recn, gnc_numeric_to_string(value));
265 
266  xaccSplitSetReconcile (split, CREC);
267  }
268  if (toclear_list == 0)
269  PINFO(" None\n");
270 
271  /* Free lists */
272  g_list_free(nc_list);
273  g_list_free(toclear_list);
274 
275  /* Close window */
276  gtk_widget_destroy(data->window);
277  g_free(data);
278 }
279 
280 void
281 gnc_autoclear_window_cancel_cb (GtkWidget *widget,
282  AutoClearWindow *data)
283 {
284  /* Close window */
285  gtk_widget_destroy(data->window);
286  g_free(data);
287 }
288 
289 /********************************************************************\
290  * autoClearWindow *
291  * opens up the window to auto-clear an account *
292  * *
293  * Args: parent - the parent of this window *
294  * account - the account to auto-clear *
295  * Return: autoClearData - the instance of this AutoClearWindow *
296 \********************************************************************/
298 autoClearWindow (GtkWidget *parent, Account *account)
299 {
300  GtkBox *box;
301  GtkLabel *label;
302  GtkBuilder *builder;
303  AutoClearWindow *data;
304  char *title;
305 
306  data = g_new0 (AutoClearWindow, 1);
307  data->account = account;
308 
309  /* Create the dialog box */
310  builder = gtk_builder_new();
311  gnc_builder_add_from_file (builder, "window-autoclear.glade", "Auto-clear Start Dialog");
312  data->window = GTK_WIDGET(gtk_builder_get_object (builder, "Auto-clear Start Dialog"));
313  title = gnc_autoclear_make_window_name (account);
314  gtk_window_set_title(GTK_WINDOW(data->window), title);
315  g_free (title);
316 
317  /* Add amount edit box */
318  data->end_value = GNC_AMOUNT_EDIT(gnc_amount_edit_new());
319  g_signal_connect(GTK_WIDGET(data->end_value), "activate",
320  G_CALLBACK(gnc_autoclear_window_ok_cb), data);
321 
322  box = GTK_BOX(gtk_builder_get_object (builder, "end_value_box"));
323  gtk_box_pack_start(box, GTK_WIDGET(data->end_value), TRUE, TRUE, 0);
324 
325  label = GTK_LABEL(gtk_builder_get_object (builder, "end_label"));
326  gtk_label_set_mnemonic_widget(label, GTK_WIDGET(data->end_value));
327  gtk_widget_grab_focus(GTK_WIDGET(data->end_value));
328 
329  data->status_label = GTK_LABEL(gtk_builder_get_object (builder, "status_label"));
330 
331  if (parent != NULL)
332  gtk_window_set_transient_for (GTK_WINDOW (data->window), GTK_WINDOW (parent));
333 
334  gtk_builder_connect_signals(builder, data);
335  g_object_unref(G_OBJECT(builder));
336 
337  return data;
338 }
339 
gboolean gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
SplitList * xaccAccountGetSplitList(const Account *acc)
Definition: Account.c:3717
Dialog for create/edit an account.
#define PINFO(format, args...)
Definition: qoflog.h:249
int xaccAccountGetCommoditySCU(const Account *acc)
Definition: Account.c:2458
char xaccSplitGetReconcile(const Split *split)
Definition: Split.c:1980
gboolean gnc_numeric_zero_p(gnc_numeric a)
void xaccSplitSetReconcile(Split *split, char recn)
Definition: Split.c:1826
gchar * gnc_numeric_to_string(gnc_numeric n)
Functions for adding content to a window.
gdouble gnc_numeric_to_double(gnc_numeric n)
convert single-entry accounts to clean double-entry
gchar * gnc_account_get_full_name(const Account *account)
Definition: Account.c:3038
Functions providing a register page for the GnuCash UI.
gnc_numeric gnc_numeric_convert(gnc_numeric n, gint64 denom, gint how)
Gnome specific utility functions.
Additional event handling code.
#define CREC
Definition: Split.h:67
Definition: SplitP.h:71
const gchar * QofLogModule
Definition: qofid.h:89
#define NREC
Definition: Split.h:70
gnc_numeric xaccSplitGetAmount(const Split *split)
Definition: Split.c:1987