mirror of
https://github.com/mudita/MuditaOS.git
synced 2026-04-20 15:07:17 -04:00
Time was incorrectly displayed when opening the "Change date and time" window due to invalid string conversion. Fixed by using different string converting function which does not use printf underneath.
442 lines
18 KiB
C++
442 lines
18 KiB
C++
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
|
|
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
|
|
|
|
#include "TimeWidget.hpp"
|
|
#include "application-calendar/widgets/CalendarStyle.hpp"
|
|
#include <ListView.hpp>
|
|
#include <Style.hpp>
|
|
#include <time/time_conversion.hpp>
|
|
#include <time/time_date_validation.hpp>
|
|
#include "DateAndTimeStyle.hpp"
|
|
#include <module-apps/application-calendar/data/dateCommon.hpp>
|
|
|
|
namespace gui
|
|
{
|
|
namespace date_and_time = style::window::date_and_time;
|
|
|
|
TimeWidget::TimeWidget(Item *parent,
|
|
const std::string &description,
|
|
Type type,
|
|
std::function<void(const UTF8 &text)> bottomBarTemporaryMode,
|
|
std::function<void()> bottomBarRestoreFromTemporaryMode)
|
|
: VBox(parent), mode24H{!utils::dateAndTimeSettings.isTimeFormat12()}, type{type},
|
|
bottomBarTemporaryMode(std::move(bottomBarTemporaryMode)),
|
|
bottomBarRestoreFromTemporaryMode(std::move(bottomBarRestoreFromTemporaryMode))
|
|
{
|
|
setMinimumSize(style::window::default_body_width, date_and_time::height);
|
|
setEdges(RectangleEdge::None);
|
|
|
|
descriptionLabel = new gui::Label(this, 0, 0, 0, 0);
|
|
descriptionLabel->setMinimumSize(style::window::default_body_width, date_and_time::topMargin);
|
|
descriptionLabel->setMargins(gui::Margins(0, 0, 0, date_and_time::topMargin / 4));
|
|
descriptionLabel->setEdges(gui::RectangleEdge::None);
|
|
descriptionLabel->setAlignment(Alignment(gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center));
|
|
descriptionLabel->setFont(style::window::font::small);
|
|
descriptionLabel->activeItem = false;
|
|
|
|
hBox = new gui::HBox(this, 0, 0, 0, 0);
|
|
hBox->setMinimumSize(style::window::default_body_width, date_and_time::hBox_h);
|
|
hBox->setEdges(gui::RectangleEdge::None);
|
|
hBox->activeItem = false;
|
|
|
|
hourInput = new gui::Label(hBox, 0, 0, 0, 0);
|
|
hourInput->setEdges(gui::RectangleEdge::Bottom);
|
|
hourInput->setAlignment(gui::Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Center));
|
|
hourInput->setFont(style::window::font::largelight);
|
|
hourInput->setPenFocusWidth(style::window::default_border_focus_w);
|
|
hourInput->setPenWidth(style::window::default_border_rect_no_focus);
|
|
|
|
colonLabel = new gui::Label(hBox, 0, 0, 0, 0);
|
|
colonLabel->setMinimumSize(date_and_time::separator, date_and_time::hBox_h);
|
|
colonLabel->setEdges(gui::RectangleEdge::None);
|
|
colonLabel->setAlignment(Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Center));
|
|
colonLabel->setFont(style::window::font::medium);
|
|
colonLabel->setText(":");
|
|
colonLabel->activeItem = false;
|
|
|
|
minuteInput = new gui::Label(hBox, 0, 0, 0, 0);
|
|
minuteInput->setEdges(gui::RectangleEdge::Bottom);
|
|
minuteInput->setAlignment(gui::Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Center));
|
|
minuteInput->setFont(style::window::font::largelight);
|
|
minuteInput->setPenFocusWidth(style::window::default_border_focus_w);
|
|
minuteInput->setPenWidth(style::window::default_border_rect_no_focus);
|
|
|
|
descriptionLabel->setText(description);
|
|
|
|
focusChangedCallback = [&](Item &item) {
|
|
setFocusItem(focus ? hBox : nullptr);
|
|
if (!item.focus) {
|
|
validateHour();
|
|
}
|
|
return true;
|
|
};
|
|
|
|
applyInputCallbacks();
|
|
prepareForTimeMode();
|
|
}
|
|
|
|
void TimeWidget::applyInputCallbacks()
|
|
{
|
|
inputCallback = [&](Item &item, const InputEvent &event) {
|
|
auto focusedItem = getFocusItem();
|
|
if (!event.isShortRelease()) {
|
|
return false;
|
|
}
|
|
if (event.is(gui::KeyCode::KEY_ENTER) || event.is(gui::KeyCode::KEY_RF)) {
|
|
return false;
|
|
}
|
|
|
|
if (focusedItem->onInput(event)) {
|
|
if (secondItem != nullptr) {
|
|
uint32_t hours;
|
|
|
|
try {
|
|
hours = std::stoi(hourInput->getText().c_str());
|
|
}
|
|
catch (std::exception &e) {
|
|
LOG_ERROR("applyInputCallbacks hours: %s", e.what());
|
|
return true;
|
|
}
|
|
|
|
auto autofill_hour = hours + 1;
|
|
if (type == Type::Start && !mode24H) {
|
|
if (mode12hInput->getText() == timeConstants::after_noon) {
|
|
if (autofill_hour == utils::time::Locale::max_hour_12H_mode) {
|
|
autofill_hour = utils::time::Locale::max_hour_12H_mode - 1;
|
|
secondItem->minuteInput->setText(std::to_string(utils::time::Locale::max_minutes));
|
|
}
|
|
else {
|
|
secondItem->minuteInput->setText(minuteInput->getText());
|
|
}
|
|
secondItem->mode12hInput->setText(mode12hInput->getText());
|
|
}
|
|
else {
|
|
if (autofill_hour == utils::time::Locale::max_hour_12H_mode) {
|
|
secondItem->mode12hInput->setText(timeConstants::after_noon);
|
|
}
|
|
secondItem->minuteInput->setText(minuteInput->getText());
|
|
}
|
|
if (autofill_hour > utils::time::Locale::max_hour_12H_mode) {
|
|
autofill_hour = 1;
|
|
secondItem->mode12hInput->setText(mode12hInput->getText());
|
|
secondItem->minuteInput->setText(minuteInput->getText());
|
|
}
|
|
secondItem->hourInput->setText(std::to_string(autofill_hour));
|
|
}
|
|
else if (type == Type::Start && mode24H) {
|
|
if (secondItem != nullptr) {
|
|
secondItem->minuteInput->setText(minuteInput->getText());
|
|
if (autofill_hour > utils::time::Locale::max_hour_24H_mode) {
|
|
autofill_hour = utils::time::Locale::max_hour_24H_mode;
|
|
secondItem->minuteInput->setText(minuteInput->getText());
|
|
}
|
|
secondItem->hourInput->setText(std::to_string(autofill_hour));
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
else if (hBox->onInput(event)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
onInputCallback(*hourInput);
|
|
onInputCallback(*minuteInput);
|
|
}
|
|
|
|
void TimeWidget::prepareForTimeMode()
|
|
{
|
|
if (!mode24H) {
|
|
prepareMode12HInputLabel();
|
|
hourInput->setMinimumSize(date_and_time::time_input_12h_w, date_and_time::hBox_h);
|
|
minuteInput->setMinimumSize(date_and_time::time_input_12h_w, date_and_time::hBox_h);
|
|
}
|
|
else {
|
|
hourInput->setMinimumSize(date_and_time::time_input_24h_w, date_and_time::hBox_h);
|
|
minuteInput->setMinimumSize(date_and_time::time_input_24h_w, date_and_time::hBox_h);
|
|
}
|
|
}
|
|
|
|
void TimeWidget::prepareMode12HInputLabel()
|
|
{
|
|
mode12hInput = new gui::Label(hBox, 0, 0, 0, 0);
|
|
mode12hInput->setEdges(gui::RectangleEdge::Bottom);
|
|
mode12hInput->setAlignment(
|
|
gui::Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Center));
|
|
mode12hInput->setFont(style::window::font::largelight);
|
|
mode12hInput->setPenFocusWidth(style::window::default_border_focus_w);
|
|
mode12hInput->setPenWidth(style::window::default_border_rect_no_focus);
|
|
mode12hInput->setText(timeConstants::before_noon);
|
|
mode12hInput->inputCallback = [&](Item &item, const InputEvent &event) {
|
|
if (!event.isShortRelease()) {
|
|
return false;
|
|
}
|
|
if (event.is(gui::KeyCode::KEY_LF)) {
|
|
if (mode12hInput->getText() == timeConstants::before_noon) {
|
|
mode12hInput->setText(timeConstants::after_noon);
|
|
}
|
|
else {
|
|
mode12hInput->setText(timeConstants::before_noon);
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
mode12hInput->focusChangedCallback = [&](Item &item) {
|
|
if (item.focus) {
|
|
bottomBarTemporaryMode(utils::translate("common_switch"));
|
|
}
|
|
else {
|
|
bottomBarRestoreFromTemporaryMode();
|
|
}
|
|
return true;
|
|
};
|
|
|
|
mode12hInput->setMinimumSize(date_and_time::time_input_12h_w, date_and_time::hBox_h);
|
|
mode12hInput->setMargins(gui::Margins(date_and_time::separator, 0, 0, 0));
|
|
}
|
|
|
|
void TimeWidget::setConnectionToSecondItem(gui::TimeWidget *item)
|
|
{
|
|
this->secondItem = item;
|
|
}
|
|
|
|
void TimeWidget::setConnectionToDateItem(gui::DateWidget *item)
|
|
{
|
|
this->dateItem = item;
|
|
}
|
|
|
|
bool TimeWidget::isPm(const std::string &text) const
|
|
{
|
|
return !(text == timeConstants::before_noon);
|
|
}
|
|
|
|
bool TimeWidget::validateHour() const
|
|
{
|
|
if (type == Type::End) {
|
|
if (secondItem == nullptr) {
|
|
LOG_ERROR("secondItem not connected!");
|
|
return false;
|
|
}
|
|
std::chrono::hours start_hour;
|
|
std::chrono::hours end_hour;
|
|
uint32_t start_minutes;
|
|
uint32_t end_minutes;
|
|
try {
|
|
auto hours = std::chrono::hours(std::stoi(secondItem->hourInput->getText().c_str()));
|
|
if (!mode24H) {
|
|
start_hour = date::make24(hours, isPm(secondItem->mode12hInput->getText()));
|
|
}
|
|
else {
|
|
start_hour = hours;
|
|
}
|
|
}
|
|
catch (std::exception &e) {
|
|
LOG_WARN("start_hour: %s", e.what());
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
auto hours = std::chrono::hours(std::stoi(hourInput->getText().c_str()));
|
|
if (!mode24H) {
|
|
end_hour = date::make24(hours, isPm(mode12hInput->getText()));
|
|
}
|
|
else {
|
|
end_hour = hours;
|
|
}
|
|
}
|
|
catch (std::exception &e) {
|
|
LOG_ERROR("end_hour: %s", e.what());
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
start_minutes = std::stoi(secondItem->minuteInput->getText().c_str());
|
|
}
|
|
catch (std::exception &e) {
|
|
LOG_ERROR("start_minutes: %s", e.what());
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
end_minutes = std::stoi(minuteInput->getText().c_str());
|
|
}
|
|
catch (std::exception &e) {
|
|
LOG_ERROR("end_minutes: %s", e.what());
|
|
return false;
|
|
}
|
|
|
|
if (!mode24H) {
|
|
validateHourFor12hMode(start_hour, end_hour, start_minutes, end_minutes);
|
|
}
|
|
else {
|
|
validateHourFor24hMode(start_hour, end_hour, start_minutes, end_minutes);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void TimeWidget::validateHourFor12hMode(std::chrono::hours start_hour,
|
|
std::chrono::minutes end_hour,
|
|
uint32_t start_minutes,
|
|
uint32_t end_minutes) const
|
|
{
|
|
if (start_hour > end_hour || (start_hour == end_hour && start_minutes > end_minutes)) {
|
|
auto hour = start_hour.count() + 1;
|
|
if (secondItem->mode12hInput->getText() == timeConstants::after_noon) {
|
|
if (hour == utils::time::Locale::max_hour_12H_mode) {
|
|
hour = utils::time::Locale::max_hour_12H_mode - 1;
|
|
minuteInput->setText(std::to_string(utils::time::Locale::max_minutes));
|
|
}
|
|
else {
|
|
minuteInput->setText(secondItem->minuteInput->getText());
|
|
}
|
|
mode12hInput->setText(secondItem->mode12hInput->getText());
|
|
}
|
|
else {
|
|
if (hour == utils::time::Locale::max_hour_12H_mode) {
|
|
mode12hInput->setText(timeConstants::after_noon);
|
|
}
|
|
minuteInput->setText(minuteInput->getText());
|
|
}
|
|
if (hour > utils::time::Locale::max_hour_12H_mode) {
|
|
hour = 1;
|
|
mode12hInput->setText(secondItem->mode12hInput->getText());
|
|
minuteInput->setText(secondItem->minuteInput->getText());
|
|
}
|
|
hourInput->setText(utils::to_string(hour));
|
|
}
|
|
}
|
|
|
|
void TimeWidget::validateHourFor24hMode(std::chrono::hours start_hour,
|
|
std::chrono::minutes end_hour,
|
|
uint32_t start_minutes,
|
|
uint32_t end_minutes) const
|
|
{
|
|
if (start_hour > end_hour || (start_hour == end_hour && start_minutes > end_minutes)) {
|
|
auto hour = start_hour.count() + 1;
|
|
if (hour > utils::time::Locale::max_hour_24H_mode) {
|
|
hour = utils::time::Locale::max_hour_24H_mode;
|
|
minuteInput->setText(std::to_string(utils::time::Locale::max_minutes));
|
|
}
|
|
else {
|
|
minuteInput->setText(secondItem->minuteInput->getText());
|
|
}
|
|
hourInput->setText(std::to_string(hour));
|
|
}
|
|
}
|
|
|
|
void TimeWidget::setTime(int keyValue, gui::Label &item)
|
|
{
|
|
auto itemValue = item.getText();
|
|
auto key = std::to_string(keyValue);
|
|
if (itemValue == "0") {
|
|
itemValue = key;
|
|
}
|
|
else {
|
|
itemValue += key;
|
|
}
|
|
item.setText(itemValue);
|
|
|
|
if (!utils::time::validateTime(hourInput->getText(), minuteInput->getText(), !mode24H)) {
|
|
item.setText(key);
|
|
}
|
|
}
|
|
|
|
void TimeWidget::onInputCallback(gui::Label &timeInput)
|
|
{
|
|
timeInput.inputCallback = [&](Item &item, const InputEvent &event) {
|
|
if (!event.isShortRelease()) {
|
|
return false;
|
|
}
|
|
if (event.isDigit()) {
|
|
setTime(event.numericValue(), timeInput);
|
|
return true;
|
|
}
|
|
else if (event.is(gui::KeyCode::KEY_PND)) {
|
|
clearInput(timeInput);
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
}
|
|
|
|
void TimeWidget::clearInput(gui::Label &timeInput)
|
|
{
|
|
if (auto length = timeInput.getText().length(); length > 0) {
|
|
auto value = timeInput.getText();
|
|
value.removeChar(length - 1);
|
|
if (value.length() == 0) {
|
|
value = "0";
|
|
}
|
|
timeInput.setText(value);
|
|
}
|
|
}
|
|
|
|
void TimeWidget::loadData(const std::chrono::hours &hoursFrom,
|
|
const std::chrono::minutes &minutesFrom,
|
|
const std::chrono::hours &hoursTill,
|
|
const std::chrono::minutes &minutesTill)
|
|
{
|
|
if (!mode24H) {
|
|
if (type == Type::Start) {
|
|
const auto hours12H = date::make12(hoursFrom);
|
|
hourInput->setText(utils::to_string(hours12H.count()));
|
|
minuteInput->setText(utils::to_string(minutesFrom.count()));
|
|
if (date::is_am(hoursFrom)) {
|
|
mode12hInput->setText(timeConstants::before_noon);
|
|
}
|
|
else {
|
|
mode12hInput->setText(timeConstants::after_noon);
|
|
}
|
|
}
|
|
else if (type == Type::End) {
|
|
const auto hours12H = date::make12(hoursTill);
|
|
hourInput->setText(utils::to_string(hours12H.count()));
|
|
minuteInput->setText(utils::to_string(minutesTill.count()));
|
|
if (date::is_am(hoursTill)) {
|
|
mode12hInput->setText(timeConstants::before_noon);
|
|
}
|
|
else {
|
|
mode12hInput->setText(timeConstants::after_noon);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (type == Type::Start) {
|
|
hourInput->setText(utils::to_string(hoursFrom.count()));
|
|
minuteInput->setText(utils::to_string(minutesFrom.count()));
|
|
}
|
|
else if (type == Type::End) {
|
|
hourInput->setText(utils::to_string(hoursTill.count()));
|
|
minuteInput->setText(utils::to_string(minutesTill.count()));
|
|
}
|
|
}
|
|
}
|
|
|
|
bool TimeWidget::saveData(std::shared_ptr<utils::time::FromTillDate> fromTillDate) const
|
|
{
|
|
if (!validateHour()) {
|
|
return false;
|
|
}
|
|
auto hours = std::chrono::hours(LocalizedHoursToUtcHours(std::stoi(hourInput->getText().c_str())));
|
|
auto minutes = std::chrono::minutes(std::stoi(minuteInput->getText().c_str()));
|
|
if (!mode24H) {
|
|
hours = date::make24(hours, isPm(mode12hInput->getText()));
|
|
}
|
|
auto date = (dateItem != nullptr) ? TimePointFromYearMonthDay(dateItem->getChosenDate())
|
|
: TimePointFromYearMonthDay(TimePointToYearMonthDay(TimePointNow()));
|
|
if (type == Type::Start) {
|
|
fromTillDate->from = date + hours + minutes;
|
|
}
|
|
else if (type == Type::End) {
|
|
fromTillDate->till = date + hours + minutes;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
} /* namespace gui */
|