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