Cleanup: whitespace
[blender.git] / source / blender / editors / space_view3d / view3d_edit.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): Blender Foundation
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/editors/space_view3d/view3d_edit.c
28  *  \ingroup spview3d
29  */
30
31 #include <string.h>
32 #include <stdio.h>
33 #include <math.h>
34 #include <float.h>
35
36 #include "DNA_armature_types.h"
37 #include "DNA_curve_types.h"
38 #include "DNA_object_types.h"
39 #include "DNA_scene_types.h"
40 #include "DNA_gpencil_types.h"
41
42 #include "MEM_guardedalloc.h"
43
44 #include "BLI_bitmap_draw_2d.h"
45 #include "BLI_blenlib.h"
46 #include "BLI_kdopbvh.h"
47 #include "BLI_math.h"
48 #include "BLI_utildefines.h"
49
50 #include "BKE_armature.h"
51 #include "BKE_camera.h"
52 #include "BKE_context.h"
53 #include "BKE_font.h"
54 #include "BKE_library.h"
55 #include "BKE_object.h"
56 #include "BKE_paint.h"
57 #include "BKE_report.h"
58 #include "BKE_scene.h"
59 #include "BKE_screen.h"
60 #include "BKE_action.h"
61 #include "BKE_depsgraph.h" /* for ED_view3d_camera_lock_sync */
62
63
64 #include "BIF_gl.h"
65 #include "BIF_glutil.h"
66
67 #include "WM_api.h"
68 #include "WM_types.h"
69
70 #include "RNA_access.h"
71 #include "RNA_define.h"
72
73 #include "ED_armature.h"
74 #include "ED_particle.h"
75 #include "ED_keyframing.h"
76 #include "ED_screen.h"
77 #include "ED_transform.h"
78 #include "ED_mesh.h"
79 #include "ED_gpencil.h"
80 #include "ED_view3d.h"
81
82 #include "UI_resources.h"
83
84 #include "PIL_time.h" /* smoothview */
85
86 #include "view3d_intern.h"  /* own include */
87
88 static bool view3d_ensure_persp(struct View3D *v3d, ARegion *ar);
89
90 bool ED_view3d_offset_lock_check(const  View3D *v3d, const  RegionView3D *rv3d)
91 {
92         return (rv3d->persp != RV3D_CAMOB) && (v3d->ob_centre_cursor || v3d->ob_centre);
93 }
94
95 /* ********************** view3d_edit: view manipulations ********************* */
96
97 /**
98  * \return true when the view-port is locked to its camera.
99  */
100 bool ED_view3d_camera_lock_check(const View3D *v3d, const RegionView3D *rv3d)
101 {
102         return ((v3d->camera) &&
103                 (!ID_IS_LINKED_DATABLOCK(v3d->camera)) &&
104                 (v3d->flag2 & V3D_LOCK_CAMERA) &&
105                 (rv3d->persp == RV3D_CAMOB));
106 }
107
108 /**
109  * Apply the camera object transformation to the view-port.
110  * (needed so we can use regular view-port manipulation operators, that sync back to the camera).
111  */
112 void ED_view3d_camera_lock_init_ex(View3D *v3d, RegionView3D *rv3d, const bool calc_dist)
113 {
114         if (ED_view3d_camera_lock_check(v3d, rv3d)) {
115                 if (calc_dist) {
116                         /* using a fallback dist is OK here since ED_view3d_from_object() compensates for it */
117                         rv3d->dist = ED_view3d_offset_distance(v3d->camera->obmat, rv3d->ofs, VIEW3D_DIST_FALLBACK);
118                 }
119                 ED_view3d_from_object(v3d->camera, rv3d->ofs, rv3d->viewquat, &rv3d->dist, NULL);
120         }
121 }
122
123 void ED_view3d_camera_lock_init(View3D *v3d, RegionView3D *rv3d)
124 {
125         ED_view3d_camera_lock_init_ex(v3d, rv3d, true);
126 }
127
128 /**
129  * Apply the view-port transformation back to the camera object.
130  *
131  * \return true if the camera is moved.
132  */
133 bool ED_view3d_camera_lock_sync(View3D *v3d, RegionView3D *rv3d)
134 {
135         if (ED_view3d_camera_lock_check(v3d, rv3d)) {
136                 ObjectTfmProtectedChannels obtfm;
137                 Object *root_parent;
138
139                 if ((U.uiflag & USER_CAM_LOCK_NO_PARENT) == 0 && (root_parent = v3d->camera->parent)) {
140                         Object *ob_update;
141                         float tmat[4][4];
142                         float imat[4][4];
143                         float view_mat[4][4];
144                         float diff_mat[4][4];
145                         float parent_mat[4][4];
146
147                         while (root_parent->parent) {
148                                 root_parent = root_parent->parent;
149                         }
150
151                         ED_view3d_to_m4(view_mat, rv3d->ofs, rv3d->viewquat, rv3d->dist);
152
153                         normalize_m4_m4(tmat, v3d->camera->obmat);
154
155                         invert_m4_m4(imat, tmat);
156                         mul_m4_m4m4(diff_mat, view_mat, imat);
157
158                         mul_m4_m4m4(parent_mat, diff_mat, root_parent->obmat);
159
160                         BKE_object_tfm_protected_backup(root_parent, &obtfm);
161                         BKE_object_apply_mat4(root_parent, parent_mat, true, false);
162                         BKE_object_tfm_protected_restore(root_parent, &obtfm, root_parent->protectflag);
163
164                         ob_update = v3d->camera;
165                         while (ob_update) {
166                                 DAG_id_tag_update(&ob_update->id, OB_RECALC_OB);
167                                 WM_main_add_notifier(NC_OBJECT | ND_TRANSFORM, ob_update);
168                                 ob_update = ob_update->parent;
169                         }
170                 }
171                 else {
172                         /* always maintain the same scale */
173                         const short protect_scale_all = (OB_LOCK_SCALEX | OB_LOCK_SCALEY | OB_LOCK_SCALEZ);
174                         BKE_object_tfm_protected_backup(v3d->camera, &obtfm);
175                         ED_view3d_to_object(v3d->camera, rv3d->ofs, rv3d->viewquat, rv3d->dist);
176                         BKE_object_tfm_protected_restore(v3d->camera, &obtfm, v3d->camera->protectflag | protect_scale_all);
177
178                         DAG_id_tag_update(&v3d->camera->id, OB_RECALC_OB);
179                         WM_main_add_notifier(NC_OBJECT | ND_TRANSFORM, v3d->camera);
180                 }
181
182                 return true;
183         }
184         else {
185                 return false;
186         }
187 }
188
189 bool ED_view3d_camera_autokey(
190         Scene *scene, ID *id_key,
191         struct bContext *C, const bool do_rotate, const bool do_translate)
192 {
193         if (autokeyframe_cfra_can_key(scene, id_key)) {
194                 const float cfra = (float)CFRA;
195                 ListBase dsources = {NULL, NULL};
196
197                 /* add data-source override for the camera object */
198                 ANIM_relative_keyingset_add_source(&dsources, id_key, NULL, NULL);
199
200                 /* insert keyframes
201                  * 1) on the first frame
202                  * 2) on each subsequent frame
203                  *    TODO: need to check in future that frame changed before doing this
204                  */
205                 if (do_rotate) {
206                         struct KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, ANIM_KS_ROTATION_ID);
207                         ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cfra);
208                 }
209                 if (do_translate) {
210                         struct KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, ANIM_KS_LOCATION_ID);
211                         ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cfra);
212                 }
213
214                 /* free temp data */
215                 BLI_freelistN(&dsources);
216
217                 return true;
218         }
219         else {
220                 return false;
221         }
222 }
223
224 /**
225  * Call after modifying a locked view.
226  *
227  * \note Not every view edit currently auto-keys (numpad for eg),
228  * this is complicated because of smoothview.
229  */
230 bool ED_view3d_camera_lock_autokey(
231         View3D *v3d, RegionView3D *rv3d,
232         struct bContext *C, const bool do_rotate, const bool do_translate)
233 {
234         /* similar to ED_view3d_cameracontrol_update */
235         if (ED_view3d_camera_lock_check(v3d, rv3d)) {
236                 Scene *scene = CTX_data_scene(C);
237                 ID *id_key;
238                 Object *root_parent;
239                 if ((U.uiflag & USER_CAM_LOCK_NO_PARENT) == 0 && (root_parent = v3d->camera->parent)) {
240                         while (root_parent->parent) {
241                                 root_parent = root_parent->parent;
242                         }
243                         id_key = &root_parent->id;
244                 }
245                 else {
246                         id_key = &v3d->camera->id;
247                 }
248
249                 return ED_view3d_camera_autokey(scene, id_key, C, do_rotate, do_translate);
250         }
251         else {
252                 return false;
253         }
254 }
255
256 /**
257  * For viewport operators that exit camera persp.
258  *
259  * \note This differs from simply setting ``rv3d->persp = persp`` because it
260  * sets the ``ofs`` and ``dist`` values of the viewport so it matches the camera,
261  * otherwise switching out of camera view may jump to a different part of the scene.
262  */
263 static void view3d_persp_switch_from_camera(View3D *v3d, RegionView3D *rv3d, const char persp)
264 {
265         BLI_assert(rv3d->persp == RV3D_CAMOB);
266         BLI_assert(persp != RV3D_CAMOB);
267
268         if (v3d->camera) {
269                 rv3d->dist = ED_view3d_offset_distance(v3d->camera->obmat, rv3d->ofs, VIEW3D_DIST_FALLBACK);
270                 ED_view3d_from_object(v3d->camera, rv3d->ofs, rv3d->viewquat, &rv3d->dist, NULL);
271         }
272
273         if (!ED_view3d_camera_lock_check(v3d, rv3d)) {
274                 rv3d->persp = persp;
275         }
276 }
277
278 /* ********************* box view support ***************** */
279
280 static void view3d_boxview_clip(ScrArea *sa)
281 {
282         ARegion *ar;
283         BoundBox *bb = MEM_callocN(sizeof(BoundBox), "clipbb");
284         float clip[6][4];
285         float x1 = 0.0f, y1 = 0.0f, z1 = 0.0f, ofs[3] = {0.0f, 0.0f, 0.0f};
286         int val;
287
288         /* create bounding box */
289         for (ar = sa->regionbase.first; ar; ar = ar->next) {
290                 if (ar->regiontype == RGN_TYPE_WINDOW) {
291                         RegionView3D *rv3d = ar->regiondata;
292
293                         if (rv3d->viewlock & RV3D_BOXCLIP) {
294                                 if (ELEM(rv3d->view, RV3D_VIEW_TOP, RV3D_VIEW_BOTTOM)) {
295                                         if (ar->winx > ar->winy) x1 = rv3d->dist;
296                                         else x1 = ar->winx * rv3d->dist / ar->winy;
297
298                                         if (ar->winx > ar->winy) y1 = ar->winy * rv3d->dist / ar->winx;
299                                         else y1 = rv3d->dist;
300                                         copy_v2_v2(ofs, rv3d->ofs);
301                                 }
302                                 else if (ELEM(rv3d->view, RV3D_VIEW_FRONT, RV3D_VIEW_BACK)) {
303                                         ofs[2] = rv3d->ofs[2];
304
305                                         if (ar->winx > ar->winy) z1 = ar->winy * rv3d->dist / ar->winx;
306                                         else z1 = rv3d->dist;
307                                 }
308                         }
309                 }
310         }
311
312         for (val = 0; val < 8; val++) {
313                 if (ELEM(val, 0, 3, 4, 7))
314                         bb->vec[val][0] = -x1 - ofs[0];
315                 else
316                         bb->vec[val][0] =  x1 - ofs[0];
317
318                 if (ELEM(val, 0, 1, 4, 5))
319                         bb->vec[val][1] = -y1 - ofs[1];
320                 else
321                         bb->vec[val][1] =  y1 - ofs[1];
322
323                 if (val > 3)
324                         bb->vec[val][2] = -z1 - ofs[2];
325                 else
326                         bb->vec[val][2] =  z1 - ofs[2];
327         }
328
329         /* normals for plane equations */
330         normal_tri_v3(clip[0], bb->vec[0], bb->vec[1], bb->vec[4]);
331         normal_tri_v3(clip[1], bb->vec[1], bb->vec[2], bb->vec[5]);
332         normal_tri_v3(clip[2], bb->vec[2], bb->vec[3], bb->vec[6]);
333         normal_tri_v3(clip[3], bb->vec[3], bb->vec[0], bb->vec[7]);
334         normal_tri_v3(clip[4], bb->vec[4], bb->vec[5], bb->vec[6]);
335         normal_tri_v3(clip[5], bb->vec[0], bb->vec[2], bb->vec[1]);
336
337         /* then plane equations */
338         for (val = 0; val < 6; val++) {
339                 clip[val][3] = -dot_v3v3(clip[val], bb->vec[val % 5]);
340         }
341
342         /* create bounding box */
343         for (ar = sa->regionbase.first; ar; ar = ar->next) {
344                 if (ar->regiontype == RGN_TYPE_WINDOW) {
345                         RegionView3D *rv3d = ar->regiondata;
346
347                         if (rv3d->viewlock & RV3D_BOXCLIP) {
348                                 rv3d->rflag |= RV3D_CLIPPING;
349                                 memcpy(rv3d->clip, clip, sizeof(clip));
350                                 if (rv3d->clipbb) MEM_freeN(rv3d->clipbb);
351                                 rv3d->clipbb = MEM_dupallocN(bb);
352                         }
353                 }
354         }
355         MEM_freeN(bb);
356 }
357
358 /**
359  * Find which axis values are shared between both views and copy to \a rv3d_dst
360  * taking axis flipping into account.
361  */
362 static void view3d_boxview_sync_axis(RegionView3D *rv3d_dst, RegionView3D *rv3d_src)
363 {
364         /* absolute axis values above this are considered to be set (will be ~1.0f) */
365         const float axis_eps = 0.5f;
366         float viewinv[4];
367
368         /* use the view rotation to identify which axis to sync on */
369         float view_axis_all[4][3] = {
370             {1.0f, 0.0f, 0.0f},
371             {0.0f, 1.0f, 0.0f},
372             {1.0f, 0.0f, 0.0f},
373             {0.0f, 1.0f, 0.0f}};
374
375         float *view_src_x = &view_axis_all[0][0];
376         float *view_src_y = &view_axis_all[1][0];
377
378         float *view_dst_x = &view_axis_all[2][0];
379         float *view_dst_y = &view_axis_all[3][0];
380         int i;
381
382
383         /* we could use rv3d->viewinv, but better not depend on view matrix being updated */
384         if (UNLIKELY(ED_view3d_quat_from_axis_view(rv3d_src->view, viewinv) == false)) {
385                 return;
386         }
387         invert_qt_normalized(viewinv);
388         mul_qt_v3(viewinv, view_src_x);
389         mul_qt_v3(viewinv, view_src_y);
390
391         if (UNLIKELY(ED_view3d_quat_from_axis_view(rv3d_dst->view, viewinv) == false)) {
392                 return;
393         }
394         invert_qt_normalized(viewinv);
395         mul_qt_v3(viewinv, view_dst_x);
396         mul_qt_v3(viewinv, view_dst_y);
397
398         /* check source and dest have a matching axis */
399         for (i = 0; i < 3; i++) {
400                 if (((fabsf(view_src_x[i]) > axis_eps) || (fabsf(view_src_y[i]) > axis_eps)) &&
401                     ((fabsf(view_dst_x[i]) > axis_eps) || (fabsf(view_dst_y[i]) > axis_eps)))
402                 {
403                         rv3d_dst->ofs[i] = rv3d_src->ofs[i];
404                 }
405         }
406 }
407
408 /* sync center/zoom view of region to others, for view transforms */
409 static void view3d_boxview_sync(ScrArea *sa, ARegion *ar)
410 {
411         ARegion *artest;
412         RegionView3D *rv3d = ar->regiondata;
413         short clip = 0;
414
415         for (artest = sa->regionbase.first; artest; artest = artest->next) {
416                 if (artest != ar && artest->regiontype == RGN_TYPE_WINDOW) {
417                         RegionView3D *rv3dtest = artest->regiondata;
418
419                         if (rv3dtest->viewlock & RV3D_LOCKED) {
420                                 rv3dtest->dist = rv3d->dist;
421                                 view3d_boxview_sync_axis(rv3dtest, rv3d);
422                                 clip |= rv3dtest->viewlock & RV3D_BOXCLIP;
423
424                                 ED_region_tag_redraw(artest);
425                         }
426                 }
427         }
428
429         if (clip) {
430                 view3d_boxview_clip(sa);
431         }
432 }
433
434 /* for home, center etc */
435 void view3d_boxview_copy(ScrArea *sa, ARegion *ar)
436 {
437         ARegion *artest;
438         RegionView3D *rv3d = ar->regiondata;
439         bool clip = false;
440
441         for (artest = sa->regionbase.first; artest; artest = artest->next) {
442                 if (artest != ar && artest->regiontype == RGN_TYPE_WINDOW) {
443                         RegionView3D *rv3dtest = artest->regiondata;
444
445                         if (rv3dtest->viewlock) {
446                                 rv3dtest->dist = rv3d->dist;
447                                 copy_v3_v3(rv3dtest->ofs, rv3d->ofs);
448                                 ED_region_tag_redraw(artest);
449
450                                 clip |= ((rv3dtest->viewlock & RV3D_BOXCLIP) != 0);
451                         }
452                 }
453         }
454
455         if (clip) {
456                 view3d_boxview_clip(sa);
457         }
458 }
459
460 /* 'clip' is used to know if our clip setting has changed */
461 void ED_view3d_quadview_update(ScrArea *sa, ARegion *ar, bool do_clip)
462 {
463         ARegion *ar_sync = NULL;
464         RegionView3D *rv3d = ar->regiondata;
465         short viewlock;
466         /* this function copies flags from the first of the 3 other quadview
467          * regions to the 2 other, so it assumes this is the region whose
468          * properties are always being edited, weak */
469         viewlock = rv3d->viewlock;
470
471         if ((viewlock & RV3D_LOCKED) == 0) {
472                 do_clip = (viewlock & RV3D_BOXCLIP) != 0;
473                 viewlock = 0;
474         }
475         else if ((viewlock & RV3D_BOXVIEW) == 0 && (viewlock & RV3D_BOXCLIP) != 0) {
476                 do_clip = true;
477                 viewlock &= ~RV3D_BOXCLIP;
478         }
479
480         for (; ar; ar = ar->prev) {
481                 if (ar->alignment == RGN_ALIGN_QSPLIT) {
482                         rv3d = ar->regiondata;
483                         rv3d->viewlock = viewlock;
484
485                         if (do_clip && (viewlock & RV3D_BOXCLIP) == 0) {
486                                 rv3d->rflag &= ~RV3D_BOXCLIP;
487                         }
488
489                         /* use ar_sync so we sync with one of the aligned views below
490                          * else the view jumps on changing view settings like 'clip'
491                          * since it copies from the perspective view */
492                         ar_sync = ar;
493                 }
494         }
495
496         if (rv3d->viewlock & RV3D_BOXVIEW) {
497                 view3d_boxview_sync(sa, ar_sync ? ar_sync : sa->regionbase.last);
498         }
499
500         /* ensure locked regions have an axis, locked user views don't make much sense */
501         if (viewlock & RV3D_LOCKED) {
502                 int index_qsplit = 0;
503                 for (ar = sa->regionbase.first; ar; ar = ar->next) {
504                         if (ar->alignment == RGN_ALIGN_QSPLIT) {
505                                 rv3d = ar->regiondata;
506                                 if (rv3d->viewlock) {
507                                         if (!RV3D_VIEW_IS_AXIS(rv3d->view)) {
508                                                 rv3d->view = ED_view3d_lock_view_from_index(index_qsplit);
509                                                 rv3d->persp = RV3D_ORTHO;
510                                                 ED_view3d_lock(rv3d);
511                                         }
512                                 }
513                                 index_qsplit++;
514                         }
515                 }
516         }
517
518         ED_area_tag_redraw(sa);
519 }
520
521 /* ************************** init for view ops **********************************/
522
523 typedef struct ViewOpsData {
524         /* context pointers (assigned by viewops_data_alloc) */
525         Scene *scene;
526         ScrArea *sa;
527         ARegion *ar;
528         View3D *v3d;
529         RegionView3D *rv3d;
530
531         /* needed for continuous zoom */
532         wmTimer *timer;
533         double timer_lastdraw;
534
535         float oldquat[4];
536         float viewquat[4]; /* working copy of rv3d->viewquat */
537         float trackvec[3];
538         float mousevec[3]; /* dolly only */
539         float reverse;
540         float dist_prev, camzoom_prev;
541         float grid, far;
542         bool axis_snap;  /* view rotate only */
543         float zfac;
544
545         /* use for orbit selection and auto-dist */
546         float ofs[3], dyn_ofs[3];
547         bool use_dyn_ofs;
548
549         int origx, origy, oldx, oldy;
550         int origkey; /* the key that triggered the operator */
551
552 } ViewOpsData;
553
554 #define TRACKBALLSIZE  (1.1f)
555
556 static void calctrackballvec(const rcti *rect, int mx, int my, float vec[3])
557 {
558         const float radius = TRACKBALLSIZE;
559         const float t = radius / (float)M_SQRT2;
560         float x, y, z, d;
561
562         /* normalize x and y */
563         x = BLI_rcti_cent_x(rect) - mx;
564         x /= (float)(BLI_rcti_size_x(rect) / 4);
565         y = BLI_rcti_cent_y(rect) - my;
566         y /= (float)(BLI_rcti_size_y(rect) / 2);
567         d = sqrtf(x * x + y * y);
568         if (d < t) { /* Inside sphere */
569                 z = sqrtf(radius * radius - d * d);
570         }
571         else { /* On hyperbola */
572                 z = t * t / d;
573         }
574
575         vec[0] = x;
576         vec[1] = y;
577         vec[2] = -z;     /* yah yah! */
578 }
579
580
581 /* -------------------------------------------------------------------- */
582 /* ViewOpsData */
583
584 /** \name Generic View Operator Custom-Data.
585  * \{ */
586
587 /**
588  * Allocate and fill in context pointers for #ViewOpsData
589  */
590 static void viewops_data_alloc(bContext *C, wmOperator *op)
591 {
592         ViewOpsData *vod = MEM_callocN(sizeof(ViewOpsData), "viewops data");
593
594         /* store data */
595         op->customdata = vod;
596         vod->scene = CTX_data_scene(C);
597         vod->sa = CTX_wm_area(C);
598         vod->ar = CTX_wm_region(C);
599         vod->v3d = vod->sa->spacedata.first;
600         vod->rv3d = vod->ar->regiondata;
601 }
602
603 void view3d_orbit_apply_dyn_ofs(
604         float r_ofs[3], const float ofs_old[3], const float viewquat_old[4],
605         const float viewquat_new[4], const float dyn_ofs[3])
606 {
607         float q[4];
608         invert_qt_qt_normalized(q, viewquat_old);
609         mul_qt_qtqt(q, q, viewquat_new);
610
611         invert_qt_normalized(q);
612
613         sub_v3_v3v3(r_ofs, ofs_old, dyn_ofs);
614         mul_qt_v3(q, r_ofs);
615         add_v3_v3(r_ofs, dyn_ofs);
616 }
617
618 static bool view3d_orbit_calc_center(bContext *C, float r_dyn_ofs[3])
619 {
620         static float lastofs[3] = {0, 0, 0};
621         bool is_set = false;
622
623         Scene *scene = CTX_data_scene(C);
624         Object *ob_act = OBACT;
625
626         if (ob_act && (ob_act->mode & OB_MODE_ALL_PAINT) &&
627             /* with weight-paint + pose-mode, fall through to using calculateTransformCenter */
628             ((ob_act->mode & OB_MODE_WEIGHT_PAINT) && BKE_object_pose_armature_get(ob_act)) == 0)
629         {
630                 /* in case of sculpting use last average stroke position as a rotation
631                  * center, in other cases it's not clear what rotation center shall be
632                  * so just rotate around object origin
633                  */
634                 if (ob_act->mode & (OB_MODE_SCULPT | OB_MODE_TEXTURE_PAINT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT)) {
635                         float stroke[3];
636                         BKE_paint_stroke_get_average(scene, ob_act, stroke);
637                         copy_v3_v3(lastofs, stroke);
638                 }
639                 else {
640                         copy_v3_v3(lastofs, ob_act->obmat[3]);
641                 }
642                 is_set = true;
643         }
644         else if (ob_act && (ob_act->mode & OB_MODE_EDIT) && (ob_act->type == OB_FONT)) {
645                 Curve *cu = ob_act->data;
646                 EditFont *ef = cu->editfont;
647                 int i;
648
649                 zero_v3(lastofs);
650                 for (i = 0; i < 4; i++) {
651                         add_v2_v2(lastofs, ef->textcurs[i]);
652                 }
653                 mul_v2_fl(lastofs, 1.0f / 4.0f);
654
655                 mul_m4_v3(ob_act->obmat, lastofs);
656
657                 is_set = true;
658         }
659         else if (ob_act == NULL || ob_act->mode == OB_MODE_OBJECT) {
660                 /* object mode use boundbox centers */
661                 View3D *v3d = CTX_wm_view3d(C);
662                 Base *base;
663                 unsigned int tot = 0;
664                 float select_center[3];
665
666                 zero_v3(select_center);
667                 for (base = FIRSTBASE; base; base = base->next) {
668                         if (TESTBASE(v3d, base)) {
669                                 /* use the boundbox if we can */
670                                 Object *ob = base->object;
671
672                                 if (ob->bb && !(ob->bb->flag & BOUNDBOX_DIRTY)) {
673                                         float cent[3];
674
675                                         BKE_boundbox_calc_center_aabb(ob->bb, cent);
676
677                                         mul_m4_v3(ob->obmat, cent);
678                                         add_v3_v3(select_center, cent);
679                                 }
680                                 else {
681                                         add_v3_v3(select_center, ob->obmat[3]);
682                                 }
683                                 tot++;
684                         }
685                 }
686                 if (tot) {
687                         mul_v3_fl(select_center, 1.0f / (float)tot);
688                         copy_v3_v3(lastofs, select_center);
689                         is_set = true;
690                 }
691         }
692         else {
693                 /* If there's no selection, lastofs is unmodified and last value since static */
694                 is_set = calculateTransformCenter(C, V3D_AROUND_CENTER_MEAN, lastofs, NULL);
695         }
696
697         copy_v3_v3(r_dyn_ofs, lastofs);
698
699         return is_set;
700 }
701
702 enum eViewOpsOrbit {
703         VIEWOPS_ORBIT_SELECT = (1 << 0),
704         VIEWOPS_ORBIT_DEPTH = (1 << 1),
705 };
706
707 static enum eViewOpsOrbit viewops_orbit_mode_ex(bool use_select, bool use_depth)
708 {
709         enum eViewOpsOrbit flag = 0;
710         if (use_select) {
711                 flag |= VIEWOPS_ORBIT_SELECT;
712         }
713         if (use_depth) {
714                 flag |= VIEWOPS_ORBIT_DEPTH;
715         }
716
717         return flag;
718 }
719
720 static enum eViewOpsOrbit viewops_orbit_mode(void)
721 {
722         return viewops_orbit_mode_ex(
723                 (U.uiflag & USER_ORBIT_SELECTION) != 0,
724                 (U.uiflag & USER_ZBUF_ORBIT) != 0);
725 }
726
727 /**
728  * Calculate the values for #ViewOpsData
729  */
730 static void viewops_data_create_ex(
731         bContext *C, wmOperator *op, const wmEvent *event,
732         bool switch_from_camera, enum eViewOpsOrbit orbit_mode)
733 {
734         ViewOpsData *vod = op->customdata;
735         RegionView3D *rv3d = vod->rv3d;
736
737         /* we need the depth info before changing any viewport options */
738         if (orbit_mode & VIEWOPS_ORBIT_DEPTH) {
739                 float fallback_depth_pt[3];
740
741                 view3d_operator_needs_opengl(C); /* needed for zbuf drawing */
742
743                 negate_v3_v3(fallback_depth_pt, rv3d->ofs);
744
745                 vod->use_dyn_ofs = ED_view3d_autodist(
746                         vod->scene, vod->ar, vod->v3d,
747                         event->mval, vod->dyn_ofs, true, fallback_depth_pt);
748         }
749         else {
750                 vod->use_dyn_ofs = false;
751         }
752
753         if (switch_from_camera) {
754                 /* switch from camera view when: */
755                 if (view3d_ensure_persp(vod->v3d, vod->ar)) {
756                         /* If we're switching from camera view to the perspective one,
757                          * need to tag viewport update, so camera vuew and borders
758                          * are properly updated.
759                          */
760                         ED_region_tag_redraw(vod->ar);
761                 }
762         }
763
764         /* set the view from the camera, if view locking is enabled.
765          * we may want to make this optional but for now its needed always */
766         ED_view3d_camera_lock_init(vod->v3d, vod->rv3d);
767
768         vod->dist_prev = rv3d->dist;
769         vod->camzoom_prev = rv3d->camzoom;
770         copy_qt_qt(vod->viewquat, rv3d->viewquat);
771         copy_qt_qt(vod->oldquat, rv3d->viewquat);
772         vod->origx = vod->oldx = event->x;
773         vod->origy = vod->oldy = event->y;
774         vod->origkey = event->type; /* the key that triggered the operator.  */
775         copy_v3_v3(vod->ofs, rv3d->ofs);
776
777         if (orbit_mode & VIEWOPS_ORBIT_SELECT) {
778                 float ofs[3];
779                 if (view3d_orbit_calc_center(C, ofs) || (vod->use_dyn_ofs == false)) {
780                         vod->use_dyn_ofs = true;
781                         negate_v3_v3(vod->dyn_ofs, ofs);
782                         orbit_mode &= ~VIEWOPS_ORBIT_DEPTH;
783                 }
784         }
785
786         if (orbit_mode & VIEWOPS_ORBIT_DEPTH) {
787                 if (vod->use_dyn_ofs) {
788                         if (rv3d->is_persp) {
789                                 float my_origin[3]; /* original G.vd->ofs */
790                                 float my_pivot[3]; /* view */
791                                 float dvec[3];
792
793                                 /* locals for dist correction */
794                                 float mat[3][3];
795                                 float upvec[3];
796
797                                 negate_v3_v3(my_origin, rv3d->ofs);             /* ofs is flipped */
798
799                                 /* Set the dist value to be the distance from this 3d point
800                                  * this means youll always be able to zoom into it and panning wont go bad when dist was zero */
801
802                                 /* remove dist value */
803                                 upvec[0] = upvec[1] = 0;
804                                 upvec[2] = rv3d->dist;
805                                 copy_m3_m4(mat, rv3d->viewinv);
806
807                                 mul_m3_v3(mat, upvec);
808                                 sub_v3_v3v3(my_pivot, rv3d->ofs, upvec);
809                                 negate_v3(my_pivot);                /* ofs is flipped */
810
811                                 /* find a new ofs value that is along the view axis (rather than the mouse location) */
812                                 closest_to_line_v3(dvec, vod->dyn_ofs, my_pivot, my_origin);
813                                 vod->dist_prev = rv3d->dist = len_v3v3(my_pivot, dvec);
814
815                                 negate_v3_v3(rv3d->ofs, dvec);
816                         }
817                         else {
818                                 const float mval_ar_mid[2] = {
819                                     (float)vod->ar->winx / 2.0f,
820                                     (float)vod->ar->winy / 2.0f};
821
822                                 ED_view3d_win_to_3d(vod->v3d, vod->ar, vod->dyn_ofs, mval_ar_mid, rv3d->ofs);
823                                 negate_v3(rv3d->ofs);
824                         }
825                         negate_v3(vod->dyn_ofs);
826                         copy_v3_v3(vod->ofs, rv3d->ofs);
827                 }
828         }
829
830         {
831                 /* for dolly */
832                 const float mval_f[2] = {(float)event->mval[0],
833                                          (float)event->mval[1]};
834                 ED_view3d_win_to_vector(vod->ar, mval_f, vod->mousevec);
835         }
836
837         /* lookup, we don't pass on v3d to prevent confusement */
838         vod->grid = vod->v3d->grid;
839         vod->far = vod->v3d->far;
840
841         calctrackballvec(&vod->ar->winrct, event->x, event->y, vod->trackvec);
842
843         {
844                 float tvec[3];
845                 negate_v3_v3(tvec, rv3d->ofs);
846                 vod->zfac = ED_view3d_calc_zfac(rv3d, tvec, NULL);
847         }
848
849         vod->reverse = 1.0f;
850         if (rv3d->persmat[2][1] < 0.0f)
851                 vod->reverse = -1.0f;
852
853         rv3d->rflag |= RV3D_NAVIGATING;
854 }
855
856 static void viewops_data_create(bContext *C, wmOperator *op, const wmEvent *event, bool switch_from_camera)
857 {
858         enum eViewOpsOrbit orbit_mode = viewops_orbit_mode();
859         viewops_data_create_ex(C, op, event, switch_from_camera, orbit_mode);
860 }
861
862 static void viewops_data_free(bContext *C, wmOperator *op)
863 {
864         ARegion *ar;
865         Paint *p = BKE_paint_get_active_from_context(C);
866
867         if (op->customdata) {
868                 ViewOpsData *vod = op->customdata;
869                 ar = vod->ar;
870                 vod->rv3d->rflag &= ~RV3D_NAVIGATING;
871
872                 if (vod->timer)
873                         WM_event_remove_timer(CTX_wm_manager(C), vod->timer->win, vod->timer);
874
875                 MEM_freeN(vod);
876                 op->customdata = NULL;
877         }
878         else {
879                 ar = CTX_wm_region(C);
880         }
881
882         if (p && (p->flags & PAINT_FAST_NAVIGATE))
883                 ED_region_tag_redraw(ar);
884 }
885 /** \} */
886
887
888 /* ************************** viewrotate **********************************/
889
890 enum {
891         VIEW_PASS = 0,
892         VIEW_APPLY,
893         VIEW_CONFIRM
894 };
895
896 /* NOTE: these defines are saved in keymap files, do not change values but just add new ones */
897 #define VIEW_MODAL_CONFIRM              1 /* used for all view operations */
898 #define VIEWROT_MODAL_AXIS_SNAP_ENABLE  2
899 #define VIEWROT_MODAL_AXIS_SNAP_DISABLE 3
900 #define VIEWROT_MODAL_SWITCH_ZOOM       4
901 #define VIEWROT_MODAL_SWITCH_MOVE       5
902 #define VIEWROT_MODAL_SWITCH_ROTATE     6
903
904 /* called in transform_ops.c, on each regeneration of keymaps  */
905 void viewrotate_modal_keymap(wmKeyConfig *keyconf)
906 {
907         static EnumPropertyItem modal_items[] = {
908                 {VIEW_MODAL_CONFIRM,    "CONFIRM", 0, "Confirm", ""},
909
910                 {VIEWROT_MODAL_AXIS_SNAP_ENABLE,    "AXIS_SNAP_ENABLE", 0, "Enable Axis Snap", ""},
911                 {VIEWROT_MODAL_AXIS_SNAP_DISABLE,   "AXIS_SNAP_DISABLE", 0, "Disable Axis Snap", ""},
912                 
913                 {VIEWROT_MODAL_SWITCH_ZOOM, "SWITCH_TO_ZOOM", 0, "Switch to Zoom"},
914                 {VIEWROT_MODAL_SWITCH_MOVE, "SWITCH_TO_MOVE", 0, "Switch to Move"},
915
916                 {0, NULL, 0, NULL, NULL}
917         };
918
919         wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "View3D Rotate Modal");
920
921         /* this function is called for each spacetype, only needs to add map once */
922         if (keymap && keymap->modal_items) return;
923
924         keymap = WM_modalkeymap_add(keyconf, "View3D Rotate Modal", modal_items);
925
926         /* items for modal map */
927         WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_RELEASE, KM_ANY, 0, VIEW_MODAL_CONFIRM);
928         WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, VIEW_MODAL_CONFIRM);
929
930         WM_modalkeymap_add_item(keymap, LEFTALTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_AXIS_SNAP_ENABLE);
931         WM_modalkeymap_add_item(keymap, LEFTALTKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_AXIS_SNAP_DISABLE);
932
933         /* disabled mode switching for now, can re-implement better, later on */
934 #if 0
935         WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM);
936         WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM);
937         WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_MOVE);
938 #endif
939         
940         /* assign map to operators */
941         WM_modalkeymap_assign(keymap, "VIEW3D_OT_rotate");
942
943 }
944
945 static void viewrotate_apply_dyn_ofs(ViewOpsData *vod, const float viewquat_new[4])
946 {
947         if (vod->use_dyn_ofs) {
948                 RegionView3D *rv3d = vod->rv3d;
949                 view3d_orbit_apply_dyn_ofs(rv3d->ofs, vod->ofs, vod->oldquat, viewquat_new, vod->dyn_ofs);
950         }
951 }
952
953 static void viewrotate_apply_snap(ViewOpsData *vod)
954 {
955         const float axis_limit = DEG2RADF(45 / 3);
956
957         RegionView3D *rv3d = vod->rv3d;
958
959         float viewquat_inv[4];
960         float zaxis[3] = {0, 0, 1};
961         float zaxis_best[3];
962         int x, y, z;
963         bool found = false;
964
965         invert_qt_qt_normalized(viewquat_inv, vod->viewquat);
966
967         mul_qt_v3(viewquat_inv, zaxis);
968         normalize_v3(zaxis);
969
970
971         for (x = -1; x < 2; x++) {
972                 for (y = -1; y < 2; y++) {
973                         for (z = -1; z < 2; z++) {
974                                 if (x || y || z) {
975                                         float zaxis_test[3] = {x, y, z};
976
977                                         normalize_v3(zaxis_test);
978
979                                         if (angle_normalized_v3v3(zaxis_test, zaxis) < axis_limit) {
980                                                 copy_v3_v3(zaxis_best, zaxis_test);
981                                                 found = true;
982                                         }
983                                 }
984                         }
985                 }
986         }
987
988         if (found) {
989
990                 /* find the best roll */
991                 float quat_roll[4], quat_final[4], quat_best[4], quat_snap[4];
992                 float viewquat_align[4]; /* viewquat aligned to zaxis_best */
993                 float viewquat_align_inv[4]; /* viewquat aligned to zaxis_best */
994                 float best_angle = axis_limit;
995                 int j;
996
997                 /* viewquat_align is the original viewquat aligned to the snapped axis
998                  * for testing roll */
999                 rotation_between_vecs_to_quat(viewquat_align, zaxis_best, zaxis);
1000                 normalize_qt(viewquat_align);
1001                 mul_qt_qtqt(viewquat_align, vod->viewquat, viewquat_align);
1002                 normalize_qt(viewquat_align);
1003                 invert_qt_qt_normalized(viewquat_align_inv, viewquat_align);
1004
1005                 vec_to_quat(quat_snap, zaxis_best, OB_NEGZ, OB_POSY);
1006                 normalize_qt(quat_snap);
1007                 invert_qt_normalized(quat_snap);
1008
1009                 /* check if we can find the roll */
1010                 found = false;
1011
1012                 /* find best roll */
1013                 for (j = 0; j < 8; j++) {
1014                         float angle;
1015                         float xaxis1[3] = {1, 0, 0};
1016                         float xaxis2[3] = {1, 0, 0};
1017                         float quat_final_inv[4];
1018
1019                         axis_angle_to_quat(quat_roll, zaxis_best, (float)j * DEG2RADF(45.0f));
1020                         normalize_qt(quat_roll);
1021
1022                         mul_qt_qtqt(quat_final, quat_snap, quat_roll);
1023                         normalize_qt(quat_final);
1024
1025                         /* compare 2 vector angles to find the least roll */
1026                         invert_qt_qt_normalized(quat_final_inv, quat_final);
1027                         mul_qt_v3(viewquat_align_inv, xaxis1);
1028                         mul_qt_v3(quat_final_inv, xaxis2);
1029                         angle = angle_v3v3(xaxis1, xaxis2);
1030
1031                         if (angle <= best_angle) {
1032                                 found = true;
1033                                 best_angle = angle;
1034                                 copy_qt_qt(quat_best, quat_final);
1035                         }
1036                 }
1037
1038                 if (found) {
1039                         /* lock 'quat_best' to an axis view if we can */
1040                         rv3d->view = ED_view3d_quat_to_axis_view(quat_best, 0.01f);
1041                         if (rv3d->view != RV3D_VIEW_USER) {
1042                                 ED_view3d_quat_from_axis_view(rv3d->view, quat_best);
1043                         }
1044                 }
1045                 else {
1046                         copy_qt_qt(quat_best, viewquat_align);
1047                 }
1048
1049                 copy_qt_qt(rv3d->viewquat, quat_best);
1050
1051                 viewrotate_apply_dyn_ofs(vod, rv3d->viewquat);
1052         }
1053 }
1054
1055 static void viewrotate_apply(ViewOpsData *vod, int x, int y)
1056 {
1057         RegionView3D *rv3d = vod->rv3d;
1058
1059         rv3d->view = RV3D_VIEW_USER; /* need to reset every time because of view snapping */
1060
1061         if (U.flag & USER_TRACKBALL) {
1062                 float axis[3], q1[4], dvec[3], newvec[3];
1063                 float angle;
1064
1065                 calctrackballvec(&vod->ar->winrct, x, y, newvec);
1066
1067                 sub_v3_v3v3(dvec, newvec, vod->trackvec);
1068
1069                 angle = (len_v3(dvec) / (2.0f * TRACKBALLSIZE)) * (float)M_PI;
1070
1071                 /* Allow for rotation beyond the interval [-pi, pi] */
1072                 angle = angle_wrap_rad(angle);
1073
1074                 /* This relation is used instead of the actual angle between vectors
1075                  * so that the angle of rotation is linearly proportional to
1076                  * the distance that the mouse is dragged. */
1077
1078                 cross_v3_v3v3(axis, vod->trackvec, newvec);
1079                 axis_angle_to_quat(q1, axis, angle);
1080
1081                 mul_qt_qtqt(vod->viewquat, q1, vod->oldquat);
1082
1083                 viewrotate_apply_dyn_ofs(vod, vod->viewquat);
1084         }
1085         else {
1086                 /* New turntable view code by John Aughey */
1087                 float quat_local_x[4], quat_global_z[4];
1088                 float m[3][3];
1089                 float m_inv[3][3];
1090                 const float zvec_global[3] = {0.0f, 0.0f, 1.0f};
1091                 float xaxis[3];
1092
1093                 /* Sensitivity will control how fast the viewport rotates.  0.007 was
1094                  * obtained experimentally by looking at viewport rotation sensitivities
1095                  * on other modeling programs. */
1096                 /* Perhaps this should be a configurable user parameter. */
1097                 const float sensitivity = 0.007f;
1098
1099                 /* Get the 3x3 matrix and its inverse from the quaternion */
1100                 quat_to_mat3(m, vod->viewquat);
1101                 invert_m3_m3(m_inv, m);
1102
1103                 /* avoid gimble lock */
1104 #if 1
1105                 if (len_squared_v3v3(zvec_global, m_inv[2]) > 0.001f) {
1106                         float fac;
1107                         cross_v3_v3v3(xaxis, zvec_global, m_inv[2]);
1108                         if (dot_v3v3(xaxis, m_inv[0]) < 0) {
1109                                 negate_v3(xaxis);
1110                         }
1111                         fac = angle_normalized_v3v3(zvec_global, m_inv[2]) / (float)M_PI;
1112                         fac = fabsf(fac - 0.5f) * 2;
1113                         fac = fac * fac;
1114                         interp_v3_v3v3(xaxis, xaxis, m_inv[0], fac);
1115                 }
1116                 else {
1117                         copy_v3_v3(xaxis, m_inv[0]);
1118                 }
1119 #else
1120                 copy_v3_v3(xaxis, m_inv[0]);
1121 #endif
1122
1123                 /* Determine the direction of the x vector (for rotating up and down) */
1124                 /* This can likely be computed directly from the quaternion. */
1125
1126                 /* Perform the up/down rotation */
1127                 axis_angle_to_quat(quat_local_x, xaxis, sensitivity * -(y - vod->oldy));
1128                 mul_qt_qtqt(quat_local_x, vod->viewquat, quat_local_x);
1129
1130                 /* Perform the orbital rotation */
1131                 axis_angle_to_quat_single(quat_global_z, 'Z', sensitivity * vod->reverse * (x - vod->oldx));
1132                 mul_qt_qtqt(vod->viewquat, quat_local_x, quat_global_z);
1133
1134                 viewrotate_apply_dyn_ofs(vod, vod->viewquat);
1135         }
1136
1137         /* avoid precision loss over time */
1138         normalize_qt(vod->viewquat);
1139
1140         /* use a working copy so view rotation locking doesnt overwrite the locked
1141          * rotation back into the view we calculate with */
1142         copy_qt_qt(rv3d->viewquat, vod->viewquat);
1143
1144         /* check for view snap,
1145          * note: don't apply snap to vod->viewquat so the view wont jam up */
1146         if (vod->axis_snap) {
1147                 viewrotate_apply_snap(vod);
1148         }
1149         vod->oldx = x;
1150         vod->oldy = y;
1151
1152         ED_view3d_camera_lock_sync(vod->v3d, rv3d);
1153
1154         ED_region_tag_redraw(vod->ar);
1155 }
1156
1157 static int viewrotate_modal(bContext *C, wmOperator *op, const wmEvent *event)
1158 {
1159         ViewOpsData *vod = op->customdata;
1160         short event_code = VIEW_PASS;
1161         bool use_autokey = false;
1162         int ret = OPERATOR_RUNNING_MODAL;
1163
1164         /* execute the events */
1165         if (event->type == MOUSEMOVE) {
1166                 event_code = VIEW_APPLY;
1167         }
1168         else if (event->type == EVT_MODAL_MAP) {
1169                 switch (event->val) {
1170                         case VIEW_MODAL_CONFIRM:
1171                                 event_code = VIEW_CONFIRM;
1172                                 break;
1173                         case VIEWROT_MODAL_AXIS_SNAP_ENABLE:
1174                                 vod->axis_snap = true;
1175                                 event_code = VIEW_APPLY;
1176                                 break;
1177                         case VIEWROT_MODAL_AXIS_SNAP_DISABLE:
1178                                 vod->axis_snap = false;
1179                                 event_code = VIEW_APPLY;
1180                                 break;
1181                         case VIEWROT_MODAL_SWITCH_ZOOM:
1182                                 WM_operator_name_call(C, "VIEW3D_OT_zoom", WM_OP_INVOKE_DEFAULT, NULL);
1183                                 event_code = VIEW_CONFIRM;
1184                                 break;
1185                         case VIEWROT_MODAL_SWITCH_MOVE:
1186                                 WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL);
1187                                 event_code = VIEW_CONFIRM;
1188                                 break;
1189                 }
1190         }
1191         else if (event->type == vod->origkey && event->val == KM_RELEASE) {
1192                 event_code = VIEW_CONFIRM;
1193         }
1194
1195         if (event_code == VIEW_APPLY) {
1196                 viewrotate_apply(vod, event->x, event->y);
1197                 if (ED_screen_animation_playing(CTX_wm_manager(C))) {
1198                         use_autokey = true;
1199                 }
1200         }
1201         else if (event_code == VIEW_CONFIRM) {
1202                 ED_view3d_depth_tag_update(vod->rv3d);
1203                 use_autokey = true;
1204                 ret = OPERATOR_FINISHED;
1205         }
1206
1207         if (use_autokey) {
1208                 ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, true, true);
1209         }
1210
1211         if (ret & OPERATOR_FINISHED) {
1212                 viewops_data_free(C, op);
1213         }
1214
1215         return ret;
1216 }
1217
1218 /**
1219  * Action to take when rotating the view,
1220  * handle auto-persp and logic for switching out of views.
1221  *
1222  * shared with NDOF.
1223  */
1224 static bool view3d_ensure_persp(struct View3D *v3d, ARegion *ar)
1225 {
1226         RegionView3D *rv3d = ar->regiondata;
1227         const bool autopersp = (U.uiflag & USER_AUTOPERSP) != 0;
1228
1229         BLI_assert((rv3d->viewlock & RV3D_LOCKED) == 0);
1230
1231         if (ED_view3d_camera_lock_check(v3d, rv3d))
1232                 return false;
1233
1234         if (rv3d->persp != RV3D_PERSP) {
1235                 if (rv3d->persp == RV3D_CAMOB) {
1236                         /* If autopersp and previous view was an axis one, switch back to PERSP mode, else reuse previous mode. */
1237                         char persp = (autopersp && RV3D_VIEW_IS_AXIS(rv3d->lview)) ? RV3D_PERSP : rv3d->lpersp;
1238                         view3d_persp_switch_from_camera(v3d, rv3d, persp);
1239                 }
1240                 else if (autopersp && RV3D_VIEW_IS_AXIS(rv3d->view)) {
1241                         rv3d->persp = RV3D_PERSP;
1242                 }
1243                 return true;
1244         }
1245
1246         return false;
1247 }
1248
1249 static int viewrotate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1250 {
1251         ViewOpsData *vod;
1252
1253         /* makes op->customdata */
1254         viewops_data_alloc(C, op);
1255         vod = op->customdata;
1256
1257         /* poll should check but in some cases fails, see poll func for details */
1258         if (vod->rv3d->viewlock & RV3D_LOCKED) {
1259                 viewops_data_free(C, op);
1260                 return OPERATOR_PASS_THROUGH;
1261         }
1262
1263         ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->ar);
1264
1265         viewops_data_create(C, op, event, true);
1266
1267         if (ELEM(event->type, MOUSEPAN, MOUSEROTATE)) {
1268                 /* Rotate direction we keep always same */
1269                 int x, y;
1270
1271                 if (event->type == MOUSEPAN) {
1272                         if (U.uiflag2 & USER_TRACKPAD_NATURAL) {
1273                                 x = 2 * event->x - event->prevx;
1274                                 y = 2 * event->y - event->prevy;
1275                         }
1276                         else {
1277                                 x = event->prevx;
1278                                 y = event->prevy;
1279                         }
1280                 }
1281                 else {
1282                         /* MOUSEROTATE performs orbital rotation, so y axis delta is set to 0 */
1283                         x = event->prevx;
1284                         y = event->y;
1285                 }
1286
1287                 viewrotate_apply(vod, x, y);
1288                 ED_view3d_depth_tag_update(vod->rv3d);
1289
1290                 viewops_data_free(C, op);
1291
1292                 return OPERATOR_FINISHED;
1293         }
1294         else {
1295                 /* add temp handler */
1296                 WM_event_add_modal_handler(C, op);
1297
1298                 return OPERATOR_RUNNING_MODAL;
1299         }
1300 }
1301
1302 /* test for unlocked camera view in quad view */
1303 static int view3d_camera_user_poll(bContext *C)
1304 {
1305         View3D *v3d;
1306         ARegion *ar;
1307
1308         if (ED_view3d_context_user_region(C, &v3d, &ar)) {
1309                 RegionView3D *rv3d = ar->regiondata;
1310                 if (rv3d->persp == RV3D_CAMOB) {
1311                         return 1;
1312                 }
1313         }
1314
1315         return 0;
1316 }
1317
1318 static int view3d_lock_poll(bContext *C)
1319 {
1320         View3D *v3d = CTX_wm_view3d(C);
1321         if (v3d) {
1322                 RegionView3D *rv3d = CTX_wm_region_view3d(C);
1323                 if (rv3d) {
1324                         return ED_view3d_offset_lock_check(v3d, rv3d);
1325                 }
1326         }
1327         return false;
1328 }
1329
1330 static void viewrotate_cancel(bContext *C, wmOperator *op)
1331 {
1332         viewops_data_free(C, op);
1333 }
1334
1335 void VIEW3D_OT_rotate(wmOperatorType *ot)
1336 {
1337         /* identifiers */
1338         ot->name = "Rotate View";
1339         ot->description = "Rotate the view";
1340         ot->idname = "VIEW3D_OT_rotate";
1341
1342         /* api callbacks */
1343         ot->invoke = viewrotate_invoke;
1344         ot->modal = viewrotate_modal;
1345         ot->poll = ED_operator_region_view3d_active;
1346         ot->cancel = viewrotate_cancel;
1347
1348         /* flags */
1349         ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR;
1350 }
1351
1352 #ifdef WITH_INPUT_NDOF
1353
1354 /** \name NDOF Utility Functions
1355  * \{ */
1356
1357 #define NDOF_HAS_TRANSLATE ((!ED_view3d_offset_lock_check(v3d, rv3d)) && !is_zero_v3(ndof->tvec))
1358 #define NDOF_HAS_ROTATE    (((rv3d->viewlock & RV3D_LOCKED) == 0) && !is_zero_v3(ndof->rvec))
1359
1360 /**
1361  * \param depth_pt: A point to calculate the depth (in perspective mode)
1362  */
1363 static float view3d_ndof_pan_speed_calc_ex(RegionView3D *rv3d, const float depth_pt[3])
1364 {
1365         float speed = rv3d->pixsize * NDOF_PIXELS_PER_SECOND;
1366
1367         if (rv3d->is_persp) {
1368                 speed *= ED_view3d_calc_zfac(rv3d, depth_pt, NULL);
1369         }
1370
1371         return speed;
1372 }
1373
1374 static float view3d_ndof_pan_speed_calc_from_dist(RegionView3D *rv3d, const float dist)
1375 {
1376         float viewinv[4];
1377         float tvec[3];
1378
1379         BLI_assert(dist >= 0.0f);
1380
1381         copy_v3_fl3(tvec, 0.0f, 0.0f, dist);
1382         /* rv3d->viewinv isn't always valid */
1383 #if 0
1384         mul_mat3_m4_v3(rv3d->viewinv, tvec);
1385 #else
1386         invert_qt_qt_normalized(viewinv, rv3d->viewquat);
1387         mul_qt_v3(viewinv, tvec);
1388 #endif
1389
1390         return view3d_ndof_pan_speed_calc_ex(rv3d, tvec);
1391 }
1392
1393 static float view3d_ndof_pan_speed_calc(RegionView3D *rv3d)
1394 {
1395         float tvec[3];
1396         negate_v3_v3(tvec, rv3d->ofs);
1397
1398         return view3d_ndof_pan_speed_calc_ex(rv3d, tvec);
1399 }
1400
1401 /**
1402  * Zoom and pan in the same function since sometimes zoom is interpreted as dolly (pan forward).
1403  *
1404  * \param has_zoom zoom, otherwise dolly, often `!rv3d->is_persp` since it doesnt make sense to dolly in ortho.
1405  */
1406 static void view3d_ndof_pan_zoom(const struct wmNDOFMotionData *ndof, ScrArea *sa, ARegion *ar,
1407                                  const bool has_translate, const bool has_zoom)
1408 {
1409         RegionView3D *rv3d = ar->regiondata;
1410         float view_inv[4];
1411         float pan_vec[3];
1412
1413         if (has_translate == false && has_zoom == false) {
1414                 return;
1415         }
1416
1417         WM_event_ndof_pan_get(ndof, pan_vec, false);
1418
1419         if (has_zoom) {
1420                 /* zoom with Z */
1421
1422                 /* Zoom!
1423                  * velocity should be proportional to the linear velocity attained by rotational motion of same strength
1424                  * [got that?]
1425                  * proportional to arclength = radius * angle
1426                  */
1427
1428                 pan_vec[2] = 0.0f;
1429
1430                 /* "zoom in" or "translate"? depends on zoom mode in user settings? */
1431                 if (ndof->tvec[2]) {
1432                         float zoom_distance = rv3d->dist * ndof->dt * ndof->tvec[2];
1433
1434                         if (U.ndof_flag & NDOF_ZOOM_INVERT)
1435                                 zoom_distance = -zoom_distance;
1436
1437                         rv3d->dist += zoom_distance;
1438                 }
1439         }
1440         else {
1441                 /* dolly with Z */
1442
1443                 /* all callers must check */
1444                 if (has_translate) {
1445                         BLI_assert(ED_view3d_offset_lock_check((View3D *)sa->spacedata.first, rv3d) == false);
1446                 }
1447         }
1448
1449         if (has_translate) {
1450                 const float speed = view3d_ndof_pan_speed_calc(rv3d);
1451
1452                 mul_v3_fl(pan_vec, speed * ndof->dt);
1453
1454                 /* transform motion from view to world coordinates */
1455                 invert_qt_qt_normalized(view_inv, rv3d->viewquat);
1456                 mul_qt_v3(view_inv, pan_vec);
1457
1458                 /* move center of view opposite of hand motion (this is camera mode, not object mode) */
1459                 sub_v3_v3(rv3d->ofs, pan_vec);
1460
1461                 if (rv3d->viewlock & RV3D_BOXVIEW) {
1462                         view3d_boxview_sync(sa, ar);
1463                 }
1464         }
1465 }
1466
1467
1468 static void view3d_ndof_orbit(const struct wmNDOFMotionData *ndof, ScrArea *sa, ARegion *ar,
1469                               /* optional, can be NULL*/
1470                               ViewOpsData *vod)
1471 {
1472         View3D *v3d = sa->spacedata.first;
1473         RegionView3D *rv3d = ar->regiondata;
1474
1475         float view_inv[4];
1476
1477         BLI_assert((rv3d->viewlock & RV3D_LOCKED) == 0);
1478
1479         view3d_ensure_persp(v3d, ar);
1480
1481         rv3d->view = RV3D_VIEW_USER;
1482
1483         invert_qt_qt_normalized(view_inv, rv3d->viewquat);
1484
1485         if (U.ndof_flag & NDOF_TURNTABLE) {
1486                 float rot[3];
1487
1488                 /* turntable view code by John Aughey, adapted for 3D mouse by [mce] */
1489                 float angle, quat[4];
1490                 float xvec[3] = {1, 0, 0};
1491
1492                 /* only use XY, ignore Z */
1493                 WM_event_ndof_rotate_get(ndof, rot);
1494
1495                 /* Determine the direction of the x vector (for rotating up and down) */
1496                 mul_qt_v3(view_inv, xvec);
1497
1498                 /* Perform the up/down rotation */
1499                 angle = ndof->dt * rot[0];
1500                 axis_angle_to_quat(quat, xvec, angle);
1501                 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, quat);
1502
1503                 /* Perform the orbital rotation */
1504                 angle = ndof->dt * rot[1];
1505
1506                 /* update the onscreen doo-dad */
1507                 rv3d->rot_angle = angle;
1508                 rv3d->rot_axis[0] = 0;
1509                 rv3d->rot_axis[1] = 0;
1510                 rv3d->rot_axis[2] = 1;
1511
1512                 axis_angle_to_quat_single(quat, 'Z', angle);
1513                 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, quat);
1514
1515         }
1516         else {
1517                 float quat[4];
1518                 float axis[3];
1519                 float angle = WM_event_ndof_to_axis_angle(ndof, axis);
1520
1521                 /* transform rotation axis from view to world coordinates */
1522                 mul_qt_v3(view_inv, axis);
1523
1524                 /* update the onscreen doo-dad */
1525                 rv3d->rot_angle = angle;
1526                 copy_v3_v3(rv3d->rot_axis, axis);
1527
1528                 axis_angle_to_quat(quat, axis, angle);
1529
1530                 /* apply rotation */
1531                 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, quat);
1532         }
1533
1534         if (vod) {
1535                 viewrotate_apply_dyn_ofs(vod, rv3d->viewquat);
1536         }
1537 }
1538
1539 /**
1540  * Called from both fly mode and walk mode,
1541  */
1542 void view3d_ndof_fly(
1543         const wmNDOFMotionData *ndof,
1544         View3D *v3d, RegionView3D *rv3d,
1545         const bool use_precision, const short protectflag,
1546         bool *r_has_translate, bool *r_has_rotate)
1547 {
1548         bool has_translate = NDOF_HAS_TRANSLATE;
1549         bool has_rotate = NDOF_HAS_ROTATE;
1550
1551         float view_inv[4];
1552         invert_qt_qt_normalized(view_inv, rv3d->viewquat);
1553
1554         rv3d->rot_angle = 0.0f;  /* disable onscreen rotation doo-dad */
1555
1556         if (has_translate) {
1557                 /* ignore real 'dist' since fly has its own speed settings,
1558                  * also its overwritten at this point. */
1559                 float speed = view3d_ndof_pan_speed_calc_from_dist(rv3d, 1.0f);
1560                 float trans[3], trans_orig_y;
1561
1562                 if (use_precision)
1563                         speed *= 0.2f;
1564
1565                 WM_event_ndof_pan_get(ndof, trans, false);
1566                 mul_v3_fl(trans, speed * ndof->dt);
1567                 trans_orig_y = trans[1];
1568
1569                 if (U.ndof_flag & NDOF_FLY_HELICOPTER) {
1570                         trans[1] = 0.0f;
1571                 }
1572
1573                 /* transform motion from view to world coordinates */
1574                 mul_qt_v3(view_inv, trans);
1575
1576                 if (U.ndof_flag & NDOF_FLY_HELICOPTER) {
1577                         /* replace world z component with device y (yes it makes sense) */
1578                         trans[2] = trans_orig_y;
1579                 }
1580
1581                 if (rv3d->persp == RV3D_CAMOB) {
1582                         /* respect camera position locks */
1583                         if (protectflag & OB_LOCK_LOCX) trans[0] = 0.0f;
1584                         if (protectflag & OB_LOCK_LOCY) trans[1] = 0.0f;
1585                         if (protectflag & OB_LOCK_LOCZ) trans[2] = 0.0f;
1586                 }
1587
1588                 if (!is_zero_v3(trans)) {
1589                         /* move center of view opposite of hand motion (this is camera mode, not object mode) */
1590                         sub_v3_v3(rv3d->ofs, trans);
1591                         has_translate = true;
1592                 }
1593                 else {
1594                         has_translate = false;
1595                 }
1596         }
1597
1598         if (has_rotate) {
1599                 const float turn_sensitivity = 1.0f;
1600
1601                 float rotation[4];
1602                 float axis[3];
1603                 float angle = turn_sensitivity * WM_event_ndof_to_axis_angle(ndof, axis);
1604
1605                 if (fabsf(angle) > 0.0001f) {
1606                         has_rotate = true;
1607
1608                         if (use_precision)
1609                                 angle *= 0.2f;
1610
1611                         /* transform rotation axis from view to world coordinates */
1612                         mul_qt_v3(view_inv, axis);
1613
1614                         /* apply rotation to view */
1615                         axis_angle_to_quat(rotation, axis, angle);
1616                         mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rotation);
1617
1618                         if (U.ndof_flag & NDOF_LOCK_HORIZON) {
1619                                 /* force an upright viewpoint
1620                                  * TODO: make this less... sudden */
1621                                 float view_horizon[3] = {1.0f, 0.0f, 0.0f}; /* view +x */
1622                                 float view_direction[3] = {0.0f, 0.0f, -1.0f}; /* view -z (into screen) */
1623
1624                                 /* find new inverse since viewquat has changed */
1625                                 invert_qt_qt_normalized(view_inv, rv3d->viewquat);
1626                                 /* could apply reverse rotation to existing view_inv to save a few cycles */
1627
1628                                 /* transform view vectors to world coordinates */
1629                                 mul_qt_v3(view_inv, view_horizon);
1630                                 mul_qt_v3(view_inv, view_direction);
1631
1632
1633                                 /* find difference between view & world horizons
1634                                  * true horizon lives in world xy plane, so look only at difference in z */
1635                                 angle = -asinf(view_horizon[2]);
1636
1637                                 /* rotate view so view horizon = world horizon */
1638                                 axis_angle_to_quat(rotation, view_direction, angle);
1639                                 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rotation);
1640                         }
1641
1642                         rv3d->view = RV3D_VIEW_USER;
1643                 }
1644                 else {
1645                         has_rotate = false;
1646                 }
1647         }
1648
1649         *r_has_translate = has_translate;
1650         *r_has_rotate    = has_rotate;
1651 }
1652
1653 /** \} */
1654
1655
1656 /* -- "orbit" navigation (trackball/turntable)
1657  * -- zooming
1658  * -- panning in rotationally-locked views
1659  */
1660 static int ndof_orbit_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1661 {
1662         
1663         if (event->type != NDOF_MOTION) {
1664                 return OPERATOR_CANCELLED;
1665         }
1666         else {
1667                 ViewOpsData *vod;
1668                 View3D *v3d;
1669                 RegionView3D *rv3d;
1670
1671                 const wmNDOFMotionData *ndof = event->customdata;
1672
1673                 viewops_data_alloc(C, op);
1674                 viewops_data_create_ex(
1675                         C, op, event,
1676                         viewops_orbit_mode_ex((U.uiflag & USER_ORBIT_SELECTION) != 0, false), false);
1677                 vod = op->customdata;
1678
1679                 ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->ar);
1680
1681                 v3d = vod->v3d;
1682                 rv3d = vod->rv3d;
1683
1684                 /* off by default, until changed later this function */
1685                 rv3d->rot_angle = 0.0f;
1686
1687                 ED_view3d_camera_lock_init_ex(v3d, rv3d, false);
1688
1689                 if (ndof->progress != P_FINISHING) {
1690                         const bool has_rotation = NDOF_HAS_ROTATE;
1691                         /* if we can't rotate, fallback to translate (locked axis views) */
1692                         const bool has_translate = NDOF_HAS_TRANSLATE && (rv3d->viewlock & RV3D_LOCKED);
1693                         const bool has_zoom = (ndof->tvec[2] != 0.0f) && !rv3d->is_persp;
1694
1695                         if (has_translate || has_zoom) {
1696                                 view3d_ndof_pan_zoom(ndof, vod->sa, vod->ar, has_translate, has_zoom);
1697                         }
1698
1699                         if (has_rotation) {
1700                                 view3d_ndof_orbit(ndof, vod->sa, vod->ar, vod);
1701                         }
1702                 }
1703
1704                 ED_view3d_camera_lock_sync(v3d, rv3d);
1705
1706                 ED_region_tag_redraw(vod->ar);
1707
1708                 viewops_data_free(C, op);
1709
1710                 return OPERATOR_FINISHED;
1711         }
1712 }
1713
1714 void VIEW3D_OT_ndof_orbit(struct wmOperatorType *ot)
1715 {
1716         /* identifiers */
1717         ot->name = "NDOF Orbit View";
1718         ot->description = "Orbit the view using the 3D mouse";
1719         ot->idname = "VIEW3D_OT_ndof_orbit";
1720
1721         /* api callbacks */
1722         ot->invoke = ndof_orbit_invoke;
1723         ot->poll = ED_operator_view3d_active;
1724
1725         /* flags */
1726         ot->flag = 0;
1727 }
1728
1729 static int ndof_orbit_zoom_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1730 {
1731         
1732         if (event->type != NDOF_MOTION) {
1733                 return OPERATOR_CANCELLED;
1734         }
1735         else {
1736                 ViewOpsData *vod;
1737                 View3D *v3d;
1738                 RegionView3D *rv3d;
1739
1740                 const wmNDOFMotionData *ndof = event->customdata;
1741
1742                 viewops_data_alloc(C, op);
1743                 viewops_data_create_ex(
1744                         C, op, event,
1745                         viewops_orbit_mode_ex((U.uiflag & USER_ORBIT_SELECTION) != 0, false), false);
1746
1747                 vod = op->customdata;
1748
1749                 ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->ar);
1750
1751                 v3d = vod->v3d;
1752                 rv3d = vod->rv3d;
1753
1754                 /* off by default, until changed later this function */
1755                 rv3d->rot_angle = 0.0f;
1756
1757                 ED_view3d_camera_lock_init_ex(v3d, rv3d, false);
1758
1759                 if (ndof->progress == P_FINISHING) {
1760                         /* pass */
1761                 }
1762                 else if ((rv3d->persp == RV3D_ORTHO) && RV3D_VIEW_IS_AXIS(rv3d->view)) {
1763                         /* if we can't rotate, fallback to translate (locked axis views) */
1764                         const bool has_translate = NDOF_HAS_TRANSLATE;
1765                         const bool has_zoom = (ndof->tvec[2] != 0.0f) && ED_view3d_offset_lock_check(v3d, rv3d);
1766
1767                         if (has_translate || has_zoom) {
1768                                 view3d_ndof_pan_zoom(ndof, vod->sa, vod->ar, has_translate, true);
1769                         }
1770                 }
1771                 else if ((U.ndof_flag & NDOF_MODE_ORBIT) ||
1772                          ED_view3d_offset_lock_check(v3d, rv3d))
1773                 {
1774                         const bool has_rotation = NDOF_HAS_ROTATE;
1775                         const bool has_zoom = (ndof->tvec[2] != 0.0f);
1776
1777                         if (has_zoom) {
1778                                 view3d_ndof_pan_zoom(ndof, vod->sa, vod->ar, false, has_zoom);
1779                         }
1780
1781                         if (has_rotation) {
1782                                 view3d_ndof_orbit(ndof, vod->sa, vod->ar, vod);
1783                         }
1784                 }
1785                 else {  /* free/explore (like fly mode) */
1786                         const bool has_rotation = NDOF_HAS_ROTATE;
1787                         const bool has_translate = NDOF_HAS_TRANSLATE;
1788                         const bool has_zoom = (ndof->tvec[2] != 0.0f) && !rv3d->is_persp;
1789
1790                         float dist_backup;
1791
1792                         if (has_translate || has_zoom) {
1793                                 view3d_ndof_pan_zoom(ndof, vod->sa, vod->ar, has_translate, has_zoom);
1794                         }
1795
1796                         dist_backup = rv3d->dist;
1797                         ED_view3d_distance_set(rv3d, 0.0f);
1798
1799                         if (has_rotation) {
1800                                 view3d_ndof_orbit(ndof, vod->sa, vod->ar, NULL);
1801                         }
1802
1803                         ED_view3d_distance_set(rv3d, dist_backup);
1804                 }
1805
1806                 ED_view3d_camera_lock_sync(v3d, rv3d);
1807
1808                 ED_region_tag_redraw(vod->ar);
1809
1810                 viewops_data_free(C, op);
1811
1812                 return OPERATOR_FINISHED;
1813         }
1814 }
1815
1816 void VIEW3D_OT_ndof_orbit_zoom(struct wmOperatorType *ot)
1817 {
1818         /* identifiers */
1819         ot->name = "NDOF Orbit View with Zoom";
1820         ot->description = "Orbit and zoom the view using the 3D mouse";
1821         ot->idname = "VIEW3D_OT_ndof_orbit_zoom";
1822
1823         /* api callbacks */
1824         ot->invoke = ndof_orbit_zoom_invoke;
1825         ot->poll = ED_operator_view3d_active;
1826
1827         /* flags */
1828         ot->flag = 0;
1829 }
1830
1831 /* -- "pan" navigation
1832  * -- zoom or dolly?
1833  */
1834 static int ndof_pan_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
1835 {
1836         if (event->type != NDOF_MOTION) {
1837                 return OPERATOR_CANCELLED;
1838         }
1839         else {
1840                 View3D *v3d = CTX_wm_view3d(C);
1841                 RegionView3D *rv3d = CTX_wm_region_view3d(C);
1842                 const wmNDOFMotionData *ndof = event->customdata;
1843
1844                 const bool has_translate = NDOF_HAS_TRANSLATE;
1845                 const bool has_zoom = (ndof->tvec[2] != 0.0f) && !rv3d->is_persp;
1846
1847                 /* we're panning here! so erase any leftover rotation from other operators */
1848                 rv3d->rot_angle = 0.0f;
1849
1850                 if (!(has_translate || has_zoom))
1851                         return OPERATOR_CANCELLED;
1852
1853                 ED_view3d_camera_lock_init_ex(v3d, rv3d, false);
1854
1855                 if (ndof->progress != P_FINISHING) {
1856                         ScrArea *sa = CTX_wm_area(C);
1857                         ARegion *ar = CTX_wm_region(C);
1858
1859                         if (has_translate || has_zoom) {
1860                                 view3d_ndof_pan_zoom(ndof, sa, ar, has_translate, has_zoom);
1861                         }
1862                 }
1863
1864                 ED_view3d_camera_lock_sync(v3d, rv3d);
1865
1866                 ED_region_tag_redraw(CTX_wm_region(C));
1867
1868                 return OPERATOR_FINISHED;
1869         }
1870 }
1871
1872 void VIEW3D_OT_ndof_pan(struct wmOperatorType *ot)
1873 {
1874         /* identifiers */
1875         ot->name = "NDOF Pan View";
1876         ot->description = "Pan the view with the 3D mouse";
1877         ot->idname = "VIEW3D_OT_ndof_pan";
1878
1879         /* api callbacks */
1880         ot->invoke = ndof_pan_invoke;
1881         ot->poll = ED_operator_view3d_active;
1882
1883         /* flags */
1884         ot->flag = 0;
1885 }
1886
1887
1888 /**
1889  * wraps #ndof_orbit_zoom but never restrict to orbit.
1890  */
1891 static int ndof_all_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1892 {
1893         /* weak!, but it works */
1894         const int ndof_flag = U.ndof_flag;
1895         int ret;
1896
1897         U.ndof_flag &= ~NDOF_MODE_ORBIT;
1898
1899         ret = ndof_orbit_zoom_invoke(C, op, event);
1900
1901         U.ndof_flag = ndof_flag;
1902
1903         return ret;
1904 }
1905
1906 void VIEW3D_OT_ndof_all(struct wmOperatorType *ot)
1907 {
1908         /* identifiers */
1909         ot->name = "NDOF Move View";
1910         ot->description = "Pan and rotate the view with the 3D mouse";
1911         ot->idname = "VIEW3D_OT_ndof_all";
1912
1913         /* api callbacks */
1914         ot->invoke = ndof_all_invoke;
1915         ot->poll = ED_operator_view3d_active;
1916
1917         /* flags */
1918         ot->flag = 0;
1919 }
1920
1921 #endif /* WITH_INPUT_NDOF */
1922
1923 /* ************************ viewmove ******************************** */
1924
1925
1926 /* NOTE: these defines are saved in keymap files, do not change values but just add new ones */
1927
1928 /* called in transform_ops.c, on each regeneration of keymaps  */
1929 void viewmove_modal_keymap(wmKeyConfig *keyconf)
1930 {
1931         static EnumPropertyItem modal_items[] = {
1932                 {VIEW_MODAL_CONFIRM,    "CONFIRM", 0, "Confirm", ""},
1933                 
1934                 {VIEWROT_MODAL_SWITCH_ZOOM, "SWITCH_TO_ZOOM", 0, "Switch to Zoom"},
1935                 {VIEWROT_MODAL_SWITCH_ROTATE, "SWITCH_TO_ROTATE", 0, "Switch to Rotate"},
1936
1937                 {0, NULL, 0, NULL, NULL}
1938         };
1939
1940         wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "View3D Move Modal");
1941
1942         /* this function is called for each spacetype, only needs to add map once */
1943         if (keymap && keymap->modal_items) return;
1944
1945         keymap = WM_modalkeymap_add(keyconf, "View3D Move Modal", modal_items);
1946
1947         /* items for modal map */
1948         WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_RELEASE, KM_ANY, 0, VIEW_MODAL_CONFIRM);
1949         WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, VIEW_MODAL_CONFIRM);
1950
1951         /* disabled mode switching for now, can re-implement better, later on */
1952 #if 0
1953         WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM);
1954         WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM);
1955         WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
1956 #endif
1957         
1958         /* assign map to operators */
1959         WM_modalkeymap_assign(keymap, "VIEW3D_OT_move");
1960 }
1961
1962
1963 static void viewmove_apply(ViewOpsData *vod, int x, int y)
1964 {
1965         if (ED_view3d_offset_lock_check(vod->v3d, vod->rv3d)) {
1966                 vod->rv3d->ofs_lock[0] -= ((vod->oldx - x) * 2.0f) / (float)vod->ar->winx;
1967                 vod->rv3d->ofs_lock[1] -= ((vod->oldy - y) * 2.0f) / (float)vod->ar->winy;
1968         }
1969         else if ((vod->rv3d->persp == RV3D_CAMOB) && !ED_view3d_camera_lock_check(vod->v3d, vod->rv3d)) {
1970                 const float zoomfac = BKE_screen_view3d_zoom_to_fac(vod->rv3d->camzoom) * 2.0f;
1971                 vod->rv3d->camdx += (vod->oldx - x) / (vod->ar->winx * zoomfac);
1972                 vod->rv3d->camdy += (vod->oldy - y) / (vod->ar->winy * zoomfac);
1973                 CLAMP(vod->rv3d->camdx, -1.0f, 1.0f);
1974                 CLAMP(vod->rv3d->camdy, -1.0f, 1.0f);
1975         }
1976         else {
1977                 float dvec[3];
1978                 float mval_f[2];
1979
1980                 mval_f[0] = x - vod->oldx;
1981                 mval_f[1] = y - vod->oldy;
1982                 ED_view3d_win_to_delta(vod->ar, mval_f, dvec, vod->zfac);
1983
1984                 add_v3_v3(vod->rv3d->ofs, dvec);
1985
1986                 if (vod->rv3d->viewlock & RV3D_BOXVIEW)
1987                         view3d_boxview_sync(vod->sa, vod->ar);
1988         }
1989
1990         vod->oldx = x;
1991         vod->oldy = y;
1992
1993         ED_view3d_camera_lock_sync(vod->v3d, vod->rv3d);
1994
1995         ED_region_tag_redraw(vod->ar);
1996 }
1997
1998
1999 static int viewmove_modal(bContext *C, wmOperator *op, const wmEvent *event)
2000 {
2001
2002         ViewOpsData *vod = op->customdata;
2003         short event_code = VIEW_PASS;
2004         bool use_autokey = false;
2005         int ret = OPERATOR_RUNNING_MODAL;
2006
2007         /* execute the events */
2008         if (event->type == MOUSEMOVE) {
2009                 event_code = VIEW_APPLY;
2010         }
2011         else if (event->type == EVT_MODAL_MAP) {
2012                 switch (event->val) {
2013                         case VIEW_MODAL_CONFIRM:
2014                                 event_code = VIEW_CONFIRM;
2015                                 break;
2016                         case VIEWROT_MODAL_SWITCH_ZOOM:
2017                                 WM_operator_name_call(C, "VIEW3D_OT_zoom", WM_OP_INVOKE_DEFAULT, NULL);
2018                                 event_code = VIEW_CONFIRM;
2019                                 break;
2020                         case VIEWROT_MODAL_SWITCH_ROTATE:
2021                                 WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL);
2022                                 event_code = VIEW_CONFIRM;
2023                                 break;
2024                 }
2025         }
2026         else if (event->type == vod->origkey && event->val == KM_RELEASE) {
2027                 event_code = VIEW_CONFIRM;
2028         }
2029
2030         if (event_code == VIEW_APPLY) {
2031                 viewmove_apply(vod, event->x, event->y);
2032                 if (ED_screen_animation_playing(CTX_wm_manager(C))) {
2033                         use_autokey = true;
2034                 }
2035         }
2036         else if (event_code == VIEW_CONFIRM) {
2037                 ED_view3d_depth_tag_update(vod->rv3d);
2038                 use_autokey = true;
2039                 ret = OPERATOR_FINISHED;
2040         }
2041
2042         if (use_autokey) {
2043                 ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, false, true);
2044         }
2045
2046         if (ret & OPERATOR_FINISHED) {
2047                 viewops_data_free(C, op);
2048         }
2049
2050         return ret;
2051 }
2052
2053 static int viewmove_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2054 {
2055         ViewOpsData *vod;
2056
2057         /* makes op->customdata */
2058         viewops_data_alloc(C, op);
2059         viewops_data_create(C, op, event, false);
2060         vod = op->customdata;
2061
2062         ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->ar);
2063
2064         if (event->type == MOUSEPAN) {
2065                 /* invert it, trackpad scroll follows same principle as 2d windows this way */
2066                 viewmove_apply(vod, 2 * event->x - event->prevx, 2 * event->y - event->prevy);
2067                 ED_view3d_depth_tag_update(vod->rv3d);
2068                 
2069                 viewops_data_free(C, op);
2070                 
2071                 return OPERATOR_FINISHED;
2072         }
2073         else {
2074                 /* add temp handler */
2075                 WM_event_add_modal_handler(C, op);
2076
2077                 return OPERATOR_RUNNING_MODAL;
2078         }
2079 }
2080
2081 static void viewmove_cancel(bContext *C, wmOperator *op)
2082 {
2083         viewops_data_free(C, op);
2084 }
2085
2086 void VIEW3D_OT_move(wmOperatorType *ot)
2087 {
2088
2089         /* identifiers */
2090         ot->name = "Move View";
2091         ot->description = "Move the view";
2092         ot->idname = "VIEW3D_OT_move";
2093
2094         /* api callbacks */
2095         ot->invoke = viewmove_invoke;
2096         ot->modal = viewmove_modal;
2097         ot->poll = ED_operator_view3d_active;
2098         ot->cancel = viewmove_cancel;
2099
2100         /* flags */
2101         ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR;
2102 }
2103
2104 /* ************************ viewzoom ******************************** */
2105
2106 /* viewdolly_modal_keymap has an exact copy of this, apply fixes to both */
2107 /* called in transform_ops.c, on each regeneration of keymaps  */
2108 void viewzoom_modal_keymap(wmKeyConfig *keyconf)
2109 {
2110         static EnumPropertyItem modal_items[] = {
2111                 {VIEW_MODAL_CONFIRM,    "CONFIRM", 0, "Confirm", ""},
2112                 
2113                 {VIEWROT_MODAL_SWITCH_ROTATE, "SWITCH_TO_ROTATE", 0, "Switch to Rotate"},
2114                 {VIEWROT_MODAL_SWITCH_MOVE, "SWITCH_TO_MOVE", 0, "Switch to Move"},
2115
2116                 {0, NULL, 0, NULL, NULL}
2117         };
2118
2119         wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "View3D Zoom Modal");
2120
2121         /* this function is called for each spacetype, only needs to add map once */
2122         if (keymap && keymap->modal_items) return;
2123
2124         keymap = WM_modalkeymap_add(keyconf, "View3D Zoom Modal", modal_items);
2125
2126         /* items for modal map */
2127         WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_RELEASE, KM_ANY, 0, VIEW_MODAL_CONFIRM);
2128         WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, VIEW_MODAL_CONFIRM);
2129
2130         /* disabled mode switching for now, can re-implement better, later on */
2131 #if 0
2132         WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
2133         WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
2134         WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_MOVE);
2135 #endif
2136         
2137         /* assign map to operators */
2138         WM_modalkeymap_assign(keymap, "VIEW3D_OT_zoom");
2139 }
2140
2141 static void view_zoom_mouseloc_camera(
2142         Scene *scene, View3D *v3d,
2143         ARegion *ar, float dfac, int mx, int my)
2144 {
2145         RegionView3D *rv3d = ar->regiondata;
2146         const float zoomfac = BKE_screen_view3d_zoom_to_fac(rv3d->camzoom);
2147         const float zoomfac_new = CLAMPIS(zoomfac * (1.0f / dfac), RV3D_CAMZOOM_MIN_FACTOR, RV3D_CAMZOOM_MAX_FACTOR);
2148         const float camzoom_new = BKE_screen_view3d_zoom_from_fac(zoomfac_new);
2149
2150
2151         if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
2152                 float zoomfac_px;
2153                 rctf camera_frame_old;
2154                 rctf camera_frame_new;
2155
2156                 const float pt_src[2] = {mx, my};
2157                 float pt_dst[2];
2158                 float delta_px[2];
2159
2160                 ED_view3d_calc_camera_border(scene, ar, v3d, rv3d, &camera_frame_old, false);
2161                 BLI_rctf_translate(&camera_frame_old, ar->winrct.xmin, ar->winrct.ymin);
2162
2163                 rv3d->camzoom = camzoom_new;
2164                 CLAMP(rv3d->camzoom, RV3D_CAMZOOM_MIN, RV3D_CAMZOOM_MAX);
2165
2166                 ED_view3d_calc_camera_border(scene, ar, v3d, rv3d, &camera_frame_new, false);
2167                 BLI_rctf_translate(&camera_frame_new, ar->winrct.xmin, ar->winrct.ymin);
2168
2169                 BLI_rctf_transform_pt_v(&camera_frame_new, &camera_frame_old, pt_dst, pt_src);
2170                 sub_v2_v2v2(delta_px, pt_dst, pt_src);
2171
2172                 /* translate the camera offset using pixel space delta
2173                  * mapped back to the camera (same logic as panning in camera view) */
2174                 zoomfac_px = BKE_screen_view3d_zoom_to_fac(rv3d->camzoom) * 2.0f;
2175
2176                 rv3d->camdx += delta_px[0] / (ar->winx * zoomfac_px);
2177                 rv3d->camdy += delta_px[1] / (ar->winy * zoomfac_px);
2178                 CLAMP(rv3d->camdx, -1.0f, 1.0f);
2179                 CLAMP(rv3d->camdy, -1.0f, 1.0f);
2180         }
2181         else {
2182                 rv3d->camzoom = camzoom_new;
2183                 CLAMP(rv3d->camzoom, RV3D_CAMZOOM_MIN, RV3D_CAMZOOM_MAX);
2184         }
2185 }
2186
2187 static void view_zoom_mouseloc_3d(ARegion *ar, float dfac, int mx, int my)
2188 {
2189         RegionView3D *rv3d = ar->regiondata;
2190         const float dist_new = rv3d->dist * dfac;
2191
2192         if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
2193                 float dvec[3];
2194                 float tvec[3];
2195                 float tpos[3];
2196                 float mval_f[2];
2197
2198                 float zfac;
2199
2200                 negate_v3_v3(tpos, rv3d->ofs);
2201
2202                 mval_f[0] = (float)(((mx - ar->winrct.xmin) * 2) - ar->winx) / 2.0f;
2203                 mval_f[1] = (float)(((my - ar->winrct.ymin) * 2) - ar->winy) / 2.0f;
2204
2205                 /* Project cursor position into 3D space */
2206                 zfac = ED_view3d_calc_zfac(rv3d, tpos, NULL);
2207                 ED_view3d_win_to_delta(ar, mval_f, dvec, zfac);
2208
2209                 /* Calculate view target position for dolly */
2210                 add_v3_v3v3(tvec, tpos, dvec);
2211                 negate_v3(tvec);
2212
2213                 /* Offset to target position and dolly */
2214                 copy_v3_v3(rv3d->ofs, tvec);
2215                 rv3d->dist = dist_new;
2216
2217                 /* Calculate final offset */
2218                 madd_v3_v3v3fl(rv3d->ofs, tvec, dvec, dfac);
2219         }
2220         else {
2221                 rv3d->dist = dist_new;
2222         }
2223 }
2224
2225 static float viewzoom_scale_value(
2226         const rcti *winrct,
2227         const short viewzoom,
2228         const bool zoom_invert, const bool zoom_invert_force,
2229         const int xy[2], const int xy_orig[2],
2230         const float val, const float val_orig,
2231         double *r_timer_lastdraw)
2232 {
2233         float zfac;
2234
2235         if (viewzoom == USER_ZOOM_CONT) {
2236                 double time = PIL_check_seconds_timer();
2237                 float time_step = (float)(time - *r_timer_lastdraw);
2238                 float fac;
2239
2240                 if (U.uiflag & USER_ZOOM_HORIZ) {
2241                         fac = (float)(xy_orig[0] - xy[0]);
2242                 }
2243                 else {
2244                         fac = (float)(xy_orig[1] - xy[1]);
2245                 }
2246
2247                 if (zoom_invert != zoom_invert_force) {
2248                         fac = -fac;
2249                 }
2250
2251                 /* oldstyle zoom */
2252                 zfac = 1.0f + ((fac / 20.0f) * time_step);
2253                 *r_timer_lastdraw = time;
2254         }
2255         else if (viewzoom == USER_ZOOM_SCALE) {
2256                 /* method which zooms based on how far you move the mouse */
2257
2258                 const int ctr[2] = {
2259                     BLI_rcti_cent_x(winrct),
2260                     BLI_rcti_cent_y(winrct),
2261                 };
2262                 float len_new = 5 + len_v2v2_int(ctr, xy);
2263                 float len_old = 5 + len_v2v2_int(ctr, xy_orig);
2264
2265                 /* intentionally ignore 'zoom_invert' for scale */
2266                 if (zoom_invert_force) {
2267                         SWAP(float, len_new, len_old);
2268                 }
2269
2270                 zfac = val_orig * (len_old / max_ff(len_new, 1.0f)) / val;
2271         }
2272         else {  /* USER_ZOOM_DOLLY */
2273                 float len_new = 5;
2274                 float len_old = 5;
2275
2276                 if (U.uiflag & USER_ZOOM_HORIZ) {
2277                         len_new += (winrct->xmax - xy[0]);
2278                         len_old += (winrct->xmax - xy_orig[0]);
2279                 }
2280                 else {
2281                         len_new += (winrct->ymax - xy[1]);
2282                         len_old += (winrct->ymax - xy_orig[1]);
2283                 }
2284
2285                 if (zoom_invert != zoom_invert_force) {
2286                         SWAP(float, len_new, len_old);
2287                 }
2288
2289                 zfac = val_orig * (2.0f * ((len_new / max_ff(len_old, 1.0f)) - 1.0f) + 1.0f) / val;
2290         }
2291
2292
2293         return zfac;
2294 }
2295
2296 static void viewzoom_apply_camera(
2297         ViewOpsData *vod, const int xy[2],
2298         const short viewzoom, const bool zoom_invert)
2299 {
2300         float zfac;
2301         float zoomfac_prev = BKE_screen_view3d_zoom_to_fac(vod->camzoom_prev) * 2.0f;
2302         float zoomfac =      BKE_screen_view3d_zoom_to_fac(vod->rv3d->camzoom) * 2.0f;
2303
2304         zfac = viewzoom_scale_value(
2305                &vod->ar->winrct, viewzoom, zoom_invert, true, xy, &vod->origx,
2306                zoomfac, zoomfac_prev,
2307                &vod->timer_lastdraw);
2308
2309         if (zfac != 1.0f && zfac != 0.0f) {
2310                 /* calculate inverted, then invert again (needed because of camera zoom scaling) */
2311                 zfac = 1.0f / zfac;
2312                 view_zoom_mouseloc_camera(
2313                         vod->scene, vod->v3d,
2314                         vod->ar, zfac, vod->oldx, vod->oldy);
2315         }
2316
2317         ED_region_tag_redraw(vod->ar);
2318 }
2319
2320 static void viewzoom_apply_3d(
2321         ViewOpsData *vod, const int xy[2],
2322         const short viewzoom, const bool zoom_invert)
2323 {
2324         float zfac;
2325         float dist_range[2];
2326
2327         ED_view3d_dist_range_get(vod->v3d, dist_range);
2328
2329         zfac = viewzoom_scale_value(
2330                &vod->ar->winrct, viewzoom, zoom_invert, false, xy, &vod->origx,
2331                vod->rv3d->dist, vod->dist_prev,
2332                &vod->timer_lastdraw);
2333
2334         if (zfac != 1.0f) {
2335                 const float zfac_min = dist_range[0] / vod->rv3d->dist;
2336                 const float zfac_max = dist_range[1] / vod->rv3d->dist;
2337                 CLAMP(zfac, zfac_min, zfac_max);
2338
2339                 view_zoom_mouseloc_3d(
2340                         vod->ar, zfac, vod->oldx, vod->oldy);
2341         }
2342
2343         /* these limits were in old code too */
2344         CLAMP(vod->rv3d->dist, dist_range[0], dist_range[1]);
2345
2346         if (vod->rv3d->viewlock & RV3D_BOXVIEW)
2347                 view3d_boxview_sync(vod->sa, vod->ar);
2348
2349         ED_view3d_camera_lock_sync(vod->v3d, vod->rv3d);
2350
2351         ED_region_tag_redraw(vod->ar);
2352 }
2353
2354 static void viewzoom_apply(
2355         ViewOpsData *vod, const int xy[2],
2356         const short viewzoom, const bool zoom_invert)
2357 {
2358         if ((vod->rv3d->persp == RV3D_CAMOB) &&
2359             (vod->rv3d->is_persp && ED_view3d_camera_lock_check(vod->v3d, vod->rv3d)) == 0)
2360         {
2361                 viewzoom_apply_camera(vod, xy, viewzoom, zoom_invert);
2362         }
2363         else {
2364                 viewzoom_apply_3d(vod, xy, viewzoom, zoom_invert);
2365         }
2366 }
2367
2368 static int viewzoom_modal(bContext *C, wmOperator *op, const wmEvent *event)
2369 {
2370         ViewOpsData *vod = op->customdata;
2371         short event_code = VIEW_PASS;
2372         bool use_autokey = false;
2373         int ret = OPERATOR_RUNNING_MODAL;
2374
2375         /* execute the events */
2376         if (event->type == TIMER && event->customdata == vod->timer) {
2377                 /* continuous zoom */
2378                 event_code = VIEW_APPLY;
2379         }
2380         else if (event->type == MOUSEMOVE) {
2381                 event_code = VIEW_APPLY;
2382         }
2383         else if (event->type == EVT_MODAL_MAP) {
2384                 switch (event->val) {
2385                         case VIEW_MODAL_CONFIRM:
2386                                 event_code = VIEW_CONFIRM;
2387                                 break;
2388                         case VIEWROT_MODAL_SWITCH_MOVE:
2389                                 WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL);
2390                                 event_code = VIEW_CONFIRM;
2391                                 break;
2392                         case VIEWROT_MODAL_SWITCH_ROTATE:
2393                                 WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL);
2394                                 event_code = VIEW_CONFIRM;
2395                                 break;
2396                 }
2397         }
2398         else if (event->type == vod->origkey && event->val == KM_RELEASE) {
2399                 event_code = VIEW_CONFIRM;
2400         }
2401
2402         if (event_code == VIEW_APPLY) {
2403                 viewzoom_apply(vod, &event->x, U.viewzoom, (U.uiflag & USER_ZOOM_INVERT) != 0);
2404                 if (ED_screen_animation_playing(CTX_wm_manager(C))) {
2405                         use_autokey = true;
2406                 }
2407         }
2408         else if (event_code == VIEW_CONFIRM) {
2409                 ED_view3d_depth_tag_update(vod->rv3d);
2410                 use_autokey = true;
2411                 ret = OPERATOR_FINISHED;
2412         }
2413
2414         if (use_autokey) {
2415                 ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, false, true);
2416         }
2417
2418         if (ret & OPERATOR_FINISHED) {
2419                 viewops_data_free(C, op);
2420         }
2421
2422         return ret;
2423 }
2424
2425 static int viewzoom_exec(bContext *C, wmOperator *op)
2426 {
2427         Scene *scene = CTX_data_scene(C);
2428         View3D *v3d;
2429         RegionView3D *rv3d;
2430         ScrArea *sa;
2431         ARegion *ar;
2432         bool use_cam_zoom;
2433         float dist_range[2];
2434
2435         const int delta = RNA_int_get(op->ptr, "delta");
2436         int mx, my;
2437
2438         if (op->customdata) {
2439                 ViewOpsData *vod = op->customdata;
2440
2441                 sa = vod->sa;
2442                 ar = vod->ar;
2443         }
2444         else {
2445                 sa = CTX_wm_area(C);
2446                 ar = CTX_wm_region(C);
2447         }
2448
2449         v3d = sa->spacedata.first;
2450         rv3d = ar->regiondata;
2451
2452         mx = RNA_struct_property_is_set(op->ptr, "mx") ? RNA_int_get(op->ptr, "mx") : ar->winx / 2;
2453         my = RNA_struct_property_is_set(op->ptr, "my") ? RNA_int_get(op->ptr, "my") : ar->winy / 2;
2454
2455         use_cam_zoom = (rv3d->persp == RV3D_CAMOB) && !(rv3d->is_persp && ED_view3d_camera_lock_check(v3d, rv3d));
2456
2457         ED_view3d_dist_range_get(v3d, dist_range);
2458
2459         if (delta < 0) {
2460                 const float step = 1.2f;
2461                 /* this min and max is also in viewmove() */
2462                 if (use_cam_zoom) {
2463                         view_zoom_mouseloc_camera(scene, v3d, ar, step, mx, my);
2464                 }
2465                 else {
2466                         if (rv3d->dist < dist_range[1]) {
2467                                 view_zoom_mouseloc_3d(ar, step, mx, my);
2468                         }
2469                 }
2470         }
2471         else {
2472                 const float step = 1.0f / 1.2f;
2473                 if (use_cam_zoom) {
2474                         view_zoom_mouseloc_camera(scene, v3d, ar, step, mx, my);
2475                 }
2476                 else {
2477                         if (rv3d->dist > dist_range[0]) {
2478                                 view_zoom_mouseloc_3d(ar, step, mx, my);
2479                         }
2480                 }
2481         }
2482
2483         if (rv3d->viewlock & RV3D_BOXVIEW)
2484                 view3d_boxview_sync(sa, ar);
2485
2486         ED_view3d_depth_tag_update(rv3d);
2487
2488         ED_view3d_camera_lock_sync(v3d, rv3d);
2489         ED_view3d_camera_lock_autokey(v3d, rv3d, C, false, true);
2490
2491         ED_region_tag_redraw(ar);
2492
2493         viewops_data_free(C, op);
2494
2495         return OPERATOR_FINISHED;
2496 }
2497
2498 /* this is an exact copy of viewzoom_modal_keymap */
2499 /* called in transform_ops.c, on each regeneration of keymaps  */
2500 void viewdolly_modal_keymap(wmKeyConfig *keyconf)
2501 {
2502         static EnumPropertyItem modal_items[] = {
2503                 {VIEW_MODAL_CONFIRM,    "CONFIRM", 0, "Confirm", ""},
2504
2505                 {VIEWROT_MODAL_SWITCH_ROTATE, "SWITCH_TO_ROTATE", 0, "Switch to Rotate"},
2506                 {VIEWROT_MODAL_SWITCH_MOVE, "SWITCH_TO_MOVE", 0, "Switch to Move"},
2507
2508                 {0, NULL, 0, NULL, NULL}
2509         };
2510
2511         wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "View3D Dolly Modal");
2512
2513         /* this function is called for each spacetype, only needs to add map once */
2514         if (keymap && keymap->modal_items) return;
2515
2516         keymap = WM_modalkeymap_add(keyconf, "View3D Dolly Modal", modal_items);
2517
2518         /* items for modal map */
2519         WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_RELEASE, KM_ANY, 0, VIEW_MODAL_CONFIRM);
2520         WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, VIEW_MODAL_CONFIRM);
2521
2522         /* disabled mode switching for now, can re-implement better, later on */
2523 #if 0
2524         WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
2525         WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
2526         WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_MOVE);
2527 #endif
2528         
2529         /* assign map to operators */
2530         WM_modalkeymap_assign(keymap, "VIEW3D_OT_dolly");
2531 }
2532
2533 /* viewdolly_invoke() copied this function, changes here may apply there */
2534 static int viewzoom_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2535 {
2536         ViewOpsData *vod;
2537
2538         /* makes op->customdata */
2539         viewops_data_alloc(C, op);
2540         viewops_data_create(C, op, event, false);
2541         vod = op->customdata;
2542
2543         ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->ar);
2544
2545         /* if one or the other zoom position aren't set, set from event */
2546         if (!RNA_struct_property_is_set(op->ptr, "mx") || !RNA_struct_property_is_set(op->ptr, "my")) {
2547                 RNA_int_set(op->ptr, "mx", event->x);
2548                 RNA_int_set(op->ptr, "my", event->y);
2549         }
2550
2551         if (RNA_struct_property_is_set(op->ptr, "delta")) {
2552                 viewzoom_exec(C, op);
2553         }
2554         else {
2555                 if (event->type == MOUSEZOOM || event->type == MOUSEPAN) {
2556
2557                         if (U.uiflag & USER_ZOOM_HORIZ) {
2558                                 vod->origx = vod->oldx = event->x;
2559                                 viewzoom_apply(vod, &event->prevx, USER_ZOOM_DOLLY, (U.uiflag & USER_ZOOM_INVERT) != 0);
2560                         }
2561                         else {
2562                                 /* Set y move = x move as MOUSEZOOM uses only x axis to pass magnification value */
2563                                 vod->origy = vod->oldy = vod->origy + event->x - event->prevx;
2564                                 viewzoom_apply(vod, &event->prevx, USER_ZOOM_DOLLY, (U.uiflag & USER_ZOOM_INVERT) != 0);
2565                         }
2566                         ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, false, true);
2567
2568                         ED_view3d_depth_tag_update(vod->rv3d);
2569
2570                         viewops_data_free(C, op);
2571                         return OPERATOR_FINISHED;
2572                 }
2573                 else {
2574                         if (U.viewzoom == USER_ZOOM_CONT) {
2575                                 /* needs a timer to continue redrawing */
2576                                 vod->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f);
2577                                 vod->timer_lastdraw = PIL_check_seconds_timer();
2578                         }
2579
2580                         /* add temp handler */
2581                         WM_event_add_modal_handler(C, op);
2582
2583                         return OPERATOR_RUNNING_MODAL;
2584                 }
2585         }
2586         return OPERATOR_FINISHED;
2587 }
2588
2589 static void viewzoom_cancel(bContext *C, wmOperator *op)
2590 {
2591         viewops_data_free(C, op);
2592 }
2593
2594 void VIEW3D_OT_zoom(wmOperatorType *ot)
2595 {
2596         PropertyRNA *prop;
2597
2598         /* identifiers */
2599         ot->name = "Zoom View";
2600         ot->description = "Zoom in/out in the view";
2601         ot->idname = "VIEW3D_OT_zoom";
2602
2603         /* api callbacks */
2604         ot->invoke = viewzoom_invoke;
2605         ot->exec = viewzoom_exec;
2606         ot->modal = viewzoom_modal;
2607         ot->poll = ED_operator_region_view3d_active;
2608         ot->cancel = viewzoom_cancel;
2609
2610         /* flags */
2611         ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR;
2612
2613         RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
2614         prop = RNA_def_int(ot->srna, "mx", 0, 0, INT_MAX, "Zoom Position X", "", 0, INT_MAX);
2615         RNA_def_property_flag(prop, PROP_HIDDEN);
2616         prop = RNA_def_int(ot->srna, "my", 0, 0, INT_MAX, "Zoom Position Y", "", 0, INT_MAX);
2617         RNA_def_property_flag(prop, PROP_HIDDEN);
2618 }
2619
2620
2621 /* ************************ viewdolly ******************************** */
2622 static bool viewdolly_offset_lock_check(bContext *C, wmOperator *op)
2623 {
2624         View3D *v3d = CTX_wm_view3d(C);
2625         RegionView3D *rv3d = CTX_wm_region_view3d(C);
2626         if (ED_view3d_offset_lock_check(v3d, rv3d)) {
2627                 BKE_report(op->reports, RPT_WARNING, "Cannot dolly when the view offset is locked");
2628                 return true;
2629         }
2630         else {
2631                 return false;
2632         }
2633 }
2634
2635 static void view_dolly_mouseloc(ARegion *ar, float orig_ofs[3], float dvec[3], float dfac)
2636 {
2637         RegionView3D *rv3d = ar->regiondata;
2638         madd_v3_v3v3fl(rv3d->ofs, orig_ofs, dvec, -(1.0f - dfac));
2639 }
2640
2641 static void viewdolly_apply(ViewOpsData *vod, int x, int y, const short zoom_invert)
2642 {
2643         float zfac = 1.0;
2644
2645         {
2646                 float len1, len2;
2647
2648                 if (U.uiflag & USER_ZOOM_HORIZ) {
2649                         len1 = (vod->ar->winrct.xmax - x) + 5;
2650                         len2 = (vod->ar->winrct.xmax - vod->origx) + 5;
2651                 }
2652                 else {
2653                         len1 = (vod->ar->winrct.ymax - y) + 5;
2654                         len2 = (vod->ar->winrct.ymax - vod->origy) + 5;
2655                 }
2656                 if (zoom_invert)
2657                         SWAP(float, len1, len2);
2658
2659                 zfac =  1.0f + ((len1 - len2) * 0.01f * vod->rv3d->dist);
2660         }
2661
2662         if (zfac != 1.0f)
2663                 view_dolly_mouseloc(vod->ar, vod->ofs, vod->mousevec, zfac);
2664
2665         if (vod->rv3d->viewlock & RV3D_BOXVIEW)
2666                 view3d_boxview_sync(vod->sa, vod->ar);
2667
2668         ED_view3d_camera_lock_sync(vod->v3d, vod->rv3d);
2669
2670         ED_region_tag_redraw(vod->ar);
2671 }
2672
2673
2674 static int viewdolly_modal(bContext *C, wmOperator *op, const wmEvent *event)
2675 {
2676         ViewOpsData *vod = op->customdata;
2677         short event_code = VIEW_PASS;
2678         bool use_autokey = false;
2679         int ret = OPERATOR_RUNNING_MODAL;
2680
2681         /* execute the events */
2682         if (event->type == MOUSEMOVE) {
2683                 event_code = VIEW_APPLY;
2684         }
2685         else if (event->type == EVT_MODAL_MAP) {
2686                 switch (event->val) {
2687                         case VIEW_MODAL_CONFIRM:
2688                                 event_code = VIEW_CONFIRM;
2689                                 break;
2690                         case VIEWROT_MODAL_SWITCH_MOVE:
2691                                 WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL);
2692                                 event_code = VIEW_CONFIRM;
2693                                 break;
2694                         case VIEWROT_MODAL_SWITCH_ROTATE:
2695                                 WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL);
2696                                 event_code = VIEW_CONFIRM;
2697                                 break;
2698                 }
2699         }
2700         else if (event->type == vod->origkey && event->val == KM_RELEASE) {
2701                 event_code = VIEW_CONFIRM;
2702         }
2703
2704         if (event_code == VIEW_APPLY) {
2705                 viewdolly_apply(vod, event->x, event->y, (U.uiflag & USER_ZOOM_INVERT) != 0);
2706                 if (ED_screen_animation_playing(CTX_wm_manager(C))) {
2707                         use_autokey = true;
2708                 }
2709         }
2710         else if (event_code == VIEW_CONFIRM) {
2711                 ED_view3d_depth_tag_update(vod->rv3d);
2712                 use_autokey = true;
2713                 ret = OPERATOR_FINISHED;
2714         }
2715
2716         if (use_autokey) {
2717                 ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, false, true);
2718         }
2719
2720         if (ret & OPERATOR_FINISHED) {
2721                 viewops_data_free(C, op);
2722         }
2723
2724         return ret;
2725 }
2726
2727 static int viewdolly_exec(bContext *C, wmOperator *op)
2728 {
2729         View3D *v3d;
2730         RegionView3D *rv3d;
2731         ScrArea *sa;
2732         ARegion *ar;
2733         float mousevec[3];
2734
2735         const int delta = RNA_int_get(op->ptr, "delta");
2736
2737         if (op->customdata) {
2738                 ViewOpsData *vod = op->customdata;
2739
2740                 sa = vod->sa;
2741                 ar = vod->ar;
2742                 copy_v3_v3(mousevec, vod->mousevec);
2743         }
2744         else {
2745                 sa = CTX_wm_area(C);
2746                 ar = CTX_wm_region(C);
2747                 negate_v3_v3(mousevec, ((RegionView3D *)ar->regiondata)->viewinv[2]);
2748                 normalize_v3(mousevec);
2749         }
2750
2751         v3d = sa->spacedata.first;
2752         rv3d = ar->regiondata;
2753
2754         /* overwrite the mouse vector with the view direction (zoom into the center) */
2755         if ((U.uiflag & USER_ZOOM_TO_MOUSEPOS) == 0) {
2756                 normalize_v3_v3(mousevec, rv3d->viewinv[2]);
2757         }
2758
2759         if (delta < 0) {
2760                 view_dolly_mouseloc(ar, rv3d->ofs, mousevec, 0.2f);
2761         }
2762         else {
2763                 view_dolly_mouseloc(ar, rv3d->ofs, mousevec, 1.8f);
2764         }
2765
2766         if (rv3d->viewlock & RV3D_BOXVIEW)
2767                 view3d_boxview_sync(sa, ar);
2768
2769         ED_view3d_depth_tag_update(rv3d);
2770
2771         ED_view3d_camera_lock_sync(v3d, rv3d);
2772
2773         ED_region_tag_redraw(ar);
2774
2775         viewops_data_free(C, op);
2776
2777         return OPERATOR_FINISHED;
2778 }
2779
2780 /* copied from viewzoom_invoke(), changes here may apply there */
2781 static int viewdolly_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2782 {
2783         ViewOpsData *vod;
2784
2785         if (viewdolly_offset_lock_check(C, op))
2786                 return OPERATOR_CANCELLED;
2787
2788         /* makes op->customdata */
2789         viewops_data_alloc(C, op);
2790         vod = op->customdata;
2791
2792         /* poll should check but in some cases fails, see poll func for details */
2793         if (vod->rv3d->viewlock & RV3D_LOCKED) {
2794                 viewops_data_free(C, op);
2795                 return OPERATOR_PASS_THROUGH;
2796         }
2797
2798         ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->ar);
2799
2800         /* needs to run before 'viewops_data_create' so the backup 'rv3d->ofs' is correct */
2801         /* switch from camera view when: */
2802         if (vod->rv3d->persp != RV3D_PERSP) {
2803                 if (vod->rv3d->persp == RV3D_CAMOB) {
2804                         /* ignore rv3d->lpersp because dolly only makes sense in perspective mode */
2805                         view3d_persp_switch_from_camera(vod->v3d, vod->rv3d, RV3D_PERSP);
2806                 }
2807                 else {
2808                         vod->rv3d->persp = RV3D_PERSP;
2809                 }
2810                 ED_region_tag_redraw(vod->ar);
2811         }
2812
2813         viewops_data_create(C, op, event, false);
2814
2815
2816         /* if one or the other zoom position aren't set, set from event */
2817         if (!RNA_struct_property_is_set(op->ptr, "mx") || !RNA_struct_property_is_set(op->ptr, "my")) {
2818                 RNA_int_set(op->ptr, "mx", event->x);
2819                 RNA_int_set(op->ptr, "my", event->y);
2820         }
2821
2822         if (RNA_struct_property_is_set(op->ptr, "delta")) {
2823                 viewdolly_exec(C, op);
2824         }
2825         else {
2826                 /* overwrite the mouse vector with the view direction (zoom into the center) */
2827                 if ((U.uiflag & USER_ZOOM_TO_MOUSEPOS) == 0) {
2828                         negate_v3_v3(vod->mousevec, vod->rv3d->viewinv[2]);
2829                         normalize_v3(vod->mousevec);
2830                 }
2831
2832                 if (event->type == MOUSEZOOM) {
2833                         /* Bypass Zoom invert flag for track pads (pass false always) */
2834
2835                         if (U.uiflag & USER_ZOOM_HORIZ) {
2836                                 vod->origx = vod->oldx = event->x;
2837                                 viewdolly_apply(vod, event->prevx, event->prevy, (U.uiflag & USER_ZOOM_INVERT) == 0);
2838                         }
2839                         else {
2840
2841                                 /* Set y move = x move as MOUSEZOOM uses only x axis to pass magnification value */
2842                                 vod->origy = vod->oldy = vod->origy + event->x - event->prevx;
2843                                 viewdolly_apply(vod, event->prevx, event->prevy, (U.uiflag & USER_ZOOM_INVERT) == 0);
2844                         }
2845                         ED_view3d_depth_tag_update(vod->rv3d);
2846
2847                         viewops_data_free(C, op);
2848                         return OPERATOR_FINISHED;
2849                 }
2850                 else {
2851                         /* add temp handler */
2852                         WM_event_add_modal_handler(C, op);
2853
2854                         return OPERATOR_RUNNING_MODAL;
2855                 }
2856         }
2857         return OPERATOR_FINISHED;
2858 }
2859
2860 static void viewdolly_cancel(bContext *C, wmOperator *op)
2861 {
2862         viewops_data_free(C, op);
2863 }
2864
2865 void VIEW3D_OT_dolly(wmOperatorType *ot)
2866 {
2867         /* identifiers */
2868         ot->name = "Dolly View";
2869         ot->description = "Dolly in/out in the view";
2870         ot->idname = "VIEW3D_OT_dolly";
2871
2872         /* api callbacks */
2873         ot->invoke = viewdolly_invoke;
2874         ot->exec = viewdolly_exec;
2875         ot->modal = viewdolly_modal;
2876         ot->poll = ED_operator_region_view3d_active;
2877         ot->cancel = viewdolly_cancel;
2878
2879         /* flags */
2880         ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR;
2881
2882         RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
2883         RNA_def_int(ot->srna, "mx", 0, 0, INT_MAX, "Zoom Position X", "", 0, INT_MAX);
2884         RNA_def_int(ot->srna, "my", 0, 0, INT_MAX, "Zoom Position Y", "", 0, INT_MAX);
2885 }
2886
2887 static void view3d_from_minmax(bContext *C, View3D *v3d, ARegion *ar,
2888                                const float min[3], const float max[3],
2889                                bool ok_dist, const int smooth_viewtx)
2890 {
2891         RegionView3D *rv3d = ar->regiondata;
2892         float afm[3];
2893         float size;
2894
2895         ED_view3d_smooth_view_force_finish(C, v3d, ar);
2896
2897         /* SMOOTHVIEW */
2898         float new_ofs[3];
2899         float new_dist;
2900
2901         sub_v3_v3v3(afm, max, min);
2902         size = max_fff(afm[0], afm[1], afm[2]);
2903
2904         if (ok_dist) {
2905                 char persp;
2906
2907                 if (rv3d->is_persp) {
2908                         if (rv3d->persp == RV3D_CAMOB && ED_view3d_camera_lock_check(v3d, rv3d)) {
2909                                 persp = RV3D_CAMOB;
2910                         }
2911                         else {
2912                                 persp = RV3D_PERSP;
2913                         }
2914                 }
2915                 else { /* ortho */
2916                         if (size < 0.0001f) {
2917                                 /* bounding box was a single point so do not zoom */
2918                                 ok_dist = false;
2919                         }
2920                         else {
2921                                 /* adjust zoom so it looks nicer */
2922                                 persp = RV3D_ORTHO;
2923                         }
2924                 }
2925
2926                 if (ok_dist) {
2927                         new_dist = ED_view3d_radius_to_dist(v3d, ar, persp, true, (size / 2) * VIEW3D_MARGIN);
2928                         if (rv3d->is_persp) {
2929                                 /* don't zoom closer than the near clipping plane */
2930                                 new_dist = max_ff(new_dist, v3d->near * 1.5f);
2931                         }
2932                 }
2933         }
2934
2935         mid_v3_v3v3(new_ofs, min, max);
2936         negate_v3(new_ofs);
2937
2938         if (rv3d->persp == RV3D_CAMOB && !ED_view3d_camera_lock_check(v3d, rv3d)) {
2939                 rv3d->persp = RV3D_PERSP;
2940                 ED_view3d_smooth_view(
2941                         C, v3d, ar, smooth_viewtx,
2942                         &(const V3D_SmoothParams) {
2943                             .camera_old = v3d->camera, .ofs = new_ofs,
2944                             .dist = ok_dist ? &new_dist : NULL});
2945         }
2946         else {
2947                 ED_view3d_smooth_view(
2948                         C, v3d, ar, smooth_viewtx,
2949                         &(const V3D_SmoothParams) {.ofs = new_ofs, .dist = ok_dist ? &new_dist : NULL});
2950         }
2951
2952         /* smooth view does viewlock RV3D_BOXVIEW copy */
2953 }
2954
2955 /* same as view3d_from_minmax but for all regions (except cameras) */
2956 static void view3d_from_minmax_multi(bContext *C, View3D *v3d,
2957                                      const float min[3], const float max[3],
2958                                      const bool ok_dist, const int smooth_viewtx)
2959 {
2960         ScrArea *sa = CTX_wm_area(C);
2961         ARegion *ar;
2962         for (ar = sa->regionbase.first; ar; ar = ar->next) {
2963                 if (ar->regiontype == RGN_TYPE_WINDOW) {
2964                         RegionView3D *rv3d = ar->regiondata;
2965                         /* when using all regions, don't jump out of camera view,
2966                          * but _do_ allow locked cameras to be moved */
2967                         if ((rv3d->persp != RV3D_CAMOB) || ED_view3d_camera_lock_check(v3d, rv3d)) {
2968                                 view3d_from_minmax(C, v3d, ar, min, max, ok_dist, smooth_viewtx);
2969                         }
2970                 }
2971         }
2972 }
2973
2974 static int view3d_all_exec(bContext *C, wmOperator *op) /* was view3d_home() in 2.4x */
2975 {
2976         ARegion *ar = CTX_wm_region(C);
2977         View3D *v3d = CTX_wm_view3d(C);
2978         Scene *scene = CTX_data_scene(C);
2979         Base *base;
2980         float *curs;
2981         const bool use_all_regions = RNA_boolean_get(op->ptr, "use_all_regions");
2982         const bool skip_camera = (ED_view3d_camera_lock_check(v3d, ar->regiondata) ||
2983                                   /* any one of the regions may be locked */
2984                                   (use_all_regions && v3d->flag2 & V3D_LOCK_CAMERA));
2985         const bool center = RNA_boolean_get(op->ptr, "center");
2986         const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
2987
2988         float min[3], max[3];
2989         bool changed = false;
2990
2991         if (center) {
2992                 /* in 2.4x this also move the cursor to (0, 0, 0) (with shift+c). */
2993                 curs = ED_view3d_cursor3d_get(scene, v3d);
2994                 zero_v3(min);
2995                 zero_v3(max);
2996                 zero_v3(curs);
2997         }
2998         else {
2999                 INIT_MINMAX(min, max);
3000         }
3001
3002         for (base = scene->base.first; base; base = base->next) {
3003                 if (BASE_VISIBLE(v3d, base)) {
3004                         changed = true;
3005
3006                         if (skip_camera && base->object == v3d->camera) {
3007                                 continue;
3008                         }
3009
3010                         BKE_object_minmax(base->object, min, max, false);
3011                 }
3012         }
3013         if (!changed) {
3014                 ED_region_tag_redraw(ar);
3015                 /* TODO - should this be cancel?
3016                  * I think no, because we always move the cursor, with or without
3017                  * object, but in this case there is no change in the scene,
3018                  * only the cursor so I choice a ED_region_tag like
3019                  * view3d_smooth_view do for the center_cursor.
3020                  * See bug #22640
3021                  */
3022                 return OPERATOR_FINISHED;
3023         }
3024
3025         if (use_all_regions) {
3026                 view3d_from_minmax_multi(C, v3d, min, max, true, smooth_viewtx);
3027         }
3028         else {
3029                 view3d_from_minmax(C, v3d, ar, min, max, true, smooth_viewtx);
3030         }
3031
3032         return OPERATOR_FINISHED;
3033 }
3034
3035
3036 void VIEW3D_OT_view_all(wmOperatorType *ot)
3037 {
3038         PropertyRNA *prop;
3039
3040         /* identifiers */
3041         ot->name = "View All";
3042         ot->description = "View all objects in scene";
3043         ot->idname = "VIEW3D_OT_view_all";
3044
3045         /* api callbacks */
3046         ot->exec = view3d_all_exec;
3047         ot->poll = ED_operator_region_view3d_active;
3048
3049         /* flags */
3050         ot->flag = 0;
3051
3052         prop = RNA_def_boolean(ot->srna, "use_all_regions", 0, "All Regions", "View selected for all regions");
3053         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
3054         RNA_def_boolean(ot->srna, "center", 0, "Center", "");
3055 }
3056
3057 /* like a localview without local!, was centerview() in 2.4x */
3058 static int viewselected_exec(bContext *C, wmOperator *op)
3059 {
3060         ARegion *ar = CTX_wm_region(C);
3061         View3D *v3d = CTX_wm_view3d(C);
3062         Scene *scene = CTX_data_scene(C);
3063         bGPdata *gpd = CTX_data_gpencil_data(C);
3064         const bool is_gp_edit = ((gpd) && (gpd->flag & GP_DATA_STROKE_EDITMODE));
3065         Object *ob = OBACT;
3066         Object *obedit = CTX_data_edit_object(C);
3067         float min[3], max[3];
3068         bool ok = false, ok_dist = true;
3069         const bool use_all_regions = RNA_boolean_get(op->ptr, "use_all_regions");
3070         const bool skip_camera = (ED_view3d_camera_lock_check(v3d, ar->regiondata) ||
3071                                   /* any one of the regions may be locked */
3072                                   (use_all_regions && v3d->flag2 & V3D_LOCK_CAMERA));
3073         const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
3074
3075         INIT_MINMAX(min, max);
3076
3077         if (is_gp_edit) {
3078                 ob = NULL;
3079         }
3080
3081         if (ob && (ob->mode & OB_MODE_WEIGHT_PAINT)) {
3082                 /* hard-coded exception, we look for the one selected armature */
3083                 /* this is weak code this way, we should make a generic active/selection callback interface once... */
3084                 Base *base;
3085                 for (base = scene->base.first; base; base = base->next) {
3086                         if (TESTBASELIB(v3d, base)) {
3087                                 if (base->object->type == OB_ARMATURE)
3088                                         if (base->object->mode & OB_MODE_POSE)
3089                                                 break;
3090                         }
3091                 }
3092                 if (base)
3093                         ob = base->object;
3094         }
3095
3096
3097         if (is_gp_edit) {
3098                 CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
3099                 {
3100                         /* we're only interested in selected points here... */
3101                         if ((gps->flag & GP_STROKE_SELECT) && (gps->flag & GP_STROKE_3DSPACE)) {
3102                                 if (ED_gpencil_stroke_minmax(gps, true, min, max)) {
3103                                         ok = true;
3104                                 }
3105                         }
3106                 }
3107                 CTX_DATA_END;
3108         }
3109         else if (obedit) {
3110                 ok = ED_view3d_minmax_verts(obedit, min, max);    /* only selected */
3111         }
3112         else if (ob && (ob->mode & OB_MODE_POSE)) {
3113                 ok = BKE_pose_minmax(ob, min, max, true, true);
3114         }
3115         else if (BKE_paint_select_face_test(ob)) {
3116                 ok = paintface_minmax(ob, min, max);
3117         }
3118         else if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT)) {
3119                 ok = PE_minmax(scene, min, max);
3120         }
3121         else if (ob &&
3122                  (ob->mode & (OB_MODE_SCULPT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT)))
3123         {
3124                 BKE_paint_stroke_get_average(scene, ob, min);
3125                 copy_v3_v3(max, min);
3126                 ok = true;
3127                 ok_dist = 0; /* don't zoom */
3128         }
3129         else {
3130                 Base *base;
3131                 for (base = FIRSTBASE; base; base = base->next) {
3132                         if (TESTBASE(v3d, base)) {
3133
3134                                 if (skip_camera && base->object == v3d->camera) {
3135                                         continue;
3136                                 }
3137
3138                                 /* account for duplis */
3139                                 if (BKE_object_minmax_dupli(scene, base->object, min, max, false) == 0)
3140                                         BKE_object_minmax(base->object, min, max, false);  /* use if duplis not found */
3141
3142                                 ok = 1;
3143                         }
3144                 }
3145         }
3146
3147         if (ok == 0) {
3148                 return OPERATOR_FINISHED;
3149         }
3150
3151         if (use_all_regions) {
3152                 view3d_from_minmax_multi(C, v3d, min, max, ok_dist, smooth_viewtx);
3153         }
3154         else {
3155                 view3d_from_minmax(C, v3d, ar, min, max, ok_dist, smooth_viewtx);
3156         }
3157
3158         return OPERATOR_FINISHED;
3159 }
3160
3161 void VIEW3D_OT_view_selected(wmOperatorType *ot)
3162 {
3163         PropertyRNA *prop;
3164
3165         /* identifiers */
3166         ot->name = "View Selected";
3167         ot->description = "Move the view to the selection center";
3168         ot->idname = "VIEW3D_OT_view_selected";
3169
3170         /* api callbacks */
3171         ot->exec = viewselected_exec;
3172         ot->poll = ED_operator_region_view3d_active;
3173
3174         /* flags */
3175         ot->flag = 0;
3176
3177         /* rna later */
3178         prop = RNA_def_boolean(ot->srna, "use_all_regions", 0, "All Regions", "View selected for all regions");
3179         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
3180 }
3181
3182 static int view_lock_clear_exec(bContext *C, wmOperator *UNUSED(op))
3183 {
3184         View3D *v3d = CTX_wm_view3d(C);
3185
3186         if (v3d) {
3187                 ED_view3D_lock_clear(v3d);
3188
3189                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
3190
3191                 return OPERATOR_FINISHED;
3192         }
3193         else {
3194                 return OPERATOR_CANCELLED;
3195         }
3196 }
3197
3198 void VIEW3D_OT_view_lock_clear(wmOperatorType *ot)
3199 {
3200
3201         /* identifiers */
3202         ot->name = "View Lock Clear";
3203         ot->description = "Clear all view locking";