Merge "Add new usercallbacks for executable to know when new webview created and...
[platform/framework/web/wrt.git] / src / wrt-client / window_data.cpp
1 /*
2  * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
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  * @file        window_data.cpp
18  * @author      Jaroslaw Osmanski (j.osmanski@samsung.com)
19  * @version     1.0
20  * @brief       Window data class implementation
21  */
22 #include "window_data.h"
23
24 #include <ui-gadget.h>
25 #include <dpl/log/log.h>
26
27 namespace {
28 char const* const PLATFORM_EDJ_PATH = "/usr/share/edje/wrt/Platform.edj";
29 char const* const DAEMON_EDJ_PATH = "/usr/share/edje/wrt/Daemon.edj";
30 char const* const THEME_EDJ_PATH = "/usr/share/edje/wrt/wrt_theme.edj";
31 char const* const ELM_STATE_SHOW_CONTENT = "elm,state,show,content";
32 char const* const ELM_SWALLOW_CONTENT = "elm.swallow.content";
33 char const* const ELM_SWALLOW_BACKWARD = "elm.swallow.backward";
34 char const* const ELM_SWALLOW_TOOLBAR = "elm.swallow.toolbar";
35 char const* const ELM_RECT_HIDDENOPTION = "elm.rect.hiddenOption";
36
37 const char *EDJE_SHOW_TOOLBAR_SIGNAL = "show,toolbar,signal";
38 const char *EDJE_HIDE_TOOLBAR_SIGNAL = "hide,toolbar,signal";
39 const char *EDJE_SHOW_HIDDENOPTION_SIGNAL = "show,hiddenOption,signal";
40 const char *EDJE_HIDE_HIDDENOPTION_SIGNAL = "hide,hiddenOption,signal";
41
42 char const* const ELM = "elm";
43 char const* const LAYOUT = "layout";
44 char const* const APPLICATION = "application";
45 char const* const INDICATOR = "indicator";
46 char const* const NOINDICATOR = "noindicator";
47 char const* const INTERNAL_LAYOUT = "internal_layout";
48 char const* const FLOATBACKWARD_BUTTON_STYLE = "wrt/backward";
49 } // anonymous namespace
50
51 WindowData::WindowData(unsigned long pid, bool manualInit) :
52     m_win(NULL), m_naviBackButton(NULL),
53     m_toolbarTimer(NULL),
54     m_debugMode(false)
55 {
56     m_win = createWindow(pid);
57
58     if (!manualInit)
59     {
60         init();
61     }
62 }
63
64 WindowData::~WindowData()
65 {
66     LogDebug("");
67     evas_object_del(m_win);
68 }
69
70 void WindowData::init()
71 {
72     Assert(m_win != NULL && "m_win is null");
73
74     // import button theme
75     elm_theme_overlay_add(NULL, THEME_EDJ_PATH);
76
77     m_platform_layout = createPlatformLayout(m_win);
78     evas_object_show(m_platform_layout);
79     m_navigation = createNavigationBar(m_platform_layout);
80     evas_object_show(m_navigation);
81     m_user_layout = createUserLayout(m_navigation);
82     evas_object_show(m_user_layout);
83     m_conformant = createConformant(m_user_layout);
84     evas_object_show(m_conformant);
85 }
86
87 void WindowData::setEvasObjectForLayout(Evas_Object* evas_object)
88 {
89     elm_object_content_set(m_conformant, evas_object);
90 }
91
92 void WindowData::unsetEvasObjectForLayout()
93 {
94     elm_object_content_unset(m_conformant);
95 }
96
97 void WindowData::toggleIndicator(bool fullscreen)
98 {
99     LogDebug("fullscreen=" << (fullscreen?"true":"false"));
100
101     if (!fullscreen) {
102         elm_win_indicator_mode_set(m_win, ELM_WIN_INDICATOR_SHOW);
103         elm_layout_theme_set(m_platform_layout,
104                 LAYOUT,
105                 APPLICATION,
106                 INDICATOR);
107     } else {
108         elm_win_indicator_mode_set(m_win, ELM_WIN_INDICATOR_HIDE);
109         elm_layout_theme_set(m_platform_layout,
110                 LAYOUT,
111                 APPLICATION,
112                 NOINDICATOR);
113     }
114 }
115
116 void WindowData::setViewMode(
117         const char *title,
118         bool fullscreen,
119         CtxMenuItems ctxMenuItems)
120 {
121     LogDebug("setViewMode " <<m_debugMode);
122     LogDebug("fullscreen: " << fullscreen);
123
124     m_fullscreen = fullscreen;
125
126     toggleIndicator(m_fullscreen);
127
128     m_title = title;
129     m_ctxMenuItems = ctxMenuItems;
130
131     if(m_debugMode) {
132         if(fullscreen)
133             showHiddenOption(m_user_layout);
134         else
135             createTitle(m_title.c_str(), m_ctxMenuItems);
136
137         createToolbar(m_ctxMenuItems);
138     }
139     else
140         if(!fullscreen)
141             createTitle(m_title.c_str(), m_ctxMenuItems);
142 }
143
144 void WindowData::createTitle(const char *data, CtxMenuItems ctxMenuItems)
145 {
146     LogDebug("createTitle");
147     showTitle(data);
148     if(m_debugMode) {
149         createMoreButton();
150         createTitleToolbar(ctxMenuItems);
151     }
152 }
153
154 void WindowData::showTitle(const char *data)
155 {
156     LogInfo("showTitle");
157
158     Elm_Object_Item* naviIt = elm_naviframe_top_item_get(m_navigation);
159     elm_naviframe_item_title_visible_set(naviIt, EINA_TRUE);
160     elm_object_item_text_set(naviIt, data);
161 }
162
163 void WindowData::hideTitle()
164 {
165     LogInfo("hideTitle");
166
167     Elm_Object_Item* naviIt = elm_naviframe_top_item_get(m_navigation);
168     elm_object_item_signal_emit(naviIt, "elm,state,optionheader,instant_close", "");
169     optionheaderClose = FALSE;
170
171     elm_naviframe_item_title_visible_set(naviIt, EINA_FALSE);
172 }
173
174 void WindowData::createMoreButton()
175 {
176     Evas_Object *btn = elm_button_add(m_navigation);
177     if (!btn)
178         return;
179     Elm_Object_Item* naviIt = elm_naviframe_top_item_get(m_navigation);
180     elm_object_style_set(btn, "naviframe/more/default");
181     evas_object_smart_callback_add(btn, "clicked", moreButtonCallback, this);
182     elm_object_item_part_content_set(naviIt, "title_more_btn", btn);
183     evas_object_show(btn);
184
185 }
186
187 void WindowData::createTitleToolbar(CtxMenuItems ctxMenuItems)
188 {
189     Elm_Object_Item *toolbarIt;
190     Evas_Object *toolbarButton;
191     Elm_Object_Item *naviIt = elm_naviframe_top_item_get(m_navigation);
192
193     Evas_Object *toolbar = elm_toolbar_add(m_navigation);
194     if (!toolbar)
195         return;
196     elm_toolbar_shrink_mode_set(toolbar, ELM_TOOLBAR_SHRINK_EXPAND);
197
198     elm_object_style_set(toolbar, "naviframe");
199
200     toolbarButton = elm_button_add(m_navigation);
201     if (!toolbarButton)
202         return;
203
204     std::list<CtxMenuItem>::iterator itor;
205     for (itor = ctxMenuItems.begin(); itor != ctxMenuItems.end(); ++itor) {
206         toolbarButton = elm_button_add(toolbar);
207         elm_object_style_set(toolbarButton, "naviframe_contrl/default");
208         evas_object_size_hint_align_set(toolbarButton,
209                 EVAS_HINT_FILL,
210                 EVAS_HINT_FILL);
211         elm_object_text_set(toolbarButton, ((*itor).label).c_str());
212         evas_object_smart_callback_add(toolbarButton,
213                 "clicked",
214                 (*itor).callback,
215                 (*itor).data);
216         toolbarIt = elm_toolbar_item_append(toolbar, NULL, NULL, NULL, NULL);
217         elm_object_item_part_content_set(toolbarIt, "object", toolbarButton);
218     }
219
220     toolbarButton = elm_button_add(toolbar);
221     elm_object_style_set(toolbarButton, "naviframe_contrl/default");
222     evas_object_size_hint_align_set(toolbarButton,
223             EVAS_HINT_FILL,
224             EVAS_HINT_FILL);
225     elm_object_text_set(toolbarButton, WRT_OPTION_LABEL_FULLVIEW);
226     evas_object_smart_callback_add(toolbarButton,
227             "clicked",
228             changeViewModeCallback,
229             this);
230     toolbarIt = elm_toolbar_item_append(toolbar, NULL, NULL, NULL, NULL);
231     elm_object_item_part_content_set(toolbarIt, "object", toolbarButton);
232
233     elm_object_item_part_content_set(naviIt, "optionheader", toolbar);
234     elm_object_item_signal_emit(naviIt,
235             "elm,state,optionheader,instant_close",
236             "");
237     optionheaderClose = TRUE;
238 }
239
240 void WindowData::createToolbar(CtxMenuItems ctxMenuItems)
241 {
242     Elm_Object_Item *toolbarIt;
243     Evas_Object *toolbarButton;
244     Evas_Object *toolbar;
245     char buf[100];
246
247     toolbar = elm_toolbar_add(m_user_layout);
248     if (!toolbar)
249         return;
250
251     elm_object_style_set(toolbar, "naviframe");
252     elm_object_part_content_set(m_user_layout,
253             ELM_SWALLOW_TOOLBAR,
254             toolbar);
255
256     std::list<CtxMenuItem>::iterator itor;
257     for (itor = ctxMenuItems.begin(); itor != ctxMenuItems.end(); ++itor) {
258         toolbarButton = elm_button_add(toolbar);
259         if (!toolbarButton)
260             return;
261
262         evas_object_size_hint_align_set(toolbarButton,
263                 EVAS_HINT_FILL,
264                 EVAS_HINT_FILL);
265         evas_object_size_hint_weight_set(toolbarButton,
266                 EVAS_HINT_EXPAND,
267                 EVAS_HINT_EXPAND);
268
269         Evas_Object *ic = elm_icon_add(toolbar);
270          snprintf(buf, sizeof(buf), (*itor).icon.c_str());
271          elm_icon_file_set(ic, buf, NULL);
272         evas_object_size_hint_aspect_set(ic, EVAS_ASPECT_CONTROL_VERTICAL, 1, 1);
273         elm_icon_resizable_set(ic, EINA_TRUE, EINA_TRUE);
274         elm_object_part_content_set(toolbarButton, "icon", ic);
275
276         evas_object_smart_callback_add(toolbarButton,
277                 "clicked",
278                 (*itor).callback,
279                 (*itor).data);
280         toolbarIt = elm_toolbar_item_append(toolbar, NULL, NULL, NULL, NULL);
281         elm_object_item_part_content_set(toolbarIt, "object", toolbarButton);
282
283         //for margin button between buttons
284         toolbarButton = elm_button_add(toolbar);
285         if (!toolbarButton)
286             return;
287         evas_object_color_set(
288                 toolbarButton,
289                 255,
290                 255,
291                 255,
292                 0);
293         toolbarIt = elm_toolbar_item_append(toolbar, NULL, NULL, NULL, NULL);
294         elm_object_item_part_content_set(toolbarIt, "object", toolbarButton);
295     }
296
297     toolbarButton = elm_button_add(toolbar);
298     if (!toolbarButton)
299         return;
300
301     Evas_Object *ic = elm_icon_add(toolbar);
302     snprintf(buf, sizeof(buf), WRT_OPTION_ICON_WINDOWVIEW);
303     elm_icon_file_set(ic, buf, NULL);
304     evas_object_size_hint_aspect_set(ic, EVAS_ASPECT_CONTROL_VERTICAL, 1, 1);
305     elm_icon_resizable_set(ic, EINA_TRUE, EINA_TRUE);
306     elm_object_part_content_set(toolbarButton, "icon", ic);
307
308     evas_object_size_hint_align_set(toolbarButton,
309             EVAS_HINT_FILL,
310             EVAS_HINT_FILL);
311     evas_object_size_hint_weight_set(toolbarButton,
312             EVAS_HINT_EXPAND,
313             EVAS_HINT_EXPAND);
314     evas_object_smart_callback_add(toolbarButton,
315             "clicked",
316             changeViewModeCallback,
317             this);
318     toolbarIt = elm_toolbar_item_append(toolbar, NULL, NULL, NULL, NULL);
319     elm_object_item_part_content_set(toolbarIt, "object", toolbarButton);
320
321     optionheaderClose = TRUE;
322
323     elm_object_part_content_set(m_user_layout,
324             ELM_SWALLOW_TOOLBAR,
325             toolbar);
326 }
327
328 void WindowData::showToolbar()
329 {
330     emitSignalForUserLayout(EDJE_SHOW_TOOLBAR_SIGNAL, "");
331     m_toolbarTimer = ecore_timer_add(10.0, hideToolbarCallback, this);
332 }
333
334 void WindowData::hideToolbar()
335 {
336     emitSignalForUserLayout(EDJE_HIDE_TOOLBAR_SIGNAL, "");
337     if(m_toolbarTimer) {
338         ecore_timer_del(m_toolbarTimer);
339         m_toolbarTimer = NULL;
340     }
341 }
342
343 void WindowData::createTitleButton()
344 {
345     // Add left button for back action
346     m_naviBackButton = elm_button_add(m_navigation);
347     elm_object_style_set(m_naviBackButton, "navigationbar_backbutton/default");
348
349     Elm_Object_Item* naviIt = elm_naviframe_top_item_get(m_navigation);
350     elm_object_item_part_content_set(naviIt, "prev_btn", m_naviBackButton);
351 }
352
353 void WindowData::createFloatBackButton()
354 {
355     // Add float backbutton on the left coner
356     m_floatBackButton = elm_button_add(m_user_layout);
357     elm_object_style_set(m_floatBackButton, FLOATBACKWARD_BUTTON_STYLE);
358     elm_object_part_content_set(m_user_layout,
359                                 ELM_SWALLOW_BACKWARD,
360                                 m_floatBackButton);
361     evas_object_show(m_floatBackButton);
362 }
363
364 void WindowData::updateTitleButton(const bool display)
365 {
366     if (display) {
367         evas_object_show(m_naviBackButton);
368     } else {
369         evas_object_hide(m_naviBackButton);
370     }
371 }
372
373 Evas_Object* WindowData::createWindow(unsigned long pid)
374 {
375     Evas_Object* window = elm_win_add(NULL, "wrt-widget", ELM_WIN_BASIC);
376     ecore_x_window_prop_property_set(
377         elm_win_xwindow_get(window),
378         ECORE_X_ATOM_NET_WM_PID,
379         ECORE_X_ATOM_CARDINAL, 32, &pid, 1);
380     elm_win_title_set(window, "wrt-widget");
381     elm_win_borderless_set(window, EINA_TRUE);
382     elm_win_conformant_set(window, EINA_TRUE);
383     int w, h;
384     ecore_x_window_size_get(ecore_x_window_root_first_get(), &w, &h);
385     evas_object_resize(window, w, h);
386     return window;
387 }
388
389 Evas_Object* WindowData::createPlatformLayout(Evas_Object* parent)
390 {
391     Evas_Object*  platform_layout = elm_layout_add(parent);
392
393     if (!elm_layout_file_set(platform_layout, PLATFORM_EDJ_PATH, "platformlayout"))
394         elm_layout_theme_set(platform_layout, LAYOUT, APPLICATION, NOINDICATOR);
395
396     evas_object_size_hint_expand_set(platform_layout,
397                                      EVAS_HINT_EXPAND,
398                                      EVAS_HINT_EXPAND);
399
400     elm_win_resize_object_add(parent, platform_layout);
401
402     edje_object_signal_emit(
403             elm_layout_edje_get(platform_layout), ELM_STATE_SHOW_CONTENT, ELM);
404     return platform_layout;
405 }
406
407 Evas_Object* WindowData::createUserLayout(Evas_Object* parent)
408 {
409     Assert(parent != NULL && "Parent for User Layout is null");
410     Evas_Object* layout = elm_layout_add(parent);
411     elm_layout_file_set(layout, DAEMON_EDJ_PATH, "client");
412     evas_object_size_hint_weight_set(
413             layout, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
414     evas_object_size_hint_align_set(layout, EVAS_HINT_FILL, EVAS_HINT_FILL);
415     elm_win_resize_object_add(parent, layout);
416
417     Elm_Object_Item* naviIt = elm_naviframe_item_push(
418         /* Evas_Object *obj */
419         parent,
420         /* const char *title_label  */
421         "",
422         /* Evas_Object *prev_btn */
423         NULL,
424         /* Evas_Object *next_btn */
425         NULL,
426         /* Evas_Object *content */
427         layout,
428         /* const char *item_style */
429         NULL);
430
431     elm_naviframe_item_title_visible_set(naviIt, EINA_FALSE);
432
433     return layout;
434 }
435
436 Evas_Object* WindowData::createConformant(Evas_Object* parent)
437 {
438     Assert(parent != NULL && "Parent is null");
439     Evas_Object* conformant = elm_conformant_add(parent);
440     elm_object_style_set(conformant, INTERNAL_LAYOUT);
441     evas_object_size_hint_weight_set(
442             conformant, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
443     evas_object_size_hint_align_set(conformant, EVAS_HINT_FILL, EVAS_HINT_FILL);
444     elm_object_part_content_set(parent, ELM_SWALLOW_CONTENT, conformant);
445     evas_object_show(conformant);
446     return conformant;
447 }
448
449 Evas_Object* WindowData::createNavigationBar(Evas_Object* parent)
450 {
451     Assert(parent != NULL && "Parent for naviframe is null");
452     Evas_Object* navigation = elm_naviframe_add(parent);
453     elm_object_part_content_set(parent, ELM_SWALLOW_CONTENT, navigation);
454     return navigation;
455 }
456
457 void WindowData::showHiddenOption(Evas_Object* parent)
458 {
459     Assert(parent != NULL && "Parent for Hiden Option");
460
461     emitSignalForUserLayout(EDJE_SHOW_HIDDENOPTION_SIGNAL, "");
462
463     edje_object_signal_callback_add(elm_layout_edje_get(parent),
464             "mouse,clicked,1",
465             "elm.rect.hiddenOption",
466             controlHiddenOptionCallback,
467             this);
468 }
469
470 void WindowData::hideHiddenOption(Evas_Object* parent)
471 {
472     Assert(parent != NULL && "Parent for Hiden Option");
473
474     emitSignalForUserLayout(EDJE_HIDE_HIDDENOPTION_SIGNAL, "");
475
476     edje_object_signal_callback_del(elm_layout_edje_get(parent),
477             "mouse,clicked,1",
478             "elm.rect.hiddenOption",
479             controlHiddenOptionCallback);
480 }
481
482 void WindowData::initFullViewMode ()
483 {
484     hideToolbar();
485     showHiddenOption(m_user_layout);
486 }
487
488 void WindowData::addNaviBackButtonCallback(
489         const char* event,
490         CallbackType callback,
491         const void* data)
492 {
493     Assert(m_naviBackButton != NULL && "m_naviBackButton is null");
494     evas_object_smart_callback_add(m_naviBackButton, event, callback, data);
495 }
496
497 void* WindowData::delNaviBackButtonCallback(
498         const char* event,
499         CallbackType callBack)
500 {
501     Assert(m_naviBackButton != NULL && "m_naviBackButton is null");
502     return evas_object_smart_callback_del(m_naviBackButton, event, callBack);
503 }
504
505 void WindowData::addFloatBackButtonCallback(
506         const char* event,
507         CallbackType callback,
508         const void* data)
509 {
510     Assert(m_floatBackButton != NULL && "m_floatBackButton is null");
511     evas_object_smart_callback_add(m_floatBackButton, event, callback, data);
512 }
513
514 void* WindowData::delFloatBackButtonCallback(
515         const char* event,
516         CallbackType callBack)
517 {
518     Assert(m_floatBackButton != NULL && "m_floatBackButton is null");
519     return evas_object_smart_callback_del(m_floatBackButton, event, callBack);
520 }
521
522 void WindowData::userlayoutCallbackAdd(
523         const Evas_Callback_Type event,
524         EvasCallbackType callback,
525         const void* data)
526 {
527     Assert(m_user_layout != NULL && "m_user_layout is null");
528     evas_object_event_callback_add(m_user_layout, event, callback, data);
529 }
530
531 void* WindowData::userlayoutCallbackDel(
532         const Evas_Callback_Type event,
533         EvasCallbackType callBack)
534 {
535     Assert(m_user_layout != NULL && "m_user_layout is null");
536     return evas_object_event_callback_del(m_user_layout, event, callBack);
537 }
538
539 void WindowData::emitSignalForUserLayout(
540         const char* emission, const char* source)
541 {
542     LogInfo("emitSignalForUserLayout called");
543     Assert(m_user_layout != NULL && "m_user_layout is null");
544     Assert(emission != NULL && "emission is null");
545     Assert(source != NULL && "source is null");
546
547     edje_object_signal_emit(
548             elm_layout_edje_get(m_user_layout), emission, source);
549 }
550
551 void WindowData::moreButtonCallback(void *data,
552         Evas_Object */*obj*/,
553         void */*event_info*/)
554 {
555     WindowData* This = static_cast<WindowData*>(data);
556     Elm_Object_Item *naviIt = elm_naviframe_top_item_get(This->m_navigation);
557
558     if (This->optionheaderClose)
559         elm_object_item_signal_emit(naviIt, "elm,state,optionheader,open", "");
560     else
561         elm_object_item_signal_emit(naviIt, "elm,state,optionheader,close", "");
562
563     This->optionheaderClose = !This->optionheaderClose;
564 }
565
566
567 void WindowData::changeViewModeCallback(void *data,
568         Evas_Object */*obj*/,
569         void */*event_info*/)
570 {
571     WindowData* This = static_cast<WindowData*>(data);
572     Elm_Object_Item *naviIt = elm_naviframe_top_item_get(This->m_navigation);
573
574     if(elm_naviframe_item_title_visible_get(naviIt)) {
575         This->hideTitle();
576         This->showHiddenOption(This->m_user_layout);
577
578     } else {
579         This->createTitle(This->m_title.c_str(), This->m_ctxMenuItems);
580         This ->hideToolbar();
581     }
582 }
583
584 void WindowData::controlHiddenOptionCallback(void *data,
585         Evas_Object */*obj*/,
586         const char */*emission*/,
587         const char */*source*/)
588 {
589     LogDebug("controlHiddenOptionCallback");
590     WindowData* This = static_cast<WindowData *>(data);
591     Elm_Object_Item *naviIt = elm_naviframe_top_item_get(This->m_navigation);
592
593     if(!elm_naviframe_item_title_visible_get(naviIt)) {
594         const char* state = edje_object_part_state_get(
595                 elm_layout_edje_get(This->m_user_layout),
596                 "elm.swallow.toolbar",
597                 NULL);
598
599         if (state && !strncmp(state, "default", strlen("default"))) {
600             This->showToolbar();
601             This->hideHiddenOption(This->m_user_layout);
602         }
603         else {
604             This->initFullViewMode();
605         }
606
607     }
608 }
609
610 Eina_Bool WindowData::hideToolbarCallback(void *data)
611 {
612     WindowData *This = static_cast<WindowData *>(data);
613
614     This->initFullViewMode();
615     return TRUE;
616 }
617
618 void WindowData::toggleFullscreen(bool fullscreen)
619 {
620     LogDebug(__PRETTY_FUNCTION__);
621
622     static bool alreadyFullscreen = false;
623
624     if (alreadyFullscreen == fullscreen) {
625         // window is in fullscreen mode and fullscreen mode is requested, or
626         // window is not in fullscreen and a request is made to exit fullscreen
627         // In these two situations we don't have to do anything here.
628         return;
629     }
630
631     if (!m_fullscreen) {
632         //If ViewMode is not fullscreen, toggle the title bar and indicator
633         fullscreen ? hideTitle() : showTitle(m_title.c_str());
634         toggleIndicator(fullscreen);
635     }
636     alreadyFullscreen = !alreadyFullscreen;
637 }