Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
CTCaer
GitHub Repository: CTCaer/hekate
Path: blob/master/bdk/libs/lvgl/lv_objx/lv_calendar.c
1476 views
1
/**
2
* @file lv_calendar.c
3
*
4
*/
5
6
/*********************
7
* INCLUDES
8
*********************/
9
#include "lv_calendar.h"
10
#if USE_LV_CALENDAR != 0
11
12
#include "../lv_draw/lv_draw.h"
13
#include "../lv_hal/lv_hal_indev.h"
14
#include "../lv_misc/lv_math.h"
15
#include "../lv_core/lv_indev.h"
16
#include "../lv_themes/lv_theme.h"
17
//#include <strings.h>
18
19
/*********************
20
* DEFINES
21
*********************/
22
23
/**********************
24
* TYPEDEFS
25
**********************/
26
enum {
27
DAY_DRAW_PREV_MONTH,
28
DAY_DRAW_ACT_MONTH,
29
DAY_DRAW_NEXT_MONTH,
30
};
31
typedef uint8_t day_draw_state_t;
32
33
/**********************
34
* STATIC PROTOTYPES
35
**********************/
36
static bool lv_calendar_design(lv_obj_t * calendar, const lv_area_t * mask, lv_design_mode_t mode);
37
static lv_res_t lv_calendar_signal(lv_obj_t * calendar, lv_signal_t sign, void * param);
38
static bool calculate_touched_day(lv_obj_t * calendar, const lv_point_t * touched_point);
39
static lv_coord_t get_header_height(lv_obj_t * calendar);
40
static lv_coord_t get_day_names_height(lv_obj_t * calendar);
41
static void draw_header(lv_obj_t * calendar, const lv_area_t * mask);
42
static void draw_day_names(lv_obj_t * calendar, const lv_area_t * mask);
43
static void draw_days(lv_obj_t * calendar, const lv_area_t * mask);
44
static uint8_t get_day_of_week(uint32_t year, uint32_t month, uint32_t day);
45
static bool is_highlighted(lv_obj_t * calendar, int32_t year, int32_t month, int32_t day);
46
static const char * get_day_name(lv_obj_t * calendar, uint8_t day);
47
static const char * get_month_name(lv_obj_t * calendar, int32_t month);
48
static uint8_t get_month_length(int32_t year, int32_t month);
49
static uint8_t is_leap_year(uint32_t year);
50
51
52
/**********************
53
* STATIC VARIABLES
54
**********************/
55
static lv_signal_func_t ancestor_signal;
56
static lv_design_func_t ancestor_design;
57
static const char * day_name[7] = {"Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"};
58
static const char * month_name[12] = {"January", "February", "March", "April",
59
"May", "June", "July", "August",
60
"September", "October", "November", "December"
61
};
62
63
/**********************
64
* MACROS
65
**********************/
66
67
/**********************
68
* GLOBAL FUNCTIONS
69
**********************/
70
71
/**
72
* Create a calendar object
73
* @param par pointer to an object, it will be the parent of the new calendar
74
* @param copy pointer to a calendar object, if not NULL then the new object will be copied from it
75
* @return pointer to the created calendar
76
*/
77
lv_obj_t * lv_calendar_create(lv_obj_t * par, const lv_obj_t * copy)
78
{
79
LV_LOG_TRACE("calendar create started");
80
81
/*Create the ancestor of calendar*/
82
lv_obj_t * new_calendar = lv_obj_create(par, copy);
83
lv_mem_assert(new_calendar);
84
if(new_calendar == NULL) return NULL;
85
86
/*Allocate the calendar type specific extended data*/
87
lv_calendar_ext_t * ext = lv_obj_allocate_ext_attr(new_calendar, sizeof(lv_calendar_ext_t));
88
lv_mem_assert(ext);
89
if(ext == NULL) return NULL;
90
if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_calendar);
91
if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_func(new_calendar);
92
93
/*Initialize the allocated 'ext' */
94
ext->today.year = 2018;
95
ext->today.month = 1;
96
ext->today.day = 1;
97
98
ext->showed_date.year = 2018;
99
ext->showed_date.month = 1;
100
ext->showed_date.day = 1;
101
102
ext->pressed_date.year = 0;
103
ext->pressed_date.month = 0;
104
ext->pressed_date.day = 0;
105
106
ext->highlighted_dates = NULL;
107
ext->highlighted_dates_num = 0;
108
ext->day_names = NULL;
109
ext->month_names = NULL;
110
ext->actions[LV_CALENDAR_ACTION_PR] = NULL;
111
ext->actions[LV_CALENDAR_ACTION_CLICK] = NULL;
112
ext->actions[LV_CALENDAR_ACTION_LONG_PR] = NULL;
113
ext->actions[LV_CALENDAR_ACTION_LONG_PR_REPEAT] = NULL;
114
ext->style_header = &lv_style_plain_color;
115
ext->style_header_pr = &lv_style_pretty_color;
116
ext->style_highlighted_days = &lv_style_plain_color;
117
ext->style_inactive_days = &lv_style_btn_ina;
118
ext->style_week_box = &lv_style_plain_color;
119
ext->style_today_box = &lv_style_pretty_color;
120
ext->style_day_names = &lv_style_pretty;
121
122
/*The signal and design functions are not copied so set them here*/
123
lv_obj_set_signal_func(new_calendar, lv_calendar_signal);
124
lv_obj_set_design_func(new_calendar, lv_calendar_design);
125
126
/*Init the new calendar calendar*/
127
if(copy == NULL) {
128
lv_obj_set_size(new_calendar, LV_DPI * 2, LV_DPI * 2);
129
lv_obj_set_style(new_calendar, &lv_style_pretty);
130
131
lv_theme_t * th = lv_theme_get_current();
132
if(th) {
133
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_BG, th->calendar.bg);
134
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_HEADER, th->calendar.header);
135
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_HEADER_PR, th->calendar.header_pr);
136
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_DAY_NAMES, th->calendar.day_names);
137
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_WEEK_BOX, th->calendar.week_box);
138
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_TODAY_BOX, th->calendar.today_box);
139
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_HIGHLIGHTED_DAYS, th->calendar.highlighted_days);
140
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_INACTIVE_DAYS, th->calendar.inactive_days);
141
} else {
142
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_BG, &lv_style_pretty);
143
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_HEADER, ext->style_header);
144
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_HEADER_PR, ext->style_header_pr);
145
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_DAY_NAMES, ext->style_day_names);
146
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_WEEK_BOX, ext->style_week_box);
147
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_TODAY_BOX, ext->style_today_box);
148
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_HIGHLIGHTED_DAYS, ext->style_highlighted_days);
149
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_INACTIVE_DAYS, ext->style_inactive_days);
150
151
}
152
153
}
154
/*Copy an existing calendar*/
155
else {
156
lv_calendar_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
157
ext->today.year = copy_ext->today.year;
158
ext->today.month = copy_ext->today.month;
159
ext->today.day = copy_ext->today.day;
160
161
ext->showed_date.year = copy_ext->showed_date.year;
162
ext->showed_date.month = copy_ext->showed_date.month;
163
ext->showed_date.day = copy_ext->showed_date.day;
164
165
ext->highlighted_dates = copy_ext->highlighted_dates;
166
ext->highlighted_dates_num = copy_ext->highlighted_dates_num;
167
ext->day_names = copy_ext->day_names;
168
169
memcpy(ext->actions, copy_ext->actions, sizeof(ext->actions));
170
171
ext->month_names = copy_ext->month_names;
172
ext->style_header = copy_ext->style_header;
173
ext->style_header_pr = copy_ext->style_header_pr;
174
ext->style_highlighted_days = copy_ext->style_highlighted_days;
175
ext->style_inactive_days = copy_ext->style_inactive_days;
176
ext->style_week_box = copy_ext->style_week_box;
177
ext->style_today_box = copy_ext->style_today_box;
178
ext->style_day_names = copy_ext->style_day_names;
179
/*Refresh the style with new signal function*/
180
lv_obj_refresh_style(new_calendar);
181
}
182
183
LV_LOG_INFO("calendar created");
184
185
return new_calendar;
186
}
187
188
/*======================
189
* Add/remove functions
190
*=====================*/
191
192
/*
193
* New object specific "add" or "remove" functions come here
194
*/
195
196
197
/*=====================
198
* Setter functions
199
*====================*/
200
201
/**
202
* Set a function to call when a calendar event happens
203
* @param calendar pointer to a calendar object
204
* @param action type of event form 'lv_action_t' (press, release, long press, long press repeat)
205
*/
206
void lv_calendar_set_action(lv_obj_t * calendar, lv_calendar_action_t type, lv_action_t action)
207
{
208
if(type >= LV_CALENDAR_ACTION_NUM) return;
209
210
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
211
ext->actions[type] = action;
212
}
213
214
/**
215
* Set the today's date
216
* @param calendar pointer to a calendar object
217
* @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.
218
*/
219
void lv_calendar_set_today_date(lv_obj_t * calendar, lv_calendar_date_t * today)
220
{
221
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
222
ext->today.year = today->year;
223
ext->today.month = today->month;
224
ext->today.day = today->day;
225
226
lv_obj_invalidate(calendar);
227
}
228
229
/**
230
* Set the currently showed
231
* @param calendar pointer to a calendar object
232
* @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.
233
*/
234
void lv_calendar_set_showed_date(lv_obj_t * calendar, lv_calendar_date_t * showed)
235
{
236
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
237
ext->showed_date.year = showed->year;
238
ext->showed_date.month = showed->month;
239
ext->showed_date.day = showed->day;
240
241
lv_obj_invalidate(calendar);
242
}
243
244
/**
245
* Set the the highlighted dates
246
* @param calendar pointer to a calendar object
247
* @param highlighted pointer to an `lv_calendar_date_t` array containing the dates. ONLY A POINTER WILL BE SAVED! CAN'T BE LOCAL ARRAY.
248
* @param date_num number of dates in the array
249
*/
250
void lv_calendar_set_highlighted_dates(lv_obj_t * calendar, lv_calendar_date_t * highlighted, uint16_t date_num)
251
{
252
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
253
ext->highlighted_dates = highlighted;
254
ext->highlighted_dates_num = date_num;
255
256
lv_obj_invalidate(calendar);
257
}
258
259
260
/**
261
* Set the name of the days
262
* @param calendar pointer to a calendar object
263
* @param day_names pointer to an array with the names. E.g. `const char * days[7] = {"Sun", "Mon", ...}`
264
* Only the pointer will be saved so this variable can't be local which will be destroyed later.
265
*/
266
void lv_calendar_set_day_names(lv_obj_t * calendar, const char ** day_names)
267
{
268
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
269
ext->day_names = day_names;
270
lv_obj_invalidate(calendar);
271
}
272
273
/**
274
* Set the name of the month
275
* @param calendar pointer to a calendar object
276
* @param day_names pointer to an array with the names. E.g. `const char * days[12] = {"Jan", "Feb", ...}`
277
* Only the pointer will be saved so this variable can't be local which will be destroyed later.
278
*/
279
void lv_calendar_set_month_names(lv_obj_t * calendar, const char ** day_names)
280
{
281
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
282
ext->month_names = day_names;
283
lv_obj_invalidate(calendar);
284
}
285
286
/**
287
* Set a style of a calendar.
288
* @param calendar pointer to calendar object
289
* @param type which style should be set
290
* @param style pointer to a style
291
* */
292
void lv_calendar_set_style(lv_obj_t * calendar, lv_calendar_style_t type, lv_style_t * style)
293
{
294
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
295
296
switch(type) {
297
case LV_CALENDAR_STYLE_BG:
298
lv_obj_set_style(calendar, style);
299
break;
300
case LV_CALENDAR_STYLE_DAY_NAMES:
301
ext->style_day_names = style;
302
break;
303
case LV_CALENDAR_STYLE_HEADER:
304
ext->style_header = style;
305
break;
306
case LV_CALENDAR_STYLE_HEADER_PR:
307
ext->style_header_pr = style;
308
break;
309
case LV_CALENDAR_STYLE_HIGHLIGHTED_DAYS:
310
ext->style_highlighted_days = style;
311
break;
312
case LV_CALENDAR_STYLE_INACTIVE_DAYS:
313
ext->style_inactive_days = style;
314
break;
315
case LV_CALENDAR_STYLE_TODAY_BOX:
316
ext->style_today_box = style;
317
break;
318
case LV_CALENDAR_STYLE_WEEK_BOX:
319
ext->style_week_box = style;
320
break;
321
}
322
323
lv_obj_invalidate(calendar);
324
}
325
326
/*=====================
327
* Getter functions
328
*====================*/
329
330
/**
331
* Get the action of a calendar
332
* @param calendar pointer to a calendar object
333
* @return pointer to the action function
334
*/
335
lv_action_t lv_calendar_get_action(const lv_obj_t * calendar, lv_calendar_action_t type)
336
{
337
if(type >= LV_CALENDAR_ACTION_NUM) return NULL;
338
339
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
340
return ext->actions[type];
341
}
342
343
/**
344
* Get the today's date
345
* @param calendar pointer to a calendar object
346
* @return return pointer to an `lv_calendar_date_t` variable containing the date of today.
347
*/
348
lv_calendar_date_t * lv_calendar_get_today_date(const lv_obj_t * calendar)
349
{
350
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
351
return &ext->today;
352
}
353
354
/**
355
* Get the currently showed
356
* @param calendar pointer to a calendar object
357
* @return pointer to an `lv_calendar_date_t` variable containing the date is being shown.
358
*/
359
lv_calendar_date_t * lv_calendar_get_showed_date(const lv_obj_t * calendar)
360
{
361
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
362
return &ext->showed_date;
363
}
364
365
/**
366
* Get the the pressed date.
367
* @param calendar pointer to a calendar object
368
* @return pointer to an `lv_calendar_date_t` variable containing the pressed date.
369
*/
370
lv_calendar_date_t * lv_calendar_get_pressed_date(const lv_obj_t * calendar)
371
{
372
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
373
return &ext->pressed_date;
374
}
375
376
/**
377
* Get the the highlighted dates
378
* @param calendar pointer to a calendar object
379
* @return pointer to an `lv_calendar_date_t` array containing the dates.
380
*/
381
lv_calendar_date_t * lv_calendar_get_highlighted_dates(const lv_obj_t * calendar)
382
{
383
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
384
return ext->highlighted_dates;
385
}
386
387
/**
388
* Get the number of the highlighted dates
389
* @param calendar pointer to a calendar object
390
* @return number of highlighted days
391
*/
392
uint16_t lv_calendar_get_highlighted_dates_num(const lv_obj_t * calendar)
393
{
394
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
395
return ext->highlighted_dates_num;
396
}
397
398
/**
399
* Get the name of the days
400
* @param calendar pointer to a calendar object
401
* @return pointer to the array of day names
402
*/
403
const char ** lv_calendar_get_day_names(const lv_obj_t * calendar)
404
{
405
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
406
return ext->day_names;
407
}
408
409
/**
410
* Get the name of the month
411
* @param calendar pointer to a calendar object
412
* @return pointer to the array of month names
413
*/
414
const char ** lv_calendar_get_month_names(const lv_obj_t * calendar)
415
{
416
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
417
return ext->month_names;
418
}
419
420
/**
421
* Get style of a calendar.
422
* @param calendar pointer to calendar object
423
* @param type which style should be get
424
* @return style pointer to the style
425
* */
426
lv_style_t * lv_calendar_get_style(const lv_obj_t * calendar, lv_calendar_style_t type)
427
{
428
lv_style_t * style = NULL;
429
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
430
431
switch(type) {
432
case LV_CALENDAR_STYLE_BG:
433
style = lv_obj_get_style(calendar);
434
break;
435
case LV_CALENDAR_STYLE_HEADER:
436
style = ext->style_header;
437
break;
438
case LV_CALENDAR_STYLE_HEADER_PR:
439
style = ext->style_header_pr;
440
break;
441
case LV_CALENDAR_STYLE_DAY_NAMES:
442
style = ext->style_day_names;
443
break;
444
case LV_CALENDAR_STYLE_HIGHLIGHTED_DAYS:
445
style = ext->style_highlighted_days;
446
break;
447
case LV_CALENDAR_STYLE_INACTIVE_DAYS:
448
style = ext->style_inactive_days;
449
break;
450
case LV_CALENDAR_STYLE_WEEK_BOX:
451
style = ext->style_week_box;
452
break;
453
case LV_CALENDAR_STYLE_TODAY_BOX:
454
style = ext->style_today_box;
455
break;
456
default:
457
style = NULL;
458
break;
459
}
460
461
return style;
462
}
463
464
/*=====================
465
* Other functions
466
*====================*/
467
468
/*
469
* New object specific "other" functions come here
470
*/
471
472
/**********************
473
* STATIC FUNCTIONS
474
**********************/
475
476
/**
477
* Handle the drawing related tasks of the calendars
478
* @param calendar pointer to an object
479
* @param mask the object will be drawn only in this area
480
* @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
481
* (return 'true' if yes)
482
* LV_DESIGN_DRAW: draw the object (always return 'true')
483
* LV_DESIGN_DRAW_POST: drawing after every children are drawn
484
* @param return true/false, depends on 'mode'
485
*/
486
static bool lv_calendar_design(lv_obj_t * calendar, const lv_area_t * mask, lv_design_mode_t mode)
487
{
488
/*Return false if the object is not covers the mask_p area*/
489
if(mode == LV_DESIGN_COVER_CHK) {
490
return false;
491
}
492
/*Draw the object*/
493
else if(mode == LV_DESIGN_DRAW_MAIN) {
494
lv_opa_t opa_scale = lv_obj_get_opa_scale(calendar);
495
lv_draw_rect(&calendar->coords, mask, lv_calendar_get_style(calendar, LV_CALENDAR_STYLE_BG), opa_scale);
496
497
draw_header(calendar, mask);
498
draw_day_names(calendar, mask);
499
draw_days(calendar, mask);
500
501
}
502
/*Post draw when the children are drawn*/
503
else if(mode == LV_DESIGN_DRAW_POST) {
504
505
}
506
507
return true;
508
}
509
510
/**
511
* Signal function of the calendar
512
* @param calendar pointer to a calendar object
513
* @param sign a signal type from lv_signal_t enum
514
* @param param pointer to a signal specific variable
515
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
516
*/
517
static lv_res_t lv_calendar_signal(lv_obj_t * calendar, lv_signal_t sign, void * param)
518
{
519
lv_res_t res;
520
521
/* Include the ancient signal function */
522
res = ancestor_signal(calendar, sign, param);
523
if(res != LV_RES_OK) return res;
524
525
526
if(sign == LV_SIGNAL_CLEANUP) {
527
/*Nothing to cleanup. (No dynamically allocated memory in 'ext')*/
528
} else if(sign == LV_SIGNAL_PRESSED) {
529
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
530
/*Call the press action, 'param' is the caller indev_proc*/
531
if(ext->actions[LV_CALENDAR_ACTION_PR]) {
532
lv_indev_t * indev = lv_indev_get_act();
533
lv_point_t p;
534
lv_indev_get_point(indev, &p);
535
536
if(calculate_touched_day(calendar, &p)){
537
if(ext->btn_pressing != 0) lv_obj_invalidate(calendar);
538
ext->btn_pressing = 0;
539
res = ext->actions[LV_CALENDAR_ACTION_PR](calendar);
540
}
541
}
542
} else if(sign == LV_SIGNAL_PRESSING) {
543
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
544
lv_area_t header_area;
545
lv_area_copy(&header_area, &calendar->coords);
546
header_area.y2 = header_area.y1 + get_header_height(calendar);
547
548
lv_indev_t * indev = lv_indev_get_act();
549
lv_point_t p;
550
lv_indev_get_point(indev, &p);
551
552
if(lv_area_is_point_on(&header_area, &p)) {
553
if(p.x < header_area.x1 + lv_area_get_width(&header_area) / 2) {
554
if(ext->btn_pressing != -1) lv_obj_invalidate(calendar);
555
ext->btn_pressing = -1;
556
} else {
557
if(ext->btn_pressing != 1) lv_obj_invalidate(calendar);
558
ext->btn_pressing = 1;
559
}
560
561
ext->pressed_date.year = 0;
562
} else if(calculate_touched_day(calendar, &p)) {
563
if(ext->btn_pressing != 0) lv_obj_invalidate(calendar);
564
ext->btn_pressing = 0;
565
} else {
566
if(ext->btn_pressing != 0) lv_obj_invalidate(calendar);
567
ext->btn_pressing = 0;
568
ext->pressed_date.year = 0;
569
}
570
} else if(sign == LV_SIGNAL_PRESS_LOST) {
571
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
572
ext->pressed_date.year = 0;
573
ext->btn_pressing = 0;
574
lv_obj_invalidate(calendar);
575
576
} else if(sign == LV_SIGNAL_RELEASED) {
577
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
578
if(ext->btn_pressing < 0) {
579
if(ext->showed_date.month <= 1) {
580
ext->showed_date.month = 12;
581
ext->showed_date.year --;
582
} else {
583
ext->showed_date.month --;
584
}
585
} else if(ext->btn_pressing > 0) {
586
if(ext->showed_date.month >= 12) {
587
ext->showed_date.month = 1;
588
ext->showed_date.year ++;
589
} else {
590
ext->showed_date.month ++;
591
}
592
}
593
else if(ext->pressed_date.year != 0)
594
{
595
if(ext->actions[LV_CALENDAR_ACTION_CLICK]) {
596
res = ext->actions[LV_CALENDAR_ACTION_CLICK](calendar);
597
}
598
}
599
600
ext->pressed_date.year = 0;
601
ext->btn_pressing = 0;
602
lv_obj_invalidate(calendar);
603
604
605
} else if(sign == LV_SIGNAL_LONG_PRESS) {
606
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
607
if(ext->actions[LV_CALENDAR_ACTION_LONG_PR] && (ext->pressed_date.year != 0)) {
608
res = ext->actions[LV_CALENDAR_ACTION_LONG_PR](calendar);
609
}
610
} else if(sign == LV_SIGNAL_LONG_PRESS_REP) {
611
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
612
if(ext->actions[LV_CALENDAR_ACTION_LONG_PR_REPEAT] && (ext->pressed_date.year != 0)) {
613
res = ext->actions[LV_CALENDAR_ACTION_LONG_PR_REPEAT](calendar);
614
}
615
} else if(sign == LV_SIGNAL_CONTROLL) {
616
uint8_t c = *((uint8_t *) param);
617
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
618
if(c == LV_GROUP_KEY_RIGHT || c == LV_GROUP_KEY_UP) {
619
if(ext->showed_date.month >= 12) {
620
ext->showed_date.month = 1;
621
ext->showed_date.year ++;
622
} else {
623
ext->showed_date.month ++;
624
}
625
lv_obj_invalidate(calendar);
626
} else if(c == LV_GROUP_KEY_LEFT || c == LV_GROUP_KEY_DOWN) {
627
if(ext->showed_date.month <= 1) {
628
ext->showed_date.month = 12;
629
ext->showed_date.year --;
630
} else {
631
ext->showed_date.month --;
632
}
633
lv_obj_invalidate(calendar);
634
}
635
} else if(sign == LV_SIGNAL_GET_TYPE) {
636
lv_obj_type_t * buf = param;
637
uint8_t i;
638
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set date*/
639
if(buf->type[i] == NULL) break;
640
}
641
buf->type[i] = "lv_calendar";
642
}
643
644
return res;
645
}
646
647
/**
648
* It will check if the days part of calendar is touched
649
* and if it is, it will calculate the day and put it in pressed_date of calendar object.
650
* @param calendar pointer to a calendar object
651
* @param pointer to a point
652
* @return true: days part of calendar is touched and its related date is put in pressed date
653
* false: the point is out of days part area.
654
*/
655
static bool calculate_touched_day(lv_obj_t * calendar, const lv_point_t * touched_point)
656
{
657
lv_area_t days_area;
658
lv_area_copy(&days_area, &calendar->coords);
659
lv_style_t * style_bg = lv_calendar_get_style(calendar, LV_CALENDAR_STYLE_BG);
660
days_area.x1 += style_bg->body.padding.hor;
661
days_area.x2 -= style_bg->body.padding.hor;
662
days_area.y1 = calendar->coords.y1 + get_header_height(calendar) + get_day_names_height(calendar) - style_bg->body.padding.ver;
663
664
if(lv_area_is_point_on(&days_area, touched_point)) {
665
lv_coord_t w = (days_area.x2 - days_area.x1 + 1) / 7;
666
lv_coord_t h = (days_area.y2 - days_area.y1 + 1) / 6;
667
uint8_t x_pos = 0;
668
x_pos = (touched_point->x - days_area.x1) / w;
669
if(x_pos > 6) x_pos = 6;
670
uint8_t y_pos = 0;
671
y_pos = (touched_point->y - days_area.y1) / h;
672
if(y_pos > 5) y_pos = 5;
673
674
uint8_t i_pos = 0;
675
i_pos = (y_pos * 7) + x_pos;
676
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
677
if(i_pos < get_day_of_week(ext->showed_date.year, ext->showed_date.month, 1) ) {
678
ext->pressed_date.year = ext->showed_date.year - (ext->showed_date.month == 1 ? 1 : 0);
679
ext->pressed_date.month = ext->showed_date.month == 1 ? 12 : (ext->showed_date.month - 1);
680
ext->pressed_date.day = get_month_length(ext->pressed_date.year, ext->pressed_date.month) -
681
get_day_of_week(ext->showed_date.year, ext->showed_date.month, 1) + 1 + i_pos;
682
}
683
else if(i_pos < (get_day_of_week(ext->showed_date.year, ext->showed_date.month, 1) +
684
get_month_length(ext->showed_date.year, ext->showed_date.month))) {
685
ext->pressed_date.year = ext->showed_date.year;
686
ext->pressed_date.month = ext->showed_date.month;
687
ext->pressed_date.day = i_pos + 1 - get_day_of_week(ext->showed_date.year, ext->showed_date.month, 1);
688
}
689
else if(i_pos < 42) {
690
ext->pressed_date.year = ext->showed_date.year + (ext->showed_date.month == 12 ? 1 : 0);
691
ext->pressed_date.month = ext->showed_date.month == 12 ? 1 : (ext->showed_date.month + 1);
692
ext->pressed_date.day = i_pos + 1 - get_day_of_week(ext->showed_date.year, ext->showed_date.month, 1)
693
- get_month_length(ext->showed_date.year, ext->showed_date.month);
694
}
695
return true;
696
}else {
697
return false;
698
}
699
}
700
701
/**
702
* Get the height of a calendar's header based on it's style
703
* @param calendar point to a calendar
704
* @return the header's height
705
*/
706
static lv_coord_t get_header_height(lv_obj_t * calendar)
707
{
708
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
709
710
return lv_font_get_height(ext->style_header->text.font) + ext->style_header->body.padding.ver * 2;
711
}
712
713
/**
714
* Get the height of a calendar's day_names based on it's style
715
* @param calendar point to a calendar
716
* @return the day_names's height
717
*/
718
static lv_coord_t get_day_names_height(lv_obj_t * calendar)
719
{
720
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
721
722
return lv_font_get_height(ext->style_day_names->text.font) + ext->style_day_names->body.padding.ver * 2;
723
}
724
725
/**
726
* Draw the calendar header with month name and arrows
727
* @param calendar point to a calendar
728
* @param mask a mask for drawing
729
*/
730
static void draw_header(lv_obj_t * calendar, const lv_area_t * mask)
731
{
732
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
733
lv_opa_t opa_scale = lv_obj_get_opa_scale(calendar);
734
735
lv_area_t header_area;
736
header_area.x1 = calendar->coords.x1;
737
header_area.x2 = calendar->coords.x2;
738
header_area.y1 = calendar->coords.y1;
739
header_area.y2 = calendar->coords.y1 + get_header_height(calendar);
740
741
lv_draw_rect(&header_area, mask, ext->style_header, opa_scale);
742
743
/*Add the year + month name*/
744
char txt_buf[64];
745
lv_math_num_to_str(ext->showed_date.year, txt_buf);
746
txt_buf[4] = ' ';
747
txt_buf[5] = '\0';
748
strcpy(&txt_buf[5], get_month_name(calendar, ext->showed_date.month));
749
header_area.y1 += ext->style_header->body.padding.ver;
750
lv_draw_label(&header_area, mask, ext->style_header, opa_scale, txt_buf, LV_TXT_FLAG_CENTER, NULL);
751
752
/*Add the left arrow*/
753
lv_style_t * arrow_style = ext->btn_pressing < 0 ? ext->style_header_pr : ext->style_header;
754
header_area.x1 += ext->style_header->body.padding.hor;
755
lv_draw_label(&header_area, mask, arrow_style, opa_scale, SYMBOL_LEFT, LV_TXT_FLAG_NONE, NULL);
756
757
/*Add the right arrow*/
758
arrow_style = ext->btn_pressing > 0 ? ext->style_header_pr : ext->style_header;
759
header_area.x1 = header_area.x2 - ext->style_header->body.padding.hor -
760
lv_txt_get_width(SYMBOL_RIGHT, strlen(SYMBOL_RIGHT), arrow_style->text.font,
761
arrow_style->text.line_space, LV_TXT_FLAG_NONE);
762
lv_draw_label(&header_area, mask, arrow_style, opa_scale, SYMBOL_RIGHT, LV_TXT_FLAG_NONE, NULL);
763
764
}
765
766
/**
767
* Draw the day's name below the header
768
* @param calendar point to a calendar
769
* @param mask a mask for drawing
770
*/
771
static void draw_day_names(lv_obj_t * calendar, const lv_area_t * mask)
772
{
773
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
774
lv_opa_t opa_scale = lv_obj_get_opa_scale(calendar);
775
776
lv_coord_t hpad = ext->style_day_names->body.padding.hor;
777
lv_coord_t w = lv_obj_get_width(calendar) - 2 * hpad;
778
lv_coord_t box_w = w / 7;
779
lv_area_t label_area;
780
label_area.y1 = calendar->coords.y1 + get_header_height(calendar) + ext->style_day_names->body.padding.ver;
781
label_area.y2 = label_area.y1 + lv_font_get_height(ext->style_day_names->text.font);
782
uint32_t i;
783
for(i = 0; i < 7; i++) {
784
label_area.x1 = calendar->coords.x1 + (w * i) / 7 + hpad;
785
label_area.x2 = label_area.x1 + box_w;
786
lv_draw_label(&label_area, mask, ext->style_day_names, opa_scale, get_day_name(calendar, i), LV_TXT_FLAG_CENTER, NULL);
787
}
788
789
}
790
791
/**
792
* Draw the date numbers in a matrix
793
* @param calendar point to a calendar
794
* @param mask a mask for drawing
795
*/
796
static void draw_days(lv_obj_t * calendar, const lv_area_t * mask)
797
{
798
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
799
lv_style_t * style_bg = lv_calendar_get_style(calendar, LV_CALENDAR_STYLE_BG);
800
lv_coord_t hpad = style_bg->body.padding.hor;
801
lv_area_t label_area;
802
lv_opa_t opa_scale = lv_obj_get_opa_scale(calendar);
803
label_area.y1 = calendar->coords.y1 + get_header_height(calendar) +
804
ext->style_day_names->body.padding.ver + lv_font_get_height(ext->style_day_names->text.font) +
805
ext->style_day_names->body.padding.ver;
806
label_area.y2 = label_area.y1 + lv_font_get_height(style_bg->text.font);
807
808
lv_coord_t w = lv_obj_get_width(calendar) - 2 * hpad;
809
lv_coord_t h = calendar->coords.y2 - label_area.y1 - style_bg->body.padding.ver;
810
lv_coord_t box_w = w / 7;
811
lv_coord_t vert_space = (h - (6 * lv_font_get_height(style_bg->text.font))) / 5;
812
813
uint32_t week;
814
uint8_t day_cnt;
815
uint8_t month_start_day = get_day_of_week(ext->showed_date.year, ext->showed_date.month, 1);
816
day_draw_state_t draw_state; /*true: Not the prev. or next month is drawn*/
817
lv_style_t * act_style;
818
819
/*If starting with the first day of the week then the previous month is not visible*/
820
if(month_start_day == 0) {
821
day_cnt = 1;
822
draw_state = DAY_DRAW_ACT_MONTH;
823
act_style = style_bg;
824
} else {
825
draw_state = DAY_DRAW_PREV_MONTH;
826
day_cnt = get_month_length(ext->showed_date.year, ext->showed_date.month - 1); /*Length of the previous month*/
827
day_cnt -= month_start_day - 1; /*First visible number of the previous month*/
828
act_style = ext->style_inactive_days;
829
}
830
831
832
bool month_of_today_shown = false;
833
if(ext->showed_date.year == ext->today.year &&
834
ext->showed_date.month == ext->today.month) {
835
month_of_today_shown = true;
836
}
837
838
char buf[3];
839
bool in_week_box = false;
840
841
/*Draw 6 weeks*/
842
for(week = 0; week < 6; week++) {
843
844
/*Draw the "week box"*/
845
if(month_of_today_shown &&
846
((draw_state == DAY_DRAW_ACT_MONTH && ext->today.day >= day_cnt && ext->today.day < day_cnt + 7) ||
847
(draw_state == DAY_DRAW_PREV_MONTH && ext->today.day <= 7 - month_start_day && week == 0))) {
848
lv_area_t week_box_area;
849
lv_area_copy(&week_box_area, &label_area); /*'label_area' is already set for this row*/
850
week_box_area.x1 = calendar->coords.x1 + style_bg->body.padding.hor - ext->style_week_box->body.padding.hor;
851
week_box_area.x2 = calendar->coords.x2 - style_bg->body.padding.hor + ext->style_week_box->body.padding.hor;
852
853
week_box_area.y1 -= ext->style_week_box->body.padding.ver;
854
week_box_area.y2 += ext->style_week_box->body.padding.ver;
855
lv_draw_rect(&week_box_area, mask, ext->style_week_box, opa_scale);
856
857
in_week_box = true;
858
} else {
859
in_week_box = false;
860
}
861
862
/*Draw the 7 days of a week*/
863
uint32_t day;
864
for(day = 0; day < 7; day++) {
865
/*The previous month is over*/
866
if(draw_state == DAY_DRAW_PREV_MONTH && day == month_start_day) {
867
draw_state = DAY_DRAW_ACT_MONTH;
868
day_cnt = 1;
869
act_style = style_bg;
870
}
871
/*The current month is over*/
872
if(draw_state == DAY_DRAW_ACT_MONTH &&
873
day_cnt > get_month_length(ext->showed_date.year, ext->showed_date.month)) {
874
draw_state = DAY_DRAW_NEXT_MONTH;
875
day_cnt = 1;
876
act_style = ext->style_inactive_days;
877
}
878
879
label_area.x1 = calendar->coords.x1 + (w * day) / 7 + hpad;
880
label_area.x2 = label_area.x1 + box_w;
881
882
/*Draw the "today box"*/
883
if(draw_state == DAY_DRAW_ACT_MONTH && month_of_today_shown && ext->today.day == day_cnt) {
884
lv_area_t today_box_area;
885
lv_area_copy(&today_box_area, &label_area);
886
today_box_area.x1 = label_area.x1;
887
today_box_area.x2 = label_area.x2;
888
889
today_box_area.y1 = label_area.y1 - ext->style_today_box->body.padding.ver;
890
today_box_area.y2 = label_area.y2 + ext->style_today_box->body.padding.ver;
891
lv_draw_rect(&today_box_area, mask, ext->style_today_box, opa_scale);
892
}
893
894
/*Get the final style : highlighted/week box/today box/normal*/
895
lv_style_t * final_style;
896
if(draw_state == DAY_DRAW_PREV_MONTH &&
897
is_highlighted(calendar, ext->showed_date.year - (ext->showed_date.month == 1 ? 1 : 0),
898
ext->showed_date.month == 1 ? 12 : ext->showed_date.month - 1,
899
day_cnt)) {
900
final_style = ext->style_highlighted_days;
901
} else if(draw_state == DAY_DRAW_ACT_MONTH &&
902
is_highlighted(calendar, ext->showed_date.year,
903
ext->showed_date.month,
904
day_cnt)) {
905
final_style = ext->style_highlighted_days;
906
} else if(draw_state == DAY_DRAW_NEXT_MONTH &&
907
is_highlighted(calendar, ext->showed_date.year + (ext->showed_date.month == 12 ? 1 : 0),
908
ext->showed_date.month == 12 ? 1 : ext->showed_date.month + 1,
909
day_cnt)) {
910
final_style = ext->style_highlighted_days;
911
} else if(month_of_today_shown && day_cnt == ext->today.day && draw_state == DAY_DRAW_ACT_MONTH) final_style = ext->style_today_box;
912
else if(in_week_box && draw_state == DAY_DRAW_ACT_MONTH) final_style = ext->style_week_box;
913
else final_style = act_style;
914
915
/*Write the day's number*/
916
lv_math_num_to_str(day_cnt, buf);
917
lv_draw_label(&label_area, mask, final_style, opa_scale, buf, LV_TXT_FLAG_CENTER, NULL);
918
919
/*Go to the next day*/
920
day_cnt ++;
921
922
}
923
924
/*Got to the next weeks row*/
925
label_area.y1 += vert_space + lv_font_get_height(style_bg->text.font);
926
label_area.y2 += vert_space + lv_font_get_height(style_bg->text.font);
927
}
928
}
929
930
/**
931
* Check weather a date is highlighted or not
932
* @param calendar pointer to a calendar object
933
* @param year a year
934
* @param month a month [1..12]
935
* @param day a day [1..31]
936
* @return true: highlighted
937
*/
938
static bool is_highlighted(lv_obj_t * calendar, int32_t year, int32_t month, int32_t day)
939
{
940
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
941
942
if(ext->highlighted_dates == NULL || ext->highlighted_dates_num == 0) return false;
943
944
uint32_t i;
945
for(i = 0; i < ext->highlighted_dates_num; i++) {
946
if(ext->highlighted_dates[i].year == year &&
947
ext->highlighted_dates[i].month == month &&
948
ext->highlighted_dates[i].day == day) {
949
return true;
950
}
951
}
952
953
return false;
954
}
955
956
/**
957
* Get the day name
958
* @param calendar pointer to a calendar object
959
* @param day a day in [0..6]
960
* @return
961
*/
962
static const char * get_day_name(lv_obj_t * calendar, uint8_t day)
963
{
964
965
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
966
if(ext->day_names) return ext->day_names[day];
967
else return day_name[day];
968
}
969
970
/**
971
* Get the month name
972
* @param calendar pointer to a calendar object
973
* @param month a month. The range is basically [1..12] but [-11..1] is also supported to handle previous year
974
* @return
975
*/
976
static const char * get_month_name(lv_obj_t * calendar, int32_t month)
977
{
978
month --; /*Range of months id [1..12] but range of indexes is [0..11]*/
979
if(month < 0) month = 12 + month;
980
981
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
982
if(ext->month_names) return ext->month_names[month];
983
else return month_name[month];
984
}
985
986
/**
987
* Get the number of days in a month
988
* @param year a year
989
* @param month a month. The range is basically [1..12] but [-11..1] is also supported to handle previous year
990
* @return [28..31]
991
*/
992
static uint8_t get_month_length(int32_t year, int32_t month)
993
{
994
month --; /*Range of months id [1..12] but range of indexes is [0..11]*/
995
if(month < 0) {
996
year--; /*Already in the previous year (won't be less then -12 to skip a whole year)*/
997
month = 12 + month; /*`month` is negative, the result will be < 12*/
998
}
999
if(month >= 12) {
1000
year ++;
1001
month -= 12;
1002
}
1003
1004
/*month == 1 is february*/
1005
return (month == 1) ? (28 + is_leap_year(year)) : 31 - month % 7 % 2;
1006
1007
1008
}
1009
1010
/**
1011
* Tells whether a year is leap year or not
1012
* @param year a year
1013
* @return 0: not leap year; 1: leap year
1014
*/
1015
static uint8_t is_leap_year(uint32_t year)
1016
{
1017
return (year % 4) || ((year % 100 == 0) && (year % 400)) ? 0 : 1;
1018
}
1019
1020
/**
1021
* Get the day of the week
1022
* @param year a year
1023
* @param month a month
1024
* @param day a day
1025
* @return [0..6] which means [Sun..Sat]
1026
*/
1027
static uint8_t get_day_of_week(uint32_t year, uint32_t month, uint32_t day)
1028
{
1029
uint32_t a = month < 3 ? 1 : 0;
1030
uint32_t b = year - a;
1031
1032
uint32_t day_of_week = (day + (31 * (month - 2 + 12 * a) / 12) +
1033
b + (b / 4) - (b / 100) + (b / 400)) % 7;
1034
1035
return day_of_week;
1036
}
1037
1038
#endif
1039
1040