PageRenderTime 139ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/External.LCA_RESTRICTED/Languages/IronPython/27/Lib/site-packages/win32comext/shell/demos/servers/shell_view.py

http://github.com/IronLanguages/main
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
  1. # A sample shell namespace view
  2. # To demostrate:
  3. # * Execute this script to register the namespace.
  4. # * Open Windows Explorer, and locate the new "Python Path Shell Browser"
  5. # folder off "My Computer"
  6. # * Browse this tree - .py files are shown expandable, with classes and
  7. # methods selectable. Selecting a Python file, or a class/method, will
  8. # display the file using Scintilla.
  9. # Known problems:
  10. # * Classes and methods don't have icons - this is a demo, so we keep it small
  11. # See icon_handler.py for examples of how to work with icons.
  12. #
  13. #
  14. # Notes on PIDLs
  15. # PIDLS are complicated, but fairly well documented in MSDN. If you need to
  16. # do much with these shell extensions, you must understand their concept.
  17. # Here is a short-course, as it applies to this sample:
  18. # A PIDL identifies an item, much in the same way that a filename does
  19. # (however, the shell is not limited to displaying "files").
  20. # An "ItemID" is a single string, each being an item in the hierarchy.
  21. # A "PIDL" is a list of these strings.
  22. # All shell etc functions work with PIDLs, so even in the case where
  23. # an ItemID is conceptually used, a 1-item list is always used.
  24. # Conceptually, think of:
  25. # pidl = pathname.split("\\") # pidl is a list of "parent" items.
  26. # # each item is a string 'item id', but these are ever used directly
  27. # As there is no concept of passing a single item, to open a file using only
  28. # a relative filename, conceptually you would say:
  29. # open_file([filename]) # Pass a single-itemed relative "PIDL"
  30. # and continuing the analogy, a "listdir" type function would return a list
  31. # of single-itemed lists - each list containing the relative PIDL of the child.
  32. #
  33. # Each PIDL entry is a binary string, and may contain any character. For
  34. # PIDLs not created by you, they can not be interpreted - they are just
  35. # blobs. PIDLs created by you (ie, children of your IShellFolder) can
  36. # store and interpret the string however makes most sense for your application.
  37. # (but within PIDL rules - they must be persistable, etc)
  38. # There is no reason that pickled strings, for example, couldn't be used
  39. # as an EntryID.
  40. # This application takes a simple approach - each PIDL is a string of form
  41. # "directory\0directory_name", "file\0file_name" or
  42. # "object\0file_name\0class_name[.method_name"
  43. # The first string in each example is literal (ie, the word 'directory',
  44. # 'file' or 'object', and every other string is variable. We use '\0' as
  45. # a field sep just 'cos we can (and 'cos it can't possibly conflict with the
  46. # string content)
  47. import sys, os
  48. import thread
  49. import pyclbr
  50. import pythoncom
  51. import win32gui, win32gui_struct, win32api, win32con, winerror
  52. import commctrl
  53. from win32com.shell import shell, shellcon
  54. from win32com.server.util import wrap, NewEnum
  55. from win32com.server.exception import COMException
  56. from win32com.util import IIDToInterfaceName
  57. from pywin.scintilla import scintillacon
  58. # Set this to 1 to cause debug version to be registered and used. A debug
  59. # version will spew output to win32traceutil.
  60. debug=0
  61. if debug:
  62. import win32traceutil
  63. # markh is toying with an implementation that allows auto reload of a module
  64. # if this attribute exists.
  65. com_auto_reload = True
  66. # Helper function to get a system IShellFolder interface, and the PIDL within
  67. # that folder for an existing file/directory.
  68. def GetFolderAndPIDLForPath(filename):
  69. desktop = shell.SHGetDesktopFolder()
  70. info = desktop.ParseDisplayName(0, None, os.path.abspath(filename))
  71. cchEaten, pidl, attr = info
  72. # We must walk the ID list, looking for one child at a time.
  73. folder = desktop
  74. while len(pidl) > 1:
  75. this = pidl.pop(0)
  76. folder = folder.BindToObject([this], None, shell.IID_IShellFolder)
  77. # We are left with the pidl for the specific item. Leave it as
  78. # a list, so it remains a valid PIDL.
  79. return folder, pidl
  80. # A cache of pyclbr module objects, so we only parse a given filename once.
  81. clbr_modules = {} # Indexed by path, item is dict as returned from pyclbr
  82. def get_clbr_for_file(path):
  83. try:
  84. objects = clbr_modules[path]
  85. except KeyError:
  86. dir, filename = os.path.split(path)
  87. base, ext = os.path.splitext(filename)
  88. objects = pyclbr.readmodule_ex(base, [dir])
  89. clbr_modules[path] = objects
  90. return objects
  91. # Our COM interfaces.
  92. # Base class for a shell folder.
  93. # All child classes use a simple PIDL of the form:
  94. # "object_type\0object_name[\0extra ...]"
  95. class ShellFolderBase:
  96. _com_interfaces_ = [shell.IID_IBrowserFrameOptions,
  97. pythoncom.IID_IPersist,
  98. shell.IID_IPersistFolder,
  99. shell.IID_IShellFolder,
  100. ]
  101. _public_methods_ = shellcon.IBrowserFrame_Methods + \
  102. shellcon.IPersistFolder_Methods + \
  103. shellcon.IShellFolder_Methods
  104. def GetFrameOptions(self, mask):
  105. #print "GetFrameOptions", self, mask
  106. return 0
  107. def ParseDisplayName(self, hwnd, reserved, displayName, attr):
  108. print "ParseDisplayName", displayName
  109. # return cchEaten, pidl, attr
  110. def BindToStorage(self, pidl, bc, iid):
  111. print "BTS", iid, IIDToInterfaceName(iid)
  112. def BindToObject(self, pidl, bc, iid):
  113. # We may be passed a set of relative PIDLs here - ie
  114. # [pidl_of_dir, pidl_of_child_dir, pidl_of_file, pidl_of_function]
  115. # But each of our PIDLs keeps the fully qualified name anyway - so
  116. # just jump directly to the last.
  117. final_pidl = pidl[-1]
  118. typ, extra = final_pidl.split('\0', 1)
  119. if typ == "directory":
  120. klass = ShellFolderDirectory
  121. elif typ == "file":
  122. klass = ShellFolderFile
  123. elif typ == "object":
  124. klass = ShellFolderObject
  125. else:
  126. raise RuntimeError("What is " + repr(typ))
  127. ret = wrap(klass(extra), iid, useDispatcher = (debug>0))
  128. return ret
  129. # A ShellFolder for an object with CHILDREN on the file system
  130. # Note that this means our "File" folder is *not* a 'FileSystem' folder,
  131. # as it's children (functions and classes) are not on the file system.
  132. #
  133. class ShellFolderFileSystem(ShellFolderBase):
  134. def _GetFolderAndPIDLForPIDL(self, my_idl):
  135. typ, name = my_idl[0].split('\0')
  136. return GetFolderAndPIDLForPath(name)
  137. # Interface methods
  138. def CompareIDs(self, param, id1, id2):
  139. if id1 < id2:
  140. return -1
  141. if id1 == id2:
  142. return 0
  143. return 1
  144. def GetUIObjectOf(self, hwndOwner, pidls, iid, inout):
  145. # delegate to the shell.
  146. assert len(pidls)==1, "oops - arent expecting more than one!"
  147. pidl = pidls[0]
  148. folder, child_pidl = self._GetFolderAndPIDLForPIDL(pidl)
  149. try:
  150. inout, ret = folder.GetUIObjectOf(hwndOwner, [child_pidl], iid,
  151. inout, iid)
  152. except pythoncom.com_error, (hr, desc, exc, arg):
  153. raise COMException(hresult=hr)
  154. return inout, ret
  155. # return object of IID
  156. def GetDisplayNameOf(self, pidl, flags):
  157. # delegate to the shell.
  158. folder, child_pidl = self._GetFolderAndPIDLForPIDL(pidl)
  159. ret = folder.GetDisplayNameOf(child_pidl, flags)
  160. return ret
  161. def GetAttributesOf(self, pidls, attrFlags):
  162. ret_flags = -1
  163. for pidl in pidls:
  164. pidl = pidl[0] # ??
  165. typ, name = pidl.split('\0')
  166. flags = shellcon.SHGFI_ATTRIBUTES
  167. rc, info = shell.SHGetFileInfo(name, 0, flags)
  168. hIcon, iIcon, dwAttr, name, typeName = info
  169. # All our items, even files, have sub-items
  170. extras = shellcon.SFGAO_HASSUBFOLDER | \
  171. shellcon.SFGAO_FOLDER | \
  172. shellcon.SFGAO_FILESYSANCESTOR | \
  173. shellcon.SFGAO_BROWSABLE
  174. ret_flags &= (dwAttr | extras)
  175. return ret_flags
  176. class ShellFolderDirectory(ShellFolderFileSystem):
  177. def __init__(self, path):
  178. self.path = os.path.abspath(path)
  179. def CreateViewObject(self, hwnd, iid):
  180. # delegate to the shell.
  181. folder, child_pidl = GetFolderAndPIDLForPath(self.path)
  182. return folder.CreateViewObject(hwnd, iid)
  183. def EnumObjects(self, hwndOwner, flags):
  184. pidls = []
  185. for fname in os.listdir(self.path):
  186. fqn = os.path.join(self.path, fname)
  187. if os.path.isdir(fqn):
  188. type_name = "directory"
  189. type_class = ShellFolderDirectory
  190. else:
  191. base, ext = os.path.splitext(fname)
  192. if ext in [".py", ".pyw"]:
  193. type_class = ShellFolderFile
  194. type_name = "file"
  195. else:
  196. type_class = None
  197. if type_class is not None:
  198. pidls.append( [type_name + "\0" + fqn] )
  199. return NewEnum(pidls, iid=shell.IID_IEnumIDList,
  200. useDispatcher=(debug>0))
  201. def GetDisplayNameOf(self, pidl, flags):
  202. final_pidl=pidl[-1]
  203. full_fname=final_pidl.split('\0')[-1]
  204. return os.path.split(full_fname)[-1]
  205. def GetAttributesOf(self, pidls, attrFlags):
  206. return shellcon.SFGAO_HASSUBFOLDER|shellcon.SFGAO_FOLDER|shellcon.SFGAO_FILESYSANCESTOR|shellcon.SFGAO_BROWSABLE
  207. # As per comments above, even though this manages a file, it is *not* a
  208. # ShellFolderFileSystem, as the children are not on the file system.
  209. class ShellFolderFile(ShellFolderBase):
  210. def __init__(self, path):
  211. self.path = os.path.abspath(path)
  212. def EnumObjects(self, hwndOwner, flags):
  213. objects = get_clbr_for_file(self.path)
  214. pidls = []
  215. for name, ob in objects.iteritems():
  216. pidls.append( ["object\0" + self.path + "\0" + name] )
  217. return NewEnum(pidls, iid=shell.IID_IEnumIDList,
  218. useDispatcher=(debug>0))
  219. def GetAttributesOf(self, pidls, attrFlags):
  220. ret_flags = -1
  221. for pidl in pidls:
  222. assert len(pidl)==1, "Expecting relative pidls"
  223. pidl = pidl[0]
  224. typ, filename, obname = pidl.split('\0')
  225. obs = get_clbr_for_file(filename)
  226. ob = obs[obname]
  227. flags = shellcon.SFGAO_BROWSABLE | shellcon.SFGAO_FOLDER | \
  228. shellcon.SFGAO_FILESYSANCESTOR
  229. if hasattr(ob, "methods"):
  230. flags |= shellcon.SFGAO_HASSUBFOLDER
  231. ret_flags &= flags
  232. return ret_flags
  233. def GetDisplayNameOf(self, pidl, flags):
  234. assert len(pidl)==1, "Expecting relative PIDL"
  235. typ, fname, obname = pidl[0].split('\0')
  236. fqname = os.path.splitext(fname)[0] + "." + obname
  237. if flags & shellcon.SHGDN_INFOLDER:
  238. ret = obname
  239. else: # SHGDN_NORMAL is the default
  240. ret = fqname
  241. # No need to look at the SHGDN_FOR* modifiers.
  242. return ret
  243. def CreateViewObject(self, hwnd, iid):
  244. return wrap(ScintillaShellView(hwnd, self.path), iid, useDispatcher=debug>0)
  245. # A ShellFolder for our Python objects
  246. class ShellFolderObject(ShellFolderBase):
  247. def __init__(self, details):
  248. self.path, details = details.split('\0')
  249. if details.find(".")>0:
  250. self.class_name, self.method_name = details.split(".")
  251. else:
  252. self.class_name = details
  253. self.method_name = None
  254. def CreateViewObject(self, hwnd, iid):
  255. mod_objects = get_clbr_for_file(self.path)
  256. object = mod_objects[self.class_name]
  257. if self.method_name is None:
  258. lineno = object.lineno
  259. else:
  260. lineno = object.methods[self.method_name]
  261. return wrap(ScintillaShellView(hwnd, self.path, lineno),
  262. iid, useDispatcher=debug>0)
  263. def EnumObjects(self, hwndOwner, flags):
  264. assert self.method_name is None, "Should not be enuming methods!"
  265. mod_objects = get_clbr_for_file(self.path)
  266. my_objects = mod_objects[self.class_name]
  267. pidls = []
  268. for func_name, lineno in my_objects.methods.iteritems():
  269. pidl = ["object\0" + self.path + "\0" +
  270. self.class_name + "." + func_name]
  271. pidls.append(pidl)
  272. return NewEnum(pidls, iid=shell.IID_IEnumIDList,
  273. useDispatcher=(debug>0))
  274. def GetDisplayNameOf(self, pidl, flags):
  275. assert len(pidl)==1, "Expecting relative PIDL"
  276. typ, fname, obname = pidl[0].split('\0')
  277. class_name, method_name = obname.split(".")
  278. fqname = os.path.splitext(fname)[0] + "." + obname
  279. if flags & shellcon.SHGDN_INFOLDER:
  280. ret = method_name
  281. else: # SHGDN_NORMAL is the default
  282. ret = fqname
  283. # No need to look at the SHGDN_FOR* modifiers.
  284. return ret
  285. def GetAttributesOf(self, pidls, attrFlags):
  286. ret_flags = -1
  287. for pidl in pidls:
  288. assert len(pidl)==1, "Expecting relative pidls"
  289. flags = shellcon.SFGAO_BROWSABLE | shellcon.SFGAO_FOLDER | \
  290. shellcon.SFGAO_FILESYSANCESTOR
  291. ret_flags &= flags
  292. return ret_flags
  293. # The "Root" folder of our namespace. As all children are directories,
  294. # it is derived from ShellFolderFileSystem
  295. # This is the only COM object actually registered and externally created.
  296. class ShellFolderRoot(ShellFolderFileSystem):
  297. _reg_progid_ = "Python.ShellExtension.Folder"
  298. _reg_desc_ = "Python Path Shell Browser"
  299. _reg_clsid_ = "{f6287035-3074-4cb5-a8a6-d3c80e206944}"
  300. def GetClassID(self):
  301. return self._reg_clsid_
  302. def Initialize(self, pidl):
  303. # This is the PIDL of us, as created by the shell. This is our
  304. # top-level ID. All other items under us have PIDLs defined
  305. # by us - see the notes at the top of the file.
  306. #print "Initialize called with pidl", repr(pidl)
  307. self.pidl = pidl
  308. def CreateViewObject(self, hwnd, iid):
  309. return wrap(FileSystemView(self, hwnd), iid, useDispatcher=debug>0)
  310. def EnumObjects(self, hwndOwner, flags):
  311. items = [ ["directory\0" + p] for p in sys.path if os.path.isdir(p)]
  312. return NewEnum(items, iid=shell.IID_IEnumIDList,
  313. useDispatcher=(debug>0))
  314. def GetDisplayNameOf(self, pidl, flags):
  315. ## return full path for sys.path dirs, since they don't appear under a parent folder
  316. final_pidl=pidl[-1]
  317. display_name=final_pidl.split('\0')[-1]
  318. return display_name
  319. # Simple shell view implementations
  320. # Uses a builtin listview control to display simple lists of directories
  321. # or filenames.
  322. class FileSystemView:
  323. _public_methods_ = shellcon.IShellView_Methods
  324. _com_interfaces_ = [pythoncom.IID_IOleWindow,
  325. shell.IID_IShellView,
  326. ]
  327. def __init__(self, folder, hwnd):
  328. self.hwnd_parent = hwnd # provided by explorer.
  329. self.hwnd = None # intermediate window for catching command notifications.
  330. self.hwnd_child = None # our ListView
  331. self.activate_state = None
  332. self.hmenu = None
  333. self.browser = None
  334. self.folder = folder
  335. self.children = None
  336. # IOleWindow
  337. def GetWindow(self):
  338. return self.hwnd
  339. def ContextSensitiveHelp(self, enter_mode):
  340. raise COMException(hresult=winerror.E_NOTIMPL)
  341. # IShellView
  342. def CreateViewWindow(self, prev, settings, browser, rect):
  343. print "FileSystemView.CreateViewWindow", prev, settings, browser, rect
  344. self.cur_foldersettings = settings
  345. self.browser = browser
  346. self._CreateMainWindow(prev, settings, browser, rect)
  347. self._CreateChildWindow(prev)
  348. # This isn't part of the sample, but the most convenient place to
  349. # test/demonstrate how you can get an IShellBrowser from a HWND
  350. # (but ONLY when you are in the same process as the IShellBrowser!)
  351. # Obviously it is not necessary here - we already have the browser!
  352. browser_ad = win32gui.SendMessage(self.hwnd_parent, win32con.WM_USER+7, 0, 0)
  353. browser_ob = pythoncom.ObjectFromAddress(browser_ad, shell.IID_IShellBrowser)
  354. assert browser==browser_ob
  355. # and make a call on the object to prove it doesn't die :)
  356. assert browser.QueryActiveShellView()==browser_ob.QueryActiveShellView()
  357. def _CreateMainWindow(self, prev, settings, browser, rect):
  358. # Creates a parent window that hosts the view window. This window
  359. # gets the control notifications etc sent from the child.
  360. style = win32con.WS_CHILD | win32con.WS_VISIBLE #
  361. wclass_name = "ShellViewDemo_DefView"
  362. # Register the Window class.
  363. wc = win32gui.WNDCLASS()
  364. wc.hInstance = win32gui.dllhandle
  365. wc.lpszClassName = wclass_name
  366. wc.style = win32con.CS_VREDRAW | win32con.CS_HREDRAW
  367. try:
  368. win32gui.RegisterClass(wc)
  369. except win32gui.error, details:
  370. # Should only happen when this module is reloaded
  371. if details[0] != winerror.ERROR_CLASS_ALREADY_EXISTS:
  372. raise
  373. message_map = {
  374. win32con.WM_DESTROY: self.OnDestroy,
  375. win32con.WM_COMMAND: self.OnCommand,
  376. win32con.WM_NOTIFY: self.OnNotify,
  377. win32con.WM_CONTEXTMENU: self.OnContextMenu,
  378. win32con.WM_SIZE: self.OnSize,
  379. }
  380. self.hwnd = win32gui.CreateWindow( wclass_name, "", style, \
  381. rect[0], rect[1], rect[2]-rect[0], rect[3]-rect[1],
  382. self.hwnd_parent, 0, win32gui.dllhandle, None)
  383. win32gui.SetWindowLong(self.hwnd, win32con.GWL_WNDPROC, message_map)
  384. print "View 's hwnd is", self.hwnd
  385. return self.hwnd
  386. def _CreateChildWindow(self, prev):
  387. # Creates the list view window.
  388. assert self.hwnd_child is None, "already have a window"
  389. assert self.cur_foldersettings is not None, "no settings"
  390. style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_BORDER | \
  391. commctrl.LVS_SHAREIMAGELISTS | commctrl.LVS_EDITLABELS
  392. view_mode, view_flags = self.cur_foldersettings
  393. if view_mode==shellcon.FVM_ICON:
  394. style |= commctrl.LVS_ICON | commctrl.LVS_AUTOARRANGE
  395. elif view_mode==shellcon.FVM_SMALLICON:
  396. style |= commctrl.LVS_SMALLICON | commctrl.LVS_AUTOARRANGE
  397. elif view_mode==shellcon.FVM_LIST:
  398. style |= commctrl.LVS_LIST | commctrl.LVS_AUTOARRANGE
  399. elif view_mode==shellcon.FVM_DETAILS:
  400. style |= commctrl.LVS_REPORT | commctrl.LVS_AUTOARRANGE
  401. else:
  402. # XP 'thumbnails' etc
  403. view_mode = shellcon.FVM_DETAILS
  404. # Default to 'report'
  405. style |= commctrl.LVS_REPORT | commctrl.LVS_AUTOARRANGE
  406. for f_flag, l_flag in [
  407. (shellcon.FWF_SINGLESEL, commctrl.LVS_SINGLESEL),
  408. (shellcon.FWF_ALIGNLEFT, commctrl.LVS_ALIGNLEFT),
  409. (shellcon.FWF_SHOWSELALWAYS, commctrl.LVS_SHOWSELALWAYS),
  410. ]:
  411. if view_flags & f_flag:
  412. style |= l_flag
  413. self.hwnd_child = win32gui.CreateWindowEx(
  414. win32con.WS_EX_CLIENTEDGE,
  415. "SysListView32", None, style,
  416. 0, 0, 0, 0,
  417. self.hwnd, 1000, 0, None)
  418. cr = win32gui.GetClientRect(self.hwnd)
  419. win32gui.MoveWindow(self.hwnd_child,
  420. 0, 0, cr[2]-cr[0], cr[3]-cr[1],
  421. True)
  422. # Setup the columns for the view.
  423. lvc, extras = win32gui_struct.PackLVCOLUMN(fmt=commctrl.LVCFMT_LEFT,
  424. subItem=1,
  425. text='Name',
  426. cx=300)
  427. win32gui.SendMessage(self.hwnd_child, commctrl.LVM_INSERTCOLUMN,
  428. 0, lvc)
  429. lvc, extras = win32gui_struct.PackLVCOLUMN(fmt=commctrl.LVCFMT_RIGHT,
  430. subItem=1,
  431. text='Exists',
  432. cx=50)
  433. win32gui.SendMessage(self.hwnd_child, commctrl.LVM_INSERTCOLUMN,
  434. 1, lvc)
  435. # and fill it with the content
  436. self.Refresh()
  437. def GetCurrentInfo(self):
  438. return self.cur_foldersettings
  439. def UIActivate(self, activate_state):
  440. print "OnActivate"
  441. def _OnActivate(self, activate_state):
  442. if self.activate_state == activate_state:
  443. return
  444. self._OnDeactivate() # restore menu's first, if necessary.
  445. if activate_state != shellcon.SVUIA_DEACTIVATE:
  446. assert self.hmenu is None, "Should have destroyed it!"
  447. self.hmenu = win32gui.CreateMenu()
  448. widths = 0,0,0,0,0,0
  449. # Ask explorer to add its standard items.
  450. self.browser.InsertMenusSB(self.hmenu, widths)
  451. # Merge with these standard items
  452. self._MergeMenus(activate_state)
  453. self.browser.SetMenuSB(self.hmenu, 0, self.hwnd);
  454. self.activate_state = activate_state
  455. def _OnDeactivate(self):
  456. if self.browser is not None and self.hmenu is not None:
  457. self.browser.SetMenuSB(0, 0, 0)
  458. self.browser.RemoveMenusSB(self.hmenu)
  459. win32gui.DestroyMenu(self.hmenu)
  460. self.hmenu = None
  461. self.hsubmenus = None
  462. self.activate_state = shellcon.SVUIA_DEACTIVATE
  463. def _MergeMenus(self, activate_state):
  464. # Merge the operations we support into the top-level menus.
  465. # NOTE: This function it *not* called each time the selection changes.
  466. # SVUIA_ACTIVATE_FOCUS really means "have a selection?"
  467. have_sel = activate_state == shellcon.SVUIA_ACTIVATE_FOCUS
  468. # only do "file" menu here, and only 1 item on it!
  469. mid = shellcon.FCIDM_MENU_FILE
  470. # Get the hmenu for the menu
  471. buf, extras = win32gui_struct.EmptyMENUITEMINFO(win32con.MIIM_SUBMENU)
  472. win32gui.GetMenuItemInfo(self.hmenu,
  473. mid,
  474. False,
  475. buf)
  476. data = win32gui_struct.UnpackMENUITEMINFO(buf)
  477. submenu = data[3]
  478. print "Do someting with the file menu!"
  479. def Refresh(self):
  480. stateMask = commctrl.LVIS_SELECTED | commctrl.LVIS_DROPHILITED
  481. state = 0
  482. self.children = []
  483. # Enumerate and store the child PIDLs
  484. for cid in self.folder.EnumObjects(self.hwnd, 0):
  485. self.children.append(cid)
  486. for row_index, data in enumerate(self.children):
  487. assert len(data)==1, "expecting just a child PIDL"
  488. typ, path = data[0].split('\0')
  489. desc = os.path.exists(path) and "Yes" or "No"
  490. prop_vals = (path, desc)
  491. # first col
  492. data, extras = win32gui_struct.PackLVITEM(
  493. item=row_index,
  494. subItem=0,
  495. text=prop_vals[0],
  496. state=state,
  497. stateMask=stateMask)
  498. win32gui.SendMessage(self.hwnd_child,
  499. commctrl.LVM_INSERTITEM,
  500. row_index, data)
  501. # rest of the cols.
  502. col_index = 1
  503. for prop_val in prop_vals[1:]:
  504. data, extras = win32gui_struct.PackLVITEM(
  505. item=row_index,
  506. subItem=col_index,
  507. text=prop_val)
  508. win32gui.SendMessage(self.hwnd_child,
  509. commctrl.LVM_SETITEM,
  510. 0, data)
  511. col_index += 1
  512. def SelectItem(self, pidl, flag):
  513. # For the sake of brevity, we don't implement this yet.
  514. # You would need to locate the index of the item in the shell-view
  515. # with that PIDL, then ask the list-view to select it.
  516. print "Please implement SelectItem for PIDL", pidl
  517. def GetItemObject(self, item_num, iid):
  518. raise COMException(hresult=winerror.E_NOTIMPL)
  519. def TranslateAccelerator(self, msg):
  520. return winerror.S_FALSE
  521. def DestroyViewWindow(self):
  522. win32gui.DestroyWindow(self.hwnd)
  523. self.hwnd = None
  524. print "Destroyed view window"
  525. # Message handlers.
  526. def OnDestroy(self, hwnd, msg, wparam, lparam):
  527. print "OnDestory"
  528. def OnCommand(self, hwnd, msg, wparam, lparam):
  529. print "OnCommand"
  530. def OnNotify(self, hwnd, msg, wparam, lparam):
  531. hwndFrom, idFrom, code = win32gui_struct.UnpackWMNOTIFY(lparam)
  532. #print "OnNotify code=0x%x (0x%x, 0x%x)" % (code, wparam, lparam)
  533. if code == commctrl.NM_SETFOCUS:
  534. # Control got focus - Explorer may not know - tell it
  535. if self.browser is not None:
  536. self.browser.OnViewWindowActive(None)
  537. # And do our menu thang
  538. self._OnActivate(shellcon.SVUIA_ACTIVATE_FOCUS)
  539. elif code == commctrl.NM_KILLFOCUS:
  540. self._OnDeactivate()
  541. elif code == commctrl.NM_DBLCLK:
  542. # This DblClick implementation leaves a little to be desired :)
  543. # It demonstrates some useful concepts, such as asking the
  544. # folder for its context-menu and invoking a command from it.
  545. # However, as our folder delegates IContextMenu to the shell
  546. # itself, the end result is that the folder is opened in
  547. # its "normal" place in Windows explorer rather than inside
  548. # our shell-extension.
  549. # Determine the selected items.
  550. sel = []
  551. n = -1
  552. while 1:
  553. n = win32gui.SendMessage(self.hwnd_child,
  554. commctrl.LVM_GETNEXTITEM,
  555. n,
  556. commctrl.LVNI_SELECTED)
  557. if n==-1:
  558. break
  559. sel.append(self.children[n][-1:])
  560. print "Selection is", sel
  561. hmenu = win32gui.CreateMenu()
  562. try:
  563. # Get the IContextMenu for the items.
  564. inout, cm = self.folder.GetUIObjectOf(self.hwnd_parent, sel,
  565. shell.IID_IContextMenu, 0)
  566. # As per 'Q179911', we need to determine if the default operation
  567. # should be 'open' or 'explore'
  568. flags = shellcon.CMF_DEFAULTONLY
  569. try:
  570. self.browser.GetControlWindow(shellcon.FCW_TREE)
  571. flags |= shellcon.CMF_EXPLORE
  572. except pythoncom.com_error:
  573. pass
  574. # *sob* - delegating to the shell does work - but lands us
  575. # in the original location. Q179911 also shows that
  576. # ShellExecuteEx should work - but I can't make it work as
  577. # described (XP: function call succeeds, but another thread
  578. # shows a dialog with text of E_INVALID_PARAM, and new
  579. # Explorer window opens with desktop view. Vista: function
  580. # call succeeds, but no window created at all.
  581. # On Vista, I'd love to get an IExplorerBrowser interface
  582. # from the shell, but a QI fails, and although the
  583. # IShellBrowser does appear to support IServiceProvider, I
  584. # still can't get it
  585. if 0:
  586. id_cmd_first = 1 # TrackPopupMenu makes it hard to use 0
  587. cm.QueryContextMenu(hmenu, 0, id_cmd_first, -1, flags)
  588. # Find the default item in the returned menu.
  589. cmd = win32gui.GetMenuDefaultItem(hmenu, False, 0)
  590. if cmd == -1:
  591. print "Oops: _doDefaultActionFor found no default menu"
  592. else:
  593. ci = 0, self.hwnd_parent, cmd-id_cmd_first, None, None, 0, 0, 0
  594. cm.InvokeCommand(ci)
  595. else:
  596. rv = shell.ShellExecuteEx(
  597. hwnd = self.hwnd_parent,
  598. nShow=win32con.SW_NORMAL,
  599. lpClass="folder",
  600. lpVerb="explore",
  601. lpIDList=sel[0])
  602. print "ShellExecuteEx returned", rv
  603. finally:
  604. win32gui.DestroyMenu(hmenu)
  605. def OnContextMenu(self, hwnd, msg, wparam, lparam):
  606. # Get the selected items.
  607. pidls = []
  608. n = -1
  609. while 1:
  610. n = win32gui.SendMessage(self.hwnd_child,
  611. commctrl.LVM_GETNEXTITEM,
  612. n,
  613. commctrl.LVNI_SELECTED)
  614. if n==-1:
  615. break
  616. pidls.append(self.children[n][-1:])
  617. spt = win32api.GetCursorPos()
  618. if not pidls:
  619. print "Ignoring background click"
  620. return
  621. # Get the IContextMenu for the items.
  622. inout, cm = self.folder.GetUIObjectOf(self.hwnd_parent, pidls, shell.IID_IContextMenu, 0)
  623. hmenu = win32gui.CreatePopupMenu()
  624. sel = None
  625. # As per 'Q179911', we need to determine if the default operation
  626. # should be 'open' or 'explore'
  627. try:
  628. flags = 0
  629. try:
  630. self.browser.GetControlWindow(shellcon.FCW_TREE)
  631. flags |= shellcon.CMF_EXPLORE
  632. except pythoncom.com_error:
  633. pass
  634. id_cmd_first = 1 # TrackPopupMenu makes it hard to use 0
  635. cm.QueryContextMenu(hmenu, 0, id_cmd_first, -1, flags)
  636. tpm_flags = win32con.TPM_LEFTALIGN | win32con.TPM_RETURNCMD | \
  637. win32con.TPM_RIGHTBUTTON
  638. sel = win32gui.TrackPopupMenu(hmenu,
  639. tpm_flags,
  640. spt[0], spt[1],
  641. 0, self.hwnd, None)
  642. print "TrackPopupMenu returned", sel
  643. finally:
  644. win32gui.DestroyMenu(hmenu)
  645. if sel:
  646. ci = 0, self.hwnd_parent, sel-id_cmd_first, None, None, 0, 0, 0
  647. cm.InvokeCommand(ci)
  648. def OnSize(self, hwnd, msg, wparam, lparam):
  649. #print "OnSize", self.hwnd_child, win32api.LOWORD(lparam), win32api.HIWORD(lparam)
  650. if self.hwnd_child is not None:
  651. x = win32api.LOWORD(lparam)
  652. y = win32api.HIWORD(lparam)
  653. win32gui.MoveWindow(self.hwnd_child, 0, 0, x, y, False)
  654. # This uses scintilla to display a filename, and optionally jump to a line
  655. # number.
  656. class ScintillaShellView:
  657. _public_methods_ = shellcon.IShellView_Methods
  658. _com_interfaces_ = [pythoncom.IID_IOleWindow,
  659. shell.IID_IShellView,
  660. ]
  661. def __init__(self, hwnd, filename, lineno = None):
  662. self.filename = filename
  663. self.lineno = lineno
  664. self.hwnd_parent = hwnd
  665. self.hwnd = None
  666. def _SendSci(self, msg, wparam=0, lparam=0):
  667. return win32gui.SendMessage(self.hwnd, msg, wparam, lparam)
  668. # IShellView
  669. def CreateViewWindow(self, prev, settings, browser, rect):
  670. print "ScintillaShellView.CreateViewWindow", prev, settings, browser, rect
  671. # Make sure scintilla.dll is loaded. If not, find it on sys.path
  672. # (which it generally is for Pythonwin)
  673. try:
  674. win32api.GetModuleHandle("Scintilla.dll")
  675. except win32api.error:
  676. for p in sys.path:
  677. fname = os.path.join(p, "Scintilla.dll")
  678. if not os.path.isfile(fname):
  679. fname = os.path.join(p, "Build", "Scintilla.dll")
  680. if os.path.isfile(fname):
  681. win32api.LoadLibrary(fname)
  682. break
  683. else:
  684. raise RuntimeError("Can't find scintilla!")
  685. style = win32con.WS_CHILD | win32con.WS_VSCROLL | \
  686. win32con.WS_HSCROLL | win32con.WS_CLIPCHILDREN | \
  687. win32con.WS_VISIBLE
  688. self.hwnd = win32gui.CreateWindow("Scintilla", "Scintilla", style,
  689. rect[0], rect[1], rect[2]-rect[0], rect[3]-rect[1],
  690. self.hwnd_parent, 1000, 0, None)
  691. message_map = {
  692. win32con.WM_SIZE: self.OnSize,
  693. }
  694. # win32gui.SetWindowLong(self.hwnd, win32con.GWL_WNDPROC, message_map)
  695. file_data = file(self.filename, "U").read()
  696. self._SetupLexer()
  697. self._SendSci(scintillacon.SCI_ADDTEXT, len(file_data), file_data)
  698. if self.lineno != None:
  699. self._SendSci(scintillacon.SCI_GOTOLINE, self.lineno)
  700. print "Scintilla's hwnd is", self.hwnd
  701. def _SetupLexer(self):
  702. h = self.hwnd
  703. styles = [
  704. ((0, 0, 200, 0, 0x808080), None, scintillacon.SCE_P_DEFAULT ),
  705. ((0, 2, 200, 0, 0x008000), None, scintillacon.SCE_P_COMMENTLINE ),
  706. ((0, 2, 200, 0, 0x808080), None, scintillacon.SCE_P_COMMENTBLOCK ),
  707. ((0, 0, 200, 0, 0x808000), None, scintillacon.SCE_P_NUMBER ),
  708. ((0, 0, 200, 0, 0x008080), None, scintillacon.SCE_P_STRING ),
  709. ((0, 0, 200, 0, 0x008080), None, scintillacon.SCE_P_CHARACTER ),
  710. ((0, 0, 200, 0, 0x008080), None, scintillacon.SCE_P_TRIPLE ),
  711. ((0, 0, 200, 0, 0x008080), None, scintillacon.SCE_P_TRIPLEDOUBLE),
  712. ((0, 0, 200, 0, 0x000000), 0x008080, scintillacon.SCE_P_STRINGEOL),
  713. ((0, 1, 200, 0, 0x800000), None, scintillacon.SCE_P_WORD),
  714. ((0, 1, 200, 0, 0xFF0000), None, scintillacon.SCE_P_CLASSNAME ),
  715. ((0, 1, 200, 0, 0x808000), None, scintillacon.SCE_P_DEFNAME),
  716. ((0, 0, 200, 0, 0x000000), None, scintillacon.SCE_P_OPERATOR),
  717. ((0, 0, 200, 0, 0x000000), None, scintillacon.SCE_P_IDENTIFIER ),
  718. ]
  719. self._SendSci(scintillacon.SCI_SETLEXER, scintillacon.SCLEX_PYTHON, 0)
  720. self._SendSci(scintillacon.SCI_SETSTYLEBITS, 5)
  721. baseFormat = (-402653169, 0, 200, 0, 0, 0, 49, 'Courier New')
  722. for f, bg, stylenum in styles:
  723. self._SendSci(scintillacon.SCI_STYLESETFORE, stylenum, f[4])
  724. self._SendSci(scintillacon.SCI_STYLESETFONT, stylenum, baseFormat[7])
  725. if f[1] & 1: self._SendSci(scintillacon.SCI_STYLESETBOLD, stylenum, 1)
  726. else: self._SendSci(scintillacon.SCI_STYLESETBOLD, stylenum, 0)
  727. if f[1] & 2: self._SendSci(scintillacon.SCI_STYLESETITALIC, stylenum, 1)
  728. else: self._SendSci(scintillacon.SCI_STYLESETITALIC, stylenum, 0)
  729. self._SendSci(scintillacon.SCI_STYLESETSIZE, stylenum, int(baseFormat[2]/20))
  730. if bg is not None:
  731. self._SendSci(scintillacon.SCI_STYLESETBACK, stylenum, bg)
  732. self._SendSci(scintillacon.SCI_STYLESETEOLFILLED, stylenum, 1) # Only needed for unclosed strings.
  733. # IOleWindow
  734. def GetWindow(self):
  735. return self.hwnd
  736. def UIActivate(self, activate_state):
  737. print "OnActivate"
  738. def DestroyViewWindow(self):
  739. win32gui.DestroyWindow(self.hwnd)
  740. self.hwnd = None
  741. print "Destroyed scintilla window"
  742. def TranslateAccelerator(self, msg):
  743. return winerror.S_FALSE
  744. def OnSize(self, hwnd, msg, wparam, lparam):
  745. x = win32api.LOWORD(lparam)
  746. y = win32api.HIWORD(lparam)
  747. win32gui.MoveWindow(self.hwnd, 0, 0, x, y, False)
  748. def DllRegisterServer():
  749. import _winreg
  750. key = _winreg.CreateKey(_winreg.HKEY_LOCAL_MACHINE,
  751. "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\" \
  752. "Explorer\\Desktop\\Namespace\\" + \
  753. ShellFolderRoot._reg_clsid_)
  754. _winreg.SetValueEx(key, None, 0, _winreg.REG_SZ, ShellFolderRoot._reg_desc_)
  755. # And special shell keys under our CLSID
  756. key = _winreg.CreateKey(_winreg.HKEY_CLASSES_ROOT,
  757. "CLSID\\" + ShellFolderRoot._reg_clsid_ + "\\ShellFolder")
  758. # 'Attributes' is an int stored as a binary! use struct
  759. attr = shellcon.SFGAO_FOLDER | shellcon.SFGAO_HASSUBFOLDER | \
  760. shellcon.SFGAO_BROWSABLE
  761. import struct
  762. s = struct.pack("i", attr)
  763. _winreg.SetValueEx(key, "Attributes", 0, _winreg.REG_BINARY, s)
  764. print ShellFolderRoot._reg_desc_, "registration complete."
  765. def DllUnregisterServer():
  766. import _winreg
  767. try:
  768. key = _winreg.DeleteKey(_winreg.HKEY_LOCAL_MACHINE,
  769. "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\" \
  770. "Explorer\\Desktop\\Namespace\\" + \
  771. ShellFolderRoot._reg_clsid_)
  772. except WindowsError, details:
  773. import errno
  774. if details.errno != errno.ENOENT:
  775. raise
  776. print ShellFolderRoot._reg_desc_, "unregistration complete."
  777. if __name__=='__main__':
  778. from win32com.server import register
  779. register.UseCommandLine(ShellFolderRoot,
  780. debug = debug,
  781. finalize_register = DllRegisterServer,
  782. finalize_unregister = DllUnregisterServer)