GnuCash  5.6-150-g038405b370+
dialog-sx-editor.c
1 /********************************************************************\
2  * dialog-sx-editor.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 #include <stdbool.h>
37 
38 #include <gtk/gtk.h>
39 #include <glib/gi18n.h>
40 #include <locale.h>
41 
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 #include "gnc-session.h"
67 #include <gnc-glib-utils.h>
68 
69 #include "gnc-split-reg.h"
70 
71 #include "gnc-sx-instance-model.h"
72 #include "dialog-sx-since-last-run.h"
73 
74 #undef G_LOG_DOMAIN
75 #define G_LOG_DOMAIN "gnc.gui.sx.editor"
76 
77 static QofLogModule log_module = GNC_MOD_GUI_SX;
78 
79 static gint _sx_engine_event_handler_id = -1;
80 
81 #define END_NEVER_OPTION 0
82 #define END_DATE_OPTION 1
83 #define NUM_OCCUR_OPTION 2
84 
85 #define NUM_LEDGER_LINES_DEFAULT 6
86 
87 #define EX_CAL_NUM_MONTHS 12
88 #define EX_CAL_MO_PER_COL 3
89 
90 #define GNC_D_WIDTH 25
91 #define GNC_D_BUF_WIDTH 26
92 
95 typedef enum _EndTypeEnum
96 {
97  END_NEVER,
98  END_DATE,
99  END_OCCUR,
100 } EndType;
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  GtkEntry *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  EndType end_type;
134  GtkEntry *endCountSpin;
135  GtkEntry *endRemainSpin;
136  GNCDateEdit *endDateEntry;
137 
138  char *sxGUIDstr;
139 
140  GncEmbeddedWindow *embed_window;
141  GncPluginPage *plugin_page;
142 };
143 
146 static void schedXact_editor_create_freq_sel (GncSxEditorDialog *sxed);
147 static void schedXact_editor_create_ledger (GncSxEditorDialog *sxed);
148 static void schedXact_editor_populate (GncSxEditorDialog *);
149 static void endgroup_rb_toggled_cb (GtkButton *b, gpointer d);
150 static void set_endgroup_toggle_states (GncSxEditorDialog *sxed, EndType t);
151 static void advance_toggled_cb (GtkButton *b, GncSxEditorDialog *sxed);
152 static void remind_toggled_cb (GtkButton *b, GncSxEditorDialog *sxed);
153 static gboolean gnc_sxed_check_consistent (GncSxEditorDialog *sxed);
154 static gboolean gnc_sxed_check_changed (GncSxEditorDialog *sxed);
155 static void gnc_sxed_save_sx (GncSxEditorDialog *sxed);
156 static void gnc_sxed_freq_changed (GncFrequency *gf, gpointer ud);
157 static void sxed_excal_update_adapt_cb (GtkWidget *o, gpointer ud);
158 static void gnc_sxed_update_cal (GncSxEditorDialog *sxed);
159 void on_sx_check_toggled_cb (GtkWidget *togglebutton, gpointer user_data);
160 static void gnc_sxed_reg_check_close (GncSxEditorDialog *sxed);
161 static gboolean sxed_delete_event (GtkWidget *widget, GdkEvent *event, gpointer ud);
162 static gboolean sxed_confirmed_cancel (GncSxEditorDialog *sxed);
163 static gboolean editor_component_sx_equality (gpointer find_data,
164  gpointer user_data);
165 
166 static GActionEntry gnc_sxed_menu_entries [] =
167 {
168  { "EditAction", NULL, NULL, NULL, NULL },
169  { "TransactionAction", NULL, NULL, NULL, NULL },
170  { "ViewAction", NULL, NULL, NULL, NULL },
171  { "ActionsAction", NULL, NULL, NULL, NULL },
172 };
173 static guint gnc_sxed_menu_n_entries = G_N_ELEMENTS(gnc_sxed_menu_entries);
174 
177 static void
178 sxed_close_handler (gpointer user_data)
179 {
180  GncSxEditorDialog *sxed = user_data;
181 
182  gnc_sxed_reg_check_close (sxed);
183  gnc_save_window_size (GNC_PREFS_GROUP_SXED, GTK_WINDOW (sxed->dialog));
184  gtk_widget_destroy (sxed->dialog);
185  /* The data will be cleaned up in the destroy handler. */
186 }
187 
188 
193 static gboolean
194 sxed_confirmed_cancel (GncSxEditorDialog *sxed)
195 {
196  SplitRegister *reg;
197 
198  reg = gnc_ledger_display_get_split_register (sxed->ledger);
199  /* check for changes */
200  if (gnc_sxed_check_changed (sxed))
201  {
202  const char *sx_changed_msg =
203  _("This Scheduled Transaction has changed; are you "
204  "sure you want to cancel?");
205  if (!gnc_verify_dialog (GTK_WINDOW (sxed->dialog), FALSE, "%s", sx_changed_msg))
206  {
207  return FALSE;
208  }
209  }
210  /* cancel ledger changes */
212  return TRUE;
213 }
214 
215 
216 /**********************************
217  * Dialog Action Button functions *
218  *********************************/
219 static void
220 editor_cancel_button_clicked_cb (GtkButton *b, GncSxEditorDialog *sxed)
221 {
222  /* close */
223  if (!sxed_confirmed_cancel (sxed))
224  return;
225 
226  gnc_close_gui_component_by_data (DIALOG_SCHEDXACTION_EDITOR_CM_CLASS,
227  sxed);
228 }
229 
230 
231 static void
232 editor_help_button_clicked_cb (GtkButton *b, GncSxEditorDialog *sxed)
233 {
234  gnc_gnome_help (GTK_WINDOW (sxed->dialog), DF_MANUAL, DL_SXEDITOR);
235 }
236 
237 
238 static void
239 editor_ok_button_clicked_cb (GtkButton *b, GncSxEditorDialog *sxed)
240 {
241  QofBook *book;
242  SchedXactions *sxes;
243 
244  if (!gnc_sxed_check_consistent (sxed))
245  return;
246 
247  gnc_sxed_save_sx (sxed);
248 
249  /* add to list */
250  // @@fixme -- forget 'new'-flag: check for existence of the SX [?]
251  if (sxed->newsxP)
252  {
253  book = gnc_get_current_book ();
254  sxes = gnc_book_get_schedxactions (book);
255  gnc_sxes_add_sx (sxes, sxed->sx);
256  sxed->newsxP = FALSE;
257  }
258 
259  /* cleanup */
260  gnc_close_gui_component_by_data (DIALOG_SCHEDXACTION_EDITOR_CM_CLASS,
261  sxed);
262 }
263 
264 
265 static gboolean
266 gnc_sxed_check_name_changed (GncSxEditorDialog *sxed)
267 {
268  const char *name = gtk_entry_get_text (sxed->nameEntry);
269 
270  if (!name || !name[0])
271  return TRUE;
272 
273  if (xaccSchedXactionGetName (sxed->sx) == NULL ||
274  strcmp (xaccSchedXactionGetName (sxed->sx), name) != 0)
275  return TRUE;
276 
277  return FALSE;
278 }
279 
280 static gboolean
281 gnc_sxed_check_end_date_changed (GncSxEditorDialog *sxed)
282 {
283  GDate sxEndDate, dlgEndDate;
284 
285  if (!xaccSchedXactionHasEndDate (sxed->sx))
286  return TRUE;
287 
288  sxEndDate = *xaccSchedXactionGetEndDate (sxed->sx);
289  gnc_gdate_set_time64 (&dlgEndDate,
290  gnc_date_edit_get_date (sxed-> endDateEntry));
291 
292  if (g_date_compare (&sxEndDate, &dlgEndDate) != 0)
293  return TRUE;
294 
295  return FALSE;
296 }
297 
298 static gboolean
299 gnc_sxed_check_num_occurs_changed (GncSxEditorDialog *sxed)
300 {
301  gint sxNumOccur, sxNumRem, dlgNumOccur, dlgNumRem;
302 
303  if (!xaccSchedXactionGetNumOccur (sxed->sx))
304  return TRUE;
305  dlgNumOccur =
306  gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (sxed->endCountSpin));
307  dlgNumRem =
308  gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (sxed->endRemainSpin));
309  sxNumOccur = xaccSchedXactionGetNumOccur (sxed->sx);
310  sxNumRem = xaccSchedXactionGetRemOccur (sxed->sx);
311 
312  if (dlgNumOccur != sxNumOccur || dlgNumRem != sxNumRem)
313  return TRUE;
314 
315  return FALSE;
316 }
317 
318 static gboolean
319 gnc_sxed_check_creation_changed (GncSxEditorDialog *sxed)
320 {
321  gboolean sxAutoCreate, sxNotify;
322  gint dlgAdvance = 0;
323  gint dlgRemind = 0;
324 
325  gboolean dlgEnabled =
326  gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (sxed->enabledOpt));
327  gboolean dlgAutoCreate =
328  gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (sxed->autocreateOpt));
329  gboolean dlgNotify =
330  gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (sxed->notifyOpt));
331 
332  if (dlgEnabled != xaccSchedXactionGetEnabled (sxed->sx))
333  return TRUE;
334 
335  xaccSchedXactionGetAutoCreate (sxed->sx, &sxAutoCreate, &sxNotify);
336  if (dlgAutoCreate != sxAutoCreate || dlgNotify != sxNotify)
337  return TRUE;
338 
339  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (sxed->advanceOpt)))
340  dlgAdvance = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (sxed->advanceSpin));
341  if (dlgAdvance != xaccSchedXactionGetAdvanceCreation (sxed->sx))
342  return TRUE;
343 
344  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (sxed->remindOpt)))
345  dlgRemind = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (sxed->remindSpin));
346  if (dlgRemind != xaccSchedXactionGetAdvanceReminder (sxed->sx))
347  return TRUE;
348 
349  return FALSE;
350 }
351 
352 static gboolean
353 gnc_sxed_check_dates_changed (GncSxEditorDialog *sxed)
354 {
355  GList *dialog_schedule = NULL;
356  GDate dialog_start_date, sx_start_date;
357  gchar *dialog_schedule_str, *sx_schedule_str;
358  gboolean schedules_are_the_same, start_dates_are_the_same;
359 
360  g_date_clear (&dialog_start_date, 1);
361  gnc_frequency_save_to_recurrence (sxed->gncfreq, &dialog_schedule,
362  &dialog_start_date);
363  dialog_schedule_str = recurrenceListToString (dialog_schedule);
364  recurrenceListFree (&dialog_schedule);
365 
366  sx_start_date = *xaccSchedXactionGetStartDate (sxed->sx);
367  sx_schedule_str = recurrenceListToString (gnc_sx_get_schedule (sxed->sx));
368 
369  DEBUG ("dialog schedule [%s], sx schedule [%s]",
370  dialog_schedule_str, sx_schedule_str);
371 
372  schedules_are_the_same = (strcmp (dialog_schedule_str,
373  sx_schedule_str) == 0);
374  g_free (dialog_schedule_str);
375  g_free (sx_schedule_str);
376 
377  start_dates_are_the_same = (g_date_compare (&dialog_start_date,
378  &sx_start_date) == 0);
379 
380  if (schedules_are_the_same && start_dates_are_the_same)
381  return FALSE;
382  return TRUE;
383 }
384 
385 /*************************************************************************
386  * Checks to see if the SX has been modified from it's previously-saved
387  * state.
388  * @return TRUE if this is a 'new' SX, or if the SX has changed from it's
389  * previous configuration.
390  ************************************************************************/
391 static gboolean
392 gnc_sxed_check_changed (GncSxEditorDialog *sxed)
393 {
394  SplitRegister *sr = NULL;
395  if (sxed->newsxP)
396  return TRUE;
397 
398  /* name */
399  if (gnc_sxed_check_name_changed (sxed))
400  return TRUE;
401  /* end options */
402  /* dialog says... no end */
403  if (gtk_toggle_button_get_active (sxed->optEndNone) &&
404  (xaccSchedXactionHasEndDate (sxed->sx) ||
405  xaccSchedXactionHasOccurDef (sxed->sx)))
406  return TRUE;
407 
408  /* dialog says... end date */
409  if (gtk_toggle_button_get_active (sxed->optEndDate) &&
410  gnc_sxed_check_end_date_changed (sxed))
411  return TRUE;
412 
413  /* dialog says... num occur */
414  if (gtk_toggle_button_get_active (sxed->optEndCount) &&
415  gnc_sxed_check_num_occurs_changed (sxed))
416  return TRUE;
417  /* SX options [autocreate, notify, reminder, advance] */
418  if (gnc_sxed_check_creation_changed (sxed))
419  return TRUE;
420  /* Dates and Schedules */
421  if (gnc_sxed_check_dates_changed (sxed))
422  return TRUE;
423 
424  /* template transactions */
425  sr = gnc_ledger_display_get_split_register (sxed->ledger);
427  return TRUE;
428 
429  return FALSE;
430 }
431 
432 
433 /*****************************************************************************
434  * Holds the credit- and debit-sum for a given Transaction, as used in
435  * gnc_sxed_check_consistent.
436  ****************************************************************************/
437 typedef struct _txnCreditDebitSums
438 {
439  gnc_numeric creditSum;
440  gnc_numeric debitSum;
442 
443 static txnCreditDebitSums *
444 tcds_new (void)
445 {
446  txnCreditDebitSums *tcds = g_new0 (txnCreditDebitSums, 1);
447  tcds->creditSum = tcds->debitSum = gnc_numeric_zero ();
448  return tcds;
449 }
450 
451 static void
452 set_sums_to_zero (gpointer key,
453  gpointer val,
454  gpointer ud)
455 {
457  tcds->creditSum = gnc_numeric_zero ();
458  tcds->debitSum = gnc_numeric_zero ();
459 }
460 
461 inline static gnc_numeric
462 tcds_difference (txnCreditDebitSums *tcds)
463 {
464  return gnc_numeric_sub_fixed (tcds->debitSum, tcds->creditSum);
465 }
466 
467 static void
468 check_credit_debit_balance (gpointer key, gpointer val, gpointer ud)
469 {
471  gboolean *unbalanced = (gboolean*)ud;
472  gnc_numeric diff = tcds_difference (tcds);
473  const char *result = gnc_numeric_zero_p (diff) ? "true" : "false";
474  *unbalanced |= !(gnc_numeric_zero_p (diff));
475 
476  DEBUG ("%p | %s [%s - %s = %s]", key, result,
477  gnc_num_dbg_to_string (tcds->debitSum),
478  gnc_num_dbg_to_string (tcds->creditSum),
479  gnc_num_dbg_to_string (diff));
480 }
481 
482 static gboolean
483 gnc_sxed_check_names (GncSxEditorDialog *sxed)
484 {
485  const gchar *name = gtk_entry_get_text (sxed->nameEntry);
486  if (!name || !name[0])
487  {
488  const char *sx_has_no_name_msg =
489  _("Please name the Scheduled Transaction.");
490  gnc_error_dialog (GTK_WINDOW (sxed->dialog), "%s", sx_has_no_name_msg);
491  return FALSE;
492 
493  }
494 
495  bool nameExists = FALSE;
496  gchar *nameKey = g_utf8_collate_key (name, -1);
497  bool nameHasChanged =
498  (xaccSchedXactionGetName (sxed->sx) == NULL)
499  || (strcmp (xaccSchedXactionGetName (sxed->sx), name) != 0);
500  for (GList *sxList = gnc_book_get_schedxactions (gnc_get_current_book ())->sx_list;
501  nameHasChanged && !nameExists && sxList;
502  sxList = sxList->next)
503  {
504  const char *existingName = xaccSchedXactionGetName ((SchedXaction*)sxList->data);
505  char *existingNameKey = g_utf8_collate_key (existingName, -1);
506  nameExists |= (strcmp (nameKey, existingNameKey) == 0);
507  g_free (existingNameKey);
508  }
509  g_free (nameKey);
510  if (nameHasChanged && nameExists)
511  {
512  const char *sx_has_existing_name_msg =
513  _("A Scheduled Transaction with the name \"%s\" already exists. "
514  "Are you sure you want to name this one the same?");
515  if (!gnc_verify_dialog (GTK_WINDOW (sxed->dialog), FALSE,
516  sx_has_existing_name_msg, name))
517  return FALSE;
518  }
519  return TRUE;
520 }
521 
522 static gboolean
523 gnc_sxed_check_endpoint (GncSxEditorDialog *sxed)
524 {
525  GDate startDate, endDate, nextDate;
526  GList *schedule = NULL;
527 
528  if (!gtk_toggle_button_get_active (sxed->optEndDate)
529  && !gtk_toggle_button_get_active (sxed->optEndCount)
530  && !gtk_toggle_button_get_active (sxed->optEndNone))
531  {
532  const char *sx_end_spec_msg =
533  _("Please provide a valid end selection.");
534  gnc_error_dialog (GTK_WINDOW (sxed->dialog), "%s", sx_end_spec_msg);
535  return FALSE;
536  }
537 
538  if (gtk_toggle_button_get_active (sxed->optEndCount))
539  {
540  gint occur =
541  gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (sxed->endCountSpin));
542  gint rem =
543  gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (sxed->endRemainSpin));
544 
545  if (occur == 0)
546  {
547  const char *sx_occur_count_zero_msg =
548  _("There must be some number of occurrences.");
549  gnc_error_dialog (GTK_WINDOW (sxed->dialog), "%s", sx_occur_count_zero_msg);
550  return FALSE;
551  }
552 
553  if (rem > occur)
554  {
555  const char *sx_occur_counts_wrong_msg =
556  _("The number of remaining occurrences (%d) is greater than "
557  "the number of total occurrences (%d).");
558  gnc_error_dialog (GTK_WINDOW (sxed->dialog), sx_occur_counts_wrong_msg,
559  rem, occur);
560  return FALSE;
561  }
562  return TRUE;
563  }
564 
565  g_date_clear (&endDate, 1);
566  if (gtk_toggle_button_get_active (sxed->optEndDate))
567  {
568  gnc_gdate_set_time64 (&endDate,
569  gnc_date_edit_get_date (sxed-> endDateEntry));
570  }
571 
572  g_date_clear (&nextDate, 1);
573  gnc_frequency_save_to_recurrence (sxed->gncfreq, &schedule, &startDate);
574  if (gnc_list_length_cmp (schedule, 0))
575  {
576  g_date_subtract_days (&startDate, 1);
577  recurrenceListNextInstance (schedule, &startDate, &nextDate);
578  }
579  recurrenceListFree (&schedule);
580 
581  if (!g_date_valid (&nextDate) ||
582  (g_date_valid (&endDate) && (g_date_compare (&nextDate, &endDate) > 0)))
583  {
584  const char *invalid_sx_check_msg =
585  _("You have attempted to create a Scheduled Transaction which "
586  "will never run. Do you really want to do this?");
587  if (!gnc_verify_dialog (GTK_WINDOW (sxed->dialog), FALSE,
588  "%s", invalid_sx_check_msg))
589  return FALSE;
590  }
591  return TRUE;
592 }
593 
594 static gboolean
595 gnc_sxed_check_autocreate (GncSxEditorDialog *sxed, int ttVarCount,
596  int splitCount, gboolean multi_commodity)
597 {
598  gboolean autocreateState;
599 
600  autocreateState =
601  gtk_toggle_button_get_active (
602  GTK_TOGGLE_BUTTON (sxed->autocreateOpt));
603 
604  if (((ttVarCount > 0) || multi_commodity) && autocreateState)
605  {
606  gnc_warning_dialog (GTK_WINDOW (sxed->dialog), "%s",
607  _("Scheduled Transactions with variables "
608  "or involving more than one commodity "
609  "cannot be automatically created."));
610  return FALSE;
611  }
612 
613  /* Fix for part of Bug#121740 -- auto-create transactions are
614  * only valid if there's actually a transaction to create. */
615  if (autocreateState && splitCount == 0)
616  {
617  gnc_warning_dialog (GTK_WINDOW (sxed->dialog), "%s",
618  _("Scheduled Transactions without a template "
619  "transaction cannot be automatically created."));
620  return FALSE;
621  }
622  return TRUE;
623 }
624 
625 static gboolean
626 gnc_sxed_split_check_account (GncSxEditorDialog *sxed, Split *s,
627  gnc_commodity *base_cmdty, gboolean *multi_cmdty)
628 {
629  gnc_commodity *split_cmdty = NULL;
630  gnc_numeric split_amount;
631  Account *acct = NULL;
632  GncGUID *acct_guid = NULL;
633  qof_instance_get (QOF_INSTANCE (s),
634  "sx-account", &acct_guid,
635  NULL);
636  acct = xaccAccountLookup (acct_guid, gnc_get_current_book ());
637  guid_free (acct_guid);
638  // If the split is being destroyed always return TRUE.
639  if (acct == NULL && !qof_instance_get_destroying (s))
640  return FALSE;
641  split_cmdty = xaccAccountGetCommodity (acct);
642  split_amount = xaccSplitGetAmount (s);
643  if (!gnc_numeric_zero_p (split_amount) && base_cmdty == NULL)
644  {
645  base_cmdty = split_cmdty;
646  }
647  *multi_cmdty |= (!gnc_numeric_zero_p (split_amount) &&
648  !gnc_commodity_equal (split_cmdty, base_cmdty));
649  return TRUE;
650 }
651 
652 static gboolean
653 gnc_sxed_split_calculate_formula (GncSxEditorDialog *sxed, Split *s,
654  GHashTable *vars, const char *key,
655  txnCreditDebitSums *tcds)
656 {
657  gnc_numeric tmp = gnc_numeric_zero ();
658  char *str = NULL;
659  qof_instance_get (QOF_INSTANCE (s),
660  key, &str,
661  NULL);
662  if (!str || !str[0])
663  {
664  if (str)
665  g_free (str);
666  return TRUE; /* No formula no foul */
667  }
668  if (gnc_sx_parse_vars_from_formula (str, vars, &tmp) < 0)
669  {
670  gchar *err = g_strdup_printf (_("Couldn't parse %s for split \"%s\"."),
671  key, xaccSplitGetMemo (s));
672  gnc_error_dialog (GTK_WINDOW (sxed->dialog), "%s", err);
673  g_free (err);
674  g_free (str);
675 
676  return FALSE;
677  }
678  if (g_strcmp0 (key, "sx-credit-formula") == 0)
679  tcds->creditSum = gnc_numeric_add (tcds->creditSum, tmp, 100,
681  else
682  tcds->debitSum = gnc_numeric_add (tcds->debitSum, tmp, 100,
684  g_free (str);
685  return TRUE;
686 }
687 
688 typedef struct
689 {
690  GncSxEditorDialog *sxed;
691  GHashTable *txns;
692  GHashTable *vars;
693  txnCreditDebitSums *tcds;
694  gboolean multi_commodity;
695  gboolean err;
697 
698 static void
699 split_error_warning_dialog (GtkWidget *parent, const gchar *title,
700  gchar *message)
701 {
702  GtkWidget *dialog = gtk_message_dialog_new (GTK_WINDOW (parent), 0,
703  GTK_MESSAGE_ERROR,
704  GTK_BUTTONS_CLOSE,
705  "%s", title);
706  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
707  "%s", message);
708  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (parent));
709  g_signal_connect_swapped (dialog, "response",
710  G_CALLBACK (gtk_widget_destroy), dialog);
711  gtk_dialog_run (GTK_DIALOG (dialog));
712 
713 }
714 
715 static gboolean
716 check_transaction_splits (Transaction *txn, gpointer data)
717 {
718  GList *splitList = xaccTransGetSplitList (txn);
720 
721  for (; splitList; splitList = splitList->next)
722  {
723  gnc_commodity *base_cmdty = NULL;
724  Split *s = (Split*)splitList->data;
725 
726  if (sd->tcds == NULL)
727  {
728  sd->tcds = tcds_new ();
729  g_hash_table_insert (sd->txns, (gpointer)txn, (gpointer)(sd->tcds));
730  }
731 
732  if (!gnc_sxed_split_check_account (sd->sxed, s, base_cmdty,
733  &sd->multi_commodity))
734  {
735  gchar *message = g_strdup_printf
736  (_("Split with memo %s has an invalid account."),
737  xaccSplitGetMemo (s));
738  split_error_warning_dialog (sd->sxed->dialog,
739  _("Invalid Account in Split"),
740  message);
741  g_free (message);
742  sd->err = TRUE;
743  return FALSE;
744  }
745 
746  if (!gnc_sxed_split_calculate_formula (sd->sxed, s, sd->vars,
747  "sx-credit-formula",
748  sd->tcds))
749  {
750  gchar *message = g_strdup_printf
751  (_("Split with memo %s has an unparsable Credit Formula."),
752  xaccSplitGetMemo (s));
753  split_error_warning_dialog (sd->sxed->dialog,
754  _("Unparsable Formula in Split"),
755  message);
756  g_free (message);
757  sd->err = TRUE;
758  return FALSE;
759  }
760 
761  if (!gnc_sxed_split_calculate_formula (sd->sxed, s, sd->vars,
762  "sx-debit-formula",
763  sd->tcds))
764 
765  {
766  gchar *message = g_strdup_printf
767  (_("Split with memo %s has an unparsable Debit Formula."),
768  xaccSplitGetMemo (s));
769  split_error_warning_dialog (sd->sxed->dialog,
770  _("Unparsable Formula in Split"),
771  message);
772  g_free (message);
773  sd->err = TRUE;
774  return FALSE;
775  }
776  }
777  return TRUE;
778 }
779 
780 /*******************************************************************************
781  * Checks to make sure that the SX is in a reasonable state to save.
782  * @return true if checks out okay, false otherwise.
783  ******************************************************************************/
784 static gboolean
785 gnc_sxed_check_consistent (GncSxEditorDialog *sxed)
786 {
787 
788  /* Do checks on validity and such, interrupting the user if
789  * things aren't right.
790  *
791  * Features...
792  * X support formulas [?!]
793  * X balancing the SX if contain numeric-only formula data.
794  * X agreement with create-automagically/notification controls
795  * X the 'will ever be valid' check should take num-occur vals into
796  * account.
797  * X SX name is unique
798  * X SX has a name
799  * X "weekly" FS has some days set.
800  * X "once" with reasonable start/end dates.
801  * X This doesn't work at the time the 'weekly' one was fixed with
802  * user-confirmation, below; the once SX is always valid.
803  * [X more generically, creating a "not scheduled" SX is probably not
804  * right... ]
805  */
806 
807  gint ttVarCount = 0, splitCount = 0;
808  static const int NUM_ITERS_WITH_VARS = 5;
809  static const int NUM_ITERS_NO_VARS = 1;
810  int numIters = NUM_ITERS_NO_VARS, i;
811  gboolean unbalanceable = FALSE;
812  gpointer unusedKey, unusedValue;
813 
814  GHashTable *vars = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
815  (GDestroyNotify)gnc_sx_variable_free);
816  GHashTable *txns = g_hash_table_new_full (g_direct_hash, g_direct_equal,
817  NULL, g_free);
818  CheckTxnSplitData sd = {sxed, txns, vars, NULL, FALSE, FALSE};
819 
829  gnc_sxed_reg_check_close (sxed);
830  /* numeric-formulas-get-balanced determination */
831  gnc_sx_get_variables (sxed->sx, vars);
832 
833  ttVarCount = g_hash_table_size (vars);
834  if (ttVarCount != 0)
835  {
836  /* balance with random variable bindings some number of times in an
837  * attempt to ferret out un-balanceable transactions.
838  */
839  numIters = NUM_ITERS_WITH_VARS;
840  }
841 
842  for (i = 0; i < numIters && !unbalanceable; i++)
843  {
844  GList *splitList = xaccSchedXactionGetSplits (sxed->sx);
845  Account *tmpl_acct = gnc_sx_get_template_transaction_account (sxed->sx);
846  gnc_sx_randomize_variables (vars);
847  g_hash_table_foreach (txns, set_sums_to_zero, NULL);
848 
849  splitCount += g_list_length (splitList);
850  g_list_free (splitList);
851 
852  xaccAccountForEachTransaction (tmpl_acct, check_transaction_splits, &sd);
853 
854  if (sd.err)
855  {
856  g_hash_table_destroy (vars);
857  g_hash_table_destroy (txns);
858  return FALSE;
859  }
860 
861  g_hash_table_foreach (txns, check_credit_debit_balance, &unbalanceable);
862  }
863 
864  /* Subtract out pre-defined vars */
865  if (g_hash_table_lookup_extended (vars, "i", &unusedKey, &unusedValue))
866  ttVarCount -= 1;
867 
868  g_hash_table_destroy (vars);
869  g_hash_table_destroy (txns);
870 
871  if (unbalanceable)
872  {
873  const char *msg =
874  _("The Scheduled Transaction Editor cannot automatically "
875  "balance this transaction. Should it still be entered?");
876  if (!gnc_verify_dialog (GTK_WINDOW (sxed->dialog), FALSE, "%s", msg))
877  return FALSE;
878  }
879 
880  if (!gnc_sxed_check_names (sxed))
881  return FALSE;
882 
883  if (!gnc_sxed_check_autocreate (sxed, ttVarCount,
884  splitCount, sd.multi_commodity))
885  return FALSE;
886 
887  if (!gnc_sxed_check_endpoint (sxed))
888  return FALSE;
889  return TRUE;
890 }
891 
892 
893 /******************************************************************************
894  * Saves the contents of the SX. This assumes that gnc_sxed_check_consistent
895  * has returned true.
896  *****************************************************************************/
897 static void
898 gnc_sxed_save_sx (GncSxEditorDialog *sxed)
899 {
900  gnc_sx_begin_edit (sxed->sx);
901 
902  /* name */
903  const gchar *name = gtk_entry_get_text (sxed->nameEntry);
904  if (name && *name)
905  xaccSchedXactionSetName (sxed->sx, name);
906 
907  /* date */
908  {
909  GDate gdate;
910 
911  if (gtk_toggle_button_get_active (sxed->optEndDate))
912  {
913  /* get the end date data */
914  gnc_gdate_set_time64(&gdate,
915  gnc_date_edit_get_date (
916  sxed->endDateEntry));
917  xaccSchedXactionSetEndDate (sxed->sx, &gdate);
918  /* set the num occurrences data */
919  xaccSchedXactionSetNumOccur (sxed->sx, 0);
920  }
921  else if (gtk_toggle_button_get_active (sxed->optEndCount))
922  {
923  gint num;
924 
925  /* get the occurrences data */
926  num =
927  gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (sxed->endCountSpin));
928  xaccSchedXactionSetNumOccur (sxed->sx, num);
929 
930  num =
931  gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (sxed->endRemainSpin));
932  xaccSchedXactionSetRemOccur (sxed->sx, num);
933 
934  g_date_clear (&gdate, 1);
935  xaccSchedXactionSetEndDate (sxed->sx, &gdate);
936  }
937  else if (gtk_toggle_button_get_active (sxed->optEndNone))
938  {
939  xaccSchedXactionSetNumOccur (sxed->sx, 0);
940  g_date_clear (&gdate, 1);
941  xaccSchedXactionSetEndDate (sxed->sx, &gdate);
942  }
943  else
944  {
945  g_critical ("no valid end specified\n");
946  }
947  }
948 
949  /* Enabled states */
950  {
951  gboolean enabledState;
952 
953  enabledState = gtk_toggle_button_get_active (sxed->enabledOpt);
954  xaccSchedXactionSetEnabled (sxed->sx, enabledState);
955  }
956 
957  /* Auto-create/notification states */
958  {
959  gboolean autocreateState, notifyState;
960 
961  autocreateState = gtk_toggle_button_get_active (sxed->autocreateOpt);
962  notifyState = gtk_toggle_button_get_active (sxed->notifyOpt);
963  /* "Notify" only makes sense if AutoCreate is activated;
964  * enforce that here. */
965  xaccSchedXactionSetAutoCreate (sxed->sx,
966  autocreateState,
967  (autocreateState & notifyState));
968  }
969 
970  /* days in advance */
971  {
972  int daysInAdvance;
973 
974  daysInAdvance = 0;
975  if (gtk_toggle_button_get_active (sxed->advanceOpt))
976  {
977  daysInAdvance =
978  gtk_spin_button_get_value_as_int (sxed->advanceSpin);
979  }
980  xaccSchedXactionSetAdvanceCreation (sxed->sx, daysInAdvance);
981 
982  daysInAdvance = 0;
983  if (gtk_toggle_button_get_active (sxed->remindOpt))
984  {
985  daysInAdvance =
986  gtk_spin_button_get_value_as_int (sxed->remindSpin);
987  }
988  xaccSchedXactionSetAdvanceReminder (sxed->sx, daysInAdvance);
989  }
990 
991  /* start date and freq spec */
992  {
993  GDate gdate;
994  GList *schedule = NULL;
995 
996  gnc_frequency_save_to_recurrence (sxed->gncfreq, &schedule, &gdate);
997  gnc_sx_set_schedule (sxed->sx, schedule);
998  {
999  gchar *recurrence_str = recurrenceListToCompactString (schedule);
1000  DEBUG ("recurrences parsed [%s]", recurrence_str);
1001  g_free (recurrence_str);
1002  }
1003 
1004  /* now that we have it, set the start date */
1005  xaccSchedXactionSetStartDate (sxed->sx, &gdate);
1006  }
1007 
1008  gnc_sx_commit_edit (sxed->sx);
1009 }
1010 
1011 static void
1012 update_sensitivity (GncSxEditorDialog *sxed)
1013 {
1014  gboolean enabled = gtk_toggle_button_get_active (sxed->enabledOpt);
1015  gboolean autocreate = gtk_toggle_button_get_active (sxed->autocreateOpt);
1016  gboolean advance = gtk_toggle_button_get_active (sxed->advanceOpt);
1017  gboolean remind = gtk_toggle_button_get_active (sxed->remindOpt);
1018  gboolean type_date = (sxed->end_type == END_DATE);
1019  gboolean type_occur = (sxed->end_type == END_OCCUR);
1020 
1021  gnc_suspend_gui_refresh ();
1022 
1023  gtk_widget_set_sensitive (GTK_WIDGET (sxed->autocreateOpt), enabled);
1024  gtk_widget_set_sensitive (GTK_WIDGET (sxed->notifyOpt), enabled && autocreate);
1025 
1026  gtk_widget_set_sensitive (GTK_WIDGET (sxed->advanceOpt), enabled);
1027  gtk_widget_set_sensitive (GTK_WIDGET (sxed->advanceSpin), enabled && advance);
1028 
1029  gtk_widget_set_sensitive (GTK_WIDGET (sxed->remindOpt), enabled);
1030  gtk_widget_set_sensitive (GTK_WIDGET (sxed->remindSpin), enabled && remind);
1031 
1032  gtk_widget_set_sensitive (GTK_WIDGET (sxed->optEndNone), enabled);
1033  gtk_widget_set_sensitive (GTK_WIDGET (sxed->optEndDate), enabled);
1034  gtk_widget_set_sensitive (GTK_WIDGET (sxed->optEndCount), enabled);
1035 
1036  gtk_widget_set_sensitive (GTK_WIDGET (sxed->endDateEntry), enabled && type_date);
1037  gtk_widget_set_sensitive (GTK_WIDGET (sxed->endCountSpin), enabled && type_occur);
1038  gtk_widget_set_sensitive (GTK_WIDGET (sxed->endRemainSpin), enabled && type_occur);
1039 
1040  gtk_widget_set_sensitive (gtk_notebook_get_nth_page (sxed->notebook, 1), enabled);
1041  gtk_widget_set_sensitive (gtk_notebook_get_nth_page (sxed->notebook, 2), enabled);
1042 
1043  gnc_resume_gui_refresh ();
1044 }
1045 
1046 static void
1047 enabled_toggled_cb (GtkToggleButton *o, GncSxEditorDialog *sxed)
1048 {
1049  update_sensitivity (sxed);
1050 }
1051 
1052 static void
1053 autocreate_toggled_cb (GtkToggleButton *o, GncSxEditorDialog *sxed)
1054 {
1055  update_sensitivity (sxed);
1056 }
1057 
1058 static void
1059 advance_toggled_cb (GtkButton *o, GncSxEditorDialog *sxed)
1060 {
1061  update_sensitivity (sxed);
1062 }
1063 
1064 static void
1065 remind_toggled_cb (GtkButton *o, GncSxEditorDialog *sxed)
1066 {
1067  update_sensitivity (sxed);
1068 }
1069 
1070 
1071 /* Local destruction of dialog */
1072 static void
1073 scheduledxaction_editor_dialog_destroy (GtkWidget *object, gpointer data)
1074 {
1075  GncSxEditorDialog *sxed = data;
1076 
1077  if (sxed == NULL)
1078  return;
1079 
1080  gnc_unregister_gui_component_by_data
1081  (DIALOG_SCHEDXACTION_EDITOR_CM_CLASS, sxed);
1082 
1083  gnc_embedded_window_close_page (sxed->embed_window, sxed->plugin_page);
1084  gtk_widget_destroy (GTK_WIDGET (sxed->embed_window));
1085  sxed->embed_window = NULL;
1086  sxed->plugin_page = NULL;
1087  sxed->ledger = NULL;
1088 
1089  g_free (sxed->sxGUIDstr);
1090  sxed->sxGUIDstr = NULL;
1091 
1092  if (sxed->newsxP)
1093  {
1094  /* FIXME: WTF???
1095  *
1096  * "WTF" explanation: in the "new" click from the caller, we
1097  * set this flag. When "ok" is pressed on the dialog, we set
1098  * this flag to false, and thus leave the SX live. If
1099  * "Cancel" is clicked, the flag will still be true, and this
1100  * SX will be cleaned, here. -- jsled
1101  */
1102  gnc_sx_begin_edit (sxed->sx);
1103  xaccSchedXactionDestroy (sxed->sx);
1104  }
1105  sxed->sx = NULL;
1106 
1107  g_free (sxed);
1108 }
1109 
1110 
1111 static
1112 gboolean
1113 sxed_delete_event (GtkWidget *widget, GdkEvent *event, gpointer ud)
1114 {
1115  GncSxEditorDialog *sxed = (GncSxEditorDialog*)ud;
1116 
1117  /* We've already processed the SX, likely because of "ok" being
1118  * clicked. */
1119  if (sxed->sx == NULL)
1120  {
1121  return FALSE;
1122  }
1123 
1124  if (!sxed_confirmed_cancel (sxed))
1125  {
1126  return TRUE;
1127  }
1128  return FALSE;
1129 }
1130 
1131 
1132 /*************************************
1133  * Create the Schedule Editor Dialog *
1134  ************************************/
1135 GncSxEditorDialog *
1136 gnc_ui_scheduled_xaction_editor_dialog_create (GtkWindow *parent,
1137  SchedXaction *sx, gboolean newSX)
1138 {
1139  GncSxEditorDialog *sxed;
1140  GtkBuilder *builder;
1141  GtkWidget *button;
1142  int i;
1143  int id;
1144  GList *dlgExists = NULL;
1145 
1146  static struct widgetSignalCallback
1147  {
1148  char *name;
1149  char *signal;
1150  void (*fn)();
1151  gpointer objectData;
1152  } widgets[] =
1153  {
1154  { "ok_button", "clicked", editor_ok_button_clicked_cb, NULL },
1155  { "cancel_button", "clicked", editor_cancel_button_clicked_cb, NULL },
1156  { "help_button", "clicked", editor_help_button_clicked_cb, NULL },
1157  { "rb_noend", "toggled", endgroup_rb_toggled_cb, GINT_TO_POINTER (END_NEVER_OPTION) },
1158  { "rb_enddate", "toggled", endgroup_rb_toggled_cb, GINT_TO_POINTER (END_DATE_OPTION) },
1159  { "rb_num_occur", "toggled", endgroup_rb_toggled_cb, GINT_TO_POINTER (NUM_OCCUR_OPTION) },
1160  { "remain_spin" , "value-changed", sxed_excal_update_adapt_cb, NULL },
1161  { "enabled_opt", "toggled", enabled_toggled_cb, NULL },
1162  { "autocreate_opt", "toggled", autocreate_toggled_cb, NULL },
1163  { "advance_opt", "toggled", advance_toggled_cb, NULL },
1164  { "remind_opt", "toggled", remind_toggled_cb, NULL },
1165  { NULL, NULL, NULL, NULL }
1166  };
1167 
1168  dlgExists = gnc_find_gui_components (DIALOG_SCHEDXACTION_EDITOR_CM_CLASS,
1169  editor_component_sx_equality,
1170  sx);
1171  if (dlgExists)
1172  {
1173  DEBUG ("dialog already exists; using that one.");
1174  sxed = (GncSxEditorDialog*)dlgExists->data;
1175  gtk_window_present (GTK_WINDOW (sxed->dialog));
1176  g_list_free (dlgExists);
1177  return sxed;
1178  }
1179 
1180  sxed = g_new0(GncSxEditorDialog, 1);
1181 
1182  sxed->sx = sx;
1183  sxed->newsxP = newSX;
1184 
1185  /* Load up Glade file */
1186  builder = gtk_builder_new ();
1187  gnc_builder_add_from_file (builder, "dialog-sx.glade", "advance_days_adj");
1188  gnc_builder_add_from_file (builder, "dialog-sx.glade", "remind_days_adj");
1189  gnc_builder_add_from_file (builder, "dialog-sx.glade", "end_spin_adj");
1190  gnc_builder_add_from_file (builder, "dialog-sx.glade", "remain_spin_adj");
1191  gnc_builder_add_from_file (builder, "dialog-sx.glade", "scheduled_transaction_editor_dialog");
1192 
1193  sxed->builder = builder;
1194 
1195  /* Connect the Widgets */
1196  sxed->dialog = GTK_WIDGET (gtk_builder_get_object (builder, "scheduled_transaction_editor_dialog"));
1197  sxed->notebook = GTK_NOTEBOOK (gtk_builder_get_object (builder, "editor_notebook"));
1198  sxed->nameEntry = GTK_ENTRY (gtk_builder_get_object (builder, "sxe_name"));
1199  sxed->enabledOpt = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "enabled_opt"));
1200  sxed->autocreateOpt = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "autocreate_opt"));
1201  sxed->notifyOpt = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "notify_opt"));
1202  sxed->advanceOpt = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "advance_opt"));
1203  sxed->advanceSpin = GTK_SPIN_BUTTON (gtk_builder_get_object (builder, "advance_days"));
1204  sxed->remindOpt = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "remind_opt"));
1205  sxed->remindSpin = GTK_SPIN_BUTTON (gtk_builder_get_object (builder, "remind_days"));
1206  sxed->lastOccurLabel = GTK_LABEL (gtk_builder_get_object (builder, "last_occur_label"));
1207  sxed->optEndNone = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "rb_noend"));
1208  sxed->optEndDate = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "rb_enddate"));
1209  sxed->optEndCount = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "rb_num_occur"));
1210  sxed->endCountSpin = GTK_ENTRY (gtk_builder_get_object (builder, "end_spin"));
1211  sxed->endRemainSpin = GTK_ENTRY (gtk_builder_get_object (builder, "remain_spin"));
1212 
1213  // Set the name of this dialog so it can be easily manipulated with css
1214  gtk_widget_set_name (GTK_WIDGET (sxed->dialog), "gnc-id-sx-editor");
1215  gnc_widget_style_context_add_class (GTK_WIDGET (sxed->dialog), "gnc-class-sx");
1216 
1217  gtk_window_set_transient_for (GTK_WINDOW (sxed->dialog), parent);
1218 
1219  /* Setup the end-date GNC widget */
1220  {
1221  GtkWidget *endDateBox = GTK_WIDGET (gtk_builder_get_object (builder, "editor_end_date_box"));
1222  sxed->endDateEntry = GNC_DATE_EDIT (gnc_date_edit_new (gnc_time (NULL), FALSE, FALSE));
1223  gtk_widget_show (GTK_WIDGET (sxed->endDateEntry));
1224  g_signal_connect (sxed->endDateEntry, "date-changed",
1225  G_CALLBACK (sxed_excal_update_adapt_cb), sxed);
1226  gtk_box_pack_start (GTK_BOX (endDateBox), GTK_WIDGET (sxed->endDateEntry),
1227  TRUE, TRUE, 0);
1228  }
1229 
1230  id = gnc_register_gui_component (DIALOG_SCHEDXACTION_EDITOR_CM_CLASS,
1231  NULL, /* no refresh handler */
1232  sxed_close_handler,
1233  sxed);
1234  // This ensure this dialog is closed when the session is closed.
1235  gnc_gui_component_set_session (id, gnc_get_current_session ());
1236 
1237  g_signal_connect (sxed->dialog, "delete_event",
1238  G_CALLBACK (sxed_delete_event), sxed);
1239  g_signal_connect (sxed->dialog, "destroy",
1240  G_CALLBACK (scheduledxaction_editor_dialog_destroy),
1241  sxed);
1242 
1243  for (i = 0; widgets[i].name; i++)
1244  {
1245  button = GTK_WIDGET (gtk_builder_get_object (builder, widgets[i].name));
1246  if (widgets[i].objectData)
1247  {
1248  g_object_set_data (G_OBJECT (button), "whichOneAmI",
1249  widgets[i].objectData);
1250  }
1251  g_signal_connect (button, widgets[i].signal,
1252  G_CALLBACK (widgets[i].fn), sxed);
1253  }
1254 
1255  /* Set sensitivity settings */
1256  gtk_widget_set_sensitive (GTK_WIDGET (sxed->notifyOpt), FALSE);
1257  gtk_widget_set_sensitive (GTK_WIDGET (sxed->advanceSpin), FALSE);
1258  gtk_widget_set_sensitive (GTK_WIDGET (sxed->remindSpin), FALSE);
1259  gtk_widget_set_sensitive (GTK_WIDGET (sxed->endCountSpin), FALSE);
1260  gtk_widget_set_sensitive (GTK_WIDGET (sxed->endRemainSpin), FALSE);
1261  gtk_editable_set_editable (GTK_EDITABLE (sxed->advanceSpin), TRUE);
1262  gtk_editable_set_editable (GTK_EDITABLE (sxed->remindSpin), TRUE);
1263 
1264  /* Allow resize */
1265  gtk_window_set_resizable (GTK_WINDOW (sxed->dialog), TRUE);
1266  gnc_restore_window_size (GNC_PREFS_GROUP_SXED, GTK_WINDOW (sxed->dialog), parent);
1267 
1268  /* create the frequency-selection widget and example [dense-]calendar. */
1269  schedXact_editor_create_freq_sel (sxed);
1270 
1271  /* create the template-transaction ledger window */
1272  schedXact_editor_create_ledger (sxed);
1273 
1274  /* populate */
1275  schedXact_editor_populate (sxed);
1276 
1277  /* Do not call show_all here */
1278  gtk_widget_show (sxed->dialog);
1279  gtk_notebook_set_current_page (GTK_NOTEBOOK (sxed->notebook), 0);
1280 
1281  /* Refresh the cal and the ledger */
1282  gtk_widget_queue_resize (GTK_WIDGET (sxed->example_cal));
1283 
1284  gnc_ledger_display_refresh (sxed->ledger);
1285 
1286  /* Move keyboard focus to the name entry */
1287  gtk_widget_grab_focus (GTK_WIDGET (sxed->nameEntry));
1288 
1289  gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, sxed);
1290  g_object_unref (G_OBJECT (builder));
1291 
1292  return sxed;
1293 }
1294 
1295 
1296 static void
1297 schedXact_editor_create_freq_sel (GncSxEditorDialog *sxed)
1298 {
1299  GtkBox *b;
1300  GtkWidget *example_cal_scrolled_win = NULL;
1301 
1302  b = GTK_BOX (gtk_builder_get_object (sxed->builder, "gncfreq_hbox"));
1303 
1304  sxed->gncfreq =
1305  GNC_FREQUENCY (gnc_frequency_new_from_recurrence (gnc_sx_get_schedule (sxed->sx),
1306  xaccSchedXactionGetStartDate (sxed->sx)));
1307  g_assert (sxed->gncfreq);
1308  g_signal_connect (sxed->gncfreq, "changed",
1309  G_CALLBACK (gnc_sxed_freq_changed),
1310  sxed);
1311 
1312  gtk_box_pack_start (GTK_BOX (b), GTK_WIDGET (sxed->gncfreq), TRUE, TRUE, 0);
1313 
1314  b = GTK_BOX (gtk_builder_get_object (sxed->builder, "example_cal_hbox"));
1315 
1316  example_cal_scrolled_win = gtk_scrolled_window_new (NULL, NULL);
1317  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (example_cal_scrolled_win),
1318  GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1319  gtk_box_pack_start (GTK_BOX (b), example_cal_scrolled_win, TRUE, TRUE, 0);
1320 
1321  sxed->dense_cal_model = gnc_dense_cal_store_new (EX_CAL_NUM_MONTHS * 31);
1322  sxed->example_cal = GNC_DENSE_CAL(gnc_dense_cal_new_with_model (GTK_WINDOW(sxed->dialog),
1323  GNC_DENSE_CAL_MODEL(sxed->dense_cal_model)));
1324  g_assert (sxed->example_cal);
1325  gnc_dense_cal_set_num_months (sxed->example_cal, EX_CAL_NUM_MONTHS);
1326  gnc_dense_cal_set_months_per_col (sxed->example_cal, EX_CAL_MO_PER_COL);
1327  gtk_container_add (GTK_CONTAINER (example_cal_scrolled_win), GTK_WIDGET (sxed->example_cal));
1328 
1329 
1330  gtk_widget_show_all (example_cal_scrolled_win);
1331 }
1332 
1333 
1334 static void
1335 schedXact_editor_create_ledger (GncSxEditorDialog *sxed)
1336 {
1337  SplitRegister *splitreg;
1338  GtkWidget *main_vbox;
1339 
1340  /* Create the ledger */
1341  sxed->sxGUIDstr = guid_to_string (xaccSchedXactionGetGUID (sxed->sx));
1342  sxed->ledger = gnc_ledger_display_template_gl (sxed->sxGUIDstr);
1343  splitreg = gnc_ledger_display_get_split_register (sxed->ledger);
1344 
1345  /* First the embedded window */
1346  main_vbox = GTK_WIDGET (gtk_builder_get_object (sxed->builder, "register_vbox"));
1347  sxed->embed_window =
1348  gnc_embedded_window_new ("embedded-win",
1349  gnc_sxed_menu_entries,
1350  gnc_sxed_menu_n_entries,
1351  "gnc-embedded-register-window.ui",
1352  sxed->dialog,
1353  FALSE, /* no accelerators */
1354  sxed);
1355  gtk_box_pack_start (GTK_BOX (main_vbox), GTK_WIDGET (sxed->embed_window),
1356  TRUE, TRUE, 0);
1357 
1358  /* Now create the register plugin page. */
1359  sxed->plugin_page = gnc_plugin_page_register_new_ledger (sxed->ledger);
1360 
1361  gnc_plugin_page_merge_actions (sxed->plugin_page);
1362 
1363  gtk_widget_insert_action_group (GTK_WIDGET(sxed->embed_window),
1365  G_ACTION_GROUP(gnc_plugin_page_get_action_group (sxed->plugin_page)));
1366 
1367  gnc_plugin_page_register_set_options (sxed->plugin_page,
1368  NUM_LEDGER_LINES_DEFAULT, FALSE);
1369  gnc_embedded_window_open_page (sxed->embed_window, sxed->plugin_page);
1370 
1371  /* configure... */
1372  /* use double-line, so scheduled transaction Notes can be edited */
1373  gnc_split_register_config (splitreg,
1374  splitreg->type, splitreg->style,
1375  TRUE);
1376  gnc_split_register_set_auto_complete (splitreg, FALSE);
1377 
1378  /* don't show present/future divider [by definition, not necessary] */
1379  gnc_split_register_show_present_divider (splitreg, FALSE);
1380 }
1381 
1382 
1383 static void
1384 schedXact_editor_populate (GncSxEditorDialog *sxed)
1385 {
1386  char *name;
1387  time64 tmpDate;
1388  SplitRegister *splitReg;
1389  const GDate *gd;
1390  gint daysInAdvance;
1391  gboolean enabledState, autoCreateState, notifyState;
1392 
1393  name = xaccSchedXactionGetName (sxed->sx);
1394  if (name)
1395  {
1396  gtk_entry_set_text (sxed->nameEntry, name);
1397  }
1398  {
1399  gd = xaccSchedXactionGetLastOccurDate (sxed->sx);
1400  if (g_date_valid (gd))
1401  {
1402  gchar dateBuf [MAX_DATE_LENGTH+1];
1403  qof_print_gdate (dateBuf, MAX_DATE_LENGTH, gd);
1404  gtk_label_set_text (sxed->lastOccurLabel, dateBuf);
1405  }
1406  else
1407  {
1408  gtk_label_set_text (sxed->lastOccurLabel, _("(never)"));
1409  }
1410  gd = NULL;
1411  }
1412 
1413  gd = xaccSchedXactionGetEndDate (sxed->sx);
1414  if (g_date_valid (gd))
1415  {
1416  gtk_toggle_button_set_active (sxed->optEndDate, TRUE);
1417  tmpDate = gnc_time64_get_day_start_gdate (gd);
1418  gnc_date_edit_set_time (sxed->endDateEntry, tmpDate);
1419 
1420  set_endgroup_toggle_states (sxed, END_DATE);
1421  }
1422  else if (xaccSchedXactionHasOccurDef (sxed->sx))
1423  {
1424  gint numOccur = xaccSchedXactionGetNumOccur (sxed->sx);
1425  gint numRemain = xaccSchedXactionGetRemOccur (sxed->sx);
1426 
1427  gtk_toggle_button_set_active (sxed->optEndCount, TRUE);
1428 
1429  gtk_spin_button_set_value (GTK_SPIN_BUTTON (sxed->endCountSpin), numOccur);
1430  gtk_spin_button_set_value (GTK_SPIN_BUTTON (sxed->endRemainSpin), numRemain);
1431 
1432  set_endgroup_toggle_states (sxed, END_OCCUR);
1433  }
1434  else
1435  {
1436  gtk_toggle_button_set_active (sxed->optEndNone, TRUE);
1437  set_endgroup_toggle_states (sxed, END_NEVER);
1438  }
1439 
1440  enabledState = xaccSchedXactionGetEnabled (sxed->sx);
1441  gtk_toggle_button_set_active (sxed->enabledOpt, enabledState);
1442 
1443  /* Do auto-create/notify setup */
1444  if (sxed->newsxP)
1445  {
1446  autoCreateState =
1447  gnc_prefs_get_bool (GNC_PREFS_GROUP_SXED, GNC_PREF_CREATE_AUTO);
1448  notifyState =
1449  gnc_prefs_get_bool (GNC_PREFS_GROUP_SXED, GNC_PREF_NOTIFY);
1450  }
1451  else
1452  {
1453  xaccSchedXactionGetAutoCreate (sxed->sx,
1454  &autoCreateState,
1455  &notifyState);
1456  }
1457  gtk_toggle_button_set_active (sxed->autocreateOpt, autoCreateState);
1458  if (!autoCreateState)
1459  {
1460  notifyState = FALSE;
1461  }
1462  gtk_toggle_button_set_active (sxed->notifyOpt, notifyState);
1463 
1464  /* Do days-in-advance-to-create widget[s] setup. */
1465  if (sxed->newsxP)
1466  {
1467  daysInAdvance =
1468  gnc_prefs_get_float (GNC_PREFS_GROUP_SXED, GNC_PREF_CREATE_DAYS);
1469  }
1470  else
1471  {
1472  daysInAdvance =
1473  xaccSchedXactionGetAdvanceCreation (sxed->sx);
1474  }
1475  if (daysInAdvance != 0)
1476  {
1477  gtk_toggle_button_set_active (sxed->advanceOpt, TRUE);
1478  gtk_spin_button_set_value (sxed->advanceSpin,
1479  (gfloat)daysInAdvance);
1480  }
1481 
1482  /* Do days-in-advance-to-remind widget[s] setup. */
1483  if (sxed->newsxP)
1484  {
1485  daysInAdvance =
1486  gnc_prefs_get_float (GNC_PREFS_GROUP_SXED, GNC_PREF_REMIND_DAYS);
1487  }
1488  else
1489  {
1490  daysInAdvance =
1491  xaccSchedXactionGetAdvanceReminder (sxed->sx);
1492  }
1493  if (daysInAdvance != 0)
1494  {
1495  gtk_toggle_button_set_active (sxed->remindOpt, TRUE);
1496  gtk_spin_button_set_value (sxed->remindSpin,
1497  (gfloat)daysInAdvance);
1498  }
1499 
1500  if (sxed->newsxP)
1501  {
1502  gnc_sx_set_instance_count (sxed->sx, 1);
1503  }
1504 
1505  /* populate the ledger */
1506  {
1507  /* create the split list */
1508  GList *splitList = xaccSchedXactionGetSplits (sxed->sx);
1509  if (splitList)
1510  {
1511  splitReg = gnc_ledger_display_get_split_register (sxed->ledger);
1512  gnc_split_register_load (splitReg, splitList, NULL, NULL);
1513  } /* otherwise, use the existing stuff. */
1514  g_list_free (splitList);
1515  }
1516 
1517  /* Update the example cal */
1518  gnc_sxed_update_cal (sxed);
1519 }
1520 
1521 
1522 static void
1523 set_endgroup_toggle_states (GncSxEditorDialog *sxed, EndType type)
1524 {
1525  sxed->end_type = type;
1526  update_sensitivity (sxed);
1527 }
1528 
1529 
1530 static void
1531 endgroup_rb_toggled_cb (GtkButton *b, gpointer d)
1532 {
1533  /* figure out which one */
1534  GncSxEditorDialog *sxed;
1535  gint id;
1536 
1537  sxed = (GncSxEditorDialog*)d;
1538  id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (b), "whichOneAmI"));
1539 
1540  switch (id)
1541  {
1542  case END_NEVER_OPTION:
1543  set_endgroup_toggle_states (sxed, END_NEVER);
1544  break;
1545  case END_DATE_OPTION:
1546  set_endgroup_toggle_states (sxed, END_DATE);
1547  break;
1548  case NUM_OCCUR_OPTION:
1549  set_endgroup_toggle_states (sxed, END_OCCUR);
1550  break;
1551  default:
1552  g_critical ("Unknown id %d", id);
1553  break;
1554  }
1555  gnc_sxed_update_cal (sxed);
1556 }
1557 
1558 
1559 /********************************************************************\
1560  * gnc_register_check_close *
1561  * *
1562  * Args: regData - the data struct for this register *
1563  * Return: none *
1564 \********************************************************************/
1565 static void
1566 gnc_sxed_reg_check_close (GncSxEditorDialog *sxed)
1567 {
1568  gboolean pending_changes;
1569  SplitRegister *reg;
1570  const char *message =
1571  _("The current template transaction "
1572  "has been changed. "
1573  "Would you like to record the changes?");
1574 
1575  reg = gnc_ledger_display_get_split_register (sxed->ledger);
1576  pending_changes = gnc_split_register_changed (reg);
1577  if (!pending_changes)
1578  {
1579  return;
1580  }
1581 
1582  if (gnc_verify_dialog (GTK_WINDOW (sxed->dialog), TRUE, "%s", message))
1583  {
1584  if (!gnc_split_register_save (reg, TRUE))
1585  return;
1586 
1588  }
1589  else
1590  {
1592  }
1593 }
1594 
1595 
1596 static gboolean
1597 editor_component_sx_equality (gpointer find_data,
1598  gpointer user_data)
1599 {
1600  return ((SchedXaction*)find_data
1601  == ((GncSxEditorDialog*)user_data)->sx);
1602 }
1603 
1604 static void
1605 gnc_sxed_update_cal (GncSxEditorDialog *sxed)
1606 {
1607  GList *recurrences = NULL;
1608  GDate start_date, first_date;
1609 
1610  g_date_clear (&start_date, 1);
1611 
1612  gnc_frequency_save_to_recurrence (sxed->gncfreq, &recurrences, &start_date);
1613  recurrenceListNextInstance (recurrences, &start_date, &first_date);
1614 
1615  /* Deal with the fact that this SX may have been run before [the
1616  * calendar should only show upcoming instances]... */
1617  {
1618  const GDate *last_sx_inst;
1619 
1620  last_sx_inst = xaccSchedXactionGetLastOccurDate (sxed->sx);
1621  if (g_date_valid (last_sx_inst)
1622  && g_date_valid (&first_date)
1623  && g_date_compare (last_sx_inst, &first_date) > 0)
1624  {
1625  /* last occurrence will be passed as initial date to update store
1626  * later on as well, but only if it's past first_date */
1627  start_date = *last_sx_inst;
1628  recurrenceListNextInstance (recurrences, &start_date, &first_date);
1629  }
1630  else
1631  /* move one day back so the store can get the proper first recurrence. */
1632  g_date_subtract_days (&start_date, 1);
1633 
1634  }
1635 
1636  if (!g_date_valid (&first_date))
1637  {
1638  /* Nothing to do. */
1639  gnc_dense_cal_store_clear (sxed->dense_cal_model);
1640  goto cleanup;
1641  }
1642 
1643  gnc_dense_cal_store_update_name (sxed->dense_cal_model, xaccSchedXactionGetName (sxed->sx));
1644  {
1645  gchar *schedule_desc = recurrenceListToCompactString (recurrences);
1646  gnc_dense_cal_store_update_info (sxed->dense_cal_model, schedule_desc);
1647  g_free (schedule_desc);
1648  }
1649 
1650  //gnc_dense_cal_set_month (sxed->example_cal, g_date_get_month (&first_date));
1651  //gnc_dense_cal_set_year (sxed->example_cal, g_date_get_year (&first_date));
1652 
1653  /* figure out the end restriction */
1654  if (gtk_toggle_button_get_active (sxed->optEndDate))
1655  {
1656  GDate end_date;
1657  g_date_clear (&end_date, 1);
1658  gnc_gdate_set_time64 (&end_date, gnc_date_edit_get_date (sxed->endDateEntry));
1659  gnc_dense_cal_store_update_recurrences_date_end (sxed->dense_cal_model, &start_date, recurrences, &end_date);
1660  }
1661  else if (gtk_toggle_button_get_active (sxed->optEndNone))
1662  {
1663  gnc_dense_cal_store_update_recurrences_no_end (sxed->dense_cal_model, &start_date, recurrences);
1664  }
1665  else if (gtk_toggle_button_get_active (sxed->optEndCount))
1666  {
1667  gint num_remain
1668  = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (sxed->endRemainSpin));
1669  gnc_dense_cal_store_update_recurrences_count_end (sxed->dense_cal_model, &start_date, recurrences, num_remain);
1670  }
1671  else
1672  {
1673  g_error ("unknown end condition");
1674  }
1675 
1676  cleanup:
1677  recurrenceListFree (&recurrences);
1678 }
1679 
1680 
1681 static void
1682 gnc_sxed_freq_changed (GncFrequency *gf, gpointer ud)
1683 {
1684  gnc_sxed_update_cal ((GncSxEditorDialog*)ud);
1685 }
1686 
1687 
1688 static void
1689 sxed_excal_update_adapt_cb (GtkWidget *o, gpointer ud)
1690 {
1691  gnc_sxed_update_cal ((GncSxEditorDialog*)ud);
1692 }
1693 
1694 
1695 void
1696 on_sx_check_toggled_cb (GtkWidget *togglebutton, gpointer user_data)
1697 {
1698  GtkWidget *widget_auto;
1699  GtkWidget *widget_notify;
1700  GHashTable *table;
1701 
1702  PINFO ("Togglebutton is %p and user_data is %p", togglebutton, user_data);
1703  PINFO ("Togglebutton builder name is %s", gtk_buildable_get_name (GTK_BUILDABLE (togglebutton)));
1704 
1705  /* We need to use the hash table to find the required widget to activate. */
1706  table = g_object_get_data (G_OBJECT (user_data), "prefs_widget_hash");
1707 
1708  /* "Auto-create" enables "notify before creation" setting */
1709  widget_auto = g_hash_table_lookup (table, "pref/" GNC_PREFS_GROUP_SXED "/" GNC_PREF_CREATE_AUTO);
1710  widget_notify = g_hash_table_lookup (table, "pref/" GNC_PREFS_GROUP_SXED "/" GNC_PREF_NOTIFY);
1711 
1712  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget_auto)))
1713  gtk_widget_set_sensitive (widget_notify, TRUE);
1714  else
1715  gtk_widget_set_sensitive (widget_notify, FALSE);
1716 
1717  /* "Run when opened" enables "show notification window" setting */
1718  widget_auto = g_hash_table_lookup (table, "pref/" GNC_PREFS_GROUP_STARTUP "/" GNC_PREF_RUN_AT_FOPEN);
1719  widget_notify = g_hash_table_lookup (table, "pref/" GNC_PREFS_GROUP_STARTUP "/" GNC_PREF_SHOW_AT_FOPEN);
1720 
1721  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget_auto)))
1722  gtk_widget_set_sensitive (widget_notify, TRUE);
1723  else
1724  gtk_widget_set_sensitive (widget_notify, FALSE);
1725 }
1726 
1727 
1728 /* ------------------------------------------------------------ */
1729 /* sx app engine; move to somewhere appropriate. :/ */
1730 
1731 typedef struct _acct_deletion_handler_data
1732 {
1733  GList *affected_sxes;
1734  GtkWidget *dialog;
1735  GtkWindow *parent;
1737 
1738 
1739 static void
1740 _open_editors (GtkDialog *dialog, gint response_code, gpointer data)
1741 {
1743  gtk_widget_hide (adhd->dialog);
1744  {
1745  GList *sx_iter;
1746  for (sx_iter = adhd->affected_sxes; sx_iter; sx_iter = sx_iter->next)
1747  {
1748  gnc_ui_scheduled_xaction_editor_dialog_create (GTK_WINDOW (adhd->parent),
1749  (SchedXaction*)sx_iter->data, FALSE);
1750  }
1751  }
1752  g_list_free (adhd->affected_sxes);
1753  gtk_widget_destroy (GTK_WIDGET (adhd->dialog));
1754  g_free (adhd);
1755 }
1756 
1757 
1758 static void
1759 _sx_engine_event_handler (QofInstance *ent, QofEventId event_type, gpointer user_data, gpointer evt_data)
1760 {
1761  Account *acct;
1762  QofBook *book;
1763  GList *affected_sxes;
1764 
1765  if (!(event_type & QOF_EVENT_DESTROY))
1766  return;
1767  if (!GNC_IS_ACCOUNT (ent))
1768  return;
1769  acct = GNC_ACCOUNT (ent);
1770  book = qof_instance_get_book (QOF_INSTANCE (acct));
1771  affected_sxes = gnc_sx_get_sxes_referencing_account (book, acct);
1772 
1773  if (!gnc_list_length_cmp (affected_sxes, 0))
1774  return;
1775 
1776  {
1777  GList *sx_iter;
1779  GtkBuilder *builder;
1780  GtkWidget *dialog;
1781  GtkWindow *parent;
1782  GtkListStore *name_list;
1783  GtkTreeView *list;
1784  GtkTreeViewColumn *name_column;
1785  GtkCellRenderer *renderer;
1786 
1787  builder = gtk_builder_new ();
1788  gnc_builder_add_from_file (builder, "dialog-sx.glade", "account_deletion_dialog");
1789 
1790  dialog = GTK_WIDGET (gtk_builder_get_object (builder, "account_deletion_dialog"));
1791  parent = gnc_ui_get_main_window (NULL);
1792 
1793  gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
1794 
1795  list = GTK_TREE_VIEW (gtk_builder_get_object (builder, "sx_list"));
1796 
1797  // Set grid lines option to preference
1798  gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (list), gnc_tree_view_get_grid_lines_pref ());
1799 
1801  data->dialog = dialog;
1802  data->parent = parent;
1803  data->affected_sxes = affected_sxes;
1804  name_list = gtk_list_store_new (1, G_TYPE_STRING);
1805  for (sx_iter = affected_sxes; sx_iter; sx_iter = sx_iter->next)
1806  {
1807  SchedXaction *sx;
1808  GtkTreeIter iter;
1809  gchar *sx_name;
1810 
1811  sx = (SchedXaction*)sx_iter->data;
1812  sx_name = xaccSchedXactionGetName (sx);
1813  gtk_list_store_append (name_list, &iter);
1814  gtk_list_store_set (name_list, &iter, 0, sx_name, -1);
1815  }
1816  gtk_tree_view_set_model (list, GTK_TREE_MODEL (name_list));
1817  g_object_unref (G_OBJECT (name_list));
1818 
1819  renderer = gtk_cell_renderer_text_new ();
1820  name_column = gtk_tree_view_column_new_with_attributes (_("Name"),
1821  renderer,
1822  "text", 0, NULL);
1823  gtk_tree_view_append_column (list, name_column);
1824 
1825  g_signal_connect (G_OBJECT (dialog), "response",
1826  G_CALLBACK (_open_editors), data);
1827 
1828  gtk_widget_show_all (GTK_WIDGET (dialog));
1829  gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, data);
1830  g_object_unref (G_OBJECT (builder));
1831  }
1832 }
1833 
1834 
1835 void
1836 gnc_ui_sx_initialize (void)
1837 {
1838  _sx_engine_event_handler_id = qof_event_register_handler (_sx_engine_event_handler, NULL);
1839 
1840  gnc_hook_add_dangler (HOOK_BOOK_OPENED,
1841  (GFunc)gnc_sx_sxsincelast_book_opened, NULL, NULL);
1842 
1843  /* Add page to preferences page for Scheduled Transactions */
1844  /* The parameters are; glade file, items to add from glade file - last being the dialog, preference tab name */
1845  gnc_preferences_add_page ("dialog-sx.glade",
1846  "create_days_adj,remind_days_adj,sx_prefs",
1847  _("Scheduled Transactions"));
1848 }
const GDate * xaccSchedXactionGetEndDate(const SchedXaction *sx)
Returns invalid date when there is no end-date specified.
gint xaccAccountForEachTransaction(const Account *acc, TransactionCallback proc, void *data)
The xaccAccountForEachTransaction() routine will traverse all of the transactions in account and call...
Definition: Account.cpp:5100
Public declarations for GncLedgerDisplay class.
void gnc_sx_set_schedule(SchedXaction *sx, GList *schedule)
void gnc_ledger_display_refresh(GNCLedgerDisplay *ld)
redisplay/redraw only the indicated window.
void gnc_sx_set_instance_count(SchedXaction *sx, gint instance_num)
Sets the instance count to something other than the default.
GList * gnc_sx_get_schedule(const SchedXaction *sx)
void gnc_preferences_add_page(const gchar *filename, const gchar *widgetname, const gchar *tabname)
This function adds a full page of preferences to the preferences dialog.
gchar * gnc_num_dbg_to_string(gnc_numeric n)
Convert to string.
void qof_instance_get(const QofInstance *inst, const gchar *first_prop,...)
Wrapper for g_object_get.
The instance data structure for a content plugin.
Date and Time handling routines.
gboolean gnc_split_register_save(SplitRegister *reg, gboolean do_commit)
Copy the contents of the current cursor to a split.
QofBook * qof_instance_get_book(gconstpointer inst)
Return the book pointer.
GtkWindow * gnc_ui_get_main_window(GtkWidget *widget)
Get a pointer to the final GncMainWindow widget is rooted in.
utility functions for the GnuCash UI
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
void xaccSchedXactionSetNumOccur(SchedXaction *sx, gint new_num)
Set to &#39;0&#39; to turn off number-of-occurrences definition.
void gnc_plugin_page_merge_actions(GncPluginPage *page)
Add the actions for a content page to the specified window.
void gnc_embedded_window_close_page(GncEmbeddedWindow *window, GncPluginPage *page)
Remove a data plugin page from a window.
STRUCTS.
size_t qof_print_gdate(char *buf, size_t bufflen, const GDate *gd)
Convenience; calls through to qof_print_date_dmy_buff().
Definition: gnc-date.cpp:597
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
gboolean qof_instance_get_destroying(gconstpointer ptr)
Retrieve the flag that indicates whether or not this object is about to be destroyed.
gboolean gnc_commodity_equal(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equal.
void gnc_embedded_window_open_page(GncEmbeddedWindow *window, GncPluginPage *page)
Display a data plugin page in a window.
GSimpleActionGroup * gnc_plugin_page_get_action_group(GncPluginPage *page)
Retrieve the GSimpleActionGroup object associated with this page.
gnc_numeric gnc_numeric_add(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Return a+b.
gboolean gnc_numeric_zero_p(gnc_numeric a)
Returns 1 if the given gnc_numeric is 0 (zero), else returns 0.
GncEmbeddedWindow * gnc_embedded_window_new(const gchar *action_group_name, GActionEntry *action_entries, gint n_action_entries, const gchar *ui_filename, GtkWidget *enclosing_win, gboolean add_accelerators, gpointer user_data)
Create a new gnc embedded window plugin.
void gnc_split_register_load(SplitRegister *reg, GList *slist, GList *pre_filter_slist, Account *default_account)
Populates the rows of a register.
GList * gnc_sx_get_sxes_referencing_account(QofBook *book, Account *acct)
Definition: SX-book.cpp:365
GncPluginPage * gnc_plugin_page_register_new_ledger(GNCLedgerDisplay *ledger)
Create a new "register" plugin page, given a pointer to an already created ledger.
gint qof_event_register_handler(QofEventHandler handler, gpointer user_data)
Register a handler for events.
Definition: qofevent.cpp:73
void gnc_split_register_redraw(SplitRegister *reg)
Causes a redraw of the register window associated with reg.
Functions providing a register page for the GnuCash UI.
Account handling public routines.
Find the least common multiple of the arguments&#39; denominators and use that as the denominator of the ...
Definition: gnc-numeric.h:200
gint QofEventId
Define the type of events allowed.
Definition: qofevent.h:45
void gnc_gnome_help(GtkWindow *parent, const char *file_name, const char *anchor)
Launch the systems default help browser, gnome&#39;s yelp for linux, and open to a given link within a gi...
gboolean gnc_split_register_changed(SplitRegister *reg)
Returns TRUE if the register has changed cells.
Anchor Scheduled Transaction info in a book.
Functions for adding plugins to a GnuCash window.
void gnc_split_register_config(SplitRegister *reg, SplitRegisterType newtype, SplitRegisterStyle newstyle, gboolean use_double_line)
Sets a split register&#39;s type, style or line use.
const gchar * gnc_plugin_page_get_simple_action_group_name(GncPluginPage *page)
Retrieve the simple action group name associated with this plugin page.
Public declarations of GnucashRegister class.
#define MAX_DATE_LENGTH
The maximum length of a string created by the date printers.
Definition: gnc-date.h:108
Dialog for handling user preferences.
#define xaccSchedXactionGetGUID(X)
Definition: SchedXaction.h:319
All type declarations for the whole Gnucash engine.
GLib helper routines.
Generic api to store and retrieve preferences.
void gnc_split_register_cancel_cursor_trans_changes(SplitRegister *reg)
Cancels any changes made to the current pending transaction, reloads the table from the engine...
GNCLedgerDisplay * gnc_ledger_display_template_gl(char *id)
Displays a template ledger.
void xaccSchedXactionSetName(SchedXaction *sx, const gchar *newName)
A copy of the name is made.
void gnc_gdate_set_time64(GDate *gd, time64 time)
Set a GDate to a time64.
Definition: gnc-date.cpp:1244
gchar * guid_to_string(const GncGUID *guid)
The guid_to_string() routine returns a null-terminated string encoding of the id. ...
Definition: guid.cpp:164
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Get the account&#39;s commodity.
Definition: Account.cpp:3371
void gnc_split_register_set_auto_complete(SplitRegister *reg, gboolean do_auto_complete)
Sets whether a register uses auto-completion.
Functions that are supported by all types of windows.
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Get a boolean value from the preferences backend.
time64 gnc_time(time64 *tbuf)
get the current time
Definition: gnc-date.cpp:261
const char * xaccSplitGetMemo(const Split *split)
Returns the memo string.
Definition: gmock-Split.cpp:99
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition: gnc-date.h:87
SplitRegister * gnc_ledger_display_get_split_register(GNCLedgerDisplay *ld)
return the split register associated with a ledger display
void gnc_split_register_show_present_divider(SplitRegister *reg, gboolean show_present)
If TRUE, visually indicate the demarcation between splits with post dates prior to the present...
gboolean xaccSchedXactionHasOccurDef(const SchedXaction *sx)
Returns true if the scheduled transaction has a defined number of occurrences, false if not...
Scheduled Transactions public handling routines.
gint gnc_list_length_cmp(const GList *list, size_t len)
Scans the GList elements the minimum number of iterations required to test it against a specified siz...
void xaccSchedXactionSetEndDate(SchedXaction *sx, const GDate *newEnd)
Set to an invalid GDate to turn off &#39;end-date&#39; definition.
#define GNC_DENOM_AUTO
Values that can be passed as the &#39;denom&#39; argument.
Definition: gnc-numeric.h:245
The type used to store guids in C.
Definition: guid.h:75
time64 gnc_time64_get_day_start_gdate(const GDate *date)
The gnc_time64_get_day_start() routine will take the given time in GLib GDate format and adjust it to...
Definition: gnc-date.cpp:1418
Implementations.
void xaccSchedXactionDestroy(SchedXaction *sx)
Cleans up and frees a SchedXaction and its associated data.
void gnc_plugin_page_register_set_options(GncPluginPage *plugin_page, gint lines_default, gboolean read_only)
Set various register options on a newly created "register" plugin page.
SplitList * xaccTransGetSplitList(const Transaction *trans)
The xaccTransGetSplitList() method returns a GList of the splits in a transaction.
gdouble gnc_prefs_get_float(const gchar *group, const gchar *pref_name)
Get an float value from the preferences backend.
gnc_numeric xaccSplitGetAmount(const Split *split)
Returns the amount of the split in the account&#39;s commodity.
Definition: gmock-Split.cpp:69
Account * xaccAccountLookup(const GncGUID *guid, QofBook *book)
The xaccAccountLookup() subroutine will return the account associated with the given id...
Definition: Account.cpp:2052