Skip to content
Snippets Groups Projects
operators.py 73.8 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,
    
        update_property_group,
    
        get_move_selection,
        get_move_active,
    
    )
    
    rto_history = {
        "exclude": {},
        "exclude_all": {},
        "select": {},
        "select_all": {},
        "hide": {},
        "hide_all": {},
        "disable": {},
        "disable_all": {},
        "render": {},
        "render_all": {}
    }
    
    
    copy_buffer = {"RTO": "", "values": []}
    
    swap_buffer = {"A": {"RTO": "", "values": []}, "B": {"RTO": "", "values": []}}
    
    
    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.append(laycol["name"])
    
            # update tree view
            update_property_group(context)
    
    expand_history = {"target": "", "history": []}
    
    class ExpandSublevelOperator(Operator):
    
        '''  * Ctrl-Click to expand/collapse all sublevels\n  * Shift-Click to isolate/restore tree\n  * Alt-Click to discard history'''
    
        bl_label = "Expand Sublevel Items"
        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.append(self.name)
                    expand = True
    
                # do expanding/collapsing
                def loop(laycol):
                    for item in laycol.children:
                        if expand:
                            if not item.name in expanded:
                                expanded.append(item.name)
                        else:
                            if item.name in expanded:
                                expanded.remove(item.name)
    
                        if len(item.children) > 0:
                            loop(item)
    
                loop(layer_collections[self.name]["ptr"])
    
                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.append(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.append(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):
    
        '''  * Click to move object to collection.\n  * Shift-Click to add/remove object from collection'''
        bl_label = "Set Object 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
            cm = bpy.context.scene.collection_manager
            cm.update_header.clear()
            new_update_header = cm.update_header.add()
            new_update_header.name = "updated"
    
    class CMExcludeOperator(Operator):
    
        '''  * Shift-Click to isolate/restore previous state\n  * Ctrl-Click to toggle children\n  * Shift-Ctrl-Click to toggle nested isolation\n  * Alt-Click to discard history'''
    
        bl_label = "Exclude Collection from View Layer"
        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": []}
    
            target = rto_history["exclude"][view_layer]["target"]
    
            exclude_history = rto_history["exclude"][view_layer]["history"]
    
            if modifiers == {"alt"}:
                del rto_history["exclude"][view_layer]
                cls.isolated = False
    
            elif modifiers == {"shift"}:
    
                # isolate/de-isolate exclusion of collections
    
                active_layer_collections = [x["ptr"] for x in layer_collections.values()
                                            if not x["ptr"].exclude]
    
                # check if previous state should be restored
                if cls.isolated and self.name == target:
                    # restore previous state
                    for x, item in enumerate(layer_collections.values()):
                        item["ptr"].exclude = exclude_history[x]
    
                    # reset exclude history
                    del rto_history["exclude"][view_layer]
    
                    cls.isolated = False
    
                # check if all collections should be enabled
    
                elif (len(active_layer_collections) == 1 and
                      active_layer_collections[0].name == self.name):
    
                    # enable all collections
                    for item in layer_collections.values():
                        item["ptr"].exclude = False
    
                    # reset exclude history
                    del rto_history["exclude"][view_layer]
    
                else:
                    # isolate collection
    
                    rto_history["exclude"][view_layer]["target"] = self.name
    
    
                    # reset exclude history
                    exclude_history.clear()
    
                    # save state
                    for item in layer_collections.values():
                        exclude_history.append(item["ptr"].exclude)
    
                    # isolate collection
                    for item in layer_collections.values():
                        if item["name"] != laycol_ptr.name:
                            item["ptr"].exclude = True
    
                    laycol_ptr.exclude = False
    
                    # exclude all children
                    laycol_iter_list = [laycol_ptr.children]
                    while len(laycol_iter_list) > 0:
                        new_laycol_iter_list = []
                        for laycol_iter in laycol_iter_list:
                            for layer_collection in laycol_iter:
                                layer_collection.exclude = True
                                if len(layer_collection.children) > 0:
                                    new_laycol_iter_list.append(layer_collection.children)
    
                        laycol_iter_list = new_laycol_iter_list
    
            elif modifiers == {"ctrl"}:
    
                # toggle children
    
                # reset exclude history
                del rto_history["exclude"][view_layer]
    
                # toggle exclusion of collection (this propagates to children)
                laycol_ptr.exclude = not laycol_ptr.exclude
    
                cls.isolated = False
    
            elif modifiers == {"ctrl", "shift"}:
                # toggle nested isolation
    
                rto_history["exclude"][view_layer]["target"] = self.name
    
                if cls.isolated and self.name == target:
                    # restore previous state
                    for x, item in enumerate(layer_collections.values()):
                        item["ptr"].exclude = exclude_history[x]
    
                    # reset exclude history
                    del rto_history["exclude"][view_layer]
    
                    cls.isolated = False
    
                else:
                    # isolate nested collections
    
                    # reset exclude history
                    exclude_history.clear()
    
                    # save state
                    for item in layer_collections.values():
                        exclude_history.append(item["ptr"].exclude)
    
                    # get child states
                    child_states = {}
                    laycol_iter_list = [laycol_ptr.children]
                    while len(laycol_iter_list) > 0:
                        new_laycol_iter_list = []
                        for laycol_iter in laycol_iter_list:
                            for layer_collection in laycol_iter:
                                child_states[layer_collection.name] = layer_collection.exclude
                                if len(layer_collection.children) > 0:
                                    new_laycol_iter_list.append(layer_collection.children)
    
                        laycol_iter_list = new_laycol_iter_list
    
                    # isolate collection
                    for item in layer_collections.values():
                        if item["name"] != laycol_ptr.name:
                            item["ptr"].exclude = True
    
                    laycol_ptr.exclude = False
    
                    # restore child states
                    laycol_iter_list = [laycol_ptr.children]
                    while len(laycol_iter_list) > 0:
                        new_laycol_iter_list = []
                        for laycol_iter in laycol_iter_list:
                            for layer_collection in laycol_iter:
                                layer_collection.exclude = child_states[layer_collection.name]
                                if len(layer_collection.children) > 0:
                                    new_laycol_iter_list.append(layer_collection.children)
    
                        laycol_iter_list = new_laycol_iter_list
    
                    cls.isolated = True
    
    
            else:
                # toggle exclusion
    
                # reset exclude history
                del rto_history["exclude"][view_layer]
    
                # get current child exclusion state
                child_exclusion = []
    
                laycol_iter_list = [laycol_ptr.children]
                while len(laycol_iter_list) > 0:
                    new_laycol_iter_list = []
                    for laycol_iter in laycol_iter_list:
                        for layer_collection in laycol_iter:
                            child_exclusion.append([layer_collection, layer_collection.exclude])
                            if len(layer_collection.children) > 0:
                                new_laycol_iter_list.append(layer_collection.children)
    
                    laycol_iter_list = new_laycol_iter_list
    
                # 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):
    
        '''  * Click to toggle between current excluded state and all included.\n  * Shift-Click to invert excluded status of all collections\n  * Ctrl-Click to Copy/Paste RTOs\n  * Ctrl-Alt-Click to swap RTOs\n  * Alt-Click to discard history and copy/swap actions'''
    
        bl_label = "Toggle Excluded Status Of All Collections"
        bl_idname = "view3d.un_exclude_all_collections"
        bl_options = {'REGISTER', 'UNDO'}
    
        def invoke(self, context, event):
            global rto_history
    
            global copy_buffer
            global swap_buffer
    
            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] = []
    
            exclude_all_history = rto_history["exclude_all"][view_layer]
    
            if modifiers == {"alt"}:
                # clear RTO history
                del rto_history["exclude_all"][view_layer]
    
                # clear copy buffer
                if copy_buffer["RTO"] == "exclude":
                    copy_buffer["RTO"] = ""
                    copy_buffer["values"].clear()
    
                # clear swap buffer
                if swap_buffer["A"]["RTO"] == "exclude":
                    swap_buffer["A"]["RTO"] = ""
                    swap_buffer["A"]["values"].clear()
                    swap_buffer["B"]["RTO"] = ""
                    swap_buffer["B"]["values"].clear()
    
                return {'FINISHED'}
    
            if modifiers == {"ctrl"}:
    
                if not copy_buffer["values"]:
                    # copy
                    copy_buffer["RTO"] = "exclude"
                    for laycol in layer_collections.values():
                        copy_buffer["values"].append(laycol["ptr"].exclude)
    
                else:
                    # paste
                    for x, laycol in enumerate(layer_collections.values()):
                        laycol["ptr"].exclude = copy_buffer["values"][x]
    
                    # clear copy buffer
                    copy_buffer["RTO"] = ""
                    copy_buffer["values"].clear()
    
                return {'FINISHED'}
    
            if modifiers == {"ctrl", "alt"}:
    
                if not swap_buffer["A"]["values"]:
                    # get A
                    swap_buffer["A"]["RTO"] = "exclude"
                    for laycol in layer_collections.values():
                        swap_buffer["A"]["values"].append(laycol["ptr"].exclude)
    
                else:
                    # get B
                    swap_buffer["B"]["RTO"] = "exclude"
                    for laycol in layer_collections.values():
                        swap_buffer["B"]["values"].append(laycol["ptr"].exclude)
    
                    # swap A with B
                    for x, laycol in enumerate(layer_collections.values()):
                        attr_A = attr_B = laycol["ptr"]
    
                        # get attributes
                        RTO_A = swap_buffer["A"]["RTO"].split(".")
                        RTO_B = swap_buffer["B"]["RTO"].split(".")
    
                        if RTO_A[0] == "collection":
                            attr_A = getattr(attr_A, RTO_A[0])
    
                        if RTO_B[0] == "collection":
                            attr_B = getattr(attr_B, RTO_B[0])
    
    
                        # swap values
                        setattr(attr_A, RTO_A[-1], swap_buffer["B"]["values"][x])
                        setattr(attr_B, RTO_B[-1], swap_buffer["A"]["values"][x])
    
                    # clear swap buffer
                    swap_buffer["A"]["RTO"] = ""
                    swap_buffer["A"]["values"].clear()
                    swap_buffer["B"]["RTO"] = ""
                    swap_buffer["B"]["values"].clear()
    
                return {'FINISHED'}
    
    
            if len(exclude_all_history) == 0:
                exclude_all_history.clear()
                keep_history = False
    
                if event.shift:
                    for item in layer_collections.values():
                        keep_history = True
                        exclude_all_history.append(item["ptr"].exclude)
    
                    for x, item in enumerate(layer_collections.values()):
                        item["ptr"].exclude = not exclude_all_history[x]
    
                else:
                    for item in reversed(list(layer_collections.values())):
                        if item["ptr"].exclude:
                            keep_history = True
    
                        exclude_all_history.append(item["ptr"].exclude)
    
                        item["ptr"].exclude = False
    
                    exclude_all_history.reverse()
    
                if not keep_history:
                    del rto_history["exclude_all"][view_layer]
    
            else:
                for x, item in enumerate(layer_collections.values()):
                    item["ptr"].exclude = exclude_all_history[x]
    
                del rto_history["exclude_all"][view_layer]
    
    class CMRestrictSelectOperator(Operator):
    
        '''  * Shift-Click to isolate/restore previous state\n  * Ctrl-Click to toggle children\n  * Shift-Ctrl-Click to toggle nested isolation\n  * Alt-Click to discard history'''
    
        bl_label = "Disable Selection of Collection"
        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": []}
    
            target = rto_history["select"][view_layer]["target"]
    
            select_history = rto_history["select"][view_layer]["history"]
    
            if modifiers == {"alt"}:
                del rto_history["select"][view_layer]
                cls.isolated = False
    
            elif modifiers == {"shift"}:
    
                # isolate/de-isolate selectability of collections
    
                laycol = layer_collections[self.name]
    
    
                # get active collections
    
                active_layer_collections = [x["ptr"] for x in layer_collections.values()
                                            if x["ptr"].collection.hide_select == False]
    
                # check if previous state should be restored
                if cls.isolated and self.name == target:
                    # restore previous state
                    for x, item in enumerate(layer_collections.values()):
                        item["ptr"].collection.hide_select = select_history[x]
    
                    # reset select history
                    del rto_history["select"][view_layer]
    
                # check if all collections should be enabled
    
                elif (len(active_layer_collections) == 1 and
                      active_layer_collections[0].name == self.name):
    
                    # make all collections selectable
                    for item in layer_collections.values():
                        item["ptr"].collection.hide_select = False
    
                    # reset select history
                    del rto_history["select"][view_layer]
    
                    # isolate selectability
    
                    rto_history["select"][view_layer]["target"] = self.name
    
    
                    # reset select history
                    select_history.clear()
    
                    # save state
                    for item in layer_collections.values():
                        select_history.append(item["ptr"].collection.hide_select)
    
                    # make all collections unselectable
                    for item in layer_collections.values():
                        item["ptr"].collection.hide_select = True
    
                    # allow selection of active collection plus parents
                    laycol_ptr.collection.hide_select = False
    
                    laycol = layer_collections[self.name]
                    while laycol["id"] != 0:
                        laycol["ptr"].collection.hide_select = False
                        laycol = laycol["parent"]
    
            elif modifiers == {"ctrl"}:
    
                # toggle children
    
                # reset select history
                del rto_history["select"][view_layer]
    
                # toggle selectability of collection
                state = not laycol_ptr.collection.hide_select
                laycol_ptr.collection.hide_select = state
    
                # pass state to children
                laycol_iter_list = [laycol_ptr.children]
                while len(laycol_iter_list) > 0:
                    new_laycol_iter_list = []
                    for laycol_iter in laycol_iter_list:
                        for layer_collection in laycol_iter:
                            layer_collection.collection.hide_select = state
                            if len(layer_collection.children) > 0:
                                new_laycol_iter_list.append(layer_collection.children)
    
                    laycol_iter_list = new_laycol_iter_list
    
                cls.isolated = False
    
            elif modifiers == {"ctrl", "shift"}:
                # isolate nested collections
    
                laycol = layer_collections[self.name]
    
                # check if previous state should be restored
                if cls.isolated and self.name == target:
                    # restore previous state
                    for x, item in enumerate(layer_collections.values()):
                        item["ptr"].collection.hide_select = select_history[x]
    
                    # reset select history
                    del rto_history["select"][view_layer]
    
                    cls.isolated = False
    
                else:
                    # isolate nested selectability
    
                    rto_history["select"][view_layer]["target"] = self.name
    
                    # reset select history
                    select_history.clear()
    
                    # save state
                    for item in layer_collections.values():
                        select_history.append(item["ptr"].collection.hide_select)
    
                    # get child states
                    child_states = {}
                    laycol_iter_list = [laycol_ptr.children]
                    while len(laycol_iter_list) > 0:
                        new_laycol_iter_list = []
                        for laycol_iter in laycol_iter_list:
                            for layer_collection in laycol_iter:
                                child_states[layer_collection.name] = layer_collection.collection.hide_select
                                if len(layer_collection.children) > 0:
                                    new_laycol_iter_list.append(layer_collection.children)
    
                        laycol_iter_list = new_laycol_iter_list
    
                    # make all collections unselectable
                    for item in layer_collections.values():
                        item["ptr"].collection.hide_select = True
    
                    # allow selection of active collection plus parents
                    laycol_ptr.collection.hide_select = False
    
                    laycol = layer_collections[self.name]
                    while laycol["id"] != 0:
                        laycol["ptr"].collection.hide_select = False
                        laycol = laycol["parent"]
    
                    # restore child states
                    laycol_iter_list = [laycol_ptr.children]
                    while len(laycol_iter_list) > 0:
                        new_laycol_iter_list = []
                        for laycol_iter in laycol_iter_list:
                            for layer_collection in laycol_iter:
                                layer_collection.collection.hide_select = child_states[layer_collection.name]
                                if len(layer_collection.children) > 0:
                                    new_laycol_iter_list.append(layer_collection.children)
    
                        laycol_iter_list = new_laycol_iter_list
    
                    cls.isolated = 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):
    
        '''  * Click to toggle between current selectable state and all selectable.\n  * Shift-Click to invert selectable status of all collections\n  * Ctrl-Click to Copy/Paste RTOs\n  * Ctrl-Alt-Click to swap RTOs\n  * Alt-Click to discard history and copy/swap actions'''
    
        bl_label = "Toggle Selectable Status Of All Collections"
        bl_idname = "view3d.un_restrict_select_all_collections"
        bl_options = {'REGISTER', 'UNDO'}
    
        def invoke(self, context, event):
            global rto_history
    
            global copy_buffer
            global swap_buffer
    
            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] = []
    
            select_all_history = rto_history["select_all"][view_layer]
    
            if modifiers == {"alt"}:
                # clear RTO history
                del rto_history["select_all"][view_layer]
    
                # clear copy buffer
                if copy_buffer["RTO"] == "collection.hide_select":
                    copy_buffer["RTO"] = ""
                    copy_buffer["values"].clear()
    
                # clear swap buffer
                if swap_buffer["A"]["RTO"] == "collection.hide_select":
                    swap_buffer["A"]["RTO"] = ""
                    swap_buffer["A"]["values"].clear()
                    swap_buffer["B"]["RTO"] = ""
                    swap_buffer["B"]["values"].clear()
    
                return {'FINISHED'}
    
            if modifiers == {"ctrl"}:
    
                if not copy_buffer["values"]:
                    # copy
                    copy_buffer["RTO"] = "collection.hide_select"
                    for laycol in layer_collections.values():
                        copy_buffer["values"].append(laycol["ptr"].collection.hide_select)
    
                else:
                    # paste
                    for x, laycol in enumerate(layer_collections.values()):
                        laycol["ptr"].collection.hide_select = copy_buffer["values"][x]
    
                    # clear copy buffer
                    copy_buffer["RTO"] = ""
                    copy_buffer["values"].clear()
    
                return {'FINISHED'}
    
            if modifiers == {"ctrl", "alt"}:
    
                if not swap_buffer["A"]["values"]:
                    # get A
                    swap_buffer["A"]["RTO"] = "collection.hide_select"
                    for laycol in layer_collections.values():
                        swap_buffer["A"]["values"].append(laycol["ptr"].collection.hide_select)
    
                else:
                    # get B
                    swap_buffer["B"]["RTO"] = "collection.hide_select"
                    for laycol in layer_collections.values():
                        swap_buffer["B"]["values"].append(laycol["ptr"].collection.hide_select)
    
                    # swap A with B
                    for x, laycol in enumerate(layer_collections.values()):
                        attr_A = attr_B = laycol["ptr"]
    
                        # get attributes
                        RTO_A = swap_buffer["A"]["RTO"].split(".")
                        RTO_B = swap_buffer["B"]["RTO"].split(".")
    
                        if RTO_A[0] == "collection":
                            attr_A = getattr(attr_A, RTO_A[0])
    
                        if RTO_B[0] == "collection":
                            attr_B = getattr(attr_B, RTO_B[0])
    
    
                        # swap values
                        setattr(attr_A, RTO_A[-1], swap_buffer["B"]["values"][x])
                        setattr(attr_B, RTO_B[-1], swap_buffer["A"]["values"][x])
    
                    # clear swap buffer
                    swap_buffer["A"]["RTO"] = ""
                    swap_buffer["A"]["values"].clear()
                    swap_buffer["B"]["RTO"] = ""
                    swap_buffer["B"]["values"].clear()
    
                return {'FINISHED'}
    
    
            if len(select_all_history) == 0:
                select_all_history.clear()
                keep_history = False
    
                for item in layer_collections.values():
    
                    collection = item["ptr"].collection
    
    
                    if event.shift:
                        keep_history = True
    
                        select_all_history.append(collection.hide_select)
                        collection.hide_select = not collection.hide_select
    
                        if collection.hide_select:
    
                            keep_history = True
    
                        select_all_history.append(collection.hide_select)
                        collection.hide_select = False
    
                if not keep_history:
                    del rto_history["select_all"][view_layer]
    
            else:
                for x, item in enumerate(layer_collections.values()):
                    item["ptr"].collection.hide_select = select_all_history[x]
    
                del rto_history["select_all"][view_layer]
    
    class CMHideOperator(Operator):
    
        '''  * Shift-Click to isolate/restore previous state\n  * Ctrl-Click to toggle children\n  * Shift-Ctrl-Click to toggle nested isolation\n  * Alt-Click to discard history'''
    
        bl_label = "Hide Collection"
        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": []}
    
            target = rto_history["hide"][view_layer]["target"]
    
            hide_history = rto_history["hide"][view_layer]["history"]
    
            if modifiers == {"alt"}:
                del rto_history["hide"][view_layer]
                cls.isolated = False
    
            elif modifiers == {"shift"}:
    
                # isolate/de-isolate view of collections
    
                laycol = layer_collections[self.name]
    
    
                # get active collections
    
                active_layer_collections = [x["ptr"] for x in layer_collections.values()
                                            if x["ptr"].hide_viewport == False]
    
                # check if previous state should be restored
                if cls.isolated and self.name == target:
                    # restore previous state
                    for x, item in enumerate(layer_collections.values()):
                        item["ptr"].hide_viewport = hide_history[x]
    
                    # reset hide history
                    del rto_history["hide"][view_layer]
    
                # check if all collections should be enabled
    
                elif (len(active_layer_collections) == 1 and
                      active_layer_collections[0].name == self.name):
    
                    # show all collections
                    for laycol in layer_collections.values():
                        laycol["ptr"].hide_viewport = False
    
                    del rto_history["hide"][view_layer]
    
                    # isolate visibility
    
                    rto_history["hide"][view_layer]["target"] = self.name
    
    
                    # reset hide history
                    hide_history.clear()
    
                    # save state
                    for item in layer_collections.values():
                        hide_history.append(item["ptr"].hide_viewport)
    
                    # hide all collections
                    for laycol in layer_collections.values():
                        laycol["ptr"].hide_viewport = True
    
                    # show active collection plus parents
                    laycol_ptr.hide_viewport = False
    
                    laycol = layer_collections[self.name]
                    while laycol["id"] != 0: