~zsquareplusc/yahebwinpy/wx.aui-test

« back to all changes in this revision

Viewing changes to plugins/hexeditorwx/diffcontrol.py

  • Committer: Chris Liechti
  • Date: 2009-10-13 23:35:32 UTC
  • Revision ID: cliechti@gmx.net-20091013233532-9486h1c8pr01c6y1
Re-Import new yahebwinpy code base. It's actually 3 years old and evolved over this time, but the history needs a cleanup.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
# -*- coding: ISO-8859-1 -*-
 
3
# generated by wxGlade 0.4 on Thu Mar 22 16:41:19 2007
 
4
 
 
5
import wx
 
6
from hexeditor.highlighter import color, Highlighter, Attribute       #XXX import... better use extension point?
 
7
from hexeditor import hexedit
 
8
from framework import util
 
9
from itertools import chain
 
10
 
 
11
def irange(fm, to, step=1):
 
12
    while fm < to:
 
13
        yield fm
 
14
        fm += step
 
15
 
 
16
def forward_blocks(fm, to, blocksize=512):
 
17
    while fm < to:
 
18
        yield fm, min(blocksize, to - fm)
 
19
        fm += blocksize
 
20
        
 
21
def reverse_blocks(fm, to, blocksize=512):
 
22
        while fm > to:
 
23
            fm -= blocksize
 
24
            if fm < to: blocksize -= to - fm; fm = to
 
25
            yield fm, blocksize
 
26
 
 
27
class DiffEdit:
 
28
    """keep two contexts, that can be switched back and forth"""
 
29
    def __init__(self, he):
 
30
        self.hexedit = he
 
31
        self.other_context = None
 
32
    
 
33
    def switch(self):
 
34
        # copy instance variables
 
35
        context = {}
 
36
        for key in ('fileobj', 'bin', 'filename', 'readonly', '_modified'):
 
37
            context[key] = self.hexedit.__dict__[key]
 
38
        new_context = self.other_context
 
39
        self.other_context = context
 
40
        # swap context
 
41
        if new_context is None:
 
42
            self.hexedit.close()
 
43
        else:
 
44
            self.hexedit.__dict__.update(new_context)
 
45
            #~ self.hexedit.send_event('OPEN', self.hexedit.filename)
 
46
            self.hexedit.send_event('MODIFY', self.hexedit._modified)
 
47
            self.hexedit.send_event('CHANGE', None, None)
 
48
 
 
49
 
 
50
 
 
51
class DiffControlPanel(wx.Panel, util.SimpleEventSink):
 
52
    def __init__(self, manager, parent, mainwindow):
 
53
        util.SimpleEventSink.__init__(self)
 
54
        self.hexview = mainwindow.main_widget
 
55
        self.hexeditor = mainwindow.main_widget.hexeditor
 
56
        self.diffedit = DiffEdit(mainwindow.main_widget.hexeditor)
 
57
        args = [parent]
 
58
        kwds = {}
 
59
        # begin wxGlade: DiffControlPanel.__init__
 
60
        kwds["style"] = wx.TAB_TRAVERSAL
 
61
        wx.Panel.__init__(self, *args, **kwds)
 
62
        self.button_toggle = wx.Button(self, -1, _("Toggle Files"))
 
63
        self.button_find_diff = wx.Button(self, -1, _("Find Next Difference"))
 
64
        self.button_previous_diff = wx.Button(self, -1, _("Find Previous Difference"))
 
65
        self.text_ctrl_info = wx.TextCtrl(self, -1, "", style=wx.TE_MULTILINE|wx.TE_READONLY)
 
66
 
 
67
        self.__set_properties()
 
68
        self.__do_layout()
 
69
 
 
70
        self.Bind(wx.EVT_BUTTON, self.on_toggle, self.button_toggle)
 
71
        self.Bind(wx.EVT_BUTTON, self.on_find_next, self.button_find_diff)
 
72
        self.Bind(wx.EVT_BUTTON, self.on_find_prev, self.button_previous_diff)
 
73
        # end wxGlade
 
74
        self.info()
 
75
        
 
76
        attribute = Attribute(
 
77
            foreground=color(0,200,0),
 
78
            bordercolor=color(50,255,10),
 
79
        )
 
80
        parent = self
 
81
        class DiffHighlighter(Highlighter):
 
82
            """colorize differences between two files"""
 
83
            # - - - highlighter interface
 
