Logo Search packages:      
Sourcecode: pal version File versions  Download package

event.c

/* pal
 *
 * Copyright (C) 2004, Scott Kuhl
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */


#include <string.h>
#include <time.h>

#include "main.h"
#include "event.h"

PalEvent* pal_event_init()
{
    PalEvent* event = g_malloc(sizeof(PalEvent));
    event->text = NULL;
    event->type = NULL;
    event->start_date = NULL;
    event->end_date = NULL;
    event->date_string = NULL;
    event->start_time = NULL;
    event->file_name = NULL;
    return event;
}

PalEvent* pal_event_copy(PalEvent* orig)
{
    PalEvent* new = g_malloc(sizeof(PalEvent));
    new->text = g_strdup(orig->text);
    new->start = orig->start;
    new->end   = orig->end;
    new->hide  = orig->hide;
    new->type  = g_strdup(orig->type);
    new->file_num = orig->file_num;
    new->file_name = g_strdup(orig->file_name);
    new->color = orig->color;

    if(orig->start_date == NULL)
      new->start_date = NULL;
    else
    {
      new->start_date = g_malloc(sizeof(GDate));
      memcpy(new->start_date, orig->start_date, sizeof(GDate));
    }

    if(orig->end_date == NULL)
      new->end_date = NULL;
    else
    {
      new->end_date = g_malloc(sizeof(GDate));
      memcpy(new->end_date, orig->end_date, sizeof(GDate));
    }

    if(orig->start_time == NULL)
      new->start_time = NULL;
    else
    {
      new->start_time = g_malloc(sizeof(PalTime));
      memcpy(new->start_time, orig->start_time, sizeof(PalTime));
    }

    new->is_todo = orig->is_todo;
    new->date_string = g_strdup(orig->date_string);
    new->global = orig->global;
    return new;
}

void pal_event_free(PalEvent* event)
{
    if(event == NULL)
      return;

    if(event->text != NULL)
      g_free(event->text);

    if(event->type != NULL)
      g_free(event->type);

    if(event->start_date != NULL)
      g_date_free(event->start_date);

    if(event->end_date != NULL)
      g_date_free(event->end_date);

    if(event->date_string != NULL)
      g_free(event->date_string);

    if(event->start_time != NULL)
      g_free(event->start_time);

    if(event->file_name != NULL)
      g_free(event->file_name);

    g_free(event);

    event = NULL;
    return;
}




/* Fills in start_date and end_date in pal_event by looking at
 * date_string.  Returns the key for the event */
void pal_event_fill_dates(PalEvent* pal_event, const gchar* date_string)
{
    gchar** s;

    s = g_strsplit(date_string, ":", 3);

    if(s[1] != NULL && s[2] != NULL)
    {
      GDate* d2 = get_date(s[1]);
      GDate* d3 = get_date(s[2]);

      if(d2 != NULL && d3 != NULL)
      {
          pal_event->start_date = get_date(s[1]);
          pal_event->end_date = get_date(s[2]);
      }
    }

    g_strfreev(s);
}

gchar* pal_event_date_string_to_key(const gchar* date_string)
{
    gchar** s;
    gchar* r;

    s = g_strsplit(date_string, ":", 3);

    r = g_strdup(s[0]);
    g_strfreev(s);
    return r;
}

/* the recur variable indicates how many :yyyymmdd's may follow the
 * current one */
gboolean is_valid_yyyymmdd(const gchar* date_string, int recur)
{
    gint d[8];

    /* if key is regular event in the form:
       single day, monthly, yearly, yearly/monthly */
    if(sscanf(date_string, "%1d%1d%1d%1d%1d%1d%1d%1d",
            &d[0],&d[1],&d[2],&d[3],&d[4],&d[5],&d[6],&d[7]) == 8)
    {
      gint year, month, day;

      year  = d[0] * 1000 + d[1] * 100 + d[2] * 10 + d[3];
      month = d[4] * 10 + d[5];
      day   = d[6] * 10 + d[7];

      if(day < 1 || day > 31 || month < 1 || month > 12 || year < 1)
          return FALSE;

      /* FIXME: Don't allow one-time events on leap day in years
       * that leap day doesn't exist */
      if(month == 2 && day > 29)
          return FALSE;

      /* 30 days in april, june, sept, nov */
      if((month == 4 || month == 6 || month == 9 || month == 11) &&
         day > 30)
          return FALSE;

      /* make sure there weren't more characters that sscanf didn't see */
      if(strlen(date_string) == 8)
          return TRUE;

      if(date_string[8] == ':' && recur != 0)
          return is_valid_yyyymmdd(&(date_string[9]), recur-1);
    }

    return FALSE;
}


