mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
There are a very few left that need deeper study, but this gets rid of most of the noise. For the most part it's just getting rid of extra variables or removing an assignment that is always replaced later but before any reads of the variable. A few are discarded result variables.
1113 lines
34 KiB
C
1113 lines
34 KiB
C
/********************************************************************\
|
|
* gnc-split-reg2.c -- A widget for the common register look-n-feel *
|
|
* Copyright (C) 1997 Robin D. Clark *
|
|
* Copyright (C) 1997-1998 Linas Vepstas <linas@linas.org> *
|
|
* Copyright (C) 1998 Rob Browning <rlb@cs.utexas.edu> *
|
|
* Copyright (C) 1999-2000 Dave Peticolas <dave@krondo.com> *
|
|
* Copyright (C) 2001 Gnumatic, Inc. *
|
|
* Copyright (C) 2002,2006 Joshua Sled <jsled@asynchronous.org> *
|
|
* Copyright (C) 2012 Robert Fewell *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or *
|
|
* modify it under the terms of the GNU General Public License as *
|
|
* published by the Free Software Foundation; either version 2 of *
|
|
* the License, or (at your option) any later version. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License*
|
|
* along with this program; if not, contact: *
|
|
* *
|
|
* Free Software Foundation Voice: +1-617-542-5942 *
|
|
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
|
|
* Boston, MA 02110-1301, USA gnu@gnu.org *
|
|
\********************************************************************/
|
|
|
|
#include <config.h>
|
|
|
|
#include <gtk/gtk.h>
|
|
#include <glib.h>
|
|
#include <glib/gi18n.h>
|
|
|
|
#include "gnc-split-reg2.h"
|
|
#include "gnc-tree-view-split-reg.h"
|
|
#include "gnc-tree-control-split-reg.h"
|
|
#include "gnc-ledger-display2.h"
|
|
|
|
#include "gnc-euro.h"
|
|
#include "gnc-state.h"
|
|
#include "gnc-warnings.h"
|
|
#include "dialog-utils.h"
|
|
|
|
#define STATE_SECTION_REG_PREFIX "Register"
|
|
#define STATE_SECTION_GEN_JOURNAL "General Journal"
|
|
|
|
static QofLogModule log_module = GNC_MOD_GUI;
|
|
|
|
/***** PROTOTYPES ***************************************************/
|
|
void gnc_split_reg2_raise (GNCSplitReg2 *gsr);
|
|
|
|
static GtkWidget* add_summary_label (GtkWidget *summarybar,
|
|
const char *label_str);
|
|
|
|
static void gnc_split_reg2_determine_read_only (GNCSplitReg2 *gsr);
|
|
|
|
static void gnc_split_reg2_determine_account_pr (GNCSplitReg2 *gsr);
|
|
|
|
static GNCPlaceholderType gnc_split_reg2_get_placeholder (GNCSplitReg2 *gsr);
|
|
static GtkWidget *gnc_split_reg2_get_parent (GNCLedgerDisplay2 *ledger);
|
|
|
|
static void gsr2_create_table (GNCSplitReg2 *gsr);
|
|
static void gsr2_setup_table (GNCSplitReg2 *gsr);
|
|
|
|
static void gsr2_setup_status_widgets (GNCSplitReg2 *gsr);
|
|
|
|
static void gsr2_update_summary_label (GtkWidget *label,
|
|
xaccGetBalanceFn getter,
|
|
Account *leader,
|
|
GNCPrintAmountInfo print_info,
|
|
gnc_commodity *cmdty,
|
|
gboolean reverse,
|
|
gboolean euroFlag );
|
|
|
|
static void gsr2_redraw_all_cb (GncTreeViewSplitReg *view, gpointer data);
|
|
|
|
static void gnc_split_reg2_ld_destroy (GNCLedgerDisplay2 *ledger);
|
|
|
|
static Transaction* gsr2_create_balancing_transaction (QofBook *book, Account *account,
|
|
time64 statement_date, gnc_numeric balancing_amount);
|
|
|
|
static void gsr2_emit_simple_signal (GNCSplitReg2 *gsr, const char *sigName);
|
|
static void gsr2_emit_help_changed (GncTreeViewSplitReg *view, gpointer user_data);
|
|
static void gsr2_scroll_value_changed_cb (GtkAdjustment *adj, gpointer user_data);
|
|
static gboolean gsr2_scroll_button_event_cb (GtkWidget *widget, GdkEventButton *event, gpointer user_data);
|
|
static void gsr2_scroll_sync_cb (GncTreeModelSplitReg *model, gpointer user_data);
|
|
|
|
void gnc_split_reg2_style_ledger_cb (GtkWidget *w, gpointer data);
|
|
void gnc_split_reg2_style_auto_ledger_cb (GtkWidget *w, gpointer data);
|
|
void gnc_split_reg2_style_journal_cb (GtkWidget *w, gpointer data);
|
|
void gnc_split_reg2_double_line_cb (GtkWidget *w, gpointer data);
|
|
|
|
void gnc_split_reg2_destroy_cb (GtkWidget *widget, gpointer data);
|
|
|
|
static void gnc_split_reg2_class_init (GNCSplitReg2Class *klass);
|
|
static void gnc_split_reg2_init (GNCSplitReg2 *gsr);
|
|
static void gnc_split_reg2_init2 (GNCSplitReg2 *gsr);
|
|
|
|
static void gnc_split_reg2_sort_changed_cb (GtkTreeSortable *sortable, gpointer user_data);
|
|
|
|
|
|
GType
|
|
gnc_split_reg2_get_type (void)
|
|
{
|
|
static GType gnc_split_reg2_type = 0;
|
|
|
|
if (!gnc_split_reg2_type)
|
|
{
|
|
GTypeInfo type_info =
|
|
{
|
|
sizeof(GNCSplitReg2Class), /* class_size */
|
|
NULL, /* base_init */
|
|
NULL, /* base_finalize */
|
|
(GClassInitFunc)gnc_split_reg2_class_init,
|
|
NULL, /* class_finalize */
|
|
NULL, /* class_data */
|
|
sizeof(GNCSplitReg2), /* */
|
|
0, /* n_preallocs */
|
|
(GInstanceInitFunc)gnc_split_reg2_init,
|
|
};
|
|
|
|
gnc_split_reg2_type = g_type_register_static (GTK_TYPE_BOX,
|
|
"GNCSplitReg2",
|
|
&type_info, 0 );
|
|
}
|
|
|
|
return gnc_split_reg2_type;
|
|
}
|
|
|
|
/* SIGNALS */
|
|
enum
|
|
{
|
|
HELP_CHANGED,
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
static guint gnc_split_reg2_signals[LAST_SIGNAL] = { 0 };
|
|
|
|
static void
|
|
gnc_split_reg2_class_init (GNCSplitReg2Class *klass)
|
|
{
|
|
GObjectClass *object_class;
|
|
|
|
object_class = (GObjectClass*) klass;
|
|
|
|
gnc_split_reg2_signals[HELP_CHANGED] =
|
|
g_signal_new("help-changed",
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (GNCSplitReg2Class, help_changed),
|
|
NULL, NULL,
|
|
g_cclosure_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
|
|
/* Setup the default handlers. */
|
|
klass->help_changed = NULL;
|
|
|
|
}
|
|
|
|
GtkWidget*
|
|
gnc_split_reg2_new (GNCLedgerDisplay2 *ld,
|
|
GtkWindow *parent,
|
|
gint numberOfLines,
|
|
gboolean read_only )
|
|
{
|
|
GNCSplitReg2 *gsrToRet;
|
|
|
|
ENTER("ld=%p, parent=%p, numberOfLines=%d, read_only=%s",
|
|
ld, parent, numberOfLines, read_only ? "TRUE" : "FALSE");
|
|
|
|
gsrToRet = g_object_new (gnc_split_reg2_get_type(), NULL);
|
|
|
|
gsrToRet->numRows = numberOfLines;
|
|
gsrToRet->read_only = read_only;
|
|
|
|
gsrToRet->ledger = ld;
|
|
gsrToRet->window = GTK_WIDGET (parent);
|
|
|
|
gnc_split_reg2_init2 (gsrToRet);
|
|
|
|
LEAVE("%p", gsrToRet);
|
|
return GTK_WIDGET (gsrToRet);
|
|
}
|
|
|
|
static void
|
|
gnc_split_reg2_init (GNCSplitReg2 *gsr)
|
|
{
|
|
gtk_orientable_set_orientation (GTK_ORIENTABLE(gsr), GTK_ORIENTATION_VERTICAL);
|
|
|
|
gsr->numRows = 10;
|
|
gsr->read_only = FALSE;
|
|
|
|
g_signal_connect (gsr, "destroy",
|
|
G_CALLBACK (gnc_split_reg2_destroy_cb), gsr );
|
|
}
|
|
|
|
static void
|
|
gnc_split_reg2_init2 (GNCSplitReg2 *gsr)
|
|
{
|
|
if (!gsr) return;
|
|
|
|
gnc_split_reg2_determine_read_only (gsr);
|
|
|
|
gnc_split_reg2_determine_account_pr (gsr);
|
|
|
|
gsr2_setup_status_widgets (gsr);
|
|
/* ordering is important here... setup_status before create_table */
|
|
|
|
gsr2_create_table (gsr);
|
|
gsr2_setup_table (gsr);
|
|
|
|
}
|
|
|
|
static
|
|
void
|
|
gsr2_setup_table (GNCSplitReg2 *gsr)
|
|
{
|
|
// GncTreeModelSplitReg *model;
|
|
|
|
ENTER("gsr=%p", gsr);
|
|
|
|
// This dose not do any thing
|
|
// model = gnc_ledger_display2_get_split_model_register (gsr->ledger);
|
|
|
|
LEAVE(" ");
|
|
}
|
|
|
|
static
|
|
void
|
|
gsr2_create_table (GNCSplitReg2 *gsr)
|
|
{
|
|
GncTreeViewSplitReg *view;
|
|
GncTreeModelSplitReg *model;
|
|
GtkTreeModel *s_model;
|
|
GtkWidget *scrolled_window;
|
|
GtkTreeViewColumn *col;
|
|
GNCLedgerDisplay2Type ledger_type;
|
|
GtkWidget *hbox;
|
|
gdouble num_of_trans;
|
|
|
|
gchar *state_section;
|
|
GKeyFile *state_file = gnc_state_get_current();
|
|
const GncGUID * guid;
|
|
Account * account;
|
|
|
|
account = gnc_ledger_display2_leader (gsr->ledger);
|
|
guid = xaccAccountGetGUID (account);
|
|
|
|
ENTER("create table gsr=%p", gsr);
|
|
|
|
gnc_ledger_display2_set_user_data (gsr->ledger, (gpointer)gsr);
|
|
gnc_ledger_display2_set_handlers (gsr->ledger,
|
|
gnc_split_reg2_ld_destroy,
|
|
gnc_split_reg2_get_parent);
|
|
|
|
model = gnc_ledger_display2_get_split_model_register (gsr->ledger);
|
|
view = gnc_tree_view_split_reg_new_with_model (model);
|
|
g_object_unref (G_OBJECT (model));
|
|
|
|
/* State_section is used to store per register state: column widths, sort order,... */
|
|
ledger_type = gnc_ledger_display2_type (gsr->ledger);
|
|
if (ledger_type == LD2_GL && model->type == GENERAL_JOURNAL2)
|
|
state_section = g_strdup (STATE_SECTION_GEN_JOURNAL);
|
|
else if (ledger_type == LD2_SUBACCOUNT)
|
|
{
|
|
gchar guidstr[GUID_ENCODING_LENGTH+1];
|
|
guid_to_string_buff (guid, guidstr);
|
|
state_section = g_strconcat (STATE_SECTION_REG_PREFIX, " ", guidstr, " w/subaccounts", NULL);
|
|
}
|
|
else
|
|
{
|
|
gchar guidstr[GUID_ENCODING_LENGTH+1];
|
|
guid_to_string_buff (guid, guidstr);
|
|
state_section = g_strconcat (STATE_SECTION_REG_PREFIX, " ", guidstr, NULL);
|
|
}
|
|
g_object_set (G_OBJECT (view), "state-section", state_section,
|
|
"show-column-menu", FALSE, NULL);
|
|
|
|
// Create a hbox for treeview and scrollbar.
|
|
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
|
|
gtk_box_set_homogeneous (GTK_BOX (hbox), FALSE);
|
|
gtk_widget_show (hbox);
|
|
|
|
scrolled_window = gtk_scrolled_window_new (NULL, NULL);
|
|
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
|
|
GTK_POLICY_AUTOMATIC,
|
|
GTK_POLICY_AUTOMATIC);
|
|
|
|
gtk_widget_show (scrolled_window);
|
|
|
|
gtk_box_pack_start (GTK_BOX (gsr), hbox, TRUE, TRUE, 0);
|
|
|
|
num_of_trans = model->number_of_trans_in_full_tlist - 1;
|
|
|
|
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));
|
|
|
|
gsr->scroll_bar = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, GTK_ADJUSTMENT (gsr->scroll_adj));
|
|
gtk_widget_show (gsr->scroll_bar);
|
|
|
|
gtk_box_pack_start (GTK_BOX (hbox), gsr->scroll_bar, FALSE, FALSE, 2);
|
|
gtk_box_pack_start (GTK_BOX (hbox), scrolled_window, TRUE, TRUE, 0);
|
|
|
|
gnc_ledger_display2_set_split_view_register (gsr->ledger, view);
|
|
|
|
/* Synchronize model state with view state
|
|
* (needed to properly set up the internal query) */
|
|
|
|
/* Restore the sort depth from saved state */
|
|
model->sort_depth = g_key_file_get_integer (state_file, state_section, "sort_depth", NULL);
|
|
g_free(state_section);
|
|
|
|
s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
|
|
if (s_model)
|
|
{
|
|
gint sort_col;
|
|
GtkSortType type;
|
|
|
|
if (gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (s_model), &sort_col, &type))
|
|
{
|
|
model->sort_col = sort_col;
|
|
model->sort_direction = type;
|
|
}
|
|
}
|
|
|
|
gnc_tree_view_configure_columns (GNC_TREE_VIEW (view));
|
|
|
|
if (ledger_type == LD2_GL && model->type == GENERAL_JOURNAL2)
|
|
gnc_tree_view_set_show_column_menu (GNC_TREE_VIEW (view), TRUE);
|
|
else
|
|
gnc_tree_view_set_show_column_menu (GNC_TREE_VIEW (view), FALSE);
|
|
|
|
/* This column gets all the free space */
|
|
gnc_tree_view_expand_columns (GNC_TREE_VIEW (view), "descnotes", NULL);
|
|
|
|
/* This sets the status color column, 4 is the minimum */
|
|
col = gnc_tree_view_find_column_by_name (GNC_TREE_VIEW (view), "status");
|
|
if (col != NULL)
|
|
g_object_set (G_OBJECT(col),
|
|
"resizable", FALSE,
|
|
"sizing", GTK_TREE_VIEW_COLUMN_FIXED,
|
|
"fixed-width", 4,
|
|
NULL);
|
|
|
|
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (view), TRUE);
|
|
gtk_widget_show (GTK_WIDGET (view));
|
|
|
|
gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (view));
|
|
gtk_widget_show (GTK_WIDGET (gsr));
|
|
|
|
/* Should this be read only */
|
|
gnc_tree_view_split_reg_set_read_only (view, gsr->read_only);
|
|
|
|
/* This tells the ledger that we have a valid tree view */
|
|
gnc_ledger_display2_set_split_view_refresh (gsr->ledger, TRUE);
|
|
|
|
/* This triggers the update of the summary bar */
|
|
g_signal_connect_after (model, "refresh_status_bar",
|
|
G_CALLBACK (gsr2_redraw_all_cb), gsr); //this works
|
|
|
|
// This will keep scrollbar in sync.
|
|
g_signal_connect (model, "scroll_sync",
|
|
G_CALLBACK (gsr2_scroll_sync_cb), gsr);
|
|
|
|
/* This triggers the update of the help text */
|
|
g_signal_connect (view, "help_signal",
|
|
G_CALLBACK (gsr2_emit_help_changed), gsr); // this works
|
|
|
|
gsr2_scroll_value_changed_cb (GTK_ADJUSTMENT (gsr->scroll_adj), gsr);
|
|
|
|
/* This triggers the tooltip to change when scrolling */
|
|
g_signal_connect (gsr->scroll_adj, "value-changed",
|
|
G_CALLBACK (gsr2_scroll_value_changed_cb), gsr); // this works
|
|
|
|
/* This triggers the model update when mouse button released */
|
|
g_signal_connect (gsr->scroll_bar, "button-release-event",
|
|
G_CALLBACK (gsr2_scroll_button_event_cb), gsr);
|
|
|
|
// Connect a call back to update the sort settings.
|
|
g_signal_connect (GTK_TREE_SORTABLE (s_model), "sort-column-changed",
|
|
G_CALLBACK (gnc_split_reg2_sort_changed_cb), gsr);
|
|
|
|
LEAVE(" ");
|
|
}
|
|
|
|
static
|
|
void
|
|
gsr2_setup_status_widgets (GNCSplitReg2 *gsr)
|
|
{
|
|
GncTreeModelSplitReg *model;
|
|
gboolean use_double_line;
|
|
|
|
model = gnc_ledger_display2_get_split_model_register (gsr->ledger);
|
|
use_double_line = gnc_ledger_display2_default_double_line (gsr->ledger);
|
|
|
|
/* be sure to initialize the gui elements */
|
|
gnc_tree_model_split_reg_config (model, model->type, model->style, use_double_line);
|
|
}
|
|
|
|
void
|
|
gnc_split_reg2_destroy_cb (GtkWidget *widget, gpointer data)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Jump to split.
|
|
**/
|
|
void
|
|
gnc_split_reg2_jump_to_split (GNCSplitReg2 *gsr, Split *split)
|
|
{
|
|
GncTreeViewSplitReg *view;
|
|
|
|
if (gsr == NULL)
|
|
return;
|
|
|
|
if (split == NULL)
|
|
return;
|
|
|
|
view = gnc_ledger_display2_get_split_view_register (gsr->ledger);
|
|
|
|
gnc_tree_control_split_reg_jump_to (view, NULL, split, FALSE);
|
|
}
|
|
|
|
|
|
/**
|
|
* Move the cursor to the split in the non-blank amount column.
|
|
**/
|
|
void
|
|
gnc_split_reg2_jump_to_split_amount (GNCSplitReg2 *gsr, Split *split)
|
|
{
|
|
GncTreeViewSplitReg *view;
|
|
|
|
if (gsr == NULL)
|
|
return;
|
|
|
|
if (split == NULL)
|
|
return;
|
|
|
|
view = gnc_ledger_display2_get_split_view_register (gsr->ledger);
|
|
|
|
gnc_tree_control_split_reg_jump_to (view, NULL, split, TRUE);
|
|
}
|
|
|
|
/**
|
|
* Raise an existing register window to the front.
|
|
**/
|
|
void
|
|
gnc_split_reg2_raise (GNCSplitReg2 *gsr)
|
|
{
|
|
if (gsr == NULL)
|
|
return;
|
|
|
|
if (gsr->window == NULL)
|
|
return;
|
|
|
|
gtk_window_present (GTK_WINDOW (gsr->window));
|
|
}
|
|
|
|
|
|
/**
|
|
* Duplicate-code reduction function; retreives, formats and updates the
|
|
* GtkLabel with the given amount.
|
|
**/
|
|
static
|
|
void
|
|
gsr2_update_summary_label (GtkWidget *label,
|
|
xaccGetBalanceFn getter,
|
|
Account *leader,
|
|
GNCPrintAmountInfo print_info,
|
|
gnc_commodity *cmdty,
|
|
gboolean reverse,
|
|
gboolean euroFlag)
|
|
{
|
|
gnc_numeric amount;
|
|
char string[256];
|
|
|
|
if ( label == NULL )
|
|
return;
|
|
|
|
amount = (*getter)( leader );
|
|
|
|
if ( reverse )
|
|
{
|
|
amount = gnc_numeric_neg( amount );
|
|
}
|
|
|
|
xaccSPrintAmount( string, amount, print_info );
|
|
|
|
if ( euroFlag )
|
|
{
|
|
strcat( string, " / " );
|
|
xaccSPrintAmount( string + strlen( string ),
|
|
gnc_convert_to_euro( cmdty, amount ),
|
|
gnc_commodity_print_info( gnc_get_euro(), TRUE ) );
|
|
}
|
|
|
|
gnc_set_label_color( label, amount );
|
|
gtk_label_set_text( GTK_LABEL(label), string );
|
|
}
|
|
|
|
static
|
|
void
|
|
gsr2_redraw_all_cb (GncTreeViewSplitReg *view, gpointer user_data)
|
|
{
|
|
GNCSplitReg2 *gsr = user_data;
|
|
gnc_commodity * commodity;
|
|
GNCPrintAmountInfo print_info;
|
|
gnc_numeric amount = gnc_numeric_zero();
|
|
Account *leader;
|
|
gboolean reverse;
|
|
gboolean euro;
|
|
|
|
if ( gsr->summarybar == NULL )
|
|
return;
|
|
|
|
leader = gnc_ledger_display2_leader( gsr->ledger );
|
|
|
|
commodity = xaccAccountGetCommodity( leader );
|
|
|
|
/* no EURO converson, if account is already EURO or no EURO currency */
|
|
if (commodity != NULL)
|
|
euro = (gnc_is_euro_currency( commodity ) &&
|
|
(strncasecmp(gnc_commodity_get_mnemonic(commodity), "EUR", 3)));
|
|
else
|
|
euro = FALSE;
|
|
|
|
print_info = gnc_account_print_info( leader, TRUE );
|
|
reverse = gnc_reverse_balance( leader );
|
|
|
|
gsr2_update_summary_label( gsr->balance_label,
|
|
xaccAccountGetPresentBalance,
|
|
leader, print_info, commodity, reverse, euro );
|
|
gsr2_update_summary_label( gsr->cleared_label,
|
|
xaccAccountGetClearedBalance,
|
|
leader, print_info, commodity, reverse, euro );
|
|
gsr2_update_summary_label( gsr->reconciled_label,
|
|
xaccAccountGetReconciledBalance,
|
|
leader, print_info, commodity, reverse, euro );
|
|
gsr2_update_summary_label( gsr->future_label,
|
|
xaccAccountGetBalance,
|
|
leader, print_info, commodity, reverse, euro );
|
|
gsr2_update_summary_label( gsr->projectedminimum_label,
|
|
xaccAccountGetProjectedMinimumBalance,
|
|
leader, print_info, commodity, reverse, euro );
|
|
|
|
/* Print the summary share amount */
|
|
if (gsr->shares_label != NULL)
|
|
{
|
|
char string[256];
|
|
print_info = gnc_account_print_info( leader, TRUE );
|
|
amount = xaccAccountGetBalance( leader );
|
|
if ( reverse )
|
|
amount = gnc_numeric_neg( amount );
|
|
xaccSPrintAmount( string, amount, print_info );
|
|
gnc_set_label_color( gsr->shares_label, amount );
|
|
gtk_label_set_text( GTK_LABEL(gsr->shares_label), string );
|
|
}
|
|
|
|
/* Print the summary share value */
|
|
if (gsr->value_label != NULL)
|
|
{
|
|
char string[256];
|
|
gnc_commodity *currency = gnc_default_currency ();
|
|
print_info = gnc_commodity_print_info (currency, TRUE);
|
|
xaccSPrintAmount (string, amount, print_info);
|
|
gnc_set_label_color (gsr->value_label, amount);
|
|
gtk_label_set_text (GTK_LABEL (gsr->value_label), string);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
gnc_split_reg2_ld_destroy (GNCLedgerDisplay2 *ledger)
|
|
{
|
|
gnc_ledger_display2_set_user_data (ledger, NULL);
|
|
}
|
|
|
|
|
|
/* ########################### Handlers ############################### */
|
|
|
|
void
|
|
gnc_split_reg2_balancing_entry (GNCSplitReg2 *gsr, Account *account,
|
|
time64 statement_date, gnc_numeric balancing_amount) // this works
|
|
{
|
|
GncTreeViewSplitReg *view;
|
|
Transaction *transaction;
|
|
Split *split;
|
|
|
|
view = gnc_ledger_display2_get_split_view_register (gsr->ledger);
|
|
|
|
// create transaction
|
|
transaction = gsr2_create_balancing_transaction (gnc_get_current_book(),
|
|
account, statement_date, balancing_amount);
|
|
|
|
// jump to transaction
|
|
split = xaccTransFindSplitByAccount (transaction, account);
|
|
if (split == NULL)
|
|
{
|
|
// default behaviour: jump to blank split
|
|
g_warning("gsr2_create_balancing_transaction failed");
|
|
gnc_tree_control_split_reg_jump_to_blank (view);
|
|
}
|
|
else
|
|
{
|
|
// goto balancing transaction
|
|
gnc_tree_control_split_reg_jump_to (view, NULL, split, FALSE);
|
|
}
|
|
}
|
|
|
|
static Transaction*
|
|
gsr2_create_balancing_transaction (QofBook *book, Account *account,
|
|
time64 statement_date, gnc_numeric balancing_amount)
|
|
{
|
|
Transaction *trans;
|
|
Split *split;
|
|
|
|
if (!account)
|
|
return NULL;
|
|
if (gnc_numeric_zero_p (balancing_amount))
|
|
return NULL;
|
|
|
|
xaccAccountBeginEdit (account);
|
|
|
|
trans = xaccMallocTransaction (book);
|
|
|
|
xaccTransBeginEdit (trans);
|
|
|
|
// fill Transaction
|
|
xaccTransSetCurrency (trans, gnc_account_or_default_currency (account, NULL));
|
|
xaccTransSetDatePostedSecsNormalized (trans, statement_date);
|
|
xaccTransSetDescription (trans, _("Balancing entry from reconciliation"));
|
|
|
|
// 1. Split
|
|
split = xaccMallocSplit (book);
|
|
xaccTransAppendSplit (trans, split);
|
|
xaccAccountInsertSplit (account, split);
|
|
xaccSplitSetAmount (split, balancing_amount);
|
|
xaccSplitSetValue (split, balancing_amount);
|
|
|
|
// 2. Split (no account is defined: split goes to orphan account)
|
|
split = xaccMallocSplit (book);
|
|
xaccTransAppendSplit (trans, split);
|
|
|
|
balancing_amount = gnc_numeric_neg (balancing_amount);
|
|
xaccSplitSetAmount (split, balancing_amount);
|
|
xaccSplitSetValue (split, balancing_amount);
|
|
|
|
xaccTransCommitEdit (trans);
|
|
xaccAccountCommitEdit (account);
|
|
return trans;
|
|
}
|
|
|
|
|
|
/* Sort changed callback */
|
|
static void
|
|
gnc_split_reg2_sort_changed_cb (GtkTreeSortable *sortable, gpointer user_data)
|
|
{
|
|
GNCSplitReg2 *gsr = user_data;
|
|
GncTreeViewSplitReg *view;
|
|
GncTreeModelSplitReg *model;
|
|
GtkSortType type;
|
|
gint sortcol;
|
|
gint sort_depth;
|
|
const gchar *state_section;
|
|
GKeyFile *state_file = gnc_state_get_current();
|
|
|
|
gtk_tree_sortable_get_sort_column_id (sortable, &sortcol, &type);
|
|
ENTER("sortcol is %d", sortcol);
|
|
|
|
view = gnc_ledger_display2_get_split_view_register (gsr->ledger);
|
|
model = gnc_ledger_display2_get_split_model_register (gsr->ledger);
|
|
|
|
sort_depth = gnc_tree_view_reg_get_selected_row_depth (view);
|
|
if (sort_depth != 0)
|
|
model->sort_depth = sort_depth;
|
|
|
|
model->sort_col = sortcol;
|
|
model->sort_direction = type;
|
|
|
|
/* Save the sort depth state */
|
|
state_section = gnc_tree_view_get_state_section (GNC_TREE_VIEW (view));
|
|
g_key_file_set_integer (state_file, state_section, "sort_depth", model->sort_depth);
|
|
|
|
LEAVE("m_sort_col %d, m_sort_direction is %d m_sort_depth is %d", model->sort_col, model->sort_direction, model->sort_depth);
|
|
|
|
if (sortcol != -1)
|
|
gnc_ledger_display2_refresh (gsr->ledger);
|
|
}
|
|
/* ############################## End Handlers ############################ */
|
|
|
|
void
|
|
gnc_split_reg2_change_style (GNCSplitReg2 *gsr, SplitRegisterStyle2 style)
|
|
{
|
|
GncTreeModelSplitReg *model = gnc_ledger_display2_get_split_model_register (gsr->ledger);
|
|
|
|
if (style == model->style)
|
|
return;
|
|
|
|
gnc_tree_model_split_reg_config (model, model->type, style, model->use_double_line);
|
|
|
|
// This will re-display the view.
|
|
gnc_tree_view_split_reg_set_format (gnc_ledger_display2_get_split_view_register (gsr->ledger));
|
|
}
|
|
|
|
void
|
|
gnc_split_reg2_style_ledger_cb (GtkWidget *w, gpointer data)
|
|
{
|
|
GNCSplitReg2 *gsr = data;
|
|
|
|
if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (w)))
|
|
return;
|
|
|
|
gnc_split_reg2_change_style (gsr, REG2_STYLE_LEDGER);
|
|
}
|
|
|
|
void
|
|
gnc_split_reg2_style_auto_ledger_cb (GtkWidget *w, gpointer data)
|
|
{
|
|
GNCSplitReg2 *gsr = data;
|
|
|
|
if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (w)))
|
|
return;
|
|
|
|
gnc_split_reg2_change_style (gsr, REG2_STYLE_AUTO_LEDGER);
|
|
}
|
|
|
|
void
|
|
gnc_split_reg2_style_journal_cb (GtkWidget *w, gpointer data)
|
|
{
|
|
GNCSplitReg2 *gsr = data;
|
|
|
|
if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (w)))
|
|
return;
|
|
|
|
gnc_split_reg2_change_style (gsr, REG2_STYLE_JOURNAL);
|
|
}
|
|
|
|
void
|
|
gnc_split_reg2_double_line_cb (GtkWidget *w, gpointer data)
|
|
{
|
|
GNCSplitReg2 *gsr = data;
|
|
GncTreeModelSplitReg *model = gnc_ledger_display2_get_split_model_register (gsr->ledger);
|
|
gboolean use_double_line;
|
|
|
|
use_double_line = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (w));
|
|
if (use_double_line == model->use_double_line)
|
|
return;
|
|
|
|
gnc_tree_model_split_reg_config (model, model->type, model->style, use_double_line);
|
|
|
|
// This will re-display the view.
|
|
gnc_tree_view_split_reg_set_format (gnc_ledger_display2_get_split_view_register (gsr->ledger));
|
|
}
|
|
|
|
static
|
|
GtkWidget*
|
|
add_summary_label (GtkWidget *summarybar, const char *label_str)
|
|
{
|
|
GtkWidget *hbox;
|
|
GtkWidget *label;
|
|
|
|
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
|
|
gtk_box_set_homogeneous (GTK_BOX (hbox), FALSE);
|
|
gtk_box_pack_start (GTK_BOX (summarybar), hbox, FALSE, FALSE, 5);
|
|
|
|
label = gtk_label_new (label_str);
|
|
gnc_label_set_alignment (label, 1.0, 0.5);
|
|
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
|
|
|
|
label = gtk_label_new ("");
|
|
gnc_label_set_alignment (label, 1.0, 0.5);
|
|
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
|
|
|
|
return label;
|
|
}
|
|
|
|
GtkWidget *
|
|
gnc_split_reg2_create_summary_bar (GNCSplitReg2 *gsr)
|
|
{
|
|
GtkWidget *summarybar;
|
|
|
|
gsr->cleared_label = NULL;
|
|
gsr->balance_label = NULL;
|
|
gsr->reconciled_label = NULL;
|
|
gsr->future_label = NULL;
|
|
gsr->projectedminimum_label = NULL;
|
|
gsr->shares_label = NULL;
|
|
gsr->value_label = NULL;
|
|
|
|
if (gnc_ledger_display2_type (gsr->ledger) >= LD2_SUBACCOUNT)
|
|
{
|
|
gsr->summarybar = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
summarybar = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
|
|
gtk_box_set_homogeneous (GTK_BOX (summarybar), FALSE);
|
|
|
|
if (!xaccAccountIsPriced(gnc_ledger_display2_leader(gsr->ledger)))
|
|
{
|
|
gsr->balance_label = add_summary_label (summarybar, _("Present:"));
|
|
gsr->future_label = add_summary_label (summarybar, _("Future:"));
|
|
gsr->cleared_label = add_summary_label (summarybar, _("Cleared:"));
|
|
gsr->reconciled_label = add_summary_label (summarybar, _("Reconciled:"));
|
|
gsr->projectedminimum_label = add_summary_label (summarybar, _("Projected Minimum:"));
|
|
}
|
|
else
|
|
{
|
|
gsr->shares_label = add_summary_label (summarybar, _("Shares:"));
|
|
gsr->value_label = add_summary_label (summarybar, _("Current Value:"));
|
|
}
|
|
|
|
gsr->summarybar = summarybar;
|
|
|
|
/* Force the first update */
|
|
gsr2_redraw_all_cb (NULL, gsr);
|
|
return gsr->summarybar;
|
|
}
|
|
|
|
/**
|
|
* Opens up a register window for a group of Accounts.
|
|
* @param gsr the register window instance
|
|
* @return A GNCPlaceholderType indicating presence and type of placeholder
|
|
* accounts
|
|
**/
|
|
static
|
|
GNCPlaceholderType
|
|
gnc_split_reg2_get_placeholder (GNCSplitReg2 *gsr)
|
|
{
|
|
Account *leader;
|
|
GncTreeModelSplitReg *model;
|
|
gboolean single_account;
|
|
|
|
if (gsr == NULL)
|
|
return PLACEHOLDER_NONE;
|
|
|
|
model = gnc_ledger_display2_get_split_model_register (gsr->ledger);
|
|
|
|
switch (model->type)
|
|
{
|
|
case GENERAL_JOURNAL2:
|
|
case INCOME_LEDGER2:
|
|
case PORTFOLIO_LEDGER2:
|
|
case SEARCH_LEDGER2:
|
|
single_account = FALSE;
|
|
break;
|
|
default:
|
|
single_account = TRUE;
|
|
break;
|
|
}
|
|
|
|
leader = gnc_ledger_display2_leader (gsr->ledger);
|
|
|
|
if (leader == NULL)
|
|
return PLACEHOLDER_NONE;
|
|
if (single_account)
|
|
{
|
|
if (xaccAccountGetPlaceholder (leader))
|
|
return PLACEHOLDER_THIS;
|
|
return PLACEHOLDER_NONE;
|
|
}
|
|
return xaccAccountGetDescendantPlaceholder (leader);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* @see gtk_callback_bug_workaround
|
|
**/
|
|
typedef struct dialog_args
|
|
{
|
|
GNCSplitReg2 *gsr;
|
|
gchar *string;
|
|
} dialog_args;
|
|
|
|
|
|
/* This Register is an Account Payable / Receivable one */
|
|
static
|
|
gboolean
|
|
gsr2_determine_account_pr_dialog (gpointer argp)
|
|
{
|
|
dialog_args *args = argp;
|
|
GtkWidget *dialog;
|
|
|
|
const char *title = _("Account Payable / Receivable Register");
|
|
const char *message =
|
|
_("The register displayed is for Account Payable or Account Receivable. "
|
|
"Changing the entries may cause harm, please use the business "
|
|
"options to change the entries.");
|
|
|
|
dialog = gtk_message_dialog_new (GTK_WINDOW (args->gsr->window),
|
|
GTK_DIALOG_DESTROY_WITH_PARENT,
|
|
GTK_MESSAGE_WARNING,
|
|
GTK_BUTTONS_CLOSE,
|
|
"%s", title);
|
|
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
|
|
"%s", message);
|
|
|
|
gnc_dialog_run (GTK_DIALOG (dialog), GNC_PREF_WARN_REG_IS_ACCT_PAY_REC);
|
|
gtk_widget_destroy (dialog);
|
|
g_free (args);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/* This Register is an Account Payable / Receivable one */
|
|
static void
|
|
gnc_split_reg2_determine_account_pr (GNCSplitReg2 *gsr)
|
|
{
|
|
dialog_args *args;
|
|
GncTreeModelSplitReg *model;
|
|
|
|
model = gnc_ledger_display2_get_split_model_register (gsr->ledger);
|
|
|
|
if (model->type != PAYABLE_REGISTER2 && model->type != RECEIVABLE_REGISTER2)
|
|
return;
|
|
|
|
/* Put up a warning dialog */
|
|
args = g_malloc (sizeof (dialog_args));
|
|
args->string = _(""); /* FIXME: No string for dialog. */
|
|
args->gsr = gsr;
|
|
g_timeout_add (250, gsr2_determine_account_pr_dialog, args); /* 0.25 seconds */
|
|
}
|
|
|
|
|
|
/**
|
|
* Gtk has occasional problems with performing function as part of a
|
|
* callback. This routine gets called via a timer callback to get it out of
|
|
* the data path with the problem.
|
|
**/
|
|
static
|
|
gboolean
|
|
gtk_callback_bug_workaround (gpointer argp)
|
|
{
|
|
dialog_args *args = argp;
|
|
const gchar *read_only = _("This account register is read-only.");
|
|
GtkWidget *dialog;
|
|
|
|
dialog = gtk_message_dialog_new (GTK_WINDOW(args->gsr->window),
|
|
GTK_DIALOG_DESTROY_WITH_PARENT,
|
|
GTK_MESSAGE_WARNING,
|
|
GTK_BUTTONS_CLOSE,
|
|
"%s", read_only);
|
|
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
|
|
"%s", args->string);
|
|
gnc_dialog_run (GTK_DIALOG (dialog), GNC_PREF_WARN_REG_IS_READ_ONLY);
|
|
gtk_widget_destroy (dialog);
|
|
g_free (args);
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* Determines whether this register window should be read-only.
|
|
**/
|
|
static
|
|
void
|
|
gnc_split_reg2_determine_read_only (GNCSplitReg2 *gsr) //this works
|
|
{
|
|
|
|
if (qof_book_is_readonly (gnc_get_current_book()))
|
|
{
|
|
/* Is the book read-only? Then for sure also make this register
|
|
read-only. */
|
|
gsr->read_only = TRUE;
|
|
}
|
|
|
|
if (!gsr->read_only)
|
|
{
|
|
dialog_args *args = g_malloc (sizeof (dialog_args));
|
|
|
|
switch (gnc_split_reg2_get_placeholder (gsr))
|
|
{
|
|
case PLACEHOLDER_NONE:
|
|
/* stay as false. */
|
|
g_free (args);
|
|
return;
|
|
|
|
case PLACEHOLDER_THIS:
|
|
args->string = _("This account may not be edited. If you want "
|
|
"to edit transactions in this register, please "
|
|
"open the account options and turn off the "
|
|
"placeholder checkbox.");
|
|
break;
|
|
|
|
default:
|
|
args->string = _("One of the sub-accounts selected may not be "
|
|
"edited. If you want to edit transactions in "
|
|
"this register, please open the sub-account "
|
|
"options and turn off the placeholder checkbox. "
|
|
"You may also open an individual account instead "
|
|
"of a set of accounts.");
|
|
break;
|
|
}
|
|
gsr->read_only = TRUE;
|
|
/* Put up a warning dialog */
|
|
args->gsr = gsr;
|
|
g_timeout_add (250, gtk_callback_bug_workaround, args); /* 0.25 seconds */
|
|
}
|
|
}
|
|
|
|
static
|
|
GtkWidget *
|
|
gnc_split_reg2_get_parent (GNCLedgerDisplay2 *ledger)
|
|
{
|
|
GNCSplitReg2 *gsr =
|
|
GNC_SPLIT_REG2 (gnc_ledger_display2_get_user_data (ledger));
|
|
|
|
if (gsr == NULL)
|
|
return NULL;
|
|
|
|
return gsr->window;
|
|
}
|
|
|
|
static void
|
|
gsr2_emit_help_changed (GncTreeViewSplitReg *view, gpointer user_data)
|
|
{
|
|
gsr2_emit_simple_signal ((GNCSplitReg2*)user_data, "help-changed" );
|
|
}
|
|
|
|
/* Callback to keep vertical scroll bar in sync */
|
|
static void
|
|
gsr2_scroll_sync_cb (GncTreeModelSplitReg *model, gpointer user_data)
|
|
{
|
|
GNCSplitReg2 *gsr = user_data;
|
|
gint trans_position;
|
|
|
|
trans_position = model->position_of_trans_in_full_tlist;
|
|
|
|
gtk_adjustment_set_value (gsr->scroll_adj, trans_position);
|
|
|
|
gtk_adjustment_set_upper (gsr->scroll_adj, model->number_of_trans_in_full_tlist + 9);
|
|
}
|
|
|
|
static void
|
|
gsr2_scroll_value_changed_cb (GtkAdjustment *adj, gpointer user_data)
|
|
{
|
|
GNCSplitReg2 *gsr = user_data;
|
|
GncTreeModelSplitReg *model;
|
|
gchar *text;
|
|
gint trans_position;
|
|
|
|
model = gnc_ledger_display2_get_split_model_register (gsr->ledger);
|
|
|
|
trans_position = gtk_adjustment_get_value (adj);
|
|
|
|
text = gnc_tree_model_split_reg_get_tooltip (model, trans_position);
|
|
|
|
g_object_set (gtk_widget_get_settings (gsr->scroll_bar), "gtk-tooltip-timeout", 2, NULL);
|
|
|
|
gtk_widget_set_tooltip_text (gsr->scroll_bar, text);
|
|
|
|
g_free (text);
|
|
}
|
|
|
|
static
|
|
gboolean
|
|
gsr2_scroll_button_event_cb (GtkWidget *widget, GdkEventButton *event, gpointer user_data)
|
|
{
|
|
GNCSplitReg2 *gsr = user_data;
|
|
GncTreeModelSplitReg *model;
|
|
gint trans_position;
|
|
|
|
model = gnc_ledger_display2_get_split_model_register (gsr->ledger);
|
|
|
|
trans_position = gtk_adjustment_get_value (gsr->scroll_adj);
|
|
|
|
gnc_tree_model_split_reg_set_current_trans_by_position (model, trans_position);
|
|
|
|
//FIXME should we store what it was...
|
|
g_object_set (gtk_widget_get_settings (gsr->scroll_bar), "gtk-tooltip-timeout", 500, NULL);
|
|
|
|
g_signal_emit_by_name (model, "refresh_trans");
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static
|
|
void
|
|
gsr2_emit_simple_signal (GNCSplitReg2 *gsr, const char *sigName)
|
|
{
|
|
g_signal_emit_by_name( gsr, sigName, NULL );
|
|
}
|
|
|
|
GncTreeViewSplitReg *
|
|
gnc_split_reg2_get_register (GNCSplitReg2 *gsr )
|
|
{
|
|
if ( !gsr )
|
|
return NULL;
|
|
|
|
return gnc_ledger_display2_get_split_view_register (gsr->ledger);
|
|
}
|
|
|
|
GtkWidget*
|
|
gnc_split_reg2_get_summarybar (GNCSplitReg2 *gsr)
|
|
{
|
|
if (!gsr) return NULL;
|
|
return gsr->summarybar;
|
|
}
|
|
|
|
gboolean
|
|
gnc_split_reg2_get_read_only (GNCSplitReg2 *gsr)
|
|
{
|
|
g_assert (gsr);
|
|
return gsr->read_only;
|
|
}
|
|
|
|
void
|
|
gnc_split_reg2_set_moved_cb (GNCSplitReg2 *gsr, GFunc cb, gpointer cb_data ) //this works
|
|
{
|
|
gnc_tree_view_split_reg_set_uiupdate_cb (gnc_ledger_display2_get_split_view_register (gsr->ledger), cb, cb_data);
|
|
}
|