elementary/popup - [E-devel] [Patch][elementary] elc_popup, focus next hook implement...
[framework/uifw/elementary.git] / src / lib / els_cursor.c
1 #include <Elementary.h>
2 #include <Elementary_Cursor.h>
3 #include "elm_priv.h"
4
5 #ifdef HAVE_ELEMENTARY_X
6 #include <Ecore_X.h>
7 #include <Ecore_X_Cursor.h>
8 #endif
9
10 #define _cursor_key "_elm_cursor"
11
12 struct _Cursor_Id
13 {
14    const char *name;
15 #ifdef HAVE_ELEMENTARY_X
16    int id;
17 #endif
18 };
19
20 #ifdef HAVE_ELEMENTARY_X
21 #define CURSOR(_name, _xid) \
22    {_name , _xid}
23 # else
24 #define CURSOR(_name, _xid) \
25    {_name}
26 #endif
27
28 /* Please keep order in sync with Ecore_X_Cursor.h values! */
29 struct _Cursor_Id _cursors[] =
30 {
31    CURSOR(ELM_CURSOR_X                  , ECORE_X_CURSOR_X                  ),
32    CURSOR(ELM_CURSOR_ARROW              , ECORE_X_CURSOR_ARROW              ),
33    CURSOR(ELM_CURSOR_BASED_ARROW_DOWN   , ECORE_X_CURSOR_BASED_ARROW_DOWN   ),
34    CURSOR(ELM_CURSOR_BASED_ARROW_UP     , ECORE_X_CURSOR_UP                 ),
35    CURSOR(ELM_CURSOR_BOAT               , ECORE_X_CURSOR_BOAT               ),
36    CURSOR(ELM_CURSOR_BOGOSITY           , ECORE_X_CURSOR_BOGOSITY           ),
37    CURSOR(ELM_CURSOR_BOTTOM_LEFT_CORNER , ECORE_X_CURSOR_BOTTOM_LEFT_CORNER ),
38    CURSOR(ELM_CURSOR_BOTTOM_RIGHT_CORNER, ECORE_X_CURSOR_BOTTOM_RIGHT_CORNER),
39    CURSOR(ELM_CURSOR_BOTTOM_SIDE        , ECORE_X_CURSOR_BOTTOM_SIDE        ),
40    CURSOR(ELM_CURSOR_BOTTOM_TEE         , ECORE_X_CURSOR_BOTTOM_TEE         ),
41    CURSOR(ELM_CURSOR_BOX_SPIRAL         , ECORE_X_CURSOR_BOX_SPIRAL         ),
42    CURSOR(ELM_CURSOR_CENTER_PTR         , ECORE_X_CURSOR_CENTER_PTR         ),
43    CURSOR(ELM_CURSOR_CIRCLE             , ECORE_X_CURSOR_CIRCLE             ),
44    CURSOR(ELM_CURSOR_CLOCK              , ECORE_X_CURSOR_CLOCK              ),
45    CURSOR(ELM_CURSOR_COFFEE_MUG         , ECORE_X_CURSOR_COFFEE_MUG         ),
46    CURSOR(ELM_CURSOR_CROSS              , ECORE_X_CURSOR_CROSS              ),
47    CURSOR(ELM_CURSOR_CROSS_REVERSE      , ECORE_X_CURSOR_CROSS_REVERSE      ),
48    CURSOR(ELM_CURSOR_CROSSHAIR          , ECORE_X_CURSOR_CROSSHAIR          ),
49    CURSOR(ELM_CURSOR_DIAMOND_CROSS      , ECORE_X_CURSOR_DIAMOND_CROSS      ),
50    CURSOR(ELM_CURSOR_DOT                , ECORE_X_CURSOR_DOT                ),
51    CURSOR(ELM_CURSOR_DOT_BOX_MASK       , ECORE_X_CURSOR_DOT_BOX_MASK       ),
52    CURSOR(ELM_CURSOR_DOUBLE_ARROW       , ECORE_X_CURSOR_DOUBLE_ARROW       ),
53    CURSOR(ELM_CURSOR_DRAFT_LARGE        , ECORE_X_CURSOR_DRAFT_LARGE        ),
54    CURSOR(ELM_CURSOR_DRAFT_SMALL        , ECORE_X_CURSOR_DRAFT_SMALL        ),
55    CURSOR(ELM_CURSOR_DRAPED_BOX         , ECORE_X_CURSOR_DRAPED_BOX         ),
56    CURSOR(ELM_CURSOR_EXCHANGE           , ECORE_X_CURSOR_EXCHANGE           ),
57    CURSOR(ELM_CURSOR_FLEUR              , ECORE_X_CURSOR_FLEUR              ),
58    CURSOR(ELM_CURSOR_GOBBLER            , ECORE_X_CURSOR_GOBBLER            ),
59    CURSOR(ELM_CURSOR_GUMBY              , ECORE_X_CURSOR_GUMBY              ),
60    CURSOR(ELM_CURSOR_HAND1              , ECORE_X_CURSOR_HAND1              ),
61    CURSOR(ELM_CURSOR_HAND2              , ECORE_X_CURSOR_HAND2              ),
62    CURSOR(ELM_CURSOR_HEART              , ECORE_X_CURSOR_HEART              ),
63    CURSOR(ELM_CURSOR_ICON               , ECORE_X_CURSOR_ICON               ),
64    CURSOR(ELM_CURSOR_IRON_CROSS         , ECORE_X_CURSOR_IRON_CROSS         ),
65    CURSOR(ELM_CURSOR_LEFT_PTR           , ECORE_X_CURSOR_LEFT_PTR           ),
66    CURSOR(ELM_CURSOR_LEFT_SIDE          , ECORE_X_CURSOR_LEFT_SIDE          ),
67    CURSOR(ELM_CURSOR_LEFT_TEE           , ECORE_X_CURSOR_LEFT_TEE           ),
68    CURSOR(ELM_CURSOR_LEFTBUTTON         , ECORE_X_CURSOR_LEFTBUTTON         ),
69    CURSOR(ELM_CURSOR_LL_ANGLE           , ECORE_X_CURSOR_LL_ANGLE           ),
70    CURSOR(ELM_CURSOR_LR_ANGLE           , ECORE_X_CURSOR_LR_ANGLE           ),
71    CURSOR(ELM_CURSOR_MAN                , ECORE_X_CURSOR_MAN                ),
72    CURSOR(ELM_CURSOR_MIDDLEBUTTON       , ECORE_X_CURSOR_MIDDLEBUTTON       ),
73    CURSOR(ELM_CURSOR_MOUSE              , ECORE_X_CURSOR_MOUSE              ),
74    CURSOR(ELM_CURSOR_PENCIL             , ECORE_X_CURSOR_PENCIL             ),
75    CURSOR(ELM_CURSOR_PIRATE             , ECORE_X_CURSOR_PIRATE             ),
76    CURSOR(ELM_CURSOR_PLUS               , ECORE_X_CURSOR_PLUS               ),
77    CURSOR(ELM_CURSOR_QUESTION_ARROW     , ECORE_X_CURSOR_QUESTION_ARROW     ),
78    CURSOR(ELM_CURSOR_RIGHT_PTR          , ECORE_X_CURSOR_RIGHT_PTR          ),
79    CURSOR(ELM_CURSOR_RIGHT_SIDE         , ECORE_X_CURSOR_RIGHT_SIDE         ),
80    CURSOR(ELM_CURSOR_RIGHT_TEE          , ECORE_X_CURSOR_RIGHT_TEE          ),
81    CURSOR(ELM_CURSOR_RIGHTBUTTON        , ECORE_X_CURSOR_RIGHTBUTTON        ),
82    CURSOR(ELM_CURSOR_RTL_LOGO           , ECORE_X_CURSOR_RTL_LOGO           ),
83    CURSOR(ELM_CURSOR_SAILBOAT           , ECORE_X_CURSOR_SAILBOAT           ),
84    CURSOR(ELM_CURSOR_SB_DOWN_ARROW      , ECORE_X_CURSOR_SB_DOWN_ARROW      ),
85    CURSOR(ELM_CURSOR_SB_H_DOUBLE_ARROW  , ECORE_X_CURSOR_SB_H_DOUBLE_ARROW  ),
86    CURSOR(ELM_CURSOR_SB_LEFT_ARROW      , ECORE_X_CURSOR_SB_LEFT_ARROW      ),
87    CURSOR(ELM_CURSOR_SB_RIGHT_ARROW     , ECORE_X_CURSOR_SB_RIGHT_ARROW     ),
88    CURSOR(ELM_CURSOR_SB_UP_ARROW        , ECORE_X_CURSOR_SB_UP_ARROW        ),
89    CURSOR(ELM_CURSOR_SB_V_DOUBLE_ARROW  , ECORE_X_CURSOR_SB_V_DOUBLE_ARROW  ),
90    CURSOR(ELM_CURSOR_SHUTTLE            , ECORE_X_CURSOR_SHUTTLE            ),
91    CURSOR(ELM_CURSOR_SIZING             , ECORE_X_CURSOR_SIZING             ),
92    CURSOR(ELM_CURSOR_SPIDER             , ECORE_X_CURSOR_SPIDER             ),
93    CURSOR(ELM_CURSOR_SPRAYCAN           , ECORE_X_CURSOR_SPRAYCAN           ),
94    CURSOR(ELM_CURSOR_STAR               , ECORE_X_CURSOR_STAR               ),
95    CURSOR(ELM_CURSOR_TARGET             , ECORE_X_CURSOR_TARGET             ),
96    CURSOR(ELM_CURSOR_TCROSS             , ECORE_X_CURSOR_TCROSS             ),
97    CURSOR(ELM_CURSOR_TOP_LEFT_ARROW     , ECORE_X_CURSOR_TOP_LEFT_ARROW     ),
98    CURSOR(ELM_CURSOR_TOP_LEFT_CORNER    , ECORE_X_CURSOR_TOP_LEFT_CORNER    ),
99    CURSOR(ELM_CURSOR_TOP_RIGHT_CORNER   , ECORE_X_CURSOR_TOP_RIGHT_CORNER   ),
100    CURSOR(ELM_CURSOR_TOP_SIDE           , ECORE_X_CURSOR_TOP_SIDE           ),
101    CURSOR(ELM_CURSOR_TOP_TEE            , ECORE_X_CURSOR_TOP_TEE            ),
102    CURSOR(ELM_CURSOR_TREK               , ECORE_X_CURSOR_TREK               ),
103    CURSOR(ELM_CURSOR_UL_ANGLE           , ECORE_X_CURSOR_UL_ANGLE           ),
104    CURSOR(ELM_CURSOR_UMBRELLA           , ECORE_X_CURSOR_UMBRELLA           ),
105    CURSOR(ELM_CURSOR_UR_ANGLE           , ECORE_X_CURSOR_UR_ANGLE           ),
106    CURSOR(ELM_CURSOR_WATCH              , ECORE_X_CURSOR_WATCH              ),
107    CURSOR(ELM_CURSOR_XTERM              , ECORE_X_CURSOR_XTERM              )
108 };
109 static const int _cursors_count = sizeof(_cursors)/sizeof(struct _Cursor_Id);
110
111 #define ELM_CURSOR_GET_OR_RETURN(cur, obj, ...)         \
112   Elm_Cursor *cur;                                      \
113   do                                                    \
114     {                                                   \
115        if (!(obj))                                      \
116          {                                              \
117             CRITICAL("Null pointer: " #obj);            \
118             return __VA_ARGS__;                         \
119          }                                              \
120        cur = evas_object_data_get((obj), _cursor_key);  \
121        if (!cur)                                        \
122          {                                              \
123             ERR("Object does not have cursor: " #obj);  \
124             return __VA_ARGS__;                         \
125          }                                              \
126     }                                                   \
127   while (0)
128
129 struct _Elm_Cursor
130 {
131    Evas_Object *obj;
132    Evas_Object *eventarea, *owner;
133    const char *style, *cursor_name;
134    int hot_x, hot_y;
135    Ecore_Evas *ee;
136    Evas *evas;
137 #ifdef HAVE_ELEMENTARY_X
138    Ecore_X_Cursor cursor;
139    Ecore_X_Window win;
140 #endif
141    Eina_Bool visible:1;
142    Eina_Bool use_engine:1;
143    Eina_Bool engine_only:1;
144 };
145
146 static void
147 _elm_cursor_obj_del(void *data, Evas *evas __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
148 {
149    Elm_Cursor *cur = data;
150
151    if (cur) cur->obj = NULL;
152 }
153
154 static Eina_Bool
155 _elm_cursor_obj_add(Evas_Object *obj, Elm_Cursor *cur)
156 {
157    int x, y;
158
159    cur->obj = edje_object_add(cur->evas);
160
161    if (!cur->obj)
162      return EINA_FALSE;
163
164    if (!_elm_theme_object_set(obj, cur->obj, "cursor", cur->cursor_name,
165                               cur->style ? cur->style : "default"))
166      {
167         evas_object_del(cur->obj);
168         cur->obj = NULL;
169         return EINA_FALSE;
170      }
171
172    evas_object_event_callback_add(cur->obj, EVAS_CALLBACK_DEL,
173                                   _elm_cursor_obj_del, cur);
174
175    edje_object_size_min_get(cur->obj, &x, &y);
176    evas_object_resize(cur->obj, x, y);
177    return EINA_TRUE;
178 }
179
180 static void
181 _elm_cursor_set_hot_spots(Elm_Cursor *cur)
182 {
183    const char *str;
184
185    str = edje_object_data_get(cur->obj, "hot_x");
186    if (str) cur->hot_x = atoi(str);
187    else cur->hot_x = 0;
188
189    str = edje_object_data_get(cur->obj, "hot_y");
190    if (str) cur->hot_y = atoi(str);
191    else cur->hot_y = 0;
192 }
193
194 static void
195 _elm_cursor_mouse_in(void *data, Evas *evas __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
196 {
197    Elm_Cursor *cur = data;
198
199    if (cur->visible) return;
200    evas_event_freeze(cur->evas);
201    cur->visible = EINA_TRUE;
202    if ((!cur->engine_only) && (!cur->use_engine))
203      {
204         if (!cur->obj)
205           _elm_cursor_obj_add(cur->eventarea, cur);
206         ecore_evas_object_cursor_set(cur->ee, cur->obj,
207                                      ELM_OBJECT_LAYER_CURSOR, cur->hot_x,
208                                      cur->hot_y);
209      }
210    else
211      {
212 #ifdef HAVE_ELEMENTARY_X
213         if (cur->win)
214           {
215              ecore_x_window_cursor_set(cur->win, cur->cursor);
216           }
217 #endif
218      }
219    evas_event_thaw(cur->evas);
220 }
221
222 static void
223 _elm_cursor_mouse_out(void *data, Evas *evas __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
224 {
225    Evas_Object *sobj_parent;
226    Elm_Cursor *pcur = NULL;
227    Elm_Cursor *cur = data;
228
229    if (!cur->visible) return;
230    evas_event_freeze(cur->evas);
231    cur->visible = EINA_FALSE;
232
233    sobj_parent = evas_object_data_get(cur->eventarea, "elm-parent");
234    while (sobj_parent)
235      {
236         pcur = evas_object_data_get((sobj_parent), _cursor_key);
237         if ((pcur) && (pcur->visible)) break;
238         sobj_parent = evas_object_data_get(sobj_parent, "elm-parent");
239      }
240
241    if (pcur)
242      {
243         pcur->visible = EINA_FALSE;
244         evas_event_thaw(cur->evas);
245         _elm_cursor_mouse_in(pcur, NULL, NULL, NULL);
246         return;
247      }
248
249    if ((!cur->engine_only) || (!cur->use_engine))
250      {
251         ecore_evas_object_cursor_set(cur->ee, NULL, ELM_OBJECT_LAYER_CURSOR,
252                                      cur->hot_x, cur->hot_y);
253      }
254    else
255      {
256 #ifdef HAVE_ELEMENTARY_X
257         if (cur->win)
258           {
259              ecore_x_window_cursor_set(cur->win, ECORE_X_CURSOR_X);
260           }
261 #endif
262      }
263    evas_event_thaw(cur->evas);
264 }
265
266 static void
267 _elm_cursor_del(void *data __UNUSED__, Evas *evas __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
268 {
269    elm_object_cursor_unset(obj);
270 }
271
272 #ifdef HAVE_ELEMENTARY_X
273 static int
274 _elm_cursor_strcmp(const void *data1, const void *data2)
275 {
276    const struct _Cursor_Id *c1 = data1;
277    const struct _Cursor_Id *c2 = data2;
278    return strcmp (c1->name, c2->name);
279 }
280 #endif
281
282 static void
283 _elm_cursor_cur_set(Elm_Cursor *cur)
284 {
285    if (cur->engine_only)
286      {
287         INF("Using only engine cursors");
288         cur->use_engine = EINA_TRUE;
289      }
290    else if (_elm_cursor_obj_add(cur->eventarea, cur))
291      {
292         _elm_cursor_set_hot_spots(cur);
293         cur->use_engine = EINA_FALSE;
294         elm_widget_cursor_add(cur->owner, cur);
295      }
296    else
297      {
298         INF("Cursor couldn't be found on theme: %s", cur->cursor_name);
299         cur->use_engine = EINA_TRUE;
300      }
301
302    if (cur->use_engine)
303      {
304 #ifdef HAVE_ELEMENTARY_X
305         struct _Cursor_Id *cur_id;
306
307         cur_id = bsearch(&(cur->cursor_name), _cursors, _cursors_count,
308                          sizeof(struct _Cursor_Id), _elm_cursor_strcmp);
309
310         cur->win = elm_win_xwindow_get(cur->eventarea);
311         if (cur->win)
312           {
313              if (!cur_id)
314                {
315                   INF("X cursor couldn't be found: %s. Using default.",
316                       cur->cursor_name);
317                   cur->cursor = ecore_x_cursor_shape_get(ECORE_X_CURSOR_X);
318                }
319              else
320                 cur->cursor = ecore_x_cursor_shape_get(cur_id->id);
321           }
322 #endif
323      }
324 }
325
326 /**
327  * Set the cursor to be shown when mouse is over the object
328  *
329  * Set the cursor that will be displayed when mouse is over the
330  * object. The object can have only one cursor set to it, so if
331  * this function is called twice for an object, the previous set
332  * will be unset.
333  * If using X cursors, a definition of all the valid cursor names
334  * is listed on Elementary_Cursors.h. If an invalid name is set
335  * the default cursor will be used.
336  *
337  * This is an internal function that is used by objects with sub-items
338  * that want to provide different cursors for each of them. The @a
339  * owner object should be an elm_widget and will be used to track
340  * theme changes and to feed @a func and @a del_cb. The @a eventarea
341  * may be any object and is the one that should be used later on with
342  * elm_object_cursor apis, such as elm_object_cursor_unset().
343  *
344  * @param eventarea the object being attached a cursor.
345  * @param owner the elm_widget that owns this object, will be used to
346  *        track theme changes and to be used in @a func or @a del_cb.
347  * @param cursor the cursor name to be used.
348  *
349  * @internal
350  * @ingroup Cursors
351  */
352 void
353 elm_object_sub_cursor_set(Evas_Object *eventarea, Evas_Object *owner, const char *cursor)
354 {
355    Elm_Cursor *cur = NULL;
356
357    cur = evas_object_data_get(eventarea, _cursor_key);
358    if (cur)
359      elm_object_cursor_unset(eventarea);
360
361    if (!cursor) return;
362
363    cur = ELM_NEW(Elm_Cursor);
364    if (!cur) return;
365
366    cur->owner = owner;
367    cur->eventarea = eventarea;
368    cur->engine_only = _elm_config->cursor_engine_only;
369    cur->visible = EINA_FALSE;
370
371    cur->cursor_name = eina_stringshare_add(cursor);
372    if (!cur->cursor_name)
373      ERR("Could not store cursor name %s", cursor);
374
375    cur->evas = evas_object_evas_get(eventarea);
376    cur->ee = ecore_evas_ecore_evas_get(cur->evas);
377
378    _elm_cursor_cur_set(cur);
379
380    evas_object_data_set(eventarea, _cursor_key, cur);
381
382    evas_object_event_callback_add(eventarea, EVAS_CALLBACK_MOUSE_IN,
383                                   _elm_cursor_mouse_in, cur);
384    evas_object_event_callback_add(eventarea, EVAS_CALLBACK_MOUSE_OUT,
385                                   _elm_cursor_mouse_out, cur);
386    evas_object_event_callback_add(eventarea, EVAS_CALLBACK_DEL,
387                                   _elm_cursor_del, cur);
388 }
389
390 EAPI void
391 elm_object_cursor_set(Evas_Object *obj, const char *cursor)
392 {
393    EINA_SAFETY_ON_NULL_RETURN(obj);
394    elm_object_sub_cursor_set(obj, obj, cursor);
395 }
396
397 EAPI const char *
398 elm_object_cursor_get(const Evas_Object *obj)
399 {
400    ELM_CURSOR_GET_OR_RETURN(cur, obj, NULL);
401    return cur->cursor_name;
402 }
403
404 EAPI void
405 elm_object_cursor_unset(Evas_Object *obj)
406 {
407    ELM_CURSOR_GET_OR_RETURN(cur, obj);
408
409    eina_stringshare_del(cur->cursor_name);
410    eina_stringshare_del(cur->style);
411
412    if (cur->owner)
413      elm_widget_cursor_del(cur->owner, cur);
414
415    if (cur->obj)
416      evas_object_del(cur->obj);
417
418    if (cur->visible)
419      {
420         if (!cur->use_engine)
421           ecore_evas_object_cursor_set(cur->ee, NULL, ELM_OBJECT_LAYER_CURSOR,
422                                        cur->hot_x, cur->hot_y);
423 #ifdef HAVE_ELEMENTARY_X
424         else if (cur->win)
425           ecore_x_window_cursor_set(cur->win, ECORE_X_CURSOR_X);
426 #endif
427      }
428
429    evas_object_event_callback_del(obj, EVAS_CALLBACK_MOUSE_IN,
430                                   _elm_cursor_mouse_in);
431    evas_object_event_callback_del(obj, EVAS_CALLBACK_MOUSE_OUT,
432                                   _elm_cursor_mouse_out);
433    evas_object_event_callback_del(obj, EVAS_CALLBACK_DEL, _elm_cursor_del);
434
435    evas_object_data_del(obj, _cursor_key);
436    free(cur);
437 }
438
439 EAPI void
440 elm_object_cursor_style_set(Evas_Object *obj, const char *style)
441 {
442    ELM_CURSOR_GET_OR_RETURN(cur, obj);
443
444    if (!eina_stringshare_replace(&cur->style, style))
445      ERR("Could not set current style=%s", style);
446
447    if (cur->use_engine) return;
448
449    if (!cur->obj)
450      {
451         if (!_elm_cursor_obj_add(obj, cur))
452           ERR("Could not create cursor object");
453         else
454           _elm_cursor_set_hot_spots(cur);
455      }
456    else
457      {
458         if (!_elm_theme_object_set(obj, cur->obj, "cursor", cur->cursor_name,
459                                    style))
460           ERR("Could not apply the theme to the cursor style=%s", style);
461         else
462           _elm_cursor_set_hot_spots(cur);
463      }
464 }
465
466 EAPI const char *
467 elm_object_cursor_style_get(const Evas_Object *obj)
468 {
469    ELM_CURSOR_GET_OR_RETURN(cur, obj, NULL);
470    return cur->style ? cur->style : "default";
471 }
472
473 /**
474  * Notify cursor should recalculate its theme.
475  * @internal
476  */
477 void
478 elm_cursor_theme(Elm_Cursor *cur)
479 {
480    if ((!cur) || (!cur->obj)) return;
481    if (!_elm_theme_object_set(cur->eventarea, cur->obj, "cursor",
482                               cur->cursor_name, cur->style))
483      ERR("Could not apply the theme to the cursor style=%s", cur->style);
484    else
485      _elm_cursor_set_hot_spots(cur);
486 }
487
488 EAPI void elm_object_cursor_theme_search_enabled_set(Evas_Object *obj, Eina_Bool theme_search)
489 {
490    ELM_CURSOR_GET_OR_RETURN(cur, obj);
491    cur->engine_only = theme_search;
492    if (cur->obj)
493      {
494         evas_object_del(cur->obj);
495         cur->obj = NULL;
496      }
497    _elm_cursor_cur_set(cur);
498 }
499
500
501 EAPI Eina_Bool
502 elm_object_cursor_theme_search_enabled_get(const Evas_Object *obj)
503 {
504    ELM_CURSOR_GET_OR_RETURN(cur, obj, EINA_FALSE);
505    return cur->engine_only;
506 }