1 /********************************************************************\
2  * gnc-split-reg2.c -- A widget for the common register look-n-feel *
3  * Copyright (C) 1997 Robin D. Clark                                *
4  * Copyright (C) 1997-1998 Linas Vepstas <linas@linas.org>          *
5  * Copyright (C) 1998 Rob Browning <rlb@cs.utexas.edu>              *
6  * Copyright (C) 1999-2000 Dave Peticolas <dave@krondo.com>         *
7  * Copyright (C) 2001 Gnumatic, Inc.                                *
8  * Copyright (C) 2002,2006 Joshua Sled <jsled@asynchronous.org>     *
9  * Copyright (C) 2012 Robert Fewell                                 *
10  *                                                                  *
11  * This program is free software; you can redistribute it and/or    *
12  * modify it under the terms of the GNU General Public License as   *
13  * published by the Free Software Foundation; either version 2 of   *
14  * the License, or (at your option) any later version.              *
15  *                                                                  *
16  * This program is distributed in the hope that it will be useful,  *
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
19  * GNU General Public License for more details.                     *
20  *                                                                  *
21  * You should have received a copy of the GNU General Public License*
22  * along with this program; if not, contact:                        *
23  *                                                                  *
24  * Free Software Foundation           Voice:  +1-617-542-5942       *
25  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
26  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
27 \********************************************************************/
28 
29 #include <config.h>
30 
31 #include <gtk/gtk.h>
32 #include <glib.h>
33 #include <glib/gi18n.h>
34 
35 #include "gnc-split-reg2.h"
36 #include "gnc-tree-view-split-reg.h"
37 #include "gnc-tree-control-split-reg.h"
38 #include "gnc-ledger-display2.h"
39 
40 #include "gnc-euro.h"
41 #include "gnc-state.h"
42 #include "gnc-warnings.h"
43 #include "dialog-utils.h"
44 
45 #define STATE_SECTION_REG_PREFIX "Register"
46 #define STATE_SECTION_GEN_JOURNAL "General Journal"
47 
48 static QofLogModule log_module = GNC_MOD_GUI;
49 
50 /***** PROTOTYPES ***************************************************/
51 void gnc_split_reg2_raise (GNCSplitReg2 *gsr);
52 
53 static GtkWidget* add_summary_label (GtkWidget *summarybar,
54                                      const char *label_str);
55 
56 static void gnc_split_reg2_determine_read_only (GNCSplitReg2 *gsr, gboolean show_dialog);
57 
58 static void gnc_split_reg2_determine_account_pr (GNCSplitReg2 *gsr);
59 
60 static GNCPlaceholderType gnc_split_reg2_get_placeholder (GNCSplitReg2 *gsr);
61 static GtkWidget *gnc_split_reg2_get_parent (GNCLedgerDisplay2 *ledger);
62 
63 static void gsr2_create_table (GNCSplitReg2 *gsr);
64 static void gsr2_setup_table (GNCSplitReg2 *gsr);
65 
66 static void gsr2_setup_status_widgets (GNCSplitReg2 *gsr);
67 
68 static void gsr2_update_summary_label (GtkWidget *label,
69                                       xaccGetBalanceFn getter,
70                                       Account *leader,
71                                       GNCPrintAmountInfo print_info,
72                                       gnc_commodity *cmdty,
73                                       gboolean reverse,
74                                       gboolean euroFlag );
75 
76 static void gsr2_redraw_all_cb (GncTreeViewSplitReg *view, gpointer data);
77 
78 static void gnc_split_reg2_ld_destroy (GNCLedgerDisplay2 *ledger);
79 
80 static Transaction* gsr2_create_balancing_transaction (QofBook *book, Account *account,
81         time64 statement_date, gnc_numeric balancing_amount);
82 
83 static void gsr2_emit_simple_signal (GNCSplitReg2 *gsr, const char *sigName);
84 static void gsr2_emit_help_changed (GncTreeViewSplitReg *view, gpointer user_data);
85 static void gsr2_scroll_value_changed_cb (GtkAdjustment *adj, gpointer user_data);
86 static gboolean gsr2_scroll_button_event_cb (GtkWidget *widget, GdkEventButton *event, gpointer user_data);
87 static void gsr2_scroll_sync_cb (GncTreeModelSplitReg *model, gpointer user_data);
88 
89 void gnc_split_reg2_style_ledger_cb (GtkWidget *w, gpointer data);
90 void gnc_split_reg2_style_auto_ledger_cb (GtkWidget *w, gpointer data);
91 void gnc_split_reg2_style_journal_cb (GtkWidget *w, gpointer data);
92 void gnc_split_reg2_double_line_cb (GtkWidget *w, gpointer data);
93 
94 void gnc_split_reg2_destroy_cb (GtkWidget *widget, gpointer data);
95 
96 static void gnc_split_reg2_class_init (GNCSplitReg2Class *klass);
97 static void gnc_split_reg2_init (GNCSplitReg2 *gsr);
98 static void gnc_split_reg2_init2 (GNCSplitReg2 *gsr);
99 
100 static void gnc_split_reg2_sort_changed_cb (GtkTreeSortable *sortable, gpointer user_data);
101 
102 
103 GType
gnc_split_reg2_get_type(void)104 gnc_split_reg2_get_type (void)
105 {
106     static GType gnc_split_reg2_type = 0;
107 
108     if (!gnc_split_reg2_type)
109     {
110         GTypeInfo type_info =
111         {
112             sizeof(GNCSplitReg2Class),          /* class_size */
113             NULL,   			        /* base_init */
114             NULL,				/* base_finalize */
115             (GClassInitFunc)gnc_split_reg2_class_init,
116             NULL,				/* class_finalize */
117             NULL,				/* class_data */
118             sizeof(GNCSplitReg2),		/* */
119             0,				        /* n_preallocs */
120             (GInstanceInitFunc)gnc_split_reg2_init,
121         };
122 
123         gnc_split_reg2_type = g_type_register_static (GTK_TYPE_BOX,
124                              "GNCSplitReg2",
125                              &type_info, 0 );
126     }
127 
128     return gnc_split_reg2_type;
129 }
130 
131 /* SIGNALS */
132 enum
133 {
134     HELP_CHANGED,
135     LAST_SIGNAL
136 };
137 
138 static guint gnc_split_reg2_signals[LAST_SIGNAL] = { 0 };
139 
140 static void
gnc_split_reg2_class_init(GNCSplitReg2Class * klass)141 gnc_split_reg2_class_init (GNCSplitReg2Class *klass)
142 {
143     GObjectClass *object_class;
144 
145     object_class = (GObjectClass*) klass;
146 
147     gnc_split_reg2_signals[HELP_CHANGED] =
148         g_signal_new("help-changed",
149                      G_TYPE_FROM_CLASS (object_class),
150                      G_SIGNAL_RUN_LAST,
151                      G_STRUCT_OFFSET (GNCSplitReg2Class, help_changed),
152                      NULL, NULL,
153                      g_cclosure_marshal_VOID__VOID,
154                      G_TYPE_NONE, 0);
155 
156     /* Setup the default handlers. */
157     klass->help_changed = NULL;
158 
159 }
160 
161 GtkWidget*
gnc_split_reg2_new(GNCLedgerDisplay2 * ld,GtkWindow * parent,gint numberOfLines,gboolean read_only)162 gnc_split_reg2_new (GNCLedgerDisplay2 *ld,
163                    GtkWindow *parent,
164                    gint numberOfLines,
165                    gboolean read_only )
166 {
167     GNCSplitReg2 *gsrToRet;
168 
169     ENTER("ld=%p, parent=%p, numberOfLines=%d, read_only=%s",
170           ld, parent, numberOfLines, read_only ? "TRUE" : "FALSE");
171 
172     gsrToRet = g_object_new (gnc_split_reg2_get_type(), NULL);
173 
174     gsrToRet->numRows = numberOfLines;
175     gsrToRet->read_only = read_only;
176 
177     gsrToRet->ledger = ld;
178     gsrToRet->window = GTK_WIDGET (parent);
179 
180     gnc_split_reg2_init2 (gsrToRet);
181 
182     LEAVE("%p", gsrToRet);
183     return GTK_WIDGET (gsrToRet);
184 }
185 
186 static void
gnc_split_reg2_init(GNCSplitReg2 * gsr)187 gnc_split_reg2_init (GNCSplitReg2 *gsr)
188 {
189     gtk_orientable_set_orientation (GTK_ORIENTABLE(gsr), GTK_ORIENTATION_VERTICAL);
190 
191     gsr->numRows = 10;
192     gsr->read_only = FALSE;
193 
194     g_signal_connect (gsr, "destroy",
195                       G_CALLBACK (gnc_split_reg2_destroy_cb), gsr );
196 }
197 
198 static void
gnc_split_reg2_init2(GNCSplitReg2 * gsr)199 gnc_split_reg2_init2 (GNCSplitReg2 *gsr)
200 {
201     if (!gsr) return;
202 
203     gnc_split_reg2_determine_read_only (gsr, TRUE);
204 
205     gnc_split_reg2_determine_account_pr (gsr);
206 
207     gsr2_setup_status_widgets (gsr);
208     /* ordering is important here... setup_status before create_table */
209 
210     gsr2_create_table (gsr);
211     gsr2_setup_table (gsr);
212 
213 }
214 
215 static
216 void
gsr2_setup_table(GNCSplitReg2 * gsr)217 gsr2_setup_table (GNCSplitReg2 *gsr)
218 {
219 //    GncTreeModelSplitReg *model;
220 
221     ENTER("gsr=%p", gsr);
222 
223 // This dose not do any thing
224 //    model = gnc_ledger_display2_get_split_model_register (gsr->ledger);
225 
226     LEAVE(" ");
227 }
228 
229 static
230 void
gsr2_create_table(GNCSplitReg2 * gsr)231 gsr2_create_table (GNCSplitReg2 *gsr)
232 {
233     GncTreeViewSplitReg *view;
234     GncTreeModelSplitReg *model;
235     GtkTreeModel *s_model;
236     GtkWidget *scrolled_window;
237     GtkTreeViewColumn *col;
238     GNCLedgerDisplay2Type ledger_type;
239     GtkWidget *hbox;
240     gdouble num_of_trans;
241 
242     gchar *state_section;
243     GKeyFile *state_file = gnc_state_get_current();
244     const GncGUID * guid;
245     Account * account;
246 
247     account = gnc_ledger_display2_leader (gsr->ledger);
248     guid = xaccAccountGetGUID (account);
249 
250     ENTER("create table gsr=%p", gsr);
251 
252     gnc_ledger_display2_set_user_data (gsr->ledger, (gpointer)gsr);
253     gnc_ledger_display2_set_handlers (gsr->ledger,
254                                      gnc_split_reg2_ld_destroy,
255                                      gnc_split_reg2_get_parent);
256 
257     model = gnc_ledger_display2_get_split_model_register (gsr->ledger);
258     view = gnc_tree_view_split_reg_new_with_model (model);
259     g_object_unref (G_OBJECT (model));
260 
261     /* State_section is used to store per register state: column widths, sort order,... */
262     ledger_type = gnc_ledger_display2_type (gsr->ledger);
263     if (ledger_type == LD2_GL && model->type == GENERAL_JOURNAL2)
264         state_section = g_strdup (STATE_SECTION_GEN_JOURNAL);
265     else if (ledger_type == LD2_SUBACCOUNT)
266     {
267         gchar guidstr[GUID_ENCODING_LENGTH+1];
268         guid_to_string_buff (guid, guidstr);
269         state_section = g_strconcat (STATE_SECTION_REG_PREFIX, " ", guidstr, " w/subaccounts", NULL);
270     }
271     else
272     {
273         gchar guidstr[GUID_ENCODING_LENGTH+1];
274         guid_to_string_buff (guid, guidstr);
275         state_section = g_strconcat (STATE_SECTION_REG_PREFIX, " ", guidstr, NULL);
276     }
277     g_object_set (G_OBJECT (view), "state-section", state_section,
278                  "show-column-menu", FALSE, NULL);
279 
280     // Create a hbox for treeview and scrollbar.
281     hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
282     gtk_box_set_homogeneous (GTK_BOX (hbox), FALSE);
283     gtk_widget_show (hbox);
284 
285     scrolled_window = gtk_scrolled_window_new (NULL, NULL);
286     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
287                                    GTK_POLICY_AUTOMATIC,
288                                    GTK_POLICY_AUTOMATIC);
289 
290     gtk_widget_show (scrolled_window);
291 
292     gtk_box_pack_start (GTK_BOX (gsr), hbox, TRUE, TRUE, 0);
293 
294     num_of_trans = model->number_of_trans_in_full_tlist - 1;
295 
296     gsr->scroll_adj = GTK_ADJUSTMENT (gtk_adjustment_new (model->position_of_trans_in_full_tlist, 0.0, num_of_trans + 10, 1.0, 10.0, 10.0));
297 
298     gsr->scroll_bar = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, GTK_ADJUSTMENT (gsr->scroll_adj));
299     gtk_widget_show (gsr->scroll_bar);
300 
301     gtk_box_pack_start (GTK_BOX (hbox), gsr->scroll_bar, FALSE, FALSE, 2);
302     gtk_box_pack_start (GTK_BOX (hbox), scrolled_window, TRUE, TRUE, 0);
303 
304     gnc_ledger_display2_set_split_view_register (gsr->ledger, view);
305 
306     /* Synchronize model state with view state
307      * (needed to properly set up the internal query) */
308 
309     /* Restore the sort depth from saved state */
310     model->sort_depth = g_key_file_get_integer (state_file, state_section, "sort_depth", NULL);
311     g_free(state_section);
312 
313     s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
314     if (s_model)
315     {
316         gint sort_col;
317         GtkSortType   type;
318 
319         if (gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (s_model), &sort_col, &type))
320         {
321             model->sort_col = sort_col;
322             model->sort_direction = type;
323         }
324     }
325 
326     gnc_tree_view_configure_columns (GNC_TREE_VIEW (view));
327 
328     if (ledger_type == LD2_GL && model->type == GENERAL_JOURNAL2)
329         gnc_tree_view_set_show_column_menu (GNC_TREE_VIEW (view), TRUE);
330     else
331         gnc_tree_view_set_show_column_menu (GNC_TREE_VIEW (view), FALSE);
332 
333     /* This column gets all the free space */
334     gnc_tree_view_expand_columns (GNC_TREE_VIEW (view), "descnotes", NULL);
335 
336     /* This sets the status color column, 4 is the minimum */
337     col = gnc_tree_view_find_column_by_name (GNC_TREE_VIEW (view), "status");
338     if (col != NULL)
339         g_object_set (G_OBJECT(col),
340                      "resizable", FALSE,
341                      "sizing", GTK_TREE_VIEW_COLUMN_FIXED,
342                      "fixed-width", 4,
343                       NULL);
344 
345     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (view), TRUE);
346     gtk_widget_show (GTK_WIDGET (view));
347 
348     gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (view));
349     gtk_widget_show (GTK_WIDGET (gsr));
350 
351     /* Should this be read only */
352     gnc_tree_view_split_reg_set_read_only (view, gsr->read_only);
353 
354     /* This tells the ledger that we have a valid tree view */
355     gnc_ledger_display2_set_split_view_refresh (gsr->ledger, TRUE);
356 
357     /* This triggers the update of the summary bar */
358     g_signal_connect_after (model, "refresh_status_bar",
359                       G_CALLBACK (gsr2_redraw_all_cb), gsr); //this works
360 
361     // This will keep scrollbar in sync.
362     g_signal_connect (model, "scroll_sync",
363                       G_CALLBACK (gsr2_scroll_sync_cb), gsr);
364 
365     /* This triggers the update of the help text */
366     g_signal_connect (view, "help_signal",
367                       G_CALLBACK (gsr2_emit_help_changed), gsr); // this works
368 
369     gsr2_scroll_value_changed_cb (GTK_ADJUSTMENT (gsr->scroll_adj), gsr);
370 
371     /* This triggers the tooltip to change when scrolling */
372     g_signal_connect (gsr->scroll_adj, "value-changed",
373                       G_CALLBACK (gsr2_scroll_value_changed_cb), gsr); // this works
374 
375     /* This triggers the model update when mouse button released */
376     g_signal_connect (gsr->scroll_bar, "button-release-event",
377                       G_CALLBACK (gsr2_scroll_button_event_cb), gsr);
378 
379     // Connect a call back to update the sort settings.
380     g_signal_connect (GTK_TREE_SORTABLE (s_model), "sort-column-changed",
381           G_CALLBACK (gnc_split_reg2_sort_changed_cb), gsr);
382 
383     LEAVE(" ");
384 }
385 
386 static
387 void
gsr2_setup_status_widgets(GNCSplitReg2 * gsr)388 gsr2_setup_status_widgets (GNCSplitReg2 *gsr)
389 {
390     GncTreeModelSplitReg *model;
391     gboolean use_double_line;
392 
393     model = gnc_ledger_display2_get_split_model_register (gsr->ledger);
394     use_double_line = gnc_ledger_display2_default_double_line (gsr->ledger);
395 
396     /* be sure to initialize the gui elements */
397     gnc_tree_model_split_reg_config (model, model->type, model->style, use_double_line);
398 }
399 
400 void
gnc_split_reg2_destroy_cb(GtkWidget * widget,gpointer data)401 gnc_split_reg2_destroy_cb (GtkWidget *widget, gpointer data)
402 {
403 }
404 
405 /**
406  * Jump to split.
407  **/
408 void
gnc_split_reg2_jump_to_split(GNCSplitReg2 * gsr,Split * split)409 gnc_split_reg2_jump_to_split (GNCSplitReg2 *gsr, Split *split)
410 {
411     GncTreeViewSplitReg *view;
412 
413     if (gsr == NULL)
414         return;
415 
416     if (split == NULL)
417         return;
418 
419     view = gnc_ledger_display2_get_split_view_register (gsr->ledger);
420 
421     gnc_tree_control_split_reg_jump_to (view, NULL, split, FALSE);
422 }
423 
424 
425 /**
426  * Move the cursor to the split in the non-blank amount column.
427  **/
428 void
gnc_split_reg2_jump_to_split_amount(GNCSplitReg2 * gsr,Split * split)429 gnc_split_reg2_jump_to_split_amount (GNCSplitReg2 *gsr, Split *split)
430 {
431     GncTreeViewSplitReg *view;
432 
433     if (gsr == NULL)
434         return;
435 
436     if (split == NULL)
437         return;
438 
439     view = gnc_ledger_display2_get_split_view_register (gsr->ledger);
440 
441     gnc_tree_control_split_reg_jump_to (view, NULL, split, TRUE);
442 }
443 
444 /**
445  * Raise an existing register window to the front.
446  **/
447 void
gnc_split_reg2_raise(GNCSplitReg2 * gsr)448 gnc_split_reg2_raise (GNCSplitReg2 *gsr)
449 {
450     if (gsr == NULL)
451         return;
452 
453     if (gsr->window == NULL)
454         return;
455 
456     gtk_window_present (GTK_WINDOW (gsr->window));
457 }
458 
459 
460 /**
461  * Duplicate-code reduction function; retrieves, formats and updates the
462  * GtkLabel with the given amount.
463  **/
464 static
465 void
gsr2_update_summary_label(GtkWidget * label,xaccGetBalanceFn getter,Account * leader,GNCPrintAmountInfo print_info,gnc_commodity * cmdty,gboolean reverse,gboolean euroFlag)466 gsr2_update_summary_label (GtkWidget *label,
467                           xaccGetBalanceFn getter,
468                           Account *leader,
469                           GNCPrintAmountInfo print_info,
470                           gnc_commodity *cmdty,
471                           gboolean reverse,
472                           gboolean euroFlag)
473 {
474     gnc_numeric amount;
475     char string[256];
476 
477     if ( label == NULL )
478         return;
479 
480     amount = (*getter)( leader );
481 
482     if ( reverse )
483     {
484         amount = gnc_numeric_neg( amount );
485     }
486 
487     xaccSPrintAmount( string, amount, print_info );
488 
489     if ( euroFlag )
490     {
491         strcat( string, " / " );
492         xaccSPrintAmount( string + strlen( string ),
493                           gnc_convert_to_euro( cmdty, amount ),
494                           gnc_commodity_print_info( gnc_get_euro(), TRUE ) );
495     }
496 
497     gnc_set_label_color( label, amount );
498     gtk_label_set_text( GTK_LABEL(label), string );
499 }
500 
501 static
502 void
gsr2_redraw_all_cb(GncTreeViewSplitReg * view,gpointer user_data)503 gsr2_redraw_all_cb (GncTreeViewSplitReg *view, gpointer user_data)
504 {
505     GNCSplitReg2 *gsr = user_data;
506     gnc_commodity * commodity;
507     GNCPrintAmountInfo print_info;
508     gnc_numeric amount = gnc_numeric_zero();
509     Account *leader;
510     gboolean reverse;
511     gboolean euro;
512 
513     if ( gsr->summarybar == NULL )
514         return;
515 
516     leader = gnc_ledger_display2_leader( gsr->ledger );
517 
518     commodity = xaccAccountGetCommodity( leader );
519 
520     /* no EURO conversion, if account is already EURO or no EURO currency */
521     if (commodity != NULL)
522         euro = (gnc_is_euro_currency( commodity ) &&
523                 (strncasecmp(gnc_commodity_get_mnemonic(commodity), "EUR", 3)));
524     else
525         euro = FALSE;
526 
527     print_info = gnc_account_print_info( leader, TRUE );
528     reverse = gnc_reverse_balance( leader );
529 
530     gsr2_update_summary_label( gsr->balance_label,
531                               xaccAccountGetPresentBalance,
532                               leader, print_info, commodity, reverse, euro );
533     gsr2_update_summary_label( gsr->cleared_label,
534                               xaccAccountGetClearedBalance,
535                               leader, print_info, commodity, reverse, euro );
536     gsr2_update_summary_label( gsr->reconciled_label,
537                               xaccAccountGetReconciledBalance,
538                               leader, print_info, commodity, reverse, euro );
539     gsr2_update_summary_label( gsr->future_label,
540                               xaccAccountGetBalance,
541                               leader, print_info, commodity, reverse, euro );
542     gsr2_update_summary_label( gsr->projectedminimum_label,
543                               xaccAccountGetProjectedMinimumBalance,
544                               leader, print_info, commodity, reverse, euro );
545 
546    /* Print the summary share amount */
547     if (gsr->shares_label != NULL)
548     {
549         char string[256];
550         print_info = gnc_account_print_info( leader, TRUE );
551         amount = xaccAccountGetBalance( leader );
552         if ( reverse )
553             amount = gnc_numeric_neg( amount );
554         xaccSPrintAmount( string, amount, print_info );
555         gnc_set_label_color( gsr->shares_label, amount );
556         gtk_label_set_text( GTK_LABEL(gsr->shares_label), string );
557     }
558 
559     /* Print the summary share value */
560     if (gsr->value_label != NULL)
561     {
562         char string[256];
563         gnc_commodity *currency = gnc_default_currency ();
564         print_info = gnc_commodity_print_info (currency, TRUE);
565         xaccSPrintAmount (string, amount, print_info);
566         gnc_set_label_color (gsr->value_label, amount);
567         gtk_label_set_text (GTK_LABEL (gsr->value_label), string);
568 
569     }
570 }
571 
572 
573 static void
gnc_split_reg2_ld_destroy(GNCLedgerDisplay2 * ledger)574 gnc_split_reg2_ld_destroy (GNCLedgerDisplay2 *ledger)
575 {
576     gnc_ledger_display2_set_user_data (ledger, NULL);
577 }
578 
579 
580 /* ########################### Handlers ############################### */
581 
582 void
gnc_split_reg2_balancing_entry(GNCSplitReg2 * gsr,Account * account,time64 statement_date,gnc_numeric balancing_amount)583 gnc_split_reg2_balancing_entry (GNCSplitReg2 *gsr, Account *account,
584                               time64 statement_date, gnc_numeric balancing_amount) // this works
585 {
586     GncTreeViewSplitReg *view;
587     Transaction *transaction;
588     Split *split;
589 
590     view = gnc_ledger_display2_get_split_view_register (gsr->ledger);
591 
592     // create transaction
593     transaction = gsr2_create_balancing_transaction (gnc_get_current_book(),
594                   account, statement_date, balancing_amount);
595 
596     // jump to transaction
597     split = xaccTransFindSplitByAccount (transaction, account);
598     if (split == NULL)
599     {
600         // default behaviour: jump to blank split
601         g_warning("gsr2_create_balancing_transaction failed");
602         gnc_tree_control_split_reg_jump_to_blank (view);
603     }
604     else
605     {
606         // goto balancing transaction
607         gnc_tree_control_split_reg_jump_to (view, NULL, split, FALSE);
608     }
609 }
610 
611 static Transaction*
gsr2_create_balancing_transaction(QofBook * book,Account * account,time64 statement_date,gnc_numeric balancing_amount)612 gsr2_create_balancing_transaction (QofBook *book, Account *account,
613                              time64 statement_date, gnc_numeric balancing_amount)
614 {
615     Transaction *trans;
616     Split *split;
617 
618     if (!account)
619         return NULL;
620     if (gnc_numeric_zero_p (balancing_amount))
621         return NULL;
622 
623     xaccAccountBeginEdit (account);
624 
625     trans = xaccMallocTransaction (book);
626 
627     xaccTransBeginEdit (trans);
628 
629     // fill Transaction
630     xaccTransSetCurrency (trans, gnc_account_or_default_currency (account, NULL));
631     xaccTransSetDatePostedSecsNormalized (trans, statement_date);
632     xaccTransSetDescription (trans, _("Balancing entry from reconciliation"));
633 
634     // 1. Split
635     split = xaccMallocSplit (book);
636     xaccTransAppendSplit (trans, split);
637     xaccAccountInsertSplit  (account, split);
638     xaccSplitSetAmount (split, balancing_amount);
639     xaccSplitSetValue (split, balancing_amount);
640 
641     // 2. Split (no account is defined: split goes to orphan account)
642     split = xaccMallocSplit (book);
643     xaccTransAppendSplit (trans, split);
644 
645     balancing_amount = gnc_numeric_neg (balancing_amount);
646     xaccSplitSetAmount (split, balancing_amount);
647     xaccSplitSetValue (split, balancing_amount);
648 
649     xaccTransCommitEdit (trans);
650     xaccAccountCommitEdit (account);
651     return trans;
652 }
653 
654 
655 /* Sort changed callback */
656 static void
gnc_split_reg2_sort_changed_cb(GtkTreeSortable * sortable,gpointer user_data)657 gnc_split_reg2_sort_changed_cb (GtkTreeSortable *sortable, gpointer user_data)
658 {
659     GNCSplitReg2 *gsr = user_data;
660     GncTreeViewSplitReg *view;
661     GncTreeModelSplitReg *model;
662     GtkSortType   type;
663     gint          sortcol;
664     gint          sort_depth;
665     const gchar  *state_section;
666     GKeyFile *state_file = gnc_state_get_current();
667 
668     gtk_tree_sortable_get_sort_column_id (sortable, &sortcol, &type);
669     ENTER("sortcol is %d", sortcol);
670 
671     view = gnc_ledger_display2_get_split_view_register (gsr->ledger);
672     model = gnc_ledger_display2_get_split_model_register (gsr->ledger);
673 
674     sort_depth = gnc_tree_view_reg_get_selected_row_depth (view);
675     if (sort_depth != 0)
676         model->sort_depth = sort_depth;
677 
678     model->sort_col = sortcol;
679     model->sort_direction = type;
680 
681     /* Save the sort depth state */
682     state_section = gnc_tree_view_get_state_section (GNC_TREE_VIEW (view));
683     g_key_file_set_integer (state_file, state_section, "sort_depth", model->sort_depth);
684 
685     LEAVE("m_sort_col %d, m_sort_direction is %d  m_sort_depth is %d", model->sort_col, model->sort_direction, model->sort_depth);
686 
687     if (sortcol != -1)
688         gnc_ledger_display2_refresh (gsr->ledger);
689 }
690 /* ############################## End Handlers ############################ */
691 
692 void
gnc_split_reg2_change_style(GNCSplitReg2 * gsr,SplitRegisterStyle2 style)693 gnc_split_reg2_change_style (GNCSplitReg2 *gsr, SplitRegisterStyle2 style)
694 {
695     GncTreeModelSplitReg *model = gnc_ledger_display2_get_split_model_register (gsr->ledger);
696 
697     if (style == model->style)
698         return;
699 
700     gnc_tree_model_split_reg_config (model, model->type, style, model->use_double_line);
701 
702     // This will re-display the view.
703     gnc_tree_view_split_reg_set_format (gnc_ledger_display2_get_split_view_register (gsr->ledger));
704 }
705 
706 void
gnc_split_reg2_style_ledger_cb(GtkWidget * w,gpointer data)707 gnc_split_reg2_style_ledger_cb (GtkWidget *w, gpointer data)
708 {
709     GNCSplitReg2 *gsr = data;
710 
711     if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (w)))
712         return;
713 
714     gnc_split_reg2_change_style (gsr, REG2_STYLE_LEDGER);
715 }
716 
717 void
gnc_split_reg2_style_auto_ledger_cb(GtkWidget * w,gpointer data)718 gnc_split_reg2_style_auto_ledger_cb (GtkWidget *w, gpointer data)
719 {
720     GNCSplitReg2 *gsr = data;
721 
722     if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (w)))
723         return;
724 
725     gnc_split_reg2_change_style (gsr, REG2_STYLE_AUTO_LEDGER);
726 }
727 
728 void
gnc_split_reg2_style_journal_cb(GtkWidget * w,gpointer data)729 gnc_split_reg2_style_journal_cb (GtkWidget *w, gpointer data)
730 {
731     GNCSplitReg2 *gsr = data;
732 
733     if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (w)))
734         return;
735 
736     gnc_split_reg2_change_style (gsr, REG2_STYLE_JOURNAL);
737 }
738 
739 void
gnc_split_reg2_double_line_cb(GtkWidget * w,gpointer data)740 gnc_split_reg2_double_line_cb (GtkWidget *w, gpointer data)
741 {
742     GNCSplitReg2 *gsr = data;
743     GncTreeModelSplitReg *model = gnc_ledger_display2_get_split_model_register (gsr->ledger);
744     gboolean use_double_line;
745 
746     use_double_line = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (w));
747     if (use_double_line == model->use_double_line)
748         return;
749 
750     gnc_tree_model_split_reg_config (model, model->type, model->style, use_double_line);
751 
752     // This will re-display the view.
753     gnc_tree_view_split_reg_set_format (gnc_ledger_display2_get_split_view_register (gsr->ledger));
754 }
755 
756 static
757 GtkWidget*
add_summary_label(GtkWidget * summarybar,const char * label_str)758 add_summary_label (GtkWidget *summarybar, const char *label_str)
759 {
760     GtkWidget *hbox;
761     GtkWidget *label;
762 
763     hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
764     gtk_box_set_homogeneous (GTK_BOX (hbox), FALSE);
765     gtk_box_pack_start (GTK_BOX (summarybar), hbox, FALSE, FALSE, 5);
766 
767     label = gtk_label_new (label_str);
768     gnc_label_set_alignment (label, 1.0, 0.5);
769     gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
770 
771     label = gtk_label_new ("");
772     gnc_label_set_alignment (label, 1.0, 0.5);
773     gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
774 
775     return label;
776 }
777 
778 GtkWidget *
gnc_split_reg2_create_summary_bar(GNCSplitReg2 * gsr)779 gnc_split_reg2_create_summary_bar (GNCSplitReg2 *gsr)
780 {
781     GtkWidget *summarybar;
782 
783     gsr->cleared_label    = NULL;
784     gsr->balance_label    = NULL;
785     gsr->reconciled_label = NULL;
786     gsr->future_label     = NULL;
787     gsr->projectedminimum_label = NULL;
788     gsr->shares_label     = NULL;
789     gsr->value_label      = NULL;
790 
791     if (gnc_ledger_display2_type (gsr->ledger) >= LD2_SUBACCOUNT)
792     {
793         gsr->summarybar = NULL;
794         return NULL;
795     }
796 
797     summarybar = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
798     gtk_box_set_homogeneous (GTK_BOX (summarybar), FALSE);
799 
800     if (!xaccAccountIsPriced(gnc_ledger_display2_leader(gsr->ledger)))
801     {
802         gsr->balance_label    = add_summary_label (summarybar, _("Present:"));
803         gsr->future_label     = add_summary_label (summarybar, _("Future:"));
804         gsr->cleared_label    = add_summary_label (summarybar, _("Cleared:"));
805         gsr->reconciled_label = add_summary_label (summarybar, _("Reconciled:"));
806         gsr->projectedminimum_label  = add_summary_label (summarybar, _("Projected Minimum:"));
807     }
808     else
809     {
810         gsr->shares_label     = add_summary_label (summarybar, _("Shares:"));
811         gsr->value_label      = add_summary_label (summarybar, _("Current Value:"));
812     }
813 
814     gsr->summarybar = summarybar;
815 
816     /* Force the first update */
817     gsr2_redraw_all_cb (NULL, gsr);
818     return gsr->summarybar;
819 }
820 
821 /**
822  * Opens up a register window for a group of Accounts.
823  * @param gsr the register window instance
824  * @return A GNCPlaceholderType indicating presence and type of placeholder
825  * accounts
826  **/
827 static
828 GNCPlaceholderType
gnc_split_reg2_get_placeholder(GNCSplitReg2 * gsr)829 gnc_split_reg2_get_placeholder (GNCSplitReg2 *gsr)
830 {
831     Account *leader;
832     GncTreeModelSplitReg *model;
833     gboolean single_account;
834 
835     if (gsr == NULL)
836         return PLACEHOLDER_NONE;
837 
838     model = gnc_ledger_display2_get_split_model_register (gsr->ledger);
839 
840     switch (model->type)
841     {
842     case GENERAL_JOURNAL2:
843     case INCOME_LEDGER2:
844     case PORTFOLIO_LEDGER2:
845     case SEARCH_LEDGER2:
846         single_account = FALSE;
847         break;
848     default:
849         single_account = TRUE;
850         break;
851     }
852 
853     leader = gnc_ledger_display2_leader (gsr->ledger);
854 
855     if (leader == NULL)
856         return PLACEHOLDER_NONE;
857     if (single_account)
858     {
859         if (xaccAccountGetPlaceholder (leader))
860             return PLACEHOLDER_THIS;
861         return PLACEHOLDER_NONE;
862     }
863     return xaccAccountGetDescendantPlaceholder (leader);
864 }
865 
866 
867 
868 /**
869  * @see gtk_callback_bug_workaround
870  **/
871 typedef struct dialog_args
872 {
873     GNCSplitReg2 *gsr;
874     gchar *string;
875 } dialog_args;
876 
877 
878 /* This Register is an Account Payable / Receivable one */
879 static
880 gboolean
gsr2_determine_account_pr_dialog(gpointer argp)881 gsr2_determine_account_pr_dialog (gpointer argp)
882 {
883     dialog_args *args = argp;
884     GtkWidget *dialog;
885 
886     const char *title = _("Account Payable / Receivable Register");
887     const char *message =
888             _("The register displayed is for Account Payable or Account Receivable. "
889               "Changing the entries may cause harm, please use the business "
890               "options to change the entries.");
891 
892     dialog = gtk_message_dialog_new (GTK_WINDOW (args->gsr->window),
893                                      GTK_DIALOG_DESTROY_WITH_PARENT,
894                                      GTK_MESSAGE_WARNING,
895                                      GTK_BUTTONS_CLOSE,
896                                      "%s", title);
897     gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
898                                               "%s", message);
899 
900     gnc_dialog_run (GTK_DIALOG (dialog), GNC_PREF_WARN_REG_IS_ACCT_PAY_REC);
901     gtk_widget_destroy (dialog);
902     g_free (args);
903     return FALSE;
904 }
905 
906 
907 /* This Register is an Account Payable / Receivable one */
908 static void
gnc_split_reg2_determine_account_pr(GNCSplitReg2 * gsr)909 gnc_split_reg2_determine_account_pr (GNCSplitReg2 *gsr)
910 {
911     dialog_args *args;
912     GncTreeModelSplitReg *model;
913 
914     model = gnc_ledger_display2_get_split_model_register (gsr->ledger);
915 
916     if (model->type != PAYABLE_REGISTER2 && model->type != RECEIVABLE_REGISTER2)
917         return;
918 
919     /* Put up a warning dialog */
920     args  = g_malloc (sizeof (dialog_args));
921     args->string = ""; /* FIXME: No string for dialog. */
922     args->gsr = gsr;
923     g_timeout_add (250, gsr2_determine_account_pr_dialog, args); /* 0.25 seconds */
924 }
925 
926 
927 /**
928  * Gtk has occasional problems with performing function as part of a
929  * callback.  This routine gets called via a timer callback to get it out of
930  * the data path with the problem.
931  **/
932 static
933 gboolean
gtk_callback_bug_workaround(gpointer argp)934 gtk_callback_bug_workaround (gpointer argp)
935 {
936     dialog_args *args = argp;
937     const gchar *read_only_this = _("This account register is read-only.");
938     const gchar *read_only_acc = _("The '%s' account register is read-only.");
939     gchar *read_only = NULL;
940     GtkWidget *dialog;
941     GNCLedgerDisplay2Type ledger_type = gnc_ledger_display2_type (args->gsr->ledger);
942     Account *acc = gnc_ledger_display2_leader (args->gsr->ledger);
943     const gchar *acc_name = NULL;
944     gchar *tmp = NULL;
945 
946     if (acc)
947     {
948         acc_name = xaccAccountGetName (acc);
949 
950         if (ledger_type == LD2_SINGLE)
951             read_only = g_strdup_printf (read_only_acc, acc_name);
952         else
953         {
954             gchar *tmp = g_strconcat (acc_name, "+", NULL);
955             read_only = g_strdup_printf (read_only_acc, tmp);
956             g_free (tmp);
957         }
958     }
959     else
960         read_only = g_strdup (read_only_this);
961 
962     dialog = gtk_message_dialog_new (GTK_WINDOW(args->gsr->window),
963                                     GTK_DIALOG_DESTROY_WITH_PARENT,
964                                     GTK_MESSAGE_WARNING,
965                                     GTK_BUTTONS_CLOSE,
966                                     "%s", read_only);
967     gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
968             "%s", args->string);
969     gnc_dialog_run (GTK_DIALOG (dialog), GNC_PREF_WARN_REG_IS_READ_ONLY);
970     gtk_widget_destroy (dialog);
971     g_free(read_only);
972     g_free (args);
973     return FALSE;
974 }
975 
976 /**
977  * Determines whether this register window should be read-only.
978  **/
979 static
980 void
gnc_split_reg2_determine_read_only(GNCSplitReg2 * gsr,gboolean show_dialog)981 gnc_split_reg2_determine_read_only (GNCSplitReg2 *gsr, gboolean show_dialog)
982 {
983 
984     if (qof_book_is_readonly (gnc_get_current_book()))
985     {
986         /* Is the book read-only? Then for sure also make this register
987         read-only. */
988         gsr->read_only = TRUE;
989     }
990 
991     if (!gsr->read_only)
992     {
993         dialog_args *args = g_malloc (sizeof (dialog_args));
994 
995         switch (gnc_split_reg2_get_placeholder (gsr))
996         {
997         case PLACEHOLDER_NONE:
998             /* stay as false. */
999             g_free (args);
1000             return;
1001 
1002         case PLACEHOLDER_THIS:
1003             args->string = _("This account may not be edited. If you want "
1004                              "to edit transactions in this register, please "
1005                              "open the account options and turn off the "
1006                              "placeholder checkbox.");
1007             break;
1008 
1009         default:
1010             args->string = _("One of the sub-accounts selected may not be "
1011                              "edited. If you want to edit transactions in "
1012                              "this register, please open the sub-account "
1013                              "options and turn off the placeholder checkbox. "
1014                              "You may also open an individual account instead "
1015                              "of a set of accounts.");
1016             break;
1017         }
1018         gsr->read_only = TRUE;
1019         /* Put up a warning dialog */
1020         args->gsr = gsr;
1021         if (show_dialog)
1022             g_timeout_add (250, gtk_callback_bug_workaround, args); /* 0.25 seconds */
1023     }
1024 }
1025 
1026 static
1027 GtkWidget *
gnc_split_reg2_get_parent(GNCLedgerDisplay2 * ledger)1028 gnc_split_reg2_get_parent (GNCLedgerDisplay2 *ledger)
1029 {
1030     GNCSplitReg2 *gsr =
1031         GNC_SPLIT_REG2 (gnc_ledger_display2_get_user_data (ledger));
1032 
1033     if (gsr == NULL)
1034         return NULL;
1035 
1036     return gsr->window;
1037 }
1038 
1039 static void
gsr2_emit_help_changed(GncTreeViewSplitReg * view,gpointer user_data)1040 gsr2_emit_help_changed (GncTreeViewSplitReg *view, gpointer user_data)
1041 {
1042     gsr2_emit_simple_signal ((GNCSplitReg2*)user_data, "help-changed" );
1043 }
1044 
1045 /* Callback to keep vertical scroll bar in sync */
1046 static void
gsr2_scroll_sync_cb(GncTreeModelSplitReg * model,gpointer user_data)1047 gsr2_scroll_sync_cb (GncTreeModelSplitReg *model, gpointer user_data)
1048 {
1049     GNCSplitReg2 *gsr = user_data;
1050     gint trans_position;
1051 
1052     trans_position = model->position_of_trans_in_full_tlist;
1053 
1054     gtk_adjustment_set_value (gsr->scroll_adj, trans_position);
1055 
1056     gtk_adjustment_set_upper (gsr->scroll_adj, model->number_of_trans_in_full_tlist + 9);
1057 }
1058 
1059 static void
gsr2_scroll_value_changed_cb(GtkAdjustment * adj,gpointer user_data)1060 gsr2_scroll_value_changed_cb (GtkAdjustment *adj, gpointer user_data)
1061 {
1062     GNCSplitReg2 *gsr = user_data;
1063     GncTreeModelSplitReg *model;
1064     gchar *text;
1065     gint  trans_position;
1066 
1067     model = gnc_ledger_display2_get_split_model_register (gsr->ledger);
1068 
1069     trans_position = gtk_adjustment_get_value (adj);
1070 
1071     text = gnc_tree_model_split_reg_get_tooltip (model, trans_position);
1072 
1073     g_object_set (gtk_widget_get_settings (gsr->scroll_bar), "gtk-tooltip-timeout", 2, NULL);
1074 
1075     gtk_widget_set_tooltip_text (gsr->scroll_bar, text);
1076 
1077     g_free (text);
1078 }
1079 
1080 static
1081 gboolean
gsr2_scroll_button_event_cb(GtkWidget * widget,GdkEventButton * event,gpointer user_data)1082 gsr2_scroll_button_event_cb (GtkWidget *widget, GdkEventButton *event, gpointer user_data)
1083 {
1084     GNCSplitReg2 *gsr = user_data;
1085     GncTreeModelSplitReg *model;
1086     gint trans_position;
1087 
1088     model = gnc_ledger_display2_get_split_model_register (gsr->ledger);
1089 
1090     trans_position = gtk_adjustment_get_value (gsr->scroll_adj);
1091 
1092     gnc_tree_model_split_reg_set_current_trans_by_position (model, trans_position);
1093 
1094 //FIXME should we store what it was...
1095     g_object_set (gtk_widget_get_settings (gsr->scroll_bar), "gtk-tooltip-timeout", 500, NULL);
1096 
1097     g_signal_emit_by_name (model, "refresh_trans");
1098 
1099     return FALSE;
1100 }
1101 
1102 static
1103 void
gsr2_emit_simple_signal(GNCSplitReg2 * gsr,const char * sigName)1104 gsr2_emit_simple_signal (GNCSplitReg2 *gsr, const char *sigName)
1105 {
1106     g_signal_emit_by_name( gsr, sigName, NULL );
1107 }
1108 
1109 GncTreeViewSplitReg *
gnc_split_reg2_get_register(GNCSplitReg2 * gsr)1110 gnc_split_reg2_get_register (GNCSplitReg2 *gsr )
1111 {
1112     if ( !gsr )
1113         return NULL;
1114 
1115     return gnc_ledger_display2_get_split_view_register (gsr->ledger);
1116 }
1117 
1118 GtkWidget*
gnc_split_reg2_get_summarybar(GNCSplitReg2 * gsr)1119 gnc_split_reg2_get_summarybar (GNCSplitReg2 *gsr)
1120 {
1121     if (!gsr) return NULL;
1122     return gsr->summarybar;
1123 }
1124 
1125 gboolean
gnc_split_reg2_get_read_only(GNCSplitReg2 * gsr)1126 gnc_split_reg2_get_read_only (GNCSplitReg2 *gsr)
1127 {
1128     g_assert (gsr);
1129 
1130     // reset read_only flag
1131     gsr->read_only = FALSE;
1132     gnc_split_reg2_determine_read_only (gsr, FALSE);
1133     return gsr->read_only;
1134 }
1135 
1136 void
gnc_split_reg2_set_moved_cb(GNCSplitReg2 * gsr,GFunc cb,gpointer cb_data)1137 gnc_split_reg2_set_moved_cb (GNCSplitReg2 *gsr, GFunc cb, gpointer cb_data ) //this works
1138 {
1139     gnc_tree_view_split_reg_set_uiupdate_cb (gnc_ledger_display2_get_split_view_register (gsr->ledger), cb, cb_data);
1140 }
1141