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

output.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 <stdarg.h>
#include <time.h>

#include <string.h>

/* for vsnprintf */
#include <stdarg.h>

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

/* set attribute w/o changing color */
void pal_output_attr(gint attr, gchar *formatString, ...)
{
    gchar color[20];
    gchar buf[2048];
    va_list argptr;

    va_start( argptr, formatString );
    colorize_attr(attr, color);
    g_print(color);

    /* glib 2.2 provides g_vfprintf */
    vsnprintf(buf, 2048, formatString, argptr);
    g_print("%s", buf); /* use g_print to convert from UTF-8 */

    colorize_reset(color);
    g_print(color);
    va_end(argptr);
}

/* set foreground color and attribute */
void pal_output_fg( gint attr, gint color, gchar *formatString, ...)
{
    gchar str[20];
    gchar buf[2048];
    va_list argptr;

    va_start( argptr, formatString );
    colorize_fg(attr, color, str);
    g_print(str);

    /* glib 2.2 provides g_vfprintf */
    vsnprintf(buf, 2048, formatString, argptr);
    g_print("%s", buf); /* use g_print to convert from UTF-8 */

    colorize_reset(str);
    g_print(str);
    va_end(argptr);
}

void pal_output_error(char *formatString, ... )
{
    gchar color[20];
    gchar buf[2048];
    va_list argptr;

    va_start( argptr, formatString );
    colorize_fg(BRIGHT, RED, color);
    g_printerr(color);

    /* glib 2.2 provides g_vfprintf */
    vsnprintf(buf, 2048, formatString, argptr);
    g_printerr("%s", buf); /* use g_print to convert from UTF-8 */

    colorize_reset(color);
    g_printerr(color);
    va_end(argptr);
}


