Merge with 2.5 -r 21003:21788.
[blender.git] / source / blender / editors / object / object_edit.c
index ddccb372ac80d680e2d914cafb6ac891c37ca64c..6c645406d2e9ea9caa124645f963b902b485fff0 100644 (file)
 #include "ED_mesh.h"
 #include "ED_object.h"
 #include "ED_screen.h"
+#include "ED_transform.h"
 #include "ED_types.h"
 #include "ED_util.h"
 #include "ED_view3d.h"
 
-#include "BIF_transform.h"
-
 #include "UI_interface.h"
 
 #include "RNA_access.h"
@@ -244,16 +243,18 @@ void ED_object_base_init_from_view(bContext *C, Base *base)
 /* ******************* add object operator ****************** */
 
 static EnumPropertyItem prop_object_types[] = {
-       {OB_EMPTY, "EMPTY", 0, "Empty", ""},
        {OB_MESH, "MESH", 0, "Mesh", ""},
        {OB_CURVE, "CURVE", 0, "Curve", ""},
        {OB_SURF, "SURFACE", 0, "Surface", ""},
-       {OB_FONT, "TEXT", 0, "Text", ""},
        {OB_MBALL, "META", 0, "Meta", ""},
-       {OB_LAMP, "LAMP", 0, "Lamp", ""},
-       {OB_CAMERA, "CAMERA", 0, "Camera", ""},
+       {OB_FONT, "TEXT", 0, "Text", ""},
+       {0, "", 0, NULL, NULL},
        {OB_ARMATURE, "ARMATURE", 0, "Armature", ""},
        {OB_LATTICE, "LATTICE", 0, "Lattice", ""},
+       {OB_EMPTY, "EMPTY", 0, "Empty", ""},
+       {0, "", 0, NULL, NULL},
+       {OB_CAMERA, "CAMERA", 0, "Camera", ""},
+       {OB_LAMP, "LAMP", 0, "Lamp", ""},
        {0, NULL, 0, NULL, NULL}
 };
 
@@ -327,15 +328,16 @@ void OBJECT_OT_object_add(wmOperatorType *ot)
 /* ******  work both in and outside editmode ****** */
 
 static EnumPropertyItem prop_mesh_types[] = {
-       {0, "PLANE", 0, "Plane", ""},
-       {1, "CUBE", 0, "Cube", ""},
-       {2, "CIRCLE", 0, "Circle", ""},
-       {3, "UVSPHERE", 0, "UVsphere", ""},
-       {4, "ICOSPHERE", 0, "Icosphere", ""},
-       {5, "CYLINDER", 0, "Cylinder", ""},
-       {6, "CONE", 0, "Cone", ""},
-       {7, "GRID", 0, "Grid", ""},
-       {8, "MONKEY", 0, "Monkey", ""},
+       {0, "PLANE", ICON_MESH_PLANE, "Plane", ""},
+       {1, "CUBE", ICON_MESH_CUBE, "Cube", ""},
+       {2, "CIRCLE", ICON_MESH_CIRCLE, "Circle", ""},
+       {3, "UVSPHERE", ICON_MESH_UVSPHERE, "UVsphere", ""},
+       {4, "ICOSPHERE", ICON_MESH_ICOSPHERE, "Icosphere", ""},
+       {5, "CYLINDER", ICON_MESH_TUBE, "Cylinder", ""},
+       {6, "CONE", ICON_MESH_CONE, "Cone", ""},
+       {0, "", 0, NULL, NULL},
+       {7, "GRID", ICON_MESH_GRID, "Grid", ""},
+       {8, "MONKEY", ICON_MESH_MONKEY, "Monkey", ""},
        {0, NULL, 0, NULL, NULL}
 };
 
@@ -346,7 +348,7 @@ static int object_add_mesh_exec(bContext *C, wmOperator *op)
        
        if(obedit==NULL || obedit->type!=OB_MESH) {
                object_add_type(C, OB_MESH);
-               ED_object_enter_editmode(C, 0);
+               ED_object_enter_editmode(C, EM_DO_UNDO);
                newob = 1;
        }
        else DAG_object_flush_update(CTX_data_scene(C), obedit, OB_RECALC_DATA);
@@ -394,7 +396,7 @@ static int object_add_mesh_exec(bContext *C, wmOperator *op)
 void OBJECT_OT_mesh_add(wmOperatorType *ot)
 {
        /* identifiers */
-       ot->name= "Mesh";
+       ot->name= "Add Mesh";
        ot->description = "Add a mesh object to the scene.";
        ot->idname= "OBJECT_OT_mesh_add";
        
@@ -404,18 +406,18 @@ void OBJECT_OT_mesh_add(wmOperatorType *ot)
        
        ot->poll= ED_operator_scene_editable;
        
-       /* flags */
-       ot->flag= 0;
+       /* flags: no register or undo, this operator calls operators */
+       ot->flag= 0; //OPTYPE_REGISTER|OPTYPE_UNDO;
        
        RNA_def_enum(ot->srna, "type", prop_mesh_types, 0, "Primitive", "");
 }
 
 static EnumPropertyItem prop_curve_types[] = {
-       {CU_BEZIER|CU_2D|CU_PRIM_CURVE, "BEZIER_CURVE", 0, "Bezier Curve", ""},
-       {CU_BEZIER|CU_2D|CU_PRIM_CIRCLE, "BEZIER_CIRCLE", 0, "Bezier Circle", ""},
-       {CU_NURBS|CU_2D|CU_PRIM_CURVE, "NURBS_CURVE", 0, "NURBS Curve", ""},
-       {CU_NURBS|CU_2D|CU_PRIM_CIRCLE, "NURBS_CIRCLE", 0, "NURBS Circle", ""},
-       {CU_NURBS|CU_2D|CU_PRIM_PATH, "PATH", 0, "Path", ""},
+       {CU_BEZIER|CU_2D|CU_PRIM_CURVE, "BEZIER_CURVE", ICON_CURVE_BEZCURVE, "Bezier Curve", ""},
+       {CU_BEZIER|CU_2D|CU_PRIM_CIRCLE, "BEZIER_CIRCLE", ICON_CURVE_BEZCIRCLE, "Bezier Circle", ""},
+       {CU_NURBS|CU_2D|CU_PRIM_CURVE, "NURBS_CURVE", ICON_CURVE_NCURVE, "NURBS Curve", ""},
+       {CU_NURBS|CU_2D|CU_PRIM_CIRCLE, "NURBS_CIRCLE", ICON_CURVE_NCIRCLE, "NURBS Circle", ""},
+       {CU_NURBS|CU_2D|CU_PRIM_PATH, "PATH", ICON_CURVE_PATH, "Path", ""},
        {0, NULL, 0, NULL, NULL}
 };
 
