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_context.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"
64 #include "UI_interface.h"
65 #include "UI_resources.h"
70 #include "RNA_access.h"
71 #include "RNA_define.h"
72 #include "RNA_enum_types.h"
74 #include "ED_gpencil.h"
76 #include "gpencil_intern.h"
78 /* ************************************************ */
79 /* Datablock Operators */
81 /* ******************* Add New Data ************************ */
83 /* add new datablock - wrapper around API */
84 static int gp_data_add_exec(bContext *C, wmOperator *op)
86 bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
87 ToolSettings *ts = CTX_data_tool_settings(C);
89 if (gpd_ptr == NULL) {
90 BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go");
91 return OPERATOR_CANCELLED;
94 /* decrement user count and add new datablock */
95 bGPdata *gpd = (*gpd_ptr);
98 *gpd_ptr = BKE_gpencil_data_addnew(DATA_("GPencil"));
100 /* if not exist brushes, create a new set */
102 if (BLI_listbase_is_empty(&ts->gp_brushes)) {
103 /* create new brushes */
104 BKE_gpencil_brush_init_presets(ts);
111 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
113 return OPERATOR_FINISHED;
116 void GPENCIL_OT_data_add(wmOperatorType *ot)
119 ot->name = "Grease Pencil Add New";
120 ot->idname = "GPENCIL_OT_data_add";
121 ot->description = "Add new Grease Pencil data-block";
122 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
125 ot->exec = gp_data_add_exec;
126 ot->poll = gp_add_poll;
129 /* ******************* Unlink Data ************************ */
131 /* poll callback for adding data/layers - special */
132 static int gp_data_unlink_poll(bContext *C)
134 bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
136 /* if we have access to some active data, make sure there's a datablock before enabling this */
137 return (gpd_ptr && *gpd_ptr);
141 /* unlink datablock - wrapper around API */
142 static int gp_data_unlink_exec(bContext *C, wmOperator *op)
144 bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
146 if (gpd_ptr == NULL) {
147 BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go");
148 return OPERATOR_CANCELLED;
151 /* just unlink datablock now, decreasing its user count */
152 bGPdata *gpd = (*gpd_ptr);
159 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
161 return OPERATOR_FINISHED;
164 void GPENCIL_OT_data_unlink(wmOperatorType *ot)
167 ot->name = "Grease Pencil Unlink";
168 ot->idname = "GPENCIL_OT_data_unlink";
169 ot->description = "Unlink active Grease Pencil data-block";
170 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
173 ot->exec = gp_data_unlink_exec;
174 ot->poll = gp_data_unlink_poll;
178 /* ************************************************ */
179 /* Layer Operators */
181 /* ******************* Add New Layer ************************ */
183 /* add new layer - wrapper around API */
184 static int gp_layer_add_exec(bContext *C, wmOperator *op)
186 bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
187 ToolSettings *ts = CTX_data_tool_settings(C);
189 /* if there's no existing Grease-Pencil data there, add some */
190 if (gpd_ptr == NULL) {
191 BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go");
192 return OPERATOR_CANCELLED;
194 if (*gpd_ptr == NULL)
195 *gpd_ptr = BKE_gpencil_data_addnew(DATA_("GPencil"));
197 /* if not exist brushes, create a new set */
199 if (BLI_listbase_is_empty(&ts->gp_brushes)) {
200 /* create new brushes */
201 BKE_gpencil_brush_init_presets(ts);
205 /* add new layer now */
206 BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("GP_Layer"), true);
209 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
211 return OPERATOR_FINISHED;
214 void GPENCIL_OT_layer_add(wmOperatorType *ot)
217 ot->name = "Add New Layer";
218 ot->idname = "GPENCIL_OT_layer_add";
219 ot->description = "Add new Grease Pencil layer for the active Grease Pencil data-block";
221 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
224 ot->exec = gp_layer_add_exec;
225 ot->poll = gp_add_poll;
228 /* ******************* Remove Active Layer ************************* */
230 static int gp_layer_remove_exec(bContext *C, wmOperator *op)
232 bGPdata *gpd = ED_gpencil_data_get_active(C);
233 bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
236 if (ELEM(NULL, gpd, gpl))
237 return OPERATOR_CANCELLED;
239 if (gpl->flag & GP_LAYER_LOCKED) {
240 BKE_report(op->reports, RPT_ERROR, "Cannot delete locked layers");
241 return OPERATOR_CANCELLED;
244 /* make the layer before this the new active layer
245 * - use the one after if this is the first
246 * - if this is the only layer, this naturally becomes NULL
249 BKE_gpencil_layer_setactive(gpd, gpl->prev);
251 BKE_gpencil_layer_setactive(gpd, gpl->next);
253 /* delete the layer now... */
254 BKE_gpencil_layer_delete(gpd, gpl);
257 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
259 return OPERATOR_FINISHED;
262 void GPENCIL_OT_layer_remove(wmOperatorType *ot)
265 ot->name = "Remove Layer";
266 ot->idname = "GPENCIL_OT_layer_remove";
267 ot->description = "Remove active Grease Pencil layer";
269 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
272 ot->exec = gp_layer_remove_exec;
273 ot->poll = gp_active_layer_poll;
276 /* ******************* Move Layer Up/Down ************************** */
279 GP_LAYER_MOVE_UP = -1,
280 GP_LAYER_MOVE_DOWN = 1
283 static int gp_layer_move_exec(bContext *C, wmOperator *op)
285 bGPdata *gpd = ED_gpencil_data_get_active(C);
286 bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
288 int direction = RNA_enum_get(op->ptr, "type");
291 if (ELEM(NULL, gpd, gpl))
292 return OPERATOR_CANCELLED;
294 BLI_assert(ELEM(direction, -1, 0, 1)); /* we use value below */
295 if (BLI_listbase_link_move(&gpd->layers, gpl, direction)) {
296 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
299 return OPERATOR_FINISHED;
302 void GPENCIL_OT_layer_move(wmOperatorType *ot)
304 static const EnumPropertyItem slot_move[] = {
305 {GP_LAYER_MOVE_UP, "UP", 0, "Up", ""},
306 {GP_LAYER_MOVE_DOWN, "DOWN", 0, "Down", ""},
307 {0, NULL, 0, NULL, NULL}
311 ot->name = "Move Grease Pencil Layer";
312 ot->idname = "GPENCIL_OT_layer_move";
313 ot->description = "Move the active Grease Pencil layer up/down in the list";
316 ot->exec = gp_layer_move_exec;
317 ot->poll = gp_active_layer_poll;
320 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
322 ot->prop = RNA_def_enum(ot->srna, "type", slot_move, 0, "Type", "");
325 /* ********************* Duplicate Layer ************************** */
327 static int gp_layer_copy_exec(bContext *C, wmOperator *UNUSED(op))
329 bGPdata *gpd = ED_gpencil_data_get_active(C);
330 bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
331 bGPDlayer *new_layer;
334 if (ELEM(NULL, gpd, gpl))
335 return OPERATOR_CANCELLED;
337 /* make copy of layer, and add it immediately after the existing layer */
338 new_layer = BKE_gpencil_layer_duplicate(gpl);
339 BLI_insertlinkafter(&gpd->layers, gpl, new_layer);
341 /* ensure new layer has a unique name, and is now the active layer */
342 BLI_uniquename(&gpd->layers, new_layer, DATA_("GP_Layer"), '.', offsetof(bGPDlayer, info), sizeof(new_layer->info));
343 BKE_gpencil_layer_setactive(gpd, new_layer);
346 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
348 return OPERATOR_FINISHED;
351 void GPENCIL_OT_layer_duplicate(wmOperatorType *ot)
354 ot->name = "Duplicate Layer";
355 ot->idname = "GPENCIL_OT_layer_duplicate";
356 ot->description = "Make a copy of the active Grease Pencil layer";
359 ot->exec = gp_layer_copy_exec;
360 ot->poll = gp_active_layer_poll;
363 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
366 /* *********************** Hide Layers ******************************** */
368 static int gp_hide_exec(bContext *C, wmOperator *op)
370 bGPdata *gpd = ED_gpencil_data_get_active(C);
371 bGPDlayer *layer = BKE_gpencil_layer_getactive(gpd);
372 bool unselected = RNA_boolean_get(op->ptr, "unselected");
375 if (ELEM(NULL, gpd, layer))
376 return OPERATOR_CANCELLED;
381 /* hide unselected */
382 for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
384 gpl->flag |= GP_LAYER_HIDE;
389 /* hide selected/active */
390 layer->flag |= GP_LAYER_HIDE;
394 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
396 return OPERATOR_FINISHED;
399 void GPENCIL_OT_hide(wmOperatorType *ot)
402 ot->name = "Hide Layer(s)";
403 ot->idname = "GPENCIL_OT_hide";
404 ot->description = "Hide selected/unselected Grease Pencil layers";
407 ot->exec = gp_hide_exec;
408 ot->poll = gp_active_layer_poll; /* NOTE: we need an active layer to play with */
411 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
414 RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected layers");
417 /* ********************** Show All Layers ***************************** */
419 /* poll callback for showing layers */
420 static int gp_reveal_poll(bContext *C)
422 return ED_gpencil_data_get_active(C) != NULL;
425 static void gp_reveal_select_frame(bContext *C, bGPDframe *frame, bool select)
428 for (gps = frame->strokes.first; gps; gps = gps->next) {
430 /* only deselect strokes that are valid in this view */
431 if (ED_gpencil_stroke_can_use(C, gps)) {
433 /* (de)select points */
436 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
437 SET_FLAG_FROM_TEST(pt->flag, select, GP_SPOINT_SELECT);
440 /* (de)select stroke */
441 SET_FLAG_FROM_TEST(gps->flag, select, GP_STROKE_SELECT);
446 static int gp_reveal_exec(bContext *C, wmOperator *op)
448 bGPdata *gpd = ED_gpencil_data_get_active(C);
450 const bool select = RNA_boolean_get(op->ptr, "select");
454 return OPERATOR_CANCELLED;
456 for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
458 if (gpl->flag & GP_LAYER_HIDE) {
459 gpl->flag &= ~GP_LAYER_HIDE;
461 /* select or deselect if requested, only on hidden layers */
462 if (gpd->flag & GP_DATA_STROKE_EDITMODE) {
464 /* select all strokes on active frame only (same as select all operator) */
466 gp_reveal_select_frame(C, gpl->actframe, true);
470 /* deselect strokes on all frames (same as deselect all operator) */
472 for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
473 gp_reveal_select_frame(C, gpf, false);
481 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
483 return OPERATOR_FINISHED;
486 void GPENCIL_OT_reveal(wmOperatorType *ot)
489 ot->name = "Show All Layers";
490 ot->idname = "GPENCIL_OT_reveal";
491 ot->description = "Show all Grease Pencil layers";
494 ot->exec = gp_reveal_exec;
495 ot->poll = gp_reveal_poll;
498 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
501 RNA_def_boolean(ot->srna, "select", true, "Select", "");
504 /* ***************** Lock/Unlock All Layers ************************ */
506 static int gp_lock_all_exec(bContext *C, wmOperator *UNUSED(op))
508 bGPdata *gpd = ED_gpencil_data_get_active(C);
513 return OPERATOR_CANCELLED;
515 /* make all layers non-editable */
516 for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
517 gpl->flag |= GP_LAYER_LOCKED;
521 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
523 return OPERATOR_FINISHED;
526 void GPENCIL_OT_lock_all(wmOperatorType *ot)
529 ot->name = "Lock All Layers";
530 ot->idname = "GPENCIL_OT_lock_all";
531 ot->description = "Lock all Grease Pencil layers to prevent them from being accidentally modified";
534 ot->exec = gp_lock_all_exec;
535 ot->poll = gp_reveal_poll; /* XXX: could use dedicated poll later */
538 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
541 /* -------------------------- */
543 static int gp_unlock_all_exec(bContext *C, wmOperator *UNUSED(op))
545 bGPdata *gpd = ED_gpencil_data_get_active(C);
550 return OPERATOR_CANCELLED;
552 /* make all layers editable again */
553 for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
554 gpl->flag &= ~GP_LAYER_LOCKED;
558 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
560 return OPERATOR_FINISHED;
563 void GPENCIL_OT_unlock_all(wmOperatorType *ot)
566 ot->name = "Unlock All Layers";
567 ot->idname = "GPENCIL_OT_unlock_all";
568 ot->description = "Unlock all Grease Pencil layers so that they can be edited";
571 ot->exec = gp_unlock_all_exec;
572 ot->poll = gp_reveal_poll; /* XXX: could use dedicated poll later */
575 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
578 /* ********************** Isolate Layer **************************** */
580 static int gp_isolate_layer_exec(bContext *C, wmOperator *op)
582 bGPdata *gpd = ED_gpencil_data_get_active(C);
583 bGPDlayer *layer = BKE_gpencil_layer_getactive(gpd);
585 int flags = GP_LAYER_LOCKED;
586 bool isolate = false;
588 if (RNA_boolean_get(op->ptr, "affect_visibility"))
589 flags |= GP_LAYER_HIDE;
591 if (ELEM(NULL, gpd, layer)) {
592 BKE_report(op->reports, RPT_ERROR, "No active layer to isolate");
593 return OPERATOR_CANCELLED;
596 /* Test whether to isolate or clear all flags */
597 for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
598 /* Skip if this is the active layer */
602 /* If the flags aren't set, that means that the layer is
603 * not alone, so we have some layers to isolate still
605 if ((gpl->flag & flags) == 0) {
611 /* Set/Clear flags as appropriate */
612 /* TODO: Include onionskinning on this list? */
614 /* Set flags on all "other" layers */
615 for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
623 /* Clear flags - Restore everything else */
624 for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
630 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
632 return OPERATOR_FINISHED;
635 void GPENCIL_OT_layer_isolate(wmOperatorType *ot)
638 ot->name = "Isolate Layer";
639 ot->idname = "GPENCIL_OT_layer_isolate";
640 ot->description = "Toggle whether the active layer is the only one that can be edited and/or visible";
643 ot->exec = gp_isolate_layer_exec;
644 ot->poll = gp_active_layer_poll;
647 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
650 RNA_def_boolean(ot->srna, "affect_visibility", false, "Affect Visibility",
651 "In addition to toggling the editability, also affect the visibility");
654 /* ********************** Merge Layer with the next layer **************************** */
656 static int gp_merge_layer_exec(bContext *C, wmOperator *op)
658 bGPdata *gpd = ED_gpencil_data_get_active(C);
659 bGPDlayer *gpl_current = BKE_gpencil_layer_getactive(gpd);
660 bGPDlayer *gpl_next = gpl_current->next;
662 if (ELEM(NULL, gpd, gpl_current, gpl_next)) {
663 BKE_report(op->reports, RPT_ERROR, "No layers to merge");
664 return OPERATOR_CANCELLED;
667 /* Collect frames of gpl_current in hash table to avoid O(n^2) lookups */
668 GHash *gh_frames_cur = BLI_ghash_int_new_ex(__func__, 64);
669 for (bGPDframe *gpf = gpl_current->frames.first; gpf; gpf = gpf->next) {
670 BLI_ghash_insert(gh_frames_cur, SET_INT_IN_POINTER(gpf->framenum), gpf);
673 /* read all frames from next layer */
674 for (bGPDframe *gpf = gpl_next->frames.first; gpf; gpf = gpf->next) {
675 /* try to find frame in active layer */
676 bGPDframe *frame = BLI_ghash_lookup(gh_frames_cur, SET_INT_IN_POINTER(gpf->framenum));
678 /* nothing found, create new */
679 frame = BKE_gpencil_frame_addnew(gpl_current, gpf->framenum);
681 /* add to tail all strokes */
682 BLI_movelisttolist(&frame->strokes, &gpf->strokes);
684 /* Now delete next layer */
685 BKE_gpencil_layer_delete(gpd, gpl_next);
686 BLI_ghash_free(gh_frames_cur, NULL, NULL);
689 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
691 return OPERATOR_FINISHED;
694 void GPENCIL_OT_layer_merge(wmOperatorType *ot)
697 ot->name = "Merge Down";
698 ot->idname = "GPENCIL_OT_layer_merge";
699 ot->description = "Merge the current layer with the layer below";
702 ot->exec = gp_merge_layer_exec;
703 ot->poll = gp_active_layer_poll;
706 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
709 /* ********************** Change Layer ***************************** */
711 static int gp_layer_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt))
716 /* call the menu, which will call this operator again, hence the canceled */
717 pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE);
718 layout = UI_popup_menu_layout(pup);
719 uiItemsEnumO(layout, "GPENCIL_OT_layer_change", "layer");
720 UI_popup_menu_end(C, pup);
722 return OPERATOR_INTERFACE;
725 static int gp_layer_change_exec(bContext *C, wmOperator *op)
727 bGPdata *gpd = CTX_data_gpencil_data(C);
728 bGPDlayer *gpl = NULL;
729 int layer_num = RNA_enum_get(op->ptr, "layer");
731 /* Get layer or create new one */
732 if (layer_num == -1) {
734 gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
737 /* Try to get layer */
738 gpl = BLI_findlink(&gpd->layers, layer_num);
741 BKE_reportf(op->reports, RPT_ERROR, "Cannot change to non-existent layer (index = %d)", layer_num);
742 return OPERATOR_CANCELLED;
746 /* Set active layer */
747 BKE_gpencil_layer_setactive(gpd, gpl);
750 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
752 return OPERATOR_FINISHED;
755 void GPENCIL_OT_layer_change(wmOperatorType *ot)
758 ot->name = "Change Layer";
759 ot->idname = "GPENCIL_OT_layer_change";
760 ot->description = "Change active Grease Pencil layer";
763 ot->invoke = gp_layer_change_invoke;
764 ot->exec = gp_layer_change_exec;
765 ot->poll = gp_active_layer_poll;
768 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
770 /* gp layer to use (dynamic enum) */
771 ot->prop = RNA_def_enum(ot->srna, "layer", DummyRNA_DEFAULT_items, 0, "Grease Pencil Layer", "");
772 RNA_def_enum_funcs(ot->prop, ED_gpencil_layers_with_new_enum_itemf);
775 /* ************************************************ */
777 /* ******************* Arrange Stroke Up/Down in drawing order ************************** */
780 GP_STROKE_MOVE_UP = -1,
781 GP_STROKE_MOVE_DOWN = 1,
782 GP_STROKE_MOVE_TOP = 2,
783 GP_STROKE_MOVE_BOTTOM = 3
786 static int gp_stroke_arrange_exec(bContext *C, wmOperator *op)
788 bGPdata *gpd = ED_gpencil_data_get_active(C);
789 bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
793 if (ELEM(NULL, gpd, gpl, gpl->actframe)) {
794 return OPERATOR_CANCELLED;
797 bGPDframe *gpf = gpl->actframe;
798 /* temp listbase to store selected strokes */
799 ListBase selected = {NULL};
800 const int direction = RNA_enum_get(op->ptr, "direction");
802 /* verify if any selected stroke is in the extreme of the stack and select to move */
803 for (gps = gpf->strokes.first; gps; gps = gps->next) {
804 /* only if selected */
805 if (gps->flag & GP_STROKE_SELECT) {
806 /* skip strokes that are invalid for current view */
807 if (ED_gpencil_stroke_can_use(C, gps) == false) {
810 /* check if the color is editable */
811 if (ED_gpencil_stroke_color_use(gpl, gps) == false) {
814 /* some stroke is already at front*/
815 if ((direction == GP_STROKE_MOVE_TOP) || (direction == GP_STROKE_MOVE_UP)) {
816 if (gps == gpf->strokes.last) {
817 return OPERATOR_CANCELLED;
820 /* some stroke is already at botom */
821 if ((direction == GP_STROKE_MOVE_BOTTOM) || (direction == GP_STROKE_MOVE_DOWN)) {
822 if (gps == gpf->strokes.first) {
823 return OPERATOR_CANCELLED;
827 BLI_addtail(&selected, BLI_genericNodeN(gps));
831 /* Now do the movement of the stroke */
834 case GP_STROKE_MOVE_TOP:
835 for (LinkData *link = selected.first; link; link = link->next) {
837 BLI_remlink(&gpf->strokes, gps);
838 BLI_addtail(&gpf->strokes, gps);
842 case GP_STROKE_MOVE_UP:
843 for (LinkData *link = selected.last; link; link = link->prev) {
845 BLI_listbase_link_move(&gpf->strokes, gps, 1);
849 case GP_STROKE_MOVE_DOWN:
850 for (LinkData *link = selected.first; link; link = link->next) {
852 BLI_listbase_link_move(&gpf->strokes, gps, -1);
856 case GP_STROKE_MOVE_BOTTOM:
857 for (LinkData *link = selected.last; link; link = link->prev) {
859 BLI_remlink(&gpf->strokes, gps);
860 BLI_addhead(&gpf->strokes, gps);
867 BLI_freelistN(&selected);
870 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
872 return OPERATOR_FINISHED;
875 void GPENCIL_OT_stroke_arrange(wmOperatorType *ot)
877 static const EnumPropertyItem slot_move[] = {
878 {GP_STROKE_MOVE_UP, "UP", 0, "Bring Forward", ""},
879 {GP_STROKE_MOVE_DOWN, "DOWN", 0, "Send Backward", ""},
880 {GP_STROKE_MOVE_TOP, "TOP", 0, "Bring to Front", ""},
881 {GP_STROKE_MOVE_BOTTOM, "BOTTOM", 0, "Send to Back", ""},
882 {0, NULL, 0, NULL, NULL }
886 ot->name = "Arrange Stroke";
887 ot->idname = "GPENCIL_OT_stroke_arrange";
888 ot->description = "Arrange selected strokes up/down in the drawing order of the active layer";
891 ot->exec = gp_stroke_arrange_exec;
892 ot->poll = gp_active_layer_poll;
895 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
897 ot->prop = RNA_def_enum(ot->srna, "direction", slot_move, GP_STROKE_MOVE_UP, "Direction", "");
899 /* ******************* Move Stroke to new color ************************** */
901 static int gp_stroke_change_color_exec(bContext *C, wmOperator *UNUSED(op))
903 bGPdata *gpd = ED_gpencil_data_get_active(C);
904 bGPDpalette *palette;
905 bGPDpalettecolor *color;
908 if (ELEM(NULL, gpd)) {
909 return OPERATOR_CANCELLED;
912 palette = BKE_gpencil_palette_getactive(gpd);
913 color = BKE_gpencil_palettecolor_getactive(palette);
914 if (ELEM(NULL, palette, color)) {
915 return OPERATOR_CANCELLED;
918 /* loop all strokes */
919 for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
920 /* only editable and visible layers are considered */
921 if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
922 for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
923 /* only if selected */
924 if (gps->flag & GP_STROKE_SELECT) {
925 /* skip strokes that are invalid for current view */
926 if (ED_gpencil_stroke_can_use(C, gps) == false)
928 /* check if the color is editable */
929 if (ED_gpencil_stroke_color_use(gpl, gps) == false)
932 /* asign new color (only if different) */
933 if ((STREQ(gps->colorname, color->info) == false) || (gps->palcolor != color)) {
934 BLI_strncpy(gps->colorname, color->info, sizeof(gps->colorname));
935 gps->palcolor = color;
942 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
944 return OPERATOR_FINISHED;
947 void GPENCIL_OT_stroke_change_color(wmOperatorType *ot)
950 ot->name = "Change Stroke Color";
951 ot->idname = "GPENCIL_OT_stroke_change_color";
952 ot->description = "Move selected strokes to active color";
955 ot->exec = gp_stroke_change_color_exec;
956 ot->poll = gp_active_layer_poll;
959 /* ******************* Lock color of non selected Strokes colors ************************** */
961 static int gp_stroke_lock_color_exec(bContext *C, wmOperator *UNUSED(op))
963 bGPdata *gpd = ED_gpencil_data_get_active(C);
964 bGPDpalette *palette;
968 return OPERATOR_CANCELLED;
970 palette = BKE_gpencil_palette_getactive(gpd);
971 if (ELEM(NULL, palette))
972 return OPERATOR_CANCELLED;
974 /* first lock all colors */
975 for (bGPDpalettecolor *palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
976 palcolor->flag |= PC_COLOR_LOCKED;
979 /* loop all selected strokes and unlock any color */
980 for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
981 /* only editable and visible layers are considered */
982 if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
983 for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
984 /* only if selected */
985 if (gps->flag & GP_STROKE_SELECT) {
986 /* skip strokes that are invalid for current view */
987 if (ED_gpencil_stroke_can_use(C, gps) == false) {
991 if (gps->palcolor != NULL) {
992 gps->palcolor->flag &= ~PC_COLOR_LOCKED;
999 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1001 return OPERATOR_FINISHED;
1004 void GPENCIL_OT_stroke_lock_color(wmOperatorType *ot)
1007 ot->name = "Lock Unused Colors";
1008 ot->idname = "GPENCIL_OT_stroke_lock_color";
1009 ot->description = "Lock any color not used in any selected stroke";
1012 ot->exec = gp_stroke_lock_color_exec;
1013 ot->poll = gp_active_layer_poll;
1016 /* ************************************************ */
1017 /* Drawing Brushes Operators */
1019 /* ******************* Add New Brush ************************ */
1021 /* add new brush - wrapper around API */
1022 static int gp_brush_add_exec(bContext *C, wmOperator *op)
1024 ToolSettings *ts = CTX_data_tool_settings(C);
1026 /* if there's no existing container */
1028 BKE_report(op->reports, RPT_ERROR, "Nowhere for brush data to go");
1029 return OPERATOR_CANCELLED;
1031 /* add new brush now */
1032 BKE_gpencil_brush_addnew(ts, DATA_("GP_Brush"), true);
1035 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1037 return OPERATOR_FINISHED;
1040 void GPENCIL_OT_brush_add(wmOperatorType *ot)
1043 ot->name = "Add Brush";
1044 ot->idname = "GPENCIL_OT_brush_add";
1045 ot->description = "Add new Grease Pencil drawing brush for the active Grease Pencil data-block";
1047 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1050 ot->exec = gp_brush_add_exec;
1051 ot->poll = gp_add_poll;
1054 /* ******************* Remove Active Brush ************************* */
1056 static int gp_brush_remove_exec(bContext *C, wmOperator *op)
1058 ToolSettings *ts = CTX_data_tool_settings(C);
1059 bGPDbrush *brush = BKE_gpencil_brush_getactive(ts);
1062 if (ELEM(NULL, ts, brush))
1063 return OPERATOR_CANCELLED;
1065 if (BLI_listbase_count_at_most(&ts->gp_brushes, 2) < 2) {
1066 BKE_report(op->reports, RPT_ERROR, "Grease Pencil needs a brush, unable to delete the last one");
1067 return OPERATOR_CANCELLED;
1071 /* make the brush before this the new active brush
1072 * - use the one after if this is the first
1073 * - if this is the only brush, this naturally becomes NULL
1076 BKE_gpencil_brush_setactive(ts, brush->prev);
1078 BKE_gpencil_brush_setactive(ts, brush->next);
1080 /* delete the brush now... */
1081 BKE_gpencil_brush_delete(ts, brush);
1084 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1086 return OPERATOR_FINISHED;
1089 void GPENCIL_OT_brush_remove(wmOperatorType *ot)
1092 ot->name = "Remove Brush";
1093 ot->idname = "GPENCIL_OT_brush_remove";
1094 ot->description = "Remove active Grease Pencil drawing brush";
1096 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1099 ot->exec = gp_brush_remove_exec;
1100 ot->poll = gp_active_brush_poll;
1103 /* ********************** Change Brush ***************************** */
1105 static int gp_brush_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt))
1110 /* call the menu, which will call this operator again, hence the canceled */
1111 pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE);
1112 layout = UI_popup_menu_layout(pup);
1113 uiItemsEnumO(layout, "GPENCIL_OT_brush_change", "brush");
1114 UI_popup_menu_end(C, pup);
1116 return OPERATOR_INTERFACE;
1119 static int gp_brush_change_exec(bContext *C, wmOperator *op)
1121 ToolSettings *ts = CTX_data_tool_settings(C);
1122 bGPDbrush *brush = NULL;
1123 int brush_num = RNA_enum_get(op->ptr, "brush");
1125 /* Get brush or create new one */
1126 if (brush_num == -1) {
1128 brush = BKE_gpencil_brush_addnew(ts, DATA_("GP_Brush"), true);
1131 /* Try to get brush */
1132 brush = BLI_findlink(&ts->gp_brushes, brush_num);
1134 if (brush == NULL) {
1135 BKE_reportf(op->reports, RPT_ERROR, "Cannot change to non-existent brush (index = %d)", brush_num);
1136 return OPERATOR_CANCELLED;
1140 /* Set active brush */
1141 BKE_gpencil_brush_setactive(ts, brush);
1144 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1146 return OPERATOR_FINISHED;
1149 void GPENCIL_OT_brush_change(wmOperatorType *ot)
1152 ot->name = "Change Brush";
1153 ot->idname = "GPENCIL_OT_brush_change";
1154 ot->description = "Change active Grease Pencil drawing brush";
1157 ot->invoke = gp_brush_change_invoke;
1158 ot->exec = gp_brush_change_exec;
1159 ot->poll = gp_active_brush_poll;
1162 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1164 /* gp brush to use (dynamic enum) */
1165 ot->prop = RNA_def_enum(ot->srna, "brush", DummyRNA_DEFAULT_items, 0, "Grease Pencil Brush", "");
1166 RNA_def_enum_funcs(ot->prop, ED_gpencil_brushes_enum_itemf);
1169 /* ******************* Move Brush Up/Down ************************** */
1172 GP_BRUSH_MOVE_UP = -1,
1173 GP_BRUSH_MOVE_DOWN = 1
1176 static int gp_brush_move_exec(bContext *C, wmOperator *op)
1178 ToolSettings *ts = CTX_data_tool_settings(C);
1179 bGPDbrush *brush = BKE_gpencil_brush_getactive(ts);
1181 int direction = RNA_enum_get(op->ptr, "type");
1184 if (ELEM(NULL, ts, brush)) {
1185 return OPERATOR_CANCELLED;
1189 if (direction == GP_BRUSH_MOVE_UP) {
1191 BLI_remlink(&ts->gp_brushes, brush);
1192 BLI_insertlinkbefore(&ts->gp_brushes, brush->prev, brush);
1194 else if (direction == GP_BRUSH_MOVE_DOWN) {
1196 BLI_remlink(&ts->gp_brushes, brush);
1197 BLI_insertlinkafter(&ts->gp_brushes, brush->next, brush);
1204 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1206 return OPERATOR_FINISHED;
1209 void GPENCIL_OT_brush_move(wmOperatorType *ot)
1211 static const EnumPropertyItem slot_move[] = {
1212 {GP_BRUSH_MOVE_UP, "UP", 0, "Up", ""},
1213 {GP_BRUSH_MOVE_DOWN, "DOWN", 0, "Down", ""},
1214 {0, NULL, 0, NULL, NULL }
1218 ot->name = "Move Brush";
1219 ot->idname = "GPENCIL_OT_brush_move";
1220 ot->description = "Move the active Grease Pencil drawing brush up/down in the list";
1223 ot->exec = gp_brush_move_exec;
1224 ot->poll = gp_active_brush_poll;
1227 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1229 ot->prop = RNA_def_enum(ot->srna, "type", slot_move, GP_BRUSH_MOVE_UP, "Type", "");
1232 /* ******************* Brush create presets ************************** */
1234 static int gp_brush_presets_create_exec(bContext *C, wmOperator *UNUSED(op))
1236 ToolSettings *ts = CTX_data_tool_settings(C);
1237 BKE_gpencil_brush_init_presets(ts);
1240 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1242 return OPERATOR_FINISHED;
1245 void GPENCIL_OT_brush_presets_create(wmOperatorType *ot)
1248 ot->name = "Create Preset Brushes";
1249 ot->idname = "GPENCIL_OT_brush_presets_create";
1250 ot->description = "Create a set of predefined Grease Pencil drawing brushes";
1253 ot->exec = gp_brush_presets_create_exec;
1256 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1260 /* ***************** Copy Brush ************************ */
1262 static int gp_brush_copy_exec(bContext *C, wmOperator *op)
1264 ToolSettings *ts = CTX_data_tool_settings(C);
1266 /* if there's no existing container */
1268 BKE_report(op->reports, RPT_ERROR, "Nowhere for brush data to go");
1269 return OPERATOR_CANCELLED;
1272 bGPDbrush *brush = BKE_gpencil_brush_getactive(ts);
1273 bGPDbrush *newbrush;
1276 if (ELEM(NULL, brush))
1277 return OPERATOR_CANCELLED;
1279 /* create a brush and duplicate data */
1280 newbrush = BKE_gpencil_brush_addnew(ts, brush->info, true);
1281 newbrush->thickness = brush->thickness;
1282 newbrush->draw_smoothfac = brush->draw_smoothfac;
1283 newbrush->draw_smoothlvl = brush->draw_smoothlvl;
1284 newbrush->sublevel = brush->sublevel;
1285 newbrush->flag = brush->flag;
1286 newbrush->draw_sensitivity = brush->draw_sensitivity;
1287 newbrush->draw_strength = brush->draw_strength;
1288 newbrush->draw_jitter = brush->draw_jitter;
1289 newbrush->draw_angle = brush->draw_angle;
1290 newbrush->draw_angle_factor = brush->draw_angle_factor;
1291 newbrush->draw_random_press = brush->draw_random_press;
1292 newbrush->draw_random_sub = brush->draw_random_sub;
1294 /* free automatic curves created by default (replaced by copy) */
1295 curvemapping_free(newbrush->cur_sensitivity);
1296 curvemapping_free(newbrush->cur_strength);
1297 curvemapping_free(newbrush->cur_jitter);
1299 /* make a copy of curves */
1300 newbrush->cur_sensitivity = curvemapping_copy(brush->cur_sensitivity);
1301 newbrush->cur_strength = curvemapping_copy(brush->cur_strength);
1302 newbrush->cur_jitter = curvemapping_copy(brush->cur_jitter);
1304 BKE_gpencil_brush_setactive(ts, newbrush);
1306 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1308 return OPERATOR_FINISHED;
1311 void GPENCIL_OT_brush_copy(wmOperatorType *ot)
1314 ot->name = "Copy Brush";
1315 ot->idname = "GPENCIL_OT_brush_copy";
1316 ot->description = "Copy current Grease Pencil drawing brush";
1319 ot->exec = gp_brush_copy_exec;
1320 ot->poll = gp_active_brush_poll;
1323 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1326 /* ***************** Select Brush ************************ */
1328 static int gp_brush_select_exec(bContext *C, wmOperator *op)
1330 ToolSettings *ts = CTX_data_tool_settings(C);
1332 /* if there's no existing container */
1334 BKE_report(op->reports, RPT_ERROR, "Nowhere to go");
1335 return OPERATOR_CANCELLED;
1338 const int index = RNA_int_get(op->ptr, "index");
1339 bGPDbrush *brush = BLI_findlink(&ts->gp_brushes, index);
1341 if (ELEM(NULL, brush)) {
1342 return OPERATOR_CANCELLED;
1345 BKE_gpencil_brush_setactive(ts, brush);
1348 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1350 return OPERATOR_FINISHED;
1353 void GPENCIL_OT_brush_select(wmOperatorType *ot)
1356 ot->name = "Select Brush";
1357 ot->idname = "GPENCIL_OT_brush_select";
1358 ot->description = "Select a Grease Pencil drawing brush";
1361 ot->exec = gp_brush_select_exec;
1362 ot->poll = gp_active_brush_poll;
1365 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1368 RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "Index of Drawing Brush", 0, INT_MAX);
1371 /* ************************************************ */
1372 /* Palette Operators */
1374 /* ******************* Add New Palette ************************ */
1376 /* add new palette - wrapper around API */
1377 static int gp_palette_add_exec(bContext *C, wmOperator *op)
1379 bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
1381 /* if there's no existing Grease-Pencil data there, add some */
1382 if (gpd_ptr == NULL) {
1383 BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go");
1384 return OPERATOR_CANCELLED;
1386 if (*gpd_ptr == NULL)
1387 *gpd_ptr = BKE_gpencil_data_addnew(DATA_("GPencil"));
1389 /* add new palette now */
1390 BKE_gpencil_palette_addnew(*gpd_ptr, DATA_("GP_Palette"), true);
1393 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1395 return OPERATOR_FINISHED;
1398 void GPENCIL_OT_palette_add(wmOperatorType *ot)
1401 ot->name = "Add Palette";
1402 ot->idname = "GPENCIL_OT_palette_add";
1403 ot->description = "Add new Grease Pencil palette for the active Grease Pencil data-block";
1405 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1408 ot->exec = gp_palette_add_exec;
1409 ot->poll = gp_add_poll;
1412 /* ******************* Remove Active Palette ************************* */
1414 static int gp_palette_remove_exec(bContext *C, wmOperator *op)
1416 bGPdata *gpd = ED_gpencil_data_get_active(C);
1417 bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
1420 if (ELEM(NULL, gpd, palette))
1421 return OPERATOR_CANCELLED;
1423 if (BLI_listbase_count_at_most(&gpd->palettes, 2) < 2) {
1424 BKE_report(op->reports, RPT_ERROR, "Grease Pencil needs a palette, unable to delete the last one");
1425 return OPERATOR_CANCELLED;
1429 /* make the palette before this the new active palette
1430 * - use the one after if this is the first
1431 * - if this is the only palette, this naturally becomes NULL
1434 BKE_gpencil_palette_setactive(gpd, palette->prev);
1436 BKE_gpencil_palette_setactive(gpd, palette->next);
1438 /* delete the palette now... */
1439 BKE_gpencil_palette_delete(gpd, palette);
1442 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1444 return OPERATOR_FINISHED;
1447 void GPENCIL_OT_palette_remove(wmOperatorType *ot)
1450 ot->name = "Remove palette";
1451 ot->idname = "GPENCIL_OT_palette_remove";
1452 ot->description = "Remove active Grease Pencil palette";
1454 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1457 ot->exec = gp_palette_remove_exec;
1458 ot->poll = gp_active_palette_poll;
1461 /* ********************** Change Palette ***************************** */
1463 static int gp_palette_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt))
1468 /* call the menu, which will call this operator again, hence the canceled */
1469 pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE);
1470 layout = UI_popup_menu_layout(pup);
1471 uiItemsEnumO(layout, "GPENCIL_OT_palette_change", "palette");
1472 UI_popup_menu_end(C, pup);
1474 return OPERATOR_INTERFACE;
1477 static int gp_palette_change_exec(bContext *C, wmOperator *op)
1479 bGPdata *gpd = CTX_data_gpencil_data(C);
1480 bGPDpalette *palette = NULL;
1481 int palette_num = RNA_enum_get(op->ptr, "palette");
1483 /* Get palette or create new one */
1484 if (palette_num == -1) {
1485 /* Create palette */
1486 palette = BKE_gpencil_palette_addnew(gpd, DATA_("GP_Palette"), true);
1489 /* Try to get palette */
1490 palette = BLI_findlink(&gpd->palettes, palette_num);
1492 if (palette == NULL) {
1493 BKE_reportf(op->reports, RPT_ERROR, "Cannot change to non-existent palette (index = %d)", palette_num);
1494 return OPERATOR_CANCELLED;
1498 /* Set active palette */
1499 BKE_gpencil_palette_setactive(gpd, palette);
1502 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1504 return OPERATOR_FINISHED;
1507 void GPENCIL_OT_palette_change(wmOperatorType *ot)
1510 ot->name = "Change Palette";
1511 ot->idname = "GPENCIL_OT_palette_change";
1512 ot->description = "Change active Grease Pencil palette";
1515 ot->invoke = gp_palette_change_invoke;
1516 ot->exec = gp_palette_change_exec;
1517 ot->poll = gp_active_palette_poll;
1520 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1522 /* gp palette to use (dynamic enum) */
1523 ot->prop = RNA_def_enum(ot->srna, "palette", DummyRNA_DEFAULT_items, 0, "Grease Pencil Palette", "");
1524 RNA_def_enum_funcs(ot->prop, ED_gpencil_palettes_enum_itemf);
1527 /* ******************* Lock and hide any color non used in current layer ************************** */
1529 static int gp_palette_lock_layer_exec(bContext *C, wmOperator *UNUSED(op))
1531 bGPdata *gpd = ED_gpencil_data_get_active(C);
1532 bGPDpalette *palette;
1535 if (ELEM(NULL, gpd))
1536 return OPERATOR_CANCELLED;
1538 palette = BKE_gpencil_palette_getactive(gpd);
1539 if (ELEM(NULL, palette))
1540 return OPERATOR_CANCELLED;
1542 /* first lock and hide all colors */
1543 for (bGPDpalettecolor *palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
1544 palcolor->flag |= PC_COLOR_LOCKED;
1545 palcolor->flag |= PC_COLOR_HIDE;
1548 /* loop all selected strokes and unlock any color used in active layer */
1549 for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
1550 /* only editable and visible layers are considered */
1551 if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL) && (gpl->flag & GP_LAYER_ACTIVE)) {
1552 for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
1553 /* skip strokes that are invalid for current view */
1554 if (ED_gpencil_stroke_can_use(C, gps) == false)
1557 /* unlock/unhide color if not unlocked before */
1558 if (gps->palcolor != NULL) {
1559 gps->palcolor->flag &= ~PC_COLOR_LOCKED;
1560 gps->palcolor->flag &= ~PC_COLOR_HIDE;
1566 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1568 return OPERATOR_FINISHED;
1571 void GPENCIL_OT_palette_lock_layer(wmOperatorType *ot)
1574 ot->name = "Disable Unused Layer Colors";
1575 ot->idname = "GPENCIL_OT_palette_lock_layer";
1576 ot->description = "Lock and hide any color not used in any layer";
1579 ot->exec = gp_palette_lock_layer_exec;
1580 ot->poll = gp_active_layer_poll;
1583 /* ************************************************ */
1584 /* Palette Colors Operators */
1586 /* ******************* Add New Palette ************************ */
1588 /* add new palette - wrapper around API */
1589 static int gp_palettecolor_add_exec(bContext *C, wmOperator *op)
1591 bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
1593 /* if there's no existing Grease-Pencil data there, add some */
1594 if (gpd_ptr == NULL) {
1595 BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go");
1596 return OPERATOR_CANCELLED;
1598 if (*gpd_ptr == NULL)
1599 *gpd_ptr = BKE_gpencil_data_addnew(DATA_("GPencil"));
1601 /* verify palette */
1602 bGPDpalette *palette = BKE_gpencil_palette_getactive(*gpd_ptr);
1603 if (palette == NULL)
1604 palette = BKE_gpencil_palette_addnew(*gpd_ptr, DATA_("GP_Palette"), true);
1606 /* add new palette color now */
1607 BKE_gpencil_palettecolor_addnew(palette, DATA_("Color"), true);
1610 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1612 return OPERATOR_FINISHED;
1615 void GPENCIL_OT_palettecolor_add(wmOperatorType *ot)
1618 ot->name = "Add Palette Color";
1619 ot->idname = "GPENCIL_OT_palettecolor_add";
1620 ot->description = "Add new Grease Pencil palette color for the active Grease Pencil data-block";
1622 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1625 ot->exec = gp_palettecolor_add_exec;
1626 ot->poll = gp_add_poll;
1629 /* ******************* Remove Active Palette color ************************* */
1631 static int gp_palettecolor_remove_exec(bContext *C, wmOperator *UNUSED(op))
1633 bGPdata *gpd = ED_gpencil_data_get_active(C);
1634 bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
1635 bGPDpalettecolor *color = BKE_gpencil_palettecolor_getactive(palette);
1638 if (ELEM(NULL, gpd, palette, color))
1639 return OPERATOR_CANCELLED;
1641 /* make the palette color before this the new active color
1642 * - use the one after if this is the first
1643 * - if this is the only color, this naturally becomes NULL
1646 BKE_gpencil_palettecolor_setactive(palette, color->prev);
1648 BKE_gpencil_palettecolor_setactive(palette, color->next);
1650 /* delete the strokes */
1651 BKE_gpencil_palettecolor_delete_strokes(gpd, color->info);
1653 /* delete the palette color now... */
1654 BKE_gpencil_palettecolor_delete(palette, color);
1657 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1659 return OPERATOR_FINISHED;
1662 void GPENCIL_OT_palettecolor_remove(wmOperatorType *ot)
1665 ot->name = "Remove palette color";
1666 ot->idname = "GPENCIL_OT_palettecolor_remove";
1667 ot->description = "Remove active Grease Pencil palette color";
1669 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1672 ot->exec = gp_palettecolor_remove_exec;
1673 ot->poll = gp_active_palettecolor_poll;
1676 /* ********************** Isolate palette color **************************** */
1678 static int gp_isolate_palettecolor_exec(bContext *C, wmOperator *op)
1680 bGPdata *gpd = ED_gpencil_data_get_active(C);
1681 bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
1682 bGPDpalettecolor *active_color = BKE_gpencil_palettecolor_getactive(palette);
1683 bGPDpalettecolor *palcolor;
1685 int flags = PC_COLOR_LOCKED;
1686 bool isolate = false;
1688 if (RNA_boolean_get(op->ptr, "affect_visibility"))
1689 flags |= PC_COLOR_HIDE;
1691 if (ELEM(NULL, gpd, active_color)) {
1692 BKE_report(op->reports, RPT_ERROR, "No active color to isolate");
1693 return OPERATOR_CANCELLED;
1696 /* Test whether to isolate or clear all flags */
1697 for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
1698 /* Skip if this is the active one */
1699 if (palcolor == active_color)
1702 /* If the flags aren't set, that means that the color is
1703 * not alone, so we have some colors to isolate still
1705 if ((palcolor->flag & flags) == 0) {
1711 /* Set/Clear flags as appropriate */
1713 /* Set flags on all "other" colors */
1714 for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
1715 if (palcolor == active_color)
1718 palcolor->flag |= flags;
1722 /* Clear flags - Restore everything else */
1723 for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
1724 palcolor->flag &= ~flags;
1729 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1731 return OPERATOR_FINISHED;
1734 void GPENCIL_OT_palettecolor_isolate(wmOperatorType *ot)
1737 ot->name = "Isolate Palette Color";
1738 ot->idname = "GPENCIL_OT_palettecolor_isolate";
1739 ot->description = "Toggle whether the active color is the only one that is editable and/or visible";
1742 ot->exec = gp_isolate_palettecolor_exec;
1743 ot->poll = gp_active_palettecolor_poll;
1746 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1749 RNA_def_boolean(ot->srna, "affect_visibility", false, "Affect Visibility", "In addition to toggling "
1750 "the editability, also affect the visibility");
1753 /* *********************** Hide Palette colors ******************************** */
1755 static int gp_palettecolor_hide_exec(bContext *C, wmOperator *op)
1757 bGPdata *gpd = ED_gpencil_data_get_active(C);
1758 bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
1759 bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette);
1761 bool unselected = RNA_boolean_get(op->ptr, "unselected");
1764 if (ELEM(NULL, gpd, palette, palcolor))
1765 return OPERATOR_CANCELLED;
1768 bGPDpalettecolor *color;
1770 /* hide unselected */
1771 for (color = palette->colors.first; color; color = color->next) {
1772 if (color != palcolor) {
1773 color->flag |= PC_COLOR_HIDE;
1778 /* hide selected/active */
1779 palcolor->flag |= PC_COLOR_HIDE;
1783 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1785 return OPERATOR_FINISHED;
1788 void GPENCIL_OT_palettecolor_hide(wmOperatorType *ot)
1791 ot->name = "Hide Color(s)";
1792 ot->idname = "GPENCIL_OT_palettecolor_hide";
1793 ot->description = "Hide selected/unselected Grease Pencil colors";
1796 ot->exec = gp_palettecolor_hide_exec;
1797 ot->poll = gp_active_palettecolor_poll; /* NOTE: we need an active color to play with */
1800 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1803 RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected colors");
1806 /* ********************** Show All Colors ***************************** */
1808 /* poll callback for showing colors */
1809 static int gp_palettecolor_reveal_poll(bContext *C)
1811 return ED_gpencil_data_get_active(C) != NULL;
1814 static int gp_palettecolor_reveal_exec(bContext *C, wmOperator *UNUSED(op))
1816 bGPdata *gpd = ED_gpencil_data_get_active(C);
1817 bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
1818 bGPDpalettecolor *palcolor;
1821 if (ELEM(NULL, gpd, palette))
1822 return OPERATOR_CANCELLED;
1824 /* make all colors visible */
1825 for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
1826 palcolor->flag &= ~PC_COLOR_HIDE;
1830 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1832 return OPERATOR_FINISHED;
1835 void GPENCIL_OT_palettecolor_reveal(wmOperatorType *ot)
1838 ot->name = "Show All Colors";
1839 ot->idname = "GPENCIL_OT_palettecolor_reveal";
1840 ot->description = "Unhide all hidden Grease Pencil palette colors";
1843 ot->exec = gp_palettecolor_reveal_exec;
1844 ot->poll = gp_palettecolor_reveal_poll;
1847 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1850 /* ***************** Lock/Unlock All Palette colors ************************ */
1852 static int gp_palettecolor_lock_all_exec(bContext *C, wmOperator *UNUSED(op))
1854 bGPdata *gpd = ED_gpencil_data_get_active(C);
1855 bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
1856 bGPDpalettecolor *palcolor;
1859 if (ELEM(NULL, gpd, palette))
1860 return OPERATOR_CANCELLED;
1862 /* make all layers non-editable */
1863 for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
1864 palcolor->flag |= PC_COLOR_LOCKED;
1868 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1870 return OPERATOR_FINISHED;
1873 void GPENCIL_OT_palettecolor_lock_all(wmOperatorType *ot)
1876 ot->name = "Lock All Colors";
1877 ot->idname = "GPENCIL_OT_palettecolor_lock_all";
1878 ot->description = "Lock all Grease Pencil colors to prevent them from being accidentally modified";
1881 ot->exec = gp_palettecolor_lock_all_exec;
1882 ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */
1885 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1888 /* -------------------------- */
1890 static int gp_palettecolor_unlock_all_exec(bContext *C, wmOperator *UNUSED(op))
1892 bGPdata *gpd = ED_gpencil_data_get_active(C);
1893 bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
1894 bGPDpalettecolor *palcolor;
1897 if (ELEM(NULL, gpd, palette))
1898 return OPERATOR_CANCELLED;
1900 /* make all layers editable again*/
1901 for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
1902 palcolor->flag &= ~PC_COLOR_LOCKED;
1906 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1908 return OPERATOR_FINISHED;
1911 void GPENCIL_OT_palettecolor_unlock_all(wmOperatorType *ot)
1914 ot->name = "Unlock All Colors";
1915 ot->idname = "GPENCIL_OT_palettecolor_unlock_all";
1916 ot->description = "Unlock all Grease Pencil colors so that they can be edited";
1919 ot->exec = gp_palettecolor_unlock_all_exec;
1920 ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */
1923 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1926 /* ******************* Move Color Up/Down ************************** */
1929 GP_COLOR_MOVE_UP = -1,
1930 GP_COLOR_MOVE_DOWN = 1
1933 static int gp_palettecolor_move_exec(bContext *C, wmOperator *op)
1935 bGPdata *gpd = ED_gpencil_data_get_active(C);
1936 bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
1937 bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette);
1939 int direction = RNA_enum_get(op->ptr, "direction");
1942 if (ELEM(NULL, gpd, palette, palcolor))
1943 return OPERATOR_CANCELLED;
1946 if (direction == GP_COLOR_MOVE_UP) {
1948 BLI_remlink(&palette->colors, palcolor);
1949 BLI_insertlinkbefore(&palette->colors, palcolor->prev, palcolor);
1951 else if (direction == GP_COLOR_MOVE_DOWN) {
1953 BLI_remlink(&palette->colors, palcolor);
1954 BLI_insertlinkafter(&palette->colors, palcolor->next, palcolor);
1961 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1963 return OPERATOR_FINISHED;
1966 void GPENCIL_OT_palettecolor_move(wmOperatorType *ot)
1968 static const EnumPropertyItem slot_move[] = {
1969 {GP_COLOR_MOVE_UP, "UP", 0, "Up", ""},
1970 {GP_COLOR_MOVE_DOWN, "DOWN", 0, "Down", ""},
1971 {0, NULL, 0, NULL, NULL}
1975 ot->name = "Move Palette color";
1976 ot->idname = "GPENCIL_OT_palettecolor_move";
1977 ot->description = "Move the active Grease Pencil palette color up/down in the list";
1980 ot->exec = gp_palettecolor_move_exec;
1981 ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */
1984 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1986 ot->prop = RNA_def_enum(ot->srna, "direction", slot_move, GP_COLOR_MOVE_UP, "Direction", "");
1989 /* ***************** Select all strokes using Palette color ************************ */
1991 static int gp_palettecolor_select_exec(bContext *C, wmOperator *UNUSED(op))
1993 bGPdata *gpd = ED_gpencil_data_get_active(C);
1994 bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
1995 bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette);
1998 if (ELEM(NULL, gpd, palette, palcolor))
1999 return OPERATOR_CANCELLED;
2001 /* read all strokes and select*/
2002 for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
2003 /* only editable and visible layers are considered */
2004 if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
2005 /* verify something to do */
2006 for (bGPDstroke *gps = gpl->actframe->strokes.first; gps; gps = gps->next) {
2007 /* skip strokes that are invalid for current view */
2008 if (ED_gpencil_stroke_can_use(C, gps) == false)
2010 /* check if the color is editable */
2011 if (ED_gpencil_stroke_color_use(gpl, gps) == false)
2015 if (strcmp(palcolor->info, gps->colorname) == 0) {
2019 gps->flag |= GP_STROKE_SELECT;
2020 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
2021 pt->flag |= GP_SPOINT_SELECT;
2028 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
2030 return OPERATOR_FINISHED;
2033 void GPENCIL_OT_palettecolor_select(wmOperatorType *ot)
2036 ot->name = "Select Color";
2037 ot->idname = "GPENCIL_OT_palettecolor_select";
2038 ot->description = "Select all Grease Pencil strokes using current color";
2041 ot->exec = gp_palettecolor_select_exec;
2042 ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */
2045 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2048 /* ***************** Copy Palette color ************************ */
2050 static int gp_palettecolor_copy_exec(bContext *C, wmOperator *UNUSED(op))
2052 bGPdata *gpd = ED_gpencil_data_get_active(C);
2053 bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
2054 bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette);
2055 bGPDpalettecolor *newcolor;
2058 if (ELEM(NULL, gpd, palette, palcolor))
2059 return OPERATOR_CANCELLED;
2061 /* create a new color and duplicate data */
2062 newcolor = BKE_gpencil_palettecolor_addnew(palette, palcolor->info, true);
2063 copy_v4_v4(newcolor->color, palcolor->color);
2064 copy_v4_v4(newcolor->fill, palcolor->fill);
2065 newcolor->flag = palcolor->flag;
2068 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
2070 return OPERATOR_FINISHED;
2073 void GPENCIL_OT_palettecolor_copy(wmOperatorType *ot)
2076 ot->name = "Copy Color";
2077 ot->idname = "GPENCIL_OT_palettecolor_copy";
2078 ot->description = "Copy current Grease Pencil palette color";
2081 ot->exec = gp_palettecolor_copy_exec;
2082 ot->poll = gp_active_palettecolor_poll;
2085 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;