/* finishes with date on the sunday of the next week */
void pal_output_text_week(GDate* date, gboolean force_month_label,
                    const GDate* today, gchar* buffer)
{
    gchar* loc = NULL;
    gint  i=0;
    gchar start_color[20];
    gchar end_color[20];

    if(settings->week_start_monday)
      /* go to last day in week (sun) */
      while(g_date_get_weekday(date) != 7)
          g_date_add_days(date,1);
    else
      /* go to last day in week (sat) */
      while(g_date_get_weekday(date) != 6)
          g_date_add_days(date,1);


    for(i=0; i<7; i++)
    {
      if(g_date_get_day(date) == 1)
          force_month_label = TRUE;

      g_date_subtract_days(date,1);
    }

    g_date_add_days(date,1);
    /* date is now at beginning of week */

    if(force_month_label)
    {
      gchar buf[1024];

      colorize_fg(BRIGHT, GREEN, start_color);
      colorize_reset(end_color);

      g_date_add_days(date,6);
      g_date_strftime(buf, 128, "%b", date);
      g_date_subtract_days(date,6);

      /* make sure we're only showing 3 characters */
      if(g_utf8_strlen(buf, -1) != 3)
      {
          /* append whitespace in case "buf" is too short */
          gchar* s = g_strconcat(buf, "        ", NULL);

          /* just show first 3 characters of month */
          g_utf8_strncpy(buf, s, 3);
          g_free(s);
      }

      loc = buffer + sprintf(buffer, "%s%s %s", start_color, buf, end_color);
    }
    else
    {
      sprintf(buffer, "    ");
      loc = buffer + 4;
    }


    for(i=0; i<7; i++)
    {
      GList* events = NULL;
      gunichar start=' ', end=' ';
      gchar utf8_buf[8];
      gint color = settings->event_color;
      events = get_events(date);

      if(g_date_compare(date,today) == 0)
          start = '@', end = '@';

      else if(events != NULL)
      {
          GList* item  = g_list_first(events);
          gboolean same_char = TRUE;
          gboolean same_color = TRUE;

          /* skip to a event that isn't hidden or to the end of the list */
          while(g_list_length(item) > 1 && ((PalEvent*) item->data)->hide)
            item = g_list_next(item);

          /* save the markers for the event */
          if(((PalEvent*) item->data)->hide)
            start = ' ', end = ' ';
          else
          {
            start = ((PalEvent*) item->data)->start;
            end   = ((PalEvent*) item->data)->end;
            color = ((PalEvent*) item->data)->color;
          }

          /* if multiple events left */
          while(g_list_length(item) > 1)
          {
            /* find next non-hidden event */
            while(g_list_length(item) > 1 && ((PalEvent*) item->data)->hide)
                item = g_list_next(item);

            /* if this event is hidden, there aren't any more non-hidden events left */
            /* if this event isn't hidden, then determine if it has different markers */
            if(!((PalEvent*) item->data)->hide)
            {
                gunichar new_start = ((PalEvent*) item->data)->start;
                gunichar new_end   = ((PalEvent*) item->data)->end;
                gint     new_color = ((PalEvent*) item->data)->color;

                if(new_start != start || new_end != end)
                  same_char = FALSE;
                if(new_color != color)
                  same_color = FALSE;

                /* jump to next event to force the execution of the while loop above
                   otherwise we get stuck in an infinite loop on a non-hidden event */
                item = g_list_next(item);
            }
            if(same_char == FALSE)
                start = '*', end = '*';
            if(same_color == FALSE)
                color = -1;
          }
      }

      /* print color for marker if needed */
      if(start != ' ' && end != ' ')
      {
          if(color == -1)
            colorize_fg(BRIGHT, settings->event_color, start_color);
          else
            colorize_fg(BRIGHT, color, start_color);

          loc += snprintf(loc, 20, "%s", start_color);
      }

      utf8_buf[g_unichar_to_utf8(start, utf8_buf)] = '\0';
      loc += sprintf(loc, "%s", utf8_buf);

      /* end color marker */
      if(start != ' ' && end != ' ')
      {
          colorize_reset(end_color);
          loc += snprintf(loc, 20, "%s", end_color);
      }

      /* make today bright */

      if(g_date_compare(date,today) == 0)
      {
          gchar start_color_today[20];
          colorize_attr(BRIGHT, start_color_today);
          loc += snprintf(loc, 20, "%s", start_color_today);
      }

      /* print day */
      loc += snprintf(loc, 4, "%02d", g_date_get_day(date));

      /* stop using bright */
      if(g_date_compare(date,today) == 0)
      {
          colorize_reset(end_color);
          loc += snprintf(loc, 20, "%s", end_color);
      }

      /* print color for marker if needed */
      if(start != ' ' && end != ' ')
      {
          if(color == -1)
            colorize_fg(BRIGHT, settings->event_color, start_color);
          else
            colorize_fg(BRIGHT, color, start_color);

          loc += snprintf(loc, 20, "%s", start_color);
      }

      utf8_buf[g_unichar_to_utf8(end, utf8_buf)] = '\0';
      loc += snprintf(loc, 10, "%s", utf8_buf);

      /* end color marker */
      if(start != ' ' && end != ' ')
      {
          colorize_reset(end_color);
          loc += snprintf(loc, 20, "%s", end_color);
      }

      /* print extra space between days */
      if(i != 6)
          loc += snprintf(loc, 2, " ");

      g_date_add_days(date,1);
      g_list_free(events);
    }

}



void pal_output_week(GDate* date, gboolean force_month_label, const GDate* today)
{
    gchar week[1024];
    gchar week2[1024];

    pal_output_text_week(date, force_month_label, today, week);

    if(!settings->no_columns)
    {
        /* skip ahead to next column */
      g_date_subtract_days(date, 6);
      g_date_add_days(date, settings->cal_lines*7);
      pal_output_text_week(date, force_month_label, today, week2);

      /* skip back to where we were */
      g_date_subtract_days(date, settings->cal_lines*7);

      g_print("%s", week);
      pal_output_fg(DIM,YELLOW,"%s","|");
      g_print("%s\n", week2);

    }
    else
      g_print("%s\n", week);

}



void pal_output_cal(GDate* date, gint num_lines, const GDate* today)
{
    gint on_week = 0;
    gchar* week_hdr = NULL;

    if(num_lines <= 0)
      return;

    if(settings->week_start_monday)
      week_hdr = g_strdup(_("Mo   Tu   We   Th   Fr   Sa   Su"));
    else
      week_hdr = g_strdup(_("Su   Mo   Tu   We   Th   Fr   Sa"));


    /* if showing enough lines, show previous week. */
    if(num_lines > 3)
      g_date_subtract_days(date, 7);

    if(settings->no_columns)
      pal_output_fg(BRIGHT,GREEN, "     %s\n", week_hdr);

    else
    {
      pal_output_fg(BRIGHT,GREEN,"     %s ", week_hdr);
      pal_output_fg(DIM,YELLOW,"%s","|");
      pal_output_fg(BRIGHT,GREEN,"     %s\n", week_hdr);
    }

    g_free(week_hdr);

    while(on_week < num_lines)
    {
      if(on_week == 0)
          pal_output_week(date, TRUE, today);
      else
          pal_output_week(date, FALSE, today);

      on_week++;

    }
}

