1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2018 Blender Foundation.
17  * All rights reserved.
18  */
19 
20 /** \file
21  * \ingroup edobj
22  */
23 
24 #include <math.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 
29 #include "MEM_guardedalloc.h"
30 
31 #include "DNA_gpencil_types.h"
32 #include "DNA_object_types.h"
33 #include "DNA_scene_types.h"
34 #include "DNA_shader_fx_types.h"
35 
36 #include "BLI_listbase.h"
37 #include "BLI_string.h"
38 #include "BLI_string_utf8.h"
39 #include "BLI_utildefines.h"
40 
41 #include "BLT_translation.h"
42 
43 #include "BKE_context.h"
44 #include "BKE_main.h"
45 #include "BKE_object.h"
46 #include "BKE_report.h"
47 #include "BKE_shader_fx.h"
48 
49 #include "DEG_depsgraph.h"
50 #include "DEG_depsgraph_build.h"
51 #include "DEG_depsgraph_query.h"
52 
53 #include "RNA_access.h"
54 #include "RNA_define.h"
55 #include "RNA_enum_types.h"
56 
57 #include "ED_object.h"
58 #include "ED_screen.h"
59 
60 #include "UI_interface.h"
61 
62 #include "WM_api.h"
63 #include "WM_types.h"
64 
65 #include "object_intern.h"
66 
67 /******************************** API ****************************/
68 
ED_object_shaderfx_add(ReportList * reports,Main * bmain,Scene * UNUSED (scene),Object * ob,const char * name,int type)69 ShaderFxData *ED_object_shaderfx_add(
70     ReportList *reports, Main *bmain, Scene *UNUSED(scene), Object *ob, const char *name, int type)
71 {
72   ShaderFxData *new_fx = NULL;
73   const ShaderFxTypeInfo *fxi = BKE_shaderfx_get_info(type);
74 
75   if (ob->type != OB_GPENCIL) {
76     BKE_reportf(reports, RPT_WARNING, "Effect cannot be added to object '%s'", ob->id.name + 2);
77     return NULL;
78   }
79 
80   if (fxi->flags & eShaderFxTypeFlag_Single) {
81     if (BKE_shaderfx_findby_type(ob, type)) {
82       BKE_report(reports, RPT_WARNING, "Only one Effect of this type is allowed");
83       return NULL;
84     }
85   }
86 
87   /* get new effect data to add */
88   new_fx = BKE_shaderfx_new(type);
89 
90   BLI_addtail(&ob->shader_fx, new_fx);
91 
92   if (name) {
93     BLI_strncpy_utf8(new_fx->name, name, sizeof(new_fx->name));
94   }
95 
96   /* make sure effect data has unique name */
97   BKE_shaderfx_unique_name(&ob->shader_fx, new_fx);
98 
99   bGPdata *gpd = ob->data;
100   DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
101 
102   DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
103   DEG_relations_tag_update(bmain);
104 
105   return new_fx;
106 }
107 
108 /* Return true if the object has a effect of type 'type' other than
109  * the shaderfx pointed to be 'exclude', otherwise returns false. */
UNUSED_FUNCTION(object_has_shaderfx)110 static bool UNUSED_FUNCTION(object_has_shaderfx)(const Object *ob,
111                                                  const ShaderFxData *exclude,
112                                                  ShaderFxType type)
113 {
114   ShaderFxData *fx;
115 
116   for (fx = ob->shader_fx.first; fx; fx = fx->next) {
117     if ((fx != exclude) && (fx->type == type)) {
118       return true;
119     }
120   }
121 
122   return false;
123 }
124 
object_shaderfx_remove(Main * bmain,Object * ob,ShaderFxData * fx,bool * UNUSED (r_sort_depsgraph))125 static bool object_shaderfx_remove(Main *bmain,
126                                    Object *ob,
127                                    ShaderFxData *fx,
128                                    bool *UNUSED(r_sort_depsgraph))
129 {
130   /* It seems on rapid delete it is possible to
131    * get called twice on same effect, so make
132    * sure it is in list. */
133   if (BLI_findindex(&ob->shader_fx, fx) == -1) {
134     return 0;
135   }
136 
137   DEG_relations_tag_update(bmain);
138 
139   BLI_remlink(&ob->shader_fx, fx);
140   BKE_shaderfx_free(fx);
141   BKE_object_free_derived_caches(ob);
142 
143   return 1;
144 }
145 
ED_object_shaderfx_remove(ReportList * reports,Main * bmain,Object * ob,ShaderFxData * fx)146 bool ED_object_shaderfx_remove(ReportList *reports, Main *bmain, Object *ob, ShaderFxData *fx)
147 {
148   bool sort_depsgraph = false;
149   bool ok;
150 
151   ok = object_shaderfx_remove(bmain, ob, fx, &sort_depsgraph);
152 
153   if (!ok) {
154     BKE_reportf(reports, RPT_ERROR, "Effect '%s' not in object '%s'", fx->name, ob->id.name);
155     return 0;
156   }
157 
158   DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
159   DEG_relations_tag_update(bmain);
160 
161   return 1;
162 }
163 
ED_object_shaderfx_clear(Main * bmain,Object * ob)164 void ED_object_shaderfx_clear(Main *bmain, Object *ob)
165 {
166   ShaderFxData *fx = ob->shader_fx.first;
167   bool sort_depsgraph = false;
168 
169   if (!fx) {
170     return;
171   }
172 
173   while (fx) {
174     ShaderFxData *next_fx;
175 
176     next_fx = fx->next;
177 
178     object_shaderfx_remove(bmain, ob, fx, &sort_depsgraph);
179 
180     fx = next_fx;
181   }
182 
183   DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
184   DEG_relations_tag_update(bmain);
185 }
186 
ED_object_shaderfx_move_up(ReportList * UNUSED (reports),Object * ob,ShaderFxData * fx)187 int ED_object_shaderfx_move_up(ReportList *UNUSED(reports), Object *ob, ShaderFxData *fx)
188 {
189   if (fx->prev) {
190     BLI_remlink(&ob->shader_fx, fx);
191     BLI_insertlinkbefore(&ob->shader_fx, fx->prev, fx);
192   }
193 
194   return 1;
195 }
196 
ED_object_shaderfx_move_down(ReportList * UNUSED (reports),Object * ob,ShaderFxData * fx)197 int ED_object_shaderfx_move_down(ReportList *UNUSED(reports), Object *ob, ShaderFxData *fx)
198 {
199   if (fx->next) {
200     BLI_remlink(&ob->shader_fx, fx);
201     BLI_insertlinkafter(&ob->shader_fx, fx->next, fx);
202   }
203 
204   return 1;
205 }
206 
ED_object_shaderfx_move_to_index(ReportList * reports,Object * ob,ShaderFxData * fx,const int index)207 bool ED_object_shaderfx_move_to_index(ReportList *reports,
208                                       Object *ob,
209                                       ShaderFxData *fx,
210                                       const int index)
211 {
212   BLI_assert(fx != NULL);
213   BLI_assert(index >= 0);
214   if (index >= BLI_listbase_count(&ob->shader_fx)) {
215     BKE_report(reports, RPT_WARNING, "Cannot move effect beyond the end of the stack");
216     return false;
217   }
218 
219   int fx_index = BLI_findindex(&ob->shader_fx, fx);
220   BLI_assert(fx_index != -1);
221   if (fx_index < index) {
222     /* Move shaderfx down in list. */
223     for (; fx_index < index; fx_index++) {
224       if (!ED_object_shaderfx_move_down(reports, ob, fx)) {
225         break;
226       }
227     }
228   }
229   else {
230     /* Move shaderfx up in list. */
231     for (; fx_index > index; fx_index--) {
232       if (!ED_object_shaderfx_move_up(reports, ob, fx)) {
233         break;
234       }
235     }
236   }
237 
238   DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
239   WM_main_add_notifier(NC_OBJECT | ND_SHADERFX, ob);
240 
241   return true;
242 }
243 
ED_object_shaderfx_link(Object * dst,Object * src)244 void ED_object_shaderfx_link(Object *dst, Object *src)
245 {
246   BLI_freelistN(&dst->shader_fx);
247   BKE_shaderfx_copy(&dst->shader_fx, &src->shader_fx);
248 
249   DEG_id_tag_update(&dst->id, ID_RECALC_GEOMETRY);
250   WM_main_add_notifier(NC_OBJECT | ND_SHADERFX, dst);
251 }
252 
ED_object_shaderfx_copy(Object * dst,ShaderFxData * fx)253 void ED_object_shaderfx_copy(Object *dst, ShaderFxData *fx)
254 {
255   ShaderFxData *nfx = BKE_shaderfx_new(fx->type);
256   BLI_strncpy(nfx->name, fx->name, sizeof(nfx->name));
257   BKE_shaderfx_copydata(fx, nfx);
258   BLI_addtail(&dst->shader_fx, nfx);
259 
260   DEG_id_tag_update(&dst->id, ID_RECALC_GEOMETRY);
261   WM_main_add_notifier(NC_OBJECT | ND_SHADERFX, dst);
262 }
263 
264 /************************ add effect operator *********************/
265 
shaderfx_add_exec(bContext * C,wmOperator * op)266 static int shaderfx_add_exec(bContext *C, wmOperator *op)
267 {
268   Main *bmain = CTX_data_main(C);
269   Scene *scene = CTX_data_scene(C);
270   Object *ob = ED_object_active_context(C);
271   int type = RNA_enum_get(op->ptr, "type");
272 
273   if (!ED_object_shaderfx_add(op->reports, bmain, scene, ob, NULL, type)) {
274     return OPERATOR_CANCELLED;
275   }
276 
277   WM_event_add_notifier(C, NC_OBJECT | ND_SHADERFX, ob);
278 
279   return OPERATOR_FINISHED;
280 }
281 
shaderfx_add_itemf(bContext * C,PointerRNA * UNUSED (ptr),PropertyRNA * UNUSED (prop),bool * r_free)282 static const EnumPropertyItem *shaderfx_add_itemf(bContext *C,
283                                                   PointerRNA *UNUSED(ptr),
284                                                   PropertyRNA *UNUSED(prop),
285                                                   bool *r_free)
286 {
287   Object *ob = ED_object_active_context(C);
288   EnumPropertyItem *item = NULL;
289   const EnumPropertyItem *fx_item, *group_item = NULL;
290   const ShaderFxTypeInfo *mti;
291   int totitem = 0, a;
292 
293   if (!ob) {
294     return rna_enum_object_shaderfx_type_items;
295   }
296 
297   for (a = 0; rna_enum_object_shaderfx_type_items[a].identifier; a++) {
298     fx_item = &rna_enum_object_shaderfx_type_items[a];
299     if (fx_item->identifier[0]) {
300       mti = BKE_shaderfx_get_info(fx_item->value);
301 
302       if (mti->flags & eShaderFxTypeFlag_NoUserAdd) {
303         continue;
304       }
305     }
306     else {
307       group_item = fx_item;
308       fx_item = NULL;
309 
310       continue;
311     }
312 
313     if (group_item) {
314       RNA_enum_item_add(&item, &totitem, group_item);
315       group_item = NULL;
316     }
317 
318     RNA_enum_item_add(&item, &totitem, fx_item);
319   }
320 
321   RNA_enum_item_end(&item, &totitem);
322   *r_free = true;
323 
324   return item;
325 }
326 
OBJECT_OT_shaderfx_add(wmOperatorType * ot)327 void OBJECT_OT_shaderfx_add(wmOperatorType *ot)
328 {
329   /* identifiers */
330   ot->name = "Add Effect";
331   ot->description = "Add a visual effect to the active object";
332   ot->idname = "OBJECT_OT_shaderfx_add";
333 
334   /* api callbacks */
335   ot->invoke = WM_menu_invoke;
336   ot->exec = shaderfx_add_exec;
337   ot->poll = ED_operator_object_active_editable;
338 
339   /* flags */
340   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
341 
342   /* properties */
343   ot->prop = RNA_def_enum(
344       ot->srna, "type", rna_enum_object_shaderfx_type_items, eShaderFxType_Blur, "Type", "");
345   RNA_def_enum_funcs(ot->prop, shaderfx_add_itemf);
346 
347   /* Abused, for "Light"... */
348   RNA_def_property_translation_context(ot->prop, BLT_I18NCONTEXT_ID_ID);
349 }
350 
351 /* -------------------------------------------------------------------- */
352 /** \name Generic Functions for Operators Using Names and Data Context
353  * \{ */
354 
edit_shaderfx_poll_generic(bContext * C,StructRNA * rna_type,int obtype_flag)355 static bool edit_shaderfx_poll_generic(bContext *C, StructRNA *rna_type, int obtype_flag)
356 {
357   PointerRNA ptr = CTX_data_pointer_get_type(C, "shaderfx", rna_type);
358   Object *ob = (ptr.owner_id) ? (Object *)ptr.owner_id : ED_object_active_context(C);
359   ShaderFxData *fx = ptr.data; /* May be NULL. */
360 
361   if (!ob || ID_IS_LINKED(ob)) {
362     return false;
363   }
364   if (obtype_flag && ((1 << ob->type) & obtype_flag) == 0) {
365     return false;
366   }
367   if (ptr.owner_id && ID_IS_LINKED(ptr.owner_id)) {
368     return false;
369   }
370 
371   if (ID_IS_OVERRIDE_LIBRARY(ob)) {
372     if ((fx == NULL) || (fx->flag & eShaderFxFlag_OverrideLibrary_Local) == 0) {
373       CTX_wm_operator_poll_msg_set(C, "Cannot edit shaderfxs coming from library override");
374       return false;
375     }
376   }
377 
378   return true;
379 }
380 
edit_shaderfx_poll(bContext * C)381 static bool edit_shaderfx_poll(bContext *C)
382 {
383   return edit_shaderfx_poll_generic(C, &RNA_ShaderFx, 0);
384 }
385 
edit_shaderfx_properties(wmOperatorType * ot)386 static void edit_shaderfx_properties(wmOperatorType *ot)
387 {
388   PropertyRNA *prop = RNA_def_string(
389       ot->srna, "shaderfx", NULL, MAX_NAME, "Shader", "Name of the shaderfx to edit");
390   RNA_def_property_flag(prop, PROP_HIDDEN);
391 }
392 
edit_shaderfx_report_property(wmOperatorType * ot)393 static void edit_shaderfx_report_property(wmOperatorType *ot)
394 {
395   PropertyRNA *prop = RNA_def_boolean(
396       ot->srna, "report", false, "Report", "Create a notification after the operation");
397   RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
398 }
399 
400 /**
401  * \param event: If this isn't NULL, the operator will also look for panels underneath
402  * the cursor with customdata set to a modifier.
403  * \param r_retval: This should be used if #event is used in order to to return
404  * #OPERATOR_PASS_THROUGH to check other operators with the same key set.
405  */
edit_shaderfx_invoke_properties(bContext * C,wmOperator * op,const wmEvent * event,int * r_retval)406 static bool edit_shaderfx_invoke_properties(bContext *C,
407                                             wmOperator *op,
408                                             const wmEvent *event,
409                                             int *r_retval)
410 {
411   if (RNA_struct_property_is_set(op->ptr, "shaderfx")) {
412     return true;
413   }
414 
415   PointerRNA ctx_ptr = CTX_data_pointer_get_type(C, "shaderfx", &RNA_ShaderFx);
416   if (ctx_ptr.data != NULL) {
417     ShaderFxData *fx = ctx_ptr.data;
418     RNA_string_set(op->ptr, "shaderfx", fx->name);
419     return true;
420   }
421 
422   /* Check the custom data of panels under the mouse for an effect. */
423   if (event != NULL) {
424     PointerRNA *panel_ptr = UI_region_panel_custom_data_under_cursor(C, event);
425 
426     if (!(panel_ptr == NULL || RNA_pointer_is_null(panel_ptr))) {
427       if (RNA_struct_is_a(panel_ptr->type, &RNA_ShaderFx)) {
428         ShaderFxData *fx = panel_ptr->data;
429         RNA_string_set(op->ptr, "shaderfx", fx->name);
430         return true;
431       }
432 
433       BLI_assert(r_retval != NULL); /* We need the return value in this case. */
434       if (r_retval != NULL) {
435         *r_retval = (OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED);
436       }
437       return false;
438     }
439   }
440 
441   if (r_retval != NULL) {
442     *r_retval = OPERATOR_CANCELLED;
443   }
444   return false;
445 }
446 
edit_shaderfx_property_get(wmOperator * op,Object * ob,int type)447 static ShaderFxData *edit_shaderfx_property_get(wmOperator *op, Object *ob, int type)
448 {
449   char shaderfx_name[MAX_NAME];
450   ShaderFxData *fx;
451   RNA_string_get(op->ptr, "shaderfx", shaderfx_name);
452 
453   fx = BKE_shaderfx_findby_name(ob, shaderfx_name);
454 
455   if (fx && type != 0 && fx->type != type) {
456     fx = NULL;
457   }
458 
459   return fx;
460 }
461 
462 /** \} */
463 
464 /************************ remove shaderfx operator *********************/
465 
shaderfx_remove_exec(bContext * C,wmOperator * op)466 static int shaderfx_remove_exec(bContext *C, wmOperator *op)
467 {
468   Main *bmain = CTX_data_main(C);
469   Object *ob = ED_object_active_context(C);
470   ShaderFxData *fx = edit_shaderfx_property_get(op, ob, 0);
471 
472   /* Store name temporarily for report. */
473   char name[MAX_NAME];
474   strcpy(name, fx->name);
475 
476   if (!fx || !ED_object_shaderfx_remove(op->reports, bmain, ob, fx)) {
477     return OPERATOR_CANCELLED;
478   }
479 
480   if (RNA_boolean_get(op->ptr, "report")) {
481     BKE_reportf(op->reports, RPT_INFO, "Removed effect: %s", name);
482   }
483 
484   WM_event_add_notifier(C, NC_OBJECT | ND_SHADERFX, ob);
485 
486   return OPERATOR_FINISHED;
487 }
488 
shaderfx_remove_invoke(bContext * C,wmOperator * op,const wmEvent * event)489 static int shaderfx_remove_invoke(bContext *C, wmOperator *op, const wmEvent *event)
490 {
491   int retval;
492   if (edit_shaderfx_invoke_properties(C, op, event, &retval)) {
493     return shaderfx_remove_exec(C, op);
494   }
495   return retval;
496 }
497 
OBJECT_OT_shaderfx_remove(wmOperatorType * ot)498 void OBJECT_OT_shaderfx_remove(wmOperatorType *ot)
499 {
500   ot->name = "Remove Grease Pencil Effect";
501   ot->description = "Remove a effect from the active grease pencil object";
502   ot->idname = "OBJECT_OT_shaderfx_remove";
503 
504   ot->invoke = shaderfx_remove_invoke;
505   ot->exec = shaderfx_remove_exec;
506   ot->poll = edit_shaderfx_poll;
507 
508   /* flags */
509   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
510   edit_shaderfx_properties(ot);
511   edit_shaderfx_report_property(ot);
512 }
513 
514 /************************ move up shaderfx operator *********************/
515 
shaderfx_move_up_exec(bContext * C,wmOperator * op)516 static int shaderfx_move_up_exec(bContext *C, wmOperator *op)
517 {
518   Object *ob = ED_object_active_context(C);
519   ShaderFxData *fx = edit_shaderfx_property_get(op, ob, 0);
520 
521   if (!fx || !ED_object_shaderfx_move_up(op->reports, ob, fx)) {
522     return OPERATOR_CANCELLED;
523   }
524 
525   DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
526   WM_event_add_notifier(C, NC_OBJECT | ND_SHADERFX, ob);
527 
528   return OPERATOR_FINISHED;
529 }
530 
shaderfx_move_up_invoke(bContext * C,wmOperator * op,const wmEvent * event)531 static int shaderfx_move_up_invoke(bContext *C, wmOperator *op, const wmEvent *event)
532 {
533   int retval;
534   if (edit_shaderfx_invoke_properties(C, op, event, &retval)) {
535     return shaderfx_move_up_exec(C, op);
536   }
537   return retval;
538 }
539 
OBJECT_OT_shaderfx_move_up(wmOperatorType * ot)540 void OBJECT_OT_shaderfx_move_up(wmOperatorType *ot)
541 {
542   ot->name = "Move Up Effect";
543   ot->description = "Move effect up in the stack";
544   ot->idname = "OBJECT_OT_shaderfx_move_up";
545 
546   ot->invoke = shaderfx_move_up_invoke;
547   ot->exec = shaderfx_move_up_exec;
548   ot->poll = edit_shaderfx_poll;
549 
550   /* flags */
551   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
552   edit_shaderfx_properties(ot);
553 }
554 
555 /************************ move down shaderfx operator *********************/
556 
shaderfx_move_down_exec(bContext * C,wmOperator * op)557 static int shaderfx_move_down_exec(bContext *C, wmOperator *op)
558 {
559   Object *ob = ED_object_active_context(C);
560   ShaderFxData *fx = edit_shaderfx_property_get(op, ob, 0);
561 
562   if (!fx || !ED_object_shaderfx_move_down(op->reports, ob, fx)) {
563     return OPERATOR_CANCELLED;
564   }
565 
566   DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
567   WM_event_add_notifier(C, NC_OBJECT | ND_SHADERFX, ob);
568 
569   return OPERATOR_FINISHED;
570 }
571 
shaderfx_move_down_invoke(bContext * C,wmOperator * op,const wmEvent * event)572 static int shaderfx_move_down_invoke(bContext *C, wmOperator *op, const wmEvent *event)
573 {
574   int retval;
575   if (edit_shaderfx_invoke_properties(C, op, event, &retval)) {
576     return shaderfx_move_down_exec(C, op);
577   }
578   return retval;
579 }
580 
OBJECT_OT_shaderfx_move_down(wmOperatorType * ot)581 void OBJECT_OT_shaderfx_move_down(wmOperatorType *ot)
582 {
583   ot->name = "Move Down Effect";
584   ot->description = "Move effect down in the stack";
585   ot->idname = "OBJECT_OT_shaderfx_move_down";
586 
587   ot->invoke = shaderfx_move_down_invoke;
588   ot->exec = shaderfx_move_down_exec;
589   ot->poll = edit_shaderfx_poll;
590 
591   /* flags */
592   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
593   edit_shaderfx_properties(ot);
594 }
595 
596 /************************ move shaderfx to index operator *********************/
597 
shaderfx_move_to_index_poll(bContext * C)598 static bool shaderfx_move_to_index_poll(bContext *C)
599 {
600   return edit_shaderfx_poll_generic(C, &RNA_ShaderFx, 0);
601 }
602 
shaderfx_move_to_index_exec(bContext * C,wmOperator * op)603 static int shaderfx_move_to_index_exec(bContext *C, wmOperator *op)
604 {
605   Object *ob = ED_object_active_context(C);
606   ShaderFxData *fx = edit_shaderfx_property_get(op, ob, 0);
607   int index = RNA_int_get(op->ptr, "index");
608 
609   if (!fx || !ED_object_shaderfx_move_to_index(op->reports, ob, fx, index)) {
610     return OPERATOR_CANCELLED;
611   }
612 
613   return OPERATOR_FINISHED;
614 }
615 
shaderfx_move_to_index_invoke(bContext * C,wmOperator * op,const wmEvent * event)616 static int shaderfx_move_to_index_invoke(bContext *C, wmOperator *op, const wmEvent *event)
617 {
618   int retval;
619   if (edit_shaderfx_invoke_properties(C, op, event, &retval)) {
620     return shaderfx_move_to_index_exec(C, op);
621   }
622   return retval;
623 }
624 
OBJECT_OT_shaderfx_move_to_index(wmOperatorType * ot)625 void OBJECT_OT_shaderfx_move_to_index(wmOperatorType *ot)
626 {
627   ot->name = "Move Effect to Index";
628   ot->idname = "OBJECT_OT_shaderfx_move_to_index";
629   ot->description =
630       "Change the effect's position in the list so it evaluates after the set number of "
631       "others";
632 
633   ot->invoke = shaderfx_move_to_index_invoke;
634   ot->exec = shaderfx_move_to_index_exec;
635   ot->poll = shaderfx_move_to_index_poll;
636 
637   /* flags */
638   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
639   edit_shaderfx_properties(ot);
640   RNA_def_int(
641       ot->srna, "index", 0, 0, INT_MAX, "Index", "The index to move the effect to", 0, INT_MAX);
642 }
643