Fix T57852: Mesh X Mirror option not working
[blender.git] / source / blender / editors / transform / transform.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) 2001-2002 by NaN Holding BV.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): none yet.
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/editors/transform/transform.c
29  *  \ingroup edtransform
30  */
31
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <math.h>
36 #include <float.h>
37
38 #include "MEM_guardedalloc.h"
39
40 #include "DNA_anim_types.h"
41 #include "DNA_armature_types.h"
42 #include "DNA_constraint_types.h"
43 #include "DNA_mask_types.h"
44 #include "DNA_mesh_types.h"
45 #include "DNA_movieclip_types.h"
46 #include "DNA_scene_types.h"  /* PET modes */
47 #include "DNA_workspace_types.h"
48 #include "DNA_gpencil_types.h"
49
50 #include "BLI_alloca.h"
51 #include "BLI_utildefines.h"
52 #include "BLI_math.h"
53 #include "BLI_rect.h"
54 #include "BLI_listbase.h"
55 #include "BLI_string.h"
56 #include "BLI_ghash.h"
57 #include "BLI_utildefines_stack.h"
58 #include "BLI_memarena.h"
59
60 #include "BKE_nla.h"
61 #include "BKE_editmesh.h"
62 #include "BKE_editmesh_bvh.h"
63 #include "BKE_context.h"
64 #include "BKE_constraint.h"
65 #include "BKE_particle.h"
66 #include "BKE_unit.h"
67 #include "BKE_scene.h"
68 #include "BKE_mask.h"
69 #include "BKE_mesh.h"
70 #include "BKE_report.h"
71 #include "BKE_workspace.h"
72
73 #include "DEG_depsgraph.h"
74
75 #include "BIF_glutil.h"
76
77 #include "GPU_immediate.h"
78 #include "GPU_immediate_util.h"
79 #include "GPU_matrix.h"
80 #include "GPU_state.h"
81
82 #include "ED_image.h"
83 #include "ED_keyframing.h"
84 #include "ED_screen.h"
85 #include "ED_space_api.h"
86 #include "ED_markers.h"
87 #include "ED_view3d.h"
88 #include "ED_mesh.h"
89 #include "ED_clip.h"
90 #include "ED_node.h"
91 #include "ED_gpencil.h"
92
93 #include "WM_types.h"
94 #include "WM_api.h"
95
96 #include "UI_view2d.h"
97 #include "UI_interface.h"
98 #include "UI_interface_icons.h"
99 #include "UI_resources.h"
100
101 #include "RNA_access.h"
102 #include "RNA_define.h"
103
104 #include "BLF_api.h"
105 #include "BLT_translation.h"
106
107 #include "transform.h"
108
109 /* Disabling, since when you type you know what you are doing, and being able to set it to zero is handy. */
110 // #define USE_NUM_NO_ZERO
111
112 static void drawTransformApply(const struct bContext *C, ARegion *ar, void *arg);
113 static void doEdgeSlide(TransInfo *t, float perc);
114 static void doVertSlide(TransInfo *t, float perc);
115
116 static void drawEdgeSlide(TransInfo *t);
117 static void drawVertSlide(TransInfo *t);
118 static void postInputRotation(TransInfo *t, float values[3]);
119
120 static void ElementRotation(TransInfo *t, TransDataContainer *tc, TransData *td, float mat[3][3], const short around);
121 static void initSnapSpatial(TransInfo *t, float r_snap[3]);
122
123 static void storeCustomLNorValue(TransDataContainer *t, BMesh *bm);
124
125 /* Transform Callbacks */
126 static void initBend(TransInfo *t);
127 static eRedrawFlag handleEventBend(TransInfo *t, const struct wmEvent *event);
128 static void Bend(TransInfo *t, const int mval[2]);
129
130 static void initShear(TransInfo *t);
131 static eRedrawFlag handleEventShear(TransInfo *t, const struct wmEvent *event);
132 static void applyShear(TransInfo *t, const int mval[2]);
133
134 static void initResize(TransInfo *t);
135 static void applyResize(TransInfo *t, const int mval[2]);
136
137 static void initSkinResize(TransInfo *t);
138 static void applySkinResize(TransInfo *t, const int mval[2]);
139
140 static void initTranslation(TransInfo *t);
141 static void applyTranslation(TransInfo *t, const int mval[2]);
142
143 static void initToSphere(TransInfo *t);
144 static void applyToSphere(TransInfo *t, const int mval[2]);
145
146 static void initRotation(TransInfo *t);
147 static void applyRotation(TransInfo *t, const int mval[2]);
148
149 static void initNormalRotation(TransInfo *t);
150 static void applyNormalRotation(TransInfo *t, const int mval[2]);
151
152 static void initShrinkFatten(TransInfo *t);
153 static void applyShrinkFatten(TransInfo *t, const int mval[2]);
154
155 static void initTilt(TransInfo *t);
156 static void applyTilt(TransInfo *t, const int mval[2]);
157
158 static void initCurveShrinkFatten(TransInfo *t);
159 static void applyCurveShrinkFatten(TransInfo *t, const int mval[2]);
160
161 static void initMaskShrinkFatten(TransInfo *t);
162 static void applyMaskShrinkFatten(TransInfo *t, const int mval[2]);
163
164 static void initGPShrinkFatten(TransInfo *t);
165 static void applyGPShrinkFatten(TransInfo *t, const int mval[2]);
166
167 static void initTrackball(TransInfo *t);
168 static void applyTrackball(TransInfo *t, const int mval[2]);
169
170 static void initPushPull(TransInfo *t);
171 static void applyPushPull(TransInfo *t, const int mval[2]);
172
173 static void initBevelWeight(TransInfo *t);
174 static void applyBevelWeight(TransInfo *t, const int mval[2]);
175
176 static void initCrease(TransInfo *t);
177 static void applyCrease(TransInfo *t, const int mval[2]);
178
179 static void initBoneSize(TransInfo *t);
180 static void applyBoneSize(TransInfo *t, const int mval[2]);
181
182 static void initBoneEnvelope(TransInfo *t);
183 static void applyBoneEnvelope(TransInfo *t, const int mval[2]);
184
185 static void initBoneRoll(TransInfo *t);
186 static void applyBoneRoll(TransInfo *t, const int mval[2]);
187
188 static void initEdgeSlide_ex(TransInfo *t, bool use_double_side, bool use_even, bool flipped, bool use_clamp);
189 static void initEdgeSlide(TransInfo *t);
190 static eRedrawFlag handleEventEdgeSlide(TransInfo *t, const struct wmEvent *event);
191 static void applyEdgeSlide(TransInfo *t, const int mval[2]);
192
193 static void initVertSlide_ex(TransInfo *t, bool use_even, bool flipped, bool use_clamp);
194 static void initVertSlide(TransInfo *t);
195 static eRedrawFlag handleEventVertSlide(TransInfo *t, const struct wmEvent *event);
196 static void applyVertSlide(TransInfo *t, const int mval[2]);
197
198 static void initTimeTranslate(TransInfo *t);
199 static void applyTimeTranslate(TransInfo *t, const int mval[2]);
200
201 static void initTimeSlide(TransInfo *t);
202 static void applyTimeSlide(TransInfo *t, const int mval[2]);
203
204 static void initTimeScale(TransInfo *t);
205 static void applyTimeScale(TransInfo *t, const int mval[2]);
206
207 static void initBakeTime(TransInfo *t);
208 static void applyBakeTime(TransInfo *t, const int mval[2]);
209
210 static void initMirror(TransInfo *t);
211 static void applyMirror(TransInfo *t, const int mval[2]);
212
213 static void initAlign(TransInfo *t);
214 static void applyAlign(TransInfo *t, const int mval[2]);
215
216 static void initSeqSlide(TransInfo *t);
217 static void applySeqSlide(TransInfo *t, const int mval[2]);
218 /* end transform callbacks */
219
220
221 static bool transdata_check_local_center(TransInfo *t, short around)
222 {
223         return ((around == V3D_AROUND_LOCAL_ORIGINS) && (
224                     (t->flag & (T_OBJECT | T_POSE)) ||
225                     /* implicit: (t->flag & T_EDIT) */
226                     (ELEM(t->obedit_type, OB_MESH, OB_CURVE, OB_MBALL, OB_ARMATURE, OB_GPENCIL)) ||
227                     (t->spacetype == SPACE_IPO) ||
228                     (t->options & (CTX_MOVIECLIP | CTX_MASK | CTX_PAINT_CURVE)))
229                 );
230 }
231
232 bool transdata_check_local_islands(TransInfo *t, short around)
233 {
234         return ((around == V3D_AROUND_LOCAL_ORIGINS) && (
235                 (ELEM(t->obedit_type, OB_MESH))));
236 }
237
238 /* ************************** SPACE DEPENDENT CODE **************************** */
239
240 void setTransformViewMatrices(TransInfo *t)
241 {
242         if (t->spacetype == SPACE_VIEW3D && t->ar && t->ar->regiontype == RGN_TYPE_WINDOW) {
243                 RegionView3D *rv3d = t->ar->regiondata;
244
245                 copy_m4_m4(t->viewmat, rv3d->viewmat);
246                 copy_m4_m4(t->viewinv, rv3d->viewinv);
247                 copy_m4_m4(t->persmat, rv3d->persmat);
248                 copy_m4_m4(t->persinv, rv3d->persinv);
249                 t->persp = rv3d->persp;
250         }
251         else {
252                 unit_m4(t->viewmat);
253                 unit_m4(t->viewinv);
254                 unit_m4(t->persmat);
255                 unit_m4(t->persinv);
256                 t->persp = RV3D_ORTHO;
257         }
258
259         calculateCenter2D(t);
260         calculateCenterLocal(t, t->center_global);
261 }
262
263 void setTransformViewAspect(TransInfo *t, float r_aspect[3])
264 {
265         copy_v3_fl(r_aspect, 1.0f);
266
267         if (t->spacetype == SPACE_IMAGE) {
268                 SpaceImage *sima = t->sa->spacedata.first;
269
270                 if (t->options & CTX_MASK) {
271                         ED_space_image_get_aspect(sima, &r_aspect[0], &r_aspect[1]);
272                 }
273                 else if (t->options & CTX_PAINT_CURVE) {
274                         /* pass */
275                 }
276                 else {
277                         ED_space_image_get_uv_aspect(sima, &r_aspect[0], &r_aspect[1]);
278                 }
279         }
280         else if (t->spacetype == SPACE_CLIP) {
281                 SpaceClip *sclip = t->sa->spacedata.first;
282
283                 if (t->options & CTX_MOVIECLIP) {
284                         ED_space_clip_get_aspect_dimension_aware(sclip, &r_aspect[0], &r_aspect[1]);
285                 }
286                 else {
287                         ED_space_clip_get_aspect(sclip, &r_aspect[0], &r_aspect[1]);
288                 }
289         }
290         else if (t->spacetype == SPACE_IPO) {
291                 /* depemds on context of usage */
292         }
293 }
294
295 static void convertViewVec2D(View2D *v2d, float r_vec[3], int dx, int dy)
296 {
297         float divx = BLI_rcti_size_x(&v2d->mask);
298         float divy = BLI_rcti_size_y(&v2d->mask);
299
300         r_vec[0] = BLI_rctf_size_x(&v2d->cur) * dx / divx;
301         r_vec[1] = BLI_rctf_size_y(&v2d->cur) * dy / divy;
302         r_vec[2] = 0.0f;
303 }
304
305 static void convertViewVec2D_mask(View2D *v2d, float r_vec[3], int dx, int dy)
306 {
307         float divx = BLI_rcti_size_x(&v2d->mask);
308         float divy = BLI_rcti_size_y(&v2d->mask);
309
310         float mulx = BLI_rctf_size_x(&v2d->cur);
311         float muly = BLI_rctf_size_y(&v2d->cur);
312
313         /* difference with convertViewVec2D */
314         /* clamp w/h, mask only */
315         if (mulx / divx < muly / divy) {
316                 divy = divx;
317                 muly = mulx;
318         }
319         else {
320                 divx = divy;
321                 mulx = muly;
322         }
323         /* end difference */
324
325         r_vec[0] = mulx * dx / divx;
326         r_vec[1] = muly * dy / divy;
327         r_vec[2] = 0.0f;
328 }
329
330 void convertViewVec(TransInfo *t, float r_vec[3], double dx, double dy)
331 {
332         if ((t->spacetype == SPACE_VIEW3D) && (t->ar->regiontype == RGN_TYPE_WINDOW)) {
333                 if (t->options & CTX_PAINT_CURVE) {
334                         r_vec[0] = dx;
335                         r_vec[1] = dy;
336                 }
337                 else {
338                         const float mval_f[2] = {(float)dx, (float)dy};
339                         ED_view3d_win_to_delta(t->ar, mval_f, r_vec, t->zfac);
340                 }
341         }
342         else if (t->spacetype == SPACE_IMAGE) {
343                 if (t->options & CTX_MASK) {
344                         convertViewVec2D_mask(t->view, r_vec, dx, dy);
345                 }
346                 else if (t->options & CTX_PAINT_CURVE) {
347                         r_vec[0] = dx;
348                         r_vec[1] = dy;
349                 }
350                 else {
351                         convertViewVec2D(t->view, r_vec, dx, dy);
352                 }
353
354                 r_vec[0] *= t->aspect[0];
355                 r_vec[1] *= t->aspect[1];
356         }
357         else if (ELEM(t->spacetype, SPACE_IPO, SPACE_NLA)) {
358                 convertViewVec2D(t->view, r_vec, dx, dy);
359         }
360         else if (ELEM(t->spacetype, SPACE_NODE, SPACE_SEQ)) {
361                 convertViewVec2D(&t->ar->v2d, r_vec, dx, dy);
362         }
363         else if (t->spacetype == SPACE_CLIP) {
364                 if (t->options & CTX_MASK) {
365                         convertViewVec2D_mask(t->view, r_vec, dx, dy);
366                 }
367                 else {
368                         convertViewVec2D(t->view, r_vec, dx, dy);
369                 }
370
371                 r_vec[0] *= t->aspect[0];
372                 r_vec[1] *= t->aspect[1];
373         }
374         else {
375                 printf("%s: called in an invalid context\n", __func__);
376                 zero_v3(r_vec);
377         }
378 }
379
380 void projectIntViewEx(TransInfo *t, const float vec[3], int adr[2], const eV3DProjTest flag)
381 {
382         if (t->spacetype == SPACE_VIEW3D) {
383                 if (t->ar->regiontype == RGN_TYPE_WINDOW) {
384                         if (ED_view3d_project_int_global(t->ar, vec, adr, flag) != V3D_PROJ_RET_OK) {
385                                 adr[0] = (int)2140000000.0f;  /* this is what was done in 2.64, perhaps we can be smarter? */
386                                 adr[1] = (int)2140000000.0f;
387                         }
388                 }
389         }
390         else if (t->spacetype == SPACE_IMAGE) {
391                 SpaceImage *sima = t->sa->spacedata.first;
392
393                 if (t->options & CTX_MASK) {
394                         float v[2];
395
396                         v[0] = vec[0] / t->aspect[0];
397                         v[1] = vec[1] / t->aspect[1];
398
399                         BKE_mask_coord_to_image(sima->image, &sima->iuser, v, v);
400
401                         ED_image_point_pos__reverse(sima, t->ar, v, v);
402
403                         adr[0] = v[0];
404                         adr[1] = v[1];
405                 }
406                 else if (t->options & CTX_PAINT_CURVE) {
407                         adr[0] = vec[0];
408                         adr[1] = vec[1];
409                 }
410                 else {
411                         float v[2];
412
413                         v[0] = vec[0] / t->aspect[0];
414                         v[1] = vec[1] / t->aspect[1];
415
416                         UI_view2d_view_to_region(t->view, v[0], v[1], &adr[0], &adr[1]);
417                 }
418         }
419         else if (t->spacetype == SPACE_ACTION) {
420                 int out[2] = {0, 0};
421 #if 0
422                 SpaceAction *sact = t->sa->spacedata.first;
423
424                 if (sact->flag & SACTION_DRAWTIME) {
425                         //vec[0] = vec[0]/((t->scene->r.frs_sec / t->scene->r.frs_sec_base));
426                         /* same as below */
427                         UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &out[0], &out[1]);
428                 }
429                 else
430 #endif
431                 {
432                         UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &out[0], &out[1]);
433                 }
434
435                 adr[0] = out[0];
436                 adr[1] = out[1];
437         }
438         else if (ELEM(t->spacetype, SPACE_IPO, SPACE_NLA)) {
439                 int out[2] = {0, 0};
440
441                 UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &out[0], &out[1]);
442                 adr[0] = out[0];
443                 adr[1] = out[1];
444         }
445         else if (t->spacetype == SPACE_SEQ) { /* XXX not tested yet, but should work */
446                 int out[2] = {0, 0};
447
448                 UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &out[0], &out[1]);
449                 adr[0] = out[0];
450                 adr[1] = out[1];
451         }
452         else if (t->spacetype == SPACE_CLIP) {
453                 SpaceClip *sc = t->sa->spacedata.first;
454
455                 if (t->options & CTX_MASK) {
456                         MovieClip *clip = ED_space_clip_get_clip(sc);
457
458                         if (clip) {
459                                 float v[2];
460
461                                 v[0] = vec[0] / t->aspect[0];
462                                 v[1] = vec[1] / t->aspect[1];
463
464                                 BKE_mask_coord_to_movieclip(sc->clip, &sc->user, v, v);
465
466                                 ED_clip_point_stable_pos__reverse(sc, t->ar, v, v);
467
468                                 adr[0] = v[0];
469                                 adr[1] = v[1];
470                         }
471                         else {
472                                 adr[0] = 0;
473                                 adr[1] = 0;
474                         }
475                 }
476                 else if (t->options & CTX_MOVIECLIP) {
477                         float v[2];
478
479                         v[0] = vec[0] / t->aspect[0];
480                         v[1] = vec[1] / t->aspect[1];
481
482                         UI_view2d_view_to_region(t->view, v[0], v[1], &adr[0], &adr[1]);
483                 }
484                 else {
485                         BLI_assert(0);
486                 }
487         }
488         else if (t->spacetype == SPACE_NODE) {
489                 UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &adr[0], &adr[1]);
490         }
491 }
492 void projectIntView(TransInfo *t, const float vec[3], int adr[2])
493 {
494         projectIntViewEx(t, vec, adr, V3D_PROJ_TEST_NOP);
495 }
496
497 void projectFloatViewEx(TransInfo *t, const float vec[3], float adr[2], const eV3DProjTest flag)
498 {
499         switch (t->spacetype) {
500                 case SPACE_VIEW3D:
501                 {
502                         if (t->options & CTX_PAINT_CURVE) {
503                                 adr[0] = vec[0];
504                                 adr[1] = vec[1];
505                         }
506                         else if (t->ar->regiontype == RGN_TYPE_WINDOW) {
507                                 /* allow points behind the view [#33643] */
508                                 if (ED_view3d_project_float_global(t->ar, vec, adr, flag) != V3D_PROJ_RET_OK) {
509                                         /* XXX, 2.64 and prior did this, weak! */
510                                         adr[0] = t->ar->winx / 2.0f;
511                                         adr[1] = t->ar->winy / 2.0f;
512                                 }
513                                 return;
514                         }
515                         break;
516                 }
517                 default:
518                 {
519                         int a[2] = {0, 0};
520                         projectIntView(t, vec, a);
521                         adr[0] = a[0];
522                         adr[1] = a[1];
523                         break;
524                 }
525         }
526 }
527 void projectFloatView(TransInfo *t, const float vec[3], float adr[2])
528 {
529         projectFloatViewEx(t, vec, adr, V3D_PROJ_TEST_NOP);
530 }
531
532 void applyAspectRatio(TransInfo *t, float vec[2])
533 {
534         if ((t->spacetype == SPACE_IMAGE) && (t->mode == TFM_TRANSLATION) && !(t->options & CTX_PAINT_CURVE)) {
535                 SpaceImage *sima = t->sa->spacedata.first;
536
537                 if ((sima->flag & SI_COORDFLOATS) == 0) {
538                         int width, height;
539                         ED_space_image_get_size(sima, &width, &height);
540
541                         vec[0] *= width;
542                         vec[1] *= height;
543                 }
544
545                 vec[0] /= t->aspect[0];
546                 vec[1] /= t->aspect[1];
547         }
548         else if ((t->spacetype == SPACE_CLIP) && (t->mode == TFM_TRANSLATION)) {
549                 if (t->options & (CTX_MOVIECLIP | CTX_MASK)) {
550                         vec[0] /= t->aspect[0];
551                         vec[1] /= t->aspect[1];
552                 }
553         }
554 }
555
556 void removeAspectRatio(TransInfo *t, float vec[2])
557 {
558         if ((t->spacetype == SPACE_IMAGE) && (t->mode == TFM_TRANSLATION)) {
559                 SpaceImage *sima = t->sa->spacedata.first;
560
561                 if ((sima->flag & SI_COORDFLOATS) == 0) {
562                         int width, height;
563                         ED_space_image_get_size(sima, &width, &height);
564
565                         vec[0] /= width;
566                         vec[1] /= height;
567                 }
568
569                 vec[0] *= t->aspect[0];
570                 vec[1] *= t->aspect[1];
571         }
572         else if ((t->spacetype == SPACE_CLIP) && (t->mode == TFM_TRANSLATION)) {
573                 if (t->options & (CTX_MOVIECLIP | CTX_MASK)) {
574                         vec[0] *= t->aspect[0];
575                         vec[1] *= t->aspect[1];
576                 }
577         }
578 }
579
580 static void viewRedrawForce(const bContext *C, TransInfo *t)
581 {
582         if (t->options & CTX_GPENCIL_STROKES) {
583                 bGPdata *gpd = ED_gpencil_data_get_active(C);
584                 if (gpd) {
585                         DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
586                 }
587                 WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL);
588         }
589         else if (t->spacetype == SPACE_VIEW3D) {
590                 if (t->options & CTX_PAINT_CURVE) {
591                         wmWindow *window = CTX_wm_window(C);
592                         WM_paint_cursor_tag_redraw(window, t->ar);
593                 }
594                 else {
595                         /* Do we need more refined tags? */
596                         if (t->flag & T_POSE)
597                                 WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
598                         else
599                                 WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
600
601                         /* for realtime animation record - send notifiers recognised by animation editors */
602                         // XXX: is this notifier a lame duck?
603                         if ((t->animtimer) && IS_AUTOKEY_ON(t->scene))
604                                 WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, NULL);
605
606                 }
607         }
608         else if (t->spacetype == SPACE_ACTION) {
609                 //SpaceAction *saction = (SpaceAction *)t->sa->spacedata.first;
610                 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
611         }
612         else if (t->spacetype == SPACE_IPO) {
613                 //SpaceIpo *sipo = (SpaceIpo *)t->sa->spacedata.first;
614                 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
615         }
616         else if (t->spacetype == SPACE_NLA) {
617                 WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_EDITED, NULL);
618         }
619         else if (t->spacetype == SPACE_NODE) {
620                 //ED_area_tag_redraw(t->sa);
621                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_NODE_VIEW, NULL);
622         }
623         else if (t->spacetype == SPACE_SEQ) {
624                 WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, NULL);
625         }
626         else if (t->spacetype == SPACE_IMAGE) {
627                 if (t->options & CTX_MASK) {
628                         Mask *mask = CTX_data_edit_mask(C);
629
630                         WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
631                 }
632                 else if (t->options & CTX_PAINT_CURVE) {
633                         wmWindow *window = CTX_wm_window(C);
634                         WM_paint_cursor_tag_redraw(window, t->ar);
635                 }
636                 else {
637                         // XXX how to deal with lock?
638                         SpaceImage *sima = (SpaceImage *)t->sa->spacedata.first;
639                         if (sima->lock) {
640                                 WM_event_add_notifier(C, NC_GEOM | ND_DATA, OBEDIT_FROM_VIEW_LAYER(t->view_layer)->data);
641                         }
642                         else {
643                                 ED_area_tag_redraw(t->sa);
644                         }
645                 }
646         }
647         else if (t->spacetype == SPACE_CLIP) {
648                 SpaceClip *sc = (SpaceClip *)t->sa->spacedata.first;
649
650                 if (ED_space_clip_check_show_trackedit(sc)) {
651                         MovieClip *clip = ED_space_clip_get_clip(sc);
652
653                         /* objects could be parented to tracking data, so send this for viewport refresh */
654                         WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
655
656                         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
657                 }
658                 else if (ED_space_clip_check_show_maskedit(sc)) {
659                         Mask *mask = CTX_data_edit_mask(C);
660
661                         WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
662                 }
663         }
664 }
665
666 static void viewRedrawPost(bContext *C, TransInfo *t)
667 {
668         ED_area_status_text(t->sa, NULL);
669
670         if (t->spacetype == SPACE_VIEW3D) {
671                 /* if autokeying is enabled, send notifiers that keyframes were added */
672                 if (IS_AUTOKEY_ON(t->scene))
673                         WM_main_add_notifier(NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
674
675                 /* redraw UV editor */
676                 if (ELEM(t->mode, TFM_VERT_SLIDE, TFM_EDGE_SLIDE) &&
677                     (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT))
678                 {
679                         WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL);
680                 }
681
682                 /* XXX temp, first hack to get auto-render in compositor work (ton) */
683                 WM_event_add_notifier(C, NC_SCENE | ND_TRANSFORM_DONE, CTX_data_scene(C));
684
685         }
686
687 #if 0 // TRANSFORM_FIX_ME
688         if (t->spacetype == SPACE_VIEW3D) {
689                 allqueue(REDRAWBUTSOBJECT, 0);
690                 allqueue(REDRAWVIEW3D, 0);
691         }
692         else if (t->spacetype == SPACE_IMAGE) {
693                 allqueue(REDRAWIMAGE, 0);
694                 allqueue(REDRAWVIEW3D, 0);
695         }
696         else if (ELEM(t->spacetype, SPACE_ACTION, SPACE_NLA, SPACE_IPO)) {
697                 allqueue(REDRAWVIEW3D, 0);
698                 allqueue(REDRAWACTION, 0);
699                 allqueue(REDRAWNLA, 0);
700                 allqueue(REDRAWIPO, 0);
701                 allqueue(REDRAWTIME, 0);
702                 allqueue(REDRAWBUTSOBJECT, 0);
703         }
704
705         scrarea_queue_headredraw(curarea);
706 #endif
707 }
708
709 /* ************************** TRANSFORMATIONS **************************** */
710
711 static void view_editmove(unsigned short UNUSED(event))
712 {
713 #if 0 // TRANSFORM_FIX_ME
714         int refresh = 0;
715         /* Regular:   Zoom in */
716         /* Shift:     Scroll up */
717         /* Ctrl:      Scroll right */
718         /* Alt-Shift: Rotate up */
719         /* Alt-Ctrl:  Rotate right */
720
721         /* only work in 3D window for now
722          * In the end, will have to send to event to a 2D window handler instead
723          */
724         if (Trans.flag & T_2D_EDIT)
725                 return;
726
727         switch (event) {
728                 case WHEELUPMOUSE:
729                         if (G.qual & LR_SHIFTKEY) {
730                                 if (G.qual & LR_ALTKEY) {
731                                         G.qual &= ~LR_SHIFTKEY;
732                                         persptoetsen(PAD2);
733                                         G.qual |= LR_SHIFTKEY;
734                                 }
735                                 else {
736                                         persptoetsen(PAD2);
737                                 }
738                         }
739                         else if (G.qual & LR_CTRLKEY) {
740                                 if (G.qual & LR_ALTKEY) {
741                                         G.qual &= ~LR_CTRLKEY;
742                                         persptoetsen(PAD4);
743                                         G.qual |= LR_CTRLKEY;
744                                 }
745                                 else {
746                                         persptoetsen(PAD4);
747                                 }
748                         }
749                         else if (U.uiflag & USER_WHEELZOOMDIR)
750                                 persptoetsen(PADMINUS);
751                         else
752                                 persptoetsen(PADPLUSKEY);
753
754                         refresh = 1;
755                         break;
756                 case WHEELDOWNMOUSE:
757                         if (G.qual & LR_SHIFTKEY) {
758                                 if (G.qual & LR_ALTKEY) {
759                                         G.qual &= ~LR_SHIFTKEY;
760                                         persptoetsen(PAD8);
761                                         G.qual |= LR_SHIFTKEY;
762                                 }
763                                 else {
764                                         persptoetsen(PAD8);
765                                 }
766                         }
767                         else if (G.qual & LR_CTRLKEY) {
768                                 if (G.qual & LR_ALTKEY) {
769                                         G.qual &= ~LR_CTRLKEY;
770                                         persptoetsen(PAD6);
771                                         G.qual |= LR_CTRLKEY;
772                                 }
773                                 else {
774                                         persptoetsen(PAD6);
775                                 }
776                         }
777                         else if (U.uiflag & USER_WHEELZOOMDIR)
778                                 persptoetsen(PADPLUSKEY);
779                         else
780                                 persptoetsen(PADMINUS);
781
782                         refresh = 1;
783                         break;
784         }
785
786         if (refresh)
787                 setTransformViewMatrices(&Trans);
788 #endif
789 }
790
791 /* ************************************************* */
792
793 /* NOTE: these defines are saved in keymap files, do not change values but just add new ones */
794 enum {
795         TFM_MODAL_CANCEL         = 1,
796         TFM_MODAL_CONFIRM        = 2,
797         TFM_MODAL_TRANSLATE      = 3,
798         TFM_MODAL_ROTATE         = 4,
799         TFM_MODAL_RESIZE         = 5,
800         TFM_MODAL_SNAP_INV_ON    = 6,
801         TFM_MODAL_SNAP_INV_OFF   = 7,
802         TFM_MODAL_SNAP_TOGGLE    = 8,
803         TFM_MODAL_AXIS_X         = 9,
804         TFM_MODAL_AXIS_Y         = 10,
805         TFM_MODAL_AXIS_Z         = 11,
806         TFM_MODAL_PLANE_X        = 12,
807         TFM_MODAL_PLANE_Y        = 13,
808         TFM_MODAL_PLANE_Z        = 14,
809         TFM_MODAL_CONS_OFF       = 15,
810         TFM_MODAL_ADD_SNAP       = 16,
811         TFM_MODAL_REMOVE_SNAP    = 17,
812
813 /* 18 and 19 used by numinput, defined in transform.h */
814
815         TFM_MODAL_PROPSIZE_UP    = 20,
816         TFM_MODAL_PROPSIZE_DOWN  = 21,
817         TFM_MODAL_AUTOIK_LEN_INC = 22,
818         TFM_MODAL_AUTOIK_LEN_DEC = 23,
819
820         TFM_MODAL_EDGESLIDE_UP   = 24,
821         TFM_MODAL_EDGESLIDE_DOWN = 25,
822
823 /* for analog input, like trackpad */
824         TFM_MODAL_PROPSIZE       = 26,
825 /* node editor insert offset (aka auto-offset) direction toggle */
826         TFM_MODAL_INSERTOFS_TOGGLE_DIR         = 27,
827 };
828
829 static bool transform_modal_item_poll(const wmOperator *op, int value)
830 {
831         const TransInfo *t = op->customdata;
832         switch (value) {
833                 case TFM_MODAL_CANCEL:
834                 {
835                         if ((t->flag & T_RELEASE_CONFIRM) && ISMOUSE(t->launch_event)) {
836                                 return false;
837                         }
838                         break;
839                 }
840                 case TFM_MODAL_PROPSIZE:
841                 case TFM_MODAL_PROPSIZE_UP:
842                 case TFM_MODAL_PROPSIZE_DOWN:
843                 {
844                         if ((t->flag & T_PROP_EDIT) == 0) {
845                                 return false;
846                         }
847                         break;
848                 }
849                 case TFM_MODAL_ADD_SNAP:
850                 case TFM_MODAL_REMOVE_SNAP:
851                 {
852                         if (t->spacetype != SPACE_VIEW3D) {
853                                 return false;
854                         }
855                         else if (t->tsnap.mode & (SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID)) {
856                                 return false;
857                         }
858                         else if (!validSnap(t)) {
859                                 return false;
860                         }
861                         break;
862                 }
863                 case TFM_MODAL_AXIS_X:
864                 case TFM_MODAL_AXIS_Y:
865                 case TFM_MODAL_AXIS_Z:
866                 case TFM_MODAL_PLANE_X:
867                 case TFM_MODAL_PLANE_Y:
868                 case TFM_MODAL_PLANE_Z:
869                 {
870                         if (t->flag & T_NO_CONSTRAINT) {
871                                 return false;
872                         }
873                         if (!ELEM(value, TFM_MODAL_AXIS_X, TFM_MODAL_AXIS_Y)) {
874                                 if (t->flag & T_2D_EDIT) {
875                                         return false;
876                                 }
877                         }
878                         break;
879                 }
880                 case TFM_MODAL_CONS_OFF:
881                 {
882                         if ((t->con.mode & CON_APPLY) == 0) {
883                                 return false;
884                         }
885                         break;
886                 }
887                 case TFM_MODAL_EDGESLIDE_UP:
888                 case TFM_MODAL_EDGESLIDE_DOWN:
889                 {
890                         if (t->mode != TFM_EDGE_SLIDE) {
891                                 return false;
892                         }
893                         break;
894                 }
895                 case TFM_MODAL_INSERTOFS_TOGGLE_DIR:
896                 {
897                         if (t->spacetype != SPACE_NODE) {
898                                 return false;
899                         }
900                         break;
901                 }
902                 case TFM_MODAL_AUTOIK_LEN_INC:
903                 case TFM_MODAL_AUTOIK_LEN_DEC:
904                 {
905                         if ((t->flag & T_AUTOIK) == 0) {
906                                 return false;
907                         }
908                         break;
909                 }
910         }
911         return true;
912 }
913
914 /* called in transform_ops.c, on each regeneration of keymaps */
915 wmKeyMap *transform_modal_keymap(wmKeyConfig *keyconf)
916 {
917         static const EnumPropertyItem modal_items[] = {
918                 {TFM_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
919                 {TFM_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
920                 {TFM_MODAL_AXIS_X, "AXIS_X", 0, "X axis", ""},
921                 {TFM_MODAL_AXIS_Y, "AXIS_Y", 0, "Y axis", ""},
922                 {TFM_MODAL_AXIS_Z, "AXIS_Z", 0, "Z axis", ""},
923                 {TFM_MODAL_PLANE_X, "PLANE_X", 0, "X plane", ""},
924                 {TFM_MODAL_PLANE_Y, "PLANE_Y", 0, "Y plane", ""},
925                 {TFM_MODAL_PLANE_Z, "PLANE_Z", 0, "Z plane", ""},
926                 {TFM_MODAL_CONS_OFF, "CONS_OFF", 0, "Clear Constraints", ""},
927                 {TFM_MODAL_SNAP_INV_ON, "SNAP_INV_ON", 0, "Snap Invert", ""},
928                 {TFM_MODAL_SNAP_INV_OFF, "SNAP_INV_OFF", 0, "Snap Invert (Off)", ""},
929                 {TFM_MODAL_SNAP_TOGGLE, "SNAP_TOGGLE", 0, "Snap Toggle", ""},
930                 {TFM_MODAL_ADD_SNAP, "ADD_SNAP", 0, "Add Snap Point", ""},
931                 {TFM_MODAL_REMOVE_SNAP, "REMOVE_SNAP", 0, "Remove Last Snap Point", ""},
932                 {NUM_MODAL_INCREMENT_UP, "INCREMENT_UP", 0, "Numinput Increment Up", ""},
933                 {NUM_MODAL_INCREMENT_DOWN, "INCREMENT_DOWN", 0, "Numinput Increment Down", ""},
934                 {TFM_MODAL_PROPSIZE_UP, "PROPORTIONAL_SIZE_UP", 0, "Increase Proportional Influence", ""},
935                 {TFM_MODAL_PROPSIZE_DOWN, "PROPORTIONAL_SIZE_DOWN", 0, "Decrease Proportional Influence", ""},
936                 {TFM_MODAL_AUTOIK_LEN_INC, "AUTOIK_CHAIN_LEN_UP", 0, "Increase Max AutoIK Chain Length", ""},
937                 {TFM_MODAL_AUTOIK_LEN_DEC, "AUTOIK_CHAIN_LEN_DOWN", 0, "Decrease Max AutoIK Chain Length", ""},
938                 {TFM_MODAL_EDGESLIDE_UP, "EDGESLIDE_EDGE_NEXT", 0, "Select next Edge Slide Edge", ""},
939                 {TFM_MODAL_EDGESLIDE_DOWN, "EDGESLIDE_PREV_NEXT", 0, "Select previous Edge Slide Edge", ""},
940                 {TFM_MODAL_PROPSIZE, "PROPORTIONAL_SIZE", 0, "Adjust Proportional Influence", ""},
941                 {TFM_MODAL_INSERTOFS_TOGGLE_DIR, "INSERTOFS_TOGGLE_DIR", 0, "Toggle Direction for Node Auto-offset", ""},
942                 {TFM_MODAL_TRANSLATE, "TRANSLATE", 0, "Move", ""},
943                 {TFM_MODAL_ROTATE, "ROTATE", 0, "Rotate", ""},
944                 {TFM_MODAL_RESIZE, "RESIZE", 0, "Resize", ""},
945                 {0, NULL, 0, NULL, NULL}
946         };
947
948         wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "Transform Modal Map");
949
950         keymap = WM_modalkeymap_add(keyconf, "Transform Modal Map", modal_items);
951         keymap->poll_modal_item = transform_modal_item_poll;
952
953         return keymap;
954 }
955
956 static void transform_event_xyz_constraint(TransInfo *t, short key_type, char cmode, bool is_plane)
957 {
958         if (!(t->flag & T_NO_CONSTRAINT)) {
959                 int constraint_axis, constraint_plane;
960                 const bool edit_2d = (t->flag & T_2D_EDIT) != 0;
961                 const char *msg1 = "", *msg2 = "", *msg3 = "";
962                 char axis;
963
964                 /* Initialize */
965                 switch (key_type) {
966                         case XKEY:
967                                 msg1 = IFACE_("along X");
968                                 msg2 = IFACE_("along %s X");
969                                 msg3 = IFACE_("locking %s X");
970                                 axis = 'X';
971                                 constraint_axis = CON_AXIS0;
972                                 break;
973                         case YKEY:
974                                 msg1 = IFACE_("along Y");
975                                 msg2 = IFACE_("along %s Y");
976                                 msg3 = IFACE_("locking %s Y");
977                                 axis = 'Y';
978                                 constraint_axis = CON_AXIS1;
979                                 break;
980                         case ZKEY:
981                                 msg1 = IFACE_("along Z");
982                                 msg2 = IFACE_("along %s Z");
983                                 msg3 = IFACE_("locking %s Z");
984                                 axis = 'Z';
985                                 constraint_axis = CON_AXIS2;
986                                 break;
987                         default:
988                                 /* Invalid key */
989                                 return;
990                 }
991                 constraint_plane = ((CON_AXIS0 | CON_AXIS1 | CON_AXIS2) & (~constraint_axis));
992
993                 if (edit_2d && (key_type != ZKEY)) {
994                         if (cmode == axis) {
995                                 stopConstraint(t);
996                         }
997                         else {
998                                 setUserConstraint(t, V3D_MANIP_GLOBAL, constraint_axis, msg1);
999                         }
1000                 }
1001                 else if (!edit_2d) {
1002                         if (cmode != axis) {
1003                                 /* First press, constraint to an axis. */
1004                                 t->orientation.index = 0;
1005                                 const short *orientation_ptr = t->orientation.types[t->orientation.index];
1006                                 const short  orientation = orientation_ptr ? *orientation_ptr : V3D_MANIP_GLOBAL;
1007                                 if (is_plane == false) {
1008                                         setUserConstraint(t, orientation, constraint_axis, msg2);
1009                                 }
1010                                 else {
1011                                         setUserConstraint(t, orientation, constraint_plane, msg3);
1012                                 }
1013                         }
1014                         else {
1015                                 /* Successive presses on existing axis, cycle orientation modes. */
1016                                 t->orientation.index = (t->orientation.index + 1) % ARRAY_SIZE(t->orientation.types);
1017
1018                                 if (t->orientation.index == 0) {
1019                                         stopConstraint(t);
1020                                 }
1021                                 else {
1022                                         const short *orientation_ptr = t->orientation.types[t->orientation.index];
1023                                         const short  orientation = orientation_ptr ? *orientation_ptr : V3D_MANIP_GLOBAL;
1024                                         if (is_plane == false) {
1025                                                 setUserConstraint(t, orientation, constraint_axis, msg2);
1026                                         }
1027                                         else {
1028                                                 setUserConstraint(t, orientation, constraint_plane, msg3);
1029                                         }
1030                                 }
1031                         }
1032                 }
1033                 t->redraw |= TREDRAW_HARD;
1034         }
1035 }
1036
1037 int transformEvent(TransInfo *t, const wmEvent *event)
1038 {
1039         char cmode = constraintModeToChar(t);
1040         bool handled = false;
1041         const int modifiers_prev = t->modifiers;
1042
1043         t->redraw |= handleMouseInput(t, &t->mouse, event);
1044
1045         /* Handle modal numinput events first, if already activated. */
1046         if (((event->val == KM_PRESS) || (event->type == EVT_MODAL_MAP)) &&
1047             hasNumInput(&t->num) && handleNumInput(t->context, &(t->num), event))
1048         {
1049                 t->redraw |= TREDRAW_HARD;
1050                 handled = true;
1051         }
1052         else if (event->type == MOUSEMOVE) {
1053                 if (t->modifiers & MOD_CONSTRAINT_SELECT)
1054                         t->con.mode |= CON_SELECT;
1055
1056                 copy_v2_v2_int(t->mval, event->mval);
1057
1058                 // t->redraw |= TREDRAW_SOFT; /* Use this for soft redraw. Might cause flicker in object mode */
1059                 t->redraw |= TREDRAW_HARD;
1060
1061                 if (t->state == TRANS_STARTING) {
1062                         t->state = TRANS_RUNNING;
1063                 }
1064
1065                 applyMouseInput(t, &t->mouse, t->mval, t->values);
1066
1067                 // Snapping mouse move events
1068                 t->redraw |= handleSnapping(t, event);
1069                 handled = true;
1070         }
1071         /* handle modal keymap first */
1072         else if (event->type == EVT_MODAL_MAP) {
1073                 switch (event->val) {
1074                         case TFM_MODAL_CANCEL:
1075                                 t->state = TRANS_CANCEL;
1076                                 handled = true;
1077                                 break;
1078                         case TFM_MODAL_CONFIRM:
1079                                 t->state = TRANS_CONFIRM;
1080                                 handled = true;
1081                                 break;
1082                         case TFM_MODAL_TRANSLATE:
1083                                 /* only switch when... */
1084                                 if (ELEM(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) {
1085                                         restoreTransObjects(t);
1086                                         resetTransModal(t);
1087                                         resetTransRestrictions(t);
1088                                         initTranslation(t);
1089                                         initSnapping(t, NULL); // need to reinit after mode change
1090                                         t->redraw |= TREDRAW_HARD;
1091                                         WM_event_add_mousemove(t->context);
1092                                         handled = true;
1093                                 }
1094                                 else if (t->mode == TFM_SEQ_SLIDE) {
1095                                         t->flag ^= T_ALT_TRANSFORM;
1096                                         t->redraw |= TREDRAW_HARD;
1097                                         handled = true;
1098                                 }
1099                                 else {
1100                                         if (t->obedit_type == OB_MESH) {
1101                                                 if ((t->mode == TFM_TRANSLATION) && (t->spacetype == SPACE_VIEW3D)) {
1102                                                         restoreTransObjects(t);
1103                                                         resetTransModal(t);
1104                                                         resetTransRestrictions(t);
1105
1106                                                         /* first try edge slide */
1107                                                         initEdgeSlide(t);
1108                                                         /* if that fails, do vertex slide */
1109                                                         if (t->state == TRANS_CANCEL) {
1110                                                                 resetTransModal(t);
1111                                                                 t->state = TRANS_STARTING;
1112                                                                 initVertSlide(t);
1113                                                         }
1114                                                         /* vert slide can fail on unconnected vertices (rare but possible) */
1115                                                         if (t->state == TRANS_CANCEL) {
1116                                                                 resetTransModal(t);
1117                                                                 t->mode = TFM_TRANSLATION;
1118                                                                 t->state = TRANS_STARTING;
1119                                                                 restoreTransObjects(t);
1120                                                                 resetTransRestrictions(t);
1121                                                                 initTranslation(t);
1122                                                         }
1123                                                         initSnapping(t, NULL); // need to reinit after mode change
1124                                                         t->redraw |= TREDRAW_HARD;
1125                                                         handled = true;
1126                                                         WM_event_add_mousemove(t->context);
1127                                                 }
1128                                         }
1129                                         else if (t->options & (CTX_MOVIECLIP | CTX_MASK)) {
1130                                                 if (t->mode == TFM_TRANSLATION) {
1131                                                         restoreTransObjects(t);
1132
1133                                                         t->flag ^= T_ALT_TRANSFORM;
1134                                                         t->redraw |= TREDRAW_HARD;
1135                                                         handled = true;
1136                                                 }
1137                                         }
1138                                 }
1139                                 break;
1140                         case TFM_MODAL_ROTATE:
1141                                 /* only switch when... */
1142                                 if (!(t->options & CTX_TEXTURE) && !(t->options & (CTX_MOVIECLIP | CTX_MASK))) {
1143                                         if (ELEM(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL, TFM_TRANSLATION, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) {
1144                                                 restoreTransObjects(t);
1145                                                 resetTransModal(t);
1146                                                 resetTransRestrictions(t);
1147
1148                                                 if (t->mode == TFM_ROTATION) {
1149                                                         initTrackball(t);
1150                                                 }
1151                                                 else {
1152                                                         initRotation(t);
1153                                                 }
1154                                                 initSnapping(t, NULL); // need to reinit after mode change
1155                                                 t->redraw |= TREDRAW_HARD;
1156                                                 handled = true;
1157                                         }
1158                                 }
1159                                 break;
1160                         case TFM_MODAL_RESIZE:
1161                                 /* only switch when... */
1162                                 if (ELEM(t->mode, TFM_ROTATION, TFM_TRANSLATION, TFM_TRACKBALL, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) {
1163
1164                                         /* Scale isn't normally very useful after extrude along normals, see T39756 */
1165                                         if ((t->con.mode & CON_APPLY) && (t->con.orientation == V3D_MANIP_NORMAL)) {
1166                                                 stopConstraint(t);
1167                                         }
1168
1169                                         restoreTransObjects(t);
1170                                         resetTransModal(t);
1171                                         resetTransRestrictions(t);
1172                                         initResize(t);
1173                                         initSnapping(t, NULL); // need to reinit after mode change
1174                                         t->redraw |= TREDRAW_HARD;
1175                                         handled = true;
1176                                 }
1177                                 else if (t->mode == TFM_SHRINKFATTEN) {
1178                                         t->flag ^= T_ALT_TRANSFORM;
1179                                         t->redraw |= TREDRAW_HARD;
1180                                         handled = true;
1181                                 }
1182                                 else if (t->mode == TFM_RESIZE) {
1183                                         if (t->options & CTX_MOVIECLIP) {
1184                                                 restoreTransObjects(t);
1185
1186                                                 t->flag ^= T_ALT_TRANSFORM;
1187                                                 t->redraw |= TREDRAW_HARD;
1188                                                 handled = true;
1189                                         }
1190                                 }
1191                                 break;
1192
1193                         case TFM_MODAL_SNAP_INV_ON:
1194                                 t->modifiers |= MOD_SNAP_INVERT;
1195                                 t->redraw |= TREDRAW_HARD;
1196                                 handled = true;
1197                                 break;
1198                         case TFM_MODAL_SNAP_INV_OFF:
1199                                 t->modifiers &= ~MOD_SNAP_INVERT;
1200                                 t->redraw |= TREDRAW_HARD;
1201                                 handled = true;
1202                                 break;
1203                         case TFM_MODAL_SNAP_TOGGLE:
1204                                 t->modifiers ^= MOD_SNAP;
1205                                 t->redraw |= TREDRAW_HARD;
1206                                 handled = true;
1207                                 break;
1208                         case TFM_MODAL_AXIS_X:
1209                                 if (!(t->flag & T_NO_CONSTRAINT)) {
1210                                         transform_event_xyz_constraint(t, XKEY, cmode, false);
1211                                         t->redraw |= TREDRAW_HARD;
1212                                         handled = true;
1213                                 }
1214                                 break;
1215                         case TFM_MODAL_AXIS_Y:
1216                                 if ((t->flag & T_NO_CONSTRAINT) == 0) {
1217                                         transform_event_xyz_constraint(t, YKEY, cmode, false);
1218                                         t->redraw |= TREDRAW_HARD;
1219                                         handled = true;
1220                                 }
1221                                 break;
1222                         case TFM_MODAL_AXIS_Z:
1223                                 if ((t->flag & (T_NO_CONSTRAINT)) == 0) {
1224                                         transform_event_xyz_constraint(t, ZKEY, cmode, false);
1225                                         t->redraw |= TREDRAW_HARD;
1226                                         handled = true;
1227                                 }
1228                                 break;
1229                         case TFM_MODAL_PLANE_X:
1230                                 if ((t->flag & (T_NO_CONSTRAINT | T_2D_EDIT)) == 0) {
1231                                         transform_event_xyz_constraint(t, XKEY, cmode, true);
1232                                         t->redraw |= TREDRAW_HARD;
1233                                         handled = true;
1234                                 }
1235                                 break;
1236                         case TFM_MODAL_PLANE_Y:
1237                                 if ((t->flag & (T_NO_CONSTRAINT | T_2D_EDIT)) == 0) {
1238                                         transform_event_xyz_constraint(t, YKEY, cmode, true);
1239                                         t->redraw |= TREDRAW_HARD;
1240                                         handled = true;
1241                                 }
1242                                 break;
1243                         case TFM_MODAL_PLANE_Z:
1244                                 if ((t->flag & (T_NO_CONSTRAINT | T_2D_EDIT)) == 0) {
1245                                         transform_event_xyz_constraint(t, ZKEY, cmode, true);
1246                                         t->redraw |= TREDRAW_HARD;
1247                                         handled = true;
1248                                 }
1249                                 break;
1250                         case TFM_MODAL_CONS_OFF:
1251                                 if ((t->flag & T_NO_CONSTRAINT) == 0) {
1252                                         stopConstraint(t);
1253                                         t->redraw |= TREDRAW_HARD;
1254                                         handled = true;
1255                                 }
1256                                 break;
1257                         case TFM_MODAL_ADD_SNAP:
1258                                 addSnapPoint(t);
1259                                 t->redraw |= TREDRAW_HARD;
1260                                 handled = true;
1261                                 break;
1262                         case TFM_MODAL_REMOVE_SNAP:
1263                                 removeSnapPoint(t);
1264                                 t->redraw |= TREDRAW_HARD;
1265                                 handled = true;
1266                                 break;
1267                         case TFM_MODAL_PROPSIZE:
1268                                 /* MOUSEPAN usage... */
1269                                 if (t->flag & T_PROP_EDIT) {
1270                                         float fac = 1.0f + 0.005f *(event->y - event->prevy);
1271                                         t->prop_size *= fac;
1272                                         if (t->spacetype == SPACE_VIEW3D && t->persp != RV3D_ORTHO) {
1273                                                 t->prop_size = max_ff(min_ff(t->prop_size, ((View3D *)t->view)->far), T_PROP_SIZE_MIN);
1274                                         }
1275                                         else {
1276                                                 t->prop_size = max_ff(min_ff(t->prop_size, T_PROP_SIZE_MAX), T_PROP_SIZE_MIN);
1277                                         }
1278                                         calculatePropRatio(t);
1279                                         t->redraw |= TREDRAW_HARD;
1280                                         handled = true;
1281                                 }
1282                                 break;
1283                         case TFM_MODAL_PROPSIZE_UP:
1284                                 if (t->flag & T_PROP_EDIT) {
1285                                         t->prop_size *= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f;
1286                                         if (t->spacetype == SPACE_VIEW3D && t->persp != RV3D_ORTHO) {
1287                                                 t->prop_size = min_ff(t->prop_size, ((View3D *)t->view)->far);
1288                                         }
1289                                         else {
1290                                                 t->prop_size = min_ff(t->prop_size, T_PROP_SIZE_MAX);
1291                                         }
1292                                         calculatePropRatio(t);
1293                                         t->redraw |= TREDRAW_HARD;
1294                                         handled = true;
1295                                 }
1296                                 break;
1297                         case TFM_MODAL_PROPSIZE_DOWN:
1298                                 if (t->flag & T_PROP_EDIT) {
1299                                         t->prop_size /= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f;
1300                                         t->prop_size = max_ff(t->prop_size, T_PROP_SIZE_MIN);
1301                                         calculatePropRatio(t);
1302                                         t->redraw |= TREDRAW_HARD;
1303                                         handled = true;
1304                                 }
1305                                 break;
1306                         case TFM_MODAL_AUTOIK_LEN_INC:
1307                                 if (t->flag & T_AUTOIK) {
1308                                         transform_autoik_update(t, 1);
1309                                         t->redraw |= TREDRAW_HARD;
1310                                         handled = true;
1311                                 }
1312                                 break;
1313                         case TFM_MODAL_AUTOIK_LEN_DEC:
1314                                 if (t->flag & T_AUTOIK) {
1315                                         transform_autoik_update(t, -1);
1316                                         t->redraw |= TREDRAW_HARD;
1317                                         handled = true;
1318                                 }
1319                                 break;
1320                         case TFM_MODAL_INSERTOFS_TOGGLE_DIR:
1321                                 if (t->spacetype == SPACE_NODE) {
1322                                         SpaceNode *snode = (SpaceNode *)t->sa->spacedata.first;
1323
1324                                         BLI_assert(t->sa->spacetype == t->spacetype);
1325
1326                                         if (snode->insert_ofs_dir == SNODE_INSERTOFS_DIR_RIGHT) {
1327                                                 snode->insert_ofs_dir = SNODE_INSERTOFS_DIR_LEFT;
1328                                         }
1329                                         else if (snode->insert_ofs_dir == SNODE_INSERTOFS_DIR_LEFT) {
1330                                                 snode->insert_ofs_dir = SNODE_INSERTOFS_DIR_RIGHT;
1331                                         }
1332                                         else {
1333                                                 BLI_assert(0);
1334                                         }
1335
1336                                         t->redraw |= TREDRAW_SOFT;
1337                                 }
1338                                 break;
1339                         /* Those two are only handled in transform's own handler, see T44634! */
1340                         case TFM_MODAL_EDGESLIDE_UP:
1341                         case TFM_MODAL_EDGESLIDE_DOWN:
1342                         default:
1343                                 break;
1344                 }
1345         }
1346         /* else do non-mapped events */
1347         else if (event->val == KM_PRESS) {
1348                 switch (event->type) {
1349                         case RIGHTMOUSE:
1350                                 t->state = TRANS_CANCEL;
1351                                 handled = true;
1352                                 break;
1353                         /* enforce redraw of transform when modifiers are used */
1354                         case LEFTSHIFTKEY:
1355                         case RIGHTSHIFTKEY:
1356                                 t->modifiers |= MOD_CONSTRAINT_PLANE;
1357                                 t->redraw |= TREDRAW_HARD;
1358                                 handled = true;
1359                                 break;
1360
1361                         case SPACEKEY:
1362                                 t->state = TRANS_CONFIRM;
1363                                 handled = true;
1364                                 break;
1365
1366                         case MIDDLEMOUSE:
1367                                 if ((t->flag & T_NO_CONSTRAINT) == 0) {
1368                                         /* exception for switching to dolly, or trackball, in camera view */
1369                                         if (t->flag & T_CAMERA) {
1370                                                 if (t->mode == TFM_TRANSLATION)
1371                                                         setLocalConstraint(t, (CON_AXIS2), IFACE_("along local Z"));
1372                                                 else if (t->mode == TFM_ROTATION) {
1373                                                         restoreTransObjects(t);
1374                                                         initTrackball(t);
1375                                                 }
1376                                         }
1377                                         else {
1378                                                 t->modifiers |= MOD_CONSTRAINT_SELECT;
1379                                                 if (t->con.mode & CON_APPLY) {
1380                                                         stopConstraint(t);
1381                                                 }
1382                                                 else {
1383                                                         if (event->shift) {
1384                                                                 /* bit hackish... but it prevents mmb select to print the orientation from menu */
1385                                                                 float mati[3][3];
1386                                                                 strcpy(t->spacename, "global");
1387                                                                 unit_m3(mati);
1388                                                                 initSelectConstraint(t, mati);
1389                                                         }
1390                                                         else {
1391                                                                 initSelectConstraint(t, t->spacemtx);
1392                                                         }
1393                                                         postSelectConstraint(t);
1394                                                 }
1395                                         }
1396                                         t->redraw |= TREDRAW_HARD;
1397                                         handled = true;
1398                                 }
1399                                 break;
1400                         case ESCKEY:
1401                                 t->state = TRANS_CANCEL;
1402                                 handled = true;
1403                                 break;
1404                         case PADENTER:
1405                         case RETKEY:
1406                                 t->state = TRANS_CONFIRM;
1407                                 handled = true;
1408                                 break;
1409                         case GKEY:
1410                                 /* only switch when... */
1411                                 if (ELEM(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL)) {
1412                                         restoreTransObjects(t);
1413                                         resetTransModal(t);
1414                                         resetTransRestrictions(t);
1415                                         initTranslation(t);
1416                                         initSnapping(t, NULL); // need to reinit after mode change
1417                                         t->redraw |= TREDRAW_HARD;
1418                                         handled = true;
1419                                 }
1420                                 break;
1421                         case SKEY:
1422                                 /* only switch when... */
1423                                 if (ELEM(t->mode, TFM_ROTATION, TFM_TRANSLATION, TFM_TRACKBALL)) {
1424                                         restoreTransObjects(t);
1425                                         resetTransModal(t);
1426                                         resetTransRestrictions(t);
1427                                         initResize(t);
1428                                         initSnapping(t, NULL); // need to reinit after mode change
1429                                         t->redraw |= TREDRAW_HARD;
1430                                         handled = true;
1431                                 }
1432                                 break;
1433                         case RKEY:
1434                                 /* only switch when... */
1435                                 if (!(t->options & CTX_TEXTURE)) {
1436                                         if (ELEM(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL, TFM_TRANSLATION)) {
1437                                                 restoreTransObjects(t);
1438                                                 resetTransModal(t);
1439                                                 resetTransRestrictions(t);
1440
1441                                                 if (t->mode == TFM_ROTATION) {
1442                                                         initTrackball(t);
1443                                                 }
1444                                                 else {
1445                                                         initRotation(t);
1446                                                 }
1447                                                 initSnapping(t, NULL); // need to reinit after mode change
1448                                                 t->redraw |= TREDRAW_HARD;
1449                                                 handled = true;
1450                                         }
1451                                 }
1452                                 break;
1453                         case CKEY:
1454                                 if (event->alt) {
1455                                         if (!(t->options & CTX_NO_PET)) {
1456                                                 t->flag ^= T_PROP_CONNECTED;
1457                                                 sort_trans_data_dist(t);
1458                                                 calculatePropRatio(t);
1459                                                 t->redraw = TREDRAW_HARD;
1460                                                 handled = true;
1461                                         }
1462                                 }
1463                                 break;
1464                         case OKEY:
1465                                 if (t->flag & T_PROP_EDIT && event->shift) {
1466                                         t->prop_mode = (t->prop_mode + 1) % PROP_MODE_MAX;
1467                                         calculatePropRatio(t);
1468                                         t->redraw |= TREDRAW_HARD;
1469                                         handled = true;
1470                                 }
1471                                 break;
1472                         case PADPLUSKEY:
1473                                 if (event->alt && t->flag & T_PROP_EDIT) {
1474                                         t->prop_size *= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f;
1475                                         if (t->spacetype == SPACE_VIEW3D && t->persp != RV3D_ORTHO)
1476                                                 t->prop_size = min_ff(t->prop_size, ((View3D *)t->view)->far);
1477                                         calculatePropRatio(t);
1478                                         t->redraw = TREDRAW_HARD;
1479                                         handled = true;
1480                                 }
1481                                 break;
1482                         case PAGEUPKEY:
1483                         case WHEELDOWNMOUSE:
1484                                 if (t->flag & T_AUTOIK) {
1485                                         transform_autoik_update(t, 1);
1486                                 }
1487                                 else {
1488                                         view_editmove(event->type);
1489                                 }
1490                                 t->redraw = TREDRAW_HARD;
1491                                 handled = true;
1492                                 break;
1493                         case PADMINUS:
1494                                 if (event->alt && t->flag & T_PROP_EDIT) {
1495                                         t->prop_size /= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f;
1496                                         calculatePropRatio(t);
1497                                         t->redraw = TREDRAW_HARD;
1498                                         handled = true;
1499                                 }
1500                                 break;
1501                         case PAGEDOWNKEY:
1502                         case WHEELUPMOUSE:
1503                                 if (t->flag & T_AUTOIK) {
1504                                         transform_autoik_update(t, -1);
1505                                 }
1506                                 else {
1507                                         view_editmove(event->type);
1508                                 }
1509                                 t->redraw = TREDRAW_HARD;
1510                                 handled = true;
1511                                 break;
1512                         case LEFTALTKEY:
1513                         case RIGHTALTKEY:
1514                                 if (ELEM(t->spacetype, SPACE_SEQ, SPACE_VIEW3D)) {
1515                                         t->flag |= T_ALT_TRANSFORM;
1516                                         t->redraw |= TREDRAW_HARD;
1517                                         handled = true;
1518                                 }
1519                                 break;
1520                         case NKEY:
1521                                 if (ELEM(t->mode, TFM_ROTATION)) {
1522                                         if ((t->flag & T_EDIT) && t->obedit_type == OB_MESH) {
1523                                                 restoreTransObjects(t);
1524                                                 resetTransModal(t);
1525                                                 resetTransRestrictions(t);
1526                                                 initNormalRotation(t);
1527                                                 t->redraw = TREDRAW_HARD;
1528                                                 handled = true;
1529                                         }
1530                                 }
1531                                 break;
1532                         default:
1533                                 break;
1534                 }
1535
1536                 /* Snapping key events */
1537                 t->redraw |= handleSnapping(t, event);
1538         }
1539         else if (event->val == KM_RELEASE) {
1540                 switch (event->type) {
1541                         case LEFTSHIFTKEY:
1542                         case RIGHTSHIFTKEY:
1543                                 t->modifiers &= ~MOD_CONSTRAINT_PLANE;
1544                                 t->redraw |= TREDRAW_HARD;
1545                                 handled = true;
1546                                 break;
1547
1548                         case MIDDLEMOUSE:
1549                                 if ((t->flag & T_NO_CONSTRAINT) == 0) {
1550                                         t->modifiers &= ~MOD_CONSTRAINT_SELECT;
1551                                         postSelectConstraint(t);
1552                                         t->redraw |= TREDRAW_HARD;
1553                                         handled = true;
1554                                 }
1555                                 break;
1556                         case LEFTALTKEY:
1557                         case RIGHTALTKEY:
1558                                 if (ELEM(t->spacetype, SPACE_SEQ, SPACE_VIEW3D)) {
1559                                         t->flag &= ~T_ALT_TRANSFORM;
1560                                         t->redraw |= TREDRAW_HARD;
1561                                         handled = true;
1562                                 }
1563                                 break;
1564                         default:
1565                                 break;
1566                 }
1567
1568                 /* confirm transform if launch key is released after mouse move */
1569                 if (t->flag & T_RELEASE_CONFIRM) {
1570                         /* XXX Keyrepeat bug in Xorg messes this up, will test when fixed */
1571                         if ((event->type == t->launch_event) && ISMOUSE(t->launch_event)) {
1572                                 t->state = TRANS_CONFIRM;
1573                         }
1574                 }
1575         }
1576
1577         /* if we change snap options, get the unsnapped values back */
1578         if ((t->modifiers   & (MOD_SNAP | MOD_SNAP_INVERT)) !=
1579             (modifiers_prev & (MOD_SNAP | MOD_SNAP_INVERT)))
1580         {
1581                 applyMouseInput(t, &t->mouse, t->mval, t->values);
1582         }
1583
1584         /* Per transform event, if present */
1585         if (t->handleEvent &&
1586             (!handled ||
1587              /* Needed for vertex slide, see [#38756] */
1588              (event->type == MOUSEMOVE)))
1589         {
1590                 t->redraw |= t->handleEvent(t, event);
1591         }
1592
1593         /* Try to init modal numinput now, if possible. */
1594         if (!(handled || t->redraw) && ((event->val == KM_PRESS) || (event->type == EVT_MODAL_MAP)) &&
1595             handleNumInput(t->context, &(t->num), event))
1596         {
1597                 t->redraw |= TREDRAW_HARD;
1598                 handled = true;
1599         }
1600
1601         if (t->redraw &&
1602             !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE))
1603         {
1604                 WM_window_status_area_tag_redraw(CTX_wm_window(t->context));
1605         }
1606
1607         if (handled || t->redraw) {
1608                 return 0;
1609         }
1610         else {
1611                 return OPERATOR_PASS_THROUGH;
1612         }
1613 }
1614
1615 bool calculateTransformCenter(bContext *C, int centerMode, float cent3d[3], float cent2d[2])
1616 {
1617         TransInfo *t = MEM_callocN(sizeof(TransInfo), "TransInfo data");
1618         bool success;
1619
1620         t->context = C;
1621
1622         t->state = TRANS_RUNNING;
1623
1624         /* avoid calculating PET */
1625         t->options = CTX_NO_PET;
1626
1627         t->mode = TFM_DUMMY;
1628
1629         initTransInfo(C, t, NULL, NULL);
1630
1631         /* avoid doing connectivity lookups (when V3D_AROUND_LOCAL_ORIGINS is set) */
1632         t->around = V3D_AROUND_CENTER_BOUNDS;
1633
1634         createTransData(C, t);              // make TransData structs from selection
1635
1636         t->around = centerMode;             // override userdefined mode
1637
1638         if (t->data_len_all == 0) {
1639                 success = false;
1640         }
1641         else {
1642                 success = true;
1643
1644                 calculateCenter(t);
1645
1646                 if (cent2d) {
1647                         copy_v2_v2(cent2d, t->center2d);
1648                 }
1649
1650                 if (cent3d) {
1651                         // Copy center from constraint center. Transform center can be local
1652                         copy_v3_v3(cent3d, t->center_global);
1653                 }
1654         }
1655
1656
1657         /* aftertrans does insert keyframes, and clears base flags; doesn't read transdata */
1658         special_aftertrans_update(C, t);
1659
1660         postTrans(C, t);
1661
1662         MEM_freeN(t);
1663
1664         return success;
1665 }
1666
1667 typedef enum {
1668         UP,
1669         DOWN,
1670         LEFT,
1671         RIGHT
1672 } ArrowDirection;
1673
1674 #define POS_INDEX 0
1675 /* NOTE: this --^ is a bit hackish, but simplifies GPUVertFormat usage among functions
1676  * private to this file  - merwin
1677  */
1678
1679 static void drawArrow(ArrowDirection d, short offset, short length, short size)
1680 {
1681         immBegin(GPU_PRIM_LINES, 6);
1682
1683         switch (d) {
1684                 case LEFT:
1685                         offset = -offset;
1686                         length = -length;
1687                         size = -size;
1688                         ATTR_FALLTHROUGH;
1689                 case RIGHT:
1690                         immVertex2f(POS_INDEX, offset, 0);
1691                         immVertex2f(POS_INDEX, offset + length, 0);
1692                         immVertex2f(POS_INDEX, offset + length, 0);
1693                         immVertex2f(POS_INDEX, offset + length - size, -size);
1694                         immVertex2f(POS_INDEX, offset + length, 0);
1695                         immVertex2f(POS_INDEX, offset + length - size,  size);
1696                         break;
1697
1698                 case DOWN:
1699                         offset = -offset;
1700                         length = -length;
1701                         size = -size;
1702                         ATTR_FALLTHROUGH;
1703                 case UP:
1704                         immVertex2f(POS_INDEX, 0, offset);
1705                         immVertex2f(POS_INDEX, 0, offset + length);
1706                         immVertex2f(POS_INDEX, 0, offset + length);
1707                         immVertex2f(POS_INDEX, -size, offset + length - size);
1708                         immVertex2f(POS_INDEX, 0, offset + length);
1709                         immVertex2f(POS_INDEX, size, offset + length - size);
1710                         break;
1711         }
1712
1713         immEnd();
1714 }
1715
1716 static void drawArrowHead(ArrowDirection d, short size)
1717 {
1718         immBegin(GPU_PRIM_LINES, 4);
1719
1720         switch (d) {
1721                 case LEFT:
1722                         size = -size;
1723                         ATTR_FALLTHROUGH;
1724                 case RIGHT:
1725                         immVertex2f(POS_INDEX, 0, 0);
1726                         immVertex2f(POS_INDEX, -size, -size);
1727                         immVertex2f(POS_INDEX, 0, 0);
1728                         immVertex2f(POS_INDEX, -size,  size);
1729                         break;
1730
1731                 case DOWN:
1732                         size = -size;
1733                         ATTR_FALLTHROUGH;
1734                 case UP:
1735                         immVertex2f(POS_INDEX, 0, 0);
1736                         immVertex2f(POS_INDEX, -size, -size);
1737                         immVertex2f(POS_INDEX, 0, 0);
1738                         immVertex2f(POS_INDEX, size, -size);
1739                         break;
1740         }
1741
1742         immEnd();
1743 }
1744
1745 static void drawArc(float size, float angle_start, float angle_end, int segments)
1746 {
1747         float delta = (angle_end - angle_start) / segments;
1748         float angle;
1749         int a;
1750
1751         immBegin(GPU_PRIM_LINE_STRIP, segments + 1);
1752
1753         for (angle = angle_start, a = 0; a < segments; angle += delta, a++) {
1754                 immVertex2f(POS_INDEX, cosf(angle) * size, sinf(angle) * size);
1755         }
1756         immVertex2f(POS_INDEX, cosf(angle_end) * size, sinf(angle_end) * size);
1757
1758         immEnd();
1759 }
1760
1761 static bool helpline_poll(bContext *C)
1762 {
1763         ARegion *ar = CTX_wm_region(C);
1764
1765         if (ar && ar->regiontype == RGN_TYPE_WINDOW)
1766                 return 1;
1767         return 0;
1768 }
1769
1770 static void drawHelpline(bContext *UNUSED(C), int x, int y, void *customdata)
1771 {
1772         TransInfo *t = (TransInfo *)customdata;
1773
1774         if (t->helpline != HLP_NONE) {
1775                 float cent[2];
1776                 float mval[3] = {
1777                     x,
1778                     y,
1779                     0.0f,
1780                 };
1781                 float tmval[2] = {
1782                     (float)t->mval[0],
1783                     (float)t->mval[1],
1784                 };
1785
1786                 projectFloatViewEx(t, t->center_global, cent, V3D_PROJ_TEST_CLIP_ZERO);
1787                 /* Offset the values for the area region. */
1788                 const float offset[2] = {
1789                     t->ar->winrct.xmin,
1790                     t->ar->winrct.ymin,
1791                 };
1792
1793                 for (int i = 0; i < 2; i++) {
1794                         cent[i] += offset[i];
1795                         tmval[i] += offset[i];
1796                 }
1797
1798                 GPU_matrix_push();
1799
1800                 /* Dashed lines first. */
1801                 if (ELEM(t->helpline, HLP_SPRING, HLP_ANGLE)) {
1802                         const uint shdr_pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
1803
1804                         UNUSED_VARS_NDEBUG(shdr_pos); /* silence warning */
1805                         BLI_assert(shdr_pos == POS_INDEX);
1806
1807                         GPU_line_width(1.0f);
1808
1809                         immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
1810
1811                         float viewport_size[4];
1812                         GPU_viewport_size_get_f(viewport_size);
1813                         immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
1814
1815                         immUniform1i("colors_len", 0);  /* "simple" mode */
1816                         immUniformThemeColor(TH_VIEW_OVERLAY);
1817                         immUniform1f("dash_width", 6.0f);
1818                         immUniform1f("dash_factor", 0.5f);
1819
1820                         immBegin(GPU_PRIM_LINES, 2);
1821                         immVertex2fv(POS_INDEX, cent);
1822                         immVertex2f(POS_INDEX, tmval[0], tmval[1]);
1823                         immEnd();
1824
1825                         immUnbindProgram();
1826                 }
1827
1828                 /* And now, solid lines. */
1829                 uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
1830                 UNUSED_VARS_NDEBUG(pos); /* silence warning */
1831                 BLI_assert(pos == POS_INDEX);
1832                 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
1833
1834                 switch (t->helpline) {
1835                         case HLP_SPRING:
1836                                 immUniformThemeColor(TH_VIEW_OVERLAY);
1837
1838                                 GPU_matrix_translate_3fv(mval);
1839                                 GPU_matrix_rotate_axis(-RAD2DEGF(atan2f(cent[0] - tmval[0], cent[1] - tmval[1])), 'Z');
1840
1841                                 GPU_line_width(3.0f);
1842                                 drawArrow(UP, 5, 10, 5);
1843                                 drawArrow(DOWN, 5, 10, 5);
1844                                 break;
1845                         case HLP_HARROW:
1846                                 immUniformThemeColor(TH_VIEW_OVERLAY);
1847                                 GPU_matrix_translate_3fv(mval);
1848
1849                                 GPU_line_width(3.0f);
1850                                 drawArrow(RIGHT, 5, 10, 5);
1851                                 drawArrow(LEFT, 5, 10, 5);
1852                                 break;
1853                         case HLP_VARROW:
1854                                 immUniformThemeColor(TH_VIEW_OVERLAY);
1855
1856                                 GPU_matrix_translate_3fv(mval);
1857
1858                                 GPU_line_width(3.0f);
1859                                 drawArrow(UP, 5, 10, 5);
1860                                 drawArrow(DOWN, 5, 10, 5);
1861                                 break;
1862                         case HLP_CARROW:
1863                         {
1864                                 /* Draw arrow based on direction defined by custom-points. */
1865                                 immUniformThemeColor(TH_VIEW_OVERLAY);
1866
1867                                 GPU_matrix_translate_3fv(mval);
1868
1869                                 GPU_line_width(3.0f);
1870
1871                                 const int *data = t->mouse.data;
1872                                 const float dx = data[2] - data[0], dy = data[3] - data[1];
1873                                 const float angle = -atan2f(dx, dy);
1874
1875                                 GPU_matrix_push();
1876
1877                                 GPU_matrix_rotate_axis(RAD2DEGF(angle), 'Z');
1878
1879                                 drawArrow(UP, 5, 10, 5);
1880                                 drawArrow(DOWN, 5, 10, 5);
1881
1882                                 GPU_matrix_pop();
1883                                 break;
1884                         }
1885                         case HLP_ANGLE:
1886                         {
1887                                 float dx = tmval[0] - cent[0], dy = tmval[1] - cent[1];
1888                                 float angle = atan2f(dy, dx);
1889                                 float dist = hypotf(dx, dy);
1890                                 float delta_angle = min_ff(15.0f / dist, (float)M_PI / 4.0f);
1891                                 float spacing_angle = min_ff(5.0f / dist, (float)M_PI / 12.0f);
1892
1893                                 immUniformThemeColor(TH_VIEW_OVERLAY);
1894
1895                                 GPU_matrix_translate_3f(cent[0] - tmval[0] + mval[0], cent[1] - tmval[1] + mval[1], 0);
1896
1897                                 GPU_line_width(3.0f);
1898                                 drawArc(dist, angle - delta_angle, angle - spacing_angle, 10);
1899                                 drawArc(dist, angle + spacing_angle, angle + delta_angle, 10);
1900
1901                                 GPU_matrix_push();
1902
1903                                 GPU_matrix_translate_3f(cosf(angle - delta_angle) * dist, sinf(angle - delta_angle) * dist, 0);
1904                                 GPU_matrix_rotate_axis(RAD2DEGF(angle - delta_angle), 'Z');
1905
1906                                 drawArrowHead(DOWN, 5);
1907
1908                                 GPU_matrix_pop();
1909
1910                                 GPU_matrix_translate_3f(cosf(angle + delta_angle) * dist, sinf(angle + delta_angle) * dist, 0);
1911                                 GPU_matrix_rotate_axis(RAD2DEGF(angle + delta_angle), 'Z');
1912
1913                                 drawArrowHead(UP, 5);
1914                                 break;
1915                         }
1916                         case HLP_TRACKBALL:
1917                         {
1918                                 unsigned char col[3], col2[3];
1919                                 UI_GetThemeColor3ubv(TH_GRID, col);
1920
1921                                 GPU_matrix_translate_3fv(mval);
1922
1923                                 GPU_line_width(3.0f);
1924
1925                                 UI_make_axis_color(col, col2, 'X');
1926                                 immUniformColor3ubv(col2);
1927
1928                                 drawArrow(RIGHT, 5, 10, 5);
1929                                 drawArrow(LEFT, 5, 10, 5);
1930
1931                                 UI_make_axis_color(col, col2, 'Y');
1932                                 immUniformColor3ubv(col2);
1933
1934                                 drawArrow(UP, 5, 10, 5);
1935                                 drawArrow(DOWN, 5, 10, 5);
1936                                 break;
1937                         }
1938                 }
1939
1940                 immUnbindProgram();
1941                 GPU_matrix_pop();
1942         }
1943 }
1944
1945 static bool transinfo_show_overlay(const struct bContext *C, TransInfo *t, ARegion *ar)
1946 {
1947         /* Don't show overlays when not the active view and when overlay is disabled: T57139 */
1948         bool ok = false;
1949         if (ar == t->ar) {
1950                 ok = true;
1951         }
1952         else {
1953                 ScrArea *sa = CTX_wm_area(C);
1954                 if (sa->spacetype == SPACE_VIEW3D) {
1955                         View3D *v3d = sa->spacedata.first;
1956                         if ((v3d->flag2 & V3D_RENDER_OVERRIDE) == 0) {
1957                                 ok = true;
1958                         }
1959                 }
1960         }
1961         return ok;
1962 }
1963
1964 static void drawTransformView(const struct bContext *C, ARegion *ar, void *arg)
1965 {
1966         TransInfo *t = arg;
1967
1968         if (!transinfo_show_overlay(C, t, ar)) {
1969                 return;
1970         }
1971
1972         GPU_line_width(1.0f);
1973
1974         drawConstraint(t);
1975         drawPropCircle(C, t);
1976         drawSnapping(C, t);
1977
1978         if (ar == t->ar) {
1979                 /* edge slide, vert slide */
1980                 drawEdgeSlide(t);
1981                 drawVertSlide(t);
1982
1983                 /* Rotation */
1984                 drawDial3d(t);
1985         }
1986 }
1987
1988 /* just draw a little warning message in the top-right corner of the viewport to warn that autokeying is enabled */
1989 static void drawAutoKeyWarning(TransInfo *UNUSED(t), ARegion *ar)
1990 {
1991         rcti rect;
1992         const char *printable = IFACE_("Auto Keying On");
1993         float      printable_size[2];
1994         int xco, yco;
1995
1996         ED_region_visible_rect(ar, &rect);
1997
1998         const int font_id = BLF_default();
1999         BLF_width_and_height(font_id, printable, BLF_DRAW_STR_DUMMY_MAX, &printable_size[0], &printable_size[1]);
2000
2001         xco = (rect.xmax - U.widget_unit) - (int)printable_size[0];
2002         yco = (rect.ymax - U.widget_unit);
2003
2004         /* warning text (to clarify meaning of overlays)
2005          * - original color was red to match the icon, but that clashes badly with a less nasty border
2006          */
2007         unsigned char color[3];
2008         UI_GetThemeColorShade3ubv(TH_TEXT_HI, -50, color);
2009         BLF_color3ubv(font_id, color);
2010 #ifdef WITH_INTERNATIONAL
2011         BLF_draw_default(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX);
2012 #else
2013         BLF_draw_default_ascii(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX);
2014 #endif
2015
2016         /* autokey recording icon... */
2017         GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
2018         GPU_blend(true);
2019
2020         xco -= U.widget_unit;
2021         yco -= (int)printable_size[1] / 2;
2022
2023         UI_icon_draw(xco, yco, ICON_REC);
2024
2025         GPU_blend(false);
2026 }
2027
2028 static void drawTransformPixel(const struct bContext *C, ARegion *ar, void *arg)
2029 {
2030         TransInfo *t = arg;
2031
2032         if (!transinfo_show_overlay(C, t, ar)) {
2033                 return;
2034         }
2035
2036         if (ar == t->ar) {
2037                 Scene *scene = t->scene;
2038                 ViewLayer *view_layer = t->view_layer;
2039                 Object *ob = OBACT(view_layer);
2040
2041                 /* draw autokeyframing hint in the corner
2042                  * - only draw if enabled (advanced users may be distracted/annoyed),
2043                  *   for objects that will be autokeyframed (no point otherwise),
2044                  *   AND only for the active region (as showing all is too overwhelming)
2045                  */
2046                 if ((U.autokey_flag & AUTOKEY_FLAG_NOWARNING) == 0) {
2047                         if (ar == t->ar) {
2048                                 if (t->flag & (T_OBJECT | T_POSE)) {
2049                                         if (ob && autokeyframe_cfra_can_key(scene, &ob->id)) {
2050                                                 drawAutoKeyWarning(t, ar);
2051                                         }
2052                                 }
2053                         }
2054                 }
2055         }
2056 }
2057
2058 /**
2059  * \see #initTransform which reads values from the operator.
2060  */
2061 void saveTransform(bContext *C, TransInfo *t, wmOperator *op)
2062 {
2063         ToolSettings *ts = CTX_data_tool_settings(C);
2064         bool constraint_axis[3] = {false, false, false};
2065         int proportional = 0;
2066         PropertyRNA *prop;
2067
2068         // Save back mode in case we're in the generic operator
2069         if ((prop = RNA_struct_find_property(op->ptr, "mode"))) {
2070                 RNA_property_enum_set(op->ptr, prop, t->mode);
2071         }
2072
2073         if ((prop = RNA_struct_find_property(op->ptr, "value"))) {
2074                 float values[4];
2075
2076                 copy_v4_v4(values, (t->flag & T_AUTOVALUES) ? t->auto_values : t->values);
2077
2078                 if (RNA_property_array_check(prop)) {
2079                         RNA_property_float_set_array(op->ptr, prop, values);
2080                 }
2081                 else {
2082                         RNA_property_float_set(op->ptr, prop, values[0]);
2083                 }
2084         }
2085
2086         /* convert flag to enum */
2087         switch (t->flag & T_PROP_EDIT_ALL) {
2088                 case T_PROP_EDIT:
2089                         proportional = PROP_EDIT_ON;
2090                         break;
2091                 case (T_PROP_EDIT | T_PROP_CONNECTED):
2092                         proportional = PROP_EDIT_CONNECTED;
2093                         break;
2094                 case (T_PROP_EDIT | T_PROP_PROJECTED):
2095                         proportional = PROP_EDIT_PROJECTED;
2096                         break;
2097                 default:
2098                         proportional = PROP_EDIT_OFF;
2099                         break;
2100         }
2101
2102         // If modal, save settings back in scene if not set as operator argument
2103         if ((t->flag & T_MODAL) || (op->flag & OP_IS_REPEAT)) {
2104                 /* save settings if not set in operator */
2105
2106                 /* skip saving proportional edit if it was not actually used */
2107                 if (!(t->options & CTX_NO_PET)) {
2108                         if ((prop = RNA_struct_find_property(op->ptr, "proportional")) &&
2109                             !RNA_property_is_set(op->ptr, prop))
2110                         {
2111                                 if (t->spacetype == SPACE_IPO)
2112                                         ts->proportional_fcurve = proportional;
2113                                 else if (t->spacetype == SPACE_ACTION)
2114                                         ts->proportional_action = proportional;
2115                                 else if (t->obedit_type != -1)
2116                                         ts->proportional = proportional;
2117                                 else if (t->options & CTX_MASK)
2118                                         ts->proportional_mask = (proportional != PROP_EDIT_OFF);
2119                                 else
2120                                         ts->proportional_objects = (proportional != PROP_EDIT_OFF);
2121                         }
2122
2123                         if ((prop = RNA_struct_find_property(op->ptr, "proportional_size"))) {
2124                                 ts->proportional_size =
2125                                         RNA_property_is_set(op->ptr, prop) ? RNA_property_float_get(op->ptr, prop) : t->prop_size;
2126                         }
2127
2128                         if ((prop = RNA_struct_find_property(op->ptr, "proportional_edit_falloff")) &&
2129                             !RNA_property_is_set(op->ptr, prop))
2130                         {
2131                                 ts->prop_mode = t->prop_mode;
2132                         }
2133                 }
2134
2135                 /* do we check for parameter? */
2136                 if (transformModeUseSnap(t)) {
2137                         if (t->modifiers & MOD_SNAP) {
2138                                 ts->snap_flag |= SCE_SNAP;
2139                         }
2140                         else {
2141                                 ts->snap_flag &= ~SCE_SNAP;
2142                         }
2143                 }
2144
2145                 if (t->spacetype == SPACE_VIEW3D) {
2146                         if ((prop = RNA_struct_find_property(op->ptr, "constraint_orientation")) &&
2147                             !RNA_property_is_set(op->ptr, prop) &&
2148                             (t->orientation.user != V3D_MANIP_CUSTOM_MATRIX))
2149                         {
2150                                 TransformOrientationSlot *orient_slot = &t->scene->orientation_slots[SCE_ORIENT_DEFAULT];
2151                                 orient_slot->type = t->orientation.user;
2152                                 BLI_assert(((orient_slot->index_custom == -1) && (t->orientation.custom == NULL)) ||
2153                                            (BKE_scene_transform_orientation_get_index(
2154                                                     t->scene, t->orientation.custom) == orient_slot->index_custom));
2155                         }
2156                 }
2157         }
2158
2159         if ((prop = RNA_struct_find_property(op->ptr, "proportional"))) {
2160                 RNA_property_enum_set(op->ptr, prop, proportional);
2161                 RNA_enum_set(op->ptr, "proportional_edit_falloff", t->prop_mode);
2162                 RNA_float_set(op->ptr, "proportional_size", t->prop_size);
2163         }
2164
2165         if ((prop = RNA_struct_find_property(op->ptr, "axis"))) {
2166                 RNA_property_float_set_array(op->ptr, prop, t->axis);
2167         }
2168
2169         if ((prop = RNA_struct_find_property(op->ptr, "axis_ortho"))) {
2170                 RNA_property_float_set_array(op->ptr, prop, t->axis_ortho);
2171         }
2172
2173         if ((prop = RNA_struct_find_property(op->ptr, "mirror"))) {
2174                 RNA_property_boolean_set(op->ptr, prop, (t->flag & T_NO_MIRROR) == 0);
2175         }
2176
2177         if ((prop = RNA_struct_find_property(op->ptr, "constraint_axis"))) {
2178                 /* constraint orientation can be global, even if user selects something else
2179                  * so use the orientation in the constraint if set */
2180                 short orientation = (t->con.mode & CON_APPLY) ? t->con.orientation : t->orientation.user;
2181
2182                 if (orientation == V3D_MANIP_CUSTOM) {
2183                         const int orientation_index_custom = BKE_scene_transform_orientation_get_index(
2184                                 t->scene, t->orientation.custom);
2185
2186                         /* Maybe we need a t->con.custom_orientation? Seems like it would always match t->orientation.custom. */
2187                         orientation = V3D_MANIP_CUSTOM + orientation_index_custom;
2188                         BLI_assert(orientation >= V3D_MANIP_CUSTOM);
2189                 }
2190
2191                 RNA_float_set_array(op->ptr, "constraint_matrix", &t->spacemtx[0][0]);
2192
2193                 /* Use 'constraint_matrix' instead. */
2194                 if (orientation != V3D_MANIP_CUSTOM_MATRIX) {
2195                         RNA_enum_set(op->ptr, "constraint_orientation", orientation);
2196                 }
2197
2198                 if (t->con.mode & CON_APPLY) {
2199                         if (t->con.mode & CON_AXIS0) {
2200                                 constraint_axis[0] = true;
2201                         }
2202                         if (t->con.mode & CON_AXIS1) {
2203                                 constraint_axis[1] = true;
2204                         }
2205                         if (t->con.mode & CON_AXIS2) {
2206                                 constraint_axis[2] = true;
2207                         }
2208                 }
2209
2210                 /* Only set if needed, so we can hide in the UI when nothing is set.
2211                  * See 'transform_poll_property'. */
2212                 if (ELEM(true, UNPACK3(constraint_axis))) {
2213                         RNA_property_boolean_set_array(op->ptr, prop, constraint_axis);
2214                 }
2215         }
2216
2217         {
2218                 const char *prop_id = NULL;
2219                 bool prop_state = true;
2220                 if (t->mode == TFM_SHRINKFATTEN) {
2221                         prop_id = "use_even_offset";
2222                         prop_state = false;
2223                 }
2224
2225                 if (prop_id && (prop = RNA_struct_find_property(op->ptr, prop_id))) {
2226                         RNA_property_boolean_set(op->ptr, prop, ((t->flag & T_ALT_TRANSFORM) == 0) == prop_state);
2227                 }
2228         }
2229
2230         if ((prop = RNA_struct_find_property(op->ptr, "correct_uv"))) {
2231                 RNA_property_boolean_set(op->ptr, prop, (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT) != 0);
2232         }
2233
2234         if (t->mode == TFM_SHEAR) {
2235                 prop = RNA_struct_find_property(op->ptr, "shear_axis");
2236                 t->custom.mode.data = POINTER_FROM_INT(RNA_property_enum_get(op->ptr, prop));
2237                 RNA_property_enum_set(op->ptr, prop, POINTER_AS_INT(t->custom.mode.data));
2238         }
2239 }
2240
2241 /**
2242  * \note  caller needs to free 't' on a 0 return
2243  * \warning  \a event might be NULL (when tweaking from redo panel)
2244  * \see #saveTransform which writes these values back.
2245  */
2246 bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *event, int mode)
2247 {
2248         int options = 0;
2249         PropertyRNA *prop;
2250
2251         t->context = C;
2252
2253         /* added initialize, for external calls to set stuff in TransInfo, like undo string */
2254
2255         t->state = TRANS_STARTING;
2256
2257         if ((prop = RNA_struct_find_property(op->ptr, "cursor_transform")) && RNA_property_is_set(op->ptr, prop)) {
2258                 if (RNA_property_boolean_get(op->ptr, prop)) {
2259                         options |= CTX_CURSOR;
2260                 }
2261         }
2262
2263         if ((prop = RNA_struct_find_property(op->ptr, "texture_space")) && RNA_property_is_set(op->ptr, prop)) {
2264                 if (RNA_property_boolean_get(op->ptr, prop)) {
2265                         options |= CTX_TEXTURE;
2266                 }
2267         }
2268
2269         if ((prop = RNA_struct_find_property(op->ptr, "gpencil_strokes")) && RNA_property_is_set(op->ptr, prop)) {
2270                 if (RNA_property_boolean_get(op->ptr, prop)) {
2271                         options |= CTX_GPENCIL_STROKES;
2272                 }
2273         }
2274
2275         t->options = options;
2276
2277         t->mode = mode;
2278
2279         /* Needed to translate tweak events to mouse buttons. */
2280         t->launch_event = event ? WM_userdef_event_type_from_keymap_type(event->type) : -1;
2281
2282         // XXX Remove this when wm_operator_call_internal doesn't use window->eventstate (which can have type = 0)
2283         // For gizmo only, so assume LEFTMOUSE
2284         if (t->launch_event == 0) {
2285                 t->launch_event = LEFTMOUSE;
2286         }
2287
2288         unit_m3(t->spacemtx);
2289
2290         initTransInfo(C, t, op, event);
2291         initTransformOrientation(C, t);
2292
2293         if (t->spacetype == SPACE_VIEW3D) {
2294                 t->draw_handle_apply = ED_region_draw_cb_activate(t->ar->type, drawTransformApply, t, REGION_DRAW_PRE_VIEW);
2295                 t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
2296                 t->draw_handle_pixel = ED_region_draw_cb_activate(t->ar->type, drawTransformPixel, t, REGION_DRAW_POST_PIXEL);
2297                 t->draw_handle_cursor = WM_paint_cursor_activate(
2298                         CTX_wm_manager(C),
2299                         SPACE_TYPE_ANY, RGN_TYPE_ANY,
2300                         helpline_poll, drawHelpline, t);
2301         }
2302         else if (t->spacetype == SPACE_IMAGE) {
2303                 t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
2304                 //t->draw_handle_pixel = ED_region_draw_cb_activate(t->ar->type, drawTransformPixel, t, REGION_DRAW_POST_PIXEL);
2305                 t->draw_handle_cursor = WM_paint_cursor_activate(
2306                         CTX_wm_manager(C),
2307                         SPACE_TYPE_ANY, RGN_TYPE_ANY,
2308                         helpline_poll, drawHelpline, t);
2309         }
2310         else if (t->spacetype == SPACE_CLIP) {
2311                 t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
2312                 t->draw_handle_cursor = WM_paint_cursor_activate(
2313                         CTX_wm_manager(C),
2314                         SPACE_TYPE_ANY, RGN_TYPE_ANY,
2315                         helpline_poll, drawHelpline, t);
2316         }
2317         else if (t->spacetype == SPACE_NODE) {
2318                 /*t->draw_handle_apply = ED_region_draw_cb_activate(t->ar->type, drawTransformApply, t, REGION_DRAW_PRE_VIEW);*/
2319                 t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
2320                 t->draw_handle_cursor = WM_paint_cursor_activate(
2321                         CTX_wm_manager(C),
2322                         SPACE_TYPE_ANY, RGN_TYPE_ANY,
2323                         helpline_poll, drawHelpline, t);
2324         }
2325         else if (t->spacetype == SPACE_IPO) {
2326                 t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
2327                 //t->draw_handle_pixel = ED_region_draw_cb_activate(t->ar->type, drawTransformPixel, t, REGION_DRAW_POST_PIXEL);
2328                 t->draw_handle_cursor = WM_paint_cursor_activate(
2329                         CTX_wm_manager(C),
2330                         SPACE_TYPE_ANY, RGN_TYPE_ANY,
2331                         helpline_poll, drawHelpline, t);
2332         }
2333         else if (t->spacetype == SPACE_ACTION) {
2334                 t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
2335                 //t->draw_handle_pixel = ED_region_draw_cb_activate(t->ar->type, drawTransformPixel, t, REGION_DRAW_POST_PIXEL);
2336                 t->draw_handle_cursor = WM_paint_cursor_activate(
2337                         CTX_wm_manager(C),
2338                         SPACE_TYPE_ANY, RGN_TYPE_ANY,
2339                         helpline_poll, drawHelpline, t);
2340         }
2341
2342         createTransData(C, t);          // make TransData structs from selection
2343
2344         if (t->data_len_all == 0) {
2345                 postTrans(C, t);
2346                 return 0;
2347         }
2348
2349         if (event) {
2350                 /* keymap for shortcut header prints */
2351                 t->keymap = WM_keymap_active(CTX_wm_manager(C), op->type->modalkeymap);
2352
2353                 /* Stupid code to have Ctrl-Click on gizmo work ok
2354                  *
2355                  * do this only for translation/rotation/resize due to only this
2356                  * moded are available from gizmo and doing such check could
2357                  * lead to keymap conflicts for other modes (see #31584)
2358                  */
2359                 if (ELEM(mode, TFM_TRANSLATION, TFM_ROTATION, TFM_RESIZE)) {
2360                         wmKeyMapItem *kmi;
2361
2362                         for (kmi = t->keymap->items.first; kmi; kmi = kmi->next) {
2363                                 if (kmi->propvalue == TFM_MODAL_SNAP_INV_ON && kmi->val == KM_PRESS) {
2364                                         if ((ELEM(kmi->type, LEFTCTRLKEY, RIGHTCTRLKEY) &&   event->ctrl)  ||
2365                                             (ELEM(kmi->type, LEFTSHIFTKEY, RIGHTSHIFTKEY) && event->shift) ||
2366                                             (ELEM(kmi->type, LEFTALTKEY, RIGHTALTKEY) &&     event->alt)   ||
2367                                             ((kmi->type == OSKEY) &&                         event->oskey) )
2368                                         {
2369                                                 t->modifiers |= MOD_SNAP_INVERT;
2370                                         }
2371                                         break;
2372                                 }
2373                         }
2374                 }
2375         }
2376
2377         initSnapping(t, op); // Initialize snapping data AFTER mode flags
2378
2379         initSnapSpatial(t, t->snap_spatial);
2380
2381         /* EVIL! posemode code can switch translation to rotate when 1 bone is selected. will be removed (ton) */
2382         /* EVIL2: we gave as argument also texture space context bit... was cleared */
2383         /* EVIL3: extend mode for animation editors also switches modes... but is best way to avoid duplicate code */
2384         mode = t->mode;
2385
2386         calculatePropRatio(t);
2387         calculateCenter(t);
2388
2389         /* Overwrite initial values if operator supplied a non-null vector.
2390          *
2391          * Run before init functions so 'values_modal_offset' can be applied on mouse input.
2392          */
2393         BLI_assert(is_zero_v4(t->values_modal_offset));
2394         if ((prop = RNA_struct_find_property(op->ptr, "value")) && RNA_property_is_set(op->ptr, prop)) {
2395                 float values[4] = {0}; /* in case value isn't length 4, avoid uninitialized memory  */
2396
2397                 if (RNA_property_array_check(prop)) {
2398                         RNA_float_get_array(op->ptr, "value", values);
2399                 }
2400                 else {
2401                         values[0] = RNA_float_get(op->ptr, "value");
2402                 }
2403
2404                 copy_v4_v4(t->values, values);
2405
2406                 if (t->flag & T_MODAL) {
2407                         copy_v4_v4(t->values_modal_offset, values);
2408                         t->redraw = TREDRAW_HARD;
2409                 }
2410                 else {
2411                         copy_v4_v4(t->auto_values, values);
2412                         t->flag |= T_AUTOVALUES;
2413                 }
2414         }
2415
2416         /* Transformation axis from operator */
2417         if ((prop = RNA_struct_find_property(op->ptr, "axis")) && RNA_property_is_set(op->ptr, prop)) {
2418                 RNA_property_float_get_array(op->ptr, prop, t->axis);
2419                 normalize_v3(t->axis);
2420                 copy_v3_v3(t->axis_orig, t->axis);
2421         }
2422
2423         if ((prop = RNA_struct_find_property(op->ptr, "axis_ortho")) && RNA_property_is_set(op->ptr, prop)) {
2424                 RNA_property_float_get_array(op->ptr, prop, t->axis_ortho);
2425                 normalize_v3(t->axis_ortho);
2426         }
2427
2428         /* Constraint init from operator */
2429         if ((prop = RNA_struct_find_property(op->ptr, "constraint_axis")) && RNA_property_is_set(op->ptr, prop)) {
2430                 bool constraint_axis[3];
2431
2432                 RNA_property_boolean_get_array(op->ptr, prop, constraint_axis);
2433
2434                 if (constraint_axis[0] || constraint_axis[1] || constraint_axis[2]) {
2435                         t->con.mode |= CON_APPLY;
2436
2437                         if (constraint_axis[0]) {
2438                                 t->con.mode |= CON_AXIS0;
2439                         }
2440                         if (constraint_axis[1]) {
2441                                 t->con.mode |= CON_AXIS1;
2442                         }
2443                         if (constraint_axis[2]) {
2444                                 t->con.mode |= CON_AXIS2;
2445                         }
2446
2447                         setUserConstraint(t, t->orientation.user, t->con.mode, "%s");
2448                 }
2449         }
2450
2451         if (event) {
2452                 /* Initialize accurate transform to settings requested by keymap. */
2453                 bool use_accurate = false;
2454                 if ((prop = RNA_struct_find_property(op->ptr, "use_accurate")) && RNA_property_is_set(op->ptr, prop)) {
2455                         if (RNA_property_boolean_get(op->ptr, prop)) {
2456                                 use_accurate = true;
2457                         }
2458                 }
2459                 initMouseInput(t, &t->mouse, t->center2d, event->mval, use_accurate);
2460         }
2461
2462         switch (mode) {
2463                 case TFM_TRANSLATION:
2464                         initTranslation(t);
2465                         break;
2466                 case TFM_ROTATION:
2467                         initRotation(t);
2468                         break;
2469                 case TFM_RESIZE:
2470                         initResize(t);
2471                         break;
2472                 case TFM_SKIN_RESIZE:
2473                         initSkinResize(t);
2474                         break;
2475                 case TFM_TOSPHERE:
2476                         initToSphere(t);
2477                         break;
2478                 case TFM_SHEAR:
2479                         prop = RNA_struct_find_property(op->ptr, "shear_axis");
2480                         t->custom.mode.data = POINTER_FROM_INT(RNA_property_enum_get(op->ptr, prop));
2481                         initShear(t);
2482                         break;
2483                 case TFM_BEND:
2484                         initBend(t);
2485                         break;
2486                 case TFM_SHRINKFATTEN:
2487                         initShrinkFatten(t);
2488                         break;
2489                 case TFM_TILT:
2490                         initTilt(t);
2491                         break;
2492                 case TFM_CURVE_SHRINKFATTEN:
2493                         initCurveShrinkFatten(t);
2494                         break;
2495                 case TFM_MASK_SHRINKFATTEN:
2496                         initMaskShrinkFatten(t);
2497                         break;
2498                 case TFM_GPENCIL_SHRINKFATTEN:
2499                         initGPShrinkFatten(t);
2500                         break;
2501                 case TFM_TRACKBALL:
2502                         initTrackball(t);
2503                         break;
2504                 case TFM_PUSHPULL:
2505                         initPushPull(t);
2506                         break;
2507                 case TFM_CREASE:
2508                         initCrease(t);
2509                         break;
2510                 case TFM_BONESIZE:
2511                 {   /* used for both B-Bone width (bonesize) as for deform-dist (envelope) */
2512                         /* Note: we have to pick one, use the active object. */
2513                         TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_OK(t);
2514                         bArmature *arm = tc->poseobj->data;
2515                         if (arm->drawtype == ARM_ENVELOPE) {
2516                                 initBoneEnvelope(t);
2517                                 t->mode = TFM_BONE_ENVELOPE_DIST;
2518                         }
2519                         else {
2520                                 initBoneSize(t);
2521                         }
2522                         break;
2523                 }
2524                 case TFM_BONE_ENVELOPE:
2525                         initBoneEnvelope(t);
2526                         break;
2527                 case TFM_BONE_ENVELOPE_DIST:
2528                         initBoneEnvelope(t);
2529                         t->mode = TFM_BONE_ENVELOPE_DIST;
2530                         break;
2531                 case TFM_EDGE_SLIDE:
2532                 case TFM_VERT_SLIDE:
2533                 {
2534                         const bool use_even = (op ? RNA_boolean_get(op->ptr, "use_even") : false);
2535                         const bool flipped = (op ? RNA_boolean_get(op->ptr, "flipped") : false);
2536                         const bool use_clamp = (op ? RNA_boolean_get(op->ptr, "use_clamp") : true);
2537                         if (mode == TFM_EDGE_SLIDE) {
2538                                 const bool use_double_side = (op ? !RNA_boolean_get(op->ptr, "single_side") : true);
2539                                 initEdgeSlide_ex(t, use_double_side, use_even, flipped, use_clamp);
2540                         }
2541                         else {
2542                                 initVertSlide_ex(t, use_even, flipped, use_clamp);
2543                         }
2544                         break;
2545                 }
2546                 case TFM_BONE_ROLL:
2547                         initBoneRoll(t);
2548                         break;
2549                 case TFM_TIME_TRANSLATE:
2550                         initTimeTranslate(t);
2551                         break;
2552                 case TFM_TIME_SLIDE:
2553                         initTimeSlide(t);
2554                         break;
2555                 case TFM_TIME_SCALE:
2556                         initTimeScale(t);
2557                         break;
2558                 case TFM_TIME_DUPLICATE:
2559                         /* same as TFM_TIME_EXTEND, but we need the mode info for later
2560                          * so that duplicate-culling will work properly
2561                          */
2562                         if (ELEM(t->spacetype, SPACE_IPO, SPACE_NLA))
2563                                 initTranslation(t);
2564                         else
2565                                 initTimeTranslate(t);
2566                         t->mode = mode;
2567                         break;
2568                 case TFM_TIME_EXTEND:
2569                         /* now that transdata has been made, do like for TFM_TIME_TRANSLATE (for most Animation
2570                          * Editors because they have only 1D transforms for time values) or TFM_TRANSLATION
2571                          * (for Graph/NLA Editors only since they uses 'standard' transforms to get 2D movement)
2572                          * depending on which editor this was called from
2573                          */
2574                         if (ELEM(t->spacetype, SPACE_IPO, SPACE_NLA))
2575                                 initTranslation(t);
2576                         else
2577                                 initTimeTranslate(t);
2578                         break;
2579                 case TFM_BAKE_TIME:
2580                         initBakeTime(t);
2581                         break;
2582                 case TFM_MIRROR:
2583                         initMirror(t);
2584                         break;
2585                 case TFM_BWEIGHT:
2586                         initBevelWeight(t);
2587                         break;
2588                 case TFM_ALIGN:
2589                         initAlign(t);
2590                         break;
2591                 case TFM_SEQ_SLIDE:
2592                         initSeqSlide(t);
2593                         break;
2594                 case TFM_NORMAL_ROTATION:
2595                         initNormalRotation(t);
2596                         break;
2597         }
2598
2599         if (t->state == TRANS_CANCEL) {
2600                 postTrans(C, t);
2601                 return 0;
2602         }
2603
2604         if ((prop = RNA_struct_find_property(op->ptr, "preserve_clnor"))) {
2605                 if ((t->flag & T_EDIT) && t->obedit_type == OB_MESH) {
2606
2607                         FOREACH_TRANS_DATA_CONTAINER(t, tc) {
2608                                 if ((((Mesh *)(tc->obedit->data))->flag & ME_AUTOSMOOTH)) {
2609                                         BMEditMesh *em = NULL;// BKE_editmesh_from_object(t->obedit);
2610                                         bool do_skip = false;
2611
2612                                         /* Currently only used for two of three most frequent transform ops, can include more ops.
2613                                          * Note that scaling cannot be included here, non-uniform scaling will affect normals. */
2614                                         if (ELEM(t->mode, TFM_TRANSLATION, TFM_ROTATION)) {
2615                                                 if (em->bm->totvertsel == em->bm->totvert) {
2616                                                         /* No need to invalidate if whole mesh is selected. */
2617                                                         do_skip = true;
2618                                                 }
2619                                         }
2620
2621                                         if (t->flag & T_MODAL) {
2622                                                 RNA_property_boolean_set(op->ptr, prop, false);
2623                                         }
2624                                         else if (!do_skip) {
2625                                                 const bool preserve_clnor = RNA_property_boolean_get(op->ptr, prop);
2626                                                 if (preserve_clnor) {
2627                                                         BKE_editmesh_lnorspace_update(em);
2628                                                         t->flag |= T_CLNOR_REBUILD;
2629                                                 }
2630                                                 BM_lnorspace_invalidate(em->bm, true);
2631                                         }
2632                                 }
2633                         }
2634                 }
2635         }
2636
2637         t->context = NULL;
2638
2639         return 1;
2640 }
2641
2642 void transformApply(bContext *C, TransInfo *t)
2643 {
2644         t->context = C;
2645
2646         if ((t->redraw & TREDRAW_HARD) || (t->draw_handle_apply == NULL && (t->redraw & TREDRAW_SOFT))) {
2647                 selectConstraint(t);
2648                 if (t->transform) {
2649                         t->transform(t, t->mval);  // calls recalcData()
2650                         viewRedrawForce(C, t);
2651                 }
2652                 t->redraw = TREDRAW_NOTHING;
2653         }
2654         else if (t->redraw & TREDRAW_SOFT) {
2655                 viewRedrawForce(C, t);
2656         }
2657
2658         /* If auto confirm is on, break after one pass */
2659         if (t->options & CTX_AUTOCONFIRM) {
2660                 t->state = TRANS_CONFIRM;
2661         }
2662
2663         t->context = NULL;
2664 }
2665
2666 static void drawTransformApply(const bContext *C, ARegion *UNUSED(ar), void *arg)
2667 {
2668         TransInfo *t = arg;
2669
2670         if (t->redraw & TREDRAW_SOFT) {
2671                 t->redraw |= TREDRAW_HARD;
2672                 transformApply((bContext *)C, t);
2673         }
2674 }
2675
2676 int transformEnd(bContext *C, TransInfo *t)
2677 {
2678         int exit_code = OPERATOR_RUNNING_MODAL;
2679
2680         t->context = C;
2681
2682         if (t->state != TRANS_STARTING && t->state != TRANS_RUNNING) {
2683                 /* handle restoring objects */
2684                 if (t->state == TRANS_CANCEL) {
2685                         /* exception, edge slide transformed UVs too */
2686                         if (t->mode == TFM_EDGE_SLIDE) {
2687                                 doEdgeSlide(t, 0.0f);
2688                         }
2689                         else if (t->mode == TFM_VERT_SLIDE) {
2690                                 doVertSlide(t, 0.0f);
2691                         }
2692
2693                         exit_code = OPERATOR_CANCELLED;
2694                         restoreTransObjects(t); // calls recalcData()
2695                 }
2696                 else {
2697                         if (t->flag & T_CLNOR_REBUILD) {
2698                                 FOREACH_TRANS_DATA_CONTAINER(t, tc) {
2699                                         BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
2700                                         BM_lnorspace_rebuild(em->bm, true);
2701                                 }
2702                         }
2703                         exit_code = OPERATOR_FINISHED;
2704                 }
2705
2706                 /* aftertrans does insert keyframes, and clears base flags; doesn't read transdata */
2707                 special_aftertrans_update(C, t);
2708
2709                 /* free data */
2710                 postTrans(C, t);
2711
2712                 /* send events out for redraws */
2713                 viewRedrawPost(C, t);
2714
2715                 viewRedrawForce(C, t);
2716         }
2717
2718         t->context = NULL;
2719
2720         return exit_code;
2721 }
2722
2723 /* ************************** TRANSFORM LOCKS **************************** */
2724
2725 static void protectedTransBits(short protectflag, float vec[3])
2726 {
2727         if (protectflag & OB_LOCK_LOCX)
2728                 vec[0] = 0.0f;
2729         if (protectflag & OB_LOCK_LOCY)
2730                 vec[1] = 0.0f;
2731         if (protectflag & OB_LOCK_LOCZ)
2732                 vec[2] = 0.0f;
2733 }
2734
2735 static void protectedSizeBits(short protectflag, float size[3])
2736 {
2737         if (protectflag & OB_LOCK_SCALEX)
2738                 size[0] = 1.0f;
2739         if (protectflag & OB_LOCK_SCALEY)
2740                 size[1] = 1.0f;
2741         if (protectflag & OB_LOCK_SCALEZ)
2742                 size[2] = 1.0f;
2743 }
2744
2745 static void protectedRotateBits(short protectflag, float eul[3], const float oldeul[3])
2746 {
2747         if (protectflag & OB_LOCK_ROTX)
2748                 eul[0] = oldeul[0];
2749         if (protectflag & OB_LOCK_ROTY)
2750                 eul[1] = oldeul[1];
2751         if (protectflag & OB_LOCK_ROTZ)
2752                 eul[2] = oldeul[2];
2753 }
2754
2755
2756 /* this function only does the delta rotation */
2757 /* axis-angle is usually internally stored as quats... */
2758 static void protectedAxisAngleBits(short protectflag, float axis[3], float *angle, float oldAxis[3], float oldAngle)
2759 {
2760         /* check that protection flags are set */
2761         if ((protectflag & (OB_LOCK_ROTX | OB_LOCK_ROTY | OB_LOCK_ROTZ | OB_LOCK_ROTW)) == 0)
2762                 return;
2763
2764         if (protectflag & OB_LOCK_ROT4D) {
2765                 /* axis-angle getting limited as 4D entities that they are... */
2766                 if (protectflag & OB_LOCK_ROTW)
2767                         *angle = oldAngle;
2768                 if (protectflag & OB_LOCK_ROTX)
2769                         axis[0] = oldAxis[0];
2770                 if (protectflag & OB_LOCK_ROTY)
2771                         axis[1] = oldAxis[1];
2772                 if (protectflag & OB_LOCK_ROTZ)
2773                         axis[2] = oldAxis[2];
2774         }
2775         else {
2776                 /* axis-angle get limited with euler... */
2777                 float eul[3], oldeul[3];
2778
2779                 axis_angle_to_eulO(eul, EULER_ORDER_DEFAULT, axis, *angle);
2780                 axis_angle_to_eulO(oldeul, EULER_ORDER_DEFAULT, oldAxis, oldAngle);
2781
2782                 if (protectflag & OB_LOCK_ROTX)
2783                         eul[0] = oldeul[0];
2784                 if (protectflag & OB_LOCK_ROTY)
2785                         eul[1] = oldeul[1];
2786                 if (protectflag & OB_LOCK_ROTZ)
2787                         eul[2] = oldeul[2];
2788
2789                 eulO_to_axis_angle(axis, angle, eul, EULER_ORDER_DEFAULT);
2790
2791                 /* when converting to axis-angle, we need a special exception for the case when there is no axis */
2792                 if (IS_EQF(axis[0], axis[1]) && IS_EQF(axis[1], axis[2])) {
2793                         /* for now, rotate around y-axis then (so that it simply becomes the roll) */
2794                         axis[1] = 1.0f;
2795                 }
2796         }
2797 }
2798
2799 /* this function only does the delta rotation */
2800 static void protectedQuaternionBits(short protectflag, float quat[4], const float oldquat[4])
2801 {
2802         /* check that protection flags are set */
2803         if ((protectflag & (OB_LOCK_ROTX | OB_LOCK_ROTY | OB_LOCK_ROTZ | OB_LOCK_ROTW)) == 0)
2804                 return;
2805
2806         if (protectflag & OB_LOCK_ROT4D) {
2807                 /* quaternions getting limited as 4D entities that they are... */
2808                 if (protectflag & OB_LOCK_ROTW)
2809                         quat[0] = oldquat[0];
2810                 if (protectflag & OB_LOCK_ROTX)
2811                         quat[1] = oldquat[1];
2812                 if (protectflag & OB_LOCK_ROTY)
2813                         quat[2] = oldquat[2];
2814                 if (protectflag & OB_LOCK_ROTZ)
2815                         quat[3] = oldquat[3];
2816         }
2817         else {
2818                 /* quaternions get limited with euler... (compatibility mode) */
2819                 float eul[3], oldeul[3], nquat[4], noldquat[4];
2820                 float qlen;
2821
2822                 qlen = normalize_qt_qt(nquat, quat);
2823                 normalize_qt_qt(noldquat, oldquat);
2824
2825                 quat_to_eul(eul, nquat);
2826                 quat_to_eul(oldeul, noldquat);
2827
2828                 if (protectflag & OB_LOCK_ROTX)
2829                         eul[0] = oldeul[0];
2830                 if (protectflag & OB_LOCK_ROTY)
2831                         eul[1] = oldeul[1];
2832                 if (protectflag & OB_LOCK_ROTZ)
2833                         eul[2] = oldeul[2];
2834
2835                 eul_to_quat(quat, eul);
2836
2837                 /* restore original quat size */
2838                 mul_qt_fl(quat, qlen);
2839
2840                 /* quaternions flip w sign to accumulate rotations correctly */
2841                 if ((nquat[0] < 0.0f && quat[0] > 0.0f) ||
2842                     (nquat[0] > 0.0f && quat[0] < 0.0f))
2843                 {
2844                         mul_qt_fl(quat, -1.0f);
2845                 }
2846         }
2847 }
2848
2849 /* ******************* TRANSFORM LIMITS ********************** */
2850
2851 static void constraintTransLim(TransInfo *t, TransData *td)
2852 {
2853         if (td->con) {
2854                 const bConstraintTypeInfo *ctiLoc = BKE_constraint_typeinfo_from_type(CONSTRAINT_TYPE_LOCLIMIT);
2855                 const bConstraintTypeInfo *ctiDist = BKE_constraint_typeinfo_from_type(CONSTRAINT_TYPE_DISTLIMIT);
2856
2857                 bConstraintOb cob = {NULL};
2858                 bConstraint *con;
2859                 float ctime = (float)(t->scene->r.cfra);
2860
2861                 /* Make a temporary bConstraintOb for using these limit constraints
2862                  * - they only care that cob->matrix is correctly set ;-)
2863                  * - current space should be local
2864                  */
2865                 unit_m4(cob.matrix);
2866                 copy_v3_v3(cob.matrix[3], td->loc);
2867
2868                 /* Evaluate valid constraints */
2869                 for (con = td->con; con; con = con->next) {
2870                         const bConstraintTypeInfo *cti = NULL;
2871                         ListBase targets = {NULL, NULL};
2872
2873                         /* only consider constraint if enabled */