# ScriptsMenu v1.2.1
# Poser's ScriptsMenu as a panel (for Poser 8 and later)
# allows running Python scripts via double click, refreshing of the whole menu or only folders 
# or to copy the path of the script to the clipboard (right click for context menu!)
# - if "Show Lib's Menus" is checked, then all Libraries added to Poser will be checked for ScriptsMenus, too.
# Feel free to redistribute
# - Python coding and by Dizzi (http://www.renderosity.com/homepage.php?userid=157998 / http://neocron.lunarpages.com/alforum/index.php?topic=1509.0)
import poser
import wx
import wx.aui
import os.path
import pickle

class ScriptsWindow(wx.Panel):
	savePath=os.path.join(poser.PrefsLocation(), "acb")
	def __init__(self,parent,title,pane):
		wx.Panel.__init__(self, parent)
		
		self.parent=parent
		self.man=poser.WxAuiManager()
		
		if not os.path.exists(self.savePath):
			os.mkdir(self.savePath)

		self.settingsFile=os.path.join(self.savePath, "Settings_ScriptsMenu_"+self.TryFindRoom()+".config")
		self.settings={}
		if os.path.exists(self.settingsFile):
			fd=open(self.settingsFile)
			self.settings=pickle.load(fd)
			fd.close()
			if self.settings.has_key("PanelLayout"):
				self.man.LoadPaneInfo(self.settings["PanelLayout"], pane)
		if not self.settings.has_key("CheckContentDirs"):
		   self.settings["CheckContentDirs"]=True
				
		self.sizer = wx.BoxSizer(wx.VERTICAL)
		self.SetSizer(self.sizer)
		self.SetAutoLayout(1)
		self.sizer.SetSizeHints(self)
		self.tree = wx.TreeCtrl(self, -1, style=wx.TR_HAS_BUTTONS|wx.TR_DEFAULT_STYLE|wx.SUNKEN_BORDER|wx.TR_HIDE_ROOT, size=(-1,-1))
		self.sizer.Add(self.tree, 1, wx.EXPAND|wx.ALL, 0)
		self.root = os.path.join(os.path.dirname(poser.AppLocation()), 'Runtime', 'Python', 'poserScripts', 'ScriptsMenu')
		self.BuildTree()
		self.Layout()
		
		self.Bind(wx.EVT_SIZE, self.EvtChildFocus)
		self.Bind(wx.EVT_CHILD_FOCUS, self.EvtChildFocus)
		self.Bind(wx.EVT_RIGHT_DOWN, self.EvtRightDown)
		self.Bind(wx.EVT_MENU, self.EvtMenu)
		self.Bind(wx.EVT_RIGHT_DOWN, self.EvtRightDown)
		
		self.tree.Bind(wx.EVT_RIGHT_DOWN, self.EvtRightDown)
		self.tree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.EvtExec)
	
	def __del__(self):
		try:
			fd=open(self.settingsFile, "w")
			pickle.dump(self.settings,fd)
			fd.close()
		except:
			pass

	def BuildTree(self):
		self.tree.DeleteAllItems()
		self.rootId=self.tree.AddRoot('ScriptsMenu', data=wx.TreeItemData(self.root))
		self.Build(self.root, self.rootId)
		if self.settings["CheckContentDirs"]:
			for path in poser.Libraries():
				if path==os.path.dirname(poser.AppLocation()):
					continue
				name=os.path.basename(path)
				scriptPath=os.path.join(path, 'Runtime', 'Python', 'poserScripts', 'ScriptsMenu')
				id=self.tree.AppendItem(self.rootId, name, data=wx.TreeItemData(scriptPath))
				self.BuildBranch(id)
				if self.tree.GetChildrenCount(id)==0:
					self.tree.Delete(id)
	def BuildBranch(self, id=None):
		self.tree.DeleteChildren(id)
		self.Build(self.GetPath(id), id)
	
	def Build(self, path, id):
		ids = {path : id}
		for (dirpath, dirnames, filenames) in os.walk(path):
			dirnames.sort(cmp=lambda x,y: cmp(x.lower(), y.lower()))
			for dirname in dirnames:
				fullpath = os.path.join(dirpath, dirname)
				ids[fullpath] = self.tree.AppendItem(ids[dirpath], dirname, data=wx.TreeItemData(fullpath))
			filenames.sort(cmp=lambda x,y: cmp(x.lower(), y.lower()))
			for filename in filenames:
				if filename.endswith(".py") or filename.endswith(".pyc"):
					self.tree.AppendItem(ids[dirpath], filename)
		
	def GetPath(self, id):
		data=self.tree.GetPyData(id)
		if data==None:
			name = self.tree.GetItemText(id)
			parent = self.tree.GetItemParent(id)
			if parent:
				data=os.path.join(self.tree.GetPyData(parent),name)
			else:
				data=os.path.join(self.root,name)
		return data
		
	def EvtExec(self, event):
		id = event.GetItem()
		if not id.IsOk():
			id = self.tree.GetSelection()
		path=self.GetPath(id)
		if os.path.exists(path) and os.path.isfile(path):
			poser.ExecFile(path)
			
	def EvtRightDown(self, event):
		tp=self.tree.HitTest(event.GetPosition())
		self.tree.SelectItem(tp[0])
		pane = self.man.GetPane(self)
		menu = wx.Menu()
		id=self.tree.GetSelection()
		if id.IsOk():
			menu.Append(6, "Copy Path to Clipboard")
			menu.AppendSeparator()
		if id.IsOk():
			path=self.GetPath(id)
			if os.path.exists(path) and os.path.isdir(path):
				menu.Append(5, "Refresh Folder")
		menu.Append(4, "Refresh Tree")
		menu.AppendSeparator()
		
		libMenus=menu.Append(7, "Show Lib's Menus", kind=wx.ITEM_CHECK)
		libMenus.Check(self.settings["CheckContentDirs"])
		menu.AppendSeparator()
		
		dock = menu.Append(1, "Docked", kind=wx.ITEM_CHECK)
		float = menu.Append(2, "Floating", kind=wx.ITEM_CHECK)
		if pane.IsDocked():
			dock.Check()
		else: 
			float.Check()
		menu.AppendSeparator()
		drag = menu.Append(3, "Drag-Docking Enabled", kind=wx.ITEM_CHECK)
		if pane.IsLeftDockable():
			drag.Check()
		self.PopupMenu(menu)
		menu.Destroy()

	def EvtMenu(self, e):
		pane = self.man.GetPane(self)
		id = e.GetId()
		if id == 1:
			if pane.IsDocked():
				return
			pane.Dock()
			self.man.Update()
		elif id == 2:
			if pane.IsFloating():
				return
			pane.Float()
			self.man.Update()
		elif id == 3:
			pane.Dockable(e.IsChecked())
		elif id == 4:
			self.BuildTree()
		elif id == 5:
			self.BuildBranch(id = self.tree.GetSelection())
		elif id == 6:
			id=self.tree.GetSelection()
			if id.IsOk():
				path=self.GetPath(id)
				cb=wx.TheClipboard
				if cb.Open():
					cb.SetData(wx.TextDataObject(path))
					cb.Flush()
					cb.Close()
		elif id == 7:
			self.settings["CheckContentDirs"]=not self.settings["CheckContentDirs"]
			self.BuildTree()
	def EvtChildFocus(self, event):
		self.SaveLayout()
		event.Skip()
	def SaveLayout(self):
		pane=self.man.GetPane(self)
		pane.BestSize(pane.floating_size)
		self.settings["PanelLayout"]=self.man.SavePaneInfo(pane)
	def TryFindRoom(self):
		for pane in self.man.GetAllPanes():
			if pane.name.startswith("Hair") and pane.IsShown():
				return "Hair"
			elif pane.name.startswith("Cloth") and pane.IsShown():
				return "Cloth"
			elif pane.name.startswith("Material") and pane.IsShown():
				return "Material"
		return "Default"


name = "ScriptsMenu"
man = poser.WxAuiManager()
root = man.GetManagedWindow()

pane = wx.aui.AuiPaneInfo()
pane.Caption(name).CaptionVisible().CloseButton().Resizable().DestroyOnClose()
pane.FloatingSize(wx.Size(150, 125)).Right().PinButton().Float()
win = ScriptsWindow(root, name, pane)
man.AddPane(win, pane)
pane.Show()
man.Update()



