38 #include <glib/gi18n.h>
47 #include "dialog-sx-editor.h"
48 #include "dialog-utils.h"
49 #include "gnc-component-manager.h"
51 #include "gnc-date-edit.h"
52 #include "gnc-dense-cal.h"
53 #include "gnc-dense-cal-store.h"
56 #include "gnc-frequency.h"
57 #include "gnc-gui-query.h"
58 #include "gnc-hooks.h"
59 #include "gnc-ledger-display.h"
65 #include "gnucash-sheet.h"
67 #include "gnc-split-reg.h"
70 #include "dialog-sx-since-last-run.h"
73 #define G_LOG_DOMAIN "gnc.gui.sx.editor"
77 static gint _sx_engine_event_handler_id = -1;
79 #define END_NEVER_OPTION 0
80 #define END_DATE_OPTION 1
81 #define NUM_OCCUR_OPTION 2
83 #define NUM_LEDGER_LINES_DEFAULT 6
85 #define EX_CAL_NUM_MONTHS 6
86 #define EX_CAL_MO_PER_COL 3
88 #define GNC_D_WIDTH 25
89 #define GNC_D_BUF_WIDTH 26
93 typedef enum _EndTypeEnum
100 typedef enum { NO_END, DATE_END, COUNT_END } END_TYPE;
106 GtkNotebook *notebook;
118 GtkEditable *nameEntry;
120 GtkLabel *lastOccurLabel;
122 GtkToggleButton *enabledOpt;
123 GtkToggleButton *autocreateOpt;
124 GtkToggleButton *notifyOpt;
125 GtkToggleButton *advanceOpt;
126 GtkSpinButton *advanceSpin;
127 GtkToggleButton *remindOpt;
128 GtkSpinButton *remindSpin;
130 GtkToggleButton *optEndDate;
131 GtkToggleButton *optEndNone;
132 GtkToggleButton *optEndCount;
133 GtkEntry *endCountSpin;
134 GtkEntry *endRemainSpin;
148 static void endgroup_rb_toggled_cb( GtkButton *b, gpointer d );
155 static void gnc_sxed_freq_changed(
GncFrequency *gf, gpointer ud );
156 static void sxed_excal_update_adapt_cb( GtkObject *o, gpointer ud );
158 void on_sx_check_toggled_cb (GtkWidget *togglebutton, gpointer user_data);
160 static gboolean sxed_delete_event( GtkWidget *widget, GdkEvent *event, gpointer ud );
162 static gboolean editor_component_sx_equality( gpointer
find_data,
163 gpointer user_data );
165 static GtkActionEntry gnc_sxed_menu_entries [] =
167 {
"EditAction", NULL, N_(
"_Edit"), NULL, NULL, NULL },
168 {
"TransactionAction", NULL, N_(
"_Transaction"), NULL, NULL, NULL },
169 {
"ViewAction", NULL, N_(
"_View"), NULL, NULL, NULL },
170 {
"ActionsAction", NULL, N_(
"_Actions"), NULL, NULL, NULL },
172 static guint gnc_sxed_menu_n_entries = G_N_ELEMENTS (gnc_sxed_menu_entries);
177 sxed_close_handler(gpointer user_data)
181 gnc_sxed_reg_check_close(sxed);
182 gnc_save_window_size( GNC_PREFS_GROUP_SXED, GTK_WINDOW(sxed->dialog) );
183 gtk_widget_destroy(sxed->dialog);
197 reg = gnc_ledger_display_get_split_register( sxed->ledger );
199 if ( gnc_sxed_check_changed( sxed ) )
201 const char *sx_changed_msg =
202 _(
"This Scheduled Transaction has changed; are you "
203 "sure you want to cancel?" );
204 if (!gnc_verify_dialog(sxed->dialog, FALSE,
"%s", sx_changed_msg))
222 if (!sxed_confirmed_cancel(sxed))
225 gnc_close_gui_component_by_data( DIALOG_SCHEDXACTION_EDITOR_CM_CLASS,
243 if ( !gnc_sxed_check_consistent( sxed ) )
246 gnc_sxed_save_sx( sxed );
252 book = gnc_get_current_book ();
253 sxes = gnc_book_get_schedxactions(book);
254 gnc_sxes_add_sx(sxes, sxed->sx);
255 sxed->newsxP = FALSE;
259 gnc_close_gui_component_by_data( DIALOG_SCHEDXACTION_EDITOR_CM_CLASS,
280 name = gtk_editable_get_chars( GTK_EDITABLE(sxed->nameEntry), 0, -1 );
281 if ( strlen(name) == 0 )
286 if ( (xaccSchedXactionGetName(sxed->sx) == NULL)
287 || (strcmp( xaccSchedXactionGetName(sxed->sx),
297 if ( gtk_toggle_button_get_active( sxed->optEndNone ) )
299 if ( xaccSchedXactionHasEndDate(sxed->sx)
307 if ( gtk_toggle_button_get_active( sxed->optEndDate ) )
309 GDate sxEndDate, dlgEndDate;
311 if ( ! xaccSchedXactionHasEndDate( sxed->sx ) )
317 gnc_date_edit_get_date( sxed->
320 if ( g_date_compare( &sxEndDate, &dlgEndDate ) != 0 )
327 if ( gtk_toggle_button_get_active( sxed->optEndCount ) )
329 gint sxNumOccur, sxNumRem, dlgNumOccur, dlgNumRem;
331 if ( ! xaccSchedXactionGetNumOccur( sxed->sx ) )
337 gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(sxed->endCountSpin) );
340 gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(sxed->endRemainSpin) );
342 sxNumOccur = xaccSchedXactionGetNumOccur( sxed->sx );
343 sxNumRem = xaccSchedXactionGetRemOccur( sxed->sx );
345 if ( (dlgNumOccur != sxNumOccur)
346 || (dlgNumRem != sxNumRem) )
361 gint dlgAdvance, sxAdvance;
362 gint dlgRemind, sxRemind;
365 gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(sxed->
368 gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(sxed->
371 gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(sxed->
374 sxEnabled = xaccSchedXactionGetEnabled( sxed->sx );
375 if ( ! ((dlgEnabled == sxEnabled)) )
380 xaccSchedXactionGetAutoCreate( sxed->sx, &sxAutoCreate, &sxNotify );
381 if ( ! ((dlgAutoCreate == sxAutoCreate)
382 && (dlgNotify == sxNotify)) )
388 if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(sxed->advanceOpt) ) )
391 gtk_spin_button_get_value_as_int( GTK_SPIN_BUTTON(sxed->
394 sxAdvance = xaccSchedXactionGetAdvanceCreation( sxed->sx );
395 if ( dlgAdvance != sxAdvance )
401 if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(sxed->remindOpt) ) )
404 gtk_spin_button_get_value_as_int( GTK_SPIN_BUTTON(sxed->remindSpin) );
406 sxRemind = xaccSchedXactionGetAdvanceReminder( sxed->sx );
407 if ( dlgRemind != sxRemind )
414 GList *dialog_schedule = NULL;
415 GDate dialog_start_date, sx_start_date;
416 gchar *dialog_schedule_str, *sx_schedule_str;
417 gboolean schedules_are_the_same, start_dates_are_the_same;
419 g_date_clear(&dialog_start_date, 1);
420 gnc_frequency_save_to_recurrence(sxed->gncfreq, &dialog_schedule, &dialog_start_date);
421 dialog_schedule_str = recurrenceListToString(dialog_schedule);
422 recurrenceListFree(&dialog_schedule);
424 sx_start_date = *xaccSchedXactionGetStartDate(sxed->sx);
427 g_debug(
"dialog schedule [%s], sx schedule [%s]",
428 dialog_schedule_str, sx_schedule_str);
430 schedules_are_the_same = (strcmp(dialog_schedule_str, sx_schedule_str) == 0);
431 g_free(dialog_schedule_str);
432 g_free(sx_schedule_str);
434 start_dates_are_the_same = (g_date_compare(&dialog_start_date, &sx_start_date) == 0);
436 if (!schedules_are_the_same || !start_dates_are_the_same)
443 gnc_ledger_display_get_split_register( sxed->ledger );
467 set_sums_to_zero( gpointer key,
472 tcds->creditSum = gnc_numeric_zero();
473 tcds->debitSum = gnc_numeric_zero();
478 check_credit_debit_balance( gpointer key,
483 gboolean *unbalanced = (gboolean*)ud;
485 gnc_numeric_sub_fixed( tcds->debitSum,
486 tcds->creditSum ) ));
491 tcds->creditSum ) ) )
493 g_debug(
"%p | true [%s - %s = %s]",
498 tcds->creditSum )) );
502 g_debug(
"%p | false [%s - %s = %s]",
507 tcds->creditSum )) );
520 gboolean multi_commodity = FALSE;
522 gint ttVarCount, splitCount;
523 GList *schedule = NULL;
547 static const int NUM_ITERS_WITH_VARS = 5;
548 static const int NUM_ITERS_NO_VARS = 1;
550 GHashTable *vars, *txns;
551 GList *splitList = NULL;
552 gchar *credit_formula = NULL, *debit_formula = NULL;
556 gboolean unbalanceable;
557 gpointer unusedKey, unusedValue;
559 unbalanceable = FALSE;
560 vars = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
561 (GDestroyNotify)gnc_sx_variable_free);
562 txns = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL,
564 numIters = NUM_ITERS_NO_VARS;
579 gnc_sx_get_variables( sxed->sx, vars );
581 ttVarCount = g_hash_table_size( vars );
582 if ( ttVarCount != 0 )
593 numIters = NUM_ITERS_WITH_VARS;
596 for ( i = 0; i < numIters && !unbalanceable; i++ )
598 gnc_sx_randomize_variables(vars);
599 g_hash_table_foreach( txns, set_sums_to_zero, NULL );
600 tmp = gnc_numeric_zero();
602 splitList = xaccSchedXactionGetSplits( sxed->sx );
603 splitCount += g_list_length( splitList );
605 for ( ; splitList; splitList = splitList->next )
612 s = (
Split*)splitList->data;
620 tcds->creditSum = gnc_numeric_zero();
621 tcds->debitSum = gnc_numeric_zero();
622 g_hash_table_insert( txns, (gpointer)t, (gpointer)tcds );
626 "sx-account", &acct_guid,
627 "sx-credit-formula", &credit_formula,
628 "sx-debit-formula", &debit_formula,
632 if (base_cmdty == NULL)
634 base_cmdty = split_cmdty;
638 if ( g_strcmp0 (credit_formula,
"") != 0 &&
639 gnc_sx_parse_vars_from_formula(credit_formula, vars,
644 errStr = g_string_sized_new( 32 );
645 g_string_printf( errStr,
646 _(
"Couldn't parse credit formula for "
649 gnc_error_dialog( GTK_WIDGET(sxed->dialog),
"%s",
651 g_string_free( errStr, TRUE );
658 tmp = gnc_numeric_zero();
659 if ( g_strcmp0 (debit_formula,
"") != 0 &&
660 gnc_sx_parse_vars_from_formula( debit_formula, vars,
665 errStr = g_string_sized_new( 32 );
666 g_string_printf( errStr,
667 _(
"Couldn't parse debit formula for "
670 gnc_error_dialog( GTK_WIDGET(sxed->dialog),
"%s",
671 (gchar*)errStr->str );
672 g_string_free( errStr, TRUE );
678 tmp = gnc_numeric_zero();
681 g_hash_table_foreach( txns,
682 check_credit_debit_balance,
687 if (g_hash_table_lookup_extended(vars,
"i",
694 g_hash_table_destroy(vars);
695 g_hash_table_destroy(txns);
698 && !gnc_verify_dialog( sxed->dialog, FALSE,
700 _(
"The Scheduled Transaction Editor "
701 "cannot automatically balance "
703 "Should it still be "
713 gchar *name, *nameKey;
714 gboolean nameExists, nameHasChanged;
717 name = gtk_editable_get_chars( GTK_EDITABLE(sxed->nameEntry), 0, -1 );
718 if ( strlen(name) == 0 )
720 const char *sx_has_no_name_msg =
721 _(
"Please name the Scheduled Transaction." );
722 gnc_error_dialog( sxed->dialog,
"%s", sx_has_no_name_msg );
729 nameKey = g_utf8_collate_key(name, -1);
731 (xaccSchedXactionGetName(sxed->sx) == NULL)
732 || (strcmp( xaccSchedXactionGetName(sxed->sx), name ) != 0);
734 gnc_book_get_schedxactions(gnc_get_current_book())->sx_list;
735 nameHasChanged && !nameExists && sxList;
736 sxList = sxList->next )
738 char *existingName, *existingNameKey;
742 existingNameKey = g_utf8_collate_key(existingName, -1);
743 nameExists |= ( strcmp(nameKey, existingNameKey) == 0 );
744 g_free( existingNameKey );
746 if ( nameHasChanged && nameExists )
748 const char *sx_has_existing_name_msg =
749 _(
"A Scheduled Transaction with the "
750 "name \"%s\" already exists. "
751 "Are you sure you want to name "
752 "this one the same?" );
753 if ( ! gnc_verify_dialog( sxed->dialog, FALSE,
754 sx_has_existing_name_msg,
768 gboolean autocreateState;
771 gtk_toggle_button_get_active (
772 GTK_TOGGLE_BUTTON(sxed->autocreateOpt));
774 if (((ttVarCount > 0) || multi_commodity) && autocreateState)
776 gnc_warning_dialog(sxed->dialog,
"%s",
777 _(
"Scheduled Transactions with variables "
778 "cannot be automatically created."));
784 if ( autocreateState && splitCount == 0 )
786 gnc_warning_dialog(sxed->dialog,
"%s",
787 _(
"Scheduled Transactions without a template "
788 "transaction cannot be automatically created.") );
795 GDate startDate, endDate, nextDate;
797 if ( !gtk_toggle_button_get_active(sxed->optEndDate)
798 && !gtk_toggle_button_get_active(sxed->optEndCount)
799 && !gtk_toggle_button_get_active(sxed->optEndNone) )
801 const char *sx_end_spec_msg =
802 _(
"Please provide a valid end selection." );
803 gnc_error_dialog( sxed->dialog,
"%s", sx_end_spec_msg );
807 if ( gtk_toggle_button_get_active(sxed->optEndCount))
812 gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(sxed->endCountSpin) );
815 gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(sxed->endRemainSpin) );
819 const char *sx_occur_count_zero_msg =
820 _(
"There must be some number of occurrences." );
821 gnc_error_dialog( sxed->dialog,
"%s",
822 sx_occur_count_zero_msg );
828 const char *sx_occur_counts_wrong_msg =
829 _(
"The number of remaining occurrences "
830 "(%d) is greater than the number of "
831 "total occurrences (%d)." );
832 gnc_error_dialog( sxed->dialog,
833 sx_occur_counts_wrong_msg,
840 g_date_clear( &endDate, 1 );
841 if ( gtk_toggle_button_get_active(sxed->optEndDate) )
844 gnc_date_edit_get_date( sxed->
848 g_date_clear(&nextDate, 1);
849 gnc_frequency_save_to_recurrence(sxed->gncfreq, &schedule, &startDate);
850 if (g_list_length(schedule) > 0)
852 g_date_subtract_days(&startDate, 1);
853 recurrenceListNextInstance(schedule, &startDate, &nextDate);
855 recurrenceListFree(&schedule);
857 if (!g_date_valid(&nextDate)
858 || (g_date_valid(&endDate) && (g_date_compare(&nextDate, &endDate) > 0)))
860 const char *invalid_sx_check_msg =
861 _(
"You have attempted to create a Scheduled "
862 "Transaction which will never run. Do you "
863 "really want to do this?");
864 if (!gnc_verify_dialog(sxed->dialog, FALSE,
865 "%s", invalid_sx_check_msg))
880 gnc_sx_begin_edit( sxed->sx );
886 name = gtk_editable_get_chars( sxed->nameEntry, 0, -1 );
895 if ( gtk_toggle_button_get_active(sxed->optEndDate) )
899 gnc_date_edit_get_date(
900 sxed->endDateEntry ) );
905 else if ( gtk_toggle_button_get_active(sxed->optEndCount) )
911 gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(sxed->endCountSpin) );
915 gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(sxed->endRemainSpin) );
916 xaccSchedXactionSetRemOccur( sxed->sx, num );
918 g_date_clear( &gdate, 1 );
921 else if ( gtk_toggle_button_get_active( sxed->optEndNone ) )
924 g_date_clear( &gdate, 1 );
929 g_critical(
"no valid end specified\n");
935 gboolean enabledState;
937 enabledState = gtk_toggle_button_get_active( sxed->enabledOpt );
938 xaccSchedXactionSetEnabled( sxed->sx, enabledState );
943 gboolean autocreateState, notifyState;
945 autocreateState = gtk_toggle_button_get_active( sxed->autocreateOpt );
946 notifyState = gtk_toggle_button_get_active( sxed->notifyOpt );
949 xaccSchedXactionSetAutoCreate( sxed->sx,
951 (autocreateState & notifyState) );
959 if ( gtk_toggle_button_get_active( sxed->advanceOpt ) )
962 gtk_spin_button_get_value_as_int( sxed->advanceSpin );
964 xaccSchedXactionSetAdvanceCreation( sxed->sx, daysInAdvance );
967 if ( gtk_toggle_button_get_active( sxed->remindOpt ) )
970 gtk_spin_button_get_value_as_int( sxed->remindSpin );
972 xaccSchedXactionSetAdvanceReminder( sxed->sx, daysInAdvance );
978 GList *schedule = NULL;
980 gnc_frequency_save_to_recurrence(sxed->gncfreq, &schedule, &gdate);
983 gchar *recurrence_str = recurrenceListToCompactString(schedule);
984 g_debug(
"recurrences parsed [%s]", recurrence_str);
985 g_free(recurrence_str);
989 xaccSchedXactionSetStartDate( sxed->sx, &gdate );
992 gnc_sx_commit_edit( sxed->sx );
1006 if ( !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(o)) )
1008 gtk_toggle_button_set_active( sxed->notifyOpt, FALSE );
1010 gtk_widget_set_sensitive( GTK_WIDGET(sxed->notifyOpt),
1011 gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(o) ) );
1019 gtk_widget_set_sensitive( GTK_WIDGET(sxed->advanceSpin),
1020 gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(sxed->advanceOpt) ) );
1021 gtk_editable_set_editable( GTK_EDITABLE(sxed->advanceSpin), TRUE );
1029 gtk_widget_set_sensitive( GTK_WIDGET(sxed->remindSpin),
1030 gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(sxed->remindOpt) ) );
1031 gtk_editable_set_editable( GTK_EDITABLE(sxed->remindSpin), TRUE );
1037 scheduledxaction_editor_dialog_destroy(GtkObject *
object, gpointer data)
1044 gnc_unregister_gui_component_by_data
1045 (DIALOG_SCHEDXACTION_EDITOR_CM_CLASS, sxed);
1048 gtk_widget_destroy(GTK_WIDGET(sxed->embed_window));
1049 sxed->embed_window = NULL;
1050 sxed->plugin_page = NULL;
1051 sxed->ledger = NULL;
1053 g_free (sxed->sxGUIDstr);
1054 sxed->sxGUIDstr = NULL;
1066 gnc_sx_begin_edit( sxed->sx );
1077 sxed_delete_event( GtkWidget *widget, GdkEvent *event, gpointer ud )
1083 if ( sxed->sx == NULL )
1088 if ( ! sxed_confirmed_cancel( sxed ) )
1100 gnc_ui_scheduled_xaction_editor_dialog_create (
SchedXaction *sx,
1104 GtkBuilder *builder;
1107 GList *dlgExists = NULL;
1109 static struct widgetSignalCallback
1114 gpointer objectData;
1117 {
"ok_button",
"clicked", editor_ok_button_clicked_cb, NULL },
1118 {
"cancel_button",
"clicked", editor_cancel_button_clicked_cb, NULL },
1119 {
"help_button",
"clicked", editor_help_button_clicked_cb, NULL },
1120 {
"rb_noend",
"toggled", endgroup_rb_toggled_cb, GINT_TO_POINTER(END_NEVER_OPTION) },
1121 {
"rb_enddate",
"toggled", endgroup_rb_toggled_cb, GINT_TO_POINTER(END_DATE_OPTION) },
1122 {
"rb_num_occur",
"toggled", endgroup_rb_toggled_cb, GINT_TO_POINTER(NUM_OCCUR_OPTION) },
1123 {
"remain_spin" ,
"value-changed", sxed_excal_update_adapt_cb, NULL },
1124 {
"enabled_opt",
"toggled", enabled_toggled_cb, NULL },
1125 {
"autocreate_opt",
"toggled", autocreate_toggled_cb, NULL },
1126 {
"advance_opt",
"toggled", advance_toggled_cb, NULL },
1127 {
"remind_opt",
"toggled", remind_toggled_cb, NULL },
1128 { NULL, NULL, NULL, NULL }
1131 dlgExists = gnc_find_gui_components( DIALOG_SCHEDXACTION_EDITOR_CM_CLASS,
1132 editor_component_sx_equality,
1134 if ( dlgExists != NULL )
1136 g_debug(
"dialog already exists; using that one." );
1138 gtk_window_present( GTK_WINDOW(sxed->dialog) );
1139 g_list_free( dlgExists );
1146 sxed->newsxP = newSX;
1149 builder = gtk_builder_new();
1150 gnc_builder_add_from_file (builder,
"dialog-sx.glade",
"advance_days_adj");
1151 gnc_builder_add_from_file (builder,
"dialog-sx.glade",
"remind_days_adj");
1152 gnc_builder_add_from_file (builder,
"dialog-sx.glade",
"end_spin_adj");
1153 gnc_builder_add_from_file (builder,
"dialog-sx.glade",
"remain_spin_adj");
1154 gnc_builder_add_from_file (builder,
"dialog-sx.glade",
"Scheduled Transaction Editor");
1156 sxed->builder = builder;
1159 sxed->dialog = GTK_WIDGET(gtk_builder_get_object (builder,
"Scheduled Transaction Editor"));
1160 sxed->notebook = GTK_NOTEBOOK(gtk_builder_get_object (builder,
"editor_notebook"));
1161 sxed->nameEntry = GTK_EDITABLE(gtk_builder_get_object (builder,
"sxe_name"));
1162 sxed->enabledOpt = GTK_TOGGLE_BUTTON(gtk_builder_get_object (builder,
"enabled_opt"));
1163 sxed->autocreateOpt = GTK_TOGGLE_BUTTON(gtk_builder_get_object (builder,
"autocreate_opt"));
1164 sxed->notifyOpt = GTK_TOGGLE_BUTTON(gtk_builder_get_object (builder,
"notify_opt"));
1165 sxed->advanceOpt = GTK_TOGGLE_BUTTON(gtk_builder_get_object (builder,
"advance_opt"));
1166 sxed->advanceSpin = GTK_SPIN_BUTTON(gtk_builder_get_object (builder,
"advance_days"));
1167 sxed->remindOpt = GTK_TOGGLE_BUTTON(gtk_builder_get_object (builder,
"remind_opt"));
1168 sxed->remindSpin = GTK_SPIN_BUTTON(gtk_builder_get_object (builder,
"remind_days"));
1169 sxed->lastOccurLabel = GTK_LABEL(gtk_builder_get_object (builder,
"last_occur_label"));
1170 sxed->optEndNone = GTK_TOGGLE_BUTTON(gtk_builder_get_object (builder,
"rb_noend"));
1171 sxed->optEndDate = GTK_TOGGLE_BUTTON(gtk_builder_get_object (builder,
"rb_enddate"));
1172 sxed->optEndCount = GTK_TOGGLE_BUTTON(gtk_builder_get_object (builder,
"rb_num_occur"));
1173 sxed->endCountSpin = GTK_ENTRY(gtk_builder_get_object (builder,
"end_spin"));
1174 sxed->endRemainSpin = GTK_ENTRY(gtk_builder_get_object (builder,
"remain_spin"));
1178 GtkWidget *endDateBox = GTK_WIDGET(gtk_builder_get_object (builder,
"end_date_hbox"));
1179 sxed->endDateEntry = GNC_DATE_EDIT(gnc_date_edit_new (
gnc_time (NULL),
1181 gtk_widget_show(GTK_WIDGET(sxed->endDateEntry));
1182 g_signal_connect( sxed->endDateEntry,
"date-changed",
1183 G_CALLBACK( sxed_excal_update_adapt_cb ), sxed );
1184 gtk_box_pack_start( GTK_BOX(endDateBox), GTK_WIDGET(sxed->endDateEntry),
1188 gnc_register_gui_component( DIALOG_SCHEDXACTION_EDITOR_CM_CLASS,
1193 g_signal_connect( sxed->dialog,
"delete_event",
1194 G_CALLBACK(sxed_delete_event), sxed );
1195 g_signal_connect( sxed->dialog,
"destroy",
1196 G_CALLBACK(scheduledxaction_editor_dialog_destroy),
1199 for ( i = 0; widgets[i].name != NULL; i++ )
1201 button = GTK_WIDGET(gtk_builder_get_object (builder, widgets[i].name ));
1202 if ( widgets[i].objectData != NULL )
1204 g_object_set_data( G_OBJECT(button),
"whichOneAmI",
1205 widgets[i].objectData );
1207 g_signal_connect( button, widgets[i].signal,
1208 G_CALLBACK( widgets[i].fn ), sxed );
1212 gtk_widget_set_sensitive( GTK_WIDGET(sxed->notifyOpt), FALSE );
1213 gtk_widget_set_sensitive( GTK_WIDGET(sxed->advanceSpin), FALSE );
1214 gtk_widget_set_sensitive( GTK_WIDGET(sxed->remindSpin), FALSE );
1215 gtk_widget_set_sensitive( GTK_WIDGET(sxed->endCountSpin), FALSE );
1216 gtk_widget_set_sensitive( GTK_WIDGET(sxed->endRemainSpin), FALSE );
1217 gtk_editable_set_editable( GTK_EDITABLE(sxed->advanceSpin), TRUE );
1218 gtk_editable_set_editable( GTK_EDITABLE(sxed->remindSpin), TRUE );
1221 gtk_window_set_resizable (GTK_WINDOW(sxed->dialog), TRUE);
1222 gnc_restore_window_size(GNC_PREFS_GROUP_SXED, GTK_WINDOW(sxed->dialog));
1225 schedXact_editor_create_freq_sel( sxed );
1228 schedXact_editor_create_ledger( sxed );
1231 schedXact_editor_populate( sxed );
1234 gtk_widget_show(sxed->dialog);
1235 gtk_notebook_set_current_page(GTK_NOTEBOOK(sxed->notebook), 0);
1238 gtk_widget_queue_resize( GTK_WIDGET( sxed->example_cal ) );
1240 gnc_ledger_display_refresh( sxed->ledger );
1243 gtk_widget_grab_focus(GTK_WIDGET(sxed->nameEntry));
1245 gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, sxed);
1246 g_object_unref(G_OBJECT(builder));
1257 GtkWidget *example_cal_scrolled_win = NULL;
1259 b = GTK_BOX(gtk_builder_get_object (sxed->builder,
"gncfreq_hbox"));
1263 xaccSchedXactionGetStartDate(sxed->sx)));
1264 g_assert(sxed->gncfreq);
1265 g_signal_connect( sxed->gncfreq,
"changed",
1266 G_CALLBACK(gnc_sxed_freq_changed),
1269 gtk_box_pack_start(GTK_BOX(b), GTK_WIDGET(sxed->gncfreq), TRUE, TRUE, 0);
1271 b = GTK_BOX(gtk_builder_get_object (sxed->builder,
"example_cal_hbox" ));
1273 example_cal_scrolled_win = gtk_scrolled_window_new(NULL, NULL);
1274 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(example_cal_scrolled_win),
1275 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1276 gtk_box_pack_start(GTK_BOX(b), example_cal_scrolled_win, TRUE, TRUE, 0);
1278 sxed->dense_cal_model = gnc_dense_cal_store_new(EX_CAL_NUM_MONTHS * 31);
1279 sxed->example_cal = GNC_DENSE_CAL(gnc_dense_cal_new_with_model(GNC_DENSE_CAL_MODEL(sxed->dense_cal_model)));
1280 g_assert(sxed->example_cal);
1281 gnc_dense_cal_set_num_months( sxed->example_cal, EX_CAL_NUM_MONTHS );
1282 gnc_dense_cal_set_months_per_col( sxed->example_cal, EX_CAL_MO_PER_COL );
1284 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(example_cal_scrolled_win),
1285 GTK_WIDGET(sxed->example_cal));
1286 gtk_widget_show_all( example_cal_scrolled_win );
1295 GtkWidget *main_vbox;
1299 sxed->ledger = gnc_ledger_display_template_gl( sxed->sxGUIDstr );
1300 splitreg = gnc_ledger_display_get_split_register( sxed->ledger );
1303 main_vbox = GTK_WIDGET(gtk_builder_get_object (sxed->builder,
"register_vbox" ));
1304 sxed->embed_window =
1306 gnc_sxed_menu_entries,
1307 gnc_sxed_menu_n_entries,
1308 "gnc-sxed-window-ui.xml",
1312 gtk_box_pack_start (GTK_BOX (main_vbox), GTK_WIDGET(sxed->embed_window),
1318 "gnc-sxed-window-ui-full.xml");
1320 NUM_LEDGER_LINES_DEFAULT, FALSE );
1326 splitreg->type, splitreg->style,
1345 gboolean enabledState, autoCreateState, notifyState;
1347 name = xaccSchedXactionGetName(sxed->sx);
1350 gtk_entry_set_text( GTK_ENTRY(sxed->nameEntry), name );
1353 gd = xaccSchedXactionGetLastOccurDate( sxed->sx );
1354 if ( g_date_valid( gd ) )
1358 gtk_label_set_text( sxed->lastOccurLabel, dateBuf );
1362 gtk_label_set_text( sxed->lastOccurLabel, _(
"(never)" ) );
1368 if ( g_date_valid( gd ) )
1370 gtk_toggle_button_set_active( sxed->optEndDate, TRUE );
1372 gnc_date_edit_set_time( sxed->endDateEntry, tmpDate );
1374 set_endgroup_toggle_states( sxed, END_DATE );
1378 gint numOccur = xaccSchedXactionGetNumOccur( sxed->sx );
1379 gint numRemain = xaccSchedXactionGetRemOccur( sxed->sx );
1381 gtk_toggle_button_set_active( sxed->optEndCount, TRUE );
1383 gtk_spin_button_set_value ( GTK_SPIN_BUTTON(sxed->endCountSpin), numOccur );
1384 gtk_spin_button_set_value ( GTK_SPIN_BUTTON(sxed->endRemainSpin), numRemain );
1386 set_endgroup_toggle_states( sxed, END_OCCUR );
1390 gtk_toggle_button_set_active( sxed->optEndNone, TRUE );
1391 set_endgroup_toggle_states( sxed, END_NEVER );
1394 enabledState = xaccSchedXactionGetEnabled( sxed->sx );
1395 gtk_toggle_button_set_active( sxed->enabledOpt, enabledState );
1407 xaccSchedXactionGetAutoCreate( sxed->sx,
1411 gtk_toggle_button_set_active( sxed->autocreateOpt, autoCreateState );
1412 if ( ! autoCreateState )
1414 notifyState = FALSE;
1416 gtk_toggle_button_set_active( sxed->notifyOpt, notifyState );
1427 xaccSchedXactionGetAdvanceCreation( sxed->sx );
1429 if ( daysInAdvance != 0 )
1431 gtk_toggle_button_set_active( sxed->advanceOpt, TRUE );
1432 gtk_spin_button_set_value( sxed->advanceSpin,
1433 (gfloat)daysInAdvance );
1445 xaccSchedXactionGetAdvanceReminder( sxed->sx );
1447 if ( daysInAdvance != 0 )
1449 gtk_toggle_button_set_active( sxed->remindOpt, TRUE );
1450 gtk_spin_button_set_value( sxed->remindSpin,
1451 (gfloat)daysInAdvance );
1464 splitList = xaccSchedXactionGetSplits( sxed->sx );
1465 if ( splitList != NULL )
1467 splitReg = gnc_ledger_display_get_split_register
1474 gnc_sxed_update_cal(sxed);
1482 gtk_widget_set_sensitive( GTK_WIDGET(sxed->endDateEntry), (type == END_DATE) );
1483 gtk_widget_set_sensitive( GTK_WIDGET(sxed->endCountSpin), (type == END_OCCUR) );
1484 gtk_widget_set_sensitive( GTK_WIDGET(sxed->endRemainSpin), (type == END_OCCUR) );
1490 endgroup_rb_toggled_cb( GtkButton *b, gpointer d )
1497 id = GPOINTER_TO_INT(g_object_get_data( G_OBJECT(b),
"whichOneAmI" ));
1501 case END_NEVER_OPTION:
1502 set_endgroup_toggle_states( sxed, END_NEVER );
1504 case END_DATE_OPTION:
1505 set_endgroup_toggle_states( sxed, END_DATE );
1507 case NUM_OCCUR_OPTION:
1508 set_endgroup_toggle_states( sxed, END_OCCUR );
1511 g_critical(
"Unknown id %d",
id );
1514 gnc_sxed_update_cal(sxed);
1527 gboolean pending_changes;
1529 const char *message =
1530 _(
"The current template transaction "
1531 "has been changed. "
1532 "Would you like to record the changes?");
1534 reg = gnc_ledger_display_get_split_register (sxed->ledger);
1536 if (!pending_changes)
1541 if (gnc_verify_dialog(sxed->dialog, TRUE,
"%s", message))
1556 editor_component_sx_equality( gpointer
find_data,
1557 gpointer user_data )
1569 GList *recurrences = NULL;
1570 GDate start_date, first_date;
1572 g_date_clear(&start_date, 1);
1574 gnc_frequency_save_to_recurrence(sxed->gncfreq, &recurrences, &start_date);
1575 g_date_subtract_days(&start_date, 1);
1576 recurrenceListNextInstance(recurrences, &start_date, &first_date);
1581 const GDate *last_sx_inst;
1583 last_sx_inst = xaccSchedXactionGetLastOccurDate(sxed->sx);
1584 if (g_date_valid(last_sx_inst)
1585 && g_date_valid(&first_date)
1586 && g_date_compare(last_sx_inst, &first_date) != 0)
1588 start_date = *last_sx_inst;
1589 recurrenceListNextInstance(recurrences, &start_date, &first_date);
1593 if (!g_date_valid(&first_date))
1596 gnc_dense_cal_store_clear(sxed->dense_cal_model);
1600 gnc_dense_cal_store_update_name(sxed->dense_cal_model, xaccSchedXactionGetName(sxed->sx));
1602 gchar *schedule_desc = recurrenceListToCompactString(recurrences);
1603 gnc_dense_cal_store_update_info(sxed->dense_cal_model, schedule_desc);
1604 g_free(schedule_desc);
1611 if (gtk_toggle_button_get_active(sxed->optEndDate))
1614 g_date_clear (&end_date, 1);
1616 gnc_dense_cal_store_update_recurrences_date_end(sxed->dense_cal_model, &first_date, recurrences, &end_date);
1618 else if (gtk_toggle_button_get_active(sxed->optEndNone))
1620 gnc_dense_cal_store_update_recurrences_no_end(sxed->dense_cal_model, &first_date, recurrences);
1622 else if (gtk_toggle_button_get_active(sxed->optEndCount))
1625 = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(sxed->endRemainSpin));
1626 gnc_dense_cal_store_update_recurrences_count_end(sxed->dense_cal_model, &first_date, recurrences, num_remain);
1630 g_error(
"unknown end condition");
1634 recurrenceListFree(&recurrences);
1646 sxed_excal_update_adapt_cb(GtkObject *o, gpointer ud)
1653 on_sx_check_toggled_cb (GtkWidget *togglebutton, gpointer user_data)
1655 GtkWidget *widget_auto;
1656 GtkWidget *widget_notify;
1659 PINFO(
"Togglebutton is %p and user_data is %p", togglebutton, user_data);
1660 PINFO(
"Togglebutton builder name is %s", gtk_buildable_get_name(GTK_BUILDABLE(togglebutton)));
1663 table = g_object_get_data(G_OBJECT(user_data),
"prefs_widget_hash");
1666 widget_auto = g_hash_table_lookup(table,
"pref/" GNC_PREFS_GROUP_SXED
"/" GNC_PREF_CREATE_AUTO);
1667 widget_notify = g_hash_table_lookup(table,
"pref/" GNC_PREFS_GROUP_SXED
"/" GNC_PREF_NOTIFY);
1669 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget_auto)))
1670 gtk_widget_set_sensitive(widget_notify, TRUE);
1672 gtk_widget_set_sensitive(widget_notify, FALSE);
1675 widget_auto = g_hash_table_lookup(table,
"pref/" GNC_PREFS_GROUP_STARTUP
"/" GNC_PREF_RUN_AT_FOPEN);
1676 widget_notify = g_hash_table_lookup(table,
"pref/" GNC_PREFS_GROUP_STARTUP
"/" GNC_PREF_SHOW_AT_FOPEN);
1678 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget_auto)))
1679 gtk_widget_set_sensitive(widget_notify, TRUE);
1681 gtk_widget_set_sensitive(widget_notify, FALSE);
1690 GList *affected_sxes;
1696 _open_editors(GtkDialog *dialog, gint response_code, gpointer data)
1699 gtk_widget_hide(adhd->dialog);
1702 for (sx_iter = adhd->affected_sxes; sx_iter; sx_iter = sx_iter->next)
1704 gnc_ui_scheduled_xaction_editor_dialog_create((
SchedXaction*)sx_iter->data,
1708 g_list_free(adhd->affected_sxes);
1709 gtk_widget_destroy(GTK_WIDGET(adhd->dialog));
1715 _sx_engine_event_handler(
QofInstance *ent,
QofEventId event_type, gpointer user_data, gpointer evt_data)
1719 GList *affected_sxes;
1721 if (!(event_type & QOF_EVENT_DESTROY))
1723 if (!GNC_IS_ACCOUNT(ent))
1725 acct = GNC_ACCOUNT(ent);
1729 if (g_list_length(affected_sxes) == 0)
1735 GtkBuilder *builder;
1737 GtkListStore *name_list;
1739 GtkTreeViewColumn *name_column;
1740 GtkCellRenderer *renderer;
1742 builder = gtk_builder_new();
1743 gnc_builder_add_from_file (builder,
"dialog-sx.glade",
"Account Deletion");
1745 dialog = GTK_WIDGET(gtk_builder_get_object (builder,
"Account Deletion"));
1747 list = GTK_TREE_VIEW(gtk_builder_get_object (builder,
"sx_list"));
1750 data->dialog = dialog;
1751 data->affected_sxes = affected_sxes;
1752 name_list = gtk_list_store_new(1, G_TYPE_STRING);
1753 for (sx_iter = affected_sxes; sx_iter != NULL; sx_iter = sx_iter->next)
1760 sx_name = xaccSchedXactionGetName(sx);
1761 gtk_list_store_append(name_list, &iter);
1762 gtk_list_store_set(name_list, &iter, 0, sx_name, -1);
1764 gtk_tree_view_set_model(list, GTK_TREE_MODEL(name_list));
1765 g_object_unref(G_OBJECT(name_list));
1767 renderer = gtk_cell_renderer_text_new();
1768 name_column = gtk_tree_view_column_new_with_attributes(_(
"Name"),
1771 gtk_tree_view_append_column(list, name_column);
1773 g_signal_connect(G_OBJECT(dialog),
"response",
1774 G_CALLBACK(_open_editors), data);
1776 gtk_widget_show_all(GTK_WIDGET(dialog));
1777 gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, data);
1778 g_object_unref(G_OBJECT(builder));
1784 gnc_ui_sx_initialize (
void)
1788 gnc_hook_add_dangler(HOOK_BOOK_OPENED,
1789 (GFunc)gnc_sx_sxsincelast_book_opened, NULL);
1794 "create_days_adj,remind_days_adj,sx_prefs",
1795 _(
"Scheduled Transactions"));
const GDate * xaccSchedXactionGetEndDate(const SchedXaction *sx)
void gnc_sx_set_schedule(SchedXaction *sx, GList *schedule)
void gnc_sx_set_instance_count(SchedXaction *sx, gint instance_num)
GList * gnc_sx_get_schedule(const SchedXaction *sx)
void gnc_preferences_add_page(const gchar *filename, const gchar *widgetname, const gchar *tabname)
void qof_instance_get(const QofInstance *inst, const gchar *first_param,...)
Wrapper for g_object_get.
GncEmbeddedWindow * gnc_embedded_window_new(const gchar *action_group_name, GtkActionEntry *action_entries, gint n_action_entries, const gchar *ui_filename, GtkWidget *enclosing_win, gboolean add_accelerators, gpointer user_data)
Date and Time handling routines.
QofBook * qof_instance_get_book(gconstpointer)
utility functions for the GnuCash UI
#define G_LOG_DOMAIN
Functions providing the SX List as a plugin page.
The type, style and table for the register.
#define PINFO(format, args...)
void xaccSchedXactionSetNumOccur(SchedXaction *sx, gint new_num)
void gnc_embedded_window_close_page(GncEmbeddedWindow *window, GncPluginPage *page)
time64 gnc_time64_get_day_start_gdate(const GDate *date)
void gnc_plugin_page_set_ui_description(GncPluginPage *page, const char *ui_filename)
gboolean gnc_commodity_equal(const gnc_commodity *a, const gnc_commodity *b)
void gnc_embedded_window_open_page(GncEmbeddedWindow *window, GncPluginPage *page)
gnc_numeric gnc_numeric_add(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
gboolean gnc_numeric_zero_p(gnc_numeric a)
gboolean gnc_split_register_changed(SplitRegister *reg)
Transaction * xaccSplitGetParent(const Split *split)
gchar * gnc_numeric_to_string(gnc_numeric n)
gboolean gnc_split_register_save(SplitRegister *reg, gboolean do_commit)
void gnc_split_register_set_auto_complete(SplitRegister *reg, gboolean do_auto_complete)
GList * gnc_sx_get_sxes_referencing_account(QofBook *book, Account *acct)
GncPluginPage * gnc_plugin_page_register_new_ledger(GNCLedgerDisplay *ledger)
gint qof_event_register_handler(QofEventHandler handler, gpointer handler_data)
Register a handler for events.
gboolean qof_log_check(QofLogModule log_module, QofLogLevel log_level)
void gnc_split_register_redraw(SplitRegister *reg)
Functions providing a register page for the GnuCash UI.
Account handling public routines.
Anchor Scheduled Transaction info in a book. See src/doc/books.txt for design overview.
Functions for adding plugins to a GnuCash window.
Dialog for handling user preferences.
#define xaccSchedXactionGetGUID(X)
void gnc_gnome_help(const char *file_name, const char *anchor)
All type declarations for the whole Gnucash engine.
Generic api to store and retrieve preferences.
void xaccSchedXactionSetName(SchedXaction *sx, const gchar *newName)
void gnc_split_register_load(SplitRegister *reg, GList *slist, Account *default_account)
gchar * guid_to_string(const GncGUID *guid)
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Functions that are supported by all types of windows.
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
time64 gnc_time(time64 *tbuf)
get the current local time
const char * xaccSplitGetMemo(const Split *split)
void gnc_split_register_show_present_divider(SplitRegister *reg, gboolean show_present)
gboolean xaccSchedXactionHasOccurDef(const SchedXaction *sx)
Scheduled Transactions public handling routines.
void xaccSchedXactionSetEndDate(SchedXaction *sx, const GDate *newEnd)
void xaccSchedXactionDestroy(SchedXaction *sx)
void gnc_plugin_page_register_set_options(GncPluginPage *plugin_page, gint lines_default, gboolean read_only)
size_t qof_print_gdate(char *buf, size_t bufflen, const GDate *gd)
void gnc_split_register_cancel_cursor_trans_changes(SplitRegister *reg)
const gchar * QofLogModule
void gnc_gdate_set_time64(GDate *gd, time64 time)
gdouble gnc_prefs_get_float(const gchar *group, const gchar *pref_name)
void gnc_split_register_config(SplitRegister *reg, SplitRegisterType newtype, SplitRegisterStyle newstyle, gboolean use_double_line)
Account * xaccAccountLookup(const GncGUID *guid, QofBook *book)