diff --git a/collection_manager/__init__.py b/collection_manager/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..75142a3543766c6f617954ceb9cc93049b38d068
--- /dev/null
+++ b/collection_manager/__init__.py
@@ -0,0 +1,121 @@
+# ##### 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 GPL licence applies"""
+
+bl_info = {
+    "name": "Collection Manager",
+    "description": "Manage collections and their objects",
+    "author": "Ryan Inch",
+    "version": (1,8,0),
+    "blender": (2, 80, 0),
+    "location": "View3D - Object Mode (Shortcut - M)",
+    "warning": '',  # used for warning icon and text in addons panel
+    "wiki_url": "",
+    "category": "User Interface"}
+
+
+if "bpy" in locals():
+    import importlib
+    
+    importlib.reload(internals)
+    importlib.reload(operators)
+    importlib.reload(ui)
+
+else:
+    from . import internals
+    from . import operators
+    from . import ui
+
+import bpy
+from bpy.props import (
+    CollectionProperty,
+    IntProperty,
+    BoolProperty,
+    )
+
+addon_keymaps = []
+
+classes = (
+    internals.CMListCollection,
+    operators.ExpandAllOperator,
+    operators.ExpandSublevelOperator,
+    operators.CMExcludeOperator,
+    operators.CMUnExcludeAllOperator,
+    operators.CMRestrictSelectOperator,
+    operators.CMUnRestrictSelectAllOperator,
+    operators.CMHideOperator,
+    operators.CMUnHideAllOperator,
+    operators.CMDisableViewportOperator,
+    operators.CMUnDisableViewportAllOperator,
+    operators.CMDisableRenderOperator,
+    operators.CMUnDisableRenderAllOperator,
+    operators.CMNewCollectionOperator,
+    operators.CMRemoveCollectionOperator,
+    operators.CMSetCollectionOperator,
+    operators.CMPhantomModeOperator,
+    ui.CM_UL_items,
+    ui.CollectionManager,
+    ui.CMRestrictionTogglesPanel,
+    )
+
+def register():
+    for cls in classes:
+        bpy.utils.register_class(cls)
+    
+    bpy.types.Scene.CMListCollection = CollectionProperty(type=internals.CMListCollection)
+    bpy.types.Scene.CMListIndex = IntProperty(update=ui.update_selection)
+    
+    bpy.types.Scene.show_exclude = BoolProperty(default=True, name="Exclude from View Layer")
+    bpy.types.Scene.show_selectable = BoolProperty(default=True, name="Selectable")
+    bpy.types.Scene.show_hideviewport = BoolProperty(default=True, name="Hide in Viewport")
+    bpy.types.Scene.show_disableviewport = BoolProperty(default=False, name="Disable in Viewports")
+    bpy.types.Scene.show_render = BoolProperty(default=False, name="Disable in Renders")
+    
+    bpy.types.Scene.CM_Phantom_Mode = BoolProperty(default=False)
+
+
+    # create the global menu hotkey
+    wm = bpy.context.window_manager
+    km = wm.keyconfigs.addon.keymaps.new(name='Object Mode')
+    kmi = km.keymap_items.new('view3d.collection_manager', 'M', 'PRESS')
+    addon_keymaps.append((km, kmi))
+ 
+def unregister():
+    for cls in classes:
+        bpy.utils.unregister_class(cls)
+    
+    del bpy.types.Scene.CMListCollection
+    del bpy.types.Scene.CMListIndex
+    
+    del bpy.types.Scene.show_exclude
+    del bpy.types.Scene.show_selectable
+    del bpy.types.Scene.show_hideviewport
+    del bpy.types.Scene.show_disableviewport
+    del bpy.types.Scene.show_render
+    
+    del bpy.types.Scene.CM_Phantom_Mode
+    
+    # remove keymaps when add-on is deactivated
+    for km, kmi in addon_keymaps:
+        km.keymap_items.remove(kmi)
+    addon_keymaps.clear()
+    
+if __name__ == "__main__":
+    register()
diff --git a/collection_manager/internals.py b/collection_manager/internals.py
new file mode 100644
index 0000000000000000000000000000000000000000..e898de1cefa7d4d0fdff705c125221f610bf4300
--- /dev/null
+++ b/collection_manager/internals.py
@@ -0,0 +1,107 @@
+from bpy.types import PropertyGroup
+from bpy.props import StringProperty
+
+layer_collections = {}
+
+collection_tree = []
+
+expanded = []
+
+max_lvl = 0
+row_index = 0
+
+def get_max_lvl():
+    return max_lvl
+
+def update_col_name(self, context):
+    if self.name != self.last_name:
+        if self.name == '':
+            self.name = self.last_name
+            return
+        
+        if self.last_name != '':
+            layer_collections[self.last_name]["ptr"].collection.name = self.name
+            
+            update_property_group(context)
+        
+        self.last_name = self.name
+
+class CMListCollection(PropertyGroup):
+    name: StringProperty(update=update_col_name)
+    last_name: StringProperty()
+
+
+def update_collection_tree(context):
+    global max_lvl
+    global row_index
+    collection_tree.clear()
+    layer_collections.clear()
+    max_lvl = 0
+    row_index = 0
+    
+    init_laycol_list = context.view_layer.layer_collection.children
+    
+    master_laycol = {"id": 0,
+                     "name": context.view_layer.layer_collection.name,
+                     "lvl": -1,
+                     "row_index": -1,
+                     "visible": True,
+                     "has_children": True,
+                     "expanded": True,
+                     "parent": None,
+                     "children": [],
+                     "ptr": context.view_layer.layer_collection
+                     }
+    
+    get_all_collections(context, init_laycol_list, master_laycol, collection_tree, visible=True)
+
+
+def get_all_collections(context, collections, parent, tree, level=0, visible=False):
+    global row_index
+    
+    for item in collections:
+        laycol = {"id": len(layer_collections) +1,
+                  "name": item.name,
+                  "lvl": level,
+                  "row_index": row_index,
+                  "visible":  visible,
+                  "has_children": False,
+                  "expanded": False,
+                  "parent": parent,
+                  "children": [],
+                  "ptr": item
+                  }
+        
+        row_index += 1
+        
+        layer_collections[item.name] = laycol
+        tree.append(laycol)
+        
+        if len(item.children) > 0:
+            global max_lvl
+            max_lvl += 1
+            laycol["has_children"] = True
+            
+            if item.name in expanded and laycol["visible"]:
+                laycol["expanded"] = True
+                get_all_collections(context, item.children, laycol, laycol["children"], level+1,  visible=True)
+                
+            else:
+                get_all_collections(context, item.children, laycol, laycol["children"], level+1)
+
+
+def update_property_group(context):
+    update_collection_tree(context)
+    context.scene.CMListCollection.clear()
+    create_property_group(context, collection_tree)
+
+
+def create_property_group(context, tree):
+    global in_filter
+    
+    for laycol in tree:
+        new_cm_listitem = context.scene.CMListCollection.add()
+        new_cm_listitem.name = laycol["name"]
+    
+        if laycol["has_children"]:
+            create_property_group(context, laycol["children"])
diff --git a/collection_manager/operators.py b/collection_manager/operators.py
new file mode 100644
index 0000000000000000000000000000000000000000..84396e7e25a8f0effc5044d6377c06d6894b3e12
--- /dev/null
+++ b/collection_manager/operators.py
@@ -0,0 +1,1210 @@
+import bpy
+from bpy.types import Operator
+from bpy.props import (
+    BoolProperty,
+    StringProperty,
+    IntProperty
+    )
+
+from .internals import *
+
+rto_history = {"exclude": {},
+               "exclude_all": {},
+               "select": {},
+               "select_all": {},
+               "hide": {},
+               "hide_all": {},
+               "disable": {},
+               "disable_all": {},
+               "render": {},
+               "render_all": {}
+               }
+
+class ExpandAllOperator(bpy.types.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)
+        
+        return {'FINISHED'}
+
+
+class ExpandSublevelOperator(bpy.types.Operator):
+    '''  * Shift-Click to expand/collapse all sublevels'''
+    bl_label = "Expand Sublevel Items"
+    bl_idname = "view3d.expand_sublevel"
+    bl_options = {'REGISTER', 'UNDO'}
+    
+    expand: BoolProperty()
+    name: StringProperty()
+    index: IntProperty()
+    
+    def invoke(self, context, event):
+        if event.shift:
+            # 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"])
+            
+        else:
+            # expand/collapse collection
+            if self.expand:
+                expanded.append(self.name)
+            else:
+                expanded.remove(self.name)
+        
+        
+        # set selected row to the collection you're expanding/collapsing and update tree view
+        context.scene.CMListIndex = self.index
+        update_property_group(context)
+        
+        return {'FINISHED'}
+
+
+class CMSetCollectionOperator(bpy.types.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):
+        collection = layer_collections[self.collection_name]["ptr"].collection
+        
+        if event.shift:
+            # add object to collection
+            
+            # check if in collection
+            in_collection = True
+            
+            for obj in context.selected_objects:
+                if obj.name not in collection.objects:
+                    in_collection = False
+            
+            if not in_collection:
+                # add to collection
+                bpy.ops.object.link_to_collection(collection_index=self.collection_index)
+                
+            else:
+                # check and disallow removing from all collections
+                for obj in context.selected_objects:
+                    if len(obj.users_collection) == 1:
+                        send_report("Error removing 1 or more objects from this collection.\nObjects would be left without a collection")
+                        
+                        return {'FINISHED'}
+                
+                # remove from collection
+                bpy.ops.collection.objects_remove(collection=collection.name)
+        
+        else:
+            # move object to collection
+            bpy.ops.object.move_to_collection(collection_index=self.collection_index)
+        
+        return {'FINISHED'}
+
+
+class CMExcludeOperator(bpy.types.Operator):
+    '''  * Shift-Click to isolate/restore previous state\n  * Ctrl-Click to toggle children'''
+    bl_label = "Exclude Collection from View Layer"
+    bl_idname = "view3d.exclude_collection"
+    bl_options = {'REGISTER', 'UNDO'}
+    
+    name: StringProperty()
+    
+    def invoke(self, context, event):
+        global rto_history
+        
+        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": []}
+        
+        rto_history["exclude"][view_layer]["target"] = self.name
+        exclude_history = rto_history["exclude"][view_layer]["history"]
+        
+        if event.shift:
+            # isolate/de-isolate exclusion of collections
+            
+            # get active layer collections
+            active_layer_collections = [x for x in layer_collections.values() \
+                                          if x["ptr"].exclude == False]
+            
+            # check if collection isolated
+            if len(active_layer_collections) == 1 and active_layer_collections[0]["name"] == self.name:
+                if len(exclude_history) > 1:
+                    # restore previous state
+                    for x, item in enumerate(layer_collections.values()):
+                        item["ptr"].exclude = exclude_history[x]
+                
+                else:
+                    # 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
+                
+                # reset exclude history
+                exclude_history.clear()
+                
+                # save state
+                keep_history = -1
+                for item in layer_collections.values():
+                    exclude_history.append(item["ptr"].exclude)
+                    
+                    if item["ptr"].exclude == False:
+                        keep_history += 1
+                
+                if not keep_history:
+                    del rto_history["exclude"][view_layer]
+                
+                
+                # 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 event.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
+        
+        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]
+        
+        return {'FINISHED'}
+
+
+class CMUnExcludeAllOperator(bpy.types.Operator):
+    '''  * Click to toggle between current excluded state and all included.\n  * Shift-Click to invert excluded status of all collections'''
+    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
+        
+        view_layer = context.view_layer.name
+        
+        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 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]
+                
+                print([not i for i in exclude_all_history])
+            
+            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]
+        
+        return {'FINISHED'}
+
+
+class CMRestrictSelectOperator(bpy.types.Operator):
+    '''  * Shift-Click to isolate/restore previous state\n  * Ctrl-Click to toggle children'''
+    bl_label = "Disable Selection of Collection"
+    bl_idname = "view3d.restrict_select_collection"
+    bl_options = {'REGISTER', 'UNDO'}
+    
+    name: StringProperty()
+    
+    def invoke(self, context, event):
+        global rto_history
+        
+        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": []}
+        
+        rto_history["select"][view_layer]["target"] = self.name
+        select_history = rto_history["select"][view_layer]["history"]
+        
+        if event.shift:
+            # isolate/de-isolate selectability of collections
+            
+            # get active collections
+            active_layer_collections = [x for x in layer_collections.values() \
+                                          if x["ptr"].collection.hide_select == False]
+            
+            layerchain = []
+            laycol = layer_collections[self.name]
+            
+            # get chain of parents up to top level collection
+            while laycol["id"] != 0:
+                    layerchain.append(laycol)
+                    laycol = laycol["parent"]
+                    
+            # check if reversed layerchain matches active collections
+            if layerchain[::-1] == active_layer_collections:
+                if len(select_history) > 1:
+                    # restore previous state
+                    for x, item in enumerate(layer_collections.values()):
+                        item["ptr"].collection.hide_select = select_history[x]
+                
+                else:
+                    # 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]
+            
+            else:
+                # reset select history
+                select_history.clear()
+                
+                # save state
+                keep_history = -1
+                for item in layer_collections.values():
+                    select_history.append(item["ptr"].collection.hide_select)
+                    
+                    if item["ptr"].collection.hide_select == False:
+                        keep_history += 1
+                
+                if not keep_history:
+                    del rto_history["select"][view_layer]
+                
+                # 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 event.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
+        
+        else:
+            # 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]
+        
+        return {'FINISHED'}
+
+
+class CMUnRestrictSelectAllOperator(bpy.types.Operator):
+    '''  * Click to toggle between current selectable state and all selectable.\n  * Shift-Click to invert selectable status of all collections'''
+    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
+        
+        view_layer = context.view_layer.name
+
+        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 len(select_all_history) == 0:
+            select_all_history.clear()
+            keep_history = False
+            
+            for item in layer_collections.values():
+                if event.shift:
+                    keep_history = True
+                    select_all_history.append(item["ptr"].collection.hide_select)
+                    item["ptr"].collection.hide_select = not item["ptr"].collection.hide_select
+                
+                else:
+                    if item["ptr"].collection.hide_select:
+                        keep_history = True
+                        
+                    select_all_history.append(item["ptr"].collection.hide_select)
+                    item["ptr"].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]
+        
+        return {'FINISHED'}
+
+
+class CMHideOperator(bpy.types.Operator):
+    '''  * Shift-Click to isolate/restore previous state\n  * Ctrl-Click to toggle children'''
+    bl_label = "Hide Collection"
+    bl_idname = "view3d.hide_collection"
+    bl_options = {'REGISTER', 'UNDO'}
+    
+    name: StringProperty()
+    
+    def invoke(self, context, event):
+        global rto_history
+        
+        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": []}
+        
+        rto_history["hide"][view_layer]["target"] = self.name
+        hide_history = rto_history["hide"][view_layer]["history"]
+        
+        if event.shift:
+            # isolate/de-isolate view of collections
+            
+            # get active collections
+            active_layer_collections = [x for x in layer_collections.values() \
+                                          if x["ptr"].hide_viewport == False]
+            
+            layerchain = []
+            laycol = layer_collections[self.name]
+            
+            # get chain of parents up to top level collection
+            while laycol["id"] != 0:
+                    layerchain.append(laycol)
+                    laycol = laycol["parent"]
+                    
+            # check if reversed layerchain matches active collections
+            if layerchain[::-1] == active_layer_collections:
+                if len(hide_history) > 1:
+                    # restore previous state
+                    for x, item in enumerate(layer_collections.values()):
+                        item["ptr"].hide_viewport = hide_history[x]
+                
+                else:
+                    # show all collections
+                    for laycol in layer_collections.values():
+                        laycol["ptr"].hide_viewport = False
+                    
+                # reset hide history
+                del rto_history["hide"][view_layer]
+                
+            else:
+                # reset hide history
+                hide_history.clear()
+                
+                # save state
+                keep_history = -1
+                for item in layer_collections.values():
+                    hide_history.append(item["ptr"].hide_viewport)
+                    
+                    if item["ptr"].hide_viewport == False:
+                        keep_history += 1
+                
+                if not keep_history:
+                    del rto_history["hide"][view_layer]
+                
+                # 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:
+                    laycol["ptr"].hide_viewport = False
+                    laycol = laycol["parent"]
+        
+        elif event.ctrl:
+            # toggle children
+            
+            # reset hide history
+            del rto_history["hide"][view_layer]
+            
+            # toggle view of collection
+            state = not laycol_ptr.hide_viewport
+            laycol_ptr.hide_viewport = 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.hide_viewport = state
+                        if len(layer_collection.children) > 0:
+                            new_laycol_iter_list.append(layer_collection.children)
+                
+                laycol_iter_list = new_laycol_iter_list
+        
+        else:
+            # 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]
+        
+        return {'FINISHED'}
+
+
+class CMUnHideAllOperator(bpy.types.Operator):
+    '''  * Click to toggle between current visibility state and all visible.\n  * Shift-Click to invert visibility status of all collections'''
+    bl_label = "Toggle Hidden Status Of All Collections"
+    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
+
+        if not view_layer in rto_history["hide_all"]:
+            rto_history["hide_all"][view_layer] = []
+
+        hide_all_history = rto_history["hide_all"][view_layer]
+        
+        if len(hide_all_history) == 0:
+            hide_all_history.clear()
+            keep_history = False
+            
+            for item in layer_collections.values():
+                if event.shift:
+                    keep_history = True
+                    hide_all_history.append(item["ptr"].hide_viewport)
+                    item["ptr"].hide_viewport = not item["ptr"].hide_viewport
+                
+                else:
+                    if item["ptr"].hide_viewport:
+                        keep_history = True
+                        
+                    hide_all_history.append(item["ptr"].hide_viewport)
+                    item["ptr"].hide_viewport = False
+            
+            if not keep_history:
+                del rto_history["hide_all"][view_layer]
+        
+        else:
+            for x, item in enumerate(layer_collections.values()):
+                item["ptr"].hide_viewport = hide_all_history[x]
+            
+            del rto_history["hide_all"][view_layer]
+        
+        return {'FINISHED'}
+
+
+class CMDisableViewportOperator(bpy.types.Operator):
+    '''  * Shift-Click to isolate/restore previous state\n  * Ctrl-Click to toggle children'''
+    bl_label = "Disable Collection in Viewport"
+    bl_idname = "view3d.disable_viewport_collection"
+    bl_options = {'REGISTER', 'UNDO'}
+    
+    name: StringProperty()
+    
+    def invoke(self, context, event):
+        global rto_history
+        
+        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": []}
+        
+        rto_history["disable"][view_layer]["target"] = self.name
+        disable_history = rto_history["disable"][view_layer]["history"]
+        
+        if event.shift:
+            # isolate/de-isolate disablement of collections in viewport
+            
+            # get active collections
+            active_layer_collections = [x for x in layer_collections.values() \
+                                          if x["ptr"].collection.hide_viewport == False]
+            
+            layerchain = []
+            laycol = layer_collections[self.name]
+            
+            # get chain of parents up to top level collection
+            while laycol["id"] != 0:
+                    layerchain.append(laycol)
+                    laycol = laycol["parent"]
+                    
+            # check if reversed layerchain matches active collections
+            if layerchain[::-1] == active_layer_collections:
+                if len(disable_history) > 1:
+                    # restore previous state
+                    for x, item in enumerate(layer_collections.values()):
+                        item["ptr"].collection.hide_viewport = disable_history[x]
+                
+                else:
+                    # enable all collections in viewport
+                    for laycol in layer_collections.values():
+                        laycol["ptr"].collection.hide_viewport = False
+                
+                # reset disable history
+                del rto_history["disable"][view_layer]
+                
+            else:
+                # reset disable history
+                disable_history.clear()
+                
+                # save state
+                keep_history = -1
+                for item in layer_collections.values():
+                    disable_history.append(item["ptr"].collection.hide_viewport)
+                    
+                    if item["ptr"].collection.hide_viewport == False:
+                        keep_history += 1
+                
+                if not keep_history:
+                    del rto_history["disable"][view_layer]
+                
+                # disable all collections in viewport
+                for laycol in layer_collections.values():
+                    laycol["ptr"].collection.hide_viewport = True
+                
+                # enable active collection plus parents in viewport
+                laycol_ptr.collection.hide_viewport = False
+                
+                laycol = layer_collections[self.name]
+                while laycol["id"] != 0:
+                    laycol["ptr"].collection.hide_viewport = False
+                    laycol = laycol["parent"]
+        
+        elif event.ctrl:
+            # toggle children
+            
+            # reset disable history
+            del rto_history["disable"][view_layer]
+            
+            # toggle view of collection
+            state = not laycol_ptr.collection.hide_viewport
+            laycol_ptr.collection.hide_viewport = 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_viewport = state
+                        if len(layer_collection.children) > 0:
+                            new_laycol_iter_list.append(layer_collection.children)
+                
+                laycol_iter_list = new_laycol_iter_list
+        
+        else:
+            # 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]
+        
+        return {'FINISHED'}
+
+
+class CMUnDisableViewportAllOperator(bpy.types.Operator):
+    '''  * Click to toggle between current viewport display and all enabled.\n  * Shift-Click to invert viewport display of all collections'''
+    bl_label = "Toggle Viewport Display of All Collections"
+    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
+
+        if not view_layer in rto_history["disable_all"]:
+            rto_history["disable_all"][view_layer] = []
+
+        disable_all_history = rto_history["disable_all"][view_layer]
+        
+        if len(disable_all_history) == 0:
+            disable_all_history.clear()
+            keep_history = False
+            
+            for item in layer_collections.values():
+                if event.shift:
+                    keep_history = True
+                    disable_all_history.append(item["ptr"].collection.hide_viewport)
+                    item["ptr"].collection.hide_viewport = not \
+                        item["ptr"].collection.hide_viewport
+                
+                else:
+                    if item["ptr"].collection.hide_viewport:
+                        keep_history = True
+                        
+                    disable_all_history.append(item["ptr"].collection.hide_viewport)
+                    item["ptr"].collection.hide_viewport = False
+            
+            if not keep_history:
+                del rto_history["disable_all"][view_layer]
+        
+        else:
+            for x, item in enumerate(layer_collections.values()):
+                item["ptr"].collection.hide_viewport = disable_all_history[x]
+            
+            del rto_history["disable_all"][view_layer]
+        
+        return {'FINISHED'}
+
+
+class CMDisableRenderOperator(bpy.types.Operator):
+    '''  * Shift-Click to isolate/restore previous state\n  * Shift-Click to invert viewport display of all collections'''
+    bl_label = "Disable Collection in Render"
+    bl_idname = "view3d.disable_render_collection"
+    bl_options = {'REGISTER', 'UNDO'}
+    
+    name: StringProperty()
+    
+    def invoke(self, context, event):
+        global rto_history
+        
+        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": []}
+
+        rto_history["render"][view_layer]["target"] = self.name
+        render_history = rto_history["render"][view_layer]["history"]
+        
+        if event.shift:
+            # isolate/de-isolate render of collections
+            
+            # get active collections
+            active_layer_collections = [x for x in layer_collections.values() \
+                                          if x["ptr"].collection.hide_render == False]
+            
+            layerchain = []
+            laycol = layer_collections[self.name]
+            
+            # get chain of parents up to top level collection
+            while laycol["id"] != 0:
+                    layerchain.append(laycol)
+                    laycol = laycol["parent"]
+                    
+            # check if reversed layerchain matches active collections
+            if layerchain[::-1] == active_layer_collections:
+                if len(render_history) > 1:
+                    # restore previous state
+                    for x, item in enumerate(layer_collections.values()):
+                        item["ptr"].collection.hide_render = render_history[x]
+                
+                else:
+                    # allow render of all collections
+                    for laycol in layer_collections.values():
+                        laycol["ptr"].collection.hide_render = False
+                
+                # reset render history
+                del rto_history["render"][view_layer]
+                
+            else:
+                # reset render history
+                render_history.clear()
+                
+                # save state
+                keep_history = -1
+                for item in layer_collections.values():
+                    render_history.append(item["ptr"].collection.hide_render)
+                    
+                    if item["ptr"].collection.hide_render == False:
+                        keep_history += 1
+                
+                if not keep_history:
+                    del rto_history["render"][view_layer]
+                
+                # disallow render of all collections
+                for laycol in layer_collections.values():
+                    laycol["ptr"].collection.hide_render = True
+                
+                # allow render of active collection plus parents
+                laycol_ptr.collection.hide_render = False
+                
+                laycol = layer_collections[self.name]
+                while laycol["id"] != 0:
+                    laycol["ptr"].collection.hide_render = False
+                    laycol = laycol["parent"]
+        
+        elif event.ctrl:
+            # toggle children
+            
+            # reset render history
+            del rto_history["render"][view_layer]
+            
+            # toggle view of collection
+            state = not laycol_ptr.collection.hide_render
+            laycol_ptr.collection.hide_render = 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_render = state
+                        if len(layer_collection.children) > 0:
+                            new_laycol_iter_list.append(layer_collection.children)
+                
+                laycol_iter_list = new_laycol_iter_list
+        
+        else:
+            # 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]
+        
+        return {'FINISHED'}
+
+
+class CMUnDisableRenderAllOperator(bpy.types.Operator):
+    '''  * Click to toggle between current render status and all rendered.\n  * Shift-Click to invert render status of all collections'''
+    bl_label = "Toggle Render Status of All Collections"
+    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
+
+        if not view_layer in rto_history["render_all"]:
+            rto_history["render_all"][view_layer] = []
+
+        render_all_history = rto_history["render_all"][view_layer]
+        
+        if len(render_all_history) == 0:
+            render_all_history.clear()
+            keep_history = False
+            
+            for item in layer_collections.values():
+                if event.shift:
+                    keep_history = True
+                    render_all_history.append(item["ptr"].collection.hide_render)
+                    item["ptr"].collection.hide_render = not \
+                        item["ptr"].collection.hide_render
+                
+                else:
+                    if item["ptr"].collection.hide_render:
+                        keep_history = True
+                        
+                    render_all_history.append(item["ptr"].collection.hide_render)
+                    item["ptr"].collection.hide_render = False
+            
+            if not keep_history:
+                del rto_history["render_all"][view_layer]
+        
+        else:
+            for x, item in enumerate(layer_collections.values()):
+                item["ptr"].collection.hide_render = render_all_history[x]
+            
+            del rto_history["render_all"][view_layer]
+        
+        return {'FINISHED'}
+
+
+class CMRemoveCollectionOperator(bpy.types.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
+        
+        laycol = layer_collections[self.collection_name]
+        collection = laycol["ptr"].collection
+        laycol_parent = laycol["parent"]
+        
+        # save state and remove all hiding properties of parent collection
+        orig_parent_hide_select = False
+        orig_parent_exclude = False
+        orig_parent_hide_viewport = False
+        
+        if laycol_parent["ptr"].collection.hide_select:
+            orig_parent_hide_select = True
+        
+        if laycol_parent["ptr"].exclude:
+            orig_parent_exclude = True
+        
+        if laycol_parent["ptr"].hide_viewport:
+            orig_parent_hide_viewport = True
+        
+        laycol_parent["ptr"].collection.hide_select = False
+        laycol_parent["ptr"].exclude = False
+        laycol_parent["ptr"].hide_viewport = False
+        
+        
+        # remove all hiding properties of this collection
+        collection.hide_select = False
+        laycol["ptr"].exclude = False
+        laycol["ptr"].hide_viewport = False
+        
+        
+        # shift all objects in this collection to the parent collection
+        if collection.objects:
+            orig_selected_objs = context.selected_objects
+            orig_active_obj = context.active_object
+        
+            # select all objects in collection
+            bpy.ops.object.select_same_collection(collection=collection.name)
+            context.view_layer.objects.active = context.selected_objects[0]
+            
+            # remove any objects already in parent collection from selection
+            for obj in context.selected_objects:
+                if obj in laycol["parent"]["ptr"].collection.objects.values():
+                    obj.select_set(False)
+            
+            # link selected objects to parent collection
+            bpy.ops.object.link_to_collection(collection_index=laycol_parent["id"])
+            
+            # remove objects from collection
+            bpy.ops.collection.objects_remove(collection=collection.name)
+            
+            # reset selection original values
+            bpy.ops.object.select_all(action='DESELECT')
+            
+            for obj in orig_selected_objs:
+                obj.select_set(True)
+            context.view_layer.objects.active = orig_active_obj
+        
+        
+        # shift all child collections to the parent collection
+        if collection.children:
+            for subcollection in collection.children:
+                laycol_parent["ptr"].collection.children.link(subcollection)
+        
+        # reset hiding properties of parent collection
+        laycol_parent["ptr"].collection.hide_select = orig_parent_hide_select
+        laycol_parent["ptr"].exclude = orig_parent_exclude
+        laycol_parent["ptr"].hide_viewport = orig_parent_hide_viewport
+        
+        
+        # remove collection and update tree view
+        bpy.data.collections.remove(collection)
+        
+        
+        update_property_group(context)
+        
+        if len(context.scene.CMListCollection) == context.scene.CMListIndex:
+            context.scene.CMListIndex = len(context.scene.CMListCollection) - 1
+            update_property_group(context)
+        
+        
+        # reset history
+        for rto in rto_history.values():
+            rto.clear()
+        
+        return {'FINISHED'}
+
+rename = [False]
+class CMNewCollectionOperator(bpy.types.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')
+        scn = context.scene
+        
+        # if there are collections
+        if len(scn.CMListCollection) > 0:
+            # get selected collection
+            laycol = layer_collections[scn.CMListCollection[scn.CMListIndex].name]
+            
+            # add new collection
+            if self.child:
+                laycol["ptr"].collection.children.link(new_collection)
+                expanded.append(laycol["name"])
+                
+                # update tree view property
+                update_property_group(context)
+                
+                scn.CMListIndex = layer_collections[new_collection.name]["row_index"]
+                
+            else:
+                laycol["parent"]["ptr"].collection.children.link(new_collection)
+                
+                # update tree view property
+                update_property_group(context)
+                
+                scn.CMListIndex = layer_collections[new_collection.name]["row_index"]
+                
+        # if no collections add top level collection and select it
+        else:
+            scn.collection.children.link(new_collection)
+            
+            # update tree view property
+            update_property_group(context)
+            
+            scn.CMListIndex = 0
+        
+        global rename
+        rename[0] = True
+        
+        # reset history
+        for rto in rto_history.values():
+            rto.clear()
+        
+        return {'FINISHED'}
+    
+
+phantom_history = {"view_layer": "",
+                   "initial_state": {},
+                   
+                   "exclude_history": [],
+                   "select_history": [],
+                   "hide_history": [],
+                   "disable_history": [],
+                   "render_history": [],
+                   
+                   "exclude_all_history": [],
+                   "select_all_history": [],
+                   "hide_all_history": [],
+                   "disable_all_history": [],
+                   "render_all_history": []
+                   }
+
+class CMPhantomModeOperator(bpy.types.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
+        
+        scn = context.scene
+        view_layer = context.view_layer.name
+        
+        # enter Phantom Mode
+        if not scn.CM_Phantom_Mode:
+            
+            scn.CM_Phantom_Mode = True
+            
+            # save current visibility state
+            phantom_history["view_layer"] = view_layer
+            
+            laycol_iter_list = [context.view_layer.layer_collection.children]
+            while len(laycol_iter_list) > 0:
+                new_laycol_iter_list = []
+                for laycol_iter in laycol_iter_list:
+                    for layer_collection in laycol_iter:
+                        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,
+                                }
+                        
+                        if len(layer_collection.children) > 0:
+                            new_laycol_iter_list.append(layer_collection.children)
+                
+                laycol_iter_list = new_laycol_iter_list
+            
+            
+            # save current rto history
+            for rto, history, in rto_history.items():
+                phantom_history[rto+"_history"] = history.get(view_layer, {}).copy()
+        
+        
+        # return to normal mode
+        else:
+            laycol_iter_list = [context.view_layer.layer_collection.children]
+            while len(laycol_iter_list) > 0:
+                new_laycol_iter_list = []
+                for laycol_iter in laycol_iter_list:
+                    for layer_collection in laycol_iter:
+                        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"]
+                                
+                        
+                        if len(layer_collection.children) > 0:
+                            new_laycol_iter_list.append(layer_collection.children)
+                
+                laycol_iter_list = new_laycol_iter_list
+                
+            
+            # restore previous rto history
+            for rto, history, in rto_history.items():
+                history[view_layer] = phantom_history[rto+"_history"].copy()
+            
+            scn.CM_Phantom_Mode = False
+            
+        
+        return {'FINISHED'}
diff --git a/collection_manager/ui.py b/collection_manager/ui.py
new file mode 100644
index 0000000000000000000000000000000000000000..4c0e4597b6badf62355d79f901fa4cf71a62c42e
--- /dev/null
+++ b/collection_manager/ui.py
@@ -0,0 +1,412 @@
+from bpy.types import (
+    Operator,
+    Panel,
+    UIList,
+    UI_UL_list,
+    )
+
+from bpy.props import BoolProperty
+
+from .internals import *
+from .operators import (
+    rto_history,
+    rename,
+    phantom_history,
+    )
+
+
+class CollectionManager(Operator):
+    bl_label = "Collection Manager"
+    bl_idname = "view3d.collection_manager"
+    
+    last_view_layer = ""
+    
+    def draw(self, context):
+        layout = self.layout
+        scn = context.scene
+        view_layer = context.view_layer.name
+        
+        if view_layer != self.last_view_layer:
+            update_collection_tree(context)
+            self.last_view_layer = view_layer
+        
+        title_row = layout.split(factor=0.5)
+        main = title_row.row()
+        view = title_row.row(align=True)
+        view.alignment = 'RIGHT'
+        
+        main.label(text="Collection Manager")
+        
+        view.prop(context.view_layer, "use", text="")
+        view.separator()
+        
+        window = context.window
+        scene = window.scene
+        view.template_search(
+            window, "view_layer",
+            scene, "view_layers",
+            new="scene.view_layer_add",
+            unlink="scene.view_layer_remove")
+        
+        layout.row().separator()
+        layout.row().separator()
+        
+        filter_row = layout.row()
+        filter_row.alignment = 'RIGHT'
+        
+        filter_row.popover(panel="COLLECTIONMANAGER_PT_restriction_toggles", text="", icon='FILTER')
+        
+        toggle_row = layout.split(factor=0.3)
+        toggle_row.alignment = 'LEFT'
+        
+        sec1 = toggle_row.row()
+        sec1.alignment = 'LEFT'
+        sec1.enabled = False
+        
+        if len(expanded) > 0:
+            text = "Collapse All Items"
+        else:
+            text = "Expand All Items"
+        
+        sec1.operator("view3d.expand_all_items", text=text)
+        
+        for laycol in collection_tree:
+            if laycol["has_children"]:
+                sec1.enabled = True
+                break
+        
+        sec2 = toggle_row.row()
+        sec2.alignment = 'RIGHT'
+        
+        if scn.show_exclude:
+            exclude_all_history = rto_history["exclude_all"].get(view_layer, [])
+            depress = True if len(exclude_all_history) else False
+            
+            sec2.operator("view3d.un_exclude_all_collections", text="", icon='CHECKBOX_HLT', depress=depress)
+        
+        if scn.show_selectable:
+            select_all_history = rto_history["select_all"].get(view_layer, [])
+            depress = True if len(select_all_history) else False
+            
+            sec2.operator("view3d.un_restrict_select_all_collections", text="", icon='RESTRICT_SELECT_OFF', depress=depress)
+        
+        if scn.show_hideviewport:
+            hide_all_history = rto_history["hide_all"].get(view_layer, [])
+            depress = True if len(hide_all_history) else False
+            
+            sec2.operator("view3d.un_hide_all_collections", text="", icon='HIDE_OFF', depress=depress)
+        
+        if scn.show_disableviewport:
+            disable_all_history = rto_history["disable_all"].get(view_layer, [])
+            depress = True if len(disable_all_history) else False
+            
+            sec2.operator("view3d.un_disable_viewport_all_collections", text="", icon='RESTRICT_VIEW_OFF', depress=depress)
+        
+        if scn.show_render:
+            render_all_history = rto_history["render_all"].get(view_layer, [])
+            depress = True if len(render_all_history) else False
+            
+            sec2.operator("view3d.un_disable_render_all_collections", text="", icon='RESTRICT_RENDER_OFF', depress=depress)
+        
+        layout.row().template_list("CM_UL_items", "", context.scene, "CMListCollection", context.scene, "CMListIndex", rows=15, sort_lock=True)
+        
+        addcollec_row = layout.row()
+        addcollec_row.operator("view3d.add_collection", text="Add Collection", icon='COLLECTION_NEW').child = False
+        
+        addcollec_row.operator("view3d.add_collection", text="Add SubCollection", icon='COLLECTION_NEW').child = True
+        
+        phantom_row = layout.row()
+        toggle_text = "Disable " if scn.CM_Phantom_Mode else "Enable "
+        phantom_row.operator("view3d.toggle_phantom_mode", text=toggle_text+"Phantom Mode")
+        
+        if scn.CM_Phantom_Mode:
+            view.enabled = False
+            addcollec_row.enabled = False
+        
+        
+    def execute(self, context):
+        wm = context.window_manager
+        lvl = 0
+        
+        #expanded.clear()
+        
+        #excludeall_history.clear()
+        #restrictselectall_history.clear()
+        #hideall_history.clear()
+        #disableviewall_history.clear()
+        #disablerenderall_history.clear()
+
+        update_property_group(context)
+        
+        lvl = get_max_lvl()
+        
+        if lvl > 25:
+            lvl = 25
+
+        self.view_layer = context.view_layer.name
+        
+        # sync selection in ui list with active layer collection
+        try:
+            active_laycol_name = context.view_layer.active_layer_collection.name
+            active_laycol_row_index = layer_collections[active_laycol_name]["row_index"]
+            context.scene.CMListIndex = active_laycol_row_index
+        except:
+            context.scene.CMListIndex = 0
+        
+        if context.scene.CM_Phantom_Mode:
+            if set(layer_collections.keys()) != set(phantom_history["initial_state"].keys()):
+                context.scene.CM_Phantom_Mode = False
+            
+            if context.view_layer.name != phantom_history["view_layer"]:
+                context.scene.CM_Phantom_Mode = False
+        
+        return wm.invoke_popup(self, width=(400+(lvl*20)))
+
+
+def update_selection(self, context):
+    selected_item = context.scene.CMListCollection[context.scene.CMListIndex]
+    layer_collection = layer_collections[selected_item.name]["ptr"]
+    
+    context.view_layer.active_layer_collection = layer_collection
+
+
+def filter_items_by_name_insensitive(pattern, bitflag, items, propname="name", flags=None, reverse=False):
+        """
+        Set FILTER_ITEM for items which name matches filter_name one (case-insensitive).
+        pattern is the filtering pattern.
+        propname is the name of the string property to use for filtering.
+        flags must be a list of integers the same length as items, or None!
+        return a list of flags (based on given flags if not None),
+        or an empty list if no flags were given and no filtering has been done.
+        """
+        import fnmatch
+
+        if not pattern or not items:  # Empty pattern or list = no filtering!
+            return flags or []
+
+        if flags is None:
+            flags = [0] * len(items)
+        
+        # Make pattern case-insensitive
+        pattern = pattern.lower()
+
+        # Implicitly add heading/trailing wildcards.
+        pattern = "*" + pattern + "*"
+
+        for i, item in enumerate(items):
+            name = getattr(item, propname, None)
+            
+            # Make name case-insensitive
+            name = name.lower()
+            
+            # This is similar to a logical xor
+            if bool(name and fnmatch.fnmatch(name, pattern)) is not bool(reverse):
+                flags[i] |= bitflag
+            
+        return flags
+
+
+class CM_UL_items(UIList):
+    last_filter_value = ""
+    
+    filter_by_selected: BoolProperty(
+                        name="Filter By Selected",
+                        default=False,
+                        description="Filter collections by selected items"
+                        )
+    
+    def draw_item(self, context, layout, data, item, icon, active_data,active_propname, index):
+        self.use_filter_show = True
+        
+        scn = context.scene
+        view_layer = context.view_layer.name
+        laycol = layer_collections[item.name]
+        collection = laycol["ptr"].collection
+        
+        split = layout.split(factor=0.96)
+        row = split.row(align=True)
+        row.alignment = 'LEFT'
+        
+        # indent child items
+        if laycol["lvl"] > 0:
+            for x in range(laycol["lvl"]):
+                row.label(icon='BLANK1')
+        
+        # add expander if collection has children to make UIList act like tree view
+        if laycol["has_children"]:
+            if laycol["expanded"]:
+                prop = row.operator("view3d.expand_sublevel", text="", icon='DISCLOSURE_TRI_DOWN', emboss=False)
+                prop.expand = False
+                prop.name = item.name
+                prop.index = index
+                
+            else:
+                prop = row.operator("view3d.expand_sublevel", text="", icon='DISCLOSURE_TRI_RIGHT', emboss=False)
+                prop.expand = True
+                prop.name = item.name
+                prop.index = index
+                
+        else:
+            row.label(icon='BLANK1')
+        
+        
+        row.label(icon='GROUP')
+        
+        name_row = row.row()
+        
+        #if rename[0] and index == scn.CMListIndex:
+            #name_row.activate_init = True
+            #rename[0] = False
+            
+        name_row.prop(item, "name", text="", expand=True)
+        
+        # used as a separator (actual separator not wide enough)
+        row.label()
+        
+        # add set_collection op
+        row_setcol = row.row()
+        row_setcol.operator_context = 'INVOKE_DEFAULT'
+        
+        icon = 'MESH_CUBE'
+        
+        if len(context.selected_objects) > 0 and context.active_object:
+            if context.active_object.name in collection.objects:
+                icon = 'SNAP_VOLUME'
+        else:
+            row_setcol.enabled = False
+        
+        
+        prop = row_setcol.operator("view3d.set_collection", text="", icon=icon, emboss=False)
+        prop.collection_index = laycol["id"]
+        prop.collection_name = item.name
+        
+        
+        if scn.show_exclude:
+            exclude_history_base = rto_history["exclude"].get(view_layer, {})
+            exclude_target = exclude_history_base.get("target", "")
+            exclude_history = exclude_history_base.get("history", [])
+            
+            depress = True if len(exclude_history) and exclude_target == item.name else False
+            emboss = True if len(exclude_history) and exclude_target == item.name else False
+            icon = 'CHECKBOX_DEHLT' if laycol["ptr"].exclude else 'CHECKBOX_HLT'
+            
+            row.operator("view3d.exclude_collection", text="", icon=icon, emboss=emboss, depress=depress).name = item.name
+            
+        if scn.show_selectable:
+            select_history_base = rto_history["select"].get(view_layer, {})
+            select_target = select_history_base.get("target", "")
+            select_history = select_history_base.get("history", [])
+            
+            depress = True if len(select_history) and select_target == item.name else False
+            emboss = True if len(select_history) and select_target == item.name else False
+            icon = 'RESTRICT_SELECT_ON' if laycol["ptr"].collection.hide_select else 'RESTRICT_SELECT_OFF'
+            
+            row.operator("view3d.restrict_select_collection", text="", icon=icon, emboss=emboss, depress=depress).name = item.name
+        
+        if scn.show_hideviewport:
+            hide_history_base = rto_history["hide"].get(view_layer, {})
+            hide_target = hide_history_base.get("target", "")
+            hide_history = hide_history_base.get("history", [])
+            
+            depress = True if len(hide_history) and hide_target == item.name else False
+            emboss = True if len(hide_history) and hide_target == item.name else False
+            icon = 'HIDE_ON' if laycol["ptr"].hide_viewport else 'HIDE_OFF'
+            
+            row.operator("view3d.hide_collection", text="", icon=icon, emboss=emboss, depress=depress).name = item.name
+        
+        if scn.show_disableviewport:
+            disable_history_base = rto_history["disable"].get(view_layer, {})
+            disable_target = disable_history_base.get("target", "")
+            disable_history = disable_history_base.get("history", [])
+            
+            depress = True if len(disable_history) and disable_target == item.name else False
+            emboss = True if len(disable_history) and disable_target == item.name else False
+            icon = 'RESTRICT_VIEW_ON' if laycol["ptr"].collection.hide_viewport else 'RESTRICT_VIEW_OFF'
+            
+            row.operator("view3d.disable_viewport_collection", text="", icon=icon, emboss=emboss, depress=depress).name = item.name
+        
+        if scn.show_render:
+            render_history_base = rto_history["render"].get(view_layer, {})
+            render_target = render_history_base.get("target", "")
+            render_history = render_history_base.get("history", [])
+            
+            depress = True if len(render_history) and render_target == item.name else False
+            emboss = True if len(render_history) and render_target == item.name else False
+            icon = 'RESTRICT_RENDER_ON' if laycol["ptr"].collection.hide_render else 'RESTRICT_RENDER_OFF'
+            
+            row.operator("view3d.disable_render_collection", text="", icon=icon, emboss=emboss, depress=depress).name = item.name
+        
+        
+        rm_op = split.row()
+        rm_op.alignment = 'RIGHT'
+        rm_op.operator("view3d.remove_collection", text="", icon='X', emboss=False).collection_name = item.name
+        
+        if scn.CM_Phantom_Mode:
+            name_row.enabled = False
+            row_setcol.enabled = False
+            rm_op.enabled = False
+    
+    
+    def draw_filter(self, context, layout):
+        row = layout.row()
+
+        subrow = row.row(align=True)
+        subrow.prop(self, "filter_name", text="")
+        
+        icon = 'ZOOM_OUT' if self.use_filter_invert else 'ZOOM_IN'
+        subrow.prop(self, "use_filter_invert", text="", icon=icon)
+        
+        subrow = row.row(align=True)
+        subrow.prop(self, "filter_by_selected", text="", icon='SNAP_VOLUME')
+    
+    def filter_items(self, context, data, propname):
+        flt_flags = []
+        flt_neworder = []
+        
+        list_items = getattr(data, propname)
+        
+        if self.filter_name:
+            flt_flags = filter_items_by_name_insensitive(self.filter_name, self.bitflag_filter_item, list_items)
+        
+        elif self.filter_by_selected:
+            flt_flags = [0] * len(list_items)
+            
+            for idx, item in enumerate(list_items):
+                collection = layer_collections[item.name]["ptr"].collection
+                
+                # check if any of the selected objects are in the collection
+                if not set(context.selected_objects).isdisjoint(collection.objects):
+                    flt_flags[idx] |= self.bitflag_filter_item
+                    
+        else: # display as treeview
+            flt_flags = [self.bitflag_filter_item] * len(list_items)
+        
+            for idx, item in enumerate(list_items):
+                if not layer_collections[item.name]["visible"]:
+                    flt_flags[idx] = 0
+        
+        return flt_flags, flt_neworder
+    
+
+    
+    def invoke(self, context, event):
+        pass
+
+
+class CMRestrictionTogglesPanel(Panel):
+    bl_label = "Restriction Toggles"
+    bl_idname = "COLLECTIONMANAGER_PT_restriction_toggles"
+    bl_space_type = 'VIEW_3D'
+    bl_region_type = 'HEADER'
+    
+    def draw(self, context):
+        
+        layout = self.layout
+        
+        row = layout.row()
+        
+        row.prop(context.scene, "show_exclude", icon='CHECKBOX_HLT', icon_only=True)
+        row.prop(context.scene, "show_selectable", icon='RESTRICT_SELECT_OFF', icon_only=True)
+        row.prop(context.scene, "show_hideviewport", icon='HIDE_OFF', icon_only=True)
+        row.prop(context.scene, "show_disableviewport", icon='RESTRICT_VIEW_OFF', icon_only=True)
+        row.prop(context.scene, "show_render", icon='RESTRICT_RENDER_OFF', icon_only=True)