Uploaded Test files
This commit is contained in:
parent
f584ad9d97
commit
2e81cb7d99
16627 changed files with 2065359 additions and 102444 deletions
|
@ -0,0 +1,853 @@
|
|||
# A sample shell namespace view
|
||||
|
||||
# To demostrate:
|
||||
# * Execute this script to register the namespace.
|
||||
# * Open Windows Explorer, and locate the new "Python Path Shell Browser"
|
||||
# folder off "My Computer"
|
||||
# * Browse this tree - .py files are shown expandable, with classes and
|
||||
# methods selectable. Selecting a Python file, or a class/method, will
|
||||
# display the file using Scintilla.
|
||||
# Known problems:
|
||||
# * Classes and methods don't have icons - this is a demo, so we keep it small
|
||||
# See icon_handler.py for examples of how to work with icons.
|
||||
#
|
||||
#
|
||||
# Notes on PIDLs
|
||||
# PIDLS are complicated, but fairly well documented in MSDN. If you need to
|
||||
# do much with these shell extensions, you must understand their concept.
|
||||
# Here is a short-course, as it applies to this sample:
|
||||
# A PIDL identifies an item, much in the same way that a filename does
|
||||
# (however, the shell is not limited to displaying "files").
|
||||
# An "ItemID" is a single string, each being an item in the hierarchy.
|
||||
# A "PIDL" is a list of these strings.
|
||||
# All shell etc functions work with PIDLs, so even in the case where
|
||||
# an ItemID is conceptually used, a 1-item list is always used.
|
||||
# Conceptually, think of:
|
||||
# pidl = pathname.split("\\") # pidl is a list of "parent" items.
|
||||
# # each item is a string 'item id', but these are ever used directly
|
||||
# As there is no concept of passing a single item, to open a file using only
|
||||
# a relative filename, conceptually you would say:
|
||||
# open_file([filename]) # Pass a single-itemed relative "PIDL"
|
||||
# and continuing the analogy, a "listdir" type function would return a list
|
||||
# of single-itemed lists - each list containing the relative PIDL of the child.
|
||||
#
|
||||
# Each PIDL entry is a binary string, and may contain any character. For
|
||||
# PIDLs not created by you, they can not be interpreted - they are just
|
||||
# blobs. PIDLs created by you (ie, children of your IShellFolder) can
|
||||
# store and interpret the string however makes most sense for your application.
|
||||
# (but within PIDL rules - they must be persistable, etc)
|
||||
# There is no reason that pickled strings, for example, couldn't be used
|
||||
# as an EntryID.
|
||||
# This application takes a simple approach - each PIDL is a string of form
|
||||
# "directory\0directory_name", "file\0file_name" or
|
||||
# "object\0file_name\0class_name[.method_name"
|
||||
# The first string in each example is literal (ie, the word 'directory',
|
||||
# 'file' or 'object', and every other string is variable. We use '\0' as
|
||||
# a field sep just 'cos we can (and 'cos it can't possibly conflict with the
|
||||
# string content)
|
||||
|
||||
import sys, os
|
||||
import _thread
|
||||
import pyclbr
|
||||
import pythoncom
|
||||
import win32gui, win32gui_struct, win32api, win32con, winerror
|
||||
import commctrl
|
||||
from win32com.shell import shell, shellcon
|
||||
from win32com.server.util import wrap, NewEnum
|
||||
from win32com.server.exception import COMException
|
||||
from win32com.util import IIDToInterfaceName
|
||||
from pywin.scintilla import scintillacon
|
||||
|
||||
# Set this to 1 to cause debug version to be registered and used. A debug
|
||||
# version will spew output to win32traceutil.
|
||||
debug=0
|
||||
if debug:
|
||||
import win32traceutil
|
||||
|
||||
# markh is toying with an implementation that allows auto reload of a module
|
||||
# if this attribute exists.
|
||||
com_auto_reload = True
|
||||
|
||||
# Helper function to get a system IShellFolder interface, and the PIDL within
|
||||
# that folder for an existing file/directory.
|
||||
def GetFolderAndPIDLForPath(filename):
|
||||
desktop = shell.SHGetDesktopFolder()
|
||||
info = desktop.ParseDisplayName(0, None, os.path.abspath(filename))
|
||||
cchEaten, pidl, attr = info
|
||||
# We must walk the ID list, looking for one child at a time.
|
||||
folder = desktop
|
||||
while len(pidl) > 1:
|
||||
this = pidl.pop(0)
|
||||
folder = folder.BindToObject([this], None, shell.IID_IShellFolder)
|
||||
# We are left with the pidl for the specific item. Leave it as
|
||||
# a list, so it remains a valid PIDL.
|
||||
return folder, pidl
|
||||
|
||||
# A cache of pyclbr module objects, so we only parse a given filename once.
|
||||
clbr_modules = {} # Indexed by path, item is dict as returned from pyclbr
|
||||
def get_clbr_for_file(path):
|
||||
try:
|
||||
objects = clbr_modules[path]
|
||||
except KeyError:
|
||||
dir, filename = os.path.split(path)
|
||||
base, ext = os.path.splitext(filename)
|
||||
objects = pyclbr.readmodule_ex(base, [dir])
|
||||
clbr_modules[path] = objects
|
||||
return objects
|
||||
|
||||
# Our COM interfaces.
|
||||
|
||||
# Base class for a shell folder.
|
||||
# All child classes use a simple PIDL of the form:
|
||||
# "object_type\0object_name[\0extra ...]"
|
||||
class ShellFolderBase:
|
||||
_com_interfaces_ = [shell.IID_IBrowserFrameOptions,
|
||||
pythoncom.IID_IPersist,
|
||||
shell.IID_IPersistFolder,
|
||||
shell.IID_IShellFolder,
|
||||
]
|
||||
|
||||
_public_methods_ = shellcon.IBrowserFrame_Methods + \
|
||||
shellcon.IPersistFolder_Methods + \
|
||||
shellcon.IShellFolder_Methods
|
||||
|
||||
def GetFrameOptions(self, mask):
|
||||
#print "GetFrameOptions", self, mask
|
||||
return 0
|
||||
def ParseDisplayName(self, hwnd, reserved, displayName, attr):
|
||||
print("ParseDisplayName", displayName)
|
||||
# return cchEaten, pidl, attr
|
||||
def BindToStorage(self, pidl, bc, iid):
|
||||
print("BTS", iid, IIDToInterfaceName(iid))
|
||||
def BindToObject(self, pidl, bc, iid):
|
||||
# We may be passed a set of relative PIDLs here - ie
|
||||
# [pidl_of_dir, pidl_of_child_dir, pidl_of_file, pidl_of_function]
|
||||
# But each of our PIDLs keeps the fully qualified name anyway - so
|
||||
# just jump directly to the last.
|
||||
final_pidl = pidl[-1]
|
||||
typ, extra = final_pidl.split('\0', 1)
|
||||
if typ == "directory":
|
||||
klass = ShellFolderDirectory
|
||||
elif typ == "file":
|
||||
klass = ShellFolderFile
|
||||
elif typ == "object":
|
||||
klass = ShellFolderObject
|
||||
else:
|
||||
raise RuntimeError("What is " + repr(typ))
|
||||
ret = wrap(klass(extra), iid, useDispatcher = (debug>0))
|
||||
return ret
|
||||
|
||||
# A ShellFolder for an object with CHILDREN on the file system
|
||||
# Note that this means our "File" folder is *not* a 'FileSystem' folder,
|
||||
# as it's children (functions and classes) are not on the file system.
|
||||
#
|
||||
class ShellFolderFileSystem(ShellFolderBase):
|
||||
def _GetFolderAndPIDLForPIDL(self, my_idl):
|
||||
typ, name = my_idl[0].split('\0')
|
||||
return GetFolderAndPIDLForPath(name)
|
||||
# Interface methods
|
||||
def CompareIDs(self, param, id1, id2):
|
||||
if id1 < id2:
|
||||
return -1
|
||||
if id1 == id2:
|
||||
return 0
|
||||
return 1
|
||||
def GetUIObjectOf(self, hwndOwner, pidls, iid, inout):
|
||||
# delegate to the shell.
|
||||
assert len(pidls)==1, "oops - arent expecting more than one!"
|
||||
pidl = pidls[0]
|
||||
folder, child_pidl = self._GetFolderAndPIDLForPIDL(pidl)
|
||||
try:
|
||||
inout, ret = folder.GetUIObjectOf(hwndOwner, [child_pidl], iid,
|
||||
inout, iid)
|
||||
except pythoncom.com_error as xxx_todo_changeme:
|
||||
(hr, desc, exc, arg) = xxx_todo_changeme.args
|
||||
raise COMException(hresult=hr)
|
||||
return inout, ret
|
||||
# return object of IID
|
||||
def GetDisplayNameOf(self, pidl, flags):
|
||||
# delegate to the shell.
|
||||
folder, child_pidl = self._GetFolderAndPIDLForPIDL(pidl)
|
||||
ret = folder.GetDisplayNameOf(child_pidl, flags)
|
||||
return ret
|
||||
def GetAttributesOf(self, pidls, attrFlags):
|
||||
ret_flags = -1
|
||||
for pidl in pidls:
|
||||
pidl = pidl[0] # ??
|
||||
typ, name = pidl.split('\0')
|
||||
flags = shellcon.SHGFI_ATTRIBUTES
|
||||
rc, info = shell.SHGetFileInfo(name, 0, flags)
|
||||
hIcon, iIcon, dwAttr, name, typeName = info
|
||||
# All our items, even files, have sub-items
|
||||
extras = shellcon.SFGAO_HASSUBFOLDER | \
|
||||
shellcon.SFGAO_FOLDER | \
|
||||
shellcon.SFGAO_FILESYSANCESTOR | \
|
||||
shellcon.SFGAO_BROWSABLE
|
||||
ret_flags &= (dwAttr | extras)
|
||||
return ret_flags
|
||||
|
||||
class ShellFolderDirectory(ShellFolderFileSystem):
|
||||
def __init__(self, path):
|
||||
self.path = os.path.abspath(path)
|
||||
def CreateViewObject(self, hwnd, iid):
|
||||
# delegate to the shell.
|
||||
folder, child_pidl = GetFolderAndPIDLForPath(self.path)
|
||||
return folder.CreateViewObject(hwnd, iid)
|
||||
def EnumObjects(self, hwndOwner, flags):
|
||||
pidls = []
|
||||
for fname in os.listdir(self.path):
|
||||
fqn = os.path.join(self.path, fname)
|
||||
if os.path.isdir(fqn):
|
||||
type_name = "directory"
|
||||
type_class = ShellFolderDirectory
|
||||
else:
|
||||
base, ext = os.path.splitext(fname)
|
||||
if ext in [".py", ".pyw"]:
|
||||
type_class = ShellFolderFile
|
||||
type_name = "file"
|
||||
else:
|
||||
type_class = None
|
||||
if type_class is not None:
|
||||
pidls.append( [type_name + "\0" + fqn] )
|
||||
return NewEnum(pidls, iid=shell.IID_IEnumIDList,
|
||||
useDispatcher=(debug>0))
|
||||
|
||||
def GetDisplayNameOf(self, pidl, flags):
|
||||
final_pidl=pidl[-1]
|
||||
full_fname=final_pidl.split('\0')[-1]
|
||||
return os.path.split(full_fname)[-1]
|
||||
|
||||
def GetAttributesOf(self, pidls, attrFlags):
|
||||
return shellcon.SFGAO_HASSUBFOLDER|shellcon.SFGAO_FOLDER|shellcon.SFGAO_FILESYSANCESTOR|shellcon.SFGAO_BROWSABLE
|
||||
|
||||
# As per comments above, even though this manages a file, it is *not* a
|
||||
# ShellFolderFileSystem, as the children are not on the file system.
|
||||
class ShellFolderFile(ShellFolderBase):
|
||||
def __init__(self, path):
|
||||
self.path = os.path.abspath(path)
|
||||
def EnumObjects(self, hwndOwner, flags):
|
||||
objects = get_clbr_for_file(self.path)
|
||||
pidls = []
|
||||
for name, ob in objects.items():
|
||||
pidls.append( ["object\0" + self.path + "\0" + name] )
|
||||
return NewEnum(pidls, iid=shell.IID_IEnumIDList,
|
||||
useDispatcher=(debug>0))
|
||||
|
||||
def GetAttributesOf(self, pidls, attrFlags):
|
||||
ret_flags = -1
|
||||
for pidl in pidls:
|
||||
assert len(pidl)==1, "Expecting relative pidls"
|
||||
pidl = pidl[0]
|
||||
typ, filename, obname = pidl.split('\0')
|
||||
obs = get_clbr_for_file(filename)
|
||||
ob = obs[obname]
|
||||
flags = shellcon.SFGAO_BROWSABLE | shellcon.SFGAO_FOLDER | \
|
||||
shellcon.SFGAO_FILESYSANCESTOR
|
||||
if hasattr(ob, "methods"):
|
||||
flags |= shellcon.SFGAO_HASSUBFOLDER
|
||||
ret_flags &= flags
|
||||
return ret_flags
|
||||
|
||||
def GetDisplayNameOf(self, pidl, flags):
|
||||
assert len(pidl)==1, "Expecting relative PIDL"
|
||||
typ, fname, obname = pidl[0].split('\0')
|
||||
fqname = os.path.splitext(fname)[0] + "." + obname
|
||||
if flags & shellcon.SHGDN_INFOLDER:
|
||||
ret = obname
|
||||
else: # SHGDN_NORMAL is the default
|
||||
ret = fqname
|
||||
# No need to look at the SHGDN_FOR* modifiers.
|
||||
return ret
|
||||
|
||||
def CreateViewObject(self, hwnd, iid):
|
||||
return wrap(ScintillaShellView(hwnd, self.path), iid, useDispatcher=debug>0)
|
||||
# A ShellFolder for our Python objects
|
||||
class ShellFolderObject(ShellFolderBase):
|
||||
def __init__(self, details):
|
||||
self.path, details = details.split('\0')
|
||||
if details.find(".")>0:
|
||||
self.class_name, self.method_name = details.split(".")
|
||||
else:
|
||||
self.class_name = details
|
||||
self.method_name = None
|
||||
def CreateViewObject(self, hwnd, iid):
|
||||
mod_objects = get_clbr_for_file(self.path)
|
||||
object = mod_objects[self.class_name]
|
||||
if self.method_name is None:
|
||||
lineno = object.lineno
|
||||
else:
|
||||
lineno = object.methods[self.method_name]
|
||||
return wrap(ScintillaShellView(hwnd, self.path, lineno),
|
||||
iid, useDispatcher=debug>0)
|
||||
|
||||
def EnumObjects(self, hwndOwner, flags):
|
||||
assert self.method_name is None, "Should not be enuming methods!"
|
||||
mod_objects = get_clbr_for_file(self.path)
|
||||
my_objects = mod_objects[self.class_name]
|
||||
pidls = []
|
||||
for func_name, lineno in my_objects.methods.items():
|
||||
pidl = ["object\0" + self.path + "\0" +
|
||||
self.class_name + "." + func_name]
|
||||
pidls.append(pidl)
|
||||
return NewEnum(pidls, iid=shell.IID_IEnumIDList,
|
||||
useDispatcher=(debug>0))
|
||||
def GetDisplayNameOf(self, pidl, flags):
|
||||
assert len(pidl)==1, "Expecting relative PIDL"
|
||||
typ, fname, obname = pidl[0].split('\0')
|
||||
class_name, method_name = obname.split(".")
|
||||
fqname = os.path.splitext(fname)[0] + "." + obname
|
||||
if flags & shellcon.SHGDN_INFOLDER:
|
||||
ret = method_name
|
||||
else: # SHGDN_NORMAL is the default
|
||||
ret = fqname
|
||||
# No need to look at the SHGDN_FOR* modifiers.
|
||||
return ret
|
||||
def GetAttributesOf(self, pidls, attrFlags):
|
||||
ret_flags = -1
|
||||
for pidl in pidls:
|
||||
assert len(pidl)==1, "Expecting relative pidls"
|
||||
flags = shellcon.SFGAO_BROWSABLE | shellcon.SFGAO_FOLDER | \
|
||||
shellcon.SFGAO_FILESYSANCESTOR
|
||||
ret_flags &= flags
|
||||
return ret_flags
|
||||
|
||||
# The "Root" folder of our namespace. As all children are directories,
|
||||
# it is derived from ShellFolderFileSystem
|
||||
# This is the only COM object actually registered and externally created.
|
||||
class ShellFolderRoot(ShellFolderFileSystem):
|
||||
_reg_progid_ = "Python.ShellExtension.Folder"
|
||||
_reg_desc_ = "Python Path Shell Browser"
|
||||
_reg_clsid_ = "{f6287035-3074-4cb5-a8a6-d3c80e206944}"
|
||||
def GetClassID(self):
|
||||
return self._reg_clsid_
|
||||
def Initialize(self, pidl):
|
||||
# This is the PIDL of us, as created by the shell. This is our
|
||||
# top-level ID. All other items under us have PIDLs defined
|
||||
# by us - see the notes at the top of the file.
|
||||
#print "Initialize called with pidl", repr(pidl)
|
||||
self.pidl = pidl
|
||||
def CreateViewObject(self, hwnd, iid):
|
||||
return wrap(FileSystemView(self, hwnd), iid, useDispatcher=debug>0)
|
||||
|
||||
def EnumObjects(self, hwndOwner, flags):
|
||||
items = [ ["directory\0" + p] for p in sys.path if os.path.isdir(p)]
|
||||
return NewEnum(items, iid=shell.IID_IEnumIDList,
|
||||
useDispatcher=(debug>0))
|
||||
def GetDisplayNameOf(self, pidl, flags):
|
||||
## return full path for sys.path dirs, since they don't appear under a parent folder
|
||||
final_pidl=pidl[-1]
|
||||
display_name=final_pidl.split('\0')[-1]
|
||||
return display_name
|
||||
|
||||
# Simple shell view implementations
|
||||
|
||||
# Uses a builtin listview control to display simple lists of directories
|
||||
# or filenames.
|
||||
class FileSystemView:
|
||||
_public_methods_ = shellcon.IShellView_Methods
|
||||
_com_interfaces_ = [pythoncom.IID_IOleWindow,
|
||||
shell.IID_IShellView,
|
||||
]
|
||||
def __init__(self, folder, hwnd):
|
||||
self.hwnd_parent = hwnd # provided by explorer.
|
||||
self.hwnd = None # intermediate window for catching command notifications.
|
||||
self.hwnd_child = None # our ListView
|
||||
self.activate_state = None
|
||||
self.hmenu = None
|
||||
self.browser = None
|
||||
self.folder = folder
|
||||
self.children = None
|
||||
|
||||
# IOleWindow
|
||||
def GetWindow(self):
|
||||
return self.hwnd
|
||||
|
||||
def ContextSensitiveHelp(self, enter_mode):
|
||||
raise COMException(hresult=winerror.E_NOTIMPL)
|
||||
|
||||
# IShellView
|
||||
def CreateViewWindow(self, prev, settings, browser, rect):
|
||||
print("FileSystemView.CreateViewWindow", prev, settings, browser, rect)
|
||||
self.cur_foldersettings = settings
|
||||
self.browser = browser
|
||||
self._CreateMainWindow(prev, settings, browser, rect)
|
||||
self._CreateChildWindow(prev)
|
||||
|
||||
# This isn't part of the sample, but the most convenient place to
|
||||
# test/demonstrate how you can get an IShellBrowser from a HWND
|
||||
# (but ONLY when you are in the same process as the IShellBrowser!)
|
||||
# Obviously it is not necessary here - we already have the browser!
|
||||
browser_ad = win32gui.SendMessage(self.hwnd_parent, win32con.WM_USER+7, 0, 0)
|
||||
browser_ob = pythoncom.ObjectFromAddress(browser_ad, shell.IID_IShellBrowser)
|
||||
assert browser==browser_ob
|
||||
# and make a call on the object to prove it doesn't die :)
|
||||
assert browser.QueryActiveShellView()==browser_ob.QueryActiveShellView()
|
||||
|
||||
def _CreateMainWindow(self, prev, settings, browser, rect):
|
||||
# Creates a parent window that hosts the view window. This window
|
||||
# gets the control notifications etc sent from the child.
|
||||
style = win32con.WS_CHILD | win32con.WS_VISIBLE #
|
||||
wclass_name = "ShellViewDemo_DefView"
|
||||
# Register the Window class.
|
||||
wc = win32gui.WNDCLASS()
|
||||
wc.hInstance = win32gui.dllhandle
|
||||
wc.lpszClassName = wclass_name
|
||||
wc.style = win32con.CS_VREDRAW | win32con.CS_HREDRAW
|
||||
try:
|
||||
win32gui.RegisterClass(wc)
|
||||
except win32gui.error as details:
|
||||
# Should only happen when this module is reloaded
|
||||
if details[0] != winerror.ERROR_CLASS_ALREADY_EXISTS:
|
||||
raise
|
||||
|
||||
message_map = {
|
||||
win32con.WM_DESTROY: self.OnDestroy,
|
||||
win32con.WM_COMMAND: self.OnCommand,
|
||||
win32con.WM_NOTIFY: self.OnNotify,
|
||||
win32con.WM_CONTEXTMENU: self.OnContextMenu,
|
||||
win32con.WM_SIZE: self.OnSize,
|
||||
}
|
||||
|
||||
self.hwnd = win32gui.CreateWindow( wclass_name, "", style, \
|
||||
rect[0], rect[1], rect[2]-rect[0], rect[3]-rect[1],
|
||||
self.hwnd_parent, 0, win32gui.dllhandle, None)
|
||||
win32gui.SetWindowLong(self.hwnd, win32con.GWL_WNDPROC, message_map)
|
||||
print("View 's hwnd is", self.hwnd)
|
||||
return self.hwnd
|
||||
|
||||
def _CreateChildWindow(self, prev):
|
||||
# Creates the list view window.
|
||||
assert self.hwnd_child is None, "already have a window"
|
||||
assert self.cur_foldersettings is not None, "no settings"
|
||||
style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_BORDER | \
|
||||
commctrl.LVS_SHAREIMAGELISTS | commctrl.LVS_EDITLABELS
|
||||
|
||||
view_mode, view_flags = self.cur_foldersettings
|
||||
if view_mode==shellcon.FVM_ICON:
|
||||
style |= commctrl.LVS_ICON | commctrl.LVS_AUTOARRANGE
|
||||
elif view_mode==shellcon.FVM_SMALLICON:
|
||||
style |= commctrl.LVS_SMALLICON | commctrl.LVS_AUTOARRANGE
|
||||
elif view_mode==shellcon.FVM_LIST:
|
||||
style |= commctrl.LVS_LIST | commctrl.LVS_AUTOARRANGE
|
||||
elif view_mode==shellcon.FVM_DETAILS:
|
||||
style |= commctrl.LVS_REPORT | commctrl.LVS_AUTOARRANGE
|
||||
else:
|
||||
# XP 'thumbnails' etc
|
||||
view_mode = shellcon.FVM_DETAILS
|
||||
# Default to 'report'
|
||||
style |= commctrl.LVS_REPORT | commctrl.LVS_AUTOARRANGE
|
||||
|
||||
for f_flag, l_flag in [
|
||||
(shellcon.FWF_SINGLESEL, commctrl.LVS_SINGLESEL),
|
||||
(shellcon.FWF_ALIGNLEFT, commctrl.LVS_ALIGNLEFT),
|
||||
(shellcon.FWF_SHOWSELALWAYS, commctrl.LVS_SHOWSELALWAYS),
|
||||
]:
|
||||
if view_flags & f_flag:
|
||||
style |= l_flag
|
||||
|
||||
self.hwnd_child = win32gui.CreateWindowEx(
|
||||
win32con.WS_EX_CLIENTEDGE,
|
||||
"SysListView32", None, style,
|
||||
0, 0, 0, 0,
|
||||
self.hwnd, 1000, 0, None)
|
||||
|
||||
cr = win32gui.GetClientRect(self.hwnd)
|
||||
win32gui.MoveWindow(self.hwnd_child,
|
||||
0, 0, cr[2]-cr[0], cr[3]-cr[1],
|
||||
True)
|
||||
|
||||
# Setup the columns for the view.
|
||||
lvc, extras = win32gui_struct.PackLVCOLUMN(fmt=commctrl.LVCFMT_LEFT,
|
||||
subItem=1,
|
||||
text='Name',
|
||||
cx=300)
|
||||
win32gui.SendMessage(self.hwnd_child, commctrl.LVM_INSERTCOLUMN,
|
||||
0, lvc)
|
||||
|
||||
lvc, extras = win32gui_struct.PackLVCOLUMN(fmt=commctrl.LVCFMT_RIGHT,
|
||||
subItem=1,
|
||||
text='Exists',
|
||||
cx=50)
|
||||
win32gui.SendMessage(self.hwnd_child, commctrl.LVM_INSERTCOLUMN,
|
||||
1, lvc)
|
||||
# and fill it with the content
|
||||
self.Refresh()
|
||||
|
||||
def GetCurrentInfo(self):
|
||||
return self.cur_foldersettings
|
||||
|
||||
def UIActivate(self, activate_state):
|
||||
print("OnActivate")
|
||||
|
||||
def _OnActivate(self, activate_state):
|
||||
if self.activate_state == activate_state:
|
||||
return
|
||||
self._OnDeactivate() # restore menu's first, if necessary.
|
||||
if activate_state != shellcon.SVUIA_DEACTIVATE:
|
||||
assert self.hmenu is None, "Should have destroyed it!"
|
||||
self.hmenu = win32gui.CreateMenu()
|
||||
widths = 0,0,0,0,0,0
|
||||
# Ask explorer to add its standard items.
|
||||
self.browser.InsertMenusSB(self.hmenu, widths)
|
||||
# Merge with these standard items
|
||||
self._MergeMenus(activate_state)
|
||||
self.browser.SetMenuSB(self.hmenu, 0, self.hwnd);
|
||||
self.activate_state = activate_state
|
||||
|
||||
def _OnDeactivate(self):
|
||||
if self.browser is not None and self.hmenu is not None:
|
||||
self.browser.SetMenuSB(0, 0, 0)
|
||||
self.browser.RemoveMenusSB(self.hmenu)
|
||||
win32gui.DestroyMenu(self.hmenu)
|
||||
self.hmenu = None
|
||||
self.hsubmenus = None
|
||||
self.activate_state = shellcon.SVUIA_DEACTIVATE
|
||||
|
||||
def _MergeMenus(self, activate_state):
|
||||
# Merge the operations we support into the top-level menus.
|
||||
# NOTE: This function it *not* called each time the selection changes.
|
||||
# SVUIA_ACTIVATE_FOCUS really means "have a selection?"
|
||||
have_sel = activate_state == shellcon.SVUIA_ACTIVATE_FOCUS
|
||||
# only do "file" menu here, and only 1 item on it!
|
||||
mid = shellcon.FCIDM_MENU_FILE
|
||||
# Get the hmenu for the menu
|
||||
buf, extras = win32gui_struct.EmptyMENUITEMINFO(win32con.MIIM_SUBMENU)
|
||||
win32gui.GetMenuItemInfo(self.hmenu,
|
||||
mid,
|
||||
False,
|
||||
buf)
|
||||
data = win32gui_struct.UnpackMENUITEMINFO(buf)
|
||||
submenu = data[3]
|
||||
print("Do someting with the file menu!")
|
||||
|
||||
def Refresh(self):
|
||||
stateMask = commctrl.LVIS_SELECTED | commctrl.LVIS_DROPHILITED
|
||||
state = 0
|
||||
self.children = []
|
||||
# Enumerate and store the child PIDLs
|
||||
for cid in self.folder.EnumObjects(self.hwnd, 0):
|
||||
self.children.append(cid)
|
||||
|
||||
for row_index, data in enumerate(self.children):
|
||||
assert len(data)==1, "expecting just a child PIDL"
|
||||
typ, path = data[0].split('\0')
|
||||
desc = os.path.exists(path) and "Yes" or "No"
|
||||
prop_vals = (path, desc)
|
||||
# first col
|
||||
data, extras = win32gui_struct.PackLVITEM(
|
||||
item=row_index,
|
||||
subItem=0,
|
||||
text=prop_vals[0],
|
||||
state=state,
|
||||
stateMask=stateMask)
|
||||
win32gui.SendMessage(self.hwnd_child,
|
||||
commctrl.LVM_INSERTITEM,
|
||||
row_index, data)
|
||||
# rest of the cols.
|
||||
col_index = 1
|
||||
for prop_val in prop_vals[1:]:
|
||||
data, extras = win32gui_struct.PackLVITEM(
|
||||
item=row_index,
|
||||
subItem=col_index,
|
||||
text=prop_val)
|
||||
|
||||
win32gui.SendMessage(self.hwnd_child,
|
||||
commctrl.LVM_SETITEM,
|
||||
0, data)
|
||||
col_index += 1
|
||||
|
||||
def SelectItem(self, pidl, flag):
|
||||
# For the sake of brevity, we don't implement this yet.
|
||||
# You would need to locate the index of the item in the shell-view
|
||||
# with that PIDL, then ask the list-view to select it.
|
||||
print("Please implement SelectItem for PIDL", pidl)
|
||||
|
||||
def GetItemObject(self, item_num, iid):
|
||||
raise COMException(hresult=winerror.E_NOTIMPL)
|
||||
|
||||
def TranslateAccelerator(self, msg):
|
||||
return winerror.S_FALSE
|
||||
|
||||
def DestroyViewWindow(self):
|
||||
win32gui.DestroyWindow(self.hwnd)
|
||||
self.hwnd = None
|
||||
print("Destroyed view window")
|
||||
|
||||
# Message handlers.
|
||||
def OnDestroy(self, hwnd, msg, wparam, lparam):
|
||||
print("OnDestory")
|
||||
|
||||
def OnCommand(self, hwnd, msg, wparam, lparam):
|
||||
print("OnCommand")
|
||||
|
||||
def OnNotify(self, hwnd, msg, wparam, lparam):
|
||||
hwndFrom, idFrom, code = win32gui_struct.UnpackWMNOTIFY(lparam)
|
||||
#print "OnNotify code=0x%x (0x%x, 0x%x)" % (code, wparam, lparam)
|
||||
if code == commctrl.NM_SETFOCUS:
|
||||
# Control got focus - Explorer may not know - tell it
|
||||
if self.browser is not None:
|
||||
self.browser.OnViewWindowActive(None)
|
||||
# And do our menu thang
|
||||
self._OnActivate(shellcon.SVUIA_ACTIVATE_FOCUS)
|
||||
elif code == commctrl.NM_KILLFOCUS:
|
||||
self._OnDeactivate()
|
||||
elif code == commctrl.NM_DBLCLK:
|
||||
# This DblClick implementation leaves a little to be desired :)
|
||||
# It demonstrates some useful concepts, such as asking the
|
||||
# folder for its context-menu and invoking a command from it.
|
||||
# However, as our folder delegates IContextMenu to the shell
|
||||
# itself, the end result is that the folder is opened in
|
||||
# its "normal" place in Windows explorer rather than inside
|
||||
# our shell-extension.
|
||||
# Determine the selected items.
|
||||
sel = []
|
||||
n = -1
|
||||
while 1:
|
||||
n = win32gui.SendMessage(self.hwnd_child,
|
||||
commctrl.LVM_GETNEXTITEM,
|
||||
n,
|
||||
commctrl.LVNI_SELECTED)
|
||||
if n==-1:
|
||||
break
|
||||
sel.append(self.children[n][-1:])
|
||||
print("Selection is", sel)
|
||||
hmenu = win32gui.CreateMenu()
|
||||
try:
|
||||
# Get the IContextMenu for the items.
|
||||
inout, cm = self.folder.GetUIObjectOf(self.hwnd_parent, sel,
|
||||
shell.IID_IContextMenu, 0)
|
||||
|
||||
# As per 'Q179911', we need to determine if the default operation
|
||||
# should be 'open' or 'explore'
|
||||
flags = shellcon.CMF_DEFAULTONLY
|
||||
try:
|
||||
self.browser.GetControlWindow(shellcon.FCW_TREE)
|
||||
flags |= shellcon.CMF_EXPLORE
|
||||
except pythoncom.com_error:
|
||||
pass
|
||||
# *sob* - delegating to the shell does work - but lands us
|
||||
# in the original location. Q179911 also shows that
|
||||
# ShellExecuteEx should work - but I can't make it work as
|
||||
# described (XP: function call succeeds, but another thread
|
||||
# shows a dialog with text of E_INVALID_PARAM, and new
|
||||
# Explorer window opens with desktop view. Vista: function
|
||||
# call succeeds, but no window created at all.
|
||||
# On Vista, I'd love to get an IExplorerBrowser interface
|
||||
# from the shell, but a QI fails, and although the
|
||||
# IShellBrowser does appear to support IServiceProvider, I
|
||||
# still can't get it
|
||||
if 0:
|
||||
id_cmd_first = 1 # TrackPopupMenu makes it hard to use 0
|
||||
cm.QueryContextMenu(hmenu, 0, id_cmd_first, -1, flags)
|
||||
# Find the default item in the returned menu.
|
||||
cmd = win32gui.GetMenuDefaultItem(hmenu, False, 0)
|
||||
if cmd == -1:
|
||||
print("Oops: _doDefaultActionFor found no default menu")
|
||||
else:
|
||||
ci = 0, self.hwnd_parent, cmd-id_cmd_first, None, None, 0, 0, 0
|
||||
cm.InvokeCommand(ci)
|
||||
else:
|
||||
rv = shell.ShellExecuteEx(
|
||||
hwnd = self.hwnd_parent,
|
||||
nShow=win32con.SW_NORMAL,
|
||||
lpClass="folder",
|
||||
lpVerb="explore",
|
||||
lpIDList=sel[0])
|
||||
print("ShellExecuteEx returned", rv)
|
||||
finally:
|
||||
win32gui.DestroyMenu(hmenu)
|
||||
|
||||
def OnContextMenu(self, hwnd, msg, wparam, lparam):
|
||||
# Get the selected items.
|
||||
pidls = []
|
||||
n = -1
|
||||
while 1:
|
||||
n = win32gui.SendMessage(self.hwnd_child,
|
||||
commctrl.LVM_GETNEXTITEM,
|
||||
n,
|
||||
commctrl.LVNI_SELECTED)
|
||||
if n==-1:
|
||||
break
|
||||
pidls.append(self.children[n][-1:])
|
||||
|
||||
spt = win32api.GetCursorPos()
|
||||
if not pidls:
|
||||
print("Ignoring background click")
|
||||
return
|
||||
# Get the IContextMenu for the items.
|
||||
inout, cm = self.folder.GetUIObjectOf(self.hwnd_parent, pidls, shell.IID_IContextMenu, 0)
|
||||
hmenu = win32gui.CreatePopupMenu()
|
||||
sel = None
|
||||
# As per 'Q179911', we need to determine if the default operation
|
||||
# should be 'open' or 'explore'
|
||||
try:
|
||||
flags = 0
|
||||
try:
|
||||
self.browser.GetControlWindow(shellcon.FCW_TREE)
|
||||
flags |= shellcon.CMF_EXPLORE
|
||||
except pythoncom.com_error:
|
||||
pass
|
||||
id_cmd_first = 1 # TrackPopupMenu makes it hard to use 0
|
||||
cm.QueryContextMenu(hmenu, 0, id_cmd_first, -1, flags)
|
||||
tpm_flags = win32con.TPM_LEFTALIGN | win32con.TPM_RETURNCMD | \
|
||||
win32con.TPM_RIGHTBUTTON
|
||||
sel = win32gui.TrackPopupMenu(hmenu,
|
||||
tpm_flags,
|
||||
spt[0], spt[1],
|
||||
0, self.hwnd, None)
|
||||
print("TrackPopupMenu returned", sel)
|
||||
finally:
|
||||
win32gui.DestroyMenu(hmenu)
|
||||
if sel:
|
||||
ci = 0, self.hwnd_parent, sel-id_cmd_first, None, None, 0, 0, 0
|
||||
cm.InvokeCommand(ci)
|
||||
|
||||
def OnSize(self, hwnd, msg, wparam, lparam):
|
||||
#print "OnSize", self.hwnd_child, win32api.LOWORD(lparam), win32api.HIWORD(lparam)
|
||||
if self.hwnd_child is not None:
|
||||
x = win32api.LOWORD(lparam)
|
||||
y = win32api.HIWORD(lparam)
|
||||
win32gui.MoveWindow(self.hwnd_child, 0, 0, x, y, False)
|
||||
|
||||
# This uses scintilla to display a filename, and optionally jump to a line
|
||||
# number.
|
||||
class ScintillaShellView:
|
||||
_public_methods_ = shellcon.IShellView_Methods
|
||||
_com_interfaces_ = [pythoncom.IID_IOleWindow,
|
||||
shell.IID_IShellView,
|
||||
]
|
||||
def __init__(self, hwnd, filename, lineno = None):
|
||||
self.filename = filename
|
||||
self.lineno = lineno
|
||||
self.hwnd_parent = hwnd
|
||||
self.hwnd = None
|
||||
def _SendSci(self, msg, wparam=0, lparam=0):
|
||||
return win32gui.SendMessage(self.hwnd, msg, wparam, lparam)
|
||||
# IShellView
|
||||
def CreateViewWindow(self, prev, settings, browser, rect):
|
||||
print("ScintillaShellView.CreateViewWindow", prev, settings, browser, rect)
|
||||
# Make sure scintilla.dll is loaded. If not, find it on sys.path
|
||||
# (which it generally is for Pythonwin)
|
||||
try:
|
||||
win32api.GetModuleHandle("Scintilla.dll")
|
||||
except win32api.error:
|
||||
for p in sys.path:
|
||||
fname = os.path.join(p, "Scintilla.dll")
|
||||
if not os.path.isfile(fname):
|
||||
fname = os.path.join(p, "Build", "Scintilla.dll")
|
||||
if os.path.isfile(fname):
|
||||
win32api.LoadLibrary(fname)
|
||||
break
|
||||
else:
|
||||
raise RuntimeError("Can't find scintilla!")
|
||||
|
||||
style = win32con.WS_CHILD | win32con.WS_VSCROLL | \
|
||||
win32con.WS_HSCROLL | win32con.WS_CLIPCHILDREN | \
|
||||
win32con.WS_VISIBLE
|
||||
self.hwnd = win32gui.CreateWindow("Scintilla", "Scintilla", style,
|
||||
rect[0], rect[1], rect[2]-rect[0], rect[3]-rect[1],
|
||||
self.hwnd_parent, 1000, 0, None)
|
||||
|
||||
message_map = {
|
||||
win32con.WM_SIZE: self.OnSize,
|
||||
}
|
||||
# win32gui.SetWindowLong(self.hwnd, win32con.GWL_WNDPROC, message_map)
|
||||
|
||||
file_data = file(self.filename, "U").read()
|
||||
|
||||
self._SetupLexer()
|
||||
self._SendSci(scintillacon.SCI_ADDTEXT, len(file_data), file_data)
|
||||
if self.lineno != None:
|
||||
self._SendSci(scintillacon.SCI_GOTOLINE, self.lineno)
|
||||
print("Scintilla's hwnd is", self.hwnd)
|
||||
|
||||
def _SetupLexer(self):
|
||||
h = self.hwnd
|
||||
styles = [
|
||||
((0, 0, 200, 0, 0x808080), None, scintillacon.SCE_P_DEFAULT ),
|
||||
((0, 2, 200, 0, 0x008000), None, scintillacon.SCE_P_COMMENTLINE ),
|
||||
((0, 2, 200, 0, 0x808080), None, scintillacon.SCE_P_COMMENTBLOCK ),
|
||||
((0, 0, 200, 0, 0x808000), None, scintillacon.SCE_P_NUMBER ),
|
||||
((0, 0, 200, 0, 0x008080), None, scintillacon.SCE_P_STRING ),
|
||||
((0, 0, 200, 0, 0x008080), None, scintillacon.SCE_P_CHARACTER ),
|
||||
((0, 0, 200, 0, 0x008080), None, scintillacon.SCE_P_TRIPLE ),
|
||||
((0, 0, 200, 0, 0x008080), None, scintillacon.SCE_P_TRIPLEDOUBLE),
|
||||
((0, 0, 200, 0, 0x000000), 0x008080, scintillacon.SCE_P_STRINGEOL),
|
||||
((0, 1, 200, 0, 0x800000), None, scintillacon.SCE_P_WORD),
|
||||
((0, 1, 200, 0, 0xFF0000), None, scintillacon.SCE_P_CLASSNAME ),
|
||||
((0, 1, 200, 0, 0x808000), None, scintillacon.SCE_P_DEFNAME),
|
||||
((0, 0, 200, 0, 0x000000), None, scintillacon.SCE_P_OPERATOR),
|
||||
((0, 0, 200, 0, 0x000000), None, scintillacon.SCE_P_IDENTIFIER ),
|
||||
]
|
||||
self._SendSci(scintillacon.SCI_SETLEXER, scintillacon.SCLEX_PYTHON, 0)
|
||||
self._SendSci(scintillacon.SCI_SETSTYLEBITS, 5)
|
||||
baseFormat = (-402653169, 0, 200, 0, 0, 0, 49, 'Courier New')
|
||||
for f, bg, stylenum in styles:
|
||||
self._SendSci(scintillacon.SCI_STYLESETFORE, stylenum, f[4])
|
||||
self._SendSci(scintillacon.SCI_STYLESETFONT, stylenum, baseFormat[7])
|
||||
if f[1] & 1: self._SendSci(scintillacon.SCI_STYLESETBOLD, stylenum, 1)
|
||||
else: self._SendSci(scintillacon.SCI_STYLESETBOLD, stylenum, 0)
|
||||
if f[1] & 2: self._SendSci(scintillacon.SCI_STYLESETITALIC, stylenum, 1)
|
||||
else: self._SendSci(scintillacon.SCI_STYLESETITALIC, stylenum, 0)
|
||||
self._SendSci(scintillacon.SCI_STYLESETSIZE, stylenum, int(baseFormat[2]/20))
|
||||
if bg is not None:
|
||||
self._SendSci(scintillacon.SCI_STYLESETBACK, stylenum, bg)
|
||||
self._SendSci(scintillacon.SCI_STYLESETEOLFILLED, stylenum, 1) # Only needed for unclosed strings.
|
||||
|
||||
# IOleWindow
|
||||
def GetWindow(self):
|
||||
return self.hwnd
|
||||
|
||||
def UIActivate(self, activate_state):
|
||||
print("OnActivate")
|
||||
|
||||
def DestroyViewWindow(self):
|
||||
win32gui.DestroyWindow(self.hwnd)
|
||||
self.hwnd = None
|
||||
print("Destroyed scintilla window")
|
||||
|
||||
def TranslateAccelerator(self, msg):
|
||||
return winerror.S_FALSE
|
||||
|
||||
def OnSize(self, hwnd, msg, wparam, lparam):
|
||||
x = win32api.LOWORD(lparam)
|
||||
y = win32api.HIWORD(lparam)
|
||||
win32gui.MoveWindow(self.hwnd, 0, 0, x, y, False)
|
||||
|
||||
def DllRegisterServer():
|
||||
import winreg
|
||||
key = winreg.CreateKey(winreg.HKEY_LOCAL_MACHINE,
|
||||
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\" \
|
||||
"Explorer\\Desktop\\Namespace\\" + \
|
||||
ShellFolderRoot._reg_clsid_)
|
||||
winreg.SetValueEx(key, None, 0, winreg.REG_SZ, ShellFolderRoot._reg_desc_)
|
||||
# And special shell keys under our CLSID
|
||||
key = winreg.CreateKey(winreg.HKEY_CLASSES_ROOT,
|
||||
"CLSID\\" + ShellFolderRoot._reg_clsid_ + "\\ShellFolder")
|
||||
# 'Attributes' is an int stored as a binary! use struct
|
||||
attr = shellcon.SFGAO_FOLDER | shellcon.SFGAO_HASSUBFOLDER | \
|
||||
shellcon.SFGAO_BROWSABLE
|
||||
import struct
|
||||
s = struct.pack("i", attr)
|
||||
winreg.SetValueEx(key, "Attributes", 0, winreg.REG_BINARY, s)
|
||||
print(ShellFolderRoot._reg_desc_, "registration complete.")
|
||||
|
||||
def DllUnregisterServer():
|
||||
import winreg
|
||||
try:
|
||||
key = winreg.DeleteKey(winreg.HKEY_LOCAL_MACHINE,
|
||||
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\" \
|
||||
"Explorer\\Desktop\\Namespace\\" + \
|
||||
ShellFolderRoot._reg_clsid_)
|
||||
except WindowsError as details:
|
||||
import errno
|
||||
if details.errno != errno.ENOENT:
|
||||
raise
|
||||
print(ShellFolderRoot._reg_desc_, "unregistration complete.")
|
||||
|
||||
if __name__=='__main__':
|
||||
from win32com.server import register
|
||||
register.UseCommandLine(ShellFolderRoot,
|
||||
debug = debug,
|
||||
finalize_register = DllRegisterServer,
|
||||
finalize_unregister = DllUnregisterServer)
|
Loading…
Add table
Add a link
Reference in a new issue