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