#include #include #include #include #include "color.h" // struct for storing dimensions used for calculations, movement, etc. struct dimensions { // Multiplication factors int row_count; /* rows */ int column_count; /* columns */ // All of these are in units chosen by width in "calp.c" // Most likely milimeters (mm) double paper_width; /* paper width */ double paper_height; /* paper height */ double month_width; /* month width */ double month_height; /* month height */ double month_top_corner_x; double month_top_corner_y; double day_width; /* day box width */ double day_height; /* day box height */ double margin; /* margin */ double header_width; double header_height; }; // struct to hold Month-specific info, like what day of the week the first // day lands on, how many days are in the month, etc. struct month_info { int month_position; int first_day; int num_of_days; int minimum_rows; }; // struct for defining RGB colors struct RGB { double r; double g; double b; }; // Convert Hex color strings to their RGB counterparts. // IMO, hex strings are easier to work with, so this is // a nice helper function. struct RGB hex_to_rgb(char *str) { unsigned int r, g, b; struct RGB color; sscanf(str, "%02x%02x%02x", &r, &g, &b); color.r = (r / 255.0); color.g = (g / 255.0); color.b = (b / 255.0); return color; } // Set a context's color using a HEX code. Color macros are defined // in color.h int set_color(cairo_t *c, char *hex) { struct RGB color = hex_to_rgb(hex); cairo_set_source_rgb (c, color.r, color.g, color.b); return 0; } // Get the day of the week for a given day // Used for calculating when the start of a month is // Relative to "Su Mo Tu ... Sa" frame, not when Su and Sa are together // Range is 0 -> 6 // https://wiki.c2.com/?PerpetualCalendarAlgorithm int day_of_week (int d, int m, int y) { return((d += m < 3 ? y-- : y - 2, 23*m/9 + d + 4 + y/4- y/100 + y/400)%7); } // Fill entire background with current color // Function name is changed for clarity int fill_bg(cairo_t *c) { cairo_paint(c); return 0; } // Needn't be a string pointer, can be fixed array of chars // Requires Font Family (string) and Size (int) // Creates and destroys layout within the scope of the function // Is that performant when looped over 365 operations? int draw_text (cairo_t *c, double x, double y, char *font_family, int font_size, char *text) { char font[100]; // Concat font string for pango_font_description_from_string snprintf(font, sizeof(font), "%s %d", font_family, font_size); //Initialize Pango Stuff PangoLayout *layout; PangoFontDescription *desc; // Create a PangoLayout, setting font & text layout = pango_cairo_create_layout(c); desc = pango_font_description_from_string(font); pango_layout_set_font_description(layout, desc); pango_font_description_free(desc); pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER); pango_layout_set_text(layout, text, -1); cairo_move_to (c, x, y); pango_cairo_show_layout (c, layout); g_object_unref(layout); return 0; } // Get the dimensions of a string of text // Returns a PangoRectangle struct PangoRectangle get_logical_extents (cairo_t *c, char *font_family, int font_size, char *text) { char font[100]; // Concat font string for pango_font_description_from_string snprintf(font, sizeof(font), "%s %d", font_family, font_size); PangoLayout *layout; PangoFontDescription *desc; PangoRectangle rect; // Create a PangoLayout, setting font & text layout = pango_cairo_create_layout(c); desc = pango_font_description_from_string(font); pango_layout_set_font_description(layout, desc); pango_font_description_free(desc); pango_layout_set_text(layout, text, -1); pango_layout_get_pixel_extents(layout, &rect, NULL); //pango_layout_get_extents(layout, NULL, &rect); g_object_unref(layout); return(rect); } // Calculate dimenions for one page and stores it in the dimensions struct // Used for determining where elements should be drawn on the page int calculate_dimensions(double pw, double ph, struct dimensions *d) { d->row_count = 6; d->column_count = 7; // Percentage of page that should be margin double margin = 0.05 * pw; // width of day is 1/7th of total width double box_width = pw / 10.0; // Everything other than margin double month_width = box_width * d->column_count; // Width = Height; Day Box is a square double box_height = box_width; // Height is based on how tall # of weeks are double month_height = box_height * d->row_count; // Set items in struct to calculated values d->paper_width = pw; d->paper_height = ph; d->margin = margin; d->month_width = month_width; d->month_top_corner_x = (pw - (month_width)) / 2.0; d->month_top_corner_y = ph - margin - month_height; d->month_height = month_height; d->day_width = box_width; d->day_height = box_height; return 0; } // Print the contents of a dimensions struct. // Use for debugging int print_dimensions(struct dimensions *d) { printf("row count: %d\n", d->row_count); printf("column count: %d\n", d->column_count); printf("paper width: %.2f\n", d->paper_width); printf("paper height: %.2f\n", d->paper_height); printf("margin: %.2f\n", d->margin); printf("month width: %.2f\n", d->month_width); printf("month height: %.2f\n", d->month_height); printf("month_top_corner_x: %f\n", d->month_top_corner_x); printf("month_top_corner_y: %f\n", d->month_top_corner_y); printf("day width: %.2f\n", d->day_width); printf("day height: %.2f\n", d->day_height); //printf("header width: %.2f\n", d->header_width); //printf("header height: %.2f\n", d->header_height); return 0; } // Calculate minimum amount of rows a month can fit in. // Most months can fit within 4 or 5 rows instead of 6. // TBD: This can probably done in a more efficient way int calculate_minimum_rows(struct month_info *m, struct dimensions *d) { int extra_rows = 0; int days_in_first_week = (d->column_count - m->first_day); if (m->first_day == 0) { days_in_first_week = m->first_day; } int days_in_last_week = (m->num_of_days - days_in_first_week) % 7; int middle_weeks = (m->num_of_days - days_in_first_week - days_in_last_week) / 7; if (days_in_first_week != 0) { extra_rows++; } if (days_in_last_week != 0) { extra_rows++; } int minimum_rows = middle_weeks + extra_rows; return minimum_rows; } // Draws the grid lines for a month int draw_month_grid (cairo_t *c, struct month_info *m, struct dimensions *d) { double cursor_x = d->month_top_corner_x; int row_offset = d->row_count - calculate_minimum_rows(m, d); double cursor_y = d->month_top_corner_y + (row_offset * d->day_height); double x = cursor_x; double y = cursor_y; set_color(c, BLACK); cairo_set_line_width (c, 1.0); for (int i = 0; i <= d->column_count; i++) { cairo_move_to(c, cursor_x, cursor_y); cairo_line_to(c, cursor_x, cursor_y + (d->month_height - (row_offset * d->day_height))); cursor_x = cursor_x + d->day_width; } // Recenter the cursor cursor_x = x; cursor_y = y; for (int i = 0; i <= d->row_count; i++) { cairo_move_to(c, cursor_x, cursor_y); cairo_line_to(c, cursor_x + d->month_width, cursor_y); cursor_y = cursor_y + d->day_height; } cairo_stroke(c); return 0; } int construct_day_box(cairo_t *cd, int month, int day, int week_day) { char str[20]; double padding = 8.0; set_color(cd, WHITE); fill_bg(cd); if ((month == 0) && (day == 1) && (week_day == 1)) { } // draw_text(), and therefore pango_layout_set_text(), needs a // string as input, so the current day's date is converted. snprintf(str, sizeof(str), "%d", day); set_color(cd, BLACK); draw_text(cd, padding, padding, "Monospace", 15, str); return 0; } // Draws one month using daybox sub-surface & context, main context, and dimension struct int draw_month (cairo_t *c, cairo_t *cd, cairo_surface_t *s, struct month_info *m, struct dimensions *d) { double cursor_x = d->month_top_corner_x; if (m->first_day != 0) cursor_x = cursor_x + (d->day_width * m->first_day); // Push bottom of month as far down the page as the margin allows int row_offset = d->row_count - calculate_minimum_rows(m, d); double cursor_y = d->month_top_corner_y + (row_offset * d->day_height); int current_day = m->first_day; int increment = 0; for (int i = 0; i < d->row_count; i++) { for (int k = current_day; k < d->column_count; k++) { // When the increment reached the end of the month, end // the function. if (increment >= m->num_of_days) return 0; construct_day_box(cd, m->month_position, increment + 1, k); // Position the constructed day box on main surface cairo_set_source_surface (c, s, cursor_x, cursor_y); cairo_paint(c); // Move cursor one "box" over cursor_x = cursor_x + d->day_width; increment++; } current_day = 0; cursor_x = d->month_top_corner_x; cursor_y = cursor_y + d->day_height; } cairo_stroke(cd); return 0; } // Draws the month being drawn at the top of the page int draw_month_title(cairo_t *c, struct dimensions *d, char *font_family, int font_size, char *name) { PangoRectangle rect; rect = get_logical_extents(c, font_family, font_size, name); //double cursor_x = (d->header_height / 2.0) - ((rect.width / 2.0)); double cursor_x = ((d->header_width - rect.width) / 2.0); //double cursor_y = height + d->margin; double cursor_y = d->header_height - rect.height - d->margin; draw_text(c, cursor_x, cursor_y, font_family, font_size, name); return 0; } //int draw_weekday_names(cairo_t *c, struct dimensions *d) { // //}