Blender  V2.59
editaction_gpencil.c
Go to the documentation of this file.
00001 /*
00002  * $Id: editaction_gpencil.c 35242 2011-02-27 20:29:51Z jesterking $
00003  *
00004  * ***** BEGIN GPL LICENSE BLOCK *****
00005  *
00006  * This program is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU General Public License
00008  * as published by the Free Software Foundation; either version 2
00009  * of the License, or (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software Foundation,
00018  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00019  *
00020  * The Original Code is Copyright (C) 2008, Blender Foundation
00021  * This is a new part of Blender
00022  *
00023  * Contributor(s): Joshua Leung
00024  *
00025  * ***** END GPL LICENSE BLOCK *****
00026  */
00027 
00033 #include <stdio.h>
00034 #include <string.h>
00035 #include <stdlib.h>
00036 #include <stddef.h>
00037 #include <math.h>
00038 
00039 #include "MEM_guardedalloc.h"
00040 
00041 #include "BLI_blenlib.h"
00042 #include "BLI_math.h"
00043 #include "BLI_utildefines.h"
00044 
00045 #include "DNA_gpencil_types.h"
00046 #include "DNA_scene_types.h"
00047 
00048 #include "BKE_fcurve.h"
00049 #include "BKE_gpencil.h"
00050 
00051 #include "ED_anim_api.h"
00052 #include "ED_gpencil.h"
00053 #include "ED_keyframes_edit.h"
00054 
00055 #include "gpencil_intern.h"
00056 
00057 /* ***************************************** */
00058 /* NOTE ABOUT THIS FILE:
00059  *      This file contains code for editing Grease Pencil data in the Action Editor
00060  *      as a 'keyframes', so that a user can adjust the timing of Grease Pencil drawings.
00061  *      Therefore, this file mostly contains functions for selecting Grease-Pencil frames.
00062  */
00063 /* ***************************************** */
00064 /* Generics - Loopers */
00065 
00066 /* Loops over the gp-frames for a gp-layer, and applies the given callback */
00067 short gplayer_frames_looper (bGPDlayer *gpl, Scene *scene, short (*gpf_cb)(bGPDframe *, Scene *))
00068 {
00069         bGPDframe *gpf;
00070         
00071         /* error checker */
00072         if (gpl == NULL)
00073                 return 0;
00074         
00075         /* do loop */
00076         for (gpf= gpl->frames.first; gpf; gpf= gpf->next) {
00077                 /* execute callback */
00078                 if (gpf_cb(gpf, scene))
00079                         return 1;
00080         }
00081                 
00082         /* nothing to return */
00083         return 0;
00084 }
00085 
00086 /* ****************************************** */
00087 /* Data Conversion Tools */
00088 
00089 /* make a listing all the gp-frames in a layer as cfraelems */
00090 void gplayer_make_cfra_list (bGPDlayer *gpl, ListBase *elems, short onlysel)
00091 {
00092         bGPDframe *gpf;
00093         CfraElem *ce;
00094         
00095         /* error checking */
00096         if (ELEM(NULL, gpl, elems))
00097                 return;
00098         
00099         /* loop through gp-frames, adding */
00100         for (gpf= gpl->frames.first; gpf; gpf= gpf->next) {
00101                 if ((onlysel == 0) || (gpf->flag & GP_FRAME_SELECT)) {
00102                         ce= MEM_callocN(sizeof(CfraElem), "CfraElem");
00103                         
00104                         ce->cfra= (float)gpf->framenum;
00105                         ce->sel= (gpf->flag & GP_FRAME_SELECT) ? 1 : 0;
00106                         
00107                         BLI_addtail(elems, ce);
00108                 }
00109         }
00110 }
00111 
00112 /* ***************************************** */
00113 /* Selection Tools */
00114 
00115 /* check if one of the frames in this layer is selected */
00116 short is_gplayer_frame_selected (bGPDlayer *gpl)
00117 {
00118         bGPDframe *gpf;
00119         
00120         /* error checking */
00121         if (gpl == NULL) 
00122                 return 0;
00123         
00124         /* stop at the first one found */
00125         for (gpf= gpl->frames.first; gpf; gpf= gpf->next) {
00126                 if (gpf->flag & GP_FRAME_SELECT)
00127                         return 1;
00128         }
00129         
00130         /* not found */
00131         return 0;
00132 }
00133 
00134 /* helper function - select gp-frame based on SELECT_* mode */
00135 static void gpframe_select (bGPDframe *gpf, short select_mode)
00136 {
00137         if (gpf == NULL)
00138                 return;
00139         
00140         switch (select_mode) {
00141                 case SELECT_ADD:
00142                         gpf->flag |= GP_FRAME_SELECT;
00143                         break;
00144                 case SELECT_SUBTRACT:
00145                         gpf->flag &= ~GP_FRAME_SELECT;
00146                         break;
00147                 case SELECT_INVERT:
00148                         gpf->flag ^= GP_FRAME_SELECT;
00149                         break;
00150         }
00151 }
00152 
00153 /* set all/none/invert select (like above, but with SELECT_* modes) */
00154 void select_gpencil_frames (bGPDlayer *gpl, short select_mode)
00155 {
00156         bGPDframe *gpf;
00157         
00158         /* error checking */
00159         if (gpl == NULL) 
00160                 return;
00161                 
00162         /* handle according to mode */
00163         for (gpf= gpl->frames.first; gpf; gpf= gpf->next) {
00164                 gpframe_select(gpf, select_mode);
00165         }
00166 }
00167 
00168 /* set all/none/invert select */
00169 void set_gplayer_frame_selection (bGPDlayer *gpl, short mode)
00170 {
00171         /* error checking */
00172         if (gpl == NULL) 
00173                 return;
00174         
00175         /* now call the standard function */
00176         select_gpencil_frames(gpl, mode);
00177 }
00178 
00179 /* select the frame in this layer that occurs on this frame (there should only be one at most) */
00180 void select_gpencil_frame (bGPDlayer *gpl, int selx, short select_mode)
00181 {
00182         bGPDframe *gpf;
00183         
00184         if (gpl == NULL) 
00185                 return;
00186         
00187         /* search through frames for a match */
00188         for (gpf= gpl->frames.first; gpf; gpf= gpf->next) {
00189                 /* there should only be one frame with this frame-number */
00190                 if (gpf->framenum == selx) {
00191                         gpframe_select(gpf, select_mode);
00192                         break;
00193                 }
00194         }
00195 }
00196 
00197 /* select the frames in this layer that occur within the bounds specified */
00198 void borderselect_gplayer_frames (bGPDlayer *gpl, float min, float max, short select_mode)
00199 {
00200         bGPDframe *gpf;
00201         
00202         if (gpl == NULL)
00203                 return;
00204         
00205         /* only select those frames which are in bounds */
00206         for (gpf= gpl->frames.first; gpf; gpf= gpf->next) {
00207                 if (IN_RANGE(gpf->framenum, min, max))
00208                         gpframe_select(gpf, select_mode);
00209         }
00210 }
00211 
00212 #if 0 // XXX disabled until grease pencil code stabilises again
00213 
00214 /* De-selects or inverts the selection of Layers for a grease-pencil block
00215  *      mode: 0 = default behaviour (select all), 1 = test if (de)select all, 2 = invert all 
00216  */
00217 void deselect_gpencil_layers (void *data, short mode)
00218 {
00219         ListBase act_data = {NULL, NULL};
00220         bActListElem *ale;
00221         int filter, sel=1;
00222         
00223         /* filter data */
00224         filter= ACTFILTER_VISIBLE;
00225         actdata_filter(&act_data, filter, data, ACTCONT_GPENCIL);
00226         
00227         /* See if we should be selecting or deselecting */
00228         if (mode == 1) {
00229                 for (ale= act_data.first; ale; ale= ale->next) {
00230                         if (sel == 0) 
00231                                 break;
00232                         
00233                         if (ale->flag & GP_LAYER_SELECT)
00234                                 sel= 0;
00235                 }
00236         }
00237         else
00238                 sel= 0;
00239                 
00240         /* Now set the flags */
00241         for (ale= act_data.first; ale; ale= ale->next) {
00242                 bGPDlayer *gpl= (bGPDlayer *)ale->data;
00243                 
00244                 if (mode == 2)
00245                         gpl->flag ^= GP_LAYER_SELECT;
00246                 else if (sel)
00247                         gpl->flag |= GP_LAYER_SELECT;
00248                 else
00249                         gpl->flag &= ~GP_LAYER_SELECT;
00250                         
00251                 gpl->flag &= ~GP_LAYER_ACTIVE;
00252         }
00253         
00254         /* Cleanup */
00255         BLI_freelistN(&act_data);
00256 }
00257 
00258 #endif // XXX disabled until Grease Pencil code stabilises again...
00259 
00260 /* ***************************************** */
00261 /* Frame Editing Tools */
00262 
00263 #if 0 // XXX disabled until grease pencil code stabilises again
00264 /* Delete selected grease-pencil layers */
00265 void delete_gpencil_layers (void)
00266 {
00267         ListBase act_data = {NULL, NULL};
00268         bActListElem *ale, *next;
00269         void *data;
00270         short datatype;
00271         int filter;
00272         
00273         /* determine what type of data we are operating on */
00274         data = get_action_context(&datatype);
00275         if (data == NULL) return;
00276         if (datatype != ACTCONT_GPENCIL) return;
00277         
00278         /* filter data */
00279         filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_CHANNELS | ACTFILTER_SEL);
00280         actdata_filter(&act_data, filter, data, datatype);
00281         
00282         /* clean up grease-pencil layers */
00283         for (ale= act_data.first; ale; ale= next) {
00284                 bGPdata *gpd= (bGPdata *)ale->owner;
00285                 bGPDlayer *gpl= (bGPDlayer *)ale->data;
00286                 next= ale->next;
00287                 
00288                 /* free layer and its data */
00289                 if (SEL_GPL(gpl)) {
00290                         free_gpencil_frames(gpl);
00291                         BLI_freelinkN(&gpd->layers, gpl);
00292                 }
00293                 
00294                 /* free temp memory */
00295                 BLI_freelinkN(&act_data, ale);
00296         }
00297         
00298         BIF_undo_push("Delete GPencil Layers");
00299 }
00300 #endif // XXX disabled until Grease Pencil code stabilises again...
00301 
00302 /* Delete selected frames */
00303 void delete_gplayer_frames (bGPDlayer *gpl)
00304 {
00305         bGPDframe *gpf, *gpfn;
00306         
00307         /* error checking */
00308         if (gpl == NULL)
00309                 return;
00310                 
00311         /* check for frames to delete */
00312         for (gpf= gpl->frames.first; gpf; gpf= gpfn) {
00313                 gpfn= gpf->next;
00314                 
00315                 if (gpf->flag & GP_FRAME_SELECT)
00316                         gpencil_layer_delframe(gpl, gpf);
00317         }
00318 }
00319 
00320 /* Duplicate selected frames from given gp-layer */
00321 void duplicate_gplayer_frames (bGPDlayer *gpl)
00322 {
00323         bGPDframe *gpf, *gpfn;
00324         
00325         /* error checking */
00326         if (gpl == NULL)
00327                 return;
00328         
00329         /* duplicate selected frames  */
00330         for (gpf= gpl->frames.first; gpf; gpf= gpfn) {
00331                 gpfn= gpf->next;
00332                 
00333                 /* duplicate this frame */
00334                 if (gpf->flag & GP_FRAME_SELECT) {
00335                         bGPDframe *gpfd; 
00336                         
00337                         /* duplicate frame, and deselect self */
00338                         gpfd= gpencil_frame_duplicate(gpf);
00339                         gpf->flag &= ~GP_FRAME_SELECT;
00340                         
00341                         BLI_insertlinkafter(&gpl->frames, gpf, gpfd);
00342                 }
00343         }
00344 }
00345 
00346 #if 0 // XXX disabled until grease pencil code stabilises again
00347 /* -------------------------------------- */
00348 /* Copy and Paste Tools */
00349 /* - The copy/paste buffer currently stores a set of GP_Layers, with temporary
00350  *      GP_Frames with the necessary strokes
00351  * - Unless there is only one element in the buffer, names are also tested to check for compatability.
00352  * - All pasted frames are offset by the same amount. This is calculated as the difference in the times of
00353  *      the current frame and the 'first keyframe' (i.e. the earliest one in all channels).
00354  * - The earliest frame is calculated per copy operation.
00355  */
00356  
00357 /* globals for copy/paste data (like for other copy/paste buffers) */
00358 ListBase gpcopybuf = {NULL, NULL};
00359 static int gpcopy_firstframe= 999999999;
00360 
00361 /* This function frees any MEM_calloc'ed copy/paste buffer data */
00362 void free_gpcopybuf ()
00363 {
00364         free_gpencil_layers(&gpcopybuf); 
00365         
00366         gpcopybuf.first= gpcopybuf.last= NULL;
00367         gpcopy_firstframe= 999999999;
00368 }
00369 
00370 /* This function adds data to the copy/paste buffer, freeing existing data first
00371  * Only the selected GP-layers get their selected keyframes copied.
00372  */
00373 void copy_gpdata ()
00374 {
00375         ListBase act_data = {NULL, NULL};
00376         bActListElem *ale;
00377         int filter;
00378         void *data;
00379         short datatype;
00380         
00381         /* clear buffer first */
00382         free_gpcopybuf();
00383         
00384         /* get data */
00385         data= get_action_context(&datatype);
00386         if (data == NULL) return;
00387         if (datatype != ACTCONT_GPENCIL) return;
00388         
00389         /* filter data */
00390         filter= (ACTFILTER_VISIBLE | ACTFILTER_SEL);
00391         actdata_filter(&act_data, filter, data, datatype);
00392         
00393         /* assume that each of these is an ipo-block */
00394         for (ale= act_data.first; ale; ale= ale->next) {
00395                 bGPDlayer *gpls, *gpln;
00396                 bGPDframe *gpf, *gpfn;
00397                 
00398                 /* get new layer to put into buffer */
00399                 gpls= (bGPDlayer *)ale->data;
00400                 gpln= MEM_callocN(sizeof(bGPDlayer), "GPCopyPasteLayer");
00401                 
00402                 gpln->frames.first= gpln->frames.last= NULL;
00403                 strcpy(gpln->info, gpls->info);
00404                 
00405                 BLI_addtail(&gpcopybuf, gpln);
00406                 
00407                 /* loop over frames, and copy only selected frames */
00408                 for (gpf= gpls->frames.first; gpf; gpf= gpf->next) {
00409                         /* if frame is selected, make duplicate it and its strokes */
00410                         if (gpf->flag & GP_FRAME_SELECT) {
00411                                 /* add frame to buffer */
00412                                 gpfn= gpencil_frame_duplicate(gpf);
00413                                 BLI_addtail(&gpln->frames, gpfn);
00414                                 
00415                                 /* check if this is the earliest frame encountered so far */
00416                                 if (gpf->framenum < gpcopy_firstframe)
00417                                         gpcopy_firstframe= gpf->framenum;
00418                         }
00419                 }
00420         }
00421         
00422         /* check if anything ended up in the buffer */
00423         if (ELEM(NULL, gpcopybuf.first, gpcopybuf.last))
00424                 error("Nothing copied to buffer");
00425         
00426         /* free temp memory */
00427         BLI_freelistN(&act_data);
00428 }
00429 
00430 void paste_gpdata (Scene *scene)
00431 {
00432         ListBase act_data = {NULL, NULL};
00433         bActListElem *ale;
00434         int filter;
00435         void *data;
00436         short datatype;
00437         
00438         const int offset = (CFRA - gpcopy_firstframe);
00439         short no_name= 0;
00440         
00441         /* check if buffer is empty */
00442         if (ELEM(NULL, gpcopybuf.first, gpcopybuf.last)) {
00443                 error("No data in buffer to paste");
00444                 return;
00445         }
00446         /* check if single channel in buffer (disregard names if so)  */
00447         if (gpcopybuf.first == gpcopybuf.last)
00448                 no_name= 1;
00449         
00450         /* get data */
00451         data= get_action_context(&datatype);
00452         if (data == NULL) return;
00453         if (datatype != ACTCONT_GPENCIL) return;
00454         
00455         /* filter data */
00456         filter= (ACTFILTER_VISIBLE | ACTFILTER_SEL | ACTFILTER_FOREDIT);
00457         actdata_filter(&act_data, filter, data, datatype);
00458         
00459         /* from selected channels */
00460         for (ale= act_data.first; ale; ale= ale->next) {
00461                 bGPDlayer *gpld= (bGPDlayer *)ale->data;
00462                 bGPDlayer *gpls= NULL;
00463                 bGPDframe *gpfs, *gpf;
00464                 
00465                 /* find suitable layer from buffer to use to paste from */
00466                 for (gpls= gpcopybuf.first; gpls; gpls= gpls->next) {
00467                         /* check if layer name matches */
00468                         if ((no_name) || (strcmp(gpls->info, gpld->info)==0))
00469                                 break;
00470                 }
00471                 
00472                 /* this situation might occur! */
00473                 if (gpls == NULL)
00474                         continue;
00475                 
00476                 /* add frames from buffer */
00477                 for (gpfs= gpls->frames.first; gpfs; gpfs= gpfs->next) {
00478                         /* temporarily apply offset to buffer-frame while copying */
00479                         gpfs->framenum += offset;
00480                         
00481                         /* get frame to copy data into (if no frame returned, then just ignore) */
00482                         gpf= gpencil_layer_getframe(gpld, gpfs->framenum, 1);
00483                         if (gpf) {
00484                                 bGPDstroke *gps, *gpsn;
00485                                 ScrArea *sa;
00486                                 
00487                                 /* get area that gp-data comes from */
00488                                 //sa= gpencil_data_findowner((bGPdata *)ale->owner);    
00489                                 sa = NULL;
00490                                 
00491                                 /* this should be the right frame... as it may be a pre-existing frame, 
00492                                  * must make sure that only compatible stroke types get copied over 
00493                                  *      - we cannot just add a duplicate frame, as that would cause errors
00494                                  *      - need to check for compatible types to minimise memory usage (copying 'junk' over)
00495                                  */
00496                                 for (gps= gpfs->strokes.first; gps; gps= gps->next) {
00497                                         short stroke_ok;
00498                                         
00499                                         /* if there's an area, check that it supports this type of stroke */
00500                                         if (sa) {
00501                                                 stroke_ok= 0;
00502                                                 
00503                                                 /* check if spacetype supports this type of stroke
00504                                                  *      - NOTE: must sync this with gp_paint_initstroke() in gpencil.c
00505                                                  */
00506                                                 switch (sa->spacetype) {
00507                                                         case SPACE_VIEW3D: /* 3D-View: either screen-aligned or 3d-space */
00508                                                                 if ((gps->flag == 0) || (gps->flag & GP_STROKE_3DSPACE))
00509                                                                         stroke_ok= 1;
00510                                                                 break;
00511                                                                 
00512                                                         case SPACE_NODE: /* Nodes Editor: either screen-aligned or view-aligned */
00513                                                         case SPACE_IMAGE: /* Image Editor: either screen-aligned or view\image-aligned */
00514                                                                 if ((gps->flag == 0) || (gps->flag & GP_STROKE_2DSPACE))
00515                                                                         stroke_ok= 1;
00516                                                                 break;
00517                                                                 
00518                                                         case SPACE_SEQ: /* Sequence Editor: either screen-aligned or view-aligned */
00519                                                                 if ((gps->flag == 0) || (gps->flag & GP_STROKE_2DIMAGE))
00520                                                                         stroke_ok= 1;
00521                                                                 break;
00522                                                 }
00523                                         }
00524                                         else
00525                                                 stroke_ok= 1;
00526                                         
00527                                         /* if stroke is ok, we make a copy of this stroke and add to frame */
00528                                         if (stroke_ok) {
00529                                                 /* make a copy of stroke, then of its points array */
00530                                                 gpsn= MEM_dupallocN(gps);
00531                                                 gpsn->points= MEM_dupallocN(gps->points);
00532                                                 
00533                                                 /* append stroke to frame */
00534                                                 BLI_addtail(&gpf->strokes, gpsn);
00535                                         }
00536                                 }
00537                                 
00538                                 /* if no strokes (i.e. new frame) added, free gpf */
00539                                 if (gpf->strokes.first == NULL)
00540                                         gpencil_layer_delframe(gpld, gpf);
00541                         }
00542                         
00543                         /* unapply offset from buffer-frame */
00544                         gpfs->framenum -= offset;
00545                 }
00546         }
00547         
00548         /* free temp memory */
00549         BLI_freelistN(&act_data);
00550         
00551         /* undo and redraw stuff */
00552         BIF_undo_push("Paste Grease Pencil Frames");
00553 }
00554 
00555 /* -------------------------------------- */
00556 /* Snap Tools */
00557 
00558 static short snap_gpf_nearest (bGPDframe *gpf, Scene *scene)
00559 {
00560         if (gpf->flag & GP_FRAME_SELECT)
00561                 gpf->framenum= (int)(floor(gpf->framenum+0.5));
00562         return 0;
00563 }
00564 
00565 static short snap_gpf_nearestsec (bGPDframe *gpf, Scene *scene)
00566 {
00567         float secf = (float)FPS;
00568         if (gpf->flag & GP_FRAME_SELECT)
00569                 gpf->framenum= (int)(floor(gpf->framenum/secf + 0.5f) * secf);
00570         return 0;
00571 }
00572 
00573 static short snap_gpf_cframe (bGPDframe *gpf, Scene *scene)
00574 {
00575         if (gpf->flag & GP_FRAME_SELECT)
00576                 gpf->framenum= (int)CFRA;
00577         return 0;
00578 }
00579 
00580 static short snap_gpf_nearmarker (bGPDframe *gpf, Scene *scene)
00581 {
00582         if (gpf->flag & GP_FRAME_SELECT)
00583                 gpf->framenum= (int)find_nearest_marker_time(&scene->markers, (float)gpf->framenum);
00584         return 0;
00585 }
00586 
00587 
00588 /* snap selected frames to ... */
00589 void snap_gplayer_frames (bGPDlayer *gpl, Scene *scene, short mode)
00590 {
00591         switch (mode) {
00592                 case 1: /* snap to nearest frame */
00593                         gplayer_frames_looper(gpl, scene, snap_gpf_nearest);
00594                         break;
00595                 case 2: /* snap to current frame */
00596                         gplayer_frames_looper(gpl, scene, snap_gpf_cframe);
00597                         break;
00598                 case 3: /* snap to nearest marker */
00599                         gplayer_frames_looper(gpl, scene, snap_gpf_nearmarker);
00600                         break;
00601                 case 4: /* snap to nearest second */
00602                         gplayer_frames_looper(gpl, scene, snap_gpf_nearestsec);
00603                         break;
00604                 default: /* just in case */
00605                         gplayer_frames_looper(gpl, scene, snap_gpf_nearest);
00606                         break;
00607         }
00608 }
00609 
00610 /* -------------------------------------- */
00611 /* Mirror Tools */
00612 
00613 static short mirror_gpf_cframe (bGPDframe *gpf, Scene *scene)
00614 {
00615         int diff;
00616         
00617         if (gpf->flag & GP_FRAME_SELECT) {
00618                 diff= CFRA - gpf->framenum;
00619                 gpf->framenum= CFRA;
00620         }
00621         
00622         return 0;
00623 }
00624 
00625 static short mirror_gpf_yaxis (bGPDframe *gpf, Scene *scene)
00626 {
00627         int diff;
00628         
00629         if (gpf->flag & GP_FRAME_SELECT) {
00630                 diff= -gpf->framenum;
00631                 gpf->framenum= diff;
00632         }
00633         
00634         return 0;
00635 }
00636 
00637 static short mirror_gpf_xaxis (bGPDframe *gpf, Scene *scene)
00638 {
00639         int diff;
00640         
00641         if (gpf->flag & GP_FRAME_SELECT) {
00642                 diff= -gpf->framenum;
00643                 gpf->framenum= diff;
00644         }
00645         
00646         return 0;
00647 }
00648 
00649 static short mirror_gpf_marker (bGPDframe *gpf, Scene *scene)
00650 {
00651         static TimeMarker *marker;
00652         static short initialised = 0;
00653         int diff;
00654         
00655         /* In order for this mirror function to work without
00656          * any extra arguments being added, we use the case
00657          * of bezt==NULL to denote that we should find the 
00658          * marker to mirror over. The static pointer is safe
00659          * to use this way, as it will be set to null after 
00660          * each cycle in which this is called.
00661          */
00662         
00663         if (gpf) {
00664                 /* mirroring time */
00665                 if ((gpf->flag & GP_FRAME_SELECT) && (marker)) {
00666                         diff= (marker->frame - gpf->framenum);
00667                         gpf->framenum= (marker->frame + diff);
00668                 }
00669         }
00670         else {
00671                 /* initialisation time */
00672                 if (initialised) {
00673                         /* reset everything for safety */
00674                         marker = NULL;
00675                         initialised = 0;
00676                 }
00677                 else {
00678                         /* try to find a marker */
00679                         marker= ED_markers_get_first_selected(&scene->markers);
00680                         if(marker) {
00681                                 initialised= 1;
00682                         }
00683                 }
00684         }
00685         
00686         return 0;
00687 }
00688 
00689 
00690 /* mirror selected gp-frames on... */
00691 void mirror_gplayer_frames (bGPDlayer *gpl, Scene *scene, short mode)
00692 {
00693         switch (mode) {
00694                 case 1: /* mirror over current frame */
00695                         gplayer_frames_looper(gpl, scene, mirror_gpf_cframe);
00696                         break;
00697                 case 2: /* mirror over frame 0 */
00698                         gplayer_frames_looper(gpl, scene, mirror_gpf_yaxis);
00699                         break;
00700                 case 3: /* mirror over value 0 */
00701                         gplayer_frames_looper(gpl, scene, mirror_gpf_xaxis);
00702                         break;
00703                 case 4: /* mirror over marker */
00704                         mirror_gpf_marker(NULL, NULL);
00705                         gplayer_frames_looper(gpl, scene, mirror_gpf_marker);
00706                         mirror_gpf_marker(NULL, NULL);
00707                         break;
00708                 default: /* just in case */
00709                         gplayer_frames_looper(gpl, scene, mirror_gpf_yaxis);
00710                         break;
00711         }
00712 }
00713 
00714 /* ***************************************** */
00715 #endif // XXX disabled until Grease Pencil code stabilises again...