@@ -468,7 +470,7 @@ static int object_add_curve_invoke(bContext *C, wmOperator *op, wmEvent *event)
 void OBJECT_OT_curve_add(wmOperatorType *ot)
 {
        /* identifiers */
-       ot->name= "Curve";
+       ot->name= "Add Curve";
        ot->description = "Add a curve object to the scene.";
        ot->idname= "OBJECT_OT_curve_add";
        
@@ -485,12 +487,12 @@ void OBJECT_OT_curve_add(wmOperatorType *ot)
 }
 
 static EnumPropertyItem prop_surface_types[]= {
-       {CU_PRIM_CURVE|CU_NURBS, "NURBS_CURVE", 0, "NURBS Curve", ""},
-       {CU_PRIM_CIRCLE|CU_NURBS, "NURBS_CIRCLE", 0, "NURBS Circle", ""},
-       {CU_PRIM_PATCH|CU_NURBS, "NURBS_SURFACE", 0, "NURBS Surface", ""},
-       {CU_PRIM_TUBE|CU_NURBS, "NURBS_TUBE", 0, "NURBS Tube", ""},
-       {CU_PRIM_SPHERE|CU_NURBS, "NURBS_SPHERE", 0, "NURBS Sphere", ""},
-       {CU_PRIM_DONUT|CU_NURBS, "NURBS_DONUT", 0, "NURBS Donut", ""},
+       {CU_PRIM_CURVE|CU_NURBS, "NURBS_CURVE", ICON_SURFACE_NCURVE, "NURBS Curve", ""},
+       {CU_PRIM_CIRCLE|CU_NURBS, "NURBS_CIRCLE", ICON_SURFACE_NCIRCLE, "NURBS Circle", ""},
+       {CU_PRIM_PATCH|CU_NURBS, "NURBS_SURFACE", ICON_SURFACE_NSURFACE, "NURBS Surface", ""},
+       {CU_PRIM_TUBE|CU_NURBS, "NURBS_TUBE", ICON_SURFACE_NTUBE, "NURBS Tube", ""},
+       {CU_PRIM_SPHERE|CU_NURBS, "NURBS_SPHERE", ICON_SURFACE_NSPHERE, "NURBS Sphere", ""},
+       {CU_PRIM_DONUT|CU_NURBS, "NURBS_DONUT", ICON_SURFACE_NDONUT, "NURBS Donut", ""},
        {0, NULL, 0, NULL, NULL}
 };
 