84
            def begin_screen_update(self, start_offset, end_offset):
 
85
                """called once per screen update. the offset range that is beeing
 
86
                   updated is passed as parameters"""
 
87
                self.cache_firstoffset = start_offset
 
88
                bin1 = parent.diffedit.other_context is not None and parent.diffedit.other_context['bin']
 
89
                bin2 = self.hexeditor.bin
 
90
                if bin1 and bin2:
 
91
                    self.different = [
 
92
                        bin1.read(offset, 1) != bin2.read(offset, 1)
 
93
                        for offset in irange(start_offset, end_offset+1)]
 
94
                else:
 
95
                    self.different = [False]*(end_offset-start_offset+1)
 
96
            
 
97
            def attribute_for_offset(self, offset):
 
98
                if self.different[offset - self.cache_firstoffset]:
 
99
                    return attribute
 
100
 
 
101
        manager['hexview.highlighter.diff'] = manager.make_extension_point(
 
102
            factory = DiffHighlighter,
 
103
            enabled = True,
 
104
            priority = -10,
 
105
        )
 
106
        self.hexeditor.subscribe(self)
 
107
        manager['hexeditor.diffcontrol'] = manager.make_extension_point(
 
108
            switch = self.diffedit.switch
 
109
        )
 
110
        self.Bind(wx.EVT_WINDOW_DESTROY, self.on_destroy)
 
111
 
 
112
    def __set_properties(self):
 
113
        # begin wxGlade: DiffControlPanel.__set_properties
 
114
        self.text_ctrl_info.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_INACTIVEBORDER))
 
115
        # end wxGlade
 
116
 
 
117
    def __do_layout(self):
 
118
        # begin wxGlade: DiffControlPanel.__do_layout
 
119
        sizer_1 = wx.BoxSizer(wx.VERTICAL)
 
