batch remove .'s used with RNA_def_struct_ui_text
[blender.git] / source / blender / editors / space_view3d / view3d_header.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. 
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2004-2008 Blender Foundation.
21  * All rights reserved.
22  *
23  * 
24  * Contributor(s): Blender Foundation
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 #include <string.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32
33 #include "DNA_armature_types.h"
34 #include "DNA_ID.h"
35 #include "DNA_image_types.h"
36 #include "DNA_meshdata_types.h"
37 #include "DNA_mesh_types.h"
38 #include "DNA_object_types.h"
39 #include "DNA_space_types.h"
40 #include "DNA_scene_types.h"
41 #include "DNA_screen_types.h"
42 #include "DNA_texture_types.h"
43 #include "DNA_userdef_types.h" /* U.smooth_viewtx */
44 #include "DNA_view3d_types.h"
45 #include "DNA_windowmanager_types.h"
46
47 #include "RNA_access.h"
48
49 #include "MEM_guardedalloc.h"
50
51 #include "BKE_action.h"
52 #include "BKE_brush.h"
53 #include "BKE_context.h"
54 #include "BKE_curve.h"
55 #include "BKE_depsgraph.h"
56 #include "BKE_displist.h"
57 #include "BKE_effect.h"
58 #include "BKE_global.h"
59 #include "BKE_image.h"
60 #include "BKE_library.h"
61 #include "BKE_main.h"
62 #include "BKE_mesh.h"
63 #include "BKE_modifier.h"
64 #include "BKE_paint.h"
65 #include "BKE_particle.h"
66 #include "BKE_screen.h"
67 #include "BKE_utildefines.h" /* for VECCOPY */
68
69 #include "ED_armature.h"
70 #include "ED_particle.h"
71 #include "ED_object.h"
72 #include "ED_mesh.h"
73 #include "ED_util.h"
74 #include "ED_screen.h"
75 #include "ED_transform.h"
76 #include "ED_types.h"
77
78 #include "WM_api.h"
79 #include "WM_types.h"
80
81 #include "RNA_access.h"
82 #include "RNA_define.h"
83 #include "RNA_enum_types.h"
84
85 #include "BIF_gl.h"
86 #include "BIF_glutil.h"
87
88 #include "BLI_math.h"
89 #include "BLI_blenlib.h"
90 #include "BLI_editVert.h"
91
92 #include "UI_interface.h"
93 #include "UI_interface_icons.h"
94 #include "UI_resources.h"
95 #include "UI_view2d.h"
96
97 #include "view3d_intern.h"
98
99
100 /* View3d->modeselect 
101  * This is a bit of a dodgy hack to enable a 'mode' menu with icons+labels
102  * rather than those buttons.
103  * I know the implementation's not good - it's an experiment to see if this
104  * approach would work well
105  *
106  * This can be cleaned when I make some new 'mode' icons.
107  */
108
109 #define TEST_EDITMESH   if(obedit==0) return; \
110                                                 if( (v3d->lay & obedit->lay)==0 ) return;
111
112 /* XXX port over */     
113 extern void borderselect();
114
115 /* view3d handler codes */
116 #define VIEW3D_HANDLER_BACKGROUND       1
117 #define VIEW3D_HANDLER_PROPERTIES       2
118 #define VIEW3D_HANDLER_OBJECT           3
119 #define VIEW3D_HANDLER_PREVIEW          4
120 #define VIEW3D_HANDLER_MULTIRES         5
121 #define VIEW3D_HANDLER_TRANSFORM        6
122 #define VIEW3D_HANDLER_GREASEPENCIL 7
123 #define VIEW3D_HANDLER_BONESKETCH       8
124
125 /* end XXX ************* */
126
127 static void do_view3d_header_buttons(bContext *C, void *arg, int event);
128
129 #define B_SCENELOCK 101
130 #define B_FULL          102
131 #define B_HOME          103
132 #define B_VIEWBUT       104
133 #define B_PERSP         105
134 #define B_MODESELECT 108
135 #define B_SEL_VERT      110
136 #define B_SEL_EDGE      111
137 #define B_SEL_FACE      112
138 #define B_MAN_TRANS     116
139 #define B_MAN_ROT       117
140 #define B_MAN_SCALE     118
141 #define B_NDOF          119     
142 #define B_MAN_MODE      120
143 #define B_REDR          122
144 #define B_NOP           123
145
146 // XXX quickly ported across
147 static void handle_view3d_lock(bContext *C) 
148 {
149         Main *bmain= CTX_data_main(C);
150         Scene *scene= CTX_data_scene(C);
151         ScrArea *sa= CTX_wm_area(C);
152         View3D *v3d= CTX_wm_view3d(C);
153         
154         if (v3d != NULL && sa != NULL) {
155                 if(v3d->localvd==NULL && v3d->scenelock && sa->spacetype==SPACE_VIEW3D) {
156                         /* copy to scene */
157                         scene->lay= v3d->lay;
158                         scene->camera= v3d->camera;
159
160                         /* not through notifiery, listener don't have context
161                            and non-open screens or spaces need to be updated too */
162                         ED_view3d_scene_layers_update(bmain, scene);
163                         
164                         /* notifiers for scene update */
165                         WM_event_add_notifier(C, NC_SCENE|ND_LAYER, scene);
166                 }
167         }
168 }
169
170 static int layers_exec(bContext *C, wmOperator *op)
171 {
172         Scene *scene= CTX_data_scene(C);
173         ScrArea *sa= CTX_wm_area(C);
174         View3D *v3d= sa->spacedata.first;
175         int nr= RNA_int_get(op->ptr, "nr");
176         int toggle= RNA_boolean_get(op->ptr, "toggle");
177         
178         if(nr < 0)
179                 return OPERATOR_CANCELLED;
180         
181         
182         if(nr == 0) {
183                 /* all layers */
184                 v3d->lay |= (1<<20)-1;
185
186                 if(!v3d->layact)
187                         v3d->layact= 1;
188         }
189         else {
190                 int bit;
191                 nr--;
192
193                 if(RNA_boolean_get(op->ptr, "extend")) {
194                         if(toggle && v3d->lay & (1<<nr) && (v3d->lay & ~(1<<nr)))
195                                 v3d->lay &= ~(1<<nr);
196                         else
197                                 v3d->lay |= (1<<nr);
198                 } else {
199                         v3d->lay = (1<<nr);
200
201                         /* sanity check - when in editmode disallow switching the editmode layer off since its confusing
202                          * an alternative would be to always draw the editmode object. */
203                         if(scene->obedit && (scene->obedit->lay & v3d->lay)==0) {
204                                 for(bit=0; bit<32; bit++) {
205                                         if(scene->obedit->lay & (1<<bit)) {
206                                                 v3d->lay |= 1<<bit;
207                                                 break;
208                                         }
209                                 }
210                         }
211                 }
212                 
213                 /* set active layer, ensure to always have one */
214                 if(v3d->lay & (1<<nr))
215                    v3d->layact= 1<<nr;
216                 else if((v3d->lay & v3d->layact)==0) {
217                         for(bit=0; bit<32; bit++) {
218                                 if(v3d->lay & (1<<bit)) {
219                                         v3d->layact= 1<<bit;
220                                         break;
221                                 }
222                         }
223                 }
224         }
225         
226         if(v3d->scenelock) handle_view3d_lock(C);
227         
228         /* new layers might need unflushed events events */
229         DAG_scene_update_flags(scene, v3d->lay);        /* tags all that moves and flushes */
230
231         ED_area_tag_redraw(sa);
232         
233         return OPERATOR_FINISHED;
234 }
235
236 /* applies shift and alt, lazy coding or ok? :) */
237 /* the local per-keymap-entry keymap will solve it */
238 static int layers_invoke(bContext *C, wmOperator *op, wmEvent *event)
239 {
240         if(event->ctrl || event->oskey)
241                 return OPERATOR_PASS_THROUGH;
242         
243         if(event->shift)
244                 RNA_boolean_set(op->ptr, "extend", 1);
245         
246         if(event->alt) {
247                 int nr= RNA_int_get(op->ptr, "nr") + 10;
248                 RNA_int_set(op->ptr, "nr", nr);
249         }
250         layers_exec(C, op);
251         
252         return OPERATOR_FINISHED;
253 }
254
255 int layers_poll(bContext *C)
256 {
257         return (ED_operator_view3d_active(C) && CTX_wm_view3d(C)->localvd==NULL);
258 }
259
260 void VIEW3D_OT_layers(wmOperatorType *ot)
261 {
262         /* identifiers */
263         ot->name= "Layers";
264         ot->description= "Toggle layer(s) visibility";
265         ot->idname= "VIEW3D_OT_layers";
266         
267         /* api callbacks */
268         ot->invoke= layers_invoke;
269         ot->exec= layers_exec;
270         ot->poll= layers_poll;
271         
272         /* flags */
273         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
274         
275         RNA_def_int(ot->srna, "nr", 1, 0, 20, "Number", "The layer number to set, zero for all layers", 0, 20);
276         RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Add this layer to the current view layers");
277         RNA_def_boolean(ot->srna, "toggle", 1, "Toggle", "Toggle the layer");
278 }
279
280 static char *view3d_modeselect_pup(Scene *scene)
281 {
282         Object *ob= OBACT;
283         static char string[1024];
284         static char formatstr[] = "|%s %%x%d %%i%d";
285         char *str = string;
286
287         str += sprintf(str, "Mode: %%t");
288         
289         str += sprintf(str, formatstr, "Object Mode", OB_MODE_OBJECT, ICON_OBJECT_DATA);
290         
291         if(ob==NULL) return string;
292         
293         /* if active object is editable */
294         if ( ((ob->type == OB_MESH)
295                 || (ob->type == OB_CURVE) || (ob->type == OB_SURF) || (ob->type == OB_FONT)
296                 || (ob->type == OB_MBALL) || (ob->type == OB_LATTICE))) {
297                 
298                 str += sprintf(str, formatstr, "Edit Mode", OB_MODE_EDIT, ICON_EDITMODE_HLT);
299         }
300         else if (ob->type == OB_ARMATURE) {
301                 if (ob->mode & OB_MODE_POSE)
302                         str += sprintf(str, formatstr, "Edit Mode", OB_MODE_EDIT|OB_MODE_POSE, ICON_EDITMODE_HLT);
303                 else
304                         str += sprintf(str, formatstr, "Edit Mode", OB_MODE_EDIT, ICON_EDITMODE_HLT);
305         }
306
307         if (ob->type == OB_MESH) {
308
309                 str += sprintf(str, formatstr, "Sculpt Mode", OB_MODE_SCULPT, ICON_SCULPTMODE_HLT);
310                 str += sprintf(str, formatstr, "Vertex Paint", OB_MODE_VERTEX_PAINT, ICON_VPAINT_HLT);
311                 str += sprintf(str, formatstr, "Texture Paint", OB_MODE_TEXTURE_PAINT, ICON_TPAINT_HLT);
312                 str += sprintf(str, formatstr, "Weight Paint", OB_MODE_WEIGHT_PAINT, ICON_WPAINT_HLT);
313         }
314
315         
316         /* if active object is an armature */
317         if (ob->type==OB_ARMATURE) {
318                 str += sprintf(str, formatstr, "Pose Mode", OB_MODE_POSE, ICON_POSE_HLT);
319         }
320
321         if (ob->particlesystem.first || modifiers_findByType(ob, eModifierType_Cloth) || modifiers_findByType(ob, eModifierType_Softbody)) {
322                 str += sprintf(str, formatstr, "Particle Mode", OB_MODE_PARTICLE_EDIT, ICON_PARTICLEMODE);
323         }
324
325         return (string);
326 }
327
328
329 static void do_view3d_header_buttons(bContext *C, void *arg, int event)
330 {
331         wmWindow *win= CTX_wm_window(C);
332         ToolSettings *ts= CTX_data_tool_settings(C);
333         ScrArea *sa= CTX_wm_area(C);
334         View3D *v3d= sa->spacedata.first;
335         Object *obedit = CTX_data_edit_object(C);
336         EditMesh *em= NULL;
337         int ctrl= win->eventstate->ctrl, shift= win->eventstate->shift;
338         PointerRNA props_ptr;
339         
340         if(obedit && obedit->type==OB_MESH) {
341                 em= BKE_mesh_get_editmesh((Mesh *)obedit->data);
342         }
343         /* watch it: if sa->win does not exist, check that when calling direct drawing routines */
344
345         switch(event) {
346         case B_REDR:
347                 ED_area_tag_redraw(sa);
348                 break;
349                 
350         case B_MODESELECT:
351                 WM_operator_properties_create(&props_ptr, "OBJECT_OT_mode_set");
352                 RNA_enum_set(&props_ptr, "mode", v3d->modeselect);
353                 WM_operator_name_call(C, "OBJECT_OT_mode_set", WM_OP_EXEC_REGION_WIN, &props_ptr);
354                 WM_operator_properties_free(&props_ptr);
355                 break;          
356                 
357         case B_SEL_VERT:
358                 if(em) {
359                         if(shift==0 || em->selectmode==0)
360                                 em->selectmode= SCE_SELECT_VERTEX;
361                         ts->selectmode= em->selectmode;
362                         EM_selectmode_set(em);
363                         WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit->data);
364                         ED_undo_push(C, "Selectmode Set: Vertex");
365                 }
366                 break;
367         case B_SEL_EDGE:
368                 if(em) {
369                         if(shift==0 || em->selectmode==0){
370                                 if( (em->selectmode ^ SCE_SELECT_EDGE) == SCE_SELECT_VERTEX){
371                                         if(ctrl) EM_convertsel(em, SCE_SELECT_VERTEX,SCE_SELECT_EDGE); 
372                                 }
373                                 em->selectmode = SCE_SELECT_EDGE;
374                         }
375                         ts->selectmode= em->selectmode;
376                         EM_selectmode_set(em);
377                         WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit->data);
378                         ED_undo_push(C, "Selectmode Set: Edge");
379                 }
380                 break;
381         case B_SEL_FACE:
382                 if(em) {
383                         if( shift==0 || em->selectmode==0){
384                                 if( ((ts->selectmode ^ SCE_SELECT_FACE) == SCE_SELECT_VERTEX) || ((ts->selectmode ^ SCE_SELECT_FACE) == SCE_SELECT_EDGE)){
385                                         if(ctrl) EM_convertsel(em, (ts->selectmode ^ SCE_SELECT_FACE),SCE_SELECT_FACE);
386                                 }
387                                 em->selectmode = SCE_SELECT_FACE;
388                         }
389                         ts->selectmode= em->selectmode;
390                         EM_selectmode_set(em);
391                         WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit->data);
392                         ED_undo_push(C, "Selectmode Set: Face");
393                 }
394                 break;  
395
396         case B_MAN_TRANS:
397                 if( shift==0 || v3d->twtype==0) {
398                         v3d->twtype= V3D_MANIP_TRANSLATE;
399                 }
400         ED_area_tag_redraw(sa);
401         break;
402         case B_MAN_ROT:
403                 if( shift==0 || v3d->twtype==0) {
404             v3d->twtype= V3D_MANIP_ROTATE;
405                 }
406         ED_area_tag_redraw(sa);
407                 break;
408         case B_MAN_SCALE:
409                 if( shift==0 || v3d->twtype==0) {
410             v3d->twtype= V3D_MANIP_SCALE;
411                 }
412         ED_area_tag_redraw(sa);
413                 break;
414         case B_NDOF:
415         ED_area_tag_redraw(sa);
416                 break;
417         case B_MAN_MODE:
418         ED_area_tag_redraw(sa);
419                 break;
420         default:
421                 break;
422         }
423
424         if(obedit && obedit->type==OB_MESH)
425                 BKE_mesh_end_editmesh(obedit->data, em);
426 }
427
428 /* Returns the icon associated with an object mode */
429 static int object_mode_icon(int mode)
430 {
431         EnumPropertyItem *item = object_mode_items;
432         
433         while(item->name != NULL) {
434                 if(item->value == mode)
435                         return item->icon;
436                 ++item;
437         }
438
439         return ICON_OBJECT_DATAMODE;
440 }
441
442 void uiTemplateHeader3D(uiLayout *layout, struct bContext *C)
443 {
444         bScreen *screen= CTX_wm_screen(C);
445         ScrArea *sa= CTX_wm_area(C);
446         View3D *v3d= sa->spacedata.first;
447         Scene *scene= CTX_data_scene(C);
448         ToolSettings *ts= CTX_data_tool_settings(C);
449         PointerRNA v3dptr, toolsptr, sceneptr;
450         Object *ob= OBACT;
451         Object *obedit = CTX_data_edit_object(C);
452         uiBlock *block;
453         uiLayout *row;
454         
455         RNA_pointer_create(&screen->id, &RNA_Space3DView, v3d, &v3dptr);        
456         RNA_pointer_create(&scene->id, &RNA_ToolSettings, ts, &toolsptr);
457         RNA_pointer_create(&scene->id, &RNA_Scene, scene, &sceneptr);
458
459         block= uiLayoutGetBlock(layout);
460         uiBlockSetHandleFunc(block, do_view3d_header_buttons, NULL);
461
462         /* other buttons: */
463         uiBlockSetEmboss(block, UI_EMBOSS);
464         
465         /* mode */
466         if(ob)
467                 v3d->modeselect = ob->mode;
468         else
469                 v3d->modeselect = OB_MODE_OBJECT;
470                 
471         v3d->flag &= ~V3D_MODE;
472         
473         /* not sure what the v3d->flag is useful for now... modeselect is confusing */
474         if(obedit) v3d->flag |= V3D_EDITMODE;
475         if(ob && (ob->mode & OB_MODE_POSE)) v3d->flag |= V3D_POSEMODE;
476         if(ob && (ob->mode & OB_MODE_VERTEX_PAINT)) v3d->flag |= V3D_VERTEXPAINT;
477         if(ob && (ob->mode & OB_MODE_WEIGHT_PAINT)) v3d->flag |= V3D_WEIGHTPAINT;
478         if(ob && (ob->mode & OB_MODE_TEXTURE_PAINT)) v3d->flag |= V3D_TEXTUREPAINT;
479         if(paint_facesel_test(ob)) v3d->flag |= V3D_FACESELECT;
480
481         uiBlockBeginAlign(block);
482         uiDefIconTextButS(block, MENU, B_MODESELECT, object_mode_icon(v3d->modeselect), view3d_modeselect_pup(scene) , 
483                           0,0,126,20, &(v3d->modeselect), 0, 0, 0, 0, "Mode");
484         uiBlockEndAlign(block);
485         
486         /* Draw type */
487         uiItemR(layout, "", 0, &v3dptr, "viewport_shading", UI_ITEM_R_ICON_ONLY);
488
489         if (obedit==NULL && ((ob && ob->mode & (OB_MODE_VERTEX_PAINT|OB_MODE_WEIGHT_PAINT|OB_MODE_TEXTURE_PAINT)))) {
490                 /* Manipulators aren't used in weight paint mode */
491                 
492                 PointerRNA meshptr;
493
494                 RNA_pointer_create(&ob->id, &RNA_Mesh, ob->data, &meshptr);
495                 uiItemR(layout, "", 0, &meshptr, "use_paint_mask", UI_ITEM_R_ICON_ONLY);
496         } else {
497                 char *str_menu;
498
499                 row= uiLayoutRow(layout, 1);
500                 uiItemR(row, "", 0, &v3dptr, "pivot_point", UI_ITEM_R_ICON_ONLY);
501                 uiItemR(row, "", 0, &v3dptr, "pivot_point_align", UI_ITEM_R_ICON_ONLY);
502
503                 /* NDOF */
504                 /* Not implemented yet
505                  if (G.ndofdevice ==0 ) {
506                         uiDefIconTextButC(block, ICONTEXTROW,B_NDOF, ICON_NDOF_TURN, ndof_pup(), 0,0,XIC+10,YIC, &(v3d->ndofmode), 0, 3.0, 0, 0, "Ndof mode");
507                 
508                         uiDefIconButC(block, TOG, B_NDOF,  ICON_NDOF_DOM,
509                                       0,0,XIC,YIC,
510                                       &v3d->ndoffilter, 0, 1, 0, 0, "dominant axis");   
511                 }
512                  */
513
514                 /* Transform widget / manipulators */
515                 row= uiLayoutRow(layout, 1);
516                 uiItemR(row, "", 0, &v3dptr, "manipulator", UI_ITEM_R_ICON_ONLY);
517                 block= uiLayoutGetBlock(row);
518                 
519                 if(v3d->twflag & V3D_USE_MANIPULATOR) {
520                         uiDefIconButBitS(block, TOG, V3D_MANIP_TRANSLATE, B_MAN_TRANS, ICON_MAN_TRANS, 0,0,XIC,YIC, &v3d->twtype, 1.0, 0.0, 0, 0, "Translate manipulator mode");
521                         uiDefIconButBitS(block, TOG, V3D_MANIP_ROTATE, B_MAN_ROT, ICON_MAN_ROT, 0,0,XIC,YIC, &v3d->twtype, 1.0, 0.0, 0, 0, "Rotate manipulator mode");
522                         uiDefIconButBitS(block, TOG, V3D_MANIP_SCALE, B_MAN_SCALE, ICON_MAN_SCALE, 0,0,XIC,YIC, &v3d->twtype, 1.0, 0.0, 0, 0, "Scale manipulator mode");
523                 }
524                         
525                 if (v3d->twmode > (BIF_countTransformOrientation(C) - 1) + V3D_MANIP_CUSTOM) {
526                         v3d->twmode = 0;
527                 }
528                         
529                 str_menu = BIF_menustringTransformOrientation(C, "Orientation");
530                 uiDefButS(block, MENU, B_MAN_MODE, str_menu,0,0,70,YIC, &v3d->twmode, 0, 0, 0, 0, "Transform Orientation");
531                 MEM_freeN(str_menu);
532         }
533                 
534         if(obedit==NULL && v3d->localvd==NULL) {
535                 int ob_lay = ob ? ob->lay : 0;
536                 
537                 /* Layers */
538                 if (v3d->scenelock)
539                         uiTemplateLayers(layout, &sceneptr, "visible_layers", &v3dptr, "used_layers", ob_lay);
540                 else
541                         uiTemplateLayers(layout, &v3dptr, "visible_layers", &v3dptr, "used_layers", ob_lay);
542
543                 /* Scene lock */
544                 uiItemR(layout, "", 0, &v3dptr, "lock_camera_and_layers", UI_ITEM_R_ICON_ONLY);
545         }
546         
547         /* selection modus, dont use python for this since it cant do the toggle buttons with shift+click as well as clicking to set one. */
548         if(obedit && (obedit->type == OB_MESH)) {
549                 EditMesh *em= BKE_mesh_get_editmesh((Mesh *)obedit->data);
550
551                 row= uiLayoutRow(layout, 1);
552                 block= uiLayoutGetBlock(row);
553                 uiDefIconButBitS(block, TOG, SCE_SELECT_VERTEX, B_SEL_VERT, ICON_VERTEXSEL, 0,0,XIC,YIC, &em->selectmode, 1.0, 0.0, 0, 0, "Vertex select mode");
554                 uiDefIconButBitS(block, TOG, SCE_SELECT_EDGE, B_SEL_EDGE, ICON_EDGESEL, 0,0,XIC,YIC, &em->selectmode, 1.0, 0.0, 0, 0, "Edge select mode");
555                 uiDefIconButBitS(block, TOG, SCE_SELECT_FACE, B_SEL_FACE, ICON_FACESEL, 0,0,XIC,YIC, &em->selectmode, 1.0, 0.0, 0, 0, "Face select mode");
556
557                 BKE_mesh_end_editmesh(obedit->data, em);
558         }
559 }