1 // Copyright 2014 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.h"
7 #include <Elementary.h>
12 #include "base/files/file_path.h"
13 #include "base/logging.h"
14 #include "base/path_service.h"
15 #include "base/time/time.h"
16 #include "common/web_contents_utils.h"
17 #include "content/common/paths_efl.h"
18 #include "content/public/browser/web_contents.h"
20 #if BUILDFLAG(IS_TIZEN)
21 #include <vconf/vconf.h>
26 static const char* kDefaultDatetimeFormat = "%Y/%m/%d %H:%M";
28 InputPicker::Layout::Layout(InputPicker* parent) : parent_(parent) {
29 evas_object_focus_set(parent_->web_view_->evas_object(), false);
30 // FIXME: Workaround. OSP requirement. OSP want to block own touch event
31 // while webkit internal picker is running.
32 evas_object_smart_callback_call(parent_->web_view_->evas_object(),
33 "input,picker,show", 0);
36 InputPicker::Layout::~Layout() {
37 // FIXME: Workaround. OSP requirement. OSP want to block own touch event
38 // while webkit internal picker is running.
39 evas_object_smart_callback_call(parent_->web_view_->evas_object(),
40 "input,picker,hide", 0);
41 evas_object_focus_set(parent_->web_view_->evas_object(), true);
47 DeleteColorPickerCallbacks();
49 DeleteDatePickerCallbacks();
51 evas_object_del(conformant_);
52 conformant_ = nullptr;
56 InputPicker::Layout* InputPicker::Layout::CreateAndShowColorPickerLayout(
61 std::unique_ptr<Layout> picker_layout(new Layout(parent));
62 picker_layout->is_color_picker_ = true;
64 if (!picker_layout->AddBaseLayout(
65 dgettext("WebKit", "IDS_WEBVIEW_HEADER_SELECT_COLOUR"),
66 "colorselector_popup_layout")) {
70 picker_layout->color_rect_ =
71 evas_object_rectangle_add(evas_object_evas_get(picker_layout->layout_));
72 if (!picker_layout->color_rect_)
75 evas_object_size_hint_weight_set(picker_layout->color_rect_, EVAS_HINT_EXPAND,
77 evas_object_show(picker_layout->color_rect_);
78 evas_object_color_set(picker_layout->color_rect_, r, g, b, 255);
79 elm_object_part_content_set(picker_layout->layout_, "rect",
80 picker_layout->color_rect_);
82 if (!picker_layout->AddColorSelector(r, g, b))
85 if (!picker_layout->AddButtons())
88 picker_layout->red_ = r;
89 picker_layout->green_ = g;
90 picker_layout->blue_ = b;
92 picker_layout->AddColorPickerCallbacks();
93 evas_object_show(picker_layout->popup_);
94 return picker_layout.release();
97 static char* GetDateTimeFormat() {
98 #if BUILDFLAG(IS_TIZEN)
99 char* language = vconf_get_str(VCONFKEY_LANGSET);
100 if (!vconf_set_str(VCONFKEY_LANGSET, "en_US"))
101 LOG(ERROR) << "vconf_set_str failed ";
103 char* region_format = vconf_get_str(VCONFKEY_REGIONFORMAT);
111 vconf_get_int(VCONFKEY_REGIONFORMAT_TIME1224, &time_value);
112 if (time_value == VCONFKEY_TIME_FORMAT_24)
113 snprintf(buf, sizeof(buf), "%s_DTFMT_24HR", region_format);
115 snprintf(buf, sizeof(buf), "%s_DTFMT_12HR", region_format);
119 // FIXME: Workaround fix for region format.
120 int buf_length = strlen(buf);
121 for (int i = 0; i < buf_length - 4; i++) {
122 if (buf[i] == 'u' && buf[i + 1] == 't' && buf[i + 2] == 'f') {
123 if (buf[i + 3] == '8') {
125 for (int j = buf_length; j > i + 3; j--)
128 buf[buf_length + 1] = '\0';
129 } else if (buf[i + 3] == '-' && buf[i + 4] == '8') {
142 char* date_time_format = dgettext("dt_fmt", buf);
143 if (language && !vconf_set_str(VCONFKEY_LANGSET, language))
144 LOG(ERROR) << "vconf_set_str failed ";
146 // FIXME: Workaround fix for not supported dt_fmt.
147 // Use default format if dt_fmt string is not exist.
148 if (strlen(date_time_format) == strlen(buf) &&
149 !strncmp(date_time_format, buf, strlen(buf))) {
153 return strdup(date_time_format);
160 InputPicker::Layout* InputPicker::Layout::CreateAndShowDateLayout(
162 struct tm* current_time,
163 ui::TextInputType type) {
164 std::unique_ptr<Layout> picker_layout(new Layout(parent));
166 picker_layout->input_type_ = type;
170 case ui::TEXT_INPUT_TYPE_DATE: {
171 title = "IDS_WEBVIEW_HEADER_SET_DATE";
174 case ui::TEXT_INPUT_TYPE_WEEK: {
175 title = "IDS_WEBVIEW_HEADER_SET_WEEK";
178 case ui::TEXT_INPUT_TYPE_MONTH: {
179 title = "IDS_WEBVIEW_HEADER_SET_MONTH";
187 if (!picker_layout->AddBaseLayout(dgettext("WebKit", title.c_str()),
192 picker_layout->date_picker_ = elm_datetime_add(picker_layout->layout_);
193 if (!picker_layout->date_picker_)
196 elm_object_part_content_set(picker_layout->layout_, "elm.swallow.datetime",
197 picker_layout->date_picker_);
199 char* format = GetDateTimeFormat();
201 elm_datetime_format_set(picker_layout->date_picker_, format);
204 elm_datetime_format_set(picker_layout->date_picker_,
205 kDefaultDatetimeFormat);
208 elm_datetime_value_set(picker_layout->date_picker_, current_time);
210 if (!picker_layout->AddButtons())
213 if (type == ui::TEXT_INPUT_TYPE_MONTH) {
214 elm_datetime_field_visible_set(picker_layout->date_picker_,
215 ELM_DATETIME_DATE, EINA_FALSE);
217 elm_datetime_field_visible_set(picker_layout->date_picker_, ELM_DATETIME_HOUR,
219 elm_datetime_field_visible_set(picker_layout->date_picker_,
220 ELM_DATETIME_MINUTE, EINA_FALSE);
222 picker_layout->AddDatePickerCallbacks();
224 evas_object_show(picker_layout->popup_);
226 return picker_layout.release();
230 InputPicker::Layout* InputPicker::Layout::CreateAndShowDateTimeLayout(
232 struct tm* current_time,
233 ui::TextInputType type) {
234 std::unique_ptr<Layout> picker_layout(new Layout(parent));
236 picker_layout->input_type_ = type;
238 elm_object_scale_set(picker_layout->popup_, 0.7);
239 if (!picker_layout->AddBaseLayout(
240 dgettext("WebKit", "IDS_WEBVIEW_HEADER_SET_DATE_AND_TIME"),
245 picker_layout->time_picker_ = elm_datetime_add(picker_layout->layout_);
246 picker_layout->date_picker_ = elm_datetime_add(picker_layout->layout_);
247 if (!picker_layout->time_picker_ || !picker_layout->date_picker_)
250 elm_object_part_content_set(picker_layout->layout_, "elm.swallow.datetime",
251 picker_layout->time_picker_);
252 elm_object_part_content_set(picker_layout->layout_, "elm.swallow.datetime2",
253 picker_layout->date_picker_);
254 elm_object_style_set(picker_layout->time_picker_, "time_layout");
256 char* format = GetDateTimeFormat();
258 elm_datetime_format_set(picker_layout->date_picker_, format);
259 elm_datetime_format_set(picker_layout->time_picker_, format);
262 elm_datetime_format_set(picker_layout->date_picker_,
263 kDefaultDatetimeFormat);
264 elm_datetime_format_set(picker_layout->time_picker_,
265 kDefaultDatetimeFormat);
268 elm_datetime_value_set(picker_layout->date_picker_, current_time);
269 elm_datetime_value_set(picker_layout->time_picker_, current_time);
270 if (!picker_layout->AddButtons())
273 picker_layout->AddDatePickerCallbacks();
274 evas_object_show(picker_layout->popup_);
276 return picker_layout.release();
279 bool InputPicker::Layout::SetDatetimePicker(Evas_Object* picker,
285 InputPicker::Layout* InputPicker::Layout::CreateAndShowTimeLayout(
287 struct tm* current_time) {
288 std::unique_ptr<Layout> picker_layout(new Layout(parent));
290 picker_layout->input_type_ = ui::TEXT_INPUT_TYPE_TIME;
292 if (!picker_layout->AddBaseLayout(
293 dgettext("WebKit", "IDS_WEBVIEW_HEADER_SET_TIME"), "date_popup")) {
297 picker_layout->time_picker_ = elm_datetime_add(picker_layout->layout_);
298 if (!picker_layout->time_picker_)
301 elm_object_style_set(picker_layout->time_picker_, "time_layout");
302 elm_object_part_content_set(picker_layout->layout_, "elm.swallow.datetime",
303 picker_layout->time_picker_);
305 char* format = GetDateTimeFormat();
307 elm_datetime_format_set(picker_layout->time_picker_, format);
310 elm_datetime_format_set(picker_layout->time_picker_,
311 kDefaultDatetimeFormat);
314 elm_datetime_value_set(picker_layout->time_picker_, current_time);
316 if (!picker_layout->AddButtons())
319 elm_datetime_field_visible_set(picker_layout->time_picker_, ELM_DATETIME_YEAR,
321 elm_datetime_field_visible_set(picker_layout->time_picker_,
322 ELM_DATETIME_MONTH, EINA_FALSE);
323 elm_datetime_field_visible_set(picker_layout->time_picker_, ELM_DATETIME_DATE,
326 picker_layout->AddDatePickerCallbacks();
328 evas_object_show(picker_layout->popup_);
330 return picker_layout.release();
333 bool InputPicker::Layout::AddBaseLayout(const char* title,
334 const char* layout_group) {
335 Evas_Object* top_widget = parent_->web_view_->GetElmWindow();
337 conformant_ = elm_conformant_add(top_widget);
341 evas_object_size_hint_weight_set(conformant_, EVAS_HINT_EXPAND,
343 elm_win_resize_object_add(top_widget, conformant_);
344 evas_object_show(conformant_);
346 Evas_Object* layout = elm_layout_add(conformant_);
350 elm_layout_theme_set(layout, "layout", "application", "default");
351 evas_object_size_hint_weight_set(layout, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
352 evas_object_show(layout);
353 elm_object_content_set(conformant_, layout);
355 popup_ = elm_popup_add(layout);
359 elm_popup_align_set(popup_, ELM_NOTIFY_ALIGN_FILL, 1.0);
361 elm_object_part_text_set(popup_, "title,text", title);
362 evas_object_size_hint_weight_set(popup_, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
364 layout_ = elm_layout_add(popup_);
368 elm_object_content_set(popup_, layout_);
369 base::FilePath edj_dir;
370 base::FilePath control_path;
371 base::PathService::Get(PathsEfl::EDJE_RESOURCE_DIR, &edj_dir);
372 control_path = edj_dir.Append(FILE_PATH_LITERAL("control.edj"));
373 elm_layout_file_set(layout_, control_path.AsUTF8Unsafe().c_str(),
375 evas_object_size_hint_align_set(layout_, EVAS_HINT_FILL, EVAS_HINT_FILL);
376 evas_object_size_hint_weight_set(layout_, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
381 bool InputPicker::Layout::AddButtons() {
382 set_button_ = elm_button_add(popup_);
383 cancel_button_ = elm_button_add(popup_);
386 elm_object_domain_translatable_part_text_set(
387 cancel_button_, NULL, "WebKit", "IDS_WEBVIEW_BUTTON_CANCEL_ABB4");
392 elm_object_domain_translatable_part_text_set(set_button_, NULL, "WebKit",
393 "IDS_WEBVIEW_BUTTON_SET_ABB2");
394 elm_object_style_set(set_button_, "popup");
395 elm_object_style_set(cancel_button_, "popup");
396 elm_object_part_content_set(popup_, "button2", set_button_);
397 elm_object_part_content_set(popup_, "button1", cancel_button_);
398 evas_object_focus_set(cancel_button_, true);
399 evas_object_focus_set(set_button_, true);
404 bool InputPicker::Layout::AddColorSelector(int r, int g, int b) {
405 color_picker_ = elm_colorselector_add(layout_);
409 elm_colorselector_mode_set(color_picker_, ELM_COLORSELECTOR_PALETTE);
410 evas_object_size_hint_fill_set(color_picker_, EVAS_HINT_FILL, EVAS_HINT_FILL);
411 evas_object_size_hint_weight_set(color_picker_, EVAS_HINT_EXPAND,
415 elm_colorselector_color_set(color_picker_, r, g, b, 255);
417 Eina_List* color_list = const_cast<Eina_List*>(
418 elm_colorselector_palette_items_get(color_picker_));
419 Eina_List* list = nullptr;
420 Elm_Object_Item* it = nullptr;
421 void* item = nullptr;
427 EINA_LIST_FOREACH(color_list, list, item) {
429 Elm_Object_Item* elm_item = static_cast<Elm_Object_Item*>(item);
430 elm_colorselector_palette_item_color_get(elm_item, &red, &green, &blue,
432 if (red == r && green == g && blue == b) {
440 it = static_cast<Elm_Object_Item*>(eina_list_nth(color_list, 0));
442 elm_object_item_signal_emit(it, "elm,state,selected", "elm");
445 elm_object_part_content_set(layout_, "colorpalette", color_picker_);
450 void InputPicker::Layout::AddColorPickerCallbacks() {
451 evas_object_smart_callback_add(color_picker_, "color,item,selected",
452 ColorPickerItemSelectedCallback, color_rect_);
453 evas_object_smart_callback_add(set_button_, "clicked",
454 ColorPickerSelectFinishedCallback, this);
455 #if BUILDFLAG(IS_TIZEN)
456 eext_object_event_callback_add(layout_, EEXT_CALLBACK_BACK,
457 ColorPickerBackKeyCallback, this);
461 void InputPicker::Layout::DeleteColorPickerCallbacks() {
463 evas_object_smart_callback_del(color_picker_, "color,item,selected",
464 ColorPickerItemSelectedCallback);
468 evas_object_smart_callback_del(set_button_, "clicked",
469 ColorPickerSelectFinishedCallback);
471 if (cancel_button_) {
472 evas_object_smart_callback_del(cancel_button_, "clicked",
473 ColorPickerBackKeyCallback);
476 #if BUILDFLAG(IS_TIZEN)
478 eext_object_event_callback_del(layout_, EEXT_CALLBACK_BACK,
479 ColorPickerBackKeyCallback);
484 void InputPicker::Layout::AddDatePickerCallbacks() {
485 evas_object_smart_callback_add(set_button_, "clicked",
486 DatePickerSelectFinishedCallback, this);
487 evas_object_smart_callback_add(cancel_button_, "clicked",
488 DatePickerBackKeyCallback, this);
490 if (input_type_ == ui::TEXT_INPUT_TYPE_DATE_TIME_FIELD ||
491 input_type_ == ui::TEXT_INPUT_TYPE_DATE_TIME ||
492 input_type_ == ui::TEXT_INPUT_TYPE_DATE_TIME_LOCAL) {
493 evas_object_smart_callback_add(date_picker_, "changed",
494 DatePickerItemChangedCallback, this);
495 evas_object_smart_callback_add(time_picker_, "changed",
496 DatePickerItemChangedCallback, this);
500 elm_object_signal_emit(layout_, "TV", "align,swallow.datetime");
503 #if BUILDFLAG(IS_TIZEN)
504 eext_object_event_callback_add(layout_, EEXT_CALLBACK_BACK,
505 DatePickerBackKeyCallback, this);
509 void InputPicker::Layout::DeleteDatePickerCallbacks() {
511 evas_object_smart_callback_del(set_button_, "clicked",
512 DatePickerSelectFinishedCallback);
514 if (cancel_button_) {
515 evas_object_smart_callback_del(cancel_button_, "clicked",
516 DatePickerBackKeyCallback);
519 if (input_type_ == ui::TEXT_INPUT_TYPE_DATE_TIME_FIELD ||
520 input_type_ == ui::TEXT_INPUT_TYPE_DATE_TIME ||
521 input_type_ == ui::TEXT_INPUT_TYPE_DATE_TIME_LOCAL) {
523 evas_object_smart_callback_del(date_picker_, "changed",
524 DatePickerItemChangedCallback);
527 evas_object_smart_callback_del(time_picker_, "changed",
528 DatePickerItemChangedCallback);
532 #if BUILDFLAG(IS_TIZEN)
534 eext_object_event_callback_del(layout_, EEXT_CALLBACK_BACK,
535 DatePickerBackKeyCallback);
541 void InputPicker::Layout::ColorPickerItemSelectedCallback(void* data,
544 int r(0), g(0), b(0), a(0);
545 Elm_Object_Item* color_it = static_cast<Elm_Object_Item*>(event_info);
546 elm_colorselector_palette_item_color_get(color_it, &r, &g, &b, &a);
547 evas_object_color_set(static_cast<Evas_Object*>(data), r, g, b, a);
551 void InputPicker::Layout::ColorPickerSelectFinishedCallback(void* data,
554 Layout* picker_layout = static_cast<Layout*>(data);
556 int r(0), g(0), b(0), a(0);
557 evas_object_color_get(picker_layout->color_rect_, &r, &g, &b, &a);
559 picker_layout->parent_->web_view_->web_contents()
560 .DidChooseColorInColorChooser(SkColorSetARGB(a, r, g, b));
561 picker_layout->parent_->RemoveColorPicker();
565 void InputPicker::Layout::DatePickerSelectFinishedCallback(void* data,
568 struct tm current_time;
569 memset(¤t_time, 0, sizeof(struct tm));
571 Layout* picker_layout = static_cast<Layout*>(data);
573 if (picker_layout->input_type_ == ui::TEXT_INPUT_TYPE_TIME)
574 elm_datetime_value_get(picker_layout->time_picker_, ¤t_time);
576 elm_datetime_value_get(picker_layout->date_picker_, ¤t_time);
582 switch (picker_layout->input_type_) {
583 case ui::TEXT_INPUT_TYPE_DATE: {
584 strftime(dateStr, 20, "%F", ¤t_time);
587 case ui::TEXT_INPUT_TYPE_DATE_TIME_FIELD:
588 case ui::TEXT_INPUT_TYPE_DATE_TIME: {
589 strftime(dateStr, 20, "%FT%RZ", ¤t_time);
592 case ui::TEXT_INPUT_TYPE_DATE_TIME_LOCAL: {
593 strftime(dateStr, 20, "%FT%R", ¤t_time);
596 case ui::TEXT_INPUT_TYPE_TIME: {
597 strftime(dateStr, 20, "%R", ¤t_time);
600 case ui::TEXT_INPUT_TYPE_WEEK: {
601 // Call mktime: current_time.tm_wday will be set
602 mktime(¤t_time);
603 strftime(dateStr, 20, "%G-W%V", ¤t_time);
606 case ui::TEXT_INPUT_TYPE_MONTH: {
607 strftime(dateStr, 20, "%Y-%m", ¤t_time);
616 picker_layout->parent_->web_view_->GetDateTimeChooser()->ReplaceDateTime(
617 std::string(dateStr));
618 picker_layout->parent_->RemoveDatePicker(false);
622 void InputPicker::Layout::DatePickerItemChangedCallback(void* data,
625 struct tm current_time;
626 memset(¤t_time, 0, sizeof(struct tm));
628 Layout* picker_layout = static_cast<Layout*>(data);
629 if (obj == picker_layout->date_picker_) {
630 elm_datetime_value_get(picker_layout->date_picker_, ¤t_time);
631 elm_datetime_value_set(picker_layout->time_picker_, ¤t_time);
632 } else if (obj == picker_layout->time_picker_) {
633 elm_datetime_value_get(picker_layout->time_picker_, ¤t_time);
634 elm_datetime_value_set(picker_layout->date_picker_, ¤t_time);
639 void InputPicker::Layout::ColorPickerBackKeyCallback(void* data,
642 Layout* picker_layout = static_cast<Layout*>(data);
644 picker_layout->parent_->web_view_->web_contents()
645 .DidChooseColorInColorChooser(SkColorSetARGB(255, picker_layout->red_,
646 picker_layout->green_,
647 picker_layout->blue_));
648 picker_layout->parent_->RemoveColorPicker();
652 void InputPicker::Layout::DatePickerBackKeyCallback(void* data,
655 Layout* picker_layout = static_cast<Layout*>(data);
657 picker_layout->parent_->web_view_->GetDateTimeChooser()->CancelDialog();
658 // pass true to RemoveDatePicker to cancelDateTimeDialog
659 picker_layout->parent_->RemoveDatePicker(true);
663 InputPicker::InputPicker(EWebView* view)
664 : web_view_(view), picker_layout_(nullptr) {}
666 InputPicker::~InputPicker() {}
668 void InputPicker::ShowColorPicker(int r, int g, int b, int a) {
669 picker_layout_.reset(Layout::CreateAndShowColorPickerLayout(this, r, g, b));
670 if (!picker_layout_) {
671 LOG(ERROR) << "Failed to create color picker.";
672 // We need to notify engine that default color is chosen
673 // otherwise selecting will never be finished.
674 web_view_->web_contents().DidChooseColorInColorChooser(
675 SkColorSetARGB(a, r, g, b));
679 void InputPicker::ShowDatePicker(ui::TextInputType input_type,
681 web_view_->ExecuteEditCommand("Unselect", 0);
685 if (!std::isfinite(input_date)) {
687 localtime_r(&timep, &tm);
688 } else if (input_type == ui::TEXT_INPUT_TYPE_MONTH) {
689 // When type is month, input_date is number of month since epoch.
690 unsigned int year = floor(input_date / 12.0) + 1970.0;
691 unsigned int month = input_date - (year - 1970) * 12 + 1;
692 CHECK_LE(month, 12u);
695 snprintf(date, sizeof(date), "%d-%d", year, month);
696 char* last_char = strptime(date, "%Y-%m", &tm);
699 // In all other cases, input_date is number of milliseconds since epoch.
700 timep = base::Time::FromDoubleT(input_date / 1000).ToTimeT();
701 gmtime_r(&timep, &tm);
703 struct tm* current_time = &tm;
704 switch (input_type) {
705 case ui::TEXT_INPUT_TYPE_DATE:
706 case ui::TEXT_INPUT_TYPE_WEEK:
707 case ui::TEXT_INPUT_TYPE_MONTH: {
708 picker_layout_.reset(
709 Layout::CreateAndShowDateLayout(this, current_time, input_type));
712 case ui::TEXT_INPUT_TYPE_DATE_TIME_FIELD:
713 case ui::TEXT_INPUT_TYPE_DATE_TIME:
714 case ui::TEXT_INPUT_TYPE_DATE_TIME_LOCAL: {
715 picker_layout_.reset(
716 Layout::CreateAndShowDateTimeLayout(this, current_time, input_type));
719 case ui::TEXT_INPUT_TYPE_TIME: {
720 picker_layout_.reset(Layout::CreateAndShowTimeLayout(this, current_time));
724 LOG(ERROR) << "Invalid date picker type.";
728 if (!picker_layout_) {
729 LOG(ERROR) << "Failed to create date picker.";
730 // We need to notify engine that empty string is chosen
731 // otherwise selecting will never be finished.
734 web_view_->GetDateTimeChooser()->ReplaceDateTime(std::string());
738 void InputPicker::RemoveColorPicker() {
742 picker_layout_.reset();
743 web_view_->web_contents().DidEndColorChooser();
746 void InputPicker::RemoveDatePicker(bool cancel) {
751 web_view_->GetDateTimeChooser()->CancelDialog();
752 picker_layout_.reset();
755 } // namespace content