]> Dogcows Code - chaz/homebank/blob - src/ui-split.c
import homebank-5.1.7
[chaz/homebank] / src / ui-split.c
1 /* HomeBank -- Free, easy, personal accounting for everyone.
2 * Copyright (C) 1995-2018 Maxime DOYEN
3 *
4 * This file is part of HomeBank.
5 *
6 * HomeBank is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * HomeBank is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty ofdeftransaction_amountchanged
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "homebank.h"
21
22 #include "ui-split.h"
23 #include "ui-transaction.h"
24 #include "ui-archive.h"
25 #include "gtk-dateentry.h"
26 #include "ui-payee.h"
27 #include "ui-category.h"
28 #include "ui-account.h"
29 #include "hb-split.h"
30
31
32
33
34 /****************************************************************************/
35 /* Debug macros */
36 /****************************************************************************/
37 #define MYDEBUG 0
38
39 #if MYDEBUG
40 #define DB(x) (x);
41 #else
42 #define DB(x);
43 #endif
44
45 /* our global datas */
46 extern struct HomeBank *GLOBALS;
47 extern struct Preferences *PREFS;
48
49
50 #define GTK_RESPONSE_SPLIT_SUM 10880
51 #define GTK_RESPONSE_SPLIT_REM 10888
52
53
54 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
55
56 static void ui_split_dialog_filter_text_handler (GtkEntry *entry,
57 const gchar *text,
58 gint length,
59 gint *position,
60 gpointer data)
61 {
62 GtkEditable *editable = GTK_EDITABLE(entry);
63 gint i, count=0;
64 gchar *result = g_new0 (gchar, length+1);
65
66 for (i=0; i < length; i++)
67 {
68 if (text[i]=='|')
69 continue;
70 result[count++] = text[i];
71 }
72
73
74 if (count > 0) {
75 g_signal_handlers_block_by_func (G_OBJECT (editable),
76 G_CALLBACK (ui_split_dialog_filter_text_handler),
77 data);
78 gtk_editable_insert_text (editable, result, count, position);
79 g_signal_handlers_unblock_by_func (G_OBJECT (editable),
80 G_CALLBACK (ui_split_dialog_filter_text_handler),
81 data);
82 }
83 g_signal_stop_emission_by_name (G_OBJECT (editable), "insert-text");
84
85 g_free (result);
86 }
87
88
89 void ui_split_dialog_line_sensitive(guint line, gboolean sensitive, gpointer user_data)
90 {
91 struct ui_split_dialog_data *data = user_data;
92
93 if( line > TXN_MAX_SPLIT )
94 return;
95
96 if( line == 0 ) // line 0 always active !
97 sensitive = TRUE;
98
99
100 gtk_widget_set_sensitive(data->PO_cat[line], sensitive);
101 gtk_widget_set_sensitive(data->ST_amount[line], sensitive);
102 gtk_widget_set_sensitive(data->ST_memo[line], sensitive);
103 if(data->BT_rem[line])
104 gtk_widget_set_sensitive(data->BT_rem[line], sensitive);
105 if(data->BT_add[line])
106 gtk_widget_set_sensitive(data->BT_add[line], sensitive);
107
108 if(sensitive == FALSE)
109 {
110 ui_cat_comboboxentry_set_active(GTK_COMBO_BOX(data->PO_cat[line]), 0);
111 gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->ST_amount[line]), 0.0);
112 gtk_entry_set_text(GTK_ENTRY(data->ST_memo[line]), "");
113 }
114
115 if(sensitive == TRUE)
116 data->activeline = line;
117
118 }
119
120
121 void ui_split_dialog_compute(GtkWidget *widget, gpointer user_data)
122 {
123 struct ui_split_dialog_data *data = user_data;
124 gint i, count, nbvalid;
125 //gint j;
126 gchar buf[48];
127 gboolean sensitive, active;
128 //guint32 cat[TXN_MAX_SPLIT];
129 gdouble amt[TXN_MAX_SPLIT];
130 gboolean valid[TXN_MAX_SPLIT];
131
132 DB( g_print("\n(ui_split_dialog_compute)\n") );
133
134 data->sumsplit = data->remsplit = 0.0;
135 nbvalid = 0;
136 for(i=0;i<TXN_MAX_SPLIT;i++)
137 {
138 active = gtk_widget_get_sensitive(data->PO_cat[i]);
139 if(!active) break;
140
141 //cat[i] = ui_cat_comboboxentry_get_key(GTK_COMBO_BOX(data->PO_cat[i]));
142 amt[i] = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->ST_amount[i]));
143 data->sumsplit += amt[i];
144 valid[i] = TRUE;
145
146 if(!amt[i])
147 valid[i] = FALSE;
148
149 /* disable use same category several time
150 for(j=0;j<i;j++)
151 {
152 if(i == j) continue;
153 if( (cat[i] == cat[j]) )
154 {
155 valid[i] = FALSE;
156 break;
157 }
158 }*/
159 DB( g_print("- split %d : act.=%d val.=%d : amt=%.2f\n", i, active, valid[i], amt[i]) );
160
161 if(valid[i])
162 nbvalid++;
163
164 DB( g_print("- nbsplit %d\n", data->nbsplit) );
165
166 if(data->nbsplit == i)
167 {
168 DB( g_print("- set last split %d\n", i) );
169
170 if(data->BT_add[i])
171 gtk_widget_set_sensitive(data->BT_add[i], valid[i]);
172
173 if(data->BT_rem[i])
174 gtk_widget_set_sensitive(data->BT_rem[i], TRUE);
175 }
176 else
177 {
178 DB( g_print("- set off to %d\n", i) );
179
180 if(data->BT_add[i])
181 gtk_widget_set_sensitive(data->BT_add[i], FALSE);
182
183 if(data->BT_rem[i])
184 gtk_widget_set_sensitive(data->BT_rem[i], FALSE);
185 }
186 }
187
188 count = i;
189 DB( g_print("- count=%d, nbvalid=%d\n", count, nbvalid ) );
190
191
192 if(data->splittype == TXN_SPLIT_AMOUNT)
193 {
194 data->remsplit = data->amount - data->sumsplit;
195 }
196
197 //rules validation
198 sensitive = ((count == nbvalid) && (count > 1)) ? TRUE : FALSE;
199 if(data->splittype == TXN_SPLIT_NEW)
200 gtk_dialog_set_response_sensitive(GTK_DIALOG(data->dialog), GTK_RESPONSE_SPLIT_SUM, sensitive);
201
202 if(data->splittype == TXN_SPLIT_AMOUNT)
203 {
204 sensitive = hb_amount_round(data->remsplit, 2) != 0.0 ? FALSE : sensitive;
205 gtk_dialog_set_response_sensitive(GTK_DIALOG(data->dialog), GTK_RESPONSE_ACCEPT, sensitive);
206
207 if(!data->remsplit)
208 g_sprintf(buf, "----");
209 else
210 g_snprintf(buf, 48, "%.2f", data->remsplit);
211
212 gtk_label_set_label(GTK_LABEL(data->LB_remain), buf);
213
214 g_snprintf(buf, 48, "%.2f", data->amount);
215 gtk_label_set_label(GTK_LABEL(data->LB_txnamount), buf);
216 }
217
218 g_snprintf(buf, 48, "%.2f", data->sumsplit);
219 gtk_label_set_text(GTK_LABEL(data->LB_sumsplit), buf);
220
221 }
222
223
224 void ui_split_dialog_inactiveline(GtkWidget *widget, gpointer user_data)
225 {
226 struct ui_split_dialog_data *data;
227 gint line;
228
229 DB( g_print("\n(ui_split_dialog_inactiveline)\n") );
230
231 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
232
233 if(data->nbsplit <= 0) //1st split always active
234 return;
235
236 line = data->nbsplit--;
237
238 DB( g_print("- nbsplit:%d off:%d\n", data->nbsplit, line) );
239
240 ui_split_dialog_line_sensitive(line, FALSE, data);
241 ui_split_dialog_compute(widget, data);
242 }
243
244
245 void ui_split_dialog_activeline(GtkWidget *widget, gpointer user_data)
246 {
247 struct ui_split_dialog_data *data;
248 gint line;
249
250 DB( g_print("\n(ui_split_dialog_activeline)\n") );
251
252 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
253
254 line = data->nbsplit;
255 if(line >= (TXN_MAX_SPLIT-1)) //bound
256 return;
257
258 line = ++data->nbsplit;
259
260 DB( g_print("- nbsplit:%d off:%d\n", data->nbsplit-1, line) );
261
262
263 ui_split_dialog_line_sensitive(line, TRUE, data);
264
265 if(data->splittype == TXN_SPLIT_AMOUNT)
266 {
267 DB( g_print("- line %d :: affect remain\n", line) );
268 g_signal_handler_block(data->ST_amount[line], data->handler_id[line]);
269 gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->ST_amount[line]), data->remsplit);
270 g_signal_handler_unblock(data->ST_amount[line], data->handler_id[line]);
271 }
272
273 ui_split_dialog_compute(widget, data);
274 }
275
276
277 void ui_split_dialog_get(struct ui_split_dialog_data *data)
278 {
279 guint i;
280 Split *split;
281 guint32 kcat;
282 gchar *memo;
283 gdouble amount;
284
285 DB( g_print("(ui_split_dialog_get)\n") );
286
287 da_splits_free(data->splits);
288
289 for(i=0;i<TXN_MAX_SPLIT;i++)
290 {
291 kcat = ui_cat_comboboxentry_get_key_add_new(GTK_COMBO_BOX(data->PO_cat[i]));
292 memo = (gchar *)gtk_entry_get_text(GTK_ENTRY(data->ST_memo[i]));
293 amount = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->ST_amount[i]));
294
295 if(amount)
296 {
297 split = da_split_new(kcat, amount, memo);
298
299 DB( g_print("- get split %d : %d, %.2f, %s\n", i, split->kcat, split->amount, split->memo) );
300
301 da_splits_append (data->splits, split);
302 }
303 }
304 }
305
306
307 void ui_split_dialog_set(struct ui_split_dialog_data *data)
308 {
309 guint count, i;
310 Split *split;
311 gchar *txt;
312
313 DB( g_print("(ui_split_dialog_set)\n") );
314
315 for(i=0;i<TXN_MAX_SPLIT;i++)
316 {
317 ui_split_dialog_line_sensitive(i, FALSE, data);
318 ui_cat_comboboxentry_populate(GTK_COMBO_BOX(data->PO_cat[i]), GLOBALS->h_cat);
319 //#1258821
320 //if( data->splittype == TXN_SPLIT_AMOUNT )
321 //{
322 //if(data->amount > 0.0)
323 // gtk_spin_button_set_range(GTK_SPIN_BUTTON(data->ST_amount[i]), 0.0, G_MAXDOUBLE);
324 //else
325 // gtk_spin_button_set_range(GTK_SPIN_BUTTON(data->ST_amount[i]), -G_MAXDOUBLE, 0.0);
326 //}
327 }
328
329
330 count = da_splits_count(data->splits);
331 data->nbsplit = count > 1 ? count-1 : 0;
332
333 DB( g_print("- count = %d\n", count) );
334
335
336 for(i=0;i<count;i++)
337 {
338 split = data->splits[i];
339
340 DB( g_print("- set split %d : %d, %.2f, %s\n", i, split->kcat, split->amount, split->memo) );
341
342 ui_cat_comboboxentry_set_active(GTK_COMBO_BOX(data->PO_cat[i]), split->kcat);
343 gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->ST_amount[i]), split->amount);
344 txt = (split->memo != NULL) ? split->memo : "";
345 gtk_entry_set_text(GTK_ENTRY(data->ST_memo[i]), txt);
346 ui_split_dialog_line_sensitive(i, TRUE, data);
347 }
348
349 }
350
351
352
353
354 GtkWidget *ui_split_dialog (GtkWidget *parent, Split *ope_splits[], gdouble amount, void (update_callbackFunction(GtkWidget*, gdouble)))
355 {
356 struct ui_split_dialog_data data;
357 GtkWidget *dialog, *content, *mainvbox, *label;
358 GtkWidget *table, *widget;
359 gint row, i;
360
361
362 dialog = gtk_dialog_new_with_buttons (_("Transaction splits"),
363 GTK_WINDOW(parent),
364 0,
365 _("_Cancel"),
366 GTK_RESPONSE_CANCEL,
367 NULL);
368
369 data.dialog = dialog;
370 data.splits = ope_splits;
371 data.amount = amount;
372 data.splittype = amount ? TXN_SPLIT_AMOUNT : TXN_SPLIT_NEW;
373
374 gtk_dialog_add_button(GTK_DIALOG(dialog), _("_Remove"), GTK_RESPONSE_SPLIT_REM);
375
376 /* sum button must appear only when new split add */
377 //#1258821
378 //if(data.splittype == TXN_SPLIT_NEW)
379 gtk_dialog_add_button(GTK_DIALOG(dialog), _("Sum"), GTK_RESPONSE_SPLIT_SUM);
380
381
382 if(data.splittype == TXN_SPLIT_AMOUNT)
383 gtk_dialog_add_button(GTK_DIALOG(dialog), _("_OK"), GTK_RESPONSE_ACCEPT);
384
385 //store our dialog private data
386 g_object_set_data(G_OBJECT(dialog), "inst_data", (gpointer)&data);
387 DB( g_print("(ui_split_dialog) dialog=%p, inst_data=%p\n", dialog, &data) );
388
389 g_signal_connect (dialog, "destroy",
390 G_CALLBACK (gtk_widget_destroyed), &dialog);
391
392 //dialog contents
393 content = gtk_dialog_get_content_area(GTK_DIALOG (dialog));
394 mainvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, SPACING_MEDIUM);
395 gtk_box_pack_start (GTK_BOX (content), mainvbox, TRUE, TRUE, 0);
396 gtk_container_set_border_width (GTK_CONTAINER(mainvbox), SPACING_MEDIUM);
397
398
399 table = gtk_grid_new ();
400 //gtk_container_set_border_width (GTK_CONTAINER (table), SP_BORDER);
401 gtk_grid_set_row_spacing (GTK_GRID (table), SPACING_SMALL/2);
402 gtk_grid_set_column_spacing (GTK_GRID (table), SPACING_MEDIUM/2);
403 gtk_box_pack_start (GTK_BOX (mainvbox), table, TRUE, TRUE, 0);
404
405 row = 0;
406 label = gtk_label_new(_("Category"));
407 gimp_label_set_attributes (GTK_LABEL (label), PANGO_ATTR_SCALE, PANGO_SCALE_SMALL, -1);
408 gtk_grid_attach (GTK_GRID (table), label, 2, row, 1, 1);
409
410 label = gtk_label_new(_("Memo"));
411 gimp_label_set_attributes (GTK_LABEL (label), PANGO_ATTR_SCALE, PANGO_SCALE_SMALL, -1);
412 gtk_grid_attach (GTK_GRID (table), label, 3, row, 1, 1);
413
414 label = gtk_label_new(_("Amount"));
415 gimp_label_set_attributes (GTK_LABEL (label), PANGO_ATTR_SCALE, PANGO_SCALE_SMALL, -1);
416 gtk_grid_attach (GTK_GRID (table), label, 4, row, 1, 1);
417
418 for(i=0;i<TXN_MAX_SPLIT;i++)
419 {
420 row++;
421
422 data.BT_rem[i] = NULL;
423 data.BT_add[i] = NULL;
424
425 if(i > 0)
426 {
427 widget = gtk_button_new_with_label ("-");
428 data.BT_rem[i] = widget;
429 gtk_grid_attach (GTK_GRID (table), widget, 0, row, 1, 1);
430 }
431
432 if( (i < (TXN_MAX_SPLIT-1)) )
433 {
434 widget = gtk_button_new_with_label ("+");
435 data.BT_add[i] = widget;
436 gtk_grid_attach (GTK_GRID (table), widget, 1, row, 1, 1);
437 }
438
439 widget = ui_cat_comboboxentry_new(NULL);
440 data.PO_cat[i] = widget;
441 gtk_widget_set_hexpand (widget, TRUE);
442 gtk_grid_attach (GTK_GRID (table), widget, 2, row, 1, 1);
443
444 widget = make_string(NULL);
445 gtk_widget_set_hexpand (widget, TRUE);
446 data.ST_memo[i] = widget;
447 gtk_grid_attach (GTK_GRID (table), widget, 3, row, 1, 1);
448
449 widget = make_amount(NULL);
450 data.ST_amount[i] = widget;
451 gtk_grid_attach (GTK_GRID (table), widget, 4, row, 1, 1);
452
453 //connect all our signals
454 g_signal_connect (data.PO_cat[i], "changed", G_CALLBACK (ui_split_dialog_compute), &data);
455 g_signal_connect (data.ST_memo[i], "insert-text", G_CALLBACK(ui_split_dialog_filter_text_handler), NULL);
456 data.handler_id[i] = g_signal_connect (G_OBJECT (data.ST_amount[i]), "value-changed", G_CALLBACK (ui_split_dialog_compute), &data);
457 if(data.BT_rem[i])
458 g_signal_connect (data.BT_rem[i], "clicked", G_CALLBACK (ui_split_dialog_inactiveline), GINT_TO_POINTER(i));
459 if(data.BT_add[i])
460 g_signal_connect (data.BT_add[i], "clicked", G_CALLBACK (ui_split_dialog_activeline), GINT_TO_POINTER(i));
461 }
462
463 row++;
464 label = gtk_label_new(_("Sum of splits:"));
465 gtk_widget_set_halign (label, GTK_ALIGN_END);
466 gtk_grid_attach (GTK_GRID (table), label, 3, row, 1, 1);
467 widget = gtk_label_new(NULL);
468 gtk_widget_set_halign (widget, GTK_ALIGN_END);
469 data.LB_sumsplit = widget;
470 gtk_grid_attach (GTK_GRID (table), widget, 4, row, 1, 1);
471
472 if( data.splittype == TXN_SPLIT_AMOUNT )
473 {
474 row++;
475 label = gtk_label_new(_("Unassigned:"));
476 gtk_widget_set_halign (label, GTK_ALIGN_END);
477 gtk_grid_attach (GTK_GRID (table), label, 3, row, 1, 1);
478 widget = gtk_label_new(NULL);
479 gtk_widget_set_halign (widget, GTK_ALIGN_END);
480 gtk_widget_set_margin_left(widget, 20);
481 gtk_widget_set_margin_right(widget, 20);
482 data.LB_remain = widget;
483 gtk_grid_attach (GTK_GRID (table), widget, 4, row, 1, 1);
484
485 row++;
486 widget = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
487 gtk_grid_attach (GTK_GRID (table), widget, 4, row, 1, 1);
488
489 row++;
490 label = gtk_label_new(_("Transaction amount:"));
491 gtk_widget_set_halign (label, GTK_ALIGN_END);
492 gtk_grid_attach (GTK_GRID (table), label, 3, row, 1, 1);
493 widget = gtk_label_new(NULL);
494 gtk_widget_set_halign (widget, GTK_ALIGN_END);
495 gtk_widget_set_margin_left(widget, 20);
496 gtk_widget_set_margin_right(widget, 20);
497 data.LB_txnamount = widget;
498 gtk_grid_attach (GTK_GRID (table), widget, 4, row, 1, 1);
499 }
500
501
502 //setup, init and show dialog
503 //ui_cur_manage_dialog_setup(&data);
504
505
506 ui_split_dialog_set(&data);
507 ui_split_dialog_compute(NULL, &data);
508
509
510 //ui_cur_manage_dialog_update(data.LV_cur, NULL);
511
512 gtk_window_set_default_size(GTK_WINDOW(dialog), 480, -1);
513
514 gtk_widget_show_all (dialog);
515
516 //wait for the user
517 gint result = gtk_dialog_run (GTK_DIALOG (dialog));
518
519 switch (result)
520 {
521 case GTK_RESPONSE_ACCEPT:
522 //do_application_specific_something ();
523 ui_split_dialog_get(&data);
524 update_callbackFunction(parent,data.sumsplit);
525 break;
526 case GTK_RESPONSE_SPLIT_REM:
527 da_splits_free(ope_splits);
528 update_callbackFunction(parent,data.sumsplit);
529 break;
530 case GTK_RESPONSE_SPLIT_SUM: // sum split and alter txn amount
531 ui_split_dialog_get(&data);
532 update_callbackFunction(parent,data.sumsplit);
533 break;
534 default:
535 //do_nothing_since_dialog_was_cancelled ();
536 break;
537 }
538
539 // debug
540 /*#if MYDEBUG == 1
541 {
542 guint i;
543
544 for(i=0;i<TXN_MAX_SPLIT;i++)
545 {
546 Split *split = data.ope_splits[i];
547 if(data.ope_splits[i] == NULL)
548 break;
549 g_print(" split %d : %d, %.2f, %s\n", i, split->kcat, split->amount, split->memo);
550 }
551 }
552 #endif*/
553
554 // cleanup and destroy
555 //GLOBALS->changes_count += data.change;
556 gtk_widget_destroy (dialog);
557
558 return NULL;
559 }
560
This page took 0.061338 seconds and 5 git commands to generate.