1 // Copyright 2022 Samsung Electronics. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "input_picker_base.h"
7 #include <Elementary.h>
10 #include "base/files/file_path.h"
11 #include "base/logging.h"
12 #include "base/path_service.h"
13 #include "content/browser/date_time_chooser_efl.h"
14 #include "content/common/paths_efl.h"
15 #include "content/public/browser/web_contents.h"
16 #include "tizen/system_info.h"
18 #if BUILDFLAG(IS_TIZEN)
19 #include <vconf/vconf.h>
24 static const char* kDefaultDatetimeFormat = "%Y/%m/%d %H:%M";
26 InputPickerBase::Layout::Layout(InputPickerBase* parent) : parent_(parent) {
27 evas_object_focus_set(parent_->evas_object_, false);
28 // FIXME: Workaround. OSP requirement. OSP want to block own touch event
29 // while webkit internal picker is running.
30 evas_object_smart_callback_call(parent_->evas_object_, "input,picker,show",
34 InputPickerBase::Layout::~Layout() {
35 // FIXME: Workaround. OSP requirement. OSP want to block own touch event
36 // while webkit internal picker is running.
37 evas_object_smart_callback_call(parent_->evas_object_, "input,picker,hide",
39 evas_object_focus_set(parent_->evas_object_, true);
45 DeleteColorPickerCallbacks();
47 DeleteDatePickerCallbacks();
49 evas_object_del(conformant_);
50 conformant_ = nullptr;
54 InputPickerBase::Layout*
55 InputPickerBase::Layout::CreateAndShowColorPickerLayout(InputPickerBase* parent,
59 std::unique_ptr<Layout> picker_layout(new Layout(parent));
60 picker_layout->is_color_picker_ = true;
62 if (!picker_layout->AddBaseLayout(
63 dgettext("WebKit", "IDS_WEBVIEW_HEADER_SELECT_COLOUR"),
64 "colorselector_popup_layout")) {
68 picker_layout->color_rect_ =
69 evas_object_rectangle_add(evas_object_evas_get(picker_layout->layout_));
70 if (!picker_layout->color_rect_)
73 evas_object_size_hint_weight_set(picker_layout->color_rect_, EVAS_HINT_EXPAND,
75 evas_object_show(picker_layout->color_rect_);
76 evas_object_color_set(picker_layout->color_rect_, r, g, b, 255);
77 elm_object_part_content_set(picker_layout->layout_, "rect",
78 picker_layout->color_rect_);
80 if (!picker_layout->AddColorSelector(r, g, b) || !picker_layout->AddButtons())
83 picker_layout->red_ = r;
84 picker_layout->green_ = g;
85 picker_layout->blue_ = b;
87 picker_layout->AddColorPickerCallbacks();
88 evas_object_show(picker_layout->popup_);
89 return picker_layout.release();
92 static char* GetDateTimeFormat() {
93 #if BUILDFLAG(IS_TIZEN)
94 char* language = vconf_get_str(VCONFKEY_LANGSET);
95 if (!vconf_set_str(VCONFKEY_LANGSET, "en_US"))
96 LOG(ERROR) << "vconf_set_str failed ";
98 char* region_format = vconf_get_str(VCONFKEY_REGIONFORMAT);
106 vconf_get_int(VCONFKEY_REGIONFORMAT_TIME1224, &time_value);
107 if (time_value == VCONFKEY_TIME_FORMAT_24)
108 snprintf(buf, sizeof(buf), "%s_DTFMT_24HR", region_format);
110 snprintf(buf, sizeof(buf), "%s_DTFMT_12HR", region_format);
114 // FIXME: Workaround fix for region format.
115 int buf_length = strlen(buf);
116 for (int i = 0; i < buf_length - 4; i++) {
117 if (buf[i] == 'u' && buf[i + 1] == 't' && buf[i + 2] == 'f') {
118 if (buf[i + 3] == '8') {
120 for (int j = buf_length; j > i + 3; j--)
123 buf[buf_length + 1] = '\0';
124 } else if (buf[i + 3] == '-' && buf[i + 4] == '8') {
137 char* date_time_format = dgettext("dt_fmt", buf);
138 if (language && !vconf_set_str(VCONFKEY_LANGSET, language))
139 LOG(ERROR) << "vconf_set_str failed ";
141 // FIXME: Workaround fix for not supported dt_fmt.
142 // Use default format if dt_fmt string is not exist.
143 if (strlen(date_time_format) == strlen(buf) &&
144 !strncmp(date_time_format, buf, strlen(buf))) {
148 return strdup(date_time_format);
155 InputPickerBase::Layout* InputPickerBase::Layout::CreateAndShowDateLayout(
156 InputPickerBase* parent,
157 struct tm* current_time,
158 ui::TextInputType type) {
159 std::unique_ptr<Layout> picker_layout(new Layout(parent));
160 picker_layout->input_type_ = type;
164 case ui::TEXT_INPUT_TYPE_DATE: {
165 title = "IDS_WEBVIEW_HEADER_SET_DATE";
168 case ui::TEXT_INPUT_TYPE_WEEK: {
169 title = "IDS_WEBVIEW_HEADER_SET_WEEK";
172 case ui::TEXT_INPUT_TYPE_MONTH: {
173 title = "IDS_WEBVIEW_HEADER_SET_MONTH";
181 if (!picker_layout->AddBaseLayout(dgettext("WebKit", title.c_str()),
186 picker_layout->date_picker_ = elm_datetime_add(picker_layout->layout_);
187 if (!picker_layout->date_picker_)
190 elm_object_part_content_set(picker_layout->layout_, "elm.swallow.datetime",
191 picker_layout->date_picker_);
193 char* format = GetDateTimeFormat();
195 elm_datetime_format_set(picker_layout->date_picker_, format);
198 elm_datetime_format_set(picker_layout->date_picker_,
199 kDefaultDatetimeFormat);
202 elm_datetime_value_set(picker_layout->date_picker_, current_time);
204 if (!picker_layout->AddButtons())
207 if (type == ui::TEXT_INPUT_TYPE_MONTH) {
208 elm_datetime_field_visible_set(picker_layout->date_picker_,
209 ELM_DATETIME_DATE, EINA_FALSE);
211 elm_datetime_field_visible_set(picker_layout->date_picker_, ELM_DATETIME_HOUR,
213 elm_datetime_field_visible_set(picker_layout->date_picker_,
214 ELM_DATETIME_MINUTE, EINA_FALSE);
216 picker_layout->AddDatePickerCallbacks();
218 evas_object_show(picker_layout->popup_);
220 return picker_layout.release();
224 InputPickerBase::Layout* InputPickerBase::Layout::CreateAndShowDateTimeLayout(
225 InputPickerBase* parent,
226 struct tm* current_time,
227 ui::TextInputType type) {
228 std::unique_ptr<Layout> picker_layout(new Layout(parent));
229 picker_layout->input_type_ = type;
230 elm_object_scale_set(picker_layout->popup_, 0.7);
232 if (!picker_layout->AddBaseLayout(
233 dgettext("WebKit", "IDS_WEBVIEW_HEADER_SET_DATE_AND_TIME"),
238 picker_layout->time_picker_ = elm_datetime_add(picker_layout->layout_);
239 picker_layout->date_picker_ = elm_datetime_add(picker_layout->layout_);
240 if (!picker_layout->time_picker_ || !picker_layout->date_picker_)
243 elm_object_part_content_set(picker_layout->layout_, "elm.swallow.datetime",
244 picker_layout->time_picker_);
245 elm_object_part_content_set(picker_layout->layout_, "elm.swallow.datetime2",
246 picker_layout->date_picker_);
247 elm_object_style_set(picker_layout->time_picker_, "time_layout");
249 char* format = GetDateTimeFormat();
251 elm_datetime_format_set(picker_layout->date_picker_, format);
252 elm_datetime_format_set(picker_layout->time_picker_, format);
255 elm_datetime_format_set(picker_layout->date_picker_,
256 kDefaultDatetimeFormat);
257 elm_datetime_format_set(picker_layout->time_picker_,
258 kDefaultDatetimeFormat);
261 elm_datetime_value_set(picker_layout->date_picker_, current_time);
262 elm_datetime_value_set(picker_layout->time_picker_, current_time);
263 if (!picker_layout->AddButtons())
266 picker_layout->AddDatePickerCallbacks();
267 evas_object_show(picker_layout->popup_);
269 return picker_layout.release();
272 bool InputPickerBase::Layout::SetDatetimePicker(Evas_Object* picker,
278 InputPickerBase::Layout* InputPickerBase::Layout::CreateAndShowTimeLayout(
279 InputPickerBase* parent,
280 struct tm* current_time) {
281 std::unique_ptr<Layout> picker_layout(new Layout(parent));
282 picker_layout->input_type_ = ui::TEXT_INPUT_TYPE_TIME;
284 if (!picker_layout->AddBaseLayout(
285 dgettext("WebKit", "IDS_WEBVIEW_HEADER_SET_TIME"), "date_popup")) {
289 picker_layout->time_picker_ = elm_datetime_add(picker_layout->layout_);
290 if (!picker_layout->time_picker_)
293 elm_object_style_set(picker_layout->time_picker_, "time_layout");
294 elm_object_part_content_set(picker_layout->layout_, "elm.swallow.datetime",
295 picker_layout->time_picker_);
297 char* format = GetDateTimeFormat();
299 elm_datetime_format_set(picker_layout->time_picker_, format);
302 elm_datetime_format_set(picker_layout->time_picker_,
303 kDefaultDatetimeFormat);
306 elm_datetime_value_set(picker_layout->time_picker_, current_time);
308 if (!picker_layout->AddButtons())
311 elm_datetime_field_visible_set(picker_layout->time_picker_, ELM_DATETIME_YEAR,
313 elm_datetime_field_visible_set(picker_layout->time_picker_,
314 ELM_DATETIME_MONTH, EINA_FALSE);
315 elm_datetime_field_visible_set(picker_layout->time_picker_, ELM_DATETIME_DATE,
318 picker_layout->AddDatePickerCallbacks();
320 evas_object_show(picker_layout->popup_);
322 return picker_layout.release();
325 bool InputPickerBase::Layout::AddBaseLayout(const char* title,
326 const char* layout_group) {
327 Evas_Object* top_widget = parent_->GetElmWindow();
328 conformant_ = elm_conformant_add(top_widget);
332 evas_object_size_hint_weight_set(conformant_, EVAS_HINT_EXPAND,
334 elm_win_resize_object_add(top_widget, conformant_);
335 evas_object_show(conformant_);
337 Evas_Object* layout = elm_layout_add(conformant_);
341 elm_layout_theme_set(layout, "layout", "application", "default");
342 evas_object_size_hint_weight_set(layout, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
343 evas_object_show(layout);
344 elm_object_content_set(conformant_, layout);
346 popup_ = elm_popup_add(layout);
350 elm_popup_align_set(popup_, ELM_NOTIFY_ALIGN_FILL, 1.0);
352 elm_object_part_text_set(popup_, "title,text", title);
353 evas_object_size_hint_weight_set(popup_, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
355 layout_ = elm_layout_add(popup_);
359 elm_object_content_set(popup_, layout_);
360 base::FilePath edj_dir;
361 base::FilePath control_path;
362 base::PathService::Get(PathsEfl::EDJE_RESOURCE_DIR, &edj_dir);
363 control_path = edj_dir.Append(FILE_PATH_LITERAL("control.edj"));
364 elm_layout_file_set(layout_, control_path.AsUTF8Unsafe().c_str(),
366 evas_object_size_hint_align_set(layout_, EVAS_HINT_FILL, EVAS_HINT_FILL);
367 evas_object_size_hint_weight_set(layout_, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
372 bool InputPickerBase::Layout::AddButtons() {
373 set_button_ = elm_button_add(popup_);
374 cancel_button_ = elm_button_add(popup_);
377 elm_object_domain_translatable_part_text_set(
378 cancel_button_, NULL, "WebKit", "IDS_WEBVIEW_BUTTON_CANCEL_ABB4");
383 elm_object_domain_translatable_part_text_set(set_button_, NULL, "WebKit",
384 "IDS_WEBVIEW_BUTTON_SET_ABB2");
385 elm_object_style_set(set_button_, "popup");
386 elm_object_style_set(cancel_button_, "popup");
387 elm_object_part_content_set(popup_, "button2", set_button_);
388 elm_object_part_content_set(popup_, "button1", cancel_button_);
389 evas_object_focus_set(cancel_button_, true);
390 evas_object_focus_set(set_button_, true);
395 bool InputPickerBase::Layout::AddColorSelector(int r, int g, int b) {
396 color_picker_ = elm_colorselector_add(layout_);
400 elm_colorselector_mode_set(color_picker_, ELM_COLORSELECTOR_PALETTE);
401 evas_object_size_hint_fill_set(color_picker_, EVAS_HINT_FILL, EVAS_HINT_FILL);
402 evas_object_size_hint_weight_set(color_picker_, EVAS_HINT_EXPAND,
406 elm_colorselector_color_set(color_picker_, r, g, b, 255);
408 Eina_List* color_list = const_cast<Eina_List*>(
409 elm_colorselector_palette_items_get(color_picker_));
410 Eina_List* list = nullptr;
411 Elm_Object_Item* it = nullptr;
412 void* item = nullptr;
418 EINA_LIST_FOREACH(color_list, list, item) {
420 Elm_Object_Item* elm_item = static_cast<Elm_Object_Item*>(item);
421 elm_colorselector_palette_item_color_get(elm_item, &red, &green, &blue,
423 if (red == r && green == g && blue == b) {
431 it = static_cast<Elm_Object_Item*>(eina_list_nth(color_list, 0));
433 elm_object_item_signal_emit(it, "elm,state,selected", "elm");
436 elm_object_part_content_set(layout_, "colorpalette", color_picker_);
441 void InputPickerBase::Layout::AddColorPickerCallbacks() {
442 evas_object_smart_callback_add(color_picker_, "color,item,selected",
443 ColorPickerItemSelectedCallback, color_rect_);
444 evas_object_smart_callback_add(set_button_, "clicked",
445 ColorPickerSelectFinishedCallback, this);
447 evas_object_smart_callback_add(cancel_button_, "clicked",
448 ColorPickerBackKeyCallback, this);
450 #if BUILDFLAG(IS_TIZEN)
451 eext_object_event_callback_add(layout_, EEXT_CALLBACK_BACK,
452 ColorPickerBackKeyCallback, this);
456 void InputPickerBase::Layout::DeleteColorPickerCallbacks() {
458 evas_object_smart_callback_del(color_picker_, "color,item,selected",
459 ColorPickerItemSelectedCallback);
463 evas_object_smart_callback_del(set_button_, "clicked",
464 ColorPickerSelectFinishedCallback);
466 if (cancel_button_) {
467 evas_object_smart_callback_del(cancel_button_, "clicked",
468 ColorPickerBackKeyCallback);
471 #if BUILDFLAG(IS_TIZEN)
473 eext_object_event_callback_del(layout_, EEXT_CALLBACK_BACK,
474 ColorPickerBackKeyCallback);
479 void InputPickerBase::Layout::AddDatePickerCallbacks() {
480 evas_object_smart_callback_add(set_button_, "clicked",
481 DatePickerSelectFinishedCallback, this);
482 evas_object_smart_callback_add(cancel_button_, "clicked",
483 DatePickerBackKeyCallback, this);
485 if (input_type_ == ui::TEXT_INPUT_TYPE_DATE_TIME_FIELD ||
486 input_type_ == ui::TEXT_INPUT_TYPE_DATE_TIME ||
487 input_type_ == ui::TEXT_INPUT_TYPE_DATE_TIME_LOCAL) {
488 evas_object_smart_callback_add(date_picker_, "changed",
489 DatePickerItemChangedCallback, this);
490 evas_object_smart_callback_add(time_picker_, "changed",
491 DatePickerItemChangedCallback, this);
495 elm_object_signal_emit(layout_, "TV", "align,swallow.datetime");
498 #if BUILDFLAG(IS_TIZEN)
499 eext_object_event_callback_add(layout_, EEXT_CALLBACK_BACK,
500 DatePickerBackKeyCallback, this);
504 void InputPickerBase::Layout::DeleteDatePickerCallbacks() {
506 evas_object_smart_callback_del(set_button_, "clicked",
507 DatePickerSelectFinishedCallback);
509 if (cancel_button_) {
510 evas_object_smart_callback_del(cancel_button_, "clicked",
511 DatePickerBackKeyCallback);
514 if (input_type_ == ui::TEXT_INPUT_TYPE_DATE_TIME_FIELD ||
515 input_type_ == ui::TEXT_INPUT_TYPE_DATE_TIME ||
516 input_type_ == ui::TEXT_INPUT_TYPE_DATE_TIME_LOCAL) {
518 evas_object_smart_callback_del(date_picker_, "changed",
519 DatePickerItemChangedCallback);
522 evas_object_smart_callback_del(time_picker_, "changed",
523 DatePickerItemChangedCallback);
527 #if BUILDFLAG(IS_TIZEN)
529 eext_object_event_callback_del(layout_, EEXT_CALLBACK_BACK,
530 DatePickerBackKeyCallback);
536 void InputPickerBase::Layout::ColorPickerItemSelectedCallback(
540 int r(0), g(0), b(0), a(0);
541 Elm_Object_Item* color_it = static_cast<Elm_Object_Item*>(event_info);
542 elm_colorselector_palette_item_color_get(color_it, &r, &g, &b, &a);
543 evas_object_color_set(static_cast<Evas_Object*>(data), r, g, b, a);
547 void InputPickerBase::Layout::ColorPickerSelectFinishedCallback(
551 Layout* picker_layout = static_cast<Layout*>(data);
553 int r(0), g(0), b(0), a(0);
554 evas_object_color_get(picker_layout->color_rect_, &r, &g, &b, &a);
556 picker_layout->parent_->web_contents_->DidChooseColorInColorChooser(
557 SkColorSetARGB(a, r, g, b));
558 picker_layout->parent_->RemoveColorPicker();
562 void InputPickerBase::Layout::DatePickerSelectFinishedCallback(
566 struct tm current_time;
567 memset(¤t_time, 0, sizeof(struct tm));
569 Layout* picker_layout = static_cast<Layout*>(data);
571 if (picker_layout->input_type_ == ui::TEXT_INPUT_TYPE_TIME)
572 elm_datetime_value_get(picker_layout->time_picker_, ¤t_time);
574 elm_datetime_value_get(picker_layout->date_picker_, ¤t_time);
580 switch (picker_layout->input_type_) {
581 case ui::TEXT_INPUT_TYPE_DATE: {
582 strftime(dateStr, 20, "%F", ¤t_time);
585 case ui::TEXT_INPUT_TYPE_DATE_TIME_FIELD:
586 case ui::TEXT_INPUT_TYPE_DATE_TIME: {
587 strftime(dateStr, 20, "%FT%RZ", ¤t_time);
590 case ui::TEXT_INPUT_TYPE_DATE_TIME_LOCAL: {
591 strftime(dateStr, 20, "%FT%R", ¤t_time);
594 case ui::TEXT_INPUT_TYPE_TIME: {
595 strftime(dateStr, 20, "%R", ¤t_time);
598 case ui::TEXT_INPUT_TYPE_WEEK: {
599 // Call mktime: current_time.tm_wday will be set
600 mktime(¤t_time);
601 strftime(dateStr, 20, "%G-W%V", ¤t_time);
604 case ui::TEXT_INPUT_TYPE_MONTH: {
605 strftime(dateStr, 20, "%Y-%m", ¤t_time);
614 picker_layout->parent_->date_time_chooser_->ReplaceDateTime(
615 std::string(dateStr));
616 picker_layout->parent_->RemoveDatePicker(false);
620 void InputPickerBase::Layout::DatePickerItemChangedCallback(void* data,
623 struct tm current_time;
624 memset(¤t_time, 0, sizeof(struct tm));
626 Layout* picker_layout = static_cast<Layout*>(data);
627 if (obj == picker_layout->date_picker_) {
628 elm_datetime_value_get(picker_layout->date_picker_, ¤t_time);
629 elm_datetime_value_set(picker_layout->time_picker_, ¤t_time);
630 } else if (obj == picker_layout->time_picker_) {
631 elm_datetime_value_get(picker_layout->time_picker_, ¤t_time);
632 elm_datetime_value_set(picker_layout->date_picker_, ¤t_time);
637 void InputPickerBase::Layout::ColorPickerBackKeyCallback(void* data,
640 Layout* picker_layout = static_cast<Layout*>(data);
642 picker_layout->parent_->web_contents_->DidChooseColorInColorChooser(
643 SkColorSetARGB(255, picker_layout->red_, picker_layout->green_,
644 picker_layout->blue_));
645 picker_layout->parent_->RemoveColorPicker();
649 void InputPickerBase::Layout::DatePickerBackKeyCallback(void* data,
652 Layout* picker_layout = static_cast<Layout*>(data);
654 picker_layout->parent_->date_time_chooser_->CancelDialog();
655 // pass true to RemoveDatePicker to cancelDateTimeDialog
656 picker_layout->parent_->RemoveDatePicker(true);
660 InputPickerBase::InputPickerBase(content::WebContents* web_contents,
661 Evas_Object* evas_object,
662 DateTimeChooserEfl* date_time_chooser)
663 : web_contents_(web_contents),
664 evas_object_(evas_object),
665 date_time_chooser_(date_time_chooser) {}
667 void InputPickerBase::ShowColorPicker(int r, int g, int b, int a) {
668 picker_layout_.reset(Layout::CreateAndShowColorPickerLayout(this, r, g, b));
669 if (!picker_layout_) {
670 LOG(ERROR) << "Failed to create color picker.";
671 // We need to notify engine that default color is chosen
672 // otherwise selecting will never be finished.
673 web_contents_->DidChooseColorInColorChooser(SkColorSetARGB(a, r, g, b));
677 Evas_Object* InputPickerBase::GetElmWindow() const {
678 Evas_Object* parent = elm_object_parent_widget_get(evas_object_);
679 return parent ? elm_object_top_widget_get(parent) : nullptr;
682 void InputPickerBase::ShowDatePicker(ui::TextInputType input_type,
684 ExecuteEditCommand("Unselect", 0);
688 if (!std::isfinite(input_date)) {
690 localtime_r(&timep, &tm);
691 } else if (input_type == ui::TEXT_INPUT_TYPE_MONTH) {
692 // When type is month, input_date is number of month since epoch.
693 unsigned int year = floor(input_date / 12.0) + 1970.0;
694 unsigned int month = input_date - (year - 1970) * 12 + 1;
695 CHECK_LE(month, 12u);
698 snprintf(date, sizeof(date), "%d-%d", year, month);
699 char* last_char = strptime(date, "%Y-%m", &tm);
702 // In all other cases, input_date is number of milliseconds since epoch.
703 timep = base::Time::FromDoubleT(input_date / 1000).ToTimeT();
704 gmtime_r(&timep, &tm);
706 struct tm* current_time = &tm;
707 switch (input_type) {
708 case ui::TEXT_INPUT_TYPE_DATE:
709 case ui::TEXT_INPUT_TYPE_WEEK:
710 case ui::TEXT_INPUT_TYPE_MONTH: {
711 picker_layout_.reset(
712 Layout::CreateAndShowDateLayout(this, current_time, input_type));
715 case ui::TEXT_INPUT_TYPE_DATE_TIME_FIELD:
716 case ui::TEXT_INPUT_TYPE_DATE_TIME:
717 case ui::TEXT_INPUT_TYPE_DATE_TIME_LOCAL: {
718 picker_layout_.reset(
719 Layout::CreateAndShowDateTimeLayout(this, current_time, input_type));
722 case ui::TEXT_INPUT_TYPE_TIME: {
723 picker_layout_.reset(Layout::CreateAndShowTimeLayout(this, current_time));
727 LOG(ERROR) << "Invalid date picker type.";
731 if (!picker_layout_) {
732 LOG(ERROR) << "Failed to create date picker.";
733 // We need to notify engine that empty string is chosen
734 // otherwise selecting will never be finished.
737 date_time_chooser_->ReplaceDateTime(std::string());
741 void InputPickerBase::RemoveColorPicker() {
745 picker_layout_.reset();
746 web_contents_->DidEndColorChooser();
749 void InputPickerBase::RemoveDatePicker(bool cancel) {
754 date_time_chooser_->CancelDialog();
755 picker_layout_.reset();
758 } // namespace content