gboolean is_valid_000000dd(const gchar* date_string)
{
    gint d[8];

    if(sscanf(date_string, "000000%1d%1d", &d[6],&d[7]) == 2)
    {
      int day   = d[6] * 10 + d[7];

      if(day < 1 || day > 31)
          return FALSE;

      if(strlen(date_string) == 8)
          return TRUE;

      if(date_string[8] == ':')
          return is_valid_yyyymmdd(&(date_string[9]), 2);

    }
    return FALSE;
}

gboolean is_valid_0000mmdd(const gchar* date_string)
{
    gint d[8];

    if(sscanf(date_string, "0000%1d%1d%1d%1d", &d[4],&d[5],&d[6],&d[7]) == 4)
    {
      gint month, day;

      day   = d[6] * 10 + d[7];
      month = d[4] * 10 + d[5];

      if(day < 1 || day > 31 || month < 1 || month > 12)
          return FALSE;

      /* FIXME: Don't allow one-time events on leap day in years
       * that leap day doesn't exist */
      if(month == 2 && day > 29)
          return FALSE;

      /* 30 days in april, june, sept, nov */
      if((month == 4 || month == 6 || month == 9 || month == 11) &&
         day > 30)
          return FALSE;

      if(strlen(date_string) == 8)
          return TRUE;

      else if(date_string[8] == ':')
          return is_valid_yyyymmdd(&(date_string[9]), 2);

    }
    return FALSE;
}




gboolean is_valid_star_mmnd(const gchar* date_string)
{
    gint d[8];
    if(sscanf(date_string, "*%1d%1d%1d%1d", &d[0],&d[1],&d[2],&d[3]) == 4) /* nth weekday of month  */
    {
      gint month, n, weekday;

      month = d[0] * 10 + d[1];
      n = d[2];
      weekday = d[3];

      if(weekday >  0 && weekday < 8  &&
           month >= 0 &&   month < 13 &&
               n >  0 &&       n < 6)        /* no more than 5 weeks in a month */
      {
          if(strlen(date_string) == 5)
             return TRUE;
          else if(date_string[5] == ':')
            return is_valid_yyyymmdd(&(date_string[6]), 2);
      }

    }

    return FALSE;
}

gboolean is_valid_star_mmLd(const gchar* date_string)
{
    gint d[8];
    if(sscanf(date_string, "*%1d%1dL%1d", &d[0],&d[1],&d[2]) == 3) /* last weekday of month */
    {
      gint month, weekday;

      month = d[0] * 10 + d[1];
      weekday = d[2];

      if(weekday >  0 && weekday < 8 &&
         month   >= 0 &&   month < 13 )
      {
          if(strlen(date_string) == 5)
             return TRUE;
          else if(date_string[5] == ':')
            return is_valid_yyyymmdd(&(date_string[6]), 2);
      }

    }

      return FALSE;
}


gboolean is_valid_EASTER(const gchar* date_string)
{

    if(strncmp(date_string, "EASTER", 6) == 0)
    {

      if(strlen(date_string) == 6)
          return TRUE;

      if(date_string[6] == ':')
          return is_valid_yyyymmdd(&(date_string[7]), 2);

      if(date_string[6] == '-' || date_string[6] == '+')
      {
          if(g_ascii_isdigit(date_string[7]) &&
             g_ascii_isdigit(date_string[8]) &&
             g_ascii_isdigit(date_string[9]))
          {
            if(date_string[10] == ':')
                return is_valid_yyyymmdd(&(date_string[11]), 2);
            else if(date_string[10] == '\0')
                return TRUE;
          }
      }

    }

    return FALSE;
}

