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