GnuCash c935c2f+
Loading...
Searching...
No Matches
gnc-frequency.c
1/********************************************************************\
2 * gnc-frequency.c -- GnuCash widget for frequency editing. *
3 * Copyright (C) 2001,2002,2007 Joshua Sled <jsled@asynchronous.org>*
4 * Copyright (C) 2003 Linas Vepstas <linas@linas.org> *
5 * Copyright (C) 2006 David Hampton <hampton@employees.org> *
6 * Copyright (C) 2011 Robert Fewell *
7 * *
8 * This program is free software; you can redistribute it and/or *
9 * modify it under the terms of the GNU General Public License as *
10 * published by the Free Software Foundation; either version 2 of *
11 * the License, or (at your option) any later version. *
12 * *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License*
19 * along with this program; if not, contact: *
20 * *
21 * Free Software Foundation Voice: +1-617-542-5942 *
22 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
23 * Boston, MA 02110-1301, USA gnu@gnu.org *
24 * *
25\********************************************************************/
26
27#include <config.h>
28
29#include <glib.h>
30#include <gtk/gtk.h>
31#include <math.h>
32#include <time.h>
33
34#include "dialog-utils.h"
35#include "gnc-component-manager.h"
36#include "gnc-engine.h"
37#include "gnc-frequency.h"
38#include "FreqSpec.h"
39#include "gnc-ui-util.h"
40
41#undef G_LOG_DOMAIN
42#define G_LOG_DOMAIN "gnc.gui.frequency"
43
44static QofLogModule log_module = GNC_MOD_GUI;
45
46#define LAST_DAY_OF_MONTH_OPTION_INDEX 31
47
50typedef enum
51{
52 GNCFREQ_CHANGED,
53 LAST_SIGNAL
54} GNCF_Signals;
55
56static guint gnc_frequency_signals[LAST_SIGNAL] = { 0 };
57
60static void gnc_frequency_destroy( GtkWidget *widget );
61
62static void freq_combo_changed( GtkComboBox *b, gpointer d );
63static void start_date_changed( GNCDateEdit *gde, gpointer d );
64static void spin_changed_helper( GtkAdjustment *adj, gpointer d );
65
66static void weekly_days_changed( GtkButton *b, gpointer d );
67
68static void monthly_sel_changed( GtkButton *b, gpointer d );
69static void semimonthly_sel_changed( GtkButton *b, gpointer d );
70
73enum
74{
75 PAGE_NONE = 0,
76 PAGE_ONCE,
77 PAGE_DAILY,
78 PAGE_WEEKLY,
79 PAGE_SEMI_MONTHLY,
80 PAGE_MONTHLY
81};
82
83static const char *CHECKBOX_NAMES[] =
84{
85 "wd_check_sun",
86 "wd_check_mon",
87 "wd_check_tue",
88 "wd_check_wed",
89 "wd_check_thu",
90 "wd_check_fri",
91 "wd_check_sat",
92 NULL
93};
94
103typedef struct _GncFrequency
104{
105 GtkBox widget;
106 GtkBox *vb;
107 GtkNotebook *nb;
108 GtkComboBox *freqComboBox;
109 GNCDateEdit *startDate;
110 GtkBuilder *builder;
112
113G_DEFINE_TYPE (GncFrequency, gnc_frequency, GTK_TYPE_BOX)
114
115static void
116gnc_frequency_class_init( GncFrequencyClass *klass )
117{
118 GObjectClass *object_class;
119 GtkWidgetClass *gtkwidget_class;
120
121 object_class = G_OBJECT_CLASS (klass);
122 gtkwidget_class = GTK_WIDGET_CLASS (klass);
123
124 gnc_frequency_signals[GNCFREQ_CHANGED] =
125 g_signal_new ("changed",
126 G_OBJECT_CLASS_TYPE (object_class),
127 G_SIGNAL_RUN_FIRST,
128 0,
129 NULL,
130 NULL,
131 g_cclosure_marshal_VOID__VOID,
132 G_TYPE_NONE,
133 0);
134
135 /* GtkWidget signals */
136 gtkwidget_class->destroy = gnc_frequency_destroy;
137}
138
139
140static void
141gnc_frequency_init(GncFrequency *gf)
142{
143 int i;
144 GtkBox* vb;
145 GtkWidget* o;
146 GtkAdjustment* adj;
147 GtkBuilder *builder;
148
149 static const struct comboBoxTuple
150 {
151 char *name;
152 void (*fn)();
153 } comboBoxes[] =
154 {
155 { "freq_combobox", freq_combo_changed },
156 { "semimonthly_first", semimonthly_sel_changed },
157 { "semimonthly_first_weekend", semimonthly_sel_changed },
158 { "semimonthly_second", semimonthly_sel_changed },
159 { "semimonthly_second_weekend", semimonthly_sel_changed },
160 { "monthly_day", monthly_sel_changed },
161 { "monthly_weekend", monthly_sel_changed },
162 { NULL, NULL }
163 };
164
165 static const struct spinvalTuple
166 {
167 char *name;
168 void (*fn)();
169 } spinVals[] =
170 {
171 { "daily_spin", spin_changed_helper },
172 { "weekly_spin", spin_changed_helper },
173 { "semimonthly_spin", spin_changed_helper },
174 { "monthly_spin", spin_changed_helper },
175 { NULL, NULL }
176 };
177
178 gtk_orientable_set_orientation (GTK_ORIENTABLE(gf), GTK_ORIENTATION_VERTICAL);
179
180 // Set the name for this widget so it can be easily manipulated with css
181 gtk_widget_set_name (GTK_WIDGET(gf), "gnc-id-frequency");
182
183 builder = gtk_builder_new();
184 gnc_builder_add_from_file (builder , "gnc-frequency.glade", "adjustment1");
185 gnc_builder_add_from_file (builder , "gnc-frequency.glade", "adjustment2");
186 gnc_builder_add_from_file (builder , "gnc-frequency.glade", "adjustment3");
187 gnc_builder_add_from_file (builder , "gnc-frequency.glade", "adjustment4");
188 gnc_builder_add_from_file (builder , "gnc-frequency.glade", "liststore1");
189 gnc_builder_add_from_file (builder , "gnc-frequency.glade", "liststore2");
190 gnc_builder_add_from_file (builder , "gnc-frequency.glade", "liststore3");
191 gnc_builder_add_from_file (builder , "gnc-frequency.glade", "liststore4");
192 gnc_builder_add_from_file (builder , "gnc-frequency.glade", "liststore5");
193 gnc_builder_add_from_file (builder , "gnc-frequency.glade", "liststore6");
194 gnc_builder_add_from_file (builder , "gnc-frequency.glade", "liststore7");
195 gnc_builder_add_from_file (builder , "gnc-frequency.glade", "gncfreq_vbox");
196
197 gf->builder = builder;
198 o = GTK_WIDGET(gtk_builder_get_object (builder, "gncfreq_nb"));
199 gf->nb = GTK_NOTEBOOK(o);
200 o = GTK_WIDGET(gtk_builder_get_object (builder, "freq_combobox"));
201 gf->freqComboBox = GTK_COMBO_BOX(o);
202 gf->startDate = GNC_DATE_EDIT(gnc_date_edit_new(time(NULL), FALSE, FALSE));
203 /* Add the new widget to the table. */
204 {
205 GtkWidget *table = GTK_WIDGET(gtk_builder_get_object (builder, "gncfreq_table"));
206 gtk_grid_attach(GTK_GRID(table), GTK_WIDGET(gf->startDate), 4, 0, 1, 1);
207 gtk_widget_set_vexpand (GTK_WIDGET(gf->startDate), FALSE);
208 gtk_widget_set_hexpand (GTK_WIDGET(gf->startDate), FALSE);
209 gtk_widget_set_valign (GTK_WIDGET(gf->startDate), GTK_ALIGN_CENTER);
210 gtk_widget_set_halign (GTK_WIDGET(gf->startDate), GTK_ALIGN_CENTER);
211 g_object_set (GTK_WIDGET(gf->startDate), "margin", 0, NULL);
212 }
213 vb = GTK_BOX(gtk_builder_get_object (builder, "gncfreq_vbox"));
214 gf->vb = vb;
215 gtk_container_add(GTK_CONTAINER(&gf->widget), GTK_WIDGET(gf->vb));
216
217 /* initialize the combo boxes */
218 for (i = 0; comboBoxes[i].name != NULL; i++)
219 {
220 o = GTK_WIDGET(gtk_builder_get_object (builder, comboBoxes[i].name));
221 gtk_combo_box_set_active(GTK_COMBO_BOX(o), 0);
222 if (comboBoxes[i].fn != NULL)
223 {
224 g_signal_connect(o, "changed", G_CALLBACK(comboBoxes[i].fn), gf);
225 }
226 }
227
228 /* initialize the spin buttons */
229 for (i = 0; spinVals[i].name != NULL; i++)
230 {
231 if (spinVals[i].fn != NULL)
232 {
233 o = GTK_WIDGET(gtk_builder_get_object (builder, spinVals[i].name));
234 adj = gtk_spin_button_get_adjustment(GTK_SPIN_BUTTON(o));
235 g_signal_connect(adj, "value_changed", G_CALLBACK(spinVals[i].fn), gf);
236 }
237 }
238
239 /* initialize the weekly::day-of-week checkbox-selection hooks */
240 for (i = 0; i < 7; i++)
241 {
242 o = GTK_WIDGET(gtk_builder_get_object (builder, CHECKBOX_NAMES[i]));
243 g_signal_connect(o, "clicked",
244 G_CALLBACK(weekly_days_changed), gf);
245 }
246
247 gtk_widget_show_all(GTK_WIDGET(&gf->widget));
248
249 /* respond to start date changes */
250 g_signal_connect(gf->startDate, "date_changed", G_CALLBACK(start_date_changed), gf);
251
252 gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, gf);
253
254}
255
256
264static void
265gnc_frequency_destroy (GtkWidget *widget)
266{
267 GncFrequency *gf;
268
269 ENTER("frequency %p", widget);
270 g_return_if_fail (widget != NULL);
271 g_return_if_fail (GNC_IS_FREQUENCY (widget));
272
273 gf = GNC_FREQUENCY (widget);
274
275 if (gf->builder)
276 {
277 DEBUG("removing builder");
278 g_object_unref(G_OBJECT(gf->builder));
279 gf->builder = NULL;
280 }
281
282 GTK_WIDGET_CLASS (gnc_frequency_parent_class)->destroy (widget);
283 LEAVE(" ");
284}
285
286
287static void
288spin_changed_helper( GtkAdjustment *adj, gpointer d )
289{
290 g_signal_emit_by_name(GNC_FREQUENCY(d), "changed");
291}
292
293
294static void
295weekly_days_changed( GtkButton *b, gpointer d )
296{
297 g_signal_emit_by_name(GNC_FREQUENCY(d), "changed");
298}
299
300
301static void
302monthly_sel_changed( GtkButton *b, gpointer d )
303{
304 g_signal_emit_by_name(GNC_FREQUENCY(d), "changed");
305}
306
307
308static void
309semimonthly_sel_changed( GtkButton *b, gpointer d )
310{
311 g_signal_emit_by_name(GNC_FREQUENCY(d), "changed");
312}
313
314
315static void
316freq_combo_changed(GtkComboBox *b, gpointer d)
317{
318 GncFrequency *gf = GNC_FREQUENCY(d);
319 int option_index;
320
321 /* Set the new page. */
322 option_index = gtk_combo_box_get_active(GTK_COMBO_BOX(gf->freqComboBox));
323 gtk_notebook_set_current_page(gf->nb, option_index);
324 g_signal_emit_by_name(gf, "changed");
325}
326
327
328static void
329start_date_changed( GNCDateEdit *gde, gpointer d )
330{
331 g_signal_emit_by_name(GNC_FREQUENCY(d), "changed");
332}
333
334
335/**************************************
336 * Relabel some of the labels *
337 *************************************/
338void
339gnc_frequency_set_frequency_label_text(GncFrequency *gf, const gchar *txt)
340{
341 GtkLabel *lbl;
342 if (!gf || !txt) return;
343 lbl = GTK_LABEL (gtk_builder_get_object (gf->builder, "freq_label"));
344 gtk_label_set_text (lbl, txt);
345}
346
347
348void
349gnc_frequency_set_date_label_text(GncFrequency *gf, const gchar *txt)
350{
351 GtkLabel *lbl;
352 if (!gf || !txt) return;
353 lbl = GTK_LABEL (gtk_builder_get_object (gf->builder, "startdate_label"));
354 gtk_label_set_text (lbl, txt);
355}
356
357
358GtkWidget*
359gnc_frequency_new_from_recurrence(GList *recurrences, const GDate *start_date)
360{
361 return gnc_frequency_new(recurrences, start_date);
362}
363
364
365GtkWidget*
366gnc_frequency_new(GList *recurrences, const GDate *start_date)
367{
368 GncFrequency *toRet;
369 toRet = g_object_new(gnc_frequency_get_type(), NULL);
370 gnc_frequency_setup_recurrence(toRet, recurrences, start_date);
371 return GTK_WIDGET(toRet);
372}
373
374
375static void
376_setup_weekly_recurrence(GncFrequency *gf, Recurrence *r)
377{
378 GDate recurrence_date;
379 GDateWeekday day_of_week;
380 guint multiplier = recurrenceGetMultiplier(r);
381 const char *checkbox_widget_name;
382 GtkWidget *weekday_checkbox;
383
384 GtkWidget *multipler_spin = GTK_WIDGET(gtk_builder_get_object (gf->builder, "weekly_spin"));
385 gtk_spin_button_set_value(GTK_SPIN_BUTTON(multipler_spin), multiplier);
386
387 recurrence_date = recurrenceGetDate(r);
388 day_of_week = g_date_get_weekday(&recurrence_date);
389 g_assert(day_of_week >= G_DATE_MONDAY && day_of_week <= G_DATE_SUNDAY);
390 // this `mod 7' is explicit knowledge of the values of (monday=1)-based
391 // GDateWeekday, vs. our (sunday=0)-based checkbox names array.
392 checkbox_widget_name = CHECKBOX_NAMES[day_of_week % 7];
393 weekday_checkbox = GTK_WIDGET(gtk_builder_get_object (gf->builder, checkbox_widget_name));
394 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(weekday_checkbox), TRUE);
395}
396
397
398static int
399_get_monthly_combobox_index(Recurrence *r)
400{
401 GDate recurrence_date = recurrenceGetDate(r);
402 int week = 0;
403 int day_of_month_index = g_date_get_day(&recurrence_date) - 1;
404 if (recurrenceGetPeriodType(r) == PERIOD_END_OF_MONTH)
405 {
406 day_of_month_index = LAST_DAY_OF_MONTH_OPTION_INDEX;
407 }
408 else if (recurrenceGetPeriodType(r) == PERIOD_LAST_WEEKDAY)
409 {
410 day_of_month_index
411 = LAST_DAY_OF_MONTH_OPTION_INDEX
412 + g_date_get_weekday(&recurrence_date);
413 }
414 else if (recurrenceGetPeriodType(r) == PERIOD_NTH_WEEKDAY)
415 {
416 week = day_of_month_index / 7 > 3 ? 3 : day_of_month_index / 7;
417 day_of_month_index = LAST_DAY_OF_MONTH_OPTION_INDEX + 7 +
418 g_date_get_weekday(&recurrence_date) + 7 * week;
419
420
421 }
422 /* else { default value } */
423 return day_of_month_index;
424}
425
426
427void
428gnc_frequency_setup_recurrence(GncFrequency *gf, GList *recurrences, const GDate *start_date)
429{
430 gnc_frequency_setup(gf, recurrences, start_date);
431}
432
433
434void
435gnc_frequency_setup(GncFrequency *gf, GList *recurrences, const GDate *start_date)
436{
437 gboolean made_changes = FALSE;
438
439 // setup start-date, if present
440 if (start_date != NULL
441 && g_date_valid(start_date))
442 {
443 gnc_date_edit_set_gdate(gf->startDate, start_date);
444 made_changes = TRUE;
445 }
446
447 if (recurrences == NULL)
448 {
449 goto maybe_signal;
450 // return...
451 }
452
453 if (g_list_length(recurrences) > 1)
454 {
455 if (recurrenceListIsWeeklyMultiple(recurrences))
456 {
457 for (; recurrences != NULL; recurrences = recurrences->next)
458 {
459 _setup_weekly_recurrence(gf, (Recurrence*)recurrences->data);
460 }
461
462 gtk_notebook_set_current_page(gf->nb, PAGE_WEEKLY);
463 gtk_combo_box_set_active(gf->freqComboBox, PAGE_WEEKLY);
464 }
465 else if (recurrenceListIsSemiMonthly(recurrences))
466 {
467 Recurrence *first, *second;
468 GtkWidget *multiplier_spin;
469 GtkWidget *dom_combobox;
470
471 first = (Recurrence*)g_list_nth_data(recurrences, 0);
472 second = (Recurrence*)g_list_nth_data(recurrences, 1);
473
474 multiplier_spin = GTK_WIDGET(gtk_builder_get_object (gf->builder, "semimonthly_spin"));
475 gtk_spin_button_set_value(GTK_SPIN_BUTTON(multiplier_spin), recurrenceGetMultiplier(first));
476 dom_combobox = GTK_WIDGET(gtk_builder_get_object (gf->builder, "semimonthly_first"));
477 gtk_combo_box_set_active(GTK_COMBO_BOX(dom_combobox), _get_monthly_combobox_index(first));
478 dom_combobox = GTK_WIDGET(gtk_builder_get_object (gf->builder, "semimonthly_first_weekend"));
479 gtk_combo_box_set_active(GTK_COMBO_BOX(dom_combobox), recurrenceGetWeekendAdjust(first));
480 dom_combobox = GTK_WIDGET(gtk_builder_get_object (gf->builder, "semimonthly_second"));
481 gtk_combo_box_set_active(GTK_COMBO_BOX(dom_combobox), _get_monthly_combobox_index(second));
482 dom_combobox = GTK_WIDGET(gtk_builder_get_object (gf->builder, "semimonthly_second_weekend"));
483 gtk_combo_box_set_active(GTK_COMBO_BOX(dom_combobox), recurrenceGetWeekendAdjust(second));
484
485 gtk_notebook_set_current_page(gf->nb, PAGE_SEMI_MONTHLY);
486 gtk_combo_box_set_active(gf->freqComboBox, PAGE_SEMI_MONTHLY);
487 }
488 else
489 {
490 g_error("unknown composite recurrence with [%d] entries", g_list_length(recurrences));
491 }
492 }
493 else
494 {
495 Recurrence *r = (Recurrence*)recurrences->data;
496 DEBUG("recurrence period [%d]", recurrenceGetPeriodType(r));
497 switch (recurrenceGetPeriodType(r))
498 {
499 case PERIOD_ONCE:
500 {
501 GDate recurrence_date = recurrenceGetDate(r);
502 if (g_date_compare(start_date, &recurrence_date) != 0)
503 {
504 char start_date_str[128], recur_date_str[128];
505 g_date_strftime(start_date_str, 127, "%x", start_date);
506 g_date_strftime(recur_date_str, 127, "%x", &recurrence_date);
507 g_critical("start_date [%s] != recurrence_date [%s]", start_date_str, recur_date_str);
508 }
509
510 gtk_notebook_set_current_page(gf->nb, PAGE_ONCE);
511 gtk_combo_box_set_active(gf->freqComboBox, PAGE_ONCE);
512 }
513 break;
514 case PERIOD_DAY:
515 {
516 guint multiplier;
517 GtkWidget *spin_button;
518
519 multiplier = recurrenceGetMultiplier(r);
520 spin_button = GTK_WIDGET(gtk_builder_get_object (gf->builder, "daily_spin"));
521 gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin_button), multiplier);
522 made_changes = TRUE;
523
524 gtk_notebook_set_current_page(gf->nb, PAGE_DAILY);
525 gtk_combo_box_set_active(gf->freqComboBox, PAGE_DAILY);
526 }
527 break;
528 case PERIOD_WEEK:
529 {
530 _setup_weekly_recurrence(gf, r);
531 gtk_notebook_set_current_page(gf->nb, PAGE_WEEKLY);
532 gtk_combo_box_set_active(gf->freqComboBox, PAGE_WEEKLY);
533 }
534 break;
535 case PERIOD_END_OF_MONTH:
536 case PERIOD_MONTH:
537 case PERIOD_YEAR:
538 case PERIOD_LAST_WEEKDAY:
539 case PERIOD_NTH_WEEKDAY:
540 {
541 guint multiplier;
542 GtkWidget *multipler_spin, *day_of_month, *weekend_mode;
543
544 multipler_spin = GTK_WIDGET(gtk_builder_get_object (gf->builder, "monthly_spin"));
545 multiplier = recurrenceGetMultiplier(r);
546 if (recurrenceGetPeriodType(r) == PERIOD_YEAR)
547 multiplier *= 12;
548 gtk_spin_button_set_value(GTK_SPIN_BUTTON(multipler_spin), multiplier);
549
550 day_of_month = GTK_WIDGET(gtk_builder_get_object (gf->builder, "monthly_day"));
551 gtk_combo_box_set_active(GTK_COMBO_BOX(day_of_month), _get_monthly_combobox_index(r));
552 weekend_mode = GTK_WIDGET(gtk_builder_get_object (gf->builder, "monthly_weekend"));
553 gtk_combo_box_set_active(GTK_COMBO_BOX(weekend_mode), recurrenceGetWeekendAdjust(r));
554
555 gtk_notebook_set_current_page(gf->nb, PAGE_MONTHLY);
556 gtk_combo_box_set_active(gf->freqComboBox, PAGE_MONTHLY);
557 }
558 break;
559 default:
560 g_error("unknown recurrence period type [%d]", recurrenceGetPeriodType(r));
561 break;
562 }
563 }
564
565maybe_signal:
566 if (made_changes)
567 g_signal_emit_by_name(gf, "changed");
568}
569
570
571static gint
572_get_multiplier_from_widget(GncFrequency *gf, char *widget_name)
573{
574 GtkWidget *multiplier_spin;
575 multiplier_spin = GTK_WIDGET(gtk_builder_get_object (gf->builder, widget_name));
576 return gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(multiplier_spin));
577}
578
579
580static Recurrence*
581_get_day_of_month_recurrence(GncFrequency *gf, GDate *start_date, int multiplier, char *combo_name, char *combo_weekend_name)
582{
583 Recurrence *r;
584 GtkWidget *day_of_month_combo = GTK_WIDGET(gtk_builder_get_object (gf->builder, combo_name));
585 int day_of_month_index = gtk_combo_box_get_active(GTK_COMBO_BOX(day_of_month_combo));
586 GtkWidget *weekend_adjust_combo = GTK_WIDGET(gtk_builder_get_object (gf->builder, combo_weekend_name));
587 int weekend_adjust = gtk_combo_box_get_active(GTK_COMBO_BOX(weekend_adjust_combo));
588 GDateWeekday selected_day_of_week;
589 GDate *day_of_week_date;
590 int selected_index, selected_week;
591 r = g_new0(Recurrence, 1);
592 if (day_of_month_index > LAST_DAY_OF_MONTH_OPTION_INDEX + 7)
593 {
594 selected_index = day_of_month_index - LAST_DAY_OF_MONTH_OPTION_INDEX - 7;
595 day_of_week_date = g_date_new_julian(g_date_get_julian(start_date));
596 selected_week = (selected_index - 1) / 7 == 4 ? 3 : (selected_index - 1) / 7;
597 selected_day_of_week = selected_index - 7 * selected_week;
598 g_date_set_day(day_of_week_date, 1);
599 while (g_date_get_weekday(day_of_week_date) != selected_day_of_week)
600 g_date_add_days(day_of_week_date, 1);
601 g_date_add_days(day_of_week_date, 7 * selected_week);
602 recurrenceSet(r, multiplier, PERIOD_NTH_WEEKDAY, day_of_week_date, WEEKEND_ADJ_NONE);
603 }
604 else if (day_of_month_index > LAST_DAY_OF_MONTH_OPTION_INDEX)
605 {
606 day_of_week_date = g_date_new_julian(g_date_get_julian(start_date));
607 selected_day_of_week = day_of_month_index - LAST_DAY_OF_MONTH_OPTION_INDEX;
608 // increment until we align on the DOW, but stay inside the month
609 g_date_set_day(day_of_week_date, 1);
610 while (g_date_get_weekday(day_of_week_date) != selected_day_of_week)
611 g_date_add_days(day_of_week_date, 1);
612 recurrenceSet(r, multiplier, PERIOD_LAST_WEEKDAY, day_of_week_date, weekend_adjust);
613 }
614 else if (day_of_month_index == LAST_DAY_OF_MONTH_OPTION_INDEX)
615 {
616 GDate *day_of_month = g_date_new_julian(g_date_get_julian(start_date));
617 recurrenceSet(r, multiplier, PERIOD_END_OF_MONTH, day_of_month, weekend_adjust);
618 }
619 else
620 {
621 int allowable_date = -1;
622 GDate *day_of_month = g_date_new_julian(g_date_get_julian(start_date));
623 allowable_date = MIN(day_of_month_index + 1,
624 g_date_get_days_in_month(g_date_get_month(day_of_month),
625 g_date_get_year(day_of_month)));
626 g_date_set_day(day_of_month, allowable_date);
627 recurrenceSet(r, multiplier, PERIOD_MONTH, day_of_month, weekend_adjust);
628 }
629 return r;
630}
631
632
633void
634gnc_frequency_save_to_recurrence(GncFrequency *gf, GList **recurrences, GDate *out_start_date)
635{
636 GDate start_date;
637 gint page_index;
638
639 gnc_date_edit_get_gdate(gf->startDate, &start_date);
640 if (out_start_date != NULL)
641 *out_start_date = start_date;
642
643 if (recurrences == NULL)
644 return;
645
646 page_index = gtk_notebook_get_current_page(gf->nb);
647
648 switch (page_index)
649 {
650 case PAGE_NONE:
651 {
652 // empty-recurrence list ~~ none.
653 } break;
654 case PAGE_ONCE:
655 {
656 Recurrence *r = g_new0(Recurrence, 1);
657 recurrenceSet(r, 1, PERIOD_ONCE, &start_date, WEEKEND_ADJ_NONE);
658 *recurrences = g_list_append(*recurrences, r);
659 }
660 break;
661 case PAGE_DAILY:
662 {
663 gint multiplier = _get_multiplier_from_widget(gf, "daily_spin");
664 Recurrence *r = g_new0(Recurrence, 1);
665 recurrenceSet(r, multiplier, PERIOD_DAY, &start_date, WEEKEND_ADJ_NONE);
666 *recurrences = g_list_append(*recurrences, r);
667 }
668 break;
669 case PAGE_WEEKLY:
670 {
671 int multiplier = _get_multiplier_from_widget(gf, "weekly_spin");
672 int checkbox_idx;
673 for (checkbox_idx = 0; CHECKBOX_NAMES[checkbox_idx] != NULL; checkbox_idx++)
674 {
675 GDate *day_of_week_aligned_date;
676 Recurrence *r;
677 const char *day_widget_name = CHECKBOX_NAMES[checkbox_idx];
678 GtkWidget *weekday_checkbox = GTK_WIDGET(gtk_builder_get_object (gf->builder, day_widget_name));
679
680 if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(weekday_checkbox)))
681 continue;
682
683 day_of_week_aligned_date = g_date_new_julian(g_date_get_julian(&start_date));
684 // increment until we align on the DOW.
685 while ((g_date_get_weekday(day_of_week_aligned_date) % 7) != checkbox_idx)
686 g_date_add_days(day_of_week_aligned_date, 1);
687
688 r = g_new0(Recurrence, 1);
689 recurrenceSet(r, multiplier, PERIOD_WEEK, day_of_week_aligned_date, WEEKEND_ADJ_NONE);
690
691 *recurrences = g_list_append(*recurrences, r);
692 }
693 }
694 break;
695 case PAGE_SEMI_MONTHLY:
696 {
697 int multiplier = _get_multiplier_from_widget(gf, "semimonthly_spin");
698 *recurrences = g_list_append(*recurrences, _get_day_of_month_recurrence(gf, &start_date, multiplier, "semimonthly_first", "semimonthly_first_weekend"));
699 *recurrences = g_list_append(*recurrences, _get_day_of_month_recurrence(gf, &start_date, multiplier, "semimonthly_second", "semimonthly_second_weekend"));
700 }
701 break;
702 case PAGE_MONTHLY:
703 {
704 int multiplier = _get_multiplier_from_widget(gf, "monthly_spin");
705 Recurrence *r = _get_day_of_month_recurrence(gf, &start_date, multiplier, "monthly_day", "monthly_weekend");
706 *recurrences = g_list_append(*recurrences, r);
707 }
708 break;
709 default:
710 g_error("unknown page index [%d]", page_index);
711 break;
712 }
713}
Period / Date Frequency Specification.
All type declarations for the whole Gnucash engine.
utility functions for the GnuCash UI
#define DEBUG(format, args...)
Print a debugging message.
Definition qoflog.h:264
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition qoflog.h:282
#define ENTER(format, args...)
Print a function entry debugging message.
Definition qoflog.h:272
Implementations.