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