1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2008, Blender Foundation
17  * This is a new part of Blender
18  */
19 
20 /** \file
21  * \ingroup edgpencil
22  */
23 
24 #include <math.h>
25 #include <stddef.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 
30 #include "MEM_guardedalloc.h"
31 
32 #include "BLI_blenlib.h"
33 #include "BLI_utildefines.h"
34 
35 #include "DNA_gpencil_types.h"
36 #include "DNA_scene_types.h"
37 
38 #include "BKE_fcurve.h"
39 #include "BKE_gpencil.h"
40 #include "BKE_report.h"
41 
42 #include "ED_anim_api.h"
43 #include "ED_gpencil.h"
44 #include "ED_keyframes_edit.h"
45 #include "ED_markers.h"
46 
47 #include "WM_api.h"
48 
49 /* ***************************************** */
50 /* NOTE ABOUT THIS FILE:
51  * This file contains code for editing Grease Pencil data in the Action Editor
52  * as a 'keyframes', so that a user can adjust the timing of Grease Pencil drawings.
53  * Therefore, this file mostly contains functions for selecting Grease-Pencil frames.
54  */
55 /* ***************************************** */
56 /* Generics - Loopers */
57 
58 /* Loops over the gp-frames for a gp-layer, and applies the given callback */
ED_gpencil_layer_frames_looper(bGPDlayer * gpl,Scene * scene,bool (* gpf_cb)(bGPDframe *,Scene *))59 bool ED_gpencil_layer_frames_looper(bGPDlayer *gpl,
60                                     Scene *scene,
61                                     bool (*gpf_cb)(bGPDframe *, Scene *))
62 {
63   /* error checker */
64   if (gpl == NULL) {
65     return false;
66   }
67 
68   /* do loop */
69   LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
70     /* execute callback */
71     if (gpf_cb(gpf, scene)) {
72       return true;
73     }
74   }
75 
76   /* nothing to return */
77   return false;
78 }
79 
80 /* ****************************************** */
81 /* Data Conversion Tools */
82 
83 /* make a listing all the gp-frames in a layer as cfraelems */
ED_gpencil_layer_make_cfra_list(bGPDlayer * gpl,ListBase * elems,bool onlysel)84 void ED_gpencil_layer_make_cfra_list(bGPDlayer *gpl, ListBase *elems, bool onlysel)
85 {
86   CfraElem *ce;
87 
88   /* error checking */
89   if (ELEM(NULL, gpl, elems)) {
90     return;
91   }
92 
93   /* loop through gp-frames, adding */
94   LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
95     if ((onlysel == 0) || (gpf->flag & GP_FRAME_SELECT)) {
96       ce = MEM_callocN(sizeof(CfraElem), "CfraElem");
97 
98       ce->cfra = (float)gpf->framenum;
99       ce->sel = (gpf->flag & GP_FRAME_SELECT) ? 1 : 0;
100 
101       BLI_addtail(elems, ce);
102     }
103   }
104 }
105 
106 /* ***************************************** */
107 /* Selection Tools */
108 
109 /* check if one of the frames in this layer is selected */
ED_gpencil_layer_frame_select_check(bGPDlayer * gpl)110 bool ED_gpencil_layer_frame_select_check(bGPDlayer *gpl)
111 {
112   /* error checking */
113   if (gpl == NULL) {
114     return false;
115   }
116 
117   /* stop at the first one found */
118   LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
119     if (gpf->flag & GP_FRAME_SELECT) {
120       return true;
121     }
122   }
123 
124   /* not found */
125   return false;
126 }
127 
128 /* helper function - select gp-frame based on SELECT_* mode */
gpencil_frame_select(bGPDframe * gpf,short select_mode)129 static void gpencil_frame_select(bGPDframe *gpf, short select_mode)
130 {
131   if (gpf == NULL) {
132     return;
133   }
134 
135   switch (select_mode) {
136     case SELECT_ADD:
137       gpf->flag |= GP_FRAME_SELECT;
138       break;
139     case SELECT_SUBTRACT:
140       gpf->flag &= ~GP_FRAME_SELECT;
141       break;
142     case SELECT_INVERT:
143       gpf->flag ^= GP_FRAME_SELECT;
144       break;
145   }
146 }
147 
148 /* set all/none/invert select (like above, but with SELECT_* modes) */
ED_gpencil_select_frames(bGPDlayer * gpl,short select_mode)149 void ED_gpencil_select_frames(bGPDlayer *gpl, short select_mode)
150 {
151   /* error checking */
152   if (gpl == NULL) {
153     return;
154   }
155 
156   /* handle according to mode */
157   LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
158     gpencil_frame_select(gpf, select_mode);
159   }
160 }
161 
162 /* set all/none/invert select */
ED_gpencil_layer_frame_select_set(bGPDlayer * gpl,short mode)163 void ED_gpencil_layer_frame_select_set(bGPDlayer *gpl, short mode)
164 {
165   /* error checking */
166   if (gpl == NULL) {
167     return;
168   }
169 
170   /* now call the standard function */
171   ED_gpencil_select_frames(gpl, mode);
172 }
173 
174 /* select the frame in this layer that occurs on this frame (there should only be one at most) */
ED_gpencil_select_frame(bGPDlayer * gpl,int selx,short select_mode)175 void ED_gpencil_select_frame(bGPDlayer *gpl, int selx, short select_mode)
176 {
177   bGPDframe *gpf;
178 
179   if (gpl == NULL) {
180     return;
181   }
182 
183   gpf = BKE_gpencil_layer_frame_find(gpl, selx);
184 
185   if (gpf) {
186     gpencil_frame_select(gpf, select_mode);
187   }
188 }
189 
190 /* select the frames in this layer that occur within the bounds specified */
ED_gpencil_layer_frames_select_box(bGPDlayer * gpl,float min,float max,short select_mode)191 void ED_gpencil_layer_frames_select_box(bGPDlayer *gpl, float min, float max, short select_mode)
192 {
193   if (gpl == NULL) {
194     return;
195   }
196 
197   /* only select those frames which are in bounds */
198   LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
199     if (IN_RANGE(gpf->framenum, min, max)) {
200       gpencil_frame_select(gpf, select_mode);
201     }
202   }
203 }
204 
205 /* select the frames in this layer that occur within the lasso/circle region specified */
ED_gpencil_layer_frames_select_region(KeyframeEditData * ked,bGPDlayer * gpl,short tool,short select_mode)206 void ED_gpencil_layer_frames_select_region(KeyframeEditData *ked,
207                                            bGPDlayer *gpl,
208                                            short tool,
209                                            short select_mode)
210 {
211   if (gpl == NULL) {
212     return;
213   }
214 
215   /* only select frames which are within the region */
216   LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
217     /* construct a dummy point coordinate to do this testing with */
218     float pt[2] = {0};
219 
220     pt[0] = gpf->framenum;
221     pt[1] = ked->channel_y;
222 
223     /* check the necessary regions */
224     if (tool == BEZT_OK_CHANNEL_LASSO) {
225       /* Lasso */
226       if (keyframe_region_lasso_test(ked->data, pt)) {
227         gpencil_frame_select(gpf, select_mode);
228       }
229     }
230     else if (tool == BEZT_OK_CHANNEL_CIRCLE) {
231       /* Circle */
232       if (keyframe_region_circle_test(ked->data, pt)) {
233         gpencil_frame_select(gpf, select_mode);
234       }
235     }
236   }
237 }
238 
239 /* ***************************************** */
240 /* Frame Editing Tools */
241 
242 /* Delete selected frames */
ED_gpencil_layer_frames_delete(bGPDlayer * gpl)243 bool ED_gpencil_layer_frames_delete(bGPDlayer *gpl)
244 {
245   bool changed = false;
246 
247   /* error checking */
248   if (gpl == NULL) {
249     return false;
250   }
251 
252   /* check for frames to delete */
253   LISTBASE_FOREACH_MUTABLE (bGPDframe *, gpf, &gpl->frames) {
254     if (gpf->flag & GP_FRAME_SELECT) {
255       BKE_gpencil_layer_frame_delete(gpl, gpf);
256       changed = true;
257     }
258   }
259 
260   return changed;
261 }
262 
263 /* Duplicate selected frames from given gp-layer */
ED_gpencil_layer_frames_duplicate(bGPDlayer * gpl)264 void ED_gpencil_layer_frames_duplicate(bGPDlayer *gpl)
265 {
266   /* error checking */
267   if (gpl == NULL) {
268     return;
269   }
270 
271   /* duplicate selected frames  */
272   LISTBASE_FOREACH_MUTABLE (bGPDframe *, gpf, &gpl->frames) {
273 
274     /* duplicate this frame */
275     if (gpf->flag & GP_FRAME_SELECT) {
276       bGPDframe *gpfd;
277 
278       /* duplicate frame, and deselect self */
279       gpfd = BKE_gpencil_frame_duplicate(gpf);
280       gpf->flag &= ~GP_FRAME_SELECT;
281 
282       BLI_insertlinkafter(&gpl->frames, gpf, gpfd);
283     }
284   }
285 }
286 
287 /**
288  * Set keyframe type for selected frames from given gp-layer
289  *
290  * \param type: The type of keyframe (#eBezTriple_KeyframeType) to set selected frames to.
291  */
ED_gpencil_layer_frames_keytype_set(bGPDlayer * gpl,short type)292 void ED_gpencil_layer_frames_keytype_set(bGPDlayer *gpl, short type)
293 {
294   if (gpl == NULL) {
295     return;
296   }
297 
298   LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
299     if (gpf->flag & GP_FRAME_SELECT) {
300       gpf->key_type = type;
301     }
302   }
303 }
304 
305 /* -------------------------------------- */
306 /* Copy and Paste Tools:
307  * - The copy/paste buffer currently stores a set of GP_Layers, with temporary
308  *   GP_Frames with the necessary strokes
309  * - Unless there is only one element in the buffer,
310  *   names are also tested to check for compatibility.
311  * - All pasted frames are offset by the same amount.
312  *   This is calculated as the difference in the times of the current frame and the
313  *   'first keyframe' (i.e. the earliest one in all channels).
314  * - The earliest frame is calculated per copy operation.
315  */
316 
317 /* globals for copy/paste data (like for other copy/paste buffers) */
318 static ListBase gpencil_anim_copybuf = {NULL, NULL};
319 static int gpencil_anim_copy_firstframe = 999999999;
320 static int gpencil_anim_copy_lastframe = -999999999;
321 static int gpencil_anim_copy_cfra = 0;
322 
323 /* This function frees any MEM_calloc'ed copy/paste buffer data */
ED_gpencil_anim_copybuf_free(void)324 void ED_gpencil_anim_copybuf_free(void)
325 {
326   BKE_gpencil_free_layers(&gpencil_anim_copybuf);
327   BLI_listbase_clear(&gpencil_anim_copybuf);
328 
329   gpencil_anim_copy_firstframe = 999999999;
330   gpencil_anim_copy_lastframe = -999999999;
331   gpencil_anim_copy_cfra = 0;
332 }
333 
334 /* This function adds data to the copy/paste buffer, freeing existing data first
335  * Only the selected GP-layers get their selected keyframes copied.
336  *
337  * Returns whether the copy operation was successful or not
338  */
ED_gpencil_anim_copybuf_copy(bAnimContext * ac)339 bool ED_gpencil_anim_copybuf_copy(bAnimContext *ac)
340 {
341   ListBase anim_data = {NULL, NULL};
342   bAnimListElem *ale;
343   int filter;
344 
345   Scene *scene = ac->scene;
346 
347   /* clear buffer first */
348   ED_gpencil_anim_copybuf_free();
349 
350   /* filter data */
351   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS);
352   ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
353 
354   /* assume that each of these is a GP layer */
355   for (ale = anim_data.first; ale; ale = ale->next) {
356     ListBase copied_frames = {NULL, NULL};
357     bGPDlayer *gpl = (bGPDlayer *)ale->data;
358 
359     /* loop over frames, and copy only selected frames */
360     LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
361       /* if frame is selected, make duplicate it and its strokes */
362       if (gpf->flag & GP_FRAME_SELECT) {
363         /* make a copy of this frame */
364         bGPDframe *new_frame = BKE_gpencil_frame_duplicate(gpf);
365         BLI_addtail(&copied_frames, new_frame);
366 
367         /* extend extents for keyframes encountered */
368         if (gpf->framenum < gpencil_anim_copy_firstframe) {
369           gpencil_anim_copy_firstframe = gpf->framenum;
370         }
371         if (gpf->framenum > gpencil_anim_copy_lastframe) {
372           gpencil_anim_copy_lastframe = gpf->framenum;
373         }
374       }
375     }
376 
377     /* create a new layer in buffer if there were keyframes here */
378     if (BLI_listbase_is_empty(&copied_frames) == false) {
379       bGPDlayer *new_layer = MEM_callocN(sizeof(bGPDlayer), "GPCopyPasteLayer");
380       BLI_addtail(&gpencil_anim_copybuf, new_layer);
381 
382       /* move over copied frames */
383       BLI_movelisttolist(&new_layer->frames, &copied_frames);
384       BLI_assert(copied_frames.first == NULL);
385 
386       /* make a copy of the layer's name - for name-based matching later... */
387       BLI_strncpy(new_layer->info, gpl->info, sizeof(new_layer->info));
388     }
389   }
390 
391   /* in case 'relative' paste method is used */
392   gpencil_anim_copy_cfra = CFRA;
393 
394   /* clean up */
395   ANIM_animdata_freelist(&anim_data);
396 
397   /* check if anything ended up in the buffer */
398   if (ELEM(NULL, gpencil_anim_copybuf.first, gpencil_anim_copybuf.last)) {
399     BKE_report(ac->reports, RPT_ERROR, "No keyframes copied to keyframes copy/paste buffer");
400     return false;
401   }
402 
403   /* report success */
404   return true;
405 }
406 
407 /* Pastes keyframes from buffer, and reports success */
ED_gpencil_anim_copybuf_paste(bAnimContext * ac,const short offset_mode)408 bool ED_gpencil_anim_copybuf_paste(bAnimContext *ac, const short offset_mode)
409 {
410   ListBase anim_data = {NULL, NULL};
411   bAnimListElem *ale;
412   int filter;
413 
414   Scene *scene = ac->scene;
415   bool no_name = false;
416   int offset = 0;
417 
418   /* check if buffer is empty */
419   if (BLI_listbase_is_empty(&gpencil_anim_copybuf)) {
420     BKE_report(ac->reports, RPT_ERROR, "No data in buffer to paste");
421     return false;
422   }
423 
424   /* check if single channel in buffer (disregard names if so)  */
425   if (gpencil_anim_copybuf.first == gpencil_anim_copybuf.last) {
426     no_name = true;
427   }
428 
429   /* methods of offset (eKeyPasteOffset) */
430   switch (offset_mode) {
431     case KEYFRAME_PASTE_OFFSET_CFRA_START:
432       offset = (CFRA - gpencil_anim_copy_firstframe);
433       break;
434     case KEYFRAME_PASTE_OFFSET_CFRA_END:
435       offset = (CFRA - gpencil_anim_copy_lastframe);
436       break;
437     case KEYFRAME_PASTE_OFFSET_CFRA_RELATIVE:
438       offset = (CFRA - gpencil_anim_copy_cfra);
439       break;
440     case KEYFRAME_PASTE_OFFSET_NONE:
441       offset = 0;
442       break;
443   }
444 
445   /* filter data */
446   /* TODO: try doing it with selection, then without selection limits. */
447   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL |
448             ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
449   ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
450 
451   /* from selected channels */
452   for (ale = anim_data.first; ale; ale = ale->next) {
453     bGPDlayer *gpld = (bGPDlayer *)ale->data;
454     bGPDlayer *gpls = NULL;
455     bGPDframe *gpfs, *gpf;
456 
457     /* find suitable layer from buffer to use to paste from */
458     for (gpls = gpencil_anim_copybuf.first; gpls; gpls = gpls->next) {
459       /* check if layer name matches */
460       if ((no_name) || STREQ(gpls->info, gpld->info)) {
461         break;
462       }
463     }
464 
465     /* this situation might occur! */
466     if (gpls == NULL) {
467       continue;
468     }
469 
470     /* add frames from buffer */
471     for (gpfs = gpls->frames.first; gpfs; gpfs = gpfs->next) {
472       /* temporarily apply offset to buffer-frame while copying */
473       gpfs->framenum += offset;
474 
475       /* get frame to copy data into (if no frame returned, then just ignore) */
476       gpf = BKE_gpencil_layer_frame_get(gpld, gpfs->framenum, GP_GETFRAME_ADD_NEW);
477       if (gpf) {
478         bGPDstroke *gps, *gpsn;
479 
480         /* This should be the right frame... as it may be a pre-existing frame,
481          * must make sure that only compatible stroke types get copied over
482          * - We cannot just add a duplicate frame, as that would cause errors
483          * - For now, we don't check if the types will be compatible since we
484          *   don't have enough info to do so. Instead, we simply just paste,
485          *   if it works, it will show up.
486          */
487         for (gps = gpfs->strokes.first; gps; gps = gps->next) {
488           /* make a copy of stroke, then of its points array */
489           gpsn = BKE_gpencil_stroke_duplicate(gps, true);
490 
491           /* append stroke to frame */
492           BLI_addtail(&gpf->strokes, gpsn);
493         }
494 
495         /* if no strokes (i.e. new frame) added, free gpf */
496         if (BLI_listbase_is_empty(&gpf->strokes)) {
497           BKE_gpencil_layer_frame_delete(gpld, gpf);
498         }
499       }
500 
501       /* unapply offset from buffer-frame */
502       gpfs->framenum -= offset;
503     }
504   }
505 
506   /* clean up */
507   ANIM_animdata_freelist(&anim_data);
508   return true;
509 }
510 
511 /* -------------------------------------- */
512 /* Snap Tools */
513 
gpencil_frame_snap_nearest(bGPDframe * UNUSED (gpf),Scene * UNUSED (scene))514 static bool gpencil_frame_snap_nearest(bGPDframe *UNUSED(gpf), Scene *UNUSED(scene))
515 {
516 #if 0 /* note: gpf->framenum is already an int! */
517   if (gpf->flag & GP_FRAME_SELECT) {
518     gpf->framenum = (int)(floor(gpf->framenum + 0.5));
519   }
520 #endif
521   return false;
522 }
523 
gpencil_frame_snap_nearestsec(bGPDframe * gpf,Scene * scene)524 static bool gpencil_frame_snap_nearestsec(bGPDframe *gpf, Scene *scene)
525 {
526   float secf = (float)FPS;
527   if (gpf->flag & GP_FRAME_SELECT) {
528     gpf->framenum = (int)(floorf(gpf->framenum / secf + 0.5f) * secf);
529   }
530   return false;
531 }
532 
gpencil_frame_snap_cframe(bGPDframe * gpf,Scene * scene)533 static bool gpencil_frame_snap_cframe(bGPDframe *gpf, Scene *scene)
534 {
535   if (gpf->flag & GP_FRAME_SELECT) {
536     gpf->framenum = (int)CFRA;
537   }
538   return false;
539 }
540 
gpencil_frame_snap_nearmarker(bGPDframe * gpf,Scene * scene)541 static bool gpencil_frame_snap_nearmarker(bGPDframe *gpf, Scene *scene)
542 {
543   if (gpf->flag & GP_FRAME_SELECT) {
544     gpf->framenum = (int)ED_markers_find_nearest_marker_time(&scene->markers,
545                                                              (float)gpf->framenum);
546   }
547   return false;
548 }
549 
550 /* snap selected frames to ... */
ED_gpencil_layer_snap_frames(bGPDlayer * gpl,Scene * scene,short mode)551 void ED_gpencil_layer_snap_frames(bGPDlayer *gpl, Scene *scene, short mode)
552 {
553   switch (mode) {
554     case SNAP_KEYS_NEARFRAME: /* snap to nearest frame */
555       ED_gpencil_layer_frames_looper(gpl, scene, gpencil_frame_snap_nearest);
556       break;
557     case SNAP_KEYS_CURFRAME: /* snap to current frame */
558       ED_gpencil_layer_frames_looper(gpl, scene, gpencil_frame_snap_cframe);
559       break;
560     case SNAP_KEYS_NEARMARKER: /* snap to nearest marker */
561       ED_gpencil_layer_frames_looper(gpl, scene, gpencil_frame_snap_nearmarker);
562       break;
563     case SNAP_KEYS_NEARSEC: /* snap to nearest second */
564       ED_gpencil_layer_frames_looper(gpl, scene, gpencil_frame_snap_nearestsec);
565       break;
566     default: /* just in case */
567       break;
568   }
569 }
570 
571 /* -------------------------------------- */
572 /* Mirror Tools */
573 
gpencil_frame_mirror_cframe(bGPDframe * gpf,Scene * scene)574 static bool gpencil_frame_mirror_cframe(bGPDframe *gpf, Scene *scene)
575 {
576   int diff;
577 
578   if (gpf->flag & GP_FRAME_SELECT) {
579     diff = CFRA - gpf->framenum;
580     gpf->framenum = CFRA + diff;
581   }
582 
583   return false;
584 }
585 
gpencil_frame_mirror_yaxis(bGPDframe * gpf,Scene * UNUSED (scene))586 static bool gpencil_frame_mirror_yaxis(bGPDframe *gpf, Scene *UNUSED(scene))
587 {
588   int diff;
589 
590   if (gpf->flag & GP_FRAME_SELECT) {
591     diff = -gpf->framenum;
592     gpf->framenum = diff;
593   }
594 
595   return false;
596 }
597 
gpencil_frame_mirror_xaxis(bGPDframe * gpf,Scene * UNUSED (scene))598 static bool gpencil_frame_mirror_xaxis(bGPDframe *gpf, Scene *UNUSED(scene))
599 {
600   int diff;
601 
602   /* NOTE: since we can't really do this, we just do the same as for yaxis... */
603   if (gpf->flag & GP_FRAME_SELECT) {
604     diff = -gpf->framenum;
605     gpf->framenum = diff;
606   }
607 
608   return false;
609 }
610 
gpencil_frame_mirror_marker(bGPDframe * gpf,Scene * scene)611 static bool gpencil_frame_mirror_marker(bGPDframe *gpf, Scene *scene)
612 {
613   static TimeMarker *marker;
614   static short initialized = 0;
615   int diff;
616 
617   /* In order for this mirror function to work without
618    * any extra arguments being added, we use the case
619    * of gpf==NULL to denote that we should find the
620    * marker to mirror over. The static pointer is safe
621    * to use this way, as it will be set to null after
622    * each cycle in which this is called.
623    */
624 
625   if (gpf != NULL) {
626     /* mirroring time */
627     if ((gpf->flag & GP_FRAME_SELECT) && (marker)) {
628       diff = (marker->frame - gpf->framenum);
629       gpf->framenum = (marker->frame + diff);
630     }
631   }
632   else {
633     /* initialization time */
634     if (initialized) {
635       /* reset everything for safety */
636       marker = NULL;
637       initialized = 0;
638     }
639     else {
640       /* try to find a marker */
641       marker = ED_markers_get_first_selected(&scene->markers);
642       if (marker) {
643         initialized = 1;
644       }
645     }
646   }
647 
648   return false;
649 }
650 
651 /* mirror selected gp-frames on... */
652 /* TODO: mirror over a specific time */
ED_gpencil_layer_mirror_frames(bGPDlayer * gpl,Scene * scene,short mode)653 void ED_gpencil_layer_mirror_frames(bGPDlayer *gpl, Scene *scene, short mode)
654 {
655   switch (mode) {
656     case MIRROR_KEYS_CURFRAME: /* mirror over current frame */
657       ED_gpencil_layer_frames_looper(gpl, scene, gpencil_frame_mirror_cframe);
658       break;
659     case MIRROR_KEYS_YAXIS: /* mirror over frame 0 */
660       ED_gpencil_layer_frames_looper(gpl, scene, gpencil_frame_mirror_yaxis);
661       break;
662     case MIRROR_KEYS_XAXIS: /* mirror over value 0 */
663       ED_gpencil_layer_frames_looper(gpl, scene, gpencil_frame_mirror_xaxis);
664       break;
665     case MIRROR_KEYS_MARKER: /* mirror over marker */
666       gpencil_frame_mirror_marker(NULL, scene);
667       ED_gpencil_layer_frames_looper(gpl, scene, gpencil_frame_mirror_marker);
668       gpencil_frame_mirror_marker(NULL, scene);
669       break;
670     default: /* just in case */
671       ED_gpencil_layer_frames_looper(gpl, scene, gpencil_frame_mirror_yaxis);
672       break;
673   }
674 }
675 
676 /* ***************************************** */
677