34 #include "dialog-utils.h"
35 #include "gnc-component-manager.h"
37 #include "gnc-frequency.h"
42 #define G_LOG_DOMAIN "gnc.gui.frequency"
46 #define LAST_DAY_OF_MONTH_OPTION_INDEX 31
56 static guint gnc_frequency_signals[LAST_SIGNAL] = { 0 };
61 static void gnc_frequency_class_destroy( GtkObject *
object );
63 static void freq_combo_changed( GtkComboBox *b, gpointer d );
64 static void start_date_changed(
GNCDateEdit *gde, gpointer d );
65 static void spin_changed_helper( GtkAdjustment *adj, gpointer d );
67 static void weekly_days_changed( GtkButton *b, gpointer d );
69 static void monthly_sel_changed( GtkButton *b, gpointer d );
70 static void semimonthly_sel_changed( GtkButton *b, gpointer d );
74 static GObjectClass *parent_class = NULL;
88 { PAGE_NONE, UIFREQ_NONE,
"None" },
89 { PAGE_ONCE, UIFREQ_ONCE,
"Once" },
90 { PAGE_DAILY, UIFREQ_DAILY,
"Daily" },
91 { PAGE_WEEKLY, UIFREQ_WEEKLY,
"Weekly" },
92 { PAGE_SEMI_MONTHLY, UIFREQ_SEMI_MONTHLY,
"Semi-Monthly" },
93 { PAGE_MONTHLY, UIFREQ_MONTHLY,
"Monthly" },
97 static const char *CHECKBOX_NAMES[] =
112 gnc_frequency_get_type()
114 static GType gncfreq_type = 0;
115 if (gncfreq_type == 0)
117 static GTypeInfo gncfreq_info =
122 (GClassInitFunc)gnc_frequency_class_init,
127 (GInstanceInitFunc)gnc_frequency_init
130 gncfreq_type = g_type_register_static (GTK_TYPE_VBOX,
142 GObjectClass *object_class;
143 GtkObjectClass *gtkobject_class;
145 parent_class = g_type_class_peek_parent (klass);
147 object_class = G_OBJECT_CLASS (klass);
148 gtkobject_class = GTK_OBJECT_CLASS (klass);
150 gnc_frequency_signals[GNCFREQ_CHANGED] =
151 g_signal_new (
"changed",
152 G_OBJECT_CLASS_TYPE (object_class),
157 g_cclosure_marshal_VOID__VOID,
162 gtkobject_class->destroy = gnc_frequency_class_destroy;
175 static const struct comboBoxTuple
181 {
"freq_combobox", freq_combo_changed },
182 {
"semimonthly_first", semimonthly_sel_changed },
183 {
"semimonthly_first_weekend", semimonthly_sel_changed },
184 {
"semimonthly_second", semimonthly_sel_changed },
185 {
"semimonthly_second_weekend", semimonthly_sel_changed },
186 {
"monthly_day", monthly_sel_changed },
187 {
"monthly_weekend", monthly_sel_changed },
191 static const struct spinvalTuple
197 {
"daily_spin", spin_changed_helper },
198 {
"weekly_spin", spin_changed_helper },
199 {
"semimonthly_spin", spin_changed_helper },
200 {
"monthly_spin", spin_changed_helper },
204 builder = gtk_builder_new();
205 gnc_builder_add_from_file (builder ,
"gnc-frequency.glade",
"adjustment1");
206 gnc_builder_add_from_file (builder ,
"gnc-frequency.glade",
"adjustment2");
207 gnc_builder_add_from_file (builder ,
"gnc-frequency.glade",
"adjustment3");
208 gnc_builder_add_from_file (builder ,
"gnc-frequency.glade",
"adjustment4");
209 gnc_builder_add_from_file (builder ,
"gnc-frequency.glade",
"liststore1");
210 gnc_builder_add_from_file (builder ,
"gnc-frequency.glade",
"liststore2");
211 gnc_builder_add_from_file (builder ,
"gnc-frequency.glade",
"liststore3");
212 gnc_builder_add_from_file (builder ,
"gnc-frequency.glade",
"liststore4");
213 gnc_builder_add_from_file (builder ,
"gnc-frequency.glade",
"liststore5");
214 gnc_builder_add_from_file (builder ,
"gnc-frequency.glade",
"liststore6");
215 gnc_builder_add_from_file (builder ,
"gnc-frequency.glade",
"liststore7");
216 gnc_builder_add_from_file (builder ,
"gnc-frequency.glade",
"gncfreq_vbox");
218 gf->builder = builder;
219 o = GTK_WIDGET(gtk_builder_get_object (builder,
"gncfreq_nb"));
220 gf->nb = GTK_NOTEBOOK(o);
221 o = GTK_WIDGET(gtk_builder_get_object (builder,
"freq_combobox"));
222 gf->freqComboBox = GTK_COMBO_BOX(o);
223 gf->startDate = GNC_DATE_EDIT(gnc_date_edit_new(time(NULL), FALSE, FALSE));
226 gint dont_expand_or_fill = 0;
227 GtkWidget *
table = GTK_WIDGET(gtk_builder_get_object (builder,
"gncfreq_table"));
228 gtk_table_attach(GTK_TABLE(table), GTK_WIDGET(gf->startDate),
229 4, 5, 0, 1, dont_expand_or_fill, 0,
232 vb = GTK_VBOX(gtk_builder_get_object (builder,
"gncfreq_vbox"));
234 gtk_container_add(GTK_CONTAINER(&gf->widget), GTK_WIDGET(gf->vb));
237 for (i = 0; comboBoxes[i].name != NULL; i++)
239 o = GTK_WIDGET(gtk_builder_get_object (builder, comboBoxes[i].name));
240 gtk_combo_box_set_active(GTK_COMBO_BOX(o), 0);
241 if (comboBoxes[i].fn != NULL)
243 g_signal_connect(o,
"changed", G_CALLBACK(comboBoxes[i].fn), gf);
248 for (i = 0; spinVals[i].name != NULL; i++)
250 if (spinVals[i].fn != NULL)
252 o = GTK_WIDGET(gtk_builder_get_object (builder, spinVals[i].name));
253 adj = gtk_spin_button_get_adjustment(GTK_SPIN_BUTTON(o));
254 g_signal_connect(adj,
"value_changed", G_CALLBACK(spinVals[i].fn), gf);
259 for (i = 0; i < 7; i++)
261 o = GTK_WIDGET(gtk_builder_get_object (builder, CHECKBOX_NAMES[i]));
262 g_signal_connect(o,
"clicked",
263 G_CALLBACK(weekly_days_changed), gf);
266 gtk_widget_show_all(GTK_WIDGET(&gf->widget));
269 g_signal_connect(gf->startDate,
"date_changed", G_CALLBACK(start_date_changed), gf);
271 gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, gf);
284 gnc_frequency_class_destroy (GtkObject *
object)
288 ENTER(
"frequency %p",
object);
289 g_return_if_fail (
object != NULL);
290 g_return_if_fail (GNC_IS_FREQUENCY (
object));
292 gf = GNC_FREQUENCY (
object);
296 DEBUG(
"removing builder");
297 g_object_unref(G_OBJECT(gf->builder));
301 if (GTK_OBJECT_CLASS (parent_class)->destroy)
302 GTK_OBJECT_CLASS (parent_class)->destroy (
object);
308 spin_changed_helper( GtkAdjustment *adj, gpointer d )
310 g_signal_emit_by_name(GNC_FREQUENCY(d),
"changed");
315 weekly_days_changed( GtkButton *b, gpointer d )
317 g_signal_emit_by_name(GNC_FREQUENCY(d),
"changed");
322 monthly_sel_changed( GtkButton *b, gpointer d )
324 g_signal_emit_by_name(GNC_FREQUENCY(d),
"changed");
329 semimonthly_sel_changed( GtkButton *b, gpointer d )
331 g_signal_emit_by_name(GNC_FREQUENCY(d),
"changed");
336 freq_combo_changed(GtkComboBox *b, gpointer d)
342 option_index = gtk_combo_box_get_active(GTK_COMBO_BOX(gf->freqComboBox));
343 gtk_notebook_set_current_page(gf->nb, option_index);
344 g_signal_emit_by_name(gf,
"changed");
351 g_signal_emit_by_name(GNC_FREQUENCY(d),
"changed");
359 gnc_frequency_set_frequency_label_text(
GncFrequency *gf,
const gchar *txt)
362 if (!gf || !txt)
return;
363 lbl = GTK_LABEL (gtk_builder_get_object (gf->builder,
"freq_label"));
364 gtk_label_set_text (lbl, txt);
369 gnc_frequency_set_date_label_text(
GncFrequency *gf,
const gchar *txt)
372 if (!gf || !txt)
return;
373 lbl = GTK_LABEL (gtk_builder_get_object (gf->builder,
"startdate_label"));
374 gtk_label_set_text (lbl, txt);
379 gnc_frequency_new_from_recurrence(GList *recurrences,
const GDate *start_date)
381 return gnc_frequency_new(recurrences, start_date);
386 gnc_frequency_new(GList *recurrences,
const GDate *start_date)
389 toRet = g_object_new(gnc_frequency_get_type(), NULL);
390 gnc_frequency_setup_recurrence(toRet, recurrences, start_date);
391 return GTK_WIDGET(toRet);
398 GDate recurrence_date;
399 GDateWeekday day_of_week;
400 guint multiplier = recurrenceGetMultiplier(r);
401 const char *checkbox_widget_name;
402 GtkWidget *weekday_checkbox;
404 GtkWidget *multipler_spin = GTK_WIDGET(gtk_builder_get_object (gf->builder,
"weekly_spin"));
405 gtk_spin_button_set_value(GTK_SPIN_BUTTON(multipler_spin), multiplier);
407 recurrence_date = recurrenceGetDate(r);
408 day_of_week = g_date_get_weekday(&recurrence_date);
409 g_assert(day_of_week >= G_DATE_MONDAY && day_of_week <= G_DATE_SUNDAY);
412 checkbox_widget_name = CHECKBOX_NAMES[day_of_week % 7];
413 weekday_checkbox = GTK_WIDGET(gtk_builder_get_object (gf->builder, checkbox_widget_name));
414 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(weekday_checkbox), TRUE);
421 GDate recurrence_date = recurrenceGetDate(r);
423 int day_of_month_index = g_date_get_day(&recurrence_date) - 1;
424 if (recurrenceGetPeriodType(r) == PERIOD_END_OF_MONTH)
426 day_of_month_index = LAST_DAY_OF_MONTH_OPTION_INDEX;
428 else if (recurrenceGetPeriodType(r) == PERIOD_LAST_WEEKDAY)
431 = LAST_DAY_OF_MONTH_OPTION_INDEX
432 + g_date_get_weekday(&recurrence_date);
434 else if (recurrenceGetPeriodType(r) == PERIOD_NTH_WEEKDAY)
436 week = day_of_month_index / 7 > 3 ? 3 : day_of_month_index / 7;
437 if (week > 0 && day_of_month_index % 7 == 0)
439 day_of_month_index = LAST_DAY_OF_MONTH_OPTION_INDEX + 7 +
440 g_date_get_weekday(&recurrence_date) + 7 * week;
445 return day_of_month_index;
450 gnc_frequency_setup_recurrence(
GncFrequency *gf, GList *recurrences,
const GDate *start_date)
452 gnc_frequency_setup(gf, recurrences, start_date);
457 gnc_frequency_setup(
GncFrequency *gf, GList *recurrences,
const GDate *start_date)
459 gboolean made_changes = FALSE;
462 if (start_date != NULL
463 && g_date_valid(start_date))
465 gnc_date_edit_set_gdate(gf->startDate, start_date);
469 if (recurrences == NULL)
475 if (g_list_length(recurrences) > 1)
477 if (recurrenceListIsWeeklyMultiple(recurrences))
479 for (; recurrences != NULL; recurrences = recurrences->next)
481 _setup_weekly_recurrence(gf, (
Recurrence*)recurrences->data);
484 gtk_notebook_set_current_page(gf->nb, PAGE_WEEKLY);
485 gtk_combo_box_set_active(gf->freqComboBox, PAGE_WEEKLY);
487 else if (recurrenceListIsSemiMonthly(recurrences))
490 GtkWidget *multiplier_spin;
491 GtkWidget *dom_combobox;
493 first = (
Recurrence*)g_list_nth_data(recurrences, 0);
494 second = (
Recurrence*)g_list_nth_data(recurrences, 1);
496 multiplier_spin = GTK_WIDGET(gtk_builder_get_object (gf->builder,
"semimonthly_spin"));
497 gtk_spin_button_set_value(GTK_SPIN_BUTTON(multiplier_spin), recurrenceGetMultiplier(first));
498 dom_combobox = GTK_WIDGET(gtk_builder_get_object (gf->builder,
"semimonthly_first"));
499 gtk_combo_box_set_active(GTK_COMBO_BOX(dom_combobox), _get_monthly_combobox_index(first));
500 dom_combobox = GTK_WIDGET(gtk_builder_get_object (gf->builder,
"semimonthly_first_weekend"));
501 gtk_combo_box_set_active(GTK_COMBO_BOX(dom_combobox), recurrenceGetWeekendAdjust(first));
502 dom_combobox = GTK_WIDGET(gtk_builder_get_object (gf->builder,
"semimonthly_second"));
503 gtk_combo_box_set_active(GTK_COMBO_BOX(dom_combobox), _get_monthly_combobox_index(second));
504 dom_combobox = GTK_WIDGET(gtk_builder_get_object (gf->builder,
"semimonthly_second_weekend"));
505 gtk_combo_box_set_active(GTK_COMBO_BOX(dom_combobox), recurrenceGetWeekendAdjust(second));
507 gtk_notebook_set_current_page(gf->nb, PAGE_SEMI_MONTHLY);
508 gtk_combo_box_set_active(gf->freqComboBox, PAGE_SEMI_MONTHLY);
512 g_error(
"unknown composite recurrence with [%d] entries", g_list_length(recurrences));
518 g_debug(
"recurrence period [%d]", recurrenceGetPeriodType(r));
519 switch (recurrenceGetPeriodType(r))
523 GDate recurrence_date = recurrenceGetDate(r);
524 if (g_date_compare(start_date, &recurrence_date) != 0)
526 char start_date_str[128], recur_date_str[128];
527 g_date_strftime(start_date_str, 127,
"%x", start_date);
528 g_date_strftime(recur_date_str, 127,
"%x", &recurrence_date);
529 g_critical(
"start_date [%s] != recurrence_date [%s]", start_date_str, recur_date_str);
532 gtk_notebook_set_current_page(gf->nb, PAGE_ONCE);
533 gtk_combo_box_set_active(gf->freqComboBox, PAGE_ONCE);
539 GtkWidget *spin_button;
541 multiplier = recurrenceGetMultiplier(r);
542 spin_button = GTK_WIDGET(gtk_builder_get_object (gf->builder,
"daily_spin"));
543 gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin_button), multiplier);
546 gtk_notebook_set_current_page(gf->nb, PAGE_DAILY);
547 gtk_combo_box_set_active(gf->freqComboBox, PAGE_DAILY);
552 _setup_weekly_recurrence(gf, r);
553 gtk_notebook_set_current_page(gf->nb, PAGE_WEEKLY);
554 gtk_combo_box_set_active(gf->freqComboBox, PAGE_WEEKLY);
557 case PERIOD_END_OF_MONTH:
560 case PERIOD_LAST_WEEKDAY:
561 case PERIOD_NTH_WEEKDAY:
564 GtkWidget *multipler_spin, *day_of_month, *weekend_mode;
566 multipler_spin = GTK_WIDGET(gtk_builder_get_object (gf->builder,
"monthly_spin"));
567 multiplier = recurrenceGetMultiplier(r);
568 if (recurrenceGetPeriodType(r) == PERIOD_YEAR)
570 gtk_spin_button_set_value(GTK_SPIN_BUTTON(multipler_spin), multiplier);
572 day_of_month = GTK_WIDGET(gtk_builder_get_object (gf->builder,
"monthly_day"));
573 gtk_combo_box_set_active(GTK_COMBO_BOX(day_of_month), _get_monthly_combobox_index(r));
574 weekend_mode = GTK_WIDGET(gtk_builder_get_object (gf->builder,
"monthly_weekend"));
575 gtk_combo_box_set_active(GTK_COMBO_BOX(weekend_mode), recurrenceGetWeekendAdjust(r));
577 gtk_notebook_set_current_page(gf->nb, PAGE_MONTHLY);
578 gtk_combo_box_set_active(gf->freqComboBox, PAGE_MONTHLY);
582 g_error(
"unknown recurrence period type [%d]", recurrenceGetPeriodType(r));
589 g_signal_emit_by_name(gf,
"changed");
594 _get_multiplier_from_widget(
GncFrequency *gf,
char *widget_name)
596 GtkWidget *multiplier_spin;
597 multiplier_spin = GTK_WIDGET(gtk_builder_get_object (gf->builder, widget_name));
598 return gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(multiplier_spin));
603 _get_day_of_month_recurrence(
GncFrequency *gf, GDate *start_date,
int multiplier,
char *combo_name,
char *combo_weekend_name)
606 GtkWidget *day_of_month_combo = GTK_WIDGET(gtk_builder_get_object (gf->builder, combo_name));
607 int day_of_month_index = gtk_combo_box_get_active(GTK_COMBO_BOX(day_of_month_combo));
608 GtkWidget *weekend_adjust_combo = GTK_WIDGET(gtk_builder_get_object (gf->builder, combo_weekend_name));
609 int weekend_adjust = gtk_combo_box_get_active(GTK_COMBO_BOX(weekend_adjust_combo));
610 GDateWeekday selected_day_of_week;
611 GDate *day_of_week_date;
612 int selected_index, selected_week;
614 if (day_of_month_index > LAST_DAY_OF_MONTH_OPTION_INDEX + 7)
616 selected_index = day_of_month_index - LAST_DAY_OF_MONTH_OPTION_INDEX - 7;
617 day_of_week_date = g_date_new_julian(g_date_get_julian(start_date));
618 selected_week = (selected_index - 1) / 7 == 4 ? 3 : (selected_index - 1) / 7;
619 selected_day_of_week = selected_index - 7 * selected_week;
620 g_date_set_day(day_of_week_date, 1);
621 while (g_date_get_weekday(day_of_week_date) != selected_day_of_week)
622 g_date_add_days(day_of_week_date, 1);
623 g_date_add_days(day_of_week_date, 7 * selected_week);
624 recurrenceSet(r, multiplier, PERIOD_NTH_WEEKDAY, day_of_week_date, WEEKEND_ADJ_NONE);
626 else if (day_of_month_index > LAST_DAY_OF_MONTH_OPTION_INDEX)
628 day_of_week_date = g_date_new_julian(g_date_get_julian(start_date));
629 selected_day_of_week = day_of_month_index - LAST_DAY_OF_MONTH_OPTION_INDEX;
631 g_date_set_day(day_of_week_date, 1);
632 while (g_date_get_weekday(day_of_week_date) != selected_day_of_week)
633 g_date_add_days(day_of_week_date, 1);
634 recurrenceSet(r, multiplier, PERIOD_LAST_WEEKDAY, day_of_week_date, weekend_adjust);
636 else if (day_of_month_index == LAST_DAY_OF_MONTH_OPTION_INDEX)
638 GDate *day_of_month = g_date_new_julian(g_date_get_julian(start_date));
639 recurrenceSet(r, multiplier, PERIOD_END_OF_MONTH, day_of_month, weekend_adjust);
643 int allowable_date = -1;
644 GDate *day_of_month = g_date_new_julian(g_date_get_julian(start_date));
645 allowable_date = MIN(day_of_month_index + 1,
646 g_date_get_days_in_month(g_date_get_month(day_of_month),
647 g_date_get_year(day_of_month)));
648 g_date_set_day(day_of_month, allowable_date);
649 recurrenceSet(r, multiplier, PERIOD_MONTH, day_of_month, weekend_adjust);
656 gnc_frequency_save_to_recurrence(
GncFrequency *gf, GList **recurrences, GDate *out_start_date)
661 gnc_date_edit_get_gdate(gf->startDate, &start_date);
662 if (out_start_date != NULL)
663 *out_start_date = start_date;
665 if (recurrences == NULL)
668 page_index = gtk_notebook_get_current_page(gf->nb);
679 recurrenceSet(r, 1, PERIOD_ONCE, &start_date, WEEKEND_ADJ_NONE);
680 *recurrences = g_list_append(*recurrences, r);
685 gint multiplier = _get_multiplier_from_widget(gf,
"daily_spin");
687 recurrenceSet(r, multiplier, PERIOD_DAY, &start_date, WEEKEND_ADJ_NONE);
688 *recurrences = g_list_append(*recurrences, r);
693 int multiplier = _get_multiplier_from_widget(gf,
"weekly_spin");
695 for (checkbox_idx = 0; CHECKBOX_NAMES[checkbox_idx] != NULL; checkbox_idx++)
697 GDate *day_of_week_aligned_date;
699 const char *day_widget_name = CHECKBOX_NAMES[checkbox_idx];
700 GtkWidget *weekday_checkbox = GTK_WIDGET(gtk_builder_get_object (gf->builder, day_widget_name));
702 if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(weekday_checkbox)))
705 day_of_week_aligned_date = g_date_new_julian(g_date_get_julian(&start_date));
707 while ((g_date_get_weekday(day_of_week_aligned_date) % 7) != checkbox_idx)
708 g_date_add_days(day_of_week_aligned_date, 1);
711 recurrenceSet(r, multiplier, PERIOD_WEEK, day_of_week_aligned_date, WEEKEND_ADJ_NONE);
713 *recurrences = g_list_append(*recurrences, r);
717 case PAGE_SEMI_MONTHLY:
719 int multiplier = _get_multiplier_from_widget(gf,
"semimonthly_spin");
720 *recurrences = g_list_append(*recurrences, _get_day_of_month_recurrence(gf, &start_date, multiplier,
"semimonthly_first",
"semimonthly_first_weekend"));
721 *recurrences = g_list_append(*recurrences, _get_day_of_month_recurrence(gf, &start_date, multiplier,
"semimonthly_second",
"semimonthly_second_weekend"));
726 int multiplier = _get_multiplier_from_widget(gf,
"monthly_spin");
727 Recurrence *r = _get_day_of_month_recurrence(gf, &start_date, multiplier,
"monthly_day",
"monthly_weekend");
728 *recurrences = g_list_append(*recurrences, r);
732 g_error(
"unknown page index [%d]", page_index);
utility functions for the GnuCash UI
#define DEBUG(format, args...)
#define ENTER(format, args...)
Period / Date Frequency Specification.
All type declarations for the whole Gnucash engine.
#define LEAVE(format, args...)
const gchar * QofLogModule