1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-20 17:35:07 +00:00
serenity/Userland/Applications/Calendar/AddEventDialog.cpp
david072 a84778f6ee Calendar/AddEventDialog: Prevent inputting invalid date ranges
The calendar now verifies that the user can't input invalid date ranges,
i.e. date ranges where the end is before the start. The UI elements do
this implicitly, by adjusting the values when changing the dates in an
illegal way (e.g. when picking an end date that is before the start
date, the end date will change to the start date).
2024-01-06 10:37:53 -07:00

162 lines
6.5 KiB
C++

/*
* Copyright (c) 2019-2020, Ryan Grieb <ryan.m.grieb@gmail.com>
* Copyright (c) 2022-2023, the SerenityOS developers.
* Copyright (c) 2023, David Ganz <david.g.ganz@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "AddEventDialog.h"
#include <Applications/Calendar/AddEventDialogGML.h>
#include <LibCore/DateTime.h>
#include <LibGUI/BoxLayout.h>
#include <LibGUI/Button.h>
#include <LibGUI/ComboBox.h>
#include <LibGUI/DatePicker.h>
#include <LibGUI/Label.h>
#include <LibGUI/MessageBox.h>
#include <LibGUI/Painter.h>
#include <LibGUI/SpinBox.h>
#include <LibGUI/TextBox.h>
#include <LibGUI/Widget.h>
#include <LibGUI/Window.h>
#include <LibGfx/Color.h>
#include <LibGfx/Font/FontDatabase.h>
namespace Calendar {
static constexpr StringView DATE_FORMAT = "%Y-%m-%d"sv;
AddEventDialog::AddEventDialog(Core::DateTime date_time, EventManager& event_manager, Window* parent_window)
: Dialog(parent_window)
, m_start_date_time(date_time)
, m_end_date_time(Core::DateTime::from_timestamp(date_time.timestamp() + (15 * 60)))
, m_event_manager(event_manager)
{
resize(360, 140);
set_title("Add Event");
set_resizable(false);
set_icon(parent_window->icon());
dbgln("start time: {}", m_start_date_time.to_string().release_value_but_fixme_should_propagate_errors());
dbgln("end time: {}", m_end_date_time.to_string().release_value_but_fixme_should_propagate_errors());
m_start_date_time = Core::DateTime::create(m_start_date_time.year(), m_start_date_time.month(), m_start_date_time.day(), 12, 0);
auto widget = set_main_widget<GUI::Widget>();
widget->load_from_gml(add_event_dialog_gml).release_value_but_fixme_should_propagate_errors();
auto& event_title_textbox = *widget->find_descendant_of_type_named<GUI::TextBox>("event_title_textbox");
event_title_textbox.set_focus(true);
m_start_date_box = *widget->find_descendant_of_type_named<GUI::TextBox>("start_date");
auto calendar_date_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/calendar-date.png"sv).release_value_but_fixme_should_propagate_errors();
auto& pick_start_date_button = *widget->find_descendant_of_type_named<GUI::Button>("pick_start_date");
pick_start_date_button.set_icon(calendar_date_icon);
pick_start_date_button.on_click = [&](auto) {
if (auto new_date = GUI::DatePicker::show(this, "Pick Start Date"_string, m_start_date_time); new_date.has_value()) {
m_start_date_time.set_date(new_date.value());
if (m_end_date_time < m_start_date_time) {
m_end_date_time.set_date(new_date.value());
update_end_date();
}
m_start_date_box->set_text(MUST(m_start_date_time.to_string(DATE_FORMAT)));
}
};
m_start_hour_box = *widget->find_descendant_of_type_named<GUI::SpinBox>("start_hour");
m_start_minute_box = *widget->find_descendant_of_type_named<GUI::SpinBox>("start_minute");
m_end_date_box = *widget->find_descendant_of_type_named<GUI::TextBox>("end_date");
auto& pick_end_date_button = *widget->find_descendant_of_type_named<GUI::Button>("pick_end_date");
pick_end_date_button.set_icon(calendar_date_icon);
pick_end_date_button.on_click = [&](auto) {
if (auto new_date = GUI::DatePicker::show(this, "Pick End Date"_string, m_end_date_time); new_date.has_value()) {
m_end_date_time.set_date(new_date.value());
if (m_end_date_time < m_start_date_time) {
m_start_date_time.set_date(new_date.value());
update_start_date();
}
m_end_date_box->set_text(MUST(m_end_date_time.to_string(DATE_FORMAT)));
}
};
m_end_hour_box = *widget->find_descendant_of_type_named<GUI::SpinBox>("end_hour");
m_end_minute_box = *widget->find_descendant_of_type_named<GUI::SpinBox>("end_minute");
auto& ok_button = *widget->find_descendant_of_type_named<GUI::Button>("ok_button");
ok_button.on_click = [&](auto) {
auto successful = add_event_to_calendar().release_value_but_fixme_should_propagate_errors();
if (!successful)
return;
done(ExecResult::OK);
};
auto& cancel_button = *widget->find_descendant_of_type_named<GUI::Button>("cancel_button");
cancel_button.on_click = [&](auto) { done(ExecResult::Cancel); };
auto update_starting_input_values = [&, this]() {
auto hour = m_start_hour_box->value();
auto minute = m_start_minute_box->value();
m_start_date_time.set_time_only(hour, minute);
if (m_end_date_time < m_start_date_time) {
m_end_date_time.set_time_only(hour, minute);
update_end_date();
}
};
auto update_ending_input_values = [&, this]() {
auto hour = m_end_hour_box->value();
auto minute = m_end_minute_box->value();
m_end_date_time.set_time_only(hour, minute);
if (m_end_date_time < m_start_date_time) {
m_start_date_time.set_time_only(hour, minute);
update_start_date();
}
};
m_start_hour_box->on_change = [update_starting_input_values](auto) { update_starting_input_values(); };
m_start_minute_box->on_change = [update_starting_input_values](auto) { update_starting_input_values(); };
m_end_hour_box->on_change = [update_ending_input_values](auto) { update_ending_input_values(); };
m_end_minute_box->on_change = [update_ending_input_values](auto) { update_ending_input_values(); };
update_start_date();
update_end_date();
}
ErrorOr<bool> AddEventDialog::add_event_to_calendar()
{
if (m_end_date_time < m_start_date_time) {
GUI::MessageBox::show_error(this, "The end date has to be after the start date."sv);
return false;
}
auto summary = find_descendant_of_type_named<GUI::TextBox>("event_title_textbox")->get_text();
m_event_manager.add_event(Event {
.summary = TRY(String::from_byte_string(summary)),
.start = m_start_date_time,
.end = m_end_date_time,
});
return true;
}
void AddEventDialog::update_start_date()
{
m_start_date_box->set_text(MUST(m_start_date_time.to_string(DATE_FORMAT)));
m_start_hour_box->set_value(m_start_date_time.hour(), GUI::AllowCallback::No);
m_start_minute_box->set_value(m_start_date_time.minute(), GUI::AllowCallback::No);
}
void AddEventDialog::update_end_date()
{
m_end_date_box->set_text(MUST(m_end_date_time.to_string(DATE_FORMAT)));
m_end_hour_box->set_value(m_end_date_time.hour(), GUI::AllowCallback::No);
m_end_minute_box->set_value(m_end_date_time.minute(), GUI::AllowCallback::No);
}
}