--	-----------------------------------------------------------------------------------------------------------------------------
--	Mass!ve v0.6 by Light @ 11.01.2006 04:17:56														www.orionflame.com
--	-----------------------------------------------------------------------------------------------------------------------------
--	Development History
--
--	v0.2 	bold style for classes & super classes
--			interactive mode as default
--
--	v0.3 	docks faster
--			remembers dialog size correctly
--
--	v0.4 	sorts properties alphabetically
--
--	v0.5 	color coded properties
--			auto macro creation
--			filtering properties
--			complete rewrite
--			modifier support
--			new interface
--
--	v0.6		better handling of instanced modifiers
--
--	v0.7		shows the number of properties for each class
--			slightly modified the right click menu
--			better naming for some properties
--			options array [ops] is rearranged
(
	global massive, toggleMassive, C4; local massiveRC
	
	rollout massive "Mass!ve" width:200 height:600
	(
		checkButton		noproperty 		"No Property Selected"		width:140		height:16		pos:[3,3]		checked:true
		
		spinner 			fromInt 			"" 						fieldWidth:51 					pos:[0,3]		type:#integer		visible:false		range:[-999999999,999999999,0]
		spinner 			fromFloat 		"" 						fieldWidth:51 					pos:[0,3]						visible:false		range:[-999999999,999999999,0]
		checkButton		fromBoolean 		"True"					width:60			height:16		pos:[3,3]		checked:true		visible:false
		colorPicker		fromColor 		""						fieldWidth:59 		height:16		pos:[-1,3]	color:white		visible:false
		checkButton		useRandom 		"to" 						width:16 			height:16		pos:[65,3]					visible:false
		spinner			toInt 			""						fieldWidth:51 		height:16		pos:[80,3]	type:#integer		visible:false		range:[-999999999,999999999,10]
		spinner			toFloat 			""						fieldWidth:51 		height:16		pos:[80,3]	enabled:false		visible:false		range:[-999999999,999999999,10]		
		checkButton		toBoolean 		"False"					width:60			height:16		pos:[83,3]	enabled:false		visible:false
		colorPicker		toColor 			""						fieldWidth:58 		height:16		pos:[80,3]	color:white		visible:false
		
		button 			setProperties 	 	"S"		 				width:16			height:16 	pos:[145,3]																		tooltip:"Set Selected Property"
		checkButton 		filterProperties 	"F"		 				width:16			height:16 	pos:[163,3]																		tooltip:"Filter"
		editText			filter				""						width:219					pos:[-2,23]					visible:false
		button 			showOptions 		""		 				width:16			height:16 	pos:[181,3]	images:#(bitmap 14 14 color:((colorMan.getColor #text) * 255), C4.getOptionsMask(), 1, 1, 1, 1, 1)	tooltip:"Options"
		activeXControl	tv 				"MSComctlLib.TreeCtrl" 		width:200 		height:578 	pos:[0,22]
		timer			timeX			interval:50
		
		--	auto load, auto focus, dock state, dialog pos, dialog size, expand nodes, all objects, include groups, relative, interactive
		local ops = #(false, false, 0, [0,0], [200,600], false, true, true, false, false), superArr, classArr, cacheArr, pIndex = 0, cIndex = 0, isOpen = false
		
		--	Update the checked state of the elements in the rcmenu using their order and the options array (ops)
		fn updateRC =
		(
			local numArr = #(1, 2, 7, 8, 9, 10)
			for i = 1 to numArr.count do #(massiveRC.autoLoad, massiveRC.autoFocus, massiveRC.allObjects, massiveRC.includeGroups, massiveRC.relative, massiveRC.interactive)[i].checked = ops[numArr[i]]
		)
		
		fn updateTitle = if ops[3] == 0 do massive.title = "Mass!ve S:" + superArr.count as string + " C:" + classArr.count as string + " P:" + (tv.nodes.count - (superArr.count + classArr.count)) as string
		
		--	Hide all controls except noproperty and set parent index (pIndex) and child index (cIndex) to 0, meaning no property is selected
		fn hideControls = (#(fromInt, fromFloat, fromBoolean, fromColor, useRandom, toInt, toFloat, toBoolean, toColor).visible = false; noproperty.visible = true; pIndex = cIndex = 0)
		
		fn expandNodes =
		(
			if tv.nodes.count > 1 then
			(
				if ops[3] == 0 do setDialogPos massive [-1000,-1000]
				--	Expand all class nodes to opposite state
				for i = classArr.count to 1 by -1 do tv.nodes[superArr.count + i].expanded = not tv.nodes[superArr.count + 1].expanded
				--	Expand all superclass nodes to true
				for i = superArr.count to 2 by -1 do tv.nodes[i].expanded = true
				--	[adjust treeview scroll]
				--	If a node has been selected before, select its class (parent) node then itself, else collapse the first node and then expand it
				if pIndex != 0 then tv.nodes[tv.nodes[pIndex + superArr.count].child.index + cIndex - 2].selected = tv.nodes[pIndex + superArr.count].selected = true else if tv.nodes.count != 0 do (tv.nodes[1].expanded = false; tv.nodes[1].expanded = true)
				if ops[3] == 0 do setDialogPos massive ops[4]
				--	Return the first class node's expand state
				tv.nodes[superArr.count + 1].expanded
			)
			else false
		)
		
		fn lowercase str =
		(
			local index = 0
			while index != undefined do
			(
				index = findString str "_"
				if index != undefined do str = C4.lower (replace str index 1 ":") (index + 1)
			)
			C4.replaceString str ":" "_"
		)
		
		fn initializeClasses =
		(
			--	Collect unique classes and a scene object for each one
			classArr = #(); objArr = #(); for i in objects where (getPropNames i).count != 0 do (if C4.appendIfNew classArr (classOf i.baseObject) do append objArr i)
			--	Collect unique superclasses of the class array above
			superArr = #(); for i in classArr do C4.appendIfNew superArr (classOf i)
			--	Collect unique modifiers and add a scene object for each one to the object array (objArr)
			local modifierArr = #(); for i in objects where i.modifiers.count != 0 do (for f = 1 to i.modifiers.count do (if C4.appendIfNew modifierArr (classOf i.modifiers[f]) do append objArr i.modifiers[f]))
			--	See if there is any modifier of the class spacewarpModifier or modifier
			local hasMod = for i in #(spacewarpModifier, modifier) collect findItem (for f in modifierArr collect classOf f) i != 0
			--	Append spacewarpModifier or modifier to the superclass array (superArr) if there is a modifier of their class in the above hasMod array
			for i = 1 to 2 where hasMod[i] do append superArr #(spacewarpModifier, modifier)[i]
			--	If there is a modifier of either of the class, join the modifier array (modifierArr) to the class array (classArr)
			if hasMod[1] or hasMod[2] do join classArr modifierArr
			--	Convert the class array (classArr) into an array of strings
			classArr = for i in classArr collect (i as string)
			--	Create a temporary array (temp) where the class names are capitalized and the array is sorted
			local copyClassArr = C4.copyArr classArr
			local temp = sort (for i in copyClassArr collect C4.upper i 1)
			--	Reorder the object array (objArr) using the class array (classArr) and the temporary array (temp)
			objArr = C4.reorderArr objArr classArr temp
			--	Convert the class array (classArr) into an array of classes, using the temporary array (temp) which is already capitalized and sorted
			classArr = for i in temp collect (execute i)
			for i = classArr.count to 1 by -1 do
			(
				--	List all properties of a class
				local props = getPropNames classArr[i]
				--	Collect properties from the property array (props) above whose class is integer, float, booleanClass or color. Try/Catch is used because in the cloth modifier there are
				--	properties accessible in vertex groups and by default no vertex group exist, so instead of modifying the scene to access the values, simply skip them.
				local arr = for f in props where try(findItem #(integer, float, booleanClass, color) (classOf (getProperty objArr[i] f)) != 0) catch(false) collect (f as string)
				--	Change the current element of the class array (classArr) to #(class, properties, object) if the array is not empty
				temp = for f in arr collect (lowercase f)
				if arr.count != 0 then classArr[i] = #(classArr[i]) + (sort (for f in temp collect C4.lower f 1)) + #(objArr[i])
				else
				(
					--	If the temporary array (arr) where properties are hold is empty, collect the class of all classes in the class array (classArr) except the current element
					local superArr2 = for f = 1 to classArr.count where f != i collect (if classOf classArr[f] == array then classOf classArr[f][1] else classOf classArr[f])
					--	If the class of the current element, which is a superclass, is not found in the superArr2 (array holding all the classes of class array (classArr)), delete it from the superclass array (superArr)
					if findItem superArr2 (classOf classArr[i]) == 0 do deleteItem superArr (findItem superArr (classOf classArr[i]))
					--	Delete the current element, which is a class, from the class array (classArr) as it doesn't have any property of type we are interested
					deleteItem classArr i
				)
			)
			--	Convert the superclass array (superArr) into an array of strings, capitalize them and sort the array, and finally convert it back to an array of superclasses
			superArr = sort (for i in (C4.stringArr superArr) collect C4.upper i 1)
			superArr = for i in superArr collect (execute i)
		)
		
		fn createTVNodes refresh =
		(
			if ops[3] == 0 do setDialogPos massive [-1000,-1000]
			tv.nodes.clear()
			if refresh do initializeClasses()
			local sClasses = #(#("Geometry", "Shapes", "Cameras", "Lights", "Helpers", "SpaceWarps", "SpaceWarp Modifiers", "Modifiers"), #(GeometryClass, shape, camera, light, helper, SpacewarpObject, SpacewarpModifier, modifier))
			--	Set the name of superclass nodes to their objectset equivalent. Make them nodes bold, expanded and red
			for i = 1 to superArr.count do C4.addNode tv sClasses[1][findItem sClasses[2] superArr[i]] expand:true bold:true color:(color 0 0 200)
			--	Add class nodes to appropriate superclass nodes based on their superclass, capitalize the first letter and make them bold
			for i in classArr do C4.addNode tv (C4.addCount (C4.upper (i[1] as string) 1) (i.count - 2) "()" false) index:(findItem superArr (classOf i[1])) bold:true
			--	Add property nodes to appropriate class nodes based on their order in the classArr, where superArr.count + classArr.count gives the correct treeview indices for class nodes
			local theNum = superArr.count
			for i in classArr do
			(
				theNum += 1
				for f = 2 to i.count - 1 do
				(
					local colorArr = #((color 200 0 150), (color 220 0 0), black, (color 0 160 0))
					local theClass = classOf (getProperty i[i.count] i[f])
					local type = findItem #(integer, float, booleanClass, color) theClass
					C4.addNode tv (lowercase i[f]) index:theNum color:colorArr[type]
				)
			)
			hideControls(); updateTitle()
			if ops[6] do expandNodes()
			if ops[3] == 0 do setDialogPos massive ops[4]
		)
		
		fn updateControls theClass =
		(
			local index = findItem #(integer, float, booleanClass, color) theClass
			local arr = #(#(fromInt, toInt), #(fromFloat, toFloat), #(fromBoolean, toBoolean), #(fromColor, toColor))
			for i = 1 to 4 do (arr[i].visible = (i == index); if i != 3 do arr[i][2].enabled = useRandom.checked)
			useRandom.visible = true
		)
		
		fn inGroup obj = ops[8] or not (if isGroupMember obj then true else isGroupHead obj)
		
		fn getCurrentValue =
		(
			local arr = #(fromInt, fromFloat, fromBoolean, fromColor)
			--	Find the visible control
			local theControl = arr[findItem (for i in arr collect i.visible) true]
			--	Return the value of the visible control based on its class
			case classOf theControl of
			(
				spinnerControl: theControl.value
				checkButtonControl: theControl.checked
				colorPickerControl: theControl.color
			)
		)
		
		fn setValues =
		(
			fn mixColors a b average = if average then (a + b) / 2 else a + b
			with undo "Massive" on with redraw off
			(
				if pIndex != 0 do
				(
					local collection = for i in selection where inGroup i collect i; if ops[7] do collection = for i in objects where inGroup i collect i
					--	Class of the currently visible control's value
					local theClass = classOf (getCurrentValue())
					local property = classArr[pIndex][cIndex]
					--	P is the array that contains the class of the selected node
					local p = classArr[pIndex], proceed = true
					--	If the object stored in p is deleted, find another object of the same class. If not found, create treeview nodes from scratch and set proceed to false
					if isDeleted p[p.count] do (if (p[p.count] = (for i in objects where classOf i == p[1] collect i)[1]) == undefined then (createTVNodes true; proceed = false) else proceed = true)
					if proceed do
					(
						case theClass of
						(
							color:
							(
								for i in collection where classOf i.baseObject == p[1] do
								(
									local baseVal = if ops[9] then getProperty i.baseObject property else color 0 0 0
									seed (random -99999999 99999999)
									if useRandom.checked then setProperty i.baseObject property (mixColors (random fromColor.color toColor.color) baseVal ops[9]) else setProperty i.baseObject property (mixColors (getCurrentValue()) baseVal ops[9])
								)
							)
							booleanClass:
							(
								for i in collection where classOf i.baseObject == p[1] do
								(
									seed (random -99999999 99999999)
									if useRandom.checked then setProperty i.baseObject property #(true, false)[random 1 2] else setProperty i.baseObject property fromBoolean.checked
								)
							)
							default:
							(
								local arr = #(#(fromInt, fromFloat), #(toInt, toFloat))
								--	Get the values of visible control pair, which would be either floats or integers
								local val = for i = 1 to 2 collect arr[i][findItem (for f in arr[i] collect f.visible) true].value
								--	If the selected property is a modifier or spacewarp modifier (wsm), deal with them seperately to check for instanced modifiers
								if classOf p[1] == modifier or classOf p[1] == SpacewarpModifier then
								(
									local instancedMods = #()
									for i in collection do
									(
										--	Collect all modifiers of the current object (in the loop)
										local modArr = for f = 1 to i.modifiers.count where classOf i.modifiers[f] == p[1] collect f
										for f in modArr do
										(
											proceed = true
											--	See if there are any instances of the current modifier (in the loop) in other objects
											local instances = for k in refs.dependents i.modifiers[f] where isValidNode k collect k
											if instances.count > 1 do
											(
												--	If the instanced modifier has already been included in the instanced modifiers array (instancedMods), do not change the value of the property by setting proceed to false.
												--	If not, store the instanced modifier in instanced modifiers array (instancedMods).
												if (for k in instancedMods where i.modifiers[f] == k collect f).count == 0 then append instancedMods i.modifiers[f] else proceed = false
											)
											if proceed do
											(
												local baseVal = if ops[9] then getProperty i.modifiers[f] property else 0
												seed (random -99999999 99999999)
												if useRandom.checked then setProperty i.modifiers[f] property ((random val[1] val[2]) + baseVal) else setProperty i.modifiers[f] property ((getCurrentValue()) + baseVal)
											)
										)
									)
								)
								else
								(
									for i in collection where classOf i.baseObject == p[1] do
									(
										local baseVal = if ops[9] then getProperty i.baseObject property else 0
										seed (random -99999999 99999999)
										if useRandom.checked then setProperty i.baseObject property ((random val[1] val[2]) + baseVal) else setProperty i.baseObject property ((getCurrentValue()) + baseVal)
									)
								)
							)
						)
					)
				)
			)
		)
		
		on massive open do
		(
			--	Create the bitmap for showOptions button, and get it's mask from getOptionsMask fn
			showOptions.images = #(bitmap 14 14 color:((colorMan.getColor #text) * 255), C4.getOptionsMask(), 1, 1, 1, 1, 1)
			--	If there is no INI setting present (first time running), use the default settings above (ops array)
			local theOps = execute (getINISetting (getMAXINIFile()) "Light" "Massive"); if theOps != OK do ops = theOps
			tv.size = ops[5] - [0,22]
			--	Set treeview layout to most commonly used features
			C4.setLayout tv
			--	Create treeview nodes from scratch
			createTVNodes true
			--	Update options menu using the last settings or the default settings above (ops array)
			updateRC()
			--	Dock dialog based on last setting, but first set the dock state (ops[3]) to 0, so dockDialog fn doesn't ignore the call with ops[3] != id condition
			case ops[3] of
			(
				1: (ops[3] = 0; C4.dockDialog massive &ops[3] 1 0)
				2: (ops[3] = 0; C4.dockDialog massive &ops[3] 2 0)
			)
			isOpen = true; updateToolbarButtons()
		)
		
		on massive moved pos do
		(
			local dPos = getDialogPos massive
			if dPos.x >= 0 and dPos.y >= 0 do ops[4] = getDialogPos massive
		)
		
		--	Let resizing dialog when undocked
		on massive resized size do if ops[3] == 0 do (ops[5] = size; if size.x >= 200 then tv.size = [size.x, size.y - 22] else massive.width = 200)
		
		--	Position the dialog when first mouse button is hold down on treeview. Dialog must be undocked (ops[3] = 0)
		local leftDown = false, firstPoint = [0,0]
		on tv mouseDown button key x y do (leftDown = true; firstPoint = [x,y])
		on tv mouseMove button key x y do (if leftDown and ops[3] == 0 and button == 1 do setDialogPos massive ((getDialogPos massive) + ([x,y] - firstPoint)))
		
		on tv mouseUp button key x y do
		(
			if button == 2 do
			(
				case key of
				(
					--	Right click to expand all class nodes
					0: ops[6] = expandNodes()
					--	Shift right click to reset the dialog size
					1: if ops[3] == 0 do (massive.width = 200; massive.height = 600)
					--	Ctrl right click to create treeview nodes from scratch
					2: if filterProperties.checked then filterProperties.changed (filterProperties.checked = false) else createTVNodes true
				)
			)
			leftDown = false
		)
		
		on tv nodeClick theNode do
		(
			hideControls()
			--	Show noproperty checkbutton if the selected node's index is smaller or equals to superArr.count + classArr.count [else] hide it
			if noproperty.visible = not (theNode.index > superArr.count + classArr.count) then
			(
				local nodeName = C4.removeCount theNode.text "("
				local arr = #(), selectedNode = execute nodeName
				--	If the selected node is a superclass node and its name is "Modifiers" or "SpaceWarp Modifiers" then collect all objects with a modifier [else] collect all objects of the same superclass
				if theNode.index <= superArr.count then arr = if nodeName == "Modifiers" or nodeName == "SpaceWarp Modifiers" then (for i in objects where i.modifiers.count != 0 collect i) else (for i in selectedNode where inGroup i collect i)
				else
				(
					--	If the selected node is a class node and its parent name is "Modifiers" or "SpaceWarp Modifiers" then collect all objects that has the selected modifier [else] collect all objects of the same class
					if theNode.parent.text == "Modifiers" or theNode.parent.text == "SpaceWarp Modifiers" then arr = for i in objects where (findItem (for f in i.modifiers collect classOf f) selectedNode != 0) and inGroup i collect i
					else
					(
						local objSet = execute theNode.parent.text, theClass = execute nodeName
						arr = for i in objSet where classOf i.baseObject == theClass and inGroup i collect i
					)
				)
				with undo "Massive" on
				(
					case of
					(
						--	If a node is ctrl clicked, select superclass or class
						(keyboard.controlPressed): if arr.count != 0 then select arr else max select none
						--	If a node is alt clicked, deselect superclass or class
						(keyboard.altPressed): deselect arr
						--	If a node is alt clicked, add superclass or class to selection
						(keyboard.shiftPressed): selectMore arr
						--	Expand/collapse superclass or class node
						default: tv.nodes[theNode.index].expanded = not tv.nodes[theNode.index].expanded
					)
				)
			)
			else
			(
				--	Store class (parent) index of the selected property node for classArr
				pIndex = theNode.parent.index - superArr.count
				--	Store property index of the selected property node for classArr
				cIndex = findItem classArr[pIndex] theNode.text
				--	Update flag that lets the appropriate controls to be shown based on selected property node's class
				local doUpdate = true
				--	P is the object used for getProperty <object> <property>
				local p = classArr[pIndex]
				--	If p is deleted, set it to another object of the same class, if not found, create treeview nodes from scratch and set doUpdate to false, to prevent updating controls
				if isDeleted p[p.count] and (p[p.count] = (for i in objects where classOf i == p[1] collect i)[1]) == undefined do (createTVNodes true; doUpdate = false)
				--	If ok to update, do so based on selected property node's class
				if doUpdate do updateControls (classOf (getProperty p[p.count] p[cIndex]))
			)
		)
		
		on noproperty changed pState do noproperty.checked = true
		
		on useRandom changed pState do
		(
			#(toInt, toFloat, toColor).enabled = pState
			--	If the currently visible control is boolean type, and useRandom is checked, set fromBoolean to true and show toBoolean (which is set to false and can't be changed), as there is no point in changing them for random values (2 possibility, true or false)
			if fromBoolean.visible do
			(
				fromBoolean.changed (fromBoolean.checked = true); fromBoolean.enabled = not pState
				if ops[10] do setValues()
			)
		)
		
		on setProperties pressed do setValues()
		
		local filtered = false
		on filterProperties changed pState do
		(
			local sign = if (filter.visible = pState) then 1 else -1
			tv.pos.y += sign * 20
			if pState then (setFocus filter; filtered = false)
			else
			(
				filter.text = ""
				if filtered do (superArr = cacheArr[1]; classArr = cacheArr[2]; createTVNodes false)
				if pIndex != 0 do (tv.nodes[pIndex + superArr.count].selected = true; tv.nodes[tv.nodes[pIndex + superArr.count].child.index + cIndex - 2].selected = true)
				setFocus tv
			)
		)
		
		on filter entered txt do
		(
			if txt != "" and filterProperties.checked do
			(
				--	Remove invalid characters from txt for convenience
				txt = (filter.text = C4.removeInvalidChars txt)
				--	If the nodes aren't filtered before, store unfiltered arrays (superArr, classArr) into cacheArr [else] restore the unfiltered arrays (superArr, classArr) and create treeview nodes using them
				if filtered then (superArr = C4.copyArr cacheArr[1]; classArr = C4.copyArr cacheArr[2]; createTVNodes false) else (filtered = true; cacheArr = #(C4.copyArr superArr, C4.copyArr classArr))
				--	Loop through property nodes and delete the ones whose name doesn't match the filter string
				for i = tv.nodes.count to superArr.count + classArr.count + 1 by -1 where findString tv.nodes[i].text txt == undefined do tv.nodes.remove i
				--	Loop through class nodes and delete the ones that has no children (no property matches the filter string)
				for i = superArr.count + classArr.count to superArr.count + 1 by -1 where tv.nodes[i].children == 0 do (tv.nodes.remove i; deleteItem classArr (i - superArr.count))
				--	Loop through superclass nodes and delete the ones that has no children (no children has any property that matches the filter string)
				for i = superArr.count to 1 by -1 where tv.nodes[i].children == 0 do (tv.nodes.remove i; deleteItem superArr i)
				hideControls(); updateTitle()
			)
		)
		
		on showOptions pressed do popupMenu massiveRC pos:(showOptions.pos + [16,-1]) rollout:massive
		
		local focused = false
		on timeX tick do
		(
			local m = mouse.screenPos, dPos = getDialogPos massive
			--	If mouse is inside the unfocused dialog and autofocus option is turned on, set focus to dialog. If filter properties is checked, set focus to filter edit text instead
			if m.x >= dPos.x and m.x <= dPos.x + massive.width + 7 and m.y >= dPos.y and m.y <= dPos.y + massive.height + 26 then (if not focused and ops[2] do (if filterProperties.checked then setFocus filter else setFocus tv); focused = true) else focused = false
			--	If mouse is inside the dialog and filter properties is checked, pressing esc turns off the filter
			if keyboard.escPressed and focused and filterProperties.checked do filterProperties.changed (filterProperties.checked = false)
		)
		
		--	If interactive option (ops[10]) is used, change object properties immediately
		
		on fromInt entered do if ops[10] do setValues()
		on fromFloat entered do if ops[10] do setValues()
		
		on fromBoolean changed pState do
		(
			if pState then fromBoolean.text = "True" else fromBoolean.text = "False"
			if ops[10] do setValues()
		)
		
		on fromColor changed clr do if ops[10] do setValues()
		on toInt entered do if ops[10] do setValues()
		on toFloat entered do if ops[10] do setValues()
		on toColor changed clr do if ops[10] do setValues()
		
		on massive close do
		(
			local dPos = getDialogPos massive
			if dPos.x >= 0 and dPos.y >= 0 do ops[4] = getDialogPos massive
			ops[5].y = (getDialogSize massive).y
			setINISetting (getMAXINIFile()) "Light" "Massive" (ops as string)
			isOpen = false; updateToolbarButtons()
		)
	)
	
	rcMenu massiveRC
	(
		fn setOps n = (massive.ops[n] = not massive.ops[n]; massive.updateRC())
		fn dock0 = massive.ops[3] != 0; fn dock1 = massive.ops[3] == 0 or massive.ops[3] == 1; fn dock2 = massive.ops[3] == 0 or massive.ops[3] == 2
		
		menuItem 		allObjects 		"All Objects"
		menuItem 		includeGroups 	"Groups"
		menuItem 		relative 			"Relative"
		menuItem 		interactive 		"Interactive"
		seperator 		s01
		menuItem 		autoLoad 		"Auto Load"
		menuItem 		autoFocus 		"Auto Focus"
		menuItem 		dockLeft 			"Dock Left" 		filter:dock2
		menuItem 		dockRight 		"Dock Right" 		filter:dock1
		menuItem 		undockDialog 		"Float" 			filter:dock0
		seperator 		s02
		menuItem 		quit 				"Exit"
		
		on allObjects picked do setOps 7
		on includeGroups picked do setOps 8
		on relative picked do setOps 9
		on interactive picked do setOps 10
		on autoLoad picked do setOps 1
		on autoFocus picked do setOps 2
		on dockLeft picked do C4.dockDialog massive &massive.ops[3] 1 0
		on dockRight picked do C4.dockDialog massive &massive.ops[3] 2 0
		on undockDialog picked do (C4.undockDialog massive &massive.ops[3] massive.ops[4] [massive.ops[5].x, massive.ops[5].y - 22]; massive.updateTitle())
		on quit picked do (if massive.ops[3] != 0 do C4.undockDialog massive &massive.ops[3] massive.ops[4] [massive.ops[5].x, massive.ops[5].y - 22] save:false; destroyDialog massive)
	)
	registerRightClickMenu massiveRC
	
	fn toggleMassive override = C4.toggleDialog "Massive" massiveRC override
	if C4 != undefined and classOf C4 == structDef and not C4.macroExists "Production Tools" "Massive" do macros.new "Production Tools" "Massive" "Mass!ve" "Mass!ve" "global massive, toggleMassive\non isChecked return massive.isOpen\non execute do toggleMassive true"
	toggleMassive false
)