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