Skip to content
Snippets Groups Projects
operators.py 49.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • # SPDX-License-Identifier: GPL-2.0-or-later
    
    from copy import deepcopy
    
    
    from bpy.types import (
        Operator,
    )
    
    
    from bpy.props import (
        BoolProperty,
        StringProperty,
        IntProperty
    
    from .internals import (
        update_property_group,
    
        generate_state,
        check_state,
    
        get_move_selection,
        get_move_active,
    
    from .operator_utils import (
        apply_to_children,
        isolate_rto,
        toggle_children,
        activate_all_rtos,
        invert_rtos,
        copy_rtos,
        swap_rtos,
        clear_copy,
        clear_swap,
    
        link_child_collections_to_parent,
        remove_collection,
    
        select_collection_objects,
    
        set_exclude_state,
    
        isolate_sel_objs_collections,
        disable_sel_objs_collections,
    
    class SetActiveCollection(Operator):
        '''Set the active collection'''
        bl_label = "Set Active Collection"
        bl_idname = "view3d.set_active_collection"
    
        is_master_collection: BoolProperty()
    
        collection_name: StringProperty()
    
        def execute(self, context):
    
            if self.is_master_collection:
    
                layer_collection = context.view_layer.layer_collection
    
            else:
    
                laycol = internals.layer_collections[self.collection_name]
    
                layer_collection = laycol["ptr"]
    
    
                # set selection to this row
                cm = context.scene.collection_manager
                cm.cm_list_index = laycol["row_index"]
    
    
            context.view_layer.active_layer_collection = layer_collection
    
    
            if context.view_layer.active_layer_collection != layer_collection:
                self.report({'WARNING'}, "Can't set excluded collection as active")
    
    
    class ExpandAllOperator(Operator):
    
        '''Expand/Collapse all collections'''
        bl_label = "Expand All Items"
        bl_idname = "view3d.expand_all_items"
    
        def execute(self, context):
    
            if len(internals.expanded) > 0:
                internals.expanded.clear()
    
                context.scene.collection_manager.cm_list_index = 0
    
                for laycol in internals.layer_collections.values():
    
                    if laycol["ptr"].children:
    
                        internals.expanded.add(laycol["name"])
    
            # clear expand history
    
            internals.expand_history["target"] = ""
            internals.expand_history["history"].clear()
    
            # update tree view
            update_property_group(context)
    
    class ExpandSublevelOperator(Operator):
    
        bl_label = "Expand Sublevel Items"
    
        bl_description = (
            "  * Ctrl+LMB - Expand/Collapse all sublevels\n"
            "  * Shift+LMB - Isolate tree/Restore\n"
            "  * Alt+LMB - Discard history"
            )
    
        bl_idname = "view3d.expand_sublevel"
    
        expand: BoolProperty()
        name: StringProperty()
        index: IntProperty()
    
        def invoke(self, context, event):
    
            cls = ExpandSublevelOperator
    
            modifiers = get_modifiers(event)
    
    
            if modifiers == {"alt"}:
    
                internals.expand_history["target"] = ""
                internals.expand_history["history"].clear()
    
                # expand/collapse all subcollections
                expand = None
    
                # check whether to expand or collapse
    
                if self.name in internals.expanded:
                    internals.expanded.remove(self.name)
    
                    internals.expanded.add(self.name)
    
                # do expanding/collapsing
    
                def set_expanded(layer_collection):
                    if expand:
    
                        internals.expanded.add(layer_collection.name)
    
                        internals.expanded.discard(layer_collection.name)
    
                apply_to_children(internals.layer_collections[self.name]["ptr"], set_expanded)
    
                internals.expand_history["target"] = ""
                internals.expand_history["history"].clear()
    
    
            elif modifiers == {"shift"}:
                def isolate_tree(current_laycol):
                    parent = current_laycol["parent"]
    
                    for laycol in parent["children"]:
    
                        if (laycol["name"] != current_laycol["name"]
                        and laycol["name"] in internals.expanded):
                            internals.expanded.remove(laycol["name"])
                            internals.expand_history["history"].append(laycol["name"])
    
    
                    if parent["parent"]:
                        isolate_tree(parent)
    
    
                if self.name == internals.expand_history["target"]:
                    for item in internals.expand_history["history"]:
                        internals.expanded.add(item)
    
                    internals.expand_history["target"] = ""
                    internals.expand_history["history"].clear()
    
                    internals.expand_history["target"] = ""
                    internals.expand_history["history"].clear()
    
                    isolate_tree(internals.layer_collections[self.name])
                    internals.expand_history["target"] = self.name
    
            else:
                # expand/collapse collection
                if self.expand:
    
                    internals.expanded.add(self.name)
    
                    internals.expanded.remove(self.name)
    
                internals.expand_history["target"] = ""
                internals.expand_history["history"].clear()
    
            # set the selected row to the collection you're expanding/collapsing to
            # preserve the tree view's scrolling
            context.scene.collection_manager.cm_list_index = self.index
    
            update_property_group(context)
    
    class CMSelectCollectionObjectsOperator(Operator):
        bl_label = "Select All Objects in the Collection"
        bl_description = (
            "  * LMB - Select all objects in collection.\n"
            "  * Shift+LMB - Add/Remove collection objects from selection.\n"
            "  * Ctrl+LMB - Isolate nested selection.\n"
            "  * Ctrl+Shift+LMB - Add/Remove nested from selection"
            )
        bl_idname = "view3d.select_collection_objects"
        bl_options = {'REGISTER', 'UNDO'}
    
    
        is_master_collection: BoolProperty()
    
        collection_name: StringProperty()
    
        def invoke(self, context, event):
            modifiers = get_modifiers(event)
    
            if modifiers == {"shift"}:
                select_collection_objects(
    
                    is_master_collection=self.is_master_collection,
    
                    collection_name=self.collection_name,
                    replace=False,
                    nested=False
                    )
    
            elif modifiers == {"ctrl"}:
                select_collection_objects(
    
                    is_master_collection=self.is_master_collection,
    
                    collection_name=self.collection_name,
                    replace=True,
                    nested=True
                    )
    
            elif modifiers == {"ctrl", "shift"}:
                select_collection_objects(
    
                    is_master_collection=self.is_master_collection,
    
                    collection_name=self.collection_name,
                    replace=False,
                    nested=True
                    )
    
            else:
                select_collection_objects(
    
                    is_master_collection=self.is_master_collection,
    
                    collection_name=self.collection_name,
                    replace=True,
                    nested=False
                    )
    
            return {'FINISHED'}
    
    
    
    class SelectAllCumulativeObjectsOperator(Operator):
        '''Select all objects that are present in more than one collection'''
        bl_label = "Select All Cumulative Objects"
        bl_idname = "view3d.select_all_cumulative_objects"
    
        def execute(self, context):
            selected_cumulative_objects = 0
            total_cumulative_objects = 0
    
            bpy.ops.object.select_all(action='DESELECT')
    
            for obj in bpy.data.objects:
                if len(obj.users_collection) > 1:
                    if obj.visible_get():
                        obj.select_set(True)
                        if obj.select_get() == True: # needed because obj.select_set can fail silently
                            selected_cumulative_objects +=1
    
                    total_cumulative_objects += 1
    
            self.report({'INFO'}, f"{selected_cumulative_objects}/{total_cumulative_objects} Cumulative Objects Selected")
    
            return {'FINISHED'}
    
    
    
    class CMSendObjectsToCollectionOperator(Operator):
        bl_label = "Send Objects to Collection"
    
        bl_description = (
    
            "  * LMB - Move objects to collection.\n"
            "  * Shift+LMB - Add/Remove objects from collection"
    
        bl_idname = "view3d.send_objects_to_collection"
    
        bl_options = {'REGISTER', 'UNDO'}
    
        is_master_collection: BoolProperty()
    
        collection_name: StringProperty()
    
        def invoke(self, context, event):
    
            if self.is_master_collection:
    
                target_collection = context.view_layer.layer_collection.collection
    
            else:
    
                laycol = internals.layer_collections[self.collection_name]
    
                target_collection = laycol["ptr"].collection
    
    
            selected_objects = get_move_selection()
            active_object = get_move_active()
    
            internals.move_triggered = True
    
            if not selected_objects:
                return {'CANCELLED'}
    
                # add objects to collection
    
                # make sure there is an active object
                if not active_object:
    
                    active_object = tuple(selected_objects)[0]
    
                # check if in collection
    
                if not active_object.name in target_collection.objects:
    
                    # add to collection
    
                    for obj in selected_objects:
                        if obj.name not in target_collection.objects:
                            target_collection.objects.link(obj)
    
                    warnings = False
                    master_warning = False
    
    
                    # remove from collections
                    for obj in selected_objects:
                        if obj.name in target_collection.objects:
    
                            # disallow removing if only one
                            if len(obj.users_collection) == 1:
    
                                warnings = True
                                master_laycol = context.view_layer.layer_collection
                                master_collection = master_laycol.collection
    
                                if obj.name not in master_collection.objects:
                                    master_collection.objects.link(obj)
    
                                else:
                                    master_warning = True
                                    continue
    
    
    
                            # remove from collection
                            target_collection.objects.unlink(obj)
    
                    if warnings:
                        if master_warning:
                            send_report(
                            "Error removing 1 or more objects from the Scene Collection.\n"
                            "Objects would be left without a collection."
                            )
                            self.report({"WARNING"},
                            "Error removing 1 or more objects from the Scene Collection."
                            "  Objects would be left without a collection."
                            )
    
                        else:
                            self.report({"INFO"}, "1 or more objects moved to Scene Collection.")
    
                # move objects to collection
                for obj in selected_objects:
                    if obj.name not in target_collection.objects:
                        target_collection.objects.link(obj)
    
                    # remove from all other collections
                    for collection in obj.users_collection:
    
                        if collection != target_collection:
    
                            collection.objects.unlink(obj)
    
            # update the active object if needed
            if not context.active_object:
                try:
                    context.view_layer.objects.active = active_object
    
                except RuntimeError: # object not in visible collection
                    pass
    
            # update qcd header UI
    
    class CMExcludeOperator(Operator):
    
        bl_label = "[EC] Exclude from View Layer"
        bl_description = (
            "  * Shift+LMB - Isolate/Restore.\n"
            "  * Shift+Ctrl+LMB - Isolate nested/Restore.\n"
            "  * Ctrl+LMB - Toggle nested.\n"
            "  * Alt+LMB - Discard history"
            )
    
        bl_idname = "view3d.exclude_collection"
        bl_options = {'REGISTER', 'UNDO'}
    
        name: StringProperty()
    
        # static class var
        isolated = False
    
    
        def invoke(self, context, event):
    
            cls = CMExcludeOperator
    
            modifiers = get_modifiers(event)
    
            view_layer = context.view_layer.name
    
            orig_active_collection = context.view_layer.active_layer_collection
    
            orig_active_object = context.view_layer.objects.active
    
            laycol_ptr = internals.layer_collections[self.name]["ptr"]
    
            if not view_layer in internals.rto_history["exclude"]:
                internals.rto_history["exclude"][view_layer] = {"target": "", "history": []}
    
            if modifiers == {"alt"}:
    
                del internals.rto_history["exclude"][view_layer]
    
                cls.isolated = False
    
            elif modifiers == {"shift"}:
    
                isolate_rto(cls, self, view_layer, "exclude")
    
            elif modifiers == {"ctrl"}:
    
                toggle_children(self, view_layer, "exclude")
    
                cls.isolated = False
    
            elif modifiers == {"ctrl", "shift"}:
    
                isolate_rto(cls, self, view_layer, "exclude", children=True)
    
            else:
                # toggle exclusion
    
                # reset exclude history
    
                del internals.rto_history["exclude"][view_layer]
    
                set_exclude_state(laycol_ptr, not laycol_ptr.exclude)
    
            # restore active collection
    
            context.view_layer.active_layer_collection = orig_active_collection
    
    
            # restore active object if possible
            if orig_active_object:
                if orig_active_object.name in context.view_layer.objects:
                    context.view_layer.objects.active = orig_active_object
    
    
            # reset exclude all history
    
            if view_layer in internals.rto_history["exclude_all"]:
                del internals.rto_history["exclude_all"][view_layer]
    
    class CMUnExcludeAllOperator(Operator):
    
        bl_label = "[EC Global] Exclude from View Layer"
        bl_description = (
            "  * LMB - Enable all/Restore.\n"
            "  * Shift+LMB - Invert.\n"
    
            "  * Shift+Ctrl+LMB - Isolate collections w/ selected objects.\n"
            "  * Shift+Alt+LMB - Disable collections w/ selected objects.\n"
    
            "  * Ctrl+LMB - Copy/Paste RTOs.\n"
            "  * Ctrl+Alt+LMB - Swap RTOs.\n"
            "  * Alt+LMB - Discard history"
            )
    
        bl_idname = "view3d.un_exclude_all_collections"
        bl_options = {'REGISTER', 'UNDO'}
    
        def invoke(self, context, event):
    
            orig_active_collection = context.view_layer.active_layer_collection
    
            orig_active_object = context.view_layer.objects.active
    
            view_layer = context.view_layer.name
    
            modifiers = get_modifiers(event)
    
            if not view_layer in internals.rto_history["exclude_all"]:
                internals.rto_history["exclude_all"][view_layer] = []
    
            if modifiers == {"alt"}:
    
                # clear all states
    
                del internals.rto_history["exclude_all"][view_layer]
    
                clear_copy("exclude")
                clear_swap("exclude")
    
            elif modifiers == {"ctrl"}:
    
            elif modifiers == {"ctrl", "alt"}:
    
            elif modifiers == {"shift"}:
    
                invert_rtos(view_layer, "exclude")
    
            elif modifiers == {"shift", "ctrl"}:
                error = isolate_sel_objs_collections(view_layer, "exclude", "CM")
    
                if error:
                    self.report({"WARNING"}, error)
                    return {'CANCELLED'}
    
            elif modifiers == {"shift", "alt"}:
                error = disable_sel_objs_collections(view_layer, "exclude", "CM")
    
                if error:
                    self.report({"WARNING"}, error)
                    return {'CANCELLED'}
    
    
                activate_all_rtos(view_layer, "exclude")
    
            # restore active collection
    
            context.view_layer.active_layer_collection = orig_active_collection
    
    
            # restore active object if possible
            if orig_active_object:
                if orig_active_object.name in context.view_layer.objects:
                    context.view_layer.objects.active = orig_active_object
    
    
    class CMRestrictSelectOperator(Operator):
    
        bl_label = "[SS] Disable Selection"
        bl_description = (
            "  * Shift+LMB - Isolate/Restore.\n"
            "  * Shift+Ctrl+LMB - Isolate nested/Restore.\n"
            "  * Ctrl+LMB - Toggle nested.\n"
            "  * Alt+LMB - Discard history"
            )
    
        bl_idname = "view3d.restrict_select_collection"
        bl_options = {'REGISTER', 'UNDO'}
    
        name: StringProperty()
    
        # static class var
        isolated = False
    
    
        def invoke(self, context, event):
    
            cls = CMRestrictSelectOperator
    
            modifiers = get_modifiers(event)
    
            view_layer = context.view_layer.name
    
            laycol_ptr = internals.layer_collections[self.name]["ptr"]
    
            if not view_layer in internals.rto_history["select"]:
                internals.rto_history["select"][view_layer] = {"target": "", "history": []}
    
            if modifiers == {"alt"}:
    
                del internals.rto_history["select"][view_layer]
    
                cls.isolated = False
    
            elif modifiers == {"shift"}:
    
                isolate_rto(cls, self, view_layer, "select")
    
            elif modifiers == {"ctrl"}:
    
                toggle_children(self, view_layer, "select")
    
                cls.isolated = False
    
            elif modifiers == {"ctrl", "shift"}:
    
                isolate_rto(cls, self, view_layer, "select", children=True)
    
                # reset select history
    
                del internals.rto_history["select"][view_layer]
    
                # toggle selectability of collection
                laycol_ptr.collection.hide_select = not laycol_ptr.collection.hide_select
    
            # reset select all history
    
            if view_layer in internals.rto_history["select_all"]:
                del internals.rto_history["select_all"][view_layer]
    
    class CMUnRestrictSelectAllOperator(Operator):
    
        bl_label = "[SS Global] Disable Selection"
        bl_description = (
            "  * LMB - Enable all/Restore.\n"
            "  * Shift+LMB - Invert.\n"
    
            "  * Shift+Ctrl+LMB - Isolate collections w/ selected objects.\n"
            "  * Shift+Alt+LMB - Disable collections w/ selected objects.\n"
    
            "  * Ctrl+LMB - Copy/Paste RTOs.\n"
            "  * Ctrl+Alt+LMB - Swap RTOs.\n"
            "  * Alt+LMB - Discard history"
            )
    
        bl_idname = "view3d.un_restrict_select_all_collections"
        bl_options = {'REGISTER', 'UNDO'}
    
        def invoke(self, context, event):
            view_layer = context.view_layer.name
    
            modifiers = get_modifiers(event)
    
            if not view_layer in internals.rto_history["select_all"]:
                internals.rto_history["select_all"][view_layer] = []
    
            if modifiers == {"alt"}:
    
                # clear all states
    
                del internals.rto_history["select_all"][view_layer]
    
                clear_copy("select")
                clear_swap("select")
    
            elif modifiers == {"ctrl"}:
    
            elif modifiers == {"ctrl", "alt"}:
    
            elif modifiers == {"shift"}:
    
            elif modifiers == {"shift", "ctrl"}:
                error = isolate_sel_objs_collections(view_layer, "select", "CM")
    
                if error:
                    self.report({"WARNING"}, error)
                    return {'CANCELLED'}
    
            elif modifiers == {"shift", "alt"}:
                error = disable_sel_objs_collections(view_layer, "select", "CM")
    
                if error:
                    self.report({"WARNING"}, error)
                    return {'CANCELLED'}
    
    
                activate_all_rtos(view_layer, "select")
    
    class CMHideOperator(Operator):
    
        bl_label = "[VV] Hide in Viewport"
        bl_description = (
            "  * Shift+LMB - Isolate/Restore.\n"
            "  * Shift+Ctrl+LMB - Isolate nested/Restore.\n"
            "  * Ctrl+LMB - Toggle nested.\n"
            "  * Alt+LMB - Discard history"
            )
    
        bl_idname = "view3d.hide_collection"
        bl_options = {'REGISTER', 'UNDO'}
    
        name: StringProperty()
    
        # static class var
        isolated = False
    
    
        def invoke(self, context, event):
    
            modifiers = get_modifiers(event)
    
            view_layer = context.view_layer.name
    
            laycol_ptr = internals.layer_collections[self.name]["ptr"]
    
            if not view_layer in internals.rto_history["hide"]:
                internals.rto_history["hide"][view_layer] = {"target": "", "history": []}
    
            if modifiers == {"alt"}:
    
                del internals.rto_history["hide"][view_layer]
    
                cls.isolated = False
    
            elif modifiers == {"shift"}:
    
                isolate_rto(cls, self, view_layer, "hide")
    
    
            elif modifiers == {"ctrl"}:
    
                toggle_children(self, view_layer, "hide")
    
                cls.isolated = False
    
            elif modifiers == {"ctrl", "shift"}:
    
                isolate_rto(cls, self, view_layer, "hide", children=True)
    
                # reset hide history
    
                del internals.rto_history["hide"][view_layer]
    
                # toggle view of collection
                laycol_ptr.hide_viewport = not laycol_ptr.hide_viewport
    
            # reset hide all history
    
            if view_layer in internals.rto_history["hide_all"]:
                del internals.rto_history["hide_all"][view_layer]
    
    class CMUnHideAllOperator(Operator):
    
        bl_label = "[VV Global] Hide in Viewport"
        bl_description = (
            "  * LMB - Enable all/Restore.\n"
            "  * Shift+LMB - Invert.\n"
    
            "  * Shift+Ctrl+LMB - Isolate collections w/ selected objects.\n"
            "  * Shift+Alt+LMB - Disable collections w/ selected objects.\n"
    
            "  * Ctrl+LMB - Copy/Paste RTOs.\n"
            "  * Ctrl+Alt+LMB - Swap RTOs.\n"
            "  * Alt+LMB - Discard history"
            )
    
        bl_idname = "view3d.un_hide_all_collections"
        bl_options = {'REGISTER', 'UNDO'}
    
        def invoke(self, context, event):
            view_layer = context.view_layer.name
    
            modifiers = get_modifiers(event)
    
            if not view_layer in internals.rto_history["hide_all"]:
                internals.rto_history["hide_all"][view_layer] = []
    
            if modifiers == {"alt"}:
    
                # clear all states
    
                del internals.rto_history["hide_all"][view_layer]
    
                clear_copy("hide")
                clear_swap("hide")
    
            elif modifiers == {"ctrl"}:
    
            elif modifiers == {"ctrl", "alt"}:
    
            elif modifiers == {"shift"}:
    
            elif modifiers == {"shift", "ctrl"}:
                error = isolate_sel_objs_collections(view_layer, "hide", "CM")
    
                if error:
                    self.report({"WARNING"}, error)
                    return {'CANCELLED'}
    
            elif modifiers == {"shift", "alt"}:
                error = disable_sel_objs_collections(view_layer, "hide", "CM")
    
                if error:
                    self.report({"WARNING"}, error)
                    return {'CANCELLED'}
    
    
                activate_all_rtos(view_layer, "hide")
    
    class CMDisableViewportOperator(Operator):
    
        bl_label = "[DV] Disable in Viewports"
        bl_description = (
            "  * Shift+LMB - Isolate/Restore.\n"
            "  * Shift+Ctrl+LMB - Isolate nested/Restore.\n"
            "  * Ctrl+LMB - Toggle nested.\n"
            "  * Alt+LMB - Discard history"
            )
    
        bl_idname = "view3d.disable_viewport_collection"
        bl_options = {'REGISTER', 'UNDO'}
    
        name: StringProperty()
    
        # static class var
        isolated = False
    
    
        def invoke(self, context, event):
    
            cls = CMDisableViewportOperator
    
            modifiers = get_modifiers(event)
    
            view_layer = context.view_layer.name
    
            laycol_ptr = internals.layer_collections[self.name]["ptr"]
    
            if not view_layer in internals.rto_history["disable"]:
                internals.rto_history["disable"][view_layer] = {"target": "", "history": []}
    
            if modifiers == {"alt"}:
    
                del internals.rto_history["disable"][view_layer]
    
                cls.isolated = False
    
            elif modifiers == {"shift"}:
    
                isolate_rto(cls, self, view_layer, "disable")
    
    
            elif modifiers == {"ctrl"}:
    
                toggle_children(self, view_layer, "disable")
    
                cls.isolated = False
    
            elif modifiers == {"ctrl", "shift"}:
    
                isolate_rto(cls, self, view_layer, "disable", children=True)
    
                # reset disable history
    
                del internals.rto_history["disable"][view_layer]
    
                # toggle disable of collection in viewport
                laycol_ptr.collection.hide_viewport = not laycol_ptr.collection.hide_viewport
    
            # reset disable all history
    
            if view_layer in internals.rto_history["disable_all"]:
                del internals.rto_history["disable_all"][view_layer]
    
    class CMUnDisableViewportAllOperator(Operator):
    
        bl_label = "[DV Global] Disable in Viewports"
        bl_description = (
            "  * LMB - Enable all/Restore.\n"
            "  * Shift+LMB - Invert.\n"
    
            "  * Shift+Ctrl+LMB - Isolate collections w/ selected objects.\n"
            "  * Shift+Alt+LMB - Disable collections w/ selected objects.\n"
    
            "  * Ctrl+LMB - Copy/Paste RTOs.\n"
            "  * Ctrl+Alt+LMB - Swap RTOs.\n"
            "  * Alt+LMB - Discard history"
            )
    
        bl_idname = "view3d.un_disable_viewport_all_collections"
        bl_options = {'REGISTER', 'UNDO'}
    
        def invoke(self, context, event):
            view_layer = context.view_layer.name
    
            modifiers = get_modifiers(event)
    
            if not view_layer in internals.rto_history["disable_all"]:
                internals.rto_history["disable_all"][view_layer] = []
    
            if modifiers == {"alt"}:
    
                # clear all states
    
                del internals.rto_history["disable_all"][view_layer]
    
                clear_copy("disable")
                clear_swap("disable")
    
            elif modifiers == {"ctrl"}:
    
            elif modifiers == {"ctrl", "alt"}:
    
            elif modifiers == {"shift"}:
    
                invert_rtos(view_layer, "disable")
    
            elif modifiers == {"shift", "ctrl"}:
                error = isolate_sel_objs_collections(view_layer, "disable", "CM")
    
                if error:
                    self.report({"WARNING"}, error)
                    return {'CANCELLED'}
    
            elif modifiers == {"shift", "alt"}:
                error = disable_sel_objs_collections(view_layer, "disable", "CM")
    
                if error:
                    self.report({"WARNING"}, error)
                    return {'CANCELLED'}
    
    
                activate_all_rtos(view_layer, "disable")
    
    class CMDisableRenderOperator(Operator):
    
        bl_label = "[RR] Disable in Renders"
        bl_description = (
            "  * Shift+LMB - Isolate/Restore.\n"
            "  * Shift+Ctrl+LMB - Isolate nested/Restore.\n"
            "  * Ctrl+LMB - Toggle nested.\n"
            "  * Alt+LMB - Discard history"
            )
    
        bl_idname = "view3d.disable_render_collection"
        bl_options = {'REGISTER', 'UNDO'}
    
        name: StringProperty()
    
        # static class var
        isolated = False
    
    
        def invoke(self, context, event):
    
            cls = CMDisableRenderOperator
    
            modifiers = get_modifiers(event)
    
            view_layer = context.view_layer.name
    
            laycol_ptr = internals.layer_collections[self.name]["ptr"]
    
            if not view_layer in internals.rto_history["render"]:
                internals.rto_history["render"][view_layer] = {"target": "", "history": []}
    
                del internals.rto_history["render"][view_layer]
    
                cls.isolated = False
    
            elif modifiers == {"shift"}:
    
                isolate_rto(cls, self, view_layer, "render")
    
    
            elif modifiers == {"ctrl"}:
    
                toggle_children(self, view_layer, "render")
    
                cls.isolated = False
    
            elif modifiers == {"ctrl", "shift"}:
    
                isolate_rto(cls, self, view_layer, "render", children=True)
    
                # reset render history
    
                del internals.rto_history["render"][view_layer]
    
                # toggle renderability of collection
                laycol_ptr.collection.hide_render = not laycol_ptr.collection.hide_render
    
            # reset render all history
    
            if view_layer in internals.rto_history["render_all"]:
                del internals.rto_history["render_all"][view_layer]
    
    class CMUnDisableRenderAllOperator(Operator):
    
        bl_label = "[RR Global] Disable in Renders"
        bl_description = (
            "  * LMB - Enable all/Restore.\n"
            "  * Shift+LMB - Invert.\n"
    
            "  * Shift+Ctrl+LMB - Isolate collections w/ selected objects.\n"
            "  * Shift+Alt+LMB - Disable collections w/ selected objects.\n"
    
            "  * Ctrl+LMB - Copy/Paste RTOs.\n"
            "  * Ctrl+Alt+LMB - Swap RTOs.\n"
            "  * Alt+LMB - Discard history"
            )
    
        bl_idname = "view3d.un_disable_render_all_collections"
        bl_options = {'REGISTER', 'UNDO'}
    
        def invoke(self, context, event):
            view_layer = context.view_layer.name
    
            modifiers = get_modifiers(event)
    
            if not view_layer in internals.rto_history["render_all"]:
                internals.rto_history["render_all"][view_layer] = []
    
            if modifiers == {"alt"}:
    
                # clear all states
    
                del internals.rto_history["render_all"][view_layer]
    
                clear_copy("render")
                clear_swap("render")
    
            elif modifiers == {"ctrl"}:
    
            elif modifiers == {"ctrl", "alt"}:
    
            elif modifiers == {"shift"}:
    
            elif modifiers == {"shift", "ctrl"}:
                error = isolate_sel_objs_collections(view_layer, "render", "CM")
    
                if error:
                    self.report({"WARNING"}, error)
                    return {'CANCELLED'}
    
            elif modifiers == {"shift", "alt"}:
                error = disable_sel_objs_collections(view_layer, "render", "CM")
    
                if error:
                    self.report({"WARNING"}, error)
                    return {'CANCELLED'}
    
    
                activate_all_rtos(view_layer, "render")
    
    class CMHoldoutOperator(Operator):
        bl_label = "[HH] Holdout"
        bl_description = (
            "  * Shift+LMB - Isolate/Restore.\n"
            "  * Shift+Ctrl+LMB - Isolate nested/Restore.\n"
            "  * Ctrl+LMB - Toggle nested.\n"
            "  * Alt+LMB - Discard history"
            )
        bl_idname = "view3d.holdout_collection"
        bl_options = {'REGISTER', 'UNDO'}
    
        name: StringProperty()
    
        # static class var
        isolated = False
    
        def invoke(self, context, event):
            cls = CMHoldoutOperator
    
            modifiers = get_modifiers(event)
            view_layer = context.view_layer.name
    
            laycol_ptr = internals.layer_collections[self.name]["ptr"]