diff -U4 -r gnucash-2.2.9/src/backend/file/gnc-freqspec-xml-v2.c gnucash-2.2.9+dd/src/backend/file/gnc-freqspec-xml-v2.c --- gnucash-2.2.9/src/backend/file/gnc-freqspec-xml-v2.c 2008-01-08 01:05:36.000000000 +0000 +++ gnucash-2.2.9+dd/src/backend/file/gnc-freqspec-xml-v2.c 2008-11-10 19:38:08.000000000 +0000 @@ -100,8 +100,9 @@ gint64 interval; /* all [except once] */ gint64 offset; /* all [except once] */ gint64 day; /* monthly or month-relative */ gint64 occurrence; /* month-relative */ + gint64 weekend_adj; /* monthly/yearly */ GList *list; /* composite */ UIFreqType uift; } fsParseData; @@ -117,8 +118,9 @@ = fspd->offset = fspd->day = fspd->occurrence = 0; + fspd->weekend_adj = WEEKEND_ADJ_NONE; g_date_clear( &fspd->once_day, 1 ); } static struct dom_tree_handler fs_dom_handlers[]; @@ -242,8 +244,22 @@ } static gboolean +fs_weekend_adj_handler( xmlNodePtr node, gpointer data ) +{ + fsParseData *fspd = data; + gboolean ret; + gint64 foo; + ret = dom_tree_to_integer( node, &foo ); + if ( !ret ) + return ret; + fspd->weekend_adj = foo; + return TRUE; +} + +static +gboolean fs_subelement_handler( xmlNodePtr node, gpointer data ) { fsParseData *fspd = data; GList *recurrences; @@ -261,24 +277,25 @@ if (fspd->uift == UIFREQ_SEMI_MONTHLY) { // complementry hack around 'once' freqspects not being valid. :/ recurrence_date = recurrenceGetDate(r); - recurrenceSet(r, recurrenceGetMultiplier(r), PERIOD_MONTH, &recurrence_date); + recurrenceSet(r, recurrenceGetMultiplier(r), PERIOD_MONTH, &recurrence_date, recurrenceGetWeekendAdjust(r)); } fspd->recurrence_list = g_list_append(fspd->recurrence_list, r); } } return TRUE; } struct dom_tree_handler fs_union_dom_handlers[] = { - { "fs:date", fs_date_handler, 0, 0 }, - { "fs:interval", fs_interval_handler, 0, 0 }, - { "fs:offset", fs_offset_handler, 0, 0 }, - { "fs:day", fs_day_handler, 0, 0 }, - { "fs:weekday", fs_weekday_handler, 0, 0 }, - { "fs:occurrence", fs_occurrence_handler, 0, 0 }, - { "gnc:freqspec", fs_subelement_handler, 0, 0 }, + { "fs:date", fs_date_handler, 0, 0 }, + { "fs:interval", fs_interval_handler, 0, 0 }, + { "fs:offset", fs_offset_handler, 0, 0 }, + { "fs:day", fs_day_handler, 0, 0 }, + { "fs:weekday", fs_weekday_handler, 0, 0 }, + { "fs:occurrence", fs_occurrence_handler, 0, 0 }, + { "fs:weekend_adj", fs_weekend_adj_handler, 0, 0 }, + { "gnc:freqspec", fs_subelement_handler, 0, 0 }, { NULL, NULL, 0, 0 }, }; static gboolean @@ -303,9 +320,9 @@ fs_union_dom_handlers, fspd ); if ( !successful ) return FALSE; - recurrenceSet(fspd->recurrence, 0, PERIOD_ONCE, &fspd->once_day); + recurrenceSet(fspd->recurrence, 0, PERIOD_ONCE, &fspd->once_day, WEEKEND_ADJ_NONE); return TRUE; } @@ -320,9 +337,9 @@ return FALSE; g_date_clear(&offset_date, 1); g_date_set_julian(&offset_date, fspd->offset == 0 ? 7 : fspd->offset); - recurrenceSet(fspd->recurrence, fspd->interval, PERIOD_DAY, &offset_date); + recurrenceSet(fspd->recurrence, fspd->interval, PERIOD_DAY, &offset_date, WEEKEND_ADJ_NONE); return TRUE; } @@ -340,9 +357,9 @@ return FALSE; g_date_clear(&offset_date, 1); g_date_set_julian(&offset_date, fspd->offset == 0 ? 7 : fspd->offset); - recurrenceSet(fspd->recurrence, fspd->interval, PERIOD_WEEK, &offset_date); + recurrenceSet(fspd->recurrence, fspd->interval, PERIOD_WEEK, &offset_date, WEEKEND_ADJ_NONE); return TRUE; } @@ -366,13 +383,13 @@ g_date_set_day(&offset_date, fspd->day); if (fspd->uift == UIFREQ_ONCE) { // hack... - recurrenceSet(fspd->recurrence, fspd->interval, PERIOD_ONCE, &offset_date); + recurrenceSet(fspd->recurrence, fspd->interval, PERIOD_ONCE, &offset_date, WEEKEND_ADJ_NONE); } else { - recurrenceSet(fspd->recurrence, fspd->interval, PERIOD_MONTH, &offset_date); + recurrenceSet(fspd->recurrence, fspd->interval, PERIOD_MONTH, &offset_date, fspd->weekend_adj); } return successful; } diff -U4 -r gnucash-2.2.9/src/backend/file/gnc-recurrence-xml-v2.c gnucash-2.2.9+dd/src/backend/file/gnc-recurrence-xml-v2.c --- gnucash-2.2.9/src/backend/file/gnc-recurrence-xml-v2.c 2008-01-08 01:05:37.000000000 +0000 +++ gnucash-2.2.9+dd/src/backend/file/gnc-recurrence-xml-v2.c 2008-11-27 19:32:22.000000000 +0000 @@ -46,8 +46,9 @@ #define recurrence_root "gnc:recurrence" #define recurrence_mult "recurrence:mult" #define recurrence_period_type "recurrence:period_type" #define recurrence_start "recurrence:start" +#define recurrence_weekend_adj "recurrence:weekend_adj" //TODO: I think three of these functions rightly belong in Recurrence.c. static gboolean @@ -82,12 +83,27 @@ { return dom_tree_to_guint16(node, &((Recurrence *)r)->mult); } +static gboolean +recurrence_weekend_adj_handler(xmlNodePtr node, gpointer d) +{ + WeekendAdjust wadj; + char *nodeTxt; + + nodeTxt = dom_tree_to_text(node); + g_return_val_if_fail(nodeTxt, FALSE); + wadj= recurrenceWeekendAdjustFromString(nodeTxt); + ((Recurrence *) d)->wadj = wadj; + g_free(nodeTxt); + return (wadj != -1); +} + static struct dom_tree_handler recurrence_dom_handlers[] = { { recurrence_mult, recurrence_mult_handler, 1, 0 }, { recurrence_period_type, recurrence_period_type_handler, 1, 0 }, { recurrence_start, recurrence_start_date_handler, 1, 0 }, + { recurrence_weekend_adj, recurrence_weekend_adj_handler, 0, 0 }, { NULL, NULL, 0, 0 } }; Recurrence* @@ -112,8 +128,9 @@ { xmlNodePtr n; PeriodType pt; GDate d; + WeekendAdjust wadj; n = xmlNewNode(NULL, tag); xmlSetProp(n, "version", recurrence_version_string ); xmlAddChild(n, guint_to_dom_tree(recurrence_mult, @@ -122,6 +139,9 @@ xmlAddChild(n, text_to_dom_tree(recurrence_period_type, recurrencePeriodTypeToString(pt))); d = recurrenceGetDate(r); xmlAddChild(n, gdate_to_dom_tree(recurrence_start, &d)); + wadj = recurrenceGetWeekendAdjust(r); + xmlAddChild(n, text_to_dom_tree(recurrence_weekend_adj, + recurrenceWeekendAdjustToString(wadj))); return n; } diff -U4 -r gnucash-2.2.9/src/backend/file/gnc-schedxaction-xml-v2.c gnucash-2.2.9+dd/src/backend/file/gnc-schedxaction-xml-v2.c --- gnucash-2.2.9/src/backend/file/gnc-schedxaction-xml-v2.c 2008-01-08 01:05:37.000000000 +0000 +++ gnucash-2.2.9+dd/src/backend/file/gnc-schedxaction-xml-v2.c 2008-11-10 19:38:08.000000000 +0000 @@ -393,18 +393,19 @@ recurrenceSet(r, recurrenceGetMultiplier(r), recurrenceGetPeriodType(r), - &next); + &next, + recurrenceGetWeekendAdjust(r)); } if (g_list_length(schedule) == 1 && recurrenceGetPeriodType((Recurrence*)g_list_nth_data(schedule, 0)) == PERIOD_ONCE) { char date_buf[128]; Recurrence *fixup = (Recurrence*)g_list_nth_data(schedule, 0); g_date_strftime(date_buf, 127, "%x", sx_start_date); - recurrenceSet(fixup, 1, PERIOD_ONCE, sx_start_date); + recurrenceSet(fixup, 1, PERIOD_ONCE, sx_start_date, WEEKEND_ADJ_NONE); g_debug("fixed up period=ONCE Recurrence to date [%s]", date_buf); } } diff -U4 -r gnucash-2.2.9/src/engine/gnc-budget.c gnucash-2.2.9+dd/src/engine/gnc-budget.c --- gnucash-2.2.9/src/engine/gnc-budget.c 2008-01-08 01:06:42.000000000 +0000 +++ gnucash-2.2.9+dd/src/engine/gnc-budget.c 2008-11-10 19:38:08.000000000 +0000 @@ -125,9 +125,9 @@ qof_instance_init_data (&budget->inst, GNC_ID_BUDGET, book); g_date_set_time_t(&date, time(NULL)); g_date_subtract_days(&date, g_date_get_day(&date)-1); - recurrenceSet(&budget->recurrence, 1, PERIOD_MONTH, &date); + recurrenceSet(&budget->recurrence, 1, PERIOD_MONTH, &date, WEEKEND_ADJ_NONE); gnc_budget_begin_edit(budget); gnc_budget_set_name(budget, _("Unnamed Budget")); gnc_budget_set_description(budget, ""); diff -U4 -r gnucash-2.2.9/src/engine/Recurrence.c gnucash-2.2.9+dd/src/engine/Recurrence.c --- gnucash-2.2.9/src/engine/Recurrence.c 2008-04-25 23:37:14.000000000 +0100 +++ gnucash-2.2.9+dd/src/engine/Recurrence.c 2008-11-10 22:02:57.000000000 +0000 @@ -42,10 +42,14 @@ static gchar *period_type_strings[NUM_PERIOD_TYPES] = { "once", "day", "week", "month", "end of month", "nth weekday", "last weekday", "year", }; +static gchar *weekend_adj_strings[NUM_WEEKEND_ADJS] = { + "none", "back", "forward", +}; -#define VALID_PERIOD_TYPE(pt) ((0 <= (pt)) && ((pt) < NUM_PERIOD_TYPES)) +#define VALID_PERIOD_TYPE(pt) ((0 <= (pt)) && ((pt) < NUM_PERIOD_TYPES)) +#define VALID_WEEKEND_ADJ(wadj) ((0 <= (wadj)) && ((wadj) < NUM_WEEKEND_ADJS)) PeriodType recurrenceGetPeriodType(const Recurrence *r) { @@ -63,10 +67,16 @@ { return r ? r->start : invalid_gdate; } +WeekendAdjust +recurrenceGetWeekendAdjust(const Recurrence *r) +{ + return r ? r->wadj : WEEKEND_ADJ_INVALID; +} + void -recurrenceSet(Recurrence *r, guint16 mult, PeriodType pt, const GDate *_start) +recurrenceSet(Recurrence *r, guint16 mult, PeriodType pt, const GDate *_start, WeekendAdjust wadj) { r->ptype = VALID_PERIOD_TYPE(pt) ? pt : PERIOD_MONTH; r->mult = (pt == PERIOD_ONCE) ? 0 : (mult > 0 ? mult : 1); @@ -96,8 +106,19 @@ r->ptype = PERIOD_LAST_WEEKDAY; break; default: break; } + + switch (r->ptype) { + case PERIOD_MONTH: + case PERIOD_END_OF_MONTH: + case PERIOD_YEAR: + r->wadj = wadj; + break; + default: + r->wadj = WEEKEND_ADJ_NONE; + break; + } } /* nth_weekday_compare() is a helper function for the PERIOD_{NTH,LAST}_WEEKDAY case. It returns the offset, in days, @@ -139,8 +160,9 @@ { PeriodType pt; const GDate *start; guint mult; + WeekendAdjust wadj; g_return_if_fail(r); g_return_if_fail(ref); g_return_if_fail(g_date_valid(&r->start)); @@ -157,17 +179,65 @@ /* Step 1: move FORWARD one period, passing exactly one occurrence. */ mult = r->mult; pt = r->ptype; + wadj = r->wadj; switch (pt) { case PERIOD_YEAR: mult *= 12; /* fall-through */ case PERIOD_MONTH: case PERIOD_NTH_WEEKDAY: case PERIOD_LAST_WEEKDAY: case PERIOD_END_OF_MONTH: /* Takes care of short months. */ - if ( g_date_is_last_of_month(next) || + if (r->wadj == WEEKEND_ADJ_BACK && + (pt == PERIOD_YEAR || pt == PERIOD_MONTH || pt == PERIOD_END_OF_MONTH) && + (g_date_get_weekday(next) == G_DATE_SATURDAY || g_date_get_weekday(next) == G_DATE_SUNDAY)) { + /* Allows the following Friday-based calculations to proceed if 'next' + is between Friday and the target day. */ + g_date_subtract_days(next, g_date_get_weekday(next) == G_DATE_SATURDAY ? 1 : 2); + } + if (r->wadj == WEEKEND_ADJ_BACK && + (pt == PERIOD_YEAR || pt == PERIOD_MONTH || pt == PERIOD_END_OF_MONTH) && + g_date_get_weekday(next) == G_DATE_FRIDAY) { + GDate tmp_sat; + GDate tmp_sun; + g_date_set_julian(&tmp_sat, g_date_get_julian(next)); + g_date_set_julian(&tmp_sun, g_date_get_julian(next)); + g_date_add_days(&tmp_sat, 1); + g_date_add_days(&tmp_sun, 2); + + if (pt == PERIOD_END_OF_MONTH) { + if (g_date_is_last_of_month(next) || + g_date_is_last_of_month(&tmp_sat) || + g_date_is_last_of_month(&tmp_sun)) + g_date_add_months(next, mult); + else + /* one fewer month fwd because of the occurrence in this month */ + g_date_add_months(next, mult - 1); + } else { + if (g_date_get_day(&tmp_sat) == g_date_get_day(start)) { + g_date_add_days(next, 1); + g_date_add_months(next, mult); + } else if (g_date_get_day(&tmp_sun) == g_date_get_day(start)) { + g_date_add_days(next, 2); + g_date_add_months(next, mult); + } else if (g_date_get_day(next) >= g_date_get_day(start)) { + g_date_add_months(next, mult); + } else if (g_date_is_last_of_month(next)) { + g_date_add_months(next, mult); + } else if (g_date_is_last_of_month(&tmp_sat)) { + g_date_add_days(next, 1); + g_date_add_months(next, mult); + } else if (g_date_is_last_of_month(&tmp_sun)) { + g_date_add_days(next, 2); + g_date_add_months(next, mult); + } else { + /* one fewer month fwd because of the occurrence in this month */ + g_date_add_months(next, mult - 1); + } + } + } else if ( g_date_is_last_of_month(next) || ((pt == PERIOD_MONTH || pt == PERIOD_YEAR) && g_date_get_day(next) >= g_date_get_day(start)) || ((pt == PERIOD_NTH_WEEKDAY || pt == PERIOD_LAST_WEEKDAY) && nth_weekday_compare(start, next, pt) <= 0) ) @@ -212,8 +282,25 @@ g_date_set_day(next, dim); /* last day in the month */ else g_date_set_day(next, g_date_get_day(start)); /*same day as start*/ + /* Adjust for dates on the weekend. */ + if (pt == PERIOD_YEAR || pt == PERIOD_MONTH || pt == PERIOD_END_OF_MONTH) { + if (g_date_get_weekday(next) == G_DATE_SATURDAY || g_date_get_weekday(next) == G_DATE_SUNDAY) { + switch (wadj) { + case WEEKEND_ADJ_BACK: + g_date_subtract_days(next, g_date_get_weekday(next) == G_DATE_SATURDAY ? 1 : 2); + break; + case WEEKEND_ADJ_FORWARD: + g_date_add_days(next, g_date_get_weekday(next) == G_DATE_SATURDAY ? 2 : 1); + break; + case WEEKEND_ADJ_NONE: + default: + break; + } + } + } + } break; case PERIOD_WEEK: case PERIOD_DAY: g_date_subtract_days(next, g_date_days_between(start, next) % mult); @@ -359,8 +446,25 @@ return i; return -1; } +gchar * +recurrenceWeekendAdjustToString(WeekendAdjust wadj) +{ + return VALID_WEEKEND_ADJ(wadj) ? g_strdup(weekend_adj_strings[wadj]) : NULL; +} + +WeekendAdjust +recurrenceWeekendAdjustFromString(const gchar *str) +{ + int i; + + for (i = 0; i < NUM_WEEKEND_ADJS; i++) + if (safe_strcmp(weekend_adj_strings[i], str) == 0) + return i; + return -1; +} + gboolean recurrenceListIsSemiMonthly(GList *recurrences) { if (g_list_length(recurrences) != 2) diff -U4 -r gnucash-2.2.9/src/engine/Recurrence.h gnucash-2.2.9+dd/src/engine/Recurrence.h --- gnucash-2.2.9/src/engine/Recurrence.h 2008-01-08 01:06:42.000000000 +0000 +++ gnucash-2.2.9+dd/src/engine/Recurrence.h 2008-11-10 19:38:08.000000000 +0000 @@ -56,14 +56,23 @@ NUM_PERIOD_TYPES, PERIOD_INVALID = -1, } PeriodType; +typedef enum { + WEEKEND_ADJ_NONE, + WEEKEND_ADJ_BACK, /* Previous weekday */ + WEEKEND_ADJ_FORWARD, /* Next weekday */ + NUM_WEEKEND_ADJS, + WEEKEND_ADJ_INVALID = -1, +} WeekendAdjust; + /* Recurrences represent both the phase and period of a recurring event. */ typedef struct { - GDate start; /* First date in the recurrence; specifies phase. */ - PeriodType ptype; /* see PeriodType enum */ - guint16 mult; /* a period multiplier */ + GDate start; /* First date in the recurrence; specifies phase. */ + PeriodType ptype; /* see PeriodType enum */ + guint16 mult; /* a period multiplier */ + WeekendAdjust wadj; /* see WeekendAdjust enum */ } Recurrence; /* recurrenceSet() will enforce internal consistency by overriding @@ -89,14 +98,15 @@ date's day-of-week is used. */ void recurrenceSet(Recurrence *r, guint16 mult, PeriodType pt, - const GDate *date); + const GDate *date, WeekendAdjust wadj); /* get the fields */ PeriodType recurrenceGetPeriodType(const Recurrence *r); guint recurrenceGetMultiplier(const Recurrence *r); GDate recurrenceGetDate(const Recurrence *r); +WeekendAdjust recurrenceGetWeekendAdjust(const Recurrence *r); /* Get the occurence immediately after refDate. * * This function has strict and precise post-conditions: @@ -134,11 +144,13 @@ /** @return the earliest of the next occurances -- a "composite" recurrence **/ void recurrenceListNextInstance(const GList *r, const GDate *refDate, GDate *nextDate); -/* These two functions are only for xml storage, not user presentation. */ +/* These four functions are only for xml storage, not user presentation. */ gchar *recurrencePeriodTypeToString(PeriodType pt); PeriodType recurrencePeriodTypeFromString(const gchar *str); +gchar *recurrenceWeekendAdjustToString(WeekendAdjust wadj); +WeekendAdjust recurrenceWeekendAdjustFromString(const gchar *str); /* For debugging. Caller owns the returned string. Not intl. */ gchar *recurrenceToString(const Recurrence *r); gchar *recurrenceListToString(const GList *rlist); diff -U4 -r gnucash-2.2.9/src/engine/test/test-recurrence.c gnucash-2.2.9+dd/src/engine/test/test-recurrence.c --- gnucash-2.2.9/src/engine/test/test-recurrence.c 2008-01-08 01:06:41.000000000 +0000 +++ gnucash-2.2.9+dd/src/engine/test/test-recurrence.c 2008-11-10 19:38:08.000000000 +0000 @@ -133,29 +133,33 @@ GDate d_start, d_start_reg; GDate d_ref, d_next; guint16 mult, mult_reg; PeriodType pt, pt_reg; + WeekendAdj wadj, wadj_reg; gint32 j1, j2; gint i_ref; for (pt = PERIOD_ONCE; pt < NUM_PERIOD_TYPES; pt++) { - for (j1 = JULIAN_START; j1 < JULIAN_START + NUM_DATES_TO_TEST; j1++) { - g_date_set_julian(&d_start, j1); - for (i_ref = 0; i_ref < NUM_DATES_TO_TEST_REF; i_ref++) { - j2 = (guint32) get_random_int_in_range(1, 1 << 19); - g_date_set_julian(&d_ref, j2); - - for (mult = 0; mult < NUM_MULT_TO_TEST; mult++) { - recurrenceSet(&r, mult, pt, &d_start); - pt_reg = recurrenceGetPeriodType(&r); - d_start_reg = recurrenceGetDate(&r); - mult_reg = recurrenceGetMultiplier(&r); - - recurrenceNextInstance(&r, &d_ref, &d_next); - check_valid(&d_next, &d_ref, &d_start_reg, - mult_reg, pt_reg); + for (wadj = WEEKEND_ADJ_NONE; wadj < NUM_WEEKEND_ADJS; wadj++) { + for (j1 = JULIAN_START; j1 < JULIAN_START + NUM_DATES_TO_TEST; j1++) { + g_date_set_julian(&d_start, j1); + for (i_ref = 0; i_ref < NUM_DATES_TO_TEST_REF; i_ref++) { + j2 = (guint32) get_random_int_in_range(1, 1 << 19); + g_date_set_julian(&d_ref, j2); + + for (mult = 0; mult < NUM_MULT_TO_TEST; mult++) { + recurrenceSet(&r, mult, pt, &d_start, wadj); + pt_reg = recurrenceGetPeriodType(&r); + d_start_reg = recurrenceGetDate(&r); + mult_reg = recurrenceGetMultiplier(&r); + wadj_reg = recurrenceGetWeekendAdjust(&r); + + recurrenceNextInstance(&r, &d_ref, &d_next); + check_valid(&d_next, &d_ref, &d_start_reg, + mult_reg, pt_reg, wadj_reg); - } + } + } } } } } diff -U4 -r gnucash-2.2.9/src/engine/test-core/test-engine-stuff.c gnucash-2.2.9+dd/src/engine/test-core/test-engine-stuff.c --- gnucash-2.2.9/src/engine/test-core/test-engine-stuff.c 2008-01-08 01:06:40.000000000 +0000 +++ gnucash-2.2.9+dd/src/engine/test-core/test-engine-stuff.c 2008-11-10 19:38:08.000000000 +0000 @@ -2147,17 +2147,17 @@ static Recurrence* daily_freq(GDate* start, int multiplier) { Recurrence *r = g_new0(Recurrence, 1); - recurrenceSet(r, multiplier, PERIOD_DAY, start); + recurrenceSet(r, multiplier, PERIOD_DAY, start, WEEKEND_ADJ_NONE); return r; } static Recurrence* once_freq(GDate *when) { Recurrence *r = g_new0(Recurrence, 1); - recurrenceSet(r, 1, PERIOD_ONCE, when); + recurrenceSet(r, 1, PERIOD_ONCE, when, WEEKEND_ADJ_NONE); return r; } static SchedXaction* diff -U4 -r gnucash-2.2.9/src/gnome/dialog-sx-from-trans.c gnucash-2.2.9+dd/src/gnome/dialog-sx-from-trans.c --- gnucash-2.2.9/src/gnome/dialog-sx-from-trans.c 2008-01-08 01:06:36.000000000 +0000 +++ gnucash-2.2.9+dd/src/gnome/dialog-sx-from-trans.c 2008-11-10 19:38:08.000000000 +0000 @@ -301,17 +301,17 @@ switch (index) { case FREQ_DAILY: { Recurrence *r = g_new0(Recurrence, 1); - recurrenceSet(r, 1, PERIOD_DAY, date); + recurrenceSet(r, 1, PERIOD_DAY, date, WEEKEND_ADJ_NONE); *recurrences = g_list_append(*recurrences, r); } break; case FREQ_WEEKLY: case FREQ_BIWEEKLY: { Recurrence *r = g_new0(Recurrence, 1); int mult = (index == FREQ_BIWEEKLY ? 2 : 1); - recurrenceSet(r, mult, PERIOD_WEEK, date); + recurrenceSet(r, mult, PERIOD_WEEK, date, WEEKEND_ADJ_NONE); *recurrences = g_list_append(*recurrences, r); } break; case FREQ_MONTHLY: @@ -322,9 +322,9 @@ ? 1 : (index == FREQ_QUARTERLY ? 3 : 12)); - recurrenceSet(r, mult, PERIOD_MONTH, date); + recurrenceSet(r, mult, PERIOD_MONTH, date, recurrenceGetWeekendAdjust(r)); *recurrences = g_list_append(*recurrences, r); } break; default: diff -U4 -r gnucash-2.2.9/src/gnome/druid-acct-period.c gnucash-2.2.9+dd/src/gnome/druid-acct-period.c --- gnucash-2.2.9/src/gnome/druid-acct-period.c 2008-07-22 23:43:12.000000000 +0100 +++ gnucash-2.2.9+dd/src/gnome/druid-acct-period.c 2008-11-10 19:38:08.000000000 +0000 @@ -543,9 +543,9 @@ g_date_add_years (&info->closing_date, 1); { Recurrence *r = g_new0(Recurrence, 1); - recurrenceSet(r, 1, PERIOD_MONTH, &info->closing_date); + recurrenceSet(r, 1, PERIOD_MONTH, &info->closing_date, WEEKEND_ADJ_NONE); info->period = NULL; info->period = g_list_append(info->period, r); } diff -U4 -r gnucash-2.2.9/src/gnome/druid-loan.c gnucash-2.2.9+dd/src/gnome/druid-loan.c --- gnucash-2.2.9/src/gnome/druid-loan.c 2008-07-22 23:43:11.000000000 +0100 +++ gnucash-2.2.9+dd/src/gnome/druid-loan.c 2008-11-10 19:38:08.000000000 +0000 @@ -803,9 +803,9 @@ ldd->ld.loan_schedule= NULL; ldd->ld.repayment_schedule = NULL; { Recurrence *r = g_new0(Recurrence, 1); - recurrenceSet(r, 1, PERIOD_MONTH, ldd->ld.startDate); + recurrenceSet(r, 1, PERIOD_MONTH, ldd->ld.startDate, WEEKEND_ADJ_NONE); ldd->ld.repayment_schedule = g_list_append(ldd->ld.repayment_schedule, r); } ldd->ld.repMemo = g_strdup( _("Loan") ); @@ -1661,9 +1661,9 @@ rod = ldd->ld.repayOpts[ ldd->currentIdx ]; if ( rod->schedule == NULL ) { Recurrence *r = g_new0(Recurrence, 1); - recurrenceSet(r, 1, PERIOD_MONTH, ldd->ld.startDate); + recurrenceSet(r, 1, PERIOD_MONTH, ldd->ld.startDate, WEEKEND_ADJ_NONE); rod->schedule = g_list_append(rod->schedule, r); } if ( rod->startDate == NULL ) { rod->startDate = g_date_new(); diff -U4 -r gnucash-2.2.9/src/gnome/glade/sched-xact.glade gnucash-2.2.9+dd/src/gnome/glade/sched-xact.glade --- gnucash-2.2.9/src/gnome/glade/sched-xact.glade 2008-01-08 01:06:30.000000000 +0000 +++ gnucash-2.2.9+dd/src/gnome/glade/sched-xact.glade 2008-11-10 19:38:08.000000000 +0000 @@ -1891,8 +1891,49 @@ True True + + + + True + except on weekends: + False + False + GTK_JUSTIFY_RIGHT + False + False + 1 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 2 + False + True + + + + + + True + No change +Use previous weekday +Use next weekday + False + True + + + 0 + False + False + + 3 False @@ -1981,8 +2022,49 @@ False False + + + + True + except on weekends: + False + False + GTK_JUSTIFY_RIGHT + False + False + 1 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 2 + False + True + + + + + + True + No change +Use previous weekday +Use next weekday + False + True + + + 0 + False + False + + 3 False @@ -2201,18 +2283,59 @@ False False + + + + True + except on weekends: + False + False + GTK_JUSTIFY_RIGHT + False + False + 1 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 2 + False + True + + + + + + True + No change +Use previous weekday +Use next weekday + False + True + + + 0 + False + False + + - 5 + 3 False True - 5 + 3 True True diff -U4 -r gnucash-2.2.9/src/gnome/gnc-plugin-page-budget.c gnucash-2.2.9+dd/src/gnome/gnc-plugin-page-budget.c --- gnucash-2.2.9/src/gnome/gnc-plugin-page-budget.c 2008-01-08 01:06:36.000000000 +0000 +++ gnucash-2.2.9+dd/src/gnome/gnc-plugin-page-budget.c 2008-11-10 19:38:08.000000000 +0000 @@ -295,9 +295,9 @@ priv->fd.show_hidden = FALSE; priv->fd.show_zero_total = TRUE; priv->sigFigs = 1; - recurrenceSet(&priv->r, 1, PERIOD_MONTH, NULL); + recurrenceSet(&priv->r, 1, PERIOD_MONTH, NULL, WEEKEND_ADJ_NONE); LEAVE("page %p, priv %p, action group %p", plugin_page, priv, action_group); } @@ -935,9 +935,10 @@ r = gnc_budget_get_recurrence(priv->budget); gnc_date_edit_get_gdate(GNC_DATE_EDIT(gde), &date); recurrenceSet(&priv->r, recurrenceGetMultiplier(r), - recurrenceGetPeriodType(r), &date); + recurrenceGetPeriodType(r), &date, + recurrenceGetWeekendAdjust(r)); priv->sigFigs = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(dtr)); gtk_tree_selection_selected_foreach(sel, estimate_budget_helper, page); diff -U4 -r gnucash-2.2.9/src/gnome/gnc-plugin-page-sx-list.c gnucash-2.2.9+dd/src/gnome/gnc-plugin-page-sx-list.c --- gnucash-2.2.9/src/gnome/gnc-plugin-page-sx-list.c 2008-01-08 01:06:36.000000000 +0000 +++ gnucash-2.2.9+dd/src/gnome/gnc-plugin-page-sx-list.c 2008-11-10 19:38:08.000000000 +0000 @@ -475,9 +475,9 @@ GList *schedule; g_date_clear(&now, 1); g_date_set_time_t(&now, time(NULL)); - recurrenceSet(r, 1, PERIOD_MONTH, &now); + recurrenceSet(r, 1, PERIOD_MONTH, &now, WEEKEND_ADJ_NONE); schedule = gnc_sx_get_schedule(new_sx); schedule = g_list_append(schedule, r); gnc_sx_set_schedule(new_sx, schedule); } diff -U4 -r gnucash-2.2.9/src/gnome-utils/gnc-frequency.c gnucash-2.2.9+dd/src/gnome-utils/gnc-frequency.c --- gnucash-2.2.9/src/gnome-utils/gnc-frequency.c 2008-01-08 01:06:26.000000000 +0000 +++ gnucash-2.2.9+dd/src/gnome-utils/gnc-frequency.c 2008-11-10 19:38:08.000000000 +0000 @@ -156,13 +156,16 @@ static const struct comboBoxTuple { char *name; void (*fn)(); } comboBoxes[] = { - { "freq_combobox", freq_combo_changed }, - { "semimonthly_first", semimonthly_sel_changed }, - { "semimonthly_second", semimonthly_sel_changed }, - { "monthly_day", monthly_sel_changed }, - { NULL, NULL } + { "freq_combobox", freq_combo_changed }, + { "semimonthly_first", semimonthly_sel_changed }, + { "semimonthly_first_weekend", semimonthly_sel_changed }, + { "semimonthly_second", semimonthly_sel_changed }, + { "semimonthly_second_weekend", semimonthly_sel_changed }, + { "monthly_day", monthly_sel_changed }, + { "monthly_weekend", monthly_sel_changed }, + { NULL, NULL } }; static const struct spinvalTuple { char *name; @@ -408,10 +411,14 @@ gtk_spin_button_set_value(GTK_SPIN_BUTTON(multiplier_spin), recurrenceGetMultiplier(first)); dom_combobox = glade_xml_get_widget(gf->gxml, "semimonthly_first"); gtk_combo_box_set_active(GTK_COMBO_BOX(dom_combobox), _get_monthly_combobox_index(first)); + dom_combobox = glade_xml_get_widget(gf->gxml, "semimonthly_first_weekend"); + gtk_combo_box_set_active(GTK_COMBO_BOX(dom_combobox), recurrenceGetWeekendAdjust(first)); dom_combobox = glade_xml_get_widget(gf->gxml, "semimonthly_second"); gtk_combo_box_set_active(GTK_COMBO_BOX(dom_combobox), _get_monthly_combobox_index(second)); + dom_combobox = glade_xml_get_widget(gf->gxml, "semimonthly_second_weekend"); + gtk_combo_box_set_active(GTK_COMBO_BOX(dom_combobox), recurrenceGetWeekendAdjust(second)); gtk_notebook_set_current_page(gf->nb, PAGE_SEMI_MONTHLY); gtk_combo_box_set_active(gf->freqComboBox, PAGE_SEMI_MONTHLY); } @@ -460,9 +467,9 @@ case PERIOD_MONTH: case PERIOD_YEAR: case PERIOD_LAST_WEEKDAY: { guint multiplier; - GtkWidget *multipler_spin, *day_of_month; + GtkWidget *multipler_spin, *day_of_month, *weekend_mode; multipler_spin = glade_xml_get_widget(gf->gxml, "monthly_spin"); multiplier = recurrenceGetMultiplier(r); if (recurrenceGetPeriodType(r) == PERIOD_YEAR) @@ -470,8 +477,10 @@ gtk_spin_button_set_value(GTK_SPIN_BUTTON(multipler_spin), multiplier); day_of_month = glade_xml_get_widget(gf->gxml, "monthly_day"); gtk_combo_box_set_active(GTK_COMBO_BOX(day_of_month), _get_monthly_combobox_index(r)); + weekend_mode = glade_xml_get_widget(gf->gxml, "monthly_weekend"); + gtk_combo_box_set_active(GTK_COMBO_BOX(weekend_mode), recurrenceGetWeekendAdjust(r)); gtk_notebook_set_current_page(gf->nb, PAGE_MONTHLY); gtk_combo_box_set_active(gf->freqComboBox, PAGE_MONTHLY); } break; @@ -497,14 +506,16 @@ return gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(multiplier_spin)); } static Recurrence* -_get_day_of_month_recurrence(GncFrequency *gf, GDate *start_date, int multiplier, char *combo_name) +_get_day_of_month_recurrence(GncFrequency *gf, GDate *start_date, int multiplier, char *combo_name, char *combo_weekend_name) { int last_day_of_month_option_index = 31; Recurrence *r; GtkWidget *day_of_month_combo = glade_xml_get_widget(gf->gxml, combo_name); int day_of_month_index = gtk_combo_box_get_active(GTK_COMBO_BOX(day_of_month_combo)); + GtkWidget *weekend_adjust_combo = glade_xml_get_widget(gf->gxml, combo_weekend_name); + int weekend_adjust = gtk_combo_box_get_active(GTK_COMBO_BOX(weekend_adjust_combo)); r = g_new0(Recurrence, 1); if (day_of_month_index > LAST_DAY_OF_MONTH_OPTION_INDEX) { @@ -513,14 +524,14 @@ // increment until we align on the DOW, but stay inside the month g_date_set_day(day_of_week_date, 1); while (g_date_get_weekday(day_of_week_date) != selected_day_of_week) g_date_add_days(day_of_week_date, 1); - recurrenceSet(r, multiplier, PERIOD_LAST_WEEKDAY, day_of_week_date); + recurrenceSet(r, multiplier, PERIOD_LAST_WEEKDAY, day_of_week_date, weekend_adjust); } else if (day_of_month_index == LAST_DAY_OF_MONTH_OPTION_INDEX) { GDate *day_of_month = g_date_new_julian(g_date_get_julian(start_date)); - recurrenceSet(r, multiplier, PERIOD_END_OF_MONTH, day_of_month); + recurrenceSet(r, multiplier, PERIOD_END_OF_MONTH, day_of_month, weekend_adjust); } else { int allowable_date = -1; @@ -528,9 +539,9 @@ allowable_date = MIN(day_of_month_index + 1, g_date_get_days_in_month(g_date_get_month(day_of_month), g_date_get_year(day_of_month))); g_date_set_day(day_of_month, allowable_date); - recurrenceSet(r, multiplier, PERIOD_MONTH, day_of_month); + recurrenceSet(r, multiplier, PERIOD_MONTH, day_of_month, weekend_adjust); } return r; } @@ -555,15 +566,15 @@ // empty-recurrence list ~~ none. } break; case PAGE_ONCE: { Recurrence *r = g_new0(Recurrence, 1); - recurrenceSet(r, 1, PERIOD_ONCE, &start_date); + recurrenceSet(r, 1, PERIOD_ONCE, &start_date, WEEKEND_ADJ_NONE); *recurrences = g_list_append(*recurrences, r); } break; case PAGE_DAILY: { gint multiplier = _get_multiplier_from_widget(gf, "daily_spin"); Recurrence *r = g_new0(Recurrence, 1); - recurrenceSet(r, multiplier, PERIOD_DAY, &start_date); + recurrenceSet(r, multiplier, PERIOD_DAY, &start_date, WEEKEND_ADJ_NONE); *recurrences = g_list_append(*recurrences, r); } break; case PAGE_WEEKLY: { int multiplier = _get_multiplier_from_widget(gf, "weekly_spin"); @@ -583,21 +594,21 @@ while ((g_date_get_weekday(day_of_week_aligned_date) % 7) != checkbox_idx) g_date_add_days(day_of_week_aligned_date, 1); r = g_new0(Recurrence, 1); - recurrenceSet(r, multiplier, PERIOD_WEEK, day_of_week_aligned_date); + recurrenceSet(r, multiplier, PERIOD_WEEK, day_of_week_aligned_date, WEEKEND_ADJ_NONE); *recurrences = g_list_append(*recurrences, r); } } break; case PAGE_SEMI_MONTHLY: { int multiplier = _get_multiplier_from_widget(gf, "semimonthly_spin"); - *recurrences = g_list_append(*recurrences, _get_day_of_month_recurrence(gf, &start_date, multiplier, "semimonthly_first")); - *recurrences = g_list_append(*recurrences, _get_day_of_month_recurrence(gf, &start_date, multiplier, "semimonthly_second")); + *recurrences = g_list_append(*recurrences, _get_day_of_month_recurrence(gf, &start_date, multiplier, "semimonthly_first", "semimonthly_first_weekend")); + *recurrences = g_list_append(*recurrences, _get_day_of_month_recurrence(gf, &start_date, multiplier, "semimonthly_second", "semimonthly_second_weekend")); } break; case PAGE_MONTHLY: { int multiplier = _get_multiplier_from_widget(gf, "monthly_spin"); - Recurrence *r = _get_day_of_month_recurrence(gf, &start_date, multiplier, "monthly_day"); + Recurrence *r = _get_day_of_month_recurrence(gf, &start_date, multiplier, "monthly_day", "monthly_weekend"); *recurrences = g_list_append(*recurrences, r); } break; default: g_error("unknown page index [%d]", page_index); diff -U4 -r gnucash-2.2.9/src/gnome-utils/gnc-recurrence.c gnucash-2.2.9+dd/src/gnome-utils/gnc-recurrence.c --- gnucash-2.2.9/src/gnome-utils/gnc-recurrence.c 2008-01-08 01:06:22.000000000 +0000 +++ gnucash-2.2.9+dd/src/gnome-utils/gnc-recurrence.c 2008-11-10 19:38:08.000000000 +0000 @@ -165,9 +165,9 @@ gnc_recurrence_init( GncRecurrence *gr ) { GtkVBox *vb; - recurrenceSet(&gr->recurrence, 1, PERIOD_MONTH, NULL); + recurrenceSet(&gr->recurrence, 1, PERIOD_MONTH, NULL, WEEKEND_ADJ_NONE); gr->xml = gnc_glade_xml_new("budget.glade", "RecurrenceEntryVBox"); vb = GTK_VBOX(glade_xml_get_widget(gr->xml, "RecurrenceEntryVBox")); gr->gde_start = GNOME_DATE_EDIT(glade_xml_get_widget(gr->xml, @@ -285,9 +285,9 @@ pt = PERIOD_INVALID; } - recurrenceSet(&gr->recurrence, mult, pt, &start); + recurrenceSet(&gr->recurrence, mult, pt, &start, WEEKEND_ADJ_NONE); return &gr->recurrence; } static void