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