120
        sizer_1.Add(self.button_toggle, 0, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
 
121
        sizer_1.Add(self.button_find_diff, 0, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
 
122
        sizer_1.Add(self.button_previous_diff, 0, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
 
123
        sizer_1.Add(self.text_ctrl_info, 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
 
124
        self.SetAutoLayout(True)
 
125
        self.SetSizer(sizer_1)
 
126
        sizer_1.Fit(self)
 
127
        sizer_1.SetSizeHints(self)
 
128
        # end wxGlade
 
129
 
 
130
    def can_close(self):
 
131
        self.diffedit.switch()
 
132
        if manager['gui.handle'].application.checkSaved(manager, self):
 
133
            self.hexeditor.close()
 
134
            self.diffedit.switch()
 
135
            return True
 
136
        return False
 
137
    
 
138
    def on_destroy(self, event):
 
139
            self.hexeditor.unsubscribe(self)
 
140
            del manager['hexeditor.diffcontrol']
 
141
            del manager['hexview.highlighter.diff']
 
142
            # XXX check other context for modifications, they're getting lost!
 
143
            event.Skip()
 
144
 
 
145
    def on_toggle(self, event): # wxGlade: DiffControlPanel.<event_handler>
 
146
        self.diffedit.switch()
 
147
    
 
148
    def on_find_next(self, event): # wxGlade: DiffControlPanel.<event_handler>
 
149
        offset = self.find_next_change()
 
150
        if offset is not None:
 
151
            self.hexview.setSelection(offset)
 
152
            manager['gui.utils.notification.statusbar'](_("Difference at offset %d" % (offset)))
 
153
    
 
154
    def on_find_prev(self, event): # wxGlade: DiffControlPanel.<event_handler>
 
155
        offset = self.find_next_change(False)
 
156
        if offset is not None:
 
157
            self.hexview.setSelection(offset)
 
158
            manager['gui.utils.notification.statusbar'](_("Difference at offset %d" % (offset)))
 
159
    
 
160
    def info(self):
 
161
        self.text_ctrl_info.SetValue("Shown:\n%s\n\nOther:\n%s" % (
 
162
            self.hexeditor.fileobj,
 
163
            self.diffedit.other_context is not None and self.diffedit.other_context['fileobj']
 
164
        ))
 
165
 
 
166
    def find_next_change(self, forward=True):
 
167
        bin1 = self.diffedit.other_context is not None and self.diffedit.other_context['bin']
 
168
        bin2 = self.hexeditor is not None and self.hexeditor.bin
 
169
        if bin1 and bin2:
 
170
            sel_start, sel_end = 0, self.hexeditor.bin.size()
 
171
            
 
172
            # search the strings
 
173
            progress_dlg = manager['hexeditor.dialogs.progress'](
 
174
                _("Searching for next difference..."),
 
175
                '',
 
176
                sel_start,
 
177
                sel_end
 
178
            )
 
179
            
 
180
            start_offset = self.hexview.getSelection()[0]
 
181
            relative_offset = 0
 
182
            try:
 
183
                try:
 
184
                    if forward:
 
185
                        for offset, blocksize in chain(
 
186
                            forward_blocks(start_offset+1, sel_end),
 
187
                            forward_blocks(sel_start, start_offset+1)
 
188
                        ):
 
189
                            b1 = bin1.read(offset, blocksize)
 
190
                            b2 = bin2.read(offset, blocksize)
 
191
                            if b1 != b2:
 
192
                                for x, (q1, q2) in enumerate(zip(b1,b2)):
 
193
                                    if q1 != q2:
 
194
                                        return offset+x
 
195
                                # if blocks do not have same length
 
196
                                return offset + min(len(b1), len(b2))
 
197
                            # update progressbar, check for abort
 
198
                            progress_dlg(relative_offset)
 
199
                            relative_offset += len(b1)
 
200
                        else:
 
201
                            manager['gui.utils.notification.message'](_("No difference found"))
 
202
                    else:
 
203
                        for offset, blocksize in chain(
 
204
                            reverse_blocks(start_offset, sel_start),
 
205
                            reverse_blocks(sel_end-1, start_offset)
 
206
                        ):
 
207
                            b1 = bin1.read(offset, blocksize)
 
208
                            b2 = bin2.read(offset, blocksize)
 
209
                            if b1 != b2:
 
210
                                for x, (q1, q2) in reversed(list(enumerate(zip(b1,b2)))):
 
211
                                    if q1 != q2:
 
212
                                        return offset+x
 
213
                                # if blocks do not have same length
 
214
                                return offset + min(len(b1), len(b2))
 
215
                            # update progressbar, check for abort
 
216
                            progress_dlg(relative_offset)
 
217
                            relative_offset += len(b1)
 
218
                        else:
 
219
                            manager['gui.utils.notification.message'](_("No difference found"))
 
220
                finally:
 
221
                    progress_dlg.close()
 
222
            except KeyboardInterrupt:
 
223
                manager['gui.utils.notification.error'](_("User aborted search"))
 
224
        else:
 
225
            manager['gui.utils.notification.error'](
 
226
                _("There are no two files!")
 
227
            )
 
228
 
 
229
    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
230
    # simple event handlers
 
231
    
 
232
    # source: hexeditor
 
233
    
 
234
    def event_CHANGE(self, offset=None, length=None):
 
235
        """update undo/redo menu entries"""
 
236
        self.info()
 
237
 
 
238
    def event_MODIFY(self, modified):
 
239
        """some data in model has changed"""
 
240
        self.info()
 
241
        
 
242
    def event_CLOSING(self):
 
243
        """file is beeing closed"""
 
244
        
 
245
    def event_CLOSED(self):
 
246
        """file was closed"""
 
247
        self.info()
 
248
    
 
249
    def event_OPEN(self, filename):
 
250
        """a new file has been opened"""
 
251
        self.info()
 
252
 
 
253
# end of class DiffControlPanel
 
254
 
 
255
 
 
256
 
 
257
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
258
# plugin interface
 
259
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
260
 
 
261
# list of dependant extension points
 
262
depends = [
 
263
    'i18n.translate',
 
264
    'gui.panels',
 
265
    #~ 'gui.handle.application',
 
266
    'hexeditor',
 
267
]
 
268
 
 
269
# list of provided extension points
 
270
provides = [
 
271
    'gui.panels.diffcontrol',
 
272
    'hexeditor.diffcontrol',
 
273
]
 
274
 
 
275
def init(manager):
 
276
    global _
 
277
    _ = manager['i18n.translate']
 
278
    
 
279
    manager['gui.panels.diffcontrol'] = manager.make_extension_point(
 
280
        __doc__ = 'Quickly switch between files and provide diff highlighting',
 
281
        label = _("Diff Control"),
 
282
        widget = DiffControlPanel,
 
283
        orientation = 'vertical',
 
284
        enabled = False,
 
285
        shortcut = 'SHIFT+CTRL+D',
 
286
    )