def findSetFromObject(self, pObject='', quiet=False):
		''' finds the set that an object is in
			Params:
				pObject: object to look for
				quiet: suppress dialogs
			Returns: FBSet/False
		'''
		# get all sets
		allSets = self.scene.Sets
		
		# check for string
		if isinstance(pObject, str):
			pObject = self.getObject(name=pObject, pyMB=False, exact=False, quiet=quiet)
			if not pObject:
				if not quiet:
					moBuLogger.infoDialog("No valid object passed.")
				return False
		
		if not allSets:
			if not quiet:
				moBuLogger.infoDialog("No sets found in scene.")
			return False

		# check sets
		for _set in allSets:
			if self.isInSet(pObject=pObject, pSet=_set):
				moBuLogger.info("Found '%s' in set '%s'" % (pObject.Name, _set.Name))
				return _set
		
		# not found
		if not quiet:
			moBuLogger.infoDialog("'%s' not found in any sets" % pObject.Name)
		return False
	def plotCharacter(self, character=None, nSpace='', quiet=False):
		''' plots character
				Params:
					character: passed FBCharacter object
					nSpace: namespace to search for character
					quiet: suppress message
				Returns: True/False
			'''
		if not character:
			if nSpace:
				character = MoBuSceneCore().getCharacter(hipsJoint='', nSpace=nSpace)
		
		lOptions = self.SetPlotOptions()
		lOptions.PlotTranslationOnRootOnly = True
#		lOptions.PreciseTimeDiscontinuities = True	#?
		
		if character.ActiveInput:
			if quiet:
				# default to choice 1, plot & continue
				character.PlotAnimation(FBCharacterPlotWhere.kFBCharacterPlotOnSkeleton, lOptions)
				character.ActiveInput = False
			else:
				res = FBMessageBox("Control Set Active", "Warning: Control set on %s is Active.\n\nWhat do you want to do?" % character.LongName, "Plot & Continue", "De-activate & Continue", "Cancel", 1, 1)
				if res == 1:
					character.PlotAnimation(FBCharacterPlotWhere.kFBCharacterPlotOnSkeleton, lOptions)
					character.ActiveInput = False
				if res == 2:
					character.ActiveInput = False
				elif res == 3:
					return False
		moBuLogger.info("Plotted to skeleton for character: '%s'" % character.LongName)
		
		return True
	def localRotationAxisToggle(self, pModel='', size=25):
		modelList = []
		#work on everything selected
		selected = self.getSelected()
		if selected:
			modelList = selected 
		else:
			modelList = [pModel]
		if len(modelList) == 1 and modelList[0] == '':
			moBuLogger.info("Nothing selected or passed to function localRotationAxisToggle()")
			return
		
		for pModel in modelList:
#			pModel = self.validate(pModel, FBModelSkeleton)
			if not self.validate(pModel, FBModelSkeleton):
				moBuLogger.error("'%s' is not type FBModelSkeleton" % pModel)
			pModel = self.getObject(pModel, pyMB=True)
			if pModel:
				vis = True
				if pModel.GetPropertyValue('Show Rotation Axis'):
					vis = False
				
				pModel.SetPropertyValue('Show Rotation Axis', vis)
				pModel.SetPropertyValue('Pivot Visibility', 2)
				pModel.SetPropertyValue('Pivot Size', size)
		
		# for unitTests
		return True
	def zeroTimeline(self, character=None):
		''' moves timeline to zero with character animation
			Params:
				character: FBcharacterNode
			Returns: True/False
		'''
		
		# find start of current take
		currentTake = FBSystem().CurrentTake
		currentTimeSpan = currentTake.LocalTimeSpan
		currentTakeStartTimeInt = int(currentTimeSpan.GetStart().GetTimeString())
		
		# check timeline
		if currentTakeStartTimeInt == 0:
			# abort
			moBuLogger.warning("No need to run zeroTimeline, first frame is already 0.")
			return False
		
		# find stop and offset
		currentTakeStopTimeInt = int(currentTimeSpan.GetStop().GetTimeString())
		offset = FBTime(0, 0, 0, -1 * currentTakeStartTimeInt)
		
		# create track
		lTrackContainer = FBStoryTrack(FBStoryTrackType.kFBStoryTrackCharacter)
