2 * ***** BEGIN GPL LICENSE BLOCK *****
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.
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.
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.
18 * The Original Code is Copyright (C) 2008, Blender Foundation, Joshua Leung
19 * This is a new part of Blender
21 * Contributor(s): Joshua Leung, Antonio Vazquez
23 * ***** END GPL LICENSE BLOCK *****
25 * Operators for dealing with GP datablocks and layers
28 /** \file blender/editors/gpencil/gpencil_data.c
39 #include "MEM_guardedalloc.h"
41 #include "BLI_blenlib.h"
42 #include "BLI_utildefines.h"
43 #include "BLI_ghash.h"
45 #include "BLI_string_utils.h"
47 #include "BLT_translation.h"
49 #include "DNA_scene_types.h"
50 #include "DNA_screen_types.h"
51 #include "DNA_space_types.h"
52 #include "DNA_view3d_types.h"
53 #include "DNA_gpencil_types.h"
55 #include "BKE_colortools.h"
56 #include "BKE_context.h"
57 #include "BKE_gpencil.h"
58 #include "BKE_library.h"
60 #include "BKE_object.h"
61 #include "BKE_report.h"
62 #include "BKE_scene.h"
63 #include "BKE_screen.h"
65 #include "UI_interface.h"
66 #include "UI_resources.h"
71 #include "RNA_access.h"
72 #include "RNA_define.h"
73 #include "RNA_enum_types.h"
75 #include "ED_gpencil.h"
77 #include "gpencil_intern.h"
79 /* ************************************************ */
80 /* Datablock Operators */
82 /* ******************* Add New Data ************************ */
84 /* add new datablock - wrapper around API */
85 static int gp_data_add_exec(bContext *C, wmOperator *op)
87 Main *bmain = CTX_data_main(C);
88 bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
89 ToolSettings *ts = CTX_data_tool_settings(C);
91 if (gpd_ptr == NULL) {
92 BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go");
93 return OPERATOR_CANCELLED;
96 /* decrement user count and add new datablock */
97 bGPdata *gpd = (*gpd_ptr);
100 *gpd_ptr = BKE_gpencil_data_addnew(bmain, DATA_("GPencil"));
102 /* if not exist brushes, create a new set */
104 if (BLI_listbase_is_empty(&ts->gp_brushes)) {
105 /* create new brushes */
106 BKE_gpencil_brush_init_presets(ts);
113 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
115 return OPERATOR_FINISHED;
118 void GPENCIL_OT_data_add(wmOperatorType *ot)
121 ot->name = "Grease Pencil Add New";
122 ot->idname = "GPENCIL_OT_data_add";
123 ot->description = "Add new Grease Pencil data-block";
124 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
127 ot->exec = gp_data_add_exec;
128 ot->poll = gp_add_poll;
131 /* ******************* Unlink Data ************************ */
133 /* poll callback for adding data/layers - special */
134 static int gp_data_unlink_poll(bContext *C)
136 bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
138 /* if we have access to some active data, make sure there's a datablock before enabling this */
139 return (gpd_ptr && *gpd_ptr);
143 /* unlink datablock - wrapper around API */
144 static int gp_data_unlink_exec(bContext *C, wmOperator *op)
146 bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
148 if (gpd_ptr == NULL) {
149 BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go");
150 return OPERATOR_CANCELLED;
153 /* just unlink datablock now, decreasing its user count */
154 bGPdata *gpd = (*gpd_ptr);
161 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
163 return OPERATOR_FINISHED;
166 void GPENCIL_OT_data_unlink(wmOperatorType *ot)
169 ot->name = "Grease Pencil Unlink";
170 ot->idname = "GPENCIL_OT_data_unlink";
171 ot->description = "Unlink active Grease Pencil data-block";
172 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
175 ot->exec = gp_data_unlink_exec;
176 ot->poll = gp_data_unlink_poll;
180 /* ************************************************ */
181 /* Layer Operators */
183 /* ******************* Add New Layer ************************ */
185 /* add new layer - wrapper around API */
186 static int gp_layer_add_exec(bContext *C, wmOperator *op)
188 Main *bmain = CTX_data_main(C);
189 bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
190 ToolSettings *ts = CTX_data_tool_settings(C);
192 /* if there's no existing Grease-Pencil data there, add some */
193 if (gpd_ptr == NULL) {
194 BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go");
195 return OPERATOR_CANCELLED;
197 if (*gpd_ptr == NULL)
198 *gpd_ptr = BKE_gpencil_data_addnew(bmain, DATA_("GPencil"));
200 /* if not exist brushes, create a new set */
202 if (BLI_listbase_is_empty(&ts->gp_brushes)) {
203 /* create new brushes */
204 BKE_gpencil_brush_init_presets(ts);
208 /* add new layer now */
209 BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("GP_Layer"), true);
212 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
214 return OPERATOR_FINISHED;
217 void GPENCIL_OT_layer_add(wmOperatorType *ot)
220 ot->name = "Add New Layer";
221 ot->idname = "GPENCIL_OT_layer_add";
222 ot->description = "Add new Grease Pencil layer for the active Grease Pencil data-block";
224 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
227 ot->exec = gp_layer_add_exec;
228 ot->poll = gp_add_poll;
231 /* ******************* Remove Active Layer ************************* */
233 static int gp_layer_remove_exec(bContext *C, wmOperator *op)
235 bGPdata *gpd = ED_gpencil_data_get_active(C);
236 bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
239 if (ELEM(NULL, gpd, gpl))
240 return OPERATOR_CANCELLED;
242 if (gpl->flag & GP_LAYER_LOCKED) {
243 BKE_report(op->reports, RPT_ERROR, "Cannot delete locked layers");
244 return OPERATOR_CANCELLED;
247 /* make the layer before this the new active layer
248 * - use the one after if this is the first
249 * - if this is the only layer, this naturally becomes NULL
252 BKE_gpencil_layer_setactive(gpd, gpl->prev);
254 BKE_gpencil_layer_setactive(gpd, gpl->next);
256 /* delete the layer now... */
257 BKE_gpencil_layer_delete(gpd, gpl);
260 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
262 return OPERATOR_FINISHED;
265 void GPENCIL_OT_layer_remove(wmOperatorType *ot)
268 ot->name = "Remove Layer";
269 ot->idname = "GPENCIL_OT_layer_remove";
270 ot->description = "Remove active Grease Pencil layer";
272 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
275 ot->exec = gp_layer_remove_exec;
276 ot->poll = gp_active_layer_poll;
279 /* ******************* Move Layer Up/Down ************************** */
282 GP_LAYER_MOVE_UP = -1,
283 GP_LAYER_MOVE_DOWN = 1
286 static int gp_layer_move_exec(bContext *C, wmOperator *op)
288 bGPdata *gpd = ED_gpencil_data_get_active(C);
289 bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
291 int direction = RNA_enum_get(op->ptr, "type");
294 if (ELEM(NULL, gpd, gpl))
295 return OPERATOR_CANCELLED;
297 BLI_assert(ELEM(direction, -1, 0, 1)); /* we use value below */
298 if (BLI_listbase_link_move(&gpd->layers, gpl, direction)) {
299 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
302 return OPERATOR_FINISHED;
305 void GPENCIL_OT_layer_move(wmOperatorType *ot)
307 static const EnumPropertyItem slot_move[] = {
308 {GP_LAYER_MOVE_UP, "UP", 0, "Up", ""},
309 {GP_LAYER_MOVE_DOWN, "DOWN", 0, "Down", ""},
310 {0, NULL, 0, NULL, NULL}
314 ot->name = "Move Grease Pencil Layer";
315 ot->idname = "GPENCIL_OT_layer_move";
316 ot->description = "Move the active Grease Pencil layer up/down in the list";
319 ot->exec = gp_layer_move_exec;
320 ot->poll = gp_active_layer_poll;
323 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
325 ot->prop = RNA_def_enum(ot->srna, "type", slot_move, 0, "Type", "");
328 /* ********************* Duplicate Layer ************************** */
330 static int gp_layer_copy_exec(bContext *C, wmOperator *UNUSED(op))
332 bGPdata *gpd = ED_gpencil_data_get_active(C);
333 bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
334 bGPDlayer *new_layer;
337 if (ELEM(NULL, gpd, gpl))
338 return OPERATOR_CANCELLED;
340 /* make copy of layer, and add it immediately after the existing layer */
341 new_layer = BKE_gpencil_layer_duplicate(gpl);
342 BLI_insertlinkafter(&gpd->layers, gpl, new_layer);
344 /* ensure new layer has a unique name, and is now the active layer */
345 BLI_uniquename(&gpd->layers, new_layer, DATA_("GP_Layer"), '.', offsetof(bGPDlayer, info), sizeof(new_layer->info));
346 BKE_gpencil_layer_setactive(gpd, new_layer);
349 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
351 return OPERATOR_FINISHED;
354 void GPENCIL_OT_layer_duplicate(wmOperatorType *ot)
357 ot->name = "Duplicate Layer";
358 ot->idname = "GPENCIL_OT_layer_duplicate";
359 ot->description = "Make a copy of the active Grease Pencil layer";
362 ot->exec = gp_layer_copy_exec;
363 ot->poll = gp_active_layer_poll;
366 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
369 /* *********************** Hide Layers ******************************** */
371 static int gp_hide_exec(bContext *C, wmOperator *op)
373 bGPdata *gpd = ED_gpencil_data_get_active(C);
374 bGPDlayer *layer = BKE_gpencil_layer_getactive(gpd);
375 bool unselected = RNA_boolean_get(op->ptr, "unselected");
378 if (ELEM(NULL, gpd, layer))
379 return OPERATOR_CANCELLED;
384 /* hide unselected */
385 for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
387 gpl->flag |= GP_LAYER_HIDE;
392 /* hide selected/active */
393 layer->flag |= GP_LAYER_HIDE;
397 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
399 return OPERATOR_FINISHED;
402 void GPENCIL_OT_hide(wmOperatorType *ot)
405 ot->name = "Hide Layer(s)";
406 ot->idname = "GPENCIL_OT_hide";
407 ot->description = "Hide selected/unselected Grease Pencil layers";
410 ot->exec = gp_hide_exec;
411 ot->poll = gp_active_layer_poll; /* NOTE: we need an active layer to play with */
414 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
417 RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected layers");
420 /* ********************** Show All Layers ***************************** */
422 /* poll callback for showing layers */
423 static int gp_reveal_poll(bContext *C)
425 return ED_gpencil_data_get_active(C) != NULL;
428 static void gp_reveal_select_frame(bContext *C, bGPDframe *frame, bool select)
431 for (gps = frame->strokes.first; gps; gps = gps->next) {
433 /* only deselect strokes that are valid in this view */
434 if (ED_gpencil_stroke_can_use(C, gps)) {
436 /* (de)select points */
439 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
440 SET_FLAG_FROM_TEST(pt->flag, select, GP_SPOINT_SELECT);
443 /* (de)select stroke */
444 SET_FLAG_FROM_TEST(gps->flag, select, GP_STROKE_SELECT);
449 static int gp_reveal_exec(bContext *C, wmOperator *op)
451 bGPdata *gpd = ED_gpencil_data_get_active(C);
453 const bool select = RNA_boolean_get(op->ptr, "select");
457 return OPERATOR_CANCELLED;
459 for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
461 if (gpl->flag & GP_LAYER_HIDE) {
462 gpl->flag &= ~GP_LAYER_HIDE;
464 /* select or deselect if requested, only on hidden layers */
465 if (gpd->flag & GP_DATA_STROKE_EDITMODE) {
467 /* select all strokes on active frame only (same as select all operator) */
469 gp_reveal_select_frame(C, gpl->actframe, true);
473 /* deselect strokes on all frames (same as deselect all operator) */
475 for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
476 gp_reveal_select_frame(C, gpf, false);
484 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
486 return OPERATOR_FINISHED;
489 void GPENCIL_OT_reveal(wmOperatorType *ot)
492 ot->name = "Show All Layers";
493 ot->idname = "GPENCIL_OT_reveal";
494 ot->description = "Show all Grease Pencil layers";
497 ot->exec = gp_reveal_exec;
498 ot->poll = gp_reveal_poll;
501 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
504 RNA_def_boolean(ot->srna, "select", true, "Select", "");
507 /* ***************** Lock/Unlock All Layers ************************ */
509 static int gp_lock_all_exec(bContext *C, wmOperator *UNUSED(op))
511 bGPdata *gpd = ED_gpencil_data_get_active(C);
516 return OPERATOR_CANCELLED;
518 /* make all layers non-editable */
519 for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
520 gpl->flag |= GP_LAYER_LOCKED;
524 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
526 return OPERATOR_FINISHED;
529 void GPENCIL_OT_lock_all(wmOperatorType *ot)
532 ot->name = "Lock All Layers";
533 ot->idname = "GPENCIL_OT_lock_all";
534 ot->description = "Lock all Grease Pencil layers to prevent them from being accidentally modified";
537 ot->exec = gp_lock_all_exec;
538 ot->poll = gp_reveal_poll; /* XXX: could use dedicated poll later */
541 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
544 /* -------------------------- */
546 static int gp_unlock_all_exec(bContext *C, wmOperator *UNUSED(op))
548 bGPdata *gpd = ED_gpencil_data_get_active(C);
553 return OPERATOR_CANCELLED;
555 /* make all layers editable again */
556 for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
557 gpl->flag &= ~GP_LAYER_LOCKED;
561 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
563 return OPERATOR_FINISHED;
566 void GPENCIL_OT_unlock_all(wmOperatorType *ot)
569 ot->name = "Unlock All Layers";
570 ot->idname = "GPENCIL_OT_unlock_all";
571 ot->description = "Unlock all Grease Pencil layers so that they can be edited";
574 ot->exec = gp_unlock_all_exec;
575 ot->poll = gp_reveal_poll; /* XXX: could use dedicated poll later */
578 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
581 /* ********************** Isolate Layer **************************** */
583 static int gp_isolate_layer_exec(bContext *C, wmOperator *op)
585 bGPdata *gpd = ED_gpencil_data_get_active(C);
586 bGPDlayer *layer = BKE_gpencil_layer_getactive(gpd);
588 int flags = GP_LAYER_LOCKED;
589 bool isolate = false;
591 if (RNA_boolean_get(op->ptr, "affect_visibility"))
592 flags |= GP_LAYER_HIDE;
594 if (ELEM(NULL, gpd, layer)) {
595 BKE_report(op->reports, RPT_ERROR, "No active layer to isolate");
596 return OPERATOR_CANCELLED;
599 /* Test whether to isolate or clear all flags */
600 for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
601 /* Skip if this is the active layer */
605 /* If the flags aren't set, that means that the layer is
606 * not alone, so we have some layers to isolate still
608 if ((gpl->flag & flags) == 0) {
614 /* Set/Clear flags as appropriate */
615 /* TODO: Include onionskinning on this list? */
617 /* Set flags on all "other" layers */
618 for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
626 /* Clear flags - Restore everything else */
627 for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
633 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
635 return OPERATOR_FINISHED;
638 void GPENCIL_OT_layer_isolate(wmOperatorType *ot)
641 ot->name = "Isolate Layer";
642 ot->idname = "GPENCIL_OT_layer_isolate";
643 ot->description = "Toggle whether the active layer is the only one that can be edited and/or visible";
646 ot->exec = gp_isolate_layer_exec;
647 ot->poll = gp_active_layer_poll;
650 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
653 RNA_def_boolean(ot->srna, "affect_visibility", false, "Affect Visibility",
654 "In addition to toggling the editability, also affect the visibility");
657 /* ********************** Merge Layer with the next layer **************************** */
659 static int gp_merge_layer_exec(bContext *C, wmOperator *op)
661 bGPdata *gpd = ED_gpencil_data_get_active(C);
662 bGPDlayer *gpl_current = BKE_gpencil_layer_getactive(gpd);
663 bGPDlayer *gpl_next = gpl_current->next;
665 if (ELEM(NULL, gpd, gpl_current, gpl_next)) {
666 BKE_report(op->reports, RPT_ERROR, "No layers to merge");
667 return OPERATOR_CANCELLED;
670 /* Collect frames of gpl_current in hash table to avoid O(n^2) lookups */
671 GHash *gh_frames_cur = BLI_ghash_int_new_ex(__func__, 64);
672 for (bGPDframe *gpf = gpl_current->frames.first; gpf; gpf = gpf->next) {
673 BLI_ghash_insert(gh_frames_cur, SET_INT_IN_POINTER(gpf->framenum), gpf);
676 /* read all frames from next layer */
677 for (bGPDframe *gpf = gpl_next->frames.first; gpf; gpf = gpf->next) {
678 /* try to find frame in active layer */
679 bGPDframe *frame = BLI_ghash_lookup(gh_frames_cur, SET_INT_IN_POINTER(gpf->framenum));
681 /* nothing found, create new */
682 frame = BKE_gpencil_frame_addnew(gpl_current, gpf->framenum);
684 /* add to tail all strokes */
685 BLI_movelisttolist(&frame->strokes, &gpf->strokes);
687 /* Now delete next layer */
688 BKE_gpencil_layer_delete(gpd, gpl_next);
689 BLI_ghash_free(gh_frames_cur, NULL, NULL);
692 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
694 return OPERATOR_FINISHED;
697 void GPENCIL_OT_layer_merge(wmOperatorType *ot)
700 ot->name = "Merge Down";
701 ot->idname = "GPENCIL_OT_layer_merge";
702 ot->description = "Merge the current layer with the layer below";
705 ot->exec = gp_merge_layer_exec;
706 ot->poll = gp_active_layer_poll;
709 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
712 /* ********************** Change Layer ***************************** */
714 static int gp_layer_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt))
719 /* call the menu, which will call this operator again, hence the canceled */
720 pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE);
721 layout = UI_popup_menu_layout(pup);
722 uiItemsEnumO(layout, "GPENCIL_OT_layer_change", "layer");
723 UI_popup_menu_end(C, pup);
725 return OPERATOR_INTERFACE;
728 static int gp_layer_change_exec(bContext *C, wmOperator *op)
730 bGPdata *gpd = CTX_data_gpencil_data(C);
731 bGPDlayer *gpl = NULL;
732 int layer_num = RNA_enum_get(op->ptr, "layer");
734 /* Get layer or create new one */
735 if (layer_num == -1) {
737 gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
740 /* Try to get layer */
741 gpl = BLI_findlink(&gpd->layers, layer_num);
744 BKE_reportf(op->reports, RPT_ERROR, "Cannot change to non-existent layer (index = %d)", layer_num);
745 return OPERATOR_CANCELLED;
749 /* Set active layer */
750 BKE_gpencil_layer_setactive(gpd, gpl);
753 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
755 return OPERATOR_FINISHED;
758 void GPENCIL_OT_layer_change(wmOperatorType *ot)
761 ot->name = "Change Layer";
762 ot->idname = "GPENCIL_OT_layer_change";
763 ot->description = "Change active Grease Pencil layer";
766 ot->invoke = gp_layer_change_invoke;
767 ot->exec = gp_layer_change_exec;
768 ot->poll = gp_active_layer_poll;
771 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
773 /* gp layer to use (dynamic enum) */
774 ot->prop = RNA_def_enum(ot->srna, "layer", DummyRNA_DEFAULT_items, 0, "Grease Pencil Layer", "");
775 RNA_def_enum_funcs(ot->prop, ED_gpencil_layers_with_new_enum_itemf);
778 /* ************************************************ */
780 /* ******************* Arrange Stroke Up/Down in drawing order ************************** */
783 GP_STROKE_MOVE_UP = -1,
784 GP_STROKE_MOVE_DOWN = 1,
785 GP_STROKE_MOVE_TOP = 2,
786 GP_STROKE_MOVE_BOTTOM = 3
789 static int gp_stroke_arrange_exec(bContext *C, wmOperator *op)
791 bGPdata *gpd = ED_gpencil_data_get_active(C);
792 bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
796 if (ELEM(NULL, gpd, gpl, gpl->actframe)) {
797 return OPERATOR_CANCELLED;
800 bGPDframe *gpf = gpl->actframe;
801 /* temp listbase to store selected strokes */
802 ListBase selected = {NULL};
803 const int direction = RNA_enum_get(op->ptr, "direction");
805 /* verify if any selected stroke is in the extreme of the stack and select to move */
806 for (gps = gpf->strokes.first; gps; gps = gps->next) {
807 /* only if selected */
808 if (gps->flag & GP_STROKE_SELECT) {
809 /* skip strokes that are invalid for current view */
810 if (ED_gpencil_stroke_can_use(C, gps) == false) {
813 /* check if the color is editable */
814 if (ED_gpencil_stroke_color_use(gpl, gps) == false) {
817 /* some stroke is already at front*/
818 if ((direction == GP_STROKE_MOVE_TOP) || (direction == GP_STROKE_MOVE_UP)) {
819 if (gps == gpf->strokes.last) {
820 return OPERATOR_CANCELLED;
823 /* some stroke is already at botom */
824 if ((direction == GP_STROKE_MOVE_BOTTOM) || (direction == GP_STROKE_MOVE_DOWN)) {
825 if (gps == gpf->strokes.first) {
826 return OPERATOR_CANCELLED;
830 BLI_addtail(&selected, BLI_genericNodeN(gps));
834 /* Now do the movement of the stroke */
837 case GP_STROKE_MOVE_TOP:
838 for (LinkData *link = selected.first; link; link = link->next) {
840 BLI_remlink(&gpf->strokes, gps);
841 BLI_addtail(&gpf->strokes, gps);
845 case GP_STROKE_MOVE_UP:
846 for (LinkData *link = selected.last; link; link = link->prev) {
848 BLI_listbase_link_move(&gpf->strokes, gps, 1);
852 case GP_STROKE_MOVE_DOWN:
853 for (LinkData *link = selected.first; link; link = link->next) {
855 BLI_listbase_link_move(&gpf->strokes, gps, -1);
859 case GP_STROKE_MOVE_BOTTOM:
860 for (LinkData *link = selected.last; link; link = link->prev) {
862 BLI_remlink(&gpf->strokes, gps);
863 BLI_addhead(&gpf->strokes, gps);
870 BLI_freelistN(&selected);
873 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
875 return OPERATOR_FINISHED;
878 void GPENCIL_OT_stroke_arrange(wmOperatorType *ot)
880 static const EnumPropertyItem slot_move[] = {
881 {GP_STROKE_MOVE_UP, "UP", 0, "Bring Forward", ""},
882 {GP_STROKE_MOVE_DOWN, "DOWN", 0, "Send Backward", ""},
883 {GP_STROKE_MOVE_TOP, "TOP", 0, "Bring to Front", ""},
884 {GP_STROKE_MOVE_BOTTOM, "BOTTOM", 0, "Send to Back", ""},
885 {0, NULL, 0, NULL, NULL }
889 ot->name = "Arrange Stroke";
890 ot->idname = "GPENCIL_OT_stroke_arrange";
891 ot->description = "Arrange selected strokes up/down in the drawing order of the active layer";
894 ot->exec = gp_stroke_arrange_exec;
895 ot->poll = gp_active_layer_poll;
898 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
900 ot->prop = RNA_def_enum(ot->srna, "direction", slot_move, GP_STROKE_MOVE_UP, "Direction", "");
902 /* ******************* Move Stroke to new color ************************** */
904 static int gp_stroke_change_color_exec(bContext *C, wmOperator *UNUSED(op))
906 bGPdata *gpd = ED_gpencil_data_get_active(C);
907 bGPDpalette *palette;
908 bGPDpalettecolor *color;
911 if (ELEM(NULL, gpd)) {
912 return OPERATOR_CANCELLED;
915 palette = BKE_gpencil_palette_getactive(gpd);
916 color = BKE_gpencil_palettecolor_getactive(palette);
917 if (ELEM(NULL, palette, color)) {
918 return OPERATOR_CANCELLED;
921 /* loop all strokes */
922 for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
923 /* only editable and visible layers are considered */
924 if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
925 for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
926 /* only if selected */
927 if (gps->flag & GP_STROKE_SELECT) {
928 /* skip strokes that are invalid for current view */
929 if (ED_gpencil_stroke_can_use(C, gps) == false)
931 /* check if the color is editable */
932 if (ED_gpencil_stroke_color_use(gpl, gps) == false)
935 /* asign new color (only if different) */
936 if ((STREQ(gps->colorname, color->info) == false) || (gps->palcolor != color)) {
937 BLI_strncpy(gps->colorname, color->info, sizeof(gps->colorname));
938 gps->palcolor = color;
945 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
947 return OPERATOR_FINISHED;
950 void GPENCIL_OT_stroke_change_color(wmOperatorType *ot)
953 ot->name = "Change Stroke Color";
954 ot->idname = "GPENCIL_OT_stroke_change_color";
955 ot->description = "Move selected strokes to active color";
958 ot->exec = gp_stroke_change_color_exec;
959 ot->poll = gp_active_layer_poll;
962 /* ******************* Lock color of non selected Strokes colors ************************** */
964 static int gp_stroke_lock_color_exec(bContext *C, wmOperator *UNUSED(op))
966 bGPdata *gpd = ED_gpencil_data_get_active(C);
967 bGPDpalette *palette;
971 return OPERATOR_CANCELLED;
973 palette = BKE_gpencil_palette_getactive(gpd);
974 if (ELEM(NULL, palette))
975 return OPERATOR_CANCELLED;
977 /* first lock all colors */
978 for (bGPDpalettecolor *palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
979 palcolor->flag |= PC_COLOR_LOCKED;
982 /* loop all selected strokes and unlock any color */
983 for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
984 /* only editable and visible layers are considered */
985 if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
986 for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
987 /* only if selected */
988 if (gps->flag & GP_STROKE_SELECT) {
989 /* skip strokes that are invalid for current view */
990 if (ED_gpencil_stroke_can_use(C, gps) == false) {
994 if (gps->palcolor != NULL) {
995 gps->palcolor->flag &= ~PC_COLOR_LOCKED;
1002 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1004 return OPERATOR_FINISHED;
1007 void GPENCIL_OT_stroke_lock_color(wmOperatorType *ot)
1010 ot->name = "Lock Unused Colors";
1011 ot->idname = "GPENCIL_OT_stroke_lock_color";
1012 ot->description = "Lock any color not used in any selected stroke";
1015 ot->exec = gp_stroke_lock_color_exec;
1016 ot->poll = gp_active_layer_poll;
1019 /* ************************************************ */
1020 /* Drawing Brushes Operators */
1022 /* ******************* Add New Brush ************************ */
1024 /* add new brush - wrapper around API */
1025 static int gp_brush_add_exec(bContext *C, wmOperator *op)
1027 ToolSettings *ts = CTX_data_tool_settings(C);
1029 /* if there's no existing container */
1031 BKE_report(op->reports, RPT_ERROR, "Nowhere for brush data to go");
1032 return OPERATOR_CANCELLED;
1034 /* add new brush now */
1035 BKE_gpencil_brush_addnew(ts, DATA_("GP_Brush"), true);
1038 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1040 return OPERATOR_FINISHED;
1043 void GPENCIL_OT_brush_add(wmOperatorType *ot)
1046 ot->name = "Add Brush";
1047 ot->idname = "GPENCIL_OT_brush_add";
1048 ot->description = "Add new Grease Pencil drawing brush for the active Grease Pencil data-block";
1050 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1053 ot->exec = gp_brush_add_exec;
1054 ot->poll = gp_add_poll;
1057 /* ******************* Remove Active Brush ************************* */
1059 static int gp_brush_remove_exec(bContext *C, wmOperator *op)
1061 ToolSettings *ts = CTX_data_tool_settings(C);
1062 bGPDbrush *brush = BKE_gpencil_brush_getactive(ts);
1065 if (ELEM(NULL, ts, brush))
1066 return OPERATOR_CANCELLED;
1068 if (BLI_listbase_count_at_most(&ts->gp_brushes, 2) < 2) {
1069 BKE_report(op->reports, RPT_ERROR, "Grease Pencil needs a brush, unable to delete the last one");
1070 return OPERATOR_CANCELLED;
1074 /* make the brush before this the new active brush
1075 * - use the one after if this is the first
1076 * - if this is the only brush, this naturally becomes NULL
1079 BKE_gpencil_brush_setactive(ts, brush->prev);
1081 BKE_gpencil_brush_setactive(ts, brush->next);
1083 /* delete the brush now... */
1084 BKE_gpencil_brush_delete(ts, brush);
1087 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1089 return OPERATOR_FINISHED;
1092 void GPENCIL_OT_brush_remove(wmOperatorType *ot)
1095 ot->name = "Remove Brush";
1096 ot->idname = "GPENCIL_OT_brush_remove";
1097 ot->description = "Remove active Grease Pencil drawing brush";
1099 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1102 ot->exec = gp_brush_remove_exec;
1103 ot->poll = gp_active_brush_poll;
1106 /* ********************** Change Brush ***************************** */
1108 static int gp_brush_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt))
1113 /* call the menu, which will call this operator again, hence the canceled */
1114 pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE);
1115 layout = UI_popup_menu_layout(pup);
1116 uiItemsEnumO(layout, "GPENCIL_OT_brush_change", "brush");
1117 UI_popup_menu_end(C, pup);
1119 return OPERATOR_INTERFACE;
1122 static int gp_brush_change_exec(bContext *C, wmOperator *op)
1124 ToolSettings *ts = CTX_data_tool_settings(C);
1125 bGPDbrush *brush = NULL;
1126 int brush_num = RNA_enum_get(op->ptr, "brush");
1128 /* Get brush or create new one */
1129 if (brush_num == -1) {
1131 brush = BKE_gpencil_brush_addnew(ts, DATA_("GP_Brush"), true);
1134 /* Try to get brush */
1135 brush = BLI_findlink(&ts->gp_brushes, brush_num);
1137 if (brush == NULL) {
1138 BKE_reportf(op->reports, RPT_ERROR, "Cannot change to non-existent brush (index = %d)", brush_num);
1139 return OPERATOR_CANCELLED;
1143 /* Set active brush */
1144 BKE_gpencil_brush_setactive(ts, brush);
1147 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1149 return OPERATOR_FINISHED;
1152 void GPENCIL_OT_brush_change(wmOperatorType *ot)
1155 ot->name = "Change Brush";
1156 ot->idname = "GPENCIL_OT_brush_change";
1157 ot->description = "Change active Grease Pencil drawing brush";
1160 ot->invoke = gp_brush_change_invoke;
1161 ot->exec = gp_brush_change_exec;
1162 ot->poll = gp_active_brush_poll;
1165 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1167 /* gp brush to use (dynamic enum) */
1168 ot->prop = RNA_def_enum(ot->srna, "brush", DummyRNA_DEFAULT_items, 0, "Grease Pencil Brush", "");
1169 RNA_def_enum_funcs(ot->prop, ED_gpencil_brushes_enum_itemf);
1172 /* ******************* Move Brush Up/Down ************************** */
1175 GP_BRUSH_MOVE_UP = -1,
1176 GP_BRUSH_MOVE_DOWN = 1
1179 static int gp_brush_move_exec(bContext *C, wmOperator *op)
1181 ToolSettings *ts = CTX_data_tool_settings(C);
1182 bGPDbrush *brush = BKE_gpencil_brush_getactive(ts);
1184 int direction = RNA_enum_get(op->ptr, "type");
1187 if (ELEM(NULL, ts, brush)) {
1188 return OPERATOR_CANCELLED;
1192 if (direction == GP_BRUSH_MOVE_UP) {
1194 BLI_remlink(&ts->gp_brushes, brush);
1195 BLI_insertlinkbefore(&ts->gp_brushes, brush->prev, brush);
1197 else if (direction == GP_BRUSH_MOVE_DOWN) {
1199 BLI_remlink(&ts->gp_brushes, brush);
1200 BLI_insertlinkafter(&ts->gp_brushes, brush->next, brush);
1207 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1209 return OPERATOR_FINISHED;
1212 void GPENCIL_OT_brush_move(wmOperatorType *ot)
1214 static const EnumPropertyItem slot_move[] = {
1215 {GP_BRUSH_MOVE_UP, "UP", 0, "Up", ""},
1216 {GP_BRUSH_MOVE_DOWN, "DOWN", 0, "Down", ""},
1217 {0, NULL, 0, NULL, NULL }
1221 ot->name = "Move Brush";
1222 ot->idname = "GPENCIL_OT_brush_move";
1223 ot->description = "Move the active Grease Pencil drawing brush up/down in the list";
1226 ot->exec = gp_brush_move_exec;
1227 ot->poll = gp_active_brush_poll;
1230 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1232 ot->prop = RNA_def_enum(ot->srna, "type", slot_move, GP_BRUSH_MOVE_UP, "Type", "");
1235 /* ******************* Brush create presets ************************** */
1237 static int gp_brush_presets_create_exec(bContext *C, wmOperator *UNUSED(op))
1239 ToolSettings *ts = CTX_data_tool_settings(C);
1240 BKE_gpencil_brush_init_presets(ts);
1243 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1245 return OPERATOR_FINISHED;
1248 void GPENCIL_OT_brush_presets_create(wmOperatorType *ot)
1251 ot->name = "Create Preset Brushes";
1252 ot->idname = "GPENCIL_OT_brush_presets_create";
1253 ot->description = "Create a set of predefined Grease Pencil drawing brushes";
1256 ot->exec = gp_brush_presets_create_exec;
1259 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1263 /* ***************** Copy Brush ************************ */
1265 static int gp_brush_copy_exec(bContext *C, wmOperator *op)
1267 ToolSettings *ts = CTX_data_tool_settings(C);
1269 /* if there's no existing container */
1271 BKE_report(op->reports, RPT_ERROR, "Nowhere for brush data to go");
1272 return OPERATOR_CANCELLED;
1275 bGPDbrush *brush = BKE_gpencil_brush_getactive(ts);
1276 bGPDbrush *newbrush;
1279 if (ELEM(NULL, brush))
1280 return OPERATOR_CANCELLED;
1282 /* create a brush and duplicate data */
1283 newbrush = BKE_gpencil_brush_addnew(ts, brush->info, true);
1284 newbrush->thickness = brush->thickness;
1285 newbrush->draw_smoothfac = brush->draw_smoothfac;
1286 newbrush->draw_smoothlvl = brush->draw_smoothlvl;
1287 newbrush->sublevel = brush->sublevel;
1288 newbrush->flag = brush->flag;
1289 newbrush->draw_sensitivity = brush->draw_sensitivity;
1290 newbrush->draw_strength = brush->draw_strength;
1291 newbrush->draw_jitter = brush->draw_jitter;
1292 newbrush->draw_angle = brush->draw_angle;
1293 newbrush->draw_angle_factor = brush->draw_angle_factor;
1294 newbrush->draw_random_press = brush->draw_random_press;
1295 newbrush->draw_random_sub = brush->draw_random_sub;
1297 /* free automatic curves created by default (replaced by copy) */
1298 curvemapping_free(newbrush->cur_sensitivity);
1299 curvemapping_free(newbrush->cur_strength);
1300 curvemapping_free(newbrush->cur_jitter);
1302 /* make a copy of curves */
1303 newbrush->cur_sensitivity = curvemapping_copy(brush->cur_sensitivity);
1304 newbrush->cur_strength = curvemapping_copy(brush->cur_strength);
1305 newbrush->cur_jitter = curvemapping_copy(brush->cur_jitter);
1307 BKE_gpencil_brush_setactive(ts, newbrush);
1309 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1311 return OPERATOR_FINISHED;
1314 void GPENCIL_OT_brush_copy(wmOperatorType *ot)
1317 ot->name = "Copy Brush";
1318 ot->idname = "GPENCIL_OT_brush_copy";
1319 ot->description = "Copy current Grease Pencil drawing brush";
1322 ot->exec = gp_brush_copy_exec;
1323 ot->poll = gp_active_brush_poll;
1326 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1329 /* ***************** Select Brush ************************ */
1331 static int gp_brush_select_exec(bContext *C, wmOperator *op)
1333 ToolSettings *ts = CTX_data_tool_settings(C);
1335 /* if there's no existing container */
1337 BKE_report(op->reports, RPT_ERROR, "Nowhere to go");
1338 return OPERATOR_CANCELLED;
1341 const int index = RNA_int_get(op->ptr, "index");
1342 bGPDbrush *brush = BLI_findlink(&ts->gp_brushes, index);
1344 if (ELEM(NULL, brush)) {
1345 return OPERATOR_CANCELLED;
1348 BKE_gpencil_brush_setactive(ts, brush);
1351 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1353 return OPERATOR_FINISHED;
1356 void GPENCIL_OT_brush_select(wmOperatorType *ot)
1359 ot->name = "Select Brush";
1360 ot->idname = "GPENCIL_OT_brush_select";
1361 ot->description = "Select a Grease Pencil drawing brush";
1364 ot->exec = gp_brush_select_exec;
1365 ot->poll = gp_active_brush_poll;
1368 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1371 RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "Index of Drawing Brush", 0, INT_MAX);
1374 /* ************************************************ */
1375 /* Palette Operators */
1377 /* ******************* Add New Palette ************************ */
1379 /* add new palette - wrapper around API */
1380 static int gp_palette_add_exec(bContext *C, wmOperator *op)
1382 Main *bmain = CTX_data_main(C);
1383 bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
1385 /* if there's no existing Grease-Pencil data there, add some */
1386 if (gpd_ptr == NULL) {
1387 BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go");
1388 return OPERATOR_CANCELLED;
1390 if (*gpd_ptr == NULL)
1391 *gpd_ptr = BKE_gpencil_data_addnew(bmain, DATA_("GPencil"));
1393 /* add new palette now */
1394 BKE_gpencil_palette_addnew(*gpd_ptr, DATA_("GP_Palette"), true);
1397 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1399 return OPERATOR_FINISHED;
1402 void GPENCIL_OT_palette_add(wmOperatorType *ot)
1405 ot->name = "Add Palette";
1406 ot->idname = "GPENCIL_OT_palette_add";
1407 ot->description = "Add new Grease Pencil palette for the active Grease Pencil data-block";
1409 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1412 ot->exec = gp_palette_add_exec;
1413 ot->poll = gp_add_poll;
1416 /* ******************* Remove Active Palette ************************* */
1418 static int gp_palette_remove_exec(bContext *C, wmOperator *op)
1420 bGPdata *gpd = ED_gpencil_data_get_active(C);
1421 bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
1424 if (ELEM(NULL, gpd, palette))
1425 return OPERATOR_CANCELLED;
1427 if (BLI_listbase_count_at_most(&gpd->palettes, 2) < 2) {
1428 BKE_report(op->reports, RPT_ERROR, "Grease Pencil needs a palette, unable to delete the last one");
1429 return OPERATOR_CANCELLED;
1433 /* make the palette before this the new active palette
1434 * - use the one after if this is the first
1435 * - if this is the only palette, this naturally becomes NULL
1438 BKE_gpencil_palette_setactive(gpd, palette->prev);
1440 BKE_gpencil_palette_setactive(gpd, palette->next);
1442 /* delete the palette now... */
1443 BKE_gpencil_palette_delete(gpd, palette);
1446 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1448 return OPERATOR_FINISHED;
1451 void GPENCIL_OT_palette_remove(wmOperatorType *ot)
1454 ot->name = "Remove palette";
1455 ot->idname = "GPENCIL_OT_palette_remove";
1456 ot->description = "Remove active Grease Pencil palette";
1458 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1461 ot->exec = gp_palette_remove_exec;
1462 ot->poll = gp_active_palette_poll;
1465 /* ********************** Change Palette ***************************** */
1467 static int gp_palette_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt))
1472 /* call the menu, which will call this operator again, hence the canceled */
1473 pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE);
1474 layout = UI_popup_menu_layout(pup);
1475 uiItemsEnumO(layout, "GPENCIL_OT_palette_change", "palette");
1476 UI_popup_menu_end(C, pup);
1478 return OPERATOR_INTERFACE;
1481 static int gp_palette_change_exec(bContext *C, wmOperator *op)
1483 bGPdata *gpd = CTX_data_gpencil_data(C);
1484 bGPDpalette *palette = NULL;
1485 int palette_num = RNA_enum_get(op->ptr, "palette");
1487 /* Get palette or create new one */
1488 if (palette_num == -1) {
1489 /* Create palette */
1490 palette = BKE_gpencil_palette_addnew(gpd, DATA_("GP_Palette"), true);
1493 /* Try to get palette */
1494 palette = BLI_findlink(&gpd->palettes, palette_num);
1496 if (palette == NULL) {
1497 BKE_reportf(op->reports, RPT_ERROR, "Cannot change to non-existent palette (index = %d)", palette_num);
1498 return OPERATOR_CANCELLED;
1502 /* Set active palette */
1503 BKE_gpencil_palette_setactive(gpd, palette);
1506 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1508 return OPERATOR_FINISHED;
1511 void GPENCIL_OT_palette_change(wmOperatorType *ot)
1514 ot->name = "Change Palette";
1515 ot->idname = "GPENCIL_OT_palette_change";
1516 ot->description = "Change active Grease Pencil palette";
1519 ot->invoke = gp_palette_change_invoke;
1520 ot->exec = gp_palette_change_exec;
1521 ot->poll = gp_active_palette_poll;
1524 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1526 /* gp palette to use (dynamic enum) */
1527 ot->prop = RNA_def_enum(ot->srna, "palette", DummyRNA_DEFAULT_items, 0, "Grease Pencil Palette", "");
1528 RNA_def_enum_funcs(ot->prop, ED_gpencil_palettes_enum_itemf);
1531 /* ******************* Lock and hide any color non used in current layer ************************** */
1533 static int gp_palette_lock_layer_exec(bContext *C, wmOperator *UNUSED(op))
1535 bGPdata *gpd = ED_gpencil_data_get_active(C);
1536 bGPDpalette *palette;
1539 if (ELEM(NULL, gpd))
1540 return OPERATOR_CANCELLED;
1542 palette = BKE_gpencil_palette_getactive(gpd);
1543 if (ELEM(NULL, palette))
1544 return OPERATOR_CANCELLED;
1546 /* first lock and hide all colors */
1547 for (bGPDpalettecolor *palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
1548 palcolor->flag |= PC_COLOR_LOCKED;
1549 palcolor->flag |= PC_COLOR_HIDE;
1552 /* loop all selected strokes and unlock any color used in active layer */
1553 for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
1554 /* only editable and visible layers are considered */
1555 if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL) && (gpl->flag & GP_LAYER_ACTIVE)) {
1556 for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
1557 /* skip strokes that are invalid for current view */
1558 if (ED_gpencil_stroke_can_use(C, gps) == false)
1561 /* unlock/unhide color if not unlocked before */
1562 if (gps->palcolor != NULL) {
1563 gps->palcolor->flag &= ~PC_COLOR_LOCKED;
1564 gps->palcolor->flag &= ~PC_COLOR_HIDE;
1570 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1572 return OPERATOR_FINISHED;
1575 void GPENCIL_OT_palette_lock_layer(wmOperatorType *ot)
1578 ot->name = "Disable Unused Layer Colors";
1579 ot->idname = "GPENCIL_OT_palette_lock_layer";
1580 ot->description = "Lock and hide any color not used in any layer";
1583 ot->exec = gp_palette_lock_layer_exec;
1584 ot->poll = gp_active_layer_poll;
1587 /* ************************************************ */
1588 /* Palette Colors Operators */
1590 /* ******************* Add New Palette ************************ */
1592 /* add new palette - wrapper around API */
1593 static int gp_palettecolor_add_exec(bContext *C, wmOperator *op)
1595 Main *bmain = CTX_data_main(C);
1596 bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
1598 /* if there's no existing Grease-Pencil data there, add some */
1599 if (gpd_ptr == NULL) {
1600 BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go");
1601 return OPERATOR_CANCELLED;
1603 if (*gpd_ptr == NULL)
1604 *gpd_ptr = BKE_gpencil_data_addnew(bmain, DATA_("GPencil"));
1606 /* verify palette */
1607 bGPDpalette *palette = BKE_gpencil_palette_getactive(*gpd_ptr);
1608 if (palette == NULL)
1609 palette = BKE_gpencil_palette_addnew(*gpd_ptr, DATA_("GP_Palette"), true);
1611 /* add new palette color now */
1612 BKE_gpencil_palettecolor_addnew(palette, DATA_("Color"), true);
1615 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1617 return OPERATOR_FINISHED;
1620 void GPENCIL_OT_palettecolor_add(wmOperatorType *ot)
1623 ot->name = "Add Palette Color";
1624 ot->idname = "GPENCIL_OT_palettecolor_add";
1625 ot->description = "Add new Grease Pencil palette color for the active Grease Pencil data-block";
1627 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1630 ot->exec = gp_palettecolor_add_exec;
1631 ot->poll = gp_add_poll;
1634 /* ******************* Remove Active Palette color ************************* */
1636 static int gp_palettecolor_remove_exec(bContext *C, wmOperator *UNUSED(op))
1638 bGPdata *gpd = ED_gpencil_data_get_active(C);
1639 bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
1640 bGPDpalettecolor *color = BKE_gpencil_palettecolor_getactive(palette);
1643 if (ELEM(NULL, gpd, palette, color))
1644 return OPERATOR_CANCELLED;
1646 /* make the palette color before this the new active color
1647 * - use the one after if this is the first
1648 * - if this is the only color, this naturally becomes NULL
1651 BKE_gpencil_palettecolor_setactive(palette, color->prev);
1653 BKE_gpencil_palettecolor_setactive(palette, color->next);
1655 /* delete the strokes */
1656 BKE_gpencil_palettecolor_delete_strokes(gpd, color->info);
1658 /* delete the palette color now... */
1659 BKE_gpencil_palettecolor_delete(palette, color);
1662 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1664 return OPERATOR_FINISHED;
1667 void GPENCIL_OT_palettecolor_remove(wmOperatorType *ot)
1670 ot->name = "Remove palette color";
1671 ot->idname = "GPENCIL_OT_palettecolor_remove";
1672 ot->description = "Remove active Grease Pencil palette color";
1674 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1677 ot->exec = gp_palettecolor_remove_exec;
1678 ot->poll = gp_active_palettecolor_poll;
1681 /* ********************** Isolate palette color **************************** */
1683 static int gp_isolate_palettecolor_exec(bContext *C, wmOperator *op)
1685 bGPdata *gpd = ED_gpencil_data_get_active(C);
1686 bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
1687 bGPDpalettecolor *active_color = BKE_gpencil_palettecolor_getactive(palette);
1688 bGPDpalettecolor *palcolor;
1690 int flags = PC_COLOR_LOCKED;
1691 bool isolate = false;
1693 if (RNA_boolean_get(op->ptr, "affect_visibility"))
1694 flags |= PC_COLOR_HIDE;
1696 if (ELEM(NULL, gpd, active_color)) {
1697 BKE_report(op->reports, RPT_ERROR, "No active color to isolate");
1698 return OPERATOR_CANCELLED;
1701 /* Test whether to isolate or clear all flags */
1702 for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
1703 /* Skip if this is the active one */
1704 if (palcolor == active_color)
1707 /* If the flags aren't set, that means that the color is
1708 * not alone, so we have some colors to isolate still
1710 if ((palcolor->flag & flags) == 0) {
1716 /* Set/Clear flags as appropriate */
1718 /* Set flags on all "other" colors */
1719 for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
1720 if (palcolor == active_color)
1723 palcolor->flag |= flags;
1727 /* Clear flags - Restore everything else */
1728 for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
1729 palcolor->flag &= ~flags;
1734 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1736 return OPERATOR_FINISHED;
1739 void GPENCIL_OT_palettecolor_isolate(wmOperatorType *ot)
1742 ot->name = "Isolate Palette Color";
1743 ot->idname = "GPENCIL_OT_palettecolor_isolate";
1744 ot->description = "Toggle whether the active color is the only one that is editable and/or visible";
1747 ot->exec = gp_isolate_palettecolor_exec;
1748 ot->poll = gp_active_palettecolor_poll;
1751 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1754 RNA_def_boolean(ot->srna, "affect_visibility", false, "Affect Visibility", "In addition to toggling "
1755 "the editability, also affect the visibility");
1758 /* *********************** Hide Palette colors ******************************** */
1760 static int gp_palettecolor_hide_exec(bContext *C, wmOperator *op)
1762 bGPdata *gpd = ED_gpencil_data_get_active(C);
1763 bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
1764 bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette);
1766 bool unselected = RNA_boolean_get(op->ptr, "unselected");
1769 if (ELEM(NULL, gpd, palette, palcolor))
1770 return OPERATOR_CANCELLED;
1773 bGPDpalettecolor *color;
1775 /* hide unselected */
1776 for (color = palette->colors.first; color; color = color->next) {
1777 if (color != palcolor) {
1778 color->flag |= PC_COLOR_HIDE;
1783 /* hide selected/active */
1784 palcolor->flag |= PC_COLOR_HIDE;
1788 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1790 return OPERATOR_FINISHED;
1793 void GPENCIL_OT_palettecolor_hide(wmOperatorType *ot)
1796 ot->name = "Hide Color(s)";
1797 ot->idname = "GPENCIL_OT_palettecolor_hide";
1798 ot->description = "Hide selected/unselected Grease Pencil colors";
1801 ot->exec = gp_palettecolor_hide_exec;
1802 ot->poll = gp_active_palettecolor_poll; /* NOTE: we need an active color to play with */
1805 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1808 RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected colors");
1811 /* ********************** Show All Colors ***************************** */
1813 /* poll callback for showing colors */
1814 static int gp_palettecolor_reveal_poll(bContext *C)
1816 return ED_gpencil_data_get_active(C) != NULL;
1819 static int gp_palettecolor_reveal_exec(bContext *C, wmOperator *UNUSED(op))
1821 bGPdata *gpd = ED_gpencil_data_get_active(C);
1822 bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
1823 bGPDpalettecolor *palcolor;
1826 if (ELEM(NULL, gpd, palette))
1827 return OPERATOR_CANCELLED;
1829 /* make all colors visible */
1830 for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
1831 palcolor->flag &= ~PC_COLOR_HIDE;
1835 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1837 return OPERATOR_FINISHED;
1840 void GPENCIL_OT_palettecolor_reveal(wmOperatorType *ot)
1843 ot->name = "Show All Colors";
1844 ot->idname = "GPENCIL_OT_palettecolor_reveal";
1845 ot->description = "Unhide all hidden Grease Pencil palette colors";
1848 ot->exec = gp_palettecolor_reveal_exec;
1849 ot->poll = gp_palettecolor_reveal_poll;
1852 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1855 /* ***************** Lock/Unlock All Palette colors ************************ */
1857 static int gp_palettecolor_lock_all_exec(bContext *C, wmOperator *UNUSED(op))
1859 bGPdata *gpd = ED_gpencil_data_get_active(C);
1860 bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
1861 bGPDpalettecolor *palcolor;
1864 if (ELEM(NULL, gpd, palette))
1865 return OPERATOR_CANCELLED;
1867 /* make all layers non-editable */
1868 for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
1869 palcolor->flag |= PC_COLOR_LOCKED;
1873 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1875 return OPERATOR_FINISHED;
1878 void GPENCIL_OT_palettecolor_lock_all(wmOperatorType *ot)
1881 ot->name = "Lock All Colors";
1882 ot->idname = "GPENCIL_OT_palettecolor_lock_all";
1883 ot->description = "Lock all Grease Pencil colors to prevent them from being accidentally modified";
1886 ot->exec = gp_palettecolor_lock_all_exec;
1887 ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */
1890 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1893 /* -------------------------- */
1895 static int gp_palettecolor_unlock_all_exec(bContext *C, wmOperator *UNUSED(op))
1897 bGPdata *gpd = ED_gpencil_data_get_active(C);
1898 bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
1899 bGPDpalettecolor *palcolor;
1902 if (ELEM(NULL, gpd, palette))
1903 return OPERATOR_CANCELLED;
1905 /* make all layers editable again*/
1906 for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
1907 palcolor->flag &= ~PC_COLOR_LOCKED;
1911 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1913 return OPERATOR_FINISHED;
1916 void GPENCIL_OT_palettecolor_unlock_all(wmOperatorType *ot)
1919 ot->name = "Unlock All Colors";
1920 ot->idname = "GPENCIL_OT_palettecolor_unlock_all";
1921 ot->description = "Unlock all Grease Pencil colors so that they can be edited";
1924 ot->exec = gp_palettecolor_unlock_all_exec;
1925 ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */
1928 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1931 /* ******************* Move Color Up/Down ************************** */
1934 GP_COLOR_MOVE_UP = -1,
1935 GP_COLOR_MOVE_DOWN = 1
1938 static int gp_palettecolor_move_exec(bContext *C, wmOperator *op)
1940 bGPdata *gpd = ED_gpencil_data_get_active(C);
1941 bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
1942 bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette);
1944 int direction = RNA_enum_get(op->ptr, "direction");
1947 if (ELEM(NULL, gpd, palette, palcolor))
1948 return OPERATOR_CANCELLED;
1951 if (direction == GP_COLOR_MOVE_UP) {
1953 BLI_remlink(&palette->colors, palcolor);
1954 BLI_insertlinkbefore(&palette->colors, palcolor->prev, palcolor);
1956 else if (direction == GP_COLOR_MOVE_DOWN) {
1958 BLI_remlink(&palette->colors, palcolor);
1959 BLI_insertlinkafter(&palette->colors, palcolor->next, palcolor);
1966 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1968 return OPERATOR_FINISHED;
1971 void GPENCIL_OT_palettecolor_move(wmOperatorType *ot)
1973 static const EnumPropertyItem slot_move[] = {
1974 {GP_COLOR_MOVE_UP, "UP", 0, "Up", ""},
1975 {GP_COLOR_MOVE_DOWN, "DOWN", 0, "Down", ""},
1976 {0, NULL, 0, NULL, NULL}
1980 ot->name = "Move Palette color";
1981 ot->idname = "GPENCIL_OT_palettecolor_move";
1982 ot->description = "Move the active Grease Pencil palette color up/down in the list";
1985 ot->exec = gp_palettecolor_move_exec;
1986 ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */
1989 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1991 ot->prop = RNA_def_enum(ot->srna, "direction", slot_move, GP_COLOR_MOVE_UP, "Direction", "");
1994 /* ***************** Select all strokes using Palette color ************************ */
1996 static int gp_palettecolor_select_exec(bContext *C, wmOperator *UNUSED(op))
1998 bGPdata *gpd = ED_gpencil_data_get_active(C);
1999 bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
2000 bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette);
2003 if (ELEM(NULL, gpd, palette, palcolor))
2004 return OPERATOR_CANCELLED;
2006 /* read all strokes and select*/
2007 for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
2008 /* only editable and visible layers are considered */
2009 if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
2010 /* verify something to do */
2011 for (bGPDstroke *gps = gpl->actframe->strokes.first; gps; gps = gps->next) {
2012 /* skip strokes that are invalid for current view */
2013 if (ED_gpencil_stroke_can_use(C, gps) == false)
2015 /* check if the color is editable */
2016 if (ED_gpencil_stroke_color_use(gpl, gps) == false)
2020 if (strcmp(palcolor->info, gps->colorname) == 0) {
2024 gps->flag |= GP_STROKE_SELECT;
2025 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
2026 pt->flag |= GP_SPOINT_SELECT;
2033 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
2035 return OPERATOR_FINISHED;
2038 void GPENCIL_OT_palettecolor_select(wmOperatorType *ot)
2041 ot->name = "Select Color";
2042 ot->idname = "GPENCIL_OT_palettecolor_select";
2043 ot->description = "Select all Grease Pencil strokes using current color";
2046 ot->exec = gp_palettecolor_select_exec;
2047 ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */
2050 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2053 /* ***************** Copy Palette color ************************ */
2055 static int gp_palettecolor_copy_exec(bContext *C, wmOperator *UNUSED(op))
2057 bGPdata *gpd = ED_gpencil_data_get_active(C);
2058 bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
2059 bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette);
2060 bGPDpalettecolor *newcolor;
2063 if (ELEM(NULL, gpd, palette, palcolor))
2064 return OPERATOR_CANCELLED;
2066 /* create a new color and duplicate data */
2067 newcolor = BKE_gpencil_palettecolor_addnew(palette, palcolor->info, true);
2068 copy_v4_v4(newcolor->color, palcolor->color);
2069 copy_v4_v4(newcolor->fill, palcolor->fill);
2070 newcolor->flag = palcolor->flag;
2073 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
2075 return OPERATOR_FINISHED;
2078 void GPENCIL_OT_palettecolor_copy(wmOperatorType *ot)
2081 ot->name = "Copy Color";
2082 ot->idname = "GPENCIL_OT_palettecolor_copy";
2083 ot->description = "Copy current Grease Pencil palette color";
2086 ot->exec = gp_palettecolor_copy_exec;
2087 ot->poll = gp_active_palettecolor_poll;
2090 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;