/* replaces tabs with spaces */
void pal_output_strip_tabs(gchar* string)
{
    gchar *ptr = string;
    while(*ptr != '\0')
    {
      if(*ptr == '\t')
          *ptr = ' ';
      ptr++;
    }
}


/* This function does not yet handle tabs and color codes.  Tabs
 * should be stripped from 'string' before this is called.
 * "chars_used" indicates the number of characters already used on the
 * line that "string" will be printed out on.
 */

/* UTF-8: "no ASCII byte can appear as part of another character" */
void pal_output_wrap(const gchar* string, gint chars_used, gint indent)
{
    gint i;
    gchar* buffer = g_malloc(sizeof(gchar) * strlen(string) * 10);
    gchar* ptr = buffer; /* beginning of actual line */
    gchar* begin_line_text = buffer; /* beginning of line's text */
    sprintf(buffer, "%s", string);

    /* Don't bother with wrapping if terminal is very narrow. As
     * things are working now, not doing this could cause a
     * segmentation faults on very narrow terminals. */
    if(settings->term_cols < 38)
    {
      g_print("%s\n", buffer);
      return;
    }

    while(g_utf8_strlen(ptr,-1)+chars_used > settings->term_cols)
    {
      gunichar saved_char = ' ';
      gint   saved_char_size = 0; /* bytes used by saved_char */

      /* jump to where newline needs to be */
      ptr = g_utf8_offset_to_pointer (ptr, (settings->term_cols)-chars_used);

      /* find a ' ' to break at. */
      while(ptr != begin_line_text && *ptr != ' ')
          ptr = g_utf8_prev_char(ptr);

      /* if we found no ' '; extremely long word */
      /* break in the middle of the word---sorry! */
      if(ptr == begin_line_text && *ptr != ' ')
      {
          /* back up to first character in line, not to the beginning of the text */
          while(*ptr != '\n' && ptr != buffer)
            ptr--;

          /* if we stopped at a new line, we want to be at the next char */
          /* if we stopped at "buffer" we are where we want to be */
          if(*ptr == '\n')
            ptr++;

          ptr = g_utf8_offset_to_pointer(ptr, (settings->term_cols)-chars_used);
      }

      /* no need for this anymore */
      chars_used = 0;

      /* we might be on a character if we hit a long word. */
      /* save this letter for later */
      if(*ptr != ' ')
      {
          gchar* saved_buf = g_malloc(sizeof(gchar)*10);
          saved_char = g_utf8_get_char(ptr);
          saved_char_size = g_unichar_to_utf8(saved_char, saved_buf);

          /* if we saved a letter larger than 1 byte, shift
           * remaining letters left */
          if(saved_char_size > 1)
          {
            gchar* new_ptr = ptr+1;

            do
            {
                *new_ptr = *(new_ptr+(saved_char_size-1));
                new_ptr++;
            } while(*(new_ptr-1) != '\0');

          }

      }
      else
          saved_char = ' ';

      /* this newline will replace either a whitespace, or the saved_char */
      *ptr = '\n';

      /* find end of string */
      while(*ptr != '\0')
          ptr++;

      /* make room for the indentation after a new line */
      while(*ptr != '\n')
      {
          /* bump all letters back 'indent' bytes right */
          if(saved_char == ' ')
            *(ptr+indent) = *ptr;
          else /* we need to shift indent+1 or more in order to print the saved letter */
            *(ptr+indent+saved_char_size) = *ptr;

          ptr--;
      }

      /* ptr is now at \n */
      /* insert hanging indent spaces */
      for(i=0; i<indent; i++)
      {
          ptr = g_utf8_next_char(ptr);
          *ptr = ' ';
      }

      /* finally, print that pesky saved character */
      if(saved_char != ' ')
          g_unichar_to_utf8(saved_char, g_utf8_next_char(ptr));

      /* leave ptr at real beginning of line
         (immediately after \n) */
      for(i=1; i<indent; i++)
          ptr = g_utf8_prev_char(ptr);

      /* beginning of actual text */
      begin_line_text = g_utf8_offset_to_pointer(ptr, indent);
    }

    g_print("%s\n", buffer);
    g_free(buffer);
}



