GnuCash  5.6-150-g038405b370+
dialog-sx-from-trans.cpp
1 /********************************************************************
2  * dialog-sx-from-trans.c -- a simple dialog for creating a *
3  * scheduled transaction from a real one *
4  * Copyright (C) 2001 Robert Merkel <rgmerk@mira.net> *
5  * Copyright (C) 2001 Joshua Sled <jsled@asynchronous.org> *
6  * Copyright (c) 2006 David Hampton <hampton@employees.org> *
7  * Copyright (c) 2011 Robert Fewell *
8  * *
9  * This program is free software; you can redistribute it and/or *
10  * modify it under the terms of the GNU General Public License as *
11  * published by the Free Software Foundation; either version 2 of *
12  * the License, or (at your option) any later version. *
13  * *
14  * This program is distributed in the hope that it will be useful, *
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
17  * GNU General Public License for more details. *
18  * *
19  * You should have received a copy of the GNU General Public License*
20  * along with this program; if not, contact: *
21  * *
22  * Free Software Foundation Voice: +1-617-542-5942 *
23  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
24  * Boston, MA 02110-1301, USA gnu@gnu.org *
25  ********************************************************************/
26 
27 #include <config.h>
28 
29 #include "dialog-sx-editor.h"
30 #include "dialog-sx-from-trans.h"
31 #include "dialog-utils.h"
32 #include "gnc-component-manager.h"
33 #include "gnc-date.h"
34 #include "gnc-date-edit.h"
35 #include "gnc-dense-cal-store.h"
36 #include "gnc-dense-cal.h"
37 #include "gnc-engine.h"
38 #include "engine-helpers.h"
39 #include "gnc-prefs.h"
40 #include "gnc-ui-util.h"
41 #include "gnc-ui.h"
42 #include "qof.h"
43 #include "Recurrence.h"
44 #include "SchedXaction.h"
45 #include "SchedXaction.hpp"
46 #include "SX-book.h"
47 #include "SX-ttinfo.hpp"
48 #include <glib/gi18n.h>
49 #include <gtk/gtk.h>
50 #include <stdlib.h>
51 
52 #define SXFTD_ERRNO_UNBALANCED_XACTION 3
53 #define SXFTD_ERRNO_OPEN_XACTION -3
54 
55 #define SXFTD_EXCAL_NUM_MONTHS 4
56 #define SXFTD_EXCAL_MONTHS_PER_COL 4
57 
58 #define SXFTD_RESPONSE_ADVANCED 100 /* 'Advanced' button response code */
59 
60 #undef G_LOG_DOMAIN
61 #define G_LOG_DOMAIN GNC_MOD_GUI_SX
62 
63 static QofLogModule log_module = GNC_MOD_GUI_SX;
64 
65 static void sxftd_freq_combo_changed( GtkWidget *w, gpointer user_data );
66 static void gnc_sx_trans_window_response_cb(GtkDialog *dialog, gint response, gpointer data);
67 
68 static void sxftd_destroy( GtkWidget *w, gpointer user_data );
69 
70 typedef enum { FREQ_DAILY = 0, /* I know the =0 is redundant, but I'm using
71  * the numeric equivalences explicitly here */
72  FREQ_WEEKLY,
73  FREQ_BIWEEKLY,
74  FREQ_MONTHLY,
75  FREQ_QUARTERLY,
76  FREQ_ANNUALLY
77  } SxftiFreqType;
78 
79 typedef struct
80 {
81  GtkBuilder *builder;
82  GtkWidget *dialog;
83  GtkEntry *name;
84  GtkComboBox *freq_combo;
85 
86  GtkToggleButton *ne_but;
87  GtkToggleButton *ed_but;
88  GtkToggleButton *oc_but;
89  GtkEntry *n_occurences;
90 
91  Transaction *trans;
92  SchedXaction *sx;
93 
94  GncDenseCalStore *dense_cal_model;
95  GncDenseCal *example_cal;
96 
97  GNCDateEdit *startDateGDE, *endDateGDE;
98 
100 
101 typedef struct
102 {
103  gdcs_end_type type;
104  GDate end_date;
105  guint n_occurrences;
106 } getEndTuple;
107 
108 static void sxftd_update_example_cal( SXFromTransInfo *sxfti );
109 static void sxftd_update_excal_adapt( GObject *o, gpointer ud );
110 
112 {
113  const gchar *name;
114  const gchar *signal;
115  void (*handlerFn)(GObject*,gpointer);
116 };
117 
118 static void sxftd_ok_clicked(SXFromTransInfo *sxfti);
119 static void sxftd_advanced_clicked(SXFromTransInfo *sxfti);
120 
121 static void
122 sxfti_attach_callbacks(SXFromTransInfo *sxfti)
123 {
124 
125  widgetSignalHandlerTuple callbacks[] =
126  {
127  /* Whenever any of the controls change, we want to update the
128  * calendar. */
129  { "never_end_button", "clicked", sxftd_update_excal_adapt },
130  { "end_on_date_button", "clicked", sxftd_update_excal_adapt },
131  { "n_occurrences_button", "clicked", sxftd_update_excal_adapt },
132  { "n_occurrences_entry", "changed", sxftd_update_excal_adapt },
133  { NULL, NULL, NULL }
134  };
135 
136  int i;
137 
138  GtkWidget *w;
139  for (i = 0; callbacks[i].name != NULL; i++)
140  {
141  w = GTK_WIDGET(gtk_builder_get_object(sxfti->builder, callbacks[i].name));
142 
143  g_signal_connect (G_OBJECT(w), callbacks[i].signal,
144  G_CALLBACK(callbacks[i].handlerFn),
145  sxfti );
146  }
147 
148  g_signal_connect (G_OBJECT(sxfti->dialog), "response",
149  G_CALLBACK (gnc_sx_trans_window_response_cb),
150  sxfti);
151 }
152 
153 
154 static getEndTuple
155 sxftd_get_end_info(SXFromTransInfo *sxfti)
156 {
157  getEndTuple retval;
158 
159  retval.type = BAD_END;
160  g_date_clear( &(retval.end_date), 1 );
161  retval.n_occurrences = 0;
162 
163  if (gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(sxfti->ne_but)))
164  {
165  retval.type = NEVER_END;
166  return retval;
167  }
168 
169  if (gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(sxfti->ed_but)))
170  {
171  time64 end_tt;
172  retval.type = END_ON_DATE;
173  g_date_clear( &(retval.end_date), 1 );
174  end_tt = gnc_date_edit_get_date(sxfti->endDateGDE);
175  gnc_gdate_set_time64( &(retval.end_date), end_tt);
176  return retval;
177  }
178 
179  if (gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(sxfti->oc_but) ))
180  {
181  guint n_occs;
182 
183  const gchar *text = gtk_entry_get_text (GTK_ENTRY(sxfti->n_occurences));
184  if (!text || !text[0])
185  {
186  n_occs = 0;
187  }
188  else
189  {
190  gchar *endptr;
191  n_occs = strtoul(text, &endptr, 10);
192  if ( !endptr )
193  {
194  n_occs = -1;
195  }
196  }
197 
198  retval.type = END_AFTER_N_OCCS;
199  retval.n_occurrences = n_occs;
200  return retval;
201  }
202  return retval;
203 }
204 
205 
206 static guint
207 sxftd_add_template_trans(SXFromTransInfo *sxfti)
208 {
209 
210  Transaction *tr = sxfti->trans;
211  GList *splits = NULL;
212  TTInfoPtr tti = std::make_shared<TTInfo>();
213  TTSplitInfoPtr ttsi;
214  gnc_numeric runningBalance;
215  gnc_numeric split_value;
216  const char *tmpStr;
217 
218  runningBalance = gnc_numeric_zero();
219 
220  tti->set_description(xaccTransGetDescription(tr));
221  tti->set_num(gnc_get_num_action(tr, NULL));
222  tti->set_notes (xaccTransGetNotes (tr));
223  tti->set_currency(xaccTransGetCurrency(tr));
224 
225  tti->clear_template_splits ();
226 
227  for (splits = xaccTransGetSplitList(tr); splits; splits = splits->next)
228  {
229  auto sp = GNC_SPLIT(splits->data);
230  ttsi = std::make_shared<TTSplitInfo>();
231  ttsi->set_action (gnc_get_num_action(NULL, sp));
232  split_value = xaccSplitGetValue(sp);
233  ttsi->set_memo (xaccSplitGetMemo(sp));
234 
235  runningBalance = gnc_numeric_add( runningBalance, split_value,
237 
238  if (gnc_numeric_positive_p(split_value))
239  {
240  tmpStr = xaccPrintAmount( split_value,
241  gnc_default_print_info(FALSE) );
242  ttsi->set_debit_formula (tmpStr);
243  }
244  else
245  {
246  /* Negate the numeric so it prints w/o the sign at the front. */
247  tmpStr = xaccPrintAmount( gnc_numeric_neg( split_value ),
248  gnc_default_print_info(FALSE) );
249  ttsi->set_credit_formula (tmpStr);
250  }
251 
252  /* Copy over per-split account info */
253  ttsi->set_account (xaccSplitGetAccount (sp));
254 
255  tti->append_template_split (ttsi);
256  }
257 
258  if ( ! gnc_numeric_zero_p( runningBalance )
259  && !gnc_verify_dialog (GTK_WINDOW (sxfti->dialog),
260  FALSE, "%s",
261  _("The Scheduled Transaction Editor "
262  "cannot automatically balance "
263  "this transaction. "
264  "Should it still be "
265  "entered?") ) )
266  {
267  return SXFTD_ERRNO_UNBALANCED_XACTION;
268  }
269 
270  gnc_suspend_gui_refresh ();
271  xaccSchedXactionSetTemplateTrans (sxfti->sx, { tti }, gnc_get_current_book ());
272  gnc_resume_gui_refresh ();
273 
274  return 0;
275 }
276 
277 
278 static void
279 sxftd_update_schedule( SXFromTransInfo *sxfti, GDate *date, GList **recurrences)
280 {
281  gint index;
282 
283  /* Note that we make the start date the *NEXT* instance, not the
284  * present one. */
285 
286  index = gtk_combo_box_get_active(GTK_COMBO_BOX(sxfti->freq_combo));
287 
288  switch (index)
289  {
290  case FREQ_DAILY:
291  {
292  Recurrence *r = g_new0(Recurrence, 1);
293  recurrenceSet(r, 1, PERIOD_DAY, date, WEEKEND_ADJ_NONE);
294  *recurrences = g_list_append(*recurrences, r);
295  }
296  break;
297 
298  case FREQ_WEEKLY:
299  case FREQ_BIWEEKLY:
300  {
301  Recurrence *r = g_new0(Recurrence, 1);
302  int mult = (index == FREQ_BIWEEKLY ? 2 : 1);
303  recurrenceSet(r, mult, PERIOD_WEEK, date, WEEKEND_ADJ_NONE);
304  *recurrences = g_list_append(*recurrences, r);
305  }
306  break;
307 
308  case FREQ_MONTHLY:
309  case FREQ_QUARTERLY:
310  case FREQ_ANNUALLY:
311  {
312  Recurrence *r = g_new0(Recurrence, 1);
313  int mult = (index == FREQ_MONTHLY
314  ? 1
315  : (index == FREQ_QUARTERLY
316  ? 3
317  : 12));
318  recurrenceSet(r, mult, PERIOD_MONTH, date, recurrenceGetWeekendAdjust(r));
319  *recurrences = g_list_append(*recurrences, r);
320  }
321  break;
322 
323  default:
324  g_critical("nonexistent frequency selected");
325  break;
326  }
327 }
328 
329 
330 static gint
331 sxftd_init( SXFromTransInfo *sxfti )
332 {
333  GtkWidget *w;
334  const char *transName;
335  gint pos;
336  GList *schedule = NULL;
337  time64 start_tt;
338  GDate date, nextDate;
339 
340  if ( ! sxfti->sx )
341  {
342  return -1;
343  }
344  if ( ! sxfti->trans )
345  {
346  return -2;
347  }
348  if ( xaccTransIsOpen( sxfti->trans ) )
349  {
350  return SXFTD_ERRNO_OPEN_XACTION;
351  }
352 
353  /* Setup Widgets */
354  {
355  sxfti->ne_but = GTK_TOGGLE_BUTTON(gtk_builder_get_object(sxfti->builder, "never_end_button"));
356  sxfti->ed_but = GTK_TOGGLE_BUTTON(gtk_builder_get_object(sxfti->builder, "end_on_date_button"));
357  sxfti->oc_but = GTK_TOGGLE_BUTTON(gtk_builder_get_object(sxfti->builder, "n_occurrences_button"));
358  sxfti->n_occurences = GTK_ENTRY(gtk_builder_get_object(sxfti->builder, "n_occurrences_entry"));
359  }
360 
361  /* Get the name from the transaction, try that as the initial SX name. */
362  transName = xaccTransGetDescription( sxfti->trans );
363  xaccSchedXactionSetName( sxfti->sx, transName );
364 
365  sxfti->name = GTK_ENTRY(gtk_builder_get_object(sxfti->builder, "name_entry" ));
366  pos = 0;
367  gtk_editable_insert_text( GTK_EDITABLE(sxfti->name), transName,
368  (strlen(transName) * sizeof(char)), &pos );
369 
370  sxfti_attach_callbacks(sxfti);
371 
372  /* Setup the example calendar and related data structures. */
373  {
374  int num_marks = SXFTD_EXCAL_NUM_MONTHS * 31;
375 
376  w = GTK_WIDGET(gtk_builder_get_object(sxfti->builder, "ex_cal_frame" ));
377  sxfti->dense_cal_model = gnc_dense_cal_store_new(num_marks);
378  sxfti->example_cal = GNC_DENSE_CAL(gnc_dense_cal_new_with_model (GTK_WINDOW(sxfti->dialog),
379  GNC_DENSE_CAL_MODEL(sxfti->dense_cal_model)));
380  g_object_ref_sink(sxfti->example_cal);
381 
382  g_assert(sxfti->example_cal);
383  gnc_dense_cal_set_num_months( sxfti->example_cal, SXFTD_EXCAL_NUM_MONTHS );
384  gnc_dense_cal_set_months_per_col( sxfti->example_cal, SXFTD_EXCAL_MONTHS_PER_COL );
385  gtk_container_add( GTK_CONTAINER(w), GTK_WIDGET(sxfti->example_cal) );
386  }
387 
388  /* Setup the start and end dates as GNCDateEdits */
389  {
390  GtkWidget *paramTable = GTK_WIDGET(gtk_builder_get_object(sxfti->builder, "param_table" ));
391  sxfti->startDateGDE = GNC_DATE_EDIT( gnc_date_edit_new (gnc_time (NULL), FALSE, FALSE));
392 
393  gtk_grid_attach (GTK_GRID(paramTable), GTK_WIDGET(sxfti->startDateGDE), 1, 2, 1, 1);
394  gtk_widget_set_halign (GTK_WIDGET(sxfti->startDateGDE), GTK_ALIGN_FILL);
395  gtk_widget_set_valign (GTK_WIDGET(sxfti->startDateGDE), GTK_ALIGN_FILL);
396  gtk_widget_set_hexpand (GTK_WIDGET(sxfti->startDateGDE), TRUE);
397  gtk_widget_set_vexpand (GTK_WIDGET(sxfti->startDateGDE), FALSE);
398  g_object_set (GTK_WIDGET(sxfti->startDateGDE), "margin", 0, NULL);
399 
400  g_signal_connect( sxfti->startDateGDE, "date-changed",
401  G_CALLBACK( sxftd_update_excal_adapt ),
402  sxfti );
403  }
404  {
405  GtkWidget *endDateBox = GTK_WIDGET(gtk_builder_get_object(sxfti->builder, "end_date_hbox" ));
406  sxfti->endDateGDE =
407  GNC_DATE_EDIT( gnc_date_edit_new (gnc_time (NULL),
408  FALSE, FALSE));
409  gtk_box_pack_start( GTK_BOX( endDateBox ),
410  GTK_WIDGET( sxfti->endDateGDE ),
411  TRUE, TRUE, 0 );
412  g_signal_connect( sxfti->endDateGDE, "date-changed",
413  G_CALLBACK( sxftd_update_excal_adapt ),
414  sxfti );
415  }
416 
417  /* Setup the initial start date for user display/confirmation */
418  /* compute good initial date. */
419  start_tt = xaccTransGetDate( sxfti->trans );
420  gnc_gdate_set_time64( &date, start_tt );
421  sxfti->freq_combo = GTK_COMBO_BOX(gtk_builder_get_object(sxfti->builder, "freq_combo_box"));
422  gtk_combo_box_set_active(GTK_COMBO_BOX(sxfti->freq_combo), 0);
423  g_signal_connect( sxfti->freq_combo, "changed",
424  G_CALLBACK(sxftd_freq_combo_changed),
425  sxfti );
426  sxftd_update_schedule( sxfti, &date, &schedule);
427  recurrenceListNextInstance(schedule, &date, &nextDate);
428  recurrenceListFree(&schedule);
429  start_tt = gnc_time64_get_day_start_gdate (&nextDate);
430  gnc_date_edit_set_time( sxfti->startDateGDE, start_tt );
431 
432  g_signal_connect( G_OBJECT(sxfti->name), "destroy",
433  G_CALLBACK(sxftd_destroy),
434  sxfti );
435 
436  sxftd_update_example_cal( sxfti );
437 
438  return 0;
439 }
440 
441 
442 static guint
443 sxftd_compute_sx(SXFromTransInfo *sxfti)
444 {
445  GDate date;
446  GList *schedule = NULL;
447  getEndTuple end_info;
448  guint sxftd_errno = 0; /* 0 == OK, > 0 means dialog needs to be run again */
449 
450  SchedXaction *sx = sxfti->sx;
451 
452  /* get the name */
453  xaccSchedXactionSetName(sx, gtk_entry_get_text (sxfti->name));
454 
455  gnc_gdate_set_time64( &date, gnc_date_edit_get_date( sxfti->startDateGDE ) );
456 
457  sxftd_update_schedule(sxfti, &date, &schedule);
458  if (sxftd_errno == 0)
459  {
460  gnc_sx_set_schedule(sx, schedule);
461  xaccSchedXactionSetStartDate( sx, &date );
462  }
463 
464  end_info = sxftd_get_end_info(sxfti);
465 
466  switch (end_info.type)
467  {
468  case NEVER_END:
469  break;
470 
471  case END_ON_DATE:
472  xaccSchedXactionSetEndDate(sx, &(end_info.end_date));
473  break;
474 
475  case END_AFTER_N_OCCS:
476  xaccSchedXactionSetNumOccur(sx, end_info.n_occurrences);
477  break;
478 
479  default:
480  sxftd_errno = 2;
481  break;
482  }
483 
484  gnc_sx_set_instance_count( sx, 1 );
485 
486  /* Set the autocreate, days-in-advance and remind-in-advance values from
487  * options. */
488  {
489  gboolean autoCreateState, notifyState;
490  gint daysInAdvance;
491 
492  autoCreateState =
493  gnc_prefs_get_bool (GNC_PREFS_GROUP_SXED, GNC_PREF_CREATE_AUTO);
494  notifyState =
495  gnc_prefs_get_bool (GNC_PREFS_GROUP_SXED, GNC_PREF_NOTIFY);
496  xaccSchedXactionSetAutoCreate( sx,
497  autoCreateState,
498  (autoCreateState & notifyState) );
499 
500  daysInAdvance =
501  gnc_prefs_get_float (GNC_PREFS_GROUP_SXED, GNC_PREF_CREATE_DAYS);
502  xaccSchedXactionSetAdvanceCreation( sx, daysInAdvance );
503 
504  daysInAdvance =
505  gnc_prefs_get_float (GNC_PREFS_GROUP_SXED, GNC_PREF_REMIND_DAYS);
506  xaccSchedXactionSetAdvanceReminder( sx, daysInAdvance );
507  }
508 
509  if ( sxftd_add_template_trans( sxfti ) != 0 )
510  {
511  sxftd_errno = SXFTD_ERRNO_UNBALANCED_XACTION;
512  }
513 
514  return sxftd_errno;
515 }
516 
517 
518 static void
519 sxftd_close(SXFromTransInfo *sxfti, gboolean delete_sx)
520 {
521  if ( sxfti->sx && delete_sx )
522  {
523  gnc_sx_begin_edit(sxfti->sx);
524  xaccSchedXactionDestroy(sxfti->sx);
525  }
526  sxfti->sx = NULL;
527 
528  gtk_widget_destroy (GTK_WIDGET (sxfti->dialog));
529 }
530 
531 
532 static void
533 sxftd_ok_clicked(SXFromTransInfo *sxfti)
534 {
535  QofBook *book;
536  SchedXactions *sxes;
537  guint sx_error = sxftd_compute_sx(sxfti);
538 
539  if (sx_error != 0
540  && sx_error != SXFTD_ERRNO_UNBALANCED_XACTION)
541  {
542  g_critical("sxftd_compute_sx after ok_clicked [%d]", sx_error);
543  }
544  else
545  {
546  if ( sx_error == SXFTD_ERRNO_UNBALANCED_XACTION )
547  {
548  gnc_error_dialog (GTK_WINDOW (sxfti->dialog), "%s",
549  _( "The Scheduled Transaction is unbalanced. "
550  "You are strongly encouraged to correct this situation." ) );
551  }
552  book = gnc_get_current_book ();
553  sxes = gnc_book_get_schedxactions(book);
554  gnc_sxes_add_sx(sxes, sxfti->sx);
555  }
556 
557  sxftd_close(sxfti, FALSE);
558  return;
559 }
560 
561 
567 static void
568 sxftd_freq_combo_changed( GtkWidget *w, gpointer user_data )
569 {
570  SXFromTransInfo *sxfti = (SXFromTransInfo*)user_data;
571  GDate date, nextDate;
572  time64 tmp_tt;
573  GList *schedule = NULL;
574 
575  tmp_tt = xaccTransGetDate( sxfti->trans );
576  gnc_gdate_set_time64 (&date, tmp_tt);
577 
578  g_date_clear(&nextDate, 1);
579  sxftd_update_schedule(sxfti, &date, &schedule);
580  recurrenceListNextInstance(schedule, &date, &nextDate);
581  tmp_tt = gnc_time64_get_day_start_gdate (&nextDate);
582  gnc_date_edit_set_time( sxfti->startDateGDE, tmp_tt );
583 
584  recurrenceListFree(&schedule);
585  sxftd_update_example_cal( sxfti );
586 }
587 
588 
589 static void
590 sxftd_advanced_clicked(SXFromTransInfo *sxfti)
591 {
592  guint sx_error = sxftd_compute_sx(sxfti);
593  GMainContext *context;
594 
595  if ( sx_error != 0 && sx_error != SXFTD_ERRNO_UNBALANCED_XACTION )
596  {
597  // unbalanced-xaction is "okay", since this is also checked for by
598  // the advanced editor.
599  g_warning("something bad happened in sxftd_compute_sx [%d]", sx_error);
600  return;
601  }
602  gtk_widget_hide( sxfti->dialog );
603  /* force a gui update. */
604  context = g_main_context_default();
605  while (g_main_context_iteration(context, FALSE));
606 
607  gnc_ui_scheduled_xaction_editor_dialog_create(gnc_ui_get_main_window (sxfti->dialog),
608  sxfti->sx, TRUE /* newSX */);
609  /* close ourself, since advanced editing entails us, and there are sync
610  * issues otherwise. */
611  sxftd_close(sxfti, FALSE);
612 }
613 
614 
615 static void
616 sxftd_destroy( GtkWidget *w, gpointer user_data )
617 {
618  SXFromTransInfo *sxfti = (SXFromTransInfo*)user_data;
619 
620  if ( sxfti->sx )
621  {
622  gnc_sx_begin_edit(sxfti->sx);
623  xaccSchedXactionDestroy(sxfti->sx);
624  sxfti->sx = NULL;
625  }
626  if (sxfti->dense_cal_model)
627  g_object_unref(G_OBJECT(sxfti->dense_cal_model));
628  if (sxfti->example_cal)
629  g_object_unref(G_OBJECT(sxfti->example_cal));
630 
631  g_free(sxfti);
632 }
633 
634 
635 static void
636 gnc_sx_trans_window_response_cb (GtkDialog *dialog,
637  gint response,
638  gpointer data)
639 {
640  SXFromTransInfo *sxfti = (SXFromTransInfo *)data;
641 
642  ENTER(" dialog %p, response %d, sx %p", dialog, response, sxfti);
643  switch (response)
644  {
645  case GTK_RESPONSE_OK:
646  DEBUG(" OK");
647  sxftd_ok_clicked(sxfti);
648  break;
649  case SXFTD_RESPONSE_ADVANCED:
650  DEBUG(" ADVANCED");
651  sxftd_advanced_clicked(sxfti);
652  break;
653  case GTK_RESPONSE_CANCEL:
654  default:
655  DEBUG(" CANCEL");
656  sxftd_close(sxfti, TRUE);
657  break;
658 
659  }
660  LEAVE(" ");
661 }
662 
663 
668 static void
669 sxftd_update_example_cal( SXFromTransInfo *sxfti )
670 {
671  time64 tmp_tt;
672  GDate date, startDate, nextDate;
673  GList *schedule = NULL;
674  getEndTuple get;
675 
676  get = sxftd_get_end_info( sxfti );
677 
678  tmp_tt = gnc_date_edit_get_date( sxfti->startDateGDE );
679  gnc_gdate_set_time64 (&date, tmp_tt);
680 
681  sxftd_update_schedule(sxfti, &date, &schedule);
682 
683  /* go one day before what's in the box so we can get the correct start
684  * date. */
685  startDate = date;
686  g_date_subtract_days(&date, 1);
687  g_date_clear(&nextDate, 1);
688  recurrenceListNextInstance(schedule, &date, &nextDate);
689 
690  gnc_dense_cal_store_update_name (sxfti->dense_cal_model,
691  gtk_entry_get_text (sxfti->name));
692 
693  {
694  gchar *schedule_desc;
695  schedule_desc = recurrenceListToCompactString(schedule);
696  gnc_dense_cal_store_update_info(sxfti->dense_cal_model, schedule_desc);
697  g_free(schedule_desc);
698  }
699 
700  /* Set End date sensitivity */
701  gtk_widget_set_sensitive( GTK_WIDGET(sxfti->endDateGDE), (get.type == END_ON_DATE) );
702  gtk_widget_set_sensitive( GTK_WIDGET(sxfti->n_occurences), (get.type == END_AFTER_N_OCCS) );
703 
704  /* Use the day preceding the start date for the store to find the correct real start date */
705  switch (get.type)
706  {
707  case NEVER_END:
708  gnc_dense_cal_store_update_recurrences_no_end(sxfti->dense_cal_model, &date, schedule);
709  break;
710  case END_ON_DATE:
711  gnc_dense_cal_store_update_recurrences_date_end(sxfti->dense_cal_model, &date, schedule, &get.end_date);
712  break;
713  case END_AFTER_N_OCCS:
714  gnc_dense_cal_store_update_recurrences_count_end(sxfti->dense_cal_model, &date, schedule, get.n_occurrences);
715  break;
716  default:
717  g_warning("unknown get.type [%d]\n", get.type);
718  break;
719  }
720 
721  gnc_dense_cal_set_month( sxfti->example_cal, g_date_get_month( &startDate ) );
722  gnc_dense_cal_set_year( sxfti->example_cal, g_date_get_year( &startDate ) );
723 
724  recurrenceListFree(&schedule);
725 }
726 
727 
728 /***********************************
729  * Callback to update the calendar *
730  **********************************/
731 static void
732 sxftd_update_excal_adapt( GObject *o, gpointer ud )
733 {
734  SXFromTransInfo *sxfti = (SXFromTransInfo*)ud;
735  sxftd_update_example_cal( sxfti );
736 }
737 
738 
739 /*********************
740  * Create the dialog *
741  ********************/
742 void
743 gnc_sx_create_from_trans( GtkWindow *parent, Transaction *trans )
744 {
745  int sx_error;
746  SXFromTransInfo *sxfti = g_new0( SXFromTransInfo, 1);
747  GtkBuilder *builder;
748  GtkWidget *dialog;
749 
750  builder = gtk_builder_new();
751 
752  gnc_builder_add_from_file (builder , "dialog-sx.glade", "freq_liststore");
753 
754  gnc_builder_add_from_file (builder , "dialog-sx.glade", "sx_from_real_trans_dialog");
755  dialog = GTK_WIDGET(gtk_builder_get_object (builder, "sx_from_real_trans_dialog"));
756 
757  // Set the name of this dialog so it can be easily manipulated with css
758  gtk_widget_set_name (GTK_WIDGET(dialog), "gnc-id-sx-from-trans");
759  gnc_widget_style_context_add_class (GTK_WIDGET(dialog), "gnc-class-sx");
760 
761  gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
762 
763  sxfti->builder = builder;
764  sxfti->dialog = dialog;
765  sxfti->trans = trans;
766 
767  sxfti->sx = xaccSchedXactionMalloc(gnc_get_current_book ());
768 
769  if ( (sx_error = sxftd_init( sxfti )) < 0 )
770  {
771  if ( sx_error == SXFTD_ERRNO_OPEN_XACTION )
772  {
773  gnc_error_dialog (NULL, "%s",
774  _( "Cannot create a Scheduled Transaction "
775  "from a Transaction currently "
776  "being edited. Please Enter the "
777  "Transaction before Scheduling." ) );
778  sxftd_destroy (NULL, sxfti);
779  return;
780  }
781  else
782  {
783  g_error("sxftd_init: %d", sx_error);
784  }
785  }
786 
787  gtk_widget_show_all(GTK_WIDGET(sxfti->dialog));
788 
789  gtk_builder_connect_signals(builder, sxfti);
790  g_object_unref(G_OBJECT(builder));
791 }
void gnc_sx_set_schedule(SchedXaction *sx, GList *schedule)
void gnc_sx_set_instance_count(SchedXaction *sx, gint instance_num)
Sets the instance count to something other than the default.
time64 xaccTransGetDate(const Transaction *trans)
Retrieve the posted date of the transaction.
Date and Time handling routines.
GtkWindow * gnc_ui_get_main_window(GtkWidget *widget)
Get a pointer to the final GncMainWindow widget is rooted in.
gboolean xaccTransIsOpen(const Transaction *trans)
The xaccTransIsOpen() method returns TRUE if the transaction is open for editing. ...
utility functions for the GnuCash UI
void xaccSchedXactionSetNumOccur(SchedXaction *sx, gint new_num)
Set to &#39;0&#39; to turn off number-of-occurrences definition.
gnc_numeric gnc_numeric_neg(gnc_numeric a)
Returns a newly created gnc_numeric that is the negative of the given gnc_numeric value...
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
const char * xaccPrintAmount(gnc_numeric val, GNCPrintAmountInfo info)
Make a string representation of a gnc_numeric.
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.
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
const char * xaccTransGetNotes(const Transaction *trans)
Gets the transaction Notes.
Find the least common multiple of the arguments&#39; denominators and use that as the denominator of the ...
Definition: gnc-numeric.h:200
Anchor Scheduled Transaction info in a book.
const char * xaccTransGetDescription(const Transaction *trans)
Gets the transaction Description.
All type declarations for the whole Gnucash engine.
gboolean gnc_numeric_positive_p(gnc_numeric a)
Returns 1 if a > 0, otherwise returns 0.
Generic api to store and retrieve preferences.
void xaccSchedXactionSetName(SchedXaction *sx, const gchar *newName)
A copy of the name is made.
gnc_numeric xaccSplitGetValue(const Split *split)
Returns the value of this split in the transaction&#39;s commodity.
Definition: gmock-Split.cpp:84
void gnc_gdate_set_time64(GDate *gd, time64 time)
Set a GDate to a time64.
Definition: gnc-date.cpp:1244
Account * xaccSplitGetAccount(const Split *split)
Returns the account of this split, which was set through xaccAccountInsertSplit().
Definition: gmock-Split.cpp:53
gnc_commodity * xaccTransGetCurrency(const Transaction *trans)
Returns the valuation commodity of this transaction.
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Get a boolean value from the preferences backend.
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
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
SchedXaction * xaccSchedXactionMalloc(QofBook *book)
Creates and initializes a scheduled transaction.
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition: gnc-date.h:87
Scheduled Transactions public handling routines.
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
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
void xaccSchedXactionDestroy(SchedXaction *sx)
Cleans up and frees a SchedXaction and its associated data.
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.