Cleanup/security fix: do not use strcpy (at least in new code).
[blender.git] / source / blender / editors / gpencil / gpencil_data.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) 2008, Blender Foundation, Joshua Leung
19  * This is a new part of Blender
20  *
21  * Contributor(s): Joshua Leung, Antonio Vazquez
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  *
25  * Operators for dealing with GP datablocks and layers
26  */
27
28 /** \file blender/editors/gpencil/gpencil_data.c
29  *  \ingroup edgpencil
30  */
31
32
33 #include <stdio.h>
34 #include <string.h>
35 #include <stdlib.h>
36 #include <stddef.h>
37 #include <math.h>
38
39 #include "MEM_guardedalloc.h"
40
41 #include "BLI_blenlib.h"
42 #include "BLI_utildefines.h"
43 #include "BLI_ghash.h"
44 #include "BLI_math.h"
45
46 #include "BLT_translation.h"
47
48 #include "DNA_scene_types.h"
49 #include "DNA_screen_types.h"
50 #include "DNA_space_types.h"
51 #include "DNA_view3d_types.h"
52 #include "DNA_gpencil_types.h"
53
54 #include "BKE_context.h"
55 #include "BKE_global.h"
56 #include "BKE_gpencil.h"
57 #include "BKE_library.h"
58 #include "BKE_object.h"
59 #include "BKE_report.h"
60 #include "BKE_scene.h"
61 #include "BKE_screen.h"
62 #include "BKE_colortools.h"
63
64 #include "UI_interface.h"
65 #include "UI_resources.h"
66
67 #include "WM_api.h"
68 #include "WM_types.h"
69
70 #include "RNA_access.h"
71 #include "RNA_define.h"
72 #include "RNA_enum_types.h"
73
74 #include "ED_gpencil.h"
75
76 #include "gpencil_intern.h"
77
78 /* maximum sizes of gp-session buffer */
79 #define GP_STROKE_BUFFER_MAX    5000
80
81 /* ************************************************ */
82 /* Datablock Operators */
83
84 /* ******************* Add New Data ************************ */
85
86 /* add new datablock - wrapper around API */
87 static int gp_data_add_exec(bContext *C, wmOperator *op)
88 {
89         bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
90         
91         if (gpd_ptr == NULL) {
92                 BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go");
93                 return OPERATOR_CANCELLED;
94         }
95         else {
96                 /* decrement user count and add new datablock */
97                 bGPdata *gpd = (*gpd_ptr);
98                 
99                 id_us_min(&gpd->id);
100                 *gpd_ptr = BKE_gpencil_data_addnew(DATA_("GPencil"));
101         }
102         
103         /* notifiers */
104         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
105         
106         return OPERATOR_FINISHED;
107 }
108
109 void GPENCIL_OT_data_add(wmOperatorType *ot)
110 {
111         /* identifiers */
112         ot->name = "Grease Pencil Add New";
113         ot->idname = "GPENCIL_OT_data_add";
114         ot->description = "Add new Grease Pencil datablock";
115         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
116         
117         /* callbacks */
118         ot->exec = gp_data_add_exec;
119         ot->poll = gp_add_poll;
120 }
121
122 /* ******************* Unlink Data ************************ */
123
124 /* poll callback for adding data/layers - special */
125 static int gp_data_unlink_poll(bContext *C)
126 {
127         bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
128         
129         /* if we have access to some active data, make sure there's a datablock before enabling this */
130         return (gpd_ptr && *gpd_ptr);
131 }
132
133
134 /* unlink datablock - wrapper around API */
135 static int gp_data_unlink_exec(bContext *C, wmOperator *op)
136 {
137         bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
138         
139         if (gpd_ptr == NULL) {
140                 BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go");
141                 return OPERATOR_CANCELLED;
142         }
143         else {
144                 /* just unlink datablock now, decreasing its user count */
145                 bGPdata *gpd = (*gpd_ptr);
146
147                 id_us_min(&gpd->id);
148                 *gpd_ptr = NULL;
149         }
150         
151         /* notifiers */
152         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
153         
154         return OPERATOR_FINISHED;
155 }
156
157 void GPENCIL_OT_data_unlink(wmOperatorType *ot)
158 {
159         /* identifiers */
160         ot->name = "Grease Pencil Unlink";
161         ot->idname = "GPENCIL_OT_data_unlink";
162         ot->description = "Unlink active Grease Pencil datablock";
163         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
164         
165         /* callbacks */
166         ot->exec = gp_data_unlink_exec;
167         ot->poll = gp_data_unlink_poll;
168 }
169
170
171 /* ************************************************ */
172 /* Layer Operators */
173
174 /* ******************* Add New Layer ************************ */
175
176 /* add new layer - wrapper around API */
177 static int gp_layer_add_exec(bContext *C, wmOperator *op)
178 {
179         bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
180         
181         /* if there's no existing Grease-Pencil data there, add some */
182         if (gpd_ptr == NULL) {
183                 BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go");
184                 return OPERATOR_CANCELLED;
185         }
186         if (*gpd_ptr == NULL)
187                 *gpd_ptr = BKE_gpencil_data_addnew(DATA_("GPencil"));
188         
189         /* add new layer now */
190         BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("GP_Layer"), true);
191         
192         /* notifiers */
193         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
194         
195         return OPERATOR_FINISHED;
196 }
197
198 void GPENCIL_OT_layer_add(wmOperatorType *ot)
199 {
200         /* identifiers */
201         ot->name = "Add New Layer";
202         ot->idname = "GPENCIL_OT_layer_add";
203         ot->description = "Add new Grease Pencil layer for the active Grease Pencil datablock";
204         
205         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
206         
207         /* callbacks */
208         ot->exec = gp_layer_add_exec;
209         ot->poll = gp_add_poll;
210 }
211
212 /* ******************* Remove Active Layer ************************* */
213
214 static int gp_layer_remove_exec(bContext *C, wmOperator *op)
215 {
216         bGPdata *gpd = ED_gpencil_data_get_active(C);
217         bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
218         
219         /* sanity checks */
220         if (ELEM(NULL, gpd, gpl))
221                 return OPERATOR_CANCELLED;
222         
223         if (gpl->flag & GP_LAYER_LOCKED) {
224                 BKE_report(op->reports, RPT_ERROR, "Cannot delete locked layers");
225                 return OPERATOR_CANCELLED;
226         }
227         
228         /* make the layer before this the new active layer
229          * - use the one after if this is the first
230          * - if this is the only layer, this naturally becomes NULL
231          */
232         if (gpl->prev)
233                 BKE_gpencil_layer_setactive(gpd, gpl->prev);
234         else
235                 BKE_gpencil_layer_setactive(gpd, gpl->next);
236         
237         /* delete the layer now... */
238         BKE_gpencil_layer_delete(gpd, gpl);
239         
240         /* notifiers */
241         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
242         
243         return OPERATOR_FINISHED;
244 }
245
246 void GPENCIL_OT_layer_remove(wmOperatorType *ot)
247 {
248         /* identifiers */
249         ot->name = "Remove Layer";
250         ot->idname = "GPENCIL_OT_layer_remove";
251         ot->description = "Remove active Grease Pencil layer";
252         
253         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
254         
255         /* callbacks */
256         ot->exec = gp_layer_remove_exec;
257         ot->poll = gp_active_layer_poll;
258 }
259
260 /* ******************* Move Layer Up/Down ************************** */
261
262 enum {
263         GP_LAYER_MOVE_UP   = -1,
264         GP_LAYER_MOVE_DOWN = 1
265 };
266
267 static int gp_layer_move_exec(bContext *C, wmOperator *op)
268 {
269         bGPdata *gpd = ED_gpencil_data_get_active(C);
270         bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
271         
272         int direction = RNA_enum_get(op->ptr, "type");
273         
274         /* sanity checks */
275         if (ELEM(NULL, gpd, gpl))
276                 return OPERATOR_CANCELLED;
277         
278         /* up or down? */
279         if (direction == GP_LAYER_MOVE_UP) {
280                 /* up */
281                 BLI_remlink(&gpd->layers, gpl);
282                 BLI_insertlinkbefore(&gpd->layers, gpl->prev, gpl);
283         }
284         else {
285                 /* down */
286                 BLI_remlink(&gpd->layers, gpl);
287                 BLI_insertlinkafter(&gpd->layers, gpl->next, gpl);
288         }
289         
290         /* notifiers */
291         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
292         
293         return OPERATOR_FINISHED;
294 }
295
296 void GPENCIL_OT_layer_move(wmOperatorType *ot)
297 {
298         static EnumPropertyItem slot_move[] = {
299                 {GP_LAYER_MOVE_UP, "UP", 0, "Up", ""},
300                 {GP_LAYER_MOVE_DOWN, "DOWN", 0, "Down", ""},
301                 {0, NULL, 0, NULL, NULL}
302         };
303         
304         /* identifiers */
305         ot->name = "Move Grease Pencil Layer";
306         ot->idname = "GPENCIL_OT_layer_move";
307         ot->description = "Move the active Grease Pencil layer up/down in the list";
308         
309         /* api callbacks */
310         ot->exec = gp_layer_move_exec;
311         ot->poll = gp_active_layer_poll;
312         
313         /* flags */
314         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
315         
316         ot->prop = RNA_def_enum(ot->srna, "type", slot_move, 0, "Type", "");
317 }
318
319 /* ********************* Duplicate Layer ************************** */
320
321 static int gp_layer_copy_exec(bContext *C, wmOperator *UNUSED(op))
322 {
323         bGPdata *gpd = ED_gpencil_data_get_active(C);
324         bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
325         bGPDlayer *new_layer;
326         
327         /* sanity checks */
328         if (ELEM(NULL, gpd, gpl))
329                 return OPERATOR_CANCELLED;
330         
331         /* make copy of layer, and add it immediately after the existing layer */
332         new_layer = BKE_gpencil_layer_duplicate(gpl);
333         BLI_insertlinkafter(&gpd->layers, gpl, new_layer);
334         
335         /* ensure new layer has a unique name, and is now the active layer */
336         BLI_uniquename(&gpd->layers, new_layer, DATA_("GP_Layer"), '.', offsetof(bGPDlayer, info), sizeof(new_layer->info));
337         BKE_gpencil_layer_setactive(gpd, new_layer);
338         
339         /* notifiers */
340         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
341         
342         return OPERATOR_FINISHED;
343 }
344
345 void GPENCIL_OT_layer_duplicate(wmOperatorType *ot)
346 {
347         /* identifiers */
348         ot->name = "Duplicate Layer";
349         ot->idname = "GPENCIL_OT_layer_duplicate";
350         ot->description = "Make a copy of the active Grease Pencil layer";
351         
352         /* callbacks */
353         ot->exec = gp_layer_copy_exec;
354         ot->poll = gp_active_layer_poll;
355         
356         /* flags */
357         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
358 }
359
360 /* *********************** Hide Layers ******************************** */
361
362 static int gp_hide_exec(bContext *C, wmOperator *op)
363 {
364         bGPdata *gpd = ED_gpencil_data_get_active(C);
365         bGPDlayer *layer = BKE_gpencil_layer_getactive(gpd);
366         bool unselected = RNA_boolean_get(op->ptr, "unselected");
367         
368         /* sanity checks */
369         if (ELEM(NULL, gpd, layer))
370                 return OPERATOR_CANCELLED;
371         
372         if (unselected) {
373                 bGPDlayer *gpl;
374                 
375                 /* hide unselected */
376                 for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
377                         if (gpl != layer) {
378                                 gpl->flag |= GP_LAYER_HIDE;
379                         }
380                 }
381         }
382         else {
383                 /* hide selected/active */
384                 layer->flag |= GP_LAYER_HIDE;
385         }
386         
387         /* notifiers */
388         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
389         
390         return OPERATOR_FINISHED;
391 }
392
393 void GPENCIL_OT_hide(wmOperatorType *ot)
394 {
395         /* identifiers */
396         ot->name = "Hide Layer(s)";
397         ot->idname = "GPENCIL_OT_hide";
398         ot->description = "Hide selected/unselected Grease Pencil layers";
399         
400         /* callbacks */
401         ot->exec = gp_hide_exec;
402         ot->poll = gp_active_layer_poll; /* NOTE: we need an active layer to play with */
403         
404         /* flags */
405         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
406         
407         /* props */
408         RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected layers");
409 }
410
411 /* ********************** Show All Layers ***************************** */
412
413 /* poll callback for showing layers */
414 static int gp_reveal_poll(bContext *C)
415 {
416         return ED_gpencil_data_get_active(C) != NULL;
417 }
418
419 static int gp_reveal_exec(bContext *C, wmOperator *UNUSED(op))
420 {
421         bGPdata *gpd = ED_gpencil_data_get_active(C);
422         bGPDlayer *gpl;
423         
424         /* sanity checks */
425         if (gpd == NULL)
426                 return OPERATOR_CANCELLED;
427         
428         /* make all layers visible */
429         for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
430                 gpl->flag &= ~GP_LAYER_HIDE;
431         }
432         
433         /* notifiers */
434         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
435         
436         return OPERATOR_FINISHED;
437 }
438
439 void GPENCIL_OT_reveal(wmOperatorType *ot)
440 {
441         /* identifiers */
442         ot->name = "Show All Layers";
443         ot->idname = "GPENCIL_OT_reveal";
444         ot->description = "Show all Grease Pencil layers";
445         
446         /* callbacks */
447         ot->exec = gp_reveal_exec;
448         ot->poll = gp_reveal_poll;
449         
450         /* flags */
451         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
452 }
453
454 /* ***************** Lock/Unlock All Layers ************************ */
455
456 static int gp_lock_all_exec(bContext *C, wmOperator *UNUSED(op))
457 {
458         bGPdata *gpd = ED_gpencil_data_get_active(C);
459         bGPDlayer *gpl;
460         
461         /* sanity checks */
462         if (gpd == NULL)
463                 return OPERATOR_CANCELLED;
464         
465         /* make all layers non-editable */
466         for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
467                 gpl->flag |= GP_LAYER_LOCKED;
468         }
469         
470         /* notifiers */
471         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
472         
473         return OPERATOR_FINISHED;
474 }
475
476 void GPENCIL_OT_lock_all(wmOperatorType *ot)
477 {
478         /* identifiers */
479         ot->name = "Lock All Layers";
480         ot->idname = "GPENCIL_OT_lock_all";
481         ot->description = "Lock all Grease Pencil layers to prevent them from being accidentally modified";
482         
483         /* callbacks */
484         ot->exec = gp_lock_all_exec;
485         ot->poll = gp_reveal_poll; /* XXX: could use dedicated poll later */
486         
487         /* flags */
488         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
489 }
490
491 /* -------------------------- */
492
493 static int gp_unlock_all_exec(bContext *C, wmOperator *UNUSED(op))
494 {
495         bGPdata *gpd = ED_gpencil_data_get_active(C);
496         bGPDlayer *gpl;
497         
498         /* sanity checks */
499         if (gpd == NULL)
500                 return OPERATOR_CANCELLED;
501         
502         /* make all layers editable again */
503         for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
504                 gpl->flag &= ~GP_LAYER_LOCKED;
505         }
506         
507         /* notifiers */
508         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
509         
510         return OPERATOR_FINISHED;
511 }
512
513 void GPENCIL_OT_unlock_all(wmOperatorType *ot)
514 {
515         /* identifiers */
516         ot->name = "Unlock All Layers";
517         ot->idname = "GPENCIL_OT_unlock_all";
518         ot->description = "Unlock all Grease Pencil layers so that they can be edited";
519         
520         /* callbacks */
521         ot->exec = gp_unlock_all_exec;
522         ot->poll = gp_reveal_poll; /* XXX: could use dedicated poll later */
523         
524         /* flags */
525         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
526 }
527
528 /* ********************** Isolate Layer **************************** */
529
530 static int gp_isolate_layer_exec(bContext *C, wmOperator *op)
531 {
532         bGPdata *gpd = ED_gpencil_data_get_active(C);
533         bGPDlayer *layer = BKE_gpencil_layer_getactive(gpd);
534         bGPDlayer *gpl;
535         int flags = GP_LAYER_LOCKED;
536         bool isolate = false;
537         
538         if (RNA_boolean_get(op->ptr, "affect_visibility"))
539                 flags |= GP_LAYER_HIDE;
540                 
541         if (ELEM(NULL, gpd, layer)) {
542                 BKE_report(op->reports, RPT_ERROR, "No active layer to isolate");
543                 return OPERATOR_CANCELLED;
544         }
545         
546         /* Test whether to isolate or clear all flags */
547         for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
548                 /* Skip if this is the active layer */
549                 if (gpl == layer)
550                         continue;
551                 
552                 /* If the flags aren't set, that means that the layer is
553                  * not alone, so we have some layers to isolate still
554                  */
555                 if ((gpl->flag & flags) == 0) {
556                         isolate = true;
557                         break;
558                 }
559         }
560         
561         /* Set/Clear flags as appropriate */
562         /* TODO: Include onionskinning on this list? */
563         if (isolate) {
564                 /* Set flags on all "other" layers */
565                 for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
566                         if (gpl == layer)
567                                 continue;
568                         else
569                                 gpl->flag |= flags;
570                 }
571         }
572         else {
573                 /* Clear flags - Restore everything else */
574                 for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
575                         gpl->flag &= ~flags;
576                 }
577         }
578         
579         /* notifiers */
580         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
581         
582         return OPERATOR_FINISHED;
583 }
584
585 void GPENCIL_OT_layer_isolate(wmOperatorType *ot)
586 {
587         /* identifiers */
588         ot->name = "Isolate Layer";
589         ot->idname = "GPENCIL_OT_layer_isolate";
590         ot->description = "Toggle whether the active layer is the only one that can be edited and/or visible";
591         
592         /* callbacks */
593         ot->exec = gp_isolate_layer_exec;
594         ot->poll = gp_active_layer_poll;
595         
596         /* flags */
597         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
598         
599         /* properties */
600         RNA_def_boolean(ot->srna, "affect_visibility", false, "Affect Visibility",
601                         "In addition to toggling the editability, also affect the visibility");
602 }
603
604 /* ********************** Merge Layer with the next layer **************************** */
605
606 static int gp_merge_layer_exec(bContext *C, wmOperator *op)
607 {
608         bGPdata *gpd = ED_gpencil_data_get_active(C);
609         bGPDlayer *gpl_current = BKE_gpencil_layer_getactive(gpd);
610         bGPDlayer *gpl_next = gpl_current->next;
611
612         if (ELEM(NULL, gpd, gpl_current, gpl_next)) {
613                 BKE_report(op->reports, RPT_ERROR, "No layers to merge");
614                 return OPERATOR_CANCELLED;
615         }
616
617         /* Collect frames of gpl_current in hash table to avoid O(n^2) lookups */
618         GHash *gh_frames_cur = BLI_ghash_int_new_ex(__func__, 64);
619         for (bGPDframe *gpf = gpl_current->frames.first; gpf; gpf = gpf->next) {
620                 BLI_ghash_insert(gh_frames_cur, SET_INT_IN_POINTER(gpf->framenum), gpf);
621         }
622
623         /* read all frames from next layer */
624         for (bGPDframe *gpf = gpl_next->frames.first; gpf; gpf = gpf->next) {
625                 /* try to find frame in active layer */
626                 bGPDframe *frame = BLI_ghash_lookup(gh_frames_cur, SET_INT_IN_POINTER(gpf->framenum));
627                 if (!frame) {
628                         /* nothing found, create new */
629                         frame = BKE_gpencil_frame_addnew(gpl_current, gpf->framenum);
630                 }
631                 /* add to tail all strokes */
632                 BLI_movelisttolist(&frame->strokes, &gpf->strokes);
633         }
634         /* Now delete next layer */
635         BKE_gpencil_layer_delete(gpd, gpl_next);
636         BLI_ghash_free(gh_frames_cur, NULL, NULL);
637
638         /* notifiers */
639         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
640
641         return OPERATOR_FINISHED;
642 }
643
644 void GPENCIL_OT_layer_merge(wmOperatorType *ot)
645 {
646         /* identifiers */
647         ot->name = "Merge Down";
648         ot->idname = "GPENCIL_OT_layer_merge";
649         ot->description = "Merge the current layer with the layer below";
650
651         /* callbacks */
652         ot->exec = gp_merge_layer_exec;
653         ot->poll = gp_active_layer_poll;
654
655         /* flags */
656         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
657 }
658
659 /* ********************** Change Layer ***************************** */
660
661 static int gp_layer_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt))
662 {
663         uiPopupMenu *pup;
664         uiLayout *layout;
665         
666         /* call the menu, which will call this operator again, hence the canceled */
667         pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE);
668         layout = UI_popup_menu_layout(pup);
669         uiItemsEnumO(layout, "GPENCIL_OT_layer_change", "layer");
670         UI_popup_menu_end(C, pup);
671         
672         return OPERATOR_INTERFACE;
673 }
674
675 static int gp_layer_change_exec(bContext *C, wmOperator *op)
676 {
677         bGPdata *gpd = CTX_data_gpencil_data(C);
678         bGPDlayer *gpl = NULL;
679         int layer_num = RNA_enum_get(op->ptr, "layer");
680         
681         /* Get layer or create new one */
682         if (layer_num == -1) {
683                 /* Create layer */
684                 gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
685         }
686         else {
687                 /* Try to get layer */
688                 gpl = BLI_findlink(&gpd->layers, layer_num);
689                 
690                 if (gpl == NULL) {
691                         BKE_reportf(op->reports, RPT_ERROR, "Cannot change to non-existent layer (index = %d)", layer_num);
692                         return OPERATOR_CANCELLED;
693                 }
694         }
695         
696         /* Set active layer */
697         BKE_gpencil_layer_setactive(gpd, gpl);
698         
699         /* updates */
700         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
701         
702         return OPERATOR_FINISHED;
703 }
704
705 void GPENCIL_OT_layer_change(wmOperatorType *ot)
706 {
707         /* identifiers */
708         ot->name = "Change Layer";
709         ot->idname = "GPENCIL_OT_layer_change";
710         ot->description = "Change active Grease Pencil layer";
711         
712         /* callbacks */
713         ot->invoke = gp_layer_change_invoke;
714         ot->exec = gp_layer_change_exec;
715         ot->poll = gp_active_layer_poll;
716         
717         /* flags */
718         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
719         
720         /* gp layer to use (dynamic enum) */
721         ot->prop = RNA_def_enum(ot->srna, "layer", DummyRNA_DEFAULT_items, 0, "Grease Pencil Layer", "");
722         RNA_def_enum_funcs(ot->prop, ED_gpencil_layers_with_new_enum_itemf);
723 }
724
725 /* ************************************************ */
726
727 /* ******************* Arrange Stroke Up/Down in drawing order ************************** */
728
729 enum {
730         GP_STROKE_MOVE_UP = -1,
731         GP_STROKE_MOVE_DOWN = 1,
732         GP_STROKE_MOVE_TOP = 2,
733         GP_STROKE_MOVE_BOTTOM = 3
734 };
735
736 static int gp_stroke_arrange_exec(bContext *C, wmOperator *op)
737 {
738         bGPdata *gpd = ED_gpencil_data_get_active(C);
739         bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
740         bGPDstroke *gps;
741
742         /* sanity checks */
743         if (ELEM(NULL, gpd, gpl, gpl->actframe)) {
744                 return OPERATOR_CANCELLED;
745         }
746
747         bGPDframe *gpf = gpl->actframe;
748         /* temp listbase to store selected strokes */
749         ListBase selected = {NULL};
750         const int direction = RNA_enum_get(op->ptr, "direction");
751
752         /* verify if any selected stroke is in the extreme of the stack and select to move */
753         for (gps = gpf->strokes.first; gps; gps = gps->next) {
754                 /* only if selected */
755                 if (gps->flag & GP_STROKE_SELECT) {
756                         /* skip strokes that are invalid for current view */
757                         if (ED_gpencil_stroke_can_use(C, gps) == false) {
758                                 continue;
759                         }
760                         /* check if the color is editable */
761                         if (ED_gpencil_stroke_color_use(gpl, gps) == false) {
762                                 continue;
763                         }
764                         /* some stroke is already at front*/
765                         if ((direction == GP_STROKE_MOVE_TOP) || (direction == GP_STROKE_MOVE_UP)) {
766                                 if (gps == gpf->strokes.last) {
767                                         BKE_report(op->reports, RPT_ERROR, "Some selected stroke is already on top");
768                                         return OPERATOR_CANCELLED;
769                                 }
770                         }
771                         /* some stroke is already at botom */
772                         if ((direction == GP_STROKE_MOVE_BOTTOM) || (direction == GP_STROKE_MOVE_DOWN)) {
773                                 if (gps == gpf->strokes.first) {
774                                         BKE_report(op->reports, RPT_ERROR, "Some selected stroke is already on bottom");
775                                         return OPERATOR_CANCELLED;
776                                 }
777                         }
778                         /* add to list */
779                         BLI_addtail(&selected, BLI_genericNodeN(gps));
780                 }
781         }
782
783         /* Now do the movement of the stroke */
784         switch (direction) {
785                 /* Bring to Front */
786                 case GP_STROKE_MOVE_TOP:
787                         for (LinkData *link = selected.first; link; link = link->next) {
788                                 gps = link->data;
789                                 BLI_remlink(&gpf->strokes, gps);
790                                 BLI_insertlinkafter(&gpf->strokes, gpf->strokes.last, gps);
791                         }
792                         break;
793                 /* Bring Forward */
794                 case GP_STROKE_MOVE_UP:
795                         for (LinkData *link = selected.last; link; link = link->prev) {
796                                 gps = link->data;
797                                 BLI_remlink(&gpf->strokes, gps);
798                                 BLI_insertlinkafter(&gpf->strokes, gps->next, gps);
799                         }
800                         break;
801                         /* Send Backward */
802                 case GP_STROKE_MOVE_DOWN:
803                         for (LinkData *link = selected.first; link; link = link->next) {
804                                 gps = link->data;
805                                 BLI_remlink(&gpf->strokes, gps);
806                                 BLI_insertlinkbefore(&gpf->strokes, gps->prev, gps);
807                         }
808                         break;
809                 /* Send to Back */
810                 case GP_STROKE_MOVE_BOTTOM:
811                         for (LinkData *link = selected.last; link; link = link->prev) {
812                                 gps = link->data;
813                                 BLI_remlink(&gpf->strokes, gps);
814                                 BLI_insertlinkbefore(&gpf->strokes, gpf->strokes.first, gps);
815                         }
816                         break;
817                 default:
818                         BLI_assert(0);
819                         break;
820         }
821         /* notifiers */
822         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
823
824         return OPERATOR_FINISHED;
825 }
826
827 void GPENCIL_OT_stroke_arrange(wmOperatorType *ot)
828 {
829         static EnumPropertyItem slot_move[] = {
830                 {GP_STROKE_MOVE_UP, "UP", 0, "Bring Forward", ""},
831                 {GP_STROKE_MOVE_DOWN, "DOWN", 0, "Send Backward", ""},
832                 {GP_STROKE_MOVE_TOP, "TOP", 0, "Bring to Front", ""},
833                 {GP_STROKE_MOVE_BOTTOM, "BOTTOM", 0, "Send to Back", ""},
834                 {0, NULL, 0, NULL, NULL }
835         };
836
837         /* identifiers */
838         ot->name = "Arrange Stroke";
839         ot->idname = "GPENCIL_OT_stroke_arrange";
840         ot->description = "Arrange selected strokes up/down in the drawing order of the active layer";
841
842         /* api callbacks */
843         ot->exec = gp_stroke_arrange_exec;
844         ot->poll = gp_active_layer_poll;
845
846         /* flags */
847         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
848
849         ot->prop = RNA_def_enum(ot->srna, "direction", slot_move, GP_STROKE_MOVE_UP, "Direction", "");
850 }
851 /* ******************* Move Stroke to new color ************************** */
852
853 static int gp_stroke_change_color_exec(bContext *C, wmOperator *UNUSED(op))
854 {
855         bGPdata *gpd = ED_gpencil_data_get_active(C);
856         bGPDpalette *palette;
857         bGPDpalettecolor *color;
858
859         /* sanity checks */
860         if (ELEM(NULL, gpd)) {
861                 return OPERATOR_CANCELLED;
862         }
863
864         palette = BKE_gpencil_palette_getactive(gpd);
865         color = BKE_gpencil_palettecolor_getactive(palette);
866         if (ELEM(NULL, palette, color)) {
867                 return OPERATOR_CANCELLED;
868         }
869
870         /* loop all strokes */
871         for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
872                 /* only editable and visible layers are considered */
873                 if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
874                         for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
875                                 /* only if selected */
876                                 if (gps->flag & GP_STROKE_SELECT) {
877                                         /* skip strokes that are invalid for current view */
878                                         if (ED_gpencil_stroke_can_use(C, gps) == false)
879                                                 continue;
880                                         /* check if the color is editable */
881                                         if (ED_gpencil_stroke_color_use(gpl, gps) == false)
882                                                 continue;
883
884                                         /* asign new color (only if different) */
885                                         if (STREQ(gps->colorname, color->info) == false) {
886                                                 BLI_strncpy(gps->colorname, color->info, sizeof(gps->colorname));
887                                                 gps->flag |= GP_STROKE_RECALC_COLOR;
888                                         }
889                                 }
890                         }
891                 }
892         }
893         /* notifiers */
894         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
895
896         return OPERATOR_FINISHED;
897 }
898
899 void GPENCIL_OT_stroke_change_color(wmOperatorType *ot)
900 {
901         /* identifiers */
902         ot->name = "Change Stroke Color";
903         ot->idname = "GPENCIL_OT_stroke_change_color";
904         ot->description = "Move selected strokes to active color";
905
906         /* api callbacks */
907         ot->exec = gp_stroke_change_color_exec;
908         ot->poll = gp_active_layer_poll;
909 }
910
911 /* ******************* Lock color of non selected Strokes colors ************************** */
912
913 static int gp_stroke_lock_color_exec(bContext *C, wmOperator *UNUSED(op))
914 {
915         bGPdata *gpd = ED_gpencil_data_get_active(C);
916         bGPDpalette *palette;
917
918         /* sanity checks */
919         if (ELEM(NULL, gpd))
920                 return OPERATOR_CANCELLED;
921
922         palette = BKE_gpencil_palette_getactive(gpd);
923         if (ELEM(NULL, palette))
924                 return OPERATOR_CANCELLED;
925
926         /* first lock all colors */
927         for (bGPDpalettecolor *palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
928                 palcolor->flag |= PC_COLOR_LOCKED;
929         }
930
931         /* loop all selected strokes and unlock any color */
932         for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
933                 /* only editable and visible layers are considered */
934                 if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
935                         for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
936                                 /* only if selected */
937                                 if (gps->flag & GP_STROKE_SELECT) {
938                                         /* skip strokes that are invalid for current view */
939                                         if (ED_gpencil_stroke_can_use(C, gps) == false) {
940                                                 continue;
941                                         }
942                                         /* unlock color */
943                                         if (gps->palcolor != NULL) {
944                                                 gps->palcolor->flag &= ~PC_COLOR_LOCKED;
945                                         }
946                                 }
947                         }
948                 }
949         }
950         /* notifiers */
951         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
952
953         return OPERATOR_FINISHED;
954 }
955
956 void GPENCIL_OT_stroke_lock_color(wmOperatorType *ot)
957 {
958         /* identifiers */
959         ot->name = "Lock Unused Colors";
960         ot->idname = "GPENCIL_OT_stroke_lock_color";
961         ot->description = "Lock any color not used in any selected stroke";
962
963         /* api callbacks */
964         ot->exec = gp_stroke_lock_color_exec;
965         ot->poll = gp_active_layer_poll;
966 }
967
968 /* ******************* Apply layer thickness change to Strokes ************************** */
969
970 static int gp_stroke_apply_thickness_exec(bContext *C, wmOperator *UNUSED(op))
971 {
972         bGPdata *gpd = ED_gpencil_data_get_active(C);
973         bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
974
975         /* sanity checks */
976         if (ELEM(NULL, gpd, gpl, gpl->frames.first))
977                 return OPERATOR_CANCELLED;
978
979         /* loop all strokes */
980         for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) {
981                 for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
982                         /* Apply thickness */
983                         gps->thickness = gps->thickness + gpl->thickness;
984                 }
985         }
986         /* clear value */
987         gpl->thickness = 0.0f;
988
989         /* notifiers */
990         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
991
992         return OPERATOR_FINISHED;
993 }
994
995 void GPENCIL_OT_stroke_apply_thickness(wmOperatorType *ot)
996 {
997         /* identifiers */
998         ot->name = "Apply Stroke Thickness";
999         ot->idname = "GPENCIL_OT_stroke_apply_thickness";
1000         ot->description = "Apply the thickness change of the layer to its strokes";
1001
1002         /* api callbacks */
1003         ot->exec = gp_stroke_apply_thickness_exec;
1004         ot->poll = gp_active_layer_poll;
1005 }
1006
1007 /* ******************* Close Strokes ************************** */
1008
1009 enum {
1010         GP_STROKE_CYCLIC_CLOSE = 1,
1011         GP_STROKE_CYCLIC_OPEN = 2,
1012         GP_STROKE_CYCLIC_TOGGLE = 3
1013 };
1014
1015 static int gp_stroke_cyclical_set_exec(bContext *C, wmOperator *op)
1016 {
1017         bGPdata *gpd = ED_gpencil_data_get_active(C);
1018         const int type = RNA_enum_get(op->ptr, "type");
1019
1020         /* sanity checks */
1021         if (ELEM(NULL, gpd))
1022                 return OPERATOR_CANCELLED;
1023
1024         /* loop all selected strokes */
1025         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
1026         {
1027                 for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
1028                         bGPDpalettecolor *palcolor = gps->palcolor;
1029
1030                         /* skip strokes that are not selected or invalid for current view */
1031                         if (((gps->flag & GP_STROKE_SELECT) == 0) || ED_gpencil_stroke_can_use(C, gps) == false)
1032                                 continue;
1033                         /* skip hidden or locked colors */
1034                         if (!palcolor || (palcolor->flag & PC_COLOR_HIDE) || (palcolor->flag & PC_COLOR_LOCKED))
1035                                 continue;
1036
1037                         switch (type) {
1038                                 case GP_STROKE_CYCLIC_CLOSE:
1039                                         /* Close all (enable) */
1040                                         gps->flag |= GP_STROKE_CYCLIC;
1041                                         break;
1042                                 case GP_STROKE_CYCLIC_OPEN:
1043                                         /* Open all (disable) */
1044                                         gps->flag &= ~GP_STROKE_CYCLIC;
1045                                         break;
1046                                 case GP_STROKE_CYCLIC_TOGGLE:
1047                                         /* Just toggle flag... */
1048                                         gps->flag ^= GP_STROKE_CYCLIC;
1049                                         break;
1050                                 default:
1051                                         BLI_assert(0);
1052                                         break;
1053                         }
1054                 }
1055         }
1056         CTX_DATA_END;
1057
1058         /* notifiers */
1059         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1060
1061         return OPERATOR_FINISHED;
1062 }
1063
1064 /**
1065  * Similar to #CURVE_OT_cyclic_toggle or #MASK_OT_cyclic_toggle, but with
1066  * option to force opened/closed strokes instead of just toggle behavior.
1067  */
1068 void GPENCIL_OT_stroke_cyclical_set(wmOperatorType *ot)
1069 {
1070         static EnumPropertyItem cyclic_type[] = {
1071                 {GP_STROKE_CYCLIC_CLOSE, "CLOSE", 0, "Close all", ""},
1072                 {GP_STROKE_CYCLIC_OPEN, "OPEN", 0, "Open all", ""},
1073                 {GP_STROKE_CYCLIC_TOGGLE, "TOGGLE", 0, "Toggle", ""},
1074                 {0, NULL, 0, NULL, NULL}
1075         };
1076
1077         /* identifiers */
1078         ot->name = "Set Cyclical State";
1079         ot->idname = "GPENCIL_OT_stroke_cyclical_set";
1080         ot->description = "Close or open the selected stroke adding an edge from last to first point";
1081
1082         /* api callbacks */
1083         ot->exec = gp_stroke_cyclical_set_exec;
1084         ot->poll = gp_active_layer_poll;
1085
1086         /* flags */
1087         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1088
1089         ot->prop = RNA_def_enum(ot->srna, "type", cyclic_type, GP_STROKE_CYCLIC_TOGGLE, "Type", "");
1090 }
1091
1092 /* ******************* Stroke join ************************** */
1093
1094 /* Helper: flip stroke */
1095 static void gpencil_flip_stroke(bGPDstroke *gps)
1096 {
1097         bGPDspoint pt, *point, *point2;
1098         int end = gps->totpoints - 1;
1099
1100         for (int i = 0; i < gps->totpoints / 2; i++) {
1101                 /* save first point */
1102                 point = &gps->points[i];
1103                 pt.x = point->x;
1104                 pt.y = point->y;
1105                 pt.z = point->z;
1106                 pt.flag = point->flag;
1107                 pt.pressure = point->pressure;
1108                 pt.strength = point->strength;
1109                 pt.time = point->time;
1110
1111                 /* replace first point with last point */
1112                 point2 = &gps->points[end];
1113                 point->x = point2->x;
1114                 point->y = point2->y;
1115                 point->z = point2->z;
1116                 point->flag = point2->flag;
1117                 point->pressure = point2->pressure;
1118                 point->strength = point2->strength;
1119                 point->time = point2->time;
1120
1121                 /* replace last point with first saved before */
1122                 point = &gps->points[end];
1123                 point->x = pt.x;
1124                 point->y = pt.y;
1125                 point->z = pt.z;
1126                 point->flag = pt.flag;
1127                 point->pressure = pt.pressure;
1128                 point->strength = pt.strength;
1129                 point->time = pt.time;
1130
1131                 end--;
1132         }
1133 }
1134
1135 /* Helper: copy point between strokes */
1136 static void gpencil_stroke_copy_point(bGPDstroke *gps, bGPDspoint *point, float delta[3],
1137                                       float pressure, float strength, float deltatime)
1138 {
1139         bGPDspoint *newpoint;
1140
1141         gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1));
1142         gps->totpoints++;
1143
1144         newpoint = &gps->points[gps->totpoints - 1];
1145         newpoint->x = point->x * delta[0];
1146         newpoint->y = point->y * delta[1];
1147         newpoint->z = point->z * delta[2];
1148         newpoint->flag = point->flag;
1149         newpoint->pressure = pressure;
1150         newpoint->strength = strength;
1151         newpoint->time = point->time + deltatime;
1152 }
1153
1154 /* Helper: join two strokes using the shortest distance (reorder stroke if necessary ) */
1155 static void gpencil_stroke_join_strokes(bGPDstroke *gps_a, bGPDstroke *gps_b, const bool leave_gaps)
1156 {
1157         bGPDspoint point, *pt;
1158         int i;
1159         float delta[3] = {1.0f, 1.0f, 1.0f};
1160         float deltatime = 0.0f;
1161
1162         /* sanity checks */
1163         if (ELEM(NULL, gps_a, gps_b))
1164                 return;
1165
1166         if ((gps_a->totpoints == 0) || (gps_b->totpoints == 0))
1167                 return;
1168
1169         /* define start and end points of each stroke */
1170         float sa[3], sb[3], ea[3], eb[3];
1171         pt = &gps_a->points[0];
1172         copy_v3_v3(sa, &pt->x);
1173
1174         pt = &gps_a->points[gps_a->totpoints - 1];
1175         copy_v3_v3(ea, &pt->x);
1176
1177         pt = &gps_b->points[0];
1178         copy_v3_v3(sb, &pt->x);
1179         
1180         pt = &gps_b->points[gps_b->totpoints - 1];
1181         copy_v3_v3(eb, &pt->x);
1182         
1183         /* review if need flip stroke B */
1184         float ea_sb = len_squared_v3v3(ea, sb);
1185         float ea_eb = len_squared_v3v3(ea, eb);
1186         /* flip if distance to end point is shorter */
1187         if (ea_eb < ea_sb) {
1188                 gpencil_flip_stroke(gps_b);
1189         }
1190
1191         /* don't visibly link the first and last points? */
1192         if (leave_gaps) {
1193                 /* 1st: add one tail point to start invisible area */
1194                 point = gps_a->points[gps_a->totpoints - 1];
1195                 deltatime = point.time;
1196                 gpencil_stroke_copy_point(gps_a, &point, delta, 0.0f, 0.0f, 0.0f);
1197
1198                 /* 2nd: add one head point to finish invisible area */
1199                 point = gps_b->points[0];
1200                 gpencil_stroke_copy_point(gps_a, &point, delta, 0.0f, 0.0f, deltatime);
1201         }
1202         
1203         /* 3rd: add all points */
1204         for (i = 0, pt = gps_b->points; i < gps_b->totpoints && pt; i++, pt++) {
1205                 /* check if still room in buffer */
1206                 if (gps_a->totpoints <= GP_STROKE_BUFFER_MAX - 2) {
1207                         gpencil_stroke_copy_point(gps_a, pt, delta, pt->pressure, pt->strength, deltatime);
1208                 }
1209         }
1210 }
1211
1212 static int gp_stroke_join_exec(bContext *C, wmOperator *op)
1213 {
1214         bGPdata *gpd = ED_gpencil_data_get_active(C);
1215         bGPDlayer *activegpl = BKE_gpencil_layer_getactive(gpd);
1216         bGPDstroke *gps, *gpsn;
1217         bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
1218         bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette);
1219
1220         bGPDframe *gpf_a = NULL;
1221         bGPDstroke *stroke_a = NULL;
1222         bGPDstroke *stroke_b = NULL;
1223         bGPDstroke *new_stroke = NULL;
1224
1225         const int type = RNA_enum_get(op->ptr, "type");
1226         const bool leave_gaps = RNA_boolean_get(op->ptr, "leave_gaps");
1227
1228         /* sanity checks */
1229         if (ELEM(NULL, gpd))
1230                 return OPERATOR_CANCELLED;
1231
1232         if (activegpl->flag & GP_LAYER_LOCKED)
1233                 return OPERATOR_CANCELLED;
1234
1235         BLI_assert(ELEM(type, GP_STROKE_JOIN, GP_STROKE_JOINCOPY));
1236
1237
1238         /* read all selected strokes */
1239         bool first = false;
1240         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
1241         {
1242                 bGPDframe *gpf = gpl->actframe;
1243                 for (gps = gpf->strokes.first; gps; gps = gpsn) {
1244                         gpsn = gps->next;
1245                         if (gps->flag & GP_STROKE_SELECT) {
1246                                 /* skip strokes that are invalid for current view */
1247                                 if (ED_gpencil_stroke_can_use(C, gps) == false) {
1248                                         continue;
1249                                 }
1250                                 /* check if the color is editable */
1251                                 if (ED_gpencil_stroke_color_use(gpl, gps) == false) {
1252                                         continue;
1253                                 }
1254                                 /* to join strokes, cyclic must be disabled */
1255                                 gps->flag &= ~GP_STROKE_CYCLIC;
1256                                 /* saves first frame and stroke */
1257                                 if (!first) {
1258                                         first = true;
1259                                         gpf_a = gpf;
1260                                         stroke_a = gps;
1261                                 }
1262                                 else {
1263                                         stroke_b = gps;
1264                                         /* create a new stroke if was not created before (only created if something to join) */
1265                                         if (new_stroke == NULL) {
1266                                                 new_stroke = MEM_dupallocN(stroke_a);
1267                                                 new_stroke->points = MEM_dupallocN(stroke_a->points);
1268                                                 new_stroke->triangles = NULL;
1269                                                 new_stroke->tot_triangles = 0;
1270                                                 new_stroke->flag |= GP_STROKE_RECALC_CACHES;
1271                                                 /* if new, set current color */
1272                                                 if (type == GP_STROKE_JOINCOPY) {
1273                                                         new_stroke->palcolor = palcolor;
1274                                                         BLI_strncpy(new_stroke->colorname, palcolor->info, sizeof(new_stroke->colorname));
1275                                                         new_stroke->flag |= GP_STROKE_RECALC_COLOR;
1276                                                 }
1277                                         }
1278                                         /* join new_stroke and stroke B. New stroke will contain all the previous data */
1279                                         gpencil_stroke_join_strokes(new_stroke, stroke_b, leave_gaps);
1280
1281                                         /* if join only, delete old strokes */
1282                                         if (type == GP_STROKE_JOIN) {
1283                                                 if (stroke_a) {
1284                                                         BLI_insertlinkbefore(&gpf_a->strokes, stroke_a, new_stroke);
1285                                                         BLI_remlink(&gpf->strokes, stroke_a);
1286                                                         BKE_gpencil_free_stroke(stroke_a);
1287                                                         stroke_a = NULL;
1288                                                 }
1289                                                 if (stroke_b) {
1290                                                         BLI_remlink(&gpf->strokes, stroke_b);
1291                                                         BKE_gpencil_free_stroke(stroke_b);
1292                                                         stroke_b = NULL;
1293                                                 }
1294                                         }
1295                                 }
1296                         }
1297                 }
1298         }
1299         CTX_DATA_END;
1300         /* add new stroke if was not added before */
1301         if (type == GP_STROKE_JOINCOPY) {
1302                 if (new_stroke) {
1303                         /* Add a new frame if needed */
1304                         if (activegpl->actframe == NULL)
1305                                 activegpl->actframe = BKE_gpencil_frame_addnew(activegpl, gpf_a->framenum);
1306
1307                         BLI_addtail(&activegpl->actframe->strokes, new_stroke);
1308                 }
1309         }
1310
1311         /* notifiers */
1312         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1313
1314         return OPERATOR_FINISHED;
1315 }
1316
1317 void GPENCIL_OT_stroke_join(wmOperatorType *ot)
1318 {
1319         static EnumPropertyItem join_type[] = {
1320                 {GP_STROKE_JOIN, "JOIN", 0, "Join", ""},
1321                 {GP_STROKE_JOINCOPY, "JOINCOPY", 0, "Join and Copy", ""},
1322                 {0, NULL, 0, NULL, NULL}
1323         };
1324
1325         /* identifiers */
1326         ot->name = "Join Strokes";
1327         ot->idname = "GPENCIL_OT_stroke_join";
1328         ot->description = "Join selected strokes (optionally as new stroke)";
1329
1330         /* api callbacks */
1331         ot->exec = gp_stroke_join_exec;
1332         ot->poll = gp_active_layer_poll;
1333
1334         /* flags */
1335         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1336
1337         ot->prop = RNA_def_enum(ot->srna, "type", join_type, GP_STROKE_JOIN, "Type", "");
1338         RNA_def_boolean(ot->srna, "leave_gaps", false, "Leave Gaps", "Leave gaps between joined strokes instead of linking them");
1339 }
1340
1341 /* ******************* Stroke flip ************************** */
1342
1343 static int gp_stroke_flip_exec(bContext *C, wmOperator *UNUSED(op))
1344 {
1345         bGPdata *gpd = ED_gpencil_data_get_active(C);
1346
1347         /* sanity checks */
1348         if (ELEM(NULL, gpd))
1349                 return OPERATOR_CANCELLED;
1350
1351         /* read all selected strokes */
1352         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
1353         {
1354                 bGPDframe *gpf = gpl->actframe;
1355                 for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
1356                         if (gps->flag & GP_STROKE_SELECT) {
1357                                 /* skip strokes that are invalid for current view */
1358                                 if (ED_gpencil_stroke_can_use(C, gps) == false) {
1359                                         continue;
1360                                 }
1361                                 /* check if the color is editable */
1362                                 if (ED_gpencil_stroke_color_use(gpl, gps) == false) {
1363                                         continue;
1364                                 }
1365                                 /* flip stroke */
1366                                 gpencil_flip_stroke(gps);
1367                         }
1368                 }
1369         }
1370         CTX_DATA_END;
1371
1372         /* notifiers */
1373         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1374
1375         return OPERATOR_FINISHED;
1376 }
1377
1378 void GPENCIL_OT_stroke_flip(wmOperatorType *ot)
1379 {
1380         /* identifiers */
1381         ot->name = "Flip Stroke";
1382         ot->idname = "GPENCIL_OT_stroke_flip";
1383         ot->description = "Change direction of the points of the selected strokes";
1384
1385         /* api callbacks */
1386         ot->exec = gp_stroke_flip_exec;
1387         ot->poll = gp_active_layer_poll;
1388
1389         /* flags */
1390         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1391 }
1392
1393 /* ************************************************ */
1394 /* Drawing Brushes Operators */
1395
1396 /* ******************* Add New Brush ************************ */
1397
1398 /* add new brush - wrapper around API */
1399 static int gp_brush_add_exec(bContext *C, wmOperator *op)
1400 {
1401         ToolSettings *ts = CTX_data_tool_settings(C);
1402
1403         /* if there's no existing container */
1404         if (ts == NULL) {
1405                 BKE_report(op->reports, RPT_ERROR, "Nowhere for brush data to go");
1406                 return OPERATOR_CANCELLED;
1407         }
1408         /* add new brush now */
1409         BKE_gpencil_brush_addnew(ts, DATA_("GP_Brush"), true);
1410
1411         /* notifiers */
1412         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1413
1414         return OPERATOR_FINISHED;
1415 }
1416
1417 void GPENCIL_OT_brush_add(wmOperatorType *ot)
1418 {
1419         /* identifiers */
1420         ot->name = "Add Brush";
1421         ot->idname = "GPENCIL_OT_brush_add";
1422         ot->description = "Add new Grease Pencil drawing brush for the active Grease Pencil datablock";
1423
1424         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1425
1426         /* callbacks */
1427         ot->exec = gp_brush_add_exec;
1428         ot->poll = gp_add_poll;
1429 }
1430
1431 /* ******************* Remove Active Brush ************************* */
1432
1433 static int gp_brush_remove_exec(bContext *C, wmOperator *op)
1434 {
1435         ToolSettings *ts = CTX_data_tool_settings(C);
1436         bGPDbrush *brush = BKE_gpencil_brush_getactive(ts);
1437
1438         /* sanity checks */
1439         if (ELEM(NULL, ts, brush))
1440                 return OPERATOR_CANCELLED;
1441
1442         if (BLI_listbase_count_ex(&ts->gp_brushes, 2) < 2) {
1443                 BKE_report(op->reports, RPT_ERROR, "Grease Pencil needs a brush, unable to delete the last one");
1444                 return OPERATOR_CANCELLED;
1445         }
1446
1447
1448         /* make the brush before this the new active brush
1449          * - use the one after if this is the first
1450          * - if this is the only brush, this naturally becomes NULL
1451          */
1452         if (brush->prev)
1453                 BKE_gpencil_brush_setactive(ts, brush->prev);
1454         else
1455                 BKE_gpencil_brush_setactive(ts, brush->next);
1456
1457         /* delete the brush now... */
1458         BKE_gpencil_brush_delete(ts, brush);
1459
1460         /* notifiers */
1461         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1462
1463         return OPERATOR_FINISHED;
1464 }
1465
1466 void GPENCIL_OT_brush_remove(wmOperatorType *ot)
1467 {
1468         /* identifiers */
1469         ot->name = "Remove Brush";
1470         ot->idname = "GPENCIL_OT_brush_remove";
1471         ot->description = "Remove active Grease Pencil drawing brush";
1472
1473         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1474
1475         /* callbacks */
1476         ot->exec = gp_brush_remove_exec;
1477         ot->poll = gp_active_brush_poll;
1478 }
1479
1480 /* ********************** Change Brush ***************************** */
1481
1482 static int gp_brush_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt))
1483 {
1484         uiPopupMenu *pup;
1485         uiLayout *layout;
1486
1487         /* call the menu, which will call this operator again, hence the canceled */
1488         pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE);
1489         layout = UI_popup_menu_layout(pup);
1490         uiItemsEnumO(layout, "GPENCIL_OT_brush_change", "brush");
1491         UI_popup_menu_end(C, pup);
1492
1493         return OPERATOR_INTERFACE;
1494 }
1495
1496 static int gp_brush_change_exec(bContext *C, wmOperator *op)
1497 {
1498         ToolSettings *ts = CTX_data_tool_settings(C);
1499         bGPDbrush *brush = NULL;
1500         int brush_num = RNA_enum_get(op->ptr, "brush");
1501
1502         /* Get brush or create new one */
1503         if (brush_num == -1) {
1504                 /* Create brush */
1505                 brush = BKE_gpencil_brush_addnew(ts, DATA_("GP_Brush"), true);
1506         }
1507         else {
1508                 /* Try to get brush */
1509                 brush = BLI_findlink(&ts->gp_brushes, brush_num);
1510
1511                 if (brush == NULL) {
1512                         BKE_reportf(op->reports, RPT_ERROR, "Cannot change to non-existent brush (index = %d)", brush_num);
1513                         return OPERATOR_CANCELLED;
1514                 }
1515         }
1516
1517         /* Set active brush */
1518         BKE_gpencil_brush_setactive(ts, brush);
1519
1520         /* updates */
1521         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1522
1523         return OPERATOR_FINISHED;
1524 }
1525
1526 void GPENCIL_OT_brush_change(wmOperatorType *ot)
1527 {
1528         /* identifiers */
1529         ot->name = "Change Brush";
1530         ot->idname = "GPENCIL_OT_brush_change";
1531         ot->description = "Change active Grease Pencil drawing brush";
1532
1533         /* callbacks */
1534         ot->invoke = gp_brush_change_invoke;
1535         ot->exec = gp_brush_change_exec;
1536         ot->poll = gp_active_brush_poll;
1537
1538         /* flags */
1539         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1540
1541         /* gp brush to use (dynamic enum) */
1542         ot->prop = RNA_def_enum(ot->srna, "brush", DummyRNA_DEFAULT_items, 0, "Grease Pencil Brush", "");
1543         RNA_def_enum_funcs(ot->prop, ED_gpencil_brushes_enum_itemf);
1544 }
1545
1546 /* ******************* Move Brush Up/Down ************************** */
1547
1548 enum {
1549         GP_BRUSH_MOVE_UP = -1,
1550         GP_BRUSH_MOVE_DOWN = 1
1551 };
1552
1553 static int gp_brush_move_exec(bContext *C, wmOperator *op)
1554 {
1555         ToolSettings *ts = CTX_data_tool_settings(C);
1556         bGPDbrush *brush = BKE_gpencil_brush_getactive(ts);
1557
1558         int direction = RNA_enum_get(op->ptr, "type");
1559
1560         /* sanity checks */
1561         if (ELEM(NULL, ts, brush)) {
1562                 return OPERATOR_CANCELLED;
1563         }
1564
1565         /* up or down? */
1566         if (direction == GP_BRUSH_MOVE_UP) {
1567                 /* up */
1568                 BLI_remlink(&ts->gp_brushes, brush);
1569                 BLI_insertlinkbefore(&ts->gp_brushes, brush->prev, brush);
1570         }
1571         else if (direction == GP_BRUSH_MOVE_DOWN) {
1572                 /* down */
1573                 BLI_remlink(&ts->gp_brushes, brush);
1574                 BLI_insertlinkafter(&ts->gp_brushes, brush->next, brush);
1575         }
1576         else {
1577                 BLI_assert(0);
1578         }
1579
1580         /* notifiers */
1581         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1582
1583         return OPERATOR_FINISHED;
1584 }
1585
1586 void GPENCIL_OT_brush_move(wmOperatorType *ot)
1587 {
1588         static EnumPropertyItem slot_move[] = {
1589                 {GP_BRUSH_MOVE_UP, "UP", 0, "Up", ""},
1590                 {GP_BRUSH_MOVE_DOWN, "DOWN", 0, "Down", ""},
1591                 {0, NULL, 0, NULL, NULL }
1592         };
1593
1594         /* identifiers */
1595         ot->name = "Move Brush";
1596         ot->idname = "GPENCIL_OT_brush_move";
1597         ot->description = "Move the active Grease Pencil drawing brush up/down in the list";
1598
1599         /* api callbacks */
1600         ot->exec = gp_brush_move_exec;
1601         ot->poll = gp_active_brush_poll;
1602
1603         /* flags */
1604         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1605
1606         ot->prop = RNA_def_enum(ot->srna, "type", slot_move, GP_BRUSH_MOVE_UP, "Type", "");
1607 }
1608
1609 /* ******************* Brush create presets ************************** */
1610
1611 static int gp_brush_presets_create_exec(bContext *C, wmOperator *UNUSED(op))
1612 {
1613         ToolSettings *ts = CTX_data_tool_settings(C);
1614         BKE_gpencil_brush_init_presets(ts);
1615
1616         /* notifiers */
1617         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1618
1619         return OPERATOR_FINISHED;
1620 }
1621
1622 void GPENCIL_OT_brush_presets_create(wmOperatorType *ot)
1623 {
1624         /* identifiers */
1625         ot->name = "Create Preset Brushes";
1626         ot->idname = "GPENCIL_OT_brush_presets_create";
1627         ot->description = "Create a set of predefined Grease Pencil drawing brushes";
1628
1629         /* api callbacks */
1630         ot->exec = gp_brush_presets_create_exec;
1631
1632         /* flags */
1633         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1634
1635 }
1636
1637 /* ***************** Copy Brush ************************ */
1638
1639 static int gp_brush_copy_exec(bContext *C, wmOperator *op)
1640 {
1641         ToolSettings *ts = CTX_data_tool_settings(C);
1642
1643         /* if there's no existing container */
1644         if (ts == NULL) {
1645                 BKE_report(op->reports, RPT_ERROR, "Nowhere for brush data to go");
1646                 return OPERATOR_CANCELLED;
1647         }
1648
1649         bGPDbrush *brush = BKE_gpencil_brush_getactive(ts);
1650         bGPDbrush *newbrush;
1651
1652         /* sanity checks */
1653         if (ELEM(NULL, brush))
1654                 return OPERATOR_CANCELLED;
1655
1656         /* create a brush and duplicate data */
1657         newbrush = BKE_gpencil_brush_addnew(ts, brush->info, true);
1658         newbrush->thickness = brush->thickness;
1659         newbrush->draw_smoothfac = brush->draw_smoothfac;
1660         newbrush->draw_smoothlvl = brush->draw_smoothlvl;
1661         newbrush->sublevel = brush->sublevel;
1662         newbrush->flag = brush->flag;
1663         newbrush->draw_sensitivity = brush->draw_sensitivity;
1664         newbrush->draw_strength = brush->draw_strength;
1665         newbrush->draw_jitter = brush->draw_jitter;
1666         newbrush->draw_angle = brush->draw_angle;
1667         newbrush->draw_angle_factor = brush->draw_angle_factor;
1668         newbrush->draw_random_press = brush->draw_random_press;
1669         newbrush->draw_random_sub = brush->draw_random_sub;
1670
1671         /* free automatic curves created by default (replaced by copy) */
1672         curvemapping_free(newbrush->cur_sensitivity);
1673         curvemapping_free(newbrush->cur_strength);
1674         curvemapping_free(newbrush->cur_jitter);
1675
1676         /* make a copy of curves */
1677         newbrush->cur_sensitivity = curvemapping_copy(brush->cur_sensitivity);
1678         newbrush->cur_strength = curvemapping_copy(brush->cur_strength);
1679         newbrush->cur_jitter = curvemapping_copy(brush->cur_jitter);
1680
1681         BKE_gpencil_brush_setactive(ts, newbrush);
1682         /* notifiers */
1683         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1684
1685         return OPERATOR_FINISHED;
1686 }
1687
1688 void GPENCIL_OT_brush_copy(wmOperatorType *ot)
1689 {
1690         /* identifiers */
1691         ot->name = "Copy Brush";
1692         ot->idname = "GPENCIL_OT_brush_copy";
1693         ot->description = "Copy current Grease Pencil drawing brush";
1694
1695         /* callbacks */
1696         ot->exec = gp_brush_copy_exec;
1697         ot->poll = gp_active_brush_poll;
1698
1699         /* flags */
1700         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1701 }
1702
1703 /* ***************** Select Brush ************************ */
1704
1705 static int gp_brush_select_exec(bContext *C, wmOperator *op)
1706 {
1707         ToolSettings *ts = CTX_data_tool_settings(C);
1708
1709         /* if there's no existing container */
1710         if (ts == NULL) {
1711                 BKE_report(op->reports, RPT_ERROR, "Nowhere to go");
1712                 return OPERATOR_CANCELLED;
1713         }
1714
1715         const int index = RNA_int_get(op->ptr, "index");
1716         bGPDbrush *brush = BLI_findlink(&ts->gp_brushes, index);
1717         /* sanity checks */
1718         if (ELEM(NULL, brush)) {
1719                 return OPERATOR_CANCELLED;
1720         }
1721
1722         BKE_gpencil_brush_setactive(ts, brush);
1723
1724         /* notifiers */
1725         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1726
1727         return OPERATOR_FINISHED;
1728 }
1729
1730 void GPENCIL_OT_brush_select(wmOperatorType *ot)
1731 {
1732         /* identifiers */
1733         ot->name = "Select Brush";
1734         ot->idname = "GPENCIL_OT_brush_select";
1735         ot->description = "Select a Grease Pencil drawing brush";
1736
1737         /* callbacks */
1738         ot->exec = gp_brush_select_exec;
1739         ot->poll = gp_active_brush_poll;
1740
1741         /* flags */
1742         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1743
1744         /* properties */
1745         RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "Index of Drawing Brush", 0, INT_MAX);
1746 }
1747
1748 /* ************************************************ */
1749 /* Palette Operators */
1750
1751 /* ******************* Add New Palette ************************ */
1752
1753 /* add new palette - wrapper around API */
1754 static int gp_palette_add_exec(bContext *C, wmOperator *op)
1755 {
1756         bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
1757
1758         /* if there's no existing Grease-Pencil data there, add some */
1759         if (gpd_ptr == NULL) {
1760                 BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go");
1761                 return OPERATOR_CANCELLED;
1762         }
1763         if (*gpd_ptr == NULL)
1764                 *gpd_ptr = BKE_gpencil_data_addnew(DATA_("GPencil"));
1765
1766         /* add new palette now */
1767         BKE_gpencil_palette_addnew(*gpd_ptr, DATA_("GP_Palette"), true);
1768
1769         /* notifiers */
1770         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1771
1772         return OPERATOR_FINISHED;
1773 }
1774
1775 void GPENCIL_OT_palette_add(wmOperatorType *ot)
1776 {
1777         /* identifiers */
1778         ot->name = "Add Palette";
1779         ot->idname = "GPENCIL_OT_palette_add";
1780         ot->description = "Add new Grease Pencil palette for the active Grease Pencil datablock";
1781
1782         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1783
1784         /* callbacks */
1785         ot->exec = gp_palette_add_exec;
1786         ot->poll = gp_add_poll;
1787 }
1788
1789 /* ******************* Remove Active Palette ************************* */
1790
1791 static int gp_palette_remove_exec(bContext *C, wmOperator *op)
1792 {
1793         bGPdata *gpd = ED_gpencil_data_get_active(C);
1794         bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
1795
1796         /* sanity checks */
1797         if (ELEM(NULL, gpd, palette))
1798                 return OPERATOR_CANCELLED;
1799
1800         if (BLI_listbase_count_ex(&gpd->palettes, 2) < 2) {
1801                 BKE_report(op->reports, RPT_ERROR, "Grease Pencil needs a palette, unable to delete the last one");
1802                 return OPERATOR_CANCELLED;
1803         }
1804
1805
1806         /* make the palette before this the new active palette
1807          * - use the one after if this is the first
1808          * - if this is the only palette, this naturally becomes NULL
1809          */
1810         if (palette->prev)
1811                 BKE_gpencil_palette_setactive(gpd, palette->prev);
1812         else
1813                 BKE_gpencil_palette_setactive(gpd, palette->next);
1814
1815         /* delete the palette now... */
1816         BKE_gpencil_palette_delete(gpd, palette);
1817
1818         /* notifiers */
1819         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1820
1821         return OPERATOR_FINISHED;
1822 }
1823
1824 void GPENCIL_OT_palette_remove(wmOperatorType *ot)
1825 {
1826         /* identifiers */
1827         ot->name = "Remove palette";
1828         ot->idname = "GPENCIL_OT_palette_remove";
1829         ot->description = "Remove active Grease Pencil palette";
1830
1831         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1832
1833         /* callbacks */
1834         ot->exec = gp_palette_remove_exec;
1835         ot->poll = gp_active_palette_poll;
1836 }
1837
1838 /* ********************** Change Palette ***************************** */
1839
1840 static int gp_palette_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt))
1841 {
1842         uiPopupMenu *pup;
1843         uiLayout *layout;
1844
1845         /* call the menu, which will call this operator again, hence the canceled */
1846         pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE);
1847         layout = UI_popup_menu_layout(pup);
1848         uiItemsEnumO(layout, "GPENCIL_OT_palette_change", "palette");
1849         UI_popup_menu_end(C, pup);
1850
1851         return OPERATOR_INTERFACE;
1852 }
1853
1854 static int gp_palette_change_exec(bContext *C, wmOperator *op)
1855 {
1856         bGPdata *gpd = CTX_data_gpencil_data(C);
1857         bGPDpalette *palette = NULL;
1858         int palette_num = RNA_enum_get(op->ptr, "palette");
1859
1860         /* Get palette or create new one */
1861         if (palette_num == -1) {
1862                 /* Create palette */
1863                 palette = BKE_gpencil_palette_addnew(gpd, DATA_("GP_Palette"), true);
1864         }
1865         else {
1866                 /* Try to get palette */
1867                 palette = BLI_findlink(&gpd->palettes, palette_num);
1868
1869                 if (palette == NULL) {
1870                         BKE_reportf(op->reports, RPT_ERROR, "Cannot change to non-existent palette (index = %d)", palette_num);
1871                         return OPERATOR_CANCELLED;
1872                 }
1873         }
1874
1875         /* Set active palette */
1876         BKE_gpencil_palette_setactive(gpd, palette);
1877
1878         /* updates */
1879         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1880
1881         return OPERATOR_FINISHED;
1882 }
1883
1884 void GPENCIL_OT_palette_change(wmOperatorType *ot)
1885 {
1886         /* identifiers */
1887         ot->name = "Change Palette";
1888         ot->idname = "GPENCIL_OT_palette_change";
1889         ot->description = "Change active Grease Pencil palette";
1890
1891         /* callbacks */
1892         ot->invoke = gp_palette_change_invoke;
1893         ot->exec = gp_palette_change_exec;
1894         ot->poll = gp_active_palette_poll;
1895
1896         /* flags */
1897         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1898
1899         /* gp palette to use (dynamic enum) */
1900         ot->prop = RNA_def_enum(ot->srna, "palette", DummyRNA_DEFAULT_items, 0, "Grease Pencil Palette", "");
1901         RNA_def_enum_funcs(ot->prop, ED_gpencil_palettes_enum_itemf);
1902 }
1903
1904 /* ******************* Lock and hide any color non used in current layer ************************** */
1905
1906 static int gp_palette_lock_layer_exec(bContext *C, wmOperator *UNUSED(op))
1907 {
1908         bGPdata *gpd = ED_gpencil_data_get_active(C);
1909         bGPDpalette *palette;
1910
1911         /* sanity checks */
1912         if (ELEM(NULL, gpd))
1913                 return OPERATOR_CANCELLED;
1914
1915         palette = BKE_gpencil_palette_getactive(gpd);
1916         if (ELEM(NULL, palette))
1917                 return OPERATOR_CANCELLED;
1918
1919         /* first lock and hide all colors */
1920         for (bGPDpalettecolor *palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
1921                 palcolor->flag |= PC_COLOR_LOCKED;
1922                 palcolor->flag |= PC_COLOR_HIDE;
1923         }
1924
1925         /* loop all selected strokes and unlock any color used in active layer */
1926         for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
1927                 /* only editable and visible layers are considered */
1928                 if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL) && (gpl->flag & GP_LAYER_ACTIVE)) {
1929                         for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
1930                                 /* skip strokes that are invalid for current view */
1931                                 if (ED_gpencil_stroke_can_use(C, gps) == false)
1932                                         continue;
1933
1934                                 /* unlock/unhide color if not unlocked before */
1935                                 if (gps->palcolor != NULL) {
1936                                         gps->palcolor->flag &= ~PC_COLOR_LOCKED;
1937                                         gps->palcolor->flag &= ~PC_COLOR_HIDE;
1938                                 }
1939                         }
1940                 }
1941         }
1942         /* notifiers */
1943         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1944
1945         return OPERATOR_FINISHED;
1946 }
1947
1948 void GPENCIL_OT_palette_lock_layer(wmOperatorType *ot)
1949 {
1950         /* identifiers */
1951         ot->name = "Disable Unused Layer Colors";
1952         ot->idname = "GPENCIL_OT_palette_lock_layer";
1953         ot->description = "Lock and hide any color not used in any layer";
1954
1955         /* api callbacks */
1956         ot->exec = gp_palette_lock_layer_exec;
1957         ot->poll = gp_active_layer_poll;
1958 }
1959
1960 /* ************************************************ */
1961 /* Palette Colors Operators */
1962
1963 /* ******************* Add New Palette ************************ */
1964
1965 /* add new palette - wrapper around API */
1966 static int gp_palettecolor_add_exec(bContext *C, wmOperator *op)
1967 {
1968         bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
1969
1970         /* if there's no existing Grease-Pencil data there, add some */
1971         if (gpd_ptr == NULL) {
1972                 BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go");
1973                 return OPERATOR_CANCELLED;
1974         }
1975         if (*gpd_ptr == NULL)
1976                 *gpd_ptr = BKE_gpencil_data_addnew(DATA_("GPencil"));
1977
1978         /* verify palette */
1979         bGPDpalette *palette = BKE_gpencil_palette_getactive(*gpd_ptr);
1980         if (palette == NULL)
1981                 palette = BKE_gpencil_palette_addnew(*gpd_ptr, DATA_("GP_Palette"), true);
1982
1983         /* add new palette color now */
1984         BKE_gpencil_palettecolor_addnew(palette, DATA_("Color"), true);
1985
1986         /* notifiers */
1987         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1988
1989         return OPERATOR_FINISHED;
1990 }
1991
1992 void GPENCIL_OT_palettecolor_add(wmOperatorType *ot)
1993 {
1994         /* identifiers */
1995         ot->name = "Add Palette Color";
1996         ot->idname = "GPENCIL_OT_palettecolor_add";
1997         ot->description = "Add new Grease Pencil palette color for the active Grease Pencil datablock";
1998
1999         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2000
2001         /* callbacks */
2002         ot->exec = gp_palettecolor_add_exec;
2003         ot->poll = gp_add_poll;
2004 }
2005
2006 /* ******************* Remove Active Palette color ************************* */
2007
2008 static int gp_palettecolor_remove_exec(bContext *C, wmOperator *UNUSED(op))
2009 {
2010         bGPdata *gpd = ED_gpencil_data_get_active(C);
2011         bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
2012         bGPDpalettecolor *color = BKE_gpencil_palettecolor_getactive(palette);
2013
2014         /* sanity checks */
2015         if (ELEM(NULL, gpd, palette, color))
2016                 return OPERATOR_CANCELLED;
2017
2018         /* make the palette color before this the new active color
2019          * - use the one after if this is the first
2020          * - if this is the only color, this naturally becomes NULL
2021          */
2022         if (color->prev)
2023                 BKE_gpencil_palettecolor_setactive(palette, color->prev);
2024         else
2025                 BKE_gpencil_palettecolor_setactive(palette, color->next);
2026
2027         /* delete the strokes */
2028         BKE_gpencil_palettecolor_delete_strokes(gpd, color->info);
2029
2030         /* delete the palette color now... */
2031         BKE_gpencil_palettecolor_delete(palette, color);
2032
2033         /* notifiers */
2034         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
2035
2036         return OPERATOR_FINISHED;
2037 }
2038
2039 void GPENCIL_OT_palettecolor_remove(wmOperatorType *ot)
2040 {
2041         /* identifiers */
2042         ot->name = "Remove palette color";
2043         ot->idname = "GPENCIL_OT_palettecolor_remove";
2044         ot->description = "Remove active Grease Pencil palette color";
2045
2046         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2047
2048         /* callbacks */
2049         ot->exec = gp_palettecolor_remove_exec;
2050         ot->poll = gp_active_palettecolor_poll;
2051 }
2052
2053 /* ********************** Isolate palette color **************************** */
2054
2055 static int gp_isolate_palettecolor_exec(bContext *C, wmOperator *op)
2056 {
2057         bGPdata *gpd = ED_gpencil_data_get_active(C);
2058         bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
2059         bGPDpalettecolor *active_color = BKE_gpencil_palettecolor_getactive(palette);
2060         bGPDpalettecolor *palcolor;
2061
2062         int flags = PC_COLOR_LOCKED;
2063         bool isolate = false;
2064
2065         if (RNA_boolean_get(op->ptr, "affect_visibility"))
2066                 flags |= PC_COLOR_HIDE;
2067
2068         if (ELEM(NULL, gpd, active_color)) {
2069                 BKE_report(op->reports, RPT_ERROR, "No active color to isolate");
2070                 return OPERATOR_CANCELLED;
2071         }
2072
2073         /* Test whether to isolate or clear all flags */
2074         for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
2075                 /* Skip if this is the active one */
2076                 if (palcolor == active_color)
2077                         continue;
2078
2079                 /* If the flags aren't set, that means that the color is
2080                  * not alone, so we have some colors to isolate still
2081                  */
2082                 if ((palcolor->flag & flags) == 0) {
2083                         isolate = true;
2084                         break;
2085                 }
2086         }
2087
2088         /* Set/Clear flags as appropriate */
2089         if (isolate) {
2090                 /* Set flags on all "other" colors */
2091                 for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
2092                         if (palcolor == active_color)
2093                                 continue;
2094                         else
2095                                 palcolor->flag |= flags;
2096                 }
2097         }
2098         else {
2099                 /* Clear flags - Restore everything else */
2100                 for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
2101                         palcolor->flag &= ~flags;
2102                 }
2103         }
2104
2105         /* notifiers */
2106         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
2107
2108         return OPERATOR_FINISHED;
2109 }
2110
2111 void GPENCIL_OT_palettecolor_isolate(wmOperatorType *ot)
2112 {
2113         /* identifiers */
2114         ot->name = "Isolate Palette Color";
2115         ot->idname = "GPENCIL_OT_palettecolor_isolate";
2116         ot->description = "Toggle whether the active color is the only one that is editable and/or visible";
2117
2118         /* callbacks */
2119         ot->exec = gp_isolate_palettecolor_exec;
2120         ot->poll = gp_active_palettecolor_poll;
2121
2122         /* flags */
2123         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2124
2125         /* properties */
2126         RNA_def_boolean(ot->srna, "affect_visibility", false, "Affect Visibility", "In addition to toggling "
2127                         "the editability, also affect the visibility");
2128 }
2129
2130 /* *********************** Hide Palette colors ******************************** */
2131
2132 static int gp_palettecolor_hide_exec(bContext *C, wmOperator *op)
2133 {
2134         bGPdata *gpd = ED_gpencil_data_get_active(C);
2135         bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
2136         bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette);
2137
2138         bool unselected = RNA_boolean_get(op->ptr, "unselected");
2139
2140         /* sanity checks */
2141         if (ELEM(NULL, gpd, palette, palcolor))
2142                 return OPERATOR_CANCELLED;
2143
2144         if (unselected) {
2145                 bGPDpalettecolor *color;
2146
2147                 /* hide unselected */
2148                 for (color = palette->colors.first; color; color = color->next) {
2149                         if (color != palcolor) {
2150                                 color->flag |= PC_COLOR_HIDE;
2151                         }
2152                 }
2153         }
2154         else {
2155                 /* hide selected/active */
2156                 palcolor->flag |= PC_COLOR_HIDE;
2157         }
2158
2159         /* notifiers */
2160         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
2161
2162         return OPERATOR_FINISHED;
2163 }
2164
2165 void GPENCIL_OT_palettecolor_hide(wmOperatorType *ot)
2166 {
2167         /* identifiers */
2168         ot->name = "Hide Color(s)";
2169         ot->idname = "GPENCIL_OT_palettecolor_hide";
2170         ot->description = "Hide selected/unselected Grease Pencil colors";
2171
2172         /* callbacks */
2173         ot->exec = gp_palettecolor_hide_exec;
2174         ot->poll = gp_active_palettecolor_poll; /* NOTE: we need an active color to play with */
2175
2176         /* flags */
2177         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2178
2179         /* props */
2180         RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected colors");
2181 }
2182
2183 /* ********************** Show All Colors ***************************** */
2184
2185 /* poll callback for showing colors */
2186 static int gp_palettecolor_reveal_poll(bContext *C)
2187 {
2188         return ED_gpencil_data_get_active(C) != NULL;
2189 }
2190
2191 static int gp_palettecolor_reveal_exec(bContext *C, wmOperator *UNUSED(op))
2192 {
2193         bGPdata *gpd = ED_gpencil_data_get_active(C);
2194         bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
2195         bGPDpalettecolor *palcolor;
2196
2197         /* sanity checks */
2198         if (ELEM(NULL, gpd, palette))
2199                 return OPERATOR_CANCELLED;
2200
2201         /* make all colors visible */
2202         for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
2203                 palcolor->flag &= ~PC_COLOR_HIDE;
2204         }
2205
2206         /* notifiers */
2207         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
2208
2209         return OPERATOR_FINISHED;
2210 }
2211
2212 void GPENCIL_OT_palettecolor_reveal(wmOperatorType *ot)
2213 {
2214         /* identifiers */
2215         ot->name = "Show All Colors";
2216         ot->idname = "GPENCIL_OT_palettecolor_reveal";
2217         ot->description = "Unhide all hidden Grease Pencil palette colors";
2218
2219         /* callbacks */
2220         ot->exec = gp_palettecolor_reveal_exec;
2221         ot->poll = gp_palettecolor_reveal_poll;
2222
2223         /* flags */
2224         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2225 }
2226
2227 /* ***************** Lock/Unlock All Palette colors ************************ */
2228
2229 static int gp_palettecolor_lock_all_exec(bContext *C, wmOperator *UNUSED(op))
2230 {
2231         bGPdata *gpd = ED_gpencil_data_get_active(C);
2232         bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
2233         bGPDpalettecolor *palcolor;
2234
2235         /* sanity checks */
2236         if (ELEM(NULL, gpd, palette))
2237                 return OPERATOR_CANCELLED;
2238
2239         /* make all layers non-editable */
2240         for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
2241                 palcolor->flag |= PC_COLOR_LOCKED;
2242         }
2243
2244         /* notifiers */
2245         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
2246
2247         return OPERATOR_FINISHED;
2248 }
2249
2250 void GPENCIL_OT_palettecolor_lock_all(wmOperatorType *ot)
2251 {
2252         /* identifiers */
2253         ot->name = "Lock All Colors";
2254         ot->idname = "GPENCIL_OT_palettecolor_lock_all";
2255         ot->description = "Lock all Grease Pencil colors to prevent them from being accidentally modified";
2256
2257         /* callbacks */
2258         ot->exec = gp_palettecolor_lock_all_exec;
2259         ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */
2260
2261         /* flags */
2262         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2263 }
2264
2265 /* -------------------------- */
2266
2267 static int gp_palettecolor_unlock_all_exec(bContext *C, wmOperator *UNUSED(op))
2268 {
2269         bGPdata *gpd = ED_gpencil_data_get_active(C);
2270         bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
2271         bGPDpalettecolor *palcolor;
2272
2273         /* sanity checks */
2274         if (ELEM(NULL, gpd, palette))
2275                 return OPERATOR_CANCELLED;
2276
2277         /* make all layers editable again*/
2278         for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
2279                 palcolor->flag &= ~PC_COLOR_LOCKED;
2280         }
2281
2282         /* notifiers */
2283         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
2284
2285         return OPERATOR_FINISHED;
2286 }
2287
2288 void GPENCIL_OT_palettecolor_unlock_all(wmOperatorType *ot)
2289 {
2290         /* identifiers */
2291         ot->name = "Unlock All Colors";
2292         ot->idname = "GPENCIL_OT_palettecolor_unlock_all";
2293         ot->description = "Unlock all Grease Pencil colors so that they can be edited";
2294
2295         /* callbacks */
2296         ot->exec = gp_palettecolor_unlock_all_exec;
2297         ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */
2298
2299         /* flags */
2300         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2301 }
2302
2303 /* ******************* Move Color Up/Down ************************** */
2304
2305 enum {
2306         GP_COLOR_MOVE_UP = -1,
2307         GP_COLOR_MOVE_DOWN = 1
2308 };
2309
2310 static int gp_palettecolor_move_exec(bContext *C, wmOperator *op)
2311 {
2312         bGPdata *gpd = ED_gpencil_data_get_active(C);
2313         bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
2314         bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette);
2315
2316         int direction = RNA_enum_get(op->ptr, "direction");
2317
2318         /* sanity checks */
2319         if (ELEM(NULL, gpd, palette, palcolor))
2320                 return OPERATOR_CANCELLED;
2321
2322         /* up or down? */
2323         if (direction == GP_COLOR_MOVE_UP) {
2324                 /* up */
2325                 BLI_remlink(&palette->colors, palcolor);
2326                 BLI_insertlinkbefore(&palette->colors, palcolor->prev, palcolor);
2327         }
2328         else if (direction == GP_COLOR_MOVE_DOWN) {
2329                 /* down */
2330                 BLI_remlink(&palette->colors, palcolor);
2331                 BLI_insertlinkafter(&palette->colors, palcolor->next, palcolor);
2332         }
2333         else {
2334                 BLI_assert(0);
2335         }
2336
2337         /* notifiers */
2338         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
2339
2340         return OPERATOR_FINISHED;
2341 }
2342
2343 void GPENCIL_OT_palettecolor_move(wmOperatorType *ot)
2344 {
2345         static EnumPropertyItem slot_move[] = {
2346                 {GP_COLOR_MOVE_UP, "UP", 0, "Up", ""},
2347                 {GP_COLOR_MOVE_DOWN, "DOWN", 0, "Down", ""},
2348                 {0, NULL, 0, NULL, NULL}
2349         };
2350
2351         /* identifiers */
2352         ot->name = "Move Palette color";
2353         ot->idname = "GPENCIL_OT_palettecolor_move";
2354         ot->description = "Move the active Grease Pencil palette color up/down in the list";
2355
2356         /* api callbacks */
2357         ot->exec = gp_palettecolor_move_exec;
2358         ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */
2359
2360         /* flags */
2361         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2362
2363         ot->prop = RNA_def_enum(ot->srna, "direction", slot_move, GP_COLOR_MOVE_UP, "Direction", "");
2364 }
2365
2366 /* ***************** Select all strokes using Palette color ************************ */
2367
2368 static int gp_palettecolor_select_exec(bContext *C, wmOperator *UNUSED(op))
2369 {
2370         bGPdata *gpd = ED_gpencil_data_get_active(C);
2371         bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
2372         bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette);
2373
2374         /* sanity checks */
2375         if (ELEM(NULL, gpd, palette, palcolor))
2376                 return OPERATOR_CANCELLED;
2377
2378         /* read all strokes and select*/
2379         for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
2380                 /* only editable and visible layers are considered */
2381                 if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
2382                         /* verify something to do */
2383                         for (bGPDstroke *gps = gpl->actframe->strokes.first; gps; gps = gps->next) {
2384                                 /* skip strokes that are invalid for current view */
2385                                 if (ED_gpencil_stroke_can_use(C, gps) == false)
2386                                         continue;
2387                                 /* check if the color is editable */
2388                                 if (ED_gpencil_stroke_color_use(gpl, gps) == false)
2389                                         continue;
2390
2391                                 /* select */
2392                                 if (strcmp(palcolor->info, gps->colorname) == 0) {
2393                                         bGPDspoint *pt;
2394                                         int i;
2395
2396                                         gps->flag |= GP_STROKE_SELECT;
2397                                         for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
2398                                                 pt->flag |= GP_SPOINT_SELECT;
2399                                         }
2400                                 }
2401                         }
2402                 }
2403         }
2404         /* notifiers */
2405         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
2406
2407         return OPERATOR_FINISHED;
2408 }
2409
2410 void GPENCIL_OT_palettecolor_select(wmOperatorType *ot)
2411 {
2412         /* identifiers */
2413         ot->name = "Select Color";
2414         ot->idname = "GPENCIL_OT_palettecolor_select";
2415         ot->description = "Select all Grease Pencil strokes using current color";
2416
2417         /* callbacks */
2418         ot->exec = gp_palettecolor_select_exec;
2419         ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */
2420
2421         /* flags */
2422         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2423 }
2424
2425 /* ***************** Copy Palette color ************************ */
2426
2427 static int gp_palettecolor_copy_exec(bContext *C, wmOperator *UNUSED(op))
2428 {
2429         bGPdata *gpd = ED_gpencil_data_get_active(C);
2430         bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
2431         bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette);
2432         bGPDpalettecolor *newcolor;
2433
2434         /* sanity checks */
2435         if (ELEM(NULL, gpd, palette, palcolor))
2436                 return OPERATOR_CANCELLED;
2437
2438         /* create a new color and duplicate data */
2439         newcolor = BKE_gpencil_palettecolor_addnew(palette, palcolor->info, true);
2440         copy_v4_v4(newcolor->color, palcolor->color);
2441         copy_v4_v4(newcolor->fill, palcolor->fill);
2442         newcolor->flag = palcolor->flag;
2443
2444         /* notifiers */
2445         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
2446
2447         return OPERATOR_FINISHED;
2448 }
2449
2450 void GPENCIL_OT_palettecolor_copy(wmOperatorType *ot)
2451 {
2452         /* identifiers */
2453         ot->name = "Copy Color";
2454         ot->idname = "GPENCIL_OT_palettecolor_copy";
2455         ot->description = "Copy current Grease Pencil palette color";
2456
2457         /* callbacks */
2458         ot->exec = gp_palettecolor_copy_exec;
2459         ot->poll = gp_active_palettecolor_poll;
2460
2461         /* flags */
2462         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2463 }