Skip to content
Snippets Groups Projects
operators.py 28.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • # ##### 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 #####
    
    # Copyright 2011, Ryan Inch
    
    
    from copy import deepcopy
    
    
    from bpy.types import (
        Operator,
    )
    
    
    from bpy.props import (
        BoolProperty,
        StringProperty,
        IntProperty
    
    from .internals import (
        expanded,
        layer_collections,
    
        rto_history,
        expand_history,
        phantom_history,
        copy_buffer,
        swap_buffer,
    
        update_property_group,
    
        get_move_selection,
        get_move_active,
    
    from .operator_utils import (
        get_rto,
        set_rto,
        apply_to_children,
        isolate_rto,
        toggle_children,
        activate_all_rtos,
        invert_rtos,
        copy_rtos,
        swap_rtos,
        clear_copy,
        clear_swap,
    )
    
    
    class ExpandAllOperator(Operator):
    
        '''Expand/Collapse all collections'''
        bl_label = "Expand All Items"
        bl_idname = "view3d.expand_all_items"
        bl_options = {'REGISTER', 'UNDO'}
    
        def execute(self, context):
            if len(expanded) > 0:
                expanded.clear()
            else:
                for laycol in layer_collections.values():
                    if laycol["ptr"].children:
    
                        expanded.add(laycol["name"])
    
            # 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"
        bl_options = {'REGISTER', 'UNDO'}
    
        expand: BoolProperty()
        name: StringProperty()
        index: IntProperty()
    
        # static class var
        isolated = False
    
    
        def invoke(self, context, event):
    
            global expand_history
            cls = ExpandSublevelOperator
    
            modifiers = get_modifiers(event)
    
    
            if modifiers == {"alt"}:
                expand_history["target"] = ""
                expand_history["history"].clear()
                cls.isolated = False
    
            elif modifiers == {"ctrl"}:
    
                # expand/collapse all subcollections
                expand = None
    
                # check whether to expand or collapse
                if self.name in expanded:
                    expanded.remove(self.name)
                    expand = False
                else:
    
                    expanded.add(self.name)
    
                # do expanding/collapsing
    
                def set_expanded(layer_collection):
                    if expand:
                        expanded.add(layer_collection.name)
                    else:
                        expanded.discard(layer_collection.name)
    
                apply_to_children(layer_collections[self.name]["ptr"], set_expanded)
    
                expand_history["target"] = ""
                expand_history["history"].clear()
                cls.isolated = False
    
            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 expanded:
                            expanded.remove(laycol["name"])
                            expand_history["history"].append(laycol["name"])
    
                    if parent["parent"]:
                        isolate_tree(parent)
    
                if cls.isolated:
                    for item in expand_history["history"]:
    
                        expanded.add(item)
    
    
                    expand_history["target"] = ""
                    expand_history["history"].clear()
                    cls.isolated = False
    
                else:
                    isolate_tree(layer_collections[self.name])
                    expand_history["target"] = self.name
                    cls.isolated = True
    
    
            else:
                # expand/collapse collection
                if self.expand:
    
                    expanded.add(self.name)
    
                else:
                    expanded.remove(self.name)
    
                expand_history["target"] = ""
                expand_history["history"].clear()
                cls.isolated = False
    
    
            # set selected row to the collection you're expanding/collapsing and update tree view
    
            context.scene.collection_manager.cm_list_index = self.index
    
            update_property_group(context)
    
    class CMSetCollectionOperator(Operator):
    
        bl_label = "Set Object Collection"
    
        bl_description = (
            "  * LMB - Move object to collection.\n"
            "  * Shift+LMB - Add/Remove object from collection"
            )
    
        bl_idname = "view3d.set_collection"
        bl_options = {'REGISTER', 'UNDO'}
    
        collection_index: IntProperty()
        collection_name: StringProperty()
    
        def invoke(self, context, event):
    
            laycol = 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 = 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)
    
                    errors = 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:
                                errors = True
                                continue
    
                            # remove from collection
                            target_collection.objects.unlink(obj)
    
                    if errors:
                        send_report("Error removing 1 or more objects from this collection.\nObjects would be left without a 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.name != target_collection.name:
                            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):
            global rto_history
    
            cls = CMExcludeOperator
    
            modifiers = get_modifiers(event)
    
            view_layer = context.view_layer.name
            laycol_ptr = layer_collections[self.name]["ptr"]
    
            if not view_layer in rto_history["exclude"]:
                rto_history["exclude"][view_layer] = {"target": "", "history": []}
    
            if modifiers == {"alt"}:
                del 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 rto_history["exclude"][view_layer]
    
                # get current child exclusion state
                child_exclusion = []
    
                def get_child_exclusion(layer_collection):
                    child_exclusion.append([layer_collection, layer_collection.exclude])
    
                apply_to_children(laycol_ptr, get_child_exclusion)
    
                # toggle exclusion of collection
                laycol_ptr.exclude = not laycol_ptr.exclude
    
                # set correct state for all children
                for laycol in child_exclusion:
                    laycol[0].exclude = laycol[1]
    
            # reset exclude all history
            if view_layer in rto_history["exclude_all"]:
                del 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"
            "  * 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):
            global rto_history
    
            view_layer = context.view_layer.name
    
            modifiers = get_modifiers(event)
    
            if not view_layer in rto_history["exclude_all"]:
                rto_history["exclude_all"][view_layer] = []
    
            if modifiers == {"alt"}:
    
                # clear all states
    
                del rto_history["exclude_all"][view_layer]
    
                clear_copy("exclude")
                clear_swap("exclude")
    
            elif modifiers == {"ctrl"}:
                copy_rtos("exclude")
    
            elif modifiers == {"ctrl", "alt"}:
                swap_rtos("exclude")
    
            elif modifiers == {"shift"}:
                invert_rtos("exclude")
    
                activate_all_rtos(view_layer, "exclude")
    
    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):
            global rto_history
    
            cls = CMRestrictSelectOperator
    
            modifiers = get_modifiers(event)
    
            view_layer = context.view_layer.name
            laycol_ptr = layer_collections[self.name]["ptr"]
    
            if not view_layer in rto_history["select"]:
                rto_history["select"][view_layer] = {"target": "", "history": []}
    
            if modifiers == {"alt"}:
                del 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 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 rto_history["select_all"]:
                del 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"
            "  * 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):
            global rto_history
    
            view_layer = context.view_layer.name
    
            modifiers = get_modifiers(event)
    
    
            if not view_layer in rto_history["select_all"]:
                rto_history["select_all"][view_layer] = []
    
    
            if modifiers == {"alt"}:
    
                # clear all states
    
                del rto_history["select_all"][view_layer]
    
                clear_copy("select")
                clear_swap("select")
    
            elif modifiers == {"ctrl"}:
                copy_rtos("select")
    
            elif modifiers == {"ctrl", "alt"}:
                swap_rtos("select")
    
            elif modifiers == {"shift"}:
                invert_rtos("select")
    
                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):
            global rto_history
    
            modifiers = get_modifiers(event)
    
            view_layer = context.view_layer.name
            laycol_ptr = layer_collections[self.name]["ptr"]
    
            if not view_layer in rto_history["hide"]:
                rto_history["hide"][view_layer] = {"target": "", "history": []}
    
            if modifiers == {"alt"}:
                del 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 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 rto_history["hide_all"]:
                del 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"
            "  * 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):
            global rto_history
    
            view_layer = context.view_layer.name
    
            modifiers = get_modifiers(event)
    
    
            if not view_layer in rto_history["hide_all"]:
                rto_history["hide_all"][view_layer] = []
    
    
            if modifiers == {"alt"}:
    
                # clear all states
    
                del rto_history["hide_all"][view_layer]
    
                clear_copy("hide")
                clear_swap("hide")
    
            elif modifiers == {"ctrl"}:
                copy_rtos("hide")
    
            elif modifiers == {"ctrl", "alt"}:
                swap_rtos("hide")
    
            elif modifiers == {"shift"}:
                invert_rtos("hide")
    
                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):
            global rto_history
    
            cls = CMDisableViewportOperator
    
            modifiers = get_modifiers(event)
    
            view_layer = context.view_layer.name
            laycol_ptr = layer_collections[self.name]["ptr"]
    
            if not view_layer in rto_history["disable"]:
                rto_history["disable"][view_layer] = {"target": "", "history": []}
    
            if modifiers == {"alt"}:
                del 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 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 rto_history["disable_all"]:
                del 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"
            "  * 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):
            global rto_history
    
            view_layer = context.view_layer.name
    
            modifiers = get_modifiers(event)
    
    
            if not view_layer in rto_history["disable_all"]:
                rto_history["disable_all"][view_layer] = []
    
    
            if modifiers == {"alt"}:
    
                # clear all states
    
                del rto_history["disable_all"][view_layer]
    
                clear_copy("disable")
                clear_swap("disable")
    
            elif modifiers == {"ctrl"}:
                copy_rtos("disable")
    
            elif modifiers == {"ctrl", "alt"}:
                swap_rtos("disable")
    
            elif modifiers == {"shift"}:
                invert_rtos("disable")
    
                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):
            global rto_history
    
            cls = CMDisableRenderOperator
    
            modifiers = get_modifiers(event)
    
            view_layer = context.view_layer.name
            laycol_ptr = layer_collections[self.name]["ptr"]
    
            if not view_layer in rto_history["render"]:
                rto_history["render"][view_layer] = {"target": "", "history": []}
    
    
    
            if modifiers == {"alt"}:
                del 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 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 rto_history["render_all"]:
                del 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"
            "  * 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):
            global rto_history
    
            view_layer = context.view_layer.name
    
            modifiers = get_modifiers(event)
    
    
            if not view_layer in rto_history["render_all"]:
                rto_history["render_all"][view_layer] = []
    
    
            if modifiers == {"alt"}:
    
                # clear all states
    
                del rto_history["render_all"][view_layer]
    
                clear_copy("render")
                clear_swap("render")
    
            elif modifiers == {"ctrl"}:
                copy_rtos("render")
    
            elif modifiers == {"ctrl", "alt"}:
                swap_rtos("render")
    
            elif modifiers == {"shift"}:
                invert_rtos("render")
    
                activate_all_rtos(view_layer, "render")
    
    class CMRemoveCollectionOperator(Operator):
    
        '''Remove Collection'''
        bl_label = "Remove Collection"
        bl_idname = "view3d.remove_collection"
        bl_options = {'UNDO'}
    
        collection_name: StringProperty()
    
        def execute(self, context):
            global rto_history
    
            cm = context.scene.collection_manager
    
    
            laycol = layer_collections[self.collection_name]
            collection = laycol["ptr"].collection
    
            parent_collection = laycol["parent"]["ptr"].collection
    
            # shift all objects in this collection to the parent collection
    
            for obj in collection.objects:
                if obj.name not in parent_collection.objects:
                    parent_collection.objects.link(obj)
    
            # shift all child collections to the parent collection
            if collection.children:
                for subcollection in collection.children:
    
                    parent_collection.children.link(subcollection)
    
            # remove collection and update tree view
            bpy.data.collections.remove(collection)
            update_property_group(context)
    
            if len(cm.cm_list_collection) == cm.cm_list_index:
                cm.cm_list_index = len(cm.cm_list_collection) - 1
    
                update_property_group(context)
    
            if qcd_slots.contains(name=self.collection_name):
                qcd_slots.del_slot(name=self.collection_name)
    
    
            if self.collection_name in qcd_slots.overrides:
    
                qcd_slots.overrides.remove(self.collection_name)
    
            # reset history
            for rto in rto_history.values():
                rto.clear()
    
            return {'FINISHED'}
    
    rename = [False]
    
    class CMNewCollectionOperator(Operator):
    
        '''Add New Collection'''
        bl_label = "Add New Collection"
        bl_idname = "view3d.add_collection"
        bl_options = {'UNDO'}
    
        child: BoolProperty()
    
        def execute(self, context):
            global rto_history
    
            new_collection = bpy.data.collections.new('Collection')
    
            cm = context.scene.collection_manager
    
    
            # if there are collections
    
            if len(cm.cm_list_collection) > 0:
    
                # get selected collection
    
                laycol = layer_collections[cm.cm_list_collection[cm.cm_list_index].name]
    
                # add new collection
                if self.child:
                    laycol["ptr"].collection.children.link(new_collection)
    
                    expanded.add(laycol["name"])
    
                    # update tree view property
                    update_property_group(context)
    
                    cm.cm_list_index = layer_collections[new_collection.name]["row_index"]
    
                else:
                    laycol["parent"]["ptr"].collection.children.link(new_collection)
    
                    # update tree view property
                    update_property_group(context)
    
                    cm.cm_list_index = layer_collections[new_collection.name]["row_index"]
    
            # if no collections add top level collection and select it
            else:
    
                context.scene.collection.children.link(new_collection)
    
                # update tree view property
                update_property_group(context)
    
                cm.cm_list_index = 0
    
            global rename
            rename[0] = True
    
            # reset history
            for rto in rto_history.values():
                rto.clear()
    
            return {'FINISHED'}
    
    class CMPhantomModeOperator(Operator):
    
        '''Toggle Phantom Mode'''
        bl_label = "Toggle Phantom Mode"
        bl_idname = "view3d.toggle_phantom_mode"
    
        def execute(self, context):
            global phantom_history
            global rto_history
    
            cm = context.scene.collection_manager
            view_layer = context.view_layer
    
            # enter Phantom Mode
    
            if not cm.in_phantom_mode:
    
                cm.in_phantom_mode = True
    
                # save current visibility state
    
                phantom_history["view_layer"] = view_layer.name
    
                def save_visibility_state(layer_collection):
                    phantom_history["initial_state"][layer_collection.name] = {
    
                                "exclude": layer_collection.exclude,
                                "select": layer_collection.collection.hide_select,
                                "hide": layer_collection.hide_viewport,
                                "disable": layer_collection.collection.hide_viewport,
                                "render": layer_collection.collection.hide_render,
                                    }
    
                apply_to_children(view_layer.layer_collection, save_visibility_state)
    
                # save current rto history
                for rto, history, in rto_history.items():
    
                    if history.get(view_layer.name, None):
                        phantom_history[rto+"_history"] = deepcopy(history[view_layer.name])
    
            else: # return to normal mode
                def restore_visibility_state(layer_collection):
                    phantom_laycol = phantom_history["initial_state"][layer_collection.name]
    
                    layer_collection.exclude = phantom_laycol["exclude"]
                    layer_collection.collection.hide_select = phantom_laycol["select"]
                    layer_collection.hide_viewport = phantom_laycol["hide"]
                    layer_collection.collection.hide_viewport = phantom_laycol["disable"]
                    layer_collection.collection.hide_render = phantom_laycol["render"]
    
                apply_to_children(view_layer.layer_collection, restore_visibility_state)
    
                # restore previous rto history
                for rto, history, in rto_history.items():
    
                    if view_layer.name in history:
                        del history[view_layer.name]
    
                    if phantom_history[rto+"_history"]:
    
                        history[view_layer.name] = deepcopy(phantom_history[rto+"_history"])
    
                    phantom_history[rto+"_history"].clear()
    
                cm.in_phantom_mode = False
    
            return {'FINISHED'}