#		lTrackContainer = FBStoryTrack(FBStoryTrackType.kFBStoryTrackAnimation)

		# find character		
		if not character:
			character = MoBuSceneCore().getCharacter(hipsJoint='')
		if not character:
			moBuLogger.error("Failed to find character.")
		
		# plot to control rig		
		if not character.GetCurrentControlSet():
			character.CreateControlRig(True) 
		character.PlotAnimation(FBCharacterPlotWhere.kFBCharacterPlotOnControlRig, self.SetPlotOptions())
		
		# assign character to track
		lTrackContainer.Character = character
		
		# create clip, and offset it
		lTrackContainer.CopyTakeIntoTrack(currentTimeSpan, currentTake, offset)
		
		# move timeline
		newTimeSpan = FBTimeSpan()
		newTimeSpan.Set(FBTime(0, 0, 0, 0), FBTime(0, 0, 0, currentTakeStopTimeInt - currentTakeStartTimeInt))
		currentTake.LocalTimeSpan = newTimeSpan
		
		# plot back down
		self.plotCharacter(character=character, nSpace='', quiet=True)
		
		# nuke story track
		lTrackContainer.FBDelete()
		
		moBuLogger.info("Zeroed-out timeline.")
		
		return True
 def newButtonCallback(self, control, event):
     moBuLogger.info("Button '%s' pressed" % control.Name)
     btn, value = FBMessageBoxGetUserValue("New Set",
                                           "Type name of new set", "mySet",
                                           FBPopupInputType.kFBPopupString,
                                           "OK", "Cancel", None, 1, True)
     if btn == 2:
         return
     self.lst.Items.append(value)
	def removeAllNamespacesFromScene(self):
		try:
			for lComp in FBSystem().Scene.Components:
				# This function is a recursive function that will go through the whole hierarchy to add or replace the prefix
				lComp.ProcessNamespaceHierarchy (FBNamespaceAction.kFBRemoveAllNamespace   , '', '', False)
			moBuLogger.info("Removed all namespaces")
			return True
		except:
			moBuLogger.info("Failed to removed all namespaces")
			return False
 def removeAllNamespacesFromScene(self):
     try:
         for lComp in FBSystem().Scene.Components:
             # This function is a recursive function that will go through the whole hierarchy to add or replace the prefix
             lComp.ProcessNamespaceHierarchy(
                 FBNamespaceAction.kFBRemoveAllNamespace, '', '', False)
         moBuLogger.info("Removed all namespaces")
         return True
     except:
         moBuLogger.info("Failed to removed all namespaces")
         return False
    def loadPCSoptions(self, pathFile=None, quiet=False, pOptions=None):
        """ saves with customFBFbxOptions
		Params:
			pathFile: complete file path to save
			quiet: suppress messages
			pOptions: pre-made options
		Returns: True/False
		"""

        # pick file if not passed
        if not pathFile:
            pathFile = self.openFileDialog(openSave="open")

        # 		# Check for binary
        # 		lFbp = FBProgress()
        # 		lFbp.ProgressBegin()
        # 		lFbp.Caption = "Checking binary on %s" % pathFile.basename
        # 		lFbp.Percent = 50
        # 		if Path(pathFile).isbin:
        # 			moBuLogger.warning("Skipping file '%s' because detected binary" % pathFile)
        # 			lFbp.ProgressDone()
        # 			return False
        # 		moBuLogger.debug("Checked '%s' for binary, passed." % pathFile)
        #
        # 		lFbp.ProgressDone()

        # check for cancel
        if not pathFile:
            moBuLogger.info("Cancelled open via loadPCSoptions()")
            return False

        if not pOptions:
            pOptions = self.customFBFbxOptions(pLoad=True)

            # by default, load all takes
        success = self.app.FileOpen(
            str(pathFile), 1 - quiet, self.customFBFbxOptions(pLoad=1, saveAllTakes=1, allElements=1)
        )
        if success:
            moBuLogger.info("Success opening '%s'" % pathFile)
        else:
            moBuLogger.error("Failed to open '%s'" % pathFile)

        # 		# check for hips
        # 		ref = self.getObject("Reference")
        # 		if ref:
        # 			if len(ref.Children) > 0:
        # 				if not ref.Children[0].Name == 'Hips':
        # 					moBuLogger.infoDialog("WARNING: No Hips found in scene. Check your joint names.", "Joints Missing")

        return success
	def createNote(self, name='', content='', attach=''):
		''' BUG: HAVE to assign result to variable!
			create FBNote
			Params:
				name: name of note
				content: content of note
			Returns: FBNote/False
		'''
		# see if note already exists
		ourNote = self.getObjectFromWildcardName(pattern=name, byName=True, returnLongNames=False, byType=FBNote)
		if ourNote:
			moBuLogger.info("Found existing FBNote: '%s'" % name)
			ourNote.StaticComment = content
			moBuLogger.info("Updating content to: '%s'" % content)
		else:
			ourNote = FBNote(name)
			moBuLogger.info("Creating new FBNote: '%s'" % name)
			ourNote.StaticComment = content
			moBuLogger.info("Setting content to: '%s'" % content)
		
		# add to scene
		if attach:
			result = ourNote.Attach(attach)
		else:
			result = ourNote.Attach()
		if not result:
			return False
		
		return ourNote
	def jpExportPropertyList (self, _object):
		logFile = '#This is the propertyList for object ' + _object.Name + '\n\n['
	
		propList = _object.PropertyList
		#list properties
		for _property in propList:
			#print character.Name
			#print _property.GetProperty
			if _property.GetName():
				logFile = logFile + _property.GetName()
			if _property.GetPropertyTypeName():
				logFile = logFile + ',' + _property.GetPropertyTypeName() + ']\n['
		
		moBuLogger.info(logFile)
	def listTakeNamesFromFile(self, targetFilePath):
		''' prints and returns list of all Takes from a file
		Params:
			targetFilePath: file to get take names from 
		Returns: [list] of take names
		'''
		takeNames = []
		if not os.path.exists(targetFilePath):
			moBuLogger.error("'%s' does not exist")
		pFbxOptionsLoad = FBFbxOptions(1, targetFilePath)
		for idx in range(0, pFbxOptionsLoad.GetTakeCount()):
			moBuLogger.info(pFbxOptionsLoad.GetTakeName(idx))
			takeNames.append(pFbxOptionsLoad.GetTakeName(idx))
		return takeNames
    def fixThis(self, message=""):
        """ sends scene and email to T.A.
			Params:
				message: passed message for email
			Returns: True/False
		"""
        successes = 0

        # 1. Note currentScene name for reload
        currentScene = self.sceneName

        # 2. Enter notes
        if not message:
            result = FBMessageBoxGetUserValue(
                "Send Message",
                "Can you tell me what you were doing?",
                "Nothing to say",
                FBPopupInputType.kFBPopupString,
                "Send scene",
                "Cancel",
            )
            if result[0] == 1:
                message = result[1]
            elif result[0] == 2:
                moBuLogger.info("Cancelled mbCore.fixThis()")
                return False

                # 3. Save to network location
        savePath = Path("%s/data/%s/fixThis" % (gv.schemaLocation, getpass.getuser()))
        saveFilePath = "%s/%s" % (savePath, Path(currentScene).basename())

        # make dir
        if not os.path.exists(savePath):
            os.makedirs(savePath)

        if self.savePCSoptions(pathFile=saveFilePath, quiet=True, pOptions=None, p4=False):
            moBuLogger.info("Saved scene to: '%s'" % saveFilePath)
            successes += 1
        else:
            moBuLogger.info("Failed to save scene to: '%s'" % saveFilePath)
            return False

            # 4. Email TechArtist
            # TODO: email message
        _file = open(saveFilePath.replace(".fbx", ".txt"), "w")
        _file.write(message)
        _file.close()

        # 5. Reload original scene
        if self.loadPCSoptions(pathFile=currentScene, quiet=False, pOptions=None):
            successes += 1
        else:
            moBuLogger.info("Failed to reload original scene: '%s'" % currentScene)

        if successes == 2:
            return True
        else:
            return False
	def isInSet(self, pObject='', pSet=''):
		''' see if object is in set
			Params:
				pObject: object to look for
				pSet: set to look in
			Returns: True/False
		'''			
		if not isinstance(pSet, FBSet):
			moBuLogger.error("'%s' is not of type(set)" % pSet)
			return False
		if not pObject:
			moBuLogger.error("No pObject passed.")
		
		# check if pObject is str
		if isinstance(pObject, str):
			#check directly
			for item in pSet.Items:
				if item.Name == pObject:
					moBuLogger.info("Found '%s' in '%s'" % (item.Name, pSet.Name))
					return True
		else:
			#check directly
			for item in pSet.Items:
				if item == pObject:
					moBuLogger.info("Found '%s' in '%s'" % (item.Name, pSet.Name))
					return True 
		
		# not found
		if isinstance(pObject, str):
			pObjectName = pObject
		else:
			pObjectName = pObject.LongName
		moBuLogger.info("Did not find '%s' in '%s'" % (pObjectName, pSet.Name))
		return False
	def moveAnimCurve(self, pModel, trans=True, rot=None, scale=None, x=None, y=None, z=None, _all=False):
		''' made to work on FBModelSkeleton, FBModelRoot
			default will move curves so first frame of curve is 0
			TODO: if int passed to an axis, will move curve so first frame
			of curve is passed int
		Params:
			
		Returns: True if moved a Key
		'''
		
		indexes = []
		if _all:
			x, y, z, = True, True, True
		
		moved = False
		
		pModel = self.getObject(pModel)
		
		# Translation
		if x:
			# validate has a curve
			if self.hasAnimCurves(pModel, 'x'):
				indexes.append(0)
				moBuLogger.info("Moved animCurve '%s.Translation.X' to 0 at frame 0" % pModel.Name)
		if y:
			if self.hasAnimCurves(pModel, 'y'):
				indexes.append(1)
				moBuLogger.info("Moved animCurve '%s.Translation.Y' to 0 at frame 0" % pModel.Name)
		if z:
			if self.hasAnimCurves(pModel, 'z'):
				indexes.append(2)
				moBuLogger.info("Moved animCurve '%s.Translation.Z' to 0 at frame 0" % pModel.Name)
		
		for axis in indexes:
			transAnimNode = None
			if isinstance(pModel, PMBModel):
				transAnimNode = pModel.GetAnimationNode()
			else:
				transAnimNode = pModel.Translation.GetAnimationNode()
			if transAnimNode.Nodes[axis].FCurve:
				if transAnimNode.Nodes[axis].FCurve.Keys:
					fFirstValAxis = transAnimNode.Nodes[axis].FCurve.Keys[0].Value
					for key in transAnimNode.Nodes[axis].FCurve.Keys:
						fKeyVal = key.Value 
						key.Value = (fKeyVal - fFirstValAxis)
						moved = True
		
		#TODO: rot and scale	
		# Rotation
		indexes = None
		if x:
			# validate has a curve
			if self.hasAnimCurves(pModel, 'x', 'R'):
				indexes = 0
				
		# Scale
		
		# for unitTests
		return moved
    def mergePCSoptions(self, pathFile="", quiet=True, pOptions=None):
        """ merges with customFBFbxOptions
		Params:
			pathFile: complete file path to merge
			quiet: suppress messages
			pOptions: pre-made options
		Returns: True/False
		"""
        if not pathFile:
            pathFile = self.openFileDialog()

        if not pOptions:
            pOptions = self.customFBFbxOptions(pLoad=True, saveAllTakes=True, allElements=True)

            # by default, do NOT merge takes
        if self.app.FileMerge(str(pathFile), 1 - quiet, pOptions):
            # report
            if not quiet:
                moBuLogger.info("Merged %s with PCSoptions" % pathFile)
            return True
        else:
            moBuLogger.error("Failed to merge '%s'" % pathFile)
            return False
    def cleanDirs(self, folderPath="", dirExtension=".fbm"):
        """ Cleans viral .bck/.fbm folders from given folderPath
			Params:
				folderPath: director to clean
				dirExtension: extension to look for on end of dir
			Returns: True if cleaned any
			"""
        deleted = False
        if not folderPath:
            moBuLogger.error("No folderPath arg passed.")
            return False

        for root, dirs, unused in os.walk(folderPath):
            for _dir in dirs:
                dirPath = os.path.join(root, _dir)
                if Path(dirPath).isdir:
                    if re.search(dirExtension, dirPath):
                        try:
                            shutil.rmtree(dirPath)
                            deleted = True
                        except WindowsError:
                            moBuLogger.info("Failed to remove '%s'" % dirPath)
        return deleted
    def findSetFromObject(self, pObject='', quiet=False):
        ''' finds the set that an object is in
			Params:
				pObject: object to look for
				quiet: suppress dialogs
			Returns: FBSet/False
		'''
        # get all sets
        allSets = self.scene.Sets

        # check for string
        if isinstance(pObject, str):
            pObject = self.getObject(name=pObject,
                                     pyMB=False,
                                     exact=False,
                                     quiet=quiet)
            if not pObject:
                if not quiet:
                    moBuLogger.infoDialog("No valid object passed.")
                return False

        if not allSets:
            if not quiet:
                moBuLogger.infoDialog("No sets found in scene.")
            return False

        # check sets
        for _set in allSets:
            if self.isInSet(pObject=pObject, pSet=_set):
                moBuLogger.info("Found '%s' in set '%s'" %
                                (pObject.Name, _set.Name))
                return _set

        # not found
        if not quiet:
            moBuLogger.infoDialog("'%s' not found in any sets" % pObject.Name)
        return False
	def getDirectoryList(self, defaultFolder, captionTitle='FolderSelect', returnList=1):
		"""
		SYNOPSIS
		  Return list of files from selected directory or just directory path
		
		INPUTS
		 (string) defaultFolder:	default directory path
		 (string) captionTitle:		Window title
		 (bool) returnList:			1 = returnList of files in folder, 0 = return folderName
	
		RETURNS: (string) path
					or
				(list) [path, files]
		"""
		
		#Get folder from current scene
		fullFileName = FBApplication().FBXFileName.replace('\\', '/')
		
		text = "Current Scene w/ path is: %s" % fullFileName
		moBuLogger.info(text)
		
		pathParts = fullFileName.split('/')
		fileNameOnly = pathParts[len(pathParts) - 1]
		currentFolder = fullFileName.replace(fileNameOnly, "")
		if not currentFolder: currentFolder = str(ParseSchema(fullFileName).characterRoot)
	
		text = "currentFolder is: %s" % currentFolder
		moBuLogger.info(text)
		
		#Build GUI
		folderWindow = FBFolderPopup()
		folderWindow.Caption = "%s: Select a folder for BatchProcessing" % captionTitle
		
		
		if not defaultFolder: defaultFolder = currentFolder
		folderWindow.Path = str(defaultFolder)
		
		#if window executes
		fileList = []
		if folderWindow.Execute():
			
			#iterate through each file
			fileList = listdir(folderWindow.Path)
		
		#assert(len(fileList)), logger.info('No directory selected')
		if not len(fileList): moBuLogger.info('No directory selected')
		
		if not returnList: return folderWindow.Path.replace('\\', '/')
		return [folderWindow.Path.replace('\\', '/'), fileList]
    def isInSet(self, pObject='', pSet=''):
        ''' see if object is in set
			Params:
				pObject: object to look for
				pSet: set to look in
			Returns: True/False
		'''
        if not isinstance(pSet, FBSet):
            moBuLogger.error("'%s' is not of type(set)" % pSet)
            return False
        if not pObject:
            moBuLogger.error("No pObject passed.")

        # check if pObject is str
        if isinstance(pObject, str):
            #check directly
            for item in pSet.Items:
                if item.Name == pObject:
                    moBuLogger.info("Found '%s' in '%s'" %
                                    (item.Name, pSet.Name))
                    return True
        else:
            #check directly
            for item in pSet.Items:
                if item == pObject:
                    moBuLogger.info("Found '%s' in '%s'" %
                                    (item.Name, pSet.Name))
                    return True

        # not found
        if isinstance(pObject, str):
            pObjectName = pObject
        else:
            pObjectName = pObject.LongName
        moBuLogger.info("Did not find '%s' in '%s'" % (pObjectName, pSet.Name))
        return False
    def createTool(self):
        moBuLogger.info('----------------------------------------')
        moBuLogger.info('|     Running Art Monkey v:%s     |' % _VERSION)
        moBuLogger.info('|     Build #:%s     |' % self.buildNumber)
        moBuLogger.info('----------------------------------------')

        pcsGlobalToolName = "Art Monkey"
        tool = FBCreateUniqueTool(pcsGlobalToolName)
        tool.StartSizeX = 610
        tool.StartSizeY = 185

        # Layout for the controls
        x = FBAddRegionParam(5, FBAttachType.kFBAttachLeft, "")
        y = FBAddRegionParam(0, FBAttachType.kFBAttachNone, "")
        w = FBAddRegionParam(50, FBAttachType.kFBAttachRight, "")
        h = FBAddRegionParam(85, FBAttachType.kFBAttachNone, "")
        tool.AddRegion("main", "main", x, y, w, h)

        # Add grid layout
        grid = FBGridLayout()
        tool.SetControl("main", grid)

        col = 0

        logo = FBButton()
        logo.SetImageFileNames('%s/logo_small.jpg' % self.pcsImagePath)
        logo.Caption = "yeah"
        logo.OnClick.Add(self.openToolDocs)
        grid.AddRange(logo, 0, 1, col, col)
        grid.SetColWidth(col, 70)

        v = FBLabel()
        v.Caption = 'v:%s' % _VERSION
        v.Style = FBTextStyle.kFBTextStyleItalic
        v.Justify = FBTextJustify.kFBTextJustifyCenter
        grid.Add(v, 2, col)
        col += 1

        # create load button
        loadB = FBButton()
        loadB.Caption = "Load"
        loadB.Look = FBButtonLook.kFBLookColorChange
        loadB.OnClick.Add(self.KToolsCallback)

        # create save button
        saveB = FBButton()
        saveB.Caption = "Save"
        saveB.Look = FBButtonLook.kFBLookColorChange
        saveB.OnClick.Add(self.KToolsCallback)

        # create saveAs button
        saveAsB = FBButton()
        saveAsB.Caption = "SaveAs"
        saveAsB.Look = FBButtonLook.kFBLookColorChange
        saveAsB.OnClick.Add(self.KToolsCallback)

        # add buttons to row 1,2,3 column 1
        grid.Add(loadB, 0, col)
        grid.Add(saveB, 1, col)
        grid.Add(saveAsB, 2, col)

        # want to fix the width of column 1 and 2 so the buttons are of a normal size
        grid.SetColWidth(col, 40)

        col += 1

        # create tab
        tab = FBTabControl()
        # we want the tab to span from row 0 to row 3 and from column 2 to column 2
        grid.AddRange(tab, 0, 3, col, col)

        # set the spacing between col0 and col1
        grid.SetColSpacing(col, 20)

        # now assign the rows and columns attributes
        # Fixed the height of row 0 and row 2 so the label and the buttons have a normal height
        grid.SetRowHeight(0, 20)
        grid.SetRowHeight(1, 20)
        grid.SetRowHeight(2, 20)
        grid.SetRowHeight(3, 70)

        # 1. Build a construction dictionary with {folder=[files]}
        self.menuDict, ext = self.getMenuDic()

        # 1.5 add top menu path for Load, Save, SaveAs
        #		menuTop = self.menuDict.keys()[0]
        if not Path.modulePath(self.mobuMenuPath):
            sys.path.append(self.mobuMenuPath)

        # 2. Sorted Keys list
        sKeys = []
        for dr in self.menuDict.iterkeys():
            sKeys.append(dr)
        sKeys.sort()

        for menu in sKeys:

            menuName = str(Path(menu).basename())
            # skip root and 'old'
            if not menuName == 'old' and not menuName == 'menu':
                tabLayout = FBGridLayout()
                #				lyt.default_space = 5
                tabLayout.SetRegionTitle("My Title", "Title")

                x = FBAddRegionParam(10, FBAttachType.kFBAttachLeft, "")
                y = FBAddRegionParam(20, FBAttachType.kFBAttachTop, "")
                w = FBAddRegionParam(140, FBAttachType.kFBAttachRight, "")
                h = FBAddRegionParam(75, FBAttachType.kFBAttachBottom, "")
                tabLayout.AddRegion(menuName, menuName, x, y, w, h)

                # must add dir to sys.path so imp can find it
                if not Path.modulePath(menu):
                    sys.path.append(menu)

                row = 0
                column = 0
                colWidth = 0
                colMax = {0: 0, 1: 0, 2: 0, 3: 0, 4: 0}
                for script in self.menuDict[menu]:
                    if not script == '__init__%s' % ext:
                        toolName = str(Path(script).namebase)
                        lTool = FBButton()
                        lTool.Caption = toolName
                        lTool.Justify = FBTextJustify.kFBTextJustifyCenter

                        # Make "Fix this!" red
                        if toolName == 'Fix this!':
                            lTool.Look = FBButtonLook.kFBLookColorChange
                            lTool.SetStateColor(FBButtonState.kFBButtonState0,
                                                mbCore.Red)
                            lTool.SetStateColor(FBButtonState.kFBButtonState1,
                                                mbCore.Red)

                        # this callBack is active and all buttons will run this "last" menuModule.run()
                        lTool.OnClick.Add(self.KToolsCallback)
                        tabLayout.Add(lTool, row, column)

                        # make column maximum of all rows
                        colWidth = len(toolName) * 7 + 2
                        if colWidth > colMax[column]:
                            colMax[column] = colWidth

                        tabLayout.SetColWidth(column, colMax[column])
                        tabLayout.SetRowHeight(row, 30)
                        # add button to tabbed sub-layout
                        tabLayout.Add(lTool, row, column)

                        # increment columns, rows
                        column += 1
                        if column > 3:
                            row += 1
                            column = 0

                # add layouts to tabControl with name of dir
                tab.Add(menuName, tabLayout)

        # finish up tab
        tab.SetContent(0)
        tab.TabPanel.TabStyle = 0  # normal tabs
    def savePCSoptions(self, pathFile=None, quiet=True, pOptions=None, p4=True):
        """ saves with customFBFbxOptions
		Params:
			pathFile: complete file path to save
			quiet: suppress messages
			pOptions: pre-made options
			p4: markForAdd/checkout or not
		Returns: True/False
		"""

        text = "Saved with PCSoptions"

        # pick file if not passed
        if not pathFile:
            if not quiet:
                pathFile = self.openFileDialog(openSave="save")
            else:
                moBuLogger.error("No pathFile passed and quiet=True")
                return False
        if not pathFile:
            moBuLogger.info("Cancelled")
            return False

            # add extension if they didn't type it
        if not Path(pathFile).ext:
            pathFile = "%s.fbx" % pathFile

        # 		# checkout from perforce
        # 		if p4:
        # 			if self.pcsParseObj.isp4Active:
        # 				self.p4.fileName = pathFile
        # 				if self.p4.isP4Connected:
        # 					try:
        # 						self.p4.p4CheckOut(desc=text)
        ##					except P4.P4Exception:
        # 					except:
        # 						moBuLogger.warning("Failed to checkout: '%s'" % pathFile)
        # 			else:
        # 				if not quiet:
        # 					moBuLogger.warning('P4Active setting FALSE, not checking out.')
        # 		else:
        # 			if not quiet:
        # 				moBuLogger.warning("p4 arg passed as False, not checking out for file: '%s'." % pathFile)

        if not pOptions:
            pOptions = self.customFBFbxOptions(pLoad=False, saveAllTakes=True)

            # 2010 save process
        currentTakeObject = FBSystem().CurrentTake
        if self.mobuVer == 2010:
            lMgr = FBFbxManager()  # @UndefinedVariable
            lMgr.SaveBegin(str(pathFile))
            lMgr.Selected = True
            for strEach in lMgr.Takes:
                if strEach.Name != currentTakeObject.Name:
                    strEach.Import = False
            lMgr.EmbedMedia = False
            lMgr.BaseCameras = False
            lMgr.CameraSwitcherSettings = False
            lMgr.CurrentCameraSettings = False
            lMgr.GlobalLightingSettings = False
            lMgr.TransportSettings = False
            if not lMgr.Save():
                moBuLogger.errorDialog("There is a problem saving the file", "Cannot Save")
            if not lMgr.SaveEnd():
                moBuLogger.errorDialog("There is a problem saving the file", "Cannot Save")

                # 2012 save process
        elif self.mobuVer == 2012 or self.mobuVer == 2013 or self.mobuVer == 2014:
            alreadyExists = False
            if Path(pathFile).exists():
                alreadyExists = True
            if not self.app.FileSave(str(pathFile), pOptions):
                # cancelled?
                moBuLogger.warning("Cancelled")
                return False
            if not alreadyExists:
                # check to see if new file is there
                res = os.path.exists(str(pathFile))
                if res:
                    if not quiet:
                        moBuLogger.info("%s, '%s'" % (text, str(pathFile)))
                    return True
                else:
                    moBuLogger.errorDialog("Failed to save '%s'" % str(pathFile))
                    return False
            else:
                # TODO: check to see if different?
                if not quiet:
                    moBuLogger.info("%s, '%s'" % (text, str(pathFile)))
                return True
	def addAllToSet(self, name='', quiet=False):
		''' finds the set with name
			Params:
				name: name of set
				quiet: suppress dialogs
			Returns: FBSet/False
		'''
		ourSet = None
		
		# type or select set name if not passed
		if not name:
			# check for selected set
			selected = self.getSelected(_type=FBSet, found=True)
			if selected:
				# test type
				if isinstance(selected, FBSet):
					ourSet = selected
			else:
				# spawn dialogue listing choices
				self.addAllToSetUI()
				return False
		else:
			# check for set existence
			if self.findSet(name=name, quiet=quiet):
				ourSet = self.findSet(name=name, quiet=quiet)
			else:
				# create set
				ourSet = FBSet(name)
			
