1 /* ************************************************************************** */
2 /*                                                                            */
3 /*     copyright (c)    2000-2008 Cédric Auger (cedric@grisbi.org)            */
4 /*          2004-2008 Benjamin Drieu (bdrieu@april.org)                       */
5 /*                      2008-2020 Pierre Biava (grisbi@pierre.biava.name)     */
6 /*          https://www.grisbi.org/                                           */
7 /*                                                                            */
8 /*  This program is free software; you can redistribute it and/or modify      */
9 /*  it under the terms of the GNU General Public License as published by      */
10 /*  the Free Software Foundation; either version 2 of the License, or         */
11 /*  (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, write to the Free Software               */
20 /*  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
21 /*                                                                            */
22 /* ************************************************************************** */
23 
24 /**
25  * \file data_partial_balance.c
26  */
27 
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31 
32 #include "include.h"
33 #include <gdk/gdkkeysyms.h>
34 #include <glib/gi18n.h>
35 
36 /*START_INCLUDE*/
37 #include "gsb_data_partial_balance.h"
38 #include "dialog.h"
39 #include "grisbi_app.h"
40 #include "gsb_currency.h"
41 #include "gsb_data_account.h"
42 #include "gsb_data_currency.h"
43 #include "gsb_data_currency_link.h"
44 #include "gsb_data_transaction.h"
45 #include "navigation.h"
46 #include "gsb_real.h"
47 #include "gsb_rgba.h"
48 #include "utils_real.h"
49 #include "utils_str.h"
50 #include "utils.h"
51 #include "structures.h"
52 #include "utils_prefs.h"
53 #include "utils_widgets.h"
54 #include "erreur.h"
55 /*END_INCLUDE*/
56 
57 /** \struct
58  * contenant les éléments d'un solde partiel
59  * */
60 typedef struct
61 {
62     gint partial_balance_number;
63     gchar *balance_name;
64     gchar *liste_cptes;
65     KindAccount kind;
66     gint currency;
67     gboolean colorise;
68 } struct_partial_balance;
69 
70 
71 /*START_STATIC*/
72 static void _gsb_data_partial_balance_free ( struct_partial_balance *partial_balance);
73 static gpointer gsb_data_partial_balance_get_structure ( gint partial_balance_number );
74 static gboolean gsb_data_partial_balance_init_from_liste_cptes ( gint partial_balance_number,
75                         GtkWidget *parent );
76 static gboolean gsb_data_partial_balance_move ( gint orig_partial_number, gint dest_pos );
77 static GtkWidget *gsb_partial_balance_create_dialog ( gint action, gint spin_value );
78 static GtkWidget *gsb_partial_balance_create_list_accounts ( GtkWidget *entry );
79 static gint gsb_partial_balance_new ( const gchar *name );
80 static void gsb_partial_balance_renumerote ( void );
81 static gint gsb_partial_balance_request_currency ( GtkWidget *parent );
82 static gboolean gsb_partial_balance_select_account ( GtkTreeSelection *selection,
83                         GtkTreeModel *model,
84                         GtkTreePath *path,
85                         gboolean path_currently_selected,
86                         GObject *entry );
87 static void gsb_partial_balance_selectionne_cptes ( GtkWidget *tree_view,
88                         const gchar *liste_cptes );
89 /*END_STATIC*/
90 
91 /*START_EXTERN*/
92 /*END_EXTERN*/
93 
94 /* devise de base des soldes partiels de comptes ayant des devises différentes */
95 gint no_devise_solde_partiels;
96 
97 /** contains the g_slist of struct_partial_balance */
98 static GSList *partial_balance_list = NULL;
99 
100 /** a pointer to the last partial_balance used (to increase the speed) */
101 static struct_partial_balance *partial_balance_buffer;
102 
103 static GtkListStore *model_accueil;
104 
105 /*********************************************************************************************/
106 /*              Fonctions générales                                                          */
107 /*********************************************************************************************/
108 /**
109  * create a new partial_balance, give him a number, append it to the list
110  * and return the number
111  *
112  * \param name the name of the partial_balance (can be freed after, it's a copy) or NULL
113  *
114  * \return the number of the new partial_balance
115  * */
gsb_partial_balance_new(const gchar * name)116 gint gsb_partial_balance_new ( const gchar *name )
117 {
118     struct_partial_balance *partial_balance;
119 
120     partial_balance = g_malloc0 ( sizeof ( struct_partial_balance ) );
121     if ( ! partial_balance )
122     {
123         dialogue_error_memory ( );
124         return 0;
125     }
126     partial_balance -> partial_balance_number = g_slist_length ( partial_balance_list ) + 1;
127 
128     if ( name )
129         partial_balance -> balance_name = my_strdup ( name );
130     else
131         partial_balance -> balance_name = NULL;
132 
133     partial_balance_list = g_slist_append ( partial_balance_list, partial_balance );
134 
135     partial_balance_buffer = partial_balance;
136 
137     return partial_balance -> partial_balance_number;
138 }
139 
140 
141 /**
142  * create a new partial balance and insert it at position "pos"
143  * and return the number
144  *
145  * \param name the name of the partial_balance (can be freed after, it's a copy) or NULL
146  * \param pos
147  *
148  * \return the number of the new partial_balance
149  * */
gsb_partial_balance_new_at_position(const gchar * name,gint pos)150 gint gsb_partial_balance_new_at_position ( const gchar *name, gint pos )
151 {
152     struct_partial_balance *partial_balance;
153 
154     partial_balance = g_malloc0 ( sizeof ( struct_partial_balance ) );
155     if ( ! partial_balance )
156     {
157         dialogue_error_memory ( );
158         return 0;
159     }
160 
161     if ( name )
162         partial_balance -> balance_name = my_strdup ( name );
163     else
164         partial_balance -> balance_name = NULL;
165 
166     partial_balance_list = g_slist_insert ( partial_balance_list, partial_balance, pos - 1 );
167     gsb_partial_balance_renumerote ( );
168 
169     partial_balance_buffer = partial_balance;
170 
171     return partial_balance -> partial_balance_number;
172 }
173 
174 
175 /**
176  *
177  * \param
178  * \param
179  *
180  * */
gsb_partial_balance_create_model(void)181 GtkListStore *gsb_partial_balance_create_model ( void )
182 {
183 
184     model_accueil = gtk_list_store_new (7, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
185 										G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN, GDK_TYPE_RGBA);
186 
187     return model_accueil;
188 }
189 
190 /**
191  *
192  * \param
193  * \param
194  *
195  * */
gsb_partial_balance_fill_model(GtkListStore * list_store)196 void gsb_partial_balance_fill_model ( GtkListStore *list_store )
197 {
198     GSList *list_tmp;
199     GtkTreeIter iter;
200 
201     list_tmp = partial_balance_list;
202     gtk_list_store_clear ( GTK_LIST_STORE (list_store) );
203 
204     while ( list_tmp )
205     {
206         struct_partial_balance *partial_balance;
207         gchar *kind_str;
208         gchar *currency_str = (gchar*)"";
209 
210         partial_balance = list_tmp -> data;
211 
212         switch ( partial_balance -> kind )
213         {
214         case GSB_TYPE_BALANCE:
215             kind_str = g_strdup ( _("Additional balance") );
216             break;
217 
218         case GSB_TYPE_CASH:
219             kind_str = g_strdup ( _("Cash account") );
220             break;
221 
222         case GSB_TYPE_LIABILITIES:
223             kind_str = g_strdup ( _("Liabilities account") );
224             break;
225 
226         case GSB_TYPE_ASSET:
227             kind_str = g_strdup ( _("Assets account") );
228             break;
229 
230 		case GSB_TYPE_BANK:
231         default:
232             kind_str = g_strdup ( _("Bank account") );
233         }
234         currency_str = gsb_data_currency_get_name ( partial_balance -> currency );
235 
236         gtk_list_store_append (GTK_LIST_STORE (list_store), &iter);
237         gtk_list_store_set (GTK_LIST_STORE (list_store), &iter,
238                     0, partial_balance -> balance_name,
239                     1, partial_balance -> liste_cptes,
240                     2, kind_str,
241                     3, currency_str,
242                     4, partial_balance -> partial_balance_number,
243                     5, partial_balance -> colorise,
244                     -1);
245 		g_free (kind_str);
246 
247         list_tmp = list_tmp -> next;
248     }
249 }
250 
251 
252 /**
253  * add a partial_balance
254  *
255  * */
gsb_partial_balance_add(GtkWidget * button,GtkWidget * main_widget)256 void gsb_partial_balance_add ( GtkWidget *button, GtkWidget *main_widget )
257 {
258     GtkWidget *dialog;
259     GtkWidget *entry_name;
260     GtkWidget *entry_list;
261     GtkWidget *spin_bouton;
262     GtkWidget *colorise_bouton;
263     gint action = 1; /* 1 create 2 modify */
264     gint result;
265 
266     devel_debug ( NULL);
267 
268     dialog = gsb_partial_balance_create_dialog ( action,
269                         g_slist_length ( partial_balance_list ) + 1 );
270 
271     entry_name = g_object_get_data ( G_OBJECT ( dialog ), "entry_name" );
272     entry_list = g_object_get_data ( G_OBJECT ( dialog ), "entry_list" );
273     spin_bouton = g_object_get_data ( G_OBJECT ( dialog ), "spin_bouton" );
274     colorise_bouton = g_object_get_data ( G_OBJECT ( dialog ), "colorise_bouton" );
275 
276     gtk_widget_show_all ( GTK_WIDGET ( dialog ) );
277 
278 dialog_return:
279     result = gtk_dialog_run ( GTK_DIALOG ( dialog ) );
280 
281     if ( result == 1)
282     {
283         GtkTreeView *treeview;
284         GtkTreeModel *model;
285         const gchar *name, *liste_cptes;
286         gint partial_balance_number;
287         gint position;
288 
289         name = gtk_entry_get_text ( GTK_ENTRY ( entry_name ) );
290         liste_cptes = gtk_entry_get_text ( GTK_ENTRY ( entry_list ) );
291 
292         if ( strlen ( name ) && strlen ( liste_cptes )
293          &&
294          g_utf8_strchr  ( liste_cptes, -1, ';' ) )
295         {
296             position = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON ( spin_bouton ) );
297             if ( position > (gint) g_slist_length ( partial_balance_list ) )
298                 partial_balance_number = gsb_partial_balance_new ( name );
299             else
300                 partial_balance_number = gsb_partial_balance_new_at_position (
301                         name, position );
302 
303             gsb_data_partial_balance_set_liste_cptes ( partial_balance_number,
304                         liste_cptes );
305             gsb_data_partial_balance_init_from_liste_cptes ( partial_balance_number,
306                         dialog );
307             gsb_data_partial_balance_set_colorise ( partial_balance_number,
308                         gtk_toggle_button_get_active (
309                         GTK_TOGGLE_BUTTON ( colorise_bouton ) ) );
310 
311             /* on met à jour le model */
312             treeview = g_object_get_data ( G_OBJECT (main_widget), "treeview" );
313             model = gtk_tree_view_get_model ( treeview );
314             gsb_partial_balance_fill_model ( GTK_LIST_STORE ( model ) );
315 
316             /* MAJ HOME_PAGE */
317             gsb_gui_navigation_update_home_page ( );
318         }
319         else if (  g_utf8_strchr ( liste_cptes, -1, ';' ) == NULL )
320         {
321             dialogue_warning_hint ( _("You must select at least two accounts."),
322                         _("Only one account is selected.") );
323             goto dialog_return;
324         }
325         else
326         {
327             dialogue_warning_hint ( _("The name of the partial balance "
328                         "and the list of accounts must be completed."),
329                         _("All fields are not filled in") );
330             goto dialog_return;
331         }
332     }
333     gtk_widget_destroy ( GTK_WIDGET ( dialog ) );
334 }
335 
336 
337 /**
338  * Edit a partial_balance
339  *
340  * */
gsb_partial_balance_edit(GtkWidget * button,GtkWidget * main_widget)341 void gsb_partial_balance_edit ( GtkWidget *button, GtkWidget *main_widget )
342 {
343     GtkWidget *dialog;
344     GtkWidget *entry_name;
345     GtkWidget *entry_list;
346     GtkWidget *spin_bouton;
347     GtkWidget *colorise_bouton;
348     GtkWidget *account_list;
349     GtkWidget *treeview;
350     GtkWidget *account_treeview;
351     GtkTreeSelection *selection;
352     GtkTreeModel *model;
353     GtkTreeIter iter;
354     gchar *balance_name;
355     gchar *liste_cptes;
356     gint partial_balance_number;
357     gint action = 2; /* 1 create 2 modify */
358     gint result;
359 
360     devel_debug ( NULL);
361 
362     dialog = gsb_partial_balance_create_dialog ( action,
363                         g_slist_length ( partial_balance_list ) + 1 );
364 
365     entry_name = g_object_get_data ( G_OBJECT ( dialog ), "entry_name" );
366     entry_list = g_object_get_data ( G_OBJECT ( dialog ), "entry_list" );
367     account_list = g_object_get_data ( G_OBJECT ( dialog ), "account_list" );
368     spin_bouton = g_object_get_data ( G_OBJECT ( dialog ), "spin_bouton" );
369     colorise_bouton = g_object_get_data ( G_OBJECT ( dialog ), "colorise_bouton" );
370 
371     gtk_widget_show_all ( GTK_WIDGET ( dialog ) );
372 
373     /* initialisation des données */
374     treeview = g_object_get_data ( G_OBJECT ( main_widget ), "treeview" );
375     selection = gtk_tree_view_get_selection ( GTK_TREE_VIEW (treeview) );
376     if ( !gtk_tree_selection_get_selected ( selection, &model, &iter) )
377         return ;
378 
379     gtk_tree_model_get ( model, &iter,
380                         0, &balance_name,
381                         1, &liste_cptes,
382                         4, &partial_balance_number,
383                         -1);
384 
385     gtk_entry_set_text ( GTK_ENTRY ( entry_name ), balance_name );
386     gtk_entry_set_text ( GTK_ENTRY ( entry_list ), liste_cptes );
387     gtk_spin_button_set_value ( GTK_SPIN_BUTTON ( spin_bouton ),
388                         (gdouble) partial_balance_number );
389     gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON ( colorise_bouton ),
390                         gsb_data_partial_balance_get_colorise (
391                         partial_balance_number ) );
392     account_treeview = g_object_get_data ( G_OBJECT ( account_list ), "account_treeview");
393     gsb_partial_balance_selectionne_cptes ( account_treeview, liste_cptes );
394 
395     gtk_widget_show_all ( GTK_WIDGET ( dialog ) );
396 
397 dialog_return:
398     result = gtk_dialog_run ( GTK_DIALOG ( dialog ));
399 
400     if ( result == 1)
401     {
402         const gchar *name, *new_liste_cptes;
403         gint position;
404 
405         name = gtk_entry_get_text ( GTK_ENTRY ( entry_name ) );
406         new_liste_cptes = gtk_entry_get_text ( GTK_ENTRY ( entry_list ) );
407 
408         if ( strlen ( name ) && strlen (new_liste_cptes) &&
409          g_utf8_strchr  (new_liste_cptes, -1, ';' ) )
410         {
411             gsb_data_partial_balance_set_name ( partial_balance_number, name );
412             gsb_data_partial_balance_set_liste_cptes ( partial_balance_number, new_liste_cptes);
413             gsb_data_partial_balance_init_from_liste_cptes ( partial_balance_number,
414                         dialog );
415             gsb_data_partial_balance_set_colorise ( partial_balance_number,
416                         gtk_toggle_button_get_active (
417                         GTK_TOGGLE_BUTTON ( colorise_bouton ) ) );
418 
419             position = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON ( spin_bouton ) );
420             if ( position != partial_balance_number )
421             gsb_data_partial_balance_move ( partial_balance_number, position );
422 
423             /* on met à jour le model */
424             gsb_partial_balance_fill_model ( GTK_LIST_STORE ( model ) );
425 
426             /* MAJ HOME_PAGE */
427             gsb_gui_navigation_update_home_page ( );
428         }
429         else if (  g_utf8_strchr (new_liste_cptes, -1, ';' ) == NULL )
430         {
431             dialogue_warning_hint ( _("You must select at least two accounts."),
432                         _("Only one account is selected.") );
433             goto dialog_return;
434         }
435         else
436         {
437             dialogue_warning_hint ( _("The name of the partial balance "
438                         "and the list of accounts must be completed."),
439                         _("All fields are not filled in") );
440             goto dialog_return;
441         }
442     }
443     gtk_widget_destroy ( GTK_WIDGET ( dialog ) );
444 }
445 
446 
447 /**
448  * remove a partial_balance
449  *
450  * */
gsb_partial_balance_remove(GtkWidget * button,GtkWidget * main_widget)451 void gsb_partial_balance_remove ( GtkWidget *button, GtkWidget *main_widget )
452 {
453     GtkTreeView *treeview;
454     GtkTreeModel *model;
455     GtkTreeIter iter;
456     GSList *list_tmp;
457     gint partial_balance_number;
458 
459     treeview = g_object_get_data ( G_OBJECT (main_widget), "treeview" );
460     if ( !gtk_tree_selection_get_selected (
461                         gtk_tree_view_get_selection (treeview),
462                         &model,
463                         &iter ))
464         return;
465 
466     gtk_tree_model_get ( model, &iter, 4, &partial_balance_number, -1 );
467 
468     if ( partial_balance_number > 0 )
469     {
470         list_tmp = partial_balance_list;
471         while ( list_tmp )
472         {
473             struct_partial_balance *partial_balance;
474 
475             partial_balance = list_tmp -> data;
476 
477             if ( partial_balance -> partial_balance_number == partial_balance_number )
478             {
479                 GtkWidget *edit_button;
480 
481                 partial_balance_list = g_slist_remove (
482                         partial_balance_list, partial_balance );
483                 gsb_partial_balance_renumerote ( );
484                 gtk_list_store_remove  ( GTK_LIST_STORE ( model ), &iter );
485 
486                 /* MAJ HOME_PAGE */
487                 gsb_gui_navigation_update_home_page ( );
488 
489                 edit_button = g_object_get_data ( G_OBJECT (main_widget), "edit_button" );
490                 gtk_widget_set_sensitive ( edit_button, FALSE );
491                 gtk_widget_set_sensitive ( button, FALSE );
492                 break;
493             }
494             list_tmp = list_tmp -> next;
495         }
496     }
497 }
498 
499 
500 /**
501  * renumerote la liste des soldes partiels
502  *
503  * */
gsb_partial_balance_renumerote(void)504 void gsb_partial_balance_renumerote ( void )
505 {
506     GSList *list_tmp;
507     gint i = 1;
508 
509     list_tmp = partial_balance_list;
510     while ( list_tmp )
511     {
512         struct_partial_balance *partial_balance;
513 
514         partial_balance = list_tmp -> data;
515         partial_balance -> partial_balance_number = i;
516         i++;
517         list_tmp = list_tmp -> next;
518     }
519 }
520 
521 
522 /**
523  * Fonction appellée quand on sélectionne un solde partiel
524  *
525  * */
gsb_partial_balance_select_func(GtkTreeSelection * selection,GtkTreeModel * model,GtkTreePath * path,gboolean path_currently_selected,GObject * main_widget)526 gboolean gsb_partial_balance_select_func ( GtkTreeSelection *selection,
527                         GtkTreeModel *model,
528                         GtkTreePath *path,
529                         gboolean path_currently_selected,
530                         GObject *main_widget )
531 {
532     GtkWidget *button;
533 
534     button = g_object_get_data ( G_OBJECT (main_widget), "edit_button" );
535     gtk_widget_set_sensitive ( button, TRUE );
536     button = g_object_get_data ( G_OBJECT (main_widget), "remove_button" );
537     gtk_widget_set_sensitive ( button, TRUE );
538 
539     return TRUE;
540 }
541 
542 
543 /**
544  * sélectionne les comptes donnés en paramètre
545  *
546  * */
gsb_partial_balance_selectionne_cptes(GtkWidget * tree_view,const gchar * liste_cptes)547 void gsb_partial_balance_selectionne_cptes ( GtkWidget *tree_view,
548                         const gchar *liste_cptes )
549 {
550     GtkTreeModel *model;
551     GtkTreeIter iter;
552     GtkTreeSelection *selection;
553     gchar **tab;
554     gint i;
555     gint num_cpte;
556     gboolean valid;
557 
558     model = gtk_tree_view_get_model ( GTK_TREE_VIEW ( tree_view ) );
559     selection = gtk_tree_view_get_selection ( GTK_TREE_VIEW ( tree_view ) );
560 
561     if ( liste_cptes == NULL || strlen ( liste_cptes ) == 0 )
562         return;
563 
564     tab = g_strsplit ( liste_cptes, ";", 0 );
565     for ( i = 0; tab[i]; i++ )
566     {
567         num_cpte = utils_str_atoi ( tab[i] );
568 
569         valid = gtk_tree_model_get_iter_first ( model, &iter);
570         while ( valid )
571         {
572             gint   account_nb;
573 
574             gtk_tree_model_get ( model, &iter, 1, &account_nb, -1 );
575             if ( account_nb == num_cpte )
576                 gtk_tree_selection_select_iter ( selection, &iter);
577 
578             valid = gtk_tree_model_iter_next ( model, &iter );
579         }
580     }
581     g_strfreev ( tab );
582 }
583 
584 
585 /**
586  * gère le clavier sur la liste des soldes partiels
587  *
588 **/
gsb_partial_balance_key_press(GtkWidget * tree_view,GdkEventKey * ev)589 gboolean gsb_partial_balance_key_press ( GtkWidget *tree_view, GdkEventKey *ev )
590 {
591     switch ( ev -> keyval )
592     {
593     case GDK_KEY_Return :   /* entrée */
594     case GDK_KEY_KP_Enter :
595     case GDK_KEY_Tab :
596         g_object_set_data ( G_OBJECT ( tree_view ), "treeview", tree_view );
597         gsb_partial_balance_edit ( NULL, tree_view );
598         return TRUE;
599         break;
600     case GDK_KEY_Delete:    /*  del  */
601         g_object_set_data ( G_OBJECT ( tree_view ), "treeview", tree_view );
602         gsb_partial_balance_remove ( NULL, tree_view );
603         return TRUE;
604         break;
605     }
606 
607     return FALSE;
608 }
609 
610 
611 /**
612  * called when press a mouse button on the partial_balance_list
613  *
614  * \param tree_view
615  * \param ev a GdkEventButton
616  *
617  * \return TRUE if double - click else FALSE
618  * */
gsb_partial_balance_button_press(GtkWidget * tree_view,GdkEventButton * ev,gpointer null)619 gboolean gsb_partial_balance_button_press ( GtkWidget *tree_view,
620                         GdkEventButton *ev,
621                         gpointer null )
622 {
623     /*     if we are not in the list, go away */
624     if ( ev -> window != gtk_tree_view_get_bin_window ( GTK_TREE_VIEW ( tree_view ) ) )
625         return FALSE;
626 
627     /*  if double - click */
628     if ( ev -> type == GDK_2BUTTON_PRESS )
629     {
630         g_object_set_data ( G_OBJECT ( tree_view ), "treeview", tree_view );
631         gsb_partial_balance_edit ( NULL, tree_view );
632         return TRUE;
633     }
634 
635     return FALSE;
636 }
637 
638 
639 /**
640  *
641  *
642 **/
gsb_partial_balance_colorise_toggled(GtkCellRendererToggle * cell,gchar * path_str,GtkWidget * tree_view)643 void gsb_partial_balance_colorise_toggled ( GtkCellRendererToggle *cell,
644                         gchar *path_str,
645                         GtkWidget *tree_view )
646 {
647     GtkTreePath * treepath;
648     GtkTreeIter iter;
649     gboolean toggle;
650     gint partial_number;
651     GtkTreeModel *model;
652 
653     devel_debug (NULL);
654 
655     model = gtk_tree_view_get_model ( GTK_TREE_VIEW ( tree_view ) );
656 
657     /* invert the toggle */
658     treepath = gtk_tree_path_new_from_string ( path_str );
659     gtk_tree_model_get_iter ( GTK_TREE_MODEL (model), &iter, treepath );
660 
661     gtk_tree_model_get ( GTK_TREE_MODEL ( model ), &iter,
662                         4, &partial_number,
663                         5, &toggle,
664                         -1);
665     toggle ^= 1;
666     gtk_list_store_set (GTK_LIST_STORE ( model ), &iter, 5, toggle, -1);
667 
668      /* and save it */
669     gsb_data_partial_balance_set_colorise ( partial_number, toggle );
670 
671     /* MAJ HOME_PAGE */
672     gsb_gui_navigation_update_home_page ( );
673 }
674 
675 
676 /*********************************************************************************************/
677 /*              Données                                                                      */
678 /*********************************************************************************************/
679 /**
680  * set the partial_balance global variables to NULL,
681  * usually when we init all the global variables
682  *
683  * \param
684  *
685  * \return FALSE
686  * */
gsb_data_partial_balance_init_variables(void)687 gboolean gsb_data_partial_balance_init_variables ( void )
688 {
689     if ( partial_balance_list )
690     {
691         GSList* tmp_list = partial_balance_list;
692         while ( tmp_list )
693         {
694             struct_partial_balance *partial_balance;
695 
696             partial_balance = tmp_list -> data;
697             tmp_list = tmp_list -> next;
698             _gsb_data_partial_balance_free ( partial_balance );
699         }
700         g_slist_free ( partial_balance_list );
701     }
702     partial_balance_list = NULL;
703     partial_balance_buffer = NULL;
704 
705     return FALSE;
706 }
707 
708 
709 /**
710  * find and return the structure of the partial_balance asked
711  *
712  * \param partial_balance_number number of partial_balance
713  *
714  * \return the adr of the struct of the partial_balance (NULL if doesn't exit)
715  * */
gsb_data_partial_balance_get_structure(gint partial_balance_number)716 gpointer gsb_data_partial_balance_get_structure ( gint partial_balance_number )
717 {
718     GSList *tmp;
719 
720     if ( !partial_balance_number )
721         return NULL;
722 
723     /* before checking all the import rule, we check the buffer */
724     if ( partial_balance_buffer
725      &&
726      partial_balance_buffer -> partial_balance_number == partial_balance_number )
727         return partial_balance_buffer;
728 
729     tmp = partial_balance_list;
730 
731     while ( tmp )
732     {
733         struct_partial_balance *partial_balance;
734 
735         partial_balance = tmp -> data;
736 
737         if ( partial_balance -> partial_balance_number == partial_balance_number )
738         {
739             partial_balance_buffer = partial_balance;
740             return partial_balance;
741         }
742 
743         tmp = tmp -> next;
744     }
745     return NULL;
746 }
747 
748 
749 /**
750  * give the g_slist of partial_balance structure
751  * usefull when want to check all partial_balance
752  * carrefull : it's not a copy, so we must not free or change it
753  *
754  * \param none
755  *
756  * \return the g_slist of partial_balance structure
757  * */
gsb_data_partial_balance_get_list(void)758 GSList *gsb_data_partial_balance_get_list ( void )
759 {
760     return partial_balance_list;
761 }
762 
763 
764 /**
765  * return the number of the partial_balance given in param
766  *
767  * \param balance_ptr a pointer to the struct of the partial_balance
768  *
769  * \return the number of the partial_balance, 0 if problem
770  * */
gsb_data_partial_balance_get_number(gpointer balance_ptr)771 gint gsb_data_partial_balance_get_number ( gpointer balance_ptr )
772 {
773     struct_partial_balance *partial_balance;
774 
775     if ( !balance_ptr )
776         return 0;
777 
778     partial_balance = balance_ptr;
779     partial_balance_buffer = partial_balance;
780     return partial_balance -> partial_balance_number;
781 }
782 
783 
784 /**
785  * This internal function is called to free the memory used by an
786  * struct_partial_balance structure
787  */
_gsb_data_partial_balance_free(struct_partial_balance * partial_balance)788 static void _gsb_data_partial_balance_free ( struct_partial_balance *partial_balance)
789 {
790     if ( ! partial_balance )
791         return;
792     if ( partial_balance -> balance_name
793      &&
794      strlen ( partial_balance -> balance_name ) )
795         g_free ( partial_balance -> balance_name );
796     if ( partial_balance -> liste_cptes
797      &&
798      strlen ( partial_balance -> liste_cptes ) )
799         g_free ( partial_balance -> liste_cptes );
800 
801     g_free ( partial_balance );
802 
803     if ( partial_balance_buffer == partial_balance )
804         partial_balance_buffer = NULL;
805 }
806 
807 
808 /**
809  * return the liste_cptes of the partial_balance
810  *
811  * \param partial_balance_number the number of the partial_balance
812  *
813  * \return the liste_cptes of the partial_balance or NULL if fail
814  * */
gsb_data_partial_balance_get_liste_cptes(gint partial_balance_number)815 const gchar *gsb_data_partial_balance_get_liste_cptes ( gint partial_balance_number )
816 {
817     struct_partial_balance *partial_balance;
818 
819     partial_balance = gsb_data_partial_balance_get_structure ( partial_balance_number );
820 
821     if ( !partial_balance )
822         return NULL;
823 
824     return partial_balance -> liste_cptes;
825 }
826 
827 
828 /**
829  * set the liste_cptes of the partial_balance
830  * the value is dupplicate in memory
831  *
832  * \param partial_balance_number the number of the partial_balance
833  * \param liste_cptes of the partial_balance
834  *
835  * \return TRUE if ok or FALSE if problem
836  * */
gsb_data_partial_balance_set_liste_cptes(gint partial_balance_number,const gchar * liste_cptes)837 gboolean gsb_data_partial_balance_set_liste_cptes ( gint partial_balance_number,
838                         const gchar *liste_cptes )
839 {
840     struct_partial_balance *partial_balance;
841 
842     partial_balance = gsb_data_partial_balance_get_structure ( partial_balance_number );
843 
844     if ( !partial_balance )
845         return FALSE;
846 
847     /* we free the last name */
848     if ( partial_balance -> liste_cptes )
849         g_free (partial_balance -> liste_cptes);
850 
851     /* and copy the new one */
852     partial_balance -> liste_cptes = my_strdup ( liste_cptes );
853 
854     return TRUE;
855 }
856 
857 
858 /**
859  * return the name of the partial_balance
860  *
861  * \param partial_balance_number the number of the partial_balance
862  *
863  * \return the name of the partial_balance or NULL if fail
864  * */
gsb_data_partial_balance_get_name(gint partial_balance_number)865 const gchar *gsb_data_partial_balance_get_name ( gint partial_balance_number )
866 {
867     struct_partial_balance *partial_balance;
868 
869     partial_balance = gsb_data_partial_balance_get_structure ( partial_balance_number );
870 
871     if ( !partial_balance )
872         return NULL;
873 
874     return partial_balance -> balance_name;
875 }
876 
877 
878 /**
879  * set the name of the import_rule
880  * the value is dupplicate in memory
881  *
882  * \param import_rule_number the number of the import_rule
883  * \param name the name of the import_rule
884  *
885  * \return TRUE if ok or FALSE if problem
886  * */
gsb_data_partial_balance_set_name(gint partial_balance_number,const gchar * name)887 gboolean gsb_data_partial_balance_set_name ( gint partial_balance_number,
888                         const gchar *name )
889 {
890     struct_partial_balance *partial_balance;
891 
892     partial_balance = gsb_data_partial_balance_get_structure ( partial_balance_number );
893 
894     if ( !partial_balance )
895         return FALSE;
896 
897     /* we free the last name */
898     if ( partial_balance -> balance_name )
899         g_free (partial_balance -> balance_name);
900 
901     /* and copy the new one */
902     partial_balance -> balance_name = my_strdup ( name );
903 
904     return TRUE;
905 }
906 
907 
908 /**
909  * get the kind of the partial_balance
910  * \param partial_balance_number no of the partial_balance
911  *
912  * \return partial_balance type or 0 if the partial_balance doesn't exist
913  * */
gsb_data_partial_balance_get_kind(gint partial_balance_number)914 KindAccount gsb_data_partial_balance_get_kind ( gint partial_balance_number )
915 {
916     struct_partial_balance *partial_balance;
917 
918     partial_balance = gsb_data_partial_balance_get_structure ( partial_balance_number );
919 
920     if ( !partial_balance )
921         return 0;
922 
923     return partial_balance -> kind;
924 }
925 
926 
927 /**
928  * set the kind of the partial_balance
929  * \param partial_balance_number no of the partial_balance
930  * \param kind type to set
931  *
932  * \return TRUE, ok ; FALSE, problem
933  * */
gsb_data_partial_balance_set_kind(gint partial_balance_number,KindAccount kind)934 gboolean gsb_data_partial_balance_set_kind ( gint partial_balance_number,
935                         KindAccount kind )
936 {
937     struct_partial_balance *partial_balance;
938 
939     partial_balance = gsb_data_partial_balance_get_structure ( partial_balance_number );
940 
941     if ( !partial_balance )
942         return FALSE;
943 
944     partial_balance -> kind = kind;
945 
946     return TRUE;
947 }
948 
949 
950 /**
951  * get the currency of the partial_balance
952  *
953  * \param partial_balance_number no of the partial_balance
954  *
955  * \return partial_balance currency or 0 if the partial_balance doesn't exist
956  * */
gsb_data_partial_balance_get_currency(gint partial_balance_number)957 gint gsb_data_partial_balance_get_currency ( gint partial_balance_number )
958 {
959     struct_partial_balance *partial_balance;
960 
961     partial_balance = gsb_data_partial_balance_get_structure ( partial_balance_number );
962 
963     if ( !partial_balance )
964         return 0;
965 
966     return partial_balance -> currency;
967 }
968 
969 
970 /**
971  * set the currency of the partial_balance
972  *
973  * \param partial_balance_number no of the partial_balance
974  * \param currency to set
975  *
976  * \return TRUE, ok ; FALSE, problem
977  * */
gsb_data_partial_balance_set_currency(gint partial_balance_number,gint currency)978 gboolean gsb_data_partial_balance_set_currency ( gint partial_balance_number,
979                         gint currency )
980 {
981     struct_partial_balance *partial_balance;
982 
983     partial_balance = gsb_data_partial_balance_get_structure ( partial_balance_number );
984 
985     if ( !partial_balance )
986         return FALSE;
987 
988     partial_balance -> currency = currency;
989 
990     return TRUE;
991 }
992 
993 
994 /**
995  *
996  *
997  * \param partial_balance_number no of the partial_balance
998  *
999  * \return
1000  * */
gsb_data_partial_balance_get_colorise(gint partial_balance_number)1001 gboolean gsb_data_partial_balance_get_colorise ( gint partial_balance_number )
1002 {
1003     struct_partial_balance *partial_balance;
1004 
1005     partial_balance = gsb_data_partial_balance_get_structure ( partial_balance_number );
1006 
1007     if ( !partial_balance )
1008         return 0;
1009 
1010     return partial_balance -> colorise;
1011 }
1012 
1013 
1014 /**
1015  * \param partial_balance_number no of the partial_balance
1016  * \param colorise
1017  *
1018  * \return TRUE, ok ; FALSE, problem
1019  * */
gsb_data_partial_balance_set_colorise(gint partial_balance_number,gboolean colorise)1020 gboolean gsb_data_partial_balance_set_colorise ( gint partial_balance_number,
1021                         gboolean colorise )
1022 {
1023     struct_partial_balance *partial_balance;
1024 
1025     partial_balance = gsb_data_partial_balance_get_structure ( partial_balance_number );
1026 
1027     if ( !partial_balance )
1028         return FALSE;
1029 
1030     partial_balance -> colorise = colorise;
1031 
1032     return TRUE;
1033 }
1034 
1035 
1036 /**
1037  *
1038  *
1039  * */
gsb_data_partial_balance_get_marked_balance(gint partial_balance_number)1040 gchar *gsb_data_partial_balance_get_marked_balance ( gint partial_balance_number )
1041 {
1042     struct_partial_balance *partial_balance;
1043     GsbReal solde = null_real;
1044     gchar **tab;
1045     gchar *string;
1046     gint i;
1047 
1048     partial_balance = gsb_data_partial_balance_get_structure ( partial_balance_number );
1049 
1050     if ( !partial_balance )
1051         return NULL;
1052 
1053     if ( partial_balance -> liste_cptes == NULL ||
1054      strlen ( partial_balance -> liste_cptes ) == 0 )
1055         return NULL;
1056 
1057     tab = g_strsplit ( partial_balance -> liste_cptes, ";", 0 );
1058     for ( i = 0; tab[i]; i++ )
1059     {
1060         GsbReal tmp_real;
1061         gint account_nb;
1062         gint account_currency;
1063         gint link_number;
1064 
1065         account_nb = utils_str_atoi ( tab[i] );
1066         account_currency = gsb_data_account_get_currency ( account_nb );
1067         tmp_real = gsb_data_account_get_marked_balance ( account_nb );
1068 
1069         if ( tmp_real.mantissa != 0 && partial_balance -> currency != account_currency )
1070         {
1071             if ( ( link_number = gsb_data_currency_link_search ( account_currency,
1072                         partial_balance -> currency ) ) )
1073             {
1074                 if ( gsb_data_currency_link_get_first_currency (
1075                  link_number) == account_currency )
1076                     tmp_real = gsb_real_mul ( tmp_real,
1077                                 gsb_data_currency_link_get_change_rate ( link_number ) );
1078                 else
1079                     tmp_real = gsb_real_div ( tmp_real,
1080                                 gsb_data_currency_link_get_change_rate ( link_number ) );
1081             }
1082 
1083         }
1084         solde = gsb_real_add ( solde, tmp_real );
1085     }
1086     g_strfreev ( tab );
1087 
1088     if (partial_balance->colorise)
1089     {
1090         gchar *color;
1091 		gchar *tmp_str;
1092 
1093         if (solde.mantissa < 0)
1094             color = g_strdup ("red");
1095         else
1096 		{
1097 			gchar *str_to_free;
1098 
1099 			str_to_free = gsb_rgba_get_couleur_to_hexa_string ("text_gsetting_option_normal");
1100             color = g_strdup (str_to_free);
1101 			g_free (str_to_free);
1102 		}
1103 
1104 		tmp_str = utils_real_get_string_with_currency (solde, partial_balance -> currency, TRUE);
1105         string = g_strdup_printf ("<span color=\"%s\">%s</span>", color, tmp_str);
1106         g_free ( color );
1107 		g_free (tmp_str);
1108     }
1109     else
1110         string = utils_real_get_string_with_currency (
1111                         solde, partial_balance -> currency, TRUE );
1112 
1113     return string;
1114 }
1115 
1116 
1117 /**
1118  *
1119  *
1120  * */
gsb_data_partial_balance_get_current_amount(gint partial_balance_number)1121 GsbReal gsb_data_partial_balance_get_current_amount ( gint partial_balance_number )
1122 {
1123     struct_partial_balance *partial_balance;
1124     GsbReal solde = null_real;
1125     gchar **tab;
1126     gint i;
1127 
1128     partial_balance = gsb_data_partial_balance_get_structure ( partial_balance_number );
1129 
1130     if ( !partial_balance )
1131         return null_real;
1132 
1133     if ( partial_balance -> liste_cptes == NULL ||
1134      strlen ( partial_balance -> liste_cptes ) == 0 )
1135         return null_real;
1136 
1137     tab = g_strsplit ( partial_balance -> liste_cptes, ";", 0 );
1138     for ( i = 0; tab[i]; i++ )
1139     {
1140         GsbReal tmp_real;
1141         gint account_nb;
1142         gint account_currency;
1143         gint link_number;
1144 
1145         account_nb = utils_str_atoi ( tab[i] );
1146         account_currency = gsb_data_account_get_currency ( account_nb );
1147         tmp_real = gsb_data_account_get_current_balance ( account_nb );
1148 
1149         if ( tmp_real.mantissa != 0 && partial_balance -> currency != account_currency )
1150         {
1151             if ( ( link_number = gsb_data_currency_link_search ( account_currency,
1152                         partial_balance -> currency ) ) )
1153             {
1154                 if ( gsb_data_currency_link_get_first_currency (
1155                         link_number) == account_currency )
1156                     tmp_real = gsb_real_mul ( tmp_real,
1157                                 gsb_data_currency_link_get_change_rate ( link_number ) );
1158                 else
1159                     tmp_real = gsb_real_div ( tmp_real,
1160                                 gsb_data_currency_link_get_change_rate ( link_number ) );
1161             }
1162         }
1163         solde = gsb_real_add ( solde, tmp_real );
1164     }
1165     g_strfreev ( tab );
1166 
1167     return solde;
1168 }
1169 
1170 
1171 /**
1172  *
1173  *
1174  * */
gsb_data_partial_balance_get_current_balance(gint partial_balance_number)1175 gchar *gsb_data_partial_balance_get_current_balance ( gint partial_balance_number )
1176 {
1177     struct_partial_balance *partial_balance;
1178     GsbReal solde = null_real;
1179     gchar *string;
1180 
1181     partial_balance = gsb_data_partial_balance_get_structure ( partial_balance_number );
1182 
1183     if ( !partial_balance )
1184         return NULL;
1185 
1186     solde = gsb_data_partial_balance_get_current_amount ( partial_balance_number );
1187 
1188     if (partial_balance->colorise)
1189     {
1190         gchar *color;
1191 		gchar *tmp_str;
1192 
1193         if (solde.mantissa < 0)
1194             color = g_strdup ("red");
1195         else
1196 		{
1197 			gchar *str_to_free;
1198 
1199 			str_to_free = gsb_rgba_get_couleur_to_hexa_string ("text_gsetting_option_normal");
1200             color = g_strdup (str_to_free);
1201 			g_free (str_to_free);
1202 		}
1203 
1204 		tmp_str = utils_real_get_string_with_currency (solde, partial_balance -> currency, TRUE);
1205         string = g_strdup_printf ( "<span color=\"%s\">%s</span>", color, tmp_str);
1206         g_free ( color );
1207 		g_free (tmp_str);
1208     }
1209     else
1210         string = utils_real_get_string_with_currency (
1211                         solde, partial_balance -> currency, TRUE );
1212 
1213     return string;
1214 }
1215 
1216 
1217 /**
1218  *
1219  *
1220  * */
gsb_partial_balance_select_account(GtkTreeSelection * selection,GtkTreeModel * model,GtkTreePath * path,gboolean path_currently_selected,GObject * entry)1221 gboolean gsb_partial_balance_select_account ( GtkTreeSelection *selection,
1222                         GtkTreeModel *model,
1223                         GtkTreePath *path,
1224                         gboolean path_currently_selected,
1225                         GObject *entry )
1226 {
1227     GList *list;
1228 
1229     if ( strlen ( gtk_entry_get_text ( GTK_ENTRY ( entry ) ) ) > 0 )
1230         gtk_entry_set_text ( GTK_ENTRY ( entry ), "" );
1231 
1232     list = gtk_tree_selection_get_selected_rows ( selection, NULL );
1233 
1234     if ( path_currently_selected )
1235     {
1236         while ( list )
1237         {
1238             GtkTreeIter iter;
1239             gint account_nb;
1240 
1241             if ( gtk_tree_path_compare ( path, list -> data ) != 0 )
1242             {
1243                 gtk_tree_model_get_iter ( model, &iter, list -> data );
1244                 gtk_tree_model_get ( model, &iter, 1, &account_nb, -1);
1245                 if ( strlen ( gtk_entry_get_text ( GTK_ENTRY ( entry ) ) ) > 0 )
1246                 {
1247                     gtk_entry_set_text ( GTK_ENTRY ( entry ),
1248                                 g_strconcat ( gtk_entry_get_text ( GTK_ENTRY ( entry ) ), ";",
1249                                 g_strdup_printf ( "%d", account_nb ), NULL ) );
1250                 }
1251                 else
1252                     gtk_entry_set_text ( GTK_ENTRY ( entry ), g_strdup_printf ( "%d", account_nb ) );
1253             }
1254             list = list -> next;
1255         }
1256     }
1257     else
1258     {
1259         list = g_list_append ( list, gtk_tree_path_copy ( path ) );
1260 
1261         while ( list )
1262         {
1263             GtkTreeIter iter;
1264             gint account_nb;
1265 
1266             gtk_tree_model_get_iter ( model, &iter, list -> data );
1267             gtk_tree_model_get ( model, &iter, 1, &account_nb, -1);
1268             if ( strlen ( gtk_entry_get_text ( GTK_ENTRY ( entry ) ) ) > 0 )
1269             {
1270                 gtk_entry_set_text ( GTK_ENTRY ( entry ),
1271                             g_strconcat ( gtk_entry_get_text ( GTK_ENTRY ( entry ) ), ";",
1272                             g_strdup_printf ( "%d", account_nb ), NULL ) );
1273             }
1274             else
1275                 gtk_entry_set_text ( GTK_ENTRY ( entry ), g_strdup_printf ( "%d", account_nb ) );
1276 
1277             list = list -> next;
1278         }
1279     }
1280 
1281     g_list_free_full ( list, ( GDestroyNotify ) gtk_tree_path_free );
1282 
1283     return TRUE;
1284 }
1285 
1286 
1287 /**
1288  * Détermine la devise et le type de compte pour le solde partiel
1289  *
1290  * \param partial_balance_number
1291  *
1292  * \return FALSE si tous les comptes n'ont pas les mêmes données sinon TRUE
1293  * */
gsb_data_partial_balance_init_from_liste_cptes(gint partial_balance_number,GtkWidget * parent)1294 gboolean gsb_data_partial_balance_init_from_liste_cptes ( gint partial_balance_number,
1295                         GtkWidget *parent )
1296 {
1297     struct_partial_balance *partial_balance;
1298     gchar **tab;
1299     gint i;
1300     gint account_nb;
1301     gint currency_nb = 0;
1302     KindAccount kind = -1;
1303     KindAccount kind_nb = -1;
1304     gchar *tmp_str;
1305     gboolean return_val = TRUE;
1306     gboolean currency_mixte = FALSE;
1307 
1308     partial_balance = gsb_data_partial_balance_get_structure ( partial_balance_number );
1309 
1310     if ( !partial_balance )
1311         return FALSE;
1312 
1313     if ( partial_balance -> liste_cptes == NULL ||
1314      strlen ( partial_balance -> liste_cptes ) == 0 )
1315         return FALSE;
1316 
1317     tab = g_strsplit ( partial_balance -> liste_cptes, ";", 0 );
1318     for ( i = 0; tab[i]; i++ )
1319     {
1320         gint account_currency;
1321 
1322         account_nb = utils_str_atoi ( tab[i] );
1323         account_currency = gsb_data_account_get_currency ( account_nb );
1324         if ( currency_nb == 0 )
1325             currency_nb = account_currency;
1326         else if ( currency_nb != account_currency )
1327         {
1328             if ( currency_mixte == FALSE )
1329             {
1330                 no_devise_solde_partiels = gsb_partial_balance_request_currency ( parent );
1331                 if ( gsb_data_currency_link_search ( currency_nb, account_currency ) == 0 )
1332                 {
1333                     tmp_str = g_strdup_printf (
1334                             _("You need to create a link between currency %s and %s."),
1335                             gsb_data_currency_get_name ( currency_nb ),
1336                             gsb_data_currency_get_name ( account_currency ) );
1337                     dialogue_warning_hint ( tmp_str,
1338                             _("Attention missing link between currencies") );
1339                 }
1340             }
1341             currency_mixte = TRUE;
1342             return_val = FALSE;
1343         }
1344         if ( kind == -1 )
1345             kind = gsb_data_account_get_kind ( account_nb );
1346         else if ( ( kind_nb = gsb_data_account_get_kind ( account_nb ) ) != kind )
1347         {
1348             switch ( kind )
1349             {
1350             case GSB_TYPE_BALANCE:
1351                 break;
1352 
1353             case GSB_TYPE_BANK:
1354             case GSB_TYPE_CASH:
1355                 if ( kind_nb >= GSB_TYPE_LIABILITIES )
1356                     return_val = FALSE;
1357                 break;
1358 
1359             case GSB_TYPE_LIABILITIES:
1360                 return_val = FALSE;
1361                 break;
1362 
1363             case GSB_TYPE_ASSET:
1364                 return_val = FALSE;
1365                 break;
1366             }
1367         }
1368     }
1369     g_strfreev ( tab );
1370 
1371     if ( currency_mixte )
1372         gsb_data_partial_balance_set_currency ( partial_balance_number,
1373                         no_devise_solde_partiels );
1374     else
1375         gsb_data_partial_balance_set_currency ( partial_balance_number, currency_nb );
1376     if ( return_val == FALSE )
1377         gsb_data_partial_balance_set_kind ( partial_balance_number, -1 );
1378     else
1379         gsb_data_partial_balance_set_kind ( partial_balance_number, kind );
1380 
1381     return return_val;
1382 }
1383 
1384 
1385 /**
1386  * callback when tree_view Home tab receive a drag and drop signal
1387  *
1388  * \param drag_dest
1389  * \param dest_path
1390  * \param selection_data
1391  *
1392  * \return FALSE
1393  */
gsb_data_partial_balance_drag_data_received(GtkTreeDragDest * drag_dest,GtkTreePath * dest_path,GtkSelectionData * selection_data)1394 gboolean gsb_data_partial_balance_drag_data_received ( GtkTreeDragDest * drag_dest,
1395                         GtkTreePath * dest_path,
1396                         GtkSelectionData * selection_data )
1397 {
1398     gchar *tmpstr = gtk_tree_path_to_string ( dest_path );
1399     gchar *tmpstr2 = g_strdup_printf ( "Dest path : %s", tmpstr);
1400     devel_debug (tmpstr2);
1401     g_free (tmpstr);
1402     g_free (tmpstr2);
1403 
1404     if ( dest_path && selection_data )
1405     {
1406         GtkTreeModel * model;
1407         GtkTreeIter iter;
1408         GtkTreePath * orig_path;
1409         gint orig_partial_number = 0;
1410         gint dest_pos;
1411 
1412         /* On récupère le model et le path d'origine */
1413         gtk_tree_get_row_drag_data (selection_data, &model, &orig_path);
1414 
1415         if ( gtk_tree_model_get_iter ( model, &iter, orig_path ) )
1416             gtk_tree_model_get ( model, &iter, 4, &orig_partial_number, -1 );
1417 
1418         dest_pos = utils_str_atoi ( gtk_tree_path_to_string ( dest_path ) );
1419 
1420         gsb_data_partial_balance_move ( orig_partial_number, dest_pos );
1421         gsb_partial_balance_fill_model ( GTK_LIST_STORE ( model ) );
1422 
1423         /* MAJ HOME_PAGE */
1424             gsb_gui_navigation_update_home_page ( );
1425     }
1426     return FALSE;
1427 }
1428 
1429 
1430 /**
1431  * Fill the drag & drop structure with the path of selected column.
1432  * This is an interface function called from GTK, much like a callback.
1433  *
1434  * \param drag_source		Not used.
1435  * \param path			Original path for the gtk selection.
1436  * \param selection_data	A pointer to the drag & drop structure.
1437  *
1438  * \return FALSE, to allow future processing by the callback chain.
1439  */
gsb_data_partial_balance_drag_data_get(GtkTreeDragSource * drag_source,GtkTreePath * path,GtkSelectionData * selection_data)1440 gboolean gsb_data_partial_balance_drag_data_get ( GtkTreeDragSource * drag_source,
1441                         GtkTreePath * path,
1442                         GtkSelectionData * selection_data )
1443 {
1444     if ( path )
1445         gtk_tree_set_row_drag_data ( selection_data, GTK_TREE_MODEL( model_accueil ), path );
1446 
1447     return FALSE;
1448 }
1449 
1450 
1451 /**
1452  * change the position of partial_balance in the list
1453  *
1454  * \param account_number	the partial_balance we want to move
1455  * \param dest_account_number	partial_balance before we want to move
1456  *
1457  * \return FALSE
1458  * */
gsb_data_partial_balance_move(gint orig_partial_number,gint dest_pos)1459 gboolean gsb_data_partial_balance_move ( gint orig_partial_number, gint dest_pos )
1460 {
1461     struct_partial_balance *partial_balance;
1462 
1463     partial_balance = gsb_data_partial_balance_get_structure ( orig_partial_number );
1464 
1465     if ( !orig_partial_number )
1466         return FALSE;
1467 
1468     partial_balance_list = g_slist_remove ( partial_balance_list, partial_balance );
1469     partial_balance_list = g_slist_insert ( partial_balance_list, partial_balance, dest_pos );
1470     gsb_partial_balance_renumerote ( );
1471 
1472     return FALSE;
1473 }
1474 
1475 
1476 /**
1477  * calcule le solde des comptes d'un solde partiel à une date donnée
1478  *
1479  * \param account_number    numéro du compte concerné
1480  * \param date              date de calcul du solde
1481  *
1482  * \return GPtrArray        un tableau avec le solde de chaque compte concerné
1483  * */
gsb_data_partial_balance_calculate_balances_at_date(gint partial_balance_number,GDate * date)1484 GPtrArray *gsb_data_partial_balance_calculate_balances_at_date ( gint partial_balance_number,
1485                         GDate *date )
1486 {
1487     GSList *tmp_list;
1488     GPtrArray *current_balances;
1489     GPtrArray *current_balances_later;
1490     GArray *floating_points;
1491     GArray *account_numbers;
1492     gchar **tab;
1493     gint i;
1494     gint nbre_comptes;
1495     struct_partial_balance *partial_balance;
1496 
1497     partial_balance = gsb_data_partial_balance_get_structure ( partial_balance_number );
1498 
1499     if ( !partial_balance )
1500         return NULL;
1501 
1502     tab = g_strsplit ( partial_balance->liste_cptes, ";", 0 );
1503 
1504     nbre_comptes = g_strv_length ( tab );
1505     if ( nbre_comptes )
1506     {
1507         floating_points = g_array_new ( FALSE, TRUE, sizeof ( gint ) );
1508         account_numbers = g_array_new ( FALSE, TRUE, sizeof ( gint ) );
1509         current_balances = g_ptr_array_new ();
1510         current_balances_later = g_ptr_array_new ();
1511     }
1512     else
1513         return NULL;
1514 
1515     for ( i = 0; tab[i]; i++ )
1516     {
1517         gint account_number;
1518         gint floating_point;
1519         GsbReal *balance;
1520         GsbReal tmp_balance;
1521 
1522         /* on remplit le tableau des numeros des comptes */
1523         account_number = utils_str_atoi ( tab[i] );
1524         g_array_append_val ( account_numbers, account_number );
1525 
1526         /* on remplit le tableau des données des devises */
1527         floating_point = gsb_data_account_get_currency_floating_point ( account_number );
1528         g_array_append_val ( floating_points, floating_point );
1529 
1530         /* on initialise le tableau des soldes de chaque compte */
1531         balance = g_malloc0 ( sizeof ( GsbReal ) );
1532         tmp_balance = gsb_data_account_get_init_balance ( account_number, floating_point );
1533         balance->mantissa = tmp_balance.mantissa;
1534         balance->exponent = tmp_balance.exponent;
1535         g_ptr_array_add ( current_balances, balance );
1536 
1537         /* on initialise le tableau des soldes en erreur de chaque compte */
1538         balance = g_malloc0 ( sizeof ( GsbReal ) );
1539         balance->mantissa = null_real.mantissa;
1540         balance->exponent = null_real.exponent;
1541         g_ptr_array_add ( current_balances_later, balance );
1542     }
1543 
1544     tmp_list = gsb_data_transaction_get_complete_transactions_list ();
1545 
1546     while (tmp_list)
1547     {
1548         gint transaction_number;
1549 
1550         transaction_number = gsb_data_transaction_get_transaction_number ( tmp_list->data );
1551 
1552         for ( i = 0; i < nbre_comptes; i++ )
1553         {
1554             gint account_number;
1555             gboolean trouve = FALSE;
1556 
1557             account_number = g_array_index ( account_numbers, gint, i );
1558 
1559             if ( gsb_data_transaction_get_account_number ( transaction_number ) == account_number )
1560             {
1561                 if ( g_date_compare ( gsb_data_transaction_get_value_date_or_date ( transaction_number ),
1562                  date ) <= 0 )
1563                 {
1564                     if ( gsb_data_transaction_get_mother_transaction_number ( transaction_number ) == 0 )
1565                     {
1566                         trouve = TRUE;
1567                     }
1568                 }
1569             }
1570             if ( trouve )
1571             {
1572                 gint floating_point;
1573                 GsbReal adjusted_amout;
1574                 GsbReal tmp_balance;
1575                 GsbReal *balance;
1576                 GsbReal current_balance;
1577 
1578                 floating_point = g_array_index ( floating_points, gint, i );
1579                 balance = (GsbReal *) g_ptr_array_index ( current_balances, i );
1580                 current_balance.mantissa = balance->mantissa;
1581                 current_balance.exponent = balance->exponent;
1582 
1583                 adjusted_amout = gsb_data_transaction_get_adjusted_amount ( transaction_number, floating_point );
1584                 tmp_balance = gsb_real_add ( current_balance, adjusted_amout );
1585 
1586                 if ( tmp_balance.mantissa != error_real.mantissa )
1587                 {
1588                     balance->mantissa = tmp_balance.mantissa;
1589                     balance->exponent = tmp_balance.exponent;
1590                 }
1591                 else
1592                 {
1593                     GsbReal *balance_later;
1594 
1595                     balance_later = (GsbReal *) g_ptr_array_index ( current_balances_later, i );
1596                     balance_later->mantissa = G_MININT64;
1597                     balance_later->exponent = 0;
1598                 }
1599             }
1600         }
1601         tmp_list = tmp_list->next;
1602     }
1603     g_array_free ( account_numbers, TRUE );
1604     g_array_free ( floating_points, TRUE );
1605 
1606     return current_balances;
1607 }
1608 
1609 /**
1610  *
1611  *
1612  * \param
1613  *
1614  * \return
1615  **/
gsb_data_partial_balance_renum_account_number_0(gint account_number)1616 void gsb_data_partial_balance_renum_account_number_0 (gint account_number)
1617 {
1618     GSList *tmp_list;
1619 
1620     tmp_list = partial_balance_list;
1621     while (tmp_list)
1622     {
1623 		gchar *liste_cptes;
1624         struct_partial_balance *partial_balance;
1625 
1626         partial_balance = tmp_list->data;
1627 		liste_cptes = partial_balance->liste_cptes;
1628 		printf ("liste_cptes = %s\n", liste_cptes);
1629 		if (g_strstr_len (liste_cptes, -1, "0"))
1630 		{
1631 			gchar **tab;
1632 			gint i;
1633 
1634 			tab = g_strsplit (liste_cptes, ";", 0);
1635 			for ( i = 0; tab[i]; i++ )
1636 			{
1637 				gint account_nb;
1638 
1639 				account_nb = utils_str_atoi (tab[i]);
1640 				if (account_nb == 0)
1641 				{
1642 					gchar *tmp_number_str;
1643 					gchar *str_to_free;
1644 					gchar *new_str;
1645 
1646 					tmp_number_str = utils_str_itoa (account_number);
1647 					str_to_free = tab[i];
1648 					tab[i] = tmp_number_str;
1649 					g_free (str_to_free);
1650 					new_str = g_strjoinv (";", tab);
1651 					g_free (partial_balance->liste_cptes);
1652 					partial_balance->liste_cptes = new_str;
1653 					g_strfreev (tab);
1654 					break;
1655 				}
1656 			}
1657 		}
1658 		tmp_list = tmp_list->next;
1659 	}
1660 }
1661 
1662 /*********************************************************************************************/
1663 /*              Interface                                                                    */
1664 /*********************************************************************************************/
1665 /**
1666  *
1667  *
1668  * */
gsb_partial_balance_create_list_accounts(GtkWidget * entry)1669 GtkWidget *gsb_partial_balance_create_list_accounts ( GtkWidget *entry )
1670 {
1671     GtkWidget *vbox;
1672     GtkWidget *sw;
1673     GtkWidget *treeview;
1674     GtkListStore *list_store;
1675     GtkTreeIter iter;
1676     GSList *list_tmp;
1677     GtkTreeViewColumn *column;
1678     GtkCellRenderer *cell;
1679     GtkTreeSelection *selection;
1680     gint i = 0;
1681 
1682     vbox = gtk_box_new ( GTK_ORIENTATION_VERTICAL, MARGIN_BOX );
1683     sw = gtk_scrolled_window_new (NULL, NULL);
1684     gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
1685                         GTK_SHADOW_ETCHED_IN);
1686     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
1687                         GTK_POLICY_NEVER,
1688                         GTK_POLICY_ALWAYS);
1689     gtk_widget_set_size_request( sw, 600, 200 );
1690 
1691     /* create the model */
1692     list_store = gtk_list_store_new ( 2, G_TYPE_STRING, G_TYPE_INT );
1693     list_tmp = gsb_data_account_get_list_accounts ();
1694 
1695     while ( list_tmp )
1696     {
1697         gint account_number;
1698 
1699         account_number = gsb_data_account_get_no_account ( list_tmp -> data );
1700 
1701         if ( !gsb_data_account_get_closed_account ( account_number ) )
1702         {
1703             gtk_list_store_append ( GTK_LIST_STORE ( list_store), &iter );
1704             gtk_list_store_set ( GTK_LIST_STORE ( list_store ), &iter,
1705                             0,  gsb_data_account_get_name ( account_number ),
1706                             1, account_number,
1707                             -1 );
1708             i++;
1709         }
1710         list_tmp = list_tmp -> next;
1711     }
1712     if ( i > 10 )
1713         i = 10;
1714     /* create the treeview */
1715     treeview = gtk_tree_view_new_with_model ( GTK_TREE_MODEL ( list_store ) );
1716  	gtk_widget_set_name (treeview, "tree_view");
1717     g_object_unref ( list_store );
1718 
1719     gtk_widget_set_size_request ( treeview, -1, i*20 );
1720     selection = gtk_tree_view_get_selection ( GTK_TREE_VIEW ( treeview ) );
1721     gtk_tree_selection_set_mode ( selection, GTK_SELECTION_MULTIPLE );
1722     gtk_tree_selection_set_select_function ( selection,
1723                         (GtkTreeSelectionFunc) gsb_partial_balance_select_account,
1724                         entry, NULL );
1725     gtk_container_add ( GTK_CONTAINER ( sw ), treeview );
1726     gtk_box_pack_start ( GTK_BOX ( vbox ), sw, FALSE, FALSE, 0 );
1727     g_object_set_data ( G_OBJECT (vbox), "account_treeview", treeview );
1728 
1729      /* account name */
1730     cell = gtk_cell_renderer_text_new ( );
1731     column = gtk_tree_view_column_new_with_attributes ( _("Account name"),
1732                         cell,
1733                         "text",
1734                         0,
1735                         NULL );
1736     gtk_tree_view_column_set_expand ( column, TRUE );
1737     gtk_tree_view_column_set_sort_column_id ( column, 0 );
1738     gtk_tree_view_append_column ( GTK_TREE_VIEW ( treeview ), column );
1739 
1740      /* account number */
1741     cell = gtk_cell_renderer_text_new ();
1742     column = gtk_tree_view_column_new_with_attributes (_("Account number"), cell, "text", 1, NULL);
1743     gtk_tree_view_column_set_expand (column, TRUE);
1744     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview ), column);
1745 
1746     return vbox;
1747 }
1748 
1749 
1750 /**
1751  *
1752  *
1753  * */
gsb_partial_balance_create_dialog(gint action,gint spin_value)1754 GtkWidget *gsb_partial_balance_create_dialog ( gint action, gint spin_value )
1755 {
1756     GtkWidget *dialog, *label, *main_vbox;
1757     GtkWidget *paddinggrid;
1758     GtkWidget *entry_name, *entry_list, *account_list, *bouton;
1759 
1760     devel_debug ( NULL);
1761 
1762     if ( action == 1 )
1763         dialog = gtk_dialog_new_with_buttons ( _("Add a partial balance"),
1764                             GTK_WINDOW ( grisbi_app_get_active_window (NULL) ),
1765                             GTK_DIALOG_MODAL,
1766                             "gtk-cancel", 0,
1767                             "gtk-ok", 1,
1768                             NULL );
1769     else
1770         dialog = gtk_dialog_new_with_buttons ( _("Modify a partial balance"),
1771                             GTK_WINDOW ( grisbi_app_get_active_window (NULL) ),
1772                             GTK_DIALOG_MODAL,
1773                             "gtk-cancel", 0,
1774                             "gtk-ok", 1,
1775                             NULL );
1776     gtk_window_set_position ( GTK_WINDOW ( dialog ), GTK_WIN_POS_CENTER_ON_PARENT );
1777 
1778     main_vbox = new_vbox_with_title_and_icon ( _("Partial balance details"), "gsb-payment-32.png" );
1779     gtk_box_pack_start ( GTK_BOX ( dialog_get_content_area ( dialog ) ), main_vbox, TRUE, TRUE, 0 );
1780 
1781     /* Create paddinggrid */
1782     paddinggrid = utils_prefs_paddinggrid_new_with_title ( main_vbox, _("Details") );
1783     gtk_grid_set_column_spacing (GTK_GRID (paddinggrid), 5);
1784     gtk_grid_set_row_spacing (GTK_GRID (paddinggrid), 5);
1785 
1786     /* Partial balance name */
1787     label = gtk_label_new ( _("Name: ") );
1788     utils_labels_set_alignment ( GTK_LABEL ( label ), 0, 1 );
1789     gtk_label_set_justify ( GTK_LABEL ( label ), GTK_JUSTIFY_RIGHT );
1790     gtk_grid_attach (GTK_GRID (paddinggrid), label, 0, 0, 1, 1);
1791 
1792     entry_name = gtk_entry_new ( );
1793     gtk_entry_set_activates_default ( GTK_ENTRY ( entry_name ), TRUE );
1794     gtk_grid_attach (GTK_GRID (paddinggrid), entry_name, 1, 0, 1, 1);
1795 
1796     /* List of the accounts */
1797     label = gtk_label_new ( _("Accounts list: ") );
1798     utils_labels_set_alignment ( GTK_LABEL ( label ), 0, 1);
1799     gtk_label_set_justify ( GTK_LABEL ( label ), GTK_JUSTIFY_LEFT );
1800     gtk_grid_attach (GTK_GRID (paddinggrid), label, 0, 1, 1, 1);
1801 
1802     entry_list = gtk_entry_new ( );
1803     gtk_editable_set_editable ( GTK_EDITABLE ( entry_list ), FALSE );
1804     gtk_widget_set_sensitive ( entry_list, FALSE );
1805     gtk_grid_attach (GTK_GRID (paddinggrid), entry_list, 1, 1, 1, 1);
1806 
1807     account_list = gsb_partial_balance_create_list_accounts ( entry_list );
1808     gtk_grid_attach (GTK_GRID (paddinggrid), account_list, 0, 2, 2, 2);
1809 
1810     /* create the position */
1811     label = gtk_label_new ( _("Position in the list of accounts: ") );
1812     gtk_widget_set_margin_top (label, MARGIN_TOP);
1813     utils_labels_set_alignment ( GTK_LABEL ( label ), 0, 1);
1814     gtk_label_set_justify ( GTK_LABEL ( label ), GTK_JUSTIFY_LEFT );
1815     gtk_grid_attach (GTK_GRID (paddinggrid), label, 0, 4, 1, 1);
1816 
1817     bouton = gtk_spin_button_new_with_range ( 1.0, spin_value, 1.0);
1818     gtk_widget_set_margin_top (bouton, MARGIN_TOP);
1819     gtk_spin_button_set_value ( GTK_SPIN_BUTTON ( bouton ),
1820                         g_slist_length ( partial_balance_list ) + 1 );
1821     gtk_grid_attach (GTK_GRID (paddinggrid), bouton, 1, 4, 1, 1);
1822     g_object_set_data ( G_OBJECT ( dialog ), "spin_bouton", bouton );
1823 
1824     /* create the colorized button */
1825     bouton = gtk_check_button_new_with_label ( _("Colorized in red if the balance is negative") );
1826     gtk_widget_set_margin_top (bouton, MARGIN_TOP);
1827     g_object_set_data ( G_OBJECT ( dialog ), "colorise_bouton", bouton );
1828     gtk_grid_attach (GTK_GRID (paddinggrid), bouton, 0, 5, 2, 1);
1829 
1830     g_object_set_data ( G_OBJECT ( dialog ), "entry_name", entry_name );
1831     g_object_set_data ( G_OBJECT ( dialog ), "entry_list", entry_list );
1832     g_object_set_data ( G_OBJECT ( dialog ), "account_list", account_list );
1833 
1834     return dialog;
1835 }
1836 
1837 
1838 /**
1839  *
1840  *
1841  * */
gsb_partial_balance_request_currency(GtkWidget * parent)1842 gint gsb_partial_balance_request_currency ( GtkWidget *parent )
1843 {
1844     GtkWidget *dialog, *hbox, *label, *combo_devise;
1845     gint currency_nb = 1;	/* Initialisation avec la première devise : fixe bug 1881 */
1846 
1847     dialog = gtk_dialog_new_with_buttons ( _("Enter the currency of the balance part"),
1848                             GTK_WINDOW ( parent ),
1849                             GTK_DIALOG_MODAL,
1850                             "gtk-cancel", 0,
1851                             "gtk-ok", 1,
1852                             NULL );
1853     gtk_widget_set_size_request ( dialog, -1, 150 );
1854     gtk_window_set_position ( GTK_WINDOW ( dialog ), GTK_WIN_POS_CENTER_ON_PARENT );
1855 
1856     hbox = gtk_box_new ( GTK_ORIENTATION_HORIZONTAL, MARGIN_BOX );
1857     gtk_box_pack_start ( GTK_BOX ( dialog_get_content_area( dialog ) ), hbox, TRUE, FALSE, 0 );
1858 
1859     label = gtk_label_new ( _("Select the currency of the partial balance: ") );
1860     utils_labels_set_alignment ( GTK_LABEL  ( label ), 0, 1 );
1861     gtk_label_set_justify ( GTK_LABEL ( label ), GTK_JUSTIFY_LEFT );
1862     gtk_box_pack_start ( GTK_BOX ( hbox ), label, TRUE, TRUE, 0 );
1863 
1864     combo_devise = gsb_currency_combobox_new ( &currency_nb, NULL );
1865     gtk_box_pack_start ( GTK_BOX ( hbox ), combo_devise, FALSE, FALSE, 10 );
1866 
1867     gtk_widget_show_all ( GTK_WIDGET ( dialog ) );
1868 
1869     gsb_currency_set_combobox_history (combo_devise, currency_nb);
1870     gtk_dialog_run ( GTK_DIALOG ( dialog ) );
1871 
1872     gtk_widget_destroy ( GTK_WIDGET ( dialog ) );
1873 
1874     return currency_nb;
1875 }
1876 
1877 /**
1878  * calcule le solde d'un solde partiel à une date donnée
1879  *
1880  * \param partial_balance_number    numéro du solde partiel concerné
1881  * \param date                      date de calcul du solde
1882  *
1883  * \return GsbReal         le solde du solde partiel
1884  * */
gsb_data_partial_balance_get_balance_at_date(gint partial_balance_number,GDate * date)1885 GsbReal gsb_data_partial_balance_get_balance_at_date ( gint partial_balance_number,
1886                         GDate *date )
1887 {
1888     struct_partial_balance *partial_balance;
1889     GsbReal solde = null_real;
1890     gchar **tab;
1891     gint i;
1892 
1893     partial_balance = gsb_data_partial_balance_get_structure ( partial_balance_number );
1894 
1895     if ( !partial_balance )
1896         return null_real;
1897 
1898     if ( partial_balance -> liste_cptes == NULL ||
1899      strlen ( partial_balance -> liste_cptes ) == 0 )
1900         return null_real;
1901 
1902     tab = g_strsplit ( partial_balance -> liste_cptes, ";", 0 );
1903     for ( i = 0; tab[i]; i++ )
1904     {
1905         GsbReal tmp_real;
1906         gint account_number;
1907         gint account_currency;
1908         gint link_number;
1909 
1910         account_number = utils_str_atoi ( tab[i] );
1911         account_currency = gsb_data_account_get_currency ( account_number );
1912         tmp_real = gsb_data_account_get_balance_at_date ( account_number, date );
1913 
1914         if ( tmp_real.mantissa != 0 && partial_balance -> currency != account_currency )
1915         {
1916             if ( ( link_number = gsb_data_currency_link_search ( account_currency,
1917                         partial_balance -> currency ) ) )
1918             {
1919                 if ( gsb_data_currency_link_get_first_currency ( link_number) == account_currency )
1920                     tmp_real = gsb_real_mul ( tmp_real,
1921                                 gsb_data_currency_link_get_change_rate ( link_number ) );
1922                 else
1923                     tmp_real = gsb_real_div ( tmp_real,
1924                                 gsb_data_currency_link_get_change_rate ( link_number ) );
1925             }
1926         }
1927         solde = gsb_real_add ( solde, tmp_real );
1928     }
1929     g_strfreev ( tab );
1930 
1931     return solde;
1932 }
1933 
1934 
1935 /**
1936  *
1937  *
1938  * \param
1939  *
1940  * \return
1941  * */
1942 /* Local Variables: */
1943 /* c-basic-offset: 4 */
1944 /* End: */
1945