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