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