hekate/bdk/libs/lvgl/lv_objx/lv_calendar.c
CTCaer 185526d134 Introducing Bootloader Development Kit (BDK)
BDK will allow developers to use the full collection of drivers,
with limited editing, if any, for making payloads for Nintendo Switch.

Using a single source for everything will also help decoupling
Switch specific code and easily port it to other Tegra X1/X1+ platforms.
And maybe even to lower targets.

Everything is now centrilized into bdk folder.
Every module or project can utilize it by simply including it.

This is just the start and it will continue to improve.
2020-06-14 15:25:21 +03:00

1039 lines
38 KiB
C

/**
* @file lv_calendar.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_calendar.h"
#if USE_LV_CALENDAR != 0
#include "../lv_draw/lv_draw.h"
#include "../lv_hal/lv_hal_indev.h"
#include "../lv_misc/lv_math.h"
#include "../lv_core/lv_indev.h"
#include "../lv_themes/lv_theme.h"
//#include <strings.h>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
enum {
DAY_DRAW_PREV_MONTH,
DAY_DRAW_ACT_MONTH,
DAY_DRAW_NEXT_MONTH,
};
typedef uint8_t day_draw_state_t;
/**********************
* STATIC PROTOTYPES
**********************/
static bool lv_calendar_design(lv_obj_t * calendar, const lv_area_t * mask, lv_design_mode_t mode);
static lv_res_t lv_calendar_signal(lv_obj_t * calendar, lv_signal_t sign, void * param);
static bool calculate_touched_day(lv_obj_t * calendar, const lv_point_t * touched_point);
static lv_coord_t get_header_height(lv_obj_t * calendar);
static lv_coord_t get_day_names_height(lv_obj_t * calendar);
static void draw_header(lv_obj_t * calendar, const lv_area_t * mask);
static void draw_day_names(lv_obj_t * calendar, const lv_area_t * mask);
static void draw_days(lv_obj_t * calendar, const lv_area_t * mask);
static uint8_t get_day_of_week(uint32_t year, uint32_t month, uint32_t day);
static bool is_highlighted(lv_obj_t * calendar, int32_t year, int32_t month, int32_t day);
static const char * get_day_name(lv_obj_t * calendar, uint8_t day);
static const char * get_month_name(lv_obj_t * calendar, int32_t month);
static uint8_t get_month_length(int32_t year, int32_t month);
static uint8_t is_leap_year(uint32_t year);
/**********************
* STATIC VARIABLES
**********************/
static lv_signal_func_t ancestor_signal;
static lv_design_func_t ancestor_design;
static const char * day_name[7] = {"Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"};
static const char * month_name[12] = {"January", "February", "March", "April",
"May", "June", "July", "August",
"September", "October", "November", "December"
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a calendar object
* @param par pointer to an object, it will be the parent of the new calendar
* @param copy pointer to a calendar object, if not NULL then the new object will be copied from it
* @return pointer to the created calendar
*/
lv_obj_t * lv_calendar_create(lv_obj_t * par, const lv_obj_t * copy)
{
LV_LOG_TRACE("calendar create started");
/*Create the ancestor of calendar*/
lv_obj_t * new_calendar = lv_obj_create(par, copy);
lv_mem_assert(new_calendar);
if(new_calendar == NULL) return NULL;
/*Allocate the calendar type specific extended data*/
lv_calendar_ext_t * ext = lv_obj_allocate_ext_attr(new_calendar, sizeof(lv_calendar_ext_t));
lv_mem_assert(ext);
if(ext == NULL) return NULL;
if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_calendar);
if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_func(new_calendar);
/*Initialize the allocated 'ext' */
ext->today.year = 2018;
ext->today.month = 1;
ext->today.day = 1;
ext->showed_date.year = 2018;
ext->showed_date.month = 1;
ext->showed_date.day = 1;
ext->pressed_date.year = 0;
ext->pressed_date.month = 0;
ext->pressed_date.day = 0;
ext->highlighted_dates = NULL;
ext->highlighted_dates_num = 0;
ext->day_names = NULL;
ext->month_names = NULL;
ext->actions[LV_CALENDAR_ACTION_PR] = NULL;
ext->actions[LV_CALENDAR_ACTION_CLICK] = NULL;
ext->actions[LV_CALENDAR_ACTION_LONG_PR] = NULL;
ext->actions[LV_CALENDAR_ACTION_LONG_PR_REPEAT] = NULL;
ext->style_header = &lv_style_plain_color;
ext->style_header_pr = &lv_style_pretty_color;
ext->style_highlighted_days = &lv_style_plain_color;
ext->style_inactive_days = &lv_style_btn_ina;
ext->style_week_box = &lv_style_plain_color;
ext->style_today_box = &lv_style_pretty_color;
ext->style_day_names = &lv_style_pretty;
/*The signal and design functions are not copied so set them here*/
lv_obj_set_signal_func(new_calendar, lv_calendar_signal);
lv_obj_set_design_func(new_calendar, lv_calendar_design);
/*Init the new calendar calendar*/
if(copy == NULL) {
lv_obj_set_size(new_calendar, LV_DPI * 2, LV_DPI * 2);
lv_obj_set_style(new_calendar, &lv_style_pretty);
lv_theme_t * th = lv_theme_get_current();
if(th) {
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_BG, th->calendar.bg);
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_HEADER, th->calendar.header);
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_HEADER_PR, th->calendar.header_pr);
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_DAY_NAMES, th->calendar.day_names);
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_WEEK_BOX, th->calendar.week_box);
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_TODAY_BOX, th->calendar.today_box);
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_HIGHLIGHTED_DAYS, th->calendar.highlighted_days);
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_INACTIVE_DAYS, th->calendar.inactive_days);
} else {
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_BG, &lv_style_pretty);
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_HEADER, ext->style_header);
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_HEADER_PR, ext->style_header_pr);
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_DAY_NAMES, ext->style_day_names);
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_WEEK_BOX, ext->style_week_box);
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_TODAY_BOX, ext->style_today_box);
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_HIGHLIGHTED_DAYS, ext->style_highlighted_days);
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_INACTIVE_DAYS, ext->style_inactive_days);
}
}
/*Copy an existing calendar*/
else {
lv_calendar_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
ext->today.year = copy_ext->today.year;
ext->today.month = copy_ext->today.month;
ext->today.day = copy_ext->today.day;
ext->showed_date.year = copy_ext->showed_date.year;
ext->showed_date.month = copy_ext->showed_date.month;
ext->showed_date.day = copy_ext->showed_date.day;
ext->highlighted_dates = copy_ext->highlighted_dates;
ext->highlighted_dates_num = copy_ext->highlighted_dates_num;
ext->day_names = copy_ext->day_names;
memcpy(ext->actions, copy_ext->actions, sizeof(ext->actions));
ext->month_names = copy_ext->month_names;
ext->style_header = copy_ext->style_header;
ext->style_header_pr = copy_ext->style_header_pr;
ext->style_highlighted_days = copy_ext->style_highlighted_days;
ext->style_inactive_days = copy_ext->style_inactive_days;
ext->style_week_box = copy_ext->style_week_box;
ext->style_today_box = copy_ext->style_today_box;
ext->style_day_names = copy_ext->style_day_names;
/*Refresh the style with new signal function*/
lv_obj_refresh_style(new_calendar);
}
LV_LOG_INFO("calendar created");
return new_calendar;
}
/*======================
* Add/remove functions
*=====================*/
/*
* New object specific "add" or "remove" functions come here
*/
/*=====================
* Setter functions
*====================*/
/**
* Set a function to call when a calendar event happens
* @param calendar pointer to a calendar object
* @param action type of event form 'lv_action_t' (press, release, long press, long press repeat)
*/
void lv_calendar_set_action(lv_obj_t * calendar, lv_calendar_action_t type, lv_action_t action)
{
if(type >= LV_CALENDAR_ACTION_NUM) return;
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
ext->actions[type] = action;
}
/**
* Set the today's date
* @param calendar pointer to a calendar object
* @param today pointer to an `lv_calendar_date_t` variable containing the date of today. The value will be saved it can be local variable too.
*/
void lv_calendar_set_today_date(lv_obj_t * calendar, lv_calendar_date_t * today)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
ext->today.year = today->year;
ext->today.month = today->month;
ext->today.day = today->day;
lv_obj_invalidate(calendar);
}
/**
* Set the currently showed
* @param calendar pointer to a calendar object
* @param showed pointer to an `lv_calendar_date_t` variable containing the date to show. The value will be saved it can be local variable too.
*/
void lv_calendar_set_showed_date(lv_obj_t * calendar, lv_calendar_date_t * showed)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
ext->showed_date.year = showed->year;
ext->showed_date.month = showed->month;
ext->showed_date.day = showed->day;
lv_obj_invalidate(calendar);
}
/**
* Set the the highlighted dates
* @param calendar pointer to a calendar object
* @param highlighted pointer to an `lv_calendar_date_t` array containing the dates. ONLY A POINTER WILL BE SAVED! CAN'T BE LOCAL ARRAY.
* @param date_num number of dates in the array
*/
void lv_calendar_set_highlighted_dates(lv_obj_t * calendar, lv_calendar_date_t * highlighted, uint16_t date_num)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
ext->highlighted_dates = highlighted;
ext->highlighted_dates_num = date_num;
lv_obj_invalidate(calendar);
}
/**
* Set the name of the days
* @param calendar pointer to a calendar object
* @param day_names pointer to an array with the names. E.g. `const char * days[7] = {"Sun", "Mon", ...}`
* Only the pointer will be saved so this variable can't be local which will be destroyed later.
*/
void lv_calendar_set_day_names(lv_obj_t * calendar, const char ** day_names)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
ext->day_names = day_names;
lv_obj_invalidate(calendar);
}
/**
* Set the name of the month
* @param calendar pointer to a calendar object
* @param day_names pointer to an array with the names. E.g. `const char * days[12] = {"Jan", "Feb", ...}`
* Only the pointer will be saved so this variable can't be local which will be destroyed later.
*/
void lv_calendar_set_month_names(lv_obj_t * calendar, const char ** day_names)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
ext->month_names = day_names;
lv_obj_invalidate(calendar);
}
/**
* Set a style of a calendar.
* @param calendar pointer to calendar object
* @param type which style should be set
* @param style pointer to a style
* */
void lv_calendar_set_style(lv_obj_t * calendar, lv_calendar_style_t type, lv_style_t * style)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
switch(type) {
case LV_CALENDAR_STYLE_BG:
lv_obj_set_style(calendar, style);
break;
case LV_CALENDAR_STYLE_DAY_NAMES:
ext->style_day_names = style;
break;
case LV_CALENDAR_STYLE_HEADER:
ext->style_header = style;
break;
case LV_CALENDAR_STYLE_HEADER_PR:
ext->style_header_pr = style;
break;
case LV_CALENDAR_STYLE_HIGHLIGHTED_DAYS:
ext->style_highlighted_days = style;
break;
case LV_CALENDAR_STYLE_INACTIVE_DAYS:
ext->style_inactive_days = style;
break;
case LV_CALENDAR_STYLE_TODAY_BOX:
ext->style_today_box = style;
break;
case LV_CALENDAR_STYLE_WEEK_BOX:
ext->style_week_box = style;
break;
}
lv_obj_invalidate(calendar);
}
/*=====================
* Getter functions
*====================*/
/**
* Get the action of a calendar
* @param calendar pointer to a calendar object
* @return pointer to the action function
*/
lv_action_t lv_calendar_get_action(const lv_obj_t * calendar, lv_calendar_action_t type)
{
if(type >= LV_CALENDAR_ACTION_NUM) return NULL;
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
return ext->actions[type];
}
/**
* Get the today's date
* @param calendar pointer to a calendar object
* @return return pointer to an `lv_calendar_date_t` variable containing the date of today.
*/
lv_calendar_date_t * lv_calendar_get_today_date(const lv_obj_t * calendar)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
return &ext->today;
}
/**
* Get the currently showed
* @param calendar pointer to a calendar object
* @return pointer to an `lv_calendar_date_t` variable containing the date is being shown.
*/
lv_calendar_date_t * lv_calendar_get_showed_date(const lv_obj_t * calendar)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
return &ext->showed_date;
}
/**
* Get the the pressed date.
* @param calendar pointer to a calendar object
* @return pointer to an `lv_calendar_date_t` variable containing the pressed date.
*/
lv_calendar_date_t * lv_calendar_get_pressed_date(const lv_obj_t * calendar)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
return &ext->pressed_date;
}
/**
* Get the the highlighted dates
* @param calendar pointer to a calendar object
* @return pointer to an `lv_calendar_date_t` array containing the dates.
*/
lv_calendar_date_t * lv_calendar_get_highlighted_dates(const lv_obj_t * calendar)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
return ext->highlighted_dates;
}
/**
* Get the number of the highlighted dates
* @param calendar pointer to a calendar object
* @return number of highlighted days
*/
uint16_t lv_calendar_get_highlighted_dates_num(const lv_obj_t * calendar)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
return ext->highlighted_dates_num;
}
/**
* Get the name of the days
* @param calendar pointer to a calendar object
* @return pointer to the array of day names
*/
const char ** lv_calendar_get_day_names(const lv_obj_t * calendar)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
return ext->day_names;
}
/**
* Get the name of the month
* @param calendar pointer to a calendar object
* @return pointer to the array of month names
*/
const char ** lv_calendar_get_month_names(const lv_obj_t * calendar)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
return ext->month_names;
}
/**
* Get style of a calendar.
* @param calendar pointer to calendar object
* @param type which style should be get
* @return style pointer to the style
* */
lv_style_t * lv_calendar_get_style(const lv_obj_t * calendar, lv_calendar_style_t type)
{
lv_style_t * style = NULL;
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
switch(type) {
case LV_CALENDAR_STYLE_BG:
style = lv_obj_get_style(calendar);
break;
case LV_CALENDAR_STYLE_HEADER:
style = ext->style_header;
break;
case LV_CALENDAR_STYLE_HEADER_PR:
style = ext->style_header_pr;
break;
case LV_CALENDAR_STYLE_DAY_NAMES:
style = ext->style_day_names;
break;
case LV_CALENDAR_STYLE_HIGHLIGHTED_DAYS:
style = ext->style_highlighted_days;
break;
case LV_CALENDAR_STYLE_INACTIVE_DAYS:
style = ext->style_inactive_days;
break;
case LV_CALENDAR_STYLE_WEEK_BOX:
style = ext->style_week_box;
break;
case LV_CALENDAR_STYLE_TODAY_BOX:
style = ext->style_today_box;
break;
default:
style = NULL;
break;
}
return style;
}
/*=====================
* Other functions
*====================*/
/*
* New object specific "other" functions come here
*/
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Handle the drawing related tasks of the calendars
* @param calendar pointer to an object
* @param mask the object will be drawn only in this area
* @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
* (return 'true' if yes)
* LV_DESIGN_DRAW: draw the object (always return 'true')
* LV_DESIGN_DRAW_POST: drawing after every children are drawn
* @param return true/false, depends on 'mode'
*/
static bool lv_calendar_design(lv_obj_t * calendar, const lv_area_t * mask, lv_design_mode_t mode)
{
/*Return false if the object is not covers the mask_p area*/
if(mode == LV_DESIGN_COVER_CHK) {
return false;
}
/*Draw the object*/
else if(mode == LV_DESIGN_DRAW_MAIN) {
lv_opa_t opa_scale = lv_obj_get_opa_scale(calendar);
lv_draw_rect(&calendar->coords, mask, lv_calendar_get_style(calendar, LV_CALENDAR_STYLE_BG), opa_scale);
draw_header(calendar, mask);
draw_day_names(calendar, mask);
draw_days(calendar, mask);
}
/*Post draw when the children are drawn*/
else if(mode == LV_DESIGN_DRAW_POST) {
}
return true;
}
/**
* Signal function of the calendar
* @param calendar pointer to a calendar object
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t lv_calendar_signal(lv_obj_t * calendar, lv_signal_t sign, void * param)
{
lv_res_t res;
/* Include the ancient signal function */
res = ancestor_signal(calendar, sign, param);
if(res != LV_RES_OK) return res;
if(sign == LV_SIGNAL_CLEANUP) {
/*Nothing to cleanup. (No dynamically allocated memory in 'ext')*/
} else if(sign == LV_SIGNAL_PRESSED) {
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
/*Call the press action, 'param' is the caller indev_proc*/
if(ext->actions[LV_CALENDAR_ACTION_PR]) {
lv_indev_t * indev = lv_indev_get_act();
lv_point_t p;
lv_indev_get_point(indev, &p);
if(calculate_touched_day(calendar, &p)){
if(ext->btn_pressing != 0) lv_obj_invalidate(calendar);
ext->btn_pressing = 0;
res = ext->actions[LV_CALENDAR_ACTION_PR](calendar);
}
}
} else if(sign == LV_SIGNAL_PRESSING) {
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
lv_area_t header_area;
lv_area_copy(&header_area, &calendar->coords);
header_area.y2 = header_area.y1 + get_header_height(calendar);
lv_indev_t * indev = lv_indev_get_act();
lv_point_t p;
lv_indev_get_point(indev, &p);
if(lv_area_is_point_on(&header_area, &p)) {
if(p.x < header_area.x1 + lv_area_get_width(&header_area) / 2) {
if(ext->btn_pressing != -1) lv_obj_invalidate(calendar);
ext->btn_pressing = -1;
} else {
if(ext->btn_pressing != 1) lv_obj_invalidate(calendar);
ext->btn_pressing = 1;
}
ext->pressed_date.year = 0;
} else if(calculate_touched_day(calendar, &p)) {
if(ext->btn_pressing != 0) lv_obj_invalidate(calendar);
ext->btn_pressing = 0;
} else {
if(ext->btn_pressing != 0) lv_obj_invalidate(calendar);
ext->btn_pressing = 0;
ext->pressed_date.year = 0;
}
} else if(sign == LV_SIGNAL_PRESS_LOST) {
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
ext->pressed_date.year = 0;
ext->btn_pressing = 0;
lv_obj_invalidate(calendar);
} else if(sign == LV_SIGNAL_RELEASED) {
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
if(ext->btn_pressing < 0) {
if(ext->showed_date.month <= 1) {
ext->showed_date.month = 12;
ext->showed_date.year --;
} else {
ext->showed_date.month --;
}
} else if(ext->btn_pressing > 0) {
if(ext->showed_date.month >= 12) {
ext->showed_date.month = 1;
ext->showed_date.year ++;
} else {
ext->showed_date.month ++;
}
}
else if(ext->pressed_date.year != 0)
{
if(ext->actions[LV_CALENDAR_ACTION_CLICK]) {
res = ext->actions[LV_CALENDAR_ACTION_CLICK](calendar);
}
}
ext->pressed_date.year = 0;
ext->btn_pressing = 0;
lv_obj_invalidate(calendar);
} else if(sign == LV_SIGNAL_LONG_PRESS) {
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
if(ext->actions[LV_CALENDAR_ACTION_LONG_PR] && (ext->pressed_date.year != 0)) {
res = ext->actions[LV_CALENDAR_ACTION_LONG_PR](calendar);
}
} else if(sign == LV_SIGNAL_LONG_PRESS_REP) {
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
if(ext->actions[LV_CALENDAR_ACTION_LONG_PR_REPEAT] && (ext->pressed_date.year != 0)) {
res = ext->actions[LV_CALENDAR_ACTION_LONG_PR_REPEAT](calendar);
}
} else if(sign == LV_SIGNAL_CONTROLL) {
uint8_t c = *((uint8_t *) param);
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
if(c == LV_GROUP_KEY_RIGHT || c == LV_GROUP_KEY_UP) {
if(ext->showed_date.month >= 12) {
ext->showed_date.month = 1;
ext->showed_date.year ++;
} else {
ext->showed_date.month ++;
}
lv_obj_invalidate(calendar);
} else if(c == LV_GROUP_KEY_LEFT || c == LV_GROUP_KEY_DOWN) {
if(ext->showed_date.month <= 1) {
ext->showed_date.month = 12;
ext->showed_date.year --;
} else {
ext->showed_date.month --;
}
lv_obj_invalidate(calendar);
}
} else if(sign == LV_SIGNAL_GET_TYPE) {
lv_obj_type_t * buf = param;
uint8_t i;
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set date*/
if(buf->type[i] == NULL) break;
}
buf->type[i] = "lv_calendar";
}
return res;
}
/**
* It will check if the days part of calendar is touched
* and if it is, it will calculate the day and put it in pressed_date of calendar object.
* @param calendar pointer to a calendar object
* @param pointer to a point
* @return true: days part of calendar is touched and its related date is put in pressed date
* false: the point is out of days part area.
*/
static bool calculate_touched_day(lv_obj_t * calendar, const lv_point_t * touched_point)
{
lv_area_t days_area;
lv_area_copy(&days_area, &calendar->coords);
lv_style_t * style_bg = lv_calendar_get_style(calendar, LV_CALENDAR_STYLE_BG);
days_area.x1 += style_bg->body.padding.hor;
days_area.x2 -= style_bg->body.padding.hor;
days_area.y1 = calendar->coords.y1 + get_header_height(calendar) + get_day_names_height(calendar) - style_bg->body.padding.ver;
if(lv_area_is_point_on(&days_area, touched_point)) {
lv_coord_t w = (days_area.x2 - days_area.x1 + 1) / 7;
lv_coord_t h = (days_area.y2 - days_area.y1 + 1) / 6;
uint8_t x_pos = 0;
x_pos = (touched_point->x - days_area.x1) / w;
if(x_pos > 6) x_pos = 6;
uint8_t y_pos = 0;
y_pos = (touched_point->y - days_area.y1) / h;
if(y_pos > 5) y_pos = 5;
uint8_t i_pos = 0;
i_pos = (y_pos * 7) + x_pos;
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
if(i_pos < get_day_of_week(ext->showed_date.year, ext->showed_date.month, 1) ) {
ext->pressed_date.year = ext->showed_date.year - (ext->showed_date.month == 1 ? 1 : 0);
ext->pressed_date.month = ext->showed_date.month == 1 ? 12 : (ext->showed_date.month - 1);
ext->pressed_date.day = get_month_length(ext->pressed_date.year, ext->pressed_date.month) -
get_day_of_week(ext->showed_date.year, ext->showed_date.month, 1) + 1 + i_pos;
}
else if(i_pos < (get_day_of_week(ext->showed_date.year, ext->showed_date.month, 1) +
get_month_length(ext->showed_date.year, ext->showed_date.month))) {
ext->pressed_date.year = ext->showed_date.year;
ext->pressed_date.month = ext->showed_date.month;
ext->pressed_date.day = i_pos + 1 - get_day_of_week(ext->showed_date.year, ext->showed_date.month, 1);
}
else if(i_pos < 42) {
ext->pressed_date.year = ext->showed_date.year + (ext->showed_date.month == 12 ? 1 : 0);
ext->pressed_date.month = ext->showed_date.month == 12 ? 1 : (ext->showed_date.month + 1);
ext->pressed_date.day = i_pos + 1 - get_day_of_week(ext->showed_date.year, ext->showed_date.month, 1)
- get_month_length(ext->showed_date.year, ext->showed_date.month);
}
return true;
}else {
return false;
}
}
/**
* Get the height of a calendar's header based on it's style
* @param calendar point to a calendar
* @return the header's height
*/
static lv_coord_t get_header_height(lv_obj_t * calendar)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
return lv_font_get_height(ext->style_header->text.font) + ext->style_header->body.padding.ver * 2;
}
/**
* Get the height of a calendar's day_names based on it's style
* @param calendar point to a calendar
* @return the day_names's height
*/
static lv_coord_t get_day_names_height(lv_obj_t * calendar)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
return lv_font_get_height(ext->style_day_names->text.font) + ext->style_day_names->body.padding.ver * 2;
}
/**
* Draw the calendar header with month name and arrows
* @param calendar point to a calendar
* @param mask a mask for drawing
*/
static void draw_header(lv_obj_t * calendar, const lv_area_t * mask)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
lv_opa_t opa_scale = lv_obj_get_opa_scale(calendar);
lv_area_t header_area;
header_area.x1 = calendar->coords.x1;
header_area.x2 = calendar->coords.x2;
header_area.y1 = calendar->coords.y1;
header_area.y2 = calendar->coords.y1 + get_header_height(calendar);
lv_draw_rect(&header_area, mask, ext->style_header, opa_scale);
/*Add the year + month name*/
char txt_buf[64];
lv_math_num_to_str(ext->showed_date.year, txt_buf);
txt_buf[4] = ' ';
txt_buf[5] = '\0';
strcpy(&txt_buf[5], get_month_name(calendar, ext->showed_date.month));
header_area.y1 += ext->style_header->body.padding.ver;
lv_draw_label(&header_area, mask, ext->style_header, opa_scale, txt_buf, LV_TXT_FLAG_CENTER, NULL);
/*Add the left arrow*/
lv_style_t * arrow_style = ext->btn_pressing < 0 ? ext->style_header_pr : ext->style_header;
header_area.x1 += ext->style_header->body.padding.hor;
lv_draw_label(&header_area, mask, arrow_style, opa_scale, SYMBOL_LEFT, LV_TXT_FLAG_NONE, NULL);
/*Add the right arrow*/
arrow_style = ext->btn_pressing > 0 ? ext->style_header_pr : ext->style_header;
header_area.x1 = header_area.x2 - ext->style_header->body.padding.hor -
lv_txt_get_width(SYMBOL_RIGHT, strlen(SYMBOL_RIGHT), arrow_style->text.font,
arrow_style->text.line_space, LV_TXT_FLAG_NONE);
lv_draw_label(&header_area, mask, arrow_style, opa_scale, SYMBOL_RIGHT, LV_TXT_FLAG_NONE, NULL);
}
/**
* Draw the day's name below the header
* @param calendar point to a calendar
* @param mask a mask for drawing
*/
static void draw_day_names(lv_obj_t * calendar, const lv_area_t * mask)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
lv_opa_t opa_scale = lv_obj_get_opa_scale(calendar);
lv_coord_t hpad = ext->style_day_names->body.padding.hor;
lv_coord_t w = lv_obj_get_width(calendar) - 2 * hpad;
lv_coord_t box_w = w / 7;
lv_area_t label_area;
label_area.y1 = calendar->coords.y1 + get_header_height(calendar) + ext->style_day_names->body.padding.ver;
label_area.y2 = label_area.y1 + lv_font_get_height(ext->style_day_names->text.font);
uint32_t i;
for(i = 0; i < 7; i++) {
label_area.x1 = calendar->coords.x1 + (w * i) / 7 + hpad;
label_area.x2 = label_area.x1 + box_w;
lv_draw_label(&label_area, mask, ext->style_day_names, opa_scale, get_day_name(calendar, i), LV_TXT_FLAG_CENTER, NULL);
}
}
/**
* Draw the date numbers in a matrix
* @param calendar point to a calendar
* @param mask a mask for drawing
*/
static void draw_days(lv_obj_t * calendar, const lv_area_t * mask)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
lv_style_t * style_bg = lv_calendar_get_style(calendar, LV_CALENDAR_STYLE_BG);
lv_coord_t hpad = style_bg->body.padding.hor;
lv_area_t label_area;
lv_opa_t opa_scale = lv_obj_get_opa_scale(calendar);
label_area.y1 = calendar->coords.y1 + get_header_height(calendar) +
ext->style_day_names->body.padding.ver + lv_font_get_height(ext->style_day_names->text.font) +
ext->style_day_names->body.padding.ver;
label_area.y2 = label_area.y1 + lv_font_get_height(style_bg->text.font);
lv_coord_t w = lv_obj_get_width(calendar) - 2 * hpad;
lv_coord_t h = calendar->coords.y2 - label_area.y1 - style_bg->body.padding.ver;
lv_coord_t box_w = w / 7;
lv_coord_t vert_space = (h - (6 * lv_font_get_height(style_bg->text.font))) / 5;
uint32_t week;
uint8_t day_cnt;
uint8_t month_start_day = get_day_of_week(ext->showed_date.year, ext->showed_date.month, 1);
day_draw_state_t draw_state; /*true: Not the prev. or next month is drawn*/
lv_style_t * act_style;
/*If starting with the first day of the week then the previous month is not visible*/
if(month_start_day == 0) {
day_cnt = 1;
draw_state = DAY_DRAW_ACT_MONTH;
act_style = style_bg;
} else {
draw_state = DAY_DRAW_PREV_MONTH;
day_cnt = get_month_length(ext->showed_date.year, ext->showed_date.month - 1); /*Length of the previous month*/
day_cnt -= month_start_day - 1; /*First visible number of the previous month*/
act_style = ext->style_inactive_days;
}
bool month_of_today_shown = false;
if(ext->showed_date.year == ext->today.year &&
ext->showed_date.month == ext->today.month) {
month_of_today_shown = true;
}
char buf[3];
bool in_week_box = false;
/*Draw 6 weeks*/
for(week = 0; week < 6; week++) {
/*Draw the "week box"*/
if(month_of_today_shown &&
((draw_state == DAY_DRAW_ACT_MONTH && ext->today.day >= day_cnt && ext->today.day < day_cnt + 7) ||
(draw_state == DAY_DRAW_PREV_MONTH && ext->today.day <= 7 - month_start_day && week == 0))) {
lv_area_t week_box_area;
lv_area_copy(&week_box_area, &label_area); /*'label_area' is already set for this row*/
week_box_area.x1 = calendar->coords.x1 + style_bg->body.padding.hor - ext->style_week_box->body.padding.hor;
week_box_area.x2 = calendar->coords.x2 - style_bg->body.padding.hor + ext->style_week_box->body.padding.hor;
week_box_area.y1 -= ext->style_week_box->body.padding.ver;
week_box_area.y2 += ext->style_week_box->body.padding.ver;
lv_draw_rect(&week_box_area, mask, ext->style_week_box, opa_scale);
in_week_box = true;
} else {
in_week_box = false;
}
/*Draw the 7 days of a week*/
uint32_t day;
for(day = 0; day < 7; day++) {
/*The previous month is over*/
if(draw_state == DAY_DRAW_PREV_MONTH && day == month_start_day) {
draw_state = DAY_DRAW_ACT_MONTH;
day_cnt = 1;
act_style = style_bg;
}
/*The current month is over*/
if(draw_state == DAY_DRAW_ACT_MONTH &&
day_cnt > get_month_length(ext->showed_date.year, ext->showed_date.month)) {
draw_state = DAY_DRAW_NEXT_MONTH;
day_cnt = 1;
act_style = ext->style_inactive_days;
}
label_area.x1 = calendar->coords.x1 + (w * day) / 7 + hpad;
label_area.x2 = label_area.x1 + box_w;
/*Draw the "today box"*/
if(draw_state == DAY_DRAW_ACT_MONTH && month_of_today_shown && ext->today.day == day_cnt) {
lv_area_t today_box_area;
lv_area_copy(&today_box_area, &label_area);
today_box_area.x1 = label_area.x1;
today_box_area.x2 = label_area.x2;
today_box_area.y1 = label_area.y1 - ext->style_today_box->body.padding.ver;
today_box_area.y2 = label_area.y2 + ext->style_today_box->body.padding.ver;
lv_draw_rect(&today_box_area, mask, ext->style_today_box, opa_scale);
}
/*Get the final style : highlighted/week box/today box/normal*/
lv_style_t * final_style;
if(draw_state == DAY_DRAW_PREV_MONTH &&
is_highlighted(calendar, ext->showed_date.year - (ext->showed_date.month == 1 ? 1 : 0),
ext->showed_date.month == 1 ? 12 : ext->showed_date.month - 1,
day_cnt)) {
final_style = ext->style_highlighted_days;
} else if(draw_state == DAY_DRAW_ACT_MONTH &&
is_highlighted(calendar, ext->showed_date.year,
ext->showed_date.month,
day_cnt)) {
final_style = ext->style_highlighted_days;
} else if(draw_state == DAY_DRAW_NEXT_MONTH &&
is_highlighted(calendar, ext->showed_date.year + (ext->showed_date.month == 12 ? 1 : 0),
ext->showed_date.month == 12 ? 1 : ext->showed_date.month + 1,
day_cnt)) {
final_style = ext->style_highlighted_days;
} else if(month_of_today_shown && day_cnt == ext->today.day && draw_state == DAY_DRAW_ACT_MONTH) final_style = ext->style_today_box;
else if(in_week_box && draw_state == DAY_DRAW_ACT_MONTH) final_style = ext->style_week_box;
else final_style = act_style;
/*Write the day's number*/
lv_math_num_to_str(day_cnt, buf);
lv_draw_label(&label_area, mask, final_style, opa_scale, buf, LV_TXT_FLAG_CENTER, NULL);
/*Go to the next day*/
day_cnt ++;
}
/*Got to the next weeks row*/
label_area.y1 += vert_space + lv_font_get_height(style_bg->text.font);
label_area.y2 += vert_space + lv_font_get_height(style_bg->text.font);
}
}
/**
* Check weather a date is highlighted or not
* @param calendar pointer to a calendar object
* @param year a year
* @param month a month [1..12]
* @param day a day [1..31]
* @return true: highlighted
*/
static bool is_highlighted(lv_obj_t * calendar, int32_t year, int32_t month, int32_t day)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
if(ext->highlighted_dates == NULL || ext->highlighted_dates_num == 0) return false;
uint32_t i;
for(i = 0; i < ext->highlighted_dates_num; i++) {
if(ext->highlighted_dates[i].year == year &&
ext->highlighted_dates[i].month == month &&
ext->highlighted_dates[i].day == day) {
return true;
}
}
return false;
}
/**
* Get the day name
* @param calendar pointer to a calendar object
* @param day a day in [0..6]
* @return
*/
static const char * get_day_name(lv_obj_t * calendar, uint8_t day)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
if(ext->day_names) return ext->day_names[day];
else return day_name[day];
}
/**
* Get the month name
* @param calendar pointer to a calendar object
* @param month a month. The range is basically [1..12] but [-11..1] is also supported to handle previous year
* @return
*/
static const char * get_month_name(lv_obj_t * calendar, int32_t month)
{
month --; /*Range of months id [1..12] but range of indexes is [0..11]*/
if(month < 0) month = 12 + month;
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
if(ext->month_names) return ext->month_names[month];
else return month_name[month];
}
/**
* Get the number of days in a month
* @param year a year
* @param month a month. The range is basically [1..12] but [-11..1] is also supported to handle previous year
* @return [28..31]
*/
static uint8_t get_month_length(int32_t year, int32_t month)
{
month --; /*Range of months id [1..12] but range of indexes is [0..11]*/
if(month < 0) {
year--; /*Already in the previous year (won't be less then -12 to skip a whole year)*/
month = 12 + month; /*`month` is negative, the result will be < 12*/
}
if(month >= 12) {
year ++;
month -= 12;
}
/*month == 1 is february*/
return (month == 1) ? (28 + is_leap_year(year)) : 31 - month % 7 % 2;
}
/**
* Tells whether a year is leap year or not
* @param year a year
* @return 0: not leap year; 1: leap year
*/
static uint8_t is_leap_year(uint32_t year)
{
return (year % 4) || ((year % 100 == 0) && (year % 400)) ? 0 : 1;
}
/**
* Get the day of the week
* @param year a year
* @param month a month
* @param day a day
* @return [0..6] which means [Sun..Sat]
*/
static uint8_t get_day_of_week(uint32_t year, uint32_t month, uint32_t day)
{
uint32_t a = month < 3 ? 1 : 0;
uint32_t b = year - a;
uint32_t day_of_week = (day + (31 * (month - 2 + 12 * a) / 12) +
b + (b / 4) - (b / 100) + (b / 400)) % 7;
return day_of_week;
}
#endif