Using UrlHistoryList do display history list (on urientry edition).
[profile/tv/apps/web/browser.git] / services / WebPageUI / URIEntry.cpp
1 /*
2  * Copyright (c) 2014 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the License);
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an AS IS BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <Elementary.h>
18 #include <Evas.h>
19 #include "URIEntry.h"
20 #include "BrowserLogger.h"
21 #include "MenuButton.h"
22 #include <algorithm>
23 #include <boost/regex.hpp>
24 #include "BrowserAssert.h"
25
26 namespace tizen_browser {
27 namespace base_ui {
28
29 #define GUIDE_TEXT_FOCUSED "Search or URL"
30 #define GUIDE_TEXT_UNFOCUSED "Search or URL - Press [A] to enter"
31
32 const std::string keynameSelect = "Select";
33 const std::string keynameClear = "Clear";
34 const std::string keynameKP_Enter = "KP_Enter";
35 const std::string keynameReturn = "Return";
36 const std::string keynameEsc = "XF86Back";
37
38 URIEntry::URIEntry()
39     : m_parent(nullptr)
40     , m_entry(NULL)
41     , m_favicon(0)
42     , m_entry_layout(NULL)
43     , m_entrySelectedAllFirst(false)
44 {
45     std::string edjFilePath = EDJE_DIR;
46     edjFilePath.append("WebPageUI/URIEntry.edj");
47     elm_theme_extension_add(NULL, edjFilePath.c_str());
48 }
49
50 URIEntry::~URIEntry()
51 {}
52
53 void URIEntry::init(Evas_Object* parent)
54 {
55     m_parent = parent;
56 }
57
58 Evas_Object* URIEntry::getContent()
59 {
60     BROWSER_LOGD("[%s:%d] ", __PRETTY_FUNCTION__, __LINE__);
61     M_ASSERT(m_parent);
62
63     if (!m_entry_layout) {
64         m_entry_layout = elm_layout_add(m_parent);
65         std::string edjFilePath = EDJE_DIR;
66         edjFilePath.append("WebPageUI/URIEntry.edj");
67         Eina_Bool layoutSetResult = elm_layout_file_set(m_entry_layout, edjFilePath.c_str(), "uri_entry_layout");
68         if (!layoutSetResult)
69             throw std::runtime_error("Layout file not found: " + edjFilePath);
70
71         m_entry = elm_entry_add(m_entry_layout);
72         elm_object_style_set(m_entry, "uri_entry");
73
74         elm_entry_single_line_set(m_entry, EINA_TRUE);
75         elm_entry_scrollable_set(m_entry, EINA_TRUE);
76         elm_entry_input_panel_layout_set(m_entry, ELM_INPUT_PANEL_LAYOUT_URL);
77
78         setUrlGuideText(GUIDE_TEXT_UNFOCUSED);
79
80         evas_object_smart_callback_add(m_entry, "activated", URIEntry::activated, this);
81         evas_object_smart_callback_add(m_entry, "aborted", URIEntry::aborted, this);
82         evas_object_smart_callback_add(m_entry, "preedit,changed", URIEntry::preeditChange, this);
83         evas_object_smart_callback_add(m_entry, "changed", URIEntry::_uri_entry_editing_changed, this);
84         evas_object_smart_callback_add(m_entry, "changed,user", URIEntry::_uri_entry_editing_changed_user, this);
85         evas_object_smart_callback_add(m_entry, "focused", URIEntry::focused, this);
86         evas_object_smart_callback_add(m_entry, "unfocused", URIEntry::unfocused, this);
87         evas_object_smart_callback_add(m_entry, "clicked", _uri_entry_clicked, this);
88         evas_object_event_callback_priority_add(m_entry, EVAS_CALLBACK_KEY_DOWN, 2 * EVAS_CALLBACK_PRIORITY_BEFORE, URIEntry::_fixed_entry_key_down_handler, this);
89
90         elm_object_part_content_set(m_entry_layout, "uri_entry_swallow", m_entry);
91
92         m_entryBtn = elm_button_add(m_entry_layout);
93
94         evas_object_smart_callback_add(m_entryBtn, "focused", URIEntry::focusedBtn, this);
95         evas_object_smart_callback_add(m_entryBtn, "unfocused", URIEntry::unfocusedBtn, this);
96
97         elm_object_style_set(m_entryBtn, "entry_btn");
98         evas_object_smart_callback_add(m_entryBtn, "clicked", _uri_entry_btn_clicked, this);
99
100         elm_object_part_content_set(m_entry_layout, "uri_entry_btn", m_entryBtn);
101     }
102     return m_entry_layout;
103 }
104
105 void URIEntry::changeUri(const std::string& newUri)
106 {
107     BROWSER_LOGD("%s: newUri=%s", __func__, newUri.c_str());
108     m_URI = newUri;
109     if (m_URI.empty()) {
110         elm_entry_entry_set(m_entry, elm_entry_utf8_to_markup(""));
111         m_pageTitle = std::string();
112     }
113 }
114
115 void URIEntry::setFavIcon(std::shared_ptr< tizen_browser::tools::BrowserImage > favicon)
116 {
117     BROWSER_LOGD("[%s:%d] faviconType:%d ", __PRETTY_FUNCTION__, __LINE__, favicon->imageType);
118     if (favicon->imageType != tools::BrowserImage::ImageTypeNoImage) {
119         m_favicon = tizen_browser::tools::EflTools::getEvasImage(favicon, m_entry_layout);
120         evas_object_image_fill_set(m_favicon, 0, 0, 36, 36);
121         evas_object_resize(m_favicon, 36, 36);
122         elm_object_part_content_set(m_entry_layout, "fav_icon", m_favicon);
123         setCurrentFavIcon();
124     } else {
125         setDocIcon();
126     }
127 }
128
129 void URIEntry::setCurrentFavIcon()
130 {
131     m_currentIconType = IconTypeFav;
132     elm_object_signal_emit(m_entry_layout, "show_favicon", "model");
133 }
134
135 void URIEntry::setSearchIcon()
136 {
137     m_currentIconType = IconTypeSearch;
138     elm_object_signal_emit(m_entry_layout, "set_search_icon", "model");
139 }
140
141 void URIEntry::setDocIcon()
142 {
143     m_currentIconType = IconTypeDoc;
144     elm_object_signal_emit(m_entry_layout, "set_doc_icon", "model");
145 }
146
147 void URIEntry::setPageTitle(const std::string& title)
148 {
149     BROWSER_LOGD("%s", __func__);
150     m_pageTitle = title;
151     elm_entry_entry_set(m_entry, elm_entry_utf8_to_markup(m_pageTitle.c_str()));
152 }
153
154 void URIEntry::setURI(const std::string& uri)
155 {
156     BROWSER_LOGD("%s, URI: %s", __func__, uri.c_str());
157     m_URI = uri;
158 }
159
160 void URIEntry::showPageTitle()
161 {
162     BROWSER_LOGD("%s, Page title: %s", __func__, m_pageTitle.c_str());
163     if (!m_pageTitle.empty())
164         elm_entry_entry_set(m_entry, elm_entry_utf8_to_markup(m_pageTitle.c_str()));
165     else if (!m_URI.empty())
166         elm_entry_entry_set(m_entry, elm_entry_utf8_to_markup(m_URI.c_str()));
167     else
168         elm_entry_entry_set(m_entry, elm_entry_utf8_to_markup(""));
169 }
170
171 URIEntry::IconType URIEntry::getCurrentIconTyep()
172 {
173     return m_currentIconType;
174 }
175
176 void URIEntry::selectWholeText()
177 {
178     m_oryginalEntryText = elm_entry_markup_to_utf8(elm_entry_entry_get(m_entry));
179     if (!m_entrySelectedAllFirst && !m_oryginalEntryText.empty()) {
180         elm_entry_select_all(m_entry);
181         elm_entry_cursor_end_set(m_entry);
182         m_entrySelectedAllFirst = true;
183     }
184 }
185
186 void URIEntry::_uri_entry_clicked(void* data, Evas_Object* /* obj */, void* /* event_info */)
187 {
188     URIEntry* self = static_cast<URIEntry*>(data);
189     self->selectWholeText();
190 }
191
192 void URIEntry::_uri_entry_btn_clicked(void* data, Evas_Object* /*obj*/, void* /*event_info*/)
193 {
194     URIEntry* self = static_cast<URIEntry*>(data);
195     elm_object_focus_set(self->m_entry, EINA_TRUE);
196     self->selectWholeText();
197
198     elm_object_signal_emit(self->m_entry_layout, "mouse,in", "over");
199 }
200
201 void URIEntry::activated(void* /* data */, Evas_Object* /* obj */, void* /*event_info*/)
202 {
203     BROWSER_LOGD("%s", __func__);
204 }
205
206 void URIEntry::aborted(void* data, Evas_Object* /* obj */, void* /*event_info*/)
207 {
208     BROWSER_LOGD("%s", __func__);
209     URIEntry* self = reinterpret_cast<URIEntry*>(data);
210     self->editingCanceled();
211 }
212
213 void URIEntry::preeditChange(void* /* data */, Evas_Object* /* obj */, void* /*event_info*/)
214 {
215     BROWSER_LOGD("%s", __func__);
216 }
217
218 void URIEntry::_uri_entry_editing_changed_user(void* data, Evas_Object* /* obj */, void* /*event_info*/)
219 {
220     BROWSER_LOGD("[%s:%d] ", __PRETTY_FUNCTION__, __LINE__);
221     URIEntry* self = reinterpret_cast<URIEntry*>(data);
222     std::string entry(elm_entry_markup_to_utf8(elm_entry_entry_get(self->m_entry)));
223     if ((entry.find("http://") == 0)
224             || (entry.find("https://") == 0)
225             || (entry.find(".") != std::string::npos)) {
226         self->setDocIcon();
227     } else {//if(entry.find(" ") != std::string::npos){
228         self->setSearchIcon();
229     }
230     self->uriEntryEditingChangedByUser(std::make_shared<std::string>(entry));
231 }
232
233 void URIEntry::_uri_entry_editing_changed(void* data, Evas_Object* /* obj */, void* /* event_info */)
234 {
235     URIEntry* self = static_cast<URIEntry*>(data);
236     self->uriEntryEditingChanged();
237 }
238
239 void URIEntry::setUrlGuideText(const char* txt) const
240 {
241 #if PLATFORM(TIZEN)
242     elm_object_translatable_part_text_set(m_entry, "elm.guide", txt);
243 #else
244     elm_object_part_text_set(m_entry, "elm.guide", txt);
245 #endif
246 }
247
248 void URIEntry::unfocused(void* data, Evas_Object*, void*)
249 {
250     BROWSER_LOGD("%s", __func__);
251     URIEntry* self = static_cast<URIEntry*>(data);
252     self->m_entrySelectedAllFirst = false;
253     self->setUrlGuideText(GUIDE_TEXT_UNFOCUSED);
254     elm_object_signal_emit(self->m_entry_layout, "mouse,out", "over");
255     elm_entry_entry_set(self->m_entry, elm_entry_utf8_to_markup(self->m_pageTitle.c_str()));
256 }
257
258 void URIEntry::focused(void* data, Evas_Object* /* obj */, void* /* event_info */)
259 {
260     URIEntry* self = static_cast<URIEntry*>(data);
261     self->setUrlGuideText(GUIDE_TEXT_FOCUSED);
262     elm_object_signal_emit(self->m_entry_layout, "mouse,in", "over");
263     elm_entry_entry_set(self->m_entry, elm_entry_utf8_to_markup(self->m_URI.c_str()));
264     BROWSER_LOGD("%s, URI: %s", __func__, self->m_URI.c_str());
265 }
266
267 void URIEntry::_fixed_entry_key_down_handler(void* data, Evas* /*e*/, Evas_Object* /*obj*/, void* event_info)
268 {
269     BROWSER_LOGD("%s", __func__);
270     Evas_Event_Key_Down* ev = static_cast<Evas_Event_Key_Down*>(event_info);
271     if (!data || !ev || !ev->keyname)
272         return;
273     URIEntry* self = static_cast<URIEntry*>(data);
274
275     if (keynameClear == ev->keyname) {
276         elm_entry_entry_set(self->m_entry, "");
277         return;
278     }
279     if (keynameSelect == ev->keyname
280             || keynameReturn == ev->keyname
281             || keynameKP_Enter == ev->keyname) {
282         self->editingCompleted();
283         return;
284     }
285     if (keynameEsc == ev->keyname) {
286         self->editingCanceled();
287         elm_object_focus_set(self->m_entryBtn, EINA_TRUE);
288         return;
289     }
290 }
291
292 void URIEntry::editingCompleted()
293 {
294     char* text = elm_entry_markup_to_utf8(elm_entry_entry_get(m_entry));
295     std::string userString(text);
296     free(text);
297
298     elm_entry_input_panel_hide(m_entry);
299     uriChanged(rewriteURI(userString));
300     elm_object_focus_set(m_entryBtn, EINA_TRUE);
301 }
302
303 std::string URIEntry::rewriteURI(const std::string& url)
304 {
305     BROWSER_LOGD("%s: %s", __PRETTY_FUNCTION__, url.c_str());
306     boost::regex urlRegex(R"(^(https?|ftp)://[^\s/$.?#].[^\s]*$)");
307
308     if (!url.empty() && url != "about:blank" && url != "about:home") {
309         if (boost::regex_match(url, urlRegex))
310             return url;
311         else if (boost::regex_match(std::string("http://") + url, urlRegex) &&  url.find(".") != std::string::npos)
312             return std::string("http://") + url;
313         else {
314             std::string searchString("http://www.google.com/search?q=");
315             searchString += url;
316             std::replace(searchString.begin(), searchString.end(), ' ', '+');
317             BROWSER_LOGD("[%s:%d] Search string: %s", __PRETTY_FUNCTION__, __LINE__, searchString.c_str());
318             return searchString;
319         }
320     }
321
322     return url;
323 }
324
325
326 void URIEntry::editingCanceled()
327 {
328     BROWSER_LOGD("[%s:%d] oryinal URL: %s ", __PRETTY_FUNCTION__, __LINE__, m_oryginalEntryText.c_str());
329     if (!m_oryginalEntryText.empty()) {
330         elm_entry_entry_set(m_entry, elm_entry_utf8_to_markup(m_oryginalEntryText.c_str()));
331         m_oryginalEntryText = "";
332     }
333     elm_entry_input_panel_hide(m_entry);
334     setCurrentFavIcon();
335 }
336
337 void URIEntry::AddAction(sharedAction action)
338 {
339     m_actions.push_back(action);
340 }
341
342 std::list<sharedAction> URIEntry::actions() const
343 {
344     return m_actions;
345 }
346
347 void URIEntry::clearFocus()
348 {
349     elm_object_focus_set(m_entry, EINA_FALSE);
350 }
351
352 void URIEntry::setFocus()
353 {
354     elm_object_focus_set(m_entryBtn, EINA_TRUE);
355 }
356
357 bool URIEntry::hasFocus() const
358 {
359     return elm_object_focus_get(m_entry) == EINA_TRUE ? true : false;
360 }
361
362 void URIEntry::focusedBtn(void* data, Evas_Object* /*obj*/, void* /*event_info*/)
363 {
364     URIEntry* self = static_cast<URIEntry*>(data);
365     elm_object_signal_emit(self->m_entry_layout, "mouse,in", "over");
366 }
367
368 void URIEntry::unfocusedBtn(void* data, Evas_Object* /*obj*/, void* /*event_info*/)
369 {
370     URIEntry* self = static_cast<URIEntry*>(data);
371     elm_object_signal_emit(self->m_entry_layout, "mouse,out", "over");
372     //elm_entry_entry_set(self->m_entry, elm_entry_utf8_to_markup(self->m_pageTitle.c_str()));
373 }
374
375 void URIEntry::setDisabled(bool disabled)
376 {
377     if (disabled) {
378         clearFocus();
379     }
380     elm_object_disabled_set(getContent(), disabled ? EINA_TRUE : EINA_FALSE);
381 }
382
383 }
384 }