origami 1.2.3 → 1.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +5 -4
- data/bin/gui/COPYING +674 -0
- data/bin/gui/about.rb +9 -9
- data/bin/gui/config.rb +7 -7
- data/bin/gui/file.rb +7 -7
- data/bin/gui/gtkhex.rb +1341 -0
- data/bin/gui/hexview.rb +16 -25
- data/bin/gui/imgview.rb +7 -7
- data/bin/gui/menu.rb +8 -8
- data/bin/gui/properties.rb +27 -14
- data/bin/gui/signing.rb +7 -7
- data/bin/gui/textview.rb +14 -17
- data/bin/gui/treeview.rb +8 -8
- data/bin/gui/walker.rb +7 -7
- data/bin/gui/xrefs.rb +7 -7
- data/bin/pdf2ruby +7 -3
- data/bin/pdfencrypt +2 -2
- data/bin/pdfextract +7 -7
- data/bin/pdfwalker +1 -1
- data/bin/shell/console.rb +2 -2
- data/lib/origami/encryption.rb +7 -9
- data/lib/origami/filters.rb +10 -0
- data/lib/origami/filters/ascii.rb +12 -7
- data/lib/origami/filters/ccitt.rb +14 -5
- data/lib/origami/filters/flate.rb +2 -11
- data/lib/origami/filters/lzw.rb +10 -3
- data/lib/origami/filters/runlength.rb +1 -1
- data/lib/origami/object.rb +7 -5
- data/lib/origami/parsers/pdf/linear.rb +6 -6
- data/lib/origami/pdf.rb +2 -2
- data/lib/origami/stream.rb +10 -9
- data/lib/origami/string.rb +9 -5
- metadata +44 -46
- data/bin/gui/hexdump.rb +0 -74
- data/lib/origami/docmdp.rb +0 -96
data/bin/gui/about.rb
CHANGED
|
@@ -4,22 +4,22 @@
|
|
|
4
4
|
about.rb
|
|
5
5
|
|
|
6
6
|
= Info
|
|
7
|
-
This file is part of
|
|
7
|
+
This file is part of PDF Walker, a graphical PDF file browser
|
|
8
8
|
Copyright (C) 2010 Guillaume Delugr� <guillaume@security-labs.org>
|
|
9
9
|
All right reserved.
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
it under the terms of the GNU
|
|
11
|
+
PDF Walker is free software: you can redistribute it and/or modify
|
|
12
|
+
it under the terms of the GNU General Public License as published by
|
|
13
13
|
the Free Software Foundation, either version 3 of the License, or
|
|
14
14
|
(at your option) any later version.
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
PDF Walker is distributed in the hope that it will be useful,
|
|
17
17
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
18
18
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
19
|
-
GNU
|
|
19
|
+
GNU General Public License for more details.
|
|
20
20
|
|
|
21
|
-
You should have received a copy of the GNU
|
|
22
|
-
along with
|
|
21
|
+
You should have received a copy of the GNU General Public License
|
|
22
|
+
along with PDF Walker. If not, see <http://www.gnu.org/licenses/>.
|
|
23
23
|
|
|
24
24
|
=end
|
|
25
25
|
|
|
@@ -34,9 +34,9 @@ module PDFWalker
|
|
|
34
34
|
:name => "PDF Walker",
|
|
35
35
|
:program_name => "PDF Walker",
|
|
36
36
|
:version => Origami::VERSION,
|
|
37
|
-
:copyright => "Copyright (C) 2010\nGuillaume Delugre
|
|
37
|
+
:copyright => "Copyright (C) 2010\nGuillaume Delugre <guillaume@security-labs.org>\nAll right reserved.",
|
|
38
38
|
:comments => "A graphical PDF parser front-end",
|
|
39
|
-
:license => File.read("#{File.dirname(__FILE__)}
|
|
39
|
+
:license => File.read("#{File.dirname(__FILE__)}/COPYING")
|
|
40
40
|
})
|
|
41
41
|
|
|
42
42
|
end
|
data/bin/gui/config.rb
CHANGED
|
@@ -4,22 +4,22 @@
|
|
|
4
4
|
config.rb
|
|
5
5
|
|
|
6
6
|
= Info
|
|
7
|
-
This file is part of
|
|
7
|
+
This file is part of PDF Walker, a graphical PDF file browser
|
|
8
8
|
Copyright (C) 2010 Guillaume Delugr� <guillaume@security-labs.org>
|
|
9
9
|
All right reserved.
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
it under the terms of the GNU
|
|
11
|
+
PDF Walker is free software: you can redistribute it and/or modify
|
|
12
|
+
it under the terms of the GNU General Public License as published by
|
|
13
13
|
the Free Software Foundation, either version 3 of the License, or
|
|
14
14
|
(at your option) any later version.
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
PDF Walker is distributed in the hope that it will be useful,
|
|
17
17
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
18
18
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
19
|
-
GNU
|
|
19
|
+
GNU General Public License for more details.
|
|
20
20
|
|
|
21
|
-
You should have received a copy of the GNU
|
|
22
|
-
along with
|
|
21
|
+
You should have received a copy of the GNU General Public License
|
|
22
|
+
along with PDF Walker. If not, see <http://www.gnu.org/licenses/>.
|
|
23
23
|
|
|
24
24
|
=end
|
|
25
25
|
|
data/bin/gui/file.rb
CHANGED
|
@@ -4,22 +4,22 @@
|
|
|
4
4
|
file.rb
|
|
5
5
|
|
|
6
6
|
= Info
|
|
7
|
-
This file is part of
|
|
7
|
+
This file is part of PDF Walker, a graphical PDF file browser
|
|
8
8
|
Copyright (C) 2010 Guillaume Delugr� <guillaume@security-labs.org>
|
|
9
9
|
All right reserved.
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
it under the terms of the GNU
|
|
11
|
+
PDF Walker is free software: you can redistribute it and/or modify
|
|
12
|
+
it under the terms of the GNU General Public License as published by
|
|
13
13
|
the Free Software Foundation, either version 3 of the License, or
|
|
14
14
|
(at your option) any later version.
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
PDF Walker is distributed in the hope that it will be useful,
|
|
17
17
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
18
18
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
19
|
-
GNU
|
|
19
|
+
GNU General Public License for more details.
|
|
20
20
|
|
|
21
|
-
You should have received a copy of the GNU
|
|
22
|
-
along with
|
|
21
|
+
You should have received a copy of the GNU General Public License
|
|
22
|
+
along with PDF Walker. If not, see <http://www.gnu.org/licenses/>.
|
|
23
23
|
|
|
24
24
|
=end
|
|
25
25
|
|
data/bin/gui/gtkhex.rb
ADDED
|
@@ -0,0 +1,1341 @@
|
|
|
1
|
+
=begin
|
|
2
|
+
|
|
3
|
+
= File
|
|
4
|
+
gtkhex.rb
|
|
5
|
+
|
|
6
|
+
= Info
|
|
7
|
+
This file is part of PDF Walker, a graphical PDF file browser
|
|
8
|
+
Copyright (C) 2010 Guillaume Delugré <guillaume@security-labs.org>
|
|
9
|
+
All right reserved.
|
|
10
|
+
|
|
11
|
+
PDF Walker is free software: you can redistribute it and/or modify
|
|
12
|
+
it under the terms of the GNU General Public License as published by
|
|
13
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
14
|
+
(at your option) any later version.
|
|
15
|
+
|
|
16
|
+
PDF Walker is distributed in the hope that it will be useful,
|
|
17
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
18
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
19
|
+
GNU General Public License for more details.
|
|
20
|
+
|
|
21
|
+
You should have received a copy of the GNU General Public License
|
|
22
|
+
along with PDF Walker. If not, see <http://www.gnu.org/licenses/>.
|
|
23
|
+
|
|
24
|
+
This work has been derived from the GHex project. Thanks to them.
|
|
25
|
+
Original implementation: Jaka Mocnik <jaka@gnu.org>
|
|
26
|
+
=end
|
|
27
|
+
|
|
28
|
+
require 'gtk2'
|
|
29
|
+
|
|
30
|
+
module Gtk
|
|
31
|
+
|
|
32
|
+
class HexEditor < Fixed
|
|
33
|
+
module View
|
|
34
|
+
HEX = 1
|
|
35
|
+
ASCII = 2
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
module Group
|
|
39
|
+
BYTE = 1
|
|
40
|
+
WORD = 2
|
|
41
|
+
LONG = 4
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
class Highlight
|
|
45
|
+
attr_accessor :start, :end
|
|
46
|
+
attr_accessor :start_line, :end_line
|
|
47
|
+
attr_accessor :style
|
|
48
|
+
attr_accessor :min_select
|
|
49
|
+
attr_accessor :valid
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
class AutoHighlight
|
|
53
|
+
attr_accessor :search_view
|
|
54
|
+
attr_accessor :search_string
|
|
55
|
+
attr_accessor :search_len
|
|
56
|
+
|
|
57
|
+
attr_accessor :color
|
|
58
|
+
|
|
59
|
+
attr_accessor :view_min
|
|
60
|
+
attr_accessor :view_max
|
|
61
|
+
|
|
62
|
+
attr_accessor :highlights
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
DEFAULT_FONT = "Monospace 12"
|
|
66
|
+
DEFAULT_CPL = 32
|
|
67
|
+
DEFAULT_LINES = 16
|
|
68
|
+
DISPLAY_BORDER = 4
|
|
69
|
+
SCROLL_TIMEOUT = 100
|
|
70
|
+
|
|
71
|
+
type_register
|
|
72
|
+
|
|
73
|
+
@@primary = Clipboard.get(Gdk::Selection::PRIMARY)
|
|
74
|
+
@@clipboard = Clipboard.get(Gdk::Selection::CLIPBOARD)
|
|
75
|
+
|
|
76
|
+
def initialize(data = '')
|
|
77
|
+
super()
|
|
78
|
+
|
|
79
|
+
@data = data
|
|
80
|
+
if RUBY_VERSION >= '1.9'
|
|
81
|
+
@data.force_encoding('binary')
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
@scroll_timeout = -1
|
|
85
|
+
@disp_buffer = ""
|
|
86
|
+
@starting_offset = 0
|
|
87
|
+
|
|
88
|
+
@xdisp_width = @adisp_width = 200
|
|
89
|
+
@xdisp_gc = @adisp_gc = nil
|
|
90
|
+
@active_view = View::HEX
|
|
91
|
+
@group_type = Group::BYTE
|
|
92
|
+
@lines = @vis_lines = @top_line = @cpl = 0
|
|
93
|
+
@cursor_pos = 0
|
|
94
|
+
@lower_nibble = false
|
|
95
|
+
@cursor_shown = false
|
|
96
|
+
@button = 0
|
|
97
|
+
@insert = false
|
|
98
|
+
@selecting = false
|
|
99
|
+
|
|
100
|
+
@selection = Highlight.new
|
|
101
|
+
@selection.start = @selection.end = 0
|
|
102
|
+
@selection.style = nil
|
|
103
|
+
@selection.min_select = 1
|
|
104
|
+
@selection.valid = false
|
|
105
|
+
|
|
106
|
+
@highlights = [ @selection ]
|
|
107
|
+
|
|
108
|
+
@auto_highlight = nil
|
|
109
|
+
|
|
110
|
+
@disp_font_metrics = load_font DEFAULT_FONT
|
|
111
|
+
@font_desc = Pango::FontDescription.new DEFAULT_FONT
|
|
112
|
+
|
|
113
|
+
@char_width = get_max_char_width(@disp_font_metrics)
|
|
114
|
+
@char_height = Pango.pixels(@disp_font_metrics.ascent) + Pango.pixels(@disp_font_metrics.descent) + 2
|
|
115
|
+
|
|
116
|
+
self.can_focus = true
|
|
117
|
+
self.events = Gdk::Event::KEY_PRESS_MASK
|
|
118
|
+
self.border_width = DISPLAY_BORDER
|
|
119
|
+
|
|
120
|
+
mouse_handler = lambda do |widget, event|
|
|
121
|
+
if event.event_type == Gdk::Event::BUTTON_RELEASE and event.button == 1
|
|
122
|
+
if @scroll_timeout
|
|
123
|
+
GLib::Source.remove @scroll_timeout
|
|
124
|
+
@scroll_timeout = nil
|
|
125
|
+
@scroll_dir = 0
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
@selecting = false
|
|
129
|
+
Gtk.grab_remove(widget)
|
|
130
|
+
@button = 0
|
|
131
|
+
elsif event.event_type == Gdk::Event::BUTTON_PRESS and event.button == 1
|
|
132
|
+
self.grab_focus unless self.has_focus?
|
|
133
|
+
|
|
134
|
+
Gtk.grab_add(widget)
|
|
135
|
+
@button = event.button
|
|
136
|
+
|
|
137
|
+
focus_view = (widget == @xdisp) ? View::HEX : View::ASCII
|
|
138
|
+
|
|
139
|
+
if @active_view == focus_view
|
|
140
|
+
if @active_view == View::HEX
|
|
141
|
+
hex_to_pointer(event.x, event.y)
|
|
142
|
+
else
|
|
143
|
+
ascii_to_pointer(event.x, event.y)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
unless @selecting
|
|
147
|
+
@selecting = true
|
|
148
|
+
set_selection(@cursor_pos, @cursor_pos)
|
|
149
|
+
end
|
|
150
|
+
else
|
|
151
|
+
hide_cursor
|
|
152
|
+
@active_view = focus_view
|
|
153
|
+
show_cursor
|
|
154
|
+
end
|
|
155
|
+
elsif event.event_type == Gdk::Event::BUTTON_PRESS and event.button == 2
|
|
156
|
+
# TODO
|
|
157
|
+
else
|
|
158
|
+
@button = 0
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
@xdisp = DrawingArea.new
|
|
163
|
+
@xdisp.modify_font @font_desc
|
|
164
|
+
@xlayout = @xdisp.create_pango_layout('')
|
|
165
|
+
@xdisp.events =
|
|
166
|
+
Gdk::Event::EXPOSURE_MASK |
|
|
167
|
+
Gdk::Event::BUTTON_PRESS_MASK |
|
|
168
|
+
Gdk::Event::BUTTON_RELEASE_MASK |
|
|
169
|
+
Gdk::Event::BUTTON_MOTION_MASK |
|
|
170
|
+
Gdk::Event::SCROLL_MASK
|
|
171
|
+
|
|
172
|
+
@xdisp.signal_connect 'realize' do
|
|
173
|
+
@xdisp_gc = Gdk::GC.new(@xdisp.window)
|
|
174
|
+
@xdisp_gc.set_exposures(true)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
@xdisp.signal_connect 'expose_event' do |xdisp, event|
|
|
178
|
+
imin = (event.area.y / @char_height).to_i
|
|
179
|
+
imax = ((event.area.y + event.area.height) / @char_height).to_i
|
|
180
|
+
imax += 1 if (event.area.y + event.area.height).to_i % @char_height != 0
|
|
181
|
+
|
|
182
|
+
imax = [ imax, @vis_lines ].min
|
|
183
|
+
|
|
184
|
+
render_hex_lines(imin, imax)
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
@xdisp.signal_connect 'scroll_event' do |xdisp, event|
|
|
188
|
+
@scrollbar.event(event)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
@xdisp.signal_connect 'button_press_event' do |xdisp, event|
|
|
192
|
+
mouse_handler[xdisp, event]
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
@xdisp.signal_connect 'button_release_event' do |xdisp, event|
|
|
196
|
+
mouse_handler[xdisp, event]
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
@xdisp.signal_connect 'motion_notify_event' do |xdisp, event|
|
|
200
|
+
w, x, y, m = xdisp.window.pointer
|
|
201
|
+
|
|
202
|
+
if y < 0
|
|
203
|
+
@scroll_dir = -1
|
|
204
|
+
elsif y >= xdisp.allocation.height
|
|
205
|
+
@scroll_dir = 1
|
|
206
|
+
else
|
|
207
|
+
@scroll_dir = 0
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
if @scroll_dir != 0
|
|
211
|
+
if @scroll_timeout == nil
|
|
212
|
+
@scroll_timeout = GLib::Timeout.add(SCROLL_TIMEOUT) {
|
|
213
|
+
if @scroll_dir < 0
|
|
214
|
+
set_cursor([ 0, @cursor_pos - @cpl ].max)
|
|
215
|
+
elsif @scroll_dir > 0
|
|
216
|
+
set_cursor([ @data.size - 1, @cursor_pos + @cpl ].min)
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
true
|
|
220
|
+
}
|
|
221
|
+
next
|
|
222
|
+
end
|
|
223
|
+
else
|
|
224
|
+
if @scroll_timeout != nil
|
|
225
|
+
GLib::Source.remove @scroll_timeout
|
|
226
|
+
@scroll_timeout = nil
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
next if event.window != xdisp.window
|
|
231
|
+
|
|
232
|
+
hex_to_pointer(x,y) if @active_view == View::HEX and @button == 1
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
put @xdisp, 0, 0
|
|
236
|
+
@xdisp.show
|
|
237
|
+
|
|
238
|
+
@adisp = DrawingArea.new
|
|
239
|
+
@adisp.modify_font @font_desc
|
|
240
|
+
@alayout = @adisp.create_pango_layout('')
|
|
241
|
+
@adisp.events =
|
|
242
|
+
Gdk::Event::EXPOSURE_MASK |
|
|
243
|
+
Gdk::Event::BUTTON_PRESS_MASK |
|
|
244
|
+
Gdk::Event::BUTTON_RELEASE_MASK |
|
|
245
|
+
Gdk::Event::BUTTON_MOTION_MASK |
|
|
246
|
+
Gdk::Event::SCROLL_MASK
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
@adisp.signal_connect 'realize' do
|
|
250
|
+
@adisp_gc = Gdk::GC.new(@adisp.window)
|
|
251
|
+
@adisp_gc.set_exposures(true)
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
@adisp.signal_connect 'expose_event' do |adisp, event|
|
|
255
|
+
imin = (event.area.y / @char_height).to_i
|
|
256
|
+
imax = ((event.area.y + event.area.height) / @char_height).to_i
|
|
257
|
+
imax += 1 if (event.area.y + event.area.height).to_i % @char_height != 0
|
|
258
|
+
|
|
259
|
+
imax = [ imax, @vis_lines ].min
|
|
260
|
+
render_ascii_lines(imin, imax)
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
@adisp.signal_connect 'scroll_event' do |adisp, event|
|
|
264
|
+
@scrollbar.event(event)
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
@adisp.signal_connect 'button_press_event' do |adisp, event|
|
|
268
|
+
mouse_handler[adisp, event]
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
@adisp.signal_connect 'button_release_event' do |adisp, event|
|
|
272
|
+
mouse_handler[adisp, event]
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
@adisp.signal_connect 'motion_notify_event' do |adisp, event|
|
|
276
|
+
w, x, y, m = adisp.window.pointer
|
|
277
|
+
|
|
278
|
+
if y < 0
|
|
279
|
+
@scroll_dir = -1
|
|
280
|
+
elsif y >= adisp.allocation.height
|
|
281
|
+
@scroll_dir = 1
|
|
282
|
+
else
|
|
283
|
+
@scroll_dir = 0
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
if @scroll_dir != 0
|
|
287
|
+
if @scroll_timeout == nil
|
|
288
|
+
@scroll_timeout = GLib::Timeout.add(SCROLL_TIMEOUT) {
|
|
289
|
+
if @scroll_dir < 0
|
|
290
|
+
set_cursor([ 0, @cursor_pos - @cpl ].max)
|
|
291
|
+
elsif @scroll_dir > 0
|
|
292
|
+
set_cursor([ @data.size - 1, @cursor_pos + @cpl ].min)
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
true
|
|
296
|
+
}
|
|
297
|
+
next
|
|
298
|
+
end
|
|
299
|
+
else
|
|
300
|
+
if @scroll_timeout != nil
|
|
301
|
+
GLib::Source.remove @scroll_timeout
|
|
302
|
+
@scroll_timeout = nil
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
next if event.window != adisp.window
|
|
307
|
+
|
|
308
|
+
ascii_to_pointer(x,y) if @active_view == View::ASCII and @button == 1
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
put @adisp, 0, 0
|
|
312
|
+
@adisp.show
|
|
313
|
+
|
|
314
|
+
@adj = Gtk::Adjustment.new(0, 0, 0, 0, 0, 0)
|
|
315
|
+
@scrollbar = VScrollbar.new(@adj)
|
|
316
|
+
@adj.signal_connect 'value_changed' do |adj|
|
|
317
|
+
unless @xdisp_gc.nil? or @adisp_gc.nil? or not @xdisp.drawable? or not @adisp.drawable?
|
|
318
|
+
|
|
319
|
+
source_min = (adj.value.to_i - @top_line) * @char_height
|
|
320
|
+
source_max = source_min + @xdisp.allocation.height
|
|
321
|
+
dest_min = 0
|
|
322
|
+
dest_max = @xdisp.allocation.height
|
|
323
|
+
|
|
324
|
+
rect = Gdk::Rectangle.new(0, 0, 0, 0)
|
|
325
|
+
@top_line = adj.value.to_i
|
|
326
|
+
if source_min < 0
|
|
327
|
+
rect.y = 0
|
|
328
|
+
rect.height = -source_min
|
|
329
|
+
rect_height = [ rect.height, @xdisp.allocation.height ].min
|
|
330
|
+
source_min = 0
|
|
331
|
+
dest_min = rect.height
|
|
332
|
+
else
|
|
333
|
+
rect.y = 2 * @xdisp.allocation.height - source_max
|
|
334
|
+
rect.y = 0 if rect.y < 0
|
|
335
|
+
rect.height = @xdisp.allocation.height - rect.y
|
|
336
|
+
source_max = @xdisp.allocation.height
|
|
337
|
+
dest_max = rect.y
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
if source_min != source_max
|
|
341
|
+
@xdisp.window.draw_drawable(
|
|
342
|
+
@xdisp_gc,
|
|
343
|
+
@xdisp.window,
|
|
344
|
+
0, source_min,
|
|
345
|
+
0, dest_min,
|
|
346
|
+
@xdisp.allocation.width,
|
|
347
|
+
source_max - source_min
|
|
348
|
+
)
|
|
349
|
+
@adisp.window.draw_drawable(
|
|
350
|
+
@adisp_gc,
|
|
351
|
+
@adisp.window,
|
|
352
|
+
0, source_min,
|
|
353
|
+
0, dest_min,
|
|
354
|
+
@adisp.allocation.width,
|
|
355
|
+
source_max - source_min
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
if @offsets
|
|
359
|
+
if @offsets_gc.nil?
|
|
360
|
+
@offsets_gc = Gdk::GC.new(@offsets.window)
|
|
361
|
+
@offsets_gc.set_exposures(true)
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
@offsets.window.draw_drawable(
|
|
365
|
+
@offsets_gc,
|
|
366
|
+
@offsets.window,
|
|
367
|
+
0, source_min,
|
|
368
|
+
0, dest_min,
|
|
369
|
+
@offsets.allocation.width,
|
|
370
|
+
source_max - source_min
|
|
371
|
+
)
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
# TODO update_all_auto_highlights(true, true)
|
|
375
|
+
invalidate_all_highlights
|
|
376
|
+
|
|
377
|
+
rect.width = @xdisp.allocation.width
|
|
378
|
+
@xdisp.window.invalidate(rect, false)
|
|
379
|
+
rect.width = @adisp.allocation.width
|
|
380
|
+
@adisp.window.invalidate(rect, false)
|
|
381
|
+
|
|
382
|
+
if @offsets
|
|
383
|
+
rect.width = @offsets.allocation.width
|
|
384
|
+
@offsets.window.invalidate(rect, false)
|
|
385
|
+
end
|
|
386
|
+
end
|
|
387
|
+
end
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
put @scrollbar, 0, 0
|
|
391
|
+
@scrollbar.show
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
def set_selection(s, e)
|
|
395
|
+
e = [ e, @data.size ].min
|
|
396
|
+
|
|
397
|
+
@@primary.clear if @selection.start != @selection.end
|
|
398
|
+
|
|
399
|
+
os, oe = [ @selection.start, @selection.end ].sort
|
|
400
|
+
|
|
401
|
+
@selection.start = [ 0, s ].max
|
|
402
|
+
@selection.start = [ @selection.start, @data.size ].min
|
|
403
|
+
@selection.end = [ e, @data.size ].min
|
|
404
|
+
|
|
405
|
+
invalidate_highlight(@selection)
|
|
406
|
+
|
|
407
|
+
ns, ne = [ @selection.start, @selection.end ].sort
|
|
408
|
+
|
|
409
|
+
if ns != os and ne != oe
|
|
410
|
+
bytes_changed([ns, os].min, [ne, oe].max)
|
|
411
|
+
elsif ne != oe
|
|
412
|
+
bytes_changed(*[ne, oe].sort)
|
|
413
|
+
elsif ns != os
|
|
414
|
+
bytes_changed(*[ns, os].sort)
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
if @selection.start != @selection.end
|
|
418
|
+
if @active_view == View::HEX
|
|
419
|
+
brk_len = 2 * @cpl + @cpl / @group_type
|
|
420
|
+
format_xblock(s,e)
|
|
421
|
+
(@disp_buffer.size / brk_len + 1).times do |i| @disp_buffer.insert(i * (brk_len + 1), $/) end
|
|
422
|
+
else
|
|
423
|
+
brk_len = @cpl
|
|
424
|
+
format_ablock(s,e)
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
@@primary.set_text(@disp_buffer)
|
|
428
|
+
end
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
def get_selection
|
|
432
|
+
[ @selection.start, @selection.end ].sort
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
def clear_selection
|
|
436
|
+
set_selection(0, 0)
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
def cursor
|
|
440
|
+
@cursor_pos
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
def set_cursor(index)
|
|
444
|
+
return if index < 0 or index > @data.size
|
|
445
|
+
|
|
446
|
+
old_pos = @cursor_pos
|
|
447
|
+
index -= 1 if @insert and index == @data.size
|
|
448
|
+
index = [ 0, index ].max
|
|
449
|
+
|
|
450
|
+
hide_cursor
|
|
451
|
+
|
|
452
|
+
@cursor_pos = index
|
|
453
|
+
return if @cpl == 0
|
|
454
|
+
|
|
455
|
+
y = index / @cpl
|
|
456
|
+
if y >= @top_line + @vis_lines
|
|
457
|
+
@adj.value = [ y - @vis_lines + 1, @lines - @vis_lines ].min
|
|
458
|
+
@adj.value = [ 0, @adj.value ].max
|
|
459
|
+
@adj.signal_emit 'value_changed'
|
|
460
|
+
elsif y < @top_line
|
|
461
|
+
@adj.value = y
|
|
462
|
+
@adj.signal_emit 'value_changed'
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
@lower_nibble = false if index == @data.size
|
|
466
|
+
|
|
467
|
+
if @selecting
|
|
468
|
+
set_selection(@selection.start, @cursor_pos)
|
|
469
|
+
bytes_changed(*[@cursor_pos, old_pos].sort)
|
|
470
|
+
else# @selection.start != @selection.end
|
|
471
|
+
s, e = [@selection.start, @selection.end].sort
|
|
472
|
+
@selection.end = @selection.start = @cursor_pos
|
|
473
|
+
bytes_changed(s, e)
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
self.signal_emit 'cursor_moved'
|
|
477
|
+
|
|
478
|
+
bytes_changed(old_pos, old_pos)
|
|
479
|
+
show_cursor
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
def set_cursor_xy(x, y)
|
|
483
|
+
pos = y.to_i * @cpl + x.to_i
|
|
484
|
+
return if y < 0 or y >= @lines or x < 0 or x >= @cpl or pos > @data.size
|
|
485
|
+
|
|
486
|
+
set_cursor(pos)
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
def set_cursor_on_lower_nibble(bool)
|
|
490
|
+
if @selecting
|
|
491
|
+
bytes_changed(@cursor_pos, @cursor_pos)
|
|
492
|
+
@lower_nibble = bool
|
|
493
|
+
elsif @selection.start != @selection.end
|
|
494
|
+
s, e = [ @selection.start, @selection.end ].sort
|
|
495
|
+
|
|
496
|
+
@selection.start = @selection.end = 0
|
|
497
|
+
bytes_changed(s, e)
|
|
498
|
+
@lower_nibble = bool
|
|
499
|
+
else
|
|
500
|
+
hide_cursor
|
|
501
|
+
@lower_nibble = bool
|
|
502
|
+
show_cursor
|
|
503
|
+
end
|
|
504
|
+
end
|
|
505
|
+
|
|
506
|
+
def set_group_type(type)
|
|
507
|
+
hide_cursor
|
|
508
|
+
@group_type = type
|
|
509
|
+
recalc_displays(self.allocation.width, self.allocation.height)
|
|
510
|
+
self.queue_resize
|
|
511
|
+
show_cursor
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
def show_offsets(bool)
|
|
515
|
+
return unless @show_offsets ^ bool
|
|
516
|
+
|
|
517
|
+
@show_offsets = bool
|
|
518
|
+
if bool
|
|
519
|
+
show_offsets_widget
|
|
520
|
+
else
|
|
521
|
+
hide_offsets_widget
|
|
522
|
+
end
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
def set_font(fontname)
|
|
526
|
+
@font_desc = Pango::FontDescription.new(fontname)
|
|
527
|
+
@disp_font_metrics = load_font(fontname)
|
|
528
|
+
|
|
529
|
+
@xdisp.modify_font(@font_desc) if @xdisp
|
|
530
|
+
@adisp.modify_font(@font_desc) if @adisp
|
|
531
|
+
@offsets.modify_font(@font_desc) if @offsets
|
|
532
|
+
|
|
533
|
+
@char_width = get_max_char_width(@disp_font_metrics)
|
|
534
|
+
@char_height = Pango.pixels(@disp_font_metrics.ascent) + Pango.pixels(@disp_font_metrics.descent) + 2
|
|
535
|
+
recalc_displays(self.allocation.width, self.allocation.height)
|
|
536
|
+
|
|
537
|
+
redraw_widget
|
|
538
|
+
end
|
|
539
|
+
|
|
540
|
+
def set_data(data)
|
|
541
|
+
prev_data_size = @data.size
|
|
542
|
+
@data = data.dup
|
|
543
|
+
|
|
544
|
+
recalc_displays(self.allocation.width, self.allocation.height)
|
|
545
|
+
|
|
546
|
+
set_cursor 0
|
|
547
|
+
bytes_changed(0, [ prev_data_size, @data.size ].max)
|
|
548
|
+
redraw_widget
|
|
549
|
+
end
|
|
550
|
+
|
|
551
|
+
def validate_highlight(hl)
|
|
552
|
+
unless hl.valid
|
|
553
|
+
hl.start_line = [ hl.start, hl.end ].min / @cpl - @top_line
|
|
554
|
+
hl.end_line = [ hl.start, hl.end ].max / @cpl - @top_line
|
|
555
|
+
hl.valid = true
|
|
556
|
+
end
|
|
557
|
+
end
|
|
558
|
+
|
|
559
|
+
def invalidate_highlight(hl)
|
|
560
|
+
hl.valid = false
|
|
561
|
+
end
|
|
562
|
+
|
|
563
|
+
def invalidate_all_highlights
|
|
564
|
+
@highlights.each do |hl| invalidate_highlight(hl) end
|
|
565
|
+
end
|
|
566
|
+
|
|
567
|
+
private
|
|
568
|
+
|
|
569
|
+
signal_new(
|
|
570
|
+
'data_changed',
|
|
571
|
+
GLib::Signal::RUN_FIRST,
|
|
572
|
+
nil,
|
|
573
|
+
nil,
|
|
574
|
+
String
|
|
575
|
+
)
|
|
576
|
+
|
|
577
|
+
signal_new(
|
|
578
|
+
'cursor_moved',
|
|
579
|
+
GLib::Signal::RUN_FIRST,
|
|
580
|
+
nil,
|
|
581
|
+
nil
|
|
582
|
+
)
|
|
583
|
+
|
|
584
|
+
def signal_do_cursor_moved
|
|
585
|
+
end
|
|
586
|
+
|
|
587
|
+
def signal_do_data_changed(data)
|
|
588
|
+
# TODO
|
|
589
|
+
end
|
|
590
|
+
|
|
591
|
+
def signal_do_realize
|
|
592
|
+
super
|
|
593
|
+
|
|
594
|
+
self.window.set_back_pixmap(nil, true)
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
def signal_do_size_allocate(alloc)
|
|
598
|
+
hide_cursor
|
|
599
|
+
|
|
600
|
+
recalc_displays(alloc.width, alloc.height)
|
|
601
|
+
|
|
602
|
+
self.set_allocation(alloc.x, alloc.y, alloc.width, alloc.height)
|
|
603
|
+
self.window.move_resize(
|
|
604
|
+
alloc.x, alloc.y,
|
|
605
|
+
alloc.width, alloc.height
|
|
606
|
+
) if self.realized?
|
|
607
|
+
|
|
608
|
+
bw = self.border_width
|
|
609
|
+
xt = widget_get_xt
|
|
610
|
+
yt = widget_get_yt
|
|
611
|
+
|
|
612
|
+
my_alloc = Gtk::Allocation.new(0, 0, 0, 0)
|
|
613
|
+
my_alloc.x = bw + xt
|
|
614
|
+
my_alloc.y = bw + yt
|
|
615
|
+
my_alloc.height = [ alloc.height - 2*bw - 2*yt, 1 ].max
|
|
616
|
+
if @show_offsets
|
|
617
|
+
my_alloc.width = 8 * @char_width
|
|
618
|
+
@offsets.size_allocate(my_alloc)
|
|
619
|
+
@offsets.queue_draw
|
|
620
|
+
my_alloc.x += 2*xt + my_alloc.width
|
|
621
|
+
end
|
|
622
|
+
|
|
623
|
+
my_alloc.width = @xdisp_width
|
|
624
|
+
@xdisp.size_allocate(my_alloc)
|
|
625
|
+
|
|
626
|
+
my_alloc.x = alloc.width - bw - @scrollbar.requisition[0]
|
|
627
|
+
my_alloc.y = bw
|
|
628
|
+
my_alloc.width = @scrollbar.requisition[0]
|
|
629
|
+
my_alloc.height = [ alloc.height - 2*bw, 1 ].max
|
|
630
|
+
@scrollbar.size_allocate(my_alloc)
|
|
631
|
+
|
|
632
|
+
my_alloc.x -= @adisp_width + xt
|
|
633
|
+
my_alloc.y = bw + yt
|
|
634
|
+
my_alloc.width = @adisp_width
|
|
635
|
+
my_alloc.height = [ alloc.height - 2*bw - 2*yt, 1 ].max
|
|
636
|
+
@adisp.size_allocate(my_alloc)
|
|
637
|
+
|
|
638
|
+
show_cursor
|
|
639
|
+
end
|
|
640
|
+
|
|
641
|
+
def signal_do_size_request(req)
|
|
642
|
+
sb_width, sb_height = @scrollbar.size_request
|
|
643
|
+
bw = self.border_width
|
|
644
|
+
xt, yt = widget_get_xt, widget_get_yt
|
|
645
|
+
|
|
646
|
+
width = 4*xt + 2*bw + sb_width +
|
|
647
|
+
@char_width*(DEFAULT_CPL + (DEFAULT_CPL-1)/@group_type)
|
|
648
|
+
|
|
649
|
+
width += 2*xt + 8*@char_width if @show_offsets
|
|
650
|
+
|
|
651
|
+
height = DEFAULT_LINES * @char_height + 2*yt + 2*bw
|
|
652
|
+
|
|
653
|
+
req[0] = width
|
|
654
|
+
req[1] = height
|
|
655
|
+
end
|
|
656
|
+
|
|
657
|
+
def signal_do_expose_event(event)
|
|
658
|
+
draw_shadow(event.area)
|
|
659
|
+
super(event)
|
|
660
|
+
|
|
661
|
+
true
|
|
662
|
+
end
|
|
663
|
+
|
|
664
|
+
def signal_do_key_press_event(event)
|
|
665
|
+
old_cp = @cursor_pos
|
|
666
|
+
|
|
667
|
+
hide_cursor
|
|
668
|
+
@selecting = (event.state & Gdk::Window::SHIFT_MASK) != 0
|
|
669
|
+
ret = true
|
|
670
|
+
|
|
671
|
+
case event.keyval
|
|
672
|
+
when Gdk::Keyval::GDK_KP_Tab, Gdk::Keyval::GDK_Tab
|
|
673
|
+
@active_view = (@active_view == View::HEX) ? View::ASCII : View::HEX
|
|
674
|
+
|
|
675
|
+
when Gdk::Keyval::GDK_Up
|
|
676
|
+
set_cursor(@cursor_pos - @cpl)
|
|
677
|
+
|
|
678
|
+
when Gdk::Keyval::GDK_Down
|
|
679
|
+
set_cursor(@cursor_pos + @cpl)
|
|
680
|
+
|
|
681
|
+
when Gdk::Keyval::GDK_Page_Up
|
|
682
|
+
set_cursor([0, @cursor_pos - @vis_lines * @cpl].max)
|
|
683
|
+
|
|
684
|
+
when Gdk::Keyval::GDK_Page_Down
|
|
685
|
+
set_cursor([@cursor_pos + @vis_lines * @cpl, @data.size].min)
|
|
686
|
+
|
|
687
|
+
when Gdk::Keyval::GDK_Left
|
|
688
|
+
if @active_view == View::HEX
|
|
689
|
+
if @selecting
|
|
690
|
+
set_cursor(@cursor_pos - 1)
|
|
691
|
+
else
|
|
692
|
+
@lower_nibble ^= 1
|
|
693
|
+
set_cursor(@cursor_pos - 1) if @lower_nibble
|
|
694
|
+
end
|
|
695
|
+
else
|
|
696
|
+
set_cursor(@cursor_pos - 1)
|
|
697
|
+
end
|
|
698
|
+
|
|
699
|
+
when Gdk::Keyval::GDK_Right
|
|
700
|
+
if @active_view == View::HEX
|
|
701
|
+
if @selecting
|
|
702
|
+
set_cursor(@cursor_pos + 1)
|
|
703
|
+
else
|
|
704
|
+
@lower_nibble ^= 1
|
|
705
|
+
set_cursor(@cursor_pos + 1) unless @lower_nibble
|
|
706
|
+
end
|
|
707
|
+
else
|
|
708
|
+
set_cursor(@cursor_pos + 1)
|
|
709
|
+
end
|
|
710
|
+
|
|
711
|
+
when Gdk::Keyval::GDK_c, Gdk::Keyval::GDK_C
|
|
712
|
+
if event.state & Gdk::Window::CONTROL_MASK != 0
|
|
713
|
+
s,e = @selection.start, @selection.end + 1
|
|
714
|
+
if @active_view == View::HEX
|
|
715
|
+
brk_len = 2 * @cpl + @cpl / @group_type
|
|
716
|
+
format_xblock(s,e)
|
|
717
|
+
(@disp_buffer.size / brk_len + 1).times do |i| @disp_buffer.insert(i * (brk_len + 1), $/) end
|
|
718
|
+
else
|
|
719
|
+
brk_len = @cpl
|
|
720
|
+
format_ablock(s,e)
|
|
721
|
+
end
|
|
722
|
+
|
|
723
|
+
@@clipboard.set_text(@disp_buffer)
|
|
724
|
+
end
|
|
725
|
+
else
|
|
726
|
+
ret = false
|
|
727
|
+
end
|
|
728
|
+
|
|
729
|
+
show_cursor
|
|
730
|
+
|
|
731
|
+
ret
|
|
732
|
+
end
|
|
733
|
+
|
|
734
|
+
def hex_to_pointer(mx, my)
|
|
735
|
+
cy = @top_line + my.to_i / @char_height
|
|
736
|
+
|
|
737
|
+
cx = x = 0
|
|
738
|
+
while cx < 2 * @cpl
|
|
739
|
+
x += @char_width
|
|
740
|
+
|
|
741
|
+
if x > mx
|
|
742
|
+
set_cursor_xy(cx / 2, cy)
|
|
743
|
+
set_cursor_on_lower_nibble(cx % 2 != 0)
|
|
744
|
+
|
|
745
|
+
cx = 2 * @cpl
|
|
746
|
+
end
|
|
747
|
+
|
|
748
|
+
cx += 1
|
|
749
|
+
x += @char_width if ( cx % (2 * @group_type) == 0 )
|
|
750
|
+
end
|
|
751
|
+
end
|
|
752
|
+
|
|
753
|
+
def ascii_to_pointer(mx, my)
|
|
754
|
+
cx = mx / @char_width
|
|
755
|
+
cy = @top_line + my / @char_height
|
|
756
|
+
|
|
757
|
+
set_cursor_xy(cx, cy)
|
|
758
|
+
end
|
|
759
|
+
|
|
760
|
+
def load_font(fontname)
|
|
761
|
+
desc = Pango::FontDescription.new(fontname)
|
|
762
|
+
context = Gdk::Pango.context
|
|
763
|
+
context.set_language(Gtk.default_language)
|
|
764
|
+
|
|
765
|
+
font = context.load_font(desc)
|
|
766
|
+
|
|
767
|
+
font.metrics(context.language)
|
|
768
|
+
end
|
|
769
|
+
|
|
770
|
+
def draw_shadow(area)
|
|
771
|
+
bw = self.border_width
|
|
772
|
+
x = bw
|
|
773
|
+
xt = widget_get_xt
|
|
774
|
+
|
|
775
|
+
if @show_offsets
|
|
776
|
+
self.style.paint_shadow(
|
|
777
|
+
self.window,
|
|
778
|
+
Gtk::STATE_NORMAL, Gtk::SHADOW_IN,
|
|
779
|
+
nil, self, nil,
|
|
780
|
+
bw, bw,
|
|
781
|
+
8*@char_width + 2*xt, self.allocation.height - 2*bw
|
|
782
|
+
)
|
|
783
|
+
|
|
784
|
+
x += 8*@char_width + 2*xt
|
|
785
|
+
end
|
|
786
|
+
|
|
787
|
+
self.style.paint_shadow(
|
|
788
|
+
self.window,
|
|
789
|
+
Gtk::STATE_NORMAL, Gtk::SHADOW_IN,
|
|
790
|
+
nil, self, nil,
|
|
791
|
+
x, bw,
|
|
792
|
+
@xdisp_width + 2*xt, self.allocation.height - 2*bw
|
|
793
|
+
)
|
|
794
|
+
|
|
795
|
+
self.style.paint_shadow(
|
|
796
|
+
self.window,
|
|
797
|
+
Gtk::STATE_NORMAL, Gtk::SHADOW_IN,
|
|
798
|
+
nil, self, nil,
|
|
799
|
+
self.allocation.width - bw - @adisp_width - @scrollbar.requisition[0] - 2*xt, bw,
|
|
800
|
+
@adisp_width + 2*xt, self.allocation.height - 2*bw
|
|
801
|
+
)
|
|
802
|
+
end
|
|
803
|
+
|
|
804
|
+
def redraw_widget
|
|
805
|
+
return unless self.realized?
|
|
806
|
+
|
|
807
|
+
self.window.invalidate(nil, false)
|
|
808
|
+
end
|
|
809
|
+
|
|
810
|
+
def widget_get_xt
|
|
811
|
+
self.style.xthickness
|
|
812
|
+
end
|
|
813
|
+
|
|
814
|
+
def widget_get_yt
|
|
815
|
+
self.style.ythickness
|
|
816
|
+
end
|
|
817
|
+
|
|
818
|
+
def recalc_displays(width, height)
|
|
819
|
+
old_cpl = @cpl
|
|
820
|
+
|
|
821
|
+
w, h = @scrollbar.size_request
|
|
822
|
+
@xdisp_width = 1
|
|
823
|
+
@adisp_width = 1
|
|
824
|
+
|
|
825
|
+
total_width = width - 2 * self.border_width - 4 * widget_get_xt - w
|
|
826
|
+
total_width -= 2 * widget_get_xt + 8 * @char_width if @show_offsets
|
|
827
|
+
|
|
828
|
+
total_cpl = total_width / @char_width
|
|
829
|
+
if total_cpl == 0 or total_width < 0
|
|
830
|
+
@cpl = @lines = @vis_lines = 0
|
|
831
|
+
return
|
|
832
|
+
end
|
|
833
|
+
|
|
834
|
+
@cpl = 0
|
|
835
|
+
begin
|
|
836
|
+
break if @cpl % @group_type == 0 and total_cpl < @group_type * 3
|
|
837
|
+
|
|
838
|
+
@cpl += 1
|
|
839
|
+
total_cpl -= 3
|
|
840
|
+
|
|
841
|
+
total_cpl -= 1 if @cpl % @group_type == 0
|
|
842
|
+
end while total_cpl > 0
|
|
843
|
+
|
|
844
|
+
return if @cpl == 0
|
|
845
|
+
|
|
846
|
+
if @data.empty?
|
|
847
|
+
@lines = 1
|
|
848
|
+
else
|
|
849
|
+
@lines = @data.size / @cpl
|
|
850
|
+
@lines += 1 if @data.size % @cpl != 0
|
|
851
|
+
end
|
|
852
|
+
|
|
853
|
+
@vis_lines = (height - 2*self.border_width - 2*widget_get_yt).to_i / @char_height.to_i
|
|
854
|
+
@adisp_width = @cpl * @char_width + 1
|
|
855
|
+
xcpl = @cpl * 2 + (@cpl - 1) / @group_type
|
|
856
|
+
@xdisp_width = xcpl * @char_width + 1
|
|
857
|
+
|
|
858
|
+
@disp_buffer = ''
|
|
859
|
+
|
|
860
|
+
@adj.value = [@top_line * old_cpl / @cpl, @lines - @vis_lines].min
|
|
861
|
+
@adj.value = [ 0, @adj.value ].max
|
|
862
|
+
if @cursor_pos / @cpl < @adj.value or @cursor_pos / @cpl > @adj.value + @vis_lines - 1
|
|
863
|
+
@adj.value = [ @cursor_pos / @cpl, @lines - @vis_lines ].min
|
|
864
|
+
@adj.value = [ 0, @adj.value ].max
|
|
865
|
+
end
|
|
866
|
+
|
|
867
|
+
@adj.lower = 0
|
|
868
|
+
@adj.upper = @lines
|
|
869
|
+
@adj.step_increment = 1
|
|
870
|
+
@adj.page_increment = @vis_lines - 1
|
|
871
|
+
@adj.page_size = @vis_lines
|
|
872
|
+
|
|
873
|
+
@adj.signal_emit 'changed'
|
|
874
|
+
@adj.signal_emit 'value_changed'
|
|
875
|
+
end
|
|
876
|
+
|
|
877
|
+
def get_max_char_width(metrics)
|
|
878
|
+
|
|
879
|
+
layout = self.create_pango_layout('')
|
|
880
|
+
layout.set_font_description(@font_desc)
|
|
881
|
+
char_widths = [ 0 ]
|
|
882
|
+
|
|
883
|
+
(1..100).each do |i|
|
|
884
|
+
logical_rect = Pango::Rectangle.new(0, 0, 0, 0)
|
|
885
|
+
if is_displayable(i.chr)
|
|
886
|
+
layout.set_text(i.chr)
|
|
887
|
+
logical_rect = layout.pixel_extents[1]
|
|
888
|
+
end
|
|
889
|
+
char_widths << logical_rect.width
|
|
890
|
+
end
|
|
891
|
+
|
|
892
|
+
char_widths[48..122].max
|
|
893
|
+
end
|
|
894
|
+
|
|
895
|
+
def show_cursor
|
|
896
|
+
unless @cursor_shown
|
|
897
|
+
if @xdisp_gc and @adisp_gc and @xdisp.realized? and @adisp.realized?
|
|
898
|
+
render_xc
|
|
899
|
+
render_ac
|
|
900
|
+
end
|
|
901
|
+
|
|
902
|
+
@cursor_shown = true
|
|
903
|
+
end
|
|
904
|
+
end
|
|
905
|
+
|
|
906
|
+
def hide_cursor
|
|
907
|
+
if @cursor_shown
|
|
908
|
+
if @xdisp_gc and @adisp_gc and @xdisp.realized? and @adisp.realized?
|
|
909
|
+
render_byte(@cursor_pos)
|
|
910
|
+
end
|
|
911
|
+
|
|
912
|
+
@cursor_shown = false
|
|
913
|
+
end
|
|
914
|
+
end
|
|
915
|
+
|
|
916
|
+
def show_offsets_widget
|
|
917
|
+
@offsets = DrawingArea.new
|
|
918
|
+
@offsets.modify_font @font_desc
|
|
919
|
+
@olayout = @offsets.create_pango_layout('')
|
|
920
|
+
|
|
921
|
+
@offsets.events = Gdk::Event::EXPOSURE_MASK
|
|
922
|
+
@offsets.signal_connect 'expose_event' do |offsets, event|
|
|
923
|
+
imin = (event.area.y / @char_height).to_i
|
|
924
|
+
imax = ((event.area.y + event.area.height) / @char_height).to_i
|
|
925
|
+
imax += 1 if (event.area.y + event.area.height).to_i % @char_height != 0
|
|
926
|
+
|
|
927
|
+
imax = [ imax, @vis_lines ].min
|
|
928
|
+
|
|
929
|
+
render_offsets(imin, imax)
|
|
930
|
+
end
|
|
931
|
+
|
|
932
|
+
put @offsets, 0, 0
|
|
933
|
+
@offsets.show
|
|
934
|
+
end
|
|
935
|
+
|
|
936
|
+
def hide_offsets_widget
|
|
937
|
+
if @offsets
|
|
938
|
+
self.remove(@offsets)
|
|
939
|
+
@offsets = @offsets_gc = nil
|
|
940
|
+
end
|
|
941
|
+
end
|
|
942
|
+
|
|
943
|
+
def is_displayable(c)
|
|
944
|
+
if RUBY_VERSION < '1.9'
|
|
945
|
+
c = c[0]
|
|
946
|
+
else
|
|
947
|
+
c = c.ord
|
|
948
|
+
end
|
|
949
|
+
|
|
950
|
+
c >= 0x20 and c < 0x7f
|
|
951
|
+
end
|
|
952
|
+
|
|
953
|
+
def bytes_changed(s, e)
|
|
954
|
+
start_line = s / @cpl - @top_line
|
|
955
|
+
end_line = e / @cpl - @top_line
|
|
956
|
+
|
|
957
|
+
return if end_line < 0 or start_line > @vis_lines
|
|
958
|
+
|
|
959
|
+
start_line = [ 0, start_line ].max
|
|
960
|
+
|
|
961
|
+
render_hex_lines(start_line, end_line)
|
|
962
|
+
render_ascii_lines(start_line, end_line)
|
|
963
|
+
render_offsets(start_line, end_line) if @show_offsets
|
|
964
|
+
end
|
|
965
|
+
|
|
966
|
+
def render_hex_highlights(cursor_line)
|
|
967
|
+
xcpl = @cpl * 2 + @cpl / @group_type
|
|
968
|
+
|
|
969
|
+
@highlights.each do |hl|
|
|
970
|
+
next if (hl.start - hl.end).abs < hl.min_select
|
|
971
|
+
|
|
972
|
+
validate_highlight(hl)
|
|
973
|
+
|
|
974
|
+
s, e = [ hl.start, hl.end ].sort
|
|
975
|
+
sl, el = hl.start_line, hl.end_line
|
|
976
|
+
|
|
977
|
+
hl.style.attach(@xdisp.window) if hl.style
|
|
978
|
+
state = (@active_view == View::HEX) ? Gtk::STATE_SELECTED : Gtk::STATE_INSENSITIVE
|
|
979
|
+
|
|
980
|
+
if cursor_line == sl
|
|
981
|
+
cursor_off = 2 * (s % @cpl) + (s % @cpl) / @group_type
|
|
982
|
+
if cursor_line == el
|
|
983
|
+
len = 2 * (e % @cpl + 1) + (e % @cpl) / @group_type
|
|
984
|
+
else
|
|
985
|
+
len = xcpl
|
|
986
|
+
end
|
|
987
|
+
|
|
988
|
+
len -= cursor_off
|
|
989
|
+
(hl.style || self.style).paint_flat_box(
|
|
990
|
+
@xdisp.window,
|
|
991
|
+
state, Gtk::SHADOW_NONE,
|
|
992
|
+
nil, @xdisp, '',
|
|
993
|
+
cursor_off * @char_width, cursor_line * @char_height,
|
|
994
|
+
len * @char_width, @char_height
|
|
995
|
+
) if len > 0
|
|
996
|
+
|
|
997
|
+
elsif cursor_line == el
|
|
998
|
+
cursor_off = 2 * (e % @cpl + 1) + (e % @cpl) / @group_type
|
|
999
|
+
(hl.style || self.style).paint_flat_box(
|
|
1000
|
+
@xdisp.window,
|
|
1001
|
+
state, Gtk::SHADOW_NONE,
|
|
1002
|
+
nil, @xdisp, '',
|
|
1003
|
+
0, cursor_line * @char_height,
|
|
1004
|
+
cursor_off * @char_width, @char_height
|
|
1005
|
+
) if cursor_off > 0
|
|
1006
|
+
|
|
1007
|
+
elsif cursor_line > sl and cursor_line < el
|
|
1008
|
+
(hl.style || self.style).paint_flat_box(
|
|
1009
|
+
@xdisp.window,
|
|
1010
|
+
state, Gtk::SHADOW_NONE,
|
|
1011
|
+
nil, @xdisp, '',
|
|
1012
|
+
0, cursor_line * @char_height,
|
|
1013
|
+
xcpl * @char_width, @char_height
|
|
1014
|
+
)
|
|
1015
|
+
end
|
|
1016
|
+
|
|
1017
|
+
hl.style.attach(@adisp.window) if hl.style
|
|
1018
|
+
end
|
|
1019
|
+
end
|
|
1020
|
+
|
|
1021
|
+
def render_hex_lines(imin, imax)
|
|
1022
|
+
return unless self.realized? and @cpl != 0
|
|
1023
|
+
|
|
1024
|
+
cursor_line = @cursor_pos / @cpl - @top_line
|
|
1025
|
+
|
|
1026
|
+
@xdisp_gc.set_foreground(self.style.base(Gtk::STATE_NORMAL))
|
|
1027
|
+
@xdisp.window.draw_rectangle(
|
|
1028
|
+
@xdisp_gc,
|
|
1029
|
+
true,
|
|
1030
|
+
0,
|
|
1031
|
+
imin * @char_height,
|
|
1032
|
+
@xdisp.allocation.width,
|
|
1033
|
+
(imax - imin + 1) * @char_height
|
|
1034
|
+
)
|
|
1035
|
+
|
|
1036
|
+
imax = [ imax, @vis_lines, @lines ].min
|
|
1037
|
+
|
|
1038
|
+
@xdisp_gc.set_foreground(self.style.text(Gtk::STATE_NORMAL))
|
|
1039
|
+
|
|
1040
|
+
frm_len = format_xblock((@top_line+imin) * @cpl, [(@top_line+imax+1) * @cpl, @data.size].min)
|
|
1041
|
+
|
|
1042
|
+
tmp = nil
|
|
1043
|
+
xcpl = @cpl*2 + @cpl/@group_type
|
|
1044
|
+
(imin..imax).each do |i|
|
|
1045
|
+
return unless (tmp = frm_len - ((i - imin) * xcpl)) > 0
|
|
1046
|
+
|
|
1047
|
+
render_hex_highlights(i)
|
|
1048
|
+
text = @disp_buffer[(i-imin) * xcpl, [xcpl, tmp].min]
|
|
1049
|
+
@xlayout.set_text(text)
|
|
1050
|
+
@xdisp.window.draw_layout(@xdisp_gc, 0, i * @char_height, @xlayout)
|
|
1051
|
+
end
|
|
1052
|
+
|
|
1053
|
+
render_xc if cursor_line >= imin and cursor_line <= imax and @cursor_shown
|
|
1054
|
+
end
|
|
1055
|
+
|
|
1056
|
+
def render_ascii_highlights(cursor_line)
|
|
1057
|
+
@highlights.each do |hl|
|
|
1058
|
+
next if (hl.start - hl.end).abs < hl.min_select
|
|
1059
|
+
|
|
1060
|
+
validate_highlight(hl)
|
|
1061
|
+
|
|
1062
|
+
s, e = [ hl.start, hl.end ].sort
|
|
1063
|
+
sl, el = hl.start_line, hl.end_line
|
|
1064
|
+
|
|
1065
|
+
hl.style.attach(@adisp.window) if hl.style
|
|
1066
|
+
state = (@active_view == View::ASCII) ? Gtk::STATE_SELECTED : Gtk::STATE_INSENSITIVE
|
|
1067
|
+
|
|
1068
|
+
if cursor_line == sl
|
|
1069
|
+
cursor_off = s % @cpl
|
|
1070
|
+
len =
|
|
1071
|
+
if cursor_line == el
|
|
1072
|
+
e - s + 1
|
|
1073
|
+
else
|
|
1074
|
+
@cpl - cursor_off
|
|
1075
|
+
end
|
|
1076
|
+
|
|
1077
|
+
(hl.style || self.style).paint_flat_box(
|
|
1078
|
+
@adisp.window,
|
|
1079
|
+
state, Gtk::SHADOW_NONE,
|
|
1080
|
+
nil, @adisp, '',
|
|
1081
|
+
cursor_off * @char_width, cursor_line * @char_height,
|
|
1082
|
+
len * @char_width, @char_height
|
|
1083
|
+
) if len > 0
|
|
1084
|
+
|
|
1085
|
+
elsif cursor_line == el
|
|
1086
|
+
cursor_off = e % @cpl + 1
|
|
1087
|
+
(hl.style || self.style).paint_flat_box(
|
|
1088
|
+
@adisp.window,
|
|
1089
|
+
state, Gtk::SHADOW_NONE,
|
|
1090
|
+
nil, @adisp, '',
|
|
1091
|
+
0, cursor_line * @char_height,
|
|
1092
|
+
cursor_off * @char_width, @char_height
|
|
1093
|
+
) if cursor_off > 0
|
|
1094
|
+
|
|
1095
|
+
elsif cursor_line > sl and cursor_line < el
|
|
1096
|
+
(hl.style || self.style).paint_flat_box(
|
|
1097
|
+
@adisp.window,
|
|
1098
|
+
state, Gtk::SHADOW_NONE,
|
|
1099
|
+
nil, @adisp, '',
|
|
1100
|
+
0, cursor_line * @char_height,
|
|
1101
|
+
@cpl * @char_width, @char_height
|
|
1102
|
+
)
|
|
1103
|
+
end
|
|
1104
|
+
|
|
1105
|
+
hl.style.attach(@adisp.window) if hl.style
|
|
1106
|
+
end
|
|
1107
|
+
end
|
|
1108
|
+
|
|
1109
|
+
def render_ascii_lines(imin, imax)
|
|
1110
|
+
return unless self.realized? and @cpl != 0
|
|
1111
|
+
|
|
1112
|
+
cursor_line = @cursor_pos / @cpl - @top_line
|
|
1113
|
+
|
|
1114
|
+
@adisp_gc.set_foreground(self.style.base(Gtk::STATE_NORMAL))
|
|
1115
|
+
@adisp.window.draw_rectangle(
|
|
1116
|
+
@adisp_gc,
|
|
1117
|
+
true,
|
|
1118
|
+
0,
|
|
1119
|
+
imin * @char_height,
|
|
1120
|
+
@adisp.allocation.width,
|
|
1121
|
+
(imax - imin + 1) * @char_height
|
|
1122
|
+
)
|
|
1123
|
+
|
|
1124
|
+
imax = [ imax, @vis_lines, @lines ].min
|
|
1125
|
+
|
|
1126
|
+
@adisp_gc.set_foreground(self.style.text(Gtk::STATE_NORMAL))
|
|
1127
|
+
|
|
1128
|
+
frm_len = format_ablock((@top_line+imin) * @cpl, [(@top_line+imax+1) * @cpl, @data.size].min)
|
|
1129
|
+
|
|
1130
|
+
tmp = nil
|
|
1131
|
+
(imin..imax).each do |i|
|
|
1132
|
+
return unless (tmp = frm_len - ((i - imin) * @cpl)) > 0
|
|
1133
|
+
|
|
1134
|
+
render_ascii_highlights(i)
|
|
1135
|
+
text = @disp_buffer[(i-imin) * @cpl, [@cpl, tmp].min]
|
|
1136
|
+
@alayout.set_text(text)
|
|
1137
|
+
@adisp.window.draw_layout(@adisp_gc, 0, i * @char_height, @alayout)
|
|
1138
|
+
end
|
|
1139
|
+
|
|
1140
|
+
render_ac if cursor_line >= imin and cursor_line <= imax and @cursor_shown
|
|
1141
|
+
end
|
|
1142
|
+
|
|
1143
|
+
def render_offsets(imin, imax)
|
|
1144
|
+
return unless self.realized?
|
|
1145
|
+
|
|
1146
|
+
unless @offsets_gc
|
|
1147
|
+
@offsets_gc = Gdk::GC.new(@offsets.window)
|
|
1148
|
+
@offsets_gc.set_exposures(true)
|
|
1149
|
+
end
|
|
1150
|
+
|
|
1151
|
+
@offsets_gc.set_foreground(self.style.base(Gtk::STATE_INSENSITIVE))
|
|
1152
|
+
@offsets.window.draw_rectangle(
|
|
1153
|
+
@offsets_gc,
|
|
1154
|
+
true,
|
|
1155
|
+
0, imin * @char_height,
|
|
1156
|
+
@offsets.allocation.width, (imax - imin + 1) * @char_height
|
|
1157
|
+
)
|
|
1158
|
+
|
|
1159
|
+
imax = [ imax, @vis_lines, @lines - @top_line - 1 ].min
|
|
1160
|
+
@offsets_gc.set_foreground(self.style.text(Gtk::STATE_NORMAL))
|
|
1161
|
+
|
|
1162
|
+
(imin..imax).each do |i|
|
|
1163
|
+
text = "%08x" % ((@top_line + i) * @cpl + @starting_offset)
|
|
1164
|
+
@olayout.set_text(text)
|
|
1165
|
+
@offsets.window.draw_layout(@offsets_gc, 0, i * @char_height, @olayout)
|
|
1166
|
+
end
|
|
1167
|
+
end
|
|
1168
|
+
|
|
1169
|
+
def render_byte(pos)
|
|
1170
|
+
return unless @xdisp_gc and @adisp_gc and @xdisp.realized? and @adisp.realized?
|
|
1171
|
+
|
|
1172
|
+
return unless (coords = get_xcoords(pos))
|
|
1173
|
+
cx, cy = coords
|
|
1174
|
+
c = format_xbyte(pos)
|
|
1175
|
+
|
|
1176
|
+
@xdisp_gc.set_foreground(self.style.base(Gtk::STATE_NORMAL))
|
|
1177
|
+
@xdisp.window.draw_rectangle(
|
|
1178
|
+
@xdisp_gc,
|
|
1179
|
+
true,
|
|
1180
|
+
cx, cy,
|
|
1181
|
+
2 * @char_width, @char_height
|
|
1182
|
+
)
|
|
1183
|
+
|
|
1184
|
+
if pos < @data.size
|
|
1185
|
+
@xdisp_gc.set_foreground(self.style.text(Gtk::STATE_NORMAL))
|
|
1186
|
+
@xlayout.set_text(c)
|
|
1187
|
+
@xdisp.window.draw_layout(@xdisp_gc, cx, cy, @xlayout)
|
|
1188
|
+
end
|
|
1189
|
+
|
|
1190
|
+
return unless (coords = get_acoords(pos))
|
|
1191
|
+
cx, cy = coords
|
|
1192
|
+
|
|
1193
|
+
@adisp_gc.set_foreground(self.style.base(Gtk::STATE_NORMAL))
|
|
1194
|
+
@adisp.window.draw_rectangle(
|
|
1195
|
+
@adisp_gc,
|
|
1196
|
+
true,
|
|
1197
|
+
cx, cy,
|
|
1198
|
+
@char_width, @char_height
|
|
1199
|
+
)
|
|
1200
|
+
|
|
1201
|
+
if pos < @data.size
|
|
1202
|
+
@adisp_gc.set_foreground(self.style.text(Gtk::STATE_NORMAL))
|
|
1203
|
+
c = get_byte(pos)
|
|
1204
|
+
c = '.' unless is_displayable(c)
|
|
1205
|
+
|
|
1206
|
+
@alayout.set_text(c)
|
|
1207
|
+
@adisp.window.draw_layout(@adisp_gc, cx, cy, @alayout)
|
|
1208
|
+
end
|
|
1209
|
+
end
|
|
1210
|
+
|
|
1211
|
+
def render_xc
|
|
1212
|
+
return unless @xdisp.realized?
|
|
1213
|
+
|
|
1214
|
+
if coords = get_xcoords(@cursor_pos)
|
|
1215
|
+
cx, cy = coords
|
|
1216
|
+
|
|
1217
|
+
c = format_xbyte(@cursor_pos)
|
|
1218
|
+
if @lower_nibble
|
|
1219
|
+
cx += @char_width
|
|
1220
|
+
c = c[1,1]
|
|
1221
|
+
else
|
|
1222
|
+
c = c[0,1]
|
|
1223
|
+
end
|
|
1224
|
+
|
|
1225
|
+
@xdisp_gc.set_foreground(self.style.base(Gtk::STATE_ACTIVE))
|
|
1226
|
+
@xdisp.window.draw_rectangle(
|
|
1227
|
+
@xdisp_gc,
|
|
1228
|
+
(@active_view == View::HEX),
|
|
1229
|
+
cx, cy,
|
|
1230
|
+
@char_width,
|
|
1231
|
+
@char_height - 1
|
|
1232
|
+
)
|
|
1233
|
+
@xdisp_gc.set_foreground(self.style.text(Gtk::STATE_ACTIVE))
|
|
1234
|
+
@xlayout.set_text(c)
|
|
1235
|
+
@xdisp.window.draw_layout(@xdisp_gc, cx, cy, @xlayout)
|
|
1236
|
+
end
|
|
1237
|
+
end
|
|
1238
|
+
|
|
1239
|
+
def render_ac
|
|
1240
|
+
return unless @adisp.realized?
|
|
1241
|
+
|
|
1242
|
+
if coords = get_acoords(@cursor_pos)
|
|
1243
|
+
cx, cy = coords
|
|
1244
|
+
|
|
1245
|
+
c = get_byte(@cursor_pos)
|
|
1246
|
+
c = '.' unless is_displayable(c)
|
|
1247
|
+
|
|
1248
|
+
@adisp_gc.set_foreground(self.style.base(Gtk::STATE_ACTIVE))
|
|
1249
|
+
@adisp.window.draw_rectangle(
|
|
1250
|
+
@adisp_gc,
|
|
1251
|
+
(@active_view == View::ASCII),
|
|
1252
|
+
cx, cy,
|
|
1253
|
+
@char_width,
|
|
1254
|
+
@char_height - 1
|
|
1255
|
+
)
|
|
1256
|
+
@adisp_gc.set_foreground(self.style.text(Gtk::STATE_ACTIVE))
|
|
1257
|
+
@alayout.set_text(c)
|
|
1258
|
+
@adisp.window.draw_layout(@adisp_gc, cx, cy, @alayout)
|
|
1259
|
+
end
|
|
1260
|
+
end
|
|
1261
|
+
|
|
1262
|
+
def get_xcoords(pos)
|
|
1263
|
+
return nil if @cpl == 0
|
|
1264
|
+
|
|
1265
|
+
cy = pos / @cpl - @top_line
|
|
1266
|
+
return nil if cy < 0
|
|
1267
|
+
|
|
1268
|
+
cx = 2 * (pos % @cpl)
|
|
1269
|
+
spaces = (pos % @cpl) / @group_type
|
|
1270
|
+
|
|
1271
|
+
cx *= @char_width
|
|
1272
|
+
cy *= @char_height
|
|
1273
|
+
spaces *= @char_width
|
|
1274
|
+
|
|
1275
|
+
[cx + spaces, cy]
|
|
1276
|
+
end
|
|
1277
|
+
|
|
1278
|
+
def get_acoords(pos)
|
|
1279
|
+
return nil if @cpl == 0
|
|
1280
|
+
|
|
1281
|
+
cy = pos / @cpl - @top_line
|
|
1282
|
+
return nil if cy < 0
|
|
1283
|
+
|
|
1284
|
+
cy *= @char_height
|
|
1285
|
+
cx = @char_width * (pos % @cpl)
|
|
1286
|
+
|
|
1287
|
+
[cx, cy]
|
|
1288
|
+
end
|
|
1289
|
+
|
|
1290
|
+
def format_xblock(s, e)
|
|
1291
|
+
@disp_buffer = ''
|
|
1292
|
+
|
|
1293
|
+
(s+1..e).each do |i|
|
|
1294
|
+
@disp_buffer << get_byte(i - 1).unpack('H2')[0]
|
|
1295
|
+
@disp_buffer << ' ' if i % @group_type == 0
|
|
1296
|
+
end
|
|
1297
|
+
|
|
1298
|
+
@disp_buffer.size
|
|
1299
|
+
end
|
|
1300
|
+
|
|
1301
|
+
def format_ablock(s, e)
|
|
1302
|
+
@disp_buffer = ''
|
|
1303
|
+
|
|
1304
|
+
(s..e-1).each do |i|
|
|
1305
|
+
c = get_byte(i)
|
|
1306
|
+
c = '.' unless is_displayable(c)
|
|
1307
|
+
@disp_buffer << c
|
|
1308
|
+
end
|
|
1309
|
+
|
|
1310
|
+
@disp_buffer.size
|
|
1311
|
+
end
|
|
1312
|
+
|
|
1313
|
+
def get_byte(offset)
|
|
1314
|
+
if offset >= 0 and offset < @data.size
|
|
1315
|
+
@data[offset, 1]
|
|
1316
|
+
else
|
|
1317
|
+
0.chr
|
|
1318
|
+
end
|
|
1319
|
+
end
|
|
1320
|
+
|
|
1321
|
+
def format_xbyte(pos)
|
|
1322
|
+
get_byte(pos).unpack('H2')[0]
|
|
1323
|
+
end
|
|
1324
|
+
end
|
|
1325
|
+
|
|
1326
|
+
end
|
|
1327
|
+
|
|
1328
|
+
__END__
|
|
1329
|
+
hexedit = Gtk::HexEditor.new(File.read '/bin/cat')
|
|
1330
|
+
hexedit.show_offsets(true)
|
|
1331
|
+
hexedit.set_cursor 2
|
|
1332
|
+
hexedit.set_cursor_on_lower_nibble true
|
|
1333
|
+
hexedit.set_font 'Terminus 12'
|
|
1334
|
+
hexedit.set_group_type Gtk::HexEditor::Group::LONG
|
|
1335
|
+
|
|
1336
|
+
window = Gtk::Window.new
|
|
1337
|
+
window.add(hexedit)
|
|
1338
|
+
|
|
1339
|
+
window.show_all
|
|
1340
|
+
|
|
1341
|
+
Gtk.main
|