/* 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 #include #include /* for vsnprintf */ #include #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; icolor == -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; idata), 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); }