--	-----------------------------------------------------------------------------------------------------------------------------
--	LayerMan v2.7 // 06.12.2006 07:10:56															www.orionflame.com
--	Developed by Yunus Emre ''Light'' Balcioglu
--	-----------------------------------------------------------------------------------------------------------------------------
--	Feature List
--
--	Unlimited number of layers in any order with the ability to group them
--	Simple interface with drag & drop support
--	Compatible with max's default layer system
--	Ability to manage alternate properties and materials
--	Context sensitive menus and hotkeys
--	Independent macroscripts to use from keyboard, toolbars and quad menus
--
--	Development History
--
--	v2.7		initial release
(
	global LayerMan, LayerManProRC, LayerManMatRC, layersRC, C4
	
	rcMenu layersRC
	(
		fn filterFN = not LayerMan.ops.hitNode.bold
		fn filterFN2 = LayerMan.ops.hitNode.text != "0"
		fn filterFN3 = LayerMan.ops.hitNode.bold and filterFN2()
		fn filterFN4 = filterFN3() and LayerMan.ops.hitNode.children == 0
		fn filterFN5 = LayerMan.ops.hitNode.children == 0 and LayerMan.ops.hitLayer.count == 1
		fn filterFN6 = filterFN5() and not LayerMan.ops.hitLayer[1].current
		fn filterFN7 = filterFN() or filterFN3() or filterFN6()
		fn filterFN8 = filterFN() or filterFN4()
		fn dockedTop = LayerMan.dialog.dockState != #cui_dock_top
		fn dockedBottom = LayerMan.dialog.dockState != #cui_dock_bottom
		fn dockedLeft = LayerMan.dialog.dockState != #cui_dock_left
		fn dockedRight = LayerMan.dialog.dockState != #cui_dock_right
		fn undocked = LayerMan.dialog.dockState != #cui_floatable
		
		menuItem		addSel2Layer				"Add Selection to Layer	A"				filter:filterFN5
		menuItem		selectLayer				"Select Layer	S"
		menuItem		selectObjectsByName		"Select Objects by Name"
		seperator			sep01
		menuItem		createNewGroup			"Create New Group	1"
		menuItem		createNewLayer			"Create New Layer	2"
		menuItem		createLayerFromSel		"Create Layer from Selection      	3"
		seperator			sep02														filter:filterFN7
		menuItem		makeLayerCurrent			"Make Layer Current	D"				filter:filterFN6
		menuItem		rename					"Rename	F2"							filter:filterFN2
		seperator			sep03														filter:filterFN8
		menuItem		deleteGroup				"Delete Group	Del"						filter:filterFN4
		menuItem		deleteLayer				"Delete Layer	Del"						filter:filterFN
		menuItem		deleteLayerAndObjects 		"Delete Layer and Objects"				filter:filterFN
		seperator			sep04
		
		subMenu									"More"
		(
			menuItem	setWirecolorsRandomly		"Set Wirecolors Randomly"
			menuItem	getWirecolorsFromObjects	"Get Wirecolors from Objects"
			menuItem	sortLayersByName			"Sort Layers by Name"
		)
		
		subMenu									"Options"
		(
			menuItem	autoLoad					"Auto Load"							checked:LayerMan.dialog.autoLoad
			menuItem	autoFocus				"Auto Focus"							checked:LayerMan.dialog.autoFocus
			seperator		sep05
			menuItem	dockTop					"Dock Top"							filter:dockedTop
			menuItem	dockBottom				"Dock Bottom"							filter:dockedBottom
			menuItem	dockLeft					"Dock Left"							filter:dockedLeft
			menuItem	dockRight				"Dock Right"							filter:dockedRight
			menuItem 	undockDialog				"Float"								filter:undocked
			seperator		sep06
			menuItem	simpleView				"Simple View      	4"					checked:LayerMan.dialog.simpleView
			seperator		sep07
			menuItem	aboutLayerMan			"About..."
		)
		
		seperator			sep08
		menuItem		quit						"Exit"
		
		on addSel2Layer picked do LayerMan.ops.addSelection2Layer()
		on selectLayer picked do LayerMan.ops.selectLayer()
		on selectObjectsByName picked do LayerMan.ops.selectObjectsByName()
		on createNewGroup picked do LayerMan.ops.createNewGroup()
		on createNewLayer picked do LayerMan.ops.createNewLayer()
		on createLayerFromSel picked do LayerMan.ops.createNewLayer fromSelection:true
		on makeLayerCurrent picked do LayerMan.ops.makeLayerCurrent LayerMan.ops.hitLayer[1] LayerMan.ops.hitNode
		on rename picked do LayerMan.ops.renameLayer()
		on deleteGroup picked do LayerMan.ops.deleteLayer group:true
		on deleteLayer picked do LayerMan.ops.deleteLayer()
		on deleteLayerAndObjects picked do LayerMan.ops.deleteLayer deleteObjects:true
		on setWirecolorsRandomly picked do LayerMan.ops.setWirecolorsRandomly()
		on getWirecolorsFromObjects picked do LayerMan.ops.getWirecolorsFromObjects()
		on sortLayersByName picked do LayerMan.ops.sortLayersByName()
		on autoLoad picked do LayerMan.dialog.autoLoad = not LayerMan.dialog.autoLoad
		on autoFocus picked do LayerMan.timeX.active = (LayerMan.dialog.autoFocus = not LayerMan.dialog.autoFocus)
		on dockTop picked do LayerMan.dialog.dock #cui_dock_top
		on dockBottom picked do LayerMan.dialog.dock #cui_dock_bottom
		on dockLeft picked do LayerMan.dialog.dock #cui_dock_left
		on dockRight picked do LayerMan.dialog.dock #cui_dock_right
		on undockDialog picked do LayerMan.dialog.dock #cui_floatable
		on simpleView picked do LayerMan.dialog.toggleSimpleView (not LayerMan.dialog.simpleView)
		on aboutLayerMan picked do LayerMan.ops.showAboutBox()
		on quit picked do (if LayerMan.dialog.dockState != #cui_floatable do LayerMan.dialog.dock #cui_floatable save:false; destroyDialog LayerMan)
	)
	
	rollout LayerManPalette "" width:338 height:91
	(
		imgTag			palette									width:338	height:91		pos:[0,0]
		timer			timeX2									interval:50
		
		on LayerManPalette open do
		(
			local img = bitmap 26 7; for i = 0 to 6 do setPixels img [0,i] (for f = 0 to 25 collect C4.hsv2RGB [f * 255 / 26, 255, 255 - i * 255 / 12.0])
			palette.bitmap = copy img (bitmap 338 91)
		)
		
		on palette lButtonDown pos flags do
		(
			LayerMan.ops.hitLayer.wirecolor = (LayerMan.ops.getObjectsByLayer LayerMan.ops.hitLayer).wirecolor = (getPixels palette.bitmap pos 1)[1]
			LayerMan.layerColor.enabled = true
			destroyDialog LayerManPalette
			LayerMan.ops.updateWirecolors()
		)
		
		on timeX2 tick do if keyboard.escPressed do ( destroyDialog LayerManPalette; LayerMan.layerColor.enabled = true )
	)
	
	rollout LayerMan "LayerMan v2.7 by Light"
	(
		activeXControl	lLabel 			"MSComctlLib.TreeCtrl" 		width:81	 	height:17 	pos:[16,2]									enabled:false
		activeXControl	cLabel	 		"MSComctlLib.TreeCtrl" 		width:13	 	height:17 	pos:[220,2]									enabled:false
		activeXControl	hLabel	 		"MSComctlLib.TreeCtrl" 		width:12	 	height:17 	pos:[239,2]									enabled:false
		activeXControl	fLabel	 		"MSComctlLib.TreeCtrl" 		width:12	 	height:17 	pos:[258,2]									enabled:false
		activeXControl	pLabel	 		"MSComctlLib.TreeCtrl" 		width:72	 	height:17 	pos:[278,2]									enabled:false
		activeXControl	mLabel	 		"MSComctlLib.TreeCtrl" 		width:64	 	height:17 	pos:[410,2]									enabled:false
		imgTag			sep01									width:558	height:1		pos:[0,19]		transparent:(color 0 1 2)		bitmap:(bitmap 1 1 color:black)
		activeXControl	layers 			"MSComctlLib.TreeCtrl" 		width:224 	height:647 	pos:[0,23]
		imgTag			layerColor								width:13		height:606	pos:[220,23]		transparent:(color 0 1 2)
		imgTag			stripes01									width:4		height:608	pos:[233,23]		transparent:(color 0 1 2)
		activeXControl	vs 				"MSComctlLib.TreeCtrl" 		width:15 		height:647 	pos:[237,23]
		imgTag			stripes02									width:4		height:608	pos:[252,23]		transparent:(color 0 1 2)
		activeXControl	fr 				"MSComctlLib.TreeCtrl" 		width:15 		height:647 	pos:[256,23]
		imgTag			stripes03									width:7		height:608	pos:[271,23]		transparent:(color 0 1 2)
		activeXControl	att 				"MSComctlLib.TreeCtrl" 		width:132	height:647 	pos:[278,23]
		activeXControl	mat 				"MSComctlLib.TreeCtrl" 		width:132 	height:647 	pos:[410,23]
		activeXControl	scroll 			"MSComctlLib.TreeCtrl" 		width:16	 	height:647 	pos:[542,23]
		timer			timeX																			interval:50					active:false
		timer			timeX2																			interval:10					active:false
		timer			timeX3																			interval:100
		timer			timeX4																			interval:10					active:false
		timer			timeX5																			interval:10					active:false
		
		struct methods
		(
																		--	Data to be saved
																		
			pickedMaterialLibrary = "",										--	Picked material library for use with alternate materials
			
																		--	Data to be saved with the scene
																		
			parentArr = #(),												--	Parent Layers
			childArr = #(),													--	Child Layers
			cIndex = #(),													--	Child Indices
			pAlternate = #(),												--	Alternate properties of parent layers
			cAlternate = #(),												--	Alternate properties of child layers
			
																		--	Data NOT to be saved
											
			--	Windows messages
			WM_HSCROLL = 276, WM_VSCROLL = 277, SB_LINEUP = 0, SB_LINEDOWN = 1, SB_PAGETOP = 6, SB_LEFT = 6,
			kDelete = 46, k1 = 49, k2 = 50, k3 = 51, k4 = 52, kA = 65, kD = 68, kS = 83, kF2 = 113,
			
			
			textColor = color 0 0 0,											--	Color of the layer nodes, inactive alternate property and material nodes
			activeLayerColor = color 0 0 200, 								--	Color of the current layer in the layers section
			activeAlternateColor = 16711680,									--	Color of an alternate property/material that is turned on
			bgColor = white,												--	Color of the treeview background
			altBgColor = bgColor - (color 10 10 10),							--	Dark color of stripped background
			noAlternateMaterial = "\x95",									--	Entry for treeview node tags for having no alternate material
			nullEntry = "0\x85\x95",											--	Entry for treeview node tags for having no alternate property...material (propertyIndex...materialName)
			hitIndex = 0,
			hitIndex2 = 0,
			hitNode = undefined,
			hitLayer = undefined,
			dropNode = undefined,
			newTreeViewNode = #(),
			allNodes = #(),
			allNodeNames = #(),
			visibleNodes = #(),												--	List of visible nodes in layers treeview
			visibleNodeNames = #(),										--	List of visible node names in layers treeview
			layerColors = #(),												--	List of layer colors
			ignoreNewLayers = false,										--	Flag that prevents the layer creation callback from signaling the layers created using this tool
			scrollIndex = 0,												--	Value that holds the current scroll position by line
			renameNode = false,
			propertyNames = #("see-through", "display as box", "backface cull", "edges only", "vertex ticks", "show trajectory", "ignore extents", "show frozen in gray", "vertex colors", "renderable", "inherit visibility", "visible to camera", "visible to reflection", "receive shadows", "cast shadows", "apply atmospherics", "render occluded"),
			properties = #("xray", "boxMode", "backfaceCull", "allEdges", "vertexTicks", "showTrajectory", "ignoreExtents", "showFrozenInGray", "showVertexColors", "renderable", "inheritVisibility", "primaryVisibility", "secondaryVisibility", "receiveShadows", "castShadows", "applyAtmospherics", "renderOccluded"),
			matArr = #(),
			
			fn getNodeByHit tv x y =
			(
				local arr = for i = 1 to 2 collect tv.hitTest (x * i * 15) (y * 15)
				if arr[1] != undefined then arr[1] else arr[2]
			),
			
			fn getLayersByName str =
			(
				local index = findItem parentArr str
				if str != "0" and index != 0 then (for i = 1 to cIndex.count where cIndex[i] == index collect (layerManager.getLayerFromName childArr[i])) else #(layerManager.getLayerFromName str)
			),
			
			fn getStrippedColors index = (if mod scrollIndex 2 != 0 then #(altBgColor, bgColor) else #(bgColor, altBgColor))[index],
			
			fn updateLayerColors =
			(
				local colorArr = (for i = scrollIndex + 1 to layerColors.count collect layerColors[i]) + (for i = 1 to scrollIndex collect layerColors[i])
				local img = bitmap 1 colorArr.count color:bgColor
				for i = 0 to colorArr.count - 1 do setPixels img [0, i] #(colorArr[i + 1])
				#(layerColor, stripes01, stripes02, stripes03).height = 16 * colorArr.count
				local resized = copy img (bitmap 1 layerColor.height)
				local arr = #(0, 1); for i = 15 to layerColor.height by 16 do join arr (for f = 0 to 2 collect i + f)
				local index = 1, hitArr = for i = 1 to 3 collect (hitIndex - 1 - scrollIndex) * 3 + i
				for i = 1 to arr.count do
				(
					setPixels resized [0, arr[i]] #(if hitIndex != 0 and findItem hitArr i != 0 then textColor else (getStrippedColors index))
					if mod i 3 == 0 do index = 3 - index
				)
				layerColor.bitmap = copy resized (bitmap 13 layerColor.height)
				img = bitmap 1 colorArr.count color:bgColor
				for i = 0 to colorArr.count do setPixels img [0, i] #(if hitIndex - 1 - scrollIndex == i then textColor else (getStrippedColors ((mod i 2) + 1)))
				#(stripes01, stripes02, stripes03).bitmap = copy img (bitmap 1 layerColor.height)
			),
			
			fn popupRightClickMenu button x y theRCMenu activeX =
			(
				local clickedNode = getNodeByHit layers 16 y
				local theNode = if button == 1 and x < 16 and activeX == layers then hitNode else clickedNode
				if hitNode != undefined and clickedNode != undefined and hitNode.parent != undefined and hitNode.parent.text == clickedNode.text do theNode = clickedNode
				if theNode != undefined do
				(
					hitIndex = findItem visibleNodeNames theNode.text
					hitNode = if activeX == layers then theNode else activeX.nodes[hitIndex]
					hitLayer = getLayersByName theNode.text
					updateLayerColors()
					layers.dropHighlight = theNode
					for i in #(vs, fr, att, mat, scroll) do i.dropHighlight = i.nodes[hitIndex]
					if button == 2 do popupMenu theRCMenu pos:(activeX.pos + [x,y]) rollout:LayerMan
				)
			),
			
			fn deselectNodes = for i in #(layers, vs, fr, att, mat, scroll) do ( i.nodes[1].selected = true; i.nodes[1].selected = false ),
			
			fn saveLayerData =
			(
				local LayerManCA = attributes LayerManSettings
				(
					parameters main
					(
						parentArr				type:#stringTab			tabSizeVariable:true			animatable:false			--	Array for holding layer groups
						childArr				type:#stringTab			tabSizeVariable:true			animatable:false			--	Array for holding child layers (actual layers)
						cIndex				type:#intTab				tabSizeVariable:true			animatable:false			--	Array for holding group indices of child layers
						pAlternate			type:#stringTab			tabSizeVariable:true			animatable:false			--	Array for holding alternate properties and materials for layer groups
						cAlternate			type:#stringTab			tabSizeVariable:true			animatable:false			--	Array for holding alternate properties and materials for child layers
						savedMaterials		type:#materialTab			tabSizeVariable:true			animatable:false			--	Array for holding original materials of objects that use alternate materials
					)
				)
				custAttributes.add rootNode LayerManCA
			),
			
			fn getObjectsByLayer theLayers = ( local arr = #(); for i in theLayers do ( local temp = #(); i.nodes &temp; join arr temp ); arr ),
			fn getAveragePropertyState arr index = ( local state = true; for i in arr while state do state = getProperty i properties[index]; state ),
			fn getGeometryObjects arr = for i in arr where findItem #(GeometryClass, Shape) (superClassOf i) != 0 and classOf i != TargetObject collect i,
			fn getTagValue tag = ( local val = filterString tag "\x85"; val[1] = val[1] as integer; val ),
			
			fn setTagValue theNode index newValue =
			(
				local val = getTagValue theNode.tag
				val[index] = newValue as string
				theNode.tag = val[1] as string + "\x85" + val[2] as string
				local altIndex = if theNode.bold then findItem parentArr theNode.text else findItem childArr theNode.text
				if theNode.bold then rootNode.pAlternate[altIndex] = pAlternate[altIndex] = theNode.tag else rootNode.cAlternate[altIndex] = cAlternate[altIndex] = theNode.tag
			),
			
			fn isAlternateMaterialOn theLayer materialName =
			(
				local layerObjects = getGeometryObjects (getObjectsByLayer theLayer)
				local found = true; for i in layerObjects while found do if i.material != undefined then found = (i.material.name == materialName) else found = false
				if layerObjects.count != 0 then found else true
			),
			
			fn updateScrollbar = ( C4.addNode scroll ""; scroll.nodes.remove scroll.nodes.count ),
			
			fn resizeTreeViews =
			(
				local simpleView = att.size.y == 0
				if visibleNodes.count * 16 > LayerMan.height - 23 then
				(
					#(layers, vs, fr, att, mat).size.y = LayerMan.height - 23
					scroll.scroll = true
				)
				else
				(
					#(layers, vs, fr, att, mat).size.y = visibleNodes.count * 16
					scroll.scroll = false
				)
				scroll.size.y = LayerMan.height - 6
				if layers.nodes.count == ((LayerMan.height - 23) / 16) + 1 do updateScrollbar()
				if simpleView do att.size.y = 0
			),
			
			fn updateArrays =
			(
				allNodes = #(layers.nodes[1]); visibleNodes = #(layers.nodes[1]); parentArr = #(layers.nodes[1])
				local s = timestamp()
				while allNodes.count != layers.nodes.count do
				(
					local theNode = allNodes[allNodes.count]
					if theNode.bold then
					(
						if theNode.children != 0 then
						(
							join allNodes (local childNodes = C4.getChildrenForward theNode)
							if theNode.expanded do join visibleNodes childNodes
						)
						else for i in #(allNodes, visibleNodes, parentArr) do append i theNode.next
					)
					else for i in #(allNodes, visibleNodes, parentArr) do append i theNode.parent.next
					if ((timestamp()) - s) / 1000 > 2 do allNodes = for f = 1 to layers.nodes.count collect 0
				)
				pAlternate =#(); cAlternate = #(); cIndex = #()
				parentArr = for i in parentArr collect ( append pAlternate i.tag; i.text )
				childArr = for i in allNodes where not i.bold collect ( append cAlternate i.tag; append cIndex (findItem parentArr i.parent.text); i.text )
				visibleNodeNames = for i in visibleNodes collect i.text
				allNodeNames = for i in allNodes collect i.text
				resizeTreeViews()
				local s = scroll.nodes.count, v = visibleNodes.count
				if s != v do (if s > v then (for i = 1 to s - v do scroll.nodes.remove 1) else (for i = 1 to v - s do C4.addNode scroll ""))
			),
			
			fn updateWirecolors =
			(
				layerColors = #()
				local temp = #(), pIndex
				for i = 1 to allNodes.count do
				(
					if allNodes[i].bold then
					(
						pIndex = findItem visibleNodeNames allNodes[i].text
						append layerColors black
					)
					else
					(
						append temp (layerManager.getLayerFromName allNodes[i].text).wirecolor
						if allNodes[i].next == undefined do
						(
							if C4.convertIntegers2Value (C4.getAverageColor temp) == C4.convertIntegers2Value temp[1] do layerColors[pIndex] = temp[1]
							if allNodes[i].parent.expanded do join layerColors temp
							temp = #()
						)
					)
				)
				layerColors[1] = (layerManager.getLayer 0).wirecolor
				local tempColors = (for i = scrollIndex + 1 to layerColors.count collect layerColors[i]) + (for i = 1 to scrollIndex collect layerColors[i])
				updateLayerColors()
			),
			
			fn updateHiddenFrozen property =
			(
				local state = true, arr = #(), stateArr = #()
				local tv = if property == #isHidden then vs else fr
				for i = 1 to allNodes.count do
				(
					local index = findItem visibleNodeNames allNodes[i].text
					local theLayer = layerManager.getLayerFromName allNodes[i].text
					local pState = if theLayer != undefined do getProperty theLayer (property as string)
					if allNodes[i].bold then
					(
						append arr index
						append stateArr (if theLayer != undefined then pState else allNodes[i].children != 0)
					)
					else
					(
						if state and not pState do state = false
						if allNodes[i].parent.expanded do tv.nodes[index].checked = pState
						if allNodes[i].next == undefined do ( stateArr[stateArr.count] = state; state = true )
					)
				)
				for i = 1 to arr.count do tv.nodes[arr[i]].checked = stateArr[i]
			),
			
			fn updateProperties index:0 =
			(
				for i = 1 to visibleNodes.count where index == 0 or i == index do
				(
					local val = getTagValue visibleNodes[i].tag
					local theLayer = getLayersByName visibleNodes[i].text
					att.nodes[i].text = if val[1] != 0 then propertyNames[val[1]] else ""
					if val[1] != 0 do att.nodes[i].forecolor = if getAveragePropertyState theLayer val[1] then activeAlternateColor else textColor
				)
			),
			
			fn updateMaterials index:0 =
			(
				for i = 1 to visibleNodes.count where index == 0 or i == index do
				(
					local val = getTagValue visibleNodes[i].tag
					local theLayer = getLayersByName visibleNodes[i].text
					mat.nodes[i].text = if val[2] != noAlternateMaterial then val[2] else ""
					if val[2] != noAlternateMaterial do mat.nodes[i].forecolor = if isAlternateMaterialOn theLayer val[2] then activeAlternateColor else textColor
				)
			),
			
			fn updateUIColors = for i = 1 to visibleNodes.count do #(visibleNodes[i], vs.nodes[i], fr.nodes[i], att.nodes[i], mat.nodes[i], scroll.nodes[i]).backColor = #(altBgColor, bgColor)[(mod i 2) + 1],
			
			fn updateData =
			(
				rootNode.parentArr = parentArr
				rootNode.childArr = childArr
				rootNode.cIndex = cIndex
				rootNode.pAlternate = pAlternate
				rootNode.cAlternate = cAlternate
			),
			
			fn updateAll arrays:true wirecolors:true hidden:true frozen:true props:true materials:true colors:true data:true =
			(
				if arrays do updateArrays()
				if wirecolors do updateWirecolors()
				if hidden do updateHiddenFrozen #isHidden
				if frozen do updateHiddenFrozen #isFrozen
				if props do updateProperties()
				if materials do updateMaterials()
				if colors do updateUIColors()
				if data do updateData()
			),
			
			fn removeInvalidLayers = for i = childArr.count to 1 by -1 where layerManager.getLayerFromName childArr[i] == undefined do for f in #(childArr, cIndex, cAlternate) do deleteItem f i,
			
			fn getNewLayers =
			(
				local index = findItem parentArr "Ungrouped"
				for i = 1 to layerManager.count - 1 where findItem childArr (local layerName = (layerManager.getLayer i).name) == 0 do
				(
					if index == 0 do (append parentArr "Ungrouped"; index = parentArr.count)
					append childArr layerName
					append cIndex index
					append pAlternate nullEntry
					append cAlternate nullEntry
				)
			),
			
			fn addLayerNode tv str index: bold:false expand:false checked:false color:textColor backColor:bgColor tag: edit:false =
			(
				local theLayer = layerManager.getLayerFromName str
				color = if theLayer != undefined and theLayer.current then activeLayerColor else textColor
				local newNode = C4.addNode tv str index:index bold:bold expand:expand checked:checked color:color backColor:backColor sorted:false tag:tag edit:edit
				for f in #(vs, fr, att, mat, scroll) do C4.addNode f ""
				newNode
			),
			
			fn initializeLayers =
			(
				with undo off
				(
					hitIndex = 0; hitNode = undefined; hitLayer = undefined
					for i in #(layers, vs, fr, att, mat, scroll) do i.nodes.clear()
					if isProperty rootNode "parentArr" then
					(
						parentArr = rootNode.parentArr
						childArr = rootNode.childArr
						cIndex = rootNode.cIndex
						pAlternate = rootNode.pAlternate
						cAlternate = rootNode.cAlternate
						removeInvalidLayers()
						getNewLayers()
					)
					else
					(
						parentArr = if layerManager.count == 1 then #("0") else #("0", "Ungrouped")
						childArr = for i = 1 to layerManager.count - 1 collect (layerManager.getLayer i).name
						cIndex = for i = 1 to childArr.count collect 2
						pAlternate = for i = 1 to parentArr.count collect nullEntry
						cAlternate = for i = 1 to childArr.count collect nullEntry
					)
					for i = 1 to parentArr.count do addLayerNode layers parentArr[i] bold:true expand:true tag:pAlternate[i]
					for i = 1 to childArr.count do addLayerNode layers childArr[i] index:cIndex[i] tag:cAlternate[i]
					if not isProperty rootNode "parentArr" do saveLayerData()
					deselectNodes()
					updateAll()
				)
			),
			
			fn toggleHideFreeze theNode property =
			(
				with undo off with redraw off
				(
					setProperty (getLayersByName visibleNodeNames[theNode.index]) (property as string) theNode.checked
					updateHiddenFrozen property
				)
			),
			
			fn restoreMaterials theObjects =
			(
				with undo off with redraw off
				(
					theObjects = getGeometryObjects theObjects
					if theObjects.count != 0 do
					(
						local restoredMaterialNames = #()
						local materialNames = for i in rootNode.savedMaterials collect i.name
						for i in theObjects do
						(
							local materialName = getAppData i 8810
							if materialName != undefined do
							(
								if materialName != "undefined" then
								(
									C4.appendIfNew restoredMaterialNames materialName
									i.material = rootNode.savedMaterials[findItem materialNames materialName]
								)
								else i.material = undefined
								deleteAppData i 8810
							)
						)
						if hitNode.tag == "" do hitNode.forecolor = textColor
						local uniqueMaterials = #()
						for i = 0 to layerManager.count - 1 do
						(
							local layerObjects = getObjectsByLayer #(layerManager.getLayer i)
							for f in layerObjects where (local data = getAppData f 8810) != undefined do C4.appendIfNew uniqueMaterials data
						)
						local materials2Delete = for i = 1 to materialNames.count where findItem uniqueMaterials materialNames[i] == 0 collect i
						for i = materials2Delete.count to 1 by -1 do deleteItem rootNode.savedMaterials materials2Delete[i]
					)
				)
			),
			
			fn setAlternateMaterial theObjects materialName changeMaterial:true =
			(
				with undo off with redraw off
				(
					local libraryLoaded = true
					if pickedMaterialLibrary != getMatlibFilename() do libraryLoaded = loadMaterialLibrary pickedMaterialLibrary
					if libraryLoaded then
					(
						if currentMaterialLibrary[materialName] != undefined then
						(
							if changeMaterial do
							(
								theObjects = getGeometryObjects theObjects
								if theObjects.count != 0 do
								(
									local materialNames = for i in rootNode.savedMaterials collect i.name
									for i in theObjects do
									(
										if getAppData i 8810 == undefined do
										(
											if i.material != undefined and C4.appendIfNew materialNames i.material.name do append rootNode.savedMaterials i.material
											setAppData i 8810 (if i.material != undefined then i.material.name else "undefined")
										)
									)
									theObjects.material = currentMaterialLibrary[materialName]
								)
							)
							if hitNode.tag == "" do
							(
								local pIndex = findItem allNodeNames visibleNodes[hitIndex].text
								setTagValue allNodes[pIndex] 2 materialName
								updateMaterials index:hitIndex
							)
							if not changeMaterial do ( hitNode.forecolor = textColor; mat.nodeClick hitNode )
						)
						else C4.notify "LayerMan v2.7" "Material can not be found in the current library.\nPlease load the original library the material is in." modal:true center:LayerMan backColor:bgColor closeAfter:4000
					)
					else C4.notify "LayerMan v2.7" "Material library can not be found.\nPlease select another one." modal:true center:LayerMan backColor:bgColor closeAfter:2500
				)
			),
			
			fn addSelection2Layer =
			(
				with undo off with redraw off
				(
					selection.wirecolor = hitLayer[1].wirecolor
					local objectLayers = #(); for i in selection do C4.appendIfNew objectLayers i.layer
					local arr = for i in objectLayers collect #(i.isHidden, i.isFrozen)
					objectLayers.isHidden = hitLayer[1].isHidden
					objectLayers.isFrozen = hitLayer[1].isFrozen
					local val = getTagValue hitNode.tag
					local layerObjects = getObjectsByLayer hitLayer
					if layerObjects.count != 0 do for i in properties do setProperty selection i (getProperty layerObjects[1] i)
					local groupHasMaterial = false
					if mat.nodes[findItem visibleNodeNames hitNode.parent.text].forecolor == activeAlternateColor then ( val = getTagValue hitNode.parent.tag; groupHasMaterial = true)
					if val[2] != noAlternateMaterial and (mat.nodes[hitIndex].forecolor == activeAlternateColor or groupHasMaterial) then setAlternateMaterial selection val[2] else restoreMaterials (for i in selection where getAppData i 8810 != undefined collect i)
					for i in selection do hitLayer[1].addNode i
					for i = 1 to objectLayers.count do ( objectLayers[i].isHidden = arr[i][1]; objectLayers[i].isFrozen = arr[i][2] )
					select selection
				)
			),
			
			fn selectLayer =
			(
				with undo "Select Layer" on
				(
					if not keyboard.controlPressed do clearSelection()
					for i in hitLayer do i.select true
				)
			),
			
			fn selectObjectsByName =
			(
				fn filterFN obj = findItem (for i in hitLayer collect i.name) obj.layer.name != 0
				local arr = selectByName title:("Select Objects on " + hitNode.text) filter:filterFN showHidden:true
				if arr != undefined do with undo "Select Objects by Name" on select arr
			),
			
			fn getCurrentLayer =
			(
				local found = false
				(for i = 0 to layerManager.count while not found where (local theLayer = layerManager.getLayer i).current collect (found = true; theLayer))[1]
			),
			
			fn makeLayerCurrent theLayer layerNode =
			(
				with undo off with redraw off
				(
					if not layerNode.bold or layerNode.text == "0" do
					(
						local currentLayer = visibleNodes[findItem visibleNodeNames (getCurrentLayer()).name]
						currentLayer.forecolor = textColor
						theLayer.current = true
						layerNode.forecolor = activeLayerColor
					)
				)
			),
			
			fn renameLayer = ( hitNode.selected = true; layers.startLabelEdit(); hitIndex2 = hitIndex ),
			
			fn defineMaterialRCMenu =
			(
				if pickedMaterialLibrary != getMatlibFilename() do (if not loadMaterialLibrary pickedMaterialLibrary do pickedMaterialLibrary = "")
				local materialNames = sort (for i in currentMaterialLibrary collect i.name)
				if pickedMaterialLibrary == "" or currentMaterialLibrary.count != matArr.count or (for i in matArr where findItem materialNames i != 0 collect 0).count != matArr.count do
				(
					local str = "rcMenu LayerManMatRC ( \n"
					if pickedMaterialLibrary != "" do
					(
						for i = 1 to materialNames.count do str += "menuItem mat" + i as string + " \"" + materialNames[i] + "\" \n"
						for i = 1 to materialNames.count do str += "on mat" + i as string + " picked do LayerMan.ops.setAlternateMaterial (LayerMan.ops.getObjectsByLayer LayerMan.ops.hitLayer) " + "\"" + materialNames[i] + "\" changeMaterial:false \n"
						str += "seperator sep \n"
					)
					str += "menuItem pickMtl \"Pick Material Library\" \n"
					str += "on LayerManMatRC open do LayerMan.ops.matArr = for i in currentMaterialLibrary collect i.name \n"
					str += "on pickMtl picked do ( \n"
					str += "local current = getMatlibFilename() \n"
					str += "fileOpenMatlib() \n"
					str += "if getMatlibFilename() != current do LayerMan.ops.pickedMaterialLibrary = getMatlibFilename() ) ) \n"
					execute str
				)
			),
			
			fn deleteLayer deleteObjects:false group:false =
			(
				if hitNode.text != "0" and (group or not hitNode.bold) do
				(
					if not group do
					(
						if hitLayer[1].current do ( (layerManager.getLayer 0).current = true; layers.nodes[1].forecolor = activeLayerColor )
						local layerObjects = getObjectsByLayer hitLayer
						local theLayer = getCurrentLayer()
						if deleteObjects then with undo "Delete Layer and Objects" on delete layerObjects
						else
						(
							with undo off with redraw off
							(
								layerObjects.wirecolor = theLayer.wirecolor
								hitLayer.isHidden = theLayer.isHidden
								hitLayer.isFrozen = theLayer.isFrozen
								local theLayerObjects = getObjectsByLayer #(theLayer)
								if theLayerObjects.count != 0 do for i in properties do setProperty selection i (getProperty theLayerObjects[1] i)
								for i in properties do setProperty layerObjects i (getProperty theLayer i)
								local index = findItem allNodeNames theLayer.name
								local theNode = allNodes[index]
								local val = getTagValue theNode.tag
								if not theNode.bold and val[2] != noAlternateMaterial and not isAlternateMaterialOn (getLayersByName theLayer.name) val[2] do ( val = getTagValue theNode.parent.tag; index = findItem visibleNodeNames theNode.parent.text )
								if val[2] != noAlternateMaterial and isAlternateMaterialOn (getLayersByName theLayer.name) val[2] then setAlternateMaterial layerObjects val[2] else restoreMaterials (for i in layerObjects where getAppData i 8810 != undefined collect i)
								for i in layerObjects do theLayer.addNode i
							)
						)
						if hitLayer[1].current do makeLayerCurrent theLayer layers.nodes[1]
						layerManager.deleteLayerByName hitLayer[1].name
					)
					local parentNode = if not group do hitNode.parent.text
					for i in #(vs, fr, att, mat, scroll) do i.nodes.remove hitIndex
					local index = (findItem visibleNodeNames hitNode.text) - 1
					local lastNode = hitNode.text == visibleNodeNames[visibleNodeNames.count]
					layers.nodes.remove hitNode.index
					hitIndex = 0
					hitNode = undefined
					hitLayer = undefined
					updateArrays(); updateWirecolors(); updateHiddenFrozen #isHidden; updateHiddenFrozen #isFrozen; updateUIColors(); updateData()
					if not group do
					(
						local pIndex = findItem visibleNodeNames parentNode
						updateProperties index:pIndex
						updateMaterials index:pIndex
					)
					deselectNodes()
					if not lastNode do windows.sendMessage scroll.hWnd WM_VSCROLL SB_LINEUP 0
				)
			),
			
			fn setWirecolorsRandomly =
			(
				for i = 0 to layerManager.count - 1 do
				(
					local theLayer = layerManager.getLayer i
					theLayer.wirecolor = (getObjectsByLayer #(theLayer)).wirecolor = random black white
				)
				updateWirecolors()
			),
			
			fn getWirecolorsFromObjects =
			(
				for i = 0 to layerManager.count - 1 do
				(
					local theLayer = layerManager.getLayer i
					local layerObjects = getObjectsByLayer #(theLayer)
					if layerObjects.count != 0 do ( theLayer.wirecolor = layerObjects.wirecolor = layerObjects[1].wirecolor )
				)
				updateWirecolors()
			),
			
			fn sortLayersByName =
			(
				local temp = for i = 1 to childArr.count collect parentArr[cIndex[i]]
				sort parentArr
				cIndex = for i in temp collect (findItem parentArr i)
				sort childArr
				rootNode.parentArr = parentArr
				rootNode.cIndex = C4.reorderArr cIndex rootNode.childArr childArr unique:true
				rootNode.cAlternate = C4.reorderArr cAlternate rootNode.childArr childArr unique:true
				rootNode.childArr = childArr
				initializeLayers()
			),
			
			fn scrollLayers =
			(
				if layers.nodes.count > (LayerMan.height - 23) / 16 do
				(
					local theNode = scroll.hitTest 0 8
					if theNode != undefined and theNode.index != scrollIndex + 1 do
					(
						#(layers, vs, fr, att, mat).scroll = true
						local layerNode = visibleNodes[(layers.size.y / 16) + theNode.index - 1]
						layerNode.ensureVisible()
						windows.sendMessage layers.hWnd WM_VSCROLL SB_LINEUP 0
						local index = findItem visibleNodeNames layerNode.text
						for i in #(vs, fr, att, mat) do i.nodes[index].ensureVisible()
						for i in #(att, mat) do windows.sendMessage i.hWnd WM_VSCROLL SB_LINEUP 0
						windows.sendMessage layers.hWnd WM_HSCROLL SB_LEFT 0
						#(layers, vs, fr, att, mat).scroll = false
						scrollIndex = theNode.index - 1
						updateLayerColors()
					)
				)
				if renameNode do ( setFocus layers; enableAccelerators = false; layers.nodes[layers.nodes.count].selected = true; layers.startLabelEdit(); renameNode = false )
			),
			
			fn createNewGroup =
			(
				local theName = C4.getNextAvailableName parentArr "Group"
				addLayerNode layers theName bold:true expand:true tag:nullEntry
				updateArrays(); updateWirecolors(); updateUIColors(); updateData()
				hitIndex2 = findItem visibleNodeNames theName
				hitNode = visibleNodes[hitIndex2]
				scroll.nodes[scroll.nodes.count].ensureVisible()
				renameNode = true
			),
			
			fn createNewLayer theName: fromSelection:false =
			(
				local layerName = if theName == unsupplied then C4.getNextAvailableName childArr "New Layer" else theName
				with undo off with redraw off
				(
					ignoreNewLayers = true
					local newNode
					local numNodes = layers.nodes.count
					if theName != unsupplied then
					(
						local arr = for i in visibleNodeNames collect C4.uppercase i
						local index = findItem arr "UNGROUPED"
						local theNode = if index != 0 then visibleNodes[index] else addLayerNode layers "Ungrouped" bold:true expand:true tag:nullEntry
						newNode = addLayerNode layers layerName index:theNode.index tag:nullEntry
					)
					else
					(
						if hitNode == undefined or hitNode.bold then
						(
							if hitNode == undefined or hitNode.text == "0" then
							(
								local theNode = addLayerNode layers (C4.getNextAvailableName parentArr "Group") bold:true expand:true tag:nullEntry
								newNode = addLayerNode layers layerName index:theNode.index tag:nullEntry
							)
							else newNode = addLayerNode layers layerName index:hitNode.index tag:nullEntry
						)
						else newNode = addLayerNode layers layerName index:hitNode.parent.index tag:nullEntry
					)
					local newLayer = if theName == unsupplied do layerManager.newLayerFromName layerName
					if fromSelection do
					(
						selection.wirecolor = newLayer.wirecolor
						for i in selection do newLayer.addNode i
						if numNodes + 1 == layers.nodes.count do
						(
							local val = getTagValue newNode.parent.tag
							local index = findItem visibleNodeNames newNode.parent.text
							if val[1] != 0 do
							(
								local state = att.nodes[index].forecolor == activeAlternateColor
								setProperty newLayer properties[val[1]] state
								setProperty selection properties[val[1]] state
							)
							if val[2] != noAlternateMaterial and mat.nodes[index].forecolor == activeAlternateColor then setAlternateMaterial selection val[2] restoreMaterials (for i in selection where getAppData i 8810 != undefined collect i)
						)
					)
					if not newNode.parent.expanded do newNode.parent.expanded = true
					updateArrays(); updateWirecolors(); updateUIColors(); updateData()
					newTreeViewNode = #(visibleNodes[findItem visibleNodeNames layerName], getLayersByName layerName)
					local layerNode = layers.nodes[layers.nodes.count]
					scroll.nodes[findItem visibleNodeNames layerNode.text].ensureVisible()
					if theName == unsupplied do renameNode = true
					ignoreNewLayers = false
				)
			),
			
			fn addProperty index =
			(
				if findItem propertyNames hitNode.text != index do
				(
					setTagValue visibleNodes[hitIndex] 1 index
					updateProperties index:hitIndex
				)
			),
			
			fn setAlternateProperty index =
			(
				with undo off with redraw off
				(
					local state = not (hitNode.forecolor == activeAlternateColor)
					setProperty hitLayer properties[index] state
					setProperty (getObjectsByLayer hitLayer) properties[index] state
					hitNode.forecolor = if state then activeAlternateColor else textColor
				)
			),
			
			fn definePropertyRCMenu =
			(
				local str = "rcMenu LayerManProRC ( \n local LM = LayerMan.ops \n fn checkFN index = LM.getAveragePropertyState LM.hitLayer index \n"
				for i = 1 to 17 do
				(
					str += "menuItem property" + i as string + " \"" + C4.capitalize LayerMan.ops.propertyNames[i] + "\" checked:(checkFN " + i as string + ") \n"
					str += "on property" + i as string + " picked do LM.addProperty " + i as string + " \n"
					if i == 9 do str += "seperator sep01 \n"
				)
				str += ") \n"
				execute str
			),
			
			fn isActive = hitNode.forecolor == activeAlternateColor,
			
			fn toggleAlternateProperty theNode =
			(
				if keyboard.controlPressed then addProperty 0
				else
				(
					local index = findItem propertyNames theNode.text
					if index != 0 do
					(
						setAlternateProperty index
						local layerNode = visibleNodes[hitIndex]
						if layerNode.bold then
						(
							if layerNode.expanded do
							(
								local pIndex = findItem parentArr layerNode.text
								for i = 1 to cIndex.count where cIndex[i] == pIndex and (getTagValue cAlternate[i])[1] == index do att.nodes[findItem visibleNodeNames childArr[i]].forecolor = theNode.forecolor
							)
						)
						else
						(
							local pIndex = findItem visibleNodeNames layerNode.parent.text
							if (getTagValue layerNode.parent.tag)[1] == index do
							(
								if isActive() then
								(
									local theLayer = getLayersByName layerNode.parent.text
									att.nodes[pIndex].forecolor = if getAveragePropertyState theLayer index then activeAlternateColor else textColor
								)
								else att.nodes[pIndex].forecolor = textColor
							)
						)
					)
				)
			),
			
			fn removeAlternateMaterial theNode layerNode =
			(
				local pIndex = findItem parentArr layerNode.text
				if isActive() then
				(
					for i = 1 to cIndex.count where cIndex[i] == pIndex do mat.nodes[findItem allNodeNames childArr[i]].forecolor = (if (getTagValue cAlternate[i])[2] == theNode.text then theNode.forecolor else textColor)
				)
				else
				(
					for i = 1 to cIndex.count where cIndex[i] == pIndex and (getTagValue cAlternate[i])[2] != noAlternateMaterial do
					(
						hitIndex = findItem allNodeNames childArr[i]
						local theNode = allNodes[hitIndex]
						hitLayer = getLayersByName childArr[i]
						local val = (getTagValue theNode.tag)[2]
						if val != noAlternateMaterial do setAlternateMaterial (getObjectsByLayer hitLayer) val
						if layerNode.expanded do mat.nodes[findItem visibleNodeNames childArr[i]].forecolor = activeAlternateColor
					)
				)
			),
			
			fn toggleAlternateMaterial theNode =
			(
				if theNode.text != "" do
				(
					local layerNode = visibleNodes[hitIndex]
					if keyboard.controlPressed then
					(
						if isActive() do restoreMaterials (getObjectsByLayer hitLayer)
						setTagValue layerNode 2 noAlternateMaterial
						updateMaterials index:hitIndex
						if layerNode.bold do removeAlternateMaterial theNode layerNode
					)
					else
					(
						if isActive() then restoreMaterials (getObjectsByLayer hitLayer) else setAlternateMaterial (getObjectsByLayer hitLayer) theNode.text
						if layerNode.bold then removeAlternateMaterial theNode layerNode
						else
						(
							local pIndex = findItem visibleNodeNames layerNode.parent.text
							if (getTagValue layerNode.parent.tag)[2] == theNode.text then
							(
								if isActive() then
								(
									local theLayer = getLayersByName layerNode.parent.text
									mat.nodes[pIndex].forecolor = if isAlternateMaterialOn theLayer theNode.text then activeAlternateColor else textColor
								)
								else mat.nodes[pIndex].forecolor = textColor
							)
							else mat.nodes[pIndex].forecolor = textColor
						)
					)
				)
			),
			
			fn keyPressed button =
			(
				case button of
				(
					kDelete: if hitNode.bold then (if hitNode.children == 0 do deleteLayer group:true) else deleteLayer()
					k1: createNewGroup()
					k2: createNewLayer()
					k3: createNewLayer fromSelection:true
					k4: LayerMan.dialog.toggleSimpleView (not LayerMan.dialog.simpleView)
					kA: addSelection2Layer()
					kD: makeLayerCurrent hitLayer[1] hitNode
					kS: selectLayer()
					kF2: renameLayer()
				)
			),
			
			fn startDrag =
			(
				for i in #(vs, fr, att, mat) do i.dropHighlight = undefined
				hitIndex = 0
				updateWirecolors()
			),
			
			fn dragOver =
			(
				local pos = getCursorPos layers
				local theNode = getNodeByHit layers pos.x pos.y
				if theNode != undefined and hitNode != undefined and hitNode.text != "0" and theNode.text != hitNode.text do
				(
					if theNode.bold and hitNode.bold and (findItem parentArr theNode.text) + 1 != (findItem parentArr hitNode.text) or not hitNode.bold and theNode.text != "0" and theNode.text != hitNode.parent.text and ((findItem childArr theNode.text) + 1 != (findItem childArr hitNode.text) or theNode.bold) then
					(
						dropNode = theNode
						layers.dropHighlight = theNode
					)
					else dropNode = undefined
				)
			),
			
			fn dragDrop =
			(
				if dropNode != undefined then
				(
					local newNode
					if hitNode.bold then
					(
						if dropNode.bold do
						(
							newNode = C4.addNode layers hitNode.text index:dropNode.index bold:true expand:true tag:hitNode.tag dragNDrop:true
							if hitNode.children != 0 do
							(
								local childNodes = C4.getChildrenForward hitNode
								local names = for i in childNodes collect i.text
								local tags = for i in childNodes collect i.tag
								for i = 1 to names.count do C4.addNode layers names[i] index:newNode.index tag:tags[i]
							)
						)
					)
					else
					(
						local layerNode
						if dropNode.bold then ( newNode = C4.addNode layers hitNode.text index:dropNode.index tag:hitNode.tag; layerNode = dropNode )
							else ( newNode = C4.addNode layers hitNode.text index:dropNode.index tag:hitNode.tag dragNDrop:true; layerNode = dropNode.parent )
						local val = getTagValue layerNode.tag
						local theLayer = getLayersByName newNode.text
						local layerObjects = getObjectsByLayer theLayer
						local index = findItem visibleNodeNames layerNode.text
						if val[1] != 0 do
						(
							local state = (att.nodes[index].forecolor == activeAlternateColor)
							setProperty theLayer properties[val[1]] state
							setProperty layerObjects properties[val[1]] state
						)
						if val[2] != noAlternateMaterial and mat.nodes[index].forecolor == activeAlternateColor do setAlternateMaterial layerObjects val[2]
					)
					local isHitNodeParent = hitNode.bold
					layers.nodes.remove hitNode.index
					hitNode = undefined
					if not isHitNodeParent then (if not dropNode.expanded do dropNode.expanded = true) else newNode.expanded = false
					updateAll()
					if dropNode.bold and dropNode.expanded or not dropNode.bold and dropNode.parent.expanded do popupRightClickMenu 1 16 (((findItem visibleNodeNames newNode.text) * 16) - 8) layersRC layers
				)
				else popupRightClickMenu 1 16 (((findItem visibleNodeNames hitNode.text) * 16) - 8) layersRC layers
			),
			
			fn completeDrag fx = if fx == 0 do popupRightClickMenu 1 16 (((findItem visibleNodeNames hitNode.text) * 16) - 8) layersRC layers,
			
			fn postLabelEdit str =
			(
				local arr = parentArr + childArr
				deleteItem arr (findItem arr newTreeViewNode[1].text)
				arr = for i in arr collect C4.uppercase i
				if str != newTreeViewNode[1].text and findItem arr (C4.uppercase str) == 0 then
				(
					if not newTreeViewNode[1].bold do newTreeViewNode[2][1].setName str
					timeX2.interval = 10
				)
				else timeX2.interval = 11
				timeX2.active = true
			),
			
			fn detectLabelChange =
			(
				if timeX2.interval == 10 then ( updateArrays(); updateData() ) else hitNode.text = visibleNodeNames[hitIndex2]
				timeX2.active = false
			),
			
			fn addCallbacks =
			(
				callbacks.addScript #filePostOpenProcess "LayerMan.ops.initializeLayers()" id:#LayerManCallbacks
				callbacks.addScript #filePostMerge "LayerMan.ops.initializeLayers()" id:#LayerManCallbacks
				callbacks.addScript #objectXRefPostMerge "LayerMan.ops.initializeLayers()" id:#LayerManCallbacks
				callbacks.addScript #sceneXRefPostMerge "LayerMan.ops.initializeLayers()" id:#LayerManCallbacks
				callbacks.addScript #systemPostNew "LayerMan.ops.initializeLayers()" id:#LayerManCallbacks
				callbacks.addScript #systemPostReset "LayerMan.ops.initializeLayers()" id:#LayerManCallbacks
				callbacks.addScript #layerCreated "if not LayerMan.ops.ignoreNewLayers do LayerMan.ops.createNewLayer theName:(callbacks.notificationParam()).name" id:#LayerManCallbacks
			),
			
			fn pickLayerColor pos =
			(
				local arr = #(0, 1); for i = 15 to layerColor.height by 16 do join arr (for f = 0 to 2 collect i + f)
				if findItem arr pos.y == 0 do
				(
					local index = (pos.y / 16) as integer
					if mod pos.y 16 != 0 do index += 1
					local pIndex = index + scrollIndex
					local theNode = visibleNodes[pIndex]
					if theNode.bold and theNode.children == 0 and theNode.text != "0" then C4.notify "LayerMan v2.7" "There are no layers in this group." modal:true center:LayerMan backColor:bgColor closeAfter:2500
					else
					(
						hitLayer = getLayersByName theNode.text
						layerColor.enabled = false
						createDialog LayerManPalette pos:((getDialogPos LayerMan) + [layerColor.pos.x + (if LayerMan.dialogBar then 0 else 4), index * 16 + (if LayerMan.dialogBar then 23 else 46)]) style:#() modal:true
					)
				)
			),
			
			fn newMacro theName macroName str = "macroscript " + theName + " category:\"LayerMan\" buttonText:\"" + macroName + "\" tooltip:\"" + macroName + "\" (" + str + ")",
			
			fn createMacrosIfNotPresent =
			(
				if C4 != undefined and classOf C4 == structDef and not C4.macroExists "LayerMan" "toggleLayerMan" do
				(
					local theFile = (getDir #export) + "\\LayerMan.mcr"
					local mcr = createFile theFile
					format "global LayerMan\n" to:mcr
					
					format "%\n" (newMacro "addSelection2Layer" 			"Add Selection to Layer" 		"on isVisible return (LayerMan.ops.filterFN() and not LayerMan.ops.hitNode.bold and selection.count != 0); 									on isEnabled return (LayerMan.ops.filterFN() and not LayerMan.ops.hitNode.bold and selection.count != 0); 									on execute do LayerMan.ops.addSelection2Layer()") to:mcr
					format "%\n" (newMacro "createNewGroup" 			"Create New Group" 			"on isVisible return LayerMan.ops.filterFN(); 																	on isEnabled return LayerMan.ops.filterFN(); 																	on execute do LayerMan.ops.createNewGroup()") to:mcr
					format "%\n" (newMacro "createNewLayer" 			"Create New Layer" 			"on isVisible return LayerMan.ops.filterFN(); 																	on isEnabled return LayerMan.ops.filterFN(); 																	on execute do LayerMan.ops.createNewLayer()") to:mcr
					format "%\n" (newMacro "createLayerFromSelection" 	"Create Layer from Selection" 	"on isVisible return (LayerMan.ops.filterFN() and selection.count != 0); 														on isEnabled return (LayerMan.ops.filterFN() and selection.count != 0); 														on execute do LayerMan.ops.createNewLayer fromSelection:true") to:mcr
					format "%\n" (newMacro "deleteGroup" 				"Delete Group" 				"on isVisible return (LayerMan.ops.filterFN() and LayerMan.ops.hitNode.bold and LayerMan.ops.hitNode.children == 0 and LayerMan.ops.hitNode.text != \"0\"); 		on isEnabled return (LayerMan.ops.filterFN() and LayerMan.ops.hitNode.bold and LayerMan.ops.hitNode.children == 0 and LayerMan.ops.hitNode.text != \"0\"); 	on execute do LayerMan.ops.deleteLayer group:true") to:mcr
					format "%\n" (newMacro "deleteLayer" 				"Delete Layer" 				"on isVisible return (LayerMan.ops.filterFN() and not LayerMan.ops.hitNode.bold and LayerMan.ops.hitNode.text != \"0\"); 							on isEnabled return (LayerMan.ops.filterFN() and not LayerMan.ops.hitNode.bold and LayerMan.ops.hitNode.text != \"0\"); 							on execute do LayerMan.ops.deleteLayer()") to:mcr
					format "%\n" (newMacro "deleteLayerAndObjects" 		"Delete Layer and Objects" 		"on isVisible return (LayerMan.ops.filterFN() and not LayerMan.ops.hitNode.bold and LayerMan.ops.hitNode.text != \"0\"); 							on isEnabled return (LayerMan.ops.filterFN() and not LayerMan.ops.hitNode.bold and LayerMan.ops.hitNode.text != \"0\"); 							on execute do LayerMan.ops.deleteLayer deleteObjects:true") to:mcr
					format "%\n" (newMacro "dockTMTop" 				"Dock Top" 					"on isVisible return (LayerMan.dialog.isOpen and LayerMan.dialog.dockState != #cui_dock_top); 										on isEnabled return (LayerMan.dialog.isOpen and LayerMan.dialog.dockState != #cui_dock_top); 										on execute do LayerMan.dialog.dock #cui_dock_top") to:mcr
					format "%\n" (newMacro "dockTMBottom" 				"Dock Bottom" 				"on isVisible return (LayerMan.dialog.isOpen and LayerMan.dialog.dockState != #cui_dock_bottom); 										on isEnabled return (LayerMan.dialog.isOpen and LayerMan.dialog.dockState != #cui_dock_bottom); 										on execute do LayerMan.dialog.dock #cui_dock_bottom") to:mcr
					format "%\n" (newMacro "dockTMLeft" 				"Dock Left" 					"on isVisible return (LayerMan.dialog.isOpen and LayerMan.dialog.dockState != #cui_dock_left); 										on isEnabled return (LayerMan.dialog.isOpen and LayerMan.dialog.dockState != #cui_dock_left); 										on execute do LayerMan.dialog.dock #cui_dock_left") to:mcr
					format "%\n" (newMacro "dockTMRight" 				"Dock Right" 					"on isVisible return (LayerMan.dialog.isOpen and LayerMan.dialog.dockState != #cui_dock_right); 										on isEnabled return (LayerMan.dialog.isOpen and LayerMan.dialog.dockState != #cui_dock_right); 										on execute do LayerMan.dialog.dock #cui_dock_right") to:mcr
					format "%\n" (newMacro "floatTM" 					"Float" 						"on isVisible return (LayerMan.dialog.isOpen and LayerMan.dialog.dockState != #cui_floatable); 										on isEnabled return (LayerMan.dialog.isOpen and LayerMan.dialog.dockState != #cui_floatable); 										on execute do LayerMan.dialog.dock #cui_floatable") to:mcr
					format "%\n" (newMacro "getWirecolorsFromObjects" 	"Get Wirecolors from Objects" 	"on isVisible return LayerMan.dialog.isOpen; 																	on isEnabled return LayerMan.dialog.isOpen; 																	on execute do LayerMan.ops.getWirecolorsFromObjects()") to:mcr
					format "%\n" (newMacro "makeLayerCurrent" 			"Make Layer Current" 			"on isVisible return (LayerMan.ops.filterFN() and not LayerMan.ops.hitNode.bold); 												on isEnabled return (LayerMan.ops.filterFN() and not LayerMan.ops.hitNode.bold); 												on execute do LayerMan.ops.makeLayerCurrent LayerMan.ops.hitLayer[1] LayerMan.ops.hitNode") to:mcr
				--	format "%\n" (newMacro "rename" 					"Rename" 					"on isVisible return (LayerMan.ops.filterFN() and LayerMan.ops.hitNode.text != \"0\"); 												on isEnabled return (LayerMan.ops.filterFN() and LayerMan.ops.hitNode.text != \"0\"); 											on execute do LayerMan.ops.renameLayer()") to:mcr
					format "%\n" (newMacro "selectLayer" 				"Select Layer" 				"on isVisible return LayerMan.ops.filterFN(); 																	on isEnabled return LayerMan.ops.filterFN(); 																	on execute do LayerMan.ops.selectLayer()") to:mcr
					format "%\n" (newMacro "selectObjectsByName" 		"Select Objects by Name" 		"on isVisible return LayerMan.ops.filterFN(); 																	on isEnabled return LayerMan.ops.filterFN(); 																	on execute do LayerMan.ops.selectObjectsByName()") to:mcr
					format "%\n" (newMacro "setWirecolorsRandomly" 		"Set Wirecolors Randomly" 		"on isVisible return LayerMan.dialog.isOpen; 																	on isEnabled return LayerMan.dialog.isOpen; 																	on execute do LayerMan.ops.setWirecolorsRandomly()") to:mcr
					format "%\n" (newMacro "sortLayersByName" 			"Sort Layers by Name" 			"on isVisible return LayerMan.dialog.isOpen; 																	on isEnabled return LayerMan.dialog.isOpen; 																	on execute do LayerMan.ops.sortLayersByName()") to:mcr
					format "%\n" (newMacro "switch2SimpleMode" 			"Switch to Simple Mode" 		"on isVisible return LayerMan.dialog.isOpen; on isChecked return LayerMan.dialog.simpleView;											on isEnabled return LayerMan.dialog.isOpen;  																	on execute do LayerMan.dialog.toggleSimpleView (not LayerMan.dialog.simpleView)") to:mcr
					
					format "macroscript toggleLayerMan category:\"LayerMan\" buttonText:\"Toggle LayerMan\" tooltip:\"Toggle LayerMan\" (on isChecked return LayerMan.dialog.isOpen; on execute do LayerMan.dialog.toggle true)\n" to:mcr
					close mcr
					macros.load (getDir #export)
					copyFile ((getDir #export) + "\\LayerMan.mcr") ((getDir #ui) + "macroscripts\\LayerMan.mcr")
					callbacks.addScript #postSystemShutdown "deleteFile ((getDir #export) + \"\\LayerMan.mcr\")" id:#deleteMCR
				)
			),
			
			fn showAboutBox = C4.notify "About LayerMan v2.7" "Developed by Yunus Emre ''Light'' Balcioglu\nwww.orionflame.com" modal:true center:LayerMan backColor:bgColor,
			fn filterFN = LayerMan.dialog.isOpen and hitIndex != 0 and hitNode != undefined and hitLayer != undefined and visibleNodeNames[hitIndex] == hitNode.text
		)
		
		local ops = methods()
		
		struct settings
		(
											--	Settings to be saved
											
			autoLoad = false,					--	Value that holds whether the tool is automatically loaded upon max startup
			autoFocus = false,					--	Value that holds whether the tool should be in focus when the mouse is inside the dialog
			dockState = #cui_floatable,			--	Value that holds the current docking state
			dialogPos = [0,0],					--	Value that holds the current dialog position
			dialogSize = #([295,647], [558,647]),	--	Value that holds the current dialog size
			simpleView = false,					--	Value that holds whether the tool shows info about alternate properties and materials or not
			
			isOpen = false,					--	Value that holds the current state of whether the dialog is open or not
			focused = false,					--	Value that holds whether the tool is focused (mouse is inside the dialog) or not
			saveDialog = true,					--	Value that specifies whether the tool should save its size and position or not
			leftDown = false,					--	Value that holds whether the left mouse button is down or not
			firstPoint = [0,0],					--	2 point coordinate when left mouse button is first clicked
			bgColor = white,					--	Color of the treeview background
			
			--	Treeview messages
			TV_FIRST = 0x1100,
			TVM_SETBKCOLOR = TV_FIRST + 29,
			TVM_SETINSERTMARKCOLOR = TV_FIRST + 37,
			
			fn resize s =
			(
				local dialogHeight = s.y - 23
				if mod dialogHeight 16 == 0 then ops.resizeTreeViews() else if dockState == #cui_floatable do LayerMan.height -= mod dialogHeight 16
				if dockState == #cui_floatable and LayerMan.height < 39 do LayerMan.height = 39
				local dialogWidth = if simpleView then (if LayerMan.width > 295 then s.x else 295) else (if LayerMan.width > 558 then s.x else 558)
				if dockState == #cui_floatable do LayerMan.width = dialogWidth
				layers.size.x = dialogWidth - (if simpleView then 74 else 338)
				local tvs = #(cLabel, hLabel, fLabel, pLabel, mLabel, vs, fr, att, mat, scroll)
				local offset = #(0, 19, 38, 58, 190, 17, 36, 58, 190, (if simpleView then 58 else 322))
				local tvWidth = #(13, 12, 12, 72, 64, 15, 15, 133, 133, 16)
				for i = 1 to tvs.count do
				(
					tvs[i].pos.x = layers.size.x + offset[i]
					tvs[i].size.x = tvWidth[i]
				)
				sep01.width = dialogWidth
				layerColor.pos.x = layers.size.x
				stripes01.pos.x = layers.size.x + 13
				stripes02.pos.x = layers.size.x + 32
				stripes03.pos.x = layers.size.x + 51
				if saveDialog do (if simpleView then ( dialogSize[1] = s; dialogSize[2] = [dialogSize[2].x, LayerMan.height] ) else dialogSize[2] = s)
				for i in #(layers, vs, fr, att, mat, scroll) do windows.sendMessage i.hWnd ops.WM_VSCROLL ops.SB_PAGETOP 0
			),
			
			fn dock style save:true =
			(
				if style != #cui_floatable then
				(
					if LayerMan.dialogBar then ( dock #cui_floatable; dockState = style; timeX4.active = true )
					else
					(
						local size = if style == #cui_dock_top or style == #cui_dock_bottom then [systemTools.getScreenWidth(), LayerMan.height + 4] else [LayerMan.width - 4, systemTools.getScreenHeight()]
						saveDialog = false
						dockState = #cui_floatable
						setDialogPos LayerMan [-1000, -1000]
						LayerMan.width = size.x
						LayerMan.height = size.y
						dockState = style
						LayerMan.width -= 4
						LayerMan.height += 4
						setDialogPos LayerMan dialogPos
						cui.registerDialogBar LayerMan minSize:size maxSize:size style:#(#cui_dock_all)
						cui.dockDialogBar LayerMan style
						ops.updateScrollbar()
					)
				)
				else
				(
					if isOpen do
					(
						cui.unRegisterDialogBar LayerMan
						setDialogPos LayerMan dialogPos
						if save do dockState = #cui_floatable
						LayerMan.width = dialogSize[if simpleView then 1 else 2].x
						LayerMan.height = dialogSize[if simpleView then 1 else 2].y
						saveDialog = true
					)
				)
			),
			
			fn toggleSimpleView state =
			(
				if dockState == #cui_floatable do
				(
					simpleView = state
					LayerMan.width = if state then (if isOpen then stripes03.pos.x + 23 else dialogSize[1].x) else dialogSize[2].x
					if state then
					(
						scroll.pos.x = stripes03.pos.x + 7
						att.size.y = pLabel.size.y = 0
					)
					else
					(
						scroll.pos.x = mat.pos.x + 132
						att.size.y = layers.size.y
						pLabel.size.y = 17
					)
					scroll.size.x = 16
				)
				ops.updateScrollbar()
			),
			
			fn setupTreeViews =
			(
				local tvs = #(layers, vs, fr, att, mat, lLabel, cLabel, hLabel, fLabel, pLabel, mLabel, scroll)
				for i in tvs do
				(
					windows.sendMessage i.hWnd TVM_SETBKCOLOR 0 (C4.rgb2HEX bgColor)
					windows.sendMessage i.hWnd TVM_SETINSERTMARKCOLOR 0 (C4.rgb2HEX bgColor)
				)
				tvs.borderStyle = #ccNone
				tvs.appearance = #ccFlat
				tvs.lineStyle = #tvwRootLines
				tvs.labelEdit = #tvwManual
				tvs.indentation = 28 * 15
				tvs.scroll = false
				tvs.fullRowSelect = true
				tvs.font = "Verdana"
				layers.OLEDragMode = #ccOLEDragAutomatic
				layers.OLEDropMode = #ccOLEDropManual
				#(vs, fr, att, mat, lLabel, cLabel, hLabel, fLabel, pLabel, mLabel, scroll).style = #tvwTextOnly
				#(vs, fr).checkBoxes = true
				scroll.scroll = true
				for i = 1 to 6 do C4.addNode #(lLabel, cLabel, hLabel, fLabel, pLabel, mLabel)[i] #("Layer Name", "C", "H", "F", "Properties", "Materials")[i] bold:true backColor:bgColor
			),
			
			fn focus =
			(
				--	If mouse is inside the unfocused dialog and autofocus option is turned on, set focus to dialog. If search or filter is on, set focus to search or filter edit text
				if C4.isMouseInside LayerMan [LayerMan.width, LayerMan.height] [0, 7, 0, 26] then (if not focused and autoFocus do setFocus layers; focused = true) else focused = false
			),
			
			fn open =
			(
				if LayerManProRC == undefined do ops.definePropertyRCMenu()
				local theOps = execute (getINISetting (getMAXINIFile()) "Light" "LayerMan")
				if theOps != OK do										--	Load settings from the ini file if present (not first time running)
				(
					autoLoad = theOps[1]; autoFocus = theOps[2]; dockState = theOps[3]; dialogPos = theOps[4]; dialogSize = theOps[5]; simpleView = theOps[6]
					ops.pickedMaterialLibrary = theOps[7]
				)
				if autoFocus do timeX.active = true						--	If auto focus is on, activate the timer that checks if the mouse is over the dialog
				setupTreeViews()										--	Set the color, style and various other properties of all treeviews
				ops.addCallbacks()										--	Creates callbacks that signals events such as file open, merge, reset and new layer creation via max's layer manager
				ops.initializeLayers()									--	Create layer nodes along with their alternate properties and materials
				toggleSimpleView simpleView								--	Toggle simple view based on the last setting
				LayerMan.resized dialogSize[if simpleView then 1 else 2]		--	Call the resized event handler to ensure the UI controls are properly scaled and placed
				if simpleView do ops.updateScrollbar()						--	Update the scrollbar to ensure the scrollbar is visible (if it has to be)
				if dockState != #cui_floatable do dock dockState			--	Dock dialog based on last setting
				isOpen = true											--	Change the isOpen flag to true
				updateToolbarButtons()									--	Update toolbar buttons to reflect the current open/closed state of the tool
			),
			
			fn move pos = if saveDialog and dockState == #cui_floatable do dialogPos = pos,
			
			fn close2 =
			(
				if LayerMan.dialogBar do dock #cui_floatable save:false
				local dPos = getDialogPos LayerMan; if dPos.x >= 0 and dPos.y >= 0 do dialogPos = dPos
				local arr = #(autoLoad, autoFocus, dockState, dialogPos, dialogSize, simpleView, ops.pickedMaterialLibrary)
				setINISetting (getMAXINIFile()) "Light" "LayerMan" (arr as string)
				isOpen = false; updateToolbarButtons()
				callbacks.removeScripts id:#LayerManCallbacks
			),
			
			fn toggle override =
			(
				if isOpen then destroyDialog LayerMan
				else
				(
					ops.createMacrosIfNotPresent()
					local theOps = execute (getINISetting (getMAXINIFile()) "Light" "LayerMan")
					local dialogSize = if theOps != OK then theOps[5][if theOps[6] then 1 else 2] else [558,647]
					if theOps != OK then (if theOps[1] or override do createDialog LayerMan width:dialogSize.x height:dialogSize.y pos:theOps[4] style:#(#style_toolWindow, #style_sysMenu, #style_resizing) bitmap:(bitmap 1 1 color:ops.bgColor) bmpstyle:#bmp_stretch)
						else (if override do createDialog LayerMan width:dialogSize.x height:dialogSize.y style:#(#style_toolWindow, #style_sysMenu, #style_resizing) bitmap:(bitmap 1 1 color:ops.bgColor) bmpstyle:#bmp_stretch)
				)
				timeX5.active = true
			)
		)
		
		local dialog = settings()
		
		on LayerMan open do dialog.open()
		on LayerMan lButtonDown pos do ( dialog.leftDown = true; dialog.firstPoint = pos )
		on LayerMan lButtonUp pos do dialog.leftDown = false
		on LayerMan mouseMove pos do if dialog.dockState == #cui_floatable and dialog.leftDown do setDialogPos LayerMan ((getDialogPos LayerMan) + (pos - dialog.firstPoint))
		on LayerMan moved pos do dialog.move pos
		on LayerMan resized s do dialog.resize s
		on layers mouseDown button modKey x y do ops.popupRightClickMenu button x y layersRC layers
		on layers mouseUp button modKey x y do enableAccelerators = false
		on layers nodeClick theNode do if keyboard.shiftPressed do ops.selectLayer()
		on layers keyUp button modKey do ops.keyPressed button
		on layers OLEStartDrag data fx do ops.startDrag()
		on layers OLEDragOver data fx button modKey x y state do ops.dragOver()
		on layers OLEDragDrop data fx button key x y do ops.dragDrop()
		on layers OLECompleteDrag fx do ops.completeDrag fx
		on layers expand theNode do ( ops.updateAll data:false; ops.popupRightClickMenu 1 0 0 layersRC layers; )
		on layers collapse theNode do ( ops.updateAll data:false; ops.popupRightClickMenu 1 0 0 layersRC layers; )
		on layers beforeLabelEdit cancel do enableAccelerators = false
		on layers afterLabelEdit cancel str do ops.postLabelEdit str
		on layerColor lButtonDown pos flags do ops.pickLayerColor pos
		on stripes01 lButtonDown pos flags do ops.popupRightClickMenu 0 pos.x (pos.y - 1) layersRC layers
		on stripes02 lButtonDown pos flags do ops.popupRightClickMenu 0 pos.x (pos.y - 1) layersRC layers
		on stripes03 lButtonDown pos flags do ops.popupRightClickMenu 0 pos.x (pos.y - 1) layersRC layers
		on vs nodeCheck theNode do ops.toggleHideFreeze theNode #isHidden
		on fr nodeCheck theNode do ops.toggleHideFreeze theNode #isFrozen
		on att mouseDown button modKey x y do ops.popupRightClickMenu button x y LayerManProRC att
		on att nodeClick theNode do ops.toggleAlternateProperty theNode
		on mat mouseDown button modKey x y do ( if button == 2 do ops.defineMaterialRCMenu(); ops.popupRightClickMenu button x y LayerManMatRC mat )
		on mat nodeClick theNode do ops.toggleAlternateMaterial theNode
		on scroll mouseDown button modKey x y do ops.popupRightClickMenu 0 x y none scroll
		on timeX tick do dialog.focus()
		on timeX2 tick do ops.detectLabelChange()
		on timeX3 tick do ops.scrollLayers()
		on timeX4 tick do ( dialog.dock dialog.dockState; timeX4.active = false )
		on timeX5 tick do ( setFocus LayerMan; timeX5.active = false )
		on LayerMan close do dialog.close2()
	)
	LayerMan.dialog.toggle false
)