Merge branch 'master' into blender2.8
authorCampbell Barton <ideasman42@gmail.com>
Tue, 26 Jun 2018 20:56:39 +0000 (22:56 +0200)
committerCampbell Barton <ideasman42@gmail.com>
Tue, 26 Jun 2018 20:56:39 +0000 (22:56 +0200)
26 files changed:
1  2 
doc/python_api/examples/gpu.offscreen.1.py
release/scripts/startup/bl_operators/add_mesh_torus.py
release/scripts/startup/bl_operators/clip.py
release/scripts/startup/bl_operators/file.py
release/scripts/startup/bl_operators/freestyle.py
release/scripts/startup/bl_operators/mesh.py
release/scripts/startup/bl_operators/object.py
release/scripts/startup/bl_operators/object_quick_effects.py
release/scripts/startup/bl_operators/presets.py
release/scripts/startup/bl_operators/rigidbody.py
release/scripts/startup/bl_operators/uvcalc_follow_active.py
release/scripts/startup/bl_operators/uvcalc_lightmap.py
release/scripts/startup/bl_operators/uvcalc_smart_project.py
release/scripts/startup/bl_operators/wm.py
release/scripts/startup/bl_ui/space_dopesheet.py
release/scripts/startup/bl_ui/space_graph.py
release/scripts/startup/bl_ui/space_nla.py
release/scripts/startup/bl_ui/space_time.py
release/scripts/templates_py/batch_export.py
release/scripts/templates_py/manipulator_operator.py
release/scripts/templates_py/manipulator_operator_target.py
release/scripts/templates_py/manipulator_simple.py
release/scripts/templates_py/operator_modal_view3d.py
source/blender/editors/mesh/editmesh_bisect.c
source/blender/editors/mesh/editmesh_tools.c
source/blender/windowmanager/intern/wm_operator_props.c

index ff1a7ad7cced21c34b19b4eadc731171057fac8d,b4eb7452ca1baac0d26b9d88f1f6852d0f618f1b..bb34603bd23ab586628bb9e92ee699bad6ab52f8
@@@ -58,20 -57,19 +58,20 @@@ class OffScreenDraw(bpy.types.Operator)
  
          modelview_matrix = camera.matrix_world.inverted()
          projection_matrix = camera.calc_matrix_camera(
-                 render.resolution_x,
-                 render.resolution_y,
-                 render.pixel_aspect_x,
-                 render.pixel_aspect_y,
-                 )
+             render.resolution_x,
+             render.resolution_y,
+             render.pixel_aspect_x,
+             render.pixel_aspect_y,
+         )
  
          offscreen.draw_view3d(
-                 scene,
-                 render_layer,
-                 context.space_data,
-                 context.region,
-                 projection_matrix,
-                 modelview_matrix,
-                 )
+             scene,
++            render_layer,
+             context.space_data,
+             context.region,
+             projection_matrix,
+             modelview_matrix,
+         )
  
      @staticmethod
      def _opengl_draw(context, texture, aspect_ratio, scale):
index 8d947d19a82363ad7b6bd9d0b5d838993b4d02a2,5bb97ff298a32db74bacc632463a8deacfa52a82..cf03b4fccdb4d4429eeed3b667da552c7c2a7263
@@@ -61,36 -61,36 +61,36 @@@ class WM_OT_previews_batch_generate(Ope
      # -----------
      # Own props.
      use_scenes = BoolProperty(
-             default=True,
-             name="Scenes",
-             description="Generate scenes' previews",
-             )
+         default=True,
+         name="Scenes",
+         description="Generate scenes' previews",
+     )
 -    use_groups = BoolProperty(
 +    use_collections = BoolProperty(
-             default=True,
-             name="Collections",
-             description="Generate collections' previews",
-             )
+         default=True,
 -        name="Groups",
 -        description="Generate groups' previews",
++        name="Collections",
++        description="Generate collections' previews",
+     )
      use_objects = BoolProperty(
-             default=True,
-             name="Objects",
-             description="Generate objects' previews",
-             )
+         default=True,
+         name="Objects",
+         description="Generate objects' previews",
+     )
      use_intern_data = BoolProperty(
-             default=True,
-             name="Mat/Tex/...",
-             description="Generate 'internal' previews (materials, textures, images, etc.)",
-             )
+         default=True,
+         name="Mat/Tex/...",
+         description="Generate 'internal' previews (materials, textures, images, etc.)",
+     )
  
      use_trusted = BoolProperty(
-             default=False,
-             name="Trusted Blend Files",
-             description="Enable python evaluation for selected files",
-             )
+         default=False,
+         name="Trusted Blend Files",
+         description="Enable python evaluation for selected files",
+     )
      use_backups = BoolProperty(
-             default=True,
-             name="Save Backups",
-             description="Keep a backup (.blend1) version of the files when saving with generated previews",
-             )
+         default=True,
+         name="Save Backups",
+         description="Keep a backup (.blend1) version of the files when saving with generated previews",
+     )
  
      def invoke(self, context, event):
          context.window_manager.fileselect_add(self)
@@@ -171,35 -171,35 +171,36 @@@ class WM_OT_previews_batch_clear(Operat
      # -----------
      # Own props.
      use_scenes = BoolProperty(
-             default=True,
-             name="Scenes",
-             description="Clear scenes' previews",
-             )
-     use_collections = BoolProperty(default=True,
-             name="Collections",
-             description="Clear collections' previews",
-             )
+         default=True,
+         name="Scenes",
+         description="Clear scenes' previews",
+     )
 -    use_groups = BoolProperty(default=True,
 -                              name="Groups",
 -                              description="Clear groups' previews",
 -                              )
++    use_collections = BoolProperty(
++        default=True,
++        name="Collections",
++        description="Clear collections' previews",
++    )
      use_objects = BoolProperty(
-             default=True,
-             name="Objects",
-             description="Clear objects' previews",
-             )
+         default=True,
+         name="Objects",
+         description="Clear objects' previews",
+     )
      use_intern_data = BoolProperty(
-             default=True,
-             name="Mat/Tex/...",
-             description="Clear 'internal' previews (materials, textures, images, etc.)",
-             )
+         default=True,
+         name="Mat/Tex/...",
+         description="Clear 'internal' previews (materials, textures, images, etc.)",
+     )
  
      use_trusted = BoolProperty(
-             default=False,
-             name="Trusted Blend Files",
-             description="Enable python evaluation for selected files",
-             )
+         default=False,
+         name="Trusted Blend Files",
+         description="Enable python evaluation for selected files",
+     )
      use_backups = BoolProperty(
-             default=True,
-             name="Save Backups",
-             description="Keep a backup (.blend1) version of the files when saving with cleared previews",
-             )
+         default=True,
+         name="Save Backups",
+         description="Keep a backup (.blend1) version of the files when saving with cleared previews",
+     )
  
      def invoke(self, context, event):
          context.window_manager.fileselect_add(self)
