1 /********************************************************************\
2  * dialog-sx-editor2.c : dialog for scheduled transaction editing   *
3  * Copyright (C) 2001,2002,2006 Joshua Sled <jsled@asynchronous.org>*
4  * Copyright (C) 2011 Robert Fewell                                 *
5  *                                                                  *
6  * This program is free software; you can redistribute it and/or    *
7  * modify it under the terms of version 2 and/or version 3 of the   *
8  * GNU General Public License as published by the Free Software     *
9  * Foundation.                                                      *
10  *                                                                  *
11  * As a special exception, permission is granted to link the binary *
12  * module resultant from this code with the OpenSSL project's       *
13  * "OpenSSL" library (or modified versions of it that use the same  *
14  * license as the "OpenSSL" library), and distribute the linked     *
15  * executable.  You must obey the GNU General Public License in all *
16  * respects for all of the code used other than "OpenSSL". If you   *
17  * modify this file, you may extend this exception to your version  *
18  * of the file, but you are not obligated to do so. If you do not   *
19  * wish to do so, delete this exception statement from your version *
20  * of this file.                                                    *
21  *                                                                  *
22  * This program is distributed in the hope that it will be useful,  *
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
25  * GNU General Public License for more details.                     *
26  *                                                                  *
27  * You should have received a copy of the GNU General Public License*
28  * along with this program; if not, contact:                        *
29  *                                                                  *
30  * Free Software Foundation           Voice:  +1-617-542-5942       *
31  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
32  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
33 \********************************************************************/
34 
35 #include <config.h>
36 
37 #include <gtk/gtk.h>
38 #include <glib/gi18n.h>
39 #include <locale.h>
40 
41 #include "qof.h"
42 #include "Account.h"
43 #include "SchedXaction.h"
44 #include "SX-book.h"
45 #include "dialog-preferences.h"
46 #include "dialog-sx-editor2.h"
47 #include "dialog-utils.h"
48 #include "gnc-component-manager.h"
49 #include "gnc-date.h"
50 #include "gnc-date-edit.h"
51 #include "gnc-dense-cal.h"
52 #include "gnc-dense-cal-store.h"
53 #include "gnc-embedded-window.h"
54 #include "gnc-engine.h"
55 #include "gnc-frequency.h"
56 #include "gnc-gui-query.h"
57 #include "gnc-hooks.h"
58 #include "gnc-ledger-display.h"
59 #include "gnc-plugin-page.h"
60 #include "gnc-plugin-page-register2.h"
61 #include "gnc-prefs.h"
62 #include "gnc-ui.h"
63 #include "gnc-ui-util.h"
64 #include "gnc-tree-model-split-reg.h"
65 #include "gnc-tree-control-split-reg.h"
66 #include <gnc-glib-utils.h>
67 
68 #include "gnc-sx-instance-model.h"
69 #include "dialog-sx-since-last-run.h"
70 
71 #undef G_LOG_DOMAIN
72 #define G_LOG_DOMAIN "gnc.gui.sx.editor"
73 
74 static QofLogModule log_module = GNC_MOD_GUI_SX;
75 
76 static gint _sx_engine_event_handler_id = -1;
77 
78 #define END_NEVER_OPTION 0
79 #define END_DATE_OPTION  1
80 #define NUM_OCCUR_OPTION 2
81 
82 #define NUM_LEDGER_LINES_DEFAULT 6
83 
84 #define EX_CAL_NUM_MONTHS 6
85 #define EX_CAL_MO_PER_COL 3
86 
87 #define GNC_D_WIDTH 25
88 #define GNC_D_BUF_WIDTH 26
89 
90 /** Datatypes ***********************************************************/
91 
92 typedef enum _EndTypeEnum
93 {
94     END_NEVER,
95     END_DATE,
96     END_OCCUR,
97 } EndType;
98 
99 typedef enum { NO_END, DATE_END, COUNT_END } END_TYPE;
100 
101 struct _GncSxEditorDialog2
102 {
103     GtkWidget    *dialog;
104     GtkBuilder   *builder;
105     GtkNotebook  *notebook;
106     SchedXaction *sx;
107     /* If this is a new scheduled transaction or not. */
108     int newsxP;
109 
110     /* The various widgets in the dialog */
111     GNCLedgerDisplay2 *ledger;
112 
113     GncFrequency     *gncfreq;
114     GncDenseCalStore *dense_cal_model;
115     GncDenseCal      *example_cal;
116 
117     GtkEditable *nameEntry;
118 
119     GtkLabel *lastOccurLabel;
120 
121     GtkToggleButton *enabledOpt;
122     GtkToggleButton *autocreateOpt;
123     GtkToggleButton *notifyOpt;
124     GtkToggleButton *advanceOpt;
125     GtkSpinButton   *advanceSpin;
126     GtkToggleButton *remindOpt;
127     GtkSpinButton   *remindSpin;
128 
129     GtkToggleButton *optEndDate;
130     GtkToggleButton *optEndNone;
131     GtkToggleButton *optEndCount;
132     GtkEntry        *endCountSpin;
133     GtkEntry        *endRemainSpin;
134     GNCDateEdit     *endDateEntry;
135 
136     char *sxGUIDstr;
137 
138     GncEmbeddedWindow *embed_window;
139     GncPluginPage     *plugin_page;
140 };
141 
142 /** Prototypes **********************************************************/
143 
144 static void schedXact_editor_create_freq_sel (GncSxEditorDialog2 *sxed);
145 static void schedXact_editor_create_ledger (GncSxEditorDialog2 *sxed);
146 static void schedXact_editor_populate (GncSxEditorDialog2 *);
147 static void endgroup_rb_toggled_cb (GtkButton *b, gpointer d);
148 static void set_endgroup_toggle_states (GncSxEditorDialog2 *sxed, EndType t);
149 static void advance_toggled_cb (GtkButton *b, GncSxEditorDialog2 *sxed);
150 static void remind_toggled_cb (GtkButton *b, GncSxEditorDialog2 *sxed);
151 static gboolean gnc_sxed_check_consistent (GncSxEditorDialog2 *sxed);
152 static gboolean gnc_sxed_check_changed (GncSxEditorDialog2 *sxed);
153 static void gnc_sxed_save_sx (GncSxEditorDialog2 *sxed);
154 static void gnc_sxed_freq_changed (GncFrequency *gf, gpointer ud);
155 static void sxed_excal_update_adapt_cb (GtkWidget *o, gpointer ud);
156 static void gnc_sxed_update_cal (GncSxEditorDialog2 *sxed);
157 static void on_sx_check_toggled_cb (GtkWidget *togglebutton, gpointer user_data);
158 static void gnc_sxed_reg_check_close (GncSxEditorDialog2 *sxed);
159 static gboolean sxed_delete_event (GtkWidget *widget, GdkEvent *event, gpointer ud);
160 static gboolean sxed_confirmed_cancel (GncSxEditorDialog2 *sxed);
161 static gboolean editor_component_sx_equality (gpointer find_data, gpointer user_data);
162 
163 static GtkActionEntry gnc_sxed_menu_entries [] =
164 {
165     { "EditAction", NULL, N_("_Edit"), NULL, NULL, NULL },
166     { "TransactionAction", NULL, N_("_Transaction"), NULL, NULL, NULL },
167     { "ViewAction", NULL, N_("_View"), NULL, NULL, NULL },
168     { "ActionsAction", NULL, N_("_Actions"), NULL, NULL, NULL },
169 };
170 static guint gnc_sxed_menu_n_entries = G_N_ELEMENTS (gnc_sxed_menu_entries);
171 
172 /** Implementations *****************************************************/
173 
174 static void
sxed_close_handler(gpointer user_data)175 sxed_close_handler(gpointer user_data)
176 {
177     GncSxEditorDialog2 *sxed = user_data;
178 
179     gnc_sxed_reg_check_close(sxed);
180     gnc_save_window_size( GNC_PREFS_GROUP_SXED, GTK_WINDOW(sxed->dialog) );
181     gtk_widget_destroy(sxed->dialog);
182     /* The data will be cleaned up in the destroy handler. */
183 }
184 
185 
186 /**
187  * @return TRUE if the user does want to cancel, FALSE if not.  If TRUE is
188  * returned, the register's changes have been cancelled.
189  **/
190 static gboolean
sxed_confirmed_cancel(GncSxEditorDialog2 * sxed)191 sxed_confirmed_cancel (GncSxEditorDialog2 *sxed)
192 {
193     GncTreeViewSplitReg *view;
194 
195     view = gnc_ledger_display2_get_split_view_register (sxed->ledger);
196     /* check for changes */
197     if (gnc_sxed_check_changed (sxed))
198     {
199         const char *sx_changed_msg =
200             _( "This Scheduled Transaction has changed; are you "
201                "sure you want to cancel?" );
202         if (!gnc_verify_dialog (GTK_WINDOW (sxed->dialog), FALSE, "%s", sx_changed_msg))
203         {
204             return FALSE;
205         }
206     }
207     /* cancel ledger changes */
208     gnc_tree_view_split_reg_cancel_edit (view, TRUE);
209     return TRUE;
210 }
211 
212 
213 /**********************************
214  * Dialog Action Button functions *
215  *********************************/
216 static void
editor_cancel_button_clicked_cb(GtkButton * b,GncSxEditorDialog2 * sxed)217 editor_cancel_button_clicked_cb (GtkButton *b, GncSxEditorDialog2 *sxed)
218 {
219     /* close */
220     if (!sxed_confirmed_cancel (sxed))
221         return;
222 
223     gnc_close_gui_component_by_data (DIALOG_SCHEDXACTION2_EDITOR_CM_CLASS,
224                                      sxed);
225 }
226 
227 
228 static void
editor_help_button_clicked_cb(GtkButton * b,GncSxEditorDialog2 * sxed)229 editor_help_button_clicked_cb (GtkButton *b, GncSxEditorDialog2 *sxed)
230 {
231     gnc_gnome_help (GTK_WINDOW(sxed->dialog), HF_HELP, HL_SXEDITOR);
232 }
233 
234 
235 static void
editor_ok_button_clicked_cb(GtkButton * b,GncSxEditorDialog2 * sxed)236 editor_ok_button_clicked_cb (GtkButton *b, GncSxEditorDialog2 *sxed)
237 {
238     QofBook *book;
239     SchedXactions *sxes;
240 
241     if (!gnc_sxed_check_consistent (sxed))
242         return;
243 
244     gnc_sxed_save_sx (sxed);
245 
246     /* add to list */
247     // @@fixme -- forget 'new'-flag: check for existence of the SX [?]
248     if ( sxed->newsxP )
249     {
250         book = gnc_get_current_book ();
251         sxes = gnc_book_get_schedxactions (book);
252         gnc_sxes_add_sx (sxes, sxed->sx);
253         sxed->newsxP = FALSE;
254     }
255 
256     /* cleanup */
257     gnc_close_gui_component_by_data (DIALOG_SCHEDXACTION2_EDITOR_CM_CLASS,
258                                      sxed);
259 }
260 
261 
262 /*************************************************************************
263  * Checks to see if the SX has been modified from it's previously-saved
264  * state.
265  * @return TRUE if this is a 'new' SX, or if the SX has changed from it's
266  *   previous configuration.
267  ************************************************************************/
268 static gboolean
gnc_sxed_check_changed(GncSxEditorDialog2 * sxed)269 gnc_sxed_check_changed (GncSxEditorDialog2 *sxed)
270 {
271     if (sxed->newsxP)
272         return TRUE;
273 
274     /* name */
275     {
276         char *name;
277 
278         name = gtk_editable_get_chars (GTK_EDITABLE (sxed->nameEntry), 0, -1);
279         if (strlen (name) == 0)
280         {
281             return TRUE;
282 
283         }
284         if ((xaccSchedXactionGetName (sxed->sx) == NULL)
285                 || (strcmp( xaccSchedXactionGetName (sxed->sx),
286                             name ) != 0))
287         {
288             return TRUE;
289         }
290     }
291 
292     /* end options */
293     {
294         /* dialog says... no end */
295         if (gtk_toggle_button_get_active (sxed->optEndNone))
296         {
297             if (xaccSchedXactionHasEndDate (sxed->sx)
298                     || xaccSchedXactionHasOccurDef (sxed->sx))
299             {
300                 return TRUE;
301             }
302         }
303 
304         /* dialog says... end date */
305         if (gtk_toggle_button_get_active (sxed->optEndDate))
306         {
307             GDate sxEndDate, dlgEndDate;
308 
309             if (!xaccSchedXactionHasEndDate (sxed->sx))
310             {
311                 return TRUE;
312             }
313             sxEndDate = *xaccSchedXactionGetEndDate (sxed->sx);
314             gnc_gdate_set_time64 (&dlgEndDate,
315                                gnc_date_edit_get_date( sxed->
316                                        endDateEntry));
317 
318             if (g_date_compare (&sxEndDate, &dlgEndDate) != 0)
319             {
320                 return TRUE;
321             }
322         }
323 
324         /* dialog says... num occur */
325         if (gtk_toggle_button_get_active (sxed->optEndCount))
326         {
327             gint sxNumOccur, sxNumRem, dlgNumOccur, dlgNumRem;
328 
329             if (!xaccSchedXactionGetNumOccur (sxed->sx))
330             {
331                 return TRUE;
332             }
333 
334             dlgNumOccur  =
335                 gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (sxed->endCountSpin));
336 
337             dlgNumRem =
338                 gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (sxed->endRemainSpin));
339 
340             sxNumOccur = xaccSchedXactionGetNumOccur (sxed->sx);
341             sxNumRem = xaccSchedXactionGetRemOccur (sxed->sx);
342 
343             if ((dlgNumOccur != sxNumOccur)
344                     || (dlgNumRem != sxNumRem))
345             {
346                 return TRUE;
347             }
348         }
349     }
350 
351     /* SX options [autocreate, notify, reminder, advance] */
352     {
353         gboolean dlgEnabled,
354                  dlgAutoCreate,
355                  dlgNotify,
356                  sxEnabled,
357                  sxAutoCreate,
358                  sxNotify;
359         gint dlgAdvance, sxAdvance;
360         gint dlgRemind, sxRemind;
361 
362         dlgEnabled =
363             gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (sxed->
364                                           enabledOpt));
365         dlgAutoCreate =
366             gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (sxed->
367                                           autocreateOpt));
368         dlgNotify =
369             gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (sxed->
370                                           notifyOpt));
371 
372         sxEnabled = xaccSchedXactionGetEnabled (sxed->sx);
373         if (!((dlgEnabled == sxEnabled)))
374         {
375             return TRUE;
376         }
377 
378         xaccSchedXactionGetAutoCreate (sxed->sx, &sxAutoCreate, &sxNotify);
379         if (!((dlgAutoCreate == sxAutoCreate)
380                 && (dlgNotify == sxNotify)))
381         {
382             return TRUE;
383         }
384 
385         dlgAdvance = 0;
386         if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (sxed->advanceOpt)))
387         {
388             dlgAdvance =
389                 gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (sxed->
390                                                   advanceSpin));
391         }
392         sxAdvance = xaccSchedXactionGetAdvanceCreation (sxed->sx);
393         if (dlgAdvance != sxAdvance)
394         {
395             return TRUE;
396         }
397 
398         dlgRemind = 0;
399         if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (sxed->remindOpt)))
400         {
401             dlgRemind =
402                 gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (sxed->remindSpin));
403         }
404         sxRemind = xaccSchedXactionGetAdvanceReminder (sxed->sx);
405         if (dlgRemind != sxRemind)
406         {
407             return TRUE;
408         }
409     }
410 
411     {
412         GList *dialog_schedule = NULL;
413         GDate dialog_start_date, sx_start_date;
414         gchar *dialog_schedule_str, *sx_schedule_str;
415         gboolean schedules_are_the_same, start_dates_are_the_same;
416 
417         g_date_clear (&dialog_start_date, 1);
418         gnc_frequency_save_to_recurrence (sxed->gncfreq, &dialog_schedule, &dialog_start_date);
419         dialog_schedule_str = recurrenceListToString (dialog_schedule);
420         recurrenceListFree (&dialog_schedule);
421 
422         sx_start_date = *xaccSchedXactionGetStartDate (sxed->sx);
423         sx_schedule_str = recurrenceListToString (gnc_sx_get_schedule (sxed->sx));
424 
425         DEBUG ("dialog schedule [%s], sx schedule [%s]",
426                 dialog_schedule_str, sx_schedule_str);
427 
428         schedules_are_the_same = (strcmp (dialog_schedule_str, sx_schedule_str) == 0);
429         g_free (dialog_schedule_str);
430         g_free (sx_schedule_str);
431 
432         start_dates_are_the_same = (g_date_compare (&dialog_start_date, &sx_start_date) == 0);
433 
434         if (!schedules_are_the_same || !start_dates_are_the_same)
435             return TRUE;
436     }
437 
438     /* template transactions */
439     {
440         GncTreeViewSplitReg *view = gnc_ledger_display2_get_split_view_register (sxed->ledger);
441         /* Make sure we have finished the edit */
442         gnc_tree_view_split_reg_finish_edit (view);
443         if (gnc_tree_view_split_reg_get_dirty_trans (view) != NULL)
444             return TRUE;
445     }
446     return FALSE;
447 }
448 
449 
450 /*****************************************************************************
451  * Holds the credit- and debit-sum for a given Transaction, as used in
452  * gnc_sxed_check_consistent.
453  ****************************************************************************/
454 typedef struct _txnCreditDebitSums
455 {
456     gnc_numeric creditSum;
457     gnc_numeric debitSum;
458 } txnCreditDebitSums;
459 
460 
461 static
462 void
set_sums_to_zero(gpointer key,gpointer val,gpointer ud)463 set_sums_to_zero (gpointer key,
464                   gpointer val,
465                   gpointer ud)
466 {
467     txnCreditDebitSums *tcds = (txnCreditDebitSums*)val;
468     tcds->creditSum = gnc_numeric_zero();
469     tcds->debitSum  = gnc_numeric_zero();
470 }
471 
472 
473 static void
check_credit_debit_balance(gpointer key,gpointer val,gpointer ud)474 check_credit_debit_balance (gpointer key,
475                             gpointer val,
476                             gpointer ud)
477 {
478     txnCreditDebitSums *tcds = (txnCreditDebitSums*)val;
479     gboolean *unbalanced = (gboolean*)ud;
480     *unbalanced |= !(gnc_numeric_zero_p (
481                          gnc_numeric_sub_fixed (tcds->debitSum,
482                                  tcds->creditSum)));
483 
484     if (qof_log_check (G_LOG_DOMAIN, QOF_LOG_DEBUG))
485     {
486         if (gnc_numeric_zero_p (gnc_numeric_sub_fixed (tcds->debitSum,
487                                  tcds->creditSum)))
488         {
489             DEBUG ("%p | true [%s - %s = %s]",
490                      key,
491                      gnc_numeric_to_string (tcds->debitSum),
492                      gnc_numeric_to_string (tcds->creditSum),
493                      gnc_numeric_to_string (gnc_numeric_sub_fixed (tcds->debitSum,
494                                            tcds->creditSum)));
495         }
496         else
497         {
498             DEBUG ("%p | false [%s - %s = %s]",
499                      key,
500                      gnc_numeric_to_string (tcds->debitSum),
501                      gnc_numeric_to_string (tcds->creditSum),
502                      gnc_numeric_to_string (gnc_numeric_sub_fixed (tcds->debitSum,
503                                            tcds->creditSum)));
504         }
505     }
506 }
507 
508 
509 /*******************************************************************************
510  * Checks to make sure that the SX is in a reasonable state to save.
511  * @return true if checks out okay, false otherwise.
512  ******************************************************************************/
513 static gboolean
gnc_sxed_check_consistent(GncSxEditorDialog2 * sxed)514 gnc_sxed_check_consistent (GncSxEditorDialog2 *sxed)
515 {
516     gboolean multi_commodity = FALSE;
517     gnc_commodity *base_cmdty = NULL;
518     gint ttVarCount, splitCount = 0;
519     GList *schedule = NULL;
520 
521     /* Do checks on validity and such, interrupting the user if
522      * things aren't right.
523      *
524      * Features...
525      * X support formulas [?!]
526      * X balancing the SX if contain numeric-only formula data.
527      *   X agreement with create-automagically/notification controls
528      * X the 'will ever be valid' check should take num-occur vals into
529      *   account.
530      * X SX name is unique
531      * X SX has a name
532      * X "weekly" FS has some days set.
533      * X "once" with reasonable start/end dates.
534      *   X This doesn't work at the time the 'weekly' one was fixed with
535      *     user-confirmation, below; the once SX is always valid.
536      * [X more generically, creating a "not scheduled" SX is probably not
537      *   right... ]
538      */
539 
540     {
541         static const int NUM_ITERS_WITH_VARS = 5;
542         static const int NUM_ITERS_NO_VARS = 1;
543         int numIters, i;
544         GHashTable *vars, *txns;
545         GList *splitList = NULL;
546         char *credit_formula = NULL, *debit_formula = NULL;
547         Split *s;
548         Transaction *t;
549         gnc_numeric tmp;
550         gboolean unbalanceable;
551         gpointer unusedKey, unusedValue;
552 
553         unbalanceable = FALSE; /* innocent until proven guilty */
554         vars = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)gnc_sx_variable_free);
555         txns = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
556         numIters = NUM_ITERS_NO_VARS;
557         /**
558          * Plan:
559          * . Do a first pass to get the variables.
560          * . Set each variable to random values.
561          * . see if we balance after that
562          *   . true: all good
563          *   . false: indicate to user, allow decision.
564          */
565 
566         /* numeric-formulas-get-balanced determination */
567         gnc_sx_get_variables (sxed->sx, vars);
568 
569         ttVarCount = g_hash_table_size (vars);
570         if (ttVarCount != 0)
571         {
572             /* balance with random variable bindings some number
573              * of times in an attempt to ferret out
574              * un-balanceable transactions.
575              *
576              * NOTE: The Real Way to do this is with some
577              * symbolic math to eliminate the variables.  This is
578              * hard, and we don't do it.  This solution will
579              * suffice for now, and perhaps for the lifetime of
580              * the software. --jsled */
581             numIters = NUM_ITERS_WITH_VARS;
582         }
583 
584         for (i = 0; i < numIters && !unbalanceable; i++)
585         {
586             gnc_sx_randomize_variables (vars);
587             g_hash_table_foreach (txns, set_sums_to_zero, NULL);
588             tmp = gnc_numeric_zero();
589 
590             splitList = xaccSchedXactionGetSplits (sxed->sx);
591             splitCount += g_list_length (splitList);
592 
593             for (; splitList; splitList = splitList->next)
594             {
595                 GncGUID *acct_guid = NULL;
596                 Account *acct;
597                 gnc_commodity *split_cmdty;
598                 txnCreditDebitSums *tcds;
599 
600                 s = (Split*)splitList->data;
601                 t = xaccSplitGetParent (s);
602 
603                 if (!(tcds =
604                             (txnCreditDebitSums*)g_hash_table_lookup (txns,
605                                     (gpointer)t)))
606                 {
607                     tcds = g_new0 (txnCreditDebitSums, 1 );
608                     tcds->creditSum = gnc_numeric_zero();
609                     tcds->debitSum  = gnc_numeric_zero();
610                     g_hash_table_insert (txns, (gpointer)t, (gpointer)tcds);
611                 }
612 
613 		qof_instance_get (QOF_INSTANCE (s),
614 				  "sx-account", &acct_guid,
615 				  "sx-credit-formula", &credit_formula,
616 				  "sx-debit-formula", &debit_formula,
617 				  NULL);
618                 acct = xaccAccountLookup( acct_guid, gnc_get_current_book ());
619                 guid_free (acct_guid);
620                 split_cmdty = xaccAccountGetCommodity(acct);
621                 if (base_cmdty == NULL)
622                 {
623                     base_cmdty = split_cmdty;
624                 }
625                 multi_commodity |= !gnc_commodity_equal(split_cmdty, base_cmdty);
626 
627 		if ( credit_formula &&
628 		     g_strcmp0 (credit_formula, "") != 0 &&
629 		     gnc_sx_parse_vars_from_formula(credit_formula, vars,
630 						    &tmp ) < 0 )
631 		{
632 		    GString *errStr;
633 
634 		    errStr = g_string_sized_new( 32 );
635 		    g_string_printf( errStr,
636 				     _( "Couldn't parse credit formula for "
637 					"split \"%s\"." ),
638 				     xaccSplitGetMemo( s ) );
639                     gnc_error_dialog (GTK_WINDOW (sxed->dialog), "%s",
640 				      errStr->str );
641 		    g_string_free( errStr, TRUE );
642 
643 		    return FALSE;
644 		}
645 		tcds->creditSum =
646 		    gnc_numeric_add( tcds->creditSum, tmp, 100,
647 				     (GNC_DENOM_AUTO | GNC_HOW_DENOM_LCD) );
648 		tmp = gnc_numeric_zero();
649 		if ( debit_formula &&
650 		     g_strcmp0 (debit_formula, "") != 0 &&
651 		     gnc_sx_parse_vars_from_formula( debit_formula, vars,
652 						     &tmp ) < 0 )
653 		{
654 		    GString *errStr;
655 
656 		    errStr = g_string_sized_new( 32 );
657 		    g_string_printf( errStr,
658 				     _( "Couldn't parse debit formula for "
659 					"split \"%s\"." ),
660 				     xaccSplitGetMemo( s ) );
661                     gnc_error_dialog (GTK_WINDOW (sxed->dialog), "%s",
662 				      (gchar*)errStr->str );
663 		    g_string_free( errStr, TRUE );
664 
665 		    return FALSE;
666 		}
667 		tcds->debitSum = gnc_numeric_add( tcds->debitSum, tmp, 100,
668 						  (GNC_DENOM_AUTO | GNC_HOW_DENOM_LCD) );
669 		tmp = gnc_numeric_zero();
670             }
671 
672             g_hash_table_foreach (txns,
673                                   check_credit_debit_balance,
674                                   &unbalanceable);
675         }
676 
677         /* Subtract out pre-defined vars */
678         if (g_hash_table_lookup_extended (vars, "i",
679                                          &unusedKey,
680                                          &unusedValue))
681         {
682             ttVarCount -= 1;
683         }
684 
685         g_hash_table_destroy (vars);
686         g_hash_table_destroy (txns);
687 
688         if (unbalanceable
689                 && !gnc_verify_dialog (GTK_WINDOW (sxed->dialog), FALSE,
690                                        "%s",
691                                        _("The Scheduled Transaction Editor "
692                                          "cannot automatically balance "
693                                          "this transaction. "
694                                          "Should it still be "
695                                          "entered?")))
696         {
697             return FALSE;
698         }
699     }
700 
701     /* read out data back into SchedXaction object. */
702     /* FIXME: this is getting too deep; split out. */
703     {
704         gchar *name, *nameKey;
705         gboolean nameExists, nameHasChanged;
706         GList *sxList;
707 
708         name = gtk_editable_get_chars (GTK_EDITABLE (sxed->nameEntry), 0, -1);
709         if (strlen (name) == 0 )
710         {
711             const char *sx_has_no_name_msg =
712                 _("Please name the Scheduled Transaction.");
713             gnc_error_dialog (GTK_WINDOW (sxed->dialog), "%s", sx_has_no_name_msg);
714             g_free (name);
715             return FALSE;
716 
717         }
718 
719         nameExists = FALSE;
720         nameKey = g_utf8_collate_key (name, -1);
721         nameHasChanged =
722             (xaccSchedXactionGetName (sxed->sx) == NULL)
723             || (strcmp (xaccSchedXactionGetName (sxed->sx), name) != 0);
724         for ( sxList =
725                     gnc_book_get_schedxactions (gnc_get_current_book())->sx_list;
726                 nameHasChanged && !nameExists && sxList;
727                 sxList = sxList->next )
728         {
729             char *existingName, *existingNameKey;
730             existingName =
731                 xaccSchedXactionGetName ((SchedXaction*)sxList->data);
732             existingNameKey = g_utf8_collate_key (existingName, -1);
733             nameExists |= (strcmp (nameKey, existingNameKey) == 0);
734             g_free (existingNameKey);
735         }
736         if (nameHasChanged && nameExists)
737         {
738             const char *sx_has_existing_name_msg =
739                 _("A Scheduled Transaction with the "
740                    "name \"%s\" already exists. "
741                    "Are you sure you want to name "
742                    "this one the same?");
743             if (!gnc_verify_dialog (GTK_WINDOW (sxed->dialog), FALSE,
744                                       sx_has_existing_name_msg,
745                                       name))
746             {
747                 g_free (nameKey);
748                 g_free (name);
749                 return FALSE;
750             }
751         }
752         g_free (nameKey);
753         g_free (name);
754     }
755 
756     // @@FIXME: similar to below, check the commodities involved, and disallow autocreation
757     {
758         gboolean autocreateState;
759 
760         autocreateState =
761             gtk_toggle_button_get_active (
762                 GTK_TOGGLE_BUTTON (sxed->autocreateOpt));
763 
764         if (((ttVarCount > 0) || multi_commodity) && autocreateState)
765         {
766             gnc_warning_dialog (GTK_WINDOW (sxed->dialog), "%s",
767                                _("Scheduled Transactions with variables "
768                                  "cannot be automatically created."));
769             return FALSE;
770         }
771 
772         /* Fix for part of Bug#121740 -- auto-create transactions are
773          * only valid if there's actually a transaction to create. */
774         if (autocreateState && splitCount == 0)
775         {
776             gnc_warning_dialog (GTK_WINDOW (sxed->dialog), "%s",
777                                _("Scheduled Transactions without a template "
778                                  "transaction cannot be automatically created."));
779             return FALSE;
780         }
781     }
782 
783     /* deal with time. */
784     {
785         GDate startDate, endDate, nextDate;
786 
787         if (!gtk_toggle_button_get_active (sxed->optEndDate)
788                 && !gtk_toggle_button_get_active (sxed->optEndCount)
789                 && !gtk_toggle_button_get_active (sxed->optEndNone) )
790         {
791             const char *sx_end_spec_msg =
792                 _("Please provide a valid end selection.");
793             gnc_error_dialog (GTK_WINDOW (sxed->dialog), "%s", sx_end_spec_msg);
794             return FALSE;
795         }
796 
797         if (gtk_toggle_button_get_active (sxed->optEndCount))
798         {
799             gint occur, rem;
800 
801             occur  =
802                 gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (sxed->endCountSpin));
803 
804             rem =
805                 gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (sxed->endRemainSpin));
806 
807             if (occur == 0)
808             {
809                 const char *sx_occur_count_zero_msg =
810                     _("There must be some number of occurrences.");
811                 gnc_error_dialog (GTK_WINDOW (sxed->dialog), "%s",
812                                   sx_occur_count_zero_msg);
813                 return FALSE;
814             }
815 
816             if (rem > occur)
817             {
818                 const char *sx_occur_counts_wrong_msg =
819                     _("The number of remaining occurrences "
820                        "(%d) is greater than the number of "
821                        "total occurrences (%d).");
822                 gnc_error_dialog (GTK_WINDOW (sxed->dialog),
823                                   sx_occur_counts_wrong_msg,
824                                   rem, occur);
825                 return FALSE;
826             }
827 
828         }
829 
830         g_date_clear (&endDate, 1);
831         if ( gtk_toggle_button_get_active(sxed->optEndDate))
832         {
833             gnc_gdate_set_time64 (&endDate,
834                                gnc_date_edit_get_date (sxed->
835                                        endDateEntry));
836         }
837 
838         g_date_clear (&nextDate, 1);
839         gnc_frequency_save_to_recurrence (sxed->gncfreq, &schedule, &startDate);
840         if (gnc_list_length_cmp (schedule, 0))
841         {
842             g_date_subtract_days (&startDate, 1);
843             recurrenceListNextInstance (schedule, &startDate, &nextDate);
844         }
845         recurrenceListFree (&schedule);
846 
847         if (!g_date_valid (&nextDate)
848                 || (g_date_valid (&endDate) && (g_date_compare (&nextDate, &endDate) > 0)))
849         {
850             const char *invalid_sx_check_msg =
851                 _("You have attempted to create a Scheduled "
852                   "Transaction which will never run. Do you "
853                   "really want to do this?");
854             if (!gnc_verify_dialog (GTK_WINDOW (sxed->dialog), FALSE,
855                                    "%s", invalid_sx_check_msg))
856                 return FALSE;
857         }
858     }
859     return TRUE;
860 }
861 
862 
863 /******************************************************************************
864  * Saves the contents of the SX.  This assumes that gnc_sxed_check_consistent
865  * has returned true.
866  *****************************************************************************/
867 static void
gnc_sxed_save_sx(GncSxEditorDialog2 * sxed)868 gnc_sxed_save_sx (GncSxEditorDialog2 *sxed )
869 {
870     gnc_sx_begin_edit (sxed->sx);
871 
872     /* name */
873     {
874         char *name;
875 
876         name = gtk_editable_get_chars (sxed->nameEntry, 0, -1);
877         xaccSchedXactionSetName (sxed->sx, name);
878         g_free (name);
879     }
880 
881     /* date */
882     {
883         GDate gdate;
884 
885         if (gtk_toggle_button_get_active (sxed->optEndDate))
886         {
887             /* get the end date data */
888             gnc_gdate_set_time64 (&gdate,
889                                gnc_date_edit_get_date (
890                                    sxed->endDateEntry));
891             xaccSchedXactionSetEndDate (sxed->sx, &gdate);
892             /* set the num occurrences data */
893             xaccSchedXactionSetNumOccur (sxed->sx, 0);
894         }
895         else if (gtk_toggle_button_get_active (sxed->optEndCount))
896         {
897             gint num;
898 
899             /* get the occurrences data */
900             num  =
901                 gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (sxed->endCountSpin));
902             xaccSchedXactionSetNumOccur (sxed->sx, num);
903 
904             num =
905                 gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (sxed->endRemainSpin));
906             xaccSchedXactionSetRemOccur (sxed->sx, num );
907 
908             g_date_clear (&gdate, 1);
909             xaccSchedXactionSetEndDate (sxed->sx, &gdate);
910         }
911         else if (gtk_toggle_button_get_active (sxed->optEndNone))
912         {
913             xaccSchedXactionSetNumOccur (sxed->sx, 0);
914             g_date_clear (&gdate, 1);
915             xaccSchedXactionSetEndDate (sxed->sx, &gdate);
916         }
917         else
918         {
919             g_critical ("no valid end specified\n");
920         }
921     }
922 
923     /* Enabled states */
924     {
925         gboolean enabledState;
926 
927         enabledState = gtk_toggle_button_get_active (sxed->enabledOpt);
928         xaccSchedXactionSetEnabled (sxed->sx, enabledState);
929     }
930 
931     /* Auto-create/notification states */
932     {
933         gboolean autocreateState, notifyState;
934 
935         autocreateState = gtk_toggle_button_get_active (sxed->autocreateOpt);
936         notifyState = gtk_toggle_button_get_active (sxed->notifyOpt);
937         /* "Notify" only makes sense if AutoCreate is activated;
938          * enforce that here. */
939         xaccSchedXactionSetAutoCreate (sxed->sx,
940                                        autocreateState,
941                                        (autocreateState & notifyState));
942     }
943 
944     /* days in advance */
945     {
946         int daysInAdvance;
947 
948         daysInAdvance = 0;
949         if (gtk_toggle_button_get_active (sxed->advanceOpt))
950         {
951             daysInAdvance =
952                 gtk_spin_button_get_value_as_int (sxed->advanceSpin);
953         }
954         xaccSchedXactionSetAdvanceCreation (sxed->sx, daysInAdvance);
955 
956         daysInAdvance = 0;
957         if (gtk_toggle_button_get_active (sxed->remindOpt))
958         {
959             daysInAdvance =
960                 gtk_spin_button_get_value_as_int (sxed->remindSpin);
961         }
962         xaccSchedXactionSetAdvanceReminder (sxed->sx, daysInAdvance);
963     }
964 
965     /* start date and freq spec */
966     {
967         GDate gdate;
968         GList *schedule = NULL;
969 
970         gnc_frequency_save_to_recurrence (sxed->gncfreq, &schedule, &gdate);
971         gnc_sx_set_schedule (sxed->sx, schedule);
972         {
973             gchar *recurrence_str = recurrenceListToCompactString (schedule);
974             DEBUG("recurrences parsed [%s]", recurrence_str);
975             g_free (recurrence_str);
976         }
977 
978         /* now that we have it, set the start date */
979         xaccSchedXactionSetStartDate (sxed->sx, &gdate);
980     }
981 
982     gnc_sx_commit_edit (sxed->sx);
983 }
984 
985 
986 static void
enabled_toggled_cb(GtkToggleButton * o,GncSxEditorDialog2 * sxed)987 enabled_toggled_cb (GtkToggleButton *o, GncSxEditorDialog2 *sxed)
988 {
989     return;
990 }
991 
992 
993 static void
autocreate_toggled_cb(GtkToggleButton * o,GncSxEditorDialog2 * sxed)994 autocreate_toggled_cb (GtkToggleButton *o, GncSxEditorDialog2 *sxed)
995 {
996     if ( !gtk_toggle_button_get_active (o))
997     {
998         gtk_toggle_button_set_active (sxed->notifyOpt, FALSE);
999     }
1000     gtk_widget_set_sensitive (GTK_WIDGET (sxed->notifyOpt),
1001                               gtk_toggle_button_get_active (o));
1002 }
1003 
1004 
1005 static void
advance_toggled_cb(GtkButton * o,GncSxEditorDialog2 * sxed)1006 advance_toggled_cb (GtkButton *o, GncSxEditorDialog2 *sxed)
1007 {
1008 
1009     gtk_widget_set_sensitive(GTK_WIDGET (sxed->advanceSpin),
1010                               gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (sxed->advanceOpt)));
1011     gtk_editable_set_editable(GTK_EDITABLE (sxed->advanceSpin), TRUE);
1012 }
1013 
1014 
1015 static void
remind_toggled_cb(GtkButton * o,GncSxEditorDialog2 * sxed)1016 remind_toggled_cb (GtkButton *o, GncSxEditorDialog2 *sxed)
1017 {
1018 
1019     gtk_widget_set_sensitive (GTK_WIDGET (sxed->remindSpin),
1020                               gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (sxed->remindOpt)));
1021     gtk_editable_set_editable (GTK_EDITABLE (sxed->remindSpin), TRUE);
1022 }
1023 
1024 
1025 /* Local destruction of dialog */
1026 static void
scheduledxaction_editor_dialog_destroy(GtkWidget * object,gpointer data)1027 scheduledxaction_editor_dialog_destroy (GtkWidget *object, gpointer data)
1028 {
1029     GncSxEditorDialog2 *sxed = data;
1030 
1031     if (sxed == NULL)
1032         return;
1033 
1034     gnc_unregister_gui_component_by_data
1035     (DIALOG_SCHEDXACTION2_EDITOR_CM_CLASS, sxed);
1036 
1037     gnc_embedded_window_close_page (sxed->embed_window, sxed->plugin_page);
1038     gtk_widget_destroy (GTK_WIDGET (sxed->embed_window));
1039     sxed->embed_window = NULL;
1040     sxed->plugin_page = NULL;
1041     sxed->ledger = NULL;
1042 
1043     g_free (sxed->sxGUIDstr);
1044     sxed->sxGUIDstr = NULL;
1045 
1046     if ( sxed->newsxP )
1047     {
1048         /* FIXME: WTF???
1049          *
1050          * "WTF" explanation: in the "new" click from the caller, we
1051          * set this flag.  When "ok" is pressed on the dialog, we set
1052          * this flag to false, and thus leave the SX live.  If
1053          * "Cancel" is clicked, the flag will still be true, and this
1054          * SX will be cleaned, here. -- jsled
1055          */
1056         gnc_sx_begin_edit (sxed->sx);
1057         xaccSchedXactionDestroy (sxed->sx);
1058     }
1059     sxed->sx = NULL;
1060 
1061     g_free (sxed);
1062 }
1063 
1064 
1065 static
1066 gboolean
sxed_delete_event(GtkWidget * widget,GdkEvent * event,gpointer ud)1067 sxed_delete_event (GtkWidget *widget, GdkEvent *event, gpointer ud)
1068 {
1069     GncSxEditorDialog2 *sxed = (GncSxEditorDialog2*)ud;
1070 
1071     /* We've already processed the SX, likely because of "ok" being
1072      * clicked. */
1073     if (sxed->sx == NULL)
1074     {
1075         return FALSE;
1076     }
1077 
1078     if (!sxed_confirmed_cancel (sxed))
1079     {
1080         return TRUE;
1081     }
1082     return FALSE;
1083 }
1084 
1085 
1086 /*************************************
1087  * Create the Schedule Editor Dialog *
1088  ************************************/
1089 GncSxEditorDialog2 *
gnc_ui_scheduled_xaction_editor_dialog_create2(GtkWindow * parent,SchedXaction * sx,gboolean newSX)1090 gnc_ui_scheduled_xaction_editor_dialog_create2 (GtkWindow *parent,
1091     SchedXaction *sx, gboolean newSX)
1092 {
1093     GncSxEditorDialog2 *sxed;
1094     GtkBuilder *builder;
1095     GtkWidget *button;
1096     int i;
1097     GList *dlgExists = NULL;
1098 
1099     static struct widgetSignalCallback
1100     {
1101         char     *name;
1102         char     *signal;
1103         void     (*fn)();
1104         gpointer objectData;
1105     } widgets[] =
1106     {
1107         { "ok_button",      "clicked",       editor_ok_button_clicked_cb,     NULL },
1108         { "cancel_button",  "clicked",       editor_cancel_button_clicked_cb, NULL },
1109         { "help_button",    "clicked",       editor_help_button_clicked_cb,   NULL },
1110         { "rb_noend",       "toggled",       endgroup_rb_toggled_cb,          GINT_TO_POINTER(END_NEVER_OPTION) },
1111         { "rb_enddate",     "toggled",       endgroup_rb_toggled_cb,          GINT_TO_POINTER(END_DATE_OPTION) },
1112         { "rb_num_occur",   "toggled",       endgroup_rb_toggled_cb,          GINT_TO_POINTER(NUM_OCCUR_OPTION) },
1113         { "remain_spin" ,   "value-changed", sxed_excal_update_adapt_cb,      NULL },
1114         { "enabled_opt",    "toggled",       enabled_toggled_cb,              NULL },
1115         { "autocreate_opt", "toggled",       autocreate_toggled_cb,           NULL },
1116         { "advance_opt",    "toggled",       advance_toggled_cb,              NULL },
1117         { "remind_opt",     "toggled",       remind_toggled_cb,               NULL },
1118         { NULL,             NULL,            NULL,                            NULL }
1119     };
1120 
1121     dlgExists = gnc_find_gui_components (DIALOG_SCHEDXACTION2_EDITOR_CM_CLASS,
1122                                          editor_component_sx_equality,
1123                                          sx);
1124     if (dlgExists != NULL)
1125     {
1126         DEBUG ("dialog already exists; using that one.");
1127         sxed = (GncSxEditorDialog2*)dlgExists->data;
1128         gtk_window_present (GTK_WINDOW (sxed->dialog));
1129         g_list_free (dlgExists);
1130         return sxed;
1131     }
1132 
1133     sxed = g_new0 (GncSxEditorDialog2, 1);
1134 
1135     sxed->sx     = sx;
1136     sxed->newsxP = newSX;
1137 
1138     /* Load up Glade file */
1139     builder = gtk_builder_new();
1140     gnc_builder_add_from_file (builder, "dialog-sx.glade", "advance_days_adj");
1141     gnc_builder_add_from_file (builder, "dialog-sx.glade", "remind_days_adj");
1142     gnc_builder_add_from_file (builder, "dialog-sx.glade", "end_spin_adj");
1143     gnc_builder_add_from_file (builder, "dialog-sx.glade", "remain_spin_adj");
1144     gnc_builder_add_from_file (builder, "dialog-sx.glade", "scheduled_transaction_editor_dialog");
1145 
1146     sxed->builder = builder;
1147 
1148     /* Connect the Widgets */
1149     sxed->dialog = GTK_WIDGET (gtk_builder_get_object (builder, "scheduled_transaction_editor_dialog"));
1150     sxed->notebook = GTK_NOTEBOOK (gtk_builder_get_object (builder, "editor_notebook"));
1151     sxed->nameEntry = GTK_EDITABLE (gtk_builder_get_object (builder, "sxe_name"));
1152     sxed->enabledOpt = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "enabled_opt"));
1153     sxed->autocreateOpt = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "autocreate_opt"));
1154     sxed->notifyOpt = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "notify_opt"));
1155     sxed->advanceOpt = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "advance_opt"));
1156     sxed->advanceSpin = GTK_SPIN_BUTTON (gtk_builder_get_object (builder, "advance_days"));
1157     sxed->remindOpt = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "remind_opt"));
1158     sxed->remindSpin = GTK_SPIN_BUTTON (gtk_builder_get_object (builder, "remind_days"));
1159     sxed->lastOccurLabel = GTK_LABEL (gtk_builder_get_object (builder, "last_occur_label"));
1160     sxed->optEndNone = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "rb_noend"));
1161     sxed->optEndDate = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "rb_enddate"));
1162     sxed->optEndCount = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "rb_num_occur"));
1163     sxed->endCountSpin = GTK_ENTRY (gtk_builder_get_object (builder, "end_spin"));
1164     sxed->endRemainSpin = GTK_ENTRY (gtk_builder_get_object (builder, "remain_spin"));
1165 
1166     // Set the name of this dialog so it can be easily manipulated with css
1167     gtk_widget_set_name (GTK_WIDGET(sxed->dialog), "gnc-id-sx2-editor");
1168     gnc_widget_style_context_add_class (GTK_WIDGET(sxed->dialog), "gnc-class-sx");
1169 
1170     gtk_window_set_transient_for (GTK_WINDOW (sxed->dialog), parent);
1171 
1172     /* Setup the end-date GNC widget */
1173     {
1174         GtkWidget *endDateBox = GTK_WIDGET(gtk_builder_get_object (builder, "editor_end_date_box"));
1175         sxed->endDateEntry = GNC_DATE_EDIT (gnc_date_edit_new (gnc_time (NULL), FALSE, FALSE));
1176         gtk_widget_show (GTK_WIDGET (sxed->endDateEntry));
1177         g_signal_connect (sxed->endDateEntry, "date-changed",
1178                           G_CALLBACK (sxed_excal_update_adapt_cb), sxed);
1179         gtk_box_pack_start (GTK_BOX (endDateBox), GTK_WIDGET (sxed->endDateEntry),
1180                             TRUE, TRUE, 0 );
1181     }
1182 
1183     gnc_register_gui_component (DIALOG_SCHEDXACTION2_EDITOR_CM_CLASS,
1184                                 NULL, /* no refresh handler */
1185                                 sxed_close_handler,
1186                                 sxed);
1187 
1188     g_signal_connect (sxed->dialog, "delete_event",
1189                       G_CALLBACK (sxed_delete_event), sxed);
1190     g_signal_connect (sxed->dialog, "destroy",
1191                       G_CALLBACK (scheduledxaction_editor_dialog_destroy),
1192                       sxed );
1193 
1194     for (i = 0; widgets[i].name != NULL; i++)
1195     {
1196         button = GTK_WIDGET (gtk_builder_get_object (builder, widgets[i].name));
1197         if (widgets[i].objectData != NULL)
1198         {
1199             g_object_set_data (G_OBJECT (button), "whichOneAmI",
1200                                widgets[i].objectData);
1201         }
1202         g_signal_connect (button, widgets[i].signal,
1203                           G_CALLBACK (widgets[i].fn ), sxed);
1204     }
1205 
1206     /* Set sensitivity settings  */
1207     gtk_widget_set_sensitive (GTK_WIDGET (sxed->notifyOpt), FALSE );
1208     gtk_widget_set_sensitive (GTK_WIDGET (sxed->advanceSpin), FALSE );
1209     gtk_widget_set_sensitive (GTK_WIDGET (sxed->remindSpin), FALSE );
1210     gtk_widget_set_sensitive (GTK_WIDGET (sxed->endCountSpin), FALSE );
1211     gtk_widget_set_sensitive (GTK_WIDGET (sxed->endRemainSpin), FALSE );
1212     gtk_editable_set_editable (GTK_EDITABLE (sxed->advanceSpin), TRUE );
1213     gtk_editable_set_editable (GTK_EDITABLE (sxed->remindSpin), TRUE );
1214 
1215     /* Allow resize */
1216     gtk_window_set_resizable (GTK_WINDOW (sxed->dialog), TRUE);
1217     gnc_restore_window_size (GNC_PREFS_GROUP_SXED, GTK_WINDOW (sxed->dialog), parent);
1218 
1219     /* create the frequency-selection widget and example [dense-]calendar. */
1220     schedXact_editor_create_freq_sel (sxed);
1221 
1222     /* create the template-transaction ledger window */
1223     schedXact_editor_create_ledger (sxed);
1224 
1225     /* populate */
1226     schedXact_editor_populate (sxed);
1227 
1228     /* Do not call show_all here. Screws up the gtkuimanager code */
1229     gtk_widget_show (sxed->dialog);
1230     gtk_notebook_set_current_page (GTK_NOTEBOOK (sxed->notebook), 0);
1231 
1232     /* Refresh the cal and the ledger */
1233     gtk_widget_queue_resize (GTK_WIDGET (sxed->example_cal));
1234 
1235     gnc_ledger_display2_refresh (sxed->ledger);
1236 
1237     /* Move keyboard focus to the name entry */
1238     gtk_widget_grab_focus (GTK_WIDGET (sxed->nameEntry));
1239 
1240     gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, sxed);
1241     g_object_unref (G_OBJECT (builder));
1242 
1243     return sxed;
1244 }
1245 
1246 
1247 static
1248 void
schedXact_editor_create_freq_sel(GncSxEditorDialog2 * sxed)1249 schedXact_editor_create_freq_sel (GncSxEditorDialog2 *sxed)
1250 {
1251     GtkBox *b;
1252 
1253     b = GTK_BOX (gtk_builder_get_object (sxed->builder, "gncfreq_hbox"));
1254 
1255     sxed->gncfreq =
1256         GNC_FREQUENCY (gnc_frequency_new_from_recurrence (gnc_sx_get_schedule (sxed->sx),
1257                       xaccSchedXactionGetStartDate (sxed->sx)));
1258     g_assert (sxed->gncfreq);
1259     g_signal_connect (sxed->gncfreq, "changed",
1260                       G_CALLBACK (gnc_sxed_freq_changed),
1261                       sxed );
1262     gtk_container_add (GTK_CONTAINER (b), GTK_WIDGET (sxed->gncfreq) );
1263 
1264     b = GTK_BOX(gtk_builder_get_object (sxed->builder, "example_cal_hbox" ));
1265     sxed->dense_cal_model = gnc_dense_cal_store_new (EX_CAL_NUM_MONTHS * 31);
1266     sxed->example_cal = GNC_DENSE_CAL (gnc_dense_cal_new_with_model (GNC_DENSE_CAL_MODEL (sxed->dense_cal_model)));
1267     g_assert (sxed->example_cal);
1268     gnc_dense_cal_set_num_months (sxed->example_cal, EX_CAL_NUM_MONTHS);
1269     gnc_dense_cal_set_months_per_col( sxed->example_cal, EX_CAL_MO_PER_COL);
1270     gtk_container_add (GTK_CONTAINER (b), GTK_WIDGET (sxed->example_cal));
1271     gtk_widget_show (GTK_WIDGET (sxed->example_cal));
1272 }
1273 
1274 
1275 static
1276 void
schedXact_editor_create_ledger(GncSxEditorDialog2 * sxed)1277 schedXact_editor_create_ledger (GncSxEditorDialog2 *sxed)
1278 {
1279     GncTreeModelSplitReg *model;
1280     GtkWidget *main_vbox;
1281     GtkWidget *label;
1282 
1283     /* Create the ledger */
1284     sxed->sxGUIDstr = guid_to_string (xaccSchedXactionGetGUID (sxed->sx));
1285     sxed->ledger = gnc_ledger_display2_template_gl (sxed->sxGUIDstr);
1286     model = gnc_ledger_display2_get_split_model_register (sxed->ledger);
1287 
1288     /* First the embedded window */
1289     main_vbox = GTK_WIDGET (gtk_builder_get_object (sxed->builder, "register_vbox" ));
1290     sxed->embed_window =
1291         gnc_embedded_window_new ("SXWindowActions",
1292                                 gnc_sxed_menu_entries,
1293                                 gnc_sxed_menu_n_entries,
1294                                 "gnc-sxed-window-ui.xml",
1295                                 sxed->dialog,
1296                                 FALSE, /* no accelerators */
1297                                 sxed);
1298 
1299     gtk_box_pack_start (GTK_BOX (main_vbox), GTK_WIDGET (sxed->embed_window), TRUE, TRUE, 0);
1300 
1301     label = gtk_label_new (_("Note: If you have already accepted changes to the Template, Cancel will not revoke them."));
1302     gtk_box_pack_end (GTK_BOX (main_vbox), GTK_WIDGET (label), FALSE, TRUE, 0);
1303     gtk_widget_show (label);
1304 
1305     /* Now create the register plugin page. */
1306     sxed->plugin_page = gnc_plugin_page_register2_new_ledger (sxed->ledger);
1307     gnc_plugin_page_set_ui_description (sxed->plugin_page,
1308                                         "gnc-sxed-window-ui-full.xml");
1309     gnc_plugin_page_register2_set_options (sxed->plugin_page,
1310                                           NUM_LEDGER_LINES_DEFAULT, FALSE );
1311 
1312     gnc_embedded_window_open_page (sxed->embed_window, sxed->plugin_page);
1313 
1314     /* configure... */
1315     /* use double-line, so scheduled transaction Notes can be edited */
1316     gnc_tree_model_split_reg_config (model, model->type, model->style, TRUE);
1317 }
1318 
1319 
1320 
1321 static
1322 void
schedXact_editor_populate(GncSxEditorDialog2 * sxed)1323 schedXact_editor_populate (GncSxEditorDialog2 *sxed)
1324 {
1325     char *name;
1326     time64 tmpDate;
1327     GncTreeModelSplitReg *model;
1328     const GDate *gd;
1329     gint daysInAdvance;
1330     gboolean enabledState, autoCreateState, notifyState;
1331 
1332     name = xaccSchedXactionGetName (sxed->sx);
1333     if (name != NULL)
1334     {
1335         gtk_entry_set_text (GTK_ENTRY (sxed->nameEntry), name);
1336     }
1337     {
1338         gd = xaccSchedXactionGetLastOccurDate (sxed->sx);
1339         if (g_date_valid (gd))
1340         {
1341             gchar dateBuf[ MAX_DATE_LENGTH+1 ];
1342             qof_print_gdate (dateBuf, MAX_DATE_LENGTH, gd);
1343             gtk_label_set_text (sxed->lastOccurLabel, dateBuf);
1344         }
1345         else
1346         {
1347             gtk_label_set_text (sxed->lastOccurLabel, _("(never)"));
1348         }
1349         gd = NULL;
1350     }
1351 
1352     gd = xaccSchedXactionGetEndDate (sxed->sx);
1353     if ( g_date_valid (gd))
1354     {
1355         gtk_toggle_button_set_active (sxed->optEndDate, TRUE);
1356         tmpDate = gnc_time64_get_day_start_gdate (gd);
1357         gnc_date_edit_set_time (sxed->endDateEntry, tmpDate);
1358 
1359         set_endgroup_toggle_states (sxed, END_DATE);
1360     }
1361     else if (xaccSchedXactionHasOccurDef (sxed->sx))
1362     {
1363         gint numOccur = xaccSchedXactionGetNumOccur (sxed->sx);
1364         gint numRemain = xaccSchedXactionGetRemOccur (sxed->sx);
1365 
1366         gtk_toggle_button_set_active (sxed->optEndCount, TRUE);
1367 
1368         gtk_spin_button_set_value (GTK_SPIN_BUTTON (sxed->endCountSpin), numOccur);
1369         gtk_spin_button_set_value (GTK_SPIN_BUTTON (sxed->endRemainSpin), numRemain);
1370 
1371         set_endgroup_toggle_states (sxed, END_OCCUR);
1372     }
1373     else
1374     {
1375         gtk_toggle_button_set_active (sxed->optEndNone, TRUE);
1376         set_endgroup_toggle_states (sxed, END_NEVER);
1377     }
1378 
1379     enabledState = xaccSchedXactionGetEnabled (sxed->sx);
1380     gtk_toggle_button_set_active (sxed->enabledOpt, enabledState);
1381 
1382     /* Do auto-create/notify setup */
1383     if (sxed->newsxP)
1384     {
1385         autoCreateState =
1386             gnc_prefs_get_bool (GNC_PREFS_GROUP_SXED, GNC_PREF_CREATE_AUTO);
1387         notifyState =
1388             gnc_prefs_get_bool (GNC_PREFS_GROUP_SXED, GNC_PREF_NOTIFY);
1389     }
1390     else
1391     {
1392         xaccSchedXactionGetAutoCreate (sxed->sx,
1393                                        &autoCreateState,
1394                                        &notifyState);
1395     }
1396     gtk_toggle_button_set_active (sxed->autocreateOpt, autoCreateState);
1397     if (!autoCreateState)
1398     {
1399         notifyState = FALSE;
1400     }
1401     gtk_toggle_button_set_active( sxed->notifyOpt, notifyState );
1402 
1403     /* Do days-in-advance-to-create widget[s] setup. */
1404     if ( sxed->newsxP )
1405     {
1406         daysInAdvance =
1407             gnc_prefs_get_float (GNC_PREFS_GROUP_SXED, GNC_PREF_CREATE_DAYS);
1408     }
1409     else
1410     {
1411         daysInAdvance =
1412             xaccSchedXactionGetAdvanceCreation (sxed->sx);
1413     }
1414     if (daysInAdvance != 0)
1415     {
1416         gtk_toggle_button_set_active (sxed->advanceOpt, TRUE);
1417         gtk_spin_button_set_value (sxed->advanceSpin,
1418                                    (gfloat)daysInAdvance);
1419     }
1420 
1421     /* Do days-in-advance-to-remind widget[s] setup. */
1422     if (sxed->newsxP)
1423     {
1424         daysInAdvance =
1425             gnc_prefs_get_float (GNC_PREFS_GROUP_SXED, GNC_PREF_REMIND_DAYS);
1426     }
1427     else
1428     {
1429         daysInAdvance =
1430             xaccSchedXactionGetAdvanceReminder (sxed->sx);
1431     }
1432     if (daysInAdvance != 0)
1433     {
1434         gtk_toggle_button_set_active (sxed->remindOpt, TRUE);
1435         gtk_spin_button_set_value (sxed->remindSpin,
1436                                    (gfloat)daysInAdvance);
1437     }
1438 
1439     if (sxed->newsxP)
1440     {
1441         gnc_sx_set_instance_count (sxed->sx, 1);
1442     }
1443 
1444     /* populate the ledger */
1445     {
1446         /* create the split list */
1447         GList        *splitList;
1448 
1449         splitList = xaccSchedXactionGetSplits (sxed->sx);
1450         if (splitList != NULL)
1451         {
1452             model = gnc_ledger_display2_get_split_model_register (sxed->ledger);
1453             gnc_tree_model_split_reg_load (model, splitList, NULL );
1454         } /* otherwise, use the existing stuff. */
1455     }
1456 
1457     /* Update the example cal */
1458     gnc_sxed_update_cal (sxed);
1459 }
1460 
1461 
1462 static
1463 void
set_endgroup_toggle_states(GncSxEditorDialog2 * sxed,EndType type)1464 set_endgroup_toggle_states (GncSxEditorDialog2 *sxed, EndType type)
1465 {
1466     gtk_widget_set_sensitive (GTK_WIDGET (sxed->endDateEntry), (type == END_DATE) );
1467     gtk_widget_set_sensitive (GTK_WIDGET (sxed->endCountSpin), (type == END_OCCUR) );
1468     gtk_widget_set_sensitive (GTK_WIDGET (sxed->endRemainSpin), (type == END_OCCUR) );
1469 }
1470 
1471 
1472 static
1473 void
endgroup_rb_toggled_cb(GtkButton * b,gpointer d)1474 endgroup_rb_toggled_cb (GtkButton *b, gpointer d)
1475 {
1476     /* figure out which one */
1477     GncSxEditorDialog2 *sxed;
1478     gint id;
1479 
1480     sxed = (GncSxEditorDialog2*)d;
1481     id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (b), "whichOneAmI"));
1482 
1483     switch (id)
1484     {
1485     case END_NEVER_OPTION:
1486         set_endgroup_toggle_states (sxed, END_NEVER);
1487         break;
1488     case END_DATE_OPTION:
1489         set_endgroup_toggle_states (sxed, END_DATE);
1490         break;
1491     case NUM_OCCUR_OPTION:
1492         set_endgroup_toggle_states (sxed, END_OCCUR);
1493         break;
1494     default:
1495         g_critical ("Unknown id %d", id);
1496         break;
1497     }
1498     gnc_sxed_update_cal (sxed);
1499 }
1500 
1501 
1502 /********************************************************************\
1503  * gnc_register_check_close                                         *
1504  *                                                                  *
1505  * Args:   regData - the data struct for this register              *
1506  * Return: none                                                     *
1507 \********************************************************************/
1508 static void
gnc_sxed_reg_check_close(GncSxEditorDialog2 * sxed)1509 gnc_sxed_reg_check_close (GncSxEditorDialog2 *sxed)
1510 {
1511     GncTreeViewSplitReg *view;
1512     Transaction *dirty_trans;
1513 
1514     const char *message =
1515         _("The current template transaction "
1516           "has been changed. "
1517           "Would you like to record the changes?");
1518 
1519     view = gnc_ledger_display2_get_split_view_register (sxed->ledger);
1520 
1521     dirty_trans = gnc_tree_view_split_reg_get_dirty_trans (view);
1522 
1523     if (dirty_trans == NULL)
1524         return;
1525 
1526     if (gnc_verify_dialog (GTK_WINDOW (sxed->dialog), TRUE, "%s", message))
1527     {
1528         /* Save the template transactions changes */
1529         xaccTransCommitEdit (dirty_trans);
1530         gnc_tree_view_split_reg_set_dirty_trans (view, NULL);
1531         return;
1532     }
1533     else
1534     {
1535         /* Cancel the template transactions changes */
1536         gnc_tree_view_split_reg_cancel_edit (view, TRUE);
1537     }
1538 }
1539 
1540 
1541 static gboolean
editor_component_sx_equality(gpointer find_data,gpointer user_data)1542 editor_component_sx_equality (gpointer find_data,
1543                               gpointer user_data)
1544 {
1545     return ((SchedXaction*)find_data
1546              == ((GncSxEditorDialog2*)user_data)->sx);
1547 }
1548 /*
1549 typedef enum { NO_END, DATE_END, COUNT_END } END_TYPE;
1550 */
1551 
1552 static void
gnc_sxed_update_cal(GncSxEditorDialog2 * sxed)1553 gnc_sxed_update_cal (GncSxEditorDialog2 *sxed)
1554 {
1555     GList *recurrences = NULL;
1556     GDate start_date, first_date;
1557 
1558     g_date_clear (&start_date, 1);
1559 
1560     gnc_frequency_save_to_recurrence (sxed->gncfreq, &recurrences, &start_date);
1561     g_date_subtract_days (&start_date, 1);
1562     recurrenceListNextInstance (recurrences, &start_date, &first_date);
1563 
1564     /* Deal with the fact that this SX may have been run before [the
1565      * calendar should only show upcoming instances]... */
1566     {
1567         const GDate *last_sx_inst;
1568 
1569         last_sx_inst = xaccSchedXactionGetLastOccurDate (sxed->sx);
1570         if (g_date_valid (last_sx_inst)
1571                 && g_date_valid (&first_date)
1572                 && g_date_compare (last_sx_inst, &first_date) != 0)
1573         {
1574             /* last occurrence will be passed as initial date to update store
1575              * later on as well */
1576             start_date = *last_sx_inst;
1577             recurrenceListNextInstance (recurrences, &start_date, &first_date);
1578         }
1579         else
1580             /* move one day back so the store can get the proper first recurrence. */
1581             g_date_subtract_days(&start_date, 1);
1582     }
1583 
1584     if (!g_date_valid (&first_date))
1585     {
1586         /* Nothing to do. */
1587         gnc_dense_cal_store_clear (sxed->dense_cal_model);
1588         goto cleanup;
1589     }
1590 
1591     gnc_dense_cal_store_update_name (sxed->dense_cal_model, xaccSchedXactionGetName (sxed->sx));
1592     {
1593         gchar *schedule_desc = recurrenceListToCompactString (recurrences);
1594         gnc_dense_cal_store_update_info (sxed->dense_cal_model, schedule_desc);
1595         g_free (schedule_desc);
1596     }
1597 
1598     //gnc_dense_cal_set_month(sxed->example_cal, g_date_get_month(&first_date));
1599     //gnc_dense_cal_set_year(sxed->example_cal, g_date_get_year(&first_date));
1600 
1601     /* figure out the end restriction */
1602     if (gtk_toggle_button_get_active (sxed->optEndDate))
1603     {
1604         GDate end_date;
1605         g_date_clear (&end_date, 1);
1606         gnc_gdate_set_time64 (&end_date, gnc_date_edit_get_date (sxed->endDateEntry));
1607         gnc_dense_cal_store_update_recurrences_date_end (sxed->dense_cal_model, &start_date, recurrences, &end_date);
1608     }
1609     else if (gtk_toggle_button_get_active (sxed->optEndNone))
1610     {
1611         gnc_dense_cal_store_update_recurrences_no_end (sxed->dense_cal_model, &start_date, recurrences);
1612     }
1613     else if (gtk_toggle_button_get_active (sxed->optEndCount))
1614     {
1615         gint num_remain
1616         = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (sxed->endRemainSpin));
1617         gnc_dense_cal_store_update_recurrences_count_end (sxed->dense_cal_model, &start_date, recurrences, num_remain);
1618     }
1619     else
1620     {
1621         g_error ("unknown end condition");
1622     }
1623 
1624 cleanup:
1625     recurrenceListFree (&recurrences);
1626 }
1627 
1628 
1629 static void
gnc_sxed_freq_changed(GncFrequency * gf,gpointer ud)1630 gnc_sxed_freq_changed (GncFrequency *gf, gpointer ud)
1631 {
1632     gnc_sxed_update_cal ((GncSxEditorDialog2*)ud);
1633 }
1634 
1635 
1636 static void
sxed_excal_update_adapt_cb(GtkWidget * o,gpointer ud)1637 sxed_excal_update_adapt_cb (GtkWidget *o, gpointer ud)
1638 {
1639     gnc_sxed_update_cal ((GncSxEditorDialog2*)ud);
1640 }
1641 
1642 
1643 static void
on_sx_check_toggled_cb(GtkWidget * togglebutton,gpointer user_data)1644 on_sx_check_toggled_cb (GtkWidget *togglebutton, gpointer user_data)
1645 {
1646     GtkWidget *widget_notify;
1647     GHashTable *table;
1648 
1649     PINFO("Togglebutton is %p and user_data is %p", togglebutton, user_data);
1650     PINFO("Togglebutton builder name is %s", gtk_buildable_get_name (GTK_BUILDABLE (togglebutton)));
1651 
1652     /* We need to use the hash table to find the required widget to activate. */
1653     table = g_object_get_data(G_OBJECT(user_data), "prefs_widget_hash");
1654     widget_notify = g_hash_table_lookup(table, "pref/" GNC_PREFS_GROUP_SXED "/" GNC_PREF_NOTIFY);
1655 
1656     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(togglebutton)))
1657         gtk_widget_set_sensitive (widget_notify, TRUE);
1658     else
1659         gtk_widget_set_sensitive (widget_notify, FALSE);
1660 }
1661 
1662 
1663 /* ------------------------------------------------------------ */
1664 /* sx app engine;  move to somewhere appropriate. :/            */
1665 
1666 typedef struct _acct_deletion_handler_data
1667 {
1668     GList *affected_sxes;
1669     GtkWidget *dialog;
1670     GtkWindow *parent;
1671 } acct_deletion_handler_data;
1672 
1673 
1674 static void
_open_editors(GtkDialog * dialog,gint response_code,gpointer data)1675 _open_editors (GtkDialog *dialog, gint response_code, gpointer data)
1676 {
1677     acct_deletion_handler_data *adhd = (acct_deletion_handler_data *)data;
1678     gtk_widget_hide (adhd->dialog);
1679     {
1680         GList *sx_iter;
1681         for (sx_iter = adhd->affected_sxes; sx_iter; sx_iter = sx_iter->next)
1682         {
1683             gnc_ui_scheduled_xaction_editor_dialog_create2 (GTK_WINDOW(adhd->parent),
1684                 (SchedXaction*)sx_iter->data, FALSE);
1685         }
1686     }
1687     g_list_free (adhd->affected_sxes);
1688     gtk_widget_destroy (GTK_WIDGET (adhd->dialog));
1689     g_free (adhd);
1690 }
1691 
1692 
1693 static void
_sx_engine_event_handler(QofInstance * ent,QofEventId event_type,gpointer user_data,gpointer evt_data)1694 _sx_engine_event_handler (QofInstance *ent, QofEventId event_type, gpointer user_data, gpointer evt_data)
1695 {
1696     Account *acct;
1697     QofBook *book;
1698     GList *affected_sxes;
1699 
1700     if (!(event_type & QOF_EVENT_DESTROY))
1701         return;
1702     if (!GNC_IS_ACCOUNT(ent))
1703         return;
1704     acct = GNC_ACCOUNT(ent);
1705     book = qof_instance_get_book (QOF_INSTANCE (acct));
1706     affected_sxes = gnc_sx_get_sxes_referencing_account (book, acct);
1707 
1708     if (!gnc_list_length_cmp (affected_sxes, 0))
1709         return;
1710 
1711     {
1712         GList *sx_iter;
1713         acct_deletion_handler_data *data;
1714         GtkBuilder *builder;
1715         GtkWidget *dialog;
1716         GtkWindow *parent;
1717         GtkListStore *name_list;
1718         GtkTreeView *list;
1719         GtkTreeViewColumn *name_column;
1720         GtkCellRenderer *renderer;
1721 
1722         builder = gtk_builder_new();
1723         gnc_builder_add_from_file (builder, "dialog-sx.glade", "account_deletion_dialog");
1724 
1725         dialog = GTK_WIDGET (gtk_builder_get_object (builder, "account_deletion_dialog"));
1726         parent = gnc_ui_get_main_window (NULL);
1727 
1728         gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
1729 
1730         list = GTK_TREE_VIEW (gtk_builder_get_object (builder, "sx_list"));
1731 
1732         // Set grid lines option to preference
1733         gtk_tree_view_set_grid_lines (GTK_TREE_VIEW(list), gnc_tree_view_get_grid_lines_pref ());
1734 
1735         data = (acct_deletion_handler_data*)g_new0 (acct_deletion_handler_data, 1);
1736         data->dialog = dialog;
1737         data->parent = parent;
1738         data->affected_sxes = affected_sxes;
1739         name_list = gtk_list_store_new (1, G_TYPE_STRING);
1740         for (sx_iter = affected_sxes; sx_iter != NULL; sx_iter = sx_iter->next)
1741         {
1742             SchedXaction *sx;
1743             GtkTreeIter iter;
1744             gchar *sx_name;
1745 
1746             sx = (SchedXaction*)sx_iter->data;
1747             sx_name = xaccSchedXactionGetName (sx);
1748             gtk_list_store_append (name_list, &iter);
1749             gtk_list_store_set (name_list, &iter, 0, sx_name, -1);
1750         }
1751         gtk_tree_view_set_model (list, GTK_TREE_MODEL (name_list));
1752         g_object_unref (G_OBJECT (name_list));
1753 
1754         renderer = gtk_cell_renderer_text_new();
1755         name_column = gtk_tree_view_column_new_with_attributes (_("Name"),
1756                       renderer,
1757                       "text", 0, NULL);
1758         gtk_tree_view_append_column (list, name_column);
1759 
1760         g_signal_connect (G_OBJECT (dialog), "response",
1761                          G_CALLBACK (_open_editors), data);
1762 
1763         gtk_widget_show_all (GTK_WIDGET (dialog));
1764         gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, data);
1765         g_object_unref (G_OBJECT (builder));
1766     }
1767 }
1768 
1769 
1770 void
gnc_ui_sx_initialize2(void)1771 gnc_ui_sx_initialize2 (void) //FIXME need to remove the 2 when live
1772 {
1773     _sx_engine_event_handler_id = qof_event_register_handler (_sx_engine_event_handler, NULL);
1774 
1775     gnc_hook_add_dangler (HOOK_BOOK_OPENED,
1776                           (GFunc)gnc_sx_sxsincelast_book_opened, NULL, NULL);
1777 
1778     /* Add page to preferences page for Scheduled Transactions */
1779     /* The parameters are; glade file, items to add from glade file - last being the dialog, preference tab name */
1780     gnc_preferences_add_page ("dialog-sx.glade",
1781                               "create_days_adj,remind_days_adj,sx_prefs",
1782                               _("Scheduled Transactions"));
1783 }
1784