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. @@

_______________________________________________
Bf-blender-cvs mailing list
[email protected]
http://lists.blender.org/mailman/listinfo/bf-blender-cvs

Reply via email to