34 #include "dialog-utils.h" 35 #include "gnc-component-manager.h" 37 #include "gnc-frequency.h" 42 #define G_LOG_DOMAIN "gnc.gui.frequency" 44 static QofLogModule log_module = GNC_MOD_GUI;
46 #define LAST_DAY_OF_MONTH_OPTION_INDEX 31 56 static guint gnc_frequency_signals[LAST_SIGNAL] = { 0 };
60 static void gnc_frequency_destroy( GtkWidget *widget );
62 static void freq_combo_changed( GtkComboBox *b, gpointer d );
63 static void start_date_changed(
GNCDateEdit *gde, gpointer d );
64 static void spin_changed_helper( GtkAdjustment *adj, gpointer d );
66 static void weekly_days_changed( GtkButton *b, gpointer d );
68 static void monthly_sel_changed( GtkButton *b, gpointer d );
69 static void semimonthly_sel_changed( GtkButton *b, gpointer d );
83 static const char *CHECKBOX_NAMES[] =
103 typedef struct _GncFrequency
108 GtkComboBox *freqComboBox;
113 G_DEFINE_TYPE (
GncFrequency, gnc_frequency, GTK_TYPE_BOX)
116 gnc_frequency_class_init( GncFrequencyClass *klass )
118 GObjectClass *object_class;
119 GtkWidgetClass *gtkwidget_class;
121 object_class = G_OBJECT_CLASS (klass);
122 gtkwidget_class = GTK_WIDGET_CLASS (klass);
124 gnc_frequency_signals[GNCFREQ_CHANGED] =
125 g_signal_new (
"changed",
126 G_OBJECT_CLASS_TYPE (object_class),
131 g_cclosure_marshal_VOID__VOID,
136 gtkwidget_class->destroy = gnc_frequency_destroy;
149 static const struct comboBoxTuple
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 },
165 static const struct spinvalTuple
171 {
"daily_spin", spin_changed_helper },
172 {
"weekly_spin", spin_changed_helper },
173 {
"semimonthly_spin", spin_changed_helper },
174 {
"monthly_spin", spin_changed_helper },
178 gtk_orientable_set_orientation (GTK_ORIENTABLE(gf), GTK_ORIENTATION_VERTICAL);
181 gtk_widget_set_name (GTK_WIDGET(gf),
"gnc-id-frequency");
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");
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));
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);
213 vb = GTK_BOX(gtk_builder_get_object (builder,
"gncfreq_vbox"));
215 gtk_container_add(GTK_CONTAINER(&gf->widget), GTK_WIDGET(gf->vb));
218 for (i = 0; comboBoxes[i].name != NULL; i++)
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)
224 g_signal_connect(o,
"changed", G_CALLBACK(comboBoxes[i].fn), gf);
229 for (i = 0; spinVals[i].name != NULL; i++)
231 if (spinVals[i].fn != NULL)
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);
240 for (i = 0; i < 7; i++)
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);
247 gtk_widget_show_all(GTK_WIDGET(&gf->widget));
250 g_signal_connect(gf->startDate,
"date_changed", G_CALLBACK(start_date_changed), gf);
252 gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, gf);
265 gnc_frequency_destroy (GtkWidget *widget)
269 ENTER(
"frequency %p", widget);
270 g_return_if_fail (widget != NULL);
271 g_return_if_fail (GNC_IS_FREQUENCY (widget));
273 gf = GNC_FREQUENCY (widget);
277 DEBUG(
"removing builder");
278 g_object_unref(G_OBJECT(gf->builder));
282 GTK_WIDGET_CLASS (gnc_frequency_parent_class)->destroy (widget);
288 spin_changed_helper( GtkAdjustment *adj, gpointer d )
290 g_signal_emit_by_name(GNC_FREQUENCY(d),
"changed");
295 weekly_days_changed( GtkButton *b, gpointer d )
297 g_signal_emit_by_name(GNC_FREQUENCY(d),
"changed");
302 monthly_sel_changed( GtkButton *b, gpointer d )
304 g_signal_emit_by_name(GNC_FREQUENCY(d),
"changed");
309 semimonthly_sel_changed( GtkButton *b, gpointer d )
311 g_signal_emit_by_name(GNC_FREQUENCY(d),
"changed");
316 freq_combo_changed(GtkComboBox *b, gpointer d)
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");
331 g_signal_emit_by_name(GNC_FREQUENCY(d),
"changed");
339 gnc_frequency_set_frequency_label_text(
GncFrequency *gf,
const gchar *txt)
342 if (!gf || !txt)
return;
343 lbl = GTK_LABEL (gtk_builder_get_object (gf->builder,
"freq_label"));
344 gtk_label_set_text (lbl, txt);
349 gnc_frequency_set_date_label_text(
GncFrequency *gf,
const gchar *txt)
352 if (!gf || !txt)
return;
353 lbl = GTK_LABEL (gtk_builder_get_object (gf->builder,
"startdate_label"));
354 gtk_label_set_text (lbl, txt);
359 gnc_frequency_new_from_recurrence(GList *recurrences,
const GDate *start_date)
361 return gnc_frequency_new(recurrences, start_date);
366 gnc_frequency_new(GList *recurrences,
const GDate *start_date)
369 toRet = g_object_new(gnc_frequency_get_type(), NULL);
370 gnc_frequency_setup_recurrence(toRet, recurrences, start_date);
371 return GTK_WIDGET(toRet);
378 GDate recurrence_date;
379 GDateWeekday day_of_week;
380 guint multiplier = recurrenceGetMultiplier(r);
381 const char *checkbox_widget_name;
382 GtkWidget *weekday_checkbox;
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);
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);
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);
401 GDate recurrence_date = recurrenceGetDate(r);
403 int day_of_month_index = g_date_get_day(&recurrence_date) - 1;
404 if (recurrenceGetPeriodType(r) == PERIOD_END_OF_MONTH)
406 day_of_month_index = LAST_DAY_OF_MONTH_OPTION_INDEX;
408 else if (recurrenceGetPeriodType(r) == PERIOD_LAST_WEEKDAY)
411 = LAST_DAY_OF_MONTH_OPTION_INDEX
412 + g_date_get_weekday(&recurrence_date);
414 else if (recurrenceGetPeriodType(r) == PERIOD_NTH_WEEKDAY)
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;
423 return day_of_month_index;
428 gnc_frequency_setup_recurrence(
GncFrequency *gf, GList *recurrences,
const GDate *start_date)
430 gnc_frequency_setup(gf, recurrences, start_date);
435 gnc_frequency_setup(
GncFrequency *gf, GList *recurrences,
const GDate *start_date)
437 gboolean made_changes = FALSE;
440 if (start_date != NULL
441 && g_date_valid(start_date))
443 gnc_date_edit_set_gdate(gf->startDate, start_date);
447 if (recurrences == NULL)
453 if (g_list_length(recurrences) > 1)
455 if (recurrenceListIsWeeklyMultiple(recurrences))
457 for (; recurrences != NULL; recurrences = recurrences->next)
459 _setup_weekly_recurrence(gf, (
Recurrence*)recurrences->data);
462 gtk_notebook_set_current_page(gf->nb, PAGE_WEEKLY);
463 gtk_combo_box_set_active(gf->freqComboBox, PAGE_WEEKLY);
465 else if (recurrenceListIsSemiMonthly(recurrences))
468 GtkWidget *multiplier_spin;
469 GtkWidget *dom_combobox;
471 first = (
Recurrence*)g_list_nth_data(recurrences, 0);
472 second = (
Recurrence*)g_list_nth_data(recurrences, 1);
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));
485 gtk_notebook_set_current_page(gf->nb, PAGE_SEMI_MONTHLY);
486 gtk_combo_box_set_active(gf->freqComboBox, PAGE_SEMI_MONTHLY);
490 g_error(
"unknown composite recurrence with [%d] entries", g_list_length(recurrences));
496 DEBUG(
"recurrence period [%d]", recurrenceGetPeriodType(r));
497 switch (recurrenceGetPeriodType(r))
501 GDate recurrence_date = recurrenceGetDate(r);
502 if (g_date_compare(start_date, &recurrence_date) != 0)
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);
510 gtk_notebook_set_current_page(gf->nb, PAGE_ONCE);
511 gtk_combo_box_set_active(gf->freqComboBox, PAGE_ONCE);
517 GtkWidget *spin_button;
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);
524 gtk_notebook_set_current_page(gf->nb, PAGE_DAILY);
525 gtk_combo_box_set_active(gf->freqComboBox, PAGE_DAILY);
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);
535 case PERIOD_END_OF_MONTH:
538 case PERIOD_LAST_WEEKDAY:
539 case PERIOD_NTH_WEEKDAY:
542 GtkWidget *multipler_spin, *day_of_month, *weekend_mode;
544 multipler_spin = GTK_WIDGET(gtk_builder_get_object (gf->builder,
"monthly_spin"));
545 multiplier = recurrenceGetMultiplier(r);
546 if (recurrenceGetPeriodType(r) == PERIOD_YEAR)
548 gtk_spin_button_set_value(GTK_SPIN_BUTTON(multipler_spin), multiplier);
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));
555 gtk_notebook_set_current_page(gf->nb, PAGE_MONTHLY);
556 gtk_combo_box_set_active(gf->freqComboBox, PAGE_MONTHLY);
560 g_error(
"unknown recurrence period type [%d]", recurrenceGetPeriodType(r));
567 g_signal_emit_by_name(gf,
"changed");
572 _get_multiplier_from_widget(
GncFrequency *gf,
char *widget_name)
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));
581 _get_day_of_month_recurrence(
GncFrequency *gf, GDate *start_date,
int multiplier,
char *combo_name,
char *combo_weekend_name)
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;
592 if (day_of_month_index > LAST_DAY_OF_MONTH_OPTION_INDEX + 7)
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);
604 else if (day_of_month_index > LAST_DAY_OF_MONTH_OPTION_INDEX)
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;
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);
614 else if (day_of_month_index == LAST_DAY_OF_MONTH_OPTION_INDEX)
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);
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);
634 gnc_frequency_save_to_recurrence(
GncFrequency *gf, GList **recurrences, GDate *out_start_date)
639 gnc_date_edit_get_gdate(gf->startDate, &start_date);
640 if (out_start_date != NULL)
641 *out_start_date = start_date;
643 if (recurrences == NULL)
646 page_index = gtk_notebook_get_current_page(gf->nb);
657 recurrenceSet(r, 1, PERIOD_ONCE, &start_date, WEEKEND_ADJ_NONE);
658 *recurrences = g_list_append(*recurrences, r);
663 gint multiplier = _get_multiplier_from_widget(gf,
"daily_spin");
665 recurrenceSet(r, multiplier, PERIOD_DAY, &start_date, WEEKEND_ADJ_NONE);
666 *recurrences = g_list_append(*recurrences, r);
671 int multiplier = _get_multiplier_from_widget(gf,
"weekly_spin");
673 for (checkbox_idx = 0; CHECKBOX_NAMES[checkbox_idx] != NULL; checkbox_idx++)
675 GDate *day_of_week_aligned_date;
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));
680 if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(weekday_checkbox)))
683 day_of_week_aligned_date = g_date_new_julian(g_date_get_julian(&start_date));
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);
689 recurrenceSet(r, multiplier, PERIOD_WEEK, day_of_week_aligned_date, WEEKEND_ADJ_NONE);
691 *recurrences = g_list_append(*recurrences, r);
695 case PAGE_SEMI_MONTHLY:
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"));
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);
710 g_error(
"unknown page index [%d]", page_index);
utility functions for the GnuCash UI
#define DEBUG(format, args...)
Print a debugging message.
#define ENTER(format, args...)
Print a function entry debugging message.
Period / Date Frequency Specification.
All type declarations for the whole Gnucash engine.
#define LEAVE(format, args...)
Print a function exit debugging message.