0001 # This is a work in progress - see Demos/win32gui_menu.py 0002 0003 # win32gui_struct.py - helpers for working with various win32gui structures. 0004 # As win32gui is "light-weight", it does not define objects for all possible 0005 # win32 structures - in general, "buffer" objects are passed around - it is 0006 # the callers responsibility to pack the buffer in the correct format. 0007 # 0008 # This module defines some helpers for the commonly used structures. 0009 # 0010 # In general, each structure has 3 functions: 0011 # 0012 # buffer, extras = PackSTRUCTURE(items, ...) 0013 # item, ... = UnpackSTRUCTURE(buffer) 0014 # buffer, extras = EmtpySTRUCTURE(...) 0015 # 0016 # 'extras' is always items that must be held along with the buffer, as the 0017 # buffer refers to these object's memory. 0018 # For structures that support a 'mask', this mask is hidden from the user - if 0019 # 'None' is passed, the mask flag will not be set, or on return, None will 0020 # be returned for the value if the mask is not set. 0021 # 0022 # NOTE: I considered making these structures look like real classes, and 0023 # support 'attributes' etc - however, ctypes already has a good structure 0024 # mechanism - I think it makes more sense to support ctype structures 0025 # at the win32gui level, then there will be no need for this module at all. 0026 0027 import win32gui 0028 import win32con 0029 import struct 0030 import array 0031 import commctrl 0032 0033 # Generic WM_NOTIFY unpacking 0034 def UnpackWMNOTIFY(lparam): 0035 format = "iii" 0036 buf = win32gui.PyMakeBuffer(struct.calcsize(format), lparam) 0037 hwndFrom, idFrom, code = struct.unpack(format, buf) 0038 code += 0x4f0000 # hrm - wtf - commctrl uses this, and it works with mfc. *sigh* 0039 return hwndFrom, idFrom, code 0040 0041 # MENUITEMINFO struct 0042 # http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/WinUI/WindowsUserInterface/Resources/Menus/MenuReference/MenuStructures/MENUITEMINFO.asp 0043 # We use the struct module to pack and unpack strings as MENUITEMINFO 0044 # structures. We also have special handling for the 'fMask' item in that 0045 # structure to avoid the caller needing to explicitly check validity 0046 # (None is used if the mask excludes/should exclude the value) 0047 menuitem_fmt = '9iP2i' 0048 0049 def PackMENUITEMINFO(fType=None, fState=None, wID=None, hSubMenu=None, 0050 hbmpChecked=None, hbmpUnchecked=None, dwTypeData=None, 0051 text=None, hbmpItem=None): 0052 # 'extras' are objects the caller must keep a reference to (as their 0053 # memory is used) for the lifetime of the INFO item. 0054 extras = [] 0055 fMask = 0 0056 if fType is None: fType = 0 0057 else: fMask |= win32con.MIIM_FTYPE 0058 if fState is None: fState = 0 0059 else: fMask |= win32con.MIIM_STATE 0060 if wID is None: wID = 0 0061 else: fMask |= win32con.MIIM_ID 0062 if hSubMenu is None: hSubMenu = 0 0063 else: fMask |= win32con.MIIM_SUBMENU 0064 if hbmpChecked is None: 0065 assert hbmpUnchecked is None, \ 0066 "neither or both checkmark bmps must be given" 0067 hbmpChecked = hbmpUnchecked = 0 0068 else: 0069 assert hbmpUnchecked is not None, \ 0070 "neither or both checkmark bmps must be given" 0071 fMask |= win32con.MIIM_CHECKMARKS 0072 if dwTypeData is None: dwTypeData = 0 0073 else: fMask |= win32con.MIIM_DATA 0074 if hbmpItem is None: hbmpItem = 0 0075 else: fMask |= win32con.MIIM_BITMAP 0076 if text is not None: 0077 fMask |= win32con.MIIM_STRING 0078 if isinstance(text, unicode): 0079 text = text.encode("mbcs") 0080 str_buf = array.array("c", text+'\0') 0081 cch = len(str_buf) 0082 # We are taking address of strbuf - it must not die until windows 0083 # has finished with our structure. 0084 lptext = str_buf.buffer_info()[0] 0085 extras.append(str_buf) 0086 else: 0087 lptext = 0 0088 cch = 0 0089 # Create the struct. 0090 dwItemData = 0 0091 item = struct.pack( 0092 menuitem_fmt, 0093 struct.calcsize(menuitem_fmt), # cbSize 0094 fMask, 0095 fType, 0096 fState, 0097 wID, 0098 hSubMenu, 0099 hbmpChecked, 0100 hbmpUnchecked, 0101 dwItemData, 0102 lptext, 0103 cch, 0104 hbmpItem 0105 ) 0106 # Now copy the string to a writable buffer, so that the result 0107 # could be passed to a 'Get' function 0108 return array.array("c", item), extras 0109 0110 def UnpackMENUITEMINFO(s): 0111 (cb, 0112 fMask, 0113 fType, 0114 fState, 0115 wID, 0116 hSubMenu, 0117 hbmpChecked, 0118 hbmpUnchecked, 0119 dwItemData, 0120 lptext, 0121 cch, 0122 hbmpItem) = struct.unpack(menuitem_fmt, s) 0123 assert cb==len(s) 0124 if fMask & win32con.MIIM_FTYPE==0: fType = None 0125 if fMask & win32con.MIIM_STATE==0: fState = None 0126 if fMask & win32con.MIIM_ID==0: wID = None 0127 if fMask & win32con.MIIM_SUBMENU==0: hSubMenu = None 0128 if fMask & win32con.MIIM_CHECKMARKS==0: hbmpChecked = hbmpUnchecked = None 0129 if fMask & win32con.MIIM_DATA==0: dwItemData = None 0130 if fMask & win32con.MIIM_BITMAP==0: hbmpItem = None 0131 if fMask & win32con.MIIM_STRING: 0132 text = win32gui.PyGetString(lptext, cch) 0133 else: 0134 text = None 0135 return fType, fState, wID, hSubMenu, hbmpChecked, hbmpUnchecked, \ 0136 dwItemData, text, hbmpItem 0137 0138 def EmptyMENUITEMINFO(mask = None, text_buf_size=512): 0139 extra = [] 0140 if mask is None: 0141 mask = win32con.MIIM_BITMAP | win32con.MIIM_CHECKMARKS | \ 0142 win32con.MIIM_DATA | win32con.MIIM_FTYPE | \ 0143 win32con.MIIM_ID | win32con.MIIM_STATE | \ 0144 win32con.MIIM_STRING | win32con.MIIM_SUBMENU | \ 0145 win32con.MIIM_TYPE 0146 0147 if mask & win32con.MIIM_STRING: 0148 text_buffer = array.array("c", "\0" * text_buf_size) 0149 extra.append(text_buffer) 0150 text_addr, text_len = text_buffer.buffer_info() 0151 else: 0152 text_addr = text_len = 0 0153 0154 buf = struct.pack( 0155 menuitem_fmt, 0156 struct.calcsize(menuitem_fmt), # cbSize 0157 mask, 0158 0, #fType, 0159 0, #fState, 0160 0, #wID, 0161 0, #hSubMenu, 0162 0, #hbmpChecked, 0163 0, #hbmpUnchecked, 0164 0, #dwItemData, 0165 text_addr, 0166 text_len, 0167 0, #hbmpItem 0168 ) 0169 return array.array("c", buf), extra 0170 0171 ########################################################################## 0172 # 0173 # Tree View structure support - TVITEM, TVINSERTSTRUCT and TVDISPINFO 0174 # 0175 ########################################################################## 0176 0177 # XXX - Note that the following implementation of TreeView structures is ripped 0178 # XXX - from the SpamBayes project. It may not quite work correctly yet - I 0179 # XXX - intend checking them later - but having them is better than not at all! 0180 0181 # Helpers for the ugly win32 structure packing/unpacking 0182 def _GetMaskAndVal(val, default, mask, flag): 0183 if val is None: 0184 return mask, default 0185 else: 0186 if flag is not None: 0187 mask |= flag 0188 return mask, val 0189 0190 def PackTVINSERTSTRUCT(parent, insertAfter, tvitem): 0191 tvitem_buf, extra = PackTVITEM(*tvitem) 0192 tvitem_buf = tvitem_buf.tostring() 0193 format = "ii%ds" % len(tvitem_buf) 0194 return struct.pack(format, parent, insertAfter, tvitem_buf), extra 0195 0196 def PackTVITEM(hitem, state, stateMask, text, image, selimage, citems, param): 0197 extra = [] # objects we must keep references to 0198 mask = 0 0199 mask, hitem = _GetMaskAndVal(hitem, 0, mask, commctrl.TVIF_HANDLE) 0200 mask, state = _GetMaskAndVal(state, 0, mask, commctrl.TVIF_STATE) 0201 if not mask & commctrl.TVIF_STATE: 0202 stateMask = 0 0203 mask, text = _GetMaskAndVal(text, None, mask, commctrl.TVIF_TEXT) 0204 mask, image = _GetMaskAndVal(image, 0, mask, commctrl.TVIF_IMAGE) 0205 mask, selimage = _GetMaskAndVal(selimage, 0, mask, commctrl.TVIF_SELECTEDIMAGE) 0206 mask, citems = _GetMaskAndVal(citems, 0, mask, commctrl.TVIF_CHILDREN) 0207 mask, param = _GetMaskAndVal(param, 0, mask, commctrl.TVIF_PARAM) 0208 if text is None: 0209 text_addr = text_len = 0 0210 else: 0211 if isinstance(text, unicode): 0212 text = text.encode("mbcs") 0213 text_buffer = array.array("c", text+"\0") 0214 extra.append(text_buffer) 0215 text_addr, text_len = text_buffer.buffer_info() 0216 format = "iiiiiiiiii" 0217 buf = struct.pack(format, 0218 mask, hitem, 0219 state, stateMask, 0220 text_addr, text_len, # text 0221 image, selimage, 0222 citems, param) 0223 return array.array("c", buf), extra 0224 0225 # Make a new buffer suitable for querying hitem's attributes. 0226 def EmptyTVITEM(hitem, mask = None, text_buf_size=512): 0227 extra = [] # objects we must keep references to 0228 if mask is None: 0229 mask = commctrl.TVIF_HANDLE | commctrl.TVIF_STATE | commctrl.TVIF_TEXT | \ 0230 commctrl.TVIF_IMAGE | commctrl.TVIF_SELECTEDIMAGE | \ 0231 commctrl.TVIF_CHILDREN | commctrl.TVIF_PARAM 0232 if mask & commctrl.TVIF_TEXT: 0233 text_buffer = array.array("c", "\0" * text_buf_size) 0234 extra.append(text_buffer) 0235 text_addr, text_len = text_buffer.buffer_info() 0236 else: 0237 text_addr = text_len = 0 0238 format = "iiiiiiiiii" 0239 buf = struct.pack(format, 0240 mask, hitem, 0241 0, 0, 0242 text_addr, text_len, # text 0243 0, 0, 0244 0, 0) 0245 return array.array("c", buf), extra 0246 0247 def UnpackTVITEM(buffer): 0248 item_mask, item_hItem, item_state, item_stateMask, \ 0249 item_textptr, item_cchText, item_image, item_selimage, \ 0250 item_cChildren, item_param = struct.unpack("10i", buffer) 0251 # ensure only items listed by the mask are valid (except we assume the 0252 # handle is always valid - some notifications (eg, TVN_ENDLABELEDIT) set a 0253 # mask that doesn't include the handle, but the docs explicity say it is.) 0254 if not (item_mask & commctrl.TVIF_TEXT): item_textptr = item_cchText = None 0255 if not (item_mask & commctrl.TVIF_CHILDREN): item_cChildren = None 0256 if not (item_mask & commctrl.TVIF_IMAGE): item_image = None 0257 if not (item_mask & commctrl.TVIF_PARAM): item_param = None 0258 if not (item_mask & commctrl.TVIF_SELECTEDIMAGE): item_selimage = None 0259 if not (item_mask & commctrl.TVIF_STATE): item_state = item_stateMask = None 0260 0261 if item_textptr: 0262 text = win32gui.PyGetString(item_textptr) 0263 else: 0264 text = None 0265 return item_hItem, item_state, item_stateMask, \ 0266 text, item_image, item_selimage, \ 0267 item_cChildren, item_param 0268 0269 # Unpack the lparm from a "TVNOTIFY" message 0270 def UnpackTVNOTIFY(lparam): 0271 format = "iiii40s40s" 0272 buf = win32gui.PyMakeBuffer(struct.calcsize(format), lparam) 0273 hwndFrom, id, code, action, buf_old, buf_new \ 0274 = struct.unpack(format, buf) 0275 item_old = UnpackTVITEM(buf_old) 0276 item_new = UnpackTVITEM(buf_new) 0277 return hwndFrom, id, code, action, item_old, item_new 0278 0279 def UnpackTVDISPINFO(lparam): 0280 format = "iii40s" 0281 buf = win32gui.PyMakeBuffer(struct.calcsize(format), lparam) 0282 hwndFrom, id, code, buf_item = struct.unpack(format, buf) 0283 item = UnpackTVITEM(buf_item) 0284 return hwndFrom, id, code, item 0285 0286 # 0287 # List view items 0288 def PackLVITEM(item=None, subItem=None, state=None, stateMask=None, text=None, image=None, param=None, indent=None): 0289 extra = [] # objects we must keep references to 0290 mask = 0 0291 mask, item = _GetMaskAndVal(item, 0, mask, None) 0292 mask, subItem = _GetMaskAndVal(subItem, 0, mask, None) 0293 mask, state = _GetMaskAndVal(state, 0, mask, commctrl.LVIF_STATE) 0294 if not mask & commctrl.LVIF_STATE: 0295 stateMask = 0 0296 else: 0297 if stateMask is None: 0298 stateMask = state 0299 mask, text = _GetMaskAndVal(text, None, mask, commctrl.LVIF_TEXT) 0300 mask, image = _GetMaskAndVal(image, 0, mask, commctrl.LVIF_IMAGE) 0301 mask, param = _GetMaskAndVal(param, 0, mask, commctrl.LVIF_PARAM) 0302 mask, indent = _GetMaskAndVal(indent, 0, mask, commctrl.LVIF_INDENT) 0303 if text is None: 0304 text_addr = text_len = 0 0305 else: 0306 if isinstance(text, unicode): 0307 text = text.encode("mbcs") 0308 text_buffer = array.array("c", text+"\0") 0309 extra.append(text_buffer) 0310 text_addr, text_len = text_buffer.buffer_info() 0311 format = "iiiiiiiiii" 0312 buf = struct.pack(format, 0313 mask, item, subItem, 0314 state, stateMask, 0315 text_addr, text_len, # text 0316 image, param, indent) 0317 return array.array("c", buf), extra 0318 0319 def UnpackLVITEM(buffer): 0320 item_mask, item_item, item_subItem, \ 0321 item_state, item_stateMask, \ 0322 item_textptr, item_cchText, item_image, \ 0323 item_param, item_indent = struct.unpack("10i", buffer) 0324 # ensure only items listed by the mask are valid 0325 if not (item_mask & commctrl.LVIF_TEXT): item_textptr = item_cchText = None 0326 if not (item_mask & commctrl.LVIF_IMAGE): item_image = None 0327 if not (item_mask & commctrl.LVIF_PARAM): item_param = None 0328 if not (item_mask & commctrl.LVIF_INDENT): item_indent = None 0329 if not (item_mask & commctrl.LVIF_STATE): item_state = item_stateMask = None 0330 0331 if item_textptr: 0332 text = win32gui.PyGetString(item_textptr) 0333 else: 0334 text = None 0335 return item_item, item_subItem, item_state, item_stateMask, \ 0336 text, item_image, item_param, item_indent 0337 0338 # Unpack an "LVNOTIFY" message 0339 def UnpackLVDISPINFO(lparam): 0340 format = "iii40s" 0341 buf = win32gui.PyMakeBuffer(struct.calcsize(format), lparam) 0342 hwndFrom, id, code, buf_item = struct.unpack(format, buf) 0343 item = UnpackLVITEM(buf_item) 0344 return hwndFrom, id, code, item 0345 0346 def UnpackLVNOTIFY(lparam): 0347 format = "3i8i" 0348 buf = win32gui.PyMakeBuffer(struct.calcsize(format), lparam) 0349 hwndFrom, id, code, item, subitem, newstate, oldstate, \ 0350 changed, pt_x, pt_y, lparam = struct.unpack(format, buf) 0351 return hwndFrom, id, code, item, subitem, newstate, oldstate, \ 0352 changed, (pt_x, pt_y), lparam 0353 0354 0355 # Make a new buffer suitable for querying an items attributes. 0356 def EmptyLVITEM(item, subitem, mask = None, text_buf_size=512): 0357 extra = [] # objects we must keep references to 0358 if mask is None: 0359 mask = commctrl.LVIF_IMAGE | commctrl.LVIF_INDENT | commctrl.LVIF_TEXT | \ 0360 commctrl.LVIF_PARAM | commctrl.LVIF_STATE 0361 if mask & commctrl.LVIF_TEXT: 0362 text_buffer = array.array("c", "\0" * text_buf_size) 0363 extra.append(text_buffer) 0364 text_addr, text_len = text_buffer.buffer_info() 0365 else: 0366 text_addr = text_len = 0 0367 format = "iiiiiiiiii" 0368 buf = struct.pack(format, 0369 mask, item, subitem, 0370 0, 0, 0371 text_addr, text_len, # text 0372 0, 0, 0) 0373 return array.array("c", buf), extra 0374 0375 0376 # List view column structure 0377 def PackLVCOLUMN(fmt=None, cx=None, text=None, subItem=None, image=None, order=None): 0378 extra = [] # objects we must keep references to 0379 mask = 0 0380 mask, fmt = _GetMaskAndVal(fmt, 0, mask, commctrl.LVCF_FMT) 0381 mask, cx = _GetMaskAndVal(cx, 0, mask, commctrl.LVCF_WIDTH) 0382 mask, text = _GetMaskAndVal(text, None, mask, commctrl.LVCF_TEXT) 0383 mask, subItem = _GetMaskAndVal(subItem, 0, mask, commctrl.LVCF_SUBITEM) 0384 mask, image = _GetMaskAndVal(image, 0, mask, commctrl.LVCF_IMAGE) 0385 mask, order= _GetMaskAndVal(order, 0, mask, commctrl.LVCF_ORDER) 0386 if text is None: 0387 text_addr = text_len = 0 0388 else: 0389 if isinstance(text, unicode): 0390 text = text.encode("mbcs") 0391 text_buffer = array.array("c", text+"\0") 0392 extra.append(text_buffer) 0393 text_addr, text_len = text_buffer.buffer_info() 0394 format = "iiiiiiii" 0395 buf = struct.pack(format, 0396 mask, fmt, cx, 0397 text_addr, text_len, # text 0398 subItem, image, order) 0399 return array.array("c", buf), extra 0400 0401 def UnpackLVCOLUMN(lparam): 0402 format = "iiiiiiii" 0403 mask, fmt, cx, text_addr, text_size, subItem, image, order = \ 0404 struct.unpack(format, lparam) 0405 # ensure only items listed by the mask are valid 0406 if not (mask & commctrl.LVCF_FMT): fmt = None 0407 if not (mask & commctrl.LVCF_WIDTH): cx = None 0408 if not (mask & commctrl.LVCF_TEXT): text_addr = test_size = None 0409 if not (mask & commctrl.LVCF_SUBITEM): subItem = None 0410 if not (mask & commctrl.LVCF_IMAGE): image = None 0411 if not (mask & commctrl.LVCF_ORDER): order = None 0412 if text_addr: 0413 text = win32gui.PyGetString(text_addr) 0414 else: 0415 text = None 0416 return fmt, cx, text, subItem, image, order 0417 0418 0419 # Make a new buffer suitable for querying an items attributes. 0420 def EmptyLVCOLUMN(mask = None, text_buf_size=512): 0421 extra = [] # objects we must keep references to 0422 if mask is None: 0423 mask = commctrl.LVCF_FMT | commctrl.LVCF_WIDTH | commctrl.LVCF_TEXT | \ 0424 commctrl.LVCF_SUBITEM | commctrl.LVCF_IMAGE | commctrl.LVCF_ORDER 0425 if mask & commctrl.LVCF_TEXT: 0426 text_buffer = array.array("c", "\0" * text_buf_size) 0427 extra.append(text_buffer) 0428 text_addr, text_len = text_buffer.buffer_info() 0429 else: 0430 text_addr = text_len = 0 0431 format = "iiiiiiii" 0432 buf = struct.pack(format, 0433 mask, 0, 0, 0434 text_addr, text_len, # text 0435 0, 0, 0) 0436 return array.array("c", buf), extra 0437 0438 # List view hit-test. 0439 def PackLVHITTEST(pt): 0440 format = "iiiii" 0441 buf = struct.pack(format, 0442 pt[0], pt[1], 0443 0, 0, 0) 0444 return array.array("c", buf), None 0445 0446 def UnpackLVHITTEST(buf): 0447 format = "iiiii" 0448 x, y, flags, item, subitem = struct.unpack(format, buf) 0449 return (x,y), flags, item, subitem 0450 0451 def PackHDITEM(cxy = None, text = None, hbm = None, fmt = None, 0452 param = None, image = None, order = None): 0453 extra = [] # objects we must keep references to 0454 mask = 0 0455 mask, cxy = _GetMaskAndVal(cxy, 0, mask, commctrl.HDI_HEIGHT) 0456 mask, text = _GetMaskAndVal(text, None, mask, commctrl.LVCF_TEXT) 0457 mask, hbm = _GetMaskAndVal(hbm, 0, mask, commctrl.HDI_BITMAP) 0458 mask, fmt = _GetMaskAndVal(fmt, 0, mask, commctrl.HDI_FORMAT) 0459 mask, param = _GetMaskAndVal(param, 0, mask, commctrl.HDI_LPARAM) 0460 mask, image = _GetMaskAndVal(image, 0, mask, commctrl.HDI_IMAGE) 0461 mask, order = _GetMaskAndVal(order, 0, mask, commctrl.HDI_ORDER) 0462 0463 if text is None: 0464 text_addr = text_len = 0 0465 else: 0466 if isinstance(text, unicode): 0467 text = text.encode("mbcs") 0468 text_buffer = array.array("c", text+"\0") 0469 extra.append(text_buffer) 0470 text_addr, text_len = text_buffer.buffer_info() 0471 0472 format = "iiiiiiiiiii" 0473 buf = struct.pack(format, 0474 mask, cxy, text_addr, hbm, text_len, 0475 fmt, param, image, order, 0, 0) 0476 return array.array("c", buf), extra 0477
Generated by PyXR 0.9.4