pebble / calendar / src / main.c

/** \file
 * Calendar clock
 */
#include <pebble_os.h>
#include <pebble_app.h>
#include <pebble_fonts.h>


#define UUID {0xA4, 0x8B, 0x43, 0x03, 0xFA, 0x4F, 0x4F, 0xF2, 0xBB, 0x49, 0x3E, 0xA2, 0x38, 0x91, 0x57, 0xE0}

PBL_APP_INFO(
	UUID,
	"Calendar",
	"hudson",
	2, 0, // Version
	RESOURCE_ID_IMAGE_MENU_ICON,
	APP_INFO_WATCH_FACE
);

#define FONT_H 16
#define FONT_W 7
#define ROWS 8
#define COLS 7
#define FONT_OFF	RESOURCE_ID_FONT_SOURCECODEPRO_LIGHT_16
static GFont font_off;

static Window window;
static TextLayer layers[ROWS][COLS];
static TextLayer time_layer;

static char time_text[8];

static int mdays[] =
{
	31, // dec, previous year == 0
	31, // jan
	28, // feb; will be updated with actual number of days
	31, // mar
	30, // april
	31, // may
	30, // jun
	31, // july
	31, // aug
	30, // sep
	31, // oct
	30, // nov
	31, // dec
	31, // jan, next year == 13
};

static const char days[7][4] =
{
	"M",
	"T",
	"W",
	"R",
	"F",
	"S",
	"Z",
};

static const char dates[32][4] =
{
	"--",
	" 1",
	" 2",
	" 3",
	" 4",
	" 5",
	" 6",
	" 7",
	" 8",
	" 9",
	"10",
	"11",
	"12",
	"13",
	"14",
	"15",
	"16",
	"17",
	"18",
	"19",
	"20",
	"21",
	"22",
	"23",
	"24",
	"25",
	"26",
	"27",
	"28",
	"29",
	"30",
	"31",
};


#if 0
/** Draw a box in a corner to indicate the number of minutes past the five.
 */
static void
minute_layer_update(
        Layer * const me,
        GContext * ctx
)
{
	const int w = 4;
	GRect r = GRect(0, 0, w, w);

	if (minute_num == 0)
		return; // nothing to draw
	else
	if (minute_num == 1)
		r.origin = GPoint(0,0);
	else
	if (minute_num == 2)
		r.origin = GPoint(144-w,0);
	else
	if (minute_num == 3)
		r.origin = GPoint(144-w,168-w);
	else
	if (minute_num == 4)
		r.origin = GPoint(0,168-w);

	graphics_context_set_fill_color(ctx, GColorWhite);
	graphics_fill_rect(ctx, r, 1, GCornersAll);
}
#endif


static void
day_mark(
	int row,
	int col,
	int mday,
	int today
)
{
	TextLayer * const layer = &layers[row][col];
	if (today)
	{
		text_layer_set_text_color(layer, GColorBlack);
		text_layer_set_background_color(layer, GColorWhite);
	} else {
		text_layer_set_text_color(layer, GColorWhite);
		text_layer_set_background_color(layer, GColorClear);
	}

	//text_layer_set_font(layer, today ? font_on : font_off);
	text_layer_set_text(layer, dates[mday]);
}


static void
handle_tick(
	AppContextRef ctx,
	PebbleTickEvent * const event
)
{
	(void) ctx;
	const PblTm * const ptm = event->tick_time;

	string_format_time(
		time_text,
		sizeof(time_text),
		"%H:%M",
		ptm
	);

	text_layer_set_text(&time_layer, time_text);

	// Update the number of days in feb
	int year = ptm->tm_year + 1900;
	if (year % 4 == 0 && year % 400 != 0)
		mdays[2] = 29;
	else
		mdays[2] = 28;

	// can we use mday or yday to simplify this?
	int mday = ptm->tm_mday;
	int month = ptm->tm_mon + 1;

	// shift to start the week on monday, not sunday
	int wday = (ptm->tm_wday + 7 - 1) % 7;

	// Compute the 
	if (mday <= 14 + wday)
	{
		month--;
		mday = mdays[month] + mday - 14 - wday;
	} else {
		mday -= 14 + wday;
	}

	// Update the text for the calendar block
	// mday and month are now set for the monday two weeks
	// before the current day.
	for (int row = 1 ; row < ROWS ; row++)
	{
		for (int col = 0 ; col < 7 ; col++)
		{
			const int today = month == ptm->tm_mon+1 && mday == ptm->tm_mday;

			day_mark(row, col, mday, today);
			if (++mday > mdays[month])
			{
				month++;
				mday = 1;
			}
		}
	}

#if 0
	// update the minute box
	minute_num = min % 5;
	layer_mark_dirty(&minute_layer);

	// light up the one hour marker
	for (int i = 1 ; i <= 12 ; i++)
		word_mark(i, i == hour ? 1 : 0);
#endif
}


static void
day_layer_init(
	int row,
	int col
)
{
	TextLayer * const layer = &layers[row][col];

	GRect frame = GRect(
		3 * col * FONT_W + (row == 0 ? FONT_W : 0),
		row * FONT_H + 20,
		2 * FONT_W,
		FONT_H+2
	);

	text_layer_init(layer, frame);
	text_layer_set_text_color(layer, GColorWhite);
	text_layer_set_background_color(layer, GColorClear);
	text_layer_set_font(layer, font_off);

	if (row == 0)
		text_layer_set_text(layer, days[col]);
	else
		text_layer_set_text(layer, ""); // will be set later

	layer_add_child(&window.layer, &layer->layer);
}


static void
handle_init(
	AppContextRef ctx
)
{
	(void) ctx;

	window_init(&window, "Main");
	window_stack_push(&window, true);
	window_set_background_color(&window, GColorBlack);

	font_off = fonts_load_custom_font(resource_get_handle(FONT_OFF));

	resource_init_current_app(&RESOURCES);

	for (int row = 0 ; row < ROWS ; row++)
	{
		for (int day = 0 ; day < 7 ; day++)
		{
			day_layer_init(row, day);
		}
	}

	text_layer_init(&time_layer, GRect(40, -4, 80, 25));
	text_layer_set_text_color(&time_layer, GColorWhite);
	text_layer_set_background_color(&time_layer, GColorClear);
	text_layer_set_font(&time_layer, fonts_load_custom_font(resource_get_handle(RESOURCE_ID_FONT_SOURCECODEPRO_LIGHT_20)));

	text_layer_set_text(&time_layer, "");
	layer_add_child(&window.layer, &time_layer.layer);

	// Create a graphics layer for the entire background
	//layer_init(&minute_layer, GRect(0, 0, 144, 168));
	//minute_layer.update_proc = minute_layer_update;
	//layer_add_child(&window.layer, &minute_layer);
}


void
pbl_main(
	void * const params
)
{
	PebbleAppHandlers handlers = {
		.init_handler	= &handle_init,
		.tick_info	= {
			.tick_handler = &handle_tick,
			.tick_units = MINUTE_UNIT,
		},
	};

	app_event_loop(params, &handlers);
}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.