|
GnuCash 2.4.99
|
00001 /********************************************************************\ 00002 * window-autoclear.c -- the autoclear window * 00003 * Copyright (C) 2010 Cristian KLEIN * 00004 * * 00005 * This program is free software; you can redistribute it and/or * 00006 * modify it under the terms of the GNU General Public License as * 00007 * published by the Free Software Foundation; either version 2 of * 00008 * the License, or (at your option) any later version. * 00009 * * 00010 * This program is distributed in the hope that it will be useful, * 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 00013 * GNU General Public License for more details. * 00014 * * 00015 * You should have received a copy of the GNU General Public License* 00016 * along with this program; if not, contact: * 00017 * * 00018 * Free Software Foundation Voice: +1-617-542-5942 * 00019 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * 00020 * Boston, MA 02110-1301, USA gnu@gnu.org * 00021 \********************************************************************/ 00022 00023 #include "config.h" 00024 00025 #include <gtk/gtk.h> 00026 #include <glib/gi18n.h> 00027 00028 #include "Scrub.h" 00029 #include "Scrub3.h" 00030 #include "dialog-account.h" 00031 #include "dialog-transfer.h" 00032 #include "dialog-utils.h" 00033 #include "gnc-amount-edit.h" 00034 #include "gnc-component-manager.h" 00035 #include "gnc-date-edit.h" 00036 #include "gnc-event.h" 00037 #include "gnc-gconf-utils.h" 00038 #include "gnc-gnome-utils.h" 00039 #include "gnc-main-window.h" 00040 #include "gnc-plugin-page-register.h" 00041 #include "gnc-ui.h" 00042 #include "guile-util.h" 00043 #include "window-autoclear.h" 00044 00045 #define WINDOW_AUTOCLEAR_CM_CLASS "window-autoclear" 00046 00047 static QofLogModule log_module = GNC_MOD_GUI; 00048 00050 struct _AutoClearWindow 00051 { 00052 Account *account; /* The account that we are auto-clearing */ 00053 00054 gint component_id; /* id of component */ 00055 00056 GtkWidget *window; /* The auto-clear window */ 00057 GNCAmountEdit *end_value;/* The ending value */ 00058 GtkWidget *ok_button; 00059 GtkWidget *cancel_button; 00060 GtkLabel *status_label; 00061 }; 00062 00064 void gnc_autoclear_window_ok_cb (GtkWidget *widget, 00065 AutoClearWindow *data); 00066 void gnc_autoclear_window_cancel_cb (GtkWidget *widget, 00067 AutoClearWindow *data); 00068 00069 /********************************************************************\ 00070 * gnc_ui_autoclear_window_raise * 00071 * shows and raises an auto-clear window * 00072 * * 00073 * Args: autoClearData - the auto-clear window structure * 00074 \********************************************************************/ 00075 void 00076 gnc_ui_autoclear_window_raise(AutoClearWindow * autoClearData) 00077 { 00078 if (autoClearData == NULL) 00079 return; 00080 00081 if (autoClearData->window == NULL) 00082 return; 00083 00084 gtk_window_present(GTK_WINDOW(autoClearData->window)); 00085 } 00086 00087 static char * 00088 gnc_autoclear_make_window_name(Account *account) 00089 { 00090 char *fullname; 00091 char *title; 00092 00093 fullname = gnc_account_get_full_name(account); 00094 title = g_strconcat(fullname, " - ", _("Auto-clear"), NULL); 00095 00096 g_free(fullname); 00097 00098 return title; 00099 } 00100 00101 static gboolean 00102 ght_gnc_numeric_equal(gconstpointer v1, gconstpointer v2) 00103 { 00104 gnc_numeric n1 = *(gnc_numeric *)v1, n2 = *(gnc_numeric *)v2; 00105 return gnc_numeric_equal(n1, n2); 00106 } 00107 00108 static guint 00109 ght_gnc_numeric_hash(gconstpointer v1) 00110 { 00111 gnc_numeric n1 = *(gnc_numeric *)v1; 00112 gdouble d1 = gnc_numeric_to_double(n1); 00113 return g_str_hash(&d1); 00114 } 00115 00116 typedef struct _sack_foreach_data_t 00117 { 00118 gnc_numeric split_value; 00119 GList *reachable_list; 00120 } *sack_foreach_data_t; 00121 00122 static void sack_foreach_func(gpointer key, gpointer value, gpointer user_data) 00123 { 00124 sack_foreach_data_t data = (sack_foreach_data_t)user_data; 00125 gnc_numeric thisvalue = *(gnc_numeric *)key; 00126 00127 gnc_numeric reachable_value = gnc_numeric_add_fixed(thisvalue, data->split_value); 00128 data->reachable_list = g_list_append(data->reachable_list, g_memdup(&reachable_value, sizeof(gnc_numeric))); 00129 PINFO(" Sack: found %s, added %s\n", gnc_numeric_to_string(thisvalue), gnc_numeric_to_string(reachable_value)); 00130 } 00131 00132 void 00133 gnc_autoclear_window_ok_cb (GtkWidget *widget, 00134 AutoClearWindow *data) 00135 { 00136 GList *node, *nc_list = 0, *toclear_list = 0; 00137 gnc_numeric toclear_value; 00138 GHashTable *sack; 00139 00140 gtk_label_set_text(data->status_label, "Searching for splits to clear ..."); 00141 00142 /* Value we have to reach */ 00143 toclear_value = gnc_amount_edit_get_amount(data->end_value); 00144 toclear_value = gnc_numeric_convert(toclear_value, xaccAccountGetCommoditySCU(data->account), GNC_HOW_RND_NEVER); 00145 00146 /* Extract which splits are not cleared and compute the amount we have to clear */ 00147 for (node = xaccAccountGetSplitList(data->account); node; node = node->next) 00148 { 00149 Split *split = (Split *)node->data; 00150 char recn; 00151 gnc_numeric value; 00152 00153 recn = xaccSplitGetReconcile (split); 00154 value = xaccSplitGetAmount (split); 00155 00156 if (recn == NREC) 00157 nc_list = g_list_append(nc_list, split); 00158 else 00159 toclear_value = gnc_numeric_sub_fixed(toclear_value, value); 00160 } 00161 00162 /* Pretty print information */ 00163 PINFO("Amount to clear: %s\n", gnc_numeric_to_string(toclear_value)); 00164 PINFO("Available splits:\n"); 00165 for (node = nc_list; node; node = node->next) 00166 { 00167 Split *split = (Split *)node->data; 00168 gnc_numeric value = xaccSplitGetAmount (split); 00169 PINFO(" %s\n", gnc_numeric_to_string(value)); 00170 } 00171 00172 /* Run knapsack */ 00173 /* Entries in the hash table are: 00174 * - key = amount to which we know how to clear (freed by GHashTable) 00175 * - value = last split we used to clear this amount (not managed by GHashTable) 00176 */ 00177 PINFO("Knapsacking ...\n"); 00178 sack = g_hash_table_new_full (ght_gnc_numeric_hash, ght_gnc_numeric_equal, g_free, NULL); 00179 for (node = nc_list; node; node = node->next) 00180 { 00181 Split *split = (Split *)node->data; 00182 gnc_numeric split_value = xaccSplitGetAmount(split); 00183 00184 GList *node; 00185 struct _sack_foreach_data_t data[1]; 00186 data->split_value = split_value; 00187 data->reachable_list = 0; 00188 00189 PINFO(" Split value: %s\n", gnc_numeric_to_string(split_value)); 00190 00191 /* For each value in the sack, compute a new reachable value */ 00192 g_hash_table_foreach (sack, sack_foreach_func, data); 00193 00194 /* Add the value of the split itself to the reachable_list */ 00195 data->reachable_list = g_list_append(data->reachable_list, g_memdup(&split_value, sizeof(gnc_numeric))); 00196 00197 /* Add everything to the sack, looking out for duplicates */ 00198 for (node = data->reachable_list; node; node = node->next) 00199 { 00200 gnc_numeric *reachable_value = node->data; 00201 Split *toinsert_split = split; 00202 00203 PINFO(" Reachable value: %s ", gnc_numeric_to_string(*reachable_value)); 00204 00205 /* Check if it already exists */ 00206 if (g_hash_table_lookup_extended(sack, reachable_value, NULL, NULL)) 00207 { 00208 /* If yes, we are in trouble, we reached an amount using two solutions */ 00209 toinsert_split = NULL; 00210 PINFO("dup"); 00211 } 00212 g_hash_table_insert (sack, reachable_value, toinsert_split); 00213 PINFO("\n"); 00214 } 00215 g_list_free(data->reachable_list); 00216 } 00217 00218 /* Check solution */ 00219 PINFO("Rebuilding solution ...\n"); 00220 while (!gnc_numeric_zero_p(toclear_value)) 00221 { 00222 gpointer psplit = NULL; 00223 00224 PINFO(" Left to clear: %s\n", gnc_numeric_to_string(toclear_value)); 00225 if (g_hash_table_lookup_extended(sack, &toclear_value, NULL, &psplit)) 00226 { 00227 if (psplit != NULL) 00228 { 00229 /* Cast the gpointer to the kind of pointer we actually need */ 00230 Split *split = (Split *)psplit; 00231 toclear_list = g_list_prepend(toclear_list, split); 00232 toclear_value = gnc_numeric_sub_fixed(toclear_value, 00233 xaccSplitGetAmount(split)); 00234 PINFO(" Cleared: %s -> %s\n", 00235 gnc_numeric_to_string(xaccSplitGetAmount(split)), 00236 gnc_numeric_to_string(toclear_value)); 00237 } 00238 else 00239 { 00240 /* We couldn't reconstruct the solution */ 00241 PINFO(" Solution not unique.\n"); 00242 gtk_label_set_text(data->status_label, "Cannot uniquely clear splits. Found multiple possibilities."); 00243 return; 00244 } 00245 } 00246 else 00247 { 00248 PINFO(" No solution found.\n"); 00249 gtk_label_set_text(data->status_label, "The selected amount cannot be cleared."); 00250 return; 00251 } 00252 } 00253 g_hash_table_destroy (sack); 00254 00255 /* Show solution */ 00256 PINFO("Clearing splits:\n"); 00257 for (node = toclear_list; node; node = node->next) 00258 { 00259 Split *split = node->data; 00260 char recn; 00261 gnc_numeric value; 00262 00263 recn = xaccSplitGetReconcile (split); 00264 value = xaccSplitGetAmount (split); 00265 00266 PINFO(" %c %s\n", recn, gnc_numeric_to_string(value)); 00267 00268 xaccSplitSetReconcile (split, CREC); 00269 } 00270 if (toclear_list == 0) 00271 PINFO(" None\n"); 00272 00273 /* Free lists */ 00274 g_list_free(nc_list); 00275 g_list_free(toclear_list); 00276 00277 /* Close window */ 00278 gtk_widget_destroy(data->window); 00279 g_free(data); 00280 } 00281 00282 void 00283 gnc_autoclear_window_cancel_cb (GtkWidget *widget, 00284 AutoClearWindow *data) 00285 { 00286 /* Close window */ 00287 gtk_widget_destroy(data->window); 00288 g_free(data); 00289 } 00290 00291 /********************************************************************\ 00292 * autoClearWindow * 00293 * opens up the window to auto-clear an account * 00294 * * 00295 * Args: parent - the parent of this window * 00296 * account - the account to auto-clear * 00297 * Return: autoClearData - the instance of this AutoClearWindow * 00298 \********************************************************************/ 00299 AutoClearWindow * 00300 autoClearWindow (GtkWidget *parent, Account *account) 00301 { 00302 GtkBox *box; 00303 GtkLabel *label; 00304 GtkBuilder *builder; 00305 AutoClearWindow *data; 00306 char *title; 00307 00308 data = g_new0 (AutoClearWindow, 1); 00309 data->account = account; 00310 00311 /* Create the dialog box */ 00312 builder = gtk_builder_new(); 00313 gnc_builder_add_from_file (builder, "window-autoclear.glade", "Auto-clear Start Dialog"); 00314 data->window = GTK_WIDGET(gtk_builder_get_object (builder, "Auto-clear Start Dialog")); 00315 title = gnc_autoclear_make_window_name (account); 00316 gtk_window_set_title(GTK_WINDOW(data->window), title); 00317 g_free (title); 00318 00319 /* Add amount edit box */ 00320 data->end_value = GNC_AMOUNT_EDIT(gnc_amount_edit_new()); 00321 g_signal_connect(GTK_WIDGET(data->end_value), "activate", 00322 G_CALLBACK(gnc_autoclear_window_ok_cb), data); 00323 00324 box = GTK_BOX(gtk_builder_get_object (builder, "end_value_box")); 00325 gtk_box_pack_start(box, GTK_WIDGET(data->end_value), TRUE, TRUE, 0); 00326 00327 label = GTK_LABEL(gtk_builder_get_object (builder, "end_label")); 00328 gtk_label_set_mnemonic_widget(label, GTK_WIDGET(data->end_value)); 00329 gtk_widget_grab_focus(GTK_WIDGET(data->end_value)); 00330 00331 data->status_label = GTK_LABEL(gtk_builder_get_object (builder, "status_label")); 00332 00333 if (parent != NULL) 00334 gtk_window_set_transient_for (GTK_WINDOW (data->window), GTK_WINDOW (parent)); 00335 00336 gtk_builder_connect_signals(builder, data); 00337 g_object_unref(G_OBJECT(builder)); 00338 00339 return data; 00340 } 00341
1.7.4