blob: 40ebef5dc078f5933056a63c498bd6f8e7f1d857 [file] [log] [blame]
/*
* Copyright (C) 2020 Apple Inc. All rights reserved.
* Copyright (C) 2021 Igalia S.L.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "WebDateTimePickerGtk.h"
#if ENABLE(DATE_AND_TIME_INPUT_TYPES)
#include "WebKitWebViewBasePrivate.h"
#include <gtk/gtk.h>
#include <wtf/SetForScope.h>
#include <wtf/glib/GRefPtr.h>
namespace WebKit {
Ref<WebDateTimePickerGtk> WebDateTimePickerGtk::create(WebPageProxy& page)
{
return adoptRef(*new WebDateTimePickerGtk(page));
}
WebDateTimePickerGtk::~WebDateTimePickerGtk()
{
invalidate();
}
WebDateTimePickerGtk::WebDateTimePickerGtk(WebPageProxy& page)
: WebDateTimePicker(page)
{
}
void WebDateTimePickerGtk::invalidate()
{
if (!m_popover)
return;
g_signal_handlers_disconnect_by_data(m_popover, this);
#if USE(GTK4)
auto* webView = gtk_widget_get_parent(m_popover);
gtk_widget_unparent(m_popover);
#else
auto* webView = gtk_popover_get_relative_to(GTK_POPOVER(m_popover));
gtk_widget_destroy(m_popover);
#endif
m_popover = nullptr;
m_calendar = nullptr;
webkitWebViewBaseSetShouldNotifyFocusEvents(WEBKIT_WEB_VIEW_BASE(webView), true);
}
void WebDateTimePickerGtk::endPicker()
{
invalidate();
WebDateTimePicker::endPicker();
}
static String timeToString(const WebCore::DateComponents& time, WebCore::SecondFormat secondFormat)
{
switch (secondFormat) {
case WebCore::SecondFormat::None:
return makeString(pad('0', 2, time.hour()), ':', pad('0', 2, time.minute()));
case WebCore::SecondFormat::Second:
return makeString(pad('0', 2, time.hour()), ':', pad('0', 2, time.minute()), ':', pad('0', 2, time.second()));
case WebCore::SecondFormat::Millisecond:
return makeString(pad('0', 2, time.hour()), ':', pad('0', 2, time.minute()), ':', pad('0', 2, time.second()), '.', pad('0', 3, time.millisecond()));
}
ASSERT_NOT_REACHED();
return { };
}
static String calendarDateToString(int year, int month, int day, const std::optional<WebCore::DateComponents>& date, WebCore::SecondFormat secondFormat)
{
auto type = date ? date->type() : WebCore::DateComponentsType::Date;
switch (type) {
case WebCore::DateComponentsType::Date:
return makeString(pad('0', 4, year), '-', pad('0', 2, month + 1), '-', pad('0', 2, day));
case WebCore::DateComponentsType::DateTimeLocal:
return makeString(pad('0', 4, year), '-', pad('0', 2, month + 1), '-', pad('0', 2, day), 'T', timeToString(*date, secondFormat));
case WebCore::DateComponentsType::Invalid:
case WebCore::DateComponentsType::Month:
case WebCore::DateComponentsType::Time:
case WebCore::DateComponentsType::Week:
break;
}
ASSERT_NOT_REACHED();
return { };
}
void WebDateTimePickerGtk::didChooseDate()
{
if (m_inUpdate)
return;
if (!m_page)
return;
int year, month, day;
g_object_get(m_calendar, "year", &year, "month", &month, "day", &day, nullptr);
m_page->didChooseDate(calendarDateToString(year, month, day, m_currentDate, m_secondFormat));
}
void WebDateTimePickerGtk::showDateTimePicker(WebCore::DateTimeChooserParameters&& params)
{
if (m_popover) {
update(WTFMove(params));
return;
}
auto* webView = m_page->viewWidget();
webkitWebViewBaseSetShouldNotifyFocusEvents(WEBKIT_WEB_VIEW_BASE(webView), false);
#if USE(GTK4)
m_popover = gtk_popover_new();
gtk_popover_set_has_arrow(GTK_POPOVER(m_popover), FALSE);
gtk_widget_set_parent(m_popover, webView);
#else
m_popover = gtk_popover_new(webView);
#endif
gtk_popover_set_position(GTK_POPOVER(m_popover), GTK_POS_BOTTOM);
GdkRectangle rectInRootView = params.anchorRectInRootView;
gtk_popover_set_pointing_to(GTK_POPOVER(m_popover), &rectInRootView);
g_signal_connect_swapped(m_popover, "closed", G_CALLBACK(+[](WebDateTimePickerGtk* picker) {
picker->endPicker();
}), this);
m_calendar = gtk_calendar_new();
g_signal_connect(m_calendar, "day-selected", G_CALLBACK(+[](GtkCalendar* calendar, WebDateTimePickerGtk* picker) {
picker->didChooseDate();
}), this);
#if USE(GTK4)
gtk_popover_set_child(GTK_POPOVER(m_popover), m_calendar);
#else
gtk_container_add(GTK_CONTAINER(m_popover), m_calendar);
gtk_widget_show(m_calendar);
#endif
update(WTFMove(params));
gtk_popover_popup(GTK_POPOVER(m_popover));
}
void WebDateTimePickerGtk::update(WebCore::DateTimeChooserParameters&& params)
{
SetForScope<bool> inUpdate(m_inUpdate, true);
if (params.type == "date")
m_currentDate = WebCore::DateComponents::fromParsingDate(params.currentValue);
else if (params.type == "datetime-local")
m_currentDate = WebCore::DateComponents::fromParsingDateTimeLocal(params.currentValue);
if (m_currentDate)
g_object_set(m_calendar, "year", m_currentDate->fullYear(), "month", m_currentDate->month(), "day", m_currentDate->monthDay(), nullptr);
else if (params.type == "datetime-local") {
GRefPtr<GDateTime> now = adoptGRef(g_date_time_new_now_local());
Seconds unixTime = Seconds(g_date_time_to_unix(now.get())) + Seconds::fromMicroseconds(g_date_time_get_utc_offset(now.get()));
m_currentDate = WebCore::DateComponents::fromMillisecondsSinceEpochForDateTimeLocal(unixTime.milliseconds());
if (params.hasMillisecondField)
m_secondFormat = WebCore::SecondFormat::Millisecond;
else if (params.hasSecondField)
m_secondFormat = WebCore::SecondFormat::Second;
else
m_secondFormat = WebCore::SecondFormat::None;
}
}
} // namespace WebKit
#endif // ENABLE(DATE_AND_TIME_INPUT_TYPES)