#		count = 0
#		allItems = []
#		
#		modelSkeletons = self.getAllByType(_type='FBModelSkeleton')
#		allItems.extend(modelSkeletons)
#		
#		models = self.getAllByType(_type='FBModel')
#		allItems.extend(models)
#		
#		modelNulls = self.getAllByType(_type='FBModelNull')
#		allItems.extend(modelNulls)
#		materials = self.getAllByType(_type='FBMaterial')
#		allItems.extend(materials)
#		textures = self.getAllByType(_type='FBTexture')
#		allItems.extend(textures)
#		
#		deformers = self.getAllByType(_type='FBDeformer')
#		allItems.extend(deformers)
#		constraintRelation = self.getAllByType(_type='FBConstraintRelation')
#		allItems.extend(constraintRelation)
#		groups = self.getAllByType(_type='FBGroup')
#		allItems.extend(groups)
#		poses = self.getAllByType(_type='FBPose')
#		allItems.extend(poses)
#		vidClips = self.getAllByType(_type='FBVideoClipImage')
#		allItems.extend(vidClips)
#		characters = self.getAllByType(_type='FBCharacter')
#		allItems.extend(characters)
#		
#		for item in allItems:
#			ourSet.Items.append(item)
#			count+=1
		
		count = 0
		allItems = self.getAllByType(_type='all')
		for item in allItems:
								
			# skip Sets
			if isinstance(item, FBSet):
				continue
				
			# safe to add
			ourSet.Items.append(item)
			count += 1
			
		if not quiet:
			moBuLogger.infoDialog("%d items added to Set '%s'" % (count, ourSet.Name), "Set Created")
		else:
			moBuLogger.info("%d items added to Set '%s'" % (count, ourSet.Name))
		
		return ourSet
	def deleteAll(self, pObjects=[]):
		''' deletes all objects passed in the list
			Params:
				pObjects: list of FBObjects
			Returns: True/False
		'''
		success = True
		
		# try selected if nothing passed
		if len(pObjects) == 0:
			pObjects = self.getSelected(_type='all', found=False)
			if len(pObjects) == 0:
				moBuLogger.warning("Must pass list or have something selected.")
				return False
		
		# covert to python list from FBPropertyListComponent
		pObjectsList = []
		if isinstance(pObjects, FBPropertyListComponent):
			for obj in pObjects:
				pObjectsList.append(obj)
		else:
			pObjectsList = pObjects
		
		# cull FBModelSkeleton type out to delete last
		deleteList = []
		modelSkels = []
		for _object in pObjectsList:
			if not isinstance(_object, FBModelSkeleton):
				deleteList.append(_object)
			else:
				modelSkels.append(_object)
		
		
		# delete everything but FBModelSkeletons
		count = 0
		for obj in deleteList:
			
			try:
				# stupid list and unbound, have to test
				try:
					name = obj.Name	# have to store before deleting
					obj.FBDelete()
					moBuLogger.debug("Deleted: %s" % name)
					count += 1
				except UnboundWrapperError:
					moBuLogger.debug("%s already deleted, skipping." % name)
			except:
				moBuLogger.debug("Failed to delete: %s" % name)
				success = False
		
		##WARNING!! Must delete other objects BEFORE
		##deleting FBModelSkeletons via children first

		# destroy FBModelSkeleton from hier up
		for modelSkel in modelSkels:
			try:
				# stupid list and unbound, have to test
				try:
					name = modelSkel.Name	# have to store before deleting
					modelSkel.FBDelete()
					moBuLogger.debug("Deleted: %s" % name)
					count += 1
				except UnboundWrapperError:
					moBuLogger.debug("%s already deleted, skipping." % name)
			except:
				moBuLogger.debug("Failed to delete: %s" % name)
				success = False
		
		moBuLogger.info("Deleted '%d' objects" % count)
		
		# clean
		del(pObjectsList, pObjects)
			
		return success
	for dr in menuDict.iterkeys():
		sKeys.append(dr)
	sKeys.sort()
	
	count = 0
	for menuNamePath in sKeys:
		menuName = str(Path(menuNamePath).basename())
		# skip root and 'old'
		if not menuName == 'old' and not menuName == 'mobuMenu':
			subMenu = FBGenericMenu()
			i = 0
			for script in menuDict[menuNamePath]:
				if not script == '__init__%s' % ext:
					toolName = str(Path(script).namebase)
					subMenu.InsertFirst(toolName, i)
					i+=1
					
			subMenu.OnMenuActivate.Add(KToolsCallbackSubMenu)
			menu.InsertLast(menuName, 101+count, subMenu)
			count+=1
