Skip to content
Snippets Groups Projects
space_view3d_materials_utils.py 27 KiB
Newer Older
  • Learn to ignore specific revisions
  • #(c) 2010 Michael Williamson (michaelw)
    
    #ported from original by Michael Williamson
    
    #
    #
    # ##### BEGIN GPL LICENSE BLOCK #####
    #
    #  This program is free software; you can redistribute it and/or
    #  modify it under the terms of the GNU General Public License
    #  as published by the Free Software Foundation; either version 2
    #  of the License, or (at your option) any later version.
    #
    #  This program is distributed in the hope that it will be useful,
    #  but WITHOUT ANY WARRANTY; without even the implied warranty of
    #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    #  GNU General Public License for more details.
    #
    #  You should have received a copy of the GNU General Public License
    #  along with this program; if not, write to the Free Software Foundation,
    #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    #
    # ##### END GPL LICENSE BLOCK #####
    
    
    bl_info = {
    
    Luca Bonavita's avatar
    Luca Bonavita committed
        "name": "Material Utils",
    
        "author": "michaelw",
    
        "location": "View3D > Q key",
    
    Campbell Barton's avatar
    Campbell Barton committed
        "description": "Menu of material tools (assign, select..)  in the 3D View",
    
        "warning": "Buggy, Broken in Cycles mode",
    
        "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"
    
    Campbell Barton's avatar
    Campbell Barton committed
                    "Scripts/3D interaction/Materials Utils",
    
        "tracker_url": "https://developer.blender.org/T22140",
    
    This script has several functions and operators, grouped for convenience:
    
    
    Campbell Barton's avatar
    Campbell Barton committed
        offers the user a list of ALL the materials in the blend file and an
        additional "new" entry the chosen material will be assigned to all the
        selected objects in object mode.
    
    
    Brendon Murphy's avatar
    Brendon Murphy committed
        in edit mode the selected polygons get the selected material applied.
    
    Campbell Barton's avatar
    Campbell Barton committed
        if the user chose "new" the new material can be renamed using the
        "last operator" section of the toolbox.
        After assigning the material "clean material slots" and
        "material to texface" are auto run to keep things tidy
        (see description bellow)
    
    * select by material
    
    Campbell Barton's avatar
    Campbell Barton committed
        in object mode this offers the user a menu of all materials in the blend
        file any objects using the selected material will become selected, any
        objects without the material will be removed from selection.
    
        in edit mode:  the menu offers only the materials attached to the current
    
    Brendon Murphy's avatar
    Brendon Murphy committed
        object. It will select the polygons that use the material and deselect those
    
    Campbell Barton's avatar
    Campbell Barton committed
        that do not.
    
    * clean material slots
    
    Campbell Barton's avatar
    Campbell Barton committed
        for all selected objects any empty material slots or material slots with
    
    Brendon Murphy's avatar
    Brendon Murphy committed
        materials that are not used by the mesh polygons will be removed.
    
    * remove material slots
        removes all material slots of the active object.
    
    * material to texface
        transfers material assignments to the UV editor. This is useful if you
        assigned materials in the properties editor, as it will use the already
        set up materials to assign the UV images per-face. It will use the first
        enabled image texture it finds.
    
    * texface to materials
        creates texture materials from images assigned in UV editor.
    
    * replace materials
        lets your replace one material by another. Optionally for all objects in
        the blend, otherwise for selected editable objects only. An additional
        option allows you to update object selection, to indicate which objects
        were affected and which not.
    
    
    * set fake user
        enable/disable fake user for materials. You can chose for which materials
        it shall be set, materials of active / selected / objects in current scene
        or used / unused / all materials.
    
    
    from bpy.props import StringProperty, BoolProperty, EnumProperty
    
    
    def fake_user_set(fake_user='ON', materials='UNUSED'):
        if materials == 'ALL':
            mats = (mat for mat in bpy.data.materials if mat.library is None)
        elif materials == 'UNUSED':
            mats = (mat for mat in bpy.data.materials if mat.library is None and mat.users == 0)
        else:
            mats = []
            if materials == 'ACTIVE':
                objs = [bpy.context.active_object]
            elif materials == 'SELECTED':
                objs = bpy.context.selected_objects
            elif materials == 'SCENE':
                objs = bpy.context.scene.objects
            else: # materials == 'USED'
                objs = bpy.data.objects
                # Maybe check for users > 0 instead?
    
            """ more reable than the following generator:
            for ob in objs:
                if hasattr(ob.data, "materials"):
                    for mat in ob.data.materials:
                        if mat.library is None: #and not in mats:
                            mats.append(mat)
            """
            mats = (mat for ob in objs if hasattr(ob.data, "materials") for mat in ob.data.materials if mat.library is None)
    
        for mat in mats:
            mat.use_fake_user = fake_user == 'ON'
    
        for area in bpy.context.screen.areas:
            if area.type in ('PROPERTIES', 'NODE_EDITOR'):
                area.tag_redraw()
    
    def replace_material(m1, m2, all_objects=False, update_selection=False):
    
    Campbell Barton's avatar
    Campbell Barton committed
        # replace material named m1 with material named m2
        # m1 is the name of original material
        # m2 is the name of the material to replace it with
        # 'all' will replace throughout the blend file
    
        matorg = bpy.data.materials.get(m1)
        matrep = bpy.data.materials.get(m2)
    
    
        if matorg != matrep and None not in (matorg, matrep):
    
            #store active object
            scn = bpy.context.scene
    
            if all_objects:
                objs = bpy.data.objects
    
            else:
                objs = bpy.context.selected_editable_objects
    
            for ob in objs:
                if ob.type == 'MESH':
    
                        if m.material == matorg:
                            m.material = matrep
    
    Campbell Barton's avatar
    Campbell Barton committed
                            # don't break the loop as the material can be
    
                            # Indicate which objects were affected
                            if update_selection:
                                ob.select = True
                                match = True
    
                    if update_selection and not match:
                        ob.select = False
    
        #else:
        #    print('Replace material: nothing to replace')
    
    Campbell Barton's avatar
    Campbell Barton committed
    
    def select_material_by_name(find_mat_name):
        #in object mode selects all objects with material find_mat_name
    
    Brendon Murphy's avatar
    Brendon Murphy committed
        #in edit mode selects all polygons with material find_mat_name
    
    Campbell Barton's avatar
    Campbell Barton committed
    
        find_mat = bpy.data.materials.get(find_mat_name)
    
        if find_mat is None:
            return
    
    
        #check for editmode
        editmode = False
    
        scn = bpy.context.scene
    
    Brendon Murphy's avatar
    Brendon Murphy committed
        #set selection mode to polygons
    
    Campbell Barton's avatar
    Campbell Barton committed
        scn.tool_settings.mesh_select_mode = False, False, True
    
    
        actob = bpy.context.active_object
        if actob.mode == 'EDIT':
    
    Campbell Barton's avatar
    Campbell Barton committed
            editmode = True
    
    Campbell Barton's avatar
    Campbell Barton committed
            objs = bpy.data.objects
    
    Campbell Barton's avatar
    Campbell Barton committed
                if ob.type in {'MESH', 'CURVE', 'SURFACE', 'FONT', 'META'}:
    
    Campbell Barton's avatar
    Campbell Barton committed
                        if m.material == find_mat:
    
                            ob.select = True
    
    Campbell Barton's avatar
    Campbell Barton committed
                            # the active object may not have the mat!
                            # set it to one that does!
    
                            scn.objects.active = ob
                            break
                        else:
    
                            ob.select = False
    
    Campbell Barton's avatar
    Campbell Barton committed
    
                #deselect non-meshes
    
                    ob.select = False
    
    Brendon Murphy's avatar
    Brendon Murphy committed
            #it's editmode, so select the polygons
    
            #same material can be on multiple slots
    
    Campbell Barton's avatar
    Campbell Barton committed
            slot_indeces = []
    
    Campbell Barton's avatar
    Campbell Barton committed
            # found = False  # UNUSED
    
    Campbell Barton's avatar
    Campbell Barton committed
                if m.material == find_mat:
    
    Campbell Barton's avatar
    Campbell Barton committed
                    # found = True  # UNUSED
    
    Brendon Murphy's avatar
    Brendon Murphy committed
            for f in me.polygons:
    
                if f.material_index in slot_indeces:
    
                    f.select = True
    
                    f.select = False
    
    Campbell Barton's avatar
    Campbell Barton committed
            me.update()
    
    Campbell Barton's avatar
    Campbell Barton committed
            bpy.ops.object.mode_set(mode='EDIT')
    
    
    Brendon Murphy's avatar
    Brendon Murphy committed
        # assigns the first image in each material to the polygons in the active
    
    Campbell Barton's avatar
    Campbell Barton committed
        # uvlayer for all selected objects
    
    
        #check for editmode
        editmode = False
    
        actob = bpy.context.active_object
        if actob.mode == 'EDIT':
    
    Campbell Barton's avatar
    Campbell Barton committed
            editmode = True
    
        for ob in bpy.context.selected_editable_objects:
    
            if ob.type == 'MESH':
                #get the materials from slots
    
                #build a list of images, one per material
    
    Campbell Barton's avatar
    Campbell Barton committed
                images = []
    
                #get the textures from the mats
                for m in ms:
    
                    gotimage = False
    
                    textures = zip(m.material.texture_slots, m.material.use_textures)
                    for t, enabled in textures:
                        if enabled and t is not None:
                            tex = t.texture
                            if tex.type == 'IMAGE':
                                img = tex.image
                                images.append(img)
                                gotimage = True
                                break
    
                    if not gotimage:
                        print('noimage on', m.name)
                        images.append(None)
    
    Campbell Barton's avatar
    Campbell Barton committed
    
                # now we have the images
                # applythem to the uvlayer
    
    
                me = ob.data
                #got uvs?
                if not me.uv_textures:
                    scn = bpy.context.scene
                    scn.objects.active = ob
                    bpy.ops.mesh.uv_texture_add()
                    scn.objects.active = actob
    
                #get active uvlayer
                for t in  me.uv_textures:
                    if t.active:
    
    Brendon Murphy's avatar
    Brendon Murphy committed
                        for f in me.polygons:
    
                            #check that material had an image!
    
                            if images[f.material_index] is not None:
    
                                uvtex[f.index].image = images[f.material_index]
                            else:
    
                                uvtex[f.index].image = None
    
    Campbell Barton's avatar
    Campbell Barton committed
            bpy.ops.object.mode_set(mode='EDIT')
    
    
    
    def assignmatslots(ob, matlist):
        #given an object and a list of material names
        #removes all material slots form the object
        #adds new ones for each material in matlist
        #adds the materials to the slots as well.
    
        scn = bpy.context.scene
        ob_active = bpy.context.active_object
        scn.objects.active = ob
    
        for s in ob.material_slots:
            bpy.ops.object.material_slot_remove()
    
    
    Campbell Barton's avatar
    Campbell Barton committed
        # re-add them and assign material
    
        i = 0
        for m in matlist:
            mat = bpy.data.materials[m]
    
            ob.data.materials.append(mat)
    
    Campbell Barton's avatar
    Campbell Barton committed
        # restore active object:
    
        scn.objects.active = ob_active
    
    
    def cleanmatslots():
        #check for edit mode
        editmode = False
        actob = bpy.context.active_object
        if actob.mode == 'EDIT':
    
    Campbell Barton's avatar
    Campbell Barton committed
            editmode = True
    
            bpy.ops.object.mode_set()
    
        objs = bpy.context.selected_editable_objects
    
            if ob.type == 'MESH':
                mats = ob.material_slots.keys()
    
    Brendon Murphy's avatar
    Brendon Murphy committed
                #check the polygons on the mesh to build a list of used materials
    
    Campbell Barton's avatar
    Campbell Barton committed
                usedMatIndex = []  # we'll store used materials indices here
                faceMats = []
    
    Brendon Murphy's avatar
    Brendon Murphy committed
                for f in me.polygons:
    
                    #get the material index for this face...
                    faceindex = f.material_index
    
                    #indices will be lost: Store face mat use by name
                    currentfacemat = mats[faceindex]
                    faceMats.append(currentfacemat)
    
    Campbell Barton's avatar
    Campbell Barton committed
    
                    # check if index is already listed as used or not
    
                    found = 0
                    for m in usedMatIndex:
                        if m == faceindex:
                            found = 1
                            #break
    
    Campbell Barton's avatar
    Campbell Barton committed
                    #add this index to the list
    
                        usedMatIndex.append(faceindex)
    
                #re-assign the used mats to the mesh and leave out the unused
                ml = []
                mnames = []
                for u in usedMatIndex:
    
    Campbell Barton's avatar
    Campbell Barton committed
                    ml.append(mats[u])
    
                    #we'll need a list of names to get the face indices...
                    mnames.append(mats[u])
    
                assignmatslots(ob, ml)
    
    Campbell Barton's avatar
    Campbell Barton committed
    
                # restore face indices:
    
    Brendon Murphy's avatar
    Brendon Murphy committed
                for f in me.polygons:
    
                    matindex = mnames.index(faceMats[i])
                    f.material_index = matindex
                    i += 1
    
    
    Campbell Barton's avatar
    Campbell Barton committed
            bpy.ops.object.mode_set(mode='EDIT')
    
    Campbell Barton's avatar
    Campbell Barton committed
    def assign_mat(matname="Default"):
        # get active object so we can restore it later
    
        actob = bpy.context.active_object
    
    Campbell Barton's avatar
    Campbell Barton committed
    
        # check if material exists, if it doesn't then create it
    
    Campbell Barton's avatar
    Campbell Barton committed
        for m in bpy.data.materials:
    
            if m.name == matname:
                target = m
                found = True
                break
        if not found:
            target = bpy.data.materials.new(matname)
    
    Brendon Murphy's avatar
    Brendon Murphy committed
        # if objectmode then set all polygons
    
    Brendon Murphy's avatar
    Brendon Murphy committed
        allpolygons = True
    
    Campbell Barton's avatar
    Campbell Barton committed
            editmode = True
    
    Brendon Murphy's avatar
    Brendon Murphy committed
            allpolygons = False
    
        objs = bpy.context.selected_editable_objects
    
    Campbell Barton's avatar
    Campbell Barton committed
    
        for ob in objs:
            # set the active object to our object
    
            scn = bpy.context.scene
            scn.objects.active = ob
    
    Campbell Barton's avatar
    Campbell Barton committed
    
            if ob.type in {'CURVE', 'SURFACE', 'FONT', 'META'}:
                found = False
    
    Campbell Barton's avatar
    Campbell Barton committed
                for m in bpy.data.materials:
    
                    if m.name == matname:
    
    Campbell Barton's avatar
    Campbell Barton committed
                        found = True
    
                        index = i
                        break
                    i += 1
                    if not found:
    
    Campbell Barton's avatar
    Campbell Barton committed
                        index = i - 1
                targetlist = [index]
    
                assignmatslots(ob, targetlist)
    
    Campbell Barton's avatar
    Campbell Barton committed
    
            elif ob.type == 'MESH':
                # check material slots for matname material
                found = False
    
                i = 0
                mats = ob.material_slots
                for m in mats:
                    if m.name == matname:
    
    Campbell Barton's avatar
    Campbell Barton committed
                        found = True
    
                        index = i
                        #make slot active
                        ob.active_material_index = i
                        break
                    i += 1
    
    Campbell Barton's avatar
    Campbell Barton committed
                    index = i
                    #the material is not attached to the object
                    ob.data.materials.append(target)
    
    
    Campbell Barton's avatar
    Campbell Barton committed
                me = ob.data
    
    Brendon Murphy's avatar
    Brendon Murphy committed
                if allpolygons:
                    for f in me.polygons:
    
    Brendon Murphy's avatar
    Brendon Murphy committed
                elif allpolygons == False:
                    for f in me.polygons:
    
    
        #restore the active object
        bpy.context.scene.objects.active = actob
        if editmode:
    
    Campbell Barton's avatar
    Campbell Barton committed
            bpy.ops.object.mode_set(mode='EDIT')
    
    Campbell Barton's avatar
    Campbell Barton committed
    def check_texture(img, mat):
    
        #finds a texture from an image
        #makes a texture if needed
        #adds it to the material if it isn't there already
    
    
        tex = bpy.data.textures.get(img.name)
    
        if tex is None:
            tex = bpy.data.textures.new(name=img.name, type='IMAGE')
    
        tex.image = img
    
        #see if the material already uses this tex
        #add it if needed
        found = False
        for m in mat.texture_slots:
            if m and m.texture == tex:
                found = True
                break
        if not found and mat:
    
            mtex = mat.texture_slots.add()
            mtex.texture = tex
            mtex.texture_coords = 'UV'
            mtex.use_map_color_diffuse = True
    
    def texface_to_mat():
        # editmode check here!
        editmode = False
        ob = bpy.context.object
    
    Campbell Barton's avatar
    Campbell Barton committed
        if ob.mode == 'EDIT':
    
            editmode = True
            bpy.ops.object.mode_set()
    
    
        for ob in bpy.context.selected_editable_objects:
    
    
            faceindex = []
            unique_images = []
    
            # get the texface images and store indices
            if (ob.data.uv_textures):
    
                for f in ob.data.uv_textures.active.data:
    
    Campbell Barton's avatar
    Campbell Barton committed
                    if f.image:
    
                        img = f.image
                        #build list of unique images
                        if img not in unique_images:
                            unique_images.append(img)
                        faceindex.append(unique_images.index(img))
    
                    else:
                        img = None
    
    Campbell Barton's avatar
    Campbell Barton committed
                        faceindex.append(None)
    
    Campbell Barton's avatar
    Campbell Barton committed
            # check materials for images exist; create if needed
    
            matlist = []
            for i in unique_images:
                if i:
                    try:
                        m = bpy.data.materials[i.name]
                    except:
    
    Campbell Barton's avatar
    Campbell Barton committed
                        m = bpy.data.materials.new(name=i.name)
    
                        continue
    
                    finally:
                        matlist.append(m.name)
                        # add textures if needed
    
    Campbell Barton's avatar
    Campbell Barton committed
                        check_texture(i, m)
    
    Campbell Barton's avatar
    Campbell Barton committed
            # set up the object material slots
    
            assignmatslots(ob, matlist)
    
            #set texface indices to material slot indices..
            me = ob.data
    
            i = 0
            for f in faceindex:
    
    Brendon Murphy's avatar
    Brendon Murphy committed
                    me.polygons[i].material_index = f
    
                i += 1
        if editmode:
    
    Campbell Barton's avatar
    Campbell Barton committed
            bpy.ops.object.mode_set(mode='EDIT')
    
    def remove_materials():
    
    	for ob in bpy.data.objects:
    		print (ob.name)
    		try:
    			bpy.ops.object.material_slot_remove()
    			print ("removed material from " + ob.name)
    		except:
    			print (ob.name + " does not have materials.")
    
    Campbell Barton's avatar
    Campbell Barton committed
    # -----------------------------------------------------------------------------
    # operator classes:
    
    
    class VIEW3D_OT_texface_to_material(bpy.types.Operator):
    
        """Create texture materials for images assigned in UV editor"""
    
        bl_idname = "view3d.texface_to_material"
    
        bl_label = "Texface Images to Material/Texture (Material Utils)"
    
        bl_options = {'REGISTER', 'UNDO'}
    
    
        @classmethod
        def poll(cls, context):
    
            return context.active_object is not None
    
            if context.selected_editable_objects:
                texface_to_mat()
                return {'FINISHED'}
            else:
    
    Campbell Barton's avatar
    Campbell Barton committed
                self.report({'WARNING'},
                            "No editable selected objects, could not finish")
    
                return {'CANCELLED'}
    
    class VIEW3D_OT_assign_material(bpy.types.Operator):
    
        """Assign a material to the selection"""
    
        bl_idname = "view3d.assign_material"
    
        bl_label = "Assign Material (Material Utils)"
    
        bl_options = {'REGISTER', 'UNDO'}
    
    
    Campbell Barton's avatar
    Campbell Barton committed
        matname = StringProperty(
                name='Material Name',
                description='Name of Material to Assign',
                default="",
    
        @classmethod
        def poll(cls, context):
    
            return context.active_object is not None
    
            print(mn)
            assign_mat(mn)
            cleanmatslots()
            mat_to_texface()
            return {'FINISHED'}
    
    
    class VIEW3D_OT_clean_material_slots(bpy.types.Operator):
    
        """Removes any material slots from selected objects """ \
        """that are not used by the mesh"""
    
        bl_idname = "view3d.clean_material_slots"
    
        bl_label = "Clean Material Slots (Material Utils)"
    
        bl_options = {'REGISTER', 'UNDO'}
    
    
        @classmethod
        def poll(cls, context):
    
            return context.active_object is not None
    
    
        def execute(self, context):
            cleanmatslots()
            return {'FINISHED'}
    
    
    class VIEW3D_OT_material_to_texface(bpy.types.Operator):
    
        """Transfer material assignments to UV editor"""
    
        bl_idname = "view3d.material_to_texface"
    
        bl_label = "Material Images to Texface (Material Utils)"
    
        bl_options = {'REGISTER', 'UNDO'}
    
    
        @classmethod
        def poll(cls, context):
    
            return context.active_object is not None
    
    
        def execute(self, context):
            mat_to_texface()
            return {'FINISHED'}
    
    
    class VIEW3D_OT_material_remove(bpy.types.Operator):
    
        """Remove all material slots from active objects"""
    
        bl_idname = "view3d.material_remove"
    
        bl_label = "Remove All Material Slots (Material Utils)"
    
        bl_options = {'REGISTER', 'UNDO'}
    
        @classmethod
        def poll(cls, context):
    
            return context.active_object is not None
    
    
        def execute(self, context):
            remove_materials()
            return {'FINISHED'}
    
    
    class VIEW3D_OT_select_material_by_name(bpy.types.Operator):
    
        """Select geometry with this material assigned to it"""
    
        bl_idname = "view3d.select_material_by_name"
    
        bl_label = "Select Material By Name (Material Utils)"
    
        bl_options = {'REGISTER', 'UNDO'}
    
    Campbell Barton's avatar
    Campbell Barton committed
        matname = StringProperty(
                name='Material Name',
                description='Name of Material to Select',
    
        @classmethod
        def poll(cls, context):
    
            return context.active_object is not None
    
            select_material_by_name(mn)
            return {'FINISHED'}
    
    
    class VIEW3D_OT_replace_material(bpy.types.Operator):
    
        """Replace a material by name"""
    
        bl_idname = "view3d.replace_material"
    
        bl_label = "Replace Material (Material Utils)"
    
        bl_options = {'REGISTER', 'UNDO'}
    
    
    Campbell Barton's avatar
    Campbell Barton committed
        matorg = StringProperty(
    
                name="Original",
                description="Material to replace",
    
        matrep = StringProperty(name="Replacement",
                description="Replacement material",
    
    Campbell Barton's avatar
    Campbell Barton committed
                )
        all_objects = BoolProperty(
    
                name="All objects",
                description="Replace for all objects in this blend file",
    
    Campbell Barton's avatar
    Campbell Barton committed
                default=True,
                )
    
        update_selection = BoolProperty(
                name="Update Selection",
                description="Select affected objects and deselect unaffected",
                default=True,
                )
    
        # Allow to replace all objects even without a selection / active object
        #@classmethod
        #def poll(cls, context):
        #    return context.active_object is not None
    
        def draw(self, context):
            layout = self.layout
            layout.prop_search(self, "matorg", bpy.data, "materials")
            layout.prop_search(self, "matrep", bpy.data, "materials")
            layout.prop(self, "all_objects")
            layout.prop(self, "update_selection")
    
        def invoke(self, context, event):
            return context.window_manager.invoke_props_dialog(self)
    
            replace_material(self.matorg, self.matrep, self.all_objects, self.update_selection)
    
    class VIEW3D_OT_fake_user_set(bpy.types.Operator):
        """Enable/disable fake user for materials"""
        bl_idname = "view3d.fake_user_set"
        bl_label = "Set Fake User (Material Utils)"
        bl_options = {'REGISTER', 'UNDO'}
    
        fake_user = EnumProperty(
                name="Fake User",
                description="Turn fake user on or off",
                items=(('ON', "On", "Enable fake user"),('OFF', "Off", "Disable fake user")),
                default='ON'
                )
    
        materials = EnumProperty(
                name="Materials",
                description="Which materials of objects to affect",
                items=(('ACTIVE', "Active object", "Materials of active object only"),
                       ('SELECTED', "Selected objects", "Materials of selected objects"),
                       ('SCENE', "Scene objects", "Materials of objects in current scene"),
                       ('USED', "Used", "All materials used by objects"),
                       ('UNUSED', "Unused", "Currently unused materials"),
                       ('ALL', "All", "All materials in this blend file")),
                default='UNUSED'
                )
    
        def draw(self, context):
            layout = self.layout
            layout.prop(self, "fake_user", expand=True)
            layout.prop(self, "materials")
    
        def invoke(self, context, event):
            return context.window_manager.invoke_props_dialog(self)
    
        def execute(self, context):
            fake_user_set(self.fake_user, self.materials)
            return {'FINISHED'}
    
    
    
    Campbell Barton's avatar
    Campbell Barton committed
    # -----------------------------------------------------------------------------
    # menu classes
    
    
    class VIEW3D_MT_master_material(bpy.types.Menu):
    
        bl_label = "Material Utils Menu"
    
    
        def draw(self, context):
            layout = self.layout
            layout.operator_context = 'INVOKE_REGION_WIN'
    
            layout.menu("VIEW3D_MT_assign_material", icon='ZOOMIN')
            layout.menu("VIEW3D_MT_select_material", icon='HAND')
            layout.separator()
    
    Campbell Barton's avatar
    Campbell Barton committed
            layout.operator("view3d.clean_material_slots",
    
                            text="Clean Material Slots",
    
    Campbell Barton's avatar
    Campbell Barton committed
                            icon='CANCEL')
    
            layout.operator("view3d.material_remove",
                            text="Remove Material Slots",
                            icon='CANCEL')
    
            layout.operator("view3d.material_to_texface",
    
                            text="Material to Texface",
                            icon='MATERIAL_DATA')
    
            layout.operator("view3d.texface_to_material",
    
    Campbell Barton's avatar
    Campbell Barton committed
                            text="Texface to Material",
    
                            icon='MATERIAL_DATA')
    
    Campbell Barton's avatar
    Campbell Barton committed
            layout.operator("view3d.replace_material",
                            text='Replace Material',
                            icon='ARROW_LEFTRIGHT')
    
            layout.operator("view3d.fake_user_set",
                            text='Set Fake User',
                            icon='UNPINNED')
    
    
    
    class VIEW3D_MT_assign_material(bpy.types.Menu):
        bl_label = "Assign Material"
    
        def draw(self, context):
            layout = self.layout
            layout.operator_context = 'INVOKE_REGION_WIN'
    
    Campbell Barton's avatar
    Campbell Barton committed
            for material_name in bpy.data.materials.keys():
    
                layout.operator("view3d.assign_material",
    
    Campbell Barton's avatar
    Campbell Barton committed
                    text=material_name,
                    icon='MATERIAL_DATA').matname = material_name
    
            layout.operator("view3d.assign_material",
                            text="Add New",
                            icon='ZOOMIN')
    
    
    
    class VIEW3D_MT_select_material(bpy.types.Menu):
        bl_label = "Select by Material"
    
        def draw(self, context):
            layout = self.layout
            layout.operator_context = 'INVOKE_REGION_WIN'
    
            ob = context.object
            layout.label
            if ob.mode == 'OBJECT':
    
                #show all used materials in entire blend file
    
    Campbell Barton's avatar
    Campbell Barton committed
                for material_name, material in bpy.data.materials.items():
                    if material.users > 0:
    
                        layout.operator("view3d.select_material_by_name",
    
    Campbell Barton's avatar
    Campbell Barton committed
                                        text=material_name,
                                        icon='MATERIAL_DATA',
                                        ).matname = material_name
    
    
            elif ob.mode == 'EDIT':
                #show only the materials on this object
                mats = ob.material_slots.keys()
                for m in mats:
    
                    layout.operator("view3d.select_material_by_name",
    
    Campbell Barton's avatar
    Campbell Barton committed
                        text=m,
    
                        icon='MATERIAL_DATA').matname = m
    
    
    def register():
    
        kc = bpy.context.window_manager.keyconfigs.addon
    
        if kc:
            km = kc.keymaps.new(name="3D View", space_type="VIEW_3D")
            kmi = km.keymap_items.new('wm.call_menu', 'Q', 'PRESS')
            kmi.properties.name = "VIEW3D_MT_master_material"
    
        kc = bpy.context.window_manager.keyconfigs.addon
    
        if kc:
            km = kc.keymaps["3D View"]
            for kmi in km.keymap_items:
                if kmi.idname == 'wm.call_menu':
                    if kmi.properties.name == "VIEW3D_MT_master_material":
                        km.keymap_items.remove(kmi)
                        break