gboolean is_valid_date_string(const gchar* match_string, const gchar* date_string)
{
    if(strncmp(match_string, date_string, strlen(match_string)) == 0)
    {
      if(strlen(date_string) == strlen(match_string))
          return TRUE;
      else if(date_string[strlen(match_string)] == ':')
          return is_valid_yyyymmdd(&(date_string[strlen(match_string)+1]), 2);
    }

    return FALSE;
}

/* checks if date_string is a valid date string.  Before calling this
 * function, g_strstrip needs to be called on date_string!  g_ascii_strup
 * should also be called on the date_string. */
gboolean is_valid_key(const gchar* date_string)
{
    if(is_valid_000000dd(date_string) ||
       is_valid_0000mmdd(date_string) ||
       is_valid_yyyymmdd(date_string, 0) ||
       is_valid_star_mmnd(date_string) ||
       is_valid_star_mmLd(date_string) ||
       is_valid_EASTER(date_string) ||
       is_valid_date_string("TODO", date_string) ||
       is_valid_date_string("DAILY", date_string) ||
       is_valid_date_string("MON", date_string) ||
       is_valid_date_string("TUE", date_string) ||
       is_valid_date_string("WED", date_string) ||
       is_valid_date_string("THU", date_string) ||
       is_valid_date_string("FRI", date_string) ||
       is_valid_date_string("SAT", date_string) ||
       is_valid_date_string("SUN", date_string))
      return TRUE;

    return FALSE;

}



GDate* find_easter(gint year)
{
    gint a,b,c,d,e,f,g,h,i,k,l,m,p,month,day;

    a = year%19;
    b = year/100;
    c = year%100;
    d = b/4;
    e = b%4;
    f = (b+8) / 25;
    g = (b-f+1) / 3;
    h = (19*a+b-d-g+15) % 30;
    i = c/4;
    k = c%4;
    l = (32+2*e+2*i-h-k)%7;
    m = (a+11*h+22*l) / 451;
    month = (h+l-7*m+114) / 31;
    p = (h+l-7*m+114) % 31;
    day = p+1;

    return g_date_new_dmy((GDateDay) day, (GDateMonth) month,
                    (GDateYear) year);
}

gchar* get_easter_key(const GDate* date)
{
    gchar* key = g_malloc(sizeof(gchar)*12);
    GDate* easter = find_easter(g_date_get_year(date));
    gint diff = g_date_days_between(date,easter);
    g_date_free(easter);

    if(diff != 0)
      snprintf(key, 12, "EASTER%c%03d", (diff > 0) ? '-' : '+', (diff > 0) ? diff : -diff);
    else
      snprintf(key, 12, "EASTER");

    return key;
}


/* gets the yyyymmdd key for the given date.  The returned string
 * should be freed. */
gchar* get_key(const GDate* date)
{
    gchar* key = g_malloc(sizeof(gchar)*9);

    snprintf(key, 9, "%04d%02d%02d", g_date_get_year(date),
          g_date_get_month(date), g_date_get_day(date));
    return key;
}


/* this only works on dates in yyyymmdd format.  It ignores everything
 * after the 8th character.  The returned GDate object should be
 * freed.  Returns NULL on failure*/
GDate* get_date(const gchar* key)
{
    GDate* date = NULL;
    gint year, month, day;

    sscanf(key, "%04d%02d%02d", &year, &month, &day);

    if(g_date_valid_dmy((GDateDay) day, (GDateMonth) month,
                  (GDateYear) year))
      date = g_date_new_dmy((GDateDay) day, (GDateMonth) month,
                        (GDateYear) year);

    return date;

}


gboolean last_weekday_of_month(const GDate* date)
{
    GDate* local = g_memdup(date,sizeof(GDate));

    g_date_add_days(local,7);
    if(g_date_get_month(local) != g_date_get_month(date))
    {
      g_date_free(local);
      return TRUE;
    }
    g_date_free(local);

    return FALSE;
}


/* Returns n from date --- as in: "date" is the "n"th
 * sunday/monday/... of the month */
gint get_nth_day(const GDate* date)
{
    int i = (g_date_get_day(date) / 7) + 1;
    if(g_date_get_day(date) % 7 == 0)
      i--;
    return i;
}


