emonti-hexwrench 0.2.0
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/History.txt +7 -0
- data/README.rdoc +69 -0
- data/Rakefile +42 -0
- data/bin/hexwrench +14 -0
- data/hexwrench.gemspec +38 -0
- data/lib/hexwrench/build_xrc.sh +1 -0
- data/lib/hexwrench/data_inspector.rb +138 -0
- data/lib/hexwrench/edit_frame.rb +501 -0
- data/lib/hexwrench/edit_window.rb +910 -0
- data/lib/hexwrench/gui.rb +62 -0
- data/lib/hexwrench/stringsgrid.rb +72 -0
- data/lib/hexwrench/stringslist.rb +105 -0
- data/lib/hexwrench/stringsvlist.rb +123 -0
- data/lib/hexwrench/ui/gui.xrc +93 -0
- data/lib/hexwrench.rb +14 -0
- data/samples/ascii_heat_map.rb +13 -0
- data/tasks/ann.rake +80 -0
- data/tasks/bones.rake +20 -0
- data/tasks/gem.rake +201 -0
- data/tasks/git.rake +40 -0
- data/tasks/notes.rake +27 -0
- data/tasks/post_load.rake +34 -0
- data/tasks/rdoc.rake +51 -0
- data/tasks/rubyforge.rake +55 -0
- data/tasks/setup.rb +292 -0
- data/tasks/spec.rake +54 -0
- data/tasks/svn.rake +47 -0
- data/tasks/test.rake +40 -0
- metadata +105 -0
@@ -0,0 +1,501 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module Hexwrench
|
4
|
+
begin
|
5
|
+
require 'wxirb'
|
6
|
+
HAVE_WXIRB=true
|
7
|
+
rescue LoadError
|
8
|
+
HAVE_WXIRB=false
|
9
|
+
end
|
10
|
+
|
11
|
+
# Our main application window super-classes EditorFrameBase, which is
|
12
|
+
# pulled in from XRC via gui.rb
|
13
|
+
class EditorFrame < EditorFrameBase
|
14
|
+
attr_accessor :filename
|
15
|
+
attr_reader :editor, :config, :util_search, :util_jump
|
16
|
+
|
17
|
+
def initialize(parent, opts={})
|
18
|
+
super(parent)
|
19
|
+
set_title "Hexwrench"
|
20
|
+
|
21
|
+
# XXX how we gonna do config? hmm.
|
22
|
+
@config ||= {}
|
23
|
+
|
24
|
+
buf = opts.delete(:data)
|
25
|
+
sizer = Wx::BoxSizer.new(Wx::HORIZONTAL)
|
26
|
+
@editor = EditWindow.new(self, buf)
|
27
|
+
if f=opts.delete(:filename)
|
28
|
+
do_open_file(f)
|
29
|
+
end
|
30
|
+
|
31
|
+
sizer.add(@editor, 1, Wx::EXPAND|Wx::ALL, 2)
|
32
|
+
self.sizer = sizer
|
33
|
+
|
34
|
+
# Set up the 'jump' and 'search' toolbar utilities
|
35
|
+
@util_search.extend(UtilTextCtrl)
|
36
|
+
@util_jump.extend(UtilTextCtrl)
|
37
|
+
|
38
|
+
@util_search.init
|
39
|
+
@util_jump.init
|
40
|
+
|
41
|
+
evt_text_enter @util_search, :on_search_util
|
42
|
+
evt_text_enter @util_jump, :on_jump_util
|
43
|
+
|
44
|
+
@editor.instance_eval do
|
45
|
+
# redirect the hex editor's 'ins_mode accessors to our own
|
46
|
+
def ins_mode; parent.ins_mode ; end
|
47
|
+
def ins_mode=(val); parent.ins_mode=(val) ; end
|
48
|
+
end
|
49
|
+
|
50
|
+
update_status_bar()
|
51
|
+
init_menu_bar()
|
52
|
+
|
53
|
+
# Custom event handlers from the EditorWindow
|
54
|
+
evt_cursor_moved @editor, :on_cursor_move
|
55
|
+
evt_data_changed @editor, :on_data_change
|
56
|
+
|
57
|
+
evt_close :on_close
|
58
|
+
|
59
|
+
@editor.set_focus
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
# Arranges all the event handlers, hot-keys, help text, and various
|
64
|
+
# other things related to menu items
|
65
|
+
def init_menu_bar
|
66
|
+
# File menu
|
67
|
+
evt_menu @mitem_open, :on_menu_open
|
68
|
+
evt_menu @mitem_new, :on_menu_new
|
69
|
+
evt_menu(@mitem_save) {|evt| do_save() }
|
70
|
+
evt_menu(@mitem_quit) {|evt| do_quit() }
|
71
|
+
|
72
|
+
# Edit menu
|
73
|
+
evt_menu @mitem_copy, :on_menu_copy
|
74
|
+
evt_menu @mitem_cut, :on_menu_cut
|
75
|
+
evt_menu @mitem_paste, :on_menu_paste
|
76
|
+
evt_menu @mitem_select_all, :on_menu_select_all
|
77
|
+
evt_menu @mitem_select_range, :on_menu_stub
|
78
|
+
evt_menu @mitem_adv_search, :on_menu_stub
|
79
|
+
|
80
|
+
# Tools menu
|
81
|
+
evt_menu @mitem_data_inspector, :on_menu_data_inspector
|
82
|
+
evt_menu @mitem_strings, :on_menu_strings
|
83
|
+
|
84
|
+
mb = self.get_menu_bar
|
85
|
+
|
86
|
+
# Create hot-key "accelerators" for menu items
|
87
|
+
# Note: Clipboard hot-keys are handled in child controls
|
88
|
+
shortcuts=[
|
89
|
+
[Wx::MOD_CMD, ?o, @mitem_open],
|
90
|
+
[Wx::MOD_CMD, ?n, @mitem_new],
|
91
|
+
[Wx::MOD_CMD, ?s, @mitem_save],
|
92
|
+
[Wx::MOD_CMD, ?q, @mitem_quit],
|
93
|
+
[Wx::MOD_CMD, ?r, @mitem_select_range],
|
94
|
+
[Wx::MOD_CMD, ?f, @mitem_adv_search],
|
95
|
+
[Wx::MOD_CMD, ?i, @mitem_data_inspector],
|
96
|
+
[Wx::MOD_CMD|Wx::MOD_SHIFT, ?s, @mitem_strings],
|
97
|
+
]
|
98
|
+
|
99
|
+
# add the WXIRB console option to the tools menu if it's available
|
100
|
+
if HAVE_WXIRB
|
101
|
+
tools = mb.get_menu(mb.find_menu("Tools"))
|
102
|
+
cons_item = Wx::MenuItem.new(
|
103
|
+
tools,
|
104
|
+
Wx::ID_ANY,
|
105
|
+
"WxIRB Console",
|
106
|
+
"Toggle Console. Key: shift+cmd+C"
|
107
|
+
)
|
108
|
+
tools.append_separator
|
109
|
+
tools.append_item(cons_item)
|
110
|
+
@mitem_console = cons_item.get_id
|
111
|
+
evt_menu @mitem_console, :on_menu_console
|
112
|
+
shortcuts << [Wx::MOD_CMD|Wx::MOD_SHIFT, ?c, @mitem_console]
|
113
|
+
end
|
114
|
+
|
115
|
+
self.accelerator_table = Wx::AcceleratorTable[*shortcuts]
|
116
|
+
|
117
|
+
# Set usage help so it will appear in status bar on mouse-over.
|
118
|
+
# We need to look menu items up since they are given to us as ID
|
119
|
+
# values by the XRC stub
|
120
|
+
mb.find_item(@mitem_open).help="Open a file in editor. Key: cmd+o"
|
121
|
+
mb.find_item(@mitem_new).help="Start a new buffer. Key: cmd+n"
|
122
|
+
mb.find_item(@mitem_save).help="Save to a file. Key: cmd+s"
|
123
|
+
mb.find_item(@mitem_quit).help="Quit program. Key: cmd+q"
|
124
|
+
mb.find_item(@mitem_copy).help="Copy to clipboard. Key: cmd+c"
|
125
|
+
mb.find_item(@mitem_cut).help="Cut to clipboard. Key: cmd+x"
|
126
|
+
mb.find_item(@mitem_paste).help="Paste from clipboard. Key: cmd+v"
|
127
|
+
mb.find_item(@mitem_select_all).help="Select entire buffer. Key: cmd+a"
|
128
|
+
mb.find_item(@mitem_select_range).help="Select a range. Key: cmd+r"
|
129
|
+
mb.find_item(@mitem_adv_search).help="Adv. search/replace. Key: cmd+f"
|
130
|
+
mb.find_item(@mitem_data_inspector).help="Toggle Inspector. Key: cmd+i"
|
131
|
+
mb.find_item(@mitem_strings).help="Toggle Strings. Key: shift+cmd+S"
|
132
|
+
|
133
|
+
return mb
|
134
|
+
end
|
135
|
+
|
136
|
+
# Called when the user clicks on File -> Quit, closes the window, or
|
137
|
+
# uses the CMD+q hotkey
|
138
|
+
def do_quit
|
139
|
+
self.close
|
140
|
+
end
|
141
|
+
|
142
|
+
# Called internally to update the status bar information
|
143
|
+
def update_status_bar
|
144
|
+
set_status_text("Offset: #{@editor.cur_pos}/#{@editor.data.size}", 0)
|
145
|
+
|
146
|
+
sel_txt = if sel=@editor.selection
|
147
|
+
"#{sel.last-sel.first+1} bytes (#{sel})"
|
148
|
+
else
|
149
|
+
"nil"
|
150
|
+
end
|
151
|
+
|
152
|
+
set_status_text("Selection: #{sel_txt}", 1)
|
153
|
+
end
|
154
|
+
|
155
|
+
# returns true/false depending on whether the "Ins:" toolbar checkbox is
|
156
|
+
# checked
|
157
|
+
def ins_mode ; @util_ins_chk.value ; end
|
158
|
+
|
159
|
+
# Changes the "Ins:" toolbar checkbox to true/false (checked/unchecked)
|
160
|
+
def ins_mode=(val); @util_ins_chk.value=value ; end
|
161
|
+
|
162
|
+
# Converts a string from hex to binary - used in the Hex Search feature
|
163
|
+
# from the tool-bar
|
164
|
+
def unhexify(val)
|
165
|
+
if (val =~ /^[a-f0-9 ]+$/i)
|
166
|
+
val.strip.gsub(/([a-f0-9]{1,2}) */i) { $1.hex.chr }
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
# Handles Wx::CloseEvent. Confirms the user want's to close when
|
171
|
+
# unsaved changes exist
|
172
|
+
def on_close(evt)
|
173
|
+
if @buffer_changed and not confirm_discard_changes?
|
174
|
+
evt.can_veto=true
|
175
|
+
evt.veto(true)
|
176
|
+
else
|
177
|
+
evt.skip(true)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
# Called when a user presses enter in the "Search" tool-bar textbox
|
182
|
+
def on_search_util(evt)
|
183
|
+
val = @util_search.value
|
184
|
+
kstr = @util_search_kind.string_selection
|
185
|
+
do_search( val, EditWindow::AREAS[ {"Hex" => 0, "ASCII" => 1}[kstr] ] )
|
186
|
+
end
|
187
|
+
|
188
|
+
# Implements data search for the "Search" tool-bar item
|
189
|
+
def do_search(val, kind)
|
190
|
+
pos = @editor.cur_pos+1
|
191
|
+
if (
|
192
|
+
( (kind == :ascii) or (kind == :hex and val=unhexify(val)) ) and
|
193
|
+
( dat = @editor.data[pos..-1]) and
|
194
|
+
( idx = @editor.data[pos..-1].index(val) )
|
195
|
+
)
|
196
|
+
|
197
|
+
idx+=pos
|
198
|
+
@editor.select_range(idx..idx+val.size-1)
|
199
|
+
@editor.scroll_to_idx(idx)
|
200
|
+
@editor.send("set_area_#{kind.to_s}")
|
201
|
+
@editor.refresh
|
202
|
+
else
|
203
|
+
@util_search.do_error
|
204
|
+
end
|
205
|
+
@editor.set_focus
|
206
|
+
end
|
207
|
+
|
208
|
+
|
209
|
+
# Called when a user presses enter in the "Jump to" tool-bar textbox
|
210
|
+
def on_jump_util(evt)
|
211
|
+
val = @util_jump.value
|
212
|
+
if((m=/^(?:0?x([A-Fa-f0-9]+)|(\d+))$/.match(val)) and
|
213
|
+
(idx = (m[1])? m[1].hex : m[2].to_i) and
|
214
|
+
(@editor.data.size > idx))
|
215
|
+
@editor.clear_selection()
|
216
|
+
@editor.set_area_hex()
|
217
|
+
@editor.move_to_idx(idx)
|
218
|
+
@editor.refresh
|
219
|
+
else
|
220
|
+
@util_jump.do_error
|
221
|
+
end
|
222
|
+
@editor.set_focus
|
223
|
+
end
|
224
|
+
|
225
|
+
|
226
|
+
# Called from event handlers to clear all utility textbox errors
|
227
|
+
def clear_util_errors
|
228
|
+
@util_search.clear_error
|
229
|
+
@util_jump.clear_error
|
230
|
+
end
|
231
|
+
|
232
|
+
|
233
|
+
# Set's the internal filename and window title info.
|
234
|
+
def set_filename(name)
|
235
|
+
@filename = name
|
236
|
+
if name
|
237
|
+
set_title "Hexwrench - "+
|
238
|
+
"#{File.basename(name)} "+
|
239
|
+
"(#{File.dirname(File.expand_path(name))})"
|
240
|
+
else
|
241
|
+
set_title "Hexwrench"
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
# Stub indicating inactive menu items with a message dialog popup
|
246
|
+
# note: XXX this is mostly to remind me to add these features =)
|
247
|
+
def on_menu_stub(evt)
|
248
|
+
Wx::MessageDialog.new(self, :caption => "Coming soon",
|
249
|
+
:message => "Sorry. This feature not yet implemented.").show_modal
|
250
|
+
end
|
251
|
+
|
252
|
+
# Used to pop-up a confirmation dialog when the user is about to discard
|
253
|
+
# changes in the editor.
|
254
|
+
def confirm_discard_changes?
|
255
|
+
ret = Wx::MessageDialog.new(
|
256
|
+
self,
|
257
|
+
:style => Wx::YES_NO|Wx::NO_DEFAULT,
|
258
|
+
:caption => "Discard Changes?",
|
259
|
+
:message => "Un-saved changes will be lost. Proceed anyway?"
|
260
|
+
).show_modal
|
261
|
+
|
262
|
+
if ret == Wx::ID_YES
|
263
|
+
@buffer_changed=nil
|
264
|
+
return true
|
265
|
+
else
|
266
|
+
return false
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
# Handles the File -> Open menu item.
|
271
|
+
def on_menu_open(evt)
|
272
|
+
return nil if @buffer_changed and not confirm_discard_changes?
|
273
|
+
|
274
|
+
open_dlg = Wx::FileDialog.new( self,
|
275
|
+
:style => Wx::FD_OPEN|Wx::FD_FILE_MUST_EXIST)
|
276
|
+
|
277
|
+
if open_dlg.show_modal == Wx::ID_OK
|
278
|
+
do_open_file(open_dlg.path)
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
# Implements opening new files - pops up a error dialog if a file
|
283
|
+
# error is encountered when reading the file.
|
284
|
+
def do_open_file(filename)
|
285
|
+
dat=nil
|
286
|
+
begin
|
287
|
+
dat = File.read(filename)
|
288
|
+
rescue => e
|
289
|
+
Wx::MessageDialog.new(self,
|
290
|
+
:caption => "Error Opening File",
|
291
|
+
:message => "#{e.class} - #{e.to_s}"
|
292
|
+
).show_modal
|
293
|
+
end
|
294
|
+
if dat
|
295
|
+
set_filename(filename)
|
296
|
+
@editor.set_data(dat) if dat
|
297
|
+
@editor.move_to_idx(0)
|
298
|
+
@new_buffer=true
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
# Implements the File -> New menu item. Replaces the current editor
|
303
|
+
# buffer with an empty string.
|
304
|
+
def on_menu_new(evt)
|
305
|
+
return nil if @buffer_changed and not confirm_discard_changes?
|
306
|
+
@new_buffer=true
|
307
|
+
set_filename(nil)
|
308
|
+
@editor.set_data nil
|
309
|
+
end
|
310
|
+
|
311
|
+
# Handles the user clicking on the Edit -> Select All menu item.
|
312
|
+
# This method just calls the EditWindow.do_select_all() method
|
313
|
+
def on_menu_select_all(evt)
|
314
|
+
@editor.do_select_all
|
315
|
+
end
|
316
|
+
|
317
|
+
# Implements the File -> Save menu item.
|
318
|
+
# This method will call on_save_as if the user has not specified a
|
319
|
+
# file yet
|
320
|
+
def do_save(filename = nil)
|
321
|
+
filename ||= @filename
|
322
|
+
if filename
|
323
|
+
begin
|
324
|
+
File.open(filename, "w") {|f| f.write @editor.data }
|
325
|
+
@filename = filename
|
326
|
+
@new_buffer=true
|
327
|
+
@buffer_changed=false
|
328
|
+
rescue => e
|
329
|
+
Wx::MessageDialog.new(self,
|
330
|
+
:caption => "Error Saving File",
|
331
|
+
:message => "#{e.class} - #{e.to_s}"
|
332
|
+
).show_modal
|
333
|
+
end
|
334
|
+
else
|
335
|
+
do_save_as()
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
# Implements the 'Save As' feature - presenting the user with a file
|
340
|
+
# save dialog.
|
341
|
+
def do_save_as()
|
342
|
+
save_dlg = Wx::FileDialog.new(self,
|
343
|
+
:style => Wx::FD_SAVE|Wx::FD_OVERWRITE_PROMPT)
|
344
|
+
if save_dlg.show_modal == Wx::ID_OK
|
345
|
+
set_filename(save_dlg.path)
|
346
|
+
do_save(save_dlg.path)
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
# Handles the user clicking on the Edit -> Copy menu item.
|
351
|
+
# This method just calls the EditWindow.do_clipboard_copy() method
|
352
|
+
def on_menu_copy(evt)
|
353
|
+
@editor.do_clipboard_copy()
|
354
|
+
end
|
355
|
+
|
356
|
+
# Handles the user clicking on the Edit -> Cut menu item.
|
357
|
+
# This method just calls the EditWindow.do_clipboard_cut() method
|
358
|
+
def on_menu_cut(evt)
|
359
|
+
@editor.do_clipboard_cut()
|
360
|
+
end
|
361
|
+
|
362
|
+
# Handles the user clicking on the Edit -> Paste menu item.
|
363
|
+
# This method just calls the EditWindow.do_clipboard_paste() method
|
364
|
+
def on_menu_paste(evt)
|
365
|
+
@editor.do_clipboard_paste()
|
366
|
+
end
|
367
|
+
|
368
|
+
# Toggles a strings listing pop-up when the user selects the
|
369
|
+
# 'Tools -> Strings' menu item
|
370
|
+
def on_menu_strings(evt)
|
371
|
+
if @strings
|
372
|
+
@strings.destroy()
|
373
|
+
@strings = nil
|
374
|
+
else
|
375
|
+
@strings = StringsFrame.new(self, @editor, @config[:strings_opts])
|
376
|
+
@strings.accelerator_table = self.accelerator_table # clone hotkeys
|
377
|
+
@strings.evt_window_destroy {|evt| @strings=nil;evt.skip() }
|
378
|
+
@strings.show
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
# Toggles the data inspector window on and off when user
|
383
|
+
# selects the "Tools -> Data Inspector" menu item
|
384
|
+
def on_menu_data_inspector(evt)
|
385
|
+
if @d_inspector
|
386
|
+
@d_inspector.destroy()
|
387
|
+
@d_inspector = nil
|
388
|
+
else
|
389
|
+
@d_inspector = DataInspector.new(self, :editor => @editor)
|
390
|
+
@d_inspector.accelerator_table = self.accelerator_table # clone hotkeys
|
391
|
+
@d_inspector.evt_window_destroy { |evt| @d_inspector=nil; evt.skip() }
|
392
|
+
@d_inspector.do_inspectors
|
393
|
+
@d_inspector.show
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
# Event handler for the Tools -> Console menu item.
|
398
|
+
# This method only ever fires if wxirb is available. (based on HAVE_WXIRB)
|
399
|
+
def on_menu_console(evt)
|
400
|
+
return nil unless HAVE_WXIRB
|
401
|
+
if $wxirb
|
402
|
+
$wxirb.destroy()
|
403
|
+
$wxirb = nil
|
404
|
+
else
|
405
|
+
$wxirb = WxIRB::BaseFrame.new(self, :binding => binding)
|
406
|
+
$wxirb.accelerator_table = self.accelerator_table # clone hotkeys
|
407
|
+
$wxirb.evt_window_destroy { |evt| $wxirb=nil; evt.skip() }
|
408
|
+
$wxirb.show
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
# Event handler for cursor movement to update various UI elements and
|
413
|
+
# active tool windows.
|
414
|
+
def on_cursor_move(evt)
|
415
|
+
clear_util_errors
|
416
|
+
update_status_bar()
|
417
|
+
@d_inspector.do_inspectors if @d_inspector
|
418
|
+
evt.skip()
|
419
|
+
end
|
420
|
+
|
421
|
+
# Event handler for data changes to update various UI elements and active
|
422
|
+
# tool windows.
|
423
|
+
def on_data_change(evt)
|
424
|
+
if @new_buffer
|
425
|
+
@new_buffer=false
|
426
|
+
else
|
427
|
+
@buffer_changed=true
|
428
|
+
end
|
429
|
+
clear_util_errors
|
430
|
+
update_status_bar()
|
431
|
+
@d_inspector.do_inspectors if @d_inspector
|
432
|
+
@strings.notify_data_change if @strings
|
433
|
+
end
|
434
|
+
end
|
435
|
+
|
436
|
+
|
437
|
+
# This module is used to extend a regular Wx::TextCtrl text box to display
|
438
|
+
# greyed out text when idle which describes the control's purpose.
|
439
|
+
# As soon as focus is set on the control, the text disappears.
|
440
|
+
module UtilTextCtrl
|
441
|
+
|
442
|
+
# This init method is called after creation since we don't
|
443
|
+
# override 'new' in the XRC derived window element. Takes a hash of
|
444
|
+
# :name => value options. The only option is currently :default_text,
|
445
|
+
# which is the text to display when the control is idle. If :default_text
|
446
|
+
# is not specified, the control will use the initial value in the text box
|
447
|
+
# as default text.
|
448
|
+
def init(opts={})
|
449
|
+
@dflt_text = (opts[:default_text] || get_value.dup)
|
450
|
+
evt_set_focus :on_set_focus
|
451
|
+
evt_kill_focus :on_kill_focus
|
452
|
+
do_default_text
|
453
|
+
end
|
454
|
+
|
455
|
+
# Returns text to its greyed-out default display
|
456
|
+
def do_default_text
|
457
|
+
@error = false
|
458
|
+
clear()
|
459
|
+
set_default_style(Wx::TextAttr.new( Wx::LIGHT_GREY) )
|
460
|
+
set_value(@dflt_text)
|
461
|
+
set_default_style(Wx::TextAttr.new( Wx::BLACK) )
|
462
|
+
end
|
463
|
+
|
464
|
+
# Indicates an error in the text box by turning current text red.
|
465
|
+
def do_error
|
466
|
+
@error = true
|
467
|
+
val = self.value
|
468
|
+
clear()
|
469
|
+
set_default_style(Wx::TextAttr.new( Wx::RED))
|
470
|
+
set_value(val)
|
471
|
+
set_default_style(Wx::TextAttr.new( Wx::BLACK))
|
472
|
+
set_insertion_point_end
|
473
|
+
end
|
474
|
+
|
475
|
+
# Clears error text set by do_error and returns the textbox to its
|
476
|
+
# default text display.
|
477
|
+
def clear_error
|
478
|
+
if @error
|
479
|
+
@error = false
|
480
|
+
do_default_text
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
# Event handler for when focus is set to this text control. Clears
|
485
|
+
# default text in preparation for user input.
|
486
|
+
def on_set_focus(evt)
|
487
|
+
@error = false
|
488
|
+
clear()
|
489
|
+
set_default_style(Wx::TextAttr.new( Wx::BLACK))
|
490
|
+
evt.skip()
|
491
|
+
end
|
492
|
+
|
493
|
+
# Event handler for when focus is lost to this text control. Returns
|
494
|
+
# the text to its default display unless an error in input was flagged.
|
495
|
+
def on_kill_focus(evt)
|
496
|
+
do_default_text if not @error
|
497
|
+
evt.skip()
|
498
|
+
end
|
499
|
+
end
|
500
|
+
end
|
501
|
+
|