doxygen: add newline after \file
[blender.git] / source / blender / editors / animation / anim_draw.c
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  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup edanimation
22  */
23
24 #include "BLI_sys_types.h"
25
26 #include "DNA_anim_types.h"
27 #include "DNA_scene_types.h"
28 #include "DNA_space_types.h"
29 #include "DNA_userdef_types.h"
30 #include "DNA_screen_types.h"
31 #include "DNA_object_types.h"
32 #include "DNA_gpencil_types.h"
33 #include "DNA_mask_types.h"
34
35 #include "BLI_math.h"
36 #include "BLI_timecode.h"
37 #include "BLI_utildefines.h"
38 #include "BLI_rect.h"
39 #include "BLI_dlrbTree.h"
40
41 #include "BKE_context.h"
42 #include "BKE_curve.h"
43 #include "BKE_fcurve.h"
44 #include "BKE_global.h"
45 #include "BKE_nla.h"
46 #include "BKE_mask.h"
47
48 #include "ED_anim_api.h"
49 #include "ED_keyframes_edit.h"
50 #include "ED_keyframes_draw.h"
51
52 #include "RNA_access.h"
53
54 #include "UI_interface.h"
55 #include "UI_resources.h"
56 #include "UI_view2d.h"
57
58 #include "GPU_immediate.h"
59 #include "GPU_matrix.h"
60 #include "GPU_state.h"
61
62 /* *************************************************** */
63 /* CURRENT FRAME DRAWING */
64
65 /* Draw current frame number in a little green box beside the current frame indicator */
66 void ANIM_draw_cfra_number(const bContext *C, View2D *v2d, short flag)
67 {
68         Scene *scene = CTX_data_scene(C);
69         const float time = scene->r.cfra + scene->r.subframe;
70         const float cfra = (float)(time * scene->r.framelen);
71         const bool show_time = (flag & DRAWCFRA_UNIT_SECONDS) != 0;
72
73         const uiFontStyle *fstyle = UI_FSTYLE_WIDGET;
74         unsigned char col[4];
75         float color[4];
76         float xscale, x, y;
77         char numstr[32] = "  t  ";  /* t is the character to start replacing from */
78         float hlen;
79         int slen;
80
81         /* because the frame number text is subject to the same scaling as the contents of the view */
82         UI_view2d_scale_get(v2d, &xscale, NULL);
83         GPU_matrix_push();
84         GPU_matrix_scale_2f(1.0f / xscale, 1.0f);
85
86         /* get timecode string
87          * - padding on str-buf passed so that it doesn't sit on the frame indicator
88          */
89         if (show_time) {
90                 BLI_timecode_string_from_time(&numstr[2], sizeof(numstr) - 2, 0, FRA2TIME(cfra), FPS, U.timecode_style);
91         }
92         else {
93                 BLI_timecode_string_from_time_seconds(&numstr[2], sizeof(numstr) - 2, 1, cfra);
94         }
95
96         slen = UI_fontstyle_string_width(fstyle, numstr) - 1;
97         hlen = slen * 0.5f;
98
99         /* get starting coordinates for drawing */
100         x = cfra * xscale;
101         y = -0.1f * U.widget_unit;
102
103         /* draw green box around/behind text */
104         UI_GetThemeColor4fv(TH_CFRAME, color);
105         color[3] = 3.0f;
106
107         UI_draw_roundbox_corner_set(UI_CNR_ALL);
108         UI_draw_roundbox_aa(true,
109                             x - hlen - 0.1f * U.widget_unit,
110                             y + 3.0f,
111                             x + hlen + 0.1f * U.widget_unit,
112                             y -3.0f + U.widget_unit,
113                             0.1f * U.widget_unit,
114                             color);
115
116         /* draw current frame number */
117         UI_GetThemeColor4ubv(TH_TEXT_HI, col);
118         UI_fontstyle_draw_simple(fstyle,
119                                  x - hlen - 0.15f * U.widget_unit,
120                                  y        + 0.28f * U.widget_unit,
121                                  numstr, col);
122
123         /* restore view transform */
124         GPU_matrix_pop();
125 }
126
127 /* General call for drawing current frame indicator in animation editor */
128 void ANIM_draw_cfra(const bContext *C, View2D *v2d, short flag)
129 {
130         Scene *scene = CTX_data_scene(C);
131
132         const float time = scene->r.cfra + scene->r.subframe;
133         const float x = (float)(time * scene->r.framelen);
134
135         GPU_line_width((flag & DRAWCFRA_WIDE) ? 3.0 : 2.0);
136
137         GPUVertFormat *format = immVertexFormat();
138         uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
139
140         immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
141
142         /* Draw a light green line to indicate current frame */
143         immUniformThemeColor(TH_CFRAME);
144
145         immBegin(GPU_PRIM_LINES, 2);
146         immVertex2f(pos, x, v2d->cur.ymin - 500.0f); /* XXX arbitrary... want it go to bottom */
147         immVertex2f(pos, x, v2d->cur.ymax);
148         immEnd();
149         immUnbindProgram();
150 }
151
152 /* *************************************************** */
153 /* PREVIEW RANGE 'CURTAINS' */
154 /* Note: 'Preview Range' tools are defined in anim_ops.c */
155
156 /* Draw preview range 'curtains' for highlighting where the animation data is */
157 void ANIM_draw_previewrange(const bContext *C, View2D *v2d, int end_frame_width)
158 {
159         Scene *scene = CTX_data_scene(C);
160
161         /* only draw this if preview range is set */
162         if (PRVRANGEON) {
163                 GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
164                 GPU_blend(true);
165
166                 GPUVertFormat *format = immVertexFormat();
167                 uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
168
169                 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
170                 immUniformThemeColorShadeAlpha(TH_ANIM_PREVIEW_RANGE, -25, -30);
171                 /* XXX: Fix this hardcoded color (anim_active) */
172                 //immUniformColor4f(0.8f, 0.44f, 0.1f, 0.2f);
173
174                 /* only draw two separate 'curtains' if there's no overlap between them */
175                 if (PSFRA < PEFRA + end_frame_width) {
176                         immRectf(pos, v2d->cur.xmin, v2d->cur.ymin, (float)PSFRA, v2d->cur.ymax);
177                         immRectf(pos, (float)(PEFRA + end_frame_width), v2d->cur.ymin, v2d->cur.xmax, v2d->cur.ymax);
178                 }
179                 else {
180                         immRectf(pos, v2d->cur.xmin, v2d->cur.ymin, v2d->cur.xmax, v2d->cur.ymax);
181                 }
182
183                 immUnbindProgram();
184
185                 GPU_blend(false);
186         }
187 }
188
189 /* *************************************************** */
190 /* SCENE FRAME RANGE */
191
192 /* Draw frame range guides (for scene frame range) in background */
193 // TODO: Should we still show these when preview range is enabled?
194 void ANIM_draw_framerange(Scene *scene, View2D *v2d)
195 {
196         /* draw darkened area outside of active timeline frame range */
197         GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
198         GPU_blend(true);
199
200         GPUVertFormat *format = immVertexFormat();
201         uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
202
203         immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
204         immUniformThemeColorShadeAlpha(TH_BACK, -25, -100);
205
206         if (SFRA < EFRA) {
207                 immRectf(pos, v2d->cur.xmin, v2d->cur.ymin, (float)SFRA, v2d->cur.ymax);
208                 immRectf(pos, (float)EFRA, v2d->cur.ymin, v2d->cur.xmax, v2d->cur.ymax);
209         }
210         else {
211                 immRectf(pos, v2d->cur.xmin, v2d->cur.ymin, v2d->cur.xmax, v2d->cur.ymax);
212         }
213
214         GPU_blend(false);
215
216         /* thin lines where the actual frames are */
217         immUniformThemeColorShade(TH_BACK, -60);
218
219         immBegin(GPU_PRIM_LINES, 4);
220
221         immVertex2f(pos, (float)SFRA, v2d->cur.ymin);
222         immVertex2f(pos, (float)SFRA, v2d->cur.ymax);
223
224         immVertex2f(pos, (float)EFRA, v2d->cur.ymin);
225         immVertex2f(pos, (float)EFRA, v2d->cur.ymax);
226
227         immEnd();
228         immUnbindProgram();
229 }
230
231 /* *************************************************** */
232 /* NLA-MAPPING UTILITIES (required for drawing and also editing keyframes)  */
233
234 /* Obtain the AnimData block providing NLA-mapping for the given channel (if applicable) */
235 // TODO: do not supply return this if the animdata tells us that there is no mapping to perform
236 AnimData *ANIM_nla_mapping_get(bAnimContext *ac, bAnimListElem *ale)
237 {
238         /* sanity checks */
239         if (ac == NULL)
240                 return NULL;
241
242         /* abort if rendering - we may get some race condition issues... */
243         if (G.is_rendering) return NULL;
244
245         /* apart from strictly keyframe-related contexts, this shouldn't even happen */
246         // XXX: nla and channel here may not be necessary...
247         if (ELEM(ac->datatype, ANIMCONT_ACTION, ANIMCONT_SHAPEKEY, ANIMCONT_DOPESHEET,
248                                ANIMCONT_FCURVES, ANIMCONT_NLA, ANIMCONT_CHANNEL))
249         {
250                 /* handling depends on the type of animation-context we've got */
251                 if (ale) {
252                         /* NLA Control Curves occur on NLA strips, and shouldn't be subjected to this kind of mapping */
253                         if (ale->type != ANIMTYPE_NLACURVE)
254                                 return ale->adt;
255                 }
256         }
257
258         /* cannot handle... */
259         return NULL;
260 }
261
262 /* ------------------- */
263
264 /* helper function for ANIM_nla_mapping_apply_fcurve() -> "restore", i.e. mapping points back to action-time */
265 static short bezt_nlamapping_restore(KeyframeEditData *ked, BezTriple *bezt)
266 {
267         /* AnimData block providing scaling is stored in 'data', only_keys option is stored in i1 */
268         AnimData *adt = (AnimData *)ked->data;
269         short only_keys = (short)ked->i1;
270
271         /* adjust BezTriple handles only if allowed to */
272         if (only_keys == 0) {
273                 bezt->vec[0][0] = BKE_nla_tweakedit_remap(adt, bezt->vec[0][0], NLATIME_CONVERT_UNMAP);
274                 bezt->vec[2][0] = BKE_nla_tweakedit_remap(adt, bezt->vec[2][0], NLATIME_CONVERT_UNMAP);
275         }
276
277         bezt->vec[1][0] = BKE_nla_tweakedit_remap(adt, bezt->vec[1][0], NLATIME_CONVERT_UNMAP);
278
279         return 0;
280 }
281
282 /* helper function for ANIM_nla_mapping_apply_fcurve() -> "apply",
283  * i.e. mapping points to NLA-mapped global time */
284 static short bezt_nlamapping_apply(KeyframeEditData *ked, BezTriple *bezt)
285 {
286         /* AnimData block providing scaling is stored in 'data', only_keys option is stored in i1 */
287         AnimData *adt = (AnimData *)ked->data;
288         short only_keys = (short)ked->i1;
289
290         /* adjust BezTriple handles only if allowed to */
291         if (only_keys == 0) {
292                 bezt->vec[0][0] = BKE_nla_tweakedit_remap(adt, bezt->vec[0][0], NLATIME_CONVERT_MAP);
293                 bezt->vec[2][0] = BKE_nla_tweakedit_remap(adt, bezt->vec[2][0], NLATIME_CONVERT_MAP);
294         }
295
296         bezt->vec[1][0] = BKE_nla_tweakedit_remap(adt, bezt->vec[1][0], NLATIME_CONVERT_MAP);
297
298         return 0;
299 }
300
301
302 /* Apply/Unapply NLA mapping to all keyframes in the nominated F-Curve
303  * - restore = whether to map points back to non-mapped time
304  * - only_keys = whether to only adjust the location of the center point of beztriples
305  */
306 void ANIM_nla_mapping_apply_fcurve(AnimData *adt, FCurve *fcu, bool restore, bool only_keys)
307 {
308         KeyframeEditData ked = {{NULL}};
309         KeyframeEditFunc map_cb;
310
311         /* init edit data
312          * - AnimData is stored in 'data'
313          * - only_keys is stored in 'i1'
314          */
315         ked.data = (void *)adt;
316         ked.i1 = (int)only_keys;
317
318         /* get editing callback */
319         if (restore)
320                 map_cb = bezt_nlamapping_restore;
321         else
322                 map_cb = bezt_nlamapping_apply;
323
324         /* apply to F-Curve */
325         ANIM_fcurve_keyframes_loop(&ked, fcu, NULL, map_cb, NULL);
326 }
327
328 /* *************************************************** */
329 /* UNITS CONVERSION MAPPING (required for drawing and editing keyframes) */
330
331 /* Get flags used for normalization in ANIM_unit_mapping_get_factor. */
332 short ANIM_get_normalization_flags(bAnimContext *ac)
333 {
334         if (ac->sl->spacetype == SPACE_GRAPH) {
335                 SpaceGraph *sipo = (SpaceGraph *) ac->sl;
336                 bool use_normalization = (sipo->flag & SIPO_NORMALIZE) != 0;
337                 bool freeze_normalization = (sipo->flag & SIPO_NORMALIZE_FREEZE) != 0;
338                 return use_normalization
339                     ? (ANIM_UNITCONV_NORMALIZE |  (freeze_normalization ? ANIM_UNITCONV_NORMALIZE_FREEZE : 0))
340                     : 0;
341         }
342
343         return 0;
344 }
345
346 static float normalization_factor_get(Scene *scene, FCurve *fcu, short flag, float *r_offset)
347 {
348         float factor = 1.0f, offset = 0.0f;
349
350         if (flag & ANIM_UNITCONV_RESTORE) {
351                 if (r_offset)
352                         *r_offset = fcu->prev_offset;
353
354                 return 1.0f / fcu->prev_norm_factor;
355         }
356
357         if (flag & ANIM_UNITCONV_NORMALIZE_FREEZE) {
358                 if (r_offset)
359                         *r_offset = fcu->prev_offset;
360                 if (fcu->prev_norm_factor == 0.0f) {
361                         /* Happens when Auto Normalize was disabled before
362                          * any curves were displayed.
363                          */
364                         return 1.0f;
365                 }
366                 return fcu->prev_norm_factor;
367         }
368
369         if (G.moving & G_TRANSFORM_FCURVES) {
370                 if (r_offset)
371                         *r_offset = fcu->prev_offset;
372                 if (fcu->prev_norm_factor == 0.0f) {
373                         /* Same as above. */
374                         return 1.0f;
375                 }
376                 return fcu->prev_norm_factor;
377         }
378
379         fcu->prev_norm_factor = 1.0f;
380         if (fcu->bezt) {
381                 const bool use_preview_only = PRVRANGEON;
382                 const BezTriple *bezt;
383                 int i;
384                 float max_coord = -FLT_MAX;
385                 float min_coord = FLT_MAX;
386                 float range;
387
388                 if (fcu->totvert < 1) {
389                         return 1.0f;
390                 }
391
392                 for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
393                         if (use_preview_only && !IN_RANGE_INCL(bezt->vec[1][0],
394                                                                scene->r.psfra,
395                                                                scene->r.pefra))
396                         {
397                                 continue;
398                         }
399
400                         if (i == 0) {
401                                 /* We ignore extrapolation flags and handle here, and use the
402                                  * control point position only. so we normalize "interesting"
403                                  * part of the curve.
404                                  *
405                                  * Here we handle left extrapolation.
406                                  */
407                                 max_coord = max_ff(max_coord, bezt->vec[1][1]);
408
409                                 min_coord = min_ff(min_coord, bezt->vec[1][1]);
410                         }
411                         else {
412                                 const BezTriple *prev_bezt = bezt - 1;
413                                 if (prev_bezt->ipo == BEZT_IPO_CONST) {
414                                         /* Constant interpolation: previous CV value is used up
415                                          * to the current keyframe.
416                                          */
417                                         max_coord = max_ff(max_coord, bezt->vec[1][1]);
418                                         min_coord = min_ff(min_coord, bezt->vec[1][1]);
419                                 }
420                                 else if (prev_bezt->ipo == BEZT_IPO_LIN) {
421                                         /* Linear interpolation: min/max using both previous and
422                                          * and current CV.
423                                          */
424                                         max_coord = max_ff(max_coord, bezt->vec[1][1]);
425                                         min_coord = min_ff(min_coord, bezt->vec[1][1]);
426                                         max_coord = max_ff(max_coord, prev_bezt->vec[1][1]);
427                                         min_coord = min_ff(min_coord, prev_bezt->vec[1][1]);
428                                 }
429                                 else if (prev_bezt->ipo == BEZT_IPO_BEZ) {
430                                         const int resol = fcu->driver
431                                                 ? 32
432                                                 : min_ii((int)(5.0f * len_v2v2(bezt->vec[1], prev_bezt->vec[1])), 32);
433                                         if (resol < 2) {
434                                                 max_coord = max_ff(max_coord, prev_bezt->vec[1][1]);
435                                                 min_coord = min_ff(min_coord, prev_bezt->vec[1][1]);
436                                         }
437                                         else {
438                                                 float data[120];
439                                                 float v1[2], v2[2], v3[2], v4[2];
440
441                                                 v1[0] = prev_bezt->vec[1][0];
442                                                 v1[1] = prev_bezt->vec[1][1];
443                                                 v2[0] = prev_bezt->vec[2][0];
444                                                 v2[1] = prev_bezt->vec[2][1];
445
446                                                 v3[0] = bezt->vec[0][0];
447                                                 v3[1] = bezt->vec[0][1];
448                                                 v4[0] = bezt->vec[1][0];
449                                                 v4[1] = bezt->vec[1][1];
450
451                                                 correct_bezpart(v1, v2, v3, v4);
452
453                                                 BKE_curve_forward_diff_bezier(v1[0], v2[0], v3[0], v4[0], data, resol, sizeof(float) * 3);
454                                                 BKE_curve_forward_diff_bezier(v1[1], v2[1], v3[1], v4[1], data + 1, resol, sizeof(float) * 3);
455
456                                                 for (int j = 0; j <= resol; ++j) {
457                                                         const float *fp = &data[j * 3];
458                                                         max_coord = max_ff(max_coord, fp[1]);
459                                                         min_coord = min_ff(min_coord, fp[1]);
460                                                 }
461                                         }
462                                 }
463                         }
464                 }
465
466                 if (max_coord > min_coord) {
467                         range = max_coord - min_coord;
468                         if (range > FLT_EPSILON) {
469                                 factor = 2.0f / range;
470                         }
471                         offset = -min_coord - range / 2.0f;
472                 }
473                 else if (max_coord == min_coord) {
474                         factor = 1.0f;
475                         offset = -min_coord;
476                 }
477         }
478         BLI_assert(factor != 0.0f);
479         if (r_offset) {
480                 *r_offset = offset;
481         }
482
483         fcu->prev_norm_factor = factor;
484         fcu->prev_offset = offset;
485         return factor;
486 }
487
488 /* Get unit conversion factor for given ID + F-Curve */
489 float ANIM_unit_mapping_get_factor(Scene *scene, ID *id, FCurve *fcu, short flag, float *r_offset)
490 {
491         if (flag & ANIM_UNITCONV_NORMALIZE) {
492                 return normalization_factor_get(scene, fcu, flag, r_offset);
493         }
494
495         if (r_offset)
496                 *r_offset = 0.0f;
497
498         /* sanity checks */
499         if (id && fcu && fcu->rna_path) {
500                 PointerRNA ptr, id_ptr;
501                 PropertyRNA *prop;
502
503                 /* get RNA property that F-Curve affects */
504                 RNA_id_pointer_create(id, &id_ptr);
505                 if (RNA_path_resolve_property(&id_ptr, fcu->rna_path, &ptr, &prop)) {
506                         /* rotations: radians <-> degrees? */
507                         if (RNA_SUBTYPE_UNIT(RNA_property_subtype(prop)) == PROP_UNIT_ROTATION) {
508                                 /* if the radians flag is not set, default to using degrees which need conversions */
509                                 if ((scene) && (scene->unit.system_rotation == USER_UNIT_ROT_RADIANS) == 0) {
510                                         if (flag & ANIM_UNITCONV_RESTORE)
511                                                 return DEG2RADF(1.0f);  /* degrees to radians */
512                                         else
513                                                 return RAD2DEGF(1.0f);  /* radians to degrees */
514                                 }
515                         }
516
517                         /* TODO: other rotation types here as necessary */
518                 }
519         }
520
521         /* no mapping needs to occur... */
522         return 1.0f;
523 }
524
525 static bool find_prev_next_keyframes(struct bContext *C, int *nextfra, int *prevfra)
526 {
527         Scene *scene = CTX_data_scene(C);
528         Object *ob = CTX_data_active_object(C);
529         Mask *mask = CTX_data_edit_mask(C);
530         bDopeSheet ads = {NULL};
531         DLRBT_Tree keys;
532         ActKeyColumn *aknext, *akprev;
533         float cfranext, cfraprev;
534         bool donenext = false, doneprev = false;
535         int nextcount = 0, prevcount = 0;
536
537         cfranext = cfraprev = (float)(CFRA);
538
539         /* init binarytree-list for getting keyframes */
540         BLI_dlrbTree_init(&keys);
541
542         /* seed up dummy dopesheet context with flags to perform necessary filtering */
543         if ((scene->flag & SCE_KEYS_NO_SELONLY) == 0) {
544                 /* only selected channels are included */
545                 ads.filterflag |= ADS_FILTER_ONLYSEL;
546         }
547
548         /* populate tree with keyframe nodes */
549         scene_to_keylist(&ads, scene, &keys, 0);
550         gpencil_to_keylist(&ads, scene->gpd, &keys, false);
551
552         if (ob) {
553                 ob_to_keylist(&ads, ob, &keys, 0);
554                 gpencil_to_keylist(&ads, ob->data, &keys, false);
555         }
556
557         if (mask) {
558                 MaskLayer *masklay = BKE_mask_layer_active(mask);
559                 mask_to_keylist(&ads, masklay, &keys);
560         }
561
562         /* find matching keyframe in the right direction */
563         do {
564                 aknext = (ActKeyColumn *)BLI_dlrbTree_search_next(&keys, compare_ak_cfraPtr, &cfranext);
565
566                 if (aknext) {
567                         if (CFRA == (int)aknext->cfra) {
568                                 /* make this the new starting point for the search and ignore */
569                                 cfranext = aknext->cfra;
570                         }
571                         else {
572                                 /* this changes the frame, so set the frame and we're done */
573                                 if (++nextcount == U.view_frame_keyframes)
574                                         donenext = true;
575                         }
576                         cfranext = aknext->cfra;
577                 }
578         } while ((aknext != NULL) && (donenext == false));
579
580         do {
581                 akprev = (ActKeyColumn *)BLI_dlrbTree_search_prev(&keys, compare_ak_cfraPtr, &cfraprev);
582
583                 if (akprev) {
584                         if (CFRA == (int)akprev->cfra) {
585                                 /* make this the new starting point for the search */
586                         }
587                         else {
588                                 /* this changes the frame, so set the frame and we're done */
589                                 if (++prevcount == U.view_frame_keyframes)
590                                         doneprev = true;
591                         }
592                         cfraprev = akprev->cfra;
593                 }
594         } while ((akprev != NULL) && (doneprev == false));
595
596         /* free temp stuff */
597         BLI_dlrbTree_free(&keys);
598
599         /* any success? */
600         if (doneprev || donenext) {
601                 if (doneprev)
602                         *prevfra = cfraprev;
603                 else
604                         *prevfra = CFRA - (cfranext - CFRA);
605
606                 if (donenext)
607                         *nextfra = cfranext;
608                 else
609                         *nextfra = CFRA + (CFRA - cfraprev);
610
611                 return true;
612         }
613
614         return false;
615 }
616
617 void ANIM_center_frame(struct bContext *C, int smooth_viewtx)
618 {
619         ARegion *ar = CTX_wm_region(C);
620         Scene *scene = CTX_data_scene(C);
621         float w = BLI_rctf_size_x(&ar->v2d.cur);
622         rctf newrct;
623         int nextfra, prevfra;
624
625         switch (U.view_frame_type) {
626                 case ZOOM_FRAME_MODE_SECONDS:
627                 {
628                         const float fps = FPS;
629                         newrct.xmax = scene->r.cfra + U.view_frame_seconds * fps + 1;
630                         newrct.xmin = scene->r.cfra - U.view_frame_seconds * fps - 1;
631                         newrct.ymax = ar->v2d.cur.ymax;
632                         newrct.ymin = ar->v2d.cur.ymin;
633                         break;
634                 }
635
636                 /* hardest case of all, look for all keyframes around frame and display those */
637                 case ZOOM_FRAME_MODE_KEYFRAMES:
638                         if (find_prev_next_keyframes(C, &nextfra, &prevfra)) {
639                                 newrct.xmax = nextfra;
640                                 newrct.xmin = prevfra;
641                                 newrct.ymax = ar->v2d.cur.ymax;
642                                 newrct.ymin = ar->v2d.cur.ymin;
643                                 break;
644                         }
645                         /* else drop through, keep range instead */
646                         ATTR_FALLTHROUGH;
647
648                 case ZOOM_FRAME_MODE_KEEP_RANGE:
649                 default:
650                         newrct.xmax = scene->r.cfra + (w / 2);
651                         newrct.xmin = scene->r.cfra - (w / 2);
652                         newrct.ymax = ar->v2d.cur.ymax;
653                         newrct.ymin = ar->v2d.cur.ymin;
654                         break;
655         }
656
657         UI_view2d_smooth_view(C, ar, &newrct, smooth_viewtx);
658 }
659 /* *************************************************** */