moving textures up and down didnt move the material flag, made editmesh skin Ctrl...
[blender-staging.git] / source / blender / editors / render / render_shading.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  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software Foundation,
17  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18  *
19  * The Original Code is Copyright (C) 2009 Blender Foundation.
20  * All rights reserved.
21  *
22  * Contributor(s): Blender Foundation
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 #include <stdlib.h>
28 #include <string.h>
29
30 #include "MEM_guardedalloc.h"
31
32 #include "DNA_curve_types.h"
33 #include "DNA_lamp_types.h"
34 #include "DNA_material_types.h"
35 #include "DNA_meshdata_types.h"
36 #include "DNA_node_types.h"
37 #include "DNA_object_types.h"
38 #include "DNA_texture_types.h"
39 #include "DNA_scene_types.h"
40 #include "DNA_world_types.h"
41
42 #include "BKE_context.h"
43 #include "BKE_depsgraph.h"
44 #include "BKE_font.h"
45 #include "BKE_icons.h"
46 #include "BKE_library.h"
47 #include "BKE_main.h"
48 #include "BKE_material.h"
49 #include "BKE_node.h"
50 #include "BKE_scene.h"
51 #include "BKE_texture.h"
52 #include "BKE_utildefines.h"
53 #include "BKE_world.h"
54
55 #include "BLI_arithb.h"
56 #include "BLI_editVert.h"
57 #include "BLI_listbase.h"
58
59 #include "GPU_material.h"
60
61 #include "RNA_access.h"
62 #include "RNA_enum_types.h"
63
64 #include "WM_api.h"
65 #include "WM_types.h"
66
67 #include "ED_curve.h"
68 #include "ED_mesh.h"
69 #include "ED_render.h"
70
71 #include "RNA_access.h"
72 #include "RNA_define.h"
73
74 #include "UI_interface.h"
75 #include "UI_resources.h"
76
77 #include "render_intern.h"      // own include
78
79 /***************************** Updates ***********************************
80  * ED_render_id_flush_update gets called from DAG_id_flush_update, to do *
81  * editor level updates when the ID changes. when these ID blocks are in *
82  * the dependency graph, we can get rid of the manual dependency checks  */
83
84 static int mtex_use_tex(MTex **mtex, int tot, Tex *tex)
85 {
86         int a;
87
88         if(!mtex)
89                 return 0;
90
91         for(a=0; a<tot; a++)
92                 if(mtex[a] && mtex[a]->tex == tex)
93                         return 1;
94         
95         return 0;
96 }
97
98 static int nodes_use_tex(bNodeTree *ntree, Tex *tex)
99 {
100         bNode *node;
101
102         for(node=ntree->nodes.first; node; node= node->next) {
103                 if(node->id) {
104                         if(node->id == (ID*)tex) {
105                                 return 1;
106                         }
107                         else if(node->type==NODE_GROUP) {
108                                 if(nodes_use_tex((bNodeTree *)node->id, tex))
109                                         return 1;
110                         }
111                 }
112         }
113
114         return 0;
115 }
116
117 static void material_changed(Main *bmain, Material *ma)
118 {
119         /* icons */
120         BKE_icon_changed(BKE_icon_getid(&ma->id));
121
122         /* glsl */
123         if(ma->gpumaterial.first)
124                 GPU_material_free(ma);
125 }
126
127 static void texture_changed(Main *bmain, Tex *tex)
128 {
129         Material *ma;
130         Lamp *la;
131         World *wo;
132
133         /* icons */
134         BKE_icon_changed(BKE_icon_getid(&tex->id));
135
136         /* find materials */
137         for(ma=bmain->mat.first; ma; ma=ma->id.next) {
138                 if(mtex_use_tex(ma->mtex, MAX_MTEX, tex));
139                 else if(ma->use_nodes && ma->nodetree && nodes_use_tex(ma->nodetree, tex));
140                 else continue;
141
142                 BKE_icon_changed(BKE_icon_getid(&ma->id));
143
144                 if(ma->gpumaterial.first)
145                         GPU_material_free(ma);
146         }
147
148         /* find lamps */
149         for(la=bmain->lamp.first; la; la=la->id.next) {
150                 if(mtex_use_tex(la->mtex, MAX_MTEX, tex));
151                 else continue;
152
153                 BKE_icon_changed(BKE_icon_getid(&la->id));
154         }
155
156         /* find worlds */
157         for(wo=bmain->world.first; wo; wo=wo->id.next) {
158                 if(mtex_use_tex(wo->mtex, MAX_MTEX, tex));
159                 else continue;
160
161                 BKE_icon_changed(BKE_icon_getid(&wo->id));
162         }
163 }
164
165 static void lamp_changed(Main *bmain, Lamp *la)
166 {
167         Object *ob;
168         Material *ma;
169
170         /* icons */
171         BKE_icon_changed(BKE_icon_getid(&la->id));
172
173         /* glsl */
174         for(ob=bmain->object.first; ob; ob=ob->id.next)
175                 if(ob->data == la && ob->gpulamp.first)
176                         GPU_lamp_free(ob);
177
178         for(ma=bmain->mat.first; ma; ma=ma->id.next)
179                 if(ma->gpumaterial.first)
180                         GPU_material_free(ma);
181 }
182
183 static void world_changed(Main *bmain, World *wo)
184 {
185         Material *ma;
186
187         /* icons */
188         BKE_icon_changed(BKE_icon_getid(&wo->id));
189
190         /* glsl */
191         for(ma=bmain->mat.first; ma; ma=ma->id.next)
192                 if(ma->gpumaterial.first)
193                         GPU_material_free(ma);
194 }
195
196 void ED_render_id_flush_update(Main *bmain, ID *id)
197 {
198         switch(GS(id->name)) {
199                 case ID_MA:
200                         material_changed(bmain, (Material*)id);
201                         break;
202                 case ID_TE:
203                         texture_changed(bmain, (Tex*)id);
204                         break;
205                 case ID_WO:
206                         world_changed(bmain, (World*)id);
207                         break;
208                 case ID_LA:
209                         lamp_changed(bmain, (Lamp*)id);
210                         break;
211                 default:
212                         break;
213         }
214 }
215
216 /********************** material slot operators *********************/
217
218 static int material_slot_add_exec(bContext *C, wmOperator *op)
219 {
220         Object *ob= CTX_data_pointer_get_type(C, "object", &RNA_Object).data;
221
222         if(!ob)
223                 return OPERATOR_CANCELLED;
224
225         object_add_material_slot(ob);
226         WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
227         
228         return OPERATOR_FINISHED;
229 }
230
231 void OBJECT_OT_material_slot_add(wmOperatorType *ot)
232 {
233         /* identifiers */
234         ot->name= "Add Material Slot";
235         ot->idname= "OBJECT_OT_material_slot_add";
236         ot->description="Add a new material slot or duplicate the selected one.";
237         
238         /* api callbacks */
239         ot->exec= material_slot_add_exec;
240
241         /* flags */
242         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
243 }
244
245 static int material_slot_remove_exec(bContext *C, wmOperator *op)
246 {
247         Object *ob= CTX_data_pointer_get_type(C, "object", &RNA_Object).data;
248
249         if(!ob)
250                 return OPERATOR_CANCELLED;
251
252         object_remove_material_slot(ob);
253         WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
254         
255         return OPERATOR_FINISHED;
256 }
257
258 void OBJECT_OT_material_slot_remove(wmOperatorType *ot)
259 {
260         /* identifiers */
261         ot->name= "Remove Material Slot";
262         ot->idname= "OBJECT_OT_material_slot_remove";
263         ot->description="Remove the selected material slot.";
264         
265         /* api callbacks */
266         ot->exec= material_slot_remove_exec;
267
268         /* flags */
269         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
270 }
271
272 static int material_slot_assign_exec(bContext *C, wmOperator *op)
273 {
274         Object *ob= CTX_data_pointer_get_type(C, "object", &RNA_Object).data;
275
276         if(!ob)
277                 return OPERATOR_CANCELLED;
278
279         if(ob && ob->actcol>0) {
280                 if(ob->type == OB_MESH) {
281                         EditMesh *em= ((Mesh*)ob->data)->edit_mesh;
282                         EditFace *efa;
283
284                         if(em) {
285                                 for(efa= em->faces.first; efa; efa=efa->next)
286                                         if(efa->f & SELECT)
287                                                 efa->mat_nr= ob->actcol-1;
288                         }
289                 }
290                 else if(ELEM(ob->type, OB_CURVE, OB_SURF)) {
291                         ListBase *editnurb= ((Curve*)ob->data)->editnurb;
292                         Nurb *nu;
293
294                         if(editnurb) {
295                                 for(nu= editnurb->first; nu; nu= nu->next)
296                                         if(isNurbsel(nu))
297                                                 nu->mat_nr= nu->charidx= ob->actcol-1;
298                         }
299                 }
300                 else if(ob->type == OB_FONT) {
301                         EditFont *ef= ((Curve*)ob->data)->editfont;
302                 int i, selstart, selend;
303
304                         if(ef && BKE_font_getselection(ob, &selstart, &selend)) {
305                                 for(i=selstart; i<=selend; i++)
306                                         ef->textbufinfo[i].mat_nr = ob->actcol-1;
307                         }
308                 }
309         }
310
311     DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
312     WM_event_add_notifier(C, NC_GEOM|ND_DATA, ob->data);
313         
314         return OPERATOR_FINISHED;
315 }
316
317 void OBJECT_OT_material_slot_assign(wmOperatorType *ot)
318 {
319         /* identifiers */
320         ot->name= "Assign Material Slot";
321         ot->idname= "OBJECT_OT_material_slot_assign";
322         ot->description="Assign the material in the selected material slot to the selected vertices.";
323         
324         /* api callbacks */
325         ot->exec= material_slot_assign_exec;
326
327         /* flags */
328         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
329 }
330
331 static int material_slot_de_select(bContext *C, int select)
332 {
333         Object *ob= CTX_data_pointer_get_type(C, "object", &RNA_Object).data;
334
335         if(!ob)
336                 return OPERATOR_CANCELLED;
337
338         if(ob->type == OB_MESH) {
339                 EditMesh *em= ((Mesh*)ob->data)->edit_mesh;
340
341                 if(em) {
342                         if(select)
343                                 EM_select_by_material(em, ob->actcol-1);
344                         else
345                                 EM_deselect_by_material(em, ob->actcol-1);
346                 }
347         }
348         else if ELEM(ob->type, OB_CURVE, OB_SURF) {
349                 ListBase *editnurb= ((Curve*)ob->data)->editnurb;
350                 Nurb *nu;
351                 BPoint *bp;
352                 BezTriple *bezt;
353                 int a;
354
355                 for(nu= editnurb->first; nu; nu=nu->next) {
356                         if(nu->mat_nr==ob->actcol-1) {
357                                 if(nu->bezt) {
358                                         a= nu->pntsu;
359                                         bezt= nu->bezt;
360                                         while(a--) {
361                                                 if(bezt->hide==0) {
362                                                         if(select) {
363                                                                 bezt->f1 |= SELECT;
364                                                                 bezt->f2 |= SELECT;
365                                                                 bezt->f3 |= SELECT;
366                                                         }
367                                                         else {
368                                                                 bezt->f1 &= ~SELECT;
369                                                                 bezt->f2 &= ~SELECT;
370                                                                 bezt->f3 &= ~SELECT;
371                                                         }
372                                                 }
373                                                 bezt++;
374                                         }
375                                 }
376                                 else if(nu->bp) {
377                                         a= nu->pntsu*nu->pntsv;
378                                         bp= nu->bp;
379                                         while(a--) {
380                                                 if(bp->hide==0) {
381                                                         if(select) bp->f1 |= SELECT;
382                                                         else bp->f1 &= ~SELECT;
383                                                 }
384                                                 bp++;
385                                         }
386                                 }
387                         }
388                 }
389         }
390
391     WM_event_add_notifier(C, NC_GEOM|ND_SELECT, ob->data);
392
393         return OPERATOR_FINISHED;
394 }
395
396 static int material_slot_select_exec(bContext *C, wmOperator *op)
397 {
398         return material_slot_de_select(C, 1);
399 }
400
401 void OBJECT_OT_material_slot_select(wmOperatorType *ot)
402 {
403         /* identifiers */
404         ot->name= "Select Material Slot";
405         ot->idname= "OBJECT_OT_material_slot_select";
406         ot->description="Select vertices assigned to the selected material slot.";
407         
408         /* api callbacks */
409         ot->exec= material_slot_select_exec;
410
411         /* flags */
412         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
413 }
414
415 static int material_slot_deselect_exec(bContext *C, wmOperator *op)
416 {
417         return material_slot_de_select(C, 0);
418 }
419
420 void OBJECT_OT_material_slot_deselect(wmOperatorType *ot)
421 {
422         /* identifiers */
423         ot->name= "Deselect Material Slot";
424         ot->idname= "OBJECT_OT_material_slot_deselect";
425         ot->description="Deselect vertices assigned to the selected material slot.";
426         
427         /* api callbacks */
428         ot->exec= material_slot_deselect_exec;
429
430         /* flags */
431         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
432 }
433
434
435 static int material_slot_copy_exec(bContext *C, wmOperator *op)
436 {
437         Object *ob= CTX_data_pointer_get_type(C, "object", &RNA_Object).data;
438         Material ***matar;
439
440         if(!ob || !(matar= give_matarar(ob)))
441                 return OPERATOR_CANCELLED;
442
443         CTX_DATA_BEGIN(C, Object*, ob_iter, selected_editable_objects) {
444                 if(ob != ob_iter && give_matarar(ob_iter)) {
445                         assign_matarar(ob_iter, matar, ob->totcol);
446                         if(ob_iter->totcol==ob->totcol) {
447                                 ob_iter->actcol= ob->actcol;
448                                 WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob_iter);
449                         }
450                 }
451         }
452         CTX_DATA_END;
453
454         return OPERATOR_FINISHED;
455 }
456
457
458 void OBJECT_OT_material_slot_copy(wmOperatorType *ot)
459 {
460         /* identifiers */
461         ot->name= "Copy Material to Others";
462         ot->idname= "OBJECT_OT_material_slot_copy";
463         ot->description="Copies materials to other selected objects.";
464
465         /* api callbacks */
466         ot->exec= material_slot_copy_exec;
467
468         /* flags */
469         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
470 }
471
472 /********************** new material operator *********************/
473
474 static int new_material_exec(bContext *C, wmOperator *op)
475 {
476         Material *ma= CTX_data_pointer_get_type(C, "material", &RNA_Material).data;
477         PointerRNA ptr, idptr;
478         PropertyRNA *prop;
479
480         /* add or copy material */
481         if(ma)
482                 ma= copy_material(ma);
483         else
484                 ma= add_material("Material");
485
486         /* hook into UI */
487         uiIDContextProperty(C, &ptr, &prop);
488
489         if(prop) {
490                 /* when creating new ID blocks, use is already 1, but RNA
491                  * pointer se also increases user, so this compensates it */
492                 ma->id.us--;
493
494                 RNA_id_pointer_create(&ma->id, &idptr);
495                 RNA_property_pointer_set(&ptr, prop, idptr);
496                 RNA_property_update(C, &ptr, prop);
497         }
498
499         WM_event_add_notifier(C, NC_MATERIAL|NA_ADDED, ma);
500         
501         return OPERATOR_FINISHED;
502 }
503
504 void MATERIAL_OT_new(wmOperatorType *ot)
505 {
506         /* identifiers */
507         ot->name= "New Material";
508         ot->idname= "MATERIAL_OT_new";
509         ot->description="Add a new material.";
510         
511         /* api callbacks */
512         ot->exec= new_material_exec;
513
514         /* flags */
515         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
516 }
517
518 /********************** new texture operator *********************/
519
520 static int new_texture_exec(bContext *C, wmOperator *op)
521 {
522         Tex *tex= CTX_data_pointer_get_type(C, "texture", &RNA_Texture).data;
523         PointerRNA ptr, idptr;
524         PropertyRNA *prop;
525
526         /* add or copy texture */
527         if(tex)
528                 tex= copy_texture(tex);
529         else
530                 tex= add_texture("Texture");
531
532         /* hook into UI */
533         uiIDContextProperty(C, &ptr, &prop);
534
535         if(prop) {
536                 /* when creating new ID blocks, use is already 1, but RNA
537                  * pointer se also increases user, so this compensates it */
538                 tex->id.us--;
539
540                 RNA_id_pointer_create(&tex->id, &idptr);
541                 RNA_property_pointer_set(&ptr, prop, idptr);
542                 RNA_property_update(C, &ptr, prop);
543         }
544
545         WM_event_add_notifier(C, NC_TEXTURE|NA_ADDED, tex);
546         
547         return OPERATOR_FINISHED;
548 }
549
550 void TEXTURE_OT_new(wmOperatorType *ot)
551 {
552         /* identifiers */
553         ot->name= "New Texture";
554         ot->idname= "TEXTURE_OT_new";
555         ot->description="Add a new texture.";
556         
557         /* api callbacks */
558         ot->exec= new_texture_exec;
559
560         /* flags */
561         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
562 }
563
564 /********************** new world operator *********************/
565
566 static int new_world_exec(bContext *C, wmOperator *op)
567 {
568         World *wo= CTX_data_pointer_get_type(C, "world", &RNA_World).data;
569         PointerRNA ptr, idptr;
570         PropertyRNA *prop;
571
572         /* add or copy world */
573         if(wo)
574                 wo= copy_world(wo);
575         else
576                 wo= add_world("World");
577
578         /* hook into UI */
579         uiIDContextProperty(C, &ptr, &prop);
580
581         if(prop) {
582                 /* when creating new ID blocks, use is already 1, but RNA
583                  * pointer se also increases user, so this compensates it */
584                 wo->id.us--;
585
586                 RNA_id_pointer_create(&wo->id, &idptr);
587                 RNA_property_pointer_set(&ptr, prop, idptr);
588                 RNA_property_update(C, &ptr, prop);
589         }
590
591         WM_event_add_notifier(C, NC_WORLD|NA_ADDED, wo);
592         
593         return OPERATOR_FINISHED;
594 }
595
596 void WORLD_OT_new(wmOperatorType *ot)
597 {
598         /* identifiers */
599         ot->name= "New World";
600         ot->idname= "WORLD_OT_new";
601         ot->description= "Add a new world.";
602         
603         /* api callbacks */
604         ot->exec= new_world_exec;
605
606         /* flags */
607         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
608 }
609
610 /********************** render layer operators *********************/
611
612 static int render_layer_add_exec(bContext *C, wmOperator *op)
613 {
614         Scene *scene= CTX_data_scene(C);
615
616         scene_add_render_layer(scene);
617         scene->r.actlay= BLI_countlist(&scene->r.layers) - 1;
618
619         WM_event_add_notifier(C, NC_SCENE|ND_RENDER_OPTIONS, scene);
620         
621         return OPERATOR_FINISHED;
622 }
623
624 void SCENE_OT_render_layer_add(wmOperatorType *ot)
625 {
626         /* identifiers */
627         ot->name= "Add Render Layer";
628         ot->idname= "SCENE_OT_render_layer_add";
629         ot->description="Add a render layer.";
630         
631         /* api callbacks */
632         ot->exec= render_layer_add_exec;
633
634         /* flags */
635         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
636 }
637
638 static int render_layer_remove_exec(bContext *C, wmOperator *op)
639 {
640         Scene *scene= CTX_data_scene(C);
641         SceneRenderLayer *rl;
642         int act= scene->r.actlay;
643
644         if(BLI_countlist(&scene->r.layers) <= 1)
645                 return OPERATOR_CANCELLED;
646         
647         rl= BLI_findlink(&scene->r.layers, scene->r.actlay);
648         BLI_remlink(&scene->r.layers, rl);
649         MEM_freeN(rl);
650
651         scene->r.actlay= 0;
652         
653         if(scene->nodetree) {
654                 bNode *node;
655                 for(node= scene->nodetree->nodes.first; node; node= node->next) {
656                         if(node->type==CMP_NODE_R_LAYERS && node->id==NULL) {
657                                 if(node->custom1==act)
658                                         node->custom1= 0;
659                                 else if(node->custom1>act)
660                                         node->custom1--;
661                         }
662                 }
663         }
664
665         WM_event_add_notifier(C, NC_SCENE|ND_RENDER_OPTIONS, scene);
666         
667         return OPERATOR_FINISHED;
668 }
669
670 void SCENE_OT_render_layer_remove(wmOperatorType *ot)
671 {
672         /* identifiers */
673         ot->name= "Remove Render Layer";
674         ot->idname= "SCENE_OT_render_layer_remove";
675         ot->description="Remove the selected render layer.";
676         
677         /* api callbacks */
678         ot->exec= render_layer_remove_exec;
679
680         /* flags */
681         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
682 }
683
684 static int texture_slot_move(bContext *C, wmOperator *op)
685 {
686         ID *id= CTX_data_pointer_get_type(C, "texture_slot", &RNA_TextureSlot).id.data;
687
688         if(id) {
689                 MTex **mtex_ar, *mtexswap;
690                 short act;
691                 int type= RNA_enum_get(op->ptr, "type");
692
693                 give_active_mtex(id, &mtex_ar, &act);
694
695                 if(type == -1) { /* Up */
696                         if(act > 0) {
697                                 mtexswap = mtex_ar[act];
698                                 mtex_ar[act] = mtex_ar[act-1];
699                                 mtex_ar[act-1] = mtexswap;
700
701                                 if(GS(id->name)==ID_MA) {
702                                         Material *ma= (Material *)id;
703                                         int mtexuse = ma->septex & (1<<act);
704                                         ma->septex &= ~(1<<act);
705                                         ma->septex |= (ma->septex & (1<<(act-1))) << 1;
706                                         ma->septex &= ~(1<<(act-1));
707                                         ma->septex |= mtexuse >> 1;
708                                 }
709
710                                 set_active_mtex(id, act-1);
711                         }
712                 }
713                 else { /* Down */
714                         if(act < MAX_MTEX-1) {
715                                 mtexswap = mtex_ar[act];
716                                 mtex_ar[act] = mtex_ar[act+1];
717                                 mtex_ar[act+1] = mtexswap;
718
719                                 if(GS(id->name)==ID_MA) {
720                                         Material *ma= (Material *)id;
721                                         int mtexuse = ma->septex & (1<<act);
722                                         ma->septex &= ~(1<<act);
723                                         ma->septex |= (ma->septex & (1<<(act+1))) >> 1;
724                                         ma->septex &= ~(1<<(act+1));
725                                         ma->septex |= mtexuse << 1;
726                                 }
727
728                                 set_active_mtex(id, act+1);
729                         }
730                 }
731
732                 WM_event_add_notifier(C, NC_TEXTURE, CTX_data_scene(C));
733         }
734
735         return OPERATOR_FINISHED;
736 }
737
738 void TEXTURE_OT_slot_move(wmOperatorType *ot)
739 {
740         static EnumPropertyItem slot_move[] = {
741                 {-1, "UP", 0, "Up", ""},
742                 {1, "DOWN", 0, "Down", ""},
743                 {0, NULL, 0, NULL, NULL}
744         };
745
746         /* identifiers */
747         ot->name= "Move Texture Slot";
748         ot->idname= "TEXTURE_OT_slot_move";
749         ot->description="Move texture slots up and down.";
750
751         /* api callbacks */
752         ot->exec= texture_slot_move;
753
754         /* flags */
755         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
756
757         RNA_def_enum(ot->srna, "type", slot_move, 0, "Type", "");
758 }