index 70334fb0f23d89a43ba6af60e44df3f6ffe66021,c2592f13d66742787b8beeebf3ba0b48905d7ff1..f087022b6fd18f1a0d3abc42a3acdaf5748e4d85
@@@ -364,38 -369,88 +369,38 @@@ class QuickSmoke(Operator)
  
          # Setup material
  
 -        # Cycles
 -        if context.scene.render.use_shading_nodes:
 -            bpy.ops.object.material_slot_add()
 -
 -            mat = bpy.data.materials.new("Smoke Domain Material")
 -            obj.material_slots[0].material = mat
 -
 -            # Make sure we use nodes
 -            mat.use_nodes = True
 -
 -            # Set node variables and clear the default nodes
 -            tree = mat.node_tree
 -            nodes = tree.nodes
 -            links = tree.links
 -
 -            nodes.clear()
 +        # Cycles and Eevee
 +        bpy.ops.object.material_slot_add()
  
 -            # Create shader nodes
 +        mat = bpy.data.materials.new("Smoke Domain Material")
 +        obj.material_slots[0].material = mat
  
 -            # Material output
 -            node_out = nodes.new(type='ShaderNodeOutputMaterial')
 -            node_out.location = grid_location(6, 1)
 +        # Make sure we use nodes
 +        mat.use_nodes = True
  
 -            # Add Principled Volume
 -            node_principled = nodes.new(type='ShaderNodeVolumePrincipled')
 -            node_principled.location = grid_location(4, 1)
 -            links.new(node_principled.outputs["Volume"],
 -                      node_out.inputs["Volume"])
 +        # Set node variables and clear the default nodes
 +        tree = mat.node_tree
 +        nodes = tree.nodes
 +        links = tree.links
  
 -            node_principled.inputs["Density"].default_value = 5.0
 +        nodes.clear()
  
 -            if self.style in {'FIRE', 'BOTH'}:
 -                node_principled.inputs["Blackbody Intensity"].default_value = 1.0
 +        # Create shader nodes
  
 -        # Blender Internal
 -        else:
 -            # create a volume material with a voxel data texture for the domain
 -            bpy.ops.object.material_slot_add()
 -
 -            mat = bpy.data.materials.new("Smoke Domain Material")
 -            obj.material_slots[0].material = mat
 -            mat.type = 'VOLUME'
 -            mat.volume.density = 0
 -            mat.volume.density_scale = 5
 -            mat.volume.step_size = 0.1
 -
 -            tex = bpy.data.textures.new("Smoke Density", 'VOXEL_DATA')
 -            tex.voxel_data.domain_object = obj
 -            tex.voxel_data.interpolation = 'TRICUBIC_BSPLINE'
 -
 -            tex_slot = mat.texture_slots.add()
 -            tex_slot.texture = tex
 -            tex_slot.texture_coords = 'ORCO'
 -            tex_slot.use_map_color_emission = False
 -            tex_slot.use_map_density = True
 -            tex_slot.use_map_color_reflection = True
 -
 -            # for fire add a second texture for flame emission
 -            mat.volume.emission_color = Vector((0.0, 0.0, 0.0))
 -            tex = bpy.data.textures.new("Flame", 'VOXEL_DATA')
 -            tex.voxel_data.domain_object = obj
 -            tex.voxel_data.smoke_data_type = 'SMOKEFLAME'
 -            tex.voxel_data.interpolation = 'TRICUBIC_BSPLINE'
 -            tex.use_color_ramp = True
 +        # Material output
 +        node_out = nodes.new(type='ShaderNodeOutputMaterial')
 +        node_out.location = grid_location(6, 1)
  
 -            tex_slot = mat.texture_slots.add()
 -            tex_slot.texture = tex
 -            tex_slot.texture_coords = 'ORCO'
 +        # Add Principled Volume
 +        node_principled = nodes.new(type='ShaderNodeVolumePrincipled')
 +        node_principled.location = grid_location(4, 1)
 +        links.new(node_principled.outputs["Volume"],
-                 node_out.inputs["Volume"])
++                  node_out.inputs["Volume"])
  
 -            # add color ramp for flame color
 -            ramp = tex.color_ramp
 -            # dark orange
 -            elem = ramp.elements.new(0.333)
 -            elem.color = (0.2, 0.03, 0.0, 1.0)
 +        node_principled.inputs["Density"].default_value = 5.0
  
 -            # yellow glow
 -            elem = ramp.elements.new(0.666)
 -            elem.color = (1, 0.65, 0.25, 1.0)
 -
 -            mat.texture_slots[1].use_map_density = True
 -            mat.texture_slots[1].use_map_emission = True
 -            mat.texture_slots[1].emission_factor = 5
 +        if self.style in {'FIRE', 'BOTH'}:
 +            node_principled.inputs["Blackbody Intensity"].default_value = 1.0
  
          return {'FINISHED'}
  
