Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/gpencil/gpencil_data.c
| Show All 40 Lines | |||||
| #include "BLI_blenlib.h" | #include "BLI_blenlib.h" | ||||
| #include "BLI_utildefines.h" | #include "BLI_utildefines.h" | ||||
| #include "BLI_ghash.h" | #include "BLI_ghash.h" | ||||
| #include "BLI_math.h" | #include "BLI_math.h" | ||||
| #include "BLI_string_utils.h" | #include "BLI_string_utils.h" | ||||
| #include "BLT_translation.h" | #include "BLT_translation.h" | ||||
| #include "DNA_anim_types.h" | |||||
| #include "DNA_brush_types.h" | |||||
| #include "DNA_gpencil_types.h" | |||||
| #include "DNA_meshdata_types.h" | |||||
| #include "DNA_modifier_types.h" | |||||
| #include "DNA_object_types.h" | |||||
| #include "DNA_scene_types.h" | #include "DNA_scene_types.h" | ||||
| #include "DNA_screen_types.h" | #include "DNA_screen_types.h" | ||||
| #include "DNA_space_types.h" | #include "DNA_space_types.h" | ||||
| #include "DNA_view3d_types.h" | #include "DNA_view3d_types.h" | ||||
| #include "DNA_gpencil_types.h" | |||||
| #include "BKE_colortools.h" | #include "BKE_main.h" | ||||
| #include "BKE_brush.h" | |||||
| #include "BKE_animsys.h" | |||||
| #include "BKE_context.h" | #include "BKE_context.h" | ||||
| #include "BKE_deform.h" | |||||
| #include "BKE_fcurve.h" | |||||
| #include "BKE_global.h" | |||||
| #include "BKE_gpencil.h" | #include "BKE_gpencil.h" | ||||
| #include "BKE_gpencil_modifier.h" | |||||
| #include "BKE_library.h" | #include "BKE_library.h" | ||||
| #include "BKE_main.h" | #include "BKE_main.h" | ||||
| #include "BKE_modifier.h" | |||||
| #include "BKE_object.h" | #include "BKE_object.h" | ||||
| #include "BKE_material.h" | |||||
| #include "BKE_paint.h" | |||||
| #include "BKE_report.h" | #include "BKE_report.h" | ||||
| #include "BKE_scene.h" | #include "BKE_scene.h" | ||||
| #include "BKE_screen.h" | #include "BKE_screen.h" | ||||
| #include "UI_interface.h" | #include "UI_interface.h" | ||||
| #include "UI_resources.h" | #include "UI_resources.h" | ||||
| #include "WM_api.h" | #include "WM_api.h" | ||||
| #include "WM_types.h" | #include "WM_types.h" | ||||
| #include "RNA_access.h" | #include "RNA_access.h" | ||||
| #include "RNA_define.h" | #include "RNA_define.h" | ||||
| #include "RNA_enum_types.h" | #include "RNA_enum_types.h" | ||||
| #include "ED_object.h" | |||||
| #include "ED_gpencil.h" | #include "ED_gpencil.h" | ||||
| #include "DEG_depsgraph.h" | |||||
| #include "DEG_depsgraph_build.h" | |||||
| #include "DEG_depsgraph_query.h" | |||||
| #include "gpencil_intern.h" | #include "gpencil_intern.h" | ||||
| /* ************************************************ */ | /* ************************************************ */ | ||||
| /* Datablock Operators */ | /* Datablock Operators */ | ||||
| /* ******************* Add New Data ************************ */ | /* ******************* Add New Data ************************ */ | ||||
| /* add new datablock - wrapper around API */ | /* add new datablock - wrapper around API */ | ||||
| static int gp_data_add_exec(bContext *C, wmOperator *op) | static int gp_data_add_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| Main *bmain = CTX_data_main(C); | PointerRNA gpd_owner = {{NULL}}; | ||||
| bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); | bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, &gpd_owner); | ||||
| ToolSettings *ts = CTX_data_tool_settings(C); | bool is_annotation = ED_gpencil_data_owner_is_annotation(&gpd_owner); | ||||
| if (gpd_ptr == NULL) { | if (gpd_ptr == NULL) { | ||||
| BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go"); | BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go"); | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| else { | else { | ||||
| /* decrement user count and add new datablock */ | /* decrement user count and add new datablock */ | ||||
| bGPdata *gpd = (*gpd_ptr); | /* TODO: if a datablock exists, we should make a copy of it instead of starting fresh (as in other areas) */ | ||||
| Main *bmain = CTX_data_main(C); | |||||
| /* decrement user count of old GP datablock */ | |||||
| if (*gpd_ptr) { | |||||
| bGPdata *gpd = (*gpd_ptr); | |||||
| id_us_min(&gpd->id); | id_us_min(&gpd->id); | ||||
| *gpd_ptr = BKE_gpencil_data_addnew(bmain, DATA_("GPencil")); | |||||
| /* if not exist brushes, create a new set */ | |||||
| if (ts) { | |||||
| if (BLI_listbase_is_empty(&ts->gp_brushes)) { | |||||
| /* create new brushes */ | |||||
| BKE_gpencil_brush_init_presets(ts); | |||||
| } | } | ||||
| /* add new datablock, with a single layer ready to use (so users don't have to perform an extra step) */ | |||||
| if (is_annotation) { | |||||
| bGPdata *gpd = BKE_gpencil_data_addnew(bmain, DATA_("Annotations")); | |||||
| *gpd_ptr = gpd; | |||||
| /* tag for annotations */ | |||||
| gpd->flag |= GP_DATA_ANNOTATIONS; | |||||
| /* add new layer (i.e. a "note") */ | |||||
| BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true); | |||||
| } | } | ||||
| else { | |||||
| /* GP Object Case - This shouldn't happen! */ | |||||
| *gpd_ptr = BKE_gpencil_data_addnew(bmain, DATA_("GPencil")); | |||||
| /* add default sets of colors and brushes */ | |||||
| ED_gpencil_add_defaults(C); | |||||
| /* add new layer */ | |||||
| BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("GP_Layer"), true); | |||||
| } | |||||
| } | } | ||||
| /* notifiers */ | /* notifiers */ | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 62 Lines • ▼ Show 20 Lines | |||||
| /* ************************************************ */ | /* ************************************************ */ | ||||
| /* Layer Operators */ | /* Layer Operators */ | ||||
| /* ******************* Add New Layer ************************ */ | /* ******************* Add New Layer ************************ */ | ||||
| /* add new layer - wrapper around API */ | /* add new layer - wrapper around API */ | ||||
| static int gp_layer_add_exec(bContext *C, wmOperator *op) | static int gp_layer_add_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| Main *bmain = CTX_data_main(C); | PointerRNA gpd_owner = {{NULL}}; | ||||
| bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); | bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, &gpd_owner); | ||||
| ToolSettings *ts = CTX_data_tool_settings(C); | bool is_annotation = ED_gpencil_data_owner_is_annotation(&gpd_owner); | ||||
| /* if there's no existing Grease-Pencil data there, add some */ | /* if there's no existing Grease-Pencil data there, add some */ | ||||
| if (gpd_ptr == NULL) { | if (gpd_ptr == NULL) { | ||||
| BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go"); | BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go"); | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| if (*gpd_ptr == NULL) | |||||
| if (*gpd_ptr == NULL) { | |||||
| Main *bmain = CTX_data_main(C); | |||||
| if (is_annotation) { | |||||
| /* Annotations */ | |||||
| *gpd_ptr = BKE_gpencil_data_addnew(bmain, DATA_("Annotations")); | |||||
| /* mark as annotation */ | |||||
| (*gpd_ptr)->flag |= GP_DATA_ANNOTATIONS; | |||||
| } | |||||
| else { | |||||
| /* GP Object */ | |||||
| /* NOTE: This shouldn't actually happen in practice */ | |||||
| *gpd_ptr = BKE_gpencil_data_addnew(bmain, DATA_("GPencil")); | *gpd_ptr = BKE_gpencil_data_addnew(bmain, DATA_("GPencil")); | ||||
| /* if not exist brushes, create a new set */ | /* add default sets of colors and brushes */ | ||||
| if (ts) { | ED_gpencil_add_defaults(C); | ||||
| if (BLI_listbase_is_empty(&ts->gp_brushes)) { | |||||
| /* create new brushes */ | |||||
| BKE_gpencil_brush_init_presets(ts); | |||||
| } | } | ||||
| } | } | ||||
| /* add new layer now */ | /* add new layer now */ | ||||
| if (is_annotation) { | |||||
| BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true); | |||||
| } | |||||
| else { | |||||
| BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("GP_Layer"), true); | BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("GP_Layer"), true); | ||||
| } | |||||
| /* notifiers */ | /* notifiers */ | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_layer_add(wmOperatorType *ot) | void GPENCIL_OT_layer_add(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Add New Layer"; | ot->name = "Add New Layer"; | ||||
| ot->idname = "GPENCIL_OT_layer_add"; | ot->idname = "GPENCIL_OT_layer_add"; | ||||
| ot->description = "Add new Grease Pencil layer for the active Grease Pencil data-block"; | ot->description = "Add new layer or note for the active data-block"; | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| /* callbacks */ | /* callbacks */ | ||||
| ot->exec = gp_layer_add_exec; | ot->exec = gp_layer_add_exec; | ||||
| ot->poll = gp_add_poll; | ot->poll = gp_add_poll; | ||||
| } | } | ||||
| Show All 21 Lines | if (gpl->prev) | ||||
| BKE_gpencil_layer_setactive(gpd, gpl->prev); | BKE_gpencil_layer_setactive(gpd, gpl->prev); | ||||
| else | else | ||||
| BKE_gpencil_layer_setactive(gpd, gpl->next); | BKE_gpencil_layer_setactive(gpd, gpl->next); | ||||
| /* delete the layer now... */ | /* delete the layer now... */ | ||||
| BKE_gpencil_layer_delete(gpd, gpl); | BKE_gpencil_layer_delete(gpd, gpl); | ||||
| /* notifiers */ | /* notifiers */ | ||||
| DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_layer_remove(wmOperatorType *ot) | void GPENCIL_OT_layer_remove(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| Show All 23 Lines | static int gp_layer_move_exec(bContext *C, wmOperator *op) | ||||
| int direction = RNA_enum_get(op->ptr, "type"); | int direction = RNA_enum_get(op->ptr, "type"); | ||||
| /* sanity checks */ | /* sanity checks */ | ||||
| if (ELEM(NULL, gpd, gpl)) | if (ELEM(NULL, gpd, gpl)) | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| BLI_assert(ELEM(direction, -1, 0, 1)); /* we use value below */ | BLI_assert(ELEM(direction, -1, 0, 1)); /* we use value below */ | ||||
| if (BLI_listbase_link_move(&gpd->layers, gpl, direction)) { | if (BLI_listbase_link_move(&gpd->layers, gpl, direction)) { | ||||
| DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| } | } | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_layer_move(wmOperatorType *ot) | void GPENCIL_OT_layer_move(wmOperatorType *ot) | ||||
| { | { | ||||
| Show All 34 Lines | static int gp_layer_copy_exec(bContext *C, wmOperator *UNUSED(op)) | ||||
| new_layer = BKE_gpencil_layer_duplicate(gpl); | new_layer = BKE_gpencil_layer_duplicate(gpl); | ||||
| BLI_insertlinkafter(&gpd->layers, gpl, new_layer); | BLI_insertlinkafter(&gpd->layers, gpl, new_layer); | ||||
| /* ensure new layer has a unique name, and is now the active layer */ | /* ensure new layer has a unique name, and is now the active layer */ | ||||
| BLI_uniquename(&gpd->layers, new_layer, DATA_("GP_Layer"), '.', offsetof(bGPDlayer, info), sizeof(new_layer->info)); | BLI_uniquename(&gpd->layers, new_layer, DATA_("GP_Layer"), '.', offsetof(bGPDlayer, info), sizeof(new_layer->info)); | ||||
| BKE_gpencil_layer_setactive(gpd, new_layer); | BKE_gpencil_layer_setactive(gpd, new_layer); | ||||
| /* notifiers */ | /* notifiers */ | ||||
| DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_layer_duplicate(wmOperatorType *ot) | void GPENCIL_OT_layer_duplicate(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Duplicate Layer"; | ot->name = "Duplicate Layer"; | ||||
| ot->idname = "GPENCIL_OT_layer_duplicate"; | ot->idname = "GPENCIL_OT_layer_duplicate"; | ||||
| ot->description = "Make a copy of the active Grease Pencil layer"; | ot->description = "Make a copy of the active Grease Pencil layer"; | ||||
| /* callbacks */ | /* callbacks */ | ||||
| ot->exec = gp_layer_copy_exec; | ot->exec = gp_layer_copy_exec; | ||||
| ot->poll = gp_active_layer_poll; | ot->poll = gp_active_layer_poll; | ||||
| /* flags */ | /* flags */ | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| } | } | ||||
| /* ********************* Duplicate Frame ************************** */ | |||||
| enum { | |||||
| GP_FRAME_DUP_ACTIVE = 0, | |||||
| GP_FRAME_DUP_ALL = 1 | |||||
| }; | |||||
| static int gp_frame_duplicate_exec(bContext *C, wmOperator *op) | |||||
| { | |||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | |||||
| bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); | |||||
| Depsgraph *depsgraph = CTX_data_depsgraph(C); | |||||
| int cfra_eval = (int)DEG_get_ctime(depsgraph); | |||||
| int mode = RNA_enum_get(op->ptr, "mode"); | |||||
| /* sanity checks */ | |||||
| if (ELEM(NULL, gpd, gpl)) | |||||
| return OPERATOR_CANCELLED; | |||||
| if (mode == 0) { | |||||
| BKE_gpencil_frame_addcopy(gpl, cfra_eval); | |||||
| } | |||||
| else { | |||||
| for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { | |||||
| if ((gpl->flag & GP_LAYER_LOCKED) == 0) { | |||||
| BKE_gpencil_frame_addcopy(gpl, cfra_eval); | |||||
| } | |||||
| } | |||||
| } | |||||
| /* notifiers */ | |||||
| DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | |||||
| return OPERATOR_FINISHED; | |||||
| } | |||||
| void GPENCIL_OT_frame_duplicate(wmOperatorType *ot) | |||||
| { | |||||
| static const EnumPropertyItem duplicate_mode[] = { | |||||
| { GP_FRAME_DUP_ACTIVE, "ACTIVE", 0, "Active", "Duplicate frame in active layer only" }, | |||||
| { GP_FRAME_DUP_ALL, "ALL", 0, "All", "Duplicate active frames in all layers" }, | |||||
| { 0, NULL, 0, NULL, NULL } | |||||
| }; | |||||
| /* identifiers */ | |||||
| ot->name = "Duplicate Frame"; | |||||
| ot->idname = "GPENCIL_OT_frame_duplicate"; | |||||
| ot->description = "Make a copy of the active Grease Pencil Frame"; | |||||
| /* callbacks */ | |||||
| ot->exec = gp_frame_duplicate_exec; | |||||
| ot->poll = gp_active_layer_poll; | |||||
| /* flags */ | |||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | |||||
| ot->prop = RNA_def_enum(ot->srna, "mode", duplicate_mode, GP_FRAME_DUP_ACTIVE, "Mode", ""); | |||||
| } | |||||
| /* ********************* Clean Fill Boundaries on Frame ************************** */ | |||||
| enum { | |||||
| GP_FRAME_CLEAN_FILL_ACTIVE = 0, | |||||
| GP_FRAME_CLEAN_FILL_ALL = 1 | |||||
| }; | |||||
| static int gp_frame_clean_fill_exec(bContext *C, wmOperator *op) | |||||
| { | |||||
| bool changed = false; | |||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | |||||
| int mode = RNA_enum_get(op->ptr, "mode"); | |||||
| CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) | |||||
| { | |||||
| bGPDframe *init_gpf = gpl->actframe; | |||||
| if (mode == GP_FRAME_CLEAN_FILL_ALL) { | |||||
| init_gpf = gpl->frames.first; | |||||
| } | |||||
| for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { | |||||
| if ((gpf == gpl->actframe) || (mode == GP_FRAME_CLEAN_FILL_ALL)) { | |||||
| bGPDstroke *gps, *gpsn; | |||||
| if (gpf == NULL) | |||||
| continue; | |||||
| /* simply delete strokes which are no fill */ | |||||
| for (gps = gpf->strokes.first; gps; gps = gpsn) { | |||||
| gpsn = gps->next; | |||||
| /* skip strokes that are invalid for current view */ | |||||
| if (ED_gpencil_stroke_can_use(C, gps) == false) | |||||
| continue; | |||||
| /* free stroke */ | |||||
| if (gps->flag & GP_STROKE_NOFILL) { | |||||
| /* free stroke memory arrays, then stroke itself */ | |||||
| if (gps->points) { | |||||
| MEM_freeN(gps->points); | |||||
| } | |||||
| if (gps->dvert) { | |||||
| BKE_gpencil_free_stroke_weights(gps); | |||||
| MEM_freeN(gps->dvert); | |||||
| } | |||||
| MEM_SAFE_FREE(gps->triangles); | |||||
| BLI_freelinkN(&gpf->strokes, gps); | |||||
| changed = true; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| CTX_DATA_END; | |||||
| /* notifiers */ | |||||
| if (changed) { | |||||
| DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | |||||
| } | |||||
| return OPERATOR_FINISHED; | |||||
| } | |||||
| void GPENCIL_OT_frame_clean_fill(wmOperatorType *ot) | |||||
| { | |||||
| static const EnumPropertyItem duplicate_mode[] = { | |||||
| { GP_FRAME_CLEAN_FILL_ACTIVE, "ACTIVE", 0, "Active Frame Only", "Clean active frame only" }, | |||||
| { GP_FRAME_CLEAN_FILL_ALL, "ALL", 0, "All Frames", "Clean all frames in all layers" }, | |||||
| { 0, NULL, 0, NULL, NULL } | |||||
| }; | |||||
| /* identifiers */ | |||||
| ot->name = "Clean Fill Boundaries"; | |||||
| ot->idname = "GPENCIL_OT_frame_clean_fill"; | |||||
| ot->description = "Remove 'no fill' boundary strokes"; | |||||
| /* callbacks */ | |||||
| ot->exec = gp_frame_clean_fill_exec; | |||||
| ot->poll = gp_active_layer_poll; | |||||
| /* flags */ | |||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | |||||
| ot->prop = RNA_def_enum(ot->srna, "mode", duplicate_mode, GP_FRAME_DUP_ACTIVE, "Mode", ""); | |||||
| } | |||||
| /* *********************** Hide Layers ******************************** */ | /* *********************** Hide Layers ******************************** */ | ||||
| static int gp_hide_exec(bContext *C, wmOperator *op) | static int gp_hide_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | bGPdata *gpd = ED_gpencil_data_get_active(C); | ||||
| bGPDlayer *layer = BKE_gpencil_layer_getactive(gpd); | bGPDlayer *layer = BKE_gpencil_layer_getactive(gpd); | ||||
| bool unselected = RNA_boolean_get(op->ptr, "unselected"); | bool unselected = RNA_boolean_get(op->ptr, "unselected"); | ||||
| Show All 12 Lines | if (unselected) { | ||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| /* hide selected/active */ | /* hide selected/active */ | ||||
| layer->flag |= GP_LAYER_HIDE; | layer->flag |= GP_LAYER_HIDE; | ||||
| } | } | ||||
| /* notifiers */ | /* notifiers */ | ||||
| DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_hide(wmOperatorType *ot) | void GPENCIL_OT_hide(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ▲ Show 20 Lines • Show All 71 Lines • ▼ Show 20 Lines | if (gpl->flag & GP_LAYER_HIDE) { | ||||
| gp_reveal_select_frame(C, gpf, false); | gp_reveal_select_frame(C, gpf, false); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* notifiers */ | /* notifiers */ | ||||
| DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_reveal(wmOperatorType *ot) | void GPENCIL_OT_reveal(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| Show All 24 Lines | if (gpd == NULL) | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| /* make all layers non-editable */ | /* make all layers non-editable */ | ||||
| for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { | for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { | ||||
| gpl->flag |= GP_LAYER_LOCKED; | gpl->flag |= GP_LAYER_LOCKED; | ||||
| } | } | ||||
| /* notifiers */ | /* notifiers */ | ||||
| DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_lock_all(wmOperatorType *ot) | void GPENCIL_OT_lock_all(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| Show All 21 Lines | if (gpd == NULL) | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| /* make all layers editable again */ | /* make all layers editable again */ | ||||
| for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { | for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { | ||||
| gpl->flag &= ~GP_LAYER_LOCKED; | gpl->flag &= ~GP_LAYER_LOCKED; | ||||
| } | } | ||||
| /* notifiers */ | /* notifiers */ | ||||
| DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_unlock_all(wmOperatorType *ot) | void GPENCIL_OT_unlock_all(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ▲ Show 20 Lines • Show All 56 Lines • ▼ Show 20 Lines | static int gp_isolate_layer_exec(bContext *C, wmOperator *op) | ||||
| else { | else { | ||||
| /* Clear flags - Restore everything else */ | /* Clear flags - Restore everything else */ | ||||
| for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { | for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { | ||||
| gpl->flag &= ~flags; | gpl->flag &= ~flags; | ||||
| } | } | ||||
| } | } | ||||
| /* notifiers */ | /* notifiers */ | ||||
| DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_layer_isolate(wmOperatorType *ot) | void GPENCIL_OT_layer_isolate(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| Show All 27 Lines | static int gp_merge_layer_exec(bContext *C, wmOperator *op) | ||||
| } | } | ||||
| /* Collect frames of gpl_current in hash table to avoid O(n^2) lookups */ | /* Collect frames of gpl_current in hash table to avoid O(n^2) lookups */ | ||||
| GHash *gh_frames_cur = BLI_ghash_int_new_ex(__func__, 64); | GHash *gh_frames_cur = BLI_ghash_int_new_ex(__func__, 64); | ||||
| for (bGPDframe *gpf = gpl_current->frames.first; gpf; gpf = gpf->next) { | for (bGPDframe *gpf = gpl_current->frames.first; gpf; gpf = gpf->next) { | ||||
| BLI_ghash_insert(gh_frames_cur, SET_INT_IN_POINTER(gpf->framenum), gpf); | BLI_ghash_insert(gh_frames_cur, SET_INT_IN_POINTER(gpf->framenum), gpf); | ||||
| } | } | ||||
| /* read all frames from next layer */ | /* read all frames from next layer and add any missing in current layer */ | ||||
| for (bGPDframe *gpf = gpl_next->frames.first; gpf; gpf = gpf->next) { | for (bGPDframe *gpf = gpl_next->frames.first; gpf; gpf = gpf->next) { | ||||
| /* try to find frame in active layer */ | /* try to find frame in current layer */ | ||||
| bGPDframe *frame = BLI_ghash_lookup(gh_frames_cur, SET_INT_IN_POINTER(gpf->framenum)); | bGPDframe *frame = BLI_ghash_lookup(gh_frames_cur, SET_INT_IN_POINTER(gpf->framenum)); | ||||
| if (!frame) { | if (!frame) { | ||||
| /* nothing found, create new */ | bGPDframe *actframe = BKE_gpencil_layer_getframe(gpl_current, gpf->framenum, GP_GETFRAME_USE_PREV); | ||||
| frame = BKE_gpencil_frame_addnew(gpl_current, gpf->framenum); | frame = BKE_gpencil_frame_addnew(gpl_current, gpf->framenum); | ||||
| /* duplicate strokes of current active frame */ | |||||
| if (actframe) { | |||||
| BKE_gpencil_frame_copy_strokes(actframe, frame); | |||||
| } | |||||
| } | } | ||||
| /* add to tail all strokes */ | /* add to tail all strokes */ | ||||
| BLI_movelisttolist(&frame->strokes, &gpf->strokes); | BLI_movelisttolist(&frame->strokes, &gpf->strokes); | ||||
| } | } | ||||
| /* Now delete next layer */ | /* Now delete next layer */ | ||||
| BKE_gpencil_layer_delete(gpd, gpl_next); | BKE_gpencil_layer_delete(gpd, gpl_next); | ||||
| BLI_ghash_free(gh_frames_cur, NULL, NULL); | BLI_ghash_free(gh_frames_cur, NULL, NULL); | ||||
| /* notifiers */ | /* notifiers */ | ||||
| DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_layer_merge(wmOperatorType *ot) | void GPENCIL_OT_layer_merge(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | if (gpl == NULL) { | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| } | } | ||||
| /* Set active layer */ | /* Set active layer */ | ||||
| BKE_gpencil_layer_setactive(gpd, gpl); | BKE_gpencil_layer_setactive(gpd, gpl); | ||||
| /* updates */ | /* updates */ | ||||
| DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_layer_change(wmOperatorType *ot) | void GPENCIL_OT_layer_change(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| Show All 22 Lines | enum { | ||||
| GP_STROKE_MOVE_UP = -1, | GP_STROKE_MOVE_UP = -1, | ||||
| GP_STROKE_MOVE_DOWN = 1, | GP_STROKE_MOVE_DOWN = 1, | ||||
| GP_STROKE_MOVE_TOP = 2, | GP_STROKE_MOVE_TOP = 2, | ||||
| GP_STROKE_MOVE_BOTTOM = 3 | GP_STROKE_MOVE_BOTTOM = 3 | ||||
| }; | }; | ||||
| static int gp_stroke_arrange_exec(bContext *C, wmOperator *op) | static int gp_stroke_arrange_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| Object *ob = CTX_data_active_object(C); | |||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | bGPdata *gpd = ED_gpencil_data_get_active(C); | ||||
| bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); | bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); | ||||
| bGPDstroke *gps; | bGPDstroke *gps; | ||||
| /* sanity checks */ | /* sanity checks */ | ||||
| if (ELEM(NULL, gpd, gpl, gpl->actframe)) { | if (ELEM(NULL, gpd, gpl, gpl->actframe)) { | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| bGPDframe *gpf = gpl->actframe; | |||||
| /* temp listbase to store selected strokes */ | |||||
| ListBase selected = {NULL}; | |||||
| const int direction = RNA_enum_get(op->ptr, "direction"); | const int direction = RNA_enum_get(op->ptr, "direction"); | ||||
| for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { | |||||
| /* temp listbase to store selected strokes by layer */ | |||||
| ListBase selected = { NULL }; | |||||
| bGPDframe *gpf = gpl->actframe; | |||||
| if (gpl->flag & GP_LAYER_LOCKED) { | |||||
| continue; | |||||
| } | |||||
| if (gpf == NULL) { | |||||
| continue; | |||||
| } | |||||
| bool gpf_lock = false; | |||||
| /* verify if any selected stroke is in the extreme of the stack and select to move */ | /* verify if any selected stroke is in the extreme of the stack and select to move */ | ||||
| for (gps = gpf->strokes.first; gps; gps = gps->next) { | for (gps = gpf->strokes.first; gps; gps = gps->next) { | ||||
| /* only if selected */ | /* only if selected */ | ||||
| if (gps->flag & GP_STROKE_SELECT) { | if (gps->flag & GP_STROKE_SELECT) { | ||||
| /* skip strokes that are invalid for current view */ | /* skip strokes that are invalid for current view */ | ||||
| if (ED_gpencil_stroke_can_use(C, gps) == false) { | if (ED_gpencil_stroke_can_use(C, gps) == false) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| /* check if the color is editable */ | /* check if the color is editable */ | ||||
| if (ED_gpencil_stroke_color_use(gpl, gps) == false) { | if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| /* some stroke is already at front*/ | /* some stroke is already at front*/ | ||||
| if ((direction == GP_STROKE_MOVE_TOP) || (direction == GP_STROKE_MOVE_UP)) { | if ((direction == GP_STROKE_MOVE_TOP) || (direction == GP_STROKE_MOVE_UP)) { | ||||
| if (gps == gpf->strokes.last) { | if (gps == gpf->strokes.last) { | ||||
| return OPERATOR_CANCELLED; | gpf_lock = true; | ||||
| continue; | |||||
| } | } | ||||
| } | } | ||||
| /* some stroke is already at botom */ | /* some stroke is already at botom */ | ||||
| if ((direction == GP_STROKE_MOVE_BOTTOM) || (direction == GP_STROKE_MOVE_DOWN)) { | if ((direction == GP_STROKE_MOVE_BOTTOM) || (direction == GP_STROKE_MOVE_DOWN)) { | ||||
| if (gps == gpf->strokes.first) { | if (gps == gpf->strokes.first) { | ||||
| return OPERATOR_CANCELLED; | gpf_lock = true; | ||||
| continue; | |||||
| } | } | ||||
| } | } | ||||
| /* add to list */ | /* add to list (if not locked) */ | ||||
| if (!gpf_lock) { | |||||
| BLI_addtail(&selected, BLI_genericNodeN(gps)); | BLI_addtail(&selected, BLI_genericNodeN(gps)); | ||||
| } | } | ||||
| } | } | ||||
| } | |||||
| /* Now do the movement of the stroke */ | /* Now do the movement of the stroke */ | ||||
| if (!gpf_lock) { | |||||
| switch (direction) { | switch (direction) { | ||||
| /* Bring to Front */ | /* Bring to Front */ | ||||
| case GP_STROKE_MOVE_TOP: | case GP_STROKE_MOVE_TOP: | ||||
| for (LinkData *link = selected.first; link; link = link->next) { | for (LinkData *link = selected.first; link; link = link->next) { | ||||
| gps = link->data; | gps = link->data; | ||||
| BLI_remlink(&gpf->strokes, gps); | BLI_remlink(&gpf->strokes, gps); | ||||
| BLI_addtail(&gpf->strokes, gps); | BLI_addtail(&gpf->strokes, gps); | ||||
| } | } | ||||
| break; | break; | ||||
| /* Bring Forward */ | /* Bring Forward */ | ||||
| case GP_STROKE_MOVE_UP: | case GP_STROKE_MOVE_UP: | ||||
| for (LinkData *link = selected.last; link; link = link->prev) { | for (LinkData *link = selected.last; link; link = link->prev) { | ||||
| gps = link->data; | gps = link->data; | ||||
| BLI_listbase_link_move(&gpf->strokes, gps, 1); | BLI_listbase_link_move(&gpf->strokes, gps, 1); | ||||
| } | } | ||||
| break; | break; | ||||
| /* Send Backward */ | /* Send Backward */ | ||||
| case GP_STROKE_MOVE_DOWN: | case GP_STROKE_MOVE_DOWN: | ||||
| for (LinkData *link = selected.first; link; link = link->next) { | for (LinkData *link = selected.first; link; link = link->next) { | ||||
| gps = link->data; | gps = link->data; | ||||
| BLI_listbase_link_move(&gpf->strokes, gps, -1); | BLI_listbase_link_move(&gpf->strokes, gps, -1); | ||||
| } | } | ||||
| break; | break; | ||||
| /* Send to Back */ | /* Send to Back */ | ||||
| case GP_STROKE_MOVE_BOTTOM: | case GP_STROKE_MOVE_BOTTOM: | ||||
| for (LinkData *link = selected.last; link; link = link->prev) { | for (LinkData *link = selected.last; link; link = link->prev) { | ||||
| gps = link->data; | gps = link->data; | ||||
| BLI_remlink(&gpf->strokes, gps); | BLI_remlink(&gpf->strokes, gps); | ||||
| BLI_addhead(&gpf->strokes, gps); | BLI_addhead(&gpf->strokes, gps); | ||||
| } | } | ||||
| break; | break; | ||||
| default: | default: | ||||
| BLI_assert(0); | BLI_assert(0); | ||||
| break; | break; | ||||
| } | } | ||||
| } | |||||
| BLI_freelistN(&selected); | BLI_freelistN(&selected); | ||||
| } | |||||
| /* notifiers */ | /* notifiers */ | ||||
| DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_stroke_arrange(wmOperatorType *ot) | void GPENCIL_OT_stroke_arrange(wmOperatorType *ot) | ||||
| { | { | ||||
| static const EnumPropertyItem slot_move[] = { | static const EnumPropertyItem slot_move[] = { | ||||
| {GP_STROKE_MOVE_UP, "UP", 0, "Bring Forward", ""}, | {GP_STROKE_MOVE_UP, "UP", 0, "Bring Forward", ""}, | ||||
| {GP_STROKE_MOVE_DOWN, "DOWN", 0, "Send Backward", ""}, | {GP_STROKE_MOVE_DOWN, "DOWN", 0, "Send Backward", ""}, | ||||
| {GP_STROKE_MOVE_TOP, "TOP", 0, "Bring to Front", ""}, | {GP_STROKE_MOVE_TOP, "TOP", 0, "Bring to Front", ""}, | ||||
| {GP_STROKE_MOVE_BOTTOM, "BOTTOM", 0, "Send to Back", ""}, | {GP_STROKE_MOVE_BOTTOM, "BOTTOM", 0, "Send to Back", ""}, | ||||
| {0, NULL, 0, NULL, NULL } | {0, NULL, 0, NULL, NULL } | ||||
| }; | }; | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Arrange Stroke"; | ot->name = "Arrange Stroke"; | ||||
| ot->idname = "GPENCIL_OT_stroke_arrange"; | ot->idname = "GPENCIL_OT_stroke_arrange"; | ||||
| ot->description = "Arrange selected strokes up/down in the drawing order of the active layer"; | ot->description = "Arrange selected strokes up/down in the drawing order of the active layer"; | ||||
| /* api callbacks */ | /* callbacks */ | ||||
| ot->exec = gp_stroke_arrange_exec; | ot->exec = gp_stroke_arrange_exec; | ||||
| ot->poll = gp_active_layer_poll; | ot->poll = gp_active_layer_poll; | ||||
| /* flags */ | /* flags */ | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| /* properties */ | |||||
| ot->prop = RNA_def_enum(ot->srna, "direction", slot_move, GP_STROKE_MOVE_UP, "Direction", ""); | ot->prop = RNA_def_enum(ot->srna, "direction", slot_move, GP_STROKE_MOVE_UP, "Direction", ""); | ||||
| } | } | ||||
| /* ******************* Move Stroke to new color ************************** */ | /* ******************* Move Stroke to new color ************************** */ | ||||
| static int gp_stroke_change_color_exec(bContext *C, wmOperator *UNUSED(op)) | static int gp_stroke_change_color_exec(bContext *C, wmOperator *UNUSED(op)) | ||||
| { | { | ||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | bGPdata *gpd = ED_gpencil_data_get_active(C); | ||||
| bGPDpalette *palette; | Object *ob = CTX_data_active_object(C); | ||||
| bGPDpalettecolor *color; | Material *ma = give_current_material(ob, ob->actcol); | ||||
| int idx = BKE_object_material_slot_find_index(ob, ma) - 1; | |||||
| /* sanity checks */ | /* sanity checks */ | ||||
| if (ELEM(NULL, gpd)) { | if (ELEM(NULL, gpd)) { | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| palette = BKE_gpencil_palette_getactive(gpd); | bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); | ||||
| color = BKE_gpencil_palettecolor_getactive(palette); | if (ELEM(NULL, ma)) { | ||||
| if (ELEM(NULL, palette, color)) { | |||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| /* loop all strokes */ | /* loop all strokes */ | ||||
| for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { | CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) | ||||
| /* only editable and visible layers are considered */ | { | ||||
| if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { | bGPDframe *init_gpf = gpl->actframe; | ||||
| for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) { | if (is_multiedit) { | ||||
| init_gpf = gpl->frames.first; | |||||
| } | |||||
| for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { | |||||
| if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { | |||||
| if (gpf == NULL) | |||||
| continue; | |||||
| for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { | |||||
| /* only if selected */ | /* only if selected */ | ||||
| if (gps->flag & GP_STROKE_SELECT) { | if (gps->flag & GP_STROKE_SELECT) { | ||||
| /* skip strokes that are invalid for current view */ | /* skip strokes that are invalid for current view */ | ||||
| if (ED_gpencil_stroke_can_use(C, gps) == false) | if (ED_gpencil_stroke_can_use(C, gps) == false) | ||||
| continue; | continue; | ||||
| /* check if the color is editable */ | /* check if the color is editable */ | ||||
| if (ED_gpencil_stroke_color_use(gpl, gps) == false) | if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) | ||||
| continue; | continue; | ||||
| /* asign new color (only if different) */ | /* asign new color */ | ||||
| if ((STREQ(gps->colorname, color->info) == false) || (gps->palcolor != color)) { | gps->mat_nr = idx; | ||||
| BLI_strncpy(gps->colorname, color->info, sizeof(gps->colorname)); | |||||
| gps->palcolor = color; | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| CTX_DATA_END; | |||||
| /* notifiers */ | /* notifiers */ | ||||
| DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_stroke_change_color(wmOperatorType *ot) | void GPENCIL_OT_stroke_change_color(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Change Stroke Color"; | ot->name = "Change Stroke Color"; | ||||
| ot->idname = "GPENCIL_OT_stroke_change_color"; | ot->idname = "GPENCIL_OT_stroke_change_color"; | ||||
| ot->description = "Move selected strokes to active color"; | ot->description = "Move selected strokes to active color"; | ||||
| /* api callbacks */ | /* callbacks */ | ||||
| ot->exec = gp_stroke_change_color_exec; | ot->exec = gp_stroke_change_color_exec; | ||||
| ot->poll = gp_active_layer_poll; | ot->poll = gp_active_layer_poll; | ||||
| /* flags */ | |||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | |||||
| } | } | ||||
| /* ******************* Lock color of non selected Strokes colors ************************** */ | /* ******************* Lock color of non selected Strokes colors ************************** */ | ||||
| static int gp_stroke_lock_color_exec(bContext *C, wmOperator *UNUSED(op)) | static int gp_stroke_lock_color_exec(bContext *C, wmOperator *UNUSED(op)) | ||||
| { | { | ||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | bGPdata *gpd = ED_gpencil_data_get_active(C); | ||||
| bGPDpalette *palette; | |||||
| Object *ob = CTX_data_active_object(C); | |||||
| Material ***matar = give_matarar(ob); | |||||
| short *totcol = give_totcolp(ob); | |||||
| /* sanity checks */ | /* sanity checks */ | ||||
| if (ELEM(NULL, gpd)) | if (ELEM(NULL, gpd)) | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| palette = BKE_gpencil_palette_getactive(gpd); | |||||
| if (ELEM(NULL, palette)) | |||||
| return OPERATOR_CANCELLED; | |||||
| /* first lock all colors */ | /* first lock all colors */ | ||||
| for (bGPDpalettecolor *palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { | for (short i = 0; i < *totcol; i++) { | ||||
| palcolor->flag |= PC_COLOR_LOCKED; | Material *tmp_ma = (*matar)[i]; | ||||
| tmp_ma->gp_style->flag |= GP_STYLE_COLOR_LOCKED; | |||||
| } | } | ||||
| /* loop all selected strokes and unlock any color */ | /* loop all selected strokes and unlock any color */ | ||||
| for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { | for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { | ||||
| /* only editable and visible layers are considered */ | /* only editable and visible layers are considered */ | ||||
| if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { | if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { | ||||
| for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) { | for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) { | ||||
| /* only if selected */ | /* only if selected */ | ||||
| if (gps->flag & GP_STROKE_SELECT) { | if (gps->flag & GP_STROKE_SELECT) { | ||||
| /* skip strokes that are invalid for current view */ | /* skip strokes that are invalid for current view */ | ||||
| if (ED_gpencil_stroke_can_use(C, gps) == false) { | if (ED_gpencil_stroke_can_use(C, gps) == false) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| /* unlock color */ | /* unlock color */ | ||||
| if (gps->palcolor != NULL) { | Material *tmp_ma = (*matar)[gps->mat_nr]; | ||||
| gps->palcolor->flag &= ~PC_COLOR_LOCKED; | tmp_ma->gp_style->flag &= ~GP_STYLE_COLOR_LOCKED; | ||||
| } | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* notifiers */ | /* notifiers */ | ||||
| DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_stroke_lock_color(wmOperatorType *ot) | void GPENCIL_OT_stroke_lock_color(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Lock Unused Colors"; | ot->name = "Lock Unused Colors"; | ||||
| ot->idname = "GPENCIL_OT_stroke_lock_color"; | ot->idname = "GPENCIL_OT_stroke_lock_color"; | ||||
| ot->description = "Lock any color not used in any selected stroke"; | ot->description = "Lock any color not used in any selected stroke"; | ||||
| /* api callbacks */ | /* api callbacks */ | ||||
| ot->exec = gp_stroke_lock_color_exec; | ot->exec = gp_stroke_lock_color_exec; | ||||
| ot->poll = gp_active_layer_poll; | ot->poll = gp_active_layer_poll; | ||||
| /* flags */ | |||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | |||||
| } | } | ||||
| /* ************************************************ */ | /* ************************************************ */ | ||||
| /* Drawing Brushes Operators */ | /* Drawing Brushes Operators */ | ||||
| /* ******************* Add New Brush ************************ */ | /* ******************* Brush create presets ************************** */ | ||||
| static int gp_brush_presets_create_exec(bContext *C, wmOperator *UNUSED(op)) | |||||
| /* add new brush - wrapper around API */ | |||||
| static int gp_brush_add_exec(bContext *C, wmOperator *op) | |||||
| { | { | ||||
| ToolSettings *ts = CTX_data_tool_settings(C); | BKE_brush_gpencil_presets(C); | ||||
| /* if there's no existing container */ | |||||
| if (ts == NULL) { | |||||
| BKE_report(op->reports, RPT_ERROR, "Nowhere for brush data to go"); | |||||
| return OPERATOR_CANCELLED; | |||||
| } | |||||
| /* add new brush now */ | |||||
| BKE_gpencil_brush_addnew(ts, DATA_("GP_Brush"), true); | |||||
| /* notifiers */ | /* notifiers */ | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_brush_add(wmOperatorType *ot) | void GPENCIL_OT_brush_presets_create(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Add Brush"; | ot->name = "Create Preset Brushes"; | ||||
| ot->idname = "GPENCIL_OT_brush_add"; | ot->idname = "GPENCIL_OT_brush_presets_create"; | ||||
| ot->description = "Add new Grease Pencil drawing brush for the active Grease Pencil data-block"; | ot->description = "Create a set of predefined Grease Pencil drawing brushes"; | ||||
| /* api callbacks */ | |||||
| ot->exec = gp_brush_presets_create_exec; | |||||
| /* flags */ | |||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| /* callbacks */ | |||||
| ot->exec = gp_brush_add_exec; | |||||
| ot->poll = gp_add_poll; | |||||
| } | } | ||||
| /* ******************* Remove Active Brush ************************* */ | /* ***************** Select Brush ************************ */ | ||||
| static int gp_brush_remove_exec(bContext *C, wmOperator *op) | static int gp_brush_select_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| ToolSettings *ts = CTX_data_tool_settings(C); | ToolSettings *ts = CTX_data_tool_settings(C); | ||||
| bGPDbrush *brush = BKE_gpencil_brush_getactive(ts); | Main *bmain = CTX_data_main(C); | ||||
| /* sanity checks */ | |||||
| if (ELEM(NULL, ts, brush)) | |||||
| return OPERATOR_CANCELLED; | |||||
| if (BLI_listbase_count_at_most(&ts->gp_brushes, 2) < 2) { | /* if there's no existing container */ | ||||
| BKE_report(op->reports, RPT_ERROR, "Grease Pencil needs a brush, unable to delete the last one"); | if (ts == NULL) { | ||||
| BKE_report(op->reports, RPT_ERROR, "Nowhere to go"); | |||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| const int index = RNA_int_get(op->ptr, "index"); | |||||
| /* make the brush before this the new active brush | Paint *paint = BKE_brush_get_gpencil_paint(ts); | ||||
| * - use the one after if this is the first | int i = 0; | ||||
| * - if this is the only brush, this naturally becomes NULL | for (Brush *brush = bmain->brush.first; brush; brush = brush->id.next) { | ||||
| */ | if (brush->ob_mode == OB_MODE_GPENCIL_PAINT) { | ||||
| if (brush->prev) | if (i == index) { | ||||
| BKE_gpencil_brush_setactive(ts, brush->prev); | BKE_paint_brush_set(paint, brush); | ||||
| else | |||||
| BKE_gpencil_brush_setactive(ts, brush->next); | |||||
| /* delete the brush now... */ | |||||
| BKE_gpencil_brush_delete(ts, brush); | |||||
| /* notifiers */ | /* notifiers */ | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| i++; | |||||
| } | |||||
| } | |||||
| void GPENCIL_OT_brush_remove(wmOperatorType *ot) | return OPERATOR_CANCELLED; | ||||
| } | |||||
| void GPENCIL_OT_brush_select(wmOperatorType *ot) | |||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Remove Brush"; | ot->name = "Select Brush"; | ||||
| ot->idname = "GPENCIL_OT_brush_remove"; | ot->idname = "GPENCIL_OT_brush_select"; | ||||
| ot->description = "Remove active Grease Pencil drawing brush"; | ot->description = "Select a Grease Pencil drawing brush"; | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | |||||
| /* callbacks */ | /* callbacks */ | ||||
| ot->exec = gp_brush_remove_exec; | ot->exec = gp_brush_select_exec; | ||||
| ot->poll = gp_active_brush_poll; | ot->poll = gp_active_brush_poll; | ||||
| } | |||||
| /* ********************** Change Brush ***************************** */ | |||||
| static int gp_brush_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt)) | |||||
| { | |||||
| uiPopupMenu *pup; | |||||
| uiLayout *layout; | |||||
| /* call the menu, which will call this operator again, hence the canceled */ | /* flags */ | ||||
| pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE); | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| layout = UI_popup_menu_layout(pup); | |||||
| uiItemsEnumO(layout, "GPENCIL_OT_brush_change", "brush"); | |||||
| UI_popup_menu_end(C, pup); | |||||
| return OPERATOR_INTERFACE; | /* properties */ | ||||
| RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "Index of Drawing Brush", 0, INT_MAX); | |||||
| } | } | ||||
| static int gp_brush_change_exec(bContext *C, wmOperator *op) | /* ***************** Select Sculpt Brush ************************ */ | ||||
| static int gp_sculpt_select_exec(bContext *C, wmOperator *op) | |||||
| { | { | ||||
| ToolSettings *ts = CTX_data_tool_settings(C); | ToolSettings *ts = CTX_data_tool_settings(C); | ||||
| bGPDbrush *brush = NULL; | |||||
| int brush_num = RNA_enum_get(op->ptr, "brush"); | |||||
| /* Get brush or create new one */ | /* if there's no existing container */ | ||||
| if (brush_num == -1) { | if (ts == NULL) { | ||||
| /* Create brush */ | BKE_report(op->reports, RPT_ERROR, "Nowhere to go"); | ||||
| brush = BKE_gpencil_brush_addnew(ts, DATA_("GP_Brush"), true); | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| else { | |||||
| /* Try to get brush */ | |||||
| brush = BLI_findlink(&ts->gp_brushes, brush_num); | |||||
| if (brush == NULL) { | const int index = RNA_int_get(op->ptr, "index"); | ||||
| BKE_reportf(op->reports, RPT_ERROR, "Cannot change to non-existent brush (index = %d)", brush_num); | GP_BrushEdit_Settings *gp_sculpt = &ts->gp_sculpt; | ||||
| /* sanity checks */ | |||||
| if (ELEM(NULL, gp_sculpt)) { | |||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| } | |||||
| /* Set active brush */ | |||||
| BKE_gpencil_brush_setactive(ts, brush); | |||||
| /* updates */ | if (index < TOT_GP_EDITBRUSH_TYPES - 1) { | ||||
| gp_sculpt->brushtype = index; | |||||
| } | |||||
| /* notifiers */ | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_brush_change(wmOperatorType *ot) | void GPENCIL_OT_sculpt_select(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Change Brush"; | ot->name = "Select Sculpt Brush"; | ||||
| ot->idname = "GPENCIL_OT_brush_change"; | ot->idname = "GPENCIL_OT_sculpt_select"; | ||||
| ot->description = "Change active Grease Pencil drawing brush"; | ot->description = "Select a Grease Pencil sculpt brush"; | ||||
| /* callbacks */ | /* callbacks */ | ||||
| ot->invoke = gp_brush_change_invoke; | ot->exec = gp_sculpt_select_exec; | ||||
| ot->exec = gp_brush_change_exec; | ot->poll = gp_add_poll; | ||||
| ot->poll = gp_active_brush_poll; | |||||
| /* flags */ | /* flags */ | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| /* gp brush to use (dynamic enum) */ | /* properties */ | ||||
| ot->prop = RNA_def_enum(ot->srna, "brush", DummyRNA_DEFAULT_items, 0, "Grease Pencil Brush", ""); | RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "Index of Sculpt Brush", 0, INT_MAX); | ||||
| RNA_def_enum_funcs(ot->prop, ED_gpencil_brushes_enum_itemf); | |||||
| } | } | ||||
| /* ******************* Move Brush Up/Down ************************** */ | /*********************** Vertex Groups ***********************************/ | ||||
| enum { | |||||
| GP_BRUSH_MOVE_UP = -1, | |||||
| GP_BRUSH_MOVE_DOWN = 1 | |||||
| }; | |||||
| static int gp_brush_move_exec(bContext *C, wmOperator *op) | static bool gpencil_vertex_group_poll(bContext *C) | ||||
| { | { | ||||
| ToolSettings *ts = CTX_data_tool_settings(C); | Object *ob = CTX_data_active_object(C); | ||||
| bGPDbrush *brush = BKE_gpencil_brush_getactive(ts); | |||||
| int direction = RNA_enum_get(op->ptr, "type"); | if ((ob) && (ob->type == OB_GPENCIL)) { | ||||
| if (!ID_IS_LINKED(ob) && !ID_IS_LINKED(ob->data) && ob->defbase.first) { | |||||
| if (ELEM(ob->mode, | |||||
| OB_MODE_GPENCIL_EDIT, | |||||
| OB_MODE_GPENCIL_SCULPT)) | |||||
| { | |||||
| return true; | |||||
| } | |||||
| } | |||||
| } | |||||
| /* sanity checks */ | return false; | ||||
| if (ELEM(NULL, ts, brush)) { | |||||
| return OPERATOR_CANCELLED; | |||||
| } | } | ||||
| /* up or down? */ | static bool gpencil_vertex_group_weight_poll(bContext *C) | ||||
| if (direction == GP_BRUSH_MOVE_UP) { | { | ||||
| /* up */ | Object *ob = CTX_data_active_object(C); | ||||
| BLI_remlink(&ts->gp_brushes, brush); | |||||
| BLI_insertlinkbefore(&ts->gp_brushes, brush->prev, brush); | if ((ob) && (ob->type == OB_GPENCIL)) { | ||||
| if (!ID_IS_LINKED(ob) && !ID_IS_LINKED(ob->data) && ob->defbase.first) { | |||||
| if (ob->mode == OB_MODE_GPENCIL_WEIGHT) | |||||
| { | |||||
| return true; | |||||
| } | } | ||||
| else if (direction == GP_BRUSH_MOVE_DOWN) { | |||||
| /* down */ | |||||
| BLI_remlink(&ts->gp_brushes, brush); | |||||
| BLI_insertlinkafter(&ts->gp_brushes, brush->next, brush); | |||||
| } | } | ||||
| else { | |||||
| BLI_assert(0); | |||||
| } | } | ||||
| return false; | |||||
| } | |||||
| static int gpencil_vertex_group_assign_exec(bContext *C, wmOperator *UNUSED(op)) | |||||
| { | |||||
| ToolSettings *ts = CTX_data_tool_settings(C); | |||||
| Object *ob = CTX_data_active_object(C); | |||||
| /* sanity checks */ | |||||
| if (ELEM(NULL, ts, ob, ob->data)) | |||||
| return OPERATOR_CANCELLED; | |||||
| ED_gpencil_vgroup_assign(C, ob, ts->vgroup_weight); | |||||
| /* notifiers */ | /* notifiers */ | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | bGPdata *gpd = ob->data; | ||||
| DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); | |||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_brush_move(wmOperatorType *ot) | void GPENCIL_OT_vertex_group_assign(wmOperatorType *ot) | ||||
| { | { | ||||
| static const EnumPropertyItem slot_move[] = { | |||||
| {GP_BRUSH_MOVE_UP, "UP", 0, "Up", ""}, | |||||
| {GP_BRUSH_MOVE_DOWN, "DOWN", 0, "Down", ""}, | |||||
| {0, NULL, 0, NULL, NULL } | |||||
| }; | |||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Move Brush"; | ot->name = "Assign to Vertex Group"; | ||||
| ot->idname = "GPENCIL_OT_brush_move"; | ot->idname = "GPENCIL_OT_vertex_group_assign"; | ||||
| ot->description = "Move the active Grease Pencil drawing brush up/down in the list"; | ot->description = "Assign the selected vertices to the active vertex group"; | ||||
| /* api callbacks */ | /* api callbacks */ | ||||
| ot->exec = gp_brush_move_exec; | ot->poll = gpencil_vertex_group_poll; | ||||
| ot->poll = gp_active_brush_poll; | ot->exec = gpencil_vertex_group_assign_exec; | ||||
| /* flags */ | /* flags */ | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| ot->prop = RNA_def_enum(ot->srna, "type", slot_move, GP_BRUSH_MOVE_UP, "Type", ""); | |||||
| } | } | ||||
| /* ******************* Brush create presets ************************** */ | /* remove point from vertex group */ | ||||
| static int gpencil_vertex_group_remove_from_exec(bContext *C, wmOperator *UNUSED(op)) | |||||
| static int gp_brush_presets_create_exec(bContext *C, wmOperator *UNUSED(op)) | |||||
| { | { | ||||
| ToolSettings *ts = CTX_data_tool_settings(C); | Object *ob = CTX_data_active_object(C); | ||||
| BKE_gpencil_brush_init_presets(ts); | |||||
| /* sanity checks */ | |||||
| if (ELEM(NULL, ob, ob->data)) | |||||
| return OPERATOR_CANCELLED; | |||||
| ED_gpencil_vgroup_remove(C, ob); | |||||
| /* notifiers */ | /* notifiers */ | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | bGPdata *gpd = ob->data; | ||||
| DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); | |||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_brush_presets_create(wmOperatorType *ot) | void GPENCIL_OT_vertex_group_remove_from(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Create Preset Brushes"; | ot->name = "Remove from Vertex Group"; | ||||
| ot->idname = "GPENCIL_OT_brush_presets_create"; | ot->idname = "GPENCIL_OT_vertex_group_remove_from"; | ||||
| ot->description = "Create a set of predefined Grease Pencil drawing brushes"; | ot->description = "Remove the selected vertices from active or all vertex group(s)"; | ||||
| /* api callbacks */ | /* api callbacks */ | ||||
| ot->exec = gp_brush_presets_create_exec; | ot->poll = gpencil_vertex_group_poll; | ||||
| ot->exec = gpencil_vertex_group_remove_from_exec; | |||||
| /* flags */ | /* flags */ | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| } | } | ||||
| /* ***************** Copy Brush ************************ */ | static int gpencil_vertex_group_select_exec(bContext *C, wmOperator *UNUSED(op)) | ||||
| static int gp_brush_copy_exec(bContext *C, wmOperator *op) | |||||
| { | { | ||||
| ToolSettings *ts = CTX_data_tool_settings(C); | Object *ob = CTX_data_active_object(C); | ||||
| /* if there's no existing container */ | |||||
| if (ts == NULL) { | |||||
| BKE_report(op->reports, RPT_ERROR, "Nowhere for brush data to go"); | |||||
| return OPERATOR_CANCELLED; | |||||
| } | |||||
| bGPDbrush *brush = BKE_gpencil_brush_getactive(ts); | |||||
| bGPDbrush *newbrush; | |||||
| /* sanity checks */ | /* sanity checks */ | ||||
| if (ELEM(NULL, brush)) | if (ELEM(NULL, ob, ob->data)) | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| /* create a brush and duplicate data */ | ED_gpencil_vgroup_select(C, ob); | ||||
| newbrush = BKE_gpencil_brush_addnew(ts, brush->info, true); | |||||
| newbrush->thickness = brush->thickness; | |||||
| newbrush->draw_smoothfac = brush->draw_smoothfac; | |||||
| newbrush->draw_smoothlvl = brush->draw_smoothlvl; | |||||
| newbrush->sublevel = brush->sublevel; | |||||
| newbrush->flag = brush->flag; | |||||
| newbrush->draw_sensitivity = brush->draw_sensitivity; | |||||
| newbrush->draw_strength = brush->draw_strength; | |||||
| newbrush->draw_jitter = brush->draw_jitter; | |||||
| newbrush->draw_angle = brush->draw_angle; | |||||
| newbrush->draw_angle_factor = brush->draw_angle_factor; | |||||
| newbrush->draw_random_press = brush->draw_random_press; | |||||
| newbrush->draw_random_sub = brush->draw_random_sub; | |||||
| /* free automatic curves created by default (replaced by copy) */ | |||||
| curvemapping_free(newbrush->cur_sensitivity); | |||||
| curvemapping_free(newbrush->cur_strength); | |||||
| curvemapping_free(newbrush->cur_jitter); | |||||
| /* make a copy of curves */ | |||||
| newbrush->cur_sensitivity = curvemapping_copy(brush->cur_sensitivity); | |||||
| newbrush->cur_strength = curvemapping_copy(brush->cur_strength); | |||||
| newbrush->cur_jitter = curvemapping_copy(brush->cur_jitter); | |||||
| BKE_gpencil_brush_setactive(ts, newbrush); | |||||
| /* notifiers */ | /* notifiers */ | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | bGPdata *gpd = ob->data; | ||||
| DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); | |||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_brush_copy(wmOperatorType *ot) | void GPENCIL_OT_vertex_group_select(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Copy Brush"; | ot->name = "Select Vertex Group"; | ||||
| ot->idname = "GPENCIL_OT_brush_copy"; | ot->idname = "GPENCIL_OT_vertex_group_select"; | ||||
| ot->description = "Copy current Grease Pencil drawing brush"; | ot->description = "Select all the vertices assigned to the active vertex group"; | ||||
| /* callbacks */ | /* api callbacks */ | ||||
| ot->exec = gp_brush_copy_exec; | ot->poll = gpencil_vertex_group_poll; | ||||
| ot->poll = gp_active_brush_poll; | ot->exec = gpencil_vertex_group_select_exec; | ||||
| /* flags */ | /* flags */ | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| } | } | ||||
| /* ***************** Select Brush ************************ */ | static int gpencil_vertex_group_deselect_exec(bContext *C, wmOperator *UNUSED(op)) | ||||
| static int gp_brush_select_exec(bContext *C, wmOperator *op) | |||||
| { | { | ||||
| ToolSettings *ts = CTX_data_tool_settings(C); | Object *ob = CTX_data_active_object(C); | ||||
| /* if there's no existing container */ | |||||
| if (ts == NULL) { | |||||
| BKE_report(op->reports, RPT_ERROR, "Nowhere to go"); | |||||
| return OPERATOR_CANCELLED; | |||||
| } | |||||
| const int index = RNA_int_get(op->ptr, "index"); | |||||
| bGPDbrush *brush = BLI_findlink(&ts->gp_brushes, index); | |||||
| /* sanity checks */ | /* sanity checks */ | ||||
| if (ELEM(NULL, brush)) { | if (ELEM(NULL, ob, ob->data)) | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | |||||
| BKE_gpencil_brush_setactive(ts, brush); | ED_gpencil_vgroup_deselect(C, ob); | ||||
| /* notifiers */ | /* notifiers */ | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | bGPdata *gpd = ob->data; | ||||
| DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); | |||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_brush_select(wmOperatorType *ot) | void GPENCIL_OT_vertex_group_deselect(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Select Brush"; | ot->name = "Deselect Vertex Group"; | ||||
| ot->idname = "GPENCIL_OT_brush_select"; | ot->idname = "GPENCIL_OT_vertex_group_deselect"; | ||||
| ot->description = "Select a Grease Pencil drawing brush"; | ot->description = "Deselect all selected vertices assigned to the active vertex group"; | ||||
| /* callbacks */ | /* api callbacks */ | ||||
| ot->exec = gp_brush_select_exec; | ot->poll = gpencil_vertex_group_poll; | ||||
| ot->poll = gp_active_brush_poll; | ot->exec = gpencil_vertex_group_deselect_exec; | ||||
| /* flags */ | /* flags */ | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| /* properties */ | |||||
| RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "Index of Drawing Brush", 0, INT_MAX); | |||||
| } | } | ||||
| /* ************************************************ */ | /* invert */ | ||||
| /* Palette Operators */ | static int gpencil_vertex_group_invert_exec(bContext *C, wmOperator *UNUSED(op)) | ||||
| /* ******************* Add New Palette ************************ */ | |||||
| /* add new palette - wrapper around API */ | |||||
| static int gp_palette_add_exec(bContext *C, wmOperator *op) | |||||
| { | { | ||||
| Main *bmain = CTX_data_main(C); | ToolSettings *ts = CTX_data_tool_settings(C); | ||||
| bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); | Object *ob = CTX_data_active_object(C); | ||||
| /* if there's no existing Grease-Pencil data there, add some */ | /* sanity checks */ | ||||
| if (gpd_ptr == NULL) { | if (ELEM(NULL, ts, ob, ob->data)) | ||||
| BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go"); | return OPERATOR_CANCELLED; | ||||
| MDeformVert *dvert; | |||||
| const int def_nr = ob->actdef - 1; | |||||
| if (!BLI_findlink(&ob->defbase, def_nr)) | |||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | |||||
| if (*gpd_ptr == NULL) | |||||
| *gpd_ptr = BKE_gpencil_data_addnew(bmain, DATA_("GPencil")); | |||||
| /* add new palette now */ | CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) | ||||
| BKE_gpencil_palette_addnew(*gpd_ptr, DATA_("GP_Palette"), true); | { | ||||
| for (int i = 0; i < gps->totpoints; i++) { | |||||
| dvert = &gps->dvert[i]; | |||||
| if (dvert->dw == NULL) { | |||||
| BKE_gpencil_vgroup_add_point_weight(dvert, def_nr, 1.0f); | |||||
| } | |||||
| else if (dvert->dw->weight == 1.0f) { | |||||
| BKE_gpencil_vgroup_remove_point_weight(dvert, def_nr); | |||||
| } | |||||
| else { | |||||
| dvert->dw->weight = 1.0f - dvert->dw->weight; | |||||
| } | |||||
| } | |||||
| } | |||||
| CTX_DATA_END; | |||||
| /* notifiers */ | /* notifiers */ | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | bGPdata *gpd = ob->data; | ||||
| DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); | |||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_palette_add(wmOperatorType *ot) | void GPENCIL_OT_vertex_group_invert(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Add Palette"; | ot->name = "Invert Vertex Group"; | ||||
| ot->idname = "GPENCIL_OT_palette_add"; | ot->idname = "GPENCIL_OT_vertex_group_invert"; | ||||
| ot->description = "Add new Grease Pencil palette for the active Grease Pencil data-block"; | ot->description = "Invert weights to the active vertex group"; | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | /* api callbacks */ | ||||
| ot->poll = gpencil_vertex_group_weight_poll; | |||||
| ot->exec = gpencil_vertex_group_invert_exec; | |||||
| /* callbacks */ | /* flags */ | ||||
| ot->exec = gp_palette_add_exec; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| ot->poll = gp_add_poll; | |||||
| } | } | ||||
| /* ******************* Remove Active Palette ************************* */ | /* smooth */ | ||||
| static int gpencil_vertex_group_smooth_exec(bContext *C, wmOperator *op) | |||||
| static int gp_palette_remove_exec(bContext *C, wmOperator *op) | |||||
| { | { | ||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | const float fac = RNA_float_get(op->ptr, "factor"); | ||||
| bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); | const int repeat = RNA_int_get(op->ptr, "repeat"); | ||||
| ToolSettings *ts = CTX_data_tool_settings(C); | |||||
| Object *ob = CTX_data_active_object(C); | |||||
| /* sanity checks */ | /* sanity checks */ | ||||
| if (ELEM(NULL, gpd, palette)) | if (ELEM(NULL, ts, ob, ob->data)) | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| if (BLI_listbase_count_at_most(&gpd->palettes, 2) < 2) { | bGPDspoint *pta, *ptb, *ptc; | ||||
| BKE_report(op->reports, RPT_ERROR, "Grease Pencil needs a palette, unable to delete the last one"); | MDeformVert *dverta, *dvertb; | ||||
| const int def_nr = ob->actdef - 1; | |||||
| if (!BLI_findlink(&ob->defbase, def_nr)) | |||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) | |||||
| { | |||||
| for (int s = 0; s < repeat; s++) { | |||||
| for (int i = 0; i < gps->totpoints; i++) { | |||||
| /* previous point */ | |||||
| if (i > 0) { | |||||
| pta = &gps->points[i - 1]; | |||||
| dverta = &gps->dvert[i - 1]; | |||||
| } | |||||
| else { | |||||
| pta = &gps->points[i]; | |||||
| dverta = &gps->dvert[i]; | |||||
| } | |||||
| /* current */ | |||||
| ptb = &gps->points[i]; | |||||
| dvertb = &gps->dvert[i]; | |||||
| /* next point */ | |||||
| if (i + 1 < gps->totpoints) { | |||||
| ptc = &gps->points[i + 1]; | |||||
| } | |||||
| else { | |||||
| ptc = &gps->points[i]; | |||||
| } | } | ||||
| float wa = BKE_gpencil_vgroup_use_index(dverta, def_nr); | |||||
| float wb = BKE_gpencil_vgroup_use_index(dvertb, def_nr); | |||||
| CLAMP_MIN(wa, 0.0f); | |||||
| CLAMP_MIN(wb, 0.0f); | |||||
| /* make the palette before this the new active palette | /* the optimal value is the corresponding to the interpolation of the weight | ||||
| * - use the one after if this is the first | * at the distance of point b | ||||
| * - if this is the only palette, this naturally becomes NULL | |||||
| */ | */ | ||||
| if (palette->prev) | const float opfac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x); | ||||
| BKE_gpencil_palette_setactive(gpd, palette->prev); | const float optimal = interpf(wa, wb, opfac); | ||||
| else | /* Based on influence factor, blend between original and optimal */ | ||||
| BKE_gpencil_palette_setactive(gpd, palette->next); | wb = interpf(wb, optimal, fac); | ||||
| BKE_gpencil_vgroup_add_point_weight(dvertb, def_nr, wb); | |||||
| /* delete the palette now... */ | } | ||||
| BKE_gpencil_palette_delete(gpd, palette); | } | ||||
| } | |||||
| CTX_DATA_END; | |||||
| /* notifiers */ | /* notifiers */ | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | bGPdata *gpd = ob->data; | ||||
| DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); | |||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_palette_remove(wmOperatorType *ot) | void GPENCIL_OT_vertex_group_smooth(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Remove palette"; | ot->name = "Smooth Vertex Group"; | ||||
| ot->idname = "GPENCIL_OT_palette_remove"; | ot->idname = "GPENCIL_OT_vertex_group_smooth"; | ||||
| ot->description = "Remove active Grease Pencil palette"; | ot->description = "Smooth weights to the active vertex group"; | ||||
| /* api callbacks */ | |||||
| ot->poll = gpencil_vertex_group_weight_poll; | |||||
| ot->exec = gpencil_vertex_group_smooth_exec; | |||||
| /* flags */ | |||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| /* callbacks */ | RNA_def_float(ot->srna, "factor", 0.5f, 0.0f, 1.0, "Factor", "", 0.0f, 1.0f); | ||||
| ot->exec = gp_palette_remove_exec; | RNA_def_int(ot->srna, "repeat", 1, 1, 10000, "Iterations", "", 1, 200); | ||||
| ot->poll = gp_active_palette_poll; | |||||
| } | } | ||||
| /* ********************** Change Palette ***************************** */ | /****************************** Join ***********************************/ | ||||
| /* userdata for joined_gpencil_fix_animdata_cb() */ | |||||
| typedef struct tJoinGPencil_AdtFixData { | |||||
| bGPdata *src_gpd; | |||||
| bGPdata *tar_gpd; | |||||
| static int gp_palette_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt)) | GHash *names_map; | ||||
| } tJoinGPencil_AdtFixData; | |||||
| /* Callback to pass to BKE_fcurves_main_cb() for RNA Paths attached to each F-Curve used in the AnimData */ | |||||
| static void joined_gpencil_fix_animdata_cb(ID *id, FCurve *fcu, void *user_data) | |||||
| { | { | ||||
| uiPopupMenu *pup; | tJoinGPencil_AdtFixData *afd = (tJoinGPencil_AdtFixData *)user_data; | ||||
| uiLayout *layout; | ID *src_id = &afd->src_gpd->id; | ||||
| ID *dst_id = &afd->tar_gpd->id; | |||||
| /* call the menu, which will call this operator again, hence the canceled */ | GHashIterator gh_iter; | ||||
| pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE); | |||||
| layout = UI_popup_menu_layout(pup); | |||||
| uiItemsEnumO(layout, "GPENCIL_OT_palette_change", "palette"); | |||||
| UI_popup_menu_end(C, pup); | |||||
| return OPERATOR_INTERFACE; | /* Fix paths - If this is the target datablock, it will have some "dirty" paths */ | ||||
| if ((id == src_id) && fcu->rna_path && strstr(fcu->rna_path, "layers[")) { | |||||
| GHASH_ITER(gh_iter, afd->names_map) { | |||||
| const char *old_name = BLI_ghashIterator_getKey(&gh_iter); | |||||
| const char *new_name = BLI_ghashIterator_getValue(&gh_iter); | |||||
| /* only remap if changed; this still means there will be some waste if there aren't many drivers/keys */ | |||||
| if (!STREQ(old_name, new_name) && strstr(fcu->rna_path, old_name)) { | |||||
| fcu->rna_path = BKE_animsys_fix_rna_path_rename(id, fcu->rna_path, "layers", | |||||
| old_name, new_name, 0, 0, false); | |||||
| /* we don't want to apply a second remapping on this F-Curve now, | |||||
| * so stop trying to fix names names | |||||
| */ | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| /* Fix driver targets */ | |||||
| if (fcu->driver) { | |||||
| /* Fix driver references to invalid ID's */ | |||||
| for (DriverVar *dvar = fcu->driver->variables.first; dvar; dvar = dvar->next) { | |||||
| /* only change the used targets, since the others will need fixing manually anyway */ | |||||
| DRIVER_TARGETS_USED_LOOPER(dvar) | |||||
| { | |||||
| /* change the ID's used... */ | |||||
| if (dtar->id == src_id) { | |||||
| dtar->id = dst_id; | |||||
| /* also check on the subtarget... | |||||
| * XXX: We duplicate the logic from drivers_path_rename_fix() here, with our own | |||||
| * little twists so that we know that it isn't going to clobber the wrong data | |||||
| */ | |||||
| if (dtar->rna_path && strstr(dtar->rna_path, "layers[")) { | |||||
| GHASH_ITER(gh_iter, afd->names_map) { | |||||
| const char *old_name = BLI_ghashIterator_getKey(&gh_iter); | |||||
| const char *new_name = BLI_ghashIterator_getValue(&gh_iter); | |||||
| /* only remap if changed */ | |||||
| if (!STREQ(old_name, new_name)) { | |||||
| if ((dtar->rna_path) && strstr(dtar->rna_path, old_name)) { | |||||
| /* Fix up path */ | |||||
| dtar->rna_path = BKE_animsys_fix_rna_path_rename(id, dtar->rna_path, "layers", | |||||
| old_name, new_name, 0, 0, false); | |||||
| break; /* no need to try any more names for layer path */ | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| DRIVER_TARGETS_LOOPER_END | |||||
| } | |||||
| } | |||||
| } | } | ||||
| static int gp_palette_change_exec(bContext *C, wmOperator *op) | /* join objects called from OBJECT_OT_join */ | ||||
| int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op) | |||||
| { | { | ||||
| bGPdata *gpd = CTX_data_gpencil_data(C); | Main *bmain = CTX_data_main(C); | ||||
| bGPDpalette *palette = NULL; | Scene *scene = CTX_data_scene(C); | ||||
| int palette_num = RNA_enum_get(op->ptr, "palette"); | Depsgraph *depsgraph = CTX_data_depsgraph(C); | ||||
| Object *obact = CTX_data_active_object(C); | |||||
| bGPdata *gpd_dst = NULL; | |||||
| bool ok = false; | |||||
| /* Get palette or create new one */ | /* Ensure we're in right mode and that the active object is correct */ | ||||
| if (palette_num == -1) { | if (!obact || obact->type != OB_GPENCIL) | ||||
| /* Create palette */ | return OPERATOR_CANCELLED; | ||||
| palette = BKE_gpencil_palette_addnew(gpd, DATA_("GP_Palette"), true); | |||||
| bGPdata *gpd = (bGPdata *)obact->data; | |||||
| if ((!gpd) || GPENCIL_ANY_MODE(gpd)) { | |||||
| return OPERATOR_CANCELLED; | |||||
| } | } | ||||
| else { | |||||
| /* Try to get palette */ | |||||
| palette = BLI_findlink(&gpd->palettes, palette_num); | |||||
| if (palette == NULL) { | /* Ensure all rotations are applied before */ | ||||
| BKE_reportf(op->reports, RPT_ERROR, "Cannot change to non-existent palette (index = %d)", palette_num); | // XXX: Why don't we apply them here instead of warning? | ||||
| CTX_DATA_BEGIN(C, Base *, base, selected_editable_bases) | |||||
| { | |||||
| if (base->object->type == OB_GPENCIL) { | |||||
| if ((base->object->rot[0] != 0) || | |||||
| (base->object->rot[1] != 0) || | |||||
| (base->object->rot[2] != 0)) | |||||
| { | |||||
| BKE_report(op->reports, RPT_ERROR, "Apply all rotations before join objects"); | |||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| } | } | ||||
| } | |||||
| CTX_DATA_END; | |||||
| /* Set active palette */ | CTX_DATA_BEGIN(C, Base *, base, selected_editable_bases) | ||||
| BKE_gpencil_palette_setactive(gpd, palette); | { | ||||
| if (base->object == obact) { | |||||
| /* updates */ | ok = true; | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | break; | ||||
| } | |||||
| } | |||||
| CTX_DATA_END; | |||||
| return OPERATOR_FINISHED; | /* that way the active object is always selected */ | ||||
| if (ok == false) { | |||||
| BKE_report(op->reports, RPT_WARNING, "Active object is not a selected grease pencil"); | |||||
| return OPERATOR_CANCELLED; | |||||
| } | } | ||||
| void GPENCIL_OT_palette_change(wmOperatorType *ot) | gpd_dst = obact->data; | ||||
| { | Object *ob_dst = obact; | ||||
| /* identifiers */ | |||||
| ot->name = "Change Palette"; | /* loop and join all data */ | ||||
| ot->idname = "GPENCIL_OT_palette_change"; | CTX_DATA_BEGIN(C, Base *, base, selected_editable_bases) | ||||
| ot->description = "Change active Grease Pencil palette"; | { | ||||
| if ((base->object->type == OB_GPENCIL) && (base->object != obact)) { | |||||
| /* we assume that each datablock is not already used in active object */ | |||||
| if (obact->data != base->object->data) { | |||||
| Object *ob_src = base->object; | |||||
| bGPdata *gpd_src = base->object->data; | |||||
| /* Apply all GP modifiers before */ | |||||
| for (GpencilModifierData *md = base->object->greasepencil_modifiers.first; md; md = md->next) { | |||||
| const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type); | |||||
| if (mti->bakeModifier) { | |||||
| mti->bakeModifier(bmain, depsgraph, md, base->object); | |||||
| } | |||||
| } | |||||
| /* callbacks */ | /* copy vertex groups to the base one's */ | ||||
| ot->invoke = gp_palette_change_invoke; | int old_idx = 0; | ||||
| ot->exec = gp_palette_change_exec; | for (bDeformGroup *dg = base->object->defbase.first; dg; dg = dg->next) { | ||||
| ot->poll = gp_active_palette_poll; | bDeformGroup *vgroup = MEM_dupallocN(dg); | ||||
| int idx = BLI_listbase_count(&obact->defbase); | |||||
| defgroup_unique_name(vgroup, obact); | |||||
| BLI_addtail(&obact->defbase, vgroup); | |||||
| /* update vertex groups in strokes in original data */ | |||||
| for (bGPDlayer *gpl_src = gpd->layers.first; gpl_src; gpl_src = gpl_src->next) { | |||||
| for (bGPDframe *gpf = gpl_src->frames.first; gpf; gpf = gpf->next) { | |||||
| for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { | |||||
| MDeformVert *dvert; | |||||
| int i; | |||||
| for (i = 0, dvert = gps->dvert; i < gps->totpoints; i++, dvert++) { | |||||
| if ((dvert->dw) && (dvert->dw->def_nr == old_idx)) { | |||||
| dvert->dw->def_nr = idx; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| old_idx++; | |||||
| } | |||||
| if (obact->defbase.first && obact->actdef == 0) | |||||
| obact->actdef = 1; | |||||
| /* flags */ | /* add missing materials reading source materials and checking in destination object */ | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | Material ***matar = give_matarar(ob_src); | ||||
| short *totcol = give_totcolp(ob_src); | |||||
| /* gp palette to use (dynamic enum) */ | for (short i = 0; i < *totcol; i++) { | ||||
| ot->prop = RNA_def_enum(ot->srna, "palette", DummyRNA_DEFAULT_items, 0, "Grease Pencil Palette", ""); | Material *tmp_ma = (*matar)[i]; | ||||
| RNA_def_enum_funcs(ot->prop, ED_gpencil_palettes_enum_itemf); | if (BKE_object_material_slot_find_index(ob_dst, tmp_ma) == 0) { | ||||
| BKE_object_material_slot_add(bmain, ob_dst); | |||||
| assign_material(bmain, ob_dst, tmp_ma, ob_dst->totcol, BKE_MAT_ASSIGN_EXISTING); | |||||
| } | |||||
| } | } | ||||
| /* ******************* Lock and hide any color non used in current layer ************************** */ | /* duplicate bGPDlayers */ | ||||
| tJoinGPencil_AdtFixData afd = {0}; | |||||
| afd.src_gpd = gpd_src; | |||||
| afd.tar_gpd = gpd_dst; | |||||
| afd.names_map = BLI_ghash_str_new("joined_gp_layers_map"); | |||||
| static int gp_palette_lock_layer_exec(bContext *C, wmOperator *UNUSED(op)) | float imat[3][3], bmat[3][3]; | ||||
| { | float offset_global[3]; | ||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | float offset_local[3]; | ||||
| bGPDpalette *palette; | |||||
| /* sanity checks */ | sub_v3_v3v3(offset_global, obact->loc, base->object->obmat[3]); | ||||
| if (ELEM(NULL, gpd)) | copy_m3_m4(bmat, obact->obmat); | ||||
| return OPERATOR_CANCELLED; | invert_m3_m3(imat, bmat); | ||||
| mul_m3_v3(imat, offset_global); | |||||
| mul_v3_m3v3(offset_local, imat, offset_global); | |||||
| palette = BKE_gpencil_palette_getactive(gpd); | |||||
| if (ELEM(NULL, palette)) | |||||
| return OPERATOR_CANCELLED; | |||||
| /* first lock and hide all colors */ | for (bGPDlayer *gpl_src = gpd_src->layers.first; gpl_src; gpl_src = gpl_src->next) { | ||||
| for (bGPDpalettecolor *palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { | bGPDlayer *gpl_new = BKE_gpencil_layer_duplicate(gpl_src); | ||||
| palcolor->flag |= PC_COLOR_LOCKED; | float diff_mat[4][4]; | ||||
| palcolor->flag |= PC_COLOR_HIDE; | float inverse_diff_mat[4][4]; | ||||
| } | |||||
| /* loop all selected strokes and unlock any color used in active layer */ | /* recalculate all stroke points */ | ||||
| for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { | ED_gpencil_parent_location(depsgraph, base->object, gpd_src, gpl_src, diff_mat); | ||||
| /* only editable and visible layers are considered */ | invert_m4_m4(inverse_diff_mat, diff_mat); | ||||
| if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL) && (gpl->flag & GP_LAYER_ACTIVE)) { | |||||
| for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) { | |||||
| /* skip strokes that are invalid for current view */ | |||||
| if (ED_gpencil_stroke_can_use(C, gps) == false) | |||||
| continue; | |||||
| /* unlock/unhide color if not unlocked before */ | Material *ma_src = NULL; | ||||
| if (gps->palcolor != NULL) { | int idx; | ||||
| gps->palcolor->flag &= ~PC_COLOR_LOCKED; | for (bGPDframe *gpf = gpl_new->frames.first; gpf; gpf = gpf->next) { | ||||
| gps->palcolor->flag &= ~PC_COLOR_HIDE; | for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { | ||||
| /* reasign material. Look old material and try to find in dst */ | |||||
| ma_src = give_current_material(ob_src, gps->mat_nr + 1); | |||||
| if (ma_src != NULL) { | |||||
| idx = BKE_object_material_slot_find_index(ob_dst, ma_src); | |||||
| if (idx > 0) { | |||||
| gps->mat_nr = idx - 1; | |||||
| } | } | ||||
| else { | |||||
| gps->mat_nr = 0; | |||||
| } | } | ||||
| } | } | ||||
| else { | |||||
| gps->mat_nr = 0; | |||||
| } | } | ||||
| /* notifiers */ | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | |||||
| return OPERATOR_FINISHED; | bGPDspoint *pt; | ||||
| int i; | |||||
| for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { | |||||
| float mpt[3]; | |||||
| mul_v3_m4v3(mpt, inverse_diff_mat, &pt->x); | |||||
| sub_v3_v3(mpt, offset_local); | |||||
| mul_v3_m4v3(&pt->x, diff_mat, mpt); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| void GPENCIL_OT_palette_lock_layer(wmOperatorType *ot) | /* be sure name is unique in new object */ | ||||
| { | BLI_uniquename(&gpd_dst->layers, gpl_new, DATA_("GP_Layer"), '.', offsetof(bGPDlayer, info), sizeof(gpl_new->info)); | ||||
| /* identifiers */ | BLI_ghash_insert(afd.names_map, BLI_strdup(gpl_src->info), gpl_new->info); | ||||
| ot->name = "Disable Unused Layer Colors"; | |||||
| ot->idname = "GPENCIL_OT_palette_lock_layer"; | |||||
| ot->description = "Lock and hide any color not used in any layer"; | |||||
| /* api callbacks */ | /* add to destination datablock */ | ||||
| ot->exec = gp_palette_lock_layer_exec; | BLI_addtail(&gpd_dst->layers, gpl_new); | ||||
| ot->poll = gp_active_layer_poll; | |||||
| } | } | ||||
| /* ************************************************ */ | /* Fix all the animation data */ | ||||
| /* Palette Colors Operators */ | BKE_fcurves_main_cb(bmain, joined_gpencil_fix_animdata_cb, &afd); | ||||
| BLI_ghash_free(afd.names_map, MEM_freeN, NULL); | |||||
| /* ******************* Add New Palette ************************ */ | |||||
| /* add new palette - wrapper around API */ | /* Only copy over animdata now, after all the remapping has been done, | ||||
| static int gp_palettecolor_add_exec(bContext *C, wmOperator *op) | * so that we don't have to worry about ambiguities re which datablock | ||||
| { | * a layer came from! | ||||
| Main *bmain = CTX_data_main(C); | */ | ||||
| bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); | if (base->object->adt) { | ||||
| if (obact->adt == NULL) { | |||||
| /* no animdata, so just use a copy of the whole thing */ | |||||
| obact->adt = BKE_animdata_copy(bmain, base->object->adt, false, true); | |||||
| } | |||||
| else { | |||||
| /* merge in data - we'll fix the drivers manually */ | |||||
| BKE_animdata_merge_copy(bmain, &obact->id, &base->object->id, ADT_MERGECOPY_KEEP_DST, false); | |||||
| } | |||||
| } | |||||
| /* if there's no existing Grease-Pencil data there, add some */ | if (gpd_src->adt) { | ||||
| if (gpd_ptr == NULL) { | if (gpd_dst->adt == NULL) { | ||||
| BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go"); | /* no animdata, so just use a copy of the whole thing */ | ||||
| return OPERATOR_CANCELLED; | gpd_dst->adt = BKE_animdata_copy(bmain, gpd_src->adt, false, true); | ||||
| } | |||||
| else { | |||||
| /* merge in data - we'll fix the drivers manually */ | |||||
| BKE_animdata_merge_copy(bmain, &gpd_dst->id, &gpd_src->id, ADT_MERGECOPY_KEEP_DST, false); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| if (*gpd_ptr == NULL) | |||||
| *gpd_ptr = BKE_gpencil_data_addnew(bmain, DATA_("GPencil")); | |||||
| /* verify palette */ | /* Free the old object */ | ||||
| bGPDpalette *palette = BKE_gpencil_palette_getactive(*gpd_ptr); | ED_object_base_free_and_unlink(bmain, scene, base->object); | ||||
| if (palette == NULL) | } | ||||
| palette = BKE_gpencil_palette_addnew(*gpd_ptr, DATA_("GP_Palette"), true); | } | ||||
| CTX_DATA_END; | |||||
| /* add new palette color now */ | DEG_relations_tag_update(bmain); /* because we removed object(s) */ | ||||
| BKE_gpencil_palettecolor_addnew(palette, DATA_("Color"), true); | |||||
| /* notifiers */ | WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | |||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_palettecolor_add(wmOperatorType *ot) | /* Color Handle operator */ | ||||
| static bool gpencil_active_color_poll(bContext *C) | |||||
| { | { | ||||
| /* identifiers */ | Object *ob = CTX_data_active_object(C); | ||||
| ot->name = "Add Palette Color"; | if (ob && ob->data && (ob->type == OB_GPENCIL)) { | ||||
| ot->idname = "GPENCIL_OT_palettecolor_add"; | short *totcolp = give_totcolp(ob); | ||||
| ot->description = "Add new Grease Pencil palette color for the active Grease Pencil data-block"; | return *totcolp > 0; | ||||
| } | |||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | return false; | ||||
| /* callbacks */ | |||||
| ot->exec = gp_palettecolor_add_exec; | |||||
| ot->poll = gp_add_poll; | |||||
| } | } | ||||
| /* ******************* Remove Active Palette color ************************* */ | |||||
| static int gp_palettecolor_remove_exec(bContext *C, wmOperator *UNUSED(op)) | /* ******************* Lock and hide any color non used in current layer ************************** */ | ||||
| static int gpencil_lock_layer_exec(bContext *C, wmOperator *UNUSED(op)) | |||||
| { | { | ||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | bGPdata *gpd = ED_gpencil_data_get_active(C); | ||||
| bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); | Object *ob = CTX_data_active_object(C); | ||||
| bGPDpalettecolor *color = BKE_gpencil_palettecolor_getactive(palette); | MaterialGPencilStyle *gp_style = NULL; | ||||
| /* sanity checks */ | /* sanity checks */ | ||||
| if (ELEM(NULL, gpd, palette, color)) | if (ELEM(NULL, gpd)) | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| /* make the palette color before this the new active color | /* first lock and hide all colors */ | ||||
| * - use the one after if this is the first | Material *ma = NULL; | ||||
| * - if this is the only color, this naturally becomes NULL | Material ***matar = give_matarar(ob); | ||||
| */ | short *totcol = give_totcolp(ob); | ||||
| if (color->prev) | if ((totcol == 0) || (matar == NULL)) | ||||
| BKE_gpencil_palettecolor_setactive(palette, color->prev); | return OPERATOR_CANCELLED; | ||||
| else | |||||
| BKE_gpencil_palettecolor_setactive(palette, color->next); | |||||
| /* delete the strokes */ | for (short i = 0; i < *totcol; i++) { | ||||
| BKE_gpencil_palettecolor_delete_strokes(gpd, color->info); | ma = (*matar)[i]; | ||||
| gp_style = ma->gp_style; | |||||
| gp_style->flag |= GP_STYLE_COLOR_LOCKED; | |||||
| gp_style->flag |= GP_STYLE_COLOR_HIDE; | |||||
| } | |||||
| /* delete the palette color now... */ | /* loop all selected strokes and unlock any color used in active layer */ | ||||
| BKE_gpencil_palettecolor_delete(palette, color); | for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { | ||||
| /* only editable and visible layers are considered */ | |||||
| if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL) && (gpl->flag & GP_LAYER_ACTIVE)) { | |||||
| for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) { | |||||
| /* skip strokes that are invalid for current view */ | |||||
| if (ED_gpencil_stroke_can_use(C, gps) == false) | |||||
| continue; | |||||
| gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1); | |||||
| /* unlock/unhide color if not unlocked before */ | |||||
| if (gp_style != NULL) { | |||||
| gp_style->flag &= ~GP_STYLE_COLOR_LOCKED; | |||||
| gp_style->flag &= ~GP_STYLE_COLOR_HIDE; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| /* notifiers */ | /* notifiers */ | ||||
| DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_palettecolor_remove(wmOperatorType *ot) | void GPENCIL_OT_lock_layer(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Remove palette color"; | ot->name = "Disable Unused Layer Colors"; | ||||
| ot->idname = "GPENCIL_OT_palettecolor_remove"; | ot->idname = "GPENCIL_OT_lock_layer"; | ||||
| ot->description = "Remove active Grease Pencil palette color"; | ot->description = "Lock and hide any color not used in any layer"; | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | |||||
| /* callbacks */ | /* api callbacks */ | ||||
| ot->exec = gp_palettecolor_remove_exec; | ot->exec = gpencil_lock_layer_exec; | ||||
| ot->poll = gp_active_palettecolor_poll; | ot->poll = gp_active_layer_poll; | ||||
| } | } | ||||
| /* ********************** Isolate palette color **************************** */ | /* ********************** Isolate gpencil_ color **************************** */ | ||||
| static int gp_isolate_palettecolor_exec(bContext *C, wmOperator *op) | static int gpencil_color_isolate_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | bGPdata *gpd = ED_gpencil_data_get_active(C); | ||||
| bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); | Object *ob = CTX_data_active_object(C); | ||||
| bGPDpalettecolor *active_color = BKE_gpencil_palettecolor_getactive(palette); | Material *active_ma = give_current_material(ob, ob->actcol); | ||||
| bGPDpalettecolor *palcolor; | MaterialGPencilStyle *active_color = BKE_material_gpencil_settings_get(ob, ob->actcol); | ||||
| MaterialGPencilStyle *gp_style; | |||||
| int flags = PC_COLOR_LOCKED; | int flags = GP_STYLE_COLOR_LOCKED; | ||||
| bool isolate = false; | bool isolate = false; | ||||
| if (RNA_boolean_get(op->ptr, "affect_visibility")) | if (RNA_boolean_get(op->ptr, "affect_visibility")) | ||||
| flags |= PC_COLOR_HIDE; | flags |= GP_STYLE_COLOR_HIDE; | ||||
| if (ELEM(NULL, gpd, active_color)) { | if (ELEM(NULL, gpd, active_color)) { | ||||
| BKE_report(op->reports, RPT_ERROR, "No active color to isolate"); | BKE_report(op->reports, RPT_ERROR, "No active color to isolate"); | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| /* Test whether to isolate or clear all flags */ | /* Test whether to isolate or clear all flags */ | ||||
| for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { | Material *ma = NULL; | ||||
| Material ***matar = give_matarar(ob); | |||||
| short *totcol = give_totcolp(ob); | |||||
| for (short i = 0; i < *totcol; i++) { | |||||
| ma = (*matar)[i]; | |||||
| /* Skip if this is the active one */ | /* Skip if this is the active one */ | ||||
| if (palcolor == active_color) | if (ma == active_ma) | ||||
| continue; | continue; | ||||
| /* If the flags aren't set, that means that the color is | /* If the flags aren't set, that means that the color is | ||||
| * not alone, so we have some colors to isolate still | * not alone, so we have some colors to isolate still | ||||
| */ | */ | ||||
| if ((palcolor->flag & flags) == 0) { | gp_style = ma->gp_style; | ||||
| if ((gp_style->flag & flags) == 0) { | |||||
| isolate = true; | isolate = true; | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| /* Set/Clear flags as appropriate */ | /* Set/Clear flags as appropriate */ | ||||
| if (isolate) { | if (isolate) { | ||||
| /* Set flags on all "other" colors */ | /* Set flags on all "other" colors */ | ||||
| for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { | for (short i = 0; i < *totcol; i++) { | ||||
| if (palcolor == active_color) | ma = (*matar)[i]; | ||||
| gp_style = ma->gp_style; | |||||
| if (gp_style == active_color) | |||||
| continue; | continue; | ||||
| else | else | ||||
| palcolor->flag |= flags; | gp_style->flag |= flags; | ||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| /* Clear flags - Restore everything else */ | /* Clear flags - Restore everything else */ | ||||
| for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { | for (short i = 0; i < *totcol; i++) { | ||||
| palcolor->flag &= ~flags; | ma = (*matar)[i]; | ||||
| gp_style = ma->gp_style; | |||||
| gp_style->flag &= ~flags; | |||||
| } | } | ||||
| } | } | ||||
| /* notifiers */ | /* notifiers */ | ||||
| DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_palettecolor_isolate(wmOperatorType *ot) | void GPENCIL_OT_color_isolate(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Isolate Palette Color"; | ot->name = "Isolate Color"; | ||||
| ot->idname = "GPENCIL_OT_palettecolor_isolate"; | ot->idname = "GPENCIL_OT_color_isolate"; | ||||
| ot->description = "Toggle whether the active color is the only one that is editable and/or visible"; | ot->description = "Toggle whether the active color is the only one that is editable and/or visible"; | ||||
| /* callbacks */ | /* callbacks */ | ||||
| ot->exec = gp_isolate_palettecolor_exec; | ot->exec = gpencil_color_isolate_exec; | ||||
| ot->poll = gp_active_palettecolor_poll; | ot->poll = gpencil_active_color_poll; | ||||
| /* flags */ | /* flags */ | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| /* properties */ | /* properties */ | ||||
| RNA_def_boolean(ot->srna, "affect_visibility", false, "Affect Visibility", "In addition to toggling " | RNA_def_boolean(ot->srna, "affect_visibility", false, "Affect Visibility", "In addition to toggling " | ||||
| "the editability, also affect the visibility"); | "the editability, also affect the visibility"); | ||||
| } | } | ||||
| /* *********************** Hide Palette colors ******************************** */ | /* *********************** Hide colors ******************************** */ | ||||
| static int gp_palettecolor_hide_exec(bContext *C, wmOperator *op) | static int gpencil_color_hide_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | Object *ob = CTX_data_active_object(C); | ||||
| bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); | MaterialGPencilStyle *active_color = BKE_material_gpencil_settings_get(ob, ob->actcol); | ||||
| bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); | |||||
| bool unselected = RNA_boolean_get(op->ptr, "unselected"); | bool unselected = RNA_boolean_get(op->ptr, "unselected"); | ||||
| /* sanity checks */ | Material *ma = NULL; | ||||
| if (ELEM(NULL, gpd, palette, palcolor)) | Material ***matar = give_matarar(ob); | ||||
| short *totcol = give_totcolp(ob); | |||||
| if ((totcol == 0) || (matar == NULL)) | |||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| if (unselected) { | if (unselected) { | ||||
| bGPDpalettecolor *color; | |||||
| /* hide unselected */ | /* hide unselected */ | ||||
| for (color = palette->colors.first; color; color = color->next) { | MaterialGPencilStyle *color = NULL; | ||||
| if (color != palcolor) { | for (short i = 0; i < *totcol; i++) { | ||||
| color->flag |= PC_COLOR_HIDE; | ma = (*matar)[i]; | ||||
| color = ma->gp_style; | |||||
| if (active_color != color) { | |||||
| color->flag |= GP_STYLE_COLOR_HIDE; | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| /* hide selected/active */ | /* hide selected/active */ | ||||
| palcolor->flag |= PC_COLOR_HIDE; | active_color->flag |= GP_STYLE_COLOR_HIDE; | ||||
| } | } | ||||
| /* notifiers */ | /* notifiers */ | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_palettecolor_hide(wmOperatorType *ot) | void GPENCIL_OT_color_hide(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Hide Color(s)"; | ot->name = "Hide Color(s)"; | ||||
| ot->idname = "GPENCIL_OT_palettecolor_hide"; | ot->idname = "GPENCIL_OT_color_hide"; | ||||
| ot->description = "Hide selected/unselected Grease Pencil colors"; | ot->description = "Hide selected/unselected Grease Pencil colors"; | ||||
| /* callbacks */ | /* callbacks */ | ||||
| ot->exec = gp_palettecolor_hide_exec; | ot->exec = gpencil_color_hide_exec; | ||||
| ot->poll = gp_active_palettecolor_poll; /* NOTE: we need an active color to play with */ | ot->poll = gpencil_active_color_poll; /* NOTE: we need an active color to play with */ | ||||
| /* flags */ | /* flags */ | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| /* props */ | /* props */ | ||||
| RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected colors"); | RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected colors"); | ||||
| } | } | ||||
| /* ********************** Show All Colors ***************************** */ | /* ********************** Show All Colors ***************************** */ | ||||
| /* poll callback for showing colors */ | static int gpencil_color_reveal_exec(bContext *C, wmOperator *UNUSED(op)) | ||||
| static bool gp_palettecolor_reveal_poll(bContext *C) | |||||
| { | |||||
| return ED_gpencil_data_get_active(C) != NULL; | |||||
| } | |||||
| static int gp_palettecolor_reveal_exec(bContext *C, wmOperator *UNUSED(op)) | |||||
| { | { | ||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | Object *ob = CTX_data_active_object(C); | ||||
| bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); | Material *ma = NULL; | ||||
| bGPDpalettecolor *palcolor; | Material ***matar = give_matarar(ob); | ||||
| short *totcol = give_totcolp(ob); | |||||
| /* sanity checks */ | if ((totcol == 0) || (matar == NULL)) | ||||
| if (ELEM(NULL, gpd, palette)) | |||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| /* make all colors visible */ | /* make all colors visible */ | ||||
| for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { | MaterialGPencilStyle *gp_style = NULL; | ||||
| palcolor->flag &= ~PC_COLOR_HIDE; | |||||
| for (short i = 0; i < *totcol; i++) { | |||||
| ma = (*matar)[i]; | |||||
| gp_style = ma->gp_style; | |||||
| gp_style->flag &= ~GP_STYLE_COLOR_HIDE; | |||||
| } | } | ||||
| /* notifiers */ | /* notifiers */ | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_palettecolor_reveal(wmOperatorType *ot) | void GPENCIL_OT_color_reveal(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Show All Colors"; | ot->name = "Show All Colors"; | ||||
| ot->idname = "GPENCIL_OT_palettecolor_reveal"; | ot->idname = "GPENCIL_OT_color_reveal"; | ||||
| ot->description = "Unhide all hidden Grease Pencil palette colors"; | ot->description = "Unhide all hidden Grease Pencil gpencil_ colors"; | ||||
| /* callbacks */ | /* callbacks */ | ||||
| ot->exec = gp_palettecolor_reveal_exec; | ot->exec = gpencil_color_reveal_exec; | ||||
| ot->poll = gp_palettecolor_reveal_poll; | ot->poll = gpencil_active_color_poll; | ||||
| /* flags */ | /* flags */ | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| } | } | ||||
| /* ***************** Lock/Unlock All Palette colors ************************ */ | /* ***************** Lock/Unlock All colors ************************ */ | ||||
| static int gp_palettecolor_lock_all_exec(bContext *C, wmOperator *UNUSED(op)) | static int gpencil_color_lock_all_exec(bContext *C, wmOperator *UNUSED(op)) | ||||
| { | { | ||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | |||||
| bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); | |||||
| bGPDpalettecolor *palcolor; | |||||
| /* sanity checks */ | Object *ob = CTX_data_active_object(C); | ||||
| if (ELEM(NULL, gpd, palette)) | Material *ma = NULL; | ||||
| Material ***matar = give_matarar(ob); | |||||
| short *totcol = give_totcolp(ob); | |||||
| if ((totcol == 0) || (matar == NULL)) | |||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| /* make all layers non-editable */ | /* make all layers non-editable */ | ||||
| for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { | MaterialGPencilStyle *gp_style = NULL; | ||||
| palcolor->flag |= PC_COLOR_LOCKED; | |||||
| for (short i = 0; i < *totcol; i++) { | |||||
| ma = (*matar)[i]; | |||||
| gp_style = ma->gp_style; | |||||
| gp_style->flag |= GP_STYLE_COLOR_LOCKED; | |||||
| } | } | ||||
| /* notifiers */ | /* notifiers */ | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_palettecolor_lock_all(wmOperatorType *ot) | void GPENCIL_OT_color_lock_all(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Lock All Colors"; | ot->name = "Lock All Colors"; | ||||
| ot->idname = "GPENCIL_OT_palettecolor_lock_all"; | ot->idname = "GPENCIL_OT_color_lock_all"; | ||||
| ot->description = "Lock all Grease Pencil colors to prevent them from being accidentally modified"; | ot->description = "Lock all Grease Pencil colors to prevent them from being accidentally modified"; | ||||
| /* callbacks */ | /* callbacks */ | ||||
| ot->exec = gp_palettecolor_lock_all_exec; | ot->exec = gpencil_color_lock_all_exec; | ||||
| ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */ | ot->poll = gpencil_active_color_poll; | ||||
| /* flags */ | /* flags */ | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| } | } | ||||
| /* -------------------------- */ | /* -------------------------- */ | ||||
| static int gp_palettecolor_unlock_all_exec(bContext *C, wmOperator *UNUSED(op)) | static int gpencil_color_unlock_all_exec(bContext *C, wmOperator *UNUSED(op)) | ||||
| { | { | ||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | Object *ob = CTX_data_active_object(C); | ||||
| bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); | Material *ma = NULL; | ||||
| bGPDpalettecolor *palcolor; | Material ***matar = give_matarar(ob); | ||||
| short *totcol = give_totcolp(ob); | |||||
| /* sanity checks */ | if ((totcol == 0) || (matar == NULL)) | ||||
| if (ELEM(NULL, gpd, palette)) | |||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| /* make all layers editable again*/ | /* make all layers editable again*/ | ||||
| for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { | MaterialGPencilStyle *gp_style = NULL; | ||||
| palcolor->flag &= ~PC_COLOR_LOCKED; | |||||
| for (short i = 0; i < *totcol; i++) { | |||||
| ma = (*matar)[i]; | |||||
| gp_style = ma->gp_style; | |||||
| gp_style->flag &= ~GP_STYLE_COLOR_LOCKED; | |||||
| } | } | ||||
| /* notifiers */ | /* notifiers */ | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_palettecolor_unlock_all(wmOperatorType *ot) | void GPENCIL_OT_color_unlock_all(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Unlock All Colors"; | ot->name = "Unlock All Colors"; | ||||
| ot->idname = "GPENCIL_OT_palettecolor_unlock_all"; | ot->idname = "GPENCIL_OT_color_unlock_all"; | ||||
| ot->description = "Unlock all Grease Pencil colors so that they can be edited"; | ot->description = "Unlock all Grease Pencil colors so that they can be edited"; | ||||
| /* callbacks */ | /* callbacks */ | ||||
| ot->exec = gp_palettecolor_unlock_all_exec; | ot->exec = gpencil_color_unlock_all_exec; | ||||
| ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */ | ot->poll = gpencil_active_color_poll; | ||||
| /* flags */ | /* flags */ | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| } | } | ||||
| /* ******************* Move Color Up/Down ************************** */ | |||||
| enum { | /* ***************** Select all strokes using color ************************ */ | ||||
| GP_COLOR_MOVE_UP = -1, | |||||
| GP_COLOR_MOVE_DOWN = 1 | |||||
| }; | |||||
| static int gp_palettecolor_move_exec(bContext *C, wmOperator *op) | static int gpencil_color_select_exec(bContext *C, wmOperator *UNUSED(op)) | ||||
| { | { | ||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | bGPdata *gpd = ED_gpencil_data_get_active(C); | ||||
| bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); | Object *ob = CTX_data_active_object(C); | ||||
| bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); | MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, ob->actcol); | ||||
| int direction = RNA_enum_get(op->ptr, "direction"); | |||||
| /* sanity checks */ | /* sanity checks */ | ||||
| if (ELEM(NULL, gpd, palette, palcolor)) | if (ELEM(NULL, gpd, gp_style)) | ||||
| return OPERATOR_CANCELLED; | |||||
| /* up or down? */ | |||||
| if (direction == GP_COLOR_MOVE_UP) { | |||||
| /* up */ | |||||
| BLI_remlink(&palette->colors, palcolor); | |||||
| BLI_insertlinkbefore(&palette->colors, palcolor->prev, palcolor); | |||||
| } | |||||
| else if (direction == GP_COLOR_MOVE_DOWN) { | |||||
| /* down */ | |||||
| BLI_remlink(&palette->colors, palcolor); | |||||
| BLI_insertlinkafter(&palette->colors, palcolor->next, palcolor); | |||||
| } | |||||
| else { | |||||
| BLI_assert(0); | |||||
| } | |||||
| /* notifiers */ | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | |||||
| return OPERATOR_FINISHED; | |||||
| } | |||||
| void GPENCIL_OT_palettecolor_move(wmOperatorType *ot) | |||||
| { | |||||
| static const EnumPropertyItem slot_move[] = { | |||||
| {GP_COLOR_MOVE_UP, "UP", 0, "Up", ""}, | |||||
| {GP_COLOR_MOVE_DOWN, "DOWN", 0, "Down", ""}, | |||||
| {0, NULL, 0, NULL, NULL} | |||||
| }; | |||||
| /* identifiers */ | |||||
| ot->name = "Move Palette color"; | |||||
| ot->idname = "GPENCIL_OT_palettecolor_move"; | |||||
| ot->description = "Move the active Grease Pencil palette color up/down in the list"; | |||||
| /* api callbacks */ | |||||
| ot->exec = gp_palettecolor_move_exec; | |||||
| ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */ | |||||
| /* flags */ | |||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | |||||
| ot->prop = RNA_def_enum(ot->srna, "direction", slot_move, GP_COLOR_MOVE_UP, "Direction", ""); | |||||
| } | |||||
| /* ***************** Select all strokes using Palette color ************************ */ | |||||
| static int gp_palettecolor_select_exec(bContext *C, wmOperator *UNUSED(op)) | |||||
| { | |||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | |||||
| bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); | |||||
| bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); | |||||
| /* sanity checks */ | |||||
| if (ELEM(NULL, gpd, palette, palcolor)) | |||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| /* read all strokes and select*/ | /* read all strokes and select*/ | ||||
| for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { | for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { | ||||
| /* only editable and visible layers are considered */ | /* only editable and visible layers are considered */ | ||||
| if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { | if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { | ||||
| /* verify something to do */ | /* verify something to do */ | ||||
| for (bGPDstroke *gps = gpl->actframe->strokes.first; gps; gps = gps->next) { | for (bGPDstroke *gps = gpl->actframe->strokes.first; gps; gps = gps->next) { | ||||
| /* skip strokes that are invalid for current view */ | /* skip strokes that are invalid for current view */ | ||||
| if (ED_gpencil_stroke_can_use(C, gps) == false) | if (ED_gpencil_stroke_can_use(C, gps) == false) | ||||
| continue; | continue; | ||||
| /* check if the color is editable */ | /* check if the color is editable */ | ||||
| if (ED_gpencil_stroke_color_use(gpl, gps) == false) | if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) | ||||
| continue; | continue; | ||||
| /* select */ | /* select */ | ||||
| if (strcmp(palcolor->info, gps->colorname) == 0) { | if (ob->actcol == gps->mat_nr) { | ||||
| bGPDspoint *pt; | bGPDspoint *pt; | ||||
| int i; | int i; | ||||
| gps->flag |= GP_STROKE_SELECT; | gps->flag |= GP_STROKE_SELECT; | ||||
| for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { | for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { | ||||
| pt->flag |= GP_SPOINT_SELECT; | pt->flag |= GP_SPOINT_SELECT; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* notifiers */ | /* notifiers */ | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_palettecolor_select(wmOperatorType *ot) | void GPENCIL_OT_color_select(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Select Color"; | ot->name = "Select Color"; | ||||
| ot->idname = "GPENCIL_OT_palettecolor_select"; | ot->idname = "GPENCIL_OT_color_select"; | ||||
| ot->description = "Select all Grease Pencil strokes using current color"; | ot->description = "Select all Grease Pencil strokes using current color"; | ||||
| /* callbacks */ | /* callbacks */ | ||||
| ot->exec = gp_palettecolor_select_exec; | ot->exec = gpencil_color_select_exec; | ||||
| ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */ | ot->poll = gpencil_active_color_poll; | ||||
| /* flags */ | |||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | |||||
| } | |||||
| /* ***************** Copy Palette color ************************ */ | |||||
| static int gp_palettecolor_copy_exec(bContext *C, wmOperator *UNUSED(op)) | |||||
| { | |||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | |||||
| bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); | |||||
| bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); | |||||
| bGPDpalettecolor *newcolor; | |||||
| /* sanity checks */ | |||||
| if (ELEM(NULL, gpd, palette, palcolor)) | |||||
| return OPERATOR_CANCELLED; | |||||
| /* create a new color and duplicate data */ | |||||
| newcolor = BKE_gpencil_palettecolor_addnew(palette, palcolor->info, true); | |||||
| copy_v4_v4(newcolor->color, palcolor->color); | |||||
| copy_v4_v4(newcolor->fill, palcolor->fill); | |||||
| newcolor->flag = palcolor->flag; | |||||
| /* notifiers */ | |||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | |||||
| return OPERATOR_FINISHED; | |||||
| } | |||||
| void GPENCIL_OT_palettecolor_copy(wmOperatorType *ot) | |||||
| { | |||||
| /* identifiers */ | |||||
| ot->name = "Copy Color"; | |||||
| ot->idname = "GPENCIL_OT_palettecolor_copy"; | |||||
| ot->description = "Copy current Grease Pencil palette color"; | |||||
| /* callbacks */ | |||||
| ot->exec = gp_palettecolor_copy_exec; | |||||
| ot->poll = gp_active_palettecolor_poll; | |||||
| /* flags */ | /* flags */ | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| } | } | ||||