Files
gnucash/gnucash/gnome/gnc-split-reg.c
Robert Fewell d2af5d703b Complete the change from using associate in the source files
This commit changes some further use of associate and derivates in the
source files to doclink to make it more consistent.
2020-09-13 13:05:46 +01:00

2620 lines
84 KiB
C

/********************************************************************\
* gnc-split-reg.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> *
* *
* 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/gi18n.h>
#include <time.h>
#include "gnc-split-reg.h"
#include "Account.h"
#include "qof.h"
#include "SX-book.h"
#include "dialog-account.h"
#include "dialog-doclink.h"
#include "dialog-doclink-utils.h"
#include "dialog-sx-editor.h"
#include "dialog-sx-from-trans.h"
#include "gnc-component-manager.h"
#include "gnc-date-edit.h"
#include "gnc-engine.h"
#include "gnc-euro.h"
#include "gnc-prefs.h"
#include "gnc-gui-query.h"
#include "gnc-gnome-utils.h"
#include "gnc-ledger-display.h"
#include "gnc-pricedb.h"
#include "gnc-ui-util.h"
#include "gnc-ui.h"
#include "gnc-uri-utils.h"
#include "gnc-filepath-utils.h"
#include "gnc-warnings.h"
#include "gnucash-sheet.h"
#include "gnucash-register.h"
#include "table-allgui.h"
#include "gnc-state.h"
#include "dialog-utils.h"
// static QofLogModule log_module = GNC_MOD_SX;
static QofLogModule log_module = GNC_MOD_GUI;
/***** PROTOTYPES ***************************************************/
void gnc_split_reg_raise( GNCSplitReg *gsr );
static GtkWidget* add_summary_label( GtkWidget *summarybar, gboolean pack_start,
const char *label_str, GtkWidget *extra );
static void gsr_summarybar_set_arrow_draw (GNCSplitReg *gsr);
static void gnc_split_reg_determine_read_only( GNCSplitReg *gsr );
static gboolean is_trans_readonly_and_warn (GtkWindow *parent, Transaction *trans);
static GNCPlaceholderType gnc_split_reg_get_placeholder( GNCSplitReg *gsr );
static GtkWidget *gnc_split_reg_get_parent( GNCLedgerDisplay *ledger );
static void gsr_create_table( GNCSplitReg *gsr );
static void gsr_setup_table( GNCSplitReg *gsr );
static void gsr_setup_status_widgets( GNCSplitReg *gsr );
static void gsr_update_summary_label( GtkWidget *label,
xaccGetBalanceFn getter,
Account *leader,
GNCPrintAmountInfo print_info,
gnc_commodity *cmdty,
gboolean reverse,
gboolean euroFlag );
static void gsr_redraw_all_cb (GnucashRegister *g_reg, gpointer data);
static void gnc_split_reg_ld_destroy( GNCLedgerDisplay *ledger );
static Transaction* create_balancing_transaction(QofBook *book, Account *account,
time64 statement_date, gnc_numeric balancing_amount);
void gsr_default_enter_handler ( GNCSplitReg *w, gpointer ud );
void gsr_default_cancel_handler ( GNCSplitReg *w, gpointer ud );
void gsr_default_delete_handler ( GNCSplitReg *w, gpointer ud );
void gsr_default_reinit_handler ( GNCSplitReg *w, gpointer ud );
void gsr_default_dup_handler ( GNCSplitReg *w, gpointer ud );
void gsr_default_schedule_handler ( GNCSplitReg *w, gpointer ud );
void gsr_default_expand_handler ( GNCSplitReg *w, gpointer ud );
void gsr_default_blank_handler ( GNCSplitReg *w, gpointer ud );
void gsr_default_jump_handler ( GNCSplitReg *w, gpointer ud );
void gsr_default_cut_handler ( GNCSplitReg *w, gpointer ud );
void gsr_default_cut_txn_handler ( GNCSplitReg *w, gpointer ud );
void gsr_default_copy_handler ( GNCSplitReg *w, gpointer ud );
void gsr_default_copy_txn_handler ( GNCSplitReg *w, gpointer ud );
void gsr_default_paste_handler ( GNCSplitReg *w, gpointer ud );
void gsr_default_paste_txn_handler( GNCSplitReg *w, gpointer ud );
void gsr_default_void_txn_handler ( GNCSplitReg *w, gpointer ud );
void gsr_default_unvoid_txn_handler ( GNCSplitReg *w, gpointer ud );
void gsr_default_reverse_txn_handler ( GNCSplitReg *w, gpointer ud );
void gsr_default_doclink_handler ( GNCSplitReg *w );
void gsr_default_doclink_open_handler ( GNCSplitReg *w );
void gsr_default_doclink_remove_handler ( GNCSplitReg *w );
static void gsr_default_doclink_from_sheet_handler ( GNCSplitReg *w );
static void gsr_emit_simple_signal ( GNCSplitReg *gsr, const char *sigName );
static void gsr_emit_help_changed ( GnucashRegister *reg, gpointer user_data );
static void gsr_emit_show_popup_menu ( GnucashRegister *reg, gpointer user_data );
static void gsr_emit_include_date_signal ( GNCSplitReg *gsr, time64 date );
void gnc_split_reg_cut_cb(GtkWidget *w, gpointer data);
void gnc_split_reg_copy_cb(GtkWidget *w, gpointer data);
void gnc_split_reg_paste_cb(GtkWidget *w, gpointer data);
void gnc_split_reg_cut_trans_cb(GtkWidget *w, gpointer data);
void gnc_split_reg_copy_trans_cb(GtkWidget *w, gpointer data);
void gnc_split_reg_paste_trans_cb(GtkWidget *w, gpointer data);
void gnc_split_reg_void_trans_cb(GtkWidget *w, gpointer data);
void gnc_split_reg_unvoid_trans_cb(GtkWidget *w, gpointer data);
void gnc_split_reg_reverse_trans_cb(GtkWidget *w, gpointer data);
void gnc_split_reg_record_cb (GnucashRegister *reg, gpointer data);
void gnc_split_reg_reinitialize_trans_cb(GtkWidget *w, gpointer data);
void gnc_split_reg_delete_trans_cb(GtkWidget *w, gpointer data);
void gnc_split_reg_duplicate_trans_cb(GtkWidget *w, gpointer data);
void gnc_split_reg_recur_cb(GtkWidget *w, gpointer data);
void gnc_split_reg_record_trans_cb(GtkWidget *w, gpointer data);
void gnc_split_reg_cancel_trans_cb(GtkWidget *w, gpointer data);
void gnc_split_reg_expand_trans_menu_cb(GtkWidget *widget, gpointer data);
void gnc_split_reg_expand_trans_toolbar_cb(GtkWidget *widget, gpointer data);
void gnc_split_reg_new_trans_cb(GtkWidget *widget, gpointer data);
void gnc_split_reg_jump_cb(GtkWidget *widget, gpointer data);
void gnc_split_reg_style_ledger_cb (GtkWidget *w, gpointer data);
void gnc_split_reg_style_auto_ledger_cb (GtkWidget *w, gpointer data);
void gnc_split_reg_style_journal_cb (GtkWidget *w, gpointer data);
void gnc_split_reg_double_line_cb (GtkWidget *w, gpointer data);
void gnc_split_reg_sort_standard_cb (GtkWidget *w, gpointer data);
void gnc_split_reg_sort_date_cb (GtkWidget *w, gpointer data);
void gnc_split_reg_sort_date_entered_cb (GtkWidget *w, gpointer data);
void gnc_split_reg_sort_date_reconciled_cb (GtkWidget *w, gpointer data);
void gnc_split_reg_sort_num_cb (GtkWidget *w, gpointer data);
void gnc_split_reg_sort_amount_cb (GtkWidget *w, gpointer data);
void gnc_split_reg_sort_memo_cb (GtkWidget *w, gpointer data);
void gnc_split_reg_sort_desc_cb (GtkWidget *w, gpointer data);
void gnc_split_reg_sort_action_cb (GtkWidget *w, gpointer data);
void gnc_split_reg_sort_notes_cb (GtkWidget *w, gpointer data);
void gnc_split_reg_size_allocate( GtkWidget *widget,
GtkAllocation *allocation,
gpointer user_data );
static void gnc_split_reg_class_init( GNCSplitRegClass *klass );
static void gnc_split_reg_init( GNCSplitReg *gsr );
static void gnc_split_reg_init2( GNCSplitReg *gsr );
void gnc_split_reg_dispose(GObject *obj);
FROM_STRING_FUNC(SortType, ENUM_LIST_SORTTYPE)
AS_STRING_FUNC(SortType, ENUM_LIST_SORTTYPE)
GType
gnc_split_reg_get_type( void )
{
static GType gnc_split_reg_type = 0;
if (!gnc_split_reg_type)
{
GTypeInfo type_info =
{
sizeof(GNCSplitRegClass), /* class_size */
NULL, /* base_init */
NULL, /* base_finalize */
(GClassInitFunc)gnc_split_reg_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof(GNCSplitReg), /* */
0, /* n_preallocs */
(GInstanceInitFunc)gnc_split_reg_init,
};
gnc_split_reg_type = g_type_register_static( GTK_TYPE_BOX,
"GNCSplitReg",
&type_info, 0 );
}
return gnc_split_reg_type;
}
/* SIGNALS */
enum gnc_split_reg_signal_enum
{
ENTER_ENT_SIGNAL,
CANCEL_ENT_SIGNAL,
DELETE_ENT_SIGNAL,
REINIT_ENT_SIGNAL,
DUP_ENT_SIGNAL,
SCHEDULE_ENT_SIGNAL,
EXPAND_ENT_SIGNAL,
BLANK_SIGNAL,
JUMP_SIGNAL,
CUT_SIGNAL,
CUT_TXN_SIGNAL,
COPY_SIGNAL,
COPY_TXN_SIGNAL,
PASTE_SIGNAL,
PASTE_TXN_SIGNAL,
VOID_TXN_SIGNAL,
UNVOID_TXN_SIGNAL,
REVERSE_TXN_SIGNAL,
HELP_CHANGED_SIGNAL,
SHOW_POPUP_MENU_SIGNAL,
INCLUDE_DATE_SIGNAL,
LAST_SIGNAL
};
static guint gnc_split_reg_signals[LAST_SIGNAL] = { 0 };
static void
gnc_split_reg_class_init( GNCSplitRegClass *klass )
{
int i;
GObjectClass *object_class;
static struct similar_signal_info
{
enum gnc_split_reg_signal_enum s;
const char *signal_name;
guint defaultOffset;
} signals[] =
{
{ ENTER_ENT_SIGNAL, "enter_ent", G_STRUCT_OFFSET( GNCSplitRegClass, enter_ent_cb ) },
{ CANCEL_ENT_SIGNAL, "cancel_ent", G_STRUCT_OFFSET( GNCSplitRegClass, cancel_ent_cb ) },
{ DELETE_ENT_SIGNAL, "delete_ent", G_STRUCT_OFFSET( GNCSplitRegClass, delete_ent_cb ) },
{ REINIT_ENT_SIGNAL, "reinit_ent", G_STRUCT_OFFSET( GNCSplitRegClass, reinit_ent_cb ) },
{ DUP_ENT_SIGNAL, "dup_ent", G_STRUCT_OFFSET( GNCSplitRegClass, dup_ent_cb ) },
{ SCHEDULE_ENT_SIGNAL, "schedule_ent", G_STRUCT_OFFSET( GNCSplitRegClass, schedule_ent_cb ) },
{ EXPAND_ENT_SIGNAL, "expand_ent", G_STRUCT_OFFSET( GNCSplitRegClass, expand_ent_cb ) },
{ BLANK_SIGNAL, "blank", G_STRUCT_OFFSET( GNCSplitRegClass, blank_cb ) },
{ JUMP_SIGNAL, "jump", G_STRUCT_OFFSET( GNCSplitRegClass, jump_cb ) },
{ CUT_SIGNAL, "cut", G_STRUCT_OFFSET( GNCSplitRegClass, cut_cb ) },
{ CUT_TXN_SIGNAL, "cut_txn", G_STRUCT_OFFSET( GNCSplitRegClass, cut_txn_cb ) },
{ COPY_SIGNAL, "copy", G_STRUCT_OFFSET( GNCSplitRegClass, copy_cb ) },
{ COPY_TXN_SIGNAL, "copy_txn", G_STRUCT_OFFSET( GNCSplitRegClass, copy_txn_cb ) },
{ PASTE_SIGNAL, "paste", G_STRUCT_OFFSET( GNCSplitRegClass, paste_cb ) },
{ PASTE_TXN_SIGNAL, "paste_txn", G_STRUCT_OFFSET( GNCSplitRegClass, paste_txn_cb ) },
{ VOID_TXN_SIGNAL, "void_txn", G_STRUCT_OFFSET( GNCSplitRegClass, void_txn_cb ) },
{ UNVOID_TXN_SIGNAL, "unvoid_txn", G_STRUCT_OFFSET( GNCSplitRegClass, unvoid_txn_cb ) },
{ REVERSE_TXN_SIGNAL, "reverse_txn", G_STRUCT_OFFSET( GNCSplitRegClass, reverse_txn_cb ) },
{ HELP_CHANGED_SIGNAL, "help-changed", G_STRUCT_OFFSET( GNCSplitRegClass, help_changed_cb ) },
{ SHOW_POPUP_MENU_SIGNAL, "show-popup-menu", G_STRUCT_OFFSET( GNCSplitRegClass, show_popup_menu_cb ) },
{ INCLUDE_DATE_SIGNAL, "include-date", G_STRUCT_OFFSET( GNCSplitRegClass, include_date_cb ) },
{ LAST_SIGNAL, NULL, 0 }
};
object_class = (GObjectClass*) klass;
for ( i = 0; signals[i].s != INCLUDE_DATE_SIGNAL; i++ )
{
gnc_split_reg_signals[ signals[i].s ] =
g_signal_new( signals[i].signal_name,
G_TYPE_FROM_CLASS(object_class),
G_SIGNAL_RUN_LAST,
signals[i].defaultOffset,
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0 );
}
/* Setup the non-default-marshalled signals; 'i' is still valid, here. */
/* "include-date" */
gnc_split_reg_signals[ INCLUDE_DATE_SIGNAL ] =
g_signal_new( "include-date",
G_TYPE_FROM_CLASS(object_class),
G_SIGNAL_RUN_LAST,
signals[i++].defaultOffset,
NULL, NULL,
g_cclosure_marshal_VOID__INT, /* time64 == int */
G_TYPE_NONE, 1, G_TYPE_INT );
g_assert( i == LAST_SIGNAL );
/* Setup the default handlers. */
klass->enter_ent_cb = gsr_default_enter_handler;
klass->cancel_ent_cb = gsr_default_cancel_handler;
klass->delete_ent_cb = gsr_default_delete_handler;
klass->reinit_ent_cb = gsr_default_reinit_handler;
klass->dup_ent_cb = gsr_default_dup_handler;
klass->schedule_ent_cb = gsr_default_schedule_handler;
klass->expand_ent_cb = gsr_default_expand_handler;
klass->blank_cb = gsr_default_blank_handler;
klass->jump_cb = gsr_default_jump_handler;
klass->cut_cb = gsr_default_cut_handler;
klass->cut_txn_cb = gsr_default_cut_txn_handler;
klass->copy_cb = gsr_default_copy_handler;
klass->copy_txn_cb = gsr_default_copy_txn_handler;
klass->paste_cb = gsr_default_paste_handler;
klass->paste_txn_cb = gsr_default_paste_txn_handler;
klass->void_txn_cb = gsr_default_void_txn_handler;
klass->unvoid_txn_cb = gsr_default_unvoid_txn_handler;
klass->reverse_txn_cb = gsr_default_reverse_txn_handler;
klass->help_changed_cb = NULL;
klass->show_popup_menu_cb = NULL;
klass->include_date_cb = NULL;
object_class->dispose = gnc_split_reg_dispose;
}
GtkWidget*
gnc_split_reg_new( GNCLedgerDisplay *ld,
GtkWindow *parent,
gint numberOfLines,
gboolean read_only )
{
GNCSplitReg *gsrToRet;
ENTER("ld=%p, parent=%p, numberOfLines=%d, read_only=%s",
ld, parent, numberOfLines, read_only ? "TRUE" : "FALSE");
gsrToRet = g_object_new( gnc_split_reg_get_type(), NULL );
gsrToRet->numRows = numberOfLines;
gsrToRet->read_only = read_only;
gsrToRet->ledger = ld;
gsrToRet->window = GTK_WIDGET(parent);
gnc_split_reg_init2( gsrToRet );
LEAVE("%p", gsrToRet);
return GTK_WIDGET( gsrToRet );
}
static void
gnc_split_reg_init( GNCSplitReg *gsr )
{
gtk_orientable_set_orientation (GTK_ORIENTABLE(gsr), GTK_ORIENTATION_VERTICAL);
gsr->sort_type = BY_STANDARD;
gsr->sort_rev = FALSE;
gsr->sort_arrow_handler_id = 0;
gsr->filter_text = NULL;
gsr->width = -1;
gsr->height = -1;
gsr->numRows = 10;
gsr->read_only = FALSE;
}
static void
gnc_split_reg_pref_acc_labels (gpointer prefs, gchar *pref, gpointer user_data)
{
GNCSplitReg *gsr = user_data;
gnucash_register_refresh_from_prefs (gsr->reg);
}
static void
gnc_split_reg_init2( GNCSplitReg *gsr )
{
if ( !gsr ) return;
gnc_split_reg_determine_read_only( gsr );
gsr_setup_status_widgets( gsr );
/* ordering is important here... setup_status before create_table */
gsr_create_table( gsr );
gsr_setup_table( gsr );
gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
GNC_PREF_ACCOUNTING_LABELS,
gnc_split_reg_pref_acc_labels,
gsr);
}
static
void
gsr_setup_table( GNCSplitReg *gsr )
{
SplitRegister *sr;
ENTER("gsr=%p", gsr);
sr = gnc_ledger_display_get_split_register( gsr->ledger );
gnc_split_register_show_present_divider( sr, TRUE );
/* events should be sufficient to redraw this */
/* gnc_ledger_display_refresh( gsr->ledger ); */
LEAVE(" ");
}
static void
gsr_move_sort_and_filter_to_state_file (GNCSplitReg *gsr, GKeyFile* state_file, const gchar *state_section)
{
GNCLedgerDisplayType ledger_type;
GNCLedgerDisplay* ld;
// Look for any old kvp entries and add them to .gcm file
ledger_type = gnc_ledger_display_type (gsr->ledger);
// General ledger should already be using .gcm file
if ((ledger_type == LD_SINGLE) || (ledger_type == LD_SUBACCOUNT))
{
Account *leader = gnc_ledger_display_leader (gsr->ledger);
const char* kvp_filter = NULL;
const char* kvp_sort_order = NULL;
gboolean kvp_sort_reversed = FALSE;
kvp_filter = xaccAccountGetFilter (leader);
if (kvp_filter)
{
gchar *temp_filter_text = g_strdup (kvp_filter);
temp_filter_text = g_strdelimit (temp_filter_text, ",",
';'); // make it conform to .gcm file list
g_key_file_set_string (state_file, state_section, KEY_PAGE_FILTER,
temp_filter_text);
g_free (temp_filter_text);
xaccAccountSetFilter (leader, NULL);
}
kvp_sort_order = xaccAccountGetSortOrder (leader);
if (kvp_sort_order)
{
g_key_file_set_string (state_file, state_section,
KEY_PAGE_SORT, kvp_sort_order);
xaccAccountSetSortOrder (leader, NULL);
}
kvp_sort_reversed = xaccAccountGetSortReversed (leader);
if (kvp_sort_reversed)
{
g_key_file_set_boolean (state_file, state_section,
KEY_PAGE_SORT_REV, kvp_sort_reversed);
xaccAccountSetSortReversed (leader, FALSE);
}
}
}
static
void
gsr_create_table( GNCSplitReg *gsr )
{
GtkWidget *register_widget = NULL;
SplitRegister *sr = NULL;
Account * account = gnc_ledger_display_leader(gsr->ledger);
const GncGUID * guid = xaccAccountGetGUID(account);
gchar guidstr[GUID_ENCODING_LENGTH+1];
GKeyFile* state_file = gnc_state_get_current();
gchar *register_state_section;
guid_to_string_buff (guid, guidstr);
register_state_section = g_strconcat (STATE_SECTION_REG_PREFIX, " ", guidstr, NULL);
ENTER("gsr=%p", gsr);
sr = gnc_ledger_display_get_split_register (gsr->ledger);
gnc_ledger_display_set_user_data( gsr->ledger, (gpointer)gsr );
gnc_ledger_display_set_handlers( gsr->ledger,
gnc_split_reg_ld_destroy,
gnc_split_reg_get_parent );
/* FIXME: We'd really rather pass this down... */
sr = gnc_ledger_display_get_split_register( gsr->ledger );
register_widget = gnucash_register_new( sr->table, register_state_section );
gsr->reg = GNUCASH_REGISTER( register_widget );
gtk_box_pack_start (GTK_BOX (gsr), GTK_WIDGET(gsr->reg), TRUE, TRUE, 0);
gnucash_sheet_set_window (gnucash_register_get_sheet (gsr->reg), gsr->window);
// setup the callback for when the doclink cell clicked on
gnucash_register_set_open_doclink_cb (gsr->reg,
(GFunc)gsr_default_doclink_from_sheet_handler, gsr);
gtk_widget_show ( GTK_WIDGET(gsr->reg) );
g_signal_connect (gsr->reg, "activate_cursor",
G_CALLBACK(gnc_split_reg_record_cb), gsr);
g_signal_connect (gsr->reg, "redraw_all",
G_CALLBACK(gsr_redraw_all_cb), gsr);
g_signal_connect (gsr->reg, "redraw_help",
G_CALLBACK(gsr_emit_help_changed), gsr);
g_signal_connect (gsr->reg, "show_popup_menu",
G_CALLBACK(gsr_emit_show_popup_menu), gsr);
gsr_move_sort_and_filter_to_state_file (gsr, state_file, register_state_section);
g_free (register_state_section);
LEAVE(" ");
}
static
void
gsr_setup_status_widgets( GNCSplitReg *gsr )
{
SplitRegister *sr;
gboolean use_double_line;
sr = gnc_ledger_display_get_split_register( gsr->ledger );
use_double_line = gnc_ledger_display_default_double_line( gsr->ledger );
/* be sure to initialize the gui elements associated with the cursor */
gnc_split_register_config( sr, sr->type, sr->style, use_double_line );
}
void
gnc_split_reg_dispose(GObject *obj)
{
GNCSplitReg *gsr = GNC_SPLIT_REG(obj);
if (gsr->filter_text)
g_free (gsr->filter_text);
gsr->filter_text = NULL;
gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
GNC_PREF_ACCOUNTING_LABELS,
gnc_split_reg_pref_acc_labels,
gsr);
if (gsr->reg)
{
g_signal_handlers_disconnect_by_data (gsr->reg, gsr);
gtk_widget_destroy (GTK_WIDGET (gsr->reg));
}
gsr->reg = NULL;
}
/**
* Raise an existing register window to the front.
**/
void
gnc_split_reg_raise( GNCSplitReg *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
gsr_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];
const gchar *label_str = NULL;
GtkWidget *text_label, *hbox;
gchar *tooltip;
if ( label == NULL )
return;
hbox = g_object_get_data (G_OBJECT(label), "text_box");
text_label = g_object_get_data (G_OBJECT(label), "text_label");
label_str = gtk_label_get_text (GTK_LABEL(text_label));
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 );
if (label_str)
{
tooltip = g_strdup_printf ("%s %s", label_str, string);
gtk_widget_set_tooltip_text (GTK_WIDGET(hbox), tooltip);
g_free (tooltip);
}
}
static
void
gsr_redraw_all_cb (GnucashRegister *g_reg, gpointer data)
{
GNCSplitReg *gsr = data;
gnc_commodity * commodity;
GNCPrintAmountInfo print_info;
gnc_numeric amount;
Account *leader;
gboolean reverse;
gboolean euro;
if ( gsr->summarybar == NULL )
return;
leader = gnc_ledger_display_leader( gsr->ledger );
commodity = xaccAccountGetCommodity( leader );
/* no EURO conversion, 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 );
if (gsr->balance_label != NULL) // only test the first as they are a group
{
gsr_update_summary_label( gsr->balance_label,
xaccAccountGetPresentBalance,
leader, print_info, commodity, reverse, euro );
gsr_update_summary_label( gsr->cleared_label,
xaccAccountGetClearedBalance,
leader, print_info, commodity, reverse, euro );
gsr_update_summary_label( gsr->reconciled_label,
xaccAccountGetReconciledBalance,
leader, print_info, commodity, reverse, euro );
gsr_update_summary_label( gsr->future_label,
xaccAccountGetBalance,
leader, print_info, commodity, reverse, euro );
gsr_update_summary_label( gsr->projectedminimum_label,
xaccAccountGetProjectedMinimumBalance,
leader, print_info, commodity, reverse, euro );
}
// Sort label
if (gsr->sort_label != NULL)
{
gchar *old_tt_text = gtk_widget_get_tooltip_text (GTK_WIDGET(gsr->sort_label));
gchar *new_tt_text;
gchar *text = NULL;
switch (gsr->sort_type)
{
case (0):
text = _("None");
break;
case (1):
text = _("Standard Order");
break;
case (2):
text = _("Date");
break;
case (3):
text = _("Date of Entry");
break;
case (4):
text = _("Statement Date");
break;
case (5):
text = _("Number");
break;
case (6):
text = _("Amount");
break;
case (7):
text = _("Memo");
break;
case (8):
text = _("Description");
break;
case (9):
text = _("Action");
break;
case (10):
text = _("Notes");
break;
}
if (gsr->sort_rev)
gtk_widget_set_tooltip_text (GTK_WIDGET(gsr->sort_label), _("Descending"));
else
gtk_widget_set_tooltip_text (GTK_WIDGET(gsr->sort_label), _("Ascending"));
new_tt_text = gtk_widget_get_tooltip_text (GTK_WIDGET(gsr->sort_label));
// does the arrow need changing
if (g_strcmp0 (old_tt_text, new_tt_text) != 0)
gsr_summarybar_set_arrow_draw (gsr);
if (old_tt_text)
g_free (old_tt_text);
if (new_tt_text)
g_free (new_tt_text);
gtk_label_set_text (GTK_LABEL(gsr->sort_label), text);
}
// Filter label
if (gsr->filter_label != NULL)
{
gchar *old_tt_text = gtk_widget_get_tooltip_text (GTK_WIDGET(gsr->filter_label));
// check for a change in text
if (g_strcmp0 (old_tt_text, gsr->filter_text) != 0)
{
if (gsr->filter_text != NULL)
gtk_label_set_text (GTK_LABEL(gsr->filter_label), _("Filtered"));
else
gtk_label_set_text (GTK_LABEL(gsr->filter_label), "");
gtk_widget_set_tooltip_text (GTK_WIDGET(gsr->filter_label), gsr->filter_text);
if (old_tt_text)
g_free (old_tt_text);
}
}
if (gsr->shares_label == NULL && gsr->value_label == NULL)
return;
amount = xaccAccountGetBalance( leader );
if (reverse)
amount = gnc_numeric_neg( amount );
/* Print the summary share amount */
if (gsr->shares_label != NULL)
{
char string[256];
print_info = gnc_account_print_info( leader, TRUE );
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];
QofBook *book = gnc_account_get_book (leader);
GNCPriceDB *pricedb = gnc_pricedb_get_db (book);
gnc_commodity *currency = gnc_default_currency ();
gnc_numeric value =
gnc_pricedb_convert_balance_latest_price (pricedb, amount,
commodity, currency);
print_info = gnc_commodity_print_info (currency, TRUE);
xaccSPrintAmount (string, value, print_info);
gnc_set_label_color (gsr->value_label, amount);
gtk_label_set_text (GTK_LABEL (gsr->value_label), string);
}
}
static void
gnc_split_reg_ld_destroy( GNCLedgerDisplay *ledger )
{
GNCSplitReg *gsr = gnc_ledger_display_get_user_data( ledger );
Account * account = gnc_ledger_display_leader(ledger);
const GncGUID * guid = xaccAccountGetGUID(account);
gchar guidstr[GUID_ENCODING_LENGTH+1];
gchar *state_section;
guid_to_string_buff(guid, guidstr);
state_section = g_strconcat (STATE_SECTION_REG_PREFIX, " ", guidstr, NULL);
if (gsr)
{
SplitRegister *reg;
reg = gnc_ledger_display_get_split_register (ledger);
if (reg && reg->table)
gnc_table_save_state (reg->table, state_section);
/*
* Don't destroy the window here any more. The register no longer
* owns it.
*/
}
g_free (state_section);
gnc_ledger_display_set_user_data (ledger, NULL);
g_object_unref (gsr);
}
void
gsr_default_cut_handler( GNCSplitReg *gsr, gpointer data )
{
gnucash_register_cut_clipboard( gsr->reg );
}
/**
* Cut the selection to the clipboard. This refers to the Split.
**/
void
gnc_split_reg_cut_cb (GtkWidget *w, gpointer data)
{
GNCSplitReg *gsr = data;
gsr_emit_simple_signal( gsr, "cut" );
}
void
gsr_default_copy_handler( GNCSplitReg *gsr, gpointer data )
{
gnucash_register_copy_clipboard( gsr->reg );
}
/**
* Copy the selection to the clipboard. This refers to the Split.
**/
void
gnc_split_reg_copy_cb (GtkWidget *w, gpointer data)
{
GNCSplitReg *gsr = data;
gsr_emit_simple_signal( gsr, "copy" );
}
void
gsr_default_paste_handler( GNCSplitReg *gsr, gpointer data )
{
gnucash_register_paste_clipboard( gsr->reg );
}
/**
* Paste the clipboard to the selection. This refers to the Split.
**/
void
gnc_split_reg_paste_cb (GtkWidget *w, gpointer data)
{
GNCSplitReg *gsr = data;
gsr_emit_simple_signal( gsr, "paste" );
}
void
gsr_default_cut_txn_handler (GNCSplitReg *gsr, gpointer data)
{
CursorClass cursor_class;
SplitRegister *reg;
Transaction *trans;
Split *split;
GtkWidget *dialog;
gint response;
const gchar *warning;
reg = gnc_ledger_display_get_split_register (gsr->ledger);
/* get the current split based on cursor position */
split = gnc_split_register_get_current_split (reg);
if (split == NULL)
{
gnc_split_register_cancel_cursor_split_changes (reg);
return;
}
trans = xaccSplitGetParent (split);
cursor_class = gnc_split_register_get_current_cursor_class (reg);
/* test for blank_split reference pointing to split */
if (gnc_split_register_is_blank_split (reg, split))
gnc_split_register_change_blank_split_ref (reg, split);
/* Cutting the blank split just cancels */
{
Split *blank_split = gnc_split_register_get_blank_split (reg);
if (split == blank_split)
{
gnc_split_register_cancel_cursor_trans_changes (reg);
return;
}
}
if (cursor_class == CURSOR_CLASS_NONE)
return;
/* this is probably not required but leave as a double check */
if (is_trans_readonly_and_warn (GTK_WINDOW(gsr->window), trans))
return;
/* On a split cursor, just delete the one split. */
if (cursor_class == CURSOR_CLASS_SPLIT)
{
const char *format = _("Cut the split '%s' from the transaction '%s'?");
const char *recn_warn = _("You would be removing a reconciled split! "
"This is not a good idea as it will cause your "
"reconciled balance to be off.");
const char *anchor_error = _("You cannot cut this split.");
const char *anchor_split = _("This is the split anchoring this transaction "
"to the register. You may not remove it from "
"this register window. You may remove the "
"entire transaction from this window, or you "
"may navigate to a register that shows "
"another side of this same transaction and "
"remove the split from that register.");
char *buf = NULL;
const char *memo;
const char *desc;
char recn;
if (reg->type != GENERAL_JOURNAL) // no anchoring split
{
if (split == gnc_split_register_get_current_trans_split (reg, NULL))
{
dialog = gtk_message_dialog_new (GTK_WINDOW(gsr->window),
GTK_DIALOG_MODAL
| GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_OK,
"%s", anchor_error);
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG(dialog),
"%s", anchor_split);
gtk_dialog_run (GTK_DIALOG(dialog));
gtk_widget_destroy (dialog);
return;
}
}
memo = xaccSplitGetMemo (split);
memo = (memo && *memo) ? memo : _("(no memo)");
desc = xaccTransGetDescription (trans);
desc = (desc && *desc) ? desc : _("(no description)");
/* ask for user confirmation before performing permanent damage */
buf = g_strdup_printf (format, memo, desc);
dialog = gtk_message_dialog_new (GTK_WINDOW(gsr->window),
GTK_DIALOG_MODAL
| GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_QUESTION,
GTK_BUTTONS_NONE,
"%s", buf);
g_free (buf);
recn = xaccSplitGetReconcile (split);
if (recn == YREC || recn == FREC)
{
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG(dialog),
"%s", recn_warn);
warning = GNC_PREF_WARN_REG_SPLIT_DEL_RECD;
}
else
{
warning = GNC_PREF_WARN_REG_SPLIT_DEL;
}
gtk_dialog_add_button (GTK_DIALOG(dialog),
_("_Cancel"), GTK_RESPONSE_CANCEL);
gnc_gtk_dialog_add_button (dialog, _("_Cut Split"),
"edit-delete", GTK_RESPONSE_ACCEPT);
response = gnc_dialog_run (GTK_DIALOG(dialog), warning);
gtk_widget_destroy (dialog);
if (response != GTK_RESPONSE_ACCEPT)
return;
gnc_split_register_cut_current (reg);
return;
}
/* On a transaction cursor with 2 or fewer splits in single or double
* mode, we just delete the whole transaction, kerblooie */
{
const char *title = _("Cut the current transaction?");
const char *recn_warn = _("You would be removing a transaction "
"with reconciled splits! "
"This is not a good idea as it will cause your "
"reconciled balance to be off.");
dialog = gtk_message_dialog_new (GTK_WINDOW(gsr->window),
GTK_DIALOG_MODAL
| GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_WARNING,
GTK_BUTTONS_NONE,
"%s", title);
if (xaccTransHasReconciledSplits (trans))
{
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG(dialog),
"%s", recn_warn);
warning = GNC_PREF_WARN_REG_TRANS_DEL_RECD;
}
else
{
warning = GNC_PREF_WARN_REG_TRANS_DEL;
}
gtk_dialog_add_button (GTK_DIALOG(dialog),
_("_Cancel"), GTK_RESPONSE_CANCEL);
gnc_gtk_dialog_add_button (dialog, _("_Cut Transaction"),
"edit-delete", GTK_RESPONSE_ACCEPT);
response = gnc_dialog_run (GTK_DIALOG(dialog), warning);
gtk_widget_destroy (dialog);
if (response != GTK_RESPONSE_ACCEPT)
return;
gnc_split_register_cut_current (reg);
return;
}
}
/**
* Cut the current transaction to the clipboard.
**/
void
gnc_split_reg_cut_trans_cb (GtkWidget *w, gpointer data)
{
GNCSplitReg *gsr = data;
gsr_emit_simple_signal( gsr, "cut_txn" );
}
void
gsr_default_copy_txn_handler( GNCSplitReg *gsr, gpointer data )
{
gnc_split_register_copy_current
(gnc_ledger_display_get_split_register( gsr->ledger ));
}
/**
* Copy the current transaction to the clipboard.
**/
void
gnc_split_reg_copy_trans_cb(GtkWidget *w, gpointer data)
{
GNCSplitReg *gsr = data;
gsr_emit_simple_signal( gsr, "copy_txn" );
}
void
gsr_default_paste_txn_handler( GNCSplitReg *gsr, gpointer data )
{
gnc_split_register_paste_current
(gnc_ledger_display_get_split_register( gsr->ledger ));
}
/**
* Paste the transaction clipboard to the selection.
**/
void
gnc_split_reg_paste_trans_cb (GtkWidget *w, gpointer data)
{
GNCSplitReg *gsr = data;
gsr_emit_simple_signal( gsr, "paste_txn" );
}
/********************************************************************\
* gnc_split_reg_void_trans_cb *
* *
* Args: widget - the widget that called us *
* data - the data struct for this register *
* Return: none *
\********************************************************************/
void
gsr_default_void_txn_handler (GNCSplitReg *gsr, gpointer data)
{
// Override this function.
}
void
gnc_split_reg_void_trans_cb (GtkWidget *w, gpointer data)
{
GNCSplitReg *gsr = data;
gsr_emit_simple_signal( gsr, "void_txn" );
}
/********************************************************************\
* gnc_split_reg_unvoid_trans_cb *
* *
* Args: widget - the widget that called us *
* data - the data struct for this register *
* Return: none *
\********************************************************************/
void
gsr_default_unvoid_txn_handler (GNCSplitReg *gsr, gpointer data)
{
// Override this function.
}
void
gnc_split_reg_unvoid_trans_cb (GtkWidget *w, gpointer data)
{
GNCSplitReg *gsr = data;
gsr_emit_simple_signal( gsr, "unvoid_txn" );
}
/********************************************************************\
* gnc_split_reg_reverse_trans_cb *
* *
* Args: widget - the widget that called us *
* data - the data struct for this register *
* Return: none *
\********************************************************************/
void
gsr_default_reverse_txn_handler (GNCSplitReg *gsr, gpointer data)
{
SplitRegister *reg;
Transaction *trans, *new_trans;
reg = gnc_ledger_display_get_split_register( gsr->ledger );
trans = gnc_split_register_get_current_trans (reg);
if (trans == NULL)
return;
if (xaccTransGetReversedBy(trans))
{
gnc_error_dialog (GTK_WINDOW (gsr->window), "%s",
_("A reversing entry has already been created for this transaction."));
return;
}
new_trans = xaccTransReverse(trans);
/* Clear transaction level info */
xaccTransSetDatePostedSecsNormalized(new_trans, gnc_time (NULL));
xaccTransSetDateEnteredSecs(new_trans, gnc_time (NULL));
/* Now jump to new trans */
gnc_split_reg_jump_to_split(gsr, xaccTransGetSplit(new_trans, 0));
}
void
gnc_split_reg_reverse_trans_cb (GtkWidget *w, gpointer data)
{
GNCSplitReg *gsr = data;
gsr_emit_simple_signal( gsr, "reverse_txn" );
}
static gboolean
is_trans_readonly_and_warn (GtkWindow *parent, Transaction *trans)
{
GtkWidget *dialog;
const gchar *reason;
const gchar *title = _("Cannot modify or delete this transaction.");
const gchar *message =
_("This transaction is marked read-only with the comment: '%s'");
if (!trans) return FALSE;
if (xaccTransIsReadonlyByPostedDate (trans))
{
dialog = gtk_message_dialog_new(parent,
0,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_OK,
"%s", title);
gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
"%s", _("The date of this transaction is older than the \"Read-Only Threshold\" set for this book. "
"This setting can be changed in File->Properties->Accounts."));
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
return TRUE;
}
reason = xaccTransGetReadOnly (trans);
if (reason)
{
dialog = gtk_message_dialog_new(parent,
0,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_OK,
"%s", title);
gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
message, reason);
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
return TRUE;
}
return FALSE;
}
void
gsr_default_reinit_handler( GNCSplitReg *gsr, gpointer data )
{
VirtualCellLocation vcell_loc;
SplitRegister *reg;
Transaction *trans;
Split *split;
GtkWidget *dialog;
gint response;
const gchar *warning;
const char *title = _("Remove the splits from this transaction?");
const char *recn_warn = _("This transaction contains reconciled splits. "
"Modifying it is not a good idea because that will "
"cause your reconciled balance to be off.");
reg = gnc_ledger_display_get_split_register( gsr->ledger );
trans = gnc_split_register_get_current_trans (reg);
if (is_trans_readonly_and_warn(GTK_WINDOW(gsr->window), trans))
return;
dialog = gtk_message_dialog_new(GTK_WINDOW(gsr->window),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_WARNING,
GTK_BUTTONS_NONE,
"%s", title);
if (xaccTransHasReconciledSplits (trans))
{
gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
"%s", recn_warn);
warning = GNC_PREF_WARN_REG_SPLIT_DEL_ALL_RECD;
}
else
{
warning = GNC_PREF_WARN_REG_SPLIT_DEL_ALL;
}
gtk_dialog_add_button(GTK_DIALOG(dialog),
_("_Cancel"), GTK_RESPONSE_CANCEL);
gnc_gtk_dialog_add_button(dialog,
/* Translators: This is the confirmation button in a warning dialog */
_("_Remove Splits"),
"edit-delete", GTK_RESPONSE_ACCEPT);
response = gnc_dialog_run(GTK_DIALOG(dialog), warning);
gtk_widget_destroy (dialog);
if (response != GTK_RESPONSE_ACCEPT)
return;
/*
* Find the "transaction" split for the current transaction. This is
* the split that appears at the top of the transaction in the
* register.
*/
split = gnc_split_register_get_current_split (reg);
if (!gnc_split_register_get_split_virt_loc(reg, split, &vcell_loc))
return;
split = gnc_split_register_get_current_trans_split (reg, &vcell_loc);
gnc_split_register_empty_current_trans_except_split (reg, split);
}
/**
* "Reinitializes" the current transaction.
**/
void
gnc_split_reg_reinitialize_trans_cb(GtkWidget *widget, gpointer data)
{
GNCSplitReg *gsr = data;
gsr_emit_simple_signal( gsr, "reinit_ent" );
}
/* Edit the document link for the current transaction. */
void
gsr_default_doclink_handler (GNCSplitReg *gsr)
{
SplitRegister *reg = gnc_ledger_display_get_split_register (gsr->ledger);
Split *split = gnc_split_register_get_current_split (reg);
Transaction *trans;
CursorClass cursor_class;
gchar *uri;
gchar *ret_uri;
/* get the current split based on cursor position */
if (!split)
{
gnc_split_register_cancel_cursor_split_changes (reg);
return;
}
trans = xaccSplitGetParent (split);
cursor_class = gnc_split_register_get_current_cursor_class (reg);
if (cursor_class == CURSOR_CLASS_NONE)
return;
if (is_trans_readonly_and_warn (GTK_WINDOW(gsr->window), trans))
return;
// fix an earlier error when storing relative paths before version 3.5
uri = gnc_doclink_convert_trans_link_uri (trans, gsr->read_only);
ret_uri =
gnc_doclink_get_uri_dialog (GTK_WINDOW (gsr->window),
_("Change a Transaction Linked Document"),
uri);
if (ret_uri && g_strcmp0 (uri, ret_uri) != 0)
xaccTransSetDocLink (trans, ret_uri);
g_free (ret_uri);
g_free (uri);
}
/* Opens the document link for the current transaction. */
void
gsr_default_doclink_open_handler (GNCSplitReg *gsr)
{
CursorClass cursor_class;
SplitRegister *reg = gnc_ledger_display_get_split_register (gsr->ledger);
Transaction *trans;
Split *split = gnc_split_register_get_current_split (reg);
gchar *uri;
/* get the current split based on cursor position */
if (!split)
{
gnc_split_register_cancel_cursor_split_changes (reg);
return;
}
trans = xaccSplitGetParent (split);
cursor_class = gnc_split_register_get_current_cursor_class (reg);
if (cursor_class == CURSOR_CLASS_NONE)
return;
// fix an earlier error when storing relative paths before version 3.5
uri = gnc_doclink_convert_trans_link_uri (trans, gsr->read_only);
gnc_doclink_open_uri (GTK_WINDOW (gsr->window), uri);
g_free (uri);
}
/* Removes the document link for the current transaction. */
void
gsr_default_doclink_remove_handler (GNCSplitReg *gsr)
{
CursorClass cursor_class;
SplitRegister *reg = gnc_ledger_display_get_split_register (gsr->ledger);
Transaction *trans;
Split *split = gnc_split_register_get_current_split (reg);
/* get the current split based on cursor position */
if (!split)
{
gnc_split_register_cancel_cursor_split_changes (reg);
return;
}
trans = xaccSplitGetParent (split);
cursor_class = gnc_split_register_get_current_cursor_class (reg);
if (cursor_class == CURSOR_CLASS_NONE)
return;
if (is_trans_readonly_and_warn (GTK_WINDOW(gsr->window), trans))
return;
xaccTransSetDocLink (trans, "");
}
static void
gsr_default_doclink_from_sheet_handler (GNCSplitReg *gsr)
{
CursorClass cursor_class;
SplitRegister *reg = gnc_ledger_display_get_split_register (gsr->ledger);
Transaction *trans;
Split *split;
gchar *uri = NULL;
/* get the current split based on cursor position */
split = gnc_split_register_get_current_split (reg);
if (!split)
return;
trans = xaccSplitGetParent (split);
// fix an earlier error when storing relative paths before version 3.5
uri = gnc_doclink_convert_trans_link_uri (trans, gsr->read_only);
if (uri)
gnc_doclink_open_uri (GTK_WINDOW (gsr->window), uri);
g_free (uri);
}
void
gsr_default_delete_handler( GNCSplitReg *gsr, gpointer data )
{
CursorClass cursor_class;
SplitRegister *reg;
Transaction *trans;
Split *split;
GtkWidget *dialog;
gint response;
const gchar *warning;
reg = gnc_ledger_display_get_split_register( gsr->ledger );
/* get the current split based on cursor position */
split = gnc_split_register_get_current_split(reg);
if (split == NULL)
{
gnc_split_register_cancel_cursor_split_changes (reg);
return;
}
trans = xaccSplitGetParent(split);
cursor_class = gnc_split_register_get_current_cursor_class (reg);
/* test for blank_split reference pointing to split */
if (gnc_split_register_is_blank_split (reg, split))
gnc_split_register_change_blank_split_ref (reg, split);
/* Deleting the blank split just cancels */
{
Split *blank_split = gnc_split_register_get_blank_split (reg);
if (split == blank_split)
{
gnc_split_register_cancel_cursor_trans_changes (reg);
return;
}
}
if (cursor_class == CURSOR_CLASS_NONE)
return;
if (is_trans_readonly_and_warn(GTK_WINDOW(gsr->window), trans))
return;
/* On a split cursor, just delete the one split. */
if (cursor_class == CURSOR_CLASS_SPLIT)
{
const char *format = _("Delete the split '%s' from the transaction '%s'?");
const char *recn_warn = _("You would be deleting a reconciled split! "
"This is not a good idea as it will cause your "
"reconciled balance to be off.");
const char *anchor_error = _("You cannot delete this split.");
const char *anchor_split = _("This is the split anchoring this transaction "
"to the register. You may not delete it from "
"this register window. You may delete the "
"entire transaction from this window, or you "
"may navigate to a register that shows "
"another side of this same transaction and "
"delete the split from that register.");
char *buf = NULL;
const char *memo;
const char *desc;
char recn;
if (reg->type != GENERAL_JOURNAL) // no anchoring split
{
if (split == gnc_split_register_get_current_trans_split (reg, NULL))
{
dialog = gtk_message_dialog_new(GTK_WINDOW(gsr->window),
GTK_DIALOG_MODAL
| GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_OK,
"%s", anchor_error);
gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
"%s", anchor_split);
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy (dialog);
return;
}
}
memo = xaccSplitGetMemo (split);
memo = (memo && *memo) ? memo : _("(no memo)");
desc = xaccTransGetDescription (trans);
desc = (desc && *desc) ? desc : _("(no description)");
/* ask for user confirmation before performing permanent damage */
buf = g_strdup_printf (format, memo, desc);
dialog = gtk_message_dialog_new(GTK_WINDOW(gsr->window),
GTK_DIALOG_MODAL
| GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_QUESTION,
GTK_BUTTONS_NONE,
"%s", buf);
g_free(buf);
recn = xaccSplitGetReconcile (split);
if (recn == YREC || recn == FREC)
{
gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
"%s", recn_warn);
warning = GNC_PREF_WARN_REG_SPLIT_DEL_RECD;
}
else
{
warning = GNC_PREF_WARN_REG_SPLIT_DEL;
}
gtk_dialog_add_button(GTK_DIALOG(dialog),
_("_Cancel"), GTK_RESPONSE_CANCEL);
gnc_gtk_dialog_add_button(dialog, _("_Delete Split"),
"edit-delete", GTK_RESPONSE_ACCEPT);
response = gnc_dialog_run(GTK_DIALOG(dialog), warning);
gtk_widget_destroy (dialog);
if (response != GTK_RESPONSE_ACCEPT)
return;
gnc_split_register_delete_current_split (reg);
return;
}
g_return_if_fail(cursor_class == CURSOR_CLASS_TRANS);
/* On a transaction cursor with 2 or fewer splits in single or double
* mode, we just delete the whole transaction, kerblooie */
{
const char *title = _("Delete the current transaction?");
const char *recn_warn = _("You would be deleting a transaction "
"with reconciled splits! "
"This is not a good idea as it will cause your "
"reconciled balance to be off.");
dialog = gtk_message_dialog_new(GTK_WINDOW(gsr->window),
GTK_DIALOG_MODAL
| GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_WARNING,
GTK_BUTTONS_NONE,
"%s", title);
if (xaccTransHasReconciledSplits (trans))
{
gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
"%s", recn_warn);
warning = GNC_PREF_WARN_REG_TRANS_DEL_RECD;
}
else
{
warning = GNC_PREF_WARN_REG_TRANS_DEL;
}
gtk_dialog_add_button(GTK_DIALOG(dialog),
_("_Cancel"), GTK_RESPONSE_CANCEL);
gnc_gtk_dialog_add_button(dialog, _("_Delete Transaction"),
"edit-delete", GTK_RESPONSE_ACCEPT);
response = gnc_dialog_run(GTK_DIALOG(dialog), warning);
gtk_widget_destroy (dialog);
if (response != GTK_RESPONSE_ACCEPT)
return;
gnc_split_register_delete_current_trans (reg);
return;
}
}
/**
* Deletes the current transaction.
**/
void
gnc_split_reg_delete_trans_cb(GtkWidget *widget, gpointer data)
{
GNCSplitReg *gsr = data;
gsr_emit_simple_signal( gsr, "delete_ent" );
}
void
gsr_default_dup_handler( GNCSplitReg *gsr, gpointer data )
{
gnc_split_register_duplicate_current
(gnc_ledger_display_get_split_register( gsr->ledger ));
}
/**
* Duplicates the current transaction in the register.
**/
void
gnc_split_reg_duplicate_trans_cb(GtkWidget *w, gpointer data)
{
GNCSplitReg *gsr = data;
gsr_emit_simple_signal( gsr, "dup_ent" );
}
/**
* Schedules the current transaction for recurring-entry.
* If the selected transaction was created from a scheduled transaction,
* opens the editor for that Scheduled Transaction.
**/
void
gsr_default_schedule_handler( GNCSplitReg *gsr, gpointer data )
{
GncGUID *fromSXId = NULL;
SchedXaction *theSX = NULL;
GList *sxElts;
SplitRegister *reg = gnc_ledger_display_get_split_register( gsr->ledger );
Transaction *pending_trans = gnc_split_register_get_current_trans (reg);
/* If the transaction has a sched-xact KVP frame, then go to the editor
* for the existing SX; otherwise, do the sx-from-trans dialog. */
qof_instance_get (QOF_INSTANCE (pending_trans),
"from-sched-xaction", &fromSXId,
NULL);
/* Get the correct SX */
for ( sxElts = gnc_book_get_schedxactions (gnc_get_current_book())->sx_list;
(!theSX) && sxElts;
sxElts = sxElts->next )
{
SchedXaction *sx = (SchedXaction*)sxElts->data;
theSX =
((guid_equal (xaccSchedXactionGetGUID (sx), fromSXId))
? sx : NULL);
}
guid_free (fromSXId);
if ( theSX )
{
gnc_ui_scheduled_xaction_editor_dialog_create(GTK_WINDOW(data), theSX, FALSE);
return;
}
gnc_sx_create_from_trans(GTK_WINDOW(data), pending_trans);
}
void
gnc_split_reg_recur_cb(GtkWidget *w, gpointer data)
{
GNCSplitReg *gsr = data;
gsr_emit_simple_signal( gsr, "schedule_ent" );
}
/**
* Records into the books the currently-selected transaction.
**/
void
gnc_split_reg_record_trans_cb (GtkWidget *w, gpointer data)
{
GNCSplitReg *gsr = data;
gsr_emit_simple_signal( gsr, "enter_ent" );
}
void
gsr_default_cancel_handler( GNCSplitReg *gsr, gpointer data )
{
gnc_split_register_cancel_cursor_trans_changes
(gnc_ledger_display_get_split_register( gsr->ledger ));
}
/**
* Cancels the edits of the currently-selected transaction.
**/
void
gnc_split_reg_cancel_trans_cb(GtkWidget *w, gpointer data)
{
GNCSplitReg *gsr = data;
gsr_emit_simple_signal( gsr, "cancel_ent" );
}
void
gsr_default_expand_handler( GNCSplitReg *gsr, gpointer data )
{
gint activeCount;
gboolean expand;
SplitRegister *reg;
if (!gsr)
return;
reg = gnc_ledger_display_get_split_register (gsr->ledger);
/* These should all be in agreement. */
activeCount =
( ( gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM(gsr->split_menu_check)) ? 1 : -1 )
+ ( gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM(gsr->split_popup_check)) ? 1 : -1 )
+ ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(gsr->split_button) )
? 1 : -1 ) );
/* If activeCount > 0, then there's more active than inactive; otherwise,
* more inactive than active. Both determine which state the user is
* attempting to get to. */
expand = ( activeCount < 0 );
/* The ledger's invocation of 'redraw_all' will force the agreement in the
* other split state widgets, so we neglect doing it here. */
gnc_split_register_expand_current_trans (reg, expand);
}
void
gnc_split_reg_expand_trans_menu_cb (GtkWidget *widget, gpointer data)
{
GNCSplitReg *gsr = data;
gsr_emit_simple_signal( gsr, "expand_ent" );
}
void
gnc_split_reg_expand_trans_toolbar_cb (GtkWidget *widget, gpointer data)
{
GNCSplitReg *gsr = data;
gsr_emit_simple_signal( gsr, "expand_ent" );
}
gboolean
gnc_split_reg_clear_filter_for_split (GNCSplitReg *gsr, Split *split)
{
VirtualCellLocation vcell_loc;
SplitRegister *reg;
if (!gsr)
return FALSE;
reg = gnc_ledger_display_get_split_register (gsr->ledger);
if (!gnc_split_register_get_split_virt_loc (reg, split, &vcell_loc))
{
gint response = gnc_ok_cancel_dialog (GTK_WINDOW(gsr->window),
GTK_RESPONSE_CANCEL,
(_("Target split is currently hidden in this register.\n\n%s\n\n"
"Select OK to temporarily clear filter and proceed,\n"
"otherwise the last active cell will be selected.")),
gsr->filter_text);
if (response == GTK_RESPONSE_OK)
return TRUE;
}
return FALSE;
}
/**
* move the cursor to the split, if present in register
**/
void
gnc_split_reg_jump_to_split(GNCSplitReg *gsr, Split *split)
{
Transaction *trans;
VirtualCellLocation vcell_loc;
SplitRegister *reg;
if (!gsr) return;
trans = xaccSplitGetParent(split);
gsr_emit_include_date_signal( gsr, xaccTransGetDate(trans) );
reg = gnc_ledger_display_get_split_register( gsr->ledger );
if (gnc_split_register_get_split_virt_loc(reg, split, &vcell_loc))
gnucash_register_goto_virt_cell( gsr->reg, vcell_loc );
gnc_ledger_display_refresh( gsr->ledger );
}
/**
* Move the cursor to the split in the non-blank amount column.
**/
void
gnc_split_reg_jump_to_split_amount(GNCSplitReg *gsr, Split *split)
{
VirtualLocation virt_loc;
SplitRegister *reg;
Transaction *trans;
if (!gsr) return;
trans = xaccSplitGetParent(split);
gsr_emit_include_date_signal( gsr, xaccTransGetDate(trans) );
reg = gnc_ledger_display_get_split_register (gsr->ledger);
if (gnc_split_register_get_split_amount_virt_loc (reg, split, &virt_loc))
gnucash_register_goto_virt_loc (gsr->reg, virt_loc);
gnc_ledger_display_refresh (gsr->ledger);
}
void
gnc_split_reg_jump_to_blank (GNCSplitReg *gsr)
{
SplitRegister *reg = gnc_ledger_display_get_split_register (gsr->ledger);
VirtualCellLocation vcell_loc;
Split *blank;
ENTER("gsr=%p", gsr);
blank = gnc_split_register_get_blank_split (reg);
if (blank == NULL)
{
LEAVE("no blank split");
return;
}
if (gnc_split_register_get_split_virt_loc (reg, blank, &vcell_loc))
gnucash_register_goto_virt_cell (gsr->reg, vcell_loc);
gnc_ledger_display_refresh (gsr->ledger);
LEAVE(" ");
}
void
gnc_split_reg_focus_on_sheet (GNCSplitReg *gsr)
{
GnucashRegister *reg = gsr->reg;
GnucashSheet *sheet = gnucash_register_get_sheet (reg);
// Make sure the sheet is the focus
if (!gtk_widget_has_focus(GTK_WIDGET (sheet)))
gtk_widget_grab_focus (GTK_WIDGET (sheet));
}
void
gnc_split_reg_set_sheet_focus (GNCSplitReg *gsr, gboolean has_focus)
{
GnucashRegister *reg = gsr->reg;
GnucashSheet *sheet = gnucash_register_get_sheet (reg);
gnucash_sheet_set_has_focus (sheet, has_focus);
}
void
gnc_split_reg_balancing_entry(GNCSplitReg *gsr, Account *account,
time64 statement_date, gnc_numeric balancing_amount)
{
Transaction *transaction;
Split *split;
// create transaction
transaction = 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("create_balancing_transaction failed");
gnc_split_reg_jump_to_blank(gsr);
}
else
{
// goto balancing transaction
gnc_split_reg_jump_to_split(gsr, split );
}
}
static Transaction*
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"));
/* We also must set a new DateEntered on the new entry
* because otherwise the ordering is not deterministic */
xaccTransSetDateEnteredSecs(trans, gnc_time(NULL));
// 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;
}
void
gsr_default_blank_handler( GNCSplitReg *gsr, gpointer data )
{
SplitRegister *reg;
ENTER("gsr=%p, gpointer=%p", gsr, data);
reg = gnc_ledger_display_get_split_register (gsr->ledger);
if (gnc_split_register_save (reg, TRUE))
gnc_split_register_redraw (reg);
gnc_split_reg_jump_to_blank (gsr);
LEAVE(" ");
}
void
gnc_split_reg_new_trans_cb (GtkWidget *widget, gpointer data)
{
GNCSplitReg *gsr = data;
gsr_emit_simple_signal( gsr, "blank" );
}
void
gsr_default_jump_handler( GNCSplitReg *gsr, gpointer data )
{
g_assert_not_reached();
}
void
gnc_split_reg_jump_cb( GtkWidget *widget, gpointer data )
{
GNCSplitReg *gsr = data;
gsr_emit_simple_signal( gsr, "jump" );
}
void
gnc_split_reg_change_style (GNCSplitReg *gsr, SplitRegisterStyle style, gboolean refresh)
{
SplitRegister *reg = gnc_ledger_display_get_split_register (gsr->ledger);
if (style == reg->style)
return;
gnc_split_register_config (reg, reg->type, style, reg->use_double_line);
if (refresh)
gnc_ledger_display_refresh (gsr->ledger);
}
void
gnc_split_reg_style_ledger_cb (GtkWidget *w, gpointer data)
{
GNCSplitReg *gsr = data;
if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM(w)))
return;
gnc_split_reg_change_style (gsr, REG_STYLE_LEDGER, TRUE);
}
void
gnc_split_reg_style_auto_ledger_cb (GtkWidget *w, gpointer data)
{
GNCSplitReg *gsr = data;
if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM(w)))
return;
gnc_split_reg_change_style (gsr, REG_STYLE_AUTO_LEDGER, TRUE);
}
void
gnc_split_reg_style_journal_cb (GtkWidget *w, gpointer data)
{
GNCSplitReg *gsr = data;
if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM(w)))
return;
gnc_split_reg_change_style (gsr, REG_STYLE_JOURNAL, TRUE);
}
void
gnc_split_reg_double_line_cb (GtkWidget *w, gpointer data)
{
GNCSplitReg *gsr = data;
SplitRegister *reg = gnc_ledger_display_get_split_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 == reg->use_double_line )
return;
gnc_split_register_config( reg, reg->type, reg->style, use_double_line );
gnc_ledger_display_refresh( gsr->ledger );
}
static void
gnc_split_reg_sort_force( GNCSplitReg *gsr, SortType sort_code, gboolean force )
{
Query *query = gnc_ledger_display_get_query( gsr->ledger );
gboolean show_present_divider = FALSE;
GSList *p1 = NULL, *p2 = NULL, *p3 = NULL, *standard;
SplitRegister *reg;
if ((gsr->sort_type == sort_code) && !force)
return;
standard = g_slist_prepend( NULL, QUERY_DEFAULT_SORT );
switch (sort_code)
{
case BY_STANDARD:
p1 = standard;
show_present_divider = TRUE;
break;
case BY_DATE:
p1 = g_slist_prepend (p1, TRANS_DATE_POSTED);
p1 = g_slist_prepend (p1, SPLIT_TRANS);
p2 = standard;
show_present_divider = TRUE;
break;
case BY_DATE_ENTERED:
p1 = g_slist_prepend (p1, TRANS_DATE_ENTERED);
p1 = g_slist_prepend (p1, SPLIT_TRANS);
p2 = standard;
break;
case BY_DATE_RECONCILED:
p1 = g_slist_prepend (p1, SPLIT_RECONCILE);
p2 = g_slist_prepend (p2, SPLIT_DATE_RECONCILED);
p3 = standard;
break;
case BY_NUM:
p1 = g_slist_prepend (p1, TRANS_NUM);
p1 = g_slist_prepend (p1, SPLIT_TRANS);
p2 = standard;
break;
case BY_AMOUNT:
p1 = g_slist_prepend (p1, SPLIT_VALUE);
p2 = standard;
break;
case BY_MEMO:
p1 = g_slist_prepend (p1, SPLIT_MEMO);
p2 = standard;
break;
case BY_DESC:
p1 = g_slist_prepend (p1, TRANS_DESCRIPTION);
p1 = g_slist_prepend (p1, SPLIT_TRANS);
p2 = standard;
break;
case BY_ACTION:
p1 = g_slist_prepend (p1, SPLIT_ACTION);
p2 = standard;
break;
case BY_NOTES:
p1 = g_slist_prepend (p1, TRANS_NOTES);
p1 = g_slist_prepend (p1, SPLIT_TRANS);
p2 = standard;
break;
default:
g_slist_free (standard);
g_return_if_fail (FALSE);
break;
}
qof_query_set_sort_order( query, p1, p2, p3 );
reg = gnc_ledger_display_get_split_register( gsr->ledger );
gnc_split_register_show_present_divider( reg, show_present_divider );
gsr->sort_type = sort_code;
gnc_ledger_display_refresh( gsr->ledger );
}
static void
gnc_split_reg_sort( GNCSplitReg *gsr, SortType sort_code )
{
gnc_split_reg_sort_force( gsr, sort_code, FALSE );
}
void
gnc_split_reg_sort_standard_cb(GtkWidget *w, gpointer data)
{
GNCSplitReg *gsr = data;
gnc_split_reg_sort(gsr, BY_STANDARD);
}
void
gnc_split_reg_sort_date_cb(GtkWidget *w, gpointer data)
{
GNCSplitReg *gsr = data;
gnc_split_reg_sort(gsr, BY_DATE);
}
void
gnc_split_reg_sort_date_entered_cb(GtkWidget *w, gpointer data)
{
GNCSplitReg *gsr = data;
gnc_split_reg_sort(gsr, BY_DATE_ENTERED);
}
void
gnc_split_reg_sort_date_reconciled_cb(GtkWidget *w, gpointer data)
{
GNCSplitReg *gsr = data;
gnc_split_reg_sort(gsr, BY_DATE_RECONCILED);
}
void
gnc_split_reg_sort_num_cb(GtkWidget *w, gpointer data)
{
GNCSplitReg *gsr = data;
gnc_split_reg_sort(gsr, BY_NUM);
}
void
gnc_split_reg_sort_amount_cb(GtkWidget *w, gpointer data)
{
GNCSplitReg *gsr = data;
gnc_split_reg_sort(gsr, BY_AMOUNT);
}
void
gnc_split_reg_sort_memo_cb(GtkWidget *w, gpointer data)
{
GNCSplitReg *gsr = data;
gnc_split_reg_sort(gsr, BY_MEMO);
}
void
gnc_split_reg_sort_desc_cb(GtkWidget *w, gpointer data)
{
GNCSplitReg *gsr = data;
gnc_split_reg_sort(gsr, BY_DESC);
}
void
gnc_split_reg_sort_action_cb(GtkWidget *w, gpointer data)
{
GNCSplitReg *gsr = data;
gnc_split_reg_sort(gsr, BY_ACTION);
}
void
gnc_split_reg_sort_notes_cb(GtkWidget *w, gpointer data)
{
GNCSplitReg *gsr = data;
gnc_split_reg_sort(gsr, BY_NOTES);
}
void
gnc_split_reg_set_sort_reversed(GNCSplitReg *gsr, gboolean rev, gboolean refresh)
{
/* Note: sort_reversed is the boolean opposite of sort_increasing
* so when rev == true, we're sorting decreasing
* In other words, qof_query_set_sort_increasing should
* always use the inverse of rev.
*/
Query *query = gnc_ledger_display_get_query( gsr->ledger );
qof_query_set_sort_increasing (query, !rev, !rev, !rev);
gsr->sort_rev = rev;
if (refresh)
gnc_ledger_display_refresh( gsr->ledger );
}
static gboolean
gnc_split_reg_record (GNCSplitReg *gsr)
{
SplitRegister *reg;
Transaction *trans;
ENTER("gsr=%p", gsr);
reg = gnc_ledger_display_get_split_register (gsr->ledger);
trans = gnc_split_register_get_current_trans (reg);
if (!gnc_split_register_save (reg, TRUE))
{
LEAVE("no save");
return FALSE;
}
gsr_emit_include_date_signal( gsr, xaccTransGetDate(trans) );
/* Explicit redraw shouldn't be needed,
* since gui_refresh events should handle this. */
/* gnc_split_register_redraw (reg); */
LEAVE(" ");
return TRUE;
}
static gboolean
gnc_split_reg_match_trans_row( VirtualLocation virt_loc,
gpointer user_data )
{
GNCSplitReg *gsr = user_data;
CursorClass cursor_class;
SplitRegister *sr;
sr = gnc_ledger_display_get_split_register (gsr->ledger);
cursor_class = gnc_split_register_get_cursor_class (sr, virt_loc.vcell_loc);
return (cursor_class == CURSOR_CLASS_TRANS);
}
static void
gnc_split_reg_goto_next_trans_row (GNCSplitReg *gsr)
{
ENTER("gsr=%p", gsr);
gnucash_register_goto_next_matching_row( gsr->reg,
gnc_split_reg_match_trans_row,
gsr );
LEAVE(" ");
}
void
gnc_split_reg_enter( GNCSplitReg *gsr, gboolean next_transaction )
{
SplitRegister *sr = gnc_ledger_display_get_split_register( gsr->ledger );
gboolean goto_blank;
ENTER("gsr=%p, next_transaction=%s", gsr, next_transaction ? "TRUE" : "FALSE");
goto_blank = gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL_REGISTER,
GNC_PREF_ENTER_MOVES_TO_END);
/* If we are in single or double line mode and we hit enter
* on the blank split, go to the blank split instead of the
* next row. This prevents the cursor from jumping around
* when you are entering transactions. */
if ( !goto_blank && !next_transaction )
{
SplitRegisterStyle style = sr->style;
if (style == REG_STYLE_LEDGER)
{
Split *blank_split;
blank_split = gnc_split_register_get_blank_split(sr);
if (blank_split != NULL)
{
Split *current_split;
current_split = gnc_split_register_get_current_split(sr);
if (blank_split == current_split)
goto_blank = TRUE;
}
}
}
/* First record the transaction. This will perform a refresh. */
if (!gnc_split_reg_record (gsr))
{
/* we may come here from the transfer cell if we decline to create a
* new account, make sure the sheet has the focus if the record is FALSE
* which results in no cursor movement. */
gnc_split_reg_focus_on_sheet (gsr);
/* if there are no changes, just enter was pressed, proceed to move
* other wise lets not move. */
if (gnc_table_current_cursor_changed (sr->table, FALSE))
{
LEAVE(" ");
return;
}
}
if (!goto_blank && next_transaction)
gnc_split_register_expand_current_trans (sr, FALSE);
/* Now move. */
if (goto_blank)
gnc_split_reg_jump_to_blank( gsr );
else if (next_transaction)
gnc_split_reg_goto_next_trans_row( gsr );
else
gnucash_register_goto_next_virt_row( gsr->reg );
LEAVE(" ");
}
void
gsr_default_enter_handler( GNCSplitReg *gsr, gpointer data )
{
gnc_split_reg_enter( gsr, FALSE );
}
void
gnc_split_reg_record_cb (GnucashRegister *reg, gpointer data)
{
gsr_emit_simple_signal( (GNCSplitReg*)data, "enter_ent" );
}
void
gnc_split_reg_size_allocate (GtkWidget *widget,
GtkAllocation *allocation,
gpointer user_data)
{
GNCSplitReg *gsr = user_data;
gsr->width = allocation->width;
gtk_window_set_default_size( GTK_WINDOW(gsr->window), gsr->width, 0 );
}
static
GtkWidget*
add_summary_label (GtkWidget *summarybar, gboolean pack_start, const char *label_str, GtkWidget *extra)
{
GtkWidget *hbox;
GtkWidget *text_label, *secondary_label;
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
gtk_box_set_homogeneous (GTK_BOX (hbox), FALSE);
if (pack_start)
gtk_box_pack_start( GTK_BOX(summarybar), hbox, FALSE, FALSE, 5 );
else
gtk_box_pack_end( GTK_BOX(summarybar), hbox, FALSE, FALSE, 5 );
text_label = gtk_label_new (label_str);
gnc_label_set_alignment (text_label, 1.0, 0.5 );
gtk_label_set_ellipsize (GTK_LABEL(text_label), PANGO_ELLIPSIZE_END);
gtk_box_pack_start (GTK_BOX(hbox), text_label, FALSE, FALSE, 0);
secondary_label = gtk_label_new ( "" );
g_object_set_data (G_OBJECT(secondary_label), "text_label", text_label);
g_object_set_data (G_OBJECT(secondary_label), "text_box", hbox);
gnc_label_set_alignment (secondary_label, 1.0, 0.5 );
gtk_box_pack_start (GTK_BOX(hbox), secondary_label, FALSE, FALSE, 0);
if (extra != NULL)
gtk_box_pack_start( GTK_BOX(hbox), extra, FALSE, FALSE, 0 );
return secondary_label;
}
static void
gsr_summarybar_set_arrow_draw (GNCSplitReg *gsr)
{
if (gsr->sort_arrow_handler_id > 0)
g_signal_handler_disconnect (G_OBJECT(gsr->sort_arrow), gsr->sort_arrow_handler_id);
gsr->sort_arrow_handler_id = g_signal_connect (G_OBJECT (gsr->sort_arrow), "draw",
G_CALLBACK (gnc_draw_arrow_cb), GINT_TO_POINTER(gsr->sort_rev));
gtk_widget_queue_draw (gsr->sort_arrow);
}
GtkWidget *
gsr_create_summary_bar( GNCSplitReg *gsr )
{
GtkWidget *summarybar = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
gtk_box_set_homogeneous (GTK_BOX (summarybar), FALSE);
gtk_widget_set_name (summarybar, "gnc-id-summarybar");
gsr->cleared_label = NULL;
gsr->balance_label = NULL;
gsr->reconciled_label = NULL;
gsr->future_label = NULL;
gsr->projectedminimum_label = NULL;
gsr->sort_label = NULL;
gsr->sort_arrow = NULL;
gsr->filter_label = NULL;
gsr->shares_label = NULL;
gsr->value_label = NULL;
if (gnc_ledger_display_type(gsr->ledger) == LD_SINGLE)
{
if (!xaccAccountIsPriced(gnc_ledger_display_leader(gsr->ledger)))
{
gsr->balance_label = add_summary_label (summarybar, TRUE, _("Present:"), NULL);
gsr->future_label = add_summary_label (summarybar, TRUE, _("Future:"), NULL);
gsr->cleared_label = add_summary_label (summarybar, TRUE, _("Cleared:"), NULL);
gsr->reconciled_label = add_summary_label (summarybar, TRUE, _("Reconciled:"), NULL);
gsr->projectedminimum_label = add_summary_label (summarybar, TRUE, _("Projected Minimum:"), NULL);
}
else
{
gsr->shares_label = add_summary_label (summarybar, TRUE, _("Shares:"), NULL);
gsr->value_label = add_summary_label (summarybar, TRUE, _("Current Value:"), NULL);
}
}
gsr->filter_label = add_summary_label (summarybar, FALSE, "", NULL);
gsr->sort_arrow = gtk_image_new_from_icon_name ("image-missing", GTK_ICON_SIZE_SMALL_TOOLBAR);
gsr->sort_label = add_summary_label (summarybar, FALSE, _("Sort By:"), gsr->sort_arrow);
gnc_widget_style_context_add_class (GTK_WIDGET(gsr->filter_label), "gnc-class-highlight");
gnc_widget_style_context_add_class (GTK_WIDGET(gsr->sort_arrow), "gnc-class-highlight");
gsr->summarybar = summarybar;
/* Force the first update */
gsr_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_reg_get_placeholder( GNCSplitReg *gsr )
{
Account *leader;
SplitRegister *reg;
gboolean single_account;
if (gsr == NULL)
return PLACEHOLDER_NONE;
reg = gnc_ledger_display_get_split_register( gsr->ledger );
switch (reg->type)
{
case GENERAL_JOURNAL:
case INCOME_LEDGER:
case PORTFOLIO_LEDGER:
case SEARCH_LEDGER:
single_account = FALSE;
break;
default:
single_account = TRUE;
break;
}
leader = gnc_ledger_display_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
{
GNCSplitReg *gsr;
gchar *string;
} dialog_args;
/**
* 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_reg_determine_read_only( GNCSplitReg *gsr )
{
SplitRegister *reg;
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;
char *string = NULL;
reg = gnc_ledger_display_get_split_register( gsr->ledger );
if(reg->mismatched_commodities)
{
string = _("The transactions of this account may not be edited "
"because its subaccounts have mismatched commodities "
"or currencies.\n"
"You need to open each account individually to edit "
"transactions.");
}
else
{
switch (gnc_split_reg_get_placeholder(gsr))
{
case PLACEHOLDER_NONE:
/* stay as false. */
return;
case PLACEHOLDER_THIS:
string = _("The transactions of this account may not be edited.\n"
"If you want to edit transactions in this register, "
"please open the account options and turn off the "
"placeholder checkbox.");
break;
default:
string = _("The transactions in one of the selected "
"sub-accounts may not be edited.\n"
"If you want to edit transactions in this register, please open "
"the sub-account options and turn off the placeholder checkbox.\n"
"You may also open an individual account instead "
"of a set of accounts.");
break;
}
}
gsr->read_only = TRUE;
/* Put up a warning dialog */
args = g_malloc(sizeof(dialog_args));
args->string = string;
args->gsr = gsr;
g_timeout_add (250, gtk_callback_bug_workaround, args); /* 0.25 seconds */
}
/* Make the contents immutable */
reg = gnc_ledger_display_get_split_register( gsr->ledger );
gnc_split_register_set_read_only( reg, TRUE );
}
static
GtkWidget *
gnc_split_reg_get_parent( GNCLedgerDisplay *ledger )
{
GNCSplitReg *gsr =
GNC_SPLIT_REG(gnc_ledger_display_get_user_data( ledger ));
if (gsr == NULL)
return NULL;
return gsr->window;
}
static
void
gsr_emit_help_changed( GnucashRegister *reg, gpointer user_data )
{
gsr_emit_simple_signal( (GNCSplitReg*)user_data, "help-changed" );
}
static
void
gsr_emit_show_popup_menu( GnucashRegister *reg, gpointer user_data )
{
gsr_emit_simple_signal( (GNCSplitReg*)user_data, "show-popup-menu" );
}
static
void
gsr_emit_include_date_signal( GNCSplitReg *gsr, time64 date )
{
g_signal_emit_by_name( gsr, "include-date", date, NULL );
}
static
void
gsr_emit_simple_signal( GNCSplitReg *gsr, const char *sigName )
{
g_signal_emit_by_name( gsr, sigName, NULL );
}
GnucashRegister*
gnc_split_reg_get_register( GNCSplitReg *gsr )
{
if ( !gsr )
return NULL;
return gsr->reg;
}
SortType
gnc_split_reg_get_sort_type( GNCSplitReg *gsr )
{
g_assert( gsr );
return gsr->sort_type;
}
void
gnc_split_reg_set_sort_type( GNCSplitReg *gsr, SortType t )
{
gnc_split_reg_sort( gsr, t );
}
void
gnc_split_reg_set_sort_type_force( GNCSplitReg *gsr, SortType t, gboolean force )
{
gnc_split_reg_sort_force( gsr, t, force );
}
GtkWidget*
gnc_split_reg_get_summarybar( GNCSplitReg *gsr )
{
if ( !gsr ) return NULL;
return gsr->summarybar;
}
gboolean
gnc_split_reg_get_read_only( GNCSplitReg *gsr )
{
g_assert( gsr );
return gsr->read_only;
}
void
gnc_split_reg_set_moved_cb( GNCSplitReg *gsr, GFunc cb, gpointer cb_data )
{
gnucash_register_set_moved_cb (gsr->reg, cb, cb_data);
}