/* removes events from the list whose range does not include "date".
 * Returns the beginning of the new list (it might have changed) */
GList* inspect_range(GList* list, const GDate* date)
{
    GList* item = list;

    if(list == NULL)
      return list;

    while(g_list_length(item) > 0)
    {
      if(((PalEvent*) item->data)->start_date != NULL &&
         ((PalEvent*) item->data)->end_date != NULL)
      {

          if(g_date_days_between(date, ((PalEvent*) item->data)->start_date) > 0 ||
             g_date_days_between(date, ((PalEvent*) item->data)->end_date) < 0)
          {
            /* if not on last item */
            if(g_list_length(item) > 1)
            {
                /* save reference to next data item */
                gpointer n = g_list_next(item)->data;

                /* remove this list element */
                list = g_list_remove(list, item->data);

                /* find what we need to look at next */
                item = g_list_find(list, n);
            }
            else /* we're on last item */
            {
                list = g_list_remove(list, item->data);
                item = g_list_last(list);
            }
          }
          else
            item = g_list_next(item);
      }
      else
          item = g_list_next(item);

    }

    return list;
}


gint pal_event_sort_fn(gconstpointer x, gconstpointer y)
{
    PalEvent* a = (PalEvent*) x;
    PalEvent* b = (PalEvent*) y;

    /* Put events with start times before events without start times */
    if(a->start_time != NULL && b->start_time == NULL)
      return -1;
    if(a->start_time == NULL && b->start_time != NULL)
      return 1;

    /* if both events have start times, sort by start time */
    if(a->start_time != NULL && b->start_time != NULL)
    {
      if(a->start_time->hour < b->start_time->hour)
          return -1;
      if(a->start_time->hour > b->start_time->hour)
          return 1;

      /* if we get here, the hours are the same */
      if(a->start_time->min < b->start_time->min)
          return -1;
      if(a->start_time->min > b->start_time->min)
          return 1;
      return 0;
    }

    /* if neither event has start times, sort by order in pal.conf */
    if(a->file_num == b->file_num)
      return 0;
    if(a->file_num <  b->file_num)
      return -1;
    else
      return 1;
}

GList* pal_event_sort_events(GList* events)
{
    if(events == NULL)
      return NULL;

    return g_list_sort(events, pal_event_sort_fn);

}


/* Returns a list of events on the given date.
   The returned list is sorted. */
