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