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