GList* get_events(const GDate* date)
{
    GList* list = NULL;
    gchar* key = get_key(date);
    gchar* new_key;
    GList* days_events;
    gint weekday;

    /* get easter related events */
    gchar* easter_key = get_easter_key(date);
    days_events = g_hash_table_lookup(ht, easter_key);
    g_free(easter_key);
    if(days_events != NULL)
      list = g_list_concat(list, g_list_copy(days_events));

    /* get events that happen only on that day */
    days_events = g_hash_table_lookup(ht,key);
    if(days_events != NULL)
      list = g_list_concat(list, g_list_copy(days_events));

    /* get days that always happen *today* (whatever that is) */
    {
      GDate* today = g_date_new();
      g_date_set_time(today, time(NULL));

      if(g_date_days_between(today, date) == 0)
      {
          days_events = g_hash_table_lookup(ht,"TODO");

          if(days_events != NULL)
            list = g_list_concat(list, g_list_copy(days_events));
      }

      g_date_free(today);
    }


    /* weekly events */
    switch(g_date_get_weekday(date))
    {
      case 1: days_events = g_hash_table_lookup(ht, "MON"); break;
      case 2: days_events = g_hash_table_lookup(ht, "TUE"); break;
      case 3: days_events = g_hash_table_lookup(ht, "WED"); break;
      case 4: days_events = g_hash_table_lookup(ht, "THU"); break;
      case 5: days_events = g_hash_table_lookup(ht, "FRI"); break;
      case 6: days_events = g_hash_table_lookup(ht, "SAT"); break;
      case 7: days_events = g_hash_table_lookup(ht, "SUN"); break;
      default: days_events = NULL;  /*impossible*/
    }

    if(days_events != NULL)
      list = g_list_concat(list, g_list_copy(days_events));


    /* daily event */
    days_events = g_hash_table_lookup(ht, "DAILY");

    if(days_events != NULL)
      list = g_list_concat(list, g_list_copy(days_events));



    /* get events that happen on that day every year */
    new_key = g_strdup(key);
    *new_key     = '0';
    *(new_key+1) = '0';
    *(new_key+2) = '0';
    *(new_key+3) = '0';
    days_events = g_hash_table_lookup(ht,new_key);
    if(days_events != NULL)
      list = g_list_concat(list, g_list_copy(days_events));
    g_free(new_key);

    /* get events that happen every month, every year */
    new_key = g_strdup(key);
    *new_key     = '0';
    *(new_key+1) = '0';
    *(new_key+2) = '0';
    *(new_key+3) = '0';
    *(new_key+4) = '0';
    *(new_key+5) = '0';
    days_events = g_hash_table_lookup(ht,new_key);
    if(days_events != NULL)
      list = g_list_concat(list, g_list_copy(days_events));
    g_free(new_key);


    /* convert weekday to friendly weekday
       from: 1(mon) -> 7(sun)
       to:   1(sun) -> 7(sat) */
    weekday = g_date_get_weekday(date);
    if(weekday == 7)
      weekday = 1;
    else
      weekday++;

    /* get events that happen every year, on certain day/week */
    new_key = g_strdup(key);
    snprintf(new_key, 9, "*%02d%01d%01d", g_date_get_month(date),
            get_nth_day(date), weekday);

    days_events = g_hash_table_lookup(ht,new_key);
    if(days_events != NULL)
      list = g_list_concat(list, g_list_copy(days_events));
    g_free(new_key);


    /* get events that happen monthly, on certain day/week */
    new_key = g_strdup(key);
    snprintf(new_key, 9, "*00%01d%01d", get_nth_day(date), weekday);

    days_events = g_hash_table_lookup(ht,new_key);
    if(days_events != NULL)
      list = g_list_concat(list, g_list_copy(days_events));
    g_free(new_key);


    if(last_weekday_of_month(date))
    {
      /* get events that happen every year, last *day of month */
      new_key = g_strdup(key);
      snprintf(new_key, 9, "*%02dL%01d", g_date_get_month(date), weekday);

      days_events = g_hash_table_lookup(ht,new_key);
      if(days_events != NULL)
          list = g_list_concat(list, g_list_copy(days_events));
      g_free(new_key);


      /* get events that happen every month, last *day of month */
      new_key = g_strdup(key);
      snprintf(new_key, 9, "*00L%01d", weekday);

      days_events = g_hash_table_lookup(ht,new_key);
      if(days_events != NULL)
          list = g_list_concat(list, g_list_copy(days_events));
      g_free(new_key);

    }

    list = inspect_range(list, date);
    list = pal_event_sort_events(list);

    g_free(key);
    return list;
}

/* the returned string should be freed */
gchar* pal_event_escape(const PalEvent* event, const GDate* today)
{
    gchar* in = event->text;
    gchar* out_string = g_malloc(sizeof(gchar)*strlen(event->text)*2);
    gchar* out = out_string;

    while(*in != '\0')
    {
      if( *in == '!' && strlen(in) > 5 &&
          g_ascii_isdigit(*(in+1)) &&
          g_ascii_isdigit(*(in+2)) &&
          g_ascii_isdigit(*(in+3)) &&
          g_ascii_isdigit(*(in+4)) &&
          *(in+5) == '!')
      {
          int diff;
          int now = g_date_get_year(today);
          int event = g_ascii_digit_value(*(in+1));
          event *= 10;
          event += g_ascii_digit_value(*(in+2));
          event *= 10;
          event += g_ascii_digit_value(*(in+3));
          event *= 10;
          event += g_ascii_digit_value(*(in+4));
          diff = now-event;
          out += sprintf(out, "%i", diff);
          in += 6;
      }
      else
      {
          *out = *in;
          out++;
          in++;
      }
    }

    *out = '\0';
    return out_string;
}

Generated by  Doxygen 1.6.0   Back to index