index 5c1ca0ab2ad7242f8412a1cc6a02237af04d23f8,8852bca971d6450914d6b9a42aaa5e77885e2185..780183fb713af8381b664dc0b878053bc335b5cb
  # <pep8 compliant>
  
  import bpy
 -from bpy.types import Menu, Operator
 +from bpy.types import Menu, Operator, Panel, WindowManager
  from bpy.props import StringProperty, BoolProperty
  
 +# For preset popover menu
 +WindowManager.preset_name = StringProperty(
 +    name="Preset Name",
 +    description="Name for new preset",
 +    default="New Preset"
 +)
 +
  class AddPresetBase:
      """Base preset class, only for subclassing
      subclasses must define
      bl_options = {'REGISTER', 'INTERNAL'}
  
      name = StringProperty(
-             name="Name",
-             description="Name of the preset, used to make the path name",
-             maxlen=64,
-             options={'SKIP_SAVE'},
-             )
+         name="Name",
+         description="Name of the preset, used to make the path name",
+         maxlen=64,
+         options={'SKIP_SAVE'},
+     )
 +    remove_name = BoolProperty(
-             default=False,
-             options={'HIDDEN', 'SKIP_SAVE'},
-             )
++        default=False,
++        options={'HIDDEN', 'SKIP_SAVE'},
++    )
      remove_active = BoolProperty(
-             default=False,
-             options={'HIDDEN', 'SKIP_SAVE'},
-             )
+         default=False,
+         options={'HIDDEN', 'SKIP_SAVE'},
+     )
  
      # needed for mix-ins
      order = [
          "name",
 +        "remove_name",
          "remove_active",
-         ]
+     ]
  
      @staticmethod
      def as_filename(name):  # could reuse for other presets
@@@ -259,40 -241,6 +260,44 @@@ class ExecutePreset(Operator)
          return {'FINISHED'}
  
  
-         layout.popover(cls.bl_space_type,
-                        cls.bl_region_type,
-                        cls.__name__,
-                        icon='PRESET',
-                        text='')
 +class PresetMenu(Panel):
 +    bl_space_type = 'PROPERTIES'
 +    bl_region_type = 'HEADER'
 +    bl_label = "Presets"
 +    path_menu = Menu.path_menu
 +
 +    @classmethod
 +    def draw_panel_header(cls, layout):
 +        layout.emboss = 'NONE'
-         if text == None:
++        layout.popover(
++            cls.bl_space_type,
++            cls.bl_region_type,
++            cls.__name__,
++            icon='PRESET',
++            text="",
++        )
 +
 +    @classmethod
 +    def draw_menu(cls, layout, text=None):
-         layout.popover(cls.bl_space_type,
-                        cls.bl_region_type,
-                        cls.__name__,
-                        icon='PRESET',
-                        text=text)
++        if text is None:
 +            text = cls.bl_label
 +
++        layout.popover(
++            cls.bl_space_type,
++            cls.bl_region_type,
++            cls.__name__,
++            icon='PRESET',
++            text=text,
++        )
 +
 +    def draw(self, context):
 +        layout = self.layout
 +        layout.emboss = 'PULLDOWN_MENU'
 +        layout.operator_context = 'EXEC_DEFAULT'
 +
 +        Menu.draw_preset(self, context)
 +
 +
  class AddPresetRender(AddPresetBase, Operator):
      """Add or remove a Render Preset"""
      bl_idname = "render.preset_add"
@@@ -434,9 -413,38 +439,9 @@@ class AddPresetHairDynamics(AddPresetBa
          "settings.density_strength",
          "settings.voxel_cell_size",
          "settings.pin_stiffness",
-         ]
+     ]
  
  
 -class AddPresetSunSky(AddPresetBase, Operator):
 -    """Add or remove a Sky & Atmosphere Preset"""
 -    bl_idname = "lamp.sunsky_preset_add"
 -    bl_label = "Add Sunsky Preset"
 -    preset_menu = "LAMP_MT_sunsky_presets"
 -
 -    preset_defines = [
 -        "sky = bpy.context.lamp.sky"
 -    ]
 -
 -    preset_values = [
 -        "sky.atmosphere_extinction",
 -        "sky.atmosphere_inscattering",
 -        "sky.atmosphere_turbidity",
 -        "sky.backscattered_light",
 -        "sky.horizon_brightness",
 -        "sky.spread",
 -        "sky.sun_brightness",
 -        "sky.sun_intensity",
 -        "sky.sun_size",
 -        "sky.sky_blend",
 -        "sky.sky_blend_type",
 -        "sky.sky_color_space",
 -        "sky.sky_exposure",
 -    ]
 -
 -    preset_subdir = "sunsky"
 -
 -
  class AddPresetInteraction(AddPresetBase, Operator):
      """Add or remove an Application Interaction Preset"""
      bl_idname = "wm.interaction_preset_add"
index 2578365341404302505dfa4ec78acfb34a302e0d,f0b1f934e389047fd8f5ca0e6abfb305fec9461e..aebc0ae03136f91cdc041b44a8350a5d0f67fdca
@@@ -810,8 -816,8 +816,8 @@@ def main(context
          # Tag as used
          me.tag = True
  
-         if not me.uv_layers: # Mesh has no UV Coords, don't bother.
 -        if not me.uv_textures:  # Mesh has no UV Coords, don't bother.
 -            me.uv_textures.new()
++        if not me.uv_layers:  # Mesh has no UV Coords, don't bother.
 +            me.uv_layers.new()
  
          uv_layer = me.uv_layers.active.data
          me_verts = list(me.vertices)
index 8a520edb70e1d28f81502156b2973b75d633a148,5b12f6196a4b241050a3c739ab27c25409668fb7..8055ff250907ebe6ce5be8087d6ae170670a7877
@@@ -36,38 -32,29 +36,38 @@@ from bpy.app.translations import pgette
  
  
  rna_path_prop = StringProperty(
-         name="Context Attributes",
-         description="RNA context string",
-         maxlen=1024,
        )
+     name="Context Attributes",
+     description="RNA context string",
+     maxlen=1024,
+ )
  
  rna_reverse_prop = BoolProperty(
-         name="Reverse",
-         description="Cycle backwards",
-         default=False,
        )
+     name="Reverse",
+     description="Cycle backwards",
+     default=False,
+ )
  
  rna_wrap_prop = BoolProperty(
-         name="Wrap",
-         description="Wrap back to the first/last values",
-         default=False,
        )
+     name="Wrap",
+     description="Wrap back to the first/last values",
+     default=False,
+ )
  
  rna_relative_prop = BoolProperty(
-         name="Relative",
-         description="Apply relative to the current value (delta)",
-         default=False,
        )
+     name="Relative",
+     description="Apply relative to the current value (delta)",
+     default=False,
+ )
  
-         name="Type",
-         items=tuple(
-             (e.identifier, e.name, "", e. value)
-             for e in bpy.types.Space.bl_rna.properties["type"].enum_items
-            ),
-         default='EMPTY',
-         )
 +rna_space_type_prop = EnumProperty(
++    name="Type",
++    items=tuple(
++        (e.identifier, e.name, "", e. value)
++        for e in bpy.types.Space.bl_rna.properties["type"].enum_items
++    ),
++    default='EMPTY',
++)
 +
  
  def context_path_validate(context, data_path):
      try:
@@@ -1068,45 -1055,40 +1068,45 @@@ class WM_OT_doc_view(Operator)
  
  
  rna_path = StringProperty(
-         name="Property Edit",
-         description="Property data_path edit",
-         maxlen=1024,
-         options={'HIDDEN'},
        )
+     name="Property Edit",
+     description="Property data_path edit",
+     maxlen=1024,
+     options={'HIDDEN'},
+ )
  
  rna_value = StringProperty(
-         name="Property Value",
-         description="Property value edit",
-         maxlen=1024,
        )
+     name="Property Value",
+     description="Property value edit",
+     maxlen=1024,
+ )
  
  rna_property = StringProperty(
-         name="Property Name",
-         description="Property name edit",
-         maxlen=1024,
        )
+     name="Property Name",
+     description="Property name edit",
+     maxlen=1024,
+ )
  
  rna_min = FloatProperty(
-         name="Min",
-         default=-10000.0,
-         precision=3,
        )
+     name="Min",
+     default=-10000.0,
+     precision=3,
+ )
  
  rna_max = FloatProperty(
-         name="Max",
-         default=10000.0,
-         precision=3,
        )
+     name="Max",
+     default=10000.0,
+     precision=3,
+ )
  
  rna_use_soft_limits = BoolProperty(
-         name="Use Soft Limits",
        )
+     name="Use Soft Limits",
+ )
  
-         name="Is Statically Overridable",
-         default=False,
-         )
 +rna_is_overridable_static = BoolProperty(
++    name="Is Statically Overridable",
++    default=False,
++)
 +
  
  class WM_OT_properties_edit(Operator):
      bl_idname = "wm.properties_edit"
@@@ -1863,37 -1887,6 +1863,37 @@@ class WM_OT_addon_disable(Operator)
          return {'FINISHED'}
  
  
-             name="UI Tag",
-             )
 +class WM_OT_owner_enable(Operator):
 +    """Enable workspace owner ID"""
 +    bl_idname = "wm.owner_enable"
 +    bl_label = "Enable Add-on"
 +
 +    owner_id = StringProperty(
-             name="UI Tag",
-             )
++        name="UI Tag",
++    )
 +
 +    def execute(self, context):
 +        workspace = context.workspace
 +        workspace.owner_ids.new(self.owner_id)
 +        return {'FINISHED'}
 +
 +
 +class WM_OT_owner_disable(Operator):
 +    """Enable workspace owner ID"""
 +    bl_idname = "wm.owner_disable"
 +    bl_label = "Disable UI Tag"
 +
 +    owner_id = StringProperty(
++        name="UI Tag",
++    )
 +
 +    def execute(self, context):
 +        workspace = context.workspace
 +        owner_id = workspace.owner_ids[self.owner_id]
 +        workspace.owner_ids.remove(owner_id)
 +        return {'FINISHED'}
 +
 +
  class WM_OT_theme_install(Operator):
      """Load and apply a Blender XML theme file"""
      bl_idname = "wm.theme_install"
@@@ -2339,174 -2332,6 +2339,174 @@@ class WM_OT_app_template_install(Operat
          return {'RUNNING_MODAL'}
  
  
-             name="Text",
-             description="Display name of the tool",
-             )
 +class WM_OT_tool_set_by_name(Operator):
 +    """Set the tool by name (for keymaps)"""
 +    bl_idname = "wm.tool_set_by_name"
 +    bl_label = "Set Tool By Name"
 +
 +    name = StringProperty(
-             name="Cycle",
-             description="Cycle through tools in this group",
-             default=False,
-             options={'SKIP_SAVE'},
-             )
++        name="Text",
++        description="Display name of the tool",
++    )
 +
 +    cycle = BoolProperty(
-             name="File Path",
-             type=OperatorFileListElement,
-             )
++        name="Cycle",
++        description="Cycle through tools in this group",
++        default=False,
++        options={'SKIP_SAVE'},
++    )
 +
 +    space_type = rna_space_type_prop
 +
 +    def execute(self, context):
 +        from bl_ui.space_toolsystem_common import (
 +            activate_by_name,
 +            activate_by_name_or_cycle,
 +        )
 +
 +        if self.properties.is_property_set("space_type"):
 +            space_type = self.space_type
 +        else:
 +            space_type = context.space_data.type
 +
 +        fn = activate_by_name_or_cycle if self.cycle else activate_by_name
 +        if fn(context, space_type, self.name):
 +            return {'FINISHED'}
 +        else:
 +            self.report({'WARNING'}, f"Tool {self.name!r} not found.")
 +            return {'CANCELLED'}
 +
 +
 +class WM_OT_toolbar(Operator):
 +    bl_idname = "wm.toolbar"
 +    bl_label = "Toolbar"
 +
 +    def execute(self, context):
 +        from bl_ui.space_toolsystem_common import (
 +            ToolSelectPanelHelper,
 +            keymap_from_context,
 +        )
 +        space_type = context.space_data.type
 +
 +        cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type)
 +        if cls is None:
 +            self.report({'WARNING'}, f"Toolbar not found for {space_type!r}")
 +            return {'CANCELLED'}
 +
 +        wm = context.window_manager
 +        keymap = keymap_from_context(context, space_type)
 +
 +        def draw_menu(popover, context):
 +            layout = popover.layout
 +            cls.draw_cls(layout, context, detect_layout=False, scale_y=1.0)
 +
 +        wm.popover(draw_menu, keymap=keymap)
 +        return {'FINISHED'}
 +
 +
 +# Studio Light operations
 +class WM_OT_studiolight_install(Operator):
 +    """Install a user defined studio light"""
 +    bl_idname = "wm.studiolight_install"
 +    bl_label = "Install Custom Studio Light"
 +
 +    files = CollectionProperty(
-             subtype='DIR_PATH',
-             )
++        name="File Path",
++        type=OperatorFileListElement,
++    )
 +    directory = StringProperty(
-             name="Filter folders",
-             default=True,
-             options={'HIDDEN'},
-             )
++        subtype='DIR_PATH',
++    )
 +    filter_folder = BoolProperty(
-             default="*.png;*.jpg;*.hdr;*.exr",
-             options={'HIDDEN'},
-             )
++        name="Filter folders",
++        default=True,
++        options={'HIDDEN'},
++    )
 +    filter_glob = StringProperty(
++        default="*.png;*.jpg;*.hdr;*.exr",
++        options={'HIDDEN'},
++    )
 +    orientation = EnumProperty(
 +        items=(
 +            ("MATCAP", "MatCap", ""),
 +            ("WORLD", "World", ""),
 +            ("CAMERA", "Camera", ""),
 +        )
 +    )
 +
 +    def execute(self, context):
 +        import traceback
 +        import shutil
 +        import pathlib
 +        userpref = context.user_preferences
 +
 +        filepaths = [pathlib.Path(self.directory, e.name) for e in self.files]
 +        path_studiolights = bpy.utils.user_resource('DATAFILES')
 +
 +        if not path_studiolights:
 +            self.report({'ERROR'}, "Failed to get Studio Light path")
 +            return {'CANCELLED'}
 +
 +        path_studiolights = pathlib.Path(path_studiolights, "studiolights", self.orientation.lower())
 +        if not path_studiolights.exists():
 +            try:
 +                path_studiolights.mkdir(parents=True, exist_ok=True)
 +            except:
 +                traceback.print_exc()
 +
 +        for filepath in filepaths:
 +            shutil.copy(str(filepath), str(path_studiolights))
 +            userpref.studio_lights.new(str(path_studiolights.joinpath(filepath.name)), self.orientation)
 +
 +        # print message
 +        msg = (
 +            tip_("StudioLight Installed %r into %r") %
 +            (", ".join(str(x.name) for x in self.files), str(path_studiolights))
 +        )
 +        print(msg)
 +        self.report({'INFO'}, msg)
 +        return {'FINISHED'}
 +
 +    def invoke(self, context, event):
 +        wm = context.window_manager
 +        wm.fileselect_add(self)
 +        return {'RUNNING_MODAL'}
 +
 +
 +class WM_OT_studiolight_uninstall(Operator):
 +    bl_idname = 'wm.studiolight_uninstall'
 +    bl_label = "Uninstall Studio Light"
 +    index = bpy.props.IntProperty()
 +
 +    def _remove_path(self, path):
 +        if path.exists():
 +            path.unlink()
 +
 +    def execute(self, context):
 +        import pathlib
 +        userpref = context.user_preferences
 +        for studio_light in userpref.studio_lights:
 +            if studio_light.index == self.index:
 +                self._remove_path(pathlib.Path(studio_light.path))
 +                self._remove_path(pathlib.Path(studio_light.path_irr_cache))
 +                self._remove_path(pathlib.Path(studio_light.path_sh_cache))
 +                userpref.studio_lights.remove(studio_light)
 +                return {'FINISHED'}
 +        return {'CANCELLED'}
 +
 +
 +class WM_OT_studiolight_userpref_show(Operator):
 +    """Show light user preferences"""
 +    bl_idname = "wm.studiolight_userpref_show"
 +    bl_label = ""
 +    bl_options = {'INTERNAL'}
 +
 +    def execute(self, context):
 +        context.user_preferences.active_section = 'LIGHTS'
 +        bpy.ops.screen.userpref_show('INVOKE_DEFAULT')
 +        return {'FINISHED'}
 +
 +
  classes = (
      BRUSH_OT_active_index_set,
      WM_OT_addon_disable,
index 7208135f5e649cb2a1c22aeabdd4b23e291ed524,731032bafa82a31556b6d20af5259a28c45461b1..9ae20ba219461befa284f9ba4dbbe5b7dc9dd572
@@@ -47,53 -48,14 +47,55 @@@ def dopesheet_filter(layout, context, g
  
      if not is_nla:
          row = layout.row(align=True)
 -        row.prop(dopesheet, "show_only_matching_fcurves", text="")
 -        if dopesheet.show_only_matching_fcurves:
 -            row.prop(dopesheet, "filter_fcurve_name", text="")
 -            row.prop(dopesheet, "use_multi_word_filter", text="")
 +        row.prop(dopesheet, "filter_fcurve_name", text="")
 +        row.prop(dopesheet, "use_multi_word_filter", text="")
      else:
          row = layout.row(align=True)
 -        row.prop(dopesheet, "use_filter_text", text="")
 -        if dopesheet.use_filter_text:
 +        row.prop(dopesheet, "filter_text", text="")
 +        row.prop(dopesheet, "use_multi_word_filter", text="")
 +
 +#######################################
 +# Dopesheet Filtering Popovers
 +
 +# Generic Layout - Used as base for filtering popovers used in all animation editors
 +# Used for DopeSheet, NLA, and Graph Editors
++
++
 +class DopesheetFilterPopoverBase:
 +    bl_region_type = 'HEADER'
 +    bl_label = "Filters"
 +
 +    # Generic = Affects all datatypes
 +    # XXX: Perhaps we want these to stay in the header instead, for easy/fast access
 +    @classmethod
 +    def draw_generic_filters(cls, context, layout):
 +        dopesheet = context.space_data.dopesheet
 +        is_nla = context.area.type == 'NLA_EDITOR'
 +
 +        col = layout.column(align=True)
 +        col.prop(dopesheet, "show_only_selected", icon='NONE')
 +        col.prop(dopesheet, "show_hidden", icon='NONE')
 +
 +        if is_nla:
 +            col.prop(dopesheet, "show_missing_nla", icon='NONE')
 +        else:  # graph and dopesheet editors - F-Curves and drivers only
 +            col.prop(dopesheet, "show_only_errors", icon='NONE')
 +
 +    # Name/Membership Filters
 +    # XXX: Perhaps these should just stay in the headers (exclusively)?
 +    @classmethod
 +    def draw_search_filters(cls, context, layout, generic_filters_only=False):
 +        dopesheet = context.space_data.dopesheet
 +        is_nla = context.area.type == 'NLA_EDITOR'
 +
 +        col = layout.column(align=True)
 +        col.label("With Name:")
 +        if not is_nla:
 +            row = col.row(align=True)
 +            row.prop(dopesheet, "filter_fcurve_name", text="")
 +            row.prop(dopesheet, "use_multi_word_filter", text="")
 +        else:
 +            row = col.row(align=True)
              row.prop(dopesheet, "filter_text", text="")
              row.prop(dopesheet, "use_multi_word_filter", text="")
  
@@@ -206,36 -119,11 +208,38 @@@ class DOPESHEET_HT_header(Header)
          row = layout.row(align=True)
          row.template_header()
  
 -        DOPESHEET_MT_editor_menus.draw_collapsible(context, layout)
 +        if st.mode == 'TIMELINE':
 +            TIME_MT_editor_menus.draw_collapsible(context, layout)
 +            TIME_HT_editor_buttons.draw_header(context, layout)
 +        else:
 +            layout.prop(st, "ui_mode", text="")
-             layout.popover(space_type='DOPESHEET_EDITOR',
-                            region_type='HEADER',
-                            panel_type="DOPESHEET_PT_filters",
-                            text="",
-                            icon='FILTER')
++            layout.popover(
++                space_type='DOPESHEET_EDITOR',
++                region_type='HEADER',
++                panel_type="DOPESHEET_PT_filters",
++                text="",
++                icon='FILTER',
++            )
 +            DOPESHEET_MT_editor_menus.draw_collapsible(context, layout)
 +            DOPESHEET_HT_editor_buttons.draw_header(context, layout)
 +
 +
 +# Header for "normal" dopesheet editor modes (e.g. Dope Sheet, Action, Shape Keys, etc.)
 +class DOPESHEET_HT_editor_buttons(Header):
 +    bl_idname = "DOPESHEET_HT_editor_buttons"
 +    bl_space_type = 'DOPESHEET_EDITOR'
 +    bl_label = ""
 +
 +    def draw(self, context):
 +        pass
  
 -        layout.prop(st, "mode", text="")
 +    @staticmethod
 +    def draw_header(context, layout):
 +        st = context.space_data
 +        toolsettings = context.tool_settings
  
          if st.mode in {'ACTION', 'SHAPEKEY'}:
 +            # TODO: These buttons need some tidying up - Probably by using a popover, and bypassing the template_id() here
              row = layout.row(align=True)
              row.operator("action.layer_prev", text="", icon='TRIA_DOWN')
              row.operator("action.layer_next", text="", icon='TRIA_UP')
index 9a22e0b4bd7dff70aa860df3b9c56332f47d1ad1,5ed58a57e47c8092ea099baab600531aa98b3ec9..e3ebb99d859cf6e5feebfb557db4b56d5d9a8068
  # <pep8 compliant>
  
  import bpy
 -from bpy.types import Header, Menu
 +from bpy.types import Header, Menu, Panel
 +from .space_dopesheet import (
 +    DopesheetFilterPopoverBase,
 +    dopesheet_filter,
-     )
++)
  
  
  class GRAPH_HT_header(Header):
          row = layout.row(align=True)
          row.template_header()
  
 -        GRAPH_MT_editor_menus.draw_collapsible(context, layout)
 +        # Now a exposed as a sub-space type
 +        # layout.prop(st, "mode", text="")
  
-         layout.popover(space_type='GRAPH_EDITOR',
-                            region_type='HEADER',
-                            panel_type="GRAPH_PT_filters",
-                            text="",
-                            icon='FILTER')
 -        layout.prop(st, "mode", text="")
++        layout.popover(
++            space_type='GRAPH_EDITOR',
++            region_type='HEADER',
++            panel_type="GRAPH_PT_filters",
++            text="",
++            icon='FILTER',
++        )
 +
 +        GRAPH_MT_editor_menus.draw_collapsible(context, layout)
  
          dopesheet_filter(layout, context)
  
index 9f23420298fe530f4c32f7d22e39bad4ee0a6db0,8a933570c5ee6dbd5d75854ecc6666920cbf65bf..9a5f835d5059ecd548e28efaf6742d106d57b54f
  # <pep8 compliant>
  
  import bpy
 -from bpy.types import Header, Menu
 +from bpy.types import Header, Menu, Panel
 +from .space_dopesheet import (
 +    DopesheetFilterPopoverBase,
 +    dopesheet_filter,
-     )
++)
  
  
  class NLA_HT_header(Header):
          row = layout.row(align=True)
          row.template_header()
  
-         layout.popover(space_type='NLA_EDITOR',
-                        region_type='HEADER',
-                        panel_type="NLA_PT_filters",
-                        text="",
-                        icon='FILTER')
++        layout.popover(
++            space_type='NLA_EDITOR',
++            region_type='HEADER',
++            panel_type="NLA_PT_filters",
++            text="",
++            icon='FILTER',
++        )
 +
          NLA_MT_editor_menus.draw_collapsible(context, layout)
  
          dopesheet_filter(layout, context)
index 358a813fd0f3af295c623c7dce35002a15536936,9026a93aa998c236810224e395816318681a9ae7..4616aeb45a46cfd31c9993fd476ebc86a831ba2e
@@@ -91,16 -107,10 +91,20 @@@ class TIME_MT_editor_menus(Menu)
  
      @staticmethod
      def draw_menus(layout, context):
-         layout.popover(space_type='DOPESHEET_EDITOR',
-                        region_type='HEADER',
-                        panel_type="TIME_PT_playback",
-                        text="Playback")
-         layout.popover(space_type='DOPESHEET_EDITOR',
-                        region_type='HEADER',
-                        panel_type="TIME_PT_keyframing_settings",
-                        text="Keying")
++        layout.popover(
++            space_type='DOPESHEET_EDITOR',
++            region_type='HEADER',
++            panel_type="TIME_PT_playback",
++            text="Playback",
++        )
++        layout.popover(
++            space_type='DOPESHEET_EDITOR',
++            region_type='HEADER',
++            panel_type="TIME_PT_keyframing_settings",
++            text="Keying",
++        )
          layout.menu("TIME_MT_view")
          layout.menu("TIME_MT_marker")
 -        layout.menu("TIME_MT_frame")
 -        layout.menu("TIME_MT_playback")
  
  
  class TIME_MT_marker(Menu):
index 8de7a5e290397f8e829e6dd127ed5562f6dab19f,0fc888a2d4e92c992176d214d3f2e45858eb4dc2..1463915886abdae2fc80c09bc4af14ee8140822c
@@@ -28,10 -28,10 +28,10 @@@ for obj in selection
  
      bpy.ops.export_scene.fbx(filepath=fn + ".fbx", use_selection=True)
  
-     ## Can be used for multiple formats
+     # Can be used for multiple formats
      # bpy.ops.export_scene.x3d(filepath=fn + ".x3d", use_selection=True)
  
 -    obj.select = False
 +    obj.select_set(action='DESELECT')
  
      print("written:", fn)
  
index c16de53e69438c15ef8e757f962d6154e3bbf3f4,0000000000000000000000000000000000000000..35e1c5b4fd10ce98e5e633695d39cb4c62d6b55d
mode 100644,000000..100644
--- /dev/null
@@@ -1,231 -1,0 +1,234 @@@
 +# Example of an operator which uses manipulators to control its properties.
 +#
 +# Usage: Run this script, then in mesh edit-mode press Spacebar
 +# to activate the operator "Select Side of Plane"
 +# The manipulators can then be used to adjust the plane in the 3D view.
 +#
 +import bpy
 +import bmesh
 +
 +from bpy.types import (
 +    Operator,
 +    ManipulatorGroup,
 +)
 +
 +from bpy.props import (
 +    FloatVectorProperty,
 +)
 +
++
 +def main(context, plane_co, plane_no):
 +    obj = context.active_object
 +    matrix = obj.matrix_world.copy()
 +    me = obj.data
 +    bm = bmesh.from_edit_mesh(me)
 +
 +    plane_dot = plane_no.dot(plane_co)
 +
 +    for v in bm.verts:
 +        co = matrix * v.co
 +        v.select = (plane_no.dot(co) > plane_dot)
 +    bm.select_flush_mode()
 +
 +    bmesh.update_edit_mesh(me)
 +
 +
 +class SelectSideOfPlane(Operator):
 +    """UV Operator description"""
 +    bl_idname = "mesh.select_side_of_plane"
 +    bl_label = "Select Side of Plane"
 +    bl_options = {'REGISTER', 'UNDO'}
 +
 +    plane_co = FloatVectorProperty(
 +        size=3,
 +        default=(0, 0, 0),
 +    )
 +    plane_no = FloatVectorProperty(
 +        size=3,
 +        default=(0, 0, 1),
 +    )
 +
 +    @classmethod
 +    def poll(cls, context):
 +        return (context.mode == 'EDIT_MESH')
 +
 +    def invoke(self, context, event):
 +
 +        if not self.properties.is_property_set("plane_co"):
 +            self.plane_co = context.scene.cursor_location
 +
 +        if not self.properties.is_property_set("plane_no"):
 +            if context.space_data.type == 'VIEW_3D':
 +                rv3d = context.space_data.region_3d
 +                view_inv = rv3d.view_matrix.to_3x3()
 +                # view y axis
 +                self.plane_no = view_inv[1].normalized()
 +
 +        self.execute(context)
 +
 +        if context.space_data.type == 'VIEW_3D':
 +            wm = context.window_manager
 +            wm.manipulator_group_type_add(SelectSideOfPlaneManipulatorGroup.bl_idname)
 +
 +        return {'FINISHED'}
 +
 +    def execute(self, context):
 +        from mathutils import Vector
 +        main(context, Vector(self.plane_co), Vector(self.plane_no))
 +        return {'FINISHED'}
 +
 +
 +# Manipulators for plane_co, plane_no
 +class SelectSideOfPlaneManipulatorGroup(ManipulatorGroup):
 +    bl_idname = "MESH_WGT_select_side_of_plane"
 +    bl_label = "Side of Plane Manipulator"
 +    bl_space_type = 'VIEW_3D'
 +    bl_region_type = 'WINDOW'
 +    bl_options = {'3D'}
 +
 +    # Helper functions
 +    @staticmethod
 +    def my_target_operator(context):
 +        wm = context.window_manager
 +        op = wm.operators[-1] if wm.operators else None
 +        if isinstance(op, SelectSideOfPlane):
 +            return op
 +        return None
 +
 +    @staticmethod
 +    def my_view_orientation(context):
 +        rv3d = context.space_data.region_3d
 +        view_inv = rv3d.view_matrix.to_3x3()
 +        return view_inv.normalized()
 +
 +    @classmethod
 +    def poll(cls, context):
 +        op = cls.my_target_operator(context)
 +        if op is None:
 +            wm = context.window_manager
 +            wm.manipulator_group_type_remove(SelectSideOfPlaneManipulatorGroup.bl_idname)
 +            return False
 +        return True
 +
 +    def setup(self, context):
 +        from mathutils import Matrix, Vector
 +
 +        # ----
 +        # Grab
 +
 +        def grab_get_cb():
 +            op = SelectSideOfPlaneManipulatorGroup.my_target_operator(context)
 +            return op.plane_co
 +
 +        def grab_set_cb(value):
 +            op = SelectSideOfPlaneManipulatorGroup.my_target_operator(context)
 +            op.plane_co = value
 +            # XXX, this may change!
 +            op.execute(context)
 +
 +        mpr = self.manipulators.new("MANIPULATOR_WT_grab_3d")
 +        mpr.target_set_handler("offset", get=grab_get_cb, set=grab_set_cb)
 +
 +        mpr.use_draw_value = True
 +
 +        mpr.color = 0.8, 0.8, 0.8
 +        mpr.alpha = 0.5
 +
 +        mpr.color_highlight = 1.0, 1.0, 1.0
 +        mpr.alpha_highlight = 1.0
 +
 +        mpr.scale_basis = 0.2
 +
 +        self.widget_grab = mpr
 +
 +        # ----
 +        # Dial
 +
 +        def direction_get_cb():
 +            op = SelectSideOfPlaneManipulatorGroup.my_target_operator(context)
 +
 +            no_a = self.widget_dial.matrix_basis.col[1].xyz
 +            no_b = Vector(op.plane_no)
 +
 +            no_a = (no_a * self.view_inv).xy.normalized()
 +            no_b = (no_b * self.view_inv).xy.normalized()
 +            return no_a.angle_signed(no_b)
 +
 +        def direction_set_cb(value):
 +            op = SelectSideOfPlaneManipulatorGroup.my_target_operator(context)
 +            matrix_rotate = Matrix.Rotation(-value, 3, self.rotate_axis)
 +            no = matrix_rotate * self.widget_dial.matrix_basis.col[1].xyz
 +            op.plane_no = no
 +            op.execute(context)
 +
 +        mpr = self.manipulators.new("MANIPULATOR_WT_dial_3d")
 +        mpr.target_set_handler("offset", get=direction_get_cb, set=direction_set_cb)
 +        mpr.draw_options = {'ANGLE_START_Y'}
 +
 +        mpr.use_draw_value = True
 +
 +        mpr.color = 0.8, 0.8, 0.8
 +        mpr.alpha = 0.5
 +
 +        mpr.color_highlight = 1.0, 1.0, 1.0
 +        mpr.alpha_highlight = 1.0
 +
 +        self.widget_dial = mpr
 +
 +    def draw_prepare(self, context):
 +        from mathutils import Vector
 +
 +        view_inv = self.my_view_orientation(context)
 +
 +        self.view_inv = view_inv
 +        self.rotate_axis = view_inv[2].xyz
 +        self.rotate_up = view_inv[1].xyz
 +
 +        op = self.my_target_operator(context)
 +
 +        co = Vector(op.plane_co)
 +        no = Vector(op.plane_no).normalized()
 +
 +        # Grab
 +        no_z = no
 +        no_y = no_z.orthogonal()
 +        no_x = no_z.cross(no_y)
 +
 +        matrix = self.widget_grab.matrix_basis
 +        matrix.identity()
 +        matrix.col[0].xyz = no_x
 +        matrix.col[1].xyz = no_y
 +        matrix.col[2].xyz = no_z
 +        matrix.col[3].xyz = co
 +
 +        # Dial
 +        no_z = self.rotate_axis
 +        no_y = (no - (no.project(no_z))).normalized()
 +        no_x = self.rotate_axis.cross(no_y)
 +
 +        matrix = self.widget_dial.matrix_basis
 +        matrix.identity()
 +        matrix.col[0].xyz = no_x
 +        matrix.col[1].xyz = no_y
 +        matrix.col[2].xyz = no_z
 +        matrix.col[3].xyz = co
 +
 +
 +classes = (
 +    SelectSideOfPlane,
 +    SelectSideOfPlaneManipulatorGroup,
 +)
 +
++
 +def register():
 +    for cls in classes:
 +        bpy.utils.register_class(cls)
 +
 +
 +def unregister():
 +    for cls in reversed(classes):
 +        bpy.utils.unregister_class(cls)
 +
++
 +if __name__ == "__main__":
 +    register()
index 0abf6f2f654d93d4f8e87a8d757492b61bf8e496,0000000000000000000000000000000000000000..ba53b5e10ffb614c59d13098f7af0acc2f084918
mode 100644,000000..100644
--- /dev/null
@@@ -1,48 -1,0 +1,50 @@@
 +# Example of a manipulator that activates an operator
 +# using the predefined dial manipulator to change the camera roll.
 +#
 +# Usage: Run this script and select a camera in the 3D view.
 +#
 +import bpy
 +from bpy.types import (
 +    ManipulatorGroup,
 +)
 +
++
 +class MyCameraWidgetGroup(ManipulatorGroup):
 +    bl_idname = "OBJECT_WGT_test_camera"
 +    bl_label = "Object Camera Test Widget"
 +    bl_space_type = 'VIEW_3D'
 +    bl_region_type = 'WINDOW'
 +    bl_options = {'3D', 'PERSISTENT'}
 +
 +    @classmethod
 +    def poll(cls, context):
 +        ob = context.object
 +        return (ob and ob.type == 'CAMERA')
 +
 +    def setup(self, context):
 +        # Run an operator using the dial manipulator
 +        ob = context.object
 +        mpr = self.manipulators.new("MANIPULATOR_WT_dial_3d")
 +        props = mpr.target_set_operator("transform.rotate")
 +        props.constraint_axis = False, False, True
 +        props.constraint_orientation = 'LOCAL'
 +        props.release_confirm = True
 +
 +        mpr.matrix_basis = ob.matrix_world.normalized()
 +        mpr.line_width = 3
 +
 +        mpr.color = 0.8, 0.8, 0.8
 +        mpr.alpha = 0.5
 +
 +        mpr.color_highlight = 1.0, 1.0, 1.0
 +        mpr.alpha_highlight = 1.0
 +
 +        self.roll_widget = mpr
 +
 +    def refresh(self, context):
 +        ob = context.object
 +        mpr = self.roll_widget
 +        mpr.matrix_basis = ob.matrix_world.normalized()
 +
++
 +bpy.utils.register_class(MyCameraWidgetGroup)
index 7e02940d52780c946c49bf7b4497d92230330180,0000000000000000000000000000000000000000..8ddb870cd133b9c29e8d6a5a11adf9f09d275132
mode 100644,000000..100644
--- /dev/null
@@@ -1,45 -1,0 +1,47 @@@
 +# Example of a group that edits a single property
 +# using the predefined manipulator arrow.
 +#
 +# Usage: Select a lamp in the 3D view and drag the arrow at it's rear
 +# to change it's energy value.
 +#
 +import bpy
 +from bpy.types import (
 +    ManipulatorGroup,
 +)
 +
++
 +class MyLampWidgetGroup(ManipulatorGroup):
 +    bl_idname = "OBJECT_WGT_lamp_test"
 +    bl_label = "Test Lamp Widget"
 +    bl_space_type = 'VIEW_3D'
 +    bl_region_type = 'WINDOW'
 +    bl_options = {'3D', 'PERSISTENT'}
 +
 +    @classmethod
 +    def poll(cls, context):
 +        ob = context.object
 +        return (ob and ob.type == 'LAMP')
 +
 +    def setup(self, context):
 +        # Arrow manipulator has one 'offset' property we can assign to the lamp energy.
 +        ob = context.object
 +        mpr = self.manipulators.new("MANIPULATOR_WT_arrow_3d")
 +        mpr.target_set_prop("offset", ob.data, "energy")
 +        mpr.matrix_basis = ob.matrix_world.normalized()
 +        mpr.draw_style = 'BOX'
 +
 +        mpr.color = 1.0, 0.5, 0.0
 +        mpr.alpha = 0.5
 +
 +        mpr.color_highlight = 1.0, 0.5, 1.0
 +        mpr.alpha_highlight = 0.5
 +
 +        self.energy_widget = mpr
 +
 +    def refresh(self, context):
 +        ob = context.object
 +        mpr = self.energy_widget
 +        mpr.matrix_basis = ob.matrix_world.normalized()
 +
++
 +bpy.utils.register_class(MyLampWidgetGroup)
index 9a8bef0c863d9955010249267959769ce362485c,d1531972eb2f4d4684e48fc7d85f96760aa5db11..43eebc9b72ca8300f22e4e6e4b25684ec82425fc
@@@ -368,334 -347,8 +368,335 @@@ void MESH_OT_bisect(struct wmOperatorTy
        RNA_def_boolean(ot->srna, "clear_inner", false, "Clear Inner", "Remove geometry behind the plane");
        RNA_def_boolean(ot->srna, "clear_outer", false, "Clear Outer", "Remove geometry in front of the plane");
  
-       RNA_def_float(ot->srna, "threshold", 0.0001, 0.0, 10.0, "Axis Threshold", "", 0.00001, 0.1);
+       RNA_def_float(ot->srna, "threshold", 0.0001, 0.0, 10.0, "Axis Threshold",
+                                                                                                       "Preserves the existing geometry along the cut plane", 0.00001, 0.1);
  
        WM_operator_properties_gesture_straightline(ot, CURSOR_EDIT);
 +
 +#ifdef USE_MANIPULATOR
 +      WM_manipulatorgrouptype_append(MESH_WGT_bisect);
 +#endif
 +}
 +
 +
 +#ifdef USE_MANIPULATOR
 +
 +/* -------------------------------------------------------------------- */
 +
 +/** \name Bisect Manipulator
 + * \{ */
 +
 +typedef struct ManipulatorGroup {
 +      /* Arrow to change plane depth. */
 +      struct wmManipulator *translate_z;
 +      /* Translate XYZ */
 +      struct wmManipulator *translate_c;
 +      /* For grabbing the manipulator and moving freely. */
 +      struct wmManipulator *rotate_c;
 +
 +      /* We could store more vars here! */
 +      struct {
 +              bContext *context;
 +              wmOperator *op;
 +              PropertyRNA *prop_plane_co;
 +              PropertyRNA *prop_plane_no;
 +
 +              float rotate_axis[3];
 +              float rotate_up[3];
 +      } data;
 +} ManipulatorGroup;
 +
 +/**
 + * XXX. calling redo from property updates is not great.
 + * This is needed because changing the RNA doesn't cause a redo
 + * and we're not using operator UI which does just this.
 + */
 +static void manipulator_bisect_exec(ManipulatorGroup *man)
 +{
 +      wmOperator *op = man->data.op;
 +      if (op == WM_operator_last_redo((bContext *)man->data.context)) {
 +              ED_undo_operator_repeat((bContext *)man->data.context, op);
 +      }
 +}
 +
 +static void manipulator_mesh_bisect_update_from_op(ManipulatorGroup *man)
 +{
 +      wmOperator *op = man->data.op;
 +
 +      float plane_co[3], plane_no[3];
 +
 +      RNA_property_float_get_array(op->ptr, man->data.prop_plane_co, plane_co);
 +      RNA_property_float_get_array(op->ptr, man->data.prop_plane_no, plane_no);
 +
 +      WM_manipulator_set_matrix_location(man->translate_z, plane_co);
 +      WM_manipulator_set_matrix_location(man->rotate_c, plane_co);
 +      /* translate_c location comes from the property. */
 +
 +      WM_manipulator_set_matrix_rotation_from_z_axis(man->translate_z, plane_no);
 +
 +      WM_manipulator_set_scale(man->translate_c, 0.2);
 +
 +      RegionView3D *rv3d = ED_view3d_context_rv3d(man->data.context);
 +      if (rv3d) {
 +              normalize_v3_v3(man->data.rotate_axis, rv3d->viewinv[2]);
 +              normalize_v3_v3(man->data.rotate_up, rv3d->viewinv[1]);
 +
 +              /* ensure its orthogonal */
 +              project_plane_normalized_v3_v3v3(man->data.rotate_up, man->data.rotate_up, man->data.rotate_axis);
 +              normalize_v3(man->data.rotate_up);
 +
 +              WM_manipulator_set_matrix_rotation_from_z_axis(man->translate_c, plane_no);
 +
 +              float plane_no_cross[3];
 +              cross_v3_v3v3(plane_no_cross, plane_no, man->data.rotate_axis);
 +
 +              WM_manipulator_set_matrix_offset_rotation_from_yz_axis(man->rotate_c, plane_no_cross, man->data.rotate_axis);
 +              RNA_enum_set(man->rotate_c->ptr, "draw_options",
 +                           ED_MANIPULATOR_DIAL_DRAW_FLAG_ANGLE_MIRROR |
 +                           ED_MANIPULATOR_DIAL_DRAW_FLAG_ANGLE_START_Y);
 +      }
 +}
 +
 +/* depth callbacks */
 +static void manipulator_bisect_prop_depth_get(
 +        const wmManipulator *mpr, wmManipulatorProperty *mpr_prop,
 +        void *value_p)
 +{
 +      ManipulatorGroup *man = mpr->parent_mgroup->customdata;
 +      wmOperator *op = man->data.op;
 +      float *value = value_p;
 +
 +      BLI_assert(mpr_prop->type->array_length == 1);
 +      UNUSED_VARS_NDEBUG(mpr_prop);
 +
 +      float plane_co[3], plane_no[3];
 +      RNA_property_float_get_array(op->ptr, man->data.prop_plane_co, plane_co);
 +      RNA_property_float_get_array(op->ptr, man->data.prop_plane_no, plane_no);
 +
 +      value[0] = dot_v3v3(plane_no, plane_co) - dot_v3v3(plane_no, mpr->matrix_basis[3]);
 +}
 +
 +static void manipulator_bisect_prop_depth_set(
 +        const wmManipulator *mpr, wmManipulatorProperty *mpr_prop,
 +        const void *value_p)
 +{
 +      ManipulatorGroup *man = mpr->parent_mgroup->customdata;
 +      wmOperator *op = man->data.op;
 +      const float *value = value_p;
 +
 +      BLI_assert(mpr_prop->type->array_length == 1);
 +      UNUSED_VARS_NDEBUG(mpr_prop);
 +
 +      float plane_co[3], plane[4];
 +      RNA_property_float_get_array(op->ptr, man->data.prop_plane_co, plane_co);
 +      RNA_property_float_get_array(op->ptr, man->data.prop_plane_no, plane);
 +      normalize_v3(plane);
 +
 +      plane[3] = -value[0] - dot_v3v3(plane, mpr->matrix_basis[3]);
 +
 +      /* Keep our location, may be offset simply to be inside the viewport. */
 +      closest_to_plane_normalized_v3(plane_co, plane, plane_co);
 +
 +      RNA_property_float_set_array(op->ptr, man->data.prop_plane_co, plane_co);
 +
 +      manipulator_bisect_exec(man);
 +}
 +
 +/* translate callbacks */
 +static void manipulator_bisect_prop_translate_get(
 +        const wmManipulator *mpr, wmManipulatorProperty *mpr_prop,
 +        void *value_p)
 +{
 +      ManipulatorGroup *man = mpr->parent_mgroup->customdata;
 +      wmOperator *op = man->data.op;
 +
 +      BLI_assert(mpr_prop->type->array_length == 3);
 +      UNUSED_VARS_NDEBUG(mpr_prop);
 +
 +      RNA_property_float_get_array(op->ptr, man->data.prop_plane_co, value_p);
 +}
 +
 +static void manipulator_bisect_prop_translate_set(
 +        const wmManipulator *mpr, wmManipulatorProperty *mpr_prop,
 +        const void *value_p)
 +{
 +      ManipulatorGroup *man = mpr->parent_mgroup->customdata;
 +      wmOperator *op = man->data.op;
 +
 +      BLI_assert(mpr_prop->type->array_length == 3);
 +      UNUSED_VARS_NDEBUG(mpr_prop);
 +
 +      RNA_property_float_set_array(op->ptr, man->data.prop_plane_co, value_p);
 +
 +      manipulator_bisect_exec(man);
 +}
 +
 +/* angle callbacks */
 +static void manipulator_bisect_prop_angle_get(
 +        const wmManipulator *mpr, wmManipulatorProperty *mpr_prop,
 +        void *value_p)
 +{
 +      ManipulatorGroup *man = mpr->parent_mgroup->customdata;
 +      wmOperator *op = man->data.op;
 +      float *value = value_p;
 +
 +      BLI_assert(mpr_prop->type->array_length == 1);
 +      UNUSED_VARS_NDEBUG(mpr_prop);
 +
 +      float plane_no[4];
 +      RNA_property_float_get_array(op->ptr, man->data.prop_plane_no, plane_no);
 +      normalize_v3(plane_no);
 +
 +      float plane_no_proj[3];
 +      project_plane_normalized_v3_v3v3(plane_no_proj, plane_no, man->data.rotate_axis);
 +
 +      if (!is_zero_v3(plane_no_proj)) {
 +              const float angle = -angle_signed_on_axis_v3v3_v3(plane_no_proj, man->data.rotate_up, man->data.rotate_axis);
 +              value[0] = angle;
 +      }
 +      else {
 +              value[0] = 0.0f;
 +      }
 +}
 +
 +static void manipulator_bisect_prop_angle_set(
 +        const wmManipulator *mpr, wmManipulatorProperty *mpr_prop,
 +        const void *value_p)
 +{
 +      ManipulatorGroup *man = mpr->parent_mgroup->customdata;
 +      wmOperator *op = man->data.op;
 +      const float *value = value_p;
 +
 +      BLI_assert(mpr_prop->type->array_length == 1);
 +      UNUSED_VARS_NDEBUG(mpr_prop);
 +
 +      float plane_no[4];
 +      RNA_property_float_get_array(op->ptr, man->data.prop_plane_no, plane_no);
 +      normalize_v3(plane_no);
 +
 +      float plane_no_proj[3];
 +      project_plane_normalized_v3_v3v3(plane_no_proj, plane_no, man->data.rotate_axis);
 +
 +      if (!is_zero_v3(plane_no_proj)) {
 +              const float angle = -angle_signed_on_axis_v3v3_v3(plane_no_proj, man->data.rotate_up, man->data.rotate_axis);
 +              const float angle_delta = angle - angle_compat_rad(value[0], angle);
 +              if (angle_delta != 0.0f) {
 +                      float mat[3][3];
 +                      axis_angle_normalized_to_mat3(mat, man->data.rotate_axis, angle_delta);
 +                      mul_m3_v3(mat, plane_no);
 +
 +                      /* re-normalize - seems acceptable */
 +                      RNA_property_float_set_array(op->ptr, man->data.prop_plane_no, plane_no);
 +
 +                      manipulator_bisect_exec(man);
 +              }
 +      }
 +}
 +
 +static bool manipulator_mesh_bisect_poll(const bContext *C, wmManipulatorGroupType *wgt)
 +{
 +      wmOperator *op = WM_operator_last_redo(C);
 +      if (op == NULL || !STREQ(op->type->idname, "MESH_OT_bisect")) {
 +              WM_manipulator_group_type_unlink_delayed_ptr(wgt);
 +              return false;
 +      }
 +      return true;
  }
 +
 +static void manipulator_mesh_bisect_setup(const bContext *C, wmManipulatorGroup *mgroup)
 +{
 +      wmOperator *op = WM_operator_last_redo(C);
 +
 +      if (op == NULL || !STREQ(op->type->idname, "MESH_OT_bisect")) {
 +              return;
 +      }
 +
 +      struct ManipulatorGroup *man = MEM_callocN(sizeof(ManipulatorGroup), __func__);
 +      mgroup->customdata = man;
 +
 +      const wmManipulatorType *wt_arrow = WM_manipulatortype_find("MANIPULATOR_WT_arrow_3d", true);
 +      const wmManipulatorType *wt_grab = WM_manipulatortype_find("MANIPULATOR_WT_grab_3d", true);
 +      const wmManipulatorType *wt_dial = WM_manipulatortype_find("MANIPULATOR_WT_dial_3d", true);
 +
 +      man->translate_z = WM_manipulator_new_ptr(wt_arrow, mgroup, NULL);
 +      man->translate_c = WM_manipulator_new_ptr(wt_grab, mgroup, NULL);
 +      man->rotate_c = WM_manipulator_new_ptr(wt_dial, mgroup, NULL);
 +
 +      UI_GetThemeColor3fv(TH_MANIPULATOR_PRIMARY, man->translate_z->color);
 +      UI_GetThemeColor3fv(TH_MANIPULATOR_PRIMARY, man->translate_c->color);
 +      UI_GetThemeColor3fv(TH_MANIPULATOR_SECONDARY, man->rotate_c->color);
 +
 +      RNA_enum_set(man->translate_z->ptr, "draw_style", ED_MANIPULATOR_ARROW_STYLE_NORMAL);
 +      RNA_enum_set(man->translate_c->ptr, "draw_style", ED_MANIPULATOR_GRAB_STYLE_RING_2D);
 +
 +      WM_manipulator_set_flag(man->translate_c, WM_MANIPULATOR_DRAW_VALUE, true);
 +      WM_manipulator_set_flag(man->rotate_c, WM_MANIPULATOR_DRAW_VALUE, true);
 +
 +      {
 +              man->data.context = (bContext *)C;
 +              man->data.op = op;
 +              man->data.prop_plane_co = RNA_struct_find_property(op->ptr, "plane_co");
 +              man->data.prop_plane_no = RNA_struct_find_property(op->ptr, "plane_no");
 +      }
 +
 +      manipulator_mesh_bisect_update_from_op(man);
 +
 +      /* Setup property callbacks */
 +      {
 +              WM_manipulator_target_property_def_func(
 +                      man->translate_z, "offset",
 +                      &(const struct wmManipulatorPropertyFnParams) {
 +                          .value_get_fn = manipulator_bisect_prop_depth_get,
 +                          .value_set_fn = manipulator_bisect_prop_depth_set,
 +                          .range_get_fn = NULL,
 +                          .user_data = NULL,
 +                      });
 +
 +              WM_manipulator_target_property_def_func(
 +                      man->translate_c, "offset",
 +                      &(const struct wmManipulatorPropertyFnParams) {
 +                          .value_get_fn = manipulator_bisect_prop_translate_get,
 +                          .value_set_fn = manipulator_bisect_prop_translate_set,
 +                          .range_get_fn = NULL,
 +                          .user_data = NULL,
 +                      });
 +
 +              WM_manipulator_target_property_def_func(
 +                      man->rotate_c, "offset",
 +                      &(const struct wmManipulatorPropertyFnParams) {
 +                          .value_get_fn = manipulator_bisect_prop_angle_get,
 +                          .value_set_fn = manipulator_bisect_prop_angle_set,
 +                          .range_get_fn = NULL,
 +                          .user_data = NULL,
 +                      });
 +      }
 +}
 +
 +static void manipulator_mesh_bisect_draw_prepare(
 +        const bContext *UNUSED(C), wmManipulatorGroup *mgroup)
 +{
 +      ManipulatorGroup *man = mgroup->customdata;
 +      if (man->data.op->next) {
 +              man->data.op = WM_operator_last_redo((bContext *)man->data.context);
 +      }
 +      manipulator_mesh_bisect_update_from_op(man);
 +}
 +
 +static void MESH_WGT_bisect(struct wmManipulatorGroupType *wgt)
 +{
 +      wgt->name = "Mesh Bisect";
 +      wgt->idname = "MESH_WGT_bisect";
 +
 +      wgt->flag = WM_MANIPULATORGROUPTYPE_3D;
 +
 +      wgt->mmap_params.spaceid = SPACE_VIEW3D;
 +      wgt->mmap_params.regionid = RGN_TYPE_WINDOW;
 +
 +      wgt->poll = manipulator_mesh_bisect_poll;
 +      wgt->setup = manipulator_mesh_bisect_setup;
 +      wgt->draw_prepare = manipulator_mesh_bisect_draw_prepare;
 +}
 +
 +/** \} */
 +
 +#endif  /* USE_MANIPULATOR */