else:
	moBuLogger.info("ArtMonkey menu already found.")
###################################################################

	
if __name__ == "__builtin__":
#	MoBuToolsMenu().createTool()
	pass
else:
	print "moBu.pcsGlobalMenu imported"
    sKeys = []
    for dr in menuDict.iterkeys():
        sKeys.append(dr)
    sKeys.sort()

    count = 0
    for menuNamePath in sKeys:
        menuName = str(Path(menuNamePath).basename())
        # skip root and 'old'
        if not menuName == 'old' and not menuName == 'mobuMenu':
            subMenu = FBGenericMenu()
            i = 0
            for script in menuDict[menuNamePath]:
                if not script == '__init__%s' % ext:
                    toolName = str(Path(script).namebase)
                    subMenu.InsertFirst(toolName, i)
                    i += 1

            subMenu.OnMenuActivate.Add(KToolsCallbackSubMenu)
            menu.InsertLast(menuName, 101 + count, subMenu)
            count += 1
else:
    moBuLogger.info("ArtMonkey menu already found.")
###################################################################

if __name__ == "__builtin__":
    #	MoBuToolsMenu().createTool()
    pass
else:
    print "moBu.pcsGlobalMenu imported"
		#--------------------------------------------
		# add PyMoBu path
		pyMoBuVer = eval(pcsXMLcore.get('pyMoBuVer'))['%s' % version]
		sys.path.append('%s/python/moBu/site-packages/%s' % (gv.toolsLocation, pyMoBuVer))
				
		#--------------------------------------------
		# start debugging
		import common.diagnostic.wingdbstub #@UnusedImport
		
		#--------------------------------------------
		from common.diagnostic.pcsLogger import moBuLogger
		# do main import loop
		try:
			from moBu.core.sysGlobalMenu import MoBuToolsMenu
		except:
			moBuLogger.info(sys.exc_info())
			moBuLogger.errorDialog("Failed to import moBu and start ArtMonkey")
			print "Failed to import moBu. Error: "
			print sys.exc_info()
		
		#--------------------------------------------
		# start ArtMonkey Global menu
		try:
			MoBuToolsMenu().createTool()
		except:
			moBuLogger.error("Failed to create Art Monkey menu")
		
		#--------------------------------------------
		# report run from location
		def tempFunc():
			pass
	def newButtonCallback(self, control, event):
		moBuLogger.info("Button '%s' pressed" % control.Name)
		btn, value = FBMessageBoxGetUserValue("New Set", "Type name of new set", "mySet", FBPopupInputType.kFBPopupString, "OK", "Cancel", None, 1, True)
		if btn == 2:
			return
		self.lst.Items.append(value)
        # add PyMoBu path
        pyMoBuVer = eval(pcsXMLcore.get('pyMoBuVer'))['%s' % version]
        sys.path.append('%s/python/moBu/site-packages/%s' %
                        (gv.toolsLocation, pyMoBuVer))

        #--------------------------------------------
        # start debugging
        import common.diagnostic.wingdbstub  #@UnusedImport

        #--------------------------------------------
        from common.diagnostic.pcsLogger import moBuLogger
        # do main import loop
        try:
            from moBu.core.sysGlobalMenu import MoBuToolsMenu
        except:
            moBuLogger.info(sys.exc_info())
            moBuLogger.errorDialog("Failed to import moBu and start ArtMonkey")
            print "Failed to import moBu. Error: "
            print sys.exc_info()

        #--------------------------------------------
        # start ArtMonkey Global menu
        try:
            MoBuToolsMenu().createTool()
        except:
            moBuLogger.error("Failed to create Art Monkey menu")

        #--------------------------------------------
        # report run from location
        def tempFunc():
            pass
 def goButtonCallback(self, control, event):
     moBuLogger.info("Button '%s' pressed" % control.Name)
     # create set
     newSet = FBSet(self.listSelection)
     moBuLogger.debug("Result of set creation is '%s'" % newSet)
     self.addAllToSet(name=newSet.Name, quiet=False)
	def allNodesByClassName(self):
		allComps = FBSystem().Scene.Components
		for node in allComps:
			moBuLogger.info("%s  <---type	 %s  <--LongName" % (node.ClassName(), node.LongName))
		
		return allComps
	def goButtonCallback(self, control, event):
		moBuLogger.info("Button '%s' pressed" % control.Name)
		# create set
		newSet = FBSet(self.listSelection)
		moBuLogger.debug("Result of set creation is '%s'" % newSet)
		self.addAllToSet(name=newSet.Name, quiet=False)
	def createTool(self):
		moBuLogger.info('----------------------------------------')
		moBuLogger.info('|     Running Art Monkey v:%s     |' % _VERSION)
		moBuLogger.info('|     Build #:%s     |' % self.buildNumber)
		moBuLogger.info('----------------------------------------')
		
		pcsGlobalToolName = "Art Monkey"
		tool = FBCreateUniqueTool(pcsGlobalToolName)
		tool.StartSizeX = 610
		tool.StartSizeY = 185
		
		# Layout for the controls
		x = FBAddRegionParam(5, FBAttachType.kFBAttachLeft, "")
		y = FBAddRegionParam(0, FBAttachType.kFBAttachNone, "")
		w = FBAddRegionParam(50, FBAttachType.kFBAttachRight, "")
		h = FBAddRegionParam(85, FBAttachType.kFBAttachNone, "")
		tool.AddRegion("main", "main", x, y, w, h)
		
		# Add grid layout
		grid = FBGridLayout()
		tool.SetControl("main", grid)
		
		col = 0
		
		logo = FBButton()
		logo.SetImageFileNames('%s/logo_small.jpg' % self.pcsImagePath)
		logo.Caption = "yeah"
		logo.OnClick.Add(self.openToolDocs)
		grid.AddRange(logo, 0, 1, col, col)
		grid.SetColWidth( col, 70 )
		
		v = FBLabel()
		v.Caption = 'v:%s' % _VERSION
		v.Style = FBTextStyle.kFBTextStyleItalic
		v.Justify = FBTextJustify.kFBTextJustifyCenter
		grid.Add(v, 2, col)
		col +=1
		
		# create load button
		loadB = FBButton()
		loadB.Caption = "Load"
		loadB.Look = FBButtonLook.kFBLookColorChange
		loadB.OnClick.Add(self.KToolsCallback)
		
		# create save button
		saveB = FBButton()
		saveB.Caption = "Save"
		saveB.Look = FBButtonLook.kFBLookColorChange
		saveB.OnClick.Add(self.KToolsCallback)
		
		# create saveAs button
		saveAsB = FBButton()
		saveAsB.Caption = "SaveAs"
		saveAsB.Look = FBButtonLook.kFBLookColorChange
		saveAsB.OnClick.Add(self.KToolsCallback)
		
		# add buttons to row 1,2,3 column 1
		grid.Add(loadB, 0, col)
		grid.Add(saveB, 1, col)
		grid.Add(saveAsB, 2, col)
		
		# want to fix the width of column 1 and 2 so the buttons are of a normal size
		grid.SetColWidth( col, 40 )

		col +=1
				
		# create tab
		tab = FBTabControl()
		# we want the tab to span from row 0 to row 3 and from column 2 to column 2
		grid.AddRange(tab, 0, 3, col, col)
		
		# set the spacing between col0 and col1
		grid.SetColSpacing(col, 20)
		
		# now assign the rows and columns attributes
		# Fixed the height of row 0 and row 2 so the label and the buttons have a normal height
		grid.SetRowHeight(0, 20)
		grid.SetRowHeight(1, 20)
		grid.SetRowHeight(2, 20)
		grid.SetRowHeight(3, 70)
		
		# 1. Build a construction dictionary with {folder=[files]}
		self.menuDict, ext = self.getMenuDic()
		
		# 1.5 add top menu path for Load, Save, SaveAs