@@ -526,7 +528,7 @@ static int object_add_surface_exec(bContext *C, wmOperator *op)
 void OBJECT_OT_surface_add(wmOperatorType *ot)
 {
        /* identifiers */
-       ot->name= "Surface";
+       ot->name= "Add Surface";
        ot->description = "Add a surface object to the scene.";
        ot->idname= "OBJECT_OT_surface_add";
        
@@ -563,7 +565,7 @@ static int object_add_text_exec(bContext *C, wmOperator *op)
 void OBJECT_OT_text_add(wmOperatorType *ot)
 {
        /* identifiers */
-       ot->name= "Text";
+       ot->name= "Add Text";
        ot->description = "Add a text object to the scene";
        ot->idname= "OBJECT_OT_text_add";
        
@@ -608,7 +610,7 @@ static int object_armature_add_exec(bContext *C, wmOperator *op)
 void OBJECT_OT_armature_add(wmOperatorType *ot)
 {      
        /* identifiers */
-       ot->name= "Armature";
+       ot->name= "Add Armature";
        ot->description = "Add an armature object to the scene.";
        ot->idname= "OBJECT_OT_armature_add";
        
@@ -626,16 +628,18 @@ static int object_primitive_add_invoke(bContext *C, wmOperator *op, wmEvent *eve
        uiPopupMenu *pup= uiPupMenuBegin(C, "Add Object", 0);
        uiLayout *layout= uiPupMenuLayout(pup);
        
-       uiItemMenuEnumO(layout, NULL, ICON_OUTLINER_OB_MESH, "OBJECT_OT_mesh_add", "type");
-       uiItemMenuEnumO(layout, NULL, ICON_OUTLINER_OB_CURVE, "OBJECT_OT_curve_add", "type");
-       uiItemMenuEnumO(layout, NULL, ICON_OUTLINER_OB_SURFACE, "OBJECT_OT_surface_add", "type");
-       uiItemO(layout, NULL, ICON_OUTLINER_OB_FONT, "OBJECT_OT_text_add");
+       uiItemMenuEnumO(layout, "Mesh", ICON_OUTLINER_OB_MESH, "OBJECT_OT_mesh_add", "type");
+       uiItemMenuEnumO(layout, "Curve", ICON_OUTLINER_OB_CURVE, "OBJECT_OT_curve_add", "type");
+       uiItemMenuEnumO(layout, "Surface", ICON_OUTLINER_OB_SURFACE, "OBJECT_OT_surface_add", "type");
        uiItemEnumO(layout, NULL, ICON_OUTLINER_OB_META, "OBJECT_OT_object_add", "type", OB_MBALL);
+       uiItemO(layout, "Text", ICON_OUTLINER_OB_FONT, "OBJECT_OT_text_add");
+       uiItemS(layout);
+       uiItemO(layout, "Armature", ICON_OUTLINER_OB_ARMATURE, "OBJECT_OT_armature_add");
+       uiItemEnumO(layout, NULL, ICON_OUTLINER_OB_LATTICE, "OBJECT_OT_object_add", "type", OB_LATTICE);
+       uiItemEnumO(layout, NULL, ICON_OUTLINER_OB_EMPTY, "OBJECT_OT_object_add", "type", OB_EMPTY);
+       uiItemS(layout);
        uiItemEnumO(layout, NULL, ICON_OUTLINER_OB_CAMERA, "OBJECT_OT_object_add", "type", OB_CAMERA);
        uiItemEnumO(layout, NULL, ICON_OUTLINER_OB_LAMP, "OBJECT_OT_object_add", "type", OB_LAMP);
-       uiItemEnumO(layout, NULL, ICON_OUTLINER_OB_EMPTY, "OBJECT_OT_object_add", "type", OB_EMPTY);
-       uiItemO(layout, NULL, ICON_OUTLINER_OB_ARMATURE, "OBJECT_OT_armature_add");
-       uiItemEnumO(layout, NULL, ICON_OUTLINER_OB_LATTICE, "OBJECT_OT_object_add", "type", OB_LATTICE);
        
        uiPupMenuEnd(C, pup);
        
@@ -706,8 +710,8 @@ void OBJECT_OT_delete(wmOperatorType *ot)
 {
        
        /* identifiers */
-       ot->name= "Delete Objects";
-       ot->description = "Delete the object.";
+       ot->name= "Delete";
+       ot->description = "Delete selected objects.";
        ot->idname= "OBJECT_OT_delete";
        
        /* api callbacks */
@@ -749,7 +753,7 @@ static void copy_object_set_idnew(Scene *scene, View3D *v3d, int dupflag)
        
        /* XXX check object pointers */
        for(base= FIRSTBASE; base; base= base->next) {
-               if(TESTBASELIB(v3d, base)) {
+               if(TESTBASELIB_BGMODE(v3d, base)) {
                        ob= base->object;
                        relink_constraints(&ob->constraints);
                        if (ob->pose){
@@ -1381,34 +1385,36 @@ static EnumPropertyItem prop_clear_parent_types[] = {
 /* note, poll should check for editable scene */
 static int parent_clear_exec(bContext *C, wmOperator *op)
 {
+       int type= RNA_enum_get(op->ptr, "type");
        
        CTX_DATA_BEGIN(C, Object*, ob, selected_editable_objects) {
 
-               if(RNA_enum_is_equal(op->ptr, "type", "CLEAR")) {
+               if(type == 0) {
                        ob->parent= NULL;
                }                       
-               if(RNA_enum_is_equal(op->ptr, "type", "CLEAR_KEEP_TRANSFORM")) {
+               else if(type == 1) {
                        ob->parent= NULL;
                        ob->track= NULL;
                        ED_object_apply_obmat(ob);
                }
-               if(RNA_enum_is_equal(op->ptr, "type", "CLEAR_INVERSE")) {
+               else if(type == 2)
                        Mat4One(ob->parentinv);
-               }
+
                ob->recalc |= OB_RECALC;
        }
        CTX_DATA_END;
        
        DAG_scene_sort(CTX_data_scene(C));
        ED_anim_dag_flush_update(C);
-       
+       WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, NULL);
+
        return OPERATOR_FINISHED;
 }
 
 void OBJECT_OT_parent_clear(wmOperatorType *ot)
 {
        /* identifiers */
-       ot->name= "Clear parent";
+       ot->name= "Clear Parent";
        ot->description = "Clear the object's parenting.";
        ot->idname= "OBJECT_OT_parent_clear";
        
@@ -1436,6 +1442,8 @@ static EnumPropertyItem prop_clear_track_types[] = {
 /* note, poll should check for editable scene */
 static int object_track_clear_exec(bContext *C, wmOperator *op)
 {
+       int type= RNA_enum_get(op->ptr, "type");
+
        if(CTX_data_edit_object(C)) {
                BKE_report(op->reports, RPT_ERROR, "Operation cannot be performed in EditMode");
                return OPERATOR_CANCELLED;
@@ -1444,9 +1452,8 @@ static int object_track_clear_exec(bContext *C, wmOperator *op)
                ob->track= NULL;
                ob->recalc |= OB_RECALC;
                
-               if(RNA_enum_is_equal(op->ptr, "type", "CLEAR_KEEP_TRANSFORM")) {
+               if(type == 1)
                        ED_object_apply_obmat(ob);
-               }                       
        }
        CTX_DATA_END;
 
@@ -1689,6 +1696,308 @@ void OBJECT_OT_select_linked(wmOperatorType *ot)
        RNA_def_enum(ot->srna, "seltype", prop_select_types, 1, "Selection", "Extend selection or clear selection then select");
 
 }
+
+/* ****** selection grouped *******/
+
+static EnumPropertyItem prop_select_grouped_types[] = {
+       {1, "CHILDREN_RECURSIVE", 0, "Children", ""}, // XXX depreceated animation system stuff...
+       {2, "CHILDREN", 0, "Immediate Children", ""},
+       {3, "PARENT", 0, "Parent", ""},
+       {4, "SIBLINGS", 0, "Siblings", "Shared Parent"},
+       {5, "TYPE", 0, "Type", "Shared object type"},
+       {6, "LAYER", 0, "Layer", "Shared layers"},
+       {7, "GROUP", 0, "Group", "Shared group"},
+       {8, "HOOK", 0, "Hook", ""},
+       {9, "PASS", 0, "Pass", "Render pass Index"},
+       {10, "COLOR", 0, "Color", "Object Color"},
+       {11, "PROPERTIES", 0, "Properties", "Game Properties"},
+       {0, NULL, 0, NULL, NULL}
+};
+
+
+static short select_grouped_children(bContext *C, Object *ob, int recursive)
+{
+       short changed = 0;
+
+       CTX_DATA_BEGIN(C, Base*, base, selectable_bases) {
+               if (ob == base->object->parent) {
+                       if (!(base->flag & SELECT)) {
+                               ED_base_object_select(base, BA_SELECT);
+                               changed = 1;
+                       }
+
+                       if (recursive)
+                               changed |= select_grouped_children(C, base->object, 1);
+               }
+       }
+       CTX_DATA_END;
+       return changed;
+}
+
+static short select_grouped_parent(bContext *C)        /* Makes parent active and de-selected OBACT */
+{
+       Scene *scene= CTX_data_scene(C);
+       View3D *v3d= CTX_wm_view3d(C);
+
+       short changed = 0;
+       Base *baspar, *basact= CTX_data_active_base(C);
+
+       if (!basact || !(basact->object->parent)) return 0; /* we know OBACT is valid */
+
+       baspar= object_in_scene(basact->object->parent, scene);
+
+       /* can be NULL if parent in other scene */
+       if(baspar && BASE_SELECTABLE(v3d, baspar)) {
+               ED_base_object_select(basact, BA_DESELECT);
+               ED_base_object_select(baspar, BA_SELECT);
+               ED_base_object_activate(C, baspar);
+               changed = 1;
+       }
+       return changed;
+}
+
+
+#define GROUP_MENU_MAX 24
+static short select_grouped_group(bContext *C, Object *ob)     /* Select objects in the same group as the active */
+{
+       short changed = 0;
+       Group *group, *ob_groups[GROUP_MENU_MAX];
+       //char str[10 + (24*GROUP_MENU_MAX)];
+       //char *p = str;
+       int group_count=0; //, menu, i;
+
+       for (   group=G.main->group.first;
+                       group && group_count < GROUP_MENU_MAX;
+                       group=group->id.next
+               ) {
+               if (object_in_group (ob, group)) {
+                       ob_groups[group_count] = group;
+                       group_count++;
+               }
+       }
+
+       if (!group_count)
+               return 0;
+
+       else if (group_count == 1) {
+               group = ob_groups[0];
+               CTX_DATA_BEGIN(C, Base*, base, visible_bases) {
+                       if (!(base->flag & SELECT) && object_in_group(base->object, group)) {
+                               ED_base_object_select(base, BA_SELECT);
+                               changed = 1;
+                       }
+               }
+               CTX_DATA_END;
+               return changed;
+       }
+#if 0 // XXX hows this work in 2.5?
+       /* build the menu. */
+       p += sprintf(str, "Groups%%t");
+       for (i=0; i<group_count; i++) {
+               group = ob_groups[i];
+               p += sprintf (p, "|%s%%x%i", group->id.name+2, i);
+       }
+
+       menu = pupmenu (str);
+       if (menu == -1)
+               return 0;
+
+       group = ob_groups[menu];
+       for (base= FIRSTBASE; base; base= base->next) {
+               if (!(base->flag & SELECT) && object_in_group(base->object, group)) {
+                       ED_base_object_select(base, BA_SELECT);
+                       changed = 1;
+               }
+       }
+#endif
+       return changed;
+}
+
+static short select_grouped_object_hooks(bContext *C, Object *ob)
+{
+       Scene *scene= CTX_data_scene(C);
+       View3D *v3d= CTX_wm_view3d(C);
+
+       short changed = 0;
+       Base *base;
+       ModifierData *md;
+       HookModifierData *hmd;
+
+       for (md = ob->modifiers.first; md; md=md->next) {
+               if (md->type==eModifierType_Hook) {
+                       hmd= (HookModifierData*) md;
+                       if (hmd->object && !(hmd->object->flag & SELECT)) {
+                               base= object_in_scene(hmd->object, scene);
+                               if (base && (BASE_SELECTABLE(v3d, base))) {
+                                       ED_base_object_select(base, BA_SELECT);
+                                       changed = 1;
+                               }
+                       }
+               }
+       }
+       return changed;
+}
+
+/* Select objects woth the same parent as the active (siblings),
+ * parent can be NULL also */
+static short select_grouped_siblings(bContext *C, Object *ob)
+{
+       short changed = 0;
+
+       CTX_DATA_BEGIN(C, Base*, base, selectable_bases) {
+               if ((base->object->parent==ob->parent)  && !(base->flag & SELECT)) {
+                       ED_base_object_select(base, BA_SELECT);
+                       changed = 1;
+               }
+       }
+       CTX_DATA_END;
+       return changed;
+}
+
+static short select_grouped_type(bContext *C, Object *ob)
+{
+       short changed = 0;
+
+       CTX_DATA_BEGIN(C, Base*, base, selectable_bases) {
+               if ((base->object->type == ob->type) && !(base->flag & SELECT)) {
+                       ED_base_object_select(base, BA_SELECT);
+                       changed = 1;
+               }
+       }
+       CTX_DATA_END;
+       return changed;
+}
+
+static short select_grouped_layer(bContext *C, Object *ob)
+{
+       char changed = 0;
+
+       CTX_DATA_BEGIN(C, Base*, base, selectable_bases) {
+               if ((base->lay & ob->lay) && !(base->flag & SELECT)) {
+                       ED_base_object_select(base, BA_SELECT);
+                       changed = 1;
+               }
+       }
+       CTX_DATA_END;
+       return changed;
+}
+
+static short select_grouped_index_object(bContext *C, Object *ob)
+{
+       char changed = 0;
+
+       CTX_DATA_BEGIN(C, Base*, base, selectable_bases) {
+               if ((base->object->index == ob->index) && !(base->flag & SELECT)) {
+                       ED_base_object_select(base, BA_SELECT);
+                       changed = 1;
+               }
+       }
+       CTX_DATA_END;
+       return changed;
+}
+
+static short select_grouped_color(bContext *C, Object *ob)
+{
+       char changed = 0;
+
+       CTX_DATA_BEGIN(C, Base*, base, selectable_bases) {
+               if (!(base->flag & SELECT) && (FloatCompare(base->object->col, ob->col, 0.005f))) {
+                       ED_base_object_select(base, BA_SELECT);
+                       changed = 1;
+               }
+       }
+       CTX_DATA_END;
+       return changed;
+}
+
+static short objects_share_gameprop(Object *a, Object *b)
+{
+       bProperty *prop;
+       /*make a copy of all its properties*/
+
+       for( prop= a->prop.first; prop; prop = prop->next ) {
+               if ( get_ob_property(b, prop->name) )
+                       return 1;
+       }
+       return 0;
+}
+
+static short select_grouped_gameprops(bContext *C, Object *ob)
+{
+       char changed = 0;
+
+       CTX_DATA_BEGIN(C, Base*, base, selectable_bases) {
+               if (!(base->flag & SELECT) && (objects_share_gameprop(base->object, ob))) {
+                       ED_base_object_select(base, BA_SELECT);
+                       changed = 1;
+               }
+       }
+       CTX_DATA_END;
+       return changed;
+}
+
+static int object_select_grouped_exec(bContext *C, wmOperator *op)
+{
+       Scene *scene= CTX_data_scene(C);
+       Object *ob;
+       int nr = RNA_enum_get(op->ptr, "type");
+       short changed = 0, seltype;
+
+       seltype = RNA_enum_get(op->ptr, "seltype");
+       
+       if (seltype == 0) {
+               CTX_DATA_BEGIN(C, Base*, base, visible_bases) {
+                       ED_base_object_select(base, BA_DESELECT);
+               }
+               CTX_DATA_END;
+       }
+       
+       ob= OBACT;
+       if(ob==0){ 
+               BKE_report(op->reports, RPT_ERROR, "No Active Object");
+               return OPERATOR_CANCELLED;
+       }
+       
+       if(nr==1)               changed = select_grouped_children(C, ob, 1);
+       else if(nr==2)  changed = select_grouped_children(C, ob, 0);
+       else if(nr==3)  changed = select_grouped_parent(C);
+       else if(nr==4)  changed = select_grouped_siblings(C, ob);
+       else if(nr==5)  changed = select_grouped_type(C, ob);
+       else if(nr==6)  changed = select_grouped_layer(C, ob);
+       else if(nr==7)  changed = select_grouped_group(C, ob);
+       else if(nr==8)  changed = select_grouped_object_hooks(C, ob);
+       else if(nr==9)  changed = select_grouped_index_object(C, ob);
+       else if(nr==10) changed = select_grouped_color(C, ob);
+       else if(nr==11) changed = select_grouped_gameprops(C, ob);
+       
+       if (changed) {
+               WM_event_add_notifier(C, NC_SCENE|ND_OB_SELECT, CTX_data_scene(C));
+               return OPERATOR_FINISHED;
+       }
+       
+       return OPERATOR_CANCELLED;
+}
+
+void OBJECT_OT_select_grouped(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name= "Select Grouped";
+       ot->description = "Select all visible objects grouped by various properties.";
+       ot->idname= "OBJECT_OT_select_grouped";
+       
+       /* api callbacks */
+       ot->invoke= WM_menu_invoke;
+       ot->exec= object_select_grouped_exec;
+       ot->poll= ED_operator_scene_editable;
+       
+       /* flags */
+       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+       
+       RNA_def_enum(ot->srna, "type", prop_select_grouped_types, 0, "Type", "");
+       RNA_def_enum(ot->srna, "seltype", prop_select_types, 1, "Selection", "Extend selection or clear selection then select");
+
+}
+
 /* ****** selection by layer *******/
 
 static int object_select_by_layer_exec(bContext *C, wmOperator *op)
@@ -1738,7 +2047,7 @@ void OBJECT_OT_select_by_layer(wmOperatorType *ot)
 }
 
 /* ****** invert selection *******/
-static int object_select_invert_exec(bContext *C, wmOperator *op)
+static int object_select_inverse_exec(bContext *C, wmOperator *op)
 {
        CTX_DATA_BEGIN(C, Base*, base, visible_bases) {
                if (base->flag & SELECT)
@@ -1754,16 +2063,16 @@ static int object_select_invert_exec(bContext *C, wmOperator *op)
        return OPERATOR_FINISHED;
 }
 
-void OBJECT_OT_select_invert(wmOperatorType *ot)
+void OBJECT_OT_select_inverse(wmOperatorType *ot)
 {
        
        /* identifiers */
-       ot->name= "Invert selection";
-       ot->description = "Invert th select of all visible objects.";
-       ot->idname= "OBJECT_OT_select_invert";
+       ot->name= "Select Inverse";
+       ot->description = "Invert selection of all visible objects.";
+       ot->idname= "OBJECT_OT_select_inverse";
        
        /* api callbacks */
-       ot->exec= object_select_invert_exec;
+       ot->exec= object_select_inverse_exec;
        ot->poll= ED_operator_scene_editable;
        
        /* flags */
@@ -1896,7 +2205,7 @@ void OBJECT_OT_location_clear(wmOperatorType *ot)
 {
        
        /* identifiers */
-       ot->name= "Clear Object Location";
+       ot->name= "Clear Location";
        ot->description = "Clear the object's location.";
        ot->idname= "OBJECT_OT_location_clear";
        
@@ -1940,7 +2249,7 @@ void OBJECT_OT_rotation_clear(wmOperatorType *ot)
 {
        
        /* identifiers */
-       ot->name= "Clear Object Rotation";
+       ot->name= "Clear Rotation";
        ot->description = "Clear the object's rotation.";
        ot->idname= "OBJECT_OT_rotation_clear";
        
@@ -1988,7 +2297,7 @@ void OBJECT_OT_scale_clear(wmOperatorType *ot)
 {
        
        /* identifiers */
-       ot->name= "Clear Object Scale";
+       ot->name= "Clear Scale";
        ot->description = "Clear the object's scale.";
        ot->idname= "OBJECT_OT_scale_clear";
        
@@ -2034,7 +2343,7 @@ void OBJECT_OT_origin_clear(wmOperatorType *ot)
 {
 
        /* identifiers */
-       ot->name= "Clear Object Origin";
+       ot->name= "Clear Origin";
        ot->description = "Clear the object's origin.";
        ot->idname= "OBJECT_OT_origin_clear";
        
@@ -2077,12 +2386,11 @@ void OBJECT_OT_restrictview_clear(wmOperatorType *ot)
 {
        
        /* identifiers */
-       ot->name= "Clear restrict view";
+       ot->name= "Clear Restrict View";
        ot->description = "Reveal the object by setting the restrictview flag.";
        ot->idname= "OBJECT_OT_restrictview_clear";
        
        /* api callbacks */
-       ot->invoke= WM_operator_confirm;
        ot->exec= object_restrictview_clear_exec;
        ot->poll= ED_operator_view3d_active;
        
@@ -2090,19 +2398,14 @@ void OBJECT_OT_restrictview_clear(wmOperatorType *ot)
        ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 }
 
-static EnumPropertyItem prop_set_restrictview_types[] = {
-       {0, "SELECTED", 0, "Selected", ""},
-       {1, "UNSELECTED", 0, "Unselected ", ""},
-       {0, NULL, 0, NULL, NULL}
-};
-
 static int object_restrictview_set_exec(bContext *C, wmOperator *op)
 {
        Scene *scene= CTX_data_scene(C);
        short changed = 0;
+       int unselected= RNA_boolean_get(op->ptr, "unselected");
        
        CTX_DATA_BEGIN(C, Base*, base, visible_bases) {
-               if(RNA_enum_is_equal(op->ptr, "type", "SELECTED")){
+               if(!unselected) {
                        if (base->flag & SELECT){
                                base->flag &= ~SELECT;
                                base->object->flag = base->flag;
@@ -2113,7 +2416,7 @@ static int object_restrictview_set_exec(bContext *C, wmOperator *op)
                                }
                        }
                }
-               else if (RNA_enum_is_equal(op->ptr, "type", "UNSELECTED")){
+               else {
                        if (!(base->flag & SELECT)){
                                base->object->restrictflag |= OB_RESTRICT_VIEW;
                                changed = 1;
@@ -2135,19 +2438,18 @@ static int object_restrictview_set_exec(bContext *C, wmOperator *op)
 void OBJECT_OT_restrictview_set(wmOperatorType *ot)
 {
        /* identifiers */
-       ot->name= "Set restrict view";
+       ot->name= "Set Restrict View";
        ot->description = "Hide the object by setting the restrictview flag.";
        ot->idname= "OBJECT_OT_restrictview_set";
        
        /* api callbacks */
-       ot->invoke= WM_menu_invoke;
        ot->exec= object_restrictview_set_exec;
        ot->poll= ED_operator_view3d_active;
        
        /* flags */
        ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
        
-       RNA_def_enum(ot->srna, "type", prop_set_restrictview_types, 0, "Type", "");
+       RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected objects.");
        
 }
 /* ************* Slow Parent ******************* */
@@ -2490,6 +2792,22 @@ static int test_parent_loop(Object *par, Object *ob)
        return test_parent_loop(par->parent, ob);
 }
 
+void ED_object_parent(Object *ob, Object *par, int type, const char *substr)
+{
+       if(!par || test_parent_loop(par, ob)) {
+               ob->parent= NULL;
+               ob->partype= PAROBJECT;
+               ob->parsubstr[0]= 0;
+               return;
+       }
+
+       /* this could use some more checks */
+
+       ob->parent= par;
+       ob->partype &= ~PARTYPE;
+       ob->partype |= type;
+       BLI_strncpy(ob->parsubstr, substr, sizeof(ob->parsubstr));
+}
 
 static int parent_set_exec(bContext *C, wmOperator *op)
 {
@@ -2606,7 +2924,8 @@ static int parent_set_exec(bContext *C, wmOperator *op)
        CTX_DATA_END;
        
        DAG_scene_sort(CTX_data_scene(C));
-       ED_anim_dag_flush_update(C);    
+       ED_anim_dag_flush_update(C);
+       WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, NULL);
        
        return OPERATOR_FINISHED;
 }
@@ -2643,7 +2962,7 @@ static int parent_set_invoke(bContext *C, wmOperator *op, wmEvent *event)
 void OBJECT_OT_parent_set(wmOperatorType *ot)
 {
        /* identifiers */
-       ot->name= "Make parent";
+       ot->name= "Make Parent";
        ot->description = "Set the object's parenting.";
        ot->idname= "OBJECT_OT_parent_set";
        
@@ -2654,7 +2973,7 @@ void OBJECT_OT_parent_set(wmOperatorType *ot)
        ot->poll= ED_operator_object_active;
        
        /* flags */
-       ot->flag= 0;
+       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
        
        RNA_def_enum(ot->srna, "type", prop_make_parent_types, 0, "Type", "");
 }
@@ -2670,8 +2989,9 @@ static EnumPropertyItem prop_make_track_types[] = {
 static int track_set_exec(bContext *C, wmOperator *op)
 {
        Scene *scene= CTX_data_scene(C);
+       int type= RNA_enum_get(op->ptr, "type");
                
-       if(RNA_enum_is_equal(op->ptr, "type", "TRACKTO")){
+       if(type == 1) {
                bConstraint *con;
                bTrackToConstraint *data;
 
@@ -2695,7 +3015,7 @@ static int track_set_exec(bContext *C, wmOperator *op)
                }
                CTX_DATA_END;
        }
-       else if(RNA_enum_is_equal(op->ptr, "type", "LOCKTRACK")){
+       else if(type == 2) {
                bConstraint *con;
                bLockTrackConstraint *data;
 
@@ -2719,7 +3039,7 @@ static int track_set_exec(bContext *C, wmOperator *op)
                }
                CTX_DATA_END;
        }
-       else if(RNA_enum_is_equal(op->ptr, "type", "OLDTRACK")){
+       else {
                CTX_DATA_BEGIN(C, Base*, base, selected_editable_bases) {
                        if(base!=BASACT) {
                                base->object->track= BASACT->object;
@@ -2797,7 +3117,7 @@ static void make_object_duplilist_real(Scene *scene, View3D *v3d, Base *base)
 }
 
 
-static int object_dupli_set_real_exec(bContext *C, wmOperator *op)
+static int object_duplicates_make_real_exec(bContext *C, wmOperator *op)
 {
        Scene *scene= CTX_data_scene(C);
        ScrArea *sa= CTX_wm_area(C);
@@ -2817,17 +3137,17 @@ static int object_dupli_set_real_exec(bContext *C, wmOperator *op)
        return OPERATOR_FINISHED;
 }
 
-void OBJECT_OT_dupli_set_real(wmOperatorType *ot)
+void OBJECT_OT_duplicates_make_real(wmOperatorType *ot)
 {
        
        /* identifiers */
-       ot->name= "Make Dupli Real";
+       ot->name= "Make Duplicates Real";
        ot->description = "Make dupli objects attached to this object real.";
-       ot->idname= "OBJECT_OT_dupli_set_real";
+       ot->idname= "OBJECT_OT_duplicates_make_real";
        
        /* api callbacks */
        ot->invoke= WM_operator_confirm;
-       ot->exec= object_dupli_set_real_exec;
+       ot->exec= object_duplicates_make_real_exec;
        
        ot->poll= ED_operator_scene_editable;
        
@@ -3212,7 +3532,11 @@ void ED_object_exit_editmode(bContext *C, int flag)
                }
                load_editMesh(scene, obedit);
                
-               if(freedata) free_editMesh(me->edit_mesh);
+               if(freedata) {
+                       free_editMesh(me->edit_mesh);
+                       MEM_freeN(me->edit_mesh);
+                       me->edit_mesh= NULL;
+               }
                
                if(G.f & G_WEIGHTPAINT)
                        mesh_octree_table(obedit, NULL, NULL, 'e');
@@ -3261,7 +3585,7 @@ void ED_object_enter_editmode(bContext *C, int flag)
 {
        Scene *scene= CTX_data_scene(C);
        Base *base= CTX_data_active_base(C);
-       Object *ob= base->object;
+       Object *ob;
        ScrArea *sa= CTX_wm_area(C);
        View3D *v3d= NULL;
        int ok= 0;
@@ -3273,7 +3597,10 @@ void ED_object_enter_editmode(bContext *C, int flag)
                v3d= sa->spacedata.first;
        
        if((v3d==NULL || (base->lay & v3d->lay))==0) return;
-       
+
+       ob = base->object;
+
+       if(ob==NULL) return;
        if(ob->data==NULL) return;
        
        if (object_data_is_libdata(ob)) {
@@ -3355,6 +3682,7 @@ void ED_object_enter_editmode(bContext *C, int flag)
                WM_event_add_notifier(C, NC_SCENE|ND_MODE|NS_MODE_OBJECT, scene);
        }
        
+       if(flag & EM_DO_UNDO) ED_undo_push(C, "Enter Editmode");
        if(flag & EM_WAITCURSOR) waitcursor(0);
 }
 
@@ -3578,9 +3906,7 @@ void special_editmenu(Scene *scene, View3D *v3d)
 // XXX static short numcuts= 2;
        Object *ob= OBACT;
        Object *obedit= NULL; // XXX
-       float fac;
        int nr,ret=0;
-       short randfac;
        
        if(ob==NULL) return;
        
@@ -3667,7 +3993,7 @@ void special_editmenu(Scene *scene, View3D *v3d)
                        if(!psys)
                                return;
 
-                       if(scene->selectmode & SCE_SELECT_POINT)
+                       if(pset->selectmode & SCE_SELECT_POINT)
                                nr= pupmenu("Specials%t|Rekey%x1|Subdivide%x2|Select First%x3|Select Last%x4|Remove Doubles%x5");
                        else
                                nr= pupmenu("Specials%t|Rekey%x1|Remove Doubles%x5");
@@ -3769,144 +4095,8 @@ void special_editmenu(Scene *scene, View3D *v3d)
                }
        }
        else if(obedit->type==OB_MESH) {
-               /* This is all that is needed, since all other functionality is in Ctrl+ V/E/F but some users didnt like, so for now have the old/big menu */
-               /*
-               nr= pupmenu("Subdivide Mesh%t|Subdivide%x1|Subdivide Multi%x2|Subdivide Multi Fractal%x3|Subdivide Smooth%x4");
-               switch(nr) {
-               case 1:
-                       waitcursor(1);
-                       esubdivideflag(1, 0.0, scene->toolsettings->editbutflag, 1, 0);
-                       
-                       break;
-               case 2:
-                       if(button(&numcuts, 1, 128, "Number of Cuts:")==0) return;
-                       waitcursor(1);
-                       esubdivideflag(1, 0.0, scene->toolsettings->editbutflag, numcuts, 0);
-                       break;
-               case 3:
-                       if(button(&numcuts, 1, 128, "Number of Cuts:")==0) return;
-                       randfac= 10;
-                       if(button(&randfac, 1, 100, "Rand fac:")==0) return;
-                       waitcursor(1);                  
-                       fac= -( (float)randfac )/100;
-                       esubdivideflag(1, fac, scene->toolsettings->editbutflag, numcuts, 0);
-                       break;
-                       
-               case 4:
-                       fac= 1.0f;
-                       if(fbutton(&fac, 0.0f, 5.0f, 10, 10, "Smooth:")==0) return;
-                               fac= 0.292f*fac;
-                       
-                       waitcursor(1);
-                       esubdivideflag(1, fac, scene->toolsettings->editbutflag | B_SMOOTH, 1, 0);
-                       break;          
-               }
-               */
-               
-               nr= pupmenu("Specials%t|Subdivide%x1|Subdivide Multi%x2|Subdivide Multi Fractal%x3|Subdivide Smooth%x12|Merge%x4|Remove Doubles%x5|Hide%x6|Reveal%x7|Select Swap%x8|Flip Normals %x9|Smooth %x10|Bevel %x11|Set Smooth %x14|Set Solid %x15|Blend From Shape%x16|Propagate To All Shapes%x17|Select Vertex Path%x18");
-               
-               switch(nr) {
-               case 1:
-                       waitcursor(1);
-// XXX                 esubdivideflag(1, 0.0, scene->toolsettings->editbutflag, 1, 0);
-                       
-                       break;
-               case 2:
-// XXX                 if(button(&numcuts, 1, 128, "Number of Cuts:")==0) return;
-                       waitcursor(1);
-// XXX                 esubdivideflag(1, 0.0, scene->toolsettings->editbutflag, numcuts, 0);
-                       break;
-               case 3:
-// XXX                 if(button(&numcuts, 1, 128, "Number of Cuts:")==0) return;
-                       randfac= 10;
-// XXX                 if(button(&randfac, 1, 100, "Rand fac:")==0) return;
-                       waitcursor(1);                  
-                       fac= -( (float)randfac )/100;
-// XXX                 esubdivideflag(1, fac, scene->toolsettings->editbutflag, numcuts, 0);
-                       break;
-                       
-               case 12:        /* smooth */
-                       /* if(button(&numcuts, 1, 128, "Number of Cuts:")==0) return; */
-                       fac= 1.0f;
-// XXX                 if(fbutton(&fac, 0.0f, 5.0f, 10, 10, "Smooth:")==0) return;
-// XXX                         fac= 0.292f*fac;
-                       
-                       waitcursor(1);
-// XXX                 esubdivideflag(1, fac, scene->toolsettings->editbutflag | B_SMOOTH, 1, 0);
-                       break;          
-
-               case 4:
-// XXX                 mergemenu();
-                       break;
-               case 5:
-// XXX                 notice("Removed %d Vertices", removedoublesflag(1, 0, scene->toolsettings->doublimit));
-                       break;
-               case 6:
-// XXX                 hide_mesh(0);
-                       break;
-               case 7:
-// XXX                 reveal_mesh();
-                       break;
-               case 8:
-// XXX                 selectswap_mesh();
-                       break;
-               case 9:
-// XXX                 flip_editnormals();
-                       break;
-               case 10:
-// XXX                 vertexsmooth();
-                       break;
-               case 11:
-// XXX                 bevel_menu();
-                       break;
-               case 14:
-// XXX                 mesh_set_smooth_faces(1);
-                       break;
-               case 15: 
-// XXX                 mesh_set_smooth_faces(0);
-                       break;
-               case 16: 
-// XXX                 shape_copy_select_from();
-                       break;
-               case 17: 
-// XXX                 shape_propagate();
-                       break;
-               case 18:
-// XXX                 pathselect();
-                       break;
-               }
-               
-               
-               DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
-               
-               if(nr>0) waitcursor(0);
-               
        }
        else if(ELEM(obedit->type, OB_CURVE, OB_SURF)) {
-
-               nr= pupmenu("Specials%t|Subdivide%x1|Switch Direction%x2|Set Goal Weight%x3|Set Radius%x4|Smooth%x5|Smooth Radius%x6");
-               
-               switch(nr) {
-               case 1:
-// XXX                 subdivideNurb();
-                       break;
-               case 2:
-// XXX                 switchdirectionNurb2();
-                       break;
-               case 3:
-// XXX                 setweightNurb();
-                       break;
-               case 4:
-// XXX                 setradiusNurb();
-                       break;
-               case 5:
-// XXX                 smoothNurb();
-                       break;
-               case 6:
-// XXX                 smoothradiusNurb();
-                       break;
-               }
-               DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
        }
        else if(obedit->type==OB_ARMATURE) {
                nr= pupmenu("Specials%t|Subdivide %x1|Subdivide Multi%x2|Switch Direction%x7|Flip Left-Right Names%x3|%l|AutoName Left-Right%x4|AutoName Front-Back%x5|AutoName Top-Bottom%x6");
@@ -6092,11 +6282,12 @@ Base *ED_object_add_duplicate(Scene *scene, Base *base, int usedupflag)
 }
 
 /* contextual operator dupli */
-static int duplicate_add_exec(bContext *C, wmOperator *op)
+static int duplicate_exec(bContext *C, wmOperator *op)
 {
        Scene *scene= CTX_data_scene(C);
        View3D *v3d= CTX_wm_view3d(C);
-       int dupflag= U.dupflag;
+       int linked= RNA_boolean_get(op->ptr, "linked");
+       int dupflag= (linked)? 0: U.dupflag;
        
        clear_id_newpoins();
        clear_sca_new_poins();  /* sensor/contr/act */
@@ -6124,9 +6315,9 @@ static int duplicate_add_exec(bContext *C, wmOperator *op)
        return OPERATOR_FINISHED;
 }
 
-static int duplicate_add_invoke(bContext *C, wmOperator *op, wmEvent *event)
+static int duplicate_invoke(bContext *C, wmOperator *op, wmEvent *event)
 {
-       duplicate_add_exec(C, op);
+       duplicate_exec(C, op);
        
        RNA_int_set(op->ptr, "mode", TFM_TRANSLATION);
        WM_operator_name_call(C, "TFM_OT_transform", WM_OP_INVOKE_REGION_WIN, op->ptr);
@@ -6134,17 +6325,17 @@ static int duplicate_add_invoke(bContext *C, wmOperator *op, wmEvent *event)
        return OPERATOR_FINISHED;
 }
 
-void OBJECT_OT_duplicate_add(wmOperatorType *ot)
+void OBJECT_OT_duplicate(wmOperatorType *ot)
 {
        
        /* identifiers */
-       ot->name= "Add Duplicate";
-       ot->description = "Duplicate the object.";
-       ot->idname= "OBJECT_OT_duplicate_add";
+       ot->name= "Duplicate";
+       ot->description = "Duplicate selected objects.";
+       ot->idname= "OBJECT_OT_duplicate";
        
        /* api callbacks */
-       ot->invoke= duplicate_add_invoke;
-       ot->exec= duplicate_add_exec;
+       ot->invoke= duplicate_invoke;
+       ot->exec= duplicate_exec;
        
        ot->poll= ED_operator_scene_editable;
        
@@ -6152,9 +6343,127 @@ void OBJECT_OT_duplicate_add(wmOperatorType *ot)
        ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
        
        /* to give to transform */
+       RNA_def_boolean(ot->srna, "linked", 0, "Linked", "Duplicate object but not object data, linking to the original data.");
        RNA_def_int(ot->srna, "mode", TFM_TRANSLATION, 0, INT_MAX, "Mode", "", 0, INT_MAX);
 }
 
+/* ************************** JOIN *********************** */
+
+static int join_exec(bContext *C, wmOperator *op)
+{
+       Scene *scene= CTX_data_scene(C);
+       Object *ob= CTX_data_active_object(C);
+
+       if(scene->obedit) {
+               BKE_report(op->reports, RPT_ERROR, "This data does not support joining in editmode.");
+               return OPERATOR_CANCELLED;
+       }
+       else if(!ob) {
+               BKE_report(op->reports, RPT_ERROR, "Can't join unless there is an active object.");
+               return OPERATOR_CANCELLED;
+       }
+       else if(object_data_is_libdata(ob)) {
+               BKE_report(op->reports, RPT_ERROR, "Can't edit external libdata.");
+               return OPERATOR_CANCELLED;
+       }
+
+       if(ob->type == OB_MESH)
+               return join_mesh_exec(C, op);
+       else if(ELEM(ob->type, OB_CURVE, OB_SURF))
+               return join_curve_exec(C, op);
+       else if(ob->type == OB_ARMATURE)
+               return join_armature_exec(C, op);
+
+       BKE_report(op->reports, RPT_ERROR, "This object type doesn't support joining.");
+
+       return OPERATOR_CANCELLED;
+}
+
+void OBJECT_OT_join(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name= "Join";
+       ot->description = "Join selected objects into active object.";
+       ot->idname= "OBJECT_OT_join";
+       
+       /* api callbacks */
+       ot->exec= join_exec;
+       ot->poll= ED_operator_scene_editable;
+       
+       /* flags */
+       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/********************** Smooth/Flat *********************/
+
+static int shade_smooth_exec(bContext *C, wmOperator *op)
+{
+       Scene *scene= CTX_data_scene(C);
+       Object *ob;
+       Curve *cu;
+       Nurb *nu;
+       int clear= (strcmp(op->idname, "OBJECT_OT_shade_flat") == 0);
+       int done= 0;
+
+       CTX_DATA_BEGIN(C, Base*, base, selected_editable_bases) {
+               ob= base->object;
+
+               if(ob->type==OB_MESH) {
+                       mesh_set_smooth_flag(ob, !clear);
+
+                       DAG_object_flush_update(scene, ob, OB_RECALC_DATA);
+                       WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
+
+                       done= 1;
+               }
+               else if ELEM(ob->type, OB_SURF, OB_CURVE) {
+                       cu= ob->data;
+
+                       for(nu=cu->nurb.first; nu; nu=nu->next) {
+                               if(!clear) nu->flag |= ME_SMOOTH;
+                               else nu->flag &= ~ME_SMOOTH;
+                               nu= nu->next;
+                       }
+
+                       DAG_object_flush_update(scene, ob, OB_RECALC_DATA);
+                       WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
+
+                       done= 1;
+               }
+       }
+       CTX_DATA_END;
+
+       return (done)? OPERATOR_FINISHED: OPERATOR_CANCELLED;
+}
+
+void OBJECT_OT_shade_flat(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name= "Shade Flat";
+       ot->idname= "OBJECT_OT_shade_flat";
+       
+       /* api callbacks */
+       ot->exec= shade_smooth_exec;
+
+       /* flags */
+       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+void OBJECT_OT_shade_smooth(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name= "Shade Smooth";
+       ot->idname= "OBJECT_OT_shade_smooth";
+       
+       /* api callbacks */
+       ot->exec= shade_smooth_exec;
+
+       /* flags */
+       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+
+
 /* ********************** */
 
 void image_aspect(Scene *scene, View3D *v3d)