/* * timesel.c - Time selector * Copyright (C) 2024 Alexander Rosenberg * * 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 3 of the License, or (at your option) any later * version. See the LICENSE file for more information. */ #include "timesel.h" #include void time_sel_init(TimeSelection *ts, DateSelection *ds, bool do_second) { ts->ds = ds; ts->do_second = do_second; time_sel_reset(ts); } static void time_sel_clamp(TimeSelection *ts) { if (ts->ds) { if (ts->ds->end_time.local_year == ts->ds->year && ts->ds->end_time.local_month == ts->ds->month && ts->ds->end_time.local_day == ts->ds->day) { if (ts->hour > ts->ds->end_time.local_hour) { ts->hour = ts->ds->end_time.local_hour; } if (ts->hour == ts->ds->end_time.local_hour && ts->minute > ts->ds->end_time.local_minute) { ts->minute = ts->ds->end_time.local_minute; } if (ts->hour == ts->ds->end_time.local_hour && ts->minute == ts->ds->end_time.local_minute && ts->second > ts->ds->end_time.local_second) { ts->second = ts->ds->end_time.local_second; } } else if (ts->ds->start_time.local_year == ts->ds->year && ts->ds->start_time.local_month == ts->ds->month && ts->ds->start_time.local_day == ts->ds->day) { if (ts->hour < ts->ds->start_time.local_hour) { ts->hour = ts->ds->start_time.local_hour; } if (ts->hour == ts->ds->start_time.local_hour && ts->minute < ts->ds->start_time.local_minute) { ts->minute = ts->ds->start_time.local_minute; } if (ts->hour == ts->ds->start_time.local_hour && ts->minute == ts->ds->start_time.local_minute && ts->second > ts->ds->start_time.local_second) { ts->second = ts->ds->start_time.local_second; } } } } static void time_sel_add(TimeSelection *ts, int amount) { switch (ts->stage) { case TS_STAGE_HOUR: ts->hour += amount; break; case TS_STAGE_MINUTE: ts->minute += amount; break; case TS_STAGE_SECOND: ts->second += amount; break; } if (ts->second >= 60) { ++ts->minute; } else if (ts->second < 0) { --ts->minute; } if (ts->minute >= 60) { ++ts->hour; } else if (ts->minute < 0) { --ts->hour; } ts->hour %= 24; ts->minute %= 60; ts->second %= 60; if (ts->hour < 0) { ts->hour += 24; } if (ts->minute < 0) { ts->minute += 60; } if (ts->second < 0) { ts->second += 60; } time_sel_clamp(ts); } TimeSelState time_sel_dispatch(TimeSelection *ts, SensorState *state, const char *label) { if (state->back_down) { if (ts->stage != TS_STAGE_HOUR) { --ts->stage; } else { lcd_display_control(state->lcd, LCD_CURSOR_NO_BLINK, LCD_CURSOR_OFF, LCD_DISPLAY_ON); return TIME_SEL_BACK; } } if (state->sel_down) { if ((ts->do_second && ts->stage == TS_STAGE_SECOND) || (!ts->do_second && ts->stage == TS_STAGE_MINUTE)) { lcd_display_control(state->lcd, LCD_CURSOR_NO_BLINK, LCD_CURSOR_OFF, LCD_DISPLAY_ON); return TIME_SEL_DONE; } else { ++ts->stage; } } if (ts->ds) { UtilDate start, end; if (!get_database_limits(state->db, PERIOD_DAY, &start, &end)) { warnx("failed to query database limits"); lcd_display_control(state->lcd, LCD_CURSOR_NO_BLINK, LCD_CURSOR_OFF, LCD_DISPLAY_ON); return TIME_SEL_ERROR; } ts->ds->start_time = start; ts->ds->end_time = end; } time_sel_add(ts, state->up_down - state->down_down); lcd_clear(state->lcd); lcd_move_to(state->lcd, 0, 0); lcd_write_string(state->lcd, label); char time_line[17]; int cursor_pos; if (ts->do_second) { const char *format; switch (ts->stage) { case TS_STAGE_HOUR: format = ">%02d:%02d:%02d"; cursor_pos = 0; break; case TS_STAGE_MINUTE: format = "%02d:>%02d:%02d"; cursor_pos = 3; break; case TS_STAGE_SECOND: format = "%02d:%02d:>%02d"; cursor_pos = 6; break; } snprintf(time_line, 17, format, ts->hour, ts->minute, ts->second); } else { const char *format; switch (ts->stage) { case TS_STAGE_HOUR: format = ">%02d:%02d"; cursor_pos = 0; break; default: // minute or second (which shouldn't happen) format = "%02d:>%02d"; cursor_pos = 3; break; } snprintf(time_line, 17, format, ts->hour, ts->minute); } lcd_move_to(state->lcd, 1, 0); lcd_write_string(state->lcd, time_line); lcd_move_to(state->lcd, 1, cursor_pos); lcd_display_control(state->lcd, LCD_CURSOR_BLINK, LCD_CURSOR_ON, LCD_DISPLAY_ON); return TIME_SEL_CONTINUE; } void time_sel_reset(TimeSelection *ts) { ts->hour = 0; ts->minute = 0; ts->stage = TS_STAGE_HOUR; } int64_t timedate_sel_to_uts_timestamp(DateSelection *ds, TimeSelection *ts) { struct tm broken_time = { .tm_sec = ts->second, .tm_min = ts->minute, .tm_hour = ts->hour, .tm_mday = ds->day, .tm_mon = ds->month - 1, .tm_year = ds->year - 1900, .tm_isdst = -1, }; time_t it = mktime(&broken_time); if (it < 0) { warnx("failed to convert to UTC timestamp"); return 0; } return it; }