/* if event_number is -1, don't number the events.  */
void pal_output_event(const PalEvent* event, const GDate* date, gint event_number)
{
    gchar date_text[128];
    gint indent = 0;
    gchar* event_text;
    date_text[0] = '\0';

    if(event_number == -1)
    {
      if(event->color == -1)
          pal_output_fg(BRIGHT, settings->event_color, "%s ", "*");
      else
          pal_output_fg(BRIGHT, event->color, "%s ", "*");
      indent = 2;
    }
    else
    {
      pal_output_attr(BRIGHT, "%2i ", event_number);
      indent = 3;
    }

    pal_output_strip_tabs(event->text);
    pal_output_strip_tabs(event->type);

    event_text = pal_event_escape(event, date);

    if(settings->compact_list)
    {
      if(settings->hide_event_type)
      {
          gchar* s;

          g_date_strftime(date_text, 128,
                      settings->compact_date_fmt, date);
          pal_output_attr(BRIGHT, "%s ", date_text);
          s = g_strconcat(event_text, NULL);
          pal_output_wrap(s, indent+g_utf8_strlen(date_text,-1)+1, indent);
          g_free(s);
      }
      else
      {
          gchar* s;

          g_date_strftime(date_text, 128,
                      settings->compact_date_fmt, date);
          pal_output_attr(BRIGHT, "%s ", date_text);
          s = g_strconcat(event->type, ": ", event_text, NULL);
          pal_output_wrap(s, indent+g_utf8_strlen(date_text,-1)+1, indent);
          g_free(s);
      }

    }
    else
    {
      if(settings->hide_event_type)
          pal_output_wrap(event_text, indent, indent);
      else
      {
          gchar* s = g_strconcat(event->type, ": ", event_text, NULL);
          pal_output_wrap(s, indent, indent);
          g_free(s);
      }
    }
    g_free(event_text);

}


void pal_output_date_line(const GDate* date)
{
    gchar pretty_date[128];
    gint diff = 0;
    GDate* today = g_date_new();
    g_date_set_time(today, time(NULL));

    g_date_strftime(pretty_date, 128, settings->date_fmt, date);

    pal_output_attr(BRIGHT, "%s", pretty_date);
    g_print(" - ");

    diff = g_date_days_between(today, date);
    if(diff == 0)
      pal_output_fg(BRIGHT, RED, "%s", _("Today"));
    else if(diff == 1)
      pal_output_fg(BRIGHT, YELLOW, "%s", _("Tomorrow"));
    else if(diff == -1)
      g_print("%s", _("Yesterday"));
    else if(diff > 1)
      g_print(_("%d days away"), diff);
    else if(diff < -1)
      g_print(_("%d days ago"), -1*diff);

    g_print("\n");

    g_date_free(today);
}


/* outputs the events in the order of PalEvent->file_num */
void pal_output_date(GDate* date, gboolean show_empty_days, gboolean number_events)
{
    GList* events = get_events(date);
    gint num_events = g_list_length(events);

    if(events != NULL || show_empty_days)
    {
      GList* item;
      int i;

      if(!settings->compact_list)
          pal_output_date_line(date);

      item = g_list_first(events);

      for(i=0; i<num_events; i++)
      {
          if(number_events)
            pal_output_event((PalEvent*) (item->data), date, i+1);
          else
            pal_output_event((PalEvent*) (item->data), date, -1);
          item = g_list_next(item);
      }


      if(num_events == 0)
      {
          if(settings->compact_list)
          {
            gchar pretty_date[128];
            gchar* s1;

            g_date_strftime(pretty_date, 128,
                        settings->compact_date_fmt, date);
            s1 = g_strconcat(pretty_date, " No events.\n" , NULL);
            pal_output_wrap(s1, 2, 2);
            g_free(s1);
          }
          else
            g_print("%s\n", _("No events."));
      }

      if(!settings->compact_list)
          g_print("\n");

    }
}


/* returns the PalEvent for the given event_number */
PalEvent* pal_output_event_num(const GDate* date, gint event_number)
{
    GList* events = get_events(date);
    gint num_events = g_list_length(events);

    if(events == NULL || event_number < 1 || event_number > num_events)
      return NULL;

    return (PalEvent*) g_list_nth_data(events, event_number-1);
}



Generated by  Doxygen 1.6.0   Back to index