[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [18145] branches/blender2.5/blender/source /blender: 2.5 - Action Editor: Copy/Paste
Joshua Leung
aligorith at gmail.com
Mon Dec 29 12:04:55 CET 2008
Revision: 18145
http://projects.blender.org/plugins/scmsvn/viewcvs.php?view=rev&root=bf-blender&revision=18145
Author: aligorith
Date: 2008-12-29 12:04:55 +0100 (Mon, 29 Dec 2008)
Log Message:
-----------
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...
Modified Paths:
--------------
branches/blender2.5/blender/source/blender/editors/space_action/action_edit_keyframes.c
branches/blender2.5/blender/source/blender/editors/space_action/action_header.c
branches/blender2.5/blender/source/blender/editors/space_action/action_intern.h
branches/blender2.5/blender/source/blender/editors/space_action/action_ops.c
branches/blender2.5/blender/source/blender/windowmanager/intern/wm_init_exit.c
Modified: branches/blender2.5/blender/source/blender/editors/space_action/action_edit_keyframes.c
===================================================================
--- branches/blender2.5/blender/source/blender/editors/space_action/action_edit_keyframes.c 2008-12-29 09:24:42 UTC (rev 18144)
+++ branches/blender2.5/blender/source/blender/editors/space_action/action_edit_keyframes.c 2008-12-29 11:04:55 UTC (rev 18145)
@@ -89,10 +89,355 @@
/* 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 */
@@ Diff output truncated at 10240 characters. @@
More information about the Bf-blender-cvs
mailing list