mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
Add a "compact" toString of a Recurrence list; this is a clone of the FreqSpec method that is used by the SX List as a summary of the period of the SX.
git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@15630 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
parent
20675026fc
commit
c123a68e28
@ -137,8 +137,9 @@ TODO
|
|||||||
- [ ] gnc_sxed_check_consistent
|
- [ ] gnc_sxed_check_consistent
|
||||||
- [x] gnc_sxed_update_cal
|
- [x] gnc_sxed_update_cal
|
||||||
- [x] gnc_sxed_save_sx
|
- [x] gnc_sxed_save_sx
|
||||||
- gnc-instances
|
- sx list
|
||||||
- [ ] More compact recurrenceListToString(...).
|
- [ ] recurrence_cmp(...)
|
||||||
|
- [x] More compact recurrenceListToString(...).
|
||||||
- [ ] remove FreqSpec code
|
- [ ] remove FreqSpec code
|
||||||
- [ ] SX code
|
- [ ] SX code
|
||||||
- [ ] src/gnome/druid-acct-period.c
|
- [ ] src/gnome/druid-acct-period.c
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
|
#include <glib/gi18n.h>
|
||||||
#include "glib-compat.h"
|
#include "glib-compat.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "Recurrence.h"
|
#include "Recurrence.h"
|
||||||
@ -30,7 +31,10 @@
|
|||||||
#include "gnc-gdate-utils.h"
|
#include "gnc-gdate-utils.h"
|
||||||
#include "Account.h"
|
#include "Account.h"
|
||||||
|
|
||||||
static QofLogModule log_module = GNC_MOD_ENGINE;
|
#define LOG_MOD "gnc.engine.recurrence"
|
||||||
|
static QofLogModule log_module = LOG_MOD;
|
||||||
|
#undef G_LOG_DOMAIN
|
||||||
|
#define G_LOG_DOMAIN LOG_MOD
|
||||||
|
|
||||||
static GDate invalid_gdate;
|
static GDate invalid_gdate;
|
||||||
|
|
||||||
@ -318,12 +322,12 @@ recurrenceListToString(const GList *r)
|
|||||||
|
|
||||||
str = g_string_new("");
|
str = g_string_new("");
|
||||||
for(iter = r; iter; iter = iter->next){
|
for(iter = r; iter; iter = iter->next){
|
||||||
|
if (iter != r)
|
||||||
|
g_string_append(str, " + ");
|
||||||
s = recurrenceToString((Recurrence *)iter->data);
|
s = recurrenceToString((Recurrence *)iter->data);
|
||||||
g_string_append(str, s);
|
g_string_append(str, s);
|
||||||
g_string_append(str, " + ");
|
|
||||||
g_free(s);
|
g_free(s);
|
||||||
}
|
}
|
||||||
g_string_truncate(str, str->len - 3); /* kill the last " + " */
|
|
||||||
return g_string_free(str, FALSE);
|
return g_string_free(str, FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -343,3 +347,233 @@ recurrencePeriodTypeFromString(const gchar *str)
|
|||||||
return i;
|
return i;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
recurrenceListIsSemiMonthly(GList *recurrences)
|
||||||
|
{
|
||||||
|
if (g_list_length(recurrences) != 2)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
// should be a "semi-monthly":
|
||||||
|
{
|
||||||
|
Recurrence *first = (Recurrence*)g_list_nth_data(recurrences, 0);
|
||||||
|
Recurrence *second = (Recurrence*)g_list_nth_data(recurrences, 1);
|
||||||
|
PeriodType first_period, second_period;
|
||||||
|
first_period = recurrenceGetPeriodType(first);
|
||||||
|
second_period = recurrenceGetPeriodType(second);
|
||||||
|
|
||||||
|
if (!((first_period == PERIOD_MONTH
|
||||||
|
|| first_period == PERIOD_END_OF_MONTH
|
||||||
|
|| first_period == PERIOD_LAST_WEEKDAY)
|
||||||
|
&& (second_period == PERIOD_MONTH
|
||||||
|
|| second_period == PERIOD_END_OF_MONTH
|
||||||
|
|| second_period == PERIOD_LAST_WEEKDAY)))
|
||||||
|
{
|
||||||
|
/*g_error("unknown 2-recurrence composite with period_types first [%d] second [%d]",
|
||||||
|
first_period, second_periodD);*/
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
recurrenceListIsWeeklyMultiple(GList *recurrences)
|
||||||
|
{
|
||||||
|
GList *r_iter;
|
||||||
|
|
||||||
|
for (r_iter = recurrences; r_iter != NULL; r_iter = r_iter->next)
|
||||||
|
{
|
||||||
|
Recurrence *r = (Recurrence*)r_iter->data;
|
||||||
|
if (recurrenceGetPeriodType(r) != PERIOD_WEEK)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Localized DOW abbrev.
|
||||||
|
* @fixme - ripped from gnc-dense-cal.c; there can be only one. :p
|
||||||
|
* @param dow struct tm semantics: 0=sunday .. 6=saturday
|
||||||
|
**/
|
||||||
|
static void
|
||||||
|
_dow_abbrev(gchar *buf, int buf_len, int dow)
|
||||||
|
{
|
||||||
|
struct tm my_tm;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
memset(buf, 0, buf_len);
|
||||||
|
memset(&my_tm, 0, sizeof(struct tm));
|
||||||
|
my_tm.tm_wday = dow;
|
||||||
|
i = strftime(buf, buf_len - 1, "%a", &my_tm);
|
||||||
|
buf[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_weekly_list_to_compact_string(GList *rs, GString *buf)
|
||||||
|
{
|
||||||
|
int dow_idx;
|
||||||
|
char dow_present_bits = 0;
|
||||||
|
int multiplier = -1;
|
||||||
|
for (; rs != NULL; rs = rs->next)
|
||||||
|
{
|
||||||
|
Recurrence *r = (Recurrence*)rs->data;
|
||||||
|
GDate date = recurrenceGetDate(r);
|
||||||
|
GDateWeekday dow = g_date_get_weekday(&date);
|
||||||
|
if (dow == G_DATE_BAD_WEEKDAY)
|
||||||
|
{
|
||||||
|
g_critical("bad weekday pretty-printing recurrence");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
dow_present_bits |= (1 << (dow % 7));
|
||||||
|
// broken, @fixme.
|
||||||
|
multiplier = recurrenceGetMultiplier(r);
|
||||||
|
}
|
||||||
|
g_string_printf(buf, _("Weekly"));
|
||||||
|
if (multiplier > 1)
|
||||||
|
{
|
||||||
|
/* translators: %u is the recurrence multipler. */
|
||||||
|
g_string_append_printf(buf, _(" (x%u)"), multiplier);
|
||||||
|
}
|
||||||
|
g_string_append_printf(buf, ": ");
|
||||||
|
|
||||||
|
// @@fixme: this is only Sunday-started weeks. :/
|
||||||
|
for (dow_idx = 0; dow_idx < 7; dow_idx++)
|
||||||
|
{
|
||||||
|
if ((dow_present_bits & (1 << dow_idx)) != 0)
|
||||||
|
{
|
||||||
|
gchar dbuf[10];
|
||||||
|
_dow_abbrev(dbuf, 10, dow_idx);
|
||||||
|
g_string_append_printf(buf, "%c", dbuf[0]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_string_append_printf(buf, "-");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_monthly_append_when(Recurrence *r, GString *buf)
|
||||||
|
{
|
||||||
|
GDate date = recurrenceGetDate(r);
|
||||||
|
if (recurrenceGetPeriodType(r) == PERIOD_LAST_WEEKDAY)
|
||||||
|
{
|
||||||
|
gint abbrev_day_name_bufsize = 10;
|
||||||
|
gchar day_name_buf[abbrev_day_name_bufsize];
|
||||||
|
|
||||||
|
_dow_abbrev(day_name_buf, abbrev_day_name_bufsize, g_date_get_weekday(&date) % 7);
|
||||||
|
|
||||||
|
/* translators: %s is an already-localized form of the day of the week. */
|
||||||
|
g_string_append_printf(buf, _("last %s"), day_name_buf);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* translators: %u is the day of month */
|
||||||
|
g_string_append_printf(buf, "%u", g_date_get_day(&date));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gchar*
|
||||||
|
recurrenceListToCompactString(GList *rs)
|
||||||
|
{
|
||||||
|
GString *buf = g_string_sized_new(16);
|
||||||
|
|
||||||
|
if (g_list_length(rs) == 0)
|
||||||
|
{
|
||||||
|
g_string_printf(buf, _("None"));
|
||||||
|
goto rtn;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_list_length(rs) > 1)
|
||||||
|
{
|
||||||
|
if (recurrenceListIsWeeklyMultiple(rs))
|
||||||
|
{
|
||||||
|
_weekly_list_to_compact_string(rs, buf);
|
||||||
|
}
|
||||||
|
else if (recurrenceListIsSemiMonthly(rs))
|
||||||
|
{
|
||||||
|
Recurrence *first, *second;
|
||||||
|
first = (Recurrence*)g_list_nth_data(rs, 0);
|
||||||
|
second = (Recurrence*)g_list_nth_data(rs, 1);
|
||||||
|
if (recurrenceGetMultiplier(first) != recurrenceGetMultiplier(second))
|
||||||
|
{
|
||||||
|
g_warning("lying about non-equal semi-monthly recurrence multiplier: %d vs. %d",
|
||||||
|
recurrenceGetMultiplier(first), recurrenceGetMultiplier(second));
|
||||||
|
}
|
||||||
|
|
||||||
|
g_string_printf(buf, _("Semi-monthly "));
|
||||||
|
if (recurrenceGetMultiplier(first) > 1)
|
||||||
|
{
|
||||||
|
/* translators: %u is the recurrence multiplier */
|
||||||
|
g_string_append_printf(buf, _(" (x%u)"), recurrenceGetMultiplier(first));
|
||||||
|
}
|
||||||
|
g_string_append_printf(buf, _(": "));
|
||||||
|
_monthly_append_when(first, buf);
|
||||||
|
g_string_append_printf(buf, ", ");
|
||||||
|
_monthly_append_when(second, buf);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* translators: %d is the number of Recurrences in the list. */
|
||||||
|
g_string_printf(buf, _("Unknown, %d-size list."), g_list_length(rs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Recurrence *r = (Recurrence*)g_list_nth_data(rs, 0);
|
||||||
|
guint multiplier = recurrenceGetMultiplier(r);
|
||||||
|
GDate date = recurrenceGetDate(r);
|
||||||
|
|
||||||
|
switch (recurrenceGetPeriodType(r))
|
||||||
|
{
|
||||||
|
case PERIOD_ONCE: {
|
||||||
|
g_string_printf(buf, _("Once"));
|
||||||
|
} break;
|
||||||
|
case PERIOD_DAY: {
|
||||||
|
g_string_printf(buf, _("Daily"));
|
||||||
|
if (multiplier > 1)
|
||||||
|
{
|
||||||
|
/* translators: %u is the number of intervals */
|
||||||
|
g_string_append_printf(buf, _(" (x%u)"), multiplier);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case PERIOD_WEEK: {
|
||||||
|
_weekly_list_to_compact_string(rs, buf);
|
||||||
|
} break;
|
||||||
|
case PERIOD_MONTH:
|
||||||
|
case PERIOD_END_OF_MONTH:
|
||||||
|
case PERIOD_LAST_WEEKDAY: {
|
||||||
|
g_string_printf(buf, _("Monthly"));
|
||||||
|
if (multiplier > 1)
|
||||||
|
{
|
||||||
|
/* translators: %u is the recurrence multipler. */
|
||||||
|
g_string_append_printf(buf, _(" (x%u)"), multiplier);
|
||||||
|
}
|
||||||
|
g_string_append_printf(buf, _(": "));
|
||||||
|
_monthly_append_when(r, buf);
|
||||||
|
} break;
|
||||||
|
case PERIOD_NTH_WEEKDAY: {
|
||||||
|
g_warning("nth weekday unhandled");
|
||||||
|
g_string_printf(buf, "@fixme: nth weekday");
|
||||||
|
} break;
|
||||||
|
case PERIOD_YEAR: {
|
||||||
|
g_string_printf(buf, _("Yearly"));
|
||||||
|
if (multiplier > 1)
|
||||||
|
{
|
||||||
|
/* translators: %u is the recurrence multiplier. */
|
||||||
|
g_string_append_printf(buf, _(" (x%u)"), multiplier);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
default:
|
||||||
|
g_error("unknown Recurrnce period %d", recurrenceGetPeriodType(r));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rtn:
|
||||||
|
return g_string_free(buf, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -128,12 +128,14 @@ void recurrenceNthInstance(const Recurrence *r, guint n, GDate *date);
|
|||||||
of the nth instance of the recurrence. Also zero-based. */
|
of the nth instance of the recurrence. Also zero-based. */
|
||||||
time_t recurrenceGetPeriodTime(const Recurrence *r, guint n, gboolean end);
|
time_t recurrenceGetPeriodTime(const Recurrence *r, guint n, gboolean end);
|
||||||
|
|
||||||
/* Get the amount that an Account's value changed between the
|
/**
|
||||||
beginning and end of the nth instance of the recurrence. */
|
* @return the amount that an Account's value changed between the beginning
|
||||||
|
* and end of the nth instance of the Recurrence.
|
||||||
|
**/
|
||||||
gnc_numeric recurrenceGetAccountPeriodValue(const Recurrence *r,
|
gnc_numeric recurrenceGetAccountPeriodValue(const Recurrence *r,
|
||||||
Account *acct, guint n);
|
Account *acct, guint n);
|
||||||
|
|
||||||
/* Get the earliest of the next occurances -- a "composite" recurrence */
|
/** @return the earliest of the next occurances -- a "composite" recurrence **/
|
||||||
void recurrenceListNextInstance(const GList *r, const GDate *refDate,
|
void recurrenceListNextInstance(const GList *r, const GDate *refDate,
|
||||||
GDate *nextDate);
|
GDate *nextDate);
|
||||||
|
|
||||||
@ -145,4 +147,23 @@ PeriodType recurrencePeriodTypeFromString(const gchar *str);
|
|||||||
gchar *recurrenceToString(const Recurrence *r);
|
gchar *recurrenceToString(const Recurrence *r);
|
||||||
gchar *recurrenceListToString(const GList *rlist);
|
gchar *recurrenceListToString(const GList *rlist);
|
||||||
|
|
||||||
|
/** @return True if the recurrence list is a common "semi-monthly" recurrence. **/
|
||||||
|
gboolean recurrenceListIsSemiMonthly(GList *recurrences);
|
||||||
|
/** @return True if the recurrence list is a common "weekly" recurrence. **/
|
||||||
|
gboolean recurrenceListIsWeeklyMultiple(GList *recurrences);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pretty-print an intentionally-short summary of the period of a (GList of)
|
||||||
|
* Recurrences, as might be commonly-created by the GncFrequency widget. In
|
||||||
|
* particular, this routine expects most lists to contain a single
|
||||||
|
* Recurrence, but also anticipates 2 "composite" scenarios:
|
||||||
|
*
|
||||||
|
* @li A list of N PERIOD_WEEK Recurrences.
|
||||||
|
* @li A list of 2 PERIOD_MONTH or PERIOD_LAST_WEEKDAY Recurrences,
|
||||||
|
* representing a Semi-Monthly period.
|
||||||
|
*
|
||||||
|
* @return A caller-owned string.
|
||||||
|
**/
|
||||||
|
gchar *recurrenceListToCompactString(GList *recurrence_list);
|
||||||
|
|
||||||
#endif /* RECURRENCE_H */
|
#endif /* RECURRENCE_H */
|
||||||
|
@ -896,51 +896,6 @@ gnc_frequency_new_from_recurrence(GList *recurrences, GDate *start_date)
|
|||||||
return GTK_WIDGET(toRet);
|
return GTK_WIDGET(toRet);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
|
||||||
_test_for_semi_monthly(GList *recurrences)
|
|
||||||
{
|
|
||||||
if (g_list_length(recurrences) != 2)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
// should be a "semi-monthly":
|
|
||||||
{
|
|
||||||
Recurrence *first = (Recurrence*)g_list_nth_data(recurrences, 0);
|
|
||||||
Recurrence *second = (Recurrence*)g_list_nth_data(recurrences, 1);
|
|
||||||
PeriodType first_period, second_period;
|
|
||||||
first_period = recurrenceGetPeriodType(first);
|
|
||||||
second_period = recurrenceGetPeriodType(second);
|
|
||||||
|
|
||||||
if (!((first_period == PERIOD_MONTH
|
|
||||||
|| first_period == PERIOD_END_OF_MONTH
|
|
||||||
|| first_period == PERIOD_LAST_WEEKDAY)
|
|
||||||
&& (second_period == PERIOD_MONTH
|
|
||||||
|| second_period == PERIOD_END_OF_MONTH
|
|
||||||
|| second_period == PERIOD_LAST_WEEKDAY)))
|
|
||||||
{
|
|
||||||
/*g_error("unknown 2-recurrence composite with period_types first [%d] second [%d]",
|
|
||||||
first_period, second_periodD);*/
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
_test_for_weekly_multiple(GList *recurrences)
|
|
||||||
{
|
|
||||||
GList *r_iter;
|
|
||||||
|
|
||||||
for (r_iter = recurrences; r_iter != NULL; r_iter = r_iter->next)
|
|
||||||
{
|
|
||||||
Recurrence *r = (Recurrence*)r_iter->data;
|
|
||||||
if (recurrenceGetPeriodType(r) != PERIOD_WEEK)
|
|
||||||
{
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_setup_weekly_recurrence(GncFrequency *gf, Recurrence *r)
|
_setup_weekly_recurrence(GncFrequency *gf, Recurrence *r)
|
||||||
{
|
{
|
||||||
@ -999,7 +954,7 @@ gnc_frequency_setup_recurrence(GncFrequency *gf, GList *recurrences, GDate *star
|
|||||||
|
|
||||||
if (g_list_length(recurrences) > 1)
|
if (g_list_length(recurrences) > 1)
|
||||||
{
|
{
|
||||||
if (_test_for_weekly_multiple(recurrences))
|
if (recurrenceListIsWeeklyMultiple(recurrences))
|
||||||
{
|
{
|
||||||
gtk_notebook_set_current_page(gf->nb, PAGE_WEEKLY);
|
gtk_notebook_set_current_page(gf->nb, PAGE_WEEKLY);
|
||||||
gtk_combo_box_set_active(gf->freqComboBox, PAGE_WEEKLY);
|
gtk_combo_box_set_active(gf->freqComboBox, PAGE_WEEKLY);
|
||||||
@ -1009,7 +964,7 @@ gnc_frequency_setup_recurrence(GncFrequency *gf, GList *recurrences, GDate *star
|
|||||||
_setup_weekly_recurrence(gf, (Recurrence*)recurrences->data);
|
_setup_weekly_recurrence(gf, (Recurrence*)recurrences->data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (_test_for_semi_monthly(recurrences))
|
else if (recurrenceListIsSemiMonthly(recurrences))
|
||||||
{
|
{
|
||||||
Recurrence *first, *second;
|
Recurrence *first, *second;
|
||||||
GtkWidget *multiplier_spin;
|
GtkWidget *multiplier_spin;
|
||||||
|
@ -494,7 +494,7 @@ gsltma_populate_tree_store(GncSxListTreeModelAdapter *model)
|
|||||||
char last_occur_date_buf[MAX_DATE_LENGTH+1];
|
char last_occur_date_buf[MAX_DATE_LENGTH+1];
|
||||||
char next_occur_date_buf[MAX_DATE_LENGTH+1];
|
char next_occur_date_buf[MAX_DATE_LENGTH+1];
|
||||||
|
|
||||||
frequency_str = recurrenceListToString(gnc_sx_get_schedule(instances->sx));
|
frequency_str = recurrenceListToCompactString(gnc_sx_get_schedule(instances->sx));
|
||||||
|
|
||||||
_format_conditional_date(xaccSchedXactionGetLastOccurDate(instances->sx),
|
_format_conditional_date(xaccSchedXactionGetLastOccurDate(instances->sx),
|
||||||
last_occur_date_buf, MAX_DATE_LENGTH);
|
last_occur_date_buf, MAX_DATE_LENGTH);
|
||||||
|
Loading…
Reference in New Issue
Block a user