#		menuTop = self.menuDict.keys()[0]
		if not Path.modulePath(self.mobuMenuPath):
			sys.path.append(self.mobuMenuPath)
		
		# 2. Sorted Keys list
		sKeys = []
		for dr in self.menuDict.iterkeys():
			sKeys.append(dr)
		sKeys.sort()
		
		for menu in sKeys:
			
			menuName = str(Path(menu).basename())
			# skip root and 'old'
			if not menuName == 'old' and not menuName == 'menu':
				tabLayout = FBGridLayout()
#				lyt.default_space = 5
				tabLayout.SetRegionTitle("My Title", "Title")
				
				x = FBAddRegionParam(10, FBAttachType.kFBAttachLeft, "")
				y = FBAddRegionParam(20, FBAttachType.kFBAttachTop, "")
				w = FBAddRegionParam(140, FBAttachType.kFBAttachRight, "")
				h = FBAddRegionParam(75, FBAttachType.kFBAttachBottom, "")
				tabLayout.AddRegion(menuName, menuName, x, y, w, h)
		
				# must add dir to sys.path so imp can find it
				if not Path.modulePath(menu):
					sys.path.append(menu)
				
				row = 0
				column = 0
				colWidth = 0
				colMax = {0:0, 1:0, 2:0, 3:0, 4:0}
				for script in self.menuDict[menu]:
					if not script == '__init__%s' % ext:
						toolName = str(Path(script).namebase)
						lTool = FBButton()
						lTool.Caption = toolName
						lTool.Justify = FBTextJustify.kFBTextJustifyCenter
						
						# Make "Fix this!" red
						if toolName == 'Fix this!':
							lTool.Look = FBButtonLook.kFBLookColorChange
							lTool.SetStateColor(FBButtonState.kFBButtonState0, mbCore.Red)
							lTool.SetStateColor(FBButtonState.kFBButtonState1, mbCore.Red)
		
						# this callBack is active and all buttons will run this "last" menuModule.run()
						lTool.OnClick.Add(self.KToolsCallback)
						tabLayout.Add(lTool, row, column)
						
						# make column maximum of all rows
						colWidth = len(toolName) * 7 + 2
						if colWidth > colMax[column]:
							colMax[column] = colWidth
						
						tabLayout.SetColWidth(column, colMax[column])
						tabLayout.SetRowHeight(row, 30)
						# add button to tabbed sub-layout
						tabLayout.Add(lTool, row, column)
						
						# increment columns, rows
						column += 1
						if column > 3:
							row += 1
							column = 0
			
				# add layouts to tabControl with name of dir
				tab.Add(menuName, tabLayout)
		
		# finish up tab
		tab.SetContent(0)
		tab.TabPanel.TabStyle = 0 # normal tabs
    def addAllToSet(self, name='', quiet=False):
        ''' finds the set with name
			Params:
				name: name of set
				quiet: suppress dialogs
			Returns: FBSet/False
		'''
        ourSet = None

        # type or select set name if not passed
        if not name:
            # check for selected set
            selected = self.getSelected(_type=FBSet, found=True)
            if selected:
                # test type
                if isinstance(selected, FBSet):
                    ourSet = selected
            else:
                # spawn dialogue listing choices
                self.addAllToSetUI()
                return False
        else:
            # check for set existence
            if self.findSet(name=name, quiet=quiet):
                ourSet = self.findSet(name=name, quiet=quiet)
            else:
                # create set
                ourSet = FBSet(name)


#		count = 0
#		allItems = []
#
#		modelSkeletons = self.getAllByType(_type='FBModelSkeleton')
#		allItems.extend(modelSkeletons)
#
#		models = self.getAllByType(_type='FBModel')
#		allItems.extend(models)
#
#		modelNulls = self.getAllByType(_type='FBModelNull')
#		allItems.extend(modelNulls)
#		materials = self.getAllByType(_type='FBMaterial')
#		allItems.extend(materials)
#		textures = self.getAllByType(_type='FBTexture')
#		allItems.extend(textures)
#
#		deformers = self.getAllByType(_type='FBDeformer')
#		allItems.extend(deformers)
#		constraintRelation = self.getAllByType(_type='FBConstraintRelation')
#		allItems.extend(constraintRelation)
#		groups = self.getAllByType(_type='FBGroup')
#		allItems.extend(groups)
#		poses = self.getAllByType(_type='FBPose')
#		allItems.extend(poses)
#		vidClips = self.getAllByType(_type='FBVideoClipImage')
#		allItems.extend(vidClips)
#		characters = self.getAllByType(_type='FBCharacter')
#		allItems.extend(characters)
#
#		for item in allItems:
#			ourSet.Items.append(item)
#			count+=1

        count = 0
        allItems = self.getAllByType(_type='all')
        for item in allItems:

            # skip Sets
            if isinstance(item, FBSet):
                continue

            # safe to add
            ourSet.Items.append(item)
            count += 1

        if not quiet:
            moBuLogger.infoDialog(
                "%d items added to Set '%s'" % (count, ourSet.Name),
                "Set Created")
        else:
            moBuLogger.info("%d items added to Set '%s'" %
                            (count, ourSet.Name))

        return ourSet