# toolmenu.py

import win32ui
import win32con
import win32api
import app
import sys
import string

tools = {}
idPos = 100

# The default items should no tools menu exist in the INI file.
defaultToolMenuItems = [
	('Browser', 'win32ui.GetApp().OnViewBrowse(0,0)'),
	('Browse PythonPath', 'from pywin.tools import browseProjects;browseProjects.Browse()'),
	('Edit Python Path', 'from pywin.tools import regedit;regedit.EditRegistry()'),
	('COM Makepy utility', 'from win32com.client import makepy;makepy.main()'),
	('COM Browser', 'from win32com.client import combrowse;combrowse.main()'),
	('Trace Collector Debugging tool', 'from pywin.tools import TraceCollector;TraceCollector.MakeOutputWindow()'),
]

def LoadToolMenuItems():
	# Load from the registry.
	items = []
	lookNo = 1
	while 1:
		menu = win32ui.GetProfileVal("Tools Menu\\%s" % lookNo, "", "")
		if menu=="":
			break
		cmd = win32ui.GetProfileVal("Tools Menu\\%s" % lookNo, "Command", "")
		items.append((menu, cmd))
		lookNo = lookNo + 1

	if len(items)==0: 
		items = defaultToolMenuItems
	return items

def WriteToolMenuItems( items ):
	# Items is a list of (menu, command)
	# Delete the entire registry tree.
	try:
		mainKey = win32ui.GetAppRegistryKey()
		toolKey = win32api.RegOpenKey(mainKey, "Tools Menu")
	except win32ui.error:
		toolKey = None
	if toolKey is not None:
		while 1:
			try:
				subkey = win32api.RegEnumKey(toolKey, 0)
			except win32api.error:
				break
			win32api.RegDeleteKey(toolKey, subkey)
	# Keys are now removed - write the new ones.
	# But first check if we have the defaults - and if so, dont write anything!
	if items==defaultToolMenuItems:
		return
	itemNo = 1
	for menu, cmd in items:
		win32ui.WriteProfileVal("Tools Menu\\%s" % itemNo, "", menu)
		win32ui.WriteProfileVal("Tools Menu\\%s" % itemNo, "Command", cmd)
		itemNo = itemNo + 1
	
def SetToolsMenu(menu, menuPos = None):
	global tools
	global idPos
	
	# todo - check the menu does not already exist.
	# Create the new menu
	toolsMenu = win32ui.CreatePopupMenu()
	
	# Load from the ini file.
	items = LoadToolMenuItems()
	for menuString, cmd in items:
		tools[idPos] = (menuString, cmd, menuString)
		toolsMenu.AppendMenu(win32con.MF_ENABLED|win32con.MF_STRING,idPos, menuString)
		win32ui.GetMainFrame().HookCommand(HandleToolCommand, idPos)
		idPos=idPos+1

	# Find the correct spot to insert the new tools menu.
	if menuPos is None:
		menuPos = menu.GetMenuItemCount()-2
		if menuPos<0: menuPos=0

	menu.InsertMenu(menuPos, win32con.MF_BYPOSITION|win32con.MF_ENABLED|win32con.MF_STRING|win32con.MF_POPUP, toolsMenu.GetHandle(), '&Tools')

def HandleToolCommand(cmd, code):
	import traceback
	import re
	global tools
	(menuString, pyCmd, desc) = tools[cmd]
	win32ui.SetStatusText("Executing tool %s" % desc, 1)
	pyCmd = re.sub('\\\\n','\n', pyCmd)
	win32ui.DoWaitCursor(1)
	oldFlag = None
	try:
		oldFlag = sys.stdout.template.writeQueueing
		sys.stdout.template.writeQueueing = 0
	except (NameError, AttributeError):
		pass
	
	try:
		exec "%s\n" % pyCmd
		worked=1
	except SystemExit:
		# The program raised a SystemExit - ignore it.
		worked = 1
	except:
		print "Failed to execute command:\n%s" % pyCmd
		traceback.print_exc()
		worked=0
	if oldFlag is not None:
		sys.stdout.template.writeQueueing = oldFlag
	win32ui.DoWaitCursor(0)
	if worked:
		text = "Completed successfully."
	else:
		text = "Error executing %s." % desc
	win32ui.SetStatusText(text, 1)

# The property page for maintaing the items on the Tools menu.
import commctrl
from pywin.mfc import dialog

if win32ui.UNICODE:
	LVN_ENDLABELEDIT = commctrl.LVN_ENDLABELEDITW
else:
	LVN_ENDLABELEDIT = commctrl.LVN_ENDLABELEDITA

class ToolMenuPropPage(dialog.PropertyPage):
	def __init__(self):
		self.bImChangingEditControls = 0 # Am I programatically changing the controls?
		dialog.PropertyPage.__init__(self, win32ui.IDD_PP_TOOLMENU)

	def OnInitDialog(self):
		self.editMenuCommand = self.GetDlgItem(win32ui.IDC_EDIT2)
		self.butNew = self.GetDlgItem(win32ui.IDC_BUTTON3)
		
		# Now hook the change notification messages for the edit controls.
		self.HookCommand(self.OnCommandEditControls, win32ui.IDC_EDIT1)
		self.HookCommand(self.OnCommandEditControls, win32ui.IDC_EDIT2)

		self.HookNotify(self.OnNotifyListControl, commctrl.LVN_ITEMCHANGED)
		self.HookNotify(self.OnNotifyListControlEndLabelEdit, commctrl.LVN_ENDLABELEDIT)
		
		# Hook the button clicks.
		self.HookCommand(self.OnButtonNew, win32ui.IDC_BUTTON3) # New Item
		self.HookCommand(self.OnButtonDelete, win32ui.IDC_BUTTON4) # Delete item
		self.HookCommand(self.OnButtonMove, win32ui.IDC_BUTTON1) # Move up
		self.HookCommand(self.OnButtonMove, win32ui.IDC_BUTTON2) # Move down

		# Setup the columns in the list control
		lc = self.GetDlgItem(win32ui.IDC_LIST1)
		rect = lc.GetWindowRect()
		cx = rect[2] - rect[0]
		colSize = cx/2 - win32api.GetSystemMetrics(win32con.SM_CXBORDER) - 1

		item = commctrl.LVCFMT_LEFT, colSize, "Menu Text"
		lc.InsertColumn(0, item)

		item = commctrl.LVCFMT_LEFT, colSize, "Python Command"
		lc.InsertColumn(1, item)

		# Insert the existing tools menu
		itemNo = 0
		for desc, cmd in LoadToolMenuItems():
			lc.InsertItem(itemNo, desc)
			lc.SetItemText(itemNo, 1, cmd)
			itemNo = itemNo + 1

		self.listControl = lc
		return dialog.PropertyPage.OnInitDialog(self)

	def OnOK(self):
		# Write the menu back to the registry.
		items = []
		itemLook = 0
		while 1:
			try:
				text = self.listControl.GetItemText(itemLook, 0);
				if not text:
					break
				items.append( (text, self.listControl.GetItemText(itemLook, 1)) )
			except win32ui.error:
				# no more items!
				break
			itemLook = itemLook + 1
		WriteToolMenuItems( items )
		return self._obj_.OnOK()

	def OnCommandEditControls(self, id, cmd):
#		print "OnEditControls", id, cmd
		if cmd==win32con.EN_CHANGE and not self.bImChangingEditControls:
			itemNo = self.listControl.GetNextItem(-1, commctrl.LVNI_SELECTED)
			newText = self.editMenuCommand.GetWindowText()
			self.listControl.SetItemText(itemNo, 1, newText)

		return 0
		
	def OnNotifyListControlEndLabelEdit(self, id, cmd):
		newText = self.listControl.GetEditControl().GetWindowText()
		itemNo = self.listControl.GetNextItem(-1, commctrl.LVNI_SELECTED)
		self.listControl.SetItemText(itemNo, 0, newText)

	def OnNotifyListControl(self, id, cmd):
#		print id, cmd
		try:
			itemNo = self.listControl.GetNextItem(-1, commctrl.LVNI_SELECTED)
		except win32ui.error: # No selection!
			return

		self.bImChangingEditControls = 1
		try:
			item = self.listControl.GetItem(itemNo, 1)
			self.editMenuCommand.SetWindowText(item[4])
		finally:
			self.bImChangingEditControls = 0
		
		return 0 # we have handled this!

	def OnButtonNew(self, id, cmd):
		if cmd==win32con.BN_CLICKED:
			newIndex = self.listControl.GetItemCount()
			self.listControl.InsertItem(newIndex, "Click to edit the text")
			self.listControl.EnsureVisible(newIndex, 0)

	def OnButtonMove(self, id, cmd):
		if cmd==win32con.BN_CLICKED:
			try:
				itemNo = self.listControl.GetNextItem(-1, commctrl.LVNI_SELECTED)
			except win32ui.error:
				return
			menu = self.listControl.GetItemText(itemNo, 0)
			cmd = self.listControl.GetItemText(itemNo, 1)
			if id == win32ui.IDC_BUTTON1:
				# Move up
				if itemNo > 0:
					self.listControl.DeleteItem(itemNo)
					# reinsert it.
					self.listControl.InsertItem(itemNo-1, menu)
					self.listControl.SetItemText(itemNo-1, 1, cmd)
			else:
				# Move down.
				if itemNo < self.listControl.GetItemCount()-1:
					self.listControl.DeleteItem(itemNo)
					# reinsert it.
					self.listControl.InsertItem(itemNo+1, menu)
					self.listControl.SetItemText(itemNo+1, 1, cmd)

	def OnButtonDelete(self, id, cmd):
		if cmd==win32con.BN_CLICKED:
			try:
				itemNo = self.listControl.GetNextItem(-1, commctrl.LVNI_SELECTED)
			except win32ui.error: # No selection!
				return
			self.listControl.DeleteItem(itemNo)
