/External.LCA_RESTRICTED/Languages/IronPython/27/Lib/site-packages/win32comext/shell/demos/servers/shell_view.py
Python | 852 lines | 638 code | 58 blank | 156 comment | 82 complexity | a5032139a904e3d1aeb8adad791f5163 MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, ISC, GPL-2.0, MPL-2.0-no-copyleft-exception
- # 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, (hr, desc, exc, arg):
- 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.iteritems():
- 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.iteritems():
- 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, 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, 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)