PyXR

c:\projects\bitpim\src \ hexeditor.py



0001 #!/usr/bin/env python
0002 ### BITPIM
0003 ###
0004 ### Copyright (C) 2003-2004 Roger Binns <rogerb@rogerbinns.com>
0005 ###
0006 ### This program is free software; you can redistribute it and/or modify
0007 ### it under the terms of the BitPim license as detailed in the LICENSE file.
0008 ###
0009 ### $Id: hexeditor.py 4377 2007-08-27 04:58:33Z djpham $
0010 
0011 """A hex editor widget"""
0012 
0013 # system modules
0014 from __future__ import with_statement
0015 import string
0016 import struct
0017 
0018 # wx modules
0019 import wx
0020 from wx.lib import masked
0021 from wx.lib import scrolledpanel as scrolled
0022 
0023 # bitpim modules
0024 import common
0025 import guihelper
0026 
0027 #-------------------------------------------------------------------------------
0028 class DataStruct(object):
0029     def __init__(self, _name):
0030         self.name=_name
0031         self.fields=[]
0032     def set(self, dict):
0033         self.name=dict.keys()[0]
0034         self.fields=[]
0035         for f in dict[self.name]:
0036             if f['type']==DataItem.numeric_type:
0037                 item=NumericDataItem(f['name'])
0038             elif f['type']==DataItem.string_type:
0039                 item=StringDataItem(f['name'])
0040             else:
0041                 item=DataItem(f['name'], DataItem.struct_type)
0042             item.set(f)
0043             self.fields.append(item)
0044     def get(self):
0045         l=[]
0046         for f in self.fields:
0047             l.append(f.get())
0048         return { self.name: l }
0049     def encode(self, data, buffer_offset=0):
0050         # encode data to each & every fields and return the results
0051         l=[]
0052         start=0
0053         data_len=len(data)
0054         for f in self.fields:
0055             s=f.encode(data, start)
0056             start=f.start+f.len
0057             l.append( { '[0x%04X=%d]%s'%(f.start+buffer_offset,
0058                                          f.start+buffer_offset, f.name): `s` })
0059             if start>=data_len:
0060                 break
0061         return l
0062 
0063 #-------------------------------------------------------------------------------
0064 class DataItem(object):
0065     """Represent a data item/component with in a record, which is a list of
0066     these things.
0067     """
0068     offset_from_start='From Start'
0069     offset_from_prev='From Last Field'
0070     string_type='string'
0071     numeric_type='numeric'
0072     struct_type='struct'
0073     def __init__(self, _name, _type=numeric_type):
0074         self.name=_name
0075         self.offset_type=self.offset_from_start
0076         self.offset=0
0077         self.size=1
0078         self.start=self.len=None   # start & length/size of actual data encoded
0079         # numeric fields
0080         self.unsigned=True
0081         self.LE=True
0082         # string fields
0083         self.fixed=True
0084         self.null_terminated=False
0085         self.type=_type
0086     def _get_type(self):
0087         return self._type
0088     def _set_type(self, _type):
0089         if _type not in (self.numeric_type, self.string_type, self.struct_type):
0090             raise TypeError
0091         self._type=_type
0092         if _type==self.numeric_type:
0093             self.__class__=NumericDataItem
0094         elif _type==self.string_type:
0095             self.__class__=StringDataItem
0096     type=property(fget=_get_type, fset=_set_type)
0097     def get(self):
0098         return { 'name': self.name, 'offset_type': self.offset_type,
0099                  'offset': self.offset, 'type': self.type }
0100     def set(self, d):
0101         self.name=d.get('name', '<None>')
0102         self.offset_type=d.get('offset_type', None)
0103         self.offset=d.get('offset', None)
0104         self.type=d.get('type', None)
0105     def encode(self, s, start=None):
0106         """Encode the value of this item based on the string s"""
0107         raise NotImplementedError
0108 
0109 #-------------------------------------------------------------------------------
0110 class NumericDataItem(DataItem):
0111     _fmts={ # struct pack/unpack formats
0112         True: { # unsigned
0113             True: { # little endian
0114                 1: 'B', 2: '<H', 4: '<I' }, # size
0115             False: { # big endian
0116                 1: 'B', 2: '>H', 4: '>I' } }, # size
0117         False: { # signed
0118             True: { # little endian
0119                 1: 'b', 2: '<h', 4: '<i' }, # size
0120             False: { # big endian
0121                 1: 'b', 2: '>h', 4: '>i' } } } # size
0122             
0123     def __init__(self, name):
0124         super(NumericDataItem, self).__init__(name, self.numeric_type)
0125     def get(self):
0126         r=super(NumericDataItem, self).get()
0127         r.update(
0128             { 'unsigned': self.unsigned,
0129               'little_endian': self.LE,
0130               'size': self.size })
0131         return r
0132     def set(self, d):
0133         super(NumericDataItem, self).set(d)
0134         if d.get('type', None)!=self.numeric_type:
0135             raise TypeError
0136         self.unsigned=d.get('unsigned', True)
0137         self.LE=d.get('little_endian', True)
0138         self.size=d.get('size', 1)
0139     def encode(self, s, start=None):
0140         fmt=self._fmts[self.unsigned][self.LE][self.size]
0141         self.len=struct.calcsize(fmt)
0142         if self.offset_type==self.offset_from_start:
0143             self.start=self.offset
0144         else:
0145             if start is None:
0146                 raise ValueError
0147             self.start=start+self.offset
0148         return struct.unpack(fmt, s[self.start:self.start+self.len])[0]
0149 
0150 #-------------------------------------------------------------------------------
0151 class StringDataItem(DataItem):
0152     def __init__(self, name):
0153         super(StringDataItem, self).__init__(name, self.string_type)
0154     def get(self):
0155         r=super(StringDataItem, self).get()
0156         r.update({ 'fixed': self.fixed, 'size': self.size,
0157                    'null_terminated': self.null_terminated })
0158         return r
0159     def set(self, d):
0160         super(StringDataItem, self).set(d)
0161         if d.get('type', None)!=self.string_type:
0162             raise TypeError
0163         self.fixed=d.get('fixed', True)
0164         self.size=d.get('size', 0)
0165         self.null_terminated=d.get('null_terminated', False)
0166     def encode(self, s, start=None):
0167         if self.offset_type==self.offset_from_start:
0168             self.start=self.offset
0169         else:
0170             if start is None:
0171                 raise ValueError
0172             self.start=start+self.offset
0173         if self.fixed:
0174             # fixed length string
0175             if self.size==-1:
0176                 # take all available space
0177                 self.len=len(s)-self.offset
0178                 s0=s[self.start:]
0179             else:
0180                 # fixed size
0181                 s0=s[self.start:self.start+self.size]
0182                 self.len=self.size
0183         else:
0184             # pascal style variable string
0185             self.len=ord(s[self.start])
0186             s0=s[self.start+1:self.start+1+self.len]
0187         if self.null_terminated:
0188             i=s0.find('\x00')
0189             if i==-1:
0190                 return s0
0191             else:
0192                 self.len=i
0193                 return s0[:i]
0194         else:
0195             return s0
0196 
0197 #-------------------------------------------------------------------------------
0198 class GeneralInfoSizer(wx.FlexGridSizer):
0199     def __init__(self, parent):
0200         super(GeneralInfoSizer, self).__init__(-1, 2, 5, 5)
0201         self.AddGrowableCol(1)
0202         self.Add(wx.StaticText(parent, -1, 'Struct Name:'), 0, wx.EXPAND|wx.ALL, 5)
0203         self._struct_name=wx.TextCtrl(parent, -1, '')
0204         self.Add(self._struct_name, 0, wx.EXPAND|wx.ALL, 5)
0205         self.Add(wx.StaticText(parent, -1, 'Field Name:'), 0, wx.EXPAND|wx.ALL, 5)
0206         self._name=wx.TextCtrl(parent, -1, '')
0207         self.Add(self._name, 0, wx.EXPAND|wx.ALL, 5)
0208         self.Add(wx.StaticText(parent, -1, 'Type:'), 0, wx.EXPAND|wx.ALL, 5)
0209         self._type=wx.ComboBox(parent, wx.NewId(),
0210                                 choices=[DataItem.numeric_type,
0211                                          DataItem.string_type],
0212                                 value=DataItem.numeric_type,
0213                                 style=wx.CB_DROPDOWN|wx.CB_READONLY)
0214         self.Add(self._type, 0, wx.EXPAND|wx.ALL, 5)
0215         self.Add(wx.StaticText(parent, -1, 'Offset Type:'), 0, wx.EXPAND|wx.ALL, 5)
0216         self._offset_type=wx.ComboBox(parent, -1,
0217                                        value=DataItem.offset_from_start,
0218                                        choices=[DataItem.offset_from_start,
0219                                                 DataItem.offset_from_prev],
0220                                        style=wx.CB_DROPDOWN|wx.CB_READONLY)
0221         self.Add(self._offset_type, 0, wx.EXPAND|wx.ALL, 5)
0222         self.Add(wx.StaticText(parent, -1, 'Offset Value:'), 0, wx.EXPAND|wx.ALL, 5)
0223         self._offset=masked.NumCtrl(parent, wx.NewId(),
0224                                     allowNegative=False,
0225                                     min=0)
0226         self.Add(self._offset, 0, wx.ALL, 5)
0227         self._fields_group=(self._name, self._type, self._offset_type,
0228                             self._offset)
0229     def set(self, data):
0230         if isinstance(data, DataStruct):
0231             self._struct_name.SetValue(data.name)
0232         elif isinstance(data, DataItem):
0233             self._name.SetValue(data.name)
0234             self._type.SetValue(data.type)
0235             self._offset_type.SetValue(data.offset_type)
0236             self._offset.SetValue(data.offset)
0237     def get(self, data):
0238         data.name=self._name.GetValue()
0239         data.type=self._type.GetValue()
0240         data.offset_type=self._offset_type.GetValue()
0241         data.offset=int(self._offset.GetValue())
0242         return data
0243     def show(self, show_struct=False, show_field=False):
0244         self._struct_name.Enable(show_struct)
0245         for w in self._fields_group:
0246             w.Enable(show_field)
0247     def _get_struct_name(self):
0248         return self._struct_name.GetValue()
0249     struct_name=property(fget=_get_struct_name)
0250     def _get_type(self):
0251         return self._type.GetValue()
0252     type=property(fget=_get_type)
0253     def _get_type_id(self):
0254         return self._type.GetId()
0255     type_id=property(fget=_get_type_id)
0256         
0257 #-------------------------------------------------------------------------------
0258 class NumericInfoSizer(wx.FlexGridSizer):
0259     _sign_choices=['Unsigned', 'Signed']
0260     _endian_choices=['Little Endian', 'Big Endian']
0261     _size_choices=['1', '2', '4']
0262     def __init__(self, parent):
0263         super(NumericInfoSizer, self).__init__(-1, 2, 5, 5)
0264         self.AddGrowableCol(1)
0265         self.Add(wx.StaticText(parent, -1, 'Signed:'), 0, wx.EXPAND|wx.ALL, 5)
0266         self._sign=wx.ComboBox(parent, -1, value=self._sign_choices[0],
0267                                choices=self._sign_choices,
0268                                style=wx.CB_DROPDOWN|wx.CB_READONLY)
0269         self.Add(self._sign, 0, wx.EXPAND|wx.ALL, 5)
0270         self.Add(wx.StaticText(parent, -1, 'Endian:'), 0, wx.EXPAND|wx.ALL, 5)
0271         self._endian=wx.ComboBox(parent, -1, value=self._endian_choices[0],
0272                                  choices=self._endian_choices,
0273                                  style=wx.CB_DROPDOWN|wx.CB_READONLY)
0274         self.Add(self._endian, 0, wx.EXPAND|wx.ALL, 5)
0275         self.Add(wx.StaticText(parent, -1, 'Size:'), 0, wx.EXPAND|wx.ALL, 5)
0276         self._size=wx.ComboBox(parent, -1, value=self._size_choices[0],
0277                                choices=self._size_choices,
0278                                style=wx.CB_DROPDOWN|wx.CB_READONLY)
0279         self.Add(self._size, 0, wx.EXPAND|wx.ALL, 5)
0280     def set(self, data):
0281         if data.unsigned:
0282             self._sign.SetValue(self._sign_choices[0])
0283         else:
0284             self._sign.SetValue(self._sign_choices[1])
0285         if data.LE:
0286             self._endian.SetValue(self._endian_choices[0])
0287         else:
0288             self._endian.SetValue(self._endian_choices[1])
0289         self._size.SetValue(`data.size`)
0290     def get(self, data):
0291         data.unsigned=self._sign.GetValue()==self._sign_choices[0]
0292         data.LE=self._endian.GetValue()==self._endian_choices[0]
0293         data.size=int(self._size.GetValue())
0294         return data
0295 
0296 #-------------------------------------------------------------------------------
0297 class StringInfoSizer(wx.FlexGridSizer):
0298     _fixed_choices=['Fixed', 'Pascal']
0299     def __init__(self, parent):
0300         super(StringInfoSizer, self).__init__(-1, 2, 5, 5)
0301         self.AddGrowableCol(1)
0302         self.Add(wx.StaticText(parent, -1, 'Fixed/Pascal:'), 0, wx.EXPAND|wx.ALL, 5)
0303         self._fixed=wx.ComboBox(parent, -1, value=self._fixed_choices[0],
0304                                  choices=self._fixed_choices,
0305                                  style=wx.CB_DROPDOWN|wx.CB_READONLY)
0306         self.Add(self._fixed, 0, wx.EXPAND|wx.ALL, 5)
0307         self.Add(wx.StaticText(parent, -1, 'Max Length:'), 0, wx.EXPAND|wx.ALL, 5)
0308         self._max_len=masked.NumCtrl(parent, -1, value=1, min=-1)
0309         self.Add(self._max_len, 0, wx.EXPAND|wx.ALL, 5)
0310         self.Add(wx.StaticText(parent, -1, 'Null Terminated:'), 0, wx.EXPAND|wx.ALL, 5)
0311         self._null_terminated=wx.CheckBox(parent, -1)
0312         self.Add(self._null_terminated, 0, wx.EXPAND|wx.ALL, 5)
0313     def set(self, data):
0314         if data.fixed:
0315             self._fixed.SetValue(self._fixed_choices[0])
0316         else:
0317             self._fixed.SetValue(self._fixed_choices[1])
0318         self._max_len.SetValue(`data.size`)
0319         self._null_terminated.SetValue(data.null_terminated)
0320     def get(self, data):
0321         data.fixed=self._fixed.GetValue()==self._fixed_choices[0]
0322         data.size=int(self._max_len.GetValue())
0323         data.null_terminated=self._null_terminated.GetValue()
0324         return data
0325         
0326 #-------------------------------------------------------------------------------
0327 class TemplateDialog(wx.Dialog):
0328     _type_choices=['Numeric', 'String']
0329     _struct_type='struct'
0330     _field_type='field'
0331     def __init__(self, parent):
0332         super(TemplateDialog, self).__init__(parent, -1,
0333                                              'Hex Template Editor',
0334                                              style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
0335         self._data=[]
0336         self._item_tree=self._numeric_bs=self._string_bs=None
0337         self._general_bs=None
0338         self._tree_root=None
0339         self._field_info=self._field_info_hbs=None
0340         self._info_sizer={ NumericDataItem.numeric_type: self._numeric_bs,
0341                            StringDataItem.string_type: self._string_bs }
0342         main_vbs=wx.BoxSizer(wx.VERTICAL)
0343         hbs1=wx.BoxSizer(wx.HORIZONTAL)
0344         hbs1.Add(self._create_tree_pane(), 1, wx.EXPAND|wx.ALL, 5)
0345         hbs1.Add(self._create_info_pane(), 2, wx.EXPAND|wx.ALL, 5)
0346         main_vbs.Add(hbs1, 1, wx.EXPAND|wx.ALL, 5)
0347         main_vbs.Add(wx.StaticLine(self, -1, style=wx.LI_HORIZONTAL), 0, wx.EXPAND|wx.ALL, 5)
0348         main_vbs.Add(self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.HELP), 0, wx.ALIGN_CENTRE|wx.ALL, 5)
0349         self.SetSizer(main_vbs)
0350         self.SetAutoLayout(True)
0351         main_vbs.Fit(self)
0352 
0353     def _create_tree_pane(self):
0354         vbs=wx.BoxSizer(wx.VERTICAL)
0355         sw=scrolled.ScrolledPanel(self, -1)
0356         self._item_tree=wx.TreeCtrl(sw, wx.NewId(),
0357                                     style=wx.TR_DEFAULT_STYLE|wx.TR_HAS_BUTTONS)
0358         wx.EVT_TREE_SEL_CHANGED(self, self._item_tree.GetId(),
0359                                 self._OnTreeSel)
0360         self._tree_root=self._item_tree.AddRoot('Data Templates')
0361         sw_bs=wx.BoxSizer(wx.VERTICAL)
0362         sw_bs.Add(self._item_tree, 1, wx.EXPAND|wx.ALL, 0)
0363         sw.SetSizer(sw_bs)
0364         sw.SetAutoLayout(True)
0365         sw_bs.Fit(sw)
0366         sw.SetupScrolling()
0367         vbs.Add(sw, 1, wx.EXPAND|wx.ALL, 5)
0368         hbs=wx.BoxSizer(wx.HORIZONTAL)
0369         hbs.Add(wx.Button(self, wx.ID_ADD, 'Add'), 0, wx.EXPAND|wx.ALL, 5)
0370         hbs.Add(wx.Button(self, wx.ID_DELETE, 'Delete'), 0, wx.EXPAND|wx.ALL, 5)
0371         wx.EVT_BUTTON(self, wx.ID_ADD, self._OnAdd)
0372         wx.EVT_BUTTON(self, wx.ID_DELETE, self._OnDelete)
0373         vbs.Add(hbs, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ALL, 5)
0374         return vbs
0375     def _create_info_pane(self):
0376         # main boxsize
0377         vbs=wx.BoxSizer(wx.VERTICAL)
0378         hbs=wx.BoxSizer(wx.HORIZONTAL)
0379         # Type & offset
0380         static_bs=wx.StaticBoxSizer(wx.StaticBox(self, -1, 'Field Type'),
0381                                     wx.VERTICAL)
0382         self._general_bs=GeneralInfoSizer(self)
0383         wx.EVT_COMBOBOX(self, self._general_bs.type_id, self._OnTypeChanged)
0384         static_bs.Add(self._general_bs, 0, wx.EXPAND|wx.ALL, 5)
0385         hbs.Add(static_bs, 0, wx.ALL, 5)
0386         # all info
0387         self._field_info=wx.StaticBoxSizer(wx.StaticBox(self, -1, 'Field Info'),
0388                                     wx.VERTICAL)
0389         # numeric info box
0390         self._numeric_bs=NumericInfoSizer(self)
0391         self._field_info.Add(self._numeric_bs, 0, wx.EXPAND|wx.ALL, 5)
0392         self._string_bs=StringInfoSizer(self)
0393         self._field_info.Add(self._string_bs, 0, wx.EXPAND|wx.ALL, 5)
0394         hbs.Add(self._field_info, 0, wx.ALL, 5)
0395         vbs.Add(hbs, 1, wx.EXPAND|wx.ALL, 5)
0396         hbs1=wx.BoxSizer(wx.HORIZONTAL)
0397         hbs1.Add(wx.Button(self, wx.ID_SAVE, 'Set'), 0, wx.EXPAND|wx.ALL, 5)
0398         hbs1.Add(wx.Button(self, wx.ID_REVERT, 'Revert'), 0, wx.EXPAND|wx.ALL, 5)
0399         wx.EVT_BUTTON(self, wx.ID_SAVE, self._OnSave)
0400         wx.EVT_BUTTON(self, wx.ID_REVERT, self._OnRevert)
0401         vbs.Add(hbs1, 0, wx.ALIGN_CENTER_HORIZONTAL, 0)
0402         self._field_info_hbs=hbs
0403         return vbs
0404     def _show_field_info(self, _struct=False, field=False,
0405                          numeric_field=False, string_field=False):
0406         # show/hide individual fields
0407         self._general_bs.show(_struct, field)
0408         self._field_info.Show(self._numeric_bs, numeric_field)
0409         self._field_info.Show(self._string_bs, string_field)
0410         self._field_info.Layout()
0411         self._field_info_hbs.Layout()
0412     def _populate(self):
0413         # clear the tree and repopulate
0414         self._item_tree.DeleteChildren(self._tree_root)
0415         for i,e in enumerate(self._data):
0416             item=self._item_tree.AppendItem(self._tree_root, e.name)
0417             self._item_tree.SetPyData(item, { 'type': self._struct_type,
0418                                               'index': i })
0419             for i1,e1 in enumerate(e.fields):
0420                 field_item=self._item_tree.AppendItem(item, e1.name)
0421                 self._item_tree.SetPyData(field_item, { 'type': self._field_type,
0422                                                         'index': i,
0423                                                         'field_index': i1 })
0424         self.expand()
0425     def _populate_struct(self, _item_index):
0426         self._general_bs.set(self._data[_item_index])
0427         self._show_field_info(True)
0428     def _populate_each(self, _struct_index, _item_index):
0429         _struct=self._data[_struct_index]
0430         _item=_struct.fields[_item_index]
0431         self._general_bs.set(_item)
0432         if _item.type==DataItem.numeric_type:
0433             self._show_field_info(True, True, True)
0434             self._numeric_bs.set(_item)
0435         else:
0436             self._show_field_info(True, True, False, True)
0437             self._string_bs.set(_item)
0438     def _OnTypeChanged(self, _):
0439         new_type=self._general_bs.type
0440         self._show_field_info(True, True, new_type==DataItem.numeric_type,
0441                               new_type==DataItem.string_type)
0442     def _OnAdd(self, _):
0443         sel_idx=self._item_tree.GetSelection()
0444         if not sel_idx.IsOk():
0445             return
0446         if sel_idx==self._tree_root:
0447             # add a new structure
0448             struct_item=DataStruct('New Struct')
0449             self._data.append(struct_item)
0450         else:
0451             # add a new field to the existing structure
0452             data_item=self._item_tree.GetPyData(sel_idx)
0453             item=NumericDataItem('New Field')
0454             self._data[data_item['index']].fields.append(item)
0455         self._populate()
0456     def _OnDelete(self, _):
0457         sel_idx=self._item_tree.GetSelection()
0458         if not sel_idx.IsOk():
0459             return
0460         node_data=self._item_tree.GetPyData(sel_idx)
0461         if node_data is None:
0462             return
0463         if node_data['type']==self._field_type:
0464             # del this field
0465             del self._data[node_data['index']].fields[node_data['field_index']]
0466         else:
0467             # del this struct and its fields
0468             del self._data[node_data['index']]
0469         # and re-populate the tree
0470         self._populate()
0471     def _OnSave(self, _):
0472         sel_idx=self._item_tree.GetSelection()
0473         if not sel_idx.IsOk():
0474             return
0475         node_data=self._item_tree.GetPyData(sel_idx)
0476         if node_data is None:
0477             return
0478         # update the struct name
0479         self._data[node_data['index']].name=self._general_bs.struct_name
0480         if node_data['type']==self._field_type:
0481             data_item=self._data[node_data['index']].\
0482                        fields[node_data['field_index']]
0483             data_item=self._general_bs.get(data_item)
0484             if data_item.type==DataItem.numeric_type:
0485                 data_item=self._numeric_bs.get(data_item)
0486             else:
0487                 data_item=self._string_bs.get(data_item)
0488             self._data[node_data['index']].fields[node_data['field_index']]=data_item
0489             self._item_tree.SetItemText(self._item_tree.GetItemParent(sel_idx),
0490                                         self._data[node_data['index']].name)
0491             self._item_tree.SetItemText(sel_idx, data_item.name)
0492         else:
0493             self._item_tree.SetItemText(sel_idx, self._data[node_data['index']].name)
0494             
0495     def _OnRevert(self, _):
0496         sel_idx=self._item_tree.GetSelection()
0497         if not sel_idx.IsOk():
0498             return
0499         node_data=self._item_tree.GetPyData(sel_idx)
0500         if node_data is None:
0501             self._show_field_info()
0502         else:
0503             self._populate_struct(node_data['index'])
0504             if node_data['type']==self._field_type:
0505                 self._populate_each(node_data['index'], node_data['field_index'])
0506 
0507     def _OnTreeSel(self, evt):
0508         sel_idx=evt.GetItem()
0509         if not sel_idx.IsOk():
0510             # invalid selection
0511             return
0512         item_data=self._item_tree.GetPyData(sel_idx)
0513         if item_data is None:
0514             self._show_field_info()
0515         else:
0516             self._populate_struct(item_data['index'])
0517             if item_data['type']==self._field_type:
0518                 self._populate_each(item_data['index'], item_data['field_index'])
0519     def expand(self):
0520         # expand the tree
0521         self._item_tree.Expand(self._tree_root)
0522         (id, cookie)=self._item_tree.GetFirstChild(self._tree_root)
0523         while id.IsOk():
0524             self._item_tree.Expand(id)
0525             (id, cookie)=self._item_tree.GetNextChild(self._tree_root, cookie)
0526     def set(self, l):
0527         self._data=l
0528         self._populate()
0529     def get(self):
0530         return self._data
0531         
0532 #-------------------------------------------------------------------------------
0533 class HexEditor(wx.ScrolledWindow):
0534 
0535     _addr_range=xrange(8)
0536     _hex_range_start=10
0537     _hex_range_start2=33
0538     _hex_range=xrange(_hex_range_start, 58)
0539     _ascii_range_start=60
0540     _ascii_range=xrange(60, 76)
0541 
0542     def __init__(self, parent, id=-1, style=wx.WANTS_CHARS,
0543                  _set_pos=None, _set_sel=None, _set_val=None):
0544         wx.ScrolledWindow.__init__(self, parent, id, style=style)
0545         self.parent=parent
0546         self.data=""
0547         self.title=""
0548         self.buffer=None
0549         self.hasfocus=False
0550         self.dragging=False
0551         self.current_ofs=None
0552         self._module=None
0553         self._templates=[]
0554         self._search_string=None
0555         # ways of displaying status
0556         self.set_pos=_set_pos or self._set_pos
0557         self.set_val=_set_val or self._set_val
0558         self.set_sel=_set_sel or self._set_sel
0559         # some GUI setup
0560         self.SetBackgroundColour("WHITE")
0561         self.SetCursor(wx.StockCursor(wx.CURSOR_IBEAM))
0562         self.sethighlight(wx.NamedColour("BLACK"), wx.NamedColour("YELLOW"))
0563         self.setnormal(wx.NamedColour("BLACK"), wx.NamedColour("WHITE"))
0564         self.setfont(wx.TheFontList.FindOrCreateFont(10, wx.MODERN, wx.NORMAL, wx.NORMAL))
0565         self.OnSize(None)
0566         self.highlightrange(None, None)
0567         # other stuff
0568         self._create_context_menu()
0569         self._map_events()
0570 
0571     def _map_events(self):
0572         wx.EVT_SCROLLWIN(self, self.OnScrollWin)
0573         wx.EVT_PAINT(self, self.OnPaint)
0574         wx.EVT_SIZE(self, self.OnSize)
0575         wx.EVT_ERASE_BACKGROUND(self, self.OnEraseBackground)
0576         wx.EVT_SET_FOCUS(self, self.OnGainFocus)
0577         wx.EVT_KILL_FOCUS(self, self.OnLoseFocus)
0578         wx.EVT_LEFT_DOWN(self, self.OnStartSelection)
0579         wx.EVT_LEFT_UP(self, self.OnEndSelection)
0580         wx.EVT_MOTION(self, self.OnMakeSelection)
0581         wx.EVT_RIGHT_UP(self, self.OnRightClick)
0582 
0583     def _create_context_menu(self):
0584         self._reload_menu_id=self._apply_menu_id=None
0585         menu_items=(
0586             ('File', (('Load', self.OnLoadFile),
0587                       ('Save As', self.OnSaveAs),
0588                       ('Save Selection As', self.OnSaveSelection),
0589                       ('Save Hexdump As', self.OnSaveHexdumpAs))),
0590             ('Set Selection', (('Start', self.OnStartSelMenu),
0591                                ('End', self.OnEndSelMenu))),
0592             ('Value', self.OnViewValue),
0593             ('Search', (('Search', self.OnSearch),
0594                         ('Search Again', self.OnSearchAgain))),
0595             ('Import Python Module', self.OnImportModule),
0596             ('Reload Python Module', self.OnReloadModule, '_reload_menu_id'),
0597             ('Apply Python Func', self.OnApplyFunc, '_apply_menu_id'),
0598             ('Template', (('Load', self.OnTemplateLoad),
0599                           ('Save As', self.OnTemplateSaveAs),
0600                           ('Edit', self.OnTemplateEdit),
0601                           ('Apply', self.OnTemplateApply)))
0602             )
0603         self._bgmenu=wx.Menu()
0604         for menu_item in menu_items:
0605             if isinstance(menu_item[1], tuple):
0606                 # submenu
0607                 sub_menu=wx.Menu()
0608                 for submenu_item in menu_item[1]:
0609                     id=wx.NewId()
0610                     sub_menu.Append(id, submenu_item[0])
0611                     wx.EVT_MENU(self, id, submenu_item[1])
0612                 self._bgmenu.AppendMenu(wx.NewId(), menu_item[0], sub_menu)
0613             else:
0614                 # regular menu item
0615                 id=wx.NewId()
0616                 self._bgmenu.Append(id, menu_item[0])
0617                 wx.EVT_MENU(self, id, menu_item[1])
0618                 if len(menu_item)>2:
0619                     # need to save menu ID
0620                     setattr(self, menu_item[2], id)
0621 
0622     def SetData(self, data):
0623         self.data=data
0624         self.needsupdate=True
0625         self.updatescrollbars()
0626         self.Refresh()
0627 
0628     def SetTitle(self, title):
0629         self.title=title
0630 
0631     def SetStatusDisplay(self, _set_pos=None, _set_sel=None, _set_val=None):
0632         self.set_pos=_set_pos or self._set_pos
0633         self.set_sel=_set_sel or self._set_sel
0634         self.set_val=_set_val or self._set_val
0635 
0636     def OnEraseBackground(self, _):
0637         pass
0638     def _set_pos(self, pos):
0639         pass
0640     def _set_sel(self, sel_start, sel_end):
0641         pass
0642     def _set_val(self, v):
0643         pass
0644 
0645     def _to_char_line(self, x, y):
0646         """Convert an x,y point to (char, line)
0647         """
0648         return x/self.charwidth, y/self.charheight
0649     def _to_xy(self, char, line):
0650         return char*self.charwidth, line*self.charheight
0651     def _to_buffer_offset(self, char, line):
0652         if char in self._hex_range:
0653             if char>self._hex_range_start2:
0654                 char-=1
0655             if ((char-self._hex_range_start)%3)<2:
0656                 return line*16+(char-self._hex_range_start)/3
0657         elif char in self._ascii_range:
0658             return line*16+char-self._ascii_range_start
0659     def _set_and_move(self, evt):
0660         c,l=self._to_char_line(evt.GetX(), evt.GetY())
0661         self.GetCaret().Move(self._to_xy(c, l))
0662         x0, y0=self.GetViewStart()
0663         char_x=c+x0
0664         line_y=l+y0
0665         return self._to_buffer_offset(char_x, line_y)
0666     _value_formats=(
0667         ('unsigned char', 'B', struct.calcsize('B')),
0668         ('signed char', 'b', struct.calcsize('b')),
0669         ('LE unsigned short', '<H', struct.calcsize('<H')),
0670         ('LE signed short', '<h', struct.calcsize('<h')),
0671         ('BE unsigned short', '>H', struct.calcsize('>H')),
0672         ('BE signed short', '>h', struct.calcsize('>h')),
0673         ('LE unsigned int', '<I', struct.calcsize('<I')),
0674         ('LE signed int', '<i', struct.calcsize('<i')),
0675         ('BE unsigned int', '>I', struct.calcsize('>I')),
0676         ('BE signed int', '>i', struct.calcsize('>i')),
0677         )
0678     def _gen_values(self, _data, _ofs):
0679         """ Generate the values of various number formats starting at the
0680         current offset.
0681         """
0682         n=_data[_ofs:]
0683         len_n=len(n)
0684         s='0x%X=%d'%(_ofs, _ofs)
0685         res=[{ 'Data Offset': s}, {'':''} ]
0686         for i,e in enumerate(self._value_formats):
0687             if len_n<e[2]:
0688                 continue
0689             v=struct.unpack(e[1], n[:e[2]])[0]
0690             if i%2:
0691                 s='%d'%v
0692             else:
0693                 fmt='0x%0'+str(e[2]*2)+'X=%d'
0694                 s=fmt%(v,v)
0695             res.append({ e[0]: s })
0696         return res
0697 
0698     def _apply_template(self, template_name):
0699         # if user specifies a block, encode that,
0700         if self.highlightstart is None or self.highlightstart==-1 or \
0701            self.highlightend is None or self.highlightend==-1:
0702             # no selection
0703             _data=self.data[self.current_ofs:]
0704             _ofs=self.current_ofs
0705         else:
0706             _data=self.data[self.highlightstart:self.highlightend]
0707             _ofs=self.highlightstart
0708         for f in self._templates:
0709             if f.name==template_name:
0710                 l=[{ 'Template': f.name },
0711                    { 'Data Offset': '0x%04X=%d'%(_ofs, _ofs) }]
0712                 return l+f.encode(_data, _ofs)
0713         return []
0714 
0715     def _display_result(self, result):
0716         """ Display the results from applying a Python routine over the data
0717         """
0718         s=''
0719         for d in result:
0720             for k,e in d.items():
0721                 s+=k+':\t'+e+'\n'
0722         guihelper.MessageDialog(self, s, 'Results', style=wx.OK)
0723 
0724     def OnLoadFile(self, _):
0725         with guihelper.WXDialogWrapper(wx.FileDialog(self, 'Select a file to load',
0726                                                      style=wx.OPEN|wx.FILE_MUST_EXIST),
0727                                        True) as (dlg, retcode):
0728             if retcode==wx.ID_OK:
0729                 self.SetData(file(dlg.GetPath(), 'rb').read())
0730     def OnSaveAs(self, _):
0731         with guihelper.WXDialogWrapper(wx.FileDialog(self, 'Select a file to save',
0732                                                      style=wx.SAVE|wx.OVERWRITE_PROMPT),
0733                                        True) as (dlg, retcode):
0734             if retcode==wx.ID_OK:
0735                 file(dlg.GetPath(), 'wb').write(self.data)
0736     def hexdumpdata(self):
0737         res=""
0738         l=len(self.data)
0739         if self.title:
0740             res += self.title+": "+`l`+" bytes\n"
0741         res += "<#! !#>\n"
0742         pos=0
0743         while pos<l:
0744             text="%08X "%(pos)
0745             line=self.data[pos:pos+16]
0746             for i in range(len(line)):
0747                 text+="%02X "%(ord(line[i]))
0748             text+="   "*(16-len(line))
0749             text+="    "
0750             for i in range(len(line)):
0751                 c=line[i]
0752                 if (ord(c)>=32 and string.printable.find(c)>=0):
0753                     text+=c
0754                 else:
0755                     text+='.'
0756             res+=text+"\n"
0757             pos+=16
0758         return res
0759         
0760     def OnSaveHexdumpAs(self, _):
0761         with guihelper.WXDialogWrapper(wx.FileDialog(self, 'Select a file to save',
0762                                                      style=wx.SAVE|wx.OVERWRITE_PROMPT),
0763                                        True) as (dlg, retcode):
0764             if retcode==wx.ID_OK:
0765                 file(dlg.GetPath(), 'wb').write(self.hexdumpdata())
0766     def OnSaveSelection(self, _):
0767         if self.highlightstart is None or self.highlightstart==-1 or \
0768            self.highlightend is None or self.highlightend==-1:
0769             # no selection
0770             return
0771         with guihelper.WXDialogWrapper(wx.FileDialog(self, 'Select a file to save',
0772                                                      style=wx.SAVE|wx.OVERWRITE_PROMPT),
0773                                        True) as (dlg, retcode):
0774             if retcode==wx.ID_OK:
0775                 file(dlg.GetPath(), 'wb').write(
0776                     self.data[self.highlightstart:self.highlightend])
0777 
0778     def OnReloadModule(self, _):
0779         try:
0780             reload(self._module)
0781         except:
0782             self._module=None
0783             guihelper.MessageDialog(self, 'Failed to reload module',
0784                                     'Reload Module Error',
0785                                     style=wx.OK|wx.ICON_ERROR)
0786 
0787     def OnApplyFunc(self, _):
0788         choices=[x for x in dir(self._module) \
0789                  if callable(getattr(self._module, x))]
0790         with guihelper.WXDialogWrapper(wx.SingleChoiceDialog(self, 'Select a function to apply:',
0791                                                              'Apply Python Func',
0792                                                              choices),
0793                                        True) as (dlg, retcode):
0794             if retcode==wx.ID_OK:
0795                 try:
0796                     res=getattr(self._module, dlg.GetStringSelection())(
0797                         self, self.data, self.current_ofs)
0798                     self._display_result(res)
0799                 except:
0800                     guihelper.MessageDialog(self, 'Apply Func raised an exception',
0801                                             'Apply Func Error',
0802                                             style=wx.OK|wx.ICON_ERROR)
0803 
0804     def OnImportModule(self, _):
0805         with guihelper.WXDialogWrapper(wx.TextEntryDialog(self, 'Enter the name of a Python Module:',
0806                                                           'Module Import'),
0807                                        True) as (dlg, retcode):
0808             if retcode==wx.ID_OK:
0809                 try:
0810                     self._module=common.importas(dlg.GetValue())
0811                 except ImportError:
0812                     self._module=None
0813                     guihelper.MessageDialog(self, 'Failed to import module: '+dlg.GetValue(),
0814                                             'Module Import Error',
0815                                              style=wx.OK|wx.ICON_ERROR)
0816 
0817     def OnStartSelMenu(self, evt):
0818         ofs=self.current_ofs
0819         if ofs is not None:
0820             self.highlightstart=ofs
0821             self.needsupdate=True
0822             self.Refresh()
0823         self.set_sel(self.highlightstart, self.highlightend)
0824             
0825     def OnEndSelMenu(self, _):
0826         ofs=self.current_ofs
0827         if ofs is not None:
0828             self.highlightend=ofs+1
0829             self.needsupdate=True
0830             self.Refresh()
0831         self.set_sel(self.highlightstart, self.highlightend)
0832 
0833     def OnViewValue(self, _):
0834         ofs=self.current_ofs
0835         if ofs is not None:
0836             self._display_result(self._gen_values(self.data, ofs))
0837 
0838     def OnStartSelection(self, evt):
0839         self.highlightstart=self.highlightend=None
0840         ofs=self._set_and_move(evt)
0841         if ofs is not None:
0842             self.highlightstart=ofs
0843             self.dragging=True
0844             self.set_val(self.data[ofs:])
0845         else:
0846             self.set_val(None)
0847         self.needsupdate=True
0848         self.Refresh()
0849         self.set_pos(ofs)
0850         self.set_sel(self.highlightstart, self.highlightend)
0851         
0852     def OnMakeSelection(self, evt):
0853         if not self.dragging:
0854             return
0855         ofs=self._set_and_move(evt)
0856         if ofs is not None:
0857             self.highlightend=ofs+1
0858             self.needsupdate=True
0859             self.Refresh()
0860         self.set_pos(ofs)
0861         self.set_sel(self.highlightstart, self.highlightend)
0862     def OnEndSelection(self, evt):
0863         self.dragging=False
0864         ofs=self._set_and_move(evt)
0865         self.set_pos(ofs)
0866         self.set_sel(self.highlightstart, self.highlightend)
0867 
0868     def OnRightClick(self, evt):
0869         self.current_ofs=self._set_and_move(evt)
0870         if self.current_ofs is None:
0871             self.set_val(None)
0872         else:
0873             self.set_val(self.data[self.current_ofs:])
0874         self.set_pos(self.current_ofs)
0875         self._bgmenu.Enable(self._apply_menu_id, self._module is not None)
0876         self._bgmenu.Enable(self._reload_menu_id, self._module is not None)
0877         self.PopupMenu(self._bgmenu, evt.GetPosition())
0878 
0879     def OnTemplateLoad(self, _):
0880         with guihelper.WXDialogWrapper(wx.FileDialog(self, 'Select a file to load',
0881                                                      wildcard='*.tmpl',
0882                                                      style=wx.OPEN|wx.FILE_MUST_EXIST),
0883                                        True) as (dlg, retcode):
0884             if retcode==wx.ID_OK:
0885                 result={}
0886                 try:
0887                     execfile(dlg.GetPath())
0888                 except UnicodeError:
0889                     common.unicode_execfile(dlg.GetPath())
0890                 exist_keys={}
0891                 for i,e in enumerate(self._templates):
0892                     exist_keys[e.name]=i
0893                 for d in result['templates']:
0894                     data_struct=DataStruct('new struct')
0895                     data_struct.set(d)
0896                     if exist_keys.has_key(data_struct.name):
0897                         self._templates[exist_keys[data_struct.name]]=data_struct
0898                     else:
0899                         self._templates.append(data_struct)
0900 
0901     def OnTemplateSaveAs(self, _):
0902         with guihelper.WXDialogWrapper(wx.FileDialog(self, 'Select a file to save',
0903                                                      wildcard='*.tmpl',
0904                                                      style=wx.SAVE|wx.OVERWRITE_PROMPT),
0905                                        True) as (dlg, retcode):
0906             if retcode==wx.ID_OK:
0907                 r=[x.get() for x in self._templates]
0908                 common.writeversionindexfile(dlg.GetPath(),
0909                                              { 'templates': r }, 1)
0910 
0911     def OnTemplateApply(self, _):
0912         if not self._templates:
0913             # no templates to apply
0914             return
0915         choices=[x.name for x in self._templates]
0916         with guihelper.WXDialogWrapper(wx.SingleChoiceDialog(self, 'Select a template to apply:',
0917                                                              'Apply Data Template',
0918                                                              choices),
0919                                        True) as (dlg, retcode):
0920             if retcode==wx.ID_OK:
0921                 try:
0922                     res=self._apply_template(dlg.GetStringSelection())
0923                     self._display_result(res)
0924                 except:
0925                     guihelper.MessageDialog(self, 'Apply Template raised an exception',
0926                                             'Apply Template Error',
0927                                             style=wx.OK|wx.ICON_ERROR),
0928 
0929     def OnTemplateEdit(self, _):
0930         dlg=TemplateDialog(self)
0931         dlg.set(self._templates)
0932         with guihelper.WXDialogWrapper(dlg, True) as (dlg, retcode):
0933             if retcode==wx.ID_OK:
0934                 self._templates=dlg.get()
0935 
0936     def OnSearch(self, evt):
0937         with guihelper.WXDialogWrapper(wx.TextEntryDialog(self, 'Enter data to search (1 0x23 045 ...):',
0938                                                           'Search Data'),
0939                                        True) as (dlg, retcode):
0940             if retcode==wx.ID_OK:
0941                 l=dlg.GetValue().split(' ')
0942                 s=''
0943                 for e in l:
0944                     if e[0:2]=='0x':
0945                         s+=chr(int(e, 16))
0946                     elif e[0]=='0':
0947                         s+=chr(int(e, 8))
0948                     else:
0949                         s+=chr(int(e))
0950                 i=self.data[self.current_ofs:].find(s)
0951                 if i!=-1:
0952                     self._search_string=s
0953                     self.highlightstart=i+self.current_ofs
0954                     self.highlightend=self.highlightstart+len(s)
0955                     self.needsupdate=True
0956                     self.Refresh()
0957                     self.set_sel(self.highlightstart, self.highlightend)
0958                 else:
0959                     self._search_string=None
0960 
0961     def OnSearchAgain(self, evt):
0962         if self._search_string is not None:
0963             i=self.data[self.current_ofs:].find(self._search_string)
0964             if i==-1:
0965                 return
0966             self.highlightstart=i+self.current_ofs
0967             self.highlightend=self.highlightstart+len(self._search_string)
0968             self.needsupdate=True
0969             self.Refresh()
0970             self.set_sel(self.highlightstart, self.highlightend)
0971 
0972     def OnSize(self, evt):
0973         # uncomment these lines to prevent going wider than is needed
0974         # if self.width>self.widthinchars*self.charwidth:
0975         #    self.SetClientSize( (self.widthinchars*self.charwidth, self.height) )
0976         if evt is None:
0977             self.width=(self.widthinchars+3)*self.charwidth
0978             self.height=self.charheight*20
0979             self.SetClientSize((self.width, self.height))
0980             self.SetCaret(wx.Caret(self, (self.charwidth, self.charheight)))
0981             self.GetCaret().Show(True)
0982         else:
0983             self.width,self.height=self.GetClientSizeTuple()
0984         self.needsupdate=True
0985 
0986     def OnGainFocus(self,_):
0987         self.hasfocus=True
0988         self.needsupdate=True
0989         self.Refresh()
0990 
0991     def OnLoseFocus(self,_):
0992         self.hasfocus=False
0993         self.needsupdate=True
0994         self.Refresh()
0995 
0996     def highlightrange(self, start, end):
0997         self.needsupdate=True
0998         self.highlightstart=start
0999         self.highlightend=end
1000         self.Refresh()
1001         self.set_pos(None)
1002         self.set_sel(self.highlightstart, self.highlightend)
1003         self.set_val(None)
1004 
1005     def _ishighlighted(self, pos):
1006         return pos>=self.highlightstart and pos<self.highlightend
1007 
1008     def sethighlight(self, foreground, background):
1009         self.highlight=foreground,background
1010 
1011     def setnormal(self, foreground, background):
1012         self.normal=foreground,background
1013 
1014     def setfont(self, font):
1015         dc=wx.ClientDC(self)
1016         dc.SetFont(font)
1017         self.charwidth, self.charheight=dc.GetTextExtent("M")
1018         self.font=font
1019         self.updatescrollbars()
1020 
1021     def updatescrollbars(self):
1022         # how many lines are we?
1023         lines=len(self.data)/16
1024         if lines==0 or len(self.data)%16:
1025             lines+=1
1026         self.datalines=lines
1027 ##        lines+=1 # status line
1028         # fixed width
1029         self.widthinchars=8+2+3*16+1+2+16
1030         self.SetScrollbars(self.charwidth, self.charheight, self.widthinchars, lines, self.GetViewStart()[0], self.GetViewStart()[1])
1031 
1032     def _setnormal(self,dc):
1033         dc.SetTextForeground(self.normal[0])
1034         dc.SetTextBackground(self.normal[1])
1035 
1036     def _sethighlight(self,dc):
1037         dc.SetTextForeground(self.highlight[0])
1038         dc.SetTextBackground(self.highlight[1])    
1039 
1040     def _setstatus(self,dc):
1041         dc.SetTextForeground(self.normal[1])
1042         dc.SetTextBackground(self.normal[0])
1043         dc.SetBrush(wx.BLACK_BRUSH)
1044         
1045 
1046     def OnDraw(self, dc):
1047         xd,yd=self.GetViewStart()
1048         st=0  # 0=normal, 1=highlight
1049         dc.BeginDrawing()
1050         dc.SetBackgroundMode(wx.SOLID)
1051         dc.SetFont(self.font)
1052         for line in range(yd, min(self.datalines, yd+self.height/self.charheight+1)):
1053             # address
1054             self._setnormal(dc)
1055             st=0
1056             dc.DrawText("%08X" % (line*16), 0, line*self.charheight)
1057             # bytes
1058             for i in range(16):
1059                 pos=line*16+i
1060                 if pos>=len(self.data):
1061                     break
1062                 hl=self._ishighlighted(pos)
1063                 if hl!=st:
1064                     if hl:
1065                         st=1
1066                         self._sethighlight(dc)
1067                     else:
1068                         st=0
1069                         self._setnormal(dc)
1070                 if hl:
1071                     space=""
1072                     if i<15:
1073                         if self._ishighlighted(pos+1):
1074                             space=" "
1075                             if i==7:
1076                                 space="  "
1077                 else:
1078                     space=""
1079                 c=self.data[pos]
1080                 dc.DrawText("%02X%s" % (ord(c),space), (10+(3*i)+(i>=8))*self.charwidth, line*self.charheight)
1081                 if not (ord(c)>=32 and string.printable.find(c)>=0):
1082                     c='.'
1083                 dc.DrawText(c, (10+(3*16)+2+i)*self.charwidth, line*self.charheight)
1084 
1085 ##        if self.hasfocus:
1086 ##            self._setstatus(dc)
1087 ##            w,h=self.GetClientSizeTuple()
1088 ##            dc.DrawRectangle(0,h-self.charheight+yd*self.charheight,self.widthinchars*self.charwidth,self.charheight)
1089 ##            dc.DrawText("A test of stuff "+`yd`, 0, h-self.charheight+yd*self.charheight)
1090                 
1091         dc.EndDrawing()
1092 
1093     def updatebuffer(self):
1094         if self.buffer is None or \
1095            self.buffer.GetWidth()!=self.width or \
1096            self.buffer.GetHeight()!=self.height:
1097             if self.buffer is not None:
1098                 del self.buffer
1099             self.buffer=wx.EmptyBitmap(self.width, self.height)
1100 
1101         mdc=wx.MemoryDC()
1102         mdc.SelectObject(self.buffer)
1103         mdc.SetBackground(wx.TheBrushList.FindOrCreateBrush(self.GetBackgroundColour(), wx.SOLID))
1104         mdc.Clear()
1105         self.PrepareDC(mdc)
1106         self.OnDraw(mdc)
1107         mdc.SelectObject(wx.NullBitmap)
1108         del mdc
1109 
1110     def OnPaint(self, event):
1111         if self.needsupdate:
1112             self.needsupdate=False
1113             self.updatebuffer()
1114         dc=wx.PaintDC(self)
1115         dc.BeginDrawing()
1116         dc.DrawBitmap(self.buffer, 0, 0, False)
1117         dc.EndDrawing()
1118 
1119     def OnScrollWin(self, event):
1120         self.needsupdate=True
1121         self.Refresh() # clear whole widget
1122         event.Skip() # default event handlers now do scrolling etc
1123 
1124 class HexEditorDialog(wx.Dialog):
1125     _pane_widths=[-2, -3, -4]
1126     _pos_pane_index=0
1127     _sel_pane_index=1
1128     _val_pane_index=2
1129     def __init__(self, parent, data='', title='BitPim Hex Editor', helpd_id=-1):
1130         super(HexEditorDialog, self).__init__(parent, -1, title,
1131                                               size=(500, 500),
1132                                               style=wx.DEFAULT_DIALOG_STYLE|\
1133                                               wx.RESIZE_BORDER)
1134         self._status_bar=wx.StatusBar(self, -1)
1135         self._status_bar.SetFieldsCount(len(self._pane_widths))
1136         self._status_bar.SetStatusWidths(self._pane_widths)
1137         vbs=wx.BoxSizer(wx.VERTICAL)
1138         self._hex_editor=HexEditor(self, _set_pos=self.set_pos,
1139                                    _set_val=self.set_val,
1140                                    _set_sel=self.set_sel)
1141         self._hex_editor.SetData(data)
1142         self._hex_editor.SetTitle(title)
1143         vbs.Add(self._hex_editor, 1, wx.EXPAND|wx.ALL, 5)
1144         vbs.Add(wx.StaticLine(self), 0, wx.EXPAND|wx.ALL, 5)
1145         ok_btn=wx.Button(self, wx.ID_OK, 'OK')
1146         vbs.Add(ok_btn, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
1147         vbs.Add(self._status_bar, 0, wx.EXPAND|wx.ALL, 0)
1148         self.SetSizer(vbs)
1149         self.SetAutoLayout(True)
1150         vbs.Fit(self)
1151     def set_pos(self, pos):
1152         """Display the current buffer offset in the format of
1153         Pos: 0x12=18
1154         """
1155         if pos is None:
1156             s=''
1157         else:
1158             s='Pos: 0x%X=%d'%(pos, pos)
1159         self._status_bar.SetStatusText(s, self._pos_pane_index)
1160     def set_sel(self, sel_start, sel_end):
1161         if sel_start is None or sel_start==-1 or\
1162            sel_end is None or sel_end ==-1:
1163             s=''
1164         else:
1165             sel_len=sel_end-sel_start
1166             sel_end-=1
1167             s='Sel: 0x%X=%d to 0x%X=%d (0x%X=%d bytes)'%(
1168                 sel_start, sel_start, sel_end, sel_end,
1169                 sel_len, sel_len)
1170         self._status_bar.SetStatusText(s, self._sel_pane_index)
1171     def set_val(self, v):
1172         if v:
1173             # char
1174             s='Val: 0x%02X=%d'%(ord(v[0]), ord(v[0]))
1175             if len(v)>1:
1176                 # short
1177                 u_s=struct.unpack('<H', v[:struct.calcsize('<H')])[0]
1178                 s+=' 0x%04X=%d'%(u_s,  u_s)
1179             if len(v)>3:
1180                 # int/long
1181                 u_i=struct.unpack('<I', v[:struct.calcsize('<I')])[0]
1182                 s+=' 0x%08X=%d'%(u_i, u_i)
1183         else:
1184             s=''
1185         self._status_bar.SetStatusText(s, self._val_pane_index)
1186 
1187     def set(self, data):
1188         self._hex_editor.SetData(data)
1189 
1190         
1191 if __name__=='__main__':
1192     import sys
1193 
1194     if len(sys.argv)!=2:
1195         print 'Usage:',sys.argv[0],'<File Name>'
1196         sys.exit(1)
1197     app=wx.PySimpleApp()
1198     with guihelper.WXDialogWrapper(HexEditorDialog(None, file(sys.argv[1], 'rb').read(),
1199                                                  sys.argv[1]),
1200                                    True):
1201         pass
1202     sys.exit(0)
1203 

Generated by PyXR 0.9.4