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