285 lines
9.1 KiB
Python
285 lines
9.1 KiB
Python
|
# toolmenu.py
|
||
|
|
||
|
import sys
|
||
|
|
||
|
import win32api
|
||
|
import win32con
|
||
|
import win32ui
|
||
|
|
||
|
from . import app
|
||
|
|
||
|
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(modal=False)",
|
||
|
),
|
||
|
(
|
||
|
"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 re
|
||
|
import traceback
|
||
|
|
||
|
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)
|