Cleanup: style, use braces for editors
[blender.git] / source / blender / editors / object / object_gpencil_modifier.c
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
28 #include "MEM_guardedalloc.h"
29
30 #include "DNA_gpencil_types.h"
31 #include "DNA_gpencil_modifier_types.h"
32 #include "DNA_object_types.h"
33 #include "DNA_scene_types.h"
34
35 #include "BLI_listbase.h"
36 #include "BLI_string_utf8.h"
37 #include "BLI_utildefines.h"
38
39 #include "BKE_context.h"
40 #include "BKE_main.h"
41 #include "BKE_gpencil_modifier.h"
42 #include "BKE_report.h"
43 #include "BKE_object.h"
44 #include "BKE_gpencil.h"
45
46 #include "DEG_depsgraph.h"
47 #include "DEG_depsgraph_build.h"
48 #include "DEG_depsgraph_query.h"
49
50 #include "RNA_access.h"
51 #include "RNA_define.h"
52 #include "RNA_enum_types.h"
53
54 #include "ED_object.h"
55 #include "ED_screen.h"
56
57 #include "WM_api.h"
58 #include "WM_types.h"
59
60 #include "object_intern.h"
61
62 /******************************** API ****************************/
63
64 GpencilModifierData *ED_object_gpencil_modifier_add(
65     ReportList *reports, Main *bmain, Scene *UNUSED(scene), Object *ob, const char *name, int type)
66 {
67   GpencilModifierData *new_md = NULL;
68   const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(type);
69
70   if (ob->type != OB_GPENCIL) {
71     BKE_reportf(reports, RPT_WARNING, "Modifiers cannot be added to object '%s'", ob->id.name + 2);
72     return NULL;
73   }
74
75   if (mti->flags & eGpencilModifierTypeFlag_Single) {
76     if (BKE_gpencil_modifiers_findByType(ob, type)) {
77       BKE_report(reports, RPT_WARNING, "Only one modifier of this type is allowed");
78       return NULL;
79     }
80   }
81
82   /* get new modifier data to add */
83   new_md = BKE_gpencil_modifier_new(type);
84
85   BLI_addtail(&ob->greasepencil_modifiers, new_md);
86
87   if (name) {
88     BLI_strncpy_utf8(new_md->name, name, sizeof(new_md->name));
89   }
90
91   /* make sure modifier data has unique name */
92   BKE_gpencil_modifier_unique_name(&ob->greasepencil_modifiers, new_md);
93
94   bGPdata *gpd = ob->data;
95   DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
96
97   DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
98   DEG_relations_tag_update(bmain);
99
100   return new_md;
101 }
102
103 /* Return true if the object has a modifier of type 'type' other than
104  * the modifier pointed to be 'exclude', otherwise returns false. */
105 static bool UNUSED_FUNCTION(gpencil_object_has_modifier)(const Object *ob,
106                                                          const GpencilModifierData *exclude,
107                                                          GpencilModifierType type)
108 {
109   GpencilModifierData *md;
110
111   for (md = ob->greasepencil_modifiers.first; md; md = md->next) {
112     if ((md != exclude) && (md->type == type)) {
113       return true;
114     }
115   }
116
117   return false;
118 }
119
120 static bool gpencil_object_modifier_remove(Main *bmain,
121                                            Object *ob,
122                                            GpencilModifierData *md,
123                                            bool *UNUSED(r_sort_depsgraph))
124 {
125   /* It seems on rapid delete it is possible to
126    * get called twice on same modifier, so make
127    * sure it is in list. */
128   if (BLI_findindex(&ob->greasepencil_modifiers, md) == -1) {
129     return 0;
130   }
131
132   DEG_relations_tag_update(bmain);
133
134   BLI_remlink(&ob->greasepencil_modifiers, md);
135   BKE_gpencil_modifier_free(md);
136   BKE_object_free_derived_caches(ob);
137
138   return 1;
139 }
140
141 bool ED_object_gpencil_modifier_remove(ReportList *reports,
142                                        Main *bmain,
143                                        Object *ob,
144                                        GpencilModifierData *md)
145 {
146   bool sort_depsgraph = false;
147   bool ok;
148
149   ok = gpencil_object_modifier_remove(bmain, ob, md, &sort_depsgraph);
150
151   if (!ok) {
152     BKE_reportf(reports, RPT_ERROR, "Modifier '%s' not in object '%s'", md->name, ob->id.name);
153     return 0;
154   }
155
156   DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
157   DEG_relations_tag_update(bmain);
158
159   return 1;
160 }
161
162 void ED_object_gpencil_modifier_clear(Main *bmain, Object *ob)
163 {
164   GpencilModifierData *md = ob->greasepencil_modifiers.first;
165   bool sort_depsgraph = false;
166
167   if (!md) {
168     return;
169   }
170
171   while (md) {
172     GpencilModifierData *next_md;
173
174     next_md = md->next;
175
176     gpencil_object_modifier_remove(bmain, ob, md, &sort_depsgraph);
177
178     md = next_md;
179   }
180
181   DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
182   DEG_relations_tag_update(bmain);
183 }
184
185 int ED_object_gpencil_modifier_move_up(ReportList *UNUSED(reports),
186                                        Object *ob,
187                                        GpencilModifierData *md)
188 {
189   if (md->prev) {
190     BLI_remlink(&ob->greasepencil_modifiers, md);
191     BLI_insertlinkbefore(&ob->greasepencil_modifiers, md->prev, md);
192   }
193
194   return 1;
195 }
196
197 int ED_object_gpencil_modifier_move_down(ReportList *UNUSED(reports),
198                                          Object *ob,
199                                          GpencilModifierData *md)
200 {
201   if (md->next) {
202     BLI_remlink(&ob->greasepencil_modifiers, md);
203     BLI_insertlinkafter(&ob->greasepencil_modifiers, md->next, md);
204   }
205
206   return 1;
207 }
208
209 static int gpencil_modifier_apply_obdata(
210     ReportList *reports, Main *bmain, Depsgraph *depsgraph, Object *ob, GpencilModifierData *md)
211 {
212   const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type);
213
214   if (mti->isDisabled && mti->isDisabled(md, 0)) {
215     BKE_report(reports, RPT_ERROR, "Modifier is disabled, skipping apply");
216     return 0;
217   }
218
219   if (ob->type == OB_GPENCIL) {
220     if (ELEM(NULL, ob, ob->data)) {
221       return 0;
222     }
223     else if (mti->bakeModifier == NULL) {
224       BKE_report(reports, RPT_ERROR, "Not implemented");
225       return 0;
226     }
227     mti->bakeModifier(bmain, depsgraph, md, ob);
228     DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
229   }
230   else {
231     BKE_report(reports, RPT_ERROR, "Cannot apply modifier for this object type");
232     return 0;
233   }
234
235   return 1;
236 }
237
238 int ED_object_gpencil_modifier_apply(Main *bmain,
239                                      ReportList *reports,
240                                      Depsgraph *depsgraph,
241                                      Object *ob,
242                                      GpencilModifierData *md,
243                                      int UNUSED(mode))
244 {
245
246   if (ob->type == OB_GPENCIL) {
247     if (ob->mode != OB_MODE_OBJECT) {
248       BKE_report(reports, RPT_ERROR, "Modifiers cannot be applied in paint, sculpt or edit mode");
249       return 0;
250     }
251
252     if (((ID *)ob->data)->us > 1) {
253       BKE_report(reports, RPT_ERROR, "Modifiers cannot be applied to multi-user data");
254       return 0;
255     }
256   }
257   else if (((ID *)ob->data)->us > 1) {
258     BKE_report(reports, RPT_ERROR, "Modifiers cannot be applied to multi-user data");
259     return 0;
260   }
261
262   if (md != ob->greasepencil_modifiers.first) {
263     BKE_report(reports, RPT_INFO, "Applied modifier was not first, result may not be as expected");
264   }
265
266   if (!gpencil_modifier_apply_obdata(reports, bmain, depsgraph, ob, md)) {
267     return 0;
268   }
269
270   BLI_remlink(&ob->greasepencil_modifiers, md);
271   BKE_gpencil_modifier_free(md);
272
273   return 1;
274 }
275
276 int ED_object_gpencil_modifier_copy(ReportList *reports, Object *ob, GpencilModifierData *md)
277 {
278   GpencilModifierData *nmd;
279   const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type);
280   GpencilModifierType type = md->type;
281
282   if (mti->flags & eGpencilModifierTypeFlag_Single) {
283     if (BKE_gpencil_modifiers_findByType(ob, type)) {
284       BKE_report(reports, RPT_WARNING, "Only one modifier of this type is allowed");
285       return 0;
286     }
287   }
288
289   nmd = BKE_gpencil_modifier_new(md->type);
290   BKE_gpencil_modifier_copyData(md, nmd);
291   BLI_insertlinkafter(&ob->greasepencil_modifiers, md, nmd);
292   BKE_gpencil_modifier_unique_name(&ob->greasepencil_modifiers, nmd);
293
294   return 1;
295 }
296
297 /************************ add modifier operator *********************/
298
299 static int gpencil_modifier_add_exec(bContext *C, wmOperator *op)
300 {
301   Main *bmain = CTX_data_main(C);
302   Scene *scene = CTX_data_scene(C);
303   Object *ob = ED_object_active_context(C);
304   int type = RNA_enum_get(op->ptr, "type");
305
306   if (!ED_object_gpencil_modifier_add(op->reports, bmain, scene, ob, NULL, type)) {
307     return OPERATOR_CANCELLED;
308   }
309
310   WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
311
312   return OPERATOR_FINISHED;
313 }
314
315 static const EnumPropertyItem *gpencil_modifier_add_itemf(bContext *C,
316                                                           PointerRNA *UNUSED(ptr),
317                                                           PropertyRNA *UNUSED(prop),
318                                                           bool *r_free)
319 {
320   Object *ob = ED_object_active_context(C);
321   EnumPropertyItem *item = NULL;
322   const EnumPropertyItem *md_item, *group_item = NULL;
323   const GpencilModifierTypeInfo *mti;
324   int totitem = 0, a;
325
326   if (!ob) {
327     return rna_enum_object_greasepencil_modifier_type_items;
328   }
329
330   for (a = 0; rna_enum_object_greasepencil_modifier_type_items[a].identifier; a++) {
331     md_item = &rna_enum_object_greasepencil_modifier_type_items[a];
332     if (md_item->identifier[0]) {
333       mti = BKE_gpencil_modifierType_getInfo(md_item->value);
334
335       if (mti->flags & eGpencilModifierTypeFlag_NoUserAdd) {
336         continue;
337       }
338     }
339     else {
340       group_item = md_item;
341       md_item = NULL;
342
343       continue;
344     }
345
346     if (group_item) {
347       RNA_enum_item_add(&item, &totitem, group_item);
348       group_item = NULL;
349     }
350
351     RNA_enum_item_add(&item, &totitem, md_item);
352   }
353
354   RNA_enum_item_end(&item, &totitem);
355   *r_free = true;
356
357   return item;
358 }
359
360 void OBJECT_OT_gpencil_modifier_add(wmOperatorType *ot)
361 {
362   PropertyRNA *prop;
363
364   /* identifiers */
365   ot->name = "Add Grease Pencil Modifier";
366   ot->description = "Add a procedural operation/effect to the active grease pencil object";
367   ot->idname = "OBJECT_OT_gpencil_modifier_add";
368
369   /* api callbacks */
370   ot->invoke = WM_menu_invoke;
371   ot->exec = gpencil_modifier_add_exec;
372   ot->poll = ED_operator_object_active_editable;
373
374   /* flags */
375   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
376
377   /* properties */
378   prop = RNA_def_enum(ot->srna,
379                       "type",
380                       rna_enum_object_modifier_type_items,
381                       eGpencilModifierType_Thick,
382                       "Type",
383                       "");
384   RNA_def_enum_funcs(prop, gpencil_modifier_add_itemf);
385   ot->prop = prop;
386 }
387
388 /********** generic functions for operators using mod names and data context *********************/
389
390 static int gpencil_edit_modifier_poll_generic(bContext *C, StructRNA *rna_type, int obtype_flag)
391 {
392   PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", rna_type);
393   Object *ob = (ptr.id.data) ? ptr.id.data : ED_object_active_context(C);
394
395   if (!ob || ID_IS_LINKED(ob)) {
396     return 0;
397   }
398   if (obtype_flag && ((1 << ob->type) & obtype_flag) == 0) {
399     return 0;
400   }
401   if (ptr.id.data && ID_IS_LINKED(ptr.id.data)) {
402     return 0;
403   }
404
405   if (ID_IS_STATIC_OVERRIDE(ob)) {
406     CTX_wm_operator_poll_msg_set(C, "Cannot edit modifiers coming from static override");
407     return (((GpencilModifierData *)ptr.data)->flag & eGpencilModifierFlag_StaticOverride_Local) !=
408            0;
409   }
410
411   return 1;
412 }
413
414 static bool gpencil_edit_modifier_poll(bContext *C)
415 {
416   return gpencil_edit_modifier_poll_generic(C, &RNA_GpencilModifier, 0);
417 }
418
419 static void gpencil_edit_modifier_properties(wmOperatorType *ot)
420 {
421   PropertyRNA *prop = RNA_def_string(
422       ot->srna, "modifier", NULL, MAX_NAME, "Modifier", "Name of the modifier to edit");
423   RNA_def_property_flag(prop, PROP_HIDDEN);
424 }
425
426 static int gpencil_edit_modifier_invoke_properties(bContext *C, wmOperator *op)
427 {
428   GpencilModifierData *md;
429
430   if (RNA_struct_property_is_set(op->ptr, "modifier")) {
431     return true;
432   }
433   else {
434     PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", &RNA_GpencilModifier);
435     if (ptr.data) {
436       md = ptr.data;
437       RNA_string_set(op->ptr, "modifier", md->name);
438       return true;
439     }
440   }
441
442   return false;
443 }
444
445 static GpencilModifierData *gpencil_edit_modifier_property_get(wmOperator *op,
446                                                                Object *ob,
447                                                                int type)
448 {
449   char modifier_name[MAX_NAME];
450   GpencilModifierData *md;
451   RNA_string_get(op->ptr, "modifier", modifier_name);
452
453   md = BKE_gpencil_modifiers_findByName(ob, modifier_name);
454
455   if (md && type != 0 && md->type != type) {
456     md = NULL;
457   }
458
459   return md;
460 }
461
462 /************************ remove modifier operator *********************/
463
464 static int gpencil_modifier_remove_exec(bContext *C, wmOperator *op)
465 {
466   Main *bmain = CTX_data_main(C);
467   Object *ob = ED_object_active_context(C);
468   GpencilModifierData *md = gpencil_edit_modifier_property_get(op, ob, 0);
469
470   if (!md || !ED_object_gpencil_modifier_remove(op->reports, bmain, ob, md)) {
471     return OPERATOR_CANCELLED;
472   }
473
474   WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
475
476   return OPERATOR_FINISHED;
477 }
478
479 static int gpencil_modifier_remove_invoke(bContext *C,
480                                           wmOperator *op,
481                                           const wmEvent *UNUSED(event))
482 {
483   if (gpencil_edit_modifier_invoke_properties(C, op)) {
484     return gpencil_modifier_remove_exec(C, op);
485   }
486   else {
487     return OPERATOR_CANCELLED;
488   }
489 }
490
491 void OBJECT_OT_gpencil_modifier_remove(wmOperatorType *ot)
492 {
493   ot->name = "Remove Grease Pencil Modifier";
494   ot->description = "Remove a modifier from the active grease pencil object";
495   ot->idname = "OBJECT_OT_gpencil_modifier_remove";
496
497   ot->invoke = gpencil_modifier_remove_invoke;
498   ot->exec = gpencil_modifier_remove_exec;
499   ot->poll = gpencil_edit_modifier_poll;
500
501   /* flags */
502   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
503   gpencil_edit_modifier_properties(ot);
504 }
505
506 /************************ move up modifier operator *********************/
507
508 static int gpencil_modifier_move_up_exec(bContext *C, wmOperator *op)
509 {
510   Object *ob = ED_object_active_context(C);
511   GpencilModifierData *md = gpencil_edit_modifier_property_get(op, ob, 0);
512
513   if (!md || !ED_object_gpencil_modifier_move_up(op->reports, ob, md)) {
514     return OPERATOR_CANCELLED;
515   }
516
517   DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
518   WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
519
520   return OPERATOR_FINISHED;
521 }
522
523 static int gpencil_modifier_move_up_invoke(bContext *C,
524                                            wmOperator *op,
525                                            const wmEvent *UNUSED(event))
526 {
527   if (gpencil_edit_modifier_invoke_properties(C, op)) {
528     return gpencil_modifier_move_up_exec(C, op);
529   }
530   else {
531     return OPERATOR_CANCELLED;
532   }
533 }
534
535 void OBJECT_OT_gpencil_modifier_move_up(wmOperatorType *ot)
536 {
537   ot->name = "Move Up Modifier";
538   ot->description = "Move modifier up in the stack";
539   ot->idname = "OBJECT_OT_gpencil_modifier_move_up";
540
541   ot->invoke = gpencil_modifier_move_up_invoke;
542   ot->exec = gpencil_modifier_move_up_exec;
543   ot->poll = gpencil_edit_modifier_poll;
544
545   /* flags */
546   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
547   gpencil_edit_modifier_properties(ot);
548 }
549
550 /************************ move down modifier operator *********************/
551
552 static int gpencil_modifier_move_down_exec(bContext *C, wmOperator *op)
553 {
554   Object *ob = ED_object_active_context(C);
555   GpencilModifierData *md = gpencil_edit_modifier_property_get(op, ob, 0);
556
557   if (!md || !ED_object_gpencil_modifier_move_down(op->reports, ob, md)) {
558     return OPERATOR_CANCELLED;
559   }
560
561   DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
562   WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
563
564   return OPERATOR_FINISHED;
565 }
566
567 static int gpencil_modifier_move_down_invoke(bContext *C,
568                                              wmOperator *op,
569                                              const wmEvent *UNUSED(event))
570 {
571   if (gpencil_edit_modifier_invoke_properties(C, op)) {
572     return gpencil_modifier_move_down_exec(C, op);
573   }
574   else {
575     return OPERATOR_CANCELLED;
576   }
577 }
578
579 void OBJECT_OT_gpencil_modifier_move_down(wmOperatorType *ot)
580 {
581   ot->name = "Move Down Modifier";
582   ot->description = "Move modifier down in the stack";
583   ot->idname = "OBJECT_OT_gpencil_modifier_move_down";
584
585   ot->invoke = gpencil_modifier_move_down_invoke;
586   ot->exec = gpencil_modifier_move_down_exec;
587   ot->poll = gpencil_edit_modifier_poll;
588
589   /* flags */
590   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
591   gpencil_edit_modifier_properties(ot);
592 }
593
594 /************************ apply modifier operator *********************/
595
596 static int gpencil_modifier_apply_exec(bContext *C, wmOperator *op)
597 {
598   Main *bmain = CTX_data_main(C);
599   Depsgraph *depsgraph = CTX_data_depsgraph(C);
600   Object *ob = ED_object_active_context(C);
601   GpencilModifierData *md = gpencil_edit_modifier_property_get(op, ob, 0);
602   int apply_as = RNA_enum_get(op->ptr, "apply_as");
603
604   if (!md || !ED_object_gpencil_modifier_apply(bmain, op->reports, depsgraph, ob, md, apply_as)) {
605     return OPERATOR_CANCELLED;
606   }
607
608   DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
609   WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
610
611   return OPERATOR_FINISHED;
612 }
613
614 static int gpencil_modifier_apply_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
615 {
616   if (gpencil_edit_modifier_invoke_properties(C, op)) {
617     return gpencil_modifier_apply_exec(C, op);
618   }
619   else {
620     return OPERATOR_CANCELLED;
621   }
622 }
623
624 static const EnumPropertyItem gpencil_modifier_apply_as_items[] = {
625     {MODIFIER_APPLY_DATA, "DATA", 0, "Object Data", "Apply modifier to the object's data"},
626     {MODIFIER_APPLY_SHAPE,
627      "SHAPE",
628      0,
629      "New Shape",
630      "Apply deform-only modifier to a new shape on this object"},
631     {0, NULL, 0, NULL, NULL},
632 };
633
634 void OBJECT_OT_gpencil_modifier_apply(wmOperatorType *ot)
635 {
636   ot->name = "Apply Modifier";
637   ot->description = "Apply modifier and remove from the stack";
638   ot->idname = "OBJECT_OT_gpencil_modifier_apply";
639
640   ot->invoke = gpencil_modifier_apply_invoke;
641   ot->exec = gpencil_modifier_apply_exec;
642   ot->poll = gpencil_edit_modifier_poll;
643
644   /* flags */
645   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
646
647   RNA_def_enum(ot->srna,
648                "apply_as",
649                gpencil_modifier_apply_as_items,
650                MODIFIER_APPLY_DATA,
651                "Apply as",
652                "How to apply the modifier to the geometry");
653   gpencil_edit_modifier_properties(ot);
654 }
655
656 /************************ copy modifier operator *********************/
657
658 static int gpencil_modifier_copy_exec(bContext *C, wmOperator *op)
659 {
660   Object *ob = ED_object_active_context(C);
661   GpencilModifierData *md = gpencil_edit_modifier_property_get(op, ob, 0);
662
663   if (!md || !ED_object_gpencil_modifier_copy(op->reports, ob, md)) {
664     return OPERATOR_CANCELLED;
665   }
666
667   DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
668   WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
669
670   return OPERATOR_FINISHED;
671 }
672
673 static int gpencil_modifier_copy_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
674 {
675   if (gpencil_edit_modifier_invoke_properties(C, op)) {
676     return gpencil_modifier_copy_exec(C, op);
677   }
678   else {
679     return OPERATOR_CANCELLED;
680   }
681 }
682
683 void OBJECT_OT_gpencil_modifier_copy(wmOperatorType *ot)
684 {
685   ot->name = "Copy Modifier";
686   ot->description = "Duplicate modifier at the same position in the stack";
687   ot->idname = "OBJECT_OT_gpencil_modifier_copy";
688
689   ot->invoke = gpencil_modifier_copy_invoke;
690   ot->exec = gpencil_modifier_copy_exec;
691   ot->poll = gpencil_edit_modifier_poll;
692
693   /* flags */
694   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
695   gpencil_edit_modifier_properties(ot);
696 }