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