diff options
author | Joshua Leung <aligorith@gmail.com> | 2008-12-29 14:04:55 +0300 |
---|---|---|
committer | Joshua Leung <aligorith@gmail.com> | 2008-12-29 14:04:55 +0300 |
commit | 2c4c7004aee2f710d08055f7724af25db5817ea0 (patch) | |
tree | a8700ade70f5df78317f13fa5f52546788b9e1e1 /source/blender | |
parent | 1d42afe56124cc9cdf76ab25272505ba77d090b2 (diff) |
2.5 - Action Editor: Copy/Paste
This can be activated using Ctrl-C/V and the buttons on the header.
It still uses an ugly global copy/paste buffer for now. In future, we could investigate alternative methods...
Diffstat (limited to 'source/blender')
5 files changed, 368 insertions, 3 deletions
diff --git a/source/blender/editors/space_action/action_edit_keyframes.c b/source/blender/editors/space_action/action_edit_keyframes.c index df62de9bd83..700be22b265 100644 --- a/source/blender/editors/space_action/action_edit_keyframes.c +++ b/source/blender/editors/space_action/action_edit_keyframes.c @@ -89,9 +89,354 @@ /* GENERAL STUFF */ // TODO: -// - delete // - insert key -// - copy/paste + +/* ******************** Copy/Paste Keyframes Operator ************************* */ +/* - The copy/paste buffer currently stores a set of Action Channels, with temporary + * IPO-blocks, and also temporary IpoCurves which only contain the selected keyframes. + * - Only pastes between compatable data is possible (i.e. same achan->name, ipo-curve type, etc.) + * Unless there is only one element in the buffer, names are also tested to check for compatability. + * - All pasted frames are offset by the same amount. This is calculated as the difference in the times of + * the current frame and the 'first keyframe' (i.e. the earliest one in all channels). + * - The earliest frame is calculated per copy operation. + */ + +/* globals for copy/paste data (like for other copy/paste buffers) */ +ListBase actcopybuf = {NULL, NULL}; +static float actcopy_firstframe= 999999999.0f; + +/* This function frees any MEM_calloc'ed copy/paste buffer data */ +// XXX find some header to put this in! +void free_actcopybuf () +{ + bActionChannel *achan, *anext; + bConstraintChannel *conchan, *cnext; + + for (achan= actcopybuf.first; achan; achan= anext) { + anext= achan->next; + + if (achan->ipo) { + free_ipo(achan->ipo); + MEM_freeN(achan->ipo); + } + + for (conchan=achan->constraintChannels.first; conchan; conchan=cnext) { + cnext= conchan->next; + + if (conchan->ipo) { + free_ipo(conchan->ipo); + MEM_freeN(conchan->ipo); + } + + BLI_freelinkN(&achan->constraintChannels, conchan); + } + + BLI_freelinkN(&actcopybuf, achan); + } + + actcopybuf.first= actcopybuf.last= NULL; + actcopy_firstframe= 999999999.0f; +} + +/* ------------------- */ + +/* This function adds data to the copy/paste buffer, freeing existing data first + * Only the selected action channels gets their selected keyframes copied. + */ +static short copy_action_keys (bAnimContext *ac) +{ + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + /* clear buffer first */ + free_actcopybuf(); + + /* filter data */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_IPOKEYS); + ANIM_animdata_filter(&anim_data, filter, ac->data, ac->datatype); + + /* assume that each of these is an ipo-block */ + for (ale= anim_data.first; ale; ale= ale->next) { + bActionChannel *achan; + Ipo *ipo= ale->key_data; + Ipo *ipn; + IpoCurve *icu, *icn; + BezTriple *bezt; + int i; + + /* coerce an action-channel out of owner */ + if (ale->ownertype == ANIMTYPE_ACHAN) { + bActionChannel *achanO= ale->owner; + achan= MEM_callocN(sizeof(bActionChannel), "ActCopyPasteAchan"); + strcpy(achan->name, achanO->name); + } + else if (ale->ownertype == ANIMTYPE_SHAPEKEY) { + achan= MEM_callocN(sizeof(bActionChannel), "ActCopyPasteAchan"); + strcpy(achan->name, "#ACP_ShapeKey"); + } + else + continue; + BLI_addtail(&actcopybuf, achan); + + /* add constraint channel if needed, then add new ipo-block */ + if (ale->type == ANIMTYPE_CONCHAN) { + bConstraintChannel *conchanO= ale->data; + bConstraintChannel *conchan; + + conchan= MEM_callocN(sizeof(bConstraintChannel), "ActCopyPasteConchan"); + strcpy(conchan->name, conchanO->name); + BLI_addtail(&achan->constraintChannels, conchan); + + conchan->ipo= ipn= MEM_callocN(sizeof(Ipo), "ActCopyPasteIpo"); + } + else { + achan->ipo= ipn= MEM_callocN(sizeof(Ipo), "ActCopyPasteIpo"); + } + ipn->blocktype = ipo->blocktype; + + /* now loop through curves, and only copy selected keyframes */ + for (icu= ipo->curve.first; icu; icu= icu->next) { + /* allocate a new curve */ + icn= MEM_callocN(sizeof(IpoCurve), "ActCopyPasteIcu"); + icn->blocktype = icu->blocktype; + icn->adrcode = icu->adrcode; + BLI_addtail(&ipn->curve, icn); + + /* find selected BezTriples to add to the buffer (and set first frame) */ + for (i=0, bezt=icu->bezt; i < icu->totvert; i++, bezt++) { + if (BEZSELECTED(bezt)) { + /* add to buffer ipo-curve */ + insert_bezt_icu(icn, bezt); + + /* check if this is the earliest frame encountered so far */ + if (bezt->vec[1][0] < actcopy_firstframe) + actcopy_firstframe= bezt->vec[1][0]; + } + } + } + } + + /* check if anything ended up in the buffer */ + if (ELEM(NULL, actcopybuf.first, actcopybuf.last)) + // error("Nothing copied to buffer"); + return -1; + + /* free temp memory */ + BLI_freelistN(&anim_data); + + /* everything went fine */ + return 0; +} + +static short paste_action_keys (bAnimContext *ac) +{ + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + const Scene *scene= (ac->scene); + const float offset = (float)(CFRA - actcopy_firstframe); + char *actname = NULL, *conname = NULL; + short no_name= 0; + + /* check if buffer is empty */ + if (ELEM(NULL, actcopybuf.first, actcopybuf.last)) { + //error("No data in buffer to paste"); + return -1; + } + /* check if single channel in buffer (disregard names if so) */ + if (actcopybuf.first == actcopybuf.last) + no_name= 1; + + /* filter data */ + filter= (ANIMFILTER_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_FOREDIT | ANIMFILTER_IPOKEYS); + ANIM_animdata_filter(&anim_data, filter, ac->data, ac->datatype); + + /* from selected channels */ + for (ale= anim_data.first; ale; ale= ale->next) { + Ipo *ipo_src = NULL; + bActionChannel *achan; + IpoCurve *ico, *icu; + BezTriple *bezt; + int i; + + /* find suitable IPO-block from buffer to paste from */ + for (achan= actcopybuf.first; achan; achan= achan->next) { + /* try to match data */ + if (ale->ownertype == ANIMTYPE_ACHAN) { + bActionChannel *achant= ale->owner; + + /* check if we have a corresponding action channel */ + if ((no_name) || (strcmp(achan->name, achant->name)==0)) { + actname= achant->name; + + /* check if this is a constraint channel */ + if (ale->type == ANIMTYPE_CONCHAN) { + bConstraintChannel *conchant= ale->data; + bConstraintChannel *conchan; + + for (conchan=achan->constraintChannels.first; conchan; conchan=conchan->next) { + if (strcmp(conchan->name, conchant->name)==0) { + conname= conchant->name; + ipo_src= conchan->ipo; + break; + } + } + if (ipo_src) break; + } + else { + ipo_src= achan->ipo; + break; + } + } + } + else if (ale->ownertype == ANIMTYPE_SHAPEKEY) { + /* check if this action channel is "#ACP_ShapeKey" */ + if ((no_name) || (strcmp(achan->name, "#ACP_ShapeKey")==0)) { + actname= NULL; + ipo_src= achan->ipo; + break; + } + } + } + + /* this shouldn't happen, but it might */ + if (ipo_src == NULL) + continue; + + /* loop over curves, pasting keyframes */ + for (ico= ipo_src->curve.first; ico; ico= ico->next) { + /* get IPO-curve to paste to (IPO-curve might not exist for destination, so gets created) */ + icu= verify_ipocurve(ale->id, ico->blocktype, actname, conname, NULL, ico->adrcode, 1); + + if (icu) { + /* just start pasting, with the the first keyframe on the current frame, and so on */ + for (i=0, bezt=ico->bezt; i < ico->totvert; i++, bezt++) { + /* temporarily apply offset to src beztriple while copying */ + bezt->vec[0][0] += offset; + bezt->vec[1][0] += offset; + bezt->vec[2][0] += offset; + + /* insert the keyframe */ + insert_bezt_icu(icu, bezt); + + /* un-apply offset from src beztriple after copying */ + bezt->vec[0][0] -= offset; + bezt->vec[1][0] -= offset; + bezt->vec[2][0] -= offset; + } + + /* recalculate channel's handles? */ + calchandles_ipocurve(icu); + } + } + } + + /* free temp memory */ + BLI_freelistN(&anim_data); + + /* do depsgraph updates (for 3d-view)? */ +#if 0 + if ((ob) && (G.saction->pin==0)) { + if (ob->type == OB_ARMATURE) + DAG_object_flush_update(G.scene, ob, OB_RECALC_OB|OB_RECALC_DATA); + else + DAG_object_flush_update(G.scene, ob, OB_RECALC_OB); + } +#endif + + return 0; +} + +/* ------------------- */ + +static int actkeys_copy_exec(bContext *C, wmOperator *op) +{ + bAnimContext ac; + + /* get editor data */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return OPERATOR_CANCELLED; + + /* copy keyframes */ + if (ac.datatype == ANIMCONT_GPENCIL) { + // FIXME... + } + else { + if (copy_action_keys(&ac)) { + printf("Action Copy: No keyframes copied to copy-paste buffer\n"); + uiPupmenuError(C, "No keyframes copied to copy-paste buffer"); + } + } + + /* set notifier tha things have changed */ + ED_area_tag_redraw(CTX_wm_area(C)); // FIXME... should be updating 'keyframes' data context or so instead! + + return OPERATOR_FINISHED; +} + +void ACT_OT_keyframes_copy (wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name= "Copy Keyframes"; + ot->idname= "ACT_OT_keyframes_copy"; + + /* api callbacks */ + ot->exec= actkeys_copy_exec; + ot->poll= ED_operator_areaactive; + + /* flags */ + ot->flag= OPTYPE_REGISTER/*|OPTYPE_UNDO*/; +} + + + +static int actkeys_paste_exec(bContext *C, wmOperator *op) +{ + bAnimContext ac; + + /* get editor data */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return OPERATOR_CANCELLED; + + /* paste keyframes */ + if (ac.datatype == ANIMCONT_GPENCIL) { + // FIXME... + } + else { + if (paste_action_keys(&ac)) { + printf("Action Paste: Nothing to paste, as Copy-Paste buffer was empty.\n"); + uiPupmenuError(C, "Nothing to paste, as Copy-Paste buffer was empty."); + } + } + + /* validate keyframes after editing */ + ANIM_editkeyframes_refresh(&ac); + + /* set notifier tha things have changed */ + ED_area_tag_redraw(CTX_wm_area(C)); // FIXME... should be updating 'keyframes' data context or so instead! + + return OPERATOR_FINISHED; +} + +void ACT_OT_keyframes_paste (wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name= "Paste Keyframes"; + ot->idname= "ACT_OT_keyframes_paste"; + + /* api callbacks */ + ot->exec= actkeys_paste_exec; + ot->poll= ED_operator_areaactive; + + /* flags */ + ot->flag= OPTYPE_REGISTER/*|OPTYPE_UNDO*/; +} /* ******************** Delete Keyframes Operator ************************* */ diff --git a/source/blender/editors/space_action/action_header.c b/source/blender/editors/space_action/action_header.c index b7fb24f400b..dbcdc0adb1e 100644 --- a/source/blender/editors/space_action/action_header.c +++ b/source/blender/editors/space_action/action_header.c @@ -461,6 +461,13 @@ static void do_action_buttons(bContext *C, void *arg, int event) case B_REDR: ED_region_tag_redraw(CTX_wm_region(C)); break; + + case B_ACTCOPYKEYS: + WM_operator_name_call(C, "ACT_OT_keyframes_copy", WM_OP_EXEC_REGION_WIN, NULL); + break; + case B_ACTPASTEKEYS: + WM_operator_name_call(C, "ACT_OT_keyframes_paste", WM_OP_EXEC_REGION_WIN, NULL); + break; } } diff --git a/source/blender/editors/space_action/action_intern.h b/source/blender/editors/space_action/action_intern.h index 6f0b5c9899d..d70cbb3f4c2 100644 --- a/source/blender/editors/space_action/action_intern.h +++ b/source/blender/editors/space_action/action_intern.h @@ -73,6 +73,9 @@ enum { /* ***************************************** */ /* action_edit_keyframes.c */ +void ACT_OT_keyframes_copy(struct wmOperatorType *ot); +void ACT_OT_keyframes_paste(struct wmOperatorType *ot); + void ACT_OT_keyframes_delete(struct wmOperatorType *ot); void ACT_OT_keyframes_clean(struct wmOperatorType *ot); void ACT_OT_keyframes_sample(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_action/action_ops.c b/source/blender/editors/space_action/action_ops.c index d09a08af0b6..b9c3b711da5 100644 --- a/source/blender/editors/space_action/action_ops.c +++ b/source/blender/editors/space_action/action_ops.c @@ -80,6 +80,8 @@ void action_operatortypes(void) WM_operatortype_append(ACT_OT_keyframes_sample); WM_operatortype_append(ACT_OT_keyframes_clean); WM_operatortype_append(ACT_OT_keyframes_delete); + WM_operatortype_append(ACT_OT_keyframes_copy); + WM_operatortype_append(ACT_OT_keyframes_paste); } /* ************************** registration - keymaps **********************************/ @@ -127,6 +129,11 @@ static void action_keymap_keyframes (wmWindowManager *wm, ListBase *keymap) WM_keymap_add_item(keymap, "ACT_OT_keyframes_delete", XKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "ACT_OT_keyframes_delete", DELKEY, KM_PRESS, 0, 0); + /* copy/paste */ + // XXX - should we keep these? + WM_keymap_add_item(keymap, "ACT_OT_keyframes_copy", CKEY, KM_PRESS, KM_CTRL, 0); + WM_keymap_add_item(keymap, "ACT_OT_keyframes_paste", VKEY, KM_PRESS, KM_CTRL, 0); + /* transform system */ transform_keymap_for_space(wm, keymap, SPACE_ACTION); } diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 0bf0a89fd7a..eca4dec122e 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -166,6 +166,9 @@ extern ListBase editelems; extern wchar_t *copybuf; extern wchar_t *copybufinfo; + // XXX copy/paste buffer stuff... +extern void free_actcopybuf(); + /* called in creator.c even... tsk, split this! */ void WM_exit(bContext *C) { @@ -219,7 +222,7 @@ void WM_exit(bContext *C) free_blender(); /* blender.c, does entire library */ // free_matcopybuf(); // free_ipocopybuf(); -// free_actcopybuf(); + free_actcopybuf(); // free_vertexpaint(); // free_imagepaint(); |