UI Messages: Consistent spelling of term "data-block"
[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 /* ************************************************ */
79 /* Datablock Operators */
80
81 /* ******************* Add New Data ************************ */
82
83 /* add new datablock - wrapper around API */
84 static int gp_data_add_exec(bContext *C, wmOperator *op)
85 {
86         bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
87         
88         if (gpd_ptr == NULL) {
89                 BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go");
90                 return OPERATOR_CANCELLED;
91         }
92         else {
93                 /* decrement user count and add new datablock */
94                 bGPdata *gpd = (*gpd_ptr);
95                 
96                 id_us_min(&gpd->id);
97                 *gpd_ptr = BKE_gpencil_data_addnew(DATA_("GPencil"));
98         }
99         
100         /* notifiers */
101         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
102         
103         return OPERATOR_FINISHED;
104 }
105
106 void GPENCIL_OT_data_add(wmOperatorType *ot)
107 {
108         /* identifiers */
109         ot->name = "Grease Pencil Add New";
110         ot->idname = "GPENCIL_OT_data_add";
111         ot->description = "Add new Grease Pencil data-block";
112         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
113         
114         /* callbacks */
115         ot->exec = gp_data_add_exec;
116         ot->poll = gp_add_poll;
117 }
118
119 /* ******************* Unlink Data ************************ */
120
121 /* poll callback for adding data/layers - special */
122 static int gp_data_unlink_poll(bContext *C)
123 {
124         bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
125         
126         /* if we have access to some active data, make sure there's a datablock before enabling this */
127         return (gpd_ptr && *gpd_ptr);
128 }
129
130
131 /* unlink datablock - wrapper around API */
132 static int gp_data_unlink_exec(bContext *C, wmOperator *op)
133 {
134         bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
135         
136         if (gpd_ptr == NULL) {
137                 BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go");
138                 return OPERATOR_CANCELLED;
139         }
140         else {
141                 /* just unlink datablock now, decreasing its user count */
142                 bGPdata *gpd = (*gpd_ptr);
143
144                 id_us_min(&gpd->id);
145                 *gpd_ptr = NULL;
146         }
147         
148         /* notifiers */
149         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
150         
151         return OPERATOR_FINISHED;
152 }
153
154 void GPENCIL_OT_data_unlink(wmOperatorType *ot)
155 {
156         /* identifiers */
157         ot->name = "Grease Pencil Unlink";
158         ot->idname = "GPENCIL_OT_data_unlink";
159         ot->description = "Unlink active Grease Pencil data-block";
160         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
161         
162         /* callbacks */
163         ot->exec = gp_data_unlink_exec;
164         ot->poll = gp_data_unlink_poll;
165 }
166
167
168 /* ************************************************ */
169 /* Layer Operators */
170
171 /* ******************* Add New Layer ************************ */
172
173 /* add new layer - wrapper around API */
174 static int gp_layer_add_exec(bContext *C, wmOperator *op)
175 {
176         bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
177         
178         /* if there's no existing Grease-Pencil data there, add some */
179         if (gpd_ptr == NULL) {
180                 BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go");
181                 return OPERATOR_CANCELLED;
182         }
183         if (*gpd_ptr == NULL)
184                 *gpd_ptr = BKE_gpencil_data_addnew(DATA_("GPencil"));
185         
186         /* add new layer now */
187         BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("GP_Layer"), true);
188         
189         /* notifiers */
190         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
191         
192         return OPERATOR_FINISHED;
193 }
194
195 void GPENCIL_OT_layer_add(wmOperatorType *ot)
196 {
197         /* identifiers */
198         ot->name = "Add New Layer";
199         ot->idname = "GPENCIL_OT_layer_add";
200         ot->description = "Add new Grease Pencil layer for the active Grease Pencil data-block";
201         
202         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
203         
204         /* callbacks */
205         ot->exec = gp_layer_add_exec;
206         ot->poll = gp_add_poll;
207 }
208
209 /* ******************* Remove Active Layer ************************* */
210
211 static int gp_layer_remove_exec(bContext *C, wmOperator *op)
212 {
213         bGPdata *gpd = ED_gpencil_data_get_active(C);
214         bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
215         
216         /* sanity checks */
217         if (ELEM(NULL, gpd, gpl))
218                 return OPERATOR_CANCELLED;
219         
220         if (gpl->flag & GP_LAYER_LOCKED) {
221                 BKE_report(op->reports, RPT_ERROR, "Cannot delete locked layers");
222                 return OPERATOR_CANCELLED;
223         }
224         
225         /* make the layer before this the new active layer
226          * - use the one after if this is the first
227          * - if this is the only layer, this naturally becomes NULL
228          */
229         if (gpl->prev)
230                 BKE_gpencil_layer_setactive(gpd, gpl->prev);
231         else
232                 BKE_gpencil_layer_setactive(gpd, gpl->next);
233         
234         /* delete the layer now... */
235         BKE_gpencil_layer_delete(gpd, gpl);
236         
237         /* notifiers */
238         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
239         
240         return OPERATOR_FINISHED;
241 }
242
243 void GPENCIL_OT_layer_remove(wmOperatorType *ot)
244 {
245         /* identifiers */
246         ot->name = "Remove Layer";
247         ot->idname = "GPENCIL_OT_layer_remove";
248         ot->description = "Remove active Grease Pencil layer";
249         
250         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
251         
252         /* callbacks */
253         ot->exec = gp_layer_remove_exec;
254         ot->poll = gp_active_layer_poll;
255 }
256
257 /* ******************* Move Layer Up/Down ************************** */
258
259 enum {
260         GP_LAYER_MOVE_UP   = -1,
261         GP_LAYER_MOVE_DOWN = 1
262 };
263
264 static int gp_layer_move_exec(bContext *C, wmOperator *op)
265 {
266         bGPdata *gpd = ED_gpencil_data_get_active(C);
267         bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
268         
269         int direction = RNA_enum_get(op->ptr, "type");
270         
271         /* sanity checks */
272         if (ELEM(NULL, gpd, gpl))
273                 return OPERATOR_CANCELLED;
274         
275         BLI_assert(ELEM(direction, -1, 0, 1)); /* we use value below */
276         if (BLI_listbase_link_move(&gpd->layers, gpl, direction)) {
277                 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
278         }
279         
280         return OPERATOR_FINISHED;
281 }
282
283 void GPENCIL_OT_layer_move(wmOperatorType *ot)
284 {
285         static EnumPropertyItem slot_move[] = {
286                 {GP_LAYER_MOVE_UP, "UP", 0, "Up", ""},
287                 {GP_LAYER_MOVE_DOWN, "DOWN", 0, "Down", ""},
288                 {0, NULL, 0, NULL, NULL}
289         };
290         
291         /* identifiers */
292         ot->name = "Move Grease Pencil Layer";
293         ot->idname = "GPENCIL_OT_layer_move";
294         ot->description = "Move the active Grease Pencil layer up/down in the list";
295         
296         /* api callbacks */
297         ot->exec = gp_layer_move_exec;
298         ot->poll = gp_active_layer_poll;
299         
300         /* flags */
301         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
302         
303         ot->prop = RNA_def_enum(ot->srna, "type", slot_move, 0, "Type", "");
304 }
305
306 /* ********************* Duplicate Layer ************************** */
307
308 static int gp_layer_copy_exec(bContext *C, wmOperator *UNUSED(op))
309 {
310         bGPdata *gpd = ED_gpencil_data_get_active(C);
311         bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
312         bGPDlayer *new_layer;
313         
314         /* sanity checks */
315         if (ELEM(NULL, gpd, gpl))
316                 return OPERATOR_CANCELLED;
317         
318         /* make copy of layer, and add it immediately after the existing layer */
319         new_layer = BKE_gpencil_layer_duplicate(gpl);
320         BLI_insertlinkafter(&gpd->layers, gpl, new_layer);
321         
322         /* ensure new layer has a unique name, and is now the active layer */
323         BLI_uniquename(&gpd->layers, new_layer, DATA_("GP_Layer"), '.', offsetof(bGPDlayer, info), sizeof(new_layer->info));
324         BKE_gpencil_layer_setactive(gpd, new_layer);
325         
326         /* notifiers */
327         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
328         
329         return OPERATOR_FINISHED;
330 }
331
332 void GPENCIL_OT_layer_duplicate(wmOperatorType *ot)
333 {
334         /* identifiers */
335         ot->name = "Duplicate Layer";
336         ot->idname = "GPENCIL_OT_layer_duplicate";
337         ot->description = "Make a copy of the active Grease Pencil layer";
338         
339         /* callbacks */
340         ot->exec = gp_layer_copy_exec;
341         ot->poll = gp_active_layer_poll;
342         
343         /* flags */
344         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
345 }
346
347 /* *********************** Hide Layers ******************************** */
348
349 static int gp_hide_exec(bContext *C, wmOperator *op)
350 {
351         bGPdata *gpd = ED_gpencil_data_get_active(C);
352         bGPDlayer *layer = BKE_gpencil_layer_getactive(gpd);
353         bool unselected = RNA_boolean_get(op->ptr, "unselected");
354         
355         /* sanity checks */
356         if (ELEM(NULL, gpd, layer))
357                 return OPERATOR_CANCELLED;
358         
359         if (unselected) {
360                 bGPDlayer *gpl;
361                 
362                 /* hide unselected */
363                 for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
364                         if (gpl != layer) {
365                                 gpl->flag |= GP_LAYER_HIDE;
366                         }
367                 }
368         }
369         else {
370                 /* hide selected/active */
371                 layer->flag |= GP_LAYER_HIDE;
372         }
373         
374         /* notifiers */
375         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
376         
377         return OPERATOR_FINISHED;
378 }
379
380 void GPENCIL_OT_hide(wmOperatorType *ot)
381 {
382         /* identifiers */
383         ot->name = "Hide Layer(s)";
384         ot->idname = "GPENCIL_OT_hide";
385         ot->description = "Hide selected/unselected Grease Pencil layers";
386         
387         /* callbacks */
388         ot->exec = gp_hide_exec;
389         ot->poll = gp_active_layer_poll; /* NOTE: we need an active layer to play with */
390         
391         /* flags */
392         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
393         
394         /* props */
395         RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected layers");
396 }
397
398 /* ********************** Show All Layers ***************************** */
399
400 /* poll callback for showing layers */
401 static int gp_reveal_poll(bContext *C)
402 {
403         return ED_gpencil_data_get_active(C) != NULL;
404 }
405
406 static int gp_reveal_exec(bContext *C, wmOperator *UNUSED(op))
407 {
408         bGPdata *gpd = ED_gpencil_data_get_active(C);
409         bGPDlayer *gpl;
410         
411         /* sanity checks */
412         if (gpd == NULL)
413                 return OPERATOR_CANCELLED;
414         
415         /* make all layers visible */
416         for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
417                 gpl->flag &= ~GP_LAYER_HIDE;
418         }
419         
420         /* notifiers */
421         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
422         
423         return OPERATOR_FINISHED;
424 }
425
426 void GPENCIL_OT_reveal(wmOperatorType *ot)
427 {
428         /* identifiers */
429         ot->name = "Show All Layers";
430         ot->idname = "GPENCIL_OT_reveal";
431         ot->description = "Show all Grease Pencil layers";
432         
433         /* callbacks */
434         ot->exec = gp_reveal_exec;
435         ot->poll = gp_reveal_poll;
436         
437         /* flags */
438         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
439 }
440
441 /* ***************** Lock/Unlock All Layers ************************ */
442
443 static int gp_lock_all_exec(bContext *C, wmOperator *UNUSED(op))
444 {
445         bGPdata *gpd = ED_gpencil_data_get_active(C);
446         bGPDlayer *gpl;
447         
448         /* sanity checks */
449         if (gpd == NULL)
450                 return OPERATOR_CANCELLED;
451         
452         /* make all layers non-editable */
453         for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
454                 gpl->flag |= GP_LAYER_LOCKED;
455         }
456         
457         /* notifiers */
458         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
459         
460         return OPERATOR_FINISHED;
461 }
462
463 void GPENCIL_OT_lock_all(wmOperatorType *ot)
464 {
465         /* identifiers */
466         ot->name = "Lock All Layers";
467         ot->idname = "GPENCIL_OT_lock_all";
468         ot->description = "Lock all Grease Pencil layers to prevent them from being accidentally modified";
469         
470         /* callbacks */
471         ot->exec = gp_lock_all_exec;
472         ot->poll = gp_reveal_poll; /* XXX: could use dedicated poll later */
473         
474         /* flags */
475         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
476 }
477
478 /* -------------------------- */
479
480 static int gp_unlock_all_exec(bContext *C, wmOperator *UNUSED(op))
481 {
482         bGPdata *gpd = ED_gpencil_data_get_active(C);
483         bGPDlayer *gpl;
484         
485         /* sanity checks */
486         if (gpd == NULL)
487                 return OPERATOR_CANCELLED;
488         
489         /* make all layers editable again */
490         for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
491                 gpl->flag &= ~GP_LAYER_LOCKED;
492         }
493         
494         /* notifiers */
495         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
496         
497         return OPERATOR_FINISHED;
498 }
499
500 void GPENCIL_OT_unlock_all(wmOperatorType *ot)
501 {
502         /* identifiers */
503         ot->name = "Unlock All Layers";
504         ot->idname = "GPENCIL_OT_unlock_all";
505         ot->description = "Unlock all Grease Pencil layers so that they can be edited";
506         
507         /* callbacks */
508         ot->exec = gp_unlock_all_exec;
509         ot->poll = gp_reveal_poll; /* XXX: could use dedicated poll later */
510         
511         /* flags */
512         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
513 }
514
515 /* ********************** Isolate Layer **************************** */
516
517 static int gp_isolate_layer_exec(bContext *C, wmOperator *op)
518 {
519         bGPdata *gpd = ED_gpencil_data_get_active(C);
520         bGPDlayer *layer = BKE_gpencil_layer_getactive(gpd);
521         bGPDlayer *gpl;
522         int flags = GP_LAYER_LOCKED;
523         bool isolate = false;
524         
525         if (RNA_boolean_get(op->ptr, "affect_visibility"))
526                 flags |= GP_LAYER_HIDE;
527                 
528         if (ELEM(NULL, gpd, layer)) {
529                 BKE_report(op->reports, RPT_ERROR, "No active layer to isolate");
530                 return OPERATOR_CANCELLED;
531         }
532         
533         /* Test whether to isolate or clear all flags */
534         for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
535                 /* Skip if this is the active layer */
536                 if (gpl == layer)
537                         continue;
538                 
539                 /* If the flags aren't set, that means that the layer is
540                  * not alone, so we have some layers to isolate still
541                  */
542                 if ((gpl->flag & flags) == 0) {
543                         isolate = true;
544                         break;
545                 }
546         }
547         
548         /* Set/Clear flags as appropriate */
549         /* TODO: Include onionskinning on this list? */
550         if (isolate) {
551                 /* Set flags on all "other" layers */
552                 for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
553                         if (gpl == layer)
554                                 continue;
555                         else
556                                 gpl->flag |= flags;
557                 }
558         }
559         else {
560                 /* Clear flags - Restore everything else */
561                 for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
562                         gpl->flag &= ~flags;
563                 }
564         }
565         
566         /* notifiers */
567         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
568         
569         return OPERATOR_FINISHED;
570 }
571
572 void GPENCIL_OT_layer_isolate(wmOperatorType *ot)
573 {
574         /* identifiers */
575         ot->name = "Isolate Layer";
576         ot->idname = "GPENCIL_OT_layer_isolate";
577         ot->description = "Toggle whether the active layer is the only one that can be edited and/or visible";
578         
579         /* callbacks */
580         ot->exec = gp_isolate_layer_exec;
581         ot->poll = gp_active_layer_poll;
582         
583         /* flags */
584         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
585         
586         /* properties */
587         RNA_def_boolean(ot->srna, "affect_visibility", false, "Affect Visibility",
588                         "In addition to toggling the editability, also affect the visibility");
589 }
590
591 /* ********************** Merge Layer with the next layer **************************** */
592
593 static int gp_merge_layer_exec(bContext *C, wmOperator *op)
594 {
595         bGPdata *gpd = ED_gpencil_data_get_active(C);
596         bGPDlayer *gpl_current = BKE_gpencil_layer_getactive(gpd);
597         bGPDlayer *gpl_next = gpl_current->next;
598
599         if (ELEM(NULL, gpd, gpl_current, gpl_next)) {
600                 BKE_report(op->reports, RPT_ERROR, "No layers to merge");
601                 return OPERATOR_CANCELLED;
602         }
603
604         /* Collect frames of gpl_current in hash table to avoid O(n^2) lookups */
605         GHash *gh_frames_cur = BLI_ghash_int_new_ex(__func__, 64);
606         for (bGPDframe *gpf = gpl_current->frames.first; gpf; gpf = gpf->next) {
607                 BLI_ghash_insert(gh_frames_cur, SET_INT_IN_POINTER(gpf->framenum), gpf);
608         }
609
610         /* read all frames from next layer */
611         for (bGPDframe *gpf = gpl_next->frames.first; gpf; gpf = gpf->next) {
612                 /* try to find frame in active layer */
613                 bGPDframe *frame = BLI_ghash_lookup(gh_frames_cur, SET_INT_IN_POINTER(gpf->framenum));
614                 if (!frame) {
615                         /* nothing found, create new */
616                         frame = BKE_gpencil_frame_addnew(gpl_current, gpf->framenum);
617                 }
618                 /* add to tail all strokes */
619                 BLI_movelisttolist(&frame->strokes, &gpf->strokes);
620         }
621         /* Now delete next layer */
622         BKE_gpencil_layer_delete(gpd, gpl_next);
623         BLI_ghash_free(gh_frames_cur, NULL, NULL);
624
625         /* notifiers */
626         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
627
628         return OPERATOR_FINISHED;
629 }
630
631 void GPENCIL_OT_layer_merge(wmOperatorType *ot)
632 {
633         /* identifiers */
634         ot->name = "Merge Down";
635         ot->idname = "GPENCIL_OT_layer_merge";
636         ot->description = "Merge the current layer with the layer below";
637
638         /* callbacks */
639         ot->exec = gp_merge_layer_exec;
640         ot->poll = gp_active_layer_poll;
641
642         /* flags */
643         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
644 }
645
646 /* ********************** Change Layer ***************************** */
647
648 static int gp_layer_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt))
649 {
650         uiPopupMenu *pup;
651         uiLayout *layout;
652         
653         /* call the menu, which will call this operator again, hence the canceled */
654         pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE);
655         layout = UI_popup_menu_layout(pup);
656         uiItemsEnumO(layout, "GPENCIL_OT_layer_change", "layer");
657         UI_popup_menu_end(C, pup);
658         
659         return OPERATOR_INTERFACE;
660 }
661
662 static int gp_layer_change_exec(bContext *C, wmOperator *op)
663 {
664         bGPdata *gpd = CTX_data_gpencil_data(C);
665         bGPDlayer *gpl = NULL;
666         int layer_num = RNA_enum_get(op->ptr, "layer");
667         
668         /* Get layer or create new one */
669         if (layer_num == -1) {
670                 /* Create layer */
671                 gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
672         }
673         else {
674                 /* Try to get layer */
675                 gpl = BLI_findlink(&gpd->layers, layer_num);
676                 
677                 if (gpl == NULL) {
678                         BKE_reportf(op->reports, RPT_ERROR, "Cannot change to non-existent layer (index = %d)", layer_num);
679                         return OPERATOR_CANCELLED;
680                 }
681         }
682         
683         /* Set active layer */
684         BKE_gpencil_layer_setactive(gpd, gpl);
685         
686         /* updates */
687         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
688         
689         return OPERATOR_FINISHED;
690 }
691
692 void GPENCIL_OT_layer_change(wmOperatorType *ot)
693 {
694         /* identifiers */
695         ot->name = "Change Layer";
696         ot->idname = "GPENCIL_OT_layer_change";
697         ot->description = "Change active Grease Pencil layer";
698         
699         /* callbacks */
700         ot->invoke = gp_layer_change_invoke;
701         ot->exec = gp_layer_change_exec;
702         ot->poll = gp_active_layer_poll;
703         
704         /* flags */
705         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
706         
707         /* gp layer to use (dynamic enum) */
708         ot->prop = RNA_def_enum(ot->srna, "layer", DummyRNA_DEFAULT_items, 0, "Grease Pencil Layer", "");
709         RNA_def_enum_funcs(ot->prop, ED_gpencil_layers_with_new_enum_itemf);
710 }
711
712 /* ************************************************ */
713
714 /* ******************* Arrange Stroke Up/Down in drawing order ************************** */
715
716 enum {
717         GP_STROKE_MOVE_UP = -1,
718         GP_STROKE_MOVE_DOWN = 1,
719         GP_STROKE_MOVE_TOP = 2,
720         GP_STROKE_MOVE_BOTTOM = 3
721 };
722
723 static int gp_stroke_arrange_exec(bContext *C, wmOperator *op)
724 {
725         bGPdata *gpd = ED_gpencil_data_get_active(C);
726         bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
727         bGPDstroke *gps;
728
729         /* sanity checks */
730         if (ELEM(NULL, gpd, gpl, gpl->actframe)) {
731                 return OPERATOR_CANCELLED;
732         }
733
734         bGPDframe *gpf = gpl->actframe;
735         /* temp listbase to store selected strokes */
736         ListBase selected = {NULL};
737         const int direction = RNA_enum_get(op->ptr, "direction");
738
739         /* verify if any selected stroke is in the extreme of the stack and select to move */
740         for (gps = gpf->strokes.first; gps; gps = gps->next) {
741                 /* only if selected */
742                 if (gps->flag & GP_STROKE_SELECT) {
743                         /* skip strokes that are invalid for current view */
744                         if (ED_gpencil_stroke_can_use(C, gps) == false) {
745                                 continue;
746                         }
747                         /* check if the color is editable */
748                         if (ED_gpencil_stroke_color_use(gpl, gps) == false) {
749                                 continue;
750                         }
751                         /* some stroke is already at front*/
752                         if ((direction == GP_STROKE_MOVE_TOP) || (direction == GP_STROKE_MOVE_UP)) {
753                                 if (gps == gpf->strokes.last) {
754                                         return OPERATOR_CANCELLED;
755                                 }
756                         }
757                         /* some stroke is already at botom */
758                         if ((direction == GP_STROKE_MOVE_BOTTOM) || (direction == GP_STROKE_MOVE_DOWN)) {
759                                 if (gps == gpf->strokes.first) {
760                                         return OPERATOR_CANCELLED;
761                                 }
762                         }
763                         /* add to list */
764                         BLI_addtail(&selected, BLI_genericNodeN(gps));
765                 }
766         }
767
768         /* Now do the movement of the stroke */
769         switch (direction) {
770                 /* Bring to Front */
771                 case GP_STROKE_MOVE_TOP:
772                         for (LinkData *link = selected.first; link; link = link->next) {
773                                 gps = link->data;
774                                 BLI_remlink(&gpf->strokes, gps);
775                                 BLI_addtail(&gpf->strokes, gps);
776                         }
777                         break;
778                 /* Bring Forward */
779                 case GP_STROKE_MOVE_UP:
780                         for (LinkData *link = selected.last; link; link = link->prev) {
781                                 gps = link->data;
782                                 BLI_listbase_link_move(&gpf->strokes, gps, 1);
783                         }
784                         break;
785                 /* Send Backward */
786                 case GP_STROKE_MOVE_DOWN:
787                         for (LinkData *link = selected.first; link; link = link->next) {
788                                 gps = link->data;
789                                 BLI_listbase_link_move(&gpf->strokes, gps, -1);
790                         }
791                         break;
792                 /* Send to Back */
793                 case GP_STROKE_MOVE_BOTTOM:
794                         for (LinkData *link = selected.last; link; link = link->prev) {
795                                 gps = link->data;
796                                 BLI_remlink(&gpf->strokes, gps);
797                                 BLI_addhead(&gpf->strokes, gps);
798                         }
799                         break;
800                 default:
801                         BLI_assert(0);
802                         break;
803         }
804         BLI_freelistN(&selected);
805
806         /* notifiers */
807         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
808
809         return OPERATOR_FINISHED;
810 }
811
812 void GPENCIL_OT_stroke_arrange(wmOperatorType *ot)
813 {
814         static EnumPropertyItem slot_move[] = {
815                 {GP_STROKE_MOVE_UP, "UP", 0, "Bring Forward", ""},
816                 {GP_STROKE_MOVE_DOWN, "DOWN", 0, "Send Backward", ""},
817                 {GP_STROKE_MOVE_TOP, "TOP", 0, "Bring to Front", ""},
818                 {GP_STROKE_MOVE_BOTTOM, "BOTTOM", 0, "Send to Back", ""},
819                 {0, NULL, 0, NULL, NULL }
820         };
821
822         /* identifiers */
823         ot->name = "Arrange Stroke";
824         ot->idname = "GPENCIL_OT_stroke_arrange";
825         ot->description = "Arrange selected strokes up/down in the drawing order of the active layer";
826
827         /* api callbacks */
828         ot->exec = gp_stroke_arrange_exec;
829         ot->poll = gp_active_layer_poll;
830
831         /* flags */
832         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
833
834         ot->prop = RNA_def_enum(ot->srna, "direction", slot_move, GP_STROKE_MOVE_UP, "Direction", "");
835 }
836 /* ******************* Move Stroke to new color ************************** */
837
838 static int gp_stroke_change_color_exec(bContext *C, wmOperator *UNUSED(op))
839 {
840         bGPdata *gpd = ED_gpencil_data_get_active(C);
841         bGPDpalette *palette;
842         bGPDpalettecolor *color;
843
844         /* sanity checks */
845         if (ELEM(NULL, gpd)) {
846                 return OPERATOR_CANCELLED;
847         }
848
849         palette = BKE_gpencil_palette_getactive(gpd);
850         color = BKE_gpencil_palettecolor_getactive(palette);
851         if (ELEM(NULL, palette, color)) {
852                 return OPERATOR_CANCELLED;
853         }
854
855         /* loop all strokes */
856         for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
857                 /* only editable and visible layers are considered */
858                 if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
859                         for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
860                                 /* only if selected */
861                                 if (gps->flag & GP_STROKE_SELECT) {
862                                         /* skip strokes that are invalid for current view */
863                                         if (ED_gpencil_stroke_can_use(C, gps) == false)
864                                                 continue;
865                                         /* check if the color is editable */
866                                         if (ED_gpencil_stroke_color_use(gpl, gps) == false)
867                                                 continue;
868
869                                         /* asign new color (only if different) */
870                                         if ((STREQ(gps->colorname, color->info) == false) || (gps->palcolor != color)) {
871                                                 BLI_strncpy(gps->colorname, color->info, sizeof(gps->colorname));
872                                                 gps->palcolor = color;
873                                         }
874                                 }
875                         }
876                 }
877         }
878         /* notifiers */
879         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
880
881         return OPERATOR_FINISHED;
882 }
883
884 void GPENCIL_OT_stroke_change_color(wmOperatorType *ot)
885 {
886         /* identifiers */
887         ot->name = "Change Stroke Color";
888         ot->idname = "GPENCIL_OT_stroke_change_color";
889         ot->description = "Move selected strokes to active color";
890
891         /* api callbacks */
892         ot->exec = gp_stroke_change_color_exec;
893         ot->poll = gp_active_layer_poll;
894 }
895
896 /* ******************* Lock color of non selected Strokes colors ************************** */
897
898 static int gp_stroke_lock_color_exec(bContext *C, wmOperator *UNUSED(op))
899 {
900         bGPdata *gpd = ED_gpencil_data_get_active(C);
901         bGPDpalette *palette;
902
903         /* sanity checks */
904         if (ELEM(NULL, gpd))
905                 return OPERATOR_CANCELLED;
906
907         palette = BKE_gpencil_palette_getactive(gpd);
908         if (ELEM(NULL, palette))
909                 return OPERATOR_CANCELLED;
910
911         /* first lock all colors */
912         for (bGPDpalettecolor *palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
913                 palcolor->flag |= PC_COLOR_LOCKED;
914         }
915
916         /* loop all selected strokes and unlock any color */
917         for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
918                 /* only editable and visible layers are considered */
919                 if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
920                         for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
921                                 /* only if selected */
922                                 if (gps->flag & GP_STROKE_SELECT) {
923                                         /* skip strokes that are invalid for current view */
924                                         if (ED_gpencil_stroke_can_use(C, gps) == false) {
925                                                 continue;
926                                         }
927                                         /* unlock color */
928                                         if (gps->palcolor != NULL) {
929                                                 gps->palcolor->flag &= ~PC_COLOR_LOCKED;
930                                         }
931                                 }
932                         }
933                 }
934         }
935         /* notifiers */
936         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
937
938         return OPERATOR_FINISHED;
939 }
940
941 void GPENCIL_OT_stroke_lock_color(wmOperatorType *ot)
942 {
943         /* identifiers */
944         ot->name = "Lock Unused Colors";
945         ot->idname = "GPENCIL_OT_stroke_lock_color";
946         ot->description = "Lock any color not used in any selected stroke";
947
948         /* api callbacks */
949         ot->exec = gp_stroke_lock_color_exec;
950         ot->poll = gp_active_layer_poll;
951 }
952
953 /* ************************************************ */
954 /* Drawing Brushes Operators */
955
956 /* ******************* Add New Brush ************************ */
957
958 /* add new brush - wrapper around API */
959 static int gp_brush_add_exec(bContext *C, wmOperator *op)
960 {
961         ToolSettings *ts = CTX_data_tool_settings(C);
962
963         /* if there's no existing container */
964         if (ts == NULL) {
965                 BKE_report(op->reports, RPT_ERROR, "Nowhere for brush data to go");
966                 return OPERATOR_CANCELLED;
967         }
968         /* add new brush now */
969         BKE_gpencil_brush_addnew(ts, DATA_("GP_Brush"), true);
970
971         /* notifiers */
972         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
973
974         return OPERATOR_FINISHED;
975 }
976
977 void GPENCIL_OT_brush_add(wmOperatorType *ot)
978 {
979         /* identifiers */
980         ot->name = "Add Brush";
981         ot->idname = "GPENCIL_OT_brush_add";
982         ot->description = "Add new Grease Pencil drawing brush for the active Grease Pencil data-block";
983
984         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
985
986         /* callbacks */
987         ot->exec = gp_brush_add_exec;
988         ot->poll = gp_add_poll;
989 }
990
991 /* ******************* Remove Active Brush ************************* */
992
993 static int gp_brush_remove_exec(bContext *C, wmOperator *op)
994 {
995         ToolSettings *ts = CTX_data_tool_settings(C);
996         bGPDbrush *brush = BKE_gpencil_brush_getactive(ts);
997
998         /* sanity checks */
999         if (ELEM(NULL, ts, brush))
1000                 return OPERATOR_CANCELLED;
1001
1002         if (BLI_listbase_count_ex(&ts->gp_brushes, 2) < 2) {
1003                 BKE_report(op->reports, RPT_ERROR, "Grease Pencil needs a brush, unable to delete the last one");
1004                 return OPERATOR_CANCELLED;
1005         }
1006
1007
1008         /* make the brush before this the new active brush
1009          * - use the one after if this is the first
1010          * - if this is the only brush, this naturally becomes NULL
1011          */
1012         if (brush->prev)
1013                 BKE_gpencil_brush_setactive(ts, brush->prev);
1014         else
1015                 BKE_gpencil_brush_setactive(ts, brush->next);
1016
1017         /* delete the brush now... */
1018         BKE_gpencil_brush_delete(ts, brush);
1019
1020         /* notifiers */
1021         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1022
1023         return OPERATOR_FINISHED;
1024 }
1025
1026 void GPENCIL_OT_brush_remove(wmOperatorType *ot)
1027 {
1028         /* identifiers */
1029         ot->name = "Remove Brush";
1030         ot->idname = "GPENCIL_OT_brush_remove";
1031         ot->description = "Remove active Grease Pencil drawing brush";
1032
1033         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1034
1035         /* callbacks */
1036         ot->exec = gp_brush_remove_exec;
1037         ot->poll = gp_active_brush_poll;
1038 }
1039
1040 /* ********************** Change Brush ***************************** */
1041
1042 static int gp_brush_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt))
1043 {
1044         uiPopupMenu *pup;
1045         uiLayout *layout;
1046
1047         /* call the menu, which will call this operator again, hence the canceled */
1048         pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE);
1049         layout = UI_popup_menu_layout(pup);
1050         uiItemsEnumO(layout, "GPENCIL_OT_brush_change", "brush");
1051         UI_popup_menu_end(C, pup);
1052
1053         return OPERATOR_INTERFACE;
1054 }
1055
1056 static int gp_brush_change_exec(bContext *C, wmOperator *op)
1057 {
1058         ToolSettings *ts = CTX_data_tool_settings(C);
1059         bGPDbrush *brush = NULL;
1060         int brush_num = RNA_enum_get(op->ptr, "brush");
1061
1062         /* Get brush or create new one */
1063         if (brush_num == -1) {
1064                 /* Create brush */
1065                 brush = BKE_gpencil_brush_addnew(ts, DATA_("GP_Brush"), true);
1066         }
1067         else {
1068                 /* Try to get brush */
1069                 brush = BLI_findlink(&ts->gp_brushes, brush_num);
1070
1071                 if (brush == NULL) {
1072                         BKE_reportf(op->reports, RPT_ERROR, "Cannot change to non-existent brush (index = %d)", brush_num);
1073                         return OPERATOR_CANCELLED;
1074                 }
1075         }
1076
1077         /* Set active brush */
1078         BKE_gpencil_brush_setactive(ts, brush);
1079
1080         /* updates */
1081         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1082
1083         return OPERATOR_FINISHED;
1084 }
1085
1086 void GPENCIL_OT_brush_change(wmOperatorType *ot)
1087 {
1088         /* identifiers */
1089         ot->name = "Change Brush";
1090         ot->idname = "GPENCIL_OT_brush_change";
1091         ot->description = "Change active Grease Pencil drawing brush";
1092
1093         /* callbacks */
1094         ot->invoke = gp_brush_change_invoke;
1095         ot->exec = gp_brush_change_exec;
1096         ot->poll = gp_active_brush_poll;
1097
1098         /* flags */
1099         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1100
1101         /* gp brush to use (dynamic enum) */
1102         ot->prop = RNA_def_enum(ot->srna, "brush", DummyRNA_DEFAULT_items, 0, "Grease Pencil Brush", "");
1103         RNA_def_enum_funcs(ot->prop, ED_gpencil_brushes_enum_itemf);
1104 }
1105
1106 /* ******************* Move Brush Up/Down ************************** */
1107
1108 enum {
1109         GP_BRUSH_MOVE_UP = -1,
1110         GP_BRUSH_MOVE_DOWN = 1
1111 };
1112
1113 static int gp_brush_move_exec(bContext *C, wmOperator *op)
1114 {
1115         ToolSettings *ts = CTX_data_tool_settings(C);
1116         bGPDbrush *brush = BKE_gpencil_brush_getactive(ts);
1117
1118         int direction = RNA_enum_get(op->ptr, "type");
1119
1120         /* sanity checks */
1121         if (ELEM(NULL, ts, brush)) {
1122                 return OPERATOR_CANCELLED;
1123         }
1124
1125         /* up or down? */
1126         if (direction == GP_BRUSH_MOVE_UP) {
1127                 /* up */
1128                 BLI_remlink(&ts->gp_brushes, brush);
1129                 BLI_insertlinkbefore(&ts->gp_brushes, brush->prev, brush);
1130         }
1131         else if (direction == GP_BRUSH_MOVE_DOWN) {
1132                 /* down */
1133                 BLI_remlink(&ts->gp_brushes, brush);
1134                 BLI_insertlinkafter(&ts->gp_brushes, brush->next, brush);
1135         }
1136         else {
1137                 BLI_assert(0);
1138         }
1139
1140         /* notifiers */
1141         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1142
1143         return OPERATOR_FINISHED;
1144 }
1145
1146 void GPENCIL_OT_brush_move(wmOperatorType *ot)
1147 {
1148         static EnumPropertyItem slot_move[] = {
1149                 {GP_BRUSH_MOVE_UP, "UP", 0, "Up", ""},
1150                 {GP_BRUSH_MOVE_DOWN, "DOWN", 0, "Down", ""},
1151                 {0, NULL, 0, NULL, NULL }
1152         };
1153
1154         /* identifiers */
1155         ot->name = "Move Brush";
1156         ot->idname = "GPENCIL_OT_brush_move";
1157         ot->description = "Move the active Grease Pencil drawing brush up/down in the list";
1158
1159         /* api callbacks */
1160         ot->exec = gp_brush_move_exec;
1161         ot->poll = gp_active_brush_poll;
1162
1163         /* flags */
1164         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1165
1166         ot->prop = RNA_def_enum(ot->srna, "type", slot_move, GP_BRUSH_MOVE_UP, "Type", "");
1167 }
1168
1169 /* ******************* Brush create presets ************************** */
1170
1171 static int gp_brush_presets_create_exec(bContext *C, wmOperator *UNUSED(op))
1172 {
1173         ToolSettings *ts = CTX_data_tool_settings(C);
1174         BKE_gpencil_brush_init_presets(ts);
1175
1176         /* notifiers */
1177         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1178
1179         return OPERATOR_FINISHED;
1180 }
1181
1182 void GPENCIL_OT_brush_presets_create(wmOperatorType *ot)
1183 {
1184         /* identifiers */
1185         ot->name = "Create Preset Brushes";
1186         ot->idname = "GPENCIL_OT_brush_presets_create";
1187         ot->description = "Create a set of predefined Grease Pencil drawing brushes";
1188
1189         /* api callbacks */
1190         ot->exec = gp_brush_presets_create_exec;
1191
1192         /* flags */
1193         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1194
1195 }
1196
1197 /* ***************** Copy Brush ************************ */
1198
1199 static int gp_brush_copy_exec(bContext *C, wmOperator *op)
1200 {
1201         ToolSettings *ts = CTX_data_tool_settings(C);
1202
1203         /* if there's no existing container */
1204         if (ts == NULL) {
1205                 BKE_report(op->reports, RPT_ERROR, "Nowhere for brush data to go");
1206                 return OPERATOR_CANCELLED;
1207         }
1208
1209         bGPDbrush *brush = BKE_gpencil_brush_getactive(ts);
1210         bGPDbrush *newbrush;
1211
1212         /* sanity checks */
1213         if (ELEM(NULL, brush))
1214                 return OPERATOR_CANCELLED;
1215
1216         /* create a brush and duplicate data */
1217         newbrush = BKE_gpencil_brush_addnew(ts, brush->info, true);
1218         newbrush->thickness = brush->thickness;
1219         newbrush->draw_smoothfac = brush->draw_smoothfac;
1220         newbrush->draw_smoothlvl = brush->draw_smoothlvl;
1221         newbrush->sublevel = brush->sublevel;
1222         newbrush->flag = brush->flag;
1223         newbrush->draw_sensitivity = brush->draw_sensitivity;
1224         newbrush->draw_strength = brush->draw_strength;
1225         newbrush->draw_jitter = brush->draw_jitter;
1226         newbrush->draw_angle = brush->draw_angle;
1227         newbrush->draw_angle_factor = brush->draw_angle_factor;
1228         newbrush->draw_random_press = brush->draw_random_press;
1229         newbrush->draw_random_sub = brush->draw_random_sub;
1230
1231         /* free automatic curves created by default (replaced by copy) */
1232         curvemapping_free(newbrush->cur_sensitivity);
1233         curvemapping_free(newbrush->cur_strength);
1234         curvemapping_free(newbrush->cur_jitter);
1235
1236         /* make a copy of curves */
1237         newbrush->cur_sensitivity = curvemapping_copy(brush->cur_sensitivity);
1238         newbrush->cur_strength = curvemapping_copy(brush->cur_strength);
1239         newbrush->cur_jitter = curvemapping_copy(brush->cur_jitter);
1240
1241         BKE_gpencil_brush_setactive(ts, newbrush);
1242         /* notifiers */
1243         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1244
1245         return OPERATOR_FINISHED;
1246 }
1247
1248 void GPENCIL_OT_brush_copy(wmOperatorType *ot)
1249 {
1250         /* identifiers */
1251         ot->name = "Copy Brush";
1252         ot->idname = "GPENCIL_OT_brush_copy";
1253         ot->description = "Copy current Grease Pencil drawing brush";
1254
1255         /* callbacks */
1256         ot->exec = gp_brush_copy_exec;
1257         ot->poll = gp_active_brush_poll;
1258
1259         /* flags */
1260         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1261 }
1262
1263 /* ***************** Select Brush ************************ */
1264
1265 static int gp_brush_select_exec(bContext *C, wmOperator *op)
1266 {
1267         ToolSettings *ts = CTX_data_tool_settings(C);
1268
1269         /* if there's no existing container */
1270         if (ts == NULL) {
1271                 BKE_report(op->reports, RPT_ERROR, "Nowhere to go");
1272                 return OPERATOR_CANCELLED;
1273         }
1274
1275         const int index = RNA_int_get(op->ptr, "index");
1276         bGPDbrush *brush = BLI_findlink(&ts->gp_brushes, index);
1277         /* sanity checks */
1278         if (ELEM(NULL, brush)) {
1279                 return OPERATOR_CANCELLED;
1280         }
1281
1282         BKE_gpencil_brush_setactive(ts, brush);
1283
1284         /* notifiers */
1285         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1286
1287         return OPERATOR_FINISHED;
1288 }
1289
1290 void GPENCIL_OT_brush_select(wmOperatorType *ot)
1291 {
1292         /* identifiers */
1293         ot->name = "Select Brush";
1294         ot->idname = "GPENCIL_OT_brush_select";
1295         ot->description = "Select a Grease Pencil drawing brush";
1296
1297         /* callbacks */
1298         ot->exec = gp_brush_select_exec;
1299         ot->poll = gp_active_brush_poll;
1300
1301         /* flags */
1302         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1303
1304         /* properties */
1305         RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "Index of Drawing Brush", 0, INT_MAX);
1306 }
1307
1308 /* ************************************************ */
1309 /* Palette Operators */
1310
1311 /* ******************* Add New Palette ************************ */
1312
1313 /* add new palette - wrapper around API */
1314 static int gp_palette_add_exec(bContext *C, wmOperator *op)
1315 {
1316         bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
1317
1318         /* if there's no existing Grease-Pencil data there, add some */
1319         if (gpd_ptr == NULL) {
1320                 BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go");
1321                 return OPERATOR_CANCELLED;
1322         }
1323         if (*gpd_ptr == NULL)
1324                 *gpd_ptr = BKE_gpencil_data_addnew(DATA_("GPencil"));
1325
1326         /* add new palette now */
1327         BKE_gpencil_palette_addnew(*gpd_ptr, DATA_("GP_Palette"), true);
1328
1329         /* notifiers */
1330         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1331
1332         return OPERATOR_FINISHED;
1333 }
1334
1335 void GPENCIL_OT_palette_add(wmOperatorType *ot)
1336 {
1337         /* identifiers */
1338         ot->name = "Add Palette";
1339         ot->idname = "GPENCIL_OT_palette_add";
1340         ot->description = "Add new Grease Pencil palette for the active Grease Pencil data-block";
1341
1342         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1343
1344         /* callbacks */
1345         ot->exec = gp_palette_add_exec;
1346         ot->poll = gp_add_poll;
1347 }
1348
1349 /* ******************* Remove Active Palette ************************* */
1350
1351 static int gp_palette_remove_exec(bContext *C, wmOperator *op)
1352 {
1353         bGPdata *gpd = ED_gpencil_data_get_active(C);
1354         bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
1355
1356         /* sanity checks */
1357         if (ELEM(NULL, gpd, palette))
1358                 return OPERATOR_CANCELLED;
1359
1360         if (BLI_listbase_count_ex(&gpd->palettes, 2) < 2) {
1361                 BKE_report(op->reports, RPT_ERROR, "Grease Pencil needs a palette, unable to delete the last one");
1362                 return OPERATOR_CANCELLED;
1363         }
1364
1365
1366         /* make the palette before this the new active palette
1367          * - use the one after if this is the first
1368          * - if this is the only palette, this naturally becomes NULL
1369          */
1370         if (palette->prev)
1371                 BKE_gpencil_palette_setactive(gpd, palette->prev);
1372         else
1373                 BKE_gpencil_palette_setactive(gpd, palette->next);
1374
1375         /* delete the palette now... */
1376         BKE_gpencil_palette_delete(gpd, palette);
1377
1378         /* notifiers */
1379         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1380
1381         return OPERATOR_FINISHED;
1382 }
1383
1384 void GPENCIL_OT_palette_remove(wmOperatorType *ot)
1385 {
1386         /* identifiers */
1387         ot->name = "Remove palette";
1388         ot->idname = "GPENCIL_OT_palette_remove";
1389         ot->description = "Remove active Grease Pencil palette";
1390
1391         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1392
1393         /* callbacks */
1394         ot->exec = gp_palette_remove_exec;
1395         ot->poll = gp_active_palette_poll;
1396 }
1397
1398 /* ********************** Change Palette ***************************** */
1399
1400 static int gp_palette_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt))
1401 {
1402         uiPopupMenu *pup;
1403         uiLayout *layout;
1404
1405         /* call the menu, which will call this operator again, hence the canceled */
1406         pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE);
1407         layout = UI_popup_menu_layout(pup);
1408         uiItemsEnumO(layout, "GPENCIL_OT_palette_change", "palette");
1409         UI_popup_menu_end(C, pup);
1410
1411         return OPERATOR_INTERFACE;
1412 }
1413
1414 static int gp_palette_change_exec(bContext *C, wmOperator *op)
1415 {
1416         bGPdata *gpd = CTX_data_gpencil_data(C);
1417         bGPDpalette *palette = NULL;
1418         int palette_num = RNA_enum_get(op->ptr, "palette");
1419
1420         /* Get palette or create new one */
1421         if (palette_num == -1) {
1422                 /* Create palette */
1423                 palette = BKE_gpencil_palette_addnew(gpd, DATA_("GP_Palette"), true);
1424         }
1425         else {
1426                 /* Try to get palette */
1427                 palette = BLI_findlink(&gpd->palettes, palette_num);
1428
1429                 if (palette == NULL) {
1430                         BKE_reportf(op->reports, RPT_ERROR, "Cannot change to non-existent palette (index = %d)", palette_num);
1431                         return OPERATOR_CANCELLED;
1432                 }
1433         }
1434
1435         /* Set active palette */
1436         BKE_gpencil_palette_setactive(gpd, palette);
1437
1438         /* updates */
1439         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1440
1441         return OPERATOR_FINISHED;
1442 }
1443
1444 void GPENCIL_OT_palette_change(wmOperatorType *ot)
1445 {
1446         /* identifiers */
1447         ot->name = "Change Palette";
1448         ot->idname = "GPENCIL_OT_palette_change";
1449         ot->description = "Change active Grease Pencil palette";
1450
1451         /* callbacks */
1452         ot->invoke = gp_palette_change_invoke;
1453         ot->exec = gp_palette_change_exec;
1454         ot->poll = gp_active_palette_poll;
1455
1456         /* flags */
1457         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1458
1459         /* gp palette to use (dynamic enum) */
1460         ot->prop = RNA_def_enum(ot->srna, "palette", DummyRNA_DEFAULT_items, 0, "Grease Pencil Palette", "");
1461         RNA_def_enum_funcs(ot->prop, ED_gpencil_palettes_enum_itemf);
1462 }
1463
1464 /* ******************* Lock and hide any color non used in current layer ************************** */
1465
1466 static int gp_palette_lock_layer_exec(bContext *C, wmOperator *UNUSED(op))
1467 {
1468         bGPdata *gpd = ED_gpencil_data_get_active(C);
1469         bGPDpalette *palette;
1470
1471         /* sanity checks */
1472         if (ELEM(NULL, gpd))
1473                 return OPERATOR_CANCELLED;
1474
1475         palette = BKE_gpencil_palette_getactive(gpd);
1476         if (ELEM(NULL, palette))
1477                 return OPERATOR_CANCELLED;
1478
1479         /* first lock and hide all colors */
1480         for (bGPDpalettecolor *palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
1481                 palcolor->flag |= PC_COLOR_LOCKED;
1482                 palcolor->flag |= PC_COLOR_HIDE;
1483         }
1484
1485         /* loop all selected strokes and unlock any color used in active layer */
1486         for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
1487                 /* only editable and visible layers are considered */
1488                 if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL) && (gpl->flag & GP_LAYER_ACTIVE)) {
1489                         for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
1490                                 /* skip strokes that are invalid for current view */
1491                                 if (ED_gpencil_stroke_can_use(C, gps) == false)
1492                                         continue;
1493
1494                                 /* unlock/unhide color if not unlocked before */
1495                                 if (gps->palcolor != NULL) {
1496                                         gps->palcolor->flag &= ~PC_COLOR_LOCKED;
1497                                         gps->palcolor->flag &= ~PC_COLOR_HIDE;
1498                                 }
1499                         }
1500                 }
1501         }
1502         /* notifiers */
1503         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1504
1505         return OPERATOR_FINISHED;
1506 }
1507
1508 void GPENCIL_OT_palette_lock_layer(wmOperatorType *ot)
1509 {
1510         /* identifiers */
1511         ot->name = "Disable Unused Layer Colors";
1512         ot->idname = "GPENCIL_OT_palette_lock_layer";
1513         ot->description = "Lock and hide any color not used in any layer";
1514
1515         /* api callbacks */
1516         ot->exec = gp_palette_lock_layer_exec;
1517         ot->poll = gp_active_layer_poll;
1518 }
1519
1520 /* ************************************************ */
1521 /* Palette Colors Operators */
1522
1523 /* ******************* Add New Palette ************************ */
1524
1525 /* add new palette - wrapper around API */
1526 static int gp_palettecolor_add_exec(bContext *C, wmOperator *op)
1527 {
1528         bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
1529
1530         /* if there's no existing Grease-Pencil data there, add some */
1531         if (gpd_ptr == NULL) {
1532                 BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go");
1533                 return OPERATOR_CANCELLED;
1534         }
1535         if (*gpd_ptr == NULL)
1536                 *gpd_ptr = BKE_gpencil_data_addnew(DATA_("GPencil"));
1537
1538         /* verify palette */
1539         bGPDpalette *palette = BKE_gpencil_palette_getactive(*gpd_ptr);
1540         if (palette == NULL)
1541                 palette = BKE_gpencil_palette_addnew(*gpd_ptr, DATA_("GP_Palette"), true);
1542
1543         /* add new palette color now */
1544         BKE_gpencil_palettecolor_addnew(palette, DATA_("Color"), true);
1545
1546         /* notifiers */
1547         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1548
1549         return OPERATOR_FINISHED;
1550 }
1551
1552 void GPENCIL_OT_palettecolor_add(wmOperatorType *ot)
1553 {
1554         /* identifiers */
1555         ot->name = "Add Palette Color";
1556         ot->idname = "GPENCIL_OT_palettecolor_add";
1557         ot->description = "Add new Grease Pencil palette color for the active Grease Pencil data-block";
1558
1559         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1560
1561         /* callbacks */
1562         ot->exec = gp_palettecolor_add_exec;
1563         ot->poll = gp_add_poll;
1564 }
1565
1566 /* ******************* Remove Active Palette color ************************* */
1567
1568 static int gp_palettecolor_remove_exec(bContext *C, wmOperator *UNUSED(op))
1569 {
1570         bGPdata *gpd = ED_gpencil_data_get_active(C);
1571         bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
1572         bGPDpalettecolor *color = BKE_gpencil_palettecolor_getactive(palette);
1573
1574         /* sanity checks */
1575         if (ELEM(NULL, gpd, palette, color))
1576                 return OPERATOR_CANCELLED;
1577
1578         /* make the palette color before this the new active color
1579          * - use the one after if this is the first
1580          * - if this is the only color, this naturally becomes NULL
1581          */
1582         if (color->prev)
1583                 BKE_gpencil_palettecolor_setactive(palette, color->prev);
1584         else
1585                 BKE_gpencil_palettecolor_setactive(palette, color->next);
1586
1587         /* delete the strokes */
1588         BKE_gpencil_palettecolor_delete_strokes(gpd, color->info);
1589
1590         /* delete the palette color now... */
1591         BKE_gpencil_palettecolor_delete(palette, color);
1592
1593         /* notifiers */
1594         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1595
1596         return OPERATOR_FINISHED;
1597 }
1598
1599 void GPENCIL_OT_palettecolor_remove(wmOperatorType *ot)
1600 {
1601         /* identifiers */
1602         ot->name = "Remove palette color";
1603         ot->idname = "GPENCIL_OT_palettecolor_remove";
1604         ot->description = "Remove active Grease Pencil palette color";
1605
1606         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1607
1608         /* callbacks */
1609         ot->exec = gp_palettecolor_remove_exec;
1610         ot->poll = gp_active_palettecolor_poll;
1611 }
1612
1613 /* ********************** Isolate palette color **************************** */
1614
1615 static int gp_isolate_palettecolor_exec(bContext *C, wmOperator *op)
1616 {
1617         bGPdata *gpd = ED_gpencil_data_get_active(C);
1618         bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
1619         bGPDpalettecolor *active_color = BKE_gpencil_palettecolor_getactive(palette);
1620         bGPDpalettecolor *palcolor;
1621
1622         int flags = PC_COLOR_LOCKED;
1623         bool isolate = false;
1624
1625         if (RNA_boolean_get(op->ptr, "affect_visibility"))
1626                 flags |= PC_COLOR_HIDE;
1627
1628         if (ELEM(NULL, gpd, active_color)) {
1629                 BKE_report(op->reports, RPT_ERROR, "No active color to isolate");
1630                 return OPERATOR_CANCELLED;
1631         }
1632
1633         /* Test whether to isolate or clear all flags */
1634         for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
1635                 /* Skip if this is the active one */
1636                 if (palcolor == active_color)
1637                         continue;
1638
1639                 /* If the flags aren't set, that means that the color is
1640                  * not alone, so we have some colors to isolate still
1641                  */
1642                 if ((palcolor->flag & flags) == 0) {
1643                         isolate = true;
1644                         break;
1645                 }
1646         }
1647
1648         /* Set/Clear flags as appropriate */
1649         if (isolate) {
1650                 /* Set flags on all "other" colors */
1651                 for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
1652                         if (palcolor == active_color)
1653                                 continue;
1654                         else
1655                                 palcolor->flag |= flags;
1656                 }
1657         }
1658         else {
1659                 /* Clear flags - Restore everything else */
1660                 for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
1661                         palcolor->flag &= ~flags;
1662                 }
1663         }
1664
1665         /* notifiers */
1666         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1667
1668         return OPERATOR_FINISHED;
1669 }
1670
1671 void GPENCIL_OT_palettecolor_isolate(wmOperatorType *ot)
1672 {
1673         /* identifiers */
1674         ot->name = "Isolate Palette Color";
1675         ot->idname = "GPENCIL_OT_palettecolor_isolate";
1676         ot->description = "Toggle whether the active color is the only one that is editable and/or visible";
1677
1678         /* callbacks */
1679         ot->exec = gp_isolate_palettecolor_exec;
1680         ot->poll = gp_active_palettecolor_poll;
1681
1682         /* flags */
1683         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1684
1685         /* properties */
1686         RNA_def_boolean(ot->srna, "affect_visibility", false, "Affect Visibility", "In addition to toggling "
1687                         "the editability, also affect the visibility");
1688 }
1689
1690 /* *********************** Hide Palette colors ******************************** */
1691
1692 static int gp_palettecolor_hide_exec(bContext *C, wmOperator *op)
1693 {
1694         bGPdata *gpd = ED_gpencil_data_get_active(C);
1695         bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
1696         bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette);
1697
1698         bool unselected = RNA_boolean_get(op->ptr, "unselected");
1699
1700         /* sanity checks */
1701         if (ELEM(NULL, gpd, palette, palcolor))
1702                 return OPERATOR_CANCELLED;
1703
1704         if (unselected) {
1705                 bGPDpalettecolor *color;
1706
1707                 /* hide unselected */
1708                 for (color = palette->colors.first; color; color = color->next) {
1709                         if (color != palcolor) {
1710                                 color->flag |= PC_COLOR_HIDE;
1711                         }
1712                 }
1713         }
1714         else {
1715                 /* hide selected/active */
1716                 palcolor->flag |= PC_COLOR_HIDE;
1717         }
1718
1719         /* notifiers */
1720         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1721
1722         return OPERATOR_FINISHED;
1723 }
1724
1725 void GPENCIL_OT_palettecolor_hide(wmOperatorType *ot)
1726 {
1727         /* identifiers */
1728         ot->name = "Hide Color(s)";
1729         ot->idname = "GPENCIL_OT_palettecolor_hide";
1730         ot->description = "Hide selected/unselected Grease Pencil colors";
1731
1732         /* callbacks */
1733         ot->exec = gp_palettecolor_hide_exec;
1734         ot->poll = gp_active_palettecolor_poll; /* NOTE: we need an active color to play with */
1735
1736         /* flags */
1737         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1738
1739         /* props */
1740         RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected colors");
1741 }
1742
1743 /* ********************** Show All Colors ***************************** */
1744
1745 /* poll callback for showing colors */
1746 static int gp_palettecolor_reveal_poll(bContext *C)
1747 {
1748         return ED_gpencil_data_get_active(C) != NULL;
1749 }
1750
1751 static int gp_palettecolor_reveal_exec(bContext *C, wmOperator *UNUSED(op))
1752 {
1753         bGPdata *gpd = ED_gpencil_data_get_active(C);
1754         bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
1755         bGPDpalettecolor *palcolor;
1756
1757         /* sanity checks */
1758         if (ELEM(NULL, gpd, palette))
1759                 return OPERATOR_CANCELLED;
1760
1761         /* make all colors visible */
1762         for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
1763                 palcolor->flag &= ~PC_COLOR_HIDE;
1764         }
1765
1766         /* notifiers */
1767         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1768
1769         return OPERATOR_FINISHED;
1770 }
1771
1772 void GPENCIL_OT_palettecolor_reveal(wmOperatorType *ot)
1773 {
1774         /* identifiers */
1775         ot->name = "Show All Colors";
1776         ot->idname = "GPENCIL_OT_palettecolor_reveal";
1777         ot->description = "Unhide all hidden Grease Pencil palette colors";
1778
1779         /* callbacks */
1780         ot->exec = gp_palettecolor_reveal_exec;
1781         ot->poll = gp_palettecolor_reveal_poll;
1782
1783         /* flags */
1784         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1785 }
1786
1787 /* ***************** Lock/Unlock All Palette colors ************************ */
1788
1789 static int gp_palettecolor_lock_all_exec(bContext *C, wmOperator *UNUSED(op))
1790 {
1791         bGPdata *gpd = ED_gpencil_data_get_active(C);
1792         bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
1793         bGPDpalettecolor *palcolor;
1794
1795         /* sanity checks */
1796         if (ELEM(NULL, gpd, palette))
1797                 return OPERATOR_CANCELLED;
1798
1799         /* make all layers non-editable */
1800         for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
1801                 palcolor->flag |= PC_COLOR_LOCKED;
1802         }
1803
1804         /* notifiers */
1805         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1806
1807         return OPERATOR_FINISHED;
1808 }
1809
1810 void GPENCIL_OT_palettecolor_lock_all(wmOperatorType *ot)
1811 {
1812         /* identifiers */
1813         ot->name = "Lock All Colors";
1814         ot->idname = "GPENCIL_OT_palettecolor_lock_all";
1815         ot->description = "Lock all Grease Pencil colors to prevent them from being accidentally modified";
1816
1817         /* callbacks */
1818         ot->exec = gp_palettecolor_lock_all_exec;
1819         ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */
1820
1821         /* flags */
1822         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1823 }
1824
1825 /* -------------------------- */
1826
1827 static int gp_palettecolor_unlock_all_exec(bContext *C, wmOperator *UNUSED(op))
1828 {
1829         bGPdata *gpd = ED_gpencil_data_get_active(C);
1830         bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
1831         bGPDpalettecolor *palcolor;
1832
1833         /* sanity checks */
1834         if (ELEM(NULL, gpd, palette))
1835                 return OPERATOR_CANCELLED;
1836
1837         /* make all layers editable again*/
1838         for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
1839                 palcolor->flag &= ~PC_COLOR_LOCKED;
1840         }
1841
1842         /* notifiers */
1843         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1844
1845         return OPERATOR_FINISHED;
1846 }
1847
1848 void GPENCIL_OT_palettecolor_unlock_all(wmOperatorType *ot)
1849 {
1850         /* identifiers */
1851         ot->name = "Unlock All Colors";
1852         ot->idname = "GPENCIL_OT_palettecolor_unlock_all";
1853         ot->description = "Unlock all Grease Pencil colors so that they can be edited";
1854
1855         /* callbacks */
1856         ot->exec = gp_palettecolor_unlock_all_exec;
1857         ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */
1858
1859         /* flags */
1860         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1861 }
1862
1863 /* ******************* Move Color Up/Down ************************** */
1864
1865 enum {
1866         GP_COLOR_MOVE_UP = -1,
1867         GP_COLOR_MOVE_DOWN = 1
1868 };
1869
1870 static int gp_palettecolor_move_exec(bContext *C, wmOperator *op)
1871 {
1872         bGPdata *gpd = ED_gpencil_data_get_active(C);
1873         bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
1874         bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette);
1875
1876         int direction = RNA_enum_get(op->ptr, "direction");
1877
1878         /* sanity checks */
1879         if (ELEM(NULL, gpd, palette, palcolor))
1880                 return OPERATOR_CANCELLED;
1881
1882         /* up or down? */
1883         if (direction == GP_COLOR_MOVE_UP) {
1884                 /* up */
1885                 BLI_remlink(&palette->colors, palcolor);
1886                 BLI_insertlinkbefore(&palette->colors, palcolor->prev, palcolor);
1887         }
1888         else if (direction == GP_COLOR_MOVE_DOWN) {
1889                 /* down */
1890                 BLI_remlink(&palette->colors, palcolor);
1891                 BLI_insertlinkafter(&palette->colors, palcolor->next, palcolor);
1892         }
1893         else {
1894                 BLI_assert(0);
1895         }
1896
1897         /* notifiers */
1898         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1899
1900         return OPERATOR_FINISHED;
1901 }
1902
1903 void GPENCIL_OT_palettecolor_move(wmOperatorType *ot)
1904 {
1905         static EnumPropertyItem slot_move[] = {
1906                 {GP_COLOR_MOVE_UP, "UP", 0, "Up", ""},
1907                 {GP_COLOR_MOVE_DOWN, "DOWN", 0, "Down", ""},
1908                 {0, NULL, 0, NULL, NULL}
1909         };
1910
1911         /* identifiers */
1912         ot->name = "Move Palette color";
1913         ot->idname = "GPENCIL_OT_palettecolor_move";
1914         ot->description = "Move the active Grease Pencil palette color up/down in the list";
1915
1916         /* api callbacks */
1917         ot->exec = gp_palettecolor_move_exec;
1918         ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */
1919
1920         /* flags */
1921         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1922
1923         ot->prop = RNA_def_enum(ot->srna, "direction", slot_move, GP_COLOR_MOVE_UP, "Direction", "");
1924 }
1925
1926 /* ***************** Select all strokes using Palette color ************************ */
1927
1928 static int gp_palettecolor_select_exec(bContext *C, wmOperator *UNUSED(op))
1929 {
1930         bGPdata *gpd = ED_gpencil_data_get_active(C);
1931         bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
1932         bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette);
1933
1934         /* sanity checks */
1935         if (ELEM(NULL, gpd, palette, palcolor))
1936                 return OPERATOR_CANCELLED;
1937
1938         /* read all strokes and select*/
1939         for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
1940                 /* only editable and visible layers are considered */
1941                 if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
1942                         /* verify something to do */
1943                         for (bGPDstroke *gps = gpl->actframe->strokes.first; gps; gps = gps->next) {
1944                                 /* skip strokes that are invalid for current view */
1945                                 if (ED_gpencil_stroke_can_use(C, gps) == false)
1946                                         continue;
1947                                 /* check if the color is editable */
1948                                 if (ED_gpencil_stroke_color_use(gpl, gps) == false)
1949                                         continue;
1950
1951                                 /* select */
1952                                 if (strcmp(palcolor->info, gps->colorname) == 0) {
1953                                         bGPDspoint *pt;
1954                                         int i;
1955
1956                                         gps->flag |= GP_STROKE_SELECT;
1957                                         for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1958                                                 pt->flag |= GP_SPOINT_SELECT;
1959                                         }
1960                                 }
1961                         }
1962                 }
1963         }
1964         /* notifiers */
1965         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1966
1967         return OPERATOR_FINISHED;
1968 }
1969
1970 void GPENCIL_OT_palettecolor_select(wmOperatorType *ot)
1971 {
1972         /* identifiers */
1973         ot->name = "Select Color";
1974         ot->idname = "GPENCIL_OT_palettecolor_select";
1975         ot->description = "Select all Grease Pencil strokes using current color";
1976
1977         /* callbacks */
1978         ot->exec = gp_palettecolor_select_exec;
1979         ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */
1980
1981         /* flags */
1982         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1983 }
1984
1985 /* ***************** Copy Palette color ************************ */
1986
1987 static int gp_palettecolor_copy_exec(bContext *C, wmOperator *UNUSED(op))
1988 {
1989         bGPdata *gpd = ED_gpencil_data_get_active(C);
1990         bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
1991         bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette);
1992         bGPDpalettecolor *newcolor;
1993
1994         /* sanity checks */
1995         if (ELEM(NULL, gpd, palette, palcolor))
1996                 return OPERATOR_CANCELLED;
1997
1998         /* create a new color and duplicate data */
1999         newcolor = BKE_gpencil_palettecolor_addnew(palette, palcolor->info, true);
2000         copy_v4_v4(newcolor->color, palcolor->color);
2001         copy_v4_v4(newcolor->fill, palcolor->fill);
2002         newcolor->flag = palcolor->flag;
2003
2004         /* notifiers */
2005         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
2006
2007         return OPERATOR_FINISHED;
2008 }
2009
2010 void GPENCIL_OT_palettecolor_copy(wmOperatorType *ot)
2011 {
2012         /* identifiers */
2013         ot->name = "Copy Color";
2014         ot->idname = "GPENCIL_OT_palettecolor_copy";
2015         ot->description = "Copy current Grease Pencil palette color";
2016
2017         /* callbacks */
2018         ot->exec = gp_palettecolor_copy_exec;
2019         ot->poll = gp_active_palettecolor_poll;
2020
2021         /* flags */
2022         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2023 }