Industry Compat Keymap: support new outliner features
[blender.git] / source / blender / editors / space_view3d / view3d_buttons.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2009 Blender Foundation.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup spview3d
22  */
23
24 #include <string.h>
25 #include <stdio.h>
26 #include <math.h>
27 #include <float.h>
28
29 #include "DNA_armature_types.h"
30 #include "DNA_curve_types.h"
31 #include "DNA_lattice_types.h"
32 #include "DNA_meta_types.h"
33 #include "DNA_mesh_types.h"
34 #include "DNA_meshdata_types.h"
35 #include "DNA_object_types.h"
36 #include "DNA_scene_types.h"
37
38 #include "MEM_guardedalloc.h"
39
40 #include "BLT_translation.h"
41
42 #include "BLI_math.h"
43 #include "BLI_blenlib.h"
44 #include "BLI_utildefines.h"
45
46 #include "BKE_action.h"
47 #include "BKE_context.h"
48 #include "BKE_curve.h"
49 #include "BKE_customdata.h"
50 #include "BKE_screen.h"
51 #include "BKE_editmesh.h"
52 #include "BKE_deform.h"
53 #include "BKE_object.h"
54 #include "BKE_object_deform.h"
55 #include "BKE_report.h"
56
57 #include "DEG_depsgraph.h"
58
59 #include "WM_api.h"
60 #include "WM_types.h"
61
62 #include "RNA_access.h"
63
64 #include "ED_armature.h"
65 #include "ED_object.h"
66 #include "ED_mesh.h"
67 #include "ED_screen.h"
68
69 #include "UI_interface.h"
70 #include "UI_resources.h"
71
72 #include "view3d_intern.h" /* own include */
73
74 /* ******************* view3d space & buttons ************** */
75 enum {
76   B_REDR = 2,
77   B_TRANSFORM_PANEL_MEDIAN = 1008,
78   B_TRANSFORM_PANEL_DIMS = 1009,
79 };
80
81 /* All must start w/ location */
82
83 typedef struct {
84   float location[3];
85 } TransformMedian_Generic;
86
87 typedef struct {
88   float location[3], bv_weight, be_weight, skin[2], crease;
89 } TransformMedian_Mesh;
90
91 typedef struct {
92   float location[3], weight, b_weight, radius, tilt;
93 } TransformMedian_Curve;
94
95 typedef struct {
96   float location[3], weight;
97 } TransformMedian_Lattice;
98
99 typedef union {
100   TransformMedian_Generic generic;
101   TransformMedian_Mesh mesh;
102   TransformMedian_Curve curve;
103   TransformMedian_Lattice lattice;
104 } TransformMedian;
105
106 /* temporary struct for storing transform properties */
107
108 typedef struct {
109   float ob_dims_orig[3];
110   float ob_dims[3];
111   /* Floats only (treated as an array). */
112   TransformMedian ve_median, median;
113 } TransformProperties;
114
115 #define TRANSFORM_MEDIAN_ARRAY_LEN (sizeof(TransformMedian) / sizeof(float))
116
117 /* Helper function to compute a median changed value,
118  * when the value should be clamped in [0.0, 1.0].
119  * Returns either 0.0, 1.0 (both can be applied directly), a positive scale factor
120  * for scale down, or a negative one for scale up.
121  */
122 static float compute_scale_factor(const float ve_median, const float median)
123 {
124   if (ve_median <= 0.0f) {
125     return 0.0f;
126   }
127   else if (ve_median >= 1.0f) {
128     return 1.0f;
129   }
130   else {
131     /* Scale value to target median. */
132     float median_new = ve_median;
133     float median_orig = ve_median - median; /* Previous median value. */
134
135     /* In case of floating point error. */
136     CLAMP(median_orig, 0.0f, 1.0f);
137     CLAMP(median_new, 0.0f, 1.0f);
138
139     if (median_new <= median_orig) {
140       /* Scale down. */
141       return median_new / median_orig;
142     }
143     else {
144       /* Scale up, negative to indicate it... */
145       return -(1.0f - median_new) / (1.0f - median_orig);
146     }
147   }
148 }
149
150 /**
151  * Apply helpers.
152  * \note In case we only have one element,
153  * copy directly the value instead of applying the diff or scale factor.
154  * Avoids some glitches when going e.g. from 3 to 0.0001 (see T37327).
155  */
156 static void apply_raw_diff(float *val, const int tot, const float ve_median, const float median)
157 {
158   *val = (tot == 1) ? ve_median : (*val + median);
159 }
160
161 static void apply_raw_diff_v3(float val[3],
162                               const int tot,
163                               const float ve_median[3],
164                               const float median[3])
165 {
166   if (tot == 1) {
167     copy_v3_v3(val, ve_median);
168   }
169   else {
170     add_v3_v3(val, median);
171   }
172 }
173
174 static void apply_scale_factor(
175     float *val, const int tot, const float ve_median, const float median, const float sca)
176 {
177   if (tot == 1 || ve_median == median) {
178     *val = ve_median;
179   }
180   else {
181     *val *= sca;
182   }
183 }
184
185 static void apply_scale_factor_clamp(float *val,
186                                      const int tot,
187                                      const float ve_median,
188                                      const float sca)
189 {
190   if (tot == 1) {
191     *val = ve_median;
192     CLAMP(*val, 0.0f, 1.0f);
193   }
194   else if (ELEM(sca, 0.0f, 1.0f)) {
195     *val = sca;
196   }
197   else {
198     *val = (sca > 0.0f) ? (*val * sca) : (1.0f + ((1.0f - *val) * sca));
199     CLAMP(*val, 0.0f, 1.0f);
200   }
201 }
202
203 static TransformProperties *v3d_transform_props_ensure(View3D *v3d)
204 {
205   if (v3d->runtime.properties_storage == NULL) {
206     v3d->runtime.properties_storage = MEM_callocN(sizeof(TransformProperties),
207                                                   "TransformProperties");
208   }
209   return v3d->runtime.properties_storage;
210 }
211
212 /* is used for both read and write... */
213 static void v3d_editvertex_buts(uiLayout *layout, View3D *v3d, Object *ob, float lim)
214 {
215   uiBlock *block = (layout) ? uiLayoutAbsoluteBlock(layout) : NULL;
216   TransformProperties *tfp = v3d_transform_props_ensure(v3d);
217   TransformMedian median_basis, ve_median_basis;
218   int tot, totedgedata, totcurvedata, totlattdata, totcurvebweight;
219   bool has_meshdata = false;
220   bool has_skinradius = false;
221   PointerRNA data_ptr;
222
223   copy_vn_fl((float *)&median_basis, TRANSFORM_MEDIAN_ARRAY_LEN, 0.0f);
224   tot = totedgedata = totcurvedata = totlattdata = totcurvebweight = 0;
225
226   if (ob->type == OB_MESH) {
227     TransformMedian_Mesh *median = &median_basis.mesh;
228     Mesh *me = ob->data;
229     BMEditMesh *em = me->edit_mesh;
230     BMesh *bm = em->bm;
231     BMVert *eve;
232     BMEdge *eed;
233     BMIter iter;
234
235     const int cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT);
236     const int cd_vert_skin_offset = CustomData_get_offset(&bm->vdata, CD_MVERT_SKIN);
237     const int cd_edge_bweight_offset = CustomData_get_offset(&bm->edata, CD_BWEIGHT);
238     const int cd_edge_crease_offset = CustomData_get_offset(&bm->edata, CD_CREASE);
239
240     has_skinradius = (cd_vert_skin_offset != -1);
241
242     if (bm->totvertsel) {
243       BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
244         if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
245           tot++;
246           add_v3_v3(median->location, eve->co);
247
248           if (cd_vert_bweight_offset != -1) {
249             median->bv_weight += BM_ELEM_CD_GET_FLOAT(eve, cd_vert_bweight_offset);
250           }
251
252           if (has_skinradius) {
253             MVertSkin *vs = BM_ELEM_CD_GET_VOID_P(eve, cd_vert_skin_offset);
254             add_v2_v2(median->skin, vs->radius); /* Third val not used currently. */
255           }
256         }
257       }
258     }
259
260     if ((cd_edge_bweight_offset != -1) || (cd_edge_crease_offset != -1)) {
261       if (bm->totedgesel) {
262         BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
263           if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
264             if (cd_edge_bweight_offset != -1) {
265               median->be_weight += BM_ELEM_CD_GET_FLOAT(eed, cd_edge_bweight_offset);
266             }
267
268             if (cd_edge_crease_offset != -1) {
269               median->crease += BM_ELEM_CD_GET_FLOAT(eed, cd_edge_crease_offset);
270             }
271
272             totedgedata++;
273           }
274         }
275       }
276     }
277     else {
278       totedgedata = bm->totedgesel;
279     }
280
281     has_meshdata = (tot || totedgedata);
282   }
283   else if (ob->type == OB_CURVE || ob->type == OB_SURF) {
284     TransformMedian_Curve *median = &median_basis.curve;
285     Curve *cu = ob->data;
286     Nurb *nu;
287     BPoint *bp;
288     BezTriple *bezt;
289     int a;
290     ListBase *nurbs = BKE_curve_editNurbs_get(cu);
291     StructRNA *seltype = NULL;
292     void *selp = NULL;
293
294     nu = nurbs->first;
295     while (nu) {
296       if (nu->type == CU_BEZIER) {
297         bezt = nu->bezt;
298         a = nu->pntsu;
299         while (a--) {
300           if (bezt->f2 & SELECT) {
301             add_v3_v3(median->location, bezt->vec[1]);
302             tot++;
303             median->weight += bezt->weight;
304             median->radius += bezt->radius;
305             median->tilt += bezt->tilt;
306             if (!totcurvedata) { /* I.e. first time... */
307               selp = bezt;
308               seltype = &RNA_BezierSplinePoint;
309             }
310             totcurvedata++;
311           }
312           else {
313             if (bezt->f1 & SELECT) {
314               add_v3_v3(median->location, bezt->vec[0]);
315               tot++;
316             }
317             if (bezt->f3 & SELECT) {
318               add_v3_v3(median->location, bezt->vec[2]);
319               tot++;
320             }
321           }
322           bezt++;
323         }
324       }
325       else {
326         bp = nu->bp;
327         a = nu->pntsu * nu->pntsv;
328         while (a--) {
329           if (bp->f1 & SELECT) {
330             add_v3_v3(median->location, bp->vec);
331             median->b_weight += bp->vec[3];
332             totcurvebweight++;
333             tot++;
334             median->weight += bp->weight;
335             median->radius += bp->radius;
336             median->tilt += bp->tilt;
337             if (!totcurvedata) { /* I.e. first time... */
338               selp = bp;
339               seltype = &RNA_SplinePoint;
340             }
341             totcurvedata++;
342           }
343           bp++;
344         }
345       }
346       nu = nu->next;
347     }
348
349     if (totcurvedata == 1) {
350       RNA_pointer_create(&cu->id, seltype, selp, &data_ptr);
351     }
352   }
353   else if (ob->type == OB_LATTICE) {
354     Lattice *lt = ob->data;
355     TransformMedian_Lattice *median = &median_basis.lattice;
356     BPoint *bp;
357     int a;
358     StructRNA *seltype = NULL;
359     void *selp = NULL;
360
361     a = lt->editlatt->latt->pntsu * lt->editlatt->latt->pntsv * lt->editlatt->latt->pntsw;
362     bp = lt->editlatt->latt->def;
363     while (a--) {
364       if (bp->f1 & SELECT) {
365         add_v3_v3(median->location, bp->vec);
366         tot++;
367         median->weight += bp->weight;
368         if (!totlattdata) { /* I.e. first time... */
369           selp = bp;
370           seltype = &RNA_LatticePoint;
371         }
372         totlattdata++;
373       }
374       bp++;
375     }
376
377     if (totlattdata == 1) {
378       RNA_pointer_create(&lt->id, seltype, selp, &data_ptr);
379     }
380   }
381
382   if (tot == 0) {
383     uiDefBut(block,
384              UI_BTYPE_LABEL,
385              0,
386              IFACE_("Nothing selected"),
387              0,
388              130,
389              200,
390              20,
391              NULL,
392              0,
393              0,
394              0,
395              0,
396              "");
397     return;
398   }
399
400   /* Location, X/Y/Z */
401   mul_v3_fl(median_basis.generic.location, 1.0f / (float)tot);
402   if (v3d->flag & V3D_GLOBAL_STATS) {
403     mul_m4_v3(ob->obmat, median_basis.generic.location);
404   }
405
406   if (has_meshdata) {
407     TransformMedian_Mesh *median = &median_basis.mesh;
408     if (totedgedata) {
409       median->crease /= (float)totedgedata;
410       median->be_weight /= (float)totedgedata;
411     }
412     if (tot) {
413       median->bv_weight /= (float)tot;
414       if (has_skinradius) {
415         median->skin[0] /= (float)tot;
416         median->skin[1] /= (float)tot;
417       }
418     }
419   }
420   else if (totcurvedata) {
421     TransformMedian_Curve *median = &median_basis.curve;
422     if (totcurvebweight) {
423       median->b_weight /= (float)totcurvebweight;
424     }
425     median->weight /= (float)totcurvedata;
426     median->radius /= (float)totcurvedata;
427     median->tilt /= (float)totcurvedata;
428   }
429   else if (totlattdata) {
430     TransformMedian_Lattice *median = &median_basis.lattice;
431     median->weight /= (float)totlattdata;
432   }
433
434   if (block) { /* buttons */
435     uiBut *but;
436     int yi = 200;
437     const float tilt_limit = DEG2RADF(21600.0f);
438     const int butw = 200;
439     const int buth = 20 * UI_DPI_FAC;
440     const int but_margin = 2;
441     const char *c;
442
443     memcpy(&tfp->ve_median, &median_basis, sizeof(tfp->ve_median));
444
445     UI_block_align_begin(block);
446     if (tot == 1) {
447       if (totcurvedata) {
448         /* Curve */
449         c = IFACE_("Control Point:");
450       }
451       else {
452         /* Mesh or lattice */
453         c = IFACE_("Vertex:");
454       }
455     }
456     else {
457       c = IFACE_("Median:");
458     }
459     uiDefBut(block, UI_BTYPE_LABEL, 0, c, 0, yi -= buth, butw, buth, NULL, 0, 0, 0, 0, "");
460
461     UI_block_align_begin(block);
462
463     /* Should be no need to translate these. */
464     but = uiDefButF(block,
465                     UI_BTYPE_NUM,
466                     B_TRANSFORM_PANEL_MEDIAN,
467                     IFACE_("X:"),
468                     0,
469                     yi -= buth,
470                     butw,
471                     buth,
472                     &tfp->ve_median.generic.location[0],
473                     -lim,
474                     lim,
475                     10,
476                     RNA_TRANSLATION_PREC_DEFAULT,
477                     "");
478     UI_but_unit_type_set(but, PROP_UNIT_LENGTH);
479     but = uiDefButF(block,
480                     UI_BTYPE_NUM,
481                     B_TRANSFORM_PANEL_MEDIAN,
482                     IFACE_("Y:"),
483                     0,
484                     yi -= buth,
485                     butw,
486                     buth,
487                     &tfp->ve_median.generic.location[1],
488                     -lim,
489                     lim,
490                     10,
491                     RNA_TRANSLATION_PREC_DEFAULT,
492                     "");
493     UI_but_unit_type_set(but, PROP_UNIT_LENGTH);
494     but = uiDefButF(block,
495                     UI_BTYPE_NUM,
496                     B_TRANSFORM_PANEL_MEDIAN,
497                     IFACE_("Z:"),
498                     0,
499                     yi -= buth,
500                     butw,
501                     buth,
502                     &tfp->ve_median.generic.location[2],
503                     -lim,
504                     lim,
505                     10,
506                     RNA_TRANSLATION_PREC_DEFAULT,
507                     "");
508     UI_but_unit_type_set(but, PROP_UNIT_LENGTH);
509
510     if (totcurvebweight == tot) {
511       uiDefButF(block,
512                 UI_BTYPE_NUM,
513                 B_TRANSFORM_PANEL_MEDIAN,
514                 IFACE_("W:"),
515                 0,
516                 yi -= buth,
517                 butw,
518                 buth,
519                 &(tfp->ve_median.curve.b_weight),
520                 0.01,
521                 100.0,
522                 1,
523                 3,
524                 "");
525     }
526
527     UI_block_align_begin(block);
528     uiDefButBitS(block,
529                  UI_BTYPE_TOGGLE,
530                  V3D_GLOBAL_STATS,
531                  B_REDR,
532                  IFACE_("Global"),
533                  0,
534                  yi -= buth + but_margin,
535                  100,
536                  buth,
537                  &v3d->flag,
538                  0,
539                  0,
540                  0,
541                  0,
542                  TIP_("Displays global values"));
543     uiDefButBitS(block,
544                  UI_BTYPE_TOGGLE_N,
545                  V3D_GLOBAL_STATS,
546                  B_REDR,
547                  IFACE_("Local"),
548                  100,
549                  yi,
550                  100,
551                  buth,
552                  &v3d->flag,
553                  0,
554                  0,
555                  0,
556                  0,
557                  TIP_("Displays local values"));
558     UI_block_align_end(block);
559
560     /* Meshes... */
561     if (has_meshdata) {
562       TransformMedian_Mesh *ve_median = &tfp->ve_median.mesh;
563       if (tot) {
564         uiDefBut(block,
565                  UI_BTYPE_LABEL,
566                  0,
567                  tot == 1 ? IFACE_("Vertex Data:") : IFACE_("Vertices Data:"),
568                  0,
569                  yi -= buth + but_margin,
570                  butw,
571                  buth,
572                  NULL,
573                  0.0,
574                  0.0,
575                  0,
576                  0,
577                  "");
578         /* customdata layer added on demand */
579         uiDefButF(block,
580                   UI_BTYPE_NUM,
581                   B_TRANSFORM_PANEL_MEDIAN,
582                   tot == 1 ? IFACE_("Bevel Weight:") : IFACE_("Mean Bevel Weight:"),
583                   0,
584                   yi -= buth + but_margin,
585                   butw,
586                   buth,
587                   &ve_median->bv_weight,
588                   0.0,
589                   1.0,
590                   1,
591                   2,
592                   TIP_("Vertex weight used by Bevel modifier"));
593       }
594       if (has_skinradius) {
595         UI_block_align_begin(block);
596         uiDefButF(block,
597                   UI_BTYPE_NUM,
598                   B_TRANSFORM_PANEL_MEDIAN,
599                   tot == 1 ? IFACE_("Radius X:") : IFACE_("Mean Radius X:"),
600                   0,
601                   yi -= buth + but_margin,
602                   butw,
603                   buth,
604                   &ve_median->skin[0],
605                   0.0,
606                   100.0,
607                   1,
608                   3,
609                   TIP_("X radius used by Skin modifier"));
610         uiDefButF(block,
611                   UI_BTYPE_NUM,
612                   B_TRANSFORM_PANEL_MEDIAN,
613                   tot == 1 ? IFACE_("Radius Y:") : IFACE_("Mean Radius Y:"),
614                   0,
615                   yi -= buth + but_margin,
616                   butw,
617                   buth,
618                   &ve_median->skin[1],
619                   0.0,
620                   100.0,
621                   1,
622                   3,
623                   TIP_("Y radius used by Skin modifier"));
624         UI_block_align_end(block);
625       }
626       if (totedgedata) {
627         uiDefBut(block,
628                  UI_BTYPE_LABEL,
629                  0,
630                  totedgedata == 1 ? IFACE_("Edge Data:") : IFACE_("Edges Data:"),
631                  0,
632                  yi -= buth + but_margin,
633                  butw,
634                  buth,
635                  NULL,
636                  0.0,
637                  0.0,
638                  0,
639                  0,
640                  "");
641         /* customdata layer added on demand */
642         uiDefButF(block,
643                   UI_BTYPE_NUM,
644                   B_TRANSFORM_PANEL_MEDIAN,
645                   totedgedata == 1 ? IFACE_("Bevel Weight:") : IFACE_("Mean Bevel Weight:"),
646                   0,
647                   yi -= buth + but_margin,
648                   butw,
649                   buth,
650                   &ve_median->be_weight,
651                   0.0,
652                   1.0,
653                   1,
654                   2,
655                   TIP_("Edge weight used by Bevel modifier"));
656         /* customdata layer added on demand */
657         uiDefButF(block,
658                   UI_BTYPE_NUM,
659                   B_TRANSFORM_PANEL_MEDIAN,
660                   totedgedata == 1 ? IFACE_("Crease:") : IFACE_("Mean Crease:"),
661                   0,
662                   yi -= buth + but_margin,
663                   butw,
664                   buth,
665                   &ve_median->crease,
666                   0.0,
667                   1.0,
668                   1,
669                   2,
670                   TIP_("Weight used by the Subdivision Surface modifier"));
671       }
672     }
673     /* Curve... */
674     else if (totcurvedata) {
675       TransformMedian_Curve *ve_median = &tfp->ve_median.curve;
676       if (totcurvedata == 1) {
677         uiDefButR(block,
678                   UI_BTYPE_NUM,
679                   0,
680                   IFACE_("Weight:"),
681                   0,
682                   yi -= buth + but_margin,
683                   butw,
684                   buth,
685                   &data_ptr,
686                   "weight_softbody",
687                   0,
688                   0.0,
689                   1.0,
690                   1,
691                   3,
692                   NULL);
693         uiDefButR(block,
694                   UI_BTYPE_NUM,
695                   0,
696                   IFACE_("Radius:"),
697                   0,
698                   yi -= buth + but_margin,
699                   butw,
700                   buth,
701                   &data_ptr,
702                   "radius",
703                   0,
704                   0.0,
705                   100.0,
706                   1,
707                   3,
708                   NULL);
709         uiDefButR(block,
710                   UI_BTYPE_NUM,
711                   0,
712                   IFACE_("Tilt:"),
713                   0,
714                   yi -= buth + but_margin,
715                   butw,
716                   buth,
717                   &data_ptr,
718                   "tilt",
719                   0,
720                   -tilt_limit,
721                   tilt_limit,
722                   1,
723                   3,
724                   NULL);
725       }
726       else if (totcurvedata > 1) {
727         uiDefButF(block,
728                   UI_BTYPE_NUM,
729                   B_TRANSFORM_PANEL_MEDIAN,
730                   IFACE_("Mean Weight:"),
731                   0,
732                   yi -= buth + but_margin,
733                   butw,
734                   buth,
735                   &ve_median->weight,
736                   0.0,
737                   1.0,
738                   1,
739                   3,
740                   TIP_("Weight used for Soft Body Goal"));
741         uiDefButF(block,
742                   UI_BTYPE_NUM,
743                   B_TRANSFORM_PANEL_MEDIAN,
744                   IFACE_("Mean Radius:"),
745                   0,
746                   yi -= buth + but_margin,
747                   butw,
748                   buth,
749                   &ve_median->radius,
750                   0.0,
751                   100.0,
752                   1,
753                   3,
754                   TIP_("Radius of curve control points"));
755         but = uiDefButF(block,
756                         UI_BTYPE_NUM,
757                         B_TRANSFORM_PANEL_MEDIAN,
758                         IFACE_("Mean Tilt:"),
759                         0,
760                         yi -= buth + but_margin,
761                         butw,
762                         buth,
763                         &ve_median->tilt,
764                         -tilt_limit,
765                         tilt_limit,
766                         1,
767                         3,
768                         TIP_("Tilt of curve control points"));
769         UI_but_unit_type_set(but, PROP_UNIT_ROTATION);
770       }
771     }
772     /* Lattice... */
773     else if (totlattdata) {
774       TransformMedian_Lattice *ve_median = &tfp->ve_median.lattice;
775       if (totlattdata == 1) {
776         uiDefButR(block,
777                   UI_BTYPE_NUM,
778                   0,
779                   IFACE_("Weight:"),
780                   0,
781                   yi -= buth + but_margin,
782                   butw,
783                   buth,
784                   &data_ptr,
785                   "weight_softbody",
786                   0,
787                   0.0,
788                   1.0,
789                   1,
790                   3,
791                   NULL);
792       }
793       else if (totlattdata > 1) {
794         uiDefButF(block,
795                   UI_BTYPE_NUM,
796                   B_TRANSFORM_PANEL_MEDIAN,
797                   IFACE_("Mean Weight:"),
798                   0,
799                   yi -= buth + but_margin,
800                   butw,
801                   buth,
802                   &ve_median->weight,
803                   0.0,
804                   1.0,
805                   1,
806                   3,
807                   TIP_("Weight used for Soft Body Goal"));
808       }
809     }
810
811     UI_block_align_end(block);
812   }
813   else { /* apply */
814     memcpy(&ve_median_basis, &tfp->ve_median, sizeof(tfp->ve_median));
815
816     if (v3d->flag & V3D_GLOBAL_STATS) {
817       invert_m4_m4(ob->imat, ob->obmat);
818       mul_m4_v3(ob->imat, median_basis.generic.location);
819       mul_m4_v3(ob->imat, ve_median_basis.generic.location);
820     }
821     sub_vn_vnvn((float *)&median_basis,
822                 (float *)&ve_median_basis,
823                 (float *)&median_basis,
824                 TRANSFORM_MEDIAN_ARRAY_LEN);
825
826     /* Note with a single element selected, we always do. */
827     const bool apply_vcos = (tot == 1) || (len_squared_v3(median_basis.generic.location) != 0.0f);
828
829     if ((ob->type == OB_MESH) &&
830         (apply_vcos || median_basis.mesh.bv_weight || median_basis.mesh.skin[0] ||
831          median_basis.mesh.skin[1] || median_basis.mesh.be_weight || median_basis.mesh.crease)) {
832       const TransformMedian_Mesh *median = &median_basis.mesh, *ve_median = &ve_median_basis.mesh;
833       Mesh *me = ob->data;
834       BMEditMesh *em = me->edit_mesh;
835       BMesh *bm = em->bm;
836       BMIter iter;
837       BMVert *eve;
838       BMEdge *eed;
839
840       int cd_vert_bweight_offset = -1;
841       int cd_vert_skin_offset = -1;
842       int cd_edge_bweight_offset = -1;
843       int cd_edge_crease_offset = -1;
844
845       float scale_bv_weight = 1.0f;
846       float scale_skin[2] = {1.0f, 1.0f};
847       float scale_be_weight = 1.0f;
848       float scale_crease = 1.0f;
849
850       /* Vertices */
851
852       if (apply_vcos || median->bv_weight || median->skin[0] || median->skin[1]) {
853         if (median->bv_weight) {
854           BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_VERT_BWEIGHT);
855           cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT);
856           BLI_assert(cd_vert_bweight_offset != -1);
857
858           scale_bv_weight = compute_scale_factor(ve_median->bv_weight, median->bv_weight);
859         }
860
861         for (int i = 0; i < 2; i++) {
862           if (median->skin[i]) {
863             cd_vert_skin_offset = CustomData_get_offset(&bm->vdata, CD_MVERT_SKIN);
864             BLI_assert(cd_vert_skin_offset != -1);
865
866             if (ve_median->skin[i] != median->skin[i]) {
867               scale_skin[i] = ve_median->skin[i] / (ve_median->skin[i] - median->skin[i]);
868             }
869           }
870         }
871
872         BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
873           if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
874             if (apply_vcos) {
875               apply_raw_diff_v3(eve->co, tot, ve_median->location, median->location);
876             }
877
878             if (cd_vert_bweight_offset != -1) {
879               float *b_weight = BM_ELEM_CD_GET_VOID_P(eve, cd_vert_bweight_offset);
880               apply_scale_factor_clamp(b_weight, tot, ve_median->bv_weight, scale_bv_weight);
881             }
882
883             if (cd_vert_skin_offset != -1) {
884               MVertSkin *vs = BM_ELEM_CD_GET_VOID_P(eve, cd_vert_skin_offset);
885
886               /* That one is not clamped to [0.0, 1.0]. */
887               for (int i = 0; i < 2; i++) {
888                 if (median->skin[i] != 0.0f) {
889                   apply_scale_factor(
890                       &vs->radius[i], tot, ve_median->skin[i], median->skin[i], scale_skin[i]);
891                 }
892               }
893             }
894           }
895         }
896       }
897
898       if (apply_vcos) {
899         EDBM_mesh_normals_update(em);
900       }
901
902       /* Edges */
903
904       if (median->be_weight || median->crease) {
905         if (median->be_weight) {
906           BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_EDGE_BWEIGHT);
907           cd_edge_bweight_offset = CustomData_get_offset(&bm->edata, CD_BWEIGHT);
908           BLI_assert(cd_edge_bweight_offset != -1);
909
910           scale_be_weight = compute_scale_factor(ve_median->be_weight, median->be_weight);
911         }
912
913         if (median->crease) {
914           BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_EDGE_CREASE);
915           cd_edge_crease_offset = CustomData_get_offset(&bm->edata, CD_CREASE);
916           BLI_assert(cd_edge_crease_offset != -1);
917
918           scale_crease = compute_scale_factor(ve_median->crease, median->crease);
919         }
920
921         BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
922           if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
923             if (median->be_weight != 0.0f) {
924               float *b_weight = BM_ELEM_CD_GET_VOID_P(eed, cd_edge_bweight_offset);
925               apply_scale_factor_clamp(b_weight, tot, ve_median->be_weight, scale_be_weight);
926             }
927
928             if (median->crease != 0.0f) {
929               float *crease = BM_ELEM_CD_GET_VOID_P(eed, cd_edge_crease_offset);
930               apply_scale_factor_clamp(crease, tot, ve_median->crease, scale_crease);
931             }
932           }
933         }
934       }
935     }
936     else if (ELEM(ob->type, OB_CURVE, OB_SURF) &&
937              (apply_vcos || median_basis.curve.b_weight || median_basis.curve.weight ||
938               median_basis.curve.radius || median_basis.curve.tilt)) {
939       const TransformMedian_Curve *median = &median_basis.curve,
940                                   *ve_median = &ve_median_basis.curve;
941       Curve *cu = ob->data;
942       Nurb *nu;
943       BPoint *bp;
944       BezTriple *bezt;
945       int a;
946       ListBase *nurbs = BKE_curve_editNurbs_get(cu);
947       const float scale_w = compute_scale_factor(ve_median->weight, median->weight);
948
949       nu = nurbs->first;
950       while (nu) {
951         if (nu->type == CU_BEZIER) {
952           for (a = nu->pntsu, bezt = nu->bezt; a--; bezt++) {
953             if (bezt->f2 & SELECT) {
954               if (apply_vcos) {
955                 /* Here we always have to use the diff... :/
956                  * Cannot avoid some glitches when going e.g. from 3 to 0.0001 (see T37327),
957                  * unless we use doubles.
958                  */
959                 add_v3_v3(bezt->vec[0], median->location);
960                 add_v3_v3(bezt->vec[1], median->location);
961                 add_v3_v3(bezt->vec[2], median->location);
962               }
963               if (median->weight) {
964                 apply_scale_factor_clamp(&bezt->weight, tot, ve_median->weight, scale_w);
965               }
966               if (median->radius) {
967                 apply_raw_diff(&bezt->radius, tot, ve_median->radius, median->radius);
968               }
969               if (median->tilt) {
970                 apply_raw_diff(&bezt->tilt, tot, ve_median->tilt, median->tilt);
971               }
972             }
973             else if (apply_vcos) {
974               /* Handles can only have their coordinates changed here. */
975               if (bezt->f1 & SELECT) {
976                 apply_raw_diff_v3(bezt->vec[0], tot, ve_median->location, median->location);
977               }
978               if (bezt->f3 & SELECT) {
979                 apply_raw_diff_v3(bezt->vec[2], tot, ve_median->location, median->location);
980               }
981             }
982           }
983         }
984         else {
985           for (a = nu->pntsu * nu->pntsv, bp = nu->bp; a--; bp++) {
986             if (bp->f1 & SELECT) {
987               if (apply_vcos) {
988                 apply_raw_diff_v3(bp->vec, tot, ve_median->location, median->location);
989               }
990               if (median->b_weight) {
991                 apply_raw_diff(&bp->vec[3], tot, ve_median->b_weight, median->b_weight);
992               }
993               if (median->weight) {
994                 apply_scale_factor_clamp(&bp->weight, tot, ve_median->weight, scale_w);
995               }
996               if (median->radius) {
997                 apply_raw_diff(&bp->radius, tot, ve_median->radius, median->radius);
998               }
999               if (median->tilt) {
1000                 apply_raw_diff(&bp->tilt, tot, ve_median->tilt, median->tilt);
1001               }
1002             }
1003           }
1004         }
1005         BKE_nurb_test_2d(nu);
1006         BKE_nurb_handles_test(nu, true); /* test for bezier too */
1007
1008         nu = nu->next;
1009       }
1010     }
1011     else if ((ob->type == OB_LATTICE) && (apply_vcos || median_basis.lattice.weight)) {
1012       const TransformMedian_Lattice *median = &median_basis.lattice,
1013                                     *ve_median = &ve_median_basis.lattice;
1014       Lattice *lt = ob->data;
1015       BPoint *bp;
1016       int a;
1017       const float scale_w = compute_scale_factor(ve_median->weight, median->weight);
1018
1019       a = lt->editlatt->latt->pntsu * lt->editlatt->latt->pntsv * lt->editlatt->latt->pntsw;
1020       bp = lt->editlatt->latt->def;
1021       while (a--) {
1022         if (bp->f1 & SELECT) {
1023           if (apply_vcos) {
1024             apply_raw_diff_v3(bp->vec, tot, ve_median->location, median->location);
1025           }
1026           if (median->weight) {
1027             apply_scale_factor_clamp(&bp->weight, tot, ve_median->weight, scale_w);
1028           }
1029         }
1030         bp++;
1031       }
1032     }
1033
1034     /*      ED_undo_push(C, "Transform properties"); */
1035   }
1036 }
1037
1038 #undef TRANSFORM_MEDIAN_ARRAY_LEN
1039
1040 static void v3d_object_dimension_buts(bContext *C, uiLayout *layout, View3D *v3d, Object *ob)
1041 {
1042   uiBlock *block = (layout) ? uiLayoutAbsoluteBlock(layout) : NULL;
1043   TransformProperties *tfp = v3d_transform_props_ensure(v3d);
1044
1045   if (block) {
1046     BLI_assert(C == NULL);
1047     int yi = 200;
1048     const int butw = 200;
1049     const int buth = 20 * UI_DPI_FAC;
1050
1051     BKE_object_dimensions_get(ob, tfp->ob_dims);
1052     copy_v3_v3(tfp->ob_dims_orig, tfp->ob_dims);
1053
1054     uiDefBut(block,
1055              UI_BTYPE_LABEL,
1056              0,
1057              IFACE_("Dimensions:"),
1058              0,
1059              yi -= buth,
1060              butw,
1061              buth,
1062              NULL,
1063              0,
1064              0,
1065              0,
1066              0,
1067              "");
1068     UI_block_align_begin(block);
1069     const float lim = FLT_MAX;
1070     for (int i = 0; i < 3; i++) {
1071       uiBut *but;
1072       char text[3] = {'X' + i, ':', '\0'};
1073       but = uiDefButF(block,
1074                       UI_BTYPE_NUM,
1075                       B_TRANSFORM_PANEL_DIMS,
1076                       text,
1077                       0,
1078                       yi -= buth,
1079                       butw,
1080                       buth,
1081                       &(tfp->ob_dims[i]),
1082                       0.0f,
1083                       lim,
1084                       10,
1085                       3,
1086                       "");
1087       UI_but_unit_type_set(but, PROP_UNIT_LENGTH);
1088     }
1089     UI_block_align_end(block);
1090   }
1091   else { /* apply */
1092     int axis_mask = 0;
1093     for (int i = 0; i < 3; i++) {
1094       if (tfp->ob_dims[i] == tfp->ob_dims_orig[i]) {
1095         axis_mask |= (1 << i);
1096       }
1097     }
1098     BKE_object_dimensions_set(ob, tfp->ob_dims, axis_mask);
1099
1100     PointerRNA obptr;
1101     RNA_id_pointer_create(&ob->id, &obptr);
1102     PropertyRNA *prop = RNA_struct_find_property(&obptr, "scale");
1103     RNA_property_update(C, &obptr, prop);
1104   }
1105 }
1106
1107 #define B_VGRP_PNL_EDIT_SINGLE 8 /* or greater */
1108
1109 static void do_view3d_vgroup_buttons(bContext *C, void *UNUSED(arg), int event)
1110 {
1111   if (event < B_VGRP_PNL_EDIT_SINGLE) {
1112     /* not for me */
1113     return;
1114   }
1115   else {
1116     ViewLayer *view_layer = CTX_data_view_layer(C);
1117     Object *ob = view_layer->basact->object;
1118     ED_vgroup_vert_active_mirror(ob, event - B_VGRP_PNL_EDIT_SINGLE);
1119     DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
1120     WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
1121   }
1122 }
1123
1124 static bool view3d_panel_vgroup_poll(const bContext *C, PanelType *UNUSED(pt))
1125 {
1126   ViewLayer *view_layer = CTX_data_view_layer(C);
1127   Object *ob = OBACT(view_layer);
1128   if (ob && (BKE_object_is_in_editmode_vgroup(ob) || BKE_object_is_in_wpaint_select_vert(ob))) {
1129     MDeformVert *dvert_act = ED_mesh_active_dvert_get_only(ob);
1130     if (dvert_act) {
1131       return (dvert_act->totweight != 0);
1132     }
1133   }
1134
1135   return false;
1136 }
1137
1138 static void view3d_panel_vgroup(const bContext *C, Panel *pa)
1139 {
1140   uiBlock *block = uiLayoutAbsoluteBlock(pa->layout);
1141   Scene *scene = CTX_data_scene(C);
1142   ViewLayer *view_layer = CTX_data_view_layer(C);
1143   Object *ob = view_layer->basact->object;
1144
1145   MDeformVert *dv;
1146
1147   dv = ED_mesh_active_dvert_get_only(ob);
1148
1149   if (dv && dv->totweight) {
1150     ToolSettings *ts = scene->toolsettings;
1151
1152     wmOperatorType *ot;
1153     PointerRNA op_ptr, tools_ptr;
1154     PointerRNA *but_ptr;
1155
1156     uiLayout *col, *bcol;
1157     uiLayout *row;
1158     uiBut *but;
1159     bDeformGroup *dg;
1160     uint i;
1161     int subset_count, vgroup_tot;
1162     const bool *vgroup_validmap;
1163     eVGroupSelect subset_type = ts->vgroupsubset;
1164     int yco = 0;
1165     int lock_count = 0;
1166
1167     UI_block_func_handle_set(block, do_view3d_vgroup_buttons, NULL);
1168
1169     bcol = uiLayoutColumn(pa->layout, true);
1170     row = uiLayoutRow(bcol, true); /* The filter button row */
1171
1172     RNA_pointer_create(NULL, &RNA_ToolSettings, ts, &tools_ptr);
1173     uiItemR(row, &tools_ptr, "vertex_group_subset", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
1174
1175     col = uiLayoutColumn(bcol, true);
1176
1177     vgroup_validmap = BKE_object_defgroup_subset_from_select_type(
1178         ob, subset_type, &vgroup_tot, &subset_count);
1179     for (i = 0, dg = ob->defbase.first; dg; i++, dg = dg->next) {
1180       bool locked = (dg->flag & DG_LOCK_WEIGHT) != 0;
1181       if (vgroup_validmap[i]) {
1182         MDeformWeight *dw = defvert_find_index(dv, i);
1183         if (dw) {
1184           int x, xco = 0;
1185           int icon;
1186           uiLayout *split = uiLayoutSplit(col, 0.45, true);
1187           row = uiLayoutRow(split, true);
1188
1189           /* The Weight Group Name */
1190
1191           ot = WM_operatortype_find("OBJECT_OT_vertex_weight_set_active", true);
1192           but = uiDefButO_ptr(block,
1193                               UI_BTYPE_BUT,
1194                               ot,
1195                               WM_OP_EXEC_DEFAULT,
1196                               dg->name,
1197                               xco,
1198                               yco,
1199                               (x = UI_UNIT_X * 5),
1200                               UI_UNIT_Y,
1201                               "");
1202           but_ptr = UI_but_operator_ptr_get(but);
1203           RNA_int_set(but_ptr, "weight_group", i);
1204           UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT);
1205           if (ob->actdef != i + 1) {
1206             UI_but_flag_enable(but, UI_BUT_INACTIVE);
1207           }
1208           xco += x;
1209
1210           row = uiLayoutRow(split, true);
1211           uiLayoutSetEnabled(row, !locked);
1212
1213           /* The weight group value */
1214           /* To be reworked still */
1215           but = uiDefButF(block,
1216                           UI_BTYPE_NUM,
1217                           B_VGRP_PNL_EDIT_SINGLE + i,
1218                           "",
1219                           xco,
1220                           yco,
1221                           (x = UI_UNIT_X * 4),
1222                           UI_UNIT_Y,
1223                           &dw->weight,
1224                           0.0,
1225                           1.0,
1226                           1,
1227                           3,
1228                           "");
1229           UI_but_drawflag_enable(but, UI_BUT_TEXT_LEFT);
1230           if (locked) {
1231             lock_count++;
1232           }
1233           xco += x;
1234
1235           /* The weight group paste function */
1236           icon = (locked) ? ICON_BLANK1 : ICON_PASTEDOWN;
1237           uiItemFullO(row,
1238                       "OBJECT_OT_vertex_weight_paste",
1239                       "",
1240                       icon,
1241                       NULL,
1242                       WM_OP_INVOKE_DEFAULT,
1243                       0,
1244                       &op_ptr);
1245           RNA_int_set(&op_ptr, "weight_group", i);
1246
1247           /* The weight entry delete function */
1248           icon = (locked) ? ICON_LOCKED : ICON_X;
1249           uiItemFullO(row,
1250                       "OBJECT_OT_vertex_weight_delete",
1251                       "",
1252                       icon,
1253                       NULL,
1254                       WM_OP_INVOKE_DEFAULT,
1255                       0,
1256                       &op_ptr);
1257           RNA_int_set(&op_ptr, "weight_group", i);
1258
1259           yco -= UI_UNIT_Y;
1260         }
1261       }
1262     }
1263     MEM_freeN((void *)vgroup_validmap);
1264
1265     yco -= 2;
1266
1267     col = uiLayoutColumn(pa->layout, true);
1268     row = uiLayoutRow(col, true);
1269
1270     ot = WM_operatortype_find("OBJECT_OT_vertex_weight_normalize_active_vertex", 1);
1271     but = uiDefButO_ptr(
1272         block,
1273         UI_BTYPE_BUT,
1274         ot,
1275         WM_OP_EXEC_DEFAULT,
1276         "Normalize",
1277         0,
1278         yco,
1279         UI_UNIT_X * 5,
1280         UI_UNIT_Y,
1281         TIP_("Normalize weights of active vertex (if affected groups are unlocked)"));
1282     if (lock_count) {
1283       UI_but_flag_enable(but, UI_BUT_DISABLED);
1284     }
1285
1286     ot = WM_operatortype_find("OBJECT_OT_vertex_weight_copy", 1);
1287     but = uiDefButO_ptr(
1288         block,
1289         UI_BTYPE_BUT,
1290         ot,
1291         WM_OP_EXEC_DEFAULT,
1292         "Copy",
1293         UI_UNIT_X * 5,
1294         yco,
1295         UI_UNIT_X * 5,
1296         UI_UNIT_Y,
1297         TIP_("Copy active vertex to other selected vertices (if affected groups are unlocked)"));
1298     if (lock_count) {
1299       UI_but_flag_enable(but, UI_BUT_DISABLED);
1300     }
1301   }
1302 }
1303
1304 static void v3d_transform_butsR(uiLayout *layout, PointerRNA *ptr)
1305 {
1306   uiLayout *split, *colsub;
1307
1308   split = uiLayoutSplit(layout, 0.8f, false);
1309
1310   if (ptr->type == &RNA_PoseBone) {
1311     PointerRNA boneptr;
1312     Bone *bone;
1313
1314     boneptr = RNA_pointer_get(ptr, "bone");
1315     bone = boneptr.data;
1316     uiLayoutSetActive(split, !(bone->parent && bone->flag & BONE_CONNECTED));
1317   }
1318   colsub = uiLayoutColumn(split, true);
1319   uiItemR(colsub, ptr, "location", 0, NULL, ICON_NONE);
1320   colsub = uiLayoutColumn(split, true);
1321   uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE);
1322   uiItemL(colsub, "", ICON_NONE);
1323   uiItemR(colsub,
1324           ptr,
1325           "lock_location",
1326           UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY,
1327           "",
1328           ICON_DECORATE_UNLOCKED);
1329
1330   split = uiLayoutSplit(layout, 0.8f, false);
1331
1332   switch (RNA_enum_get(ptr, "rotation_mode")) {
1333     case ROT_MODE_QUAT: /* quaternion */
1334       colsub = uiLayoutColumn(split, true);
1335       uiItemR(colsub, ptr, "rotation_quaternion", 0, IFACE_("Rotation"), ICON_NONE);
1336       colsub = uiLayoutColumn(split, true);
1337       uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE);
1338       uiItemR(colsub, ptr, "lock_rotations_4d", UI_ITEM_R_TOGGLE, IFACE_("4L"), ICON_NONE);
1339       if (RNA_boolean_get(ptr, "lock_rotations_4d")) {
1340         uiItemR(colsub,
1341                 ptr,
1342                 "lock_rotation_w",
1343                 UI_ITEM_R_TOGGLE + UI_ITEM_R_ICON_ONLY,
1344                 "",
1345                 ICON_DECORATE_UNLOCKED);
1346       }
1347       else {
1348         uiItemL(colsub, "", ICON_NONE);
1349       }
1350       uiItemR(colsub,
1351               ptr,
1352               "lock_rotation",
1353               UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY,
1354               "",
1355               ICON_DECORATE_UNLOCKED);
1356       break;
1357     case ROT_MODE_AXISANGLE: /* axis angle */
1358       colsub = uiLayoutColumn(split, true);
1359       uiItemR(colsub, ptr, "rotation_axis_angle", 0, IFACE_("Rotation"), ICON_NONE);
1360       colsub = uiLayoutColumn(split, true);
1361       uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE);
1362       uiItemR(colsub, ptr, "lock_rotations_4d", UI_ITEM_R_TOGGLE, IFACE_("4L"), ICON_NONE);
1363       if (RNA_boolean_get(ptr, "lock_rotations_4d")) {
1364         uiItemR(colsub,
1365                 ptr,
1366                 "lock_rotation_w",
1367                 UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY,
1368                 "",
1369                 ICON_DECORATE_UNLOCKED);
1370       }
1371       else {
1372         uiItemL(colsub, "", ICON_NONE);
1373       }
1374       uiItemR(colsub,
1375               ptr,
1376               "lock_rotation",
1377               UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY,
1378               "",
1379               ICON_DECORATE_UNLOCKED);
1380       break;
1381     default: /* euler rotations */
1382       colsub = uiLayoutColumn(split, true);
1383       uiItemR(colsub, ptr, "rotation_euler", 0, IFACE_("Rotation"), ICON_NONE);
1384       colsub = uiLayoutColumn(split, true);
1385       uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE);
1386       uiItemL(colsub, "", ICON_NONE);
1387       uiItemR(colsub,
1388               ptr,
1389               "lock_rotation",
1390               UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY,
1391               "",
1392               ICON_DECORATE_UNLOCKED);
1393       break;
1394   }
1395   uiItemR(layout, ptr, "rotation_mode", 0, "", ICON_NONE);
1396
1397   split = uiLayoutSplit(layout, 0.8f, false);
1398   colsub = uiLayoutColumn(split, true);
1399   uiItemR(colsub, ptr, "scale", 0, NULL, ICON_NONE);
1400   colsub = uiLayoutColumn(split, true);
1401   uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE);
1402   uiItemL(colsub, "", ICON_NONE);
1403   uiItemR(colsub,
1404           ptr,
1405           "lock_scale",
1406           UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY,
1407           "",
1408           ICON_DECORATE_UNLOCKED);
1409 }
1410
1411 static void v3d_posearmature_buts(uiLayout *layout, Object *ob)
1412 {
1413   bPoseChannel *pchan;
1414   PointerRNA pchanptr;
1415   uiLayout *col;
1416
1417   pchan = BKE_pose_channel_active(ob);
1418
1419   if (!pchan) {
1420     uiItemL(layout, IFACE_("No Bone Active"), ICON_NONE);
1421     return;
1422   }
1423
1424   RNA_pointer_create(&ob->id, &RNA_PoseBone, pchan, &pchanptr);
1425
1426   col = uiLayoutColumn(layout, false);
1427
1428   /* XXX: RNA buts show data in native types (i.e. quats, 4-component axis/angle, etc.)
1429    * but old-school UI shows in eulers always. Do we want to be able to still display in Eulers?
1430    * Maybe needs RNA/ui options to display rotations as different types... */
1431   v3d_transform_butsR(col, &pchanptr);
1432 }
1433
1434 static void v3d_editarmature_buts(uiLayout *layout, Object *ob)
1435 {
1436   bArmature *arm = ob->data;
1437   EditBone *ebone;
1438   uiLayout *col;
1439   PointerRNA eboneptr;
1440
1441   ebone = arm->act_edbone;
1442
1443   if (!ebone || (ebone->layer & arm->layer) == 0) {
1444     uiItemL(layout, IFACE_("Nothing selected"), ICON_NONE);
1445     return;
1446   }
1447
1448   RNA_pointer_create(&arm->id, &RNA_EditBone, ebone, &eboneptr);
1449
1450   col = uiLayoutColumn(layout, false);
1451   uiItemR(col, &eboneptr, "head", 0, NULL, ICON_NONE);
1452   if (ebone->parent && ebone->flag & BONE_CONNECTED) {
1453     PointerRNA parptr = RNA_pointer_get(&eboneptr, "parent");
1454     uiItemR(col, &parptr, "tail_radius", 0, IFACE_("Radius (Parent)"), ICON_NONE);
1455   }
1456   else {
1457     uiItemR(col, &eboneptr, "head_radius", 0, IFACE_("Radius"), ICON_NONE);
1458   }
1459
1460   uiItemR(col, &eboneptr, "tail", 0, NULL, ICON_NONE);
1461   uiItemR(col, &eboneptr, "tail_radius", 0, IFACE_("Radius"), ICON_NONE);
1462
1463   uiItemR(col, &eboneptr, "roll", 0, NULL, ICON_NONE);
1464   uiItemR(col, &eboneptr, "envelope_distance", 0, IFACE_("Envelope"), ICON_NONE);
1465 }
1466
1467 static void v3d_editmetaball_buts(uiLayout *layout, Object *ob)
1468 {
1469   PointerRNA mbptr, ptr;
1470   MetaBall *mball = ob->data;
1471   uiLayout *col;
1472
1473   if (!mball || !(mball->lastelem)) {
1474     return;
1475   }
1476
1477   RNA_pointer_create(&mball->id, &RNA_MetaBall, mball, &mbptr);
1478
1479   RNA_pointer_create(&mball->id, &RNA_MetaElement, mball->lastelem, &ptr);
1480
1481   col = uiLayoutColumn(layout, false);
1482   uiItemR(col, &ptr, "co", 0, NULL, ICON_NONE);
1483
1484   uiItemR(col, &ptr, "radius", 0, NULL, ICON_NONE);
1485   uiItemR(col, &ptr, "stiffness", 0, NULL, ICON_NONE);
1486
1487   uiItemR(col, &ptr, "type", 0, NULL, ICON_NONE);
1488
1489   col = uiLayoutColumn(layout, true);
1490   switch (RNA_enum_get(&ptr, "type")) {
1491     case MB_BALL:
1492       break;
1493     case MB_CUBE:
1494       uiItemL(col, IFACE_("Size:"), ICON_NONE);
1495       uiItemR(col, &ptr, "size_x", 0, "X", ICON_NONE);
1496       uiItemR(col, &ptr, "size_y", 0, "Y", ICON_NONE);
1497       uiItemR(col, &ptr, "size_z", 0, "Z", ICON_NONE);
1498       break;
1499     case MB_TUBE:
1500       uiItemL(col, IFACE_("Size:"), ICON_NONE);
1501       uiItemR(col, &ptr, "size_x", 0, "X", ICON_NONE);
1502       break;
1503     case MB_PLANE:
1504       uiItemL(col, IFACE_("Size:"), ICON_NONE);
1505       uiItemR(col, &ptr, "size_x", 0, "X", ICON_NONE);
1506       uiItemR(col, &ptr, "size_y", 0, "Y", ICON_NONE);
1507       break;
1508     case MB_ELIPSOID:
1509       uiItemL(col, IFACE_("Size:"), ICON_NONE);
1510       uiItemR(col, &ptr, "size_x", 0, "X", ICON_NONE);
1511       uiItemR(col, &ptr, "size_y", 0, "Y", ICON_NONE);
1512       uiItemR(col, &ptr, "size_z", 0, "Z", ICON_NONE);
1513       break;
1514   }
1515 }
1516
1517 static void do_view3d_region_buttons(bContext *C, void *UNUSED(index), int event)
1518 {
1519   ViewLayer *view_layer = CTX_data_view_layer(C);
1520   View3D *v3d = CTX_wm_view3d(C);
1521   Object *ob = OBACT(view_layer);
1522
1523   switch (event) {
1524
1525     case B_REDR:
1526       ED_area_tag_redraw(CTX_wm_area(C));
1527       return; /* no notifier! */
1528
1529     case B_TRANSFORM_PANEL_MEDIAN:
1530       if (ob) {
1531         v3d_editvertex_buts(NULL, v3d, ob, 1.0);
1532         DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
1533       }
1534       break;
1535     case B_TRANSFORM_PANEL_DIMS:
1536       if (ob) {
1537         v3d_object_dimension_buts(C, NULL, v3d, ob);
1538       }
1539       break;
1540   }
1541
1542   /* default for now */
1543   WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
1544 }
1545
1546 static bool view3d_panel_transform_poll(const bContext *C, PanelType *UNUSED(pt))
1547 {
1548   ViewLayer *view_layer = CTX_data_view_layer(C);
1549   return (view_layer->basact != NULL);
1550 }
1551
1552 static void view3d_panel_transform(const bContext *C, Panel *pa)
1553 {
1554   uiBlock *block;
1555   ViewLayer *view_layer = CTX_data_view_layer(C);
1556   Object *ob = view_layer->basact->object;
1557   Object *obedit = OBEDIT_FROM_OBACT(ob);
1558   uiLayout *col;
1559
1560   block = uiLayoutGetBlock(pa->layout);
1561   UI_block_func_handle_set(block, do_view3d_region_buttons, NULL);
1562
1563   col = uiLayoutColumn(pa->layout, false);
1564
1565   if (ob == obedit) {
1566     if (ob->type == OB_ARMATURE) {
1567       v3d_editarmature_buts(col, ob);
1568     }
1569     else if (ob->type == OB_MBALL) {
1570       v3d_editmetaball_buts(col, ob);
1571     }
1572     else {
1573       View3D *v3d = CTX_wm_view3d(C);
1574       Scene *scene = CTX_data_scene(C);
1575       const float lim = 10000.0f * max_ff(1.0f, ED_view3d_grid_scale(scene, v3d, NULL));
1576       v3d_editvertex_buts(col, v3d, ob, lim);
1577     }
1578   }
1579   else if (ob->mode & OB_MODE_POSE) {
1580     v3d_posearmature_buts(col, ob);
1581   }
1582   else {
1583     PointerRNA obptr;
1584
1585     RNA_id_pointer_create(&ob->id, &obptr);
1586     v3d_transform_butsR(col, &obptr);
1587
1588     /* dimensions and editmode just happen to be the same checks */
1589     if (OB_TYPE_SUPPORT_EDITMODE(ob->type)) {
1590       View3D *v3d = CTX_wm_view3d(C);
1591       v3d_object_dimension_buts(NULL, col, v3d, ob);
1592     }
1593   }
1594 }
1595
1596 static void hide_collections_menu_draw(const bContext *C, Menu *menu)
1597 {
1598   ED_collection_hide_menu_draw(C, menu->layout);
1599 }
1600
1601 void view3d_buttons_register(ARegionType *art)
1602 {
1603   PanelType *pt;
1604
1605   pt = MEM_callocN(sizeof(PanelType), "spacetype view3d panel object");
1606   strcpy(pt->idname, "VIEW3D_PT_transform");
1607   strcpy(pt->label, N_("Transform")); /* XXX C panels unavailable through RNA bpy.types! */
1608   strcpy(pt->category, "Item");
1609   strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
1610   pt->draw = view3d_panel_transform;
1611   pt->poll = view3d_panel_transform_poll;
1612   BLI_addtail(&art->paneltypes, pt);
1613
1614   pt = MEM_callocN(sizeof(PanelType), "spacetype view3d panel vgroup");
1615   strcpy(pt->idname, "VIEW3D_PT_vgroup");
1616   strcpy(pt->label, N_("Vertex Weights")); /* XXX C panels unavailable through RNA bpy.types! */
1617   strcpy(pt->category, "Item");
1618   strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
1619   pt->draw = view3d_panel_vgroup;
1620   pt->poll = view3d_panel_vgroup_poll;
1621   BLI_addtail(&art->paneltypes, pt);
1622
1623   MenuType *mt;
1624
1625   mt = MEM_callocN(sizeof(MenuType), "spacetype view3d menu collections");
1626   strcpy(mt->idname, "VIEW3D_MT_collection");
1627   strcpy(mt->label, N_("Collection"));
1628   strcpy(mt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
1629   mt->draw = hide_collections_menu_draw;
1630   WM_menutype_add(mt);
1631 }
1632
1633 static int view3d_object_mode_menu(bContext *C, wmOperator *op)
1634 {
1635   Object *ob = CTX_data_active_object(C);
1636   if (ob == NULL) {
1637     BKE_report(op->reports, RPT_WARNING, "No active object found");
1638     return OPERATOR_CANCELLED;
1639   }
1640   else if (((ob->mode & OB_MODE_EDIT) == 0) && (ELEM(ob->type, OB_ARMATURE))) {
1641     ED_object_mode_toggle(C, OB_MODE_POSE);
1642     return OPERATOR_CANCELLED;
1643   }
1644   else {
1645     UI_pie_menu_invoke(C, "VIEW3D_MT_object_mode_pie", CTX_wm_window(C)->eventstate);
1646     return OPERATOR_CANCELLED;
1647   }
1648 }
1649
1650 void VIEW3D_OT_object_mode_pie_or_toggle(wmOperatorType *ot)
1651 {
1652   ot->name = "Object Mode Menu";
1653   ot->idname = "VIEW3D_OT_object_mode_pie_or_toggle";
1654
1655   ot->exec = view3d_object_mode_menu;
1656   ot->poll = ED_operator_view3d_active;
1657
1658   /* flags */
1659   ot->flag = 0;
1660 }