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