doxygen: blender/editors tagged.
[blender-staging.git] / source / blender / editors / gpencil / editaction_gpencil.c
1 /*
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * The Original Code is Copyright (C) 2008, Blender Foundation
21  * This is a new part of Blender
22  *
23  * Contributor(s): Joshua Leung
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/editors/gpencil/editaction_gpencil.c
29  *  \ingroup edgpencil
30  */
31
32  
33 #include <stdio.h>
34 #include <string.h>
35 #include <stdlib.h>
36 #include <stddef.h>
37 #include <math.h>
38
39 #include "MEM_guardedalloc.h"
40
41 #include "BLI_blenlib.h"
42 #include "BLI_math.h"
43 #include "BLI_utildefines.h"
44
45 #include "DNA_gpencil_types.h"
46 #include "DNA_scene_types.h"
47
48 #include "BKE_fcurve.h"
49 #include "BKE_gpencil.h"
50
51 #include "ED_anim_api.h"
52 #include "ED_gpencil.h"
53 #include "ED_keyframes_edit.h"
54
55 #include "gpencil_intern.h"
56
57 /* ***************************************** */
58 /* NOTE ABOUT THIS FILE:
59  *      This file contains code for editing Grease Pencil data in the Action Editor
60  *      as a 'keyframes', so that a user can adjust the timing of Grease Pencil drawings.
61  *      Therefore, this file mostly contains functions for selecting Grease-Pencil frames.
62  */
63 /* ***************************************** */
64 /* Generics - Loopers */
65
66 /* Loops over the gp-frames for a gp-layer, and applies the given callback */
67 short gplayer_frames_looper (bGPDlayer *gpl, Scene *scene, short (*gpf_cb)(bGPDframe *, Scene *))
68 {
69         bGPDframe *gpf;
70         
71         /* error checker */
72         if (gpl == NULL)
73                 return 0;
74         
75         /* do loop */
76         for (gpf= gpl->frames.first; gpf; gpf= gpf->next) {
77                 /* execute callback */
78                 if (gpf_cb(gpf, scene))
79                         return 1;
80         }
81                 
82         /* nothing to return */
83         return 0;
84 }
85
86 /* ****************************************** */
87 /* Data Conversion Tools */
88
89 /* make a listing all the gp-frames in a layer as cfraelems */
90 void gplayer_make_cfra_list (bGPDlayer *gpl, ListBase *elems, short onlysel)
91 {
92         bGPDframe *gpf;
93         CfraElem *ce;
94         
95         /* error checking */
96         if (ELEM(NULL, gpl, elems))
97                 return;
98         
99         /* loop through gp-frames, adding */
100         for (gpf= gpl->frames.first; gpf; gpf= gpf->next) {
101                 if ((onlysel == 0) || (gpf->flag & GP_FRAME_SELECT)) {
102                         ce= MEM_callocN(sizeof(CfraElem), "CfraElem");
103                         
104                         ce->cfra= (float)gpf->framenum;
105                         ce->sel= (gpf->flag & GP_FRAME_SELECT) ? 1 : 0;
106                         
107                         BLI_addtail(elems, ce);
108                 }
109         }
110 }
111
112 /* ***************************************** */
113 /* Selection Tools */
114
115 /* check if one of the frames in this layer is selected */
116 short is_gplayer_frame_selected (bGPDlayer *gpl)
117 {
118         bGPDframe *gpf;
119         
120         /* error checking */
121         if (gpl == NULL) 
122                 return 0;
123         
124         /* stop at the first one found */
125         for (gpf= gpl->frames.first; gpf; gpf= gpf->next) {
126                 if (gpf->flag & GP_FRAME_SELECT)
127                         return 1;
128         }
129         
130         /* not found */
131         return 0;
132 }
133
134 /* helper function - select gp-frame based on SELECT_* mode */
135 static void gpframe_select (bGPDframe *gpf, short select_mode)
136 {
137         if (gpf == NULL)
138                 return;
139         
140         switch (select_mode) {
141                 case SELECT_ADD:
142                         gpf->flag |= GP_FRAME_SELECT;
143                         break;
144                 case SELECT_SUBTRACT:
145                         gpf->flag &= ~GP_FRAME_SELECT;
146                         break;
147                 case SELECT_INVERT:
148                         gpf->flag ^= GP_FRAME_SELECT;
149                         break;
150         }
151 }
152
153 /* set all/none/invert select (like above, but with SELECT_* modes) */
154 void select_gpencil_frames (bGPDlayer *gpl, short select_mode)
155 {
156         bGPDframe *gpf;
157         
158         /* error checking */
159         if (gpl == NULL) 
160                 return;
161                 
162         /* handle according to mode */
163         for (gpf= gpl->frames.first; gpf; gpf= gpf->next) {
164                 gpframe_select(gpf, select_mode);
165         }
166 }
167
168 /* set all/none/invert select */
169 void set_gplayer_frame_selection (bGPDlayer *gpl, short mode)
170 {
171         /* error checking */
172         if (gpl == NULL) 
173                 return;
174         
175         /* now call the standard function */
176         select_gpencil_frames(gpl, mode);
177 }
178
179 /* select the frame in this layer that occurs on this frame (there should only be one at most) */
180 void select_gpencil_frame (bGPDlayer *gpl, int selx, short select_mode)
181 {
182         bGPDframe *gpf;
183         
184         if (gpl == NULL) 
185                 return;
186         
187         /* search through frames for a match */
188         for (gpf= gpl->frames.first; gpf; gpf= gpf->next) {
189                 /* there should only be one frame with this frame-number */
190                 if (gpf->framenum == selx) {
191                         gpframe_select(gpf, select_mode);
192                         break;
193                 }
194         }
195 }
196
197 /* select the frames in this layer that occur within the bounds specified */
198 void borderselect_gplayer_frames (bGPDlayer *gpl, float min, float max, short select_mode)
199 {
200         bGPDframe *gpf;
201         
202         if (gpl == NULL)
203                 return;
204         
205         /* only select those frames which are in bounds */
206         for (gpf= gpl->frames.first; gpf; gpf= gpf->next) {
207                 if (IN_RANGE(gpf->framenum, min, max))
208                         gpframe_select(gpf, select_mode);
209         }
210 }
211
212 #if 0 // XXX disabled until grease pencil code stabilises again
213
214 /* De-selects or inverts the selection of Layers for a grease-pencil block
215  *      mode: 0 = default behaviour (select all), 1 = test if (de)select all, 2 = invert all 
216  */
217 void deselect_gpencil_layers (void *data, short mode)
218 {
219         ListBase act_data = {NULL, NULL};
220         bActListElem *ale;
221         int filter, sel=1;
222         
223         /* filter data */
224         filter= ACTFILTER_VISIBLE;
225         actdata_filter(&act_data, filter, data, ACTCONT_GPENCIL);
226         
227         /* See if we should be selecting or deselecting */
228         if (mode == 1) {
229                 for (ale= act_data.first; ale; ale= ale->next) {
230                         if (sel == 0) 
231                                 break;
232                         
233                         if (ale->flag & GP_LAYER_SELECT)
234                                 sel= 0;
235                 }
236         }
237         else
238                 sel= 0;
239                 
240         /* Now set the flags */
241         for (ale= act_data.first; ale; ale= ale->next) {
242                 bGPDlayer *gpl= (bGPDlayer *)ale->data;
243                 
244                 if (mode == 2)
245                         gpl->flag ^= GP_LAYER_SELECT;
246                 else if (sel)
247                         gpl->flag |= GP_LAYER_SELECT;
248                 else
249                         gpl->flag &= ~GP_LAYER_SELECT;
250                         
251                 gpl->flag &= ~GP_LAYER_ACTIVE;
252         }
253         
254         /* Cleanup */
255         BLI_freelistN(&act_data);
256 }
257
258 #endif // XXX disabled until Grease Pencil code stabilises again...
259
260 /* ***************************************** */
261 /* Frame Editing Tools */
262
263 #if 0 // XXX disabled until grease pencil code stabilises again
264 /* Delete selected grease-pencil layers */
265 void delete_gpencil_layers (void)
266 {
267         ListBase act_data = {NULL, NULL};
268         bActListElem *ale, *next;
269         void *data;
270         short datatype;
271         int filter;
272         
273         /* determine what type of data we are operating on */
274         data = get_action_context(&datatype);
275         if (data == NULL) return;
276         if (datatype != ACTCONT_GPENCIL) return;
277         
278         /* filter data */
279         filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_CHANNELS | ACTFILTER_SEL);
280         actdata_filter(&act_data, filter, data, datatype);
281         
282         /* clean up grease-pencil layers */
283         for (ale= act_data.first; ale; ale= next) {
284                 bGPdata *gpd= (bGPdata *)ale->owner;
285                 bGPDlayer *gpl= (bGPDlayer *)ale->data;
286                 next= ale->next;
287                 
288                 /* free layer and its data */
289                 if (SEL_GPL(gpl)) {
290                         free_gpencil_frames(gpl);
291                         BLI_freelinkN(&gpd->layers, gpl);
292                 }
293                 
294                 /* free temp memory */
295                 BLI_freelinkN(&act_data, ale);
296         }
297         
298         BIF_undo_push("Delete GPencil Layers");
299 }
300 #endif // XXX disabled until Grease Pencil code stabilises again...
301
302 /* Delete selected frames */
303 void delete_gplayer_frames (bGPDlayer *gpl)
304 {
305         bGPDframe *gpf, *gpfn;
306         
307         /* error checking */
308         if (gpl == NULL)
309                 return;
310                 
311         /* check for frames to delete */
312         for (gpf= gpl->frames.first; gpf; gpf= gpfn) {
313                 gpfn= gpf->next;
314                 
315                 if (gpf->flag & GP_FRAME_SELECT)
316                         gpencil_layer_delframe(gpl, gpf);
317         }
318 }
319
320 /* Duplicate selected frames from given gp-layer */
321 void duplicate_gplayer_frames (bGPDlayer *gpl)
322 {
323         bGPDframe *gpf, *gpfn;
324         
325         /* error checking */
326         if (gpl == NULL)
327                 return;
328         
329         /* duplicate selected frames  */
330         for (gpf= gpl->frames.first; gpf; gpf= gpfn) {
331                 gpfn= gpf->next;
332                 
333                 /* duplicate this frame */
334                 if (gpf->flag & GP_FRAME_SELECT) {
335                         bGPDframe *gpfd; 
336                         
337                         /* duplicate frame, and deselect self */
338                         gpfd= gpencil_frame_duplicate(gpf);
339                         gpf->flag &= ~GP_FRAME_SELECT;
340                         
341                         BLI_insertlinkafter(&gpl->frames, gpf, gpfd);
342                 }
343         }
344 }
345
346 #if 0 // XXX disabled until grease pencil code stabilises again
347 /* -------------------------------------- */
348 /* Copy and Paste Tools */
349 /* - The copy/paste buffer currently stores a set of GP_Layers, with temporary
350  *      GP_Frames with the necessary strokes
351  * - Unless there is only one element in the buffer, names are also tested to check for compatability.
352  * - All pasted frames are offset by the same amount. This is calculated as the difference in the times of
353  *      the current frame and the 'first keyframe' (i.e. the earliest one in all channels).
354  * - The earliest frame is calculated per copy operation.
355  */
356  
357 /* globals for copy/paste data (like for other copy/paste buffers) */
358 ListBase gpcopybuf = {NULL, NULL};
359 static int gpcopy_firstframe= 999999999;
360
361 /* This function frees any MEM_calloc'ed copy/paste buffer data */
362 void free_gpcopybuf ()
363 {
364         free_gpencil_layers(&gpcopybuf); 
365         
366         gpcopybuf.first= gpcopybuf.last= NULL;
367         gpcopy_firstframe= 999999999;
368 }
369
370 /* This function adds data to the copy/paste buffer, freeing existing data first
371  * Only the selected GP-layers get their selected keyframes copied.
372  */
373 void copy_gpdata ()
374 {
375         ListBase act_data = {NULL, NULL};
376         bActListElem *ale;
377         int filter;
378         void *data;
379         short datatype;
380         
381         /* clear buffer first */
382         free_gpcopybuf();
383         
384         /* get data */
385         data= get_action_context(&datatype);
386         if (data == NULL) return;
387         if (datatype != ACTCONT_GPENCIL) return;
388         
389         /* filter data */
390         filter= (ACTFILTER_VISIBLE | ACTFILTER_SEL);
391         actdata_filter(&act_data, filter, data, datatype);
392         
393         /* assume that each of these is an ipo-block */
394         for (ale= act_data.first; ale; ale= ale->next) {
395                 bGPDlayer *gpls, *gpln;
396                 bGPDframe *gpf, *gpfn;
397                 
398                 /* get new layer to put into buffer */
399                 gpls= (bGPDlayer *)ale->data;
400                 gpln= MEM_callocN(sizeof(bGPDlayer), "GPCopyPasteLayer");
401                 
402                 gpln->frames.first= gpln->frames.last= NULL;
403                 strcpy(gpln->info, gpls->info);
404                 
405                 BLI_addtail(&gpcopybuf, gpln);
406                 
407                 /* loop over frames, and copy only selected frames */
408                 for (gpf= gpls->frames.first; gpf; gpf= gpf->next) {
409                         /* if frame is selected, make duplicate it and its strokes */
410                         if (gpf->flag & GP_FRAME_SELECT) {
411                                 /* add frame to buffer */
412                                 gpfn= gpencil_frame_duplicate(gpf);
413                                 BLI_addtail(&gpln->frames, gpfn);
414                                 
415                                 /* check if this is the earliest frame encountered so far */
416                                 if (gpf->framenum < gpcopy_firstframe)
417                                         gpcopy_firstframe= gpf->framenum;
418                         }
419                 }
420         }
421         
422         /* check if anything ended up in the buffer */
423         if (ELEM(NULL, gpcopybuf.first, gpcopybuf.last))
424                 error("Nothing copied to buffer");
425         
426         /* free temp memory */
427         BLI_freelistN(&act_data);
428 }
429
430 void paste_gpdata (Scene *scene)
431 {
432         ListBase act_data = {NULL, NULL};
433         bActListElem *ale;
434         int filter;
435         void *data;
436         short datatype;
437         
438         const int offset = (CFRA - gpcopy_firstframe);
439         short no_name= 0;
440         
441         /* check if buffer is empty */
442         if (ELEM(NULL, gpcopybuf.first, gpcopybuf.last)) {
443                 error("No data in buffer to paste");
444                 return;
445         }
446         /* check if single channel in buffer (disregard names if so)  */
447         if (gpcopybuf.first == gpcopybuf.last)
448                 no_name= 1;
449         
450         /* get data */
451         data= get_action_context(&datatype);
452         if (data == NULL) return;
453         if (datatype != ACTCONT_GPENCIL) return;
454         
455         /* filter data */
456         filter= (ACTFILTER_VISIBLE | ACTFILTER_SEL | ACTFILTER_FOREDIT);
457         actdata_filter(&act_data, filter, data, datatype);
458         
459         /* from selected channels */
460         for (ale= act_data.first; ale; ale= ale->next) {
461                 bGPDlayer *gpld= (bGPDlayer *)ale->data;
462                 bGPDlayer *gpls= NULL;
463                 bGPDframe *gpfs, *gpf;
464                 
465                 /* find suitable layer from buffer to use to paste from */
466                 for (gpls= gpcopybuf.first; gpls; gpls= gpls->next) {
467                         /* check if layer name matches */
468                         if ((no_name) || (strcmp(gpls->info, gpld->info)==0))
469                                 break;
470                 }
471                 
472                 /* this situation might occur! */
473                 if (gpls == NULL)
474                         continue;
475                 
476                 /* add frames from buffer */
477                 for (gpfs= gpls->frames.first; gpfs; gpfs= gpfs->next) {
478                         /* temporarily apply offset to buffer-frame while copying */
479                         gpfs->framenum += offset;
480                         
481                         /* get frame to copy data into (if no frame returned, then just ignore) */
482                         gpf= gpencil_layer_getframe(gpld, gpfs->framenum, 1);
483                         if (gpf) {
484                                 bGPDstroke *gps, *gpsn;
485                                 ScrArea *sa;
486                                 
487                                 /* get area that gp-data comes from */
488                                 //sa= gpencil_data_findowner((bGPdata *)ale->owner);    
489                                 sa = NULL;
490                                 
491                                 /* this should be the right frame... as it may be a pre-existing frame, 
492                                  * must make sure that only compatible stroke types get copied over 
493                                  *      - we cannot just add a duplicate frame, as that would cause errors
494                                  *      - need to check for compatible types to minimise memory usage (copying 'junk' over)
495                                  */
496                                 for (gps= gpfs->strokes.first; gps; gps= gps->next) {
497                                         short stroke_ok;
498                                         
499                                         /* if there's an area, check that it supports this type of stroke */
500                                         if (sa) {
501                                                 stroke_ok= 0;
502                                                 
503                                                 /* check if spacetype supports this type of stroke
504                                                  *      - NOTE: must sync this with gp_paint_initstroke() in gpencil.c
505                                                  */
506                                                 switch (sa->spacetype) {
507                                                         case SPACE_VIEW3D: /* 3D-View: either screen-aligned or 3d-space */
508                                                                 if ((gps->flag == 0) || (gps->flag & GP_STROKE_3DSPACE))
509                                                                         stroke_ok= 1;
510                                                                 break;
511                                                                 
512                                                         case SPACE_NODE: /* Nodes Editor: either screen-aligned or view-aligned */
513                                                         case SPACE_IMAGE: /* Image Editor: either screen-aligned or view\image-aligned */
514                                                                 if ((gps->flag == 0) || (gps->flag & GP_STROKE_2DSPACE))
515                                                                         stroke_ok= 1;
516                                                                 break;
517                                                                 
518                                                         case SPACE_SEQ: /* Sequence Editor: either screen-aligned or view-aligned */
519                                                                 if ((gps->flag == 0) || (gps->flag & GP_STROKE_2DIMAGE))
520                                                                         stroke_ok= 1;
521                                                                 break;
522                                                 }
523                                         }
524                                         else
525                                                 stroke_ok= 1;
526                                         
527                                         /* if stroke is ok, we make a copy of this stroke and add to frame */
528                                         if (stroke_ok) {
529                                                 /* make a copy of stroke, then of its points array */
530                                                 gpsn= MEM_dupallocN(gps);
531                                                 gpsn->points= MEM_dupallocN(gps->points);
532                                                 
533                                                 /* append stroke to frame */
534                                                 BLI_addtail(&gpf->strokes, gpsn);
535                                         }
536                                 }
537                                 
538                                 /* if no strokes (i.e. new frame) added, free gpf */
539                                 if (gpf->strokes.first == NULL)
540                                         gpencil_layer_delframe(gpld, gpf);
541                         }
542                         
543                         /* unapply offset from buffer-frame */
544                         gpfs->framenum -= offset;
545                 }
546         }
547         
548         /* free temp memory */
549         BLI_freelistN(&act_data);
550         
551         /* undo and redraw stuff */
552         BIF_undo_push("Paste Grease Pencil Frames");
553 }
554
555 /* -------------------------------------- */
556 /* Snap Tools */
557
558 static short snap_gpf_nearest (bGPDframe *gpf, Scene *scene)
559 {
560         if (gpf->flag & GP_FRAME_SELECT)
561                 gpf->framenum= (int)(floor(gpf->framenum+0.5));
562         return 0;
563 }
564
565 static short snap_gpf_nearestsec (bGPDframe *gpf, Scene *scene)
566 {
567         float secf = (float)FPS;
568         if (gpf->flag & GP_FRAME_SELECT)
569                 gpf->framenum= (int)(floor(gpf->framenum/secf + 0.5f) * secf);
570         return 0;
571 }
572
573 static short snap_gpf_cframe (bGPDframe *gpf, Scene *scene)
574 {
575         if (gpf->flag & GP_FRAME_SELECT)
576                 gpf->framenum= (int)CFRA;
577         return 0;
578 }
579
580 static short snap_gpf_nearmarker (bGPDframe *gpf, Scene *scene)
581 {
582         if (gpf->flag & GP_FRAME_SELECT)
583                 gpf->framenum= (int)find_nearest_marker_time(&scene->markers, (float)gpf->framenum);
584         return 0;
585 }
586
587
588 /* snap selected frames to ... */
589 void snap_gplayer_frames (bGPDlayer *gpl, Scene *scene, short mode)
590 {
591         switch (mode) {
592                 case 1: /* snap to nearest frame */
593                         gplayer_frames_looper(gpl, scene, snap_gpf_nearest);
594                         break;
595                 case 2: /* snap to current frame */
596                         gplayer_frames_looper(gpl, scene, snap_gpf_cframe);
597                         break;
598                 case 3: /* snap to nearest marker */
599                         gplayer_frames_looper(gpl, scene, snap_gpf_nearmarker);
600                         break;
601                 case 4: /* snap to nearest second */
602                         gplayer_frames_looper(gpl, scene, snap_gpf_nearestsec);
603                         break;
604                 default: /* just in case */
605                         gplayer_frames_looper(gpl, scene, snap_gpf_nearest);
606                         break;
607         }
608 }
609
610 /* -------------------------------------- */
611 /* Mirror Tools */
612
613 static short mirror_gpf_cframe (bGPDframe *gpf, Scene *scene)
614 {
615         int diff;
616         
617         if (gpf->flag & GP_FRAME_SELECT) {
618                 diff= CFRA - gpf->framenum;
619                 gpf->framenum= CFRA;
620         }
621         
622         return 0;
623 }
624
625 static short mirror_gpf_yaxis (bGPDframe *gpf, Scene *scene)
626 {
627         int diff;
628         
629         if (gpf->flag & GP_FRAME_SELECT) {
630                 diff= -gpf->framenum;
631                 gpf->framenum= diff;
632         }
633         
634         return 0;
635 }
636
637 static short mirror_gpf_xaxis (bGPDframe *gpf, Scene *scene)
638 {
639         int diff;
640         
641         if (gpf->flag & GP_FRAME_SELECT) {
642                 diff= -gpf->framenum;
643                 gpf->framenum= diff;
644         }
645         
646         return 0;
647 }
648
649 static short mirror_gpf_marker (bGPDframe *gpf, Scene *scene)
650 {
651         static TimeMarker *marker;
652         static short initialised = 0;
653         int diff;
654         
655         /* In order for this mirror function to work without
656          * any extra arguments being added, we use the case
657          * of bezt==NULL to denote that we should find the 
658          * marker to mirror over. The static pointer is safe
659          * to use this way, as it will be set to null after 
660          * each cycle in which this is called.
661          */
662         
663         if (gpf) {
664                 /* mirroring time */
665                 if ((gpf->flag & GP_FRAME_SELECT) && (marker)) {
666                         diff= (marker->frame - gpf->framenum);
667                         gpf->framenum= (marker->frame + diff);
668                 }
669         }
670         else {
671                 /* initialisation time */
672                 if (initialised) {
673                         /* reset everything for safety */
674                         marker = NULL;
675                         initialised = 0;
676                 }
677                 else {
678                         /* try to find a marker */
679                         marker= ED_markers_get_first_selected(&scene->markers);
680                         if(marker) {
681                                 initialised= 1;
682                         }
683                 }
684         }
685         
686         return 0;
687 }
688
689
690 /* mirror selected gp-frames on... */
691 void mirror_gplayer_frames (bGPDlayer *gpl, Scene *scene, short mode)
692 {
693         switch (mode) {
694                 case 1: /* mirror over current frame */
695                         gplayer_frames_looper(gpl, scene, mirror_gpf_cframe);
696                         break;
697                 case 2: /* mirror over frame 0 */
698                         gplayer_frames_looper(gpl, scene, mirror_gpf_yaxis);
699                         break;
700                 case 3: /* mirror over value 0 */
701                         gplayer_frames_looper(gpl, scene, mirror_gpf_xaxis);
702                         break;
703                 case 4: /* mirror over marker */
704                         mirror_gpf_marker(NULL, NULL);
705                         gplayer_frames_looper(gpl, scene, mirror_gpf_marker);
706                         mirror_gpf_marker(NULL, NULL);
707                         break;
708                 default: /* just in case */
709                         gplayer_frames_looper(gpl, scene, mirror_gpf_yaxis);
710                         break;
711         }
712 }
713
714 /* ***************************************** */
715 #endif // XXX disabled